From 1aa8aaf8186a21a135a7ba0525b0e01726cb8e4e Mon Sep 17 00:00:00 2001 From: Andrew Baldwin Date: Thu, 27 Mar 2025 10:30:58 -0700 Subject: [PATCH] Add the ability to expand selection-by-word initiated by double-click in `text_input`. --- widget/src/text_input.rs | 54 ++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/widget/src/text_input.rs b/widget/src/text_input.rs index f214fe1d..1d22521b 100644 --- a/widget/src/text_input.rs +++ b/widget/src/text_input.rs @@ -780,11 +780,14 @@ where } else { state.cursor.move_to(position); } - state.is_dragging = true; + + state.is_dragging = Some(Drag::SingleClick); } click::Kind::Double => { if self.is_secure { state.cursor.select_all(&self.value); + + state.is_dragging = None; } else { let position = find_cursor_position( text_layout.bounds(), @@ -798,13 +801,15 @@ where self.value.previous_start_of_word(position), self.value.next_end_of_word(position), ); - } - state.is_dragging = false; + state.is_dragging = Some(Drag::DoubleClick { + click_position: position, + }); + } } click::Kind::Triple => { state.cursor.select_all(&self.value); - state.is_dragging = false; + state.is_dragging = None; } } @@ -820,13 +825,13 @@ where Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) | Event::Touch(touch::Event::FingerLifted { .. }) | Event::Touch(touch::Event::FingerLost { .. }) => { - state::(tree).is_dragging = false; + state::(tree).is_dragging = None; } Event::Mouse(mouse::Event::CursorMoved { position }) | Event::Touch(touch::Event::FingerMoved { position, .. }) => { let state = state::(tree); - if state.is_dragging { + if let Some(is_dragging) = &state.is_dragging { let text_layout = layout.children().next().unwrap(); let target = { @@ -857,9 +862,30 @@ where let selection_before = state.cursor.selection(&value); - state - .cursor - .select_range(state.cursor.start(&value), position); + match is_dragging { + Drag::SingleClick => { + state.cursor.select_range( + state.cursor.start(&value), + position, + ); + } + Drag::DoubleClick { click_position } => { + if position < *click_position { + state.cursor.select_range( + self.value.previous_start_of_word(position), + self.value + .next_end_of_word(*click_position), + ); + } else { + state.cursor.select_range( + self.value.previous_start_of_word( + *click_position, + ), + self.value.next_end_of_word(position), + ); + } + } + } if let Some(focus) = &mut state.is_focused { focus.updated_at = Instant::now(); @@ -1201,7 +1227,7 @@ where } keyboard::Key::Named(key::Named::Escape) => { state.is_focused = None; - state.is_dragging = false; + state.is_dragging = None; state.is_pasting = None; state.keyboard_modifiers = @@ -1430,7 +1456,7 @@ pub struct State { placeholder: paragraph::Plain

, icon: paragraph::Plain

, is_focused: Option, - is_dragging: bool, + is_dragging: Option, is_pasting: Option, preedit: Option, last_click: Option, @@ -1452,6 +1478,12 @@ struct Focus { is_window_focused: bool, } +#[derive(Debug, Clone)] +enum Drag { + SingleClick, + DoubleClick { click_position: usize }, +} + impl State

{ /// Creates a new [`State`], representing an unfocused [`TextInput`]. pub fn new() -> Self {