Skip to content

Commit ec048ab

Browse files
cmyrluleyleo
andcommitted
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) Update druid/src/widget/textbox.rs Co-authored-by: Leopold Luley <git@leopoldluley.de>
1 parent fae3d04 commit ec048ab

File tree

4 files changed

+32
-9
lines changed

4 files changed

+32
-9
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ You can find its changes [documented below](#060---2020-06-01).
7272
- `LocalizedString` and `LabelText` use `ArcStr` instead of String ([#1245] by [@cmyr])
7373
- `LensWrap` widget moved into widget module ([#1251] by [@cmyr])
7474
- `Delegate::command` now returns `Handled`, not `bool` ([#1298] by [@jneem])
75+
- `TextBox` selects all contents when tabbed to on macOS ([#1283] by [@cmyr])
7576

7677
### Deprecated
7778

@@ -498,6 +499,7 @@ Last release without a changelog :(
498499
[#1276]: https://github.com/linebender/druid/pull/1276
499500
[#1278]: https://github.com/linebender/druid/pull/1278
500501
[#1280]: https://github.com/linebender/druid/pull/1280
502+
[#1283]: https://github.com/linebender/druid/pull/1283
501503
[#1295]: https://github.com/linebender/druid/pull/1280
502504
[#1298]: https://github.com/linebender/druid/pull/1298
503505
[#1299]: https://github.com/linebender/druid/pull/1299

druid/src/text/editor.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ impl<T: TextStorage + EditableText> Editor<T> {
137137
self.do_edit(EditAction::Paste(t), data)
138138
}
139139

140+
/// Set the selection to the entire buffer.
141+
pub fn select_all(&mut self, data: &T) {
142+
self.selection = Selection::new(0, data.len());
143+
}
144+
140145
fn mouse_action_for_event(&self, event: &MouseEvent) -> MouseAction {
141146
let pos = self.layout.text_position_for_point(event.pos);
142147
MouseAction {

druid/src/text/selection.rs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,6 @@ impl Selection {
5353
self
5454
}
5555

56-
/// Create a selection that starts at the beginning and ends at text length.
57-
/// TODO: can text length be at a non-codepoint or a non-grapheme?
58-
pub fn all(&mut self, text: &impl EditableText) {
59-
self.start = 0;
60-
self.end = text.len();
61-
}
62-
6356
/// Create a caret, which is just a selection with the same and start and end.
6457
pub fn caret(pos: usize) -> Self {
6558
Selection {

druid/src/widget/textbox.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ pub struct TextBox<T> {
4444
cursor_timer: TimerToken,
4545
cursor_on: bool,
4646
multiline: bool,
47+
/// true if a click event caused us to gain focus.
48+
///
49+
/// On macOS, if focus happens via click then we set the selection based
50+
/// on the click position; if focus happens automatically (e.g. on tab)
51+
/// then we select our entire contents.
52+
was_focused_from_click: bool,
4753
}
4854

4955
impl TextBox<()> {
@@ -66,6 +72,7 @@ impl<T> TextBox<T> {
6672
cursor_on: false,
6773
placeholder,
6874
multiline: false,
75+
was_focused_from_click: false,
6976
}
7077
}
7178

@@ -196,6 +203,17 @@ impl<T: TextStorage + EditableText> TextBox<T> {
196203
self.cursor_on = true;
197204
self.cursor_timer = token;
198205
}
206+
207+
// on macos we only draw the cursor if the selection is non-caret
208+
#[cfg(target_os = "macos")]
209+
fn should_draw_cursor(&self) -> bool {
210+
self.cursor_on && self.editor.selection().is_caret()
211+
}
212+
213+
#[cfg(not(target_os = "macos"))]
214+
fn should_draw_cursor(&self) -> bool {
215+
self.cursor_on
216+
}
199217
}
200218

201219
impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
@@ -209,6 +227,7 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
209227
mouse.pos += Vec2::new(self.hscroll_offset, 0.0);
210228

211229
if !mouse.focus {
230+
self.was_focused_from_click = true;
212231
self.reset_cursor_blink(ctx.request_timer(CURSOR_BLINK_DURATION));
213232
self.editor.click(&mouse, data);
214233
}
@@ -284,7 +303,11 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
284303
self.editor.set_text(data.to_owned());
285304
self.editor.rebuild_if_needed(ctx.text(), env);
286305
}
287-
LifeCycle::FocusChanged(_) => {
306+
LifeCycle::FocusChanged(is_focused) => {
307+
if cfg!(target_os = "macos") && *is_focused && !self.was_focused_from_click {
308+
self.editor.select_all(data);
309+
}
310+
self.was_focused_from_click = false;
288311
self.reset_cursor_blink(ctx.request_timer(CURSOR_BLINK_DURATION));
289312
ctx.request_paint();
290313
}
@@ -370,7 +393,7 @@ impl<T: TextStorage + EditableText> Widget<T> for TextBox<T> {
370393
}
371394

372395
// Paint the cursor if focused and there's no selection
373-
if is_focused && self.cursor_on {
396+
if is_focused && self.should_draw_cursor() {
374397
// the cursor position can extend past the edge of the layout
375398
// (commonly when there is trailing whitespace) so we clamp it
376399
// to the right edge.

0 commit comments

Comments
 (0)