|
|
|
|
@ -9,8 +9,15 @@ use sdl2::rect::{Point, Rect}; |
|
|
|
|
|
|
|
|
|
mod editor_render; |
|
|
|
|
|
|
|
|
|
static SCREEN_WIDTH: u32 = 1680; |
|
|
|
|
static SCREEN_HEIGHT: u32 = 945; |
|
|
|
|
static SCREEN_WIDTH: u32 = 1280; |
|
|
|
|
static SCREEN_HEIGHT: u32 = 720; |
|
|
|
|
|
|
|
|
|
// TODO: Make this configurable
|
|
|
|
|
static REFRESH_RATE: u32 = 50; |
|
|
|
|
|
|
|
|
|
// TODO: Make this configurable
|
|
|
|
|
static UNDO_TIME: u32 = 500; |
|
|
|
|
static UNDO_TIME_COUNT: u32 = (REFRESH_RATE as f32 * (UNDO_TIME as f32 / 1000.0)) as u32; |
|
|
|
|
|
|
|
|
|
struct ModifierKeys { |
|
|
|
|
alt: bool, |
|
|
|
|
@ -19,8 +26,9 @@ struct ModifierKeys { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn main() -> Result<(), String> { |
|
|
|
|
let mut modifier_keys = ModifierKeys {alt: false, ctrl: false, shift: false}; |
|
|
|
|
|
|
|
|
|
let mut clipboard_context: ClipboardContext = ClipboardProvider::new().unwrap(); |
|
|
|
|
let (glyph_atlas, glyph_metrics) = editor_render::generate_glyph_data(); |
|
|
|
|
|
|
|
|
|
let sdl_context = sdl2::init()?; |
|
|
|
|
let video_subsys = sdl_context.video()?; |
|
|
|
|
@ -41,9 +49,15 @@ pub fn main() -> Result<(), String> { |
|
|
|
|
let mut cursor_position = 0; |
|
|
|
|
let mut selection_anchor: Option<usize> = None; |
|
|
|
|
|
|
|
|
|
let mut undo_history: Vec<(String, usize)> = vec![]; |
|
|
|
|
let mut undo_position: usize = 0; |
|
|
|
|
let mut undo_timer: u32 = UNDO_TIME_COUNT; |
|
|
|
|
|
|
|
|
|
let pad_offset = Point::new(10, 10); |
|
|
|
|
|
|
|
|
|
let mut draw_text = |window_size: (u32, u32), text: &str, pos: usize| -> Result<(), String> { |
|
|
|
|
let (glyph_atlas, glyph_metrics) = editor_render::generate_glyph_data(); |
|
|
|
|
|
|
|
|
|
let mut draw = |window_size: (u32, u32), text: &str, pos: usize| -> Result<(), String> { |
|
|
|
|
// Draw background
|
|
|
|
|
canvas.set_draw_color(Color::RGB(32, 32, 32)); |
|
|
|
|
canvas.clear(); |
|
|
|
|
@ -96,9 +110,7 @@ pub fn main() -> Result<(), String> { |
|
|
|
|
Ok(()) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
draw_text(window_size, "", cursor_position)?; |
|
|
|
|
|
|
|
|
|
let mut modifier_keys = ModifierKeys {alt: false, ctrl: false, shift: false}; |
|
|
|
|
draw(window_size, "", cursor_position)?; |
|
|
|
|
|
|
|
|
|
'mainloop: loop { |
|
|
|
|
// TODO: Make this completely user-configurable instead of hardcoded
|
|
|
|
|
@ -107,11 +119,11 @@ pub fn main() -> Result<(), String> { |
|
|
|
|
Event::Window { win_event, .. } => { |
|
|
|
|
if let WindowEvent::Resized(w, h) = win_event { |
|
|
|
|
window_size = (w as u32, h as u32); |
|
|
|
|
draw_text(window_size, &buffer, cursor_position)? |
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// ESC or SIGKILL
|
|
|
|
|
Event::KeyDown { keycode: Some(Keycode::Escape), .. } | |
|
|
|
|
// Event::KeyDown { keycode: Some(Keycode::Escape), .. } |
|
|
|
|
|
Event::Quit { .. } => break 'mainloop, |
|
|
|
|
|
|
|
|
|
Event::KeyUp { keycode, .. } => { |
|
|
|
|
@ -156,53 +168,68 @@ pub fn main() -> Result<(), String> { |
|
|
|
|
// DELETE key
|
|
|
|
|
Some(Keycode::Delete) => { |
|
|
|
|
if buffer.len() > 0 && cursor_position < buffer.len() { |
|
|
|
|
undo_timer = 0; |
|
|
|
|
selection_anchor = None; |
|
|
|
|
|
|
|
|
|
buffer.remove(cursor_position); |
|
|
|
|
draw_text(window_size, &buffer, cursor_position)? |
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// ENTER key
|
|
|
|
|
Some(Keycode::Return) => { |
|
|
|
|
undo_timer = 0; |
|
|
|
|
selection_anchor = None; |
|
|
|
|
|
|
|
|
|
let key = '\n'; |
|
|
|
|
buffer.insert(cursor_position, key); |
|
|
|
|
cursor_position += 1; |
|
|
|
|
draw_text(window_size, &buffer, cursor_position)? |
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// HOME key
|
|
|
|
|
Some(Keycode::Home) => { |
|
|
|
|
selection_anchor = None; |
|
|
|
|
|
|
|
|
|
cursor_position = 0; |
|
|
|
|
draw_text(window_size, &buffer, cursor_position)? |
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// END key
|
|
|
|
|
Some(Keycode::End) => { |
|
|
|
|
selection_anchor = None; |
|
|
|
|
|
|
|
|
|
cursor_position = buffer.len(); |
|
|
|
|
draw_text(window_size, &buffer, cursor_position)? |
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// Left/Back arrow
|
|
|
|
|
Some(Keycode::Left) => { |
|
|
|
|
selection_anchor = None; |
|
|
|
|
|
|
|
|
|
cursor_position = usize::checked_sub(cursor_position, 1) |
|
|
|
|
.unwrap_or(0); |
|
|
|
|
draw_text(window_size, &buffer, cursor_position)? |
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// Right/Forward arrow
|
|
|
|
|
Some(Keycode::Right) => { |
|
|
|
|
selection_anchor = None; |
|
|
|
|
|
|
|
|
|
cursor_position = (cursor_position + 1).min(buffer.len()); |
|
|
|
|
draw_text(window_size, &buffer, cursor_position)? |
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// BACKSPACE key
|
|
|
|
|
// Character backspace
|
|
|
|
|
Some(Keycode::Backspace) => { |
|
|
|
|
if buffer.len() > 0 { |
|
|
|
|
// Character backspace; regular
|
|
|
|
|
undo_timer = 0; |
|
|
|
|
selection_anchor = None; |
|
|
|
|
|
|
|
|
|
buffer.remove(cursor_position - 1); |
|
|
|
|
cursor_position -= 1; |
|
|
|
|
draw_text(window_size, &buffer, cursor_position)? |
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
@ -213,18 +240,42 @@ pub fn main() -> Result<(), String> { |
|
|
|
|
// CTRL down
|
|
|
|
|
(false, true, false) => { |
|
|
|
|
match keycode { |
|
|
|
|
Some(Keycode::Z) => println!("Undo"), |
|
|
|
|
Some(Keycode::Z) => { |
|
|
|
|
if undo_position > 1 { |
|
|
|
|
undo_position -= 1; |
|
|
|
|
let last_undo = undo_history[undo_position - 1].clone(); |
|
|
|
|
buffer = last_undo.0; |
|
|
|
|
cursor_position = last_undo.1; |
|
|
|
|
|
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
Some(Keycode::X) => println!("Cut"), |
|
|
|
|
|
|
|
|
|
Some(Keycode::C) => { |
|
|
|
|
clipboard_context.set_contents(buffer.clone()).unwrap() |
|
|
|
|
}, |
|
|
|
|
Some(Keycode::V) => println!("Paste"), |
|
|
|
|
Some(Keycode::V) => { |
|
|
|
|
let paste = clipboard_context.get_contents().unwrap(); |
|
|
|
|
for character in paste.chars() { |
|
|
|
|
buffer.insert(cursor_position, character); |
|
|
|
|
cursor_position += 1; |
|
|
|
|
|
|
|
|
|
undo_timer = UNDO_TIME_COUNT; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
// BACKSPACE key
|
|
|
|
|
// Word backspace
|
|
|
|
|
// TODO: Clean up this cursed expression
|
|
|
|
|
Some(Keycode::Backspace) => { |
|
|
|
|
if buffer.len() > 0 { |
|
|
|
|
undo_timer = 0; |
|
|
|
|
selection_anchor = None; |
|
|
|
|
|
|
|
|
|
let buffer_chars: Vec<char> = buffer.chars() |
|
|
|
|
.collect(); |
|
|
|
|
while !(buffer_chars[cursor_position - 1] == ' ' || |
|
|
|
|
@ -236,7 +287,7 @@ pub fn main() -> Result<(), String> { |
|
|
|
|
buffer.remove(cursor_position - 1); |
|
|
|
|
cursor_position -= 1; |
|
|
|
|
|
|
|
|
|
draw_text(window_size, &buffer, cursor_position)? |
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
_ => (), |
|
|
|
|
@ -245,8 +296,19 @@ pub fn main() -> Result<(), String> { |
|
|
|
|
|
|
|
|
|
(true, true, false) => { |
|
|
|
|
match keycode { |
|
|
|
|
Some(Keycode::Z) => println!("Redo"), |
|
|
|
|
Some(Keycode::Z) => { |
|
|
|
|
if undo_position < undo_history.len() { |
|
|
|
|
undo_position += 1; |
|
|
|
|
let last_redo = undo_history[undo_position - 1].clone(); |
|
|
|
|
buffer = last_redo.0; |
|
|
|
|
cursor_position = last_redo.1; |
|
|
|
|
|
|
|
|
|
draw(window_size, &buffer, cursor_position)? |
|
|
|
|
} |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
Some(Keycode::X) => println!("Cut line(s)"), |
|
|
|
|
|
|
|
|
|
Some(Keycode::C) => println!("Copy line(s)"), |
|
|
|
|
_ => (), |
|
|
|
|
} |
|
|
|
|
@ -257,18 +319,36 @@ pub fn main() -> Result<(), String> { |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
Event::TextInput { text, .. } => { |
|
|
|
|
undo_timer = 0; |
|
|
|
|
selection_anchor = None; |
|
|
|
|
|
|
|
|
|
let input_char = text.chars().nth(0).expect("Empty"); |
|
|
|
|
|
|
|
|
|
buffer.insert(cursor_position, input_char); |
|
|
|
|
cursor_position += 1; |
|
|
|
|
draw_text(window_size, &buffer, cursor_position)?; |
|
|
|
|
draw(window_size, &buffer, cursor_position)?; |
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
_ => {} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
::std::thread::sleep(std::time::Duration::new(0, 50_000_000)); |
|
|
|
|
// Wait for pause in input to snapshot buffer for undoing/redoing
|
|
|
|
|
if undo_timer < UNDO_TIME_COUNT { |
|
|
|
|
undo_timer += 1; |
|
|
|
|
} else if undo_timer == UNDO_TIME_COUNT { |
|
|
|
|
// println!("Saving undo step.");
|
|
|
|
|
if undo_position < undo_history.len() { |
|
|
|
|
undo_history.truncate(undo_position); |
|
|
|
|
} |
|
|
|
|
undo_history.push((buffer.clone(), cursor_position)); |
|
|
|
|
undo_timer += 1; |
|
|
|
|
undo_position += 1; |
|
|
|
|
|
|
|
|
|
//println!("Undo data: {undo_history:?}");
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
::std::thread::sleep(std::time::Duration::new(0, 1_000_000_000 / REFRESH_RATE)); |
|
|
|
|
} |
|
|
|
|
format!("{selection_anchor:?}"); |
|
|
|
|
println!("{buffer}"); |
|
|
|
|
|