From aa2afa2599bc3a963103d4509eac207311545b4e Mon Sep 17 00:00:00 2001 From: Pavel Strakhov Date: Sat, 6 Apr 2024 15:29:12 +0100 Subject: [PATCH] Add select flag for motion and click actions --- examples/editor-test/src/main.rs | 10 ++- examples/editor/src/main.rs | 51 +++++++---- examples/rich-text/src/main.rs | 51 +++++++---- src/edit/editor.rs | 52 ++++++++++- src/edit/mod.rs | 6 +- src/edit/vi.rs | 145 ++++++++++++++++++++++++------- 6 files changed, 246 insertions(+), 69 deletions(-) diff --git a/examples/editor-test/src/main.rs b/examples/editor-test/src/main.rs index 8a35afc42e..6c76e187dc 100644 --- a/examples/editor-test/src/main.rs +++ b/examples/editor-test/src/main.rs @@ -114,7 +114,10 @@ fn main() { // Test delete of EGC { let cursor = editor.cursor(); - editor.action(Action::Motion(Motion::Previous)); + editor.action(Action::Motion { + motion: Motion::Previous, + select: false, + }); editor.action(Action::Delete); for c in grapheme.chars() { editor.action(Action::Insert(c)); @@ -138,7 +141,10 @@ fn main() { { let cursor = editor.cursor(); editor.action(Action::Enter); - editor.action(Action::Motion(Motion::Previous)); + editor.action(Action::Motion { + motion: Motion::Previous, + select: false, + }); editor.action(Action::Delete); assert_eq!(cursor, editor.cursor()); } diff --git a/examples/editor/src/main.rs b/examples/editor/src/main.rs index 6afeec127c..99c45362dc 100644 --- a/examples/editor/src/main.rs +++ b/examples/editor/src/main.rs @@ -62,6 +62,7 @@ fn main() { } let mut ctrl_pressed = false; + let mut shift_pressed = false; let mut mouse_x = 0.0; let mut mouse_y = 0.0; let mut mouse_left = ElementState::Released; @@ -168,7 +169,8 @@ fn main() { surface_buffer.present().unwrap(); } WindowEvent::ModifiersChanged(modifiers) => { - ctrl_pressed = modifiers.state().control_key() + ctrl_pressed = modifiers.state().control_key(); + shift_pressed = modifiers.state().shift_key(); } WindowEvent::KeyboardInput { event, .. } => { let KeyEvent { @@ -178,28 +180,46 @@ fn main() { if state.is_pressed() { match logical_key { Key::Named(NamedKey::ArrowLeft) => { - editor.action(Action::Motion(Motion::Left)) + editor.action(Action::Motion { + motion: Motion::Left, + select: shift_pressed, + }) } Key::Named(NamedKey::ArrowRight) => { - editor.action(Action::Motion(Motion::Right)) + editor.action(Action::Motion { + motion: Motion::Right, + select: shift_pressed, + }) } Key::Named(NamedKey::ArrowUp) => { - editor.action(Action::Motion(Motion::Up)) + editor.action(Action::Motion { + motion: Motion::Up, + select: shift_pressed, + }) } Key::Named(NamedKey::ArrowDown) => { - editor.action(Action::Motion(Motion::Down)) - } - Key::Named(NamedKey::Home) => { - editor.action(Action::Motion(Motion::Home)) - } - Key::Named(NamedKey::End) => { - editor.action(Action::Motion(Motion::End)) - } - Key::Named(NamedKey::PageUp) => { - editor.action(Action::Motion(Motion::PageUp)) + editor.action(Action::Motion { + motion: Motion::Down, + select: shift_pressed, + }) } + Key::Named(NamedKey::Home) => editor.action(Action::Motion { + motion: Motion::Home, + select: shift_pressed, + }), + Key::Named(NamedKey::End) => editor.action(Action::Motion { + motion: Motion::End, + select: shift_pressed, + }), + Key::Named(NamedKey::PageUp) => editor.action(Action::Motion { + motion: Motion::PageUp, + select: shift_pressed, + }), Key::Named(NamedKey::PageDown) => { - editor.action(Action::Motion(Motion::PageDown)) + editor.action(Action::Motion { + motion: Motion::PageDown, + select: shift_pressed, + }) } Key::Named(NamedKey::Escape) => editor.action(Action::Escape), Key::Named(NamedKey::Enter) => editor.action(Action::Enter), @@ -310,6 +330,7 @@ fn main() { editor.action(Action::Click { x: mouse_x as i32, y: mouse_y as i32, + select: shift_pressed, }); window.request_redraw(); } diff --git a/examples/rich-text/src/main.rs b/examples/rich-text/src/main.rs index 2c42b73eb2..590e4720bd 100644 --- a/examples/rich-text/src/main.rs +++ b/examples/rich-text/src/main.rs @@ -129,6 +129,7 @@ fn main() { editor.with_buffer_mut(|buffer| set_buffer_text(buffer)); let mut ctrl_pressed = false; + let mut shift_pressed = false; let mut mouse_x = 0.0; let mut mouse_y = 0.0; let mut mouse_left = ElementState::Released; @@ -215,7 +216,8 @@ fn main() { surface_buffer.present().unwrap(); } WindowEvent::ModifiersChanged(modifiers) => { - ctrl_pressed = modifiers.state().control_key() + ctrl_pressed = modifiers.state().control_key(); + shift_pressed = modifiers.state().shift_key(); } WindowEvent::KeyboardInput { event, .. } => { let KeyEvent { @@ -225,28 +227,46 @@ fn main() { if state.is_pressed() { match logical_key { Key::Named(NamedKey::ArrowLeft) => { - editor.action(Action::Motion(Motion::Left)) + editor.action(Action::Motion { + motion: Motion::Left, + select: shift_pressed, + }) } Key::Named(NamedKey::ArrowRight) => { - editor.action(Action::Motion(Motion::Right)) + editor.action(Action::Motion { + motion: Motion::Right, + select: shift_pressed, + }) } Key::Named(NamedKey::ArrowUp) => { - editor.action(Action::Motion(Motion::Up)) + editor.action(Action::Motion { + motion: Motion::Up, + select: shift_pressed, + }) } Key::Named(NamedKey::ArrowDown) => { - editor.action(Action::Motion(Motion::Down)) - } - Key::Named(NamedKey::Home) => { - editor.action(Action::Motion(Motion::Home)) - } - Key::Named(NamedKey::End) => { - editor.action(Action::Motion(Motion::End)) - } - Key::Named(NamedKey::PageUp) => { - editor.action(Action::Motion(Motion::PageUp)) + editor.action(Action::Motion { + motion: Motion::Down, + select: shift_pressed, + }) } + Key::Named(NamedKey::Home) => editor.action(Action::Motion { + motion: Motion::Home, + select: shift_pressed, + }), + Key::Named(NamedKey::End) => editor.action(Action::Motion { + motion: Motion::End, + select: shift_pressed, + }), + Key::Named(NamedKey::PageUp) => editor.action(Action::Motion { + motion: Motion::PageUp, + select: shift_pressed, + }), Key::Named(NamedKey::PageDown) => { - editor.action(Action::Motion(Motion::PageDown)) + editor.action(Action::Motion { + motion: Motion::PageDown, + select: shift_pressed, + }) } Key::Named(NamedKey::Escape) => editor.action(Action::Escape), Key::Named(NamedKey::Enter) => editor.action(Action::Enter), @@ -311,6 +331,7 @@ fn main() { editor.action(Action::Click { x: mouse_x /*- line_x*/ as i32, y: mouse_y as i32, + select: shift_pressed, }); window.request_redraw(); } diff --git a/src/edit/editor.rs b/src/edit/editor.rs index c7784d92bb..0de5b9d55f 100644 --- a/src/edit/editor.rs +++ b/src/edit/editor.rs @@ -12,7 +12,7 @@ use unicode_segmentation::UnicodeSegmentation; use crate::Color; use crate::{ Action, Attrs, AttrsList, BorrowedWithFontSystem, BufferLine, BufferRef, Change, ChangeItem, - Cursor, Edit, FontSystem, Selection, Shaping, + Cursor, Edit, FontSystem, Motion, Selection, Shaping, }; /// A wrapper of [`Buffer`] for easy editing @@ -556,8 +556,46 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> { let old_cursor = self.cursor; match action { - Action::Motion(motion) => { + Action::Motion { motion, select } => { let cursor = self.cursor; + if select { + if self.selection == Selection::None { + self.selection = Selection::Normal(self.cursor); + } + } else if let Some((start, end)) = self.selection_bounds() { + if start.line != end.line || start.index != end.index { + let new_cursor = match motion { + // These actions have special behavior when there is an active selection. + Motion::Previous => Some(start), + Motion::Next => Some(end), + Motion::Left => self + .with_buffer_mut(|buffer| { + buffer + .line_shape(font_system, cursor.line) + .map(|shape| shape.rtl) + }) + .map(|rtl| if rtl { end } else { start }), + Motion::Right => self + .with_buffer_mut(|buffer| { + buffer + .line_shape(font_system, cursor.line) + .map(|shape| shape.rtl) + }) + .map(|rtl| if rtl { start } else { end }), + _ => None, + }; + if let Some(new_cursor) = new_cursor { + self.cursor = new_cursor; + self.cursor_x_opt = None; + self.cursor_moved = true; + self.selection = Selection::None; + self.set_redraw(true); + return; + } + } + self.selection = Selection::None; + } + let cursor_x_opt = self.cursor_x_opt; if let Some((new_cursor, new_cursor_x_opt)) = self.with_buffer_mut(|buffer| { buffer.cursor_motion(font_system, cursor, cursor_x_opt, motion) @@ -798,8 +836,14 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> { self.with_buffer_mut(|buffer| buffer.set_redraw(true)); } } - Action::Click { x, y } => { - self.set_selection(Selection::None); + Action::Click { x, y, select } => { + if select { + if self.selection == Selection::None { + self.selection = Selection::Normal(self.cursor); + } + } else { + self.selection = Selection::None; + } if let Some(new_cursor) = self.with_buffer(|buffer| buffer.hit(x as f32, y as f32)) { diff --git a/src/edit/mod.rs b/src/edit/mod.rs index 76bff0e8b9..69d381b00f 100644 --- a/src/edit/mod.rs +++ b/src/edit/mod.rs @@ -23,7 +23,10 @@ mod vi; #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Action { /// Move the cursor with some motion - Motion(Motion), + Motion { + motion: Motion, + select: bool, + }, /// Escape, clears selection Escape, /// Insert character at cursor @@ -42,6 +45,7 @@ pub enum Action { Click { x: i32, y: i32, + select: bool, }, /// Mouse double click at specified position DoubleClick { diff --git a/src/edit/vi.rs b/src/edit/vi.rs index 92b0480cc2..7e8b084811 100644 --- a/src/edit/vi.rs +++ b/src/edit/vi.rs @@ -615,19 +615,42 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer //TODO: this leaves lots of room for issues in translation, should we directly accept Key? Action::Backspace => Key::Backspace, Action::Delete => Key::Delete, - Action::Motion(Motion::Down) => Key::Down, - Action::Motion(Motion::End) => Key::End, + Action::Motion { + motion: Motion::Down, + .. + } => Key::Down, + Action::Motion { + motion: Motion::End, + .. + } => Key::End, Action::Enter => Key::Enter, Action::Escape => Key::Escape, - Action::Motion(Motion::Home) => Key::Home, + Action::Motion { + motion: Motion::Home, + .. + } => Key::Home, Action::Indent => Key::Tab, Action::Insert(c) => Key::Char(c), - Action::Motion(Motion::Left) => Key::Left, - Action::Motion(Motion::PageDown) => Key::PageDown, - Action::Motion(Motion::PageUp) => Key::PageUp, - Action::Motion(Motion::Right) => Key::Right, + Action::Motion { + motion: Motion::Left, + .. + } => Key::Left, + Action::Motion { + motion: Motion::PageDown, + .. + } => Key::PageDown, + Action::Motion { + motion: Motion::PageUp, + .. + } => Key::PageUp, + Action::Motion { + motion: Motion::Right, + .. + } => Key::Right, Action::Unindent => Key::Backtab, - Action::Motion(Motion::Up) => Key::Up, + Action::Motion { + motion: Motion::Up, .. + } => Key::Up, _ => { log::debug!("Pass through action {:?}", action); editor.action(font_system, action); @@ -733,9 +756,21 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer // Move to inserted line, preserving cursor x position if after { - editor.action(font_system, Action::Motion(Motion::Down)); + editor.action( + font_system, + Action::Motion { + motion: Motion::Down, + select: false, + }, + ); } else { - editor.action(font_system, Action::Motion(Motion::Up)); + editor.action( + font_system, + Action::Motion { + motion: Motion::Up, + select: false, + }, + ); } } } @@ -845,24 +880,43 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer //TODO: what to do for this psuedo-motion? return; } - modit::Motion::Down => Action::Motion(Motion::Down), - modit::Motion::End => Action::Motion(Motion::End), - modit::Motion::GotoLine(line) => { - Action::Motion(Motion::GotoLine(line.saturating_sub(1))) - } - modit::Motion::GotoEof => Action::Motion(Motion::GotoLine( - editor.with_buffer(|buffer| buffer.lines.len().saturating_sub(1)), - )), - modit::Motion::Home => Action::Motion(Motion::Home), + modit::Motion::Down => Action::Motion { + motion: Motion::Down, + select: false, + }, + modit::Motion::End => Action::Motion { + motion: Motion::End, + select: false, + }, + modit::Motion::GotoLine(line) => Action::Motion { + motion: Motion::GotoLine(line.saturating_sub(1)), + select: false, + }, + modit::Motion::GotoEof => Action::Motion { + motion: Motion::GotoLine( + editor.with_buffer(|buffer| buffer.lines.len().saturating_sub(1)), + ), + select: false, + }, + modit::Motion::Home => Action::Motion { + motion: Motion::Home, + select: false, + }, modit::Motion::Inside => { //TODO: what to do for this psuedo-motion? return; } - modit::Motion::Left => Action::Motion(Motion::Left), + modit::Motion::Left => Action::Motion { + motion: Motion::Left, + select: false, + }, modit::Motion::LeftInLine => { let cursor = editor.cursor(); if cursor.index > 0 { - Action::Motion(Motion::Left) + Action::Motion { + motion: Motion::Left, + select: false, + } } else { return; } @@ -975,8 +1029,14 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer editor.set_cursor(cursor); return; } - modit::Motion::PageDown => Action::Motion(Motion::PageDown), - modit::Motion::PageUp => Action::Motion(Motion::PageUp), + modit::Motion::PageDown => Action::Motion { + motion: Motion::PageDown, + select: false, + }, + modit::Motion::PageUp => Action::Motion { + motion: Motion::PageUp, + select: false, + }, modit::Motion::PreviousChar(find_c) => { let mut cursor = editor.cursor(); editor.with_buffer(|buffer| { @@ -1092,14 +1152,20 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer editor.set_cursor(cursor); return; } - modit::Motion::Right => Action::Motion(Motion::Right), + modit::Motion::Right => Action::Motion { + motion: Motion::Right, + select: false, + }, modit::Motion::RightInLine => { let cursor = editor.cursor(); if cursor.index < editor .with_buffer(|buffer| buffer.lines[cursor.line].text().len()) { - Action::Motion(Motion::Right) + Action::Motion { + motion: Motion::Right, + select: false, + } } else { return; } @@ -1109,7 +1175,10 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer if let Some(line_i) = editor.with_buffer(|buffer| { buffer.layout_runs().next().map(|first| first.line_i) }) { - Action::Motion(Motion::GotoLine(line_i)) + Action::Motion { + motion: Motion::GotoLine(line_i), + select: false, + } } else { return; } @@ -1119,7 +1188,10 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer if let Some(line_i) = editor.with_buffer(|buffer| { buffer.layout_runs().last().map(|last| last.line_i) }) { - Action::Motion(Motion::GotoLine(line_i)) + Action::Motion { + motion: Motion::GotoLine(line_i), + select: false, + } } else { return; } @@ -1130,9 +1202,12 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer let mut layout_runs = buffer.layout_runs(); if let Some(first) = layout_runs.next() { if let Some(last) = layout_runs.last() { - Some(Action::Motion(Motion::GotoLine( - (last.line_i + first.line_i) / 2, - ))) + Some(Action::Motion { + motion: Motion::GotoLine( + (last.line_i + first.line_i) / 2, + ), + select: false, + }) } else { None } @@ -1149,8 +1224,14 @@ impl<'syntax_system, 'buffer> Edit<'buffer> for ViEditor<'syntax_system, 'buffer //TODO: what to do for this psuedo-motion? return; } - modit::Motion::SoftHome => Action::Motion(Motion::SoftHome), - modit::Motion::Up => Action::Motion(Motion::Up), + modit::Motion::SoftHome => Action::Motion { + motion: Motion::SoftHome, + select: false, + }, + modit::Motion::Up => Action::Motion { + motion: Motion::Up, + select: false, + }, } } };