From c8ef79230a640b81f5b6bf38b47eee31c116170d Mon Sep 17 00:00:00 2001 From: Colin Rofls Date: Mon, 5 Oct 2020 14:20:58 -0400 Subject: [PATCH] Improve selection behaviour on focus change On windows, we persist the existing selection when focus changes. On mac, the behaviour depends on whether or not there was a click. If there was a click, we use the click to determine the selection; otherwise we select the whole buffer. In addition, on mac, we no longer draw the cursor if the selection is not a caret. - fixes #1225 (again) --- druid/src/text/editor.rs | 5 +++++ druid/src/text/selection.rs | 7 ------- druid/src/widget/textbox.rs | 28 ++++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/druid/src/text/editor.rs b/druid/src/text/editor.rs index 58e7c20960..d5b273691b 100644 --- a/druid/src/text/editor.rs +++ b/druid/src/text/editor.rs @@ -129,6 +129,11 @@ impl Editor { self.do_edit(EditAction::Paste(t), data) } + /// Set the selection to the entire buffer. + pub fn select_all(&mut self, data: &T) { + self.selection = Selection::new(0, data.len()); + } + fn mouse_action_for_event(&self, event: &MouseEvent) -> MouseAction { let pos = self.layout.text_position_for_point(event.pos); MouseAction { diff --git a/druid/src/text/selection.rs b/druid/src/text/selection.rs index 4db53ae259..258bb77906 100644 --- a/druid/src/text/selection.rs +++ b/druid/src/text/selection.rs @@ -37,13 +37,6 @@ impl Selection { Selection { start, end } } - /// Create a selection that starts at the beginning and ends at text length. - /// TODO: can text length be at a non-codepoint or a non-grapheme? - pub fn all(&mut self, text: &impl EditableText) { - self.start = 0; - self.end = text.len(); - } - /// Create a caret, which is just a selection with the same and start and end. pub fn caret(pos: usize) -> Self { Selection { diff --git a/druid/src/widget/textbox.rs b/druid/src/widget/textbox.rs index 284d12caa7..ed05ecf610 100644 --- a/druid/src/widget/textbox.rs +++ b/druid/src/widget/textbox.rs @@ -45,6 +45,12 @@ pub struct TextBox { cursor_timer: TimerToken, cursor_on: bool, multiline: bool, + /// true if a click event caused us to gain focus. + /// + /// On macOS, if focus happens via click then we set the selection based + /// on the click position; if focus happens automatically (e.g. on tab) + /// then we select our entire contents. + was_focused_from_click: bool, } impl TextBox<()> { @@ -67,6 +73,7 @@ impl TextBox { cursor_on: false, placeholder, multiline: false, + was_focused_from_click: false, } } @@ -166,6 +173,17 @@ impl TextBox { self.cursor_on = true; self.cursor_timer = token; } + + // on macos we only draw the cursor if the selection is non-caret + #[cfg(target_os = "macos")] + fn should_draw_cursor(&self) -> bool { + self.cursor_on && self.editor.selection().is_caret() + } + + #[cfg(not(target_os = "macos"))] + fn should_draw_cursor(&self) -> bool { + self.cursor_on + } } impl Widget for TextBox { @@ -177,6 +195,7 @@ impl Widget for TextBox { ctx.set_active(true); if !mouse.focus { + self.was_focused_from_click = true; self.reset_cursor_blink(ctx.request_timer(CURSOR_BLINK_DURATION)); self.editor.click(mouse, data); } @@ -250,7 +269,12 @@ impl Widget for TextBox { self.editor.set_text(data.to_owned()); self.editor.rebuild_if_needed(ctx.text(), env); } - LifeCycle::FocusChanged(_) => { + LifeCycle::FocusChanged(_is_focused) => { + #[cfg(target_os = "macos")] + if *_is_focused && !self.was_focused_from_click { + self.editor.select_all(data); + } + self.was_focused_from_click = false; self.reset_cursor_blink(ctx.request_timer(CURSOR_BLINK_DURATION)); ctx.request_paint(); } @@ -339,7 +363,7 @@ impl Widget for TextBox { } // Paint the cursor if focused and there's no selection - if is_focused && self.cursor_on { + if is_focused && self.should_draw_cursor() { // the cursor position can extend past the edge of the layout // (commonly when there is trailing whitespace) so we clamp it // to the right edge.