diff options
| author | Polesznyák Márk <contact@pml68.dev> | 2025-11-12 16:31:59 +0100 |
|---|---|---|
| committer | Polesznyák Márk <contact@pml68.dev> | 2025-11-12 16:31:59 +0100 |
| commit | a01024abcd4d9c7e4ad9f9761bf1adf2bc5e6994 (patch) | |
| tree | bede84f543871c867d6deb51cd8ed1aea78694fc | |
| parent | feat(name example): replace TextInput with TextEditor for multi-line support,... (diff) | |
| download | iced_selection-a01024abcd4d9c7e4ad9f9761bf1adf2bc5e6994.tar.gz | |
feat: implement double-click + drag by-word selection
| -rw-r--r-- | README.md | 2 | ||||
| -rw-r--r-- | src/selection.rs | 73 | ||||
| -rw-r--r-- | src/text.rs | 25 | ||||
| -rw-r--r-- | src/text/rich.rs | 25 |
4 files changed, 106 insertions, 19 deletions
@@ -38,7 +38,7 @@ iced_selection = { git = "https://git.sr.ht/~pml68/iced_selection" } - [ ] allow out-of-bounds selection dragging - [X] custom markdown `Viewer` -- [ ] double-click + drag for by-word selection +- [X] double-click + drag for by-word selection - [X] triple-click + drag for by-line selection - [ ] support wrapped lines diff --git a/src/selection.rs b/src/selection.rs index 9be01dd..375072c 100644 --- a/src/selection.rs +++ b/src/selection.rs @@ -213,6 +213,78 @@ impl Selection { self.select_range(start, end); } + /// Updates the current selection by setting a new end point, either to the start of the + /// previous word, or to the next one's end. + pub fn change_selection_by_word( + &mut self, + new_end: SelectionEnd, + paragraph: &Paragraph, + ) { + let (base_word_start, base_word_end) = { + if self.direction == Direction::Right { + let value = Value::new( + paragraph.buffer().lines[self.start.line].text(), + ); + + let end = SelectionEnd::new( + self.start.line, + value.next_end_of_word(self.start.index), + ); + + (self.start, end) + } else { + let value = + Value::new(paragraph.buffer().lines[self.end.line].text()); + + let start = SelectionEnd::new( + self.end.line, + value.previous_start_of_word(self.end.index), + ); + + (start, self.end) + } + }; + + let value = Value::new(paragraph.buffer().lines[new_end.line].text()); + + let (start, end) = if new_end < self.start { + self.direction = Direction::Left; + + let start = SelectionEnd::new( + new_end.line, + value.previous_start_of_word(new_end.index), + ); + + (start, base_word_end) + } else if new_end > self.end { + self.direction = Direction::Right; + + let end = SelectionEnd::new( + new_end.line, + value.next_end_of_word(new_end.index), + ); + + (base_word_start, end) + } else if self.direction == Direction::Right { + let end = SelectionEnd::new( + new_end.line, + value.next_end_of_word(new_end.index), + ); + + (base_word_start, end.max(base_word_end)) + } else { + let start = SelectionEnd::new( + new_end.line, + value.previous_start_of_word(new_end.index), + ); + + (start.min(base_word_start), base_word_end) + }; + + self.moving_line_index = None; + self.select_range(start, end); + } + /// Updates the current selection by setting a new end point, either to the end of a following /// line, or the beginning of a previous one. pub fn change_selection_by_line( @@ -246,6 +318,7 @@ impl Selection { (start, end) } else { let start = SelectionEnd::new(new_line, 0); + let end = if self.direction == old_direction { self.end } else { diff --git a/src/text.rs b/src/text.rs index ad5c350..ff5d5a4 100644 --- a/src/text.rs +++ b/src/text.rs @@ -388,7 +388,7 @@ where let click_position = cursor.position_in(bounds); if viewport.intersection(&bounds).is_none() - && state.selection == Selection::default() + && state.selection.is_empty() && state.dragging.is_none() { return; @@ -466,7 +466,14 @@ where state.selection.change_selection(new_end); } - Dragging::Word => {} + Dragging::Word => { + let new_end = SelectionEnd { line, index }; + + state.selection.change_selection_by_word( + new_end, + &state.paragraph, + ); + } Dragging::Line => { state.selection.change_selection_by_line( line, @@ -491,7 +498,7 @@ where } keyboard::Key::Character("a") if state.keyboard_modifiers.command() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { state.selection.select_all(&state.paragraph); @@ -499,7 +506,7 @@ where } keyboard::Key::Named(key::Named::Home) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.jump() { state.selection.select_beginning(); @@ -511,7 +518,7 @@ where } keyboard::Key::Named(key::Named::End) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.jump() { state.selection.select_end(&state.paragraph); @@ -523,7 +530,7 @@ where } keyboard::Key::Named(key::Named::ArrowLeft) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.macos_command() { state.selection.select_line_beginning(); @@ -539,7 +546,7 @@ where } keyboard::Key::Named(key::Named::ArrowRight) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.macos_command() { state.selection.select_line_end(&state.paragraph); @@ -555,7 +562,7 @@ where } keyboard::Key::Named(key::Named::ArrowUp) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.macos_command() { state.selection.select_beginning(); @@ -569,7 +576,7 @@ where } keyboard::Key::Named(key::Named::ArrowDown) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.macos_command() { state.selection.select_end(&state.paragraph); diff --git a/src/text/rich.rs b/src/text/rich.rs index a30663b..5ca3051 100644 --- a/src/text/rich.rs +++ b/src/text/rich.rs @@ -600,7 +600,7 @@ where let click_position = cursor.position_in(bounds); if viewport.intersection(&bounds).is_none() - && state.selection == Selection::default() + && state.selection.is_empty() && state.dragging.is_none() { return; @@ -720,7 +720,14 @@ where state.selection.change_selection(new_end); } - Dragging::Word => {} + Dragging::Word => { + let new_end = SelectionEnd { line, index }; + + state.selection.change_selection_by_word( + new_end, + &state.paragraph, + ); + } Dragging::Line => { state.selection.change_selection_by_line( line, @@ -745,7 +752,7 @@ where } keyboard::Key::Character("a") if state.keyboard_modifiers.command() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { state.selection.select_all(&state.paragraph); @@ -753,7 +760,7 @@ where } keyboard::Key::Named(key::Named::Home) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.jump() { state.selection.select_beginning(); @@ -765,7 +772,7 @@ where } keyboard::Key::Named(key::Named::End) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.jump() { state.selection.select_end(&state.paragraph); @@ -777,7 +784,7 @@ where } keyboard::Key::Named(key::Named::ArrowLeft) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.macos_command() { state.selection.select_line_beginning(); @@ -793,7 +800,7 @@ where } keyboard::Key::Named(key::Named::ArrowRight) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.macos_command() { state.selection.select_line_end(&state.paragraph); @@ -809,7 +816,7 @@ where } keyboard::Key::Named(key::Named::ArrowUp) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.macos_command() { state.selection.select_beginning(); @@ -823,7 +830,7 @@ where } keyboard::Key::Named(key::Named::ArrowDown) if state.keyboard_modifiers.shift() - && state.selection != Selection::default() => + && !state.selection.is_empty() => { if state.keyboard_modifiers.macos_command() { state.selection.select_end(&state.paragraph); |
