From 34675227143ab45a98d771061a7cfd7eb5b04ec0 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Wed, 17 Jul 2024 11:41:04 +0800 Subject: [PATCH] Add double click to select word support for Input. (#38) Continue #27 --- README.md | 12 +++-------- crates/ui/src/input.rs | 47 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 689bee95..eae2d27a 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,11 @@ A UI components for building desktop application by using [GPUI](https://gpui.rs - [ ] TextField - [x] Ctrl+a, e to move cursor to start/end - [x] Copy, Cut, Paste by keyboard - - [ ] ContextMenu to let user copy, cut, paste - [x] Selection by mouse, drag to select text - [x] Cursor blinking - - [ ] Textarea - [x] Input icon + - [ ] Textarea + - [ ] ContextMenu to let user copy, cut, paste - [ ] InputOTP - [x] Button - [x] Button with Icon @@ -66,13 +66,7 @@ A UI components for building desktop application by using [GPUI](https://gpui.rs ## Showcase - - - - - - - + ## Demo diff --git a/crates/ui/src/input.rs b/crates/ui/src/input.rs index 4f31b97d..d3729bea 100644 --- a/crates/ui/src/input.rs +++ b/crates/ui/src/input.rs @@ -297,11 +297,18 @@ impl TextInput { fn on_mouse_down(&mut self, event: &MouseDownEvent, cx: &mut ViewContext) { self.is_selecting = true; + let offset = self.index_for_mouse_position(event.position); + + // Double click to select word + if event.button == MouseButton::Left && event.click_count == 2 { + self.select_word(offset, cx); + return; + } if event.modifiers.shift { - self.select_to(self.index_for_mouse_position(event.position), cx); + self.select_to(offset, cx); } else { - self.move_to(self.index_for_mouse_position(event.position), cx) + self.move_to(offset, cx) } } @@ -391,6 +398,42 @@ impl TextInput { cx.notify() } + /// Select the word at the given offset. + fn select_word(&mut self, offset: usize, cx: &mut ViewContext) { + fn is_word(c: char) -> bool { + c.is_alphanumeric() || matches!(c, '_') + } + + let mut start = self.offset_to_utf16(offset); + let mut end = start; + let prev_text = self.text_for_range(0..start, cx).unwrap_or_default(); + let next_text = self + .text_for_range(end..self.text.len(), cx) + .unwrap_or_default(); + + let prev_chars = prev_text.chars().rev().peekable(); + let next_chars = next_text.chars().peekable(); + + for (_, c) in prev_chars.enumerate() { + if !is_word(c) { + break; + } + + start -= c.len_utf16(); + } + + for (_, c) in next_chars.enumerate() { + if !is_word(c) { + break; + } + + end += c.len_utf16(); + } + + self.selected_range = self.range_from_utf16(&(start..end)); + cx.notify() + } + fn unselect(&mut self, cx: &mut ViewContext) { self.selected_range = self.cursor_offset()..self.cursor_offset(); cx.notify()