diff --git a/crates/ui-egui/src/tests_numfields.rs b/crates/ui-egui/src/tests_numfields.rs index 98d8d4b4e..d18af4ad5 100644 --- a/crates/ui-egui/src/tests_numfields.rs +++ b/crates/ui-egui/src/tests_numfields.rs @@ -398,6 +398,23 @@ fn a_new_numeric_click_selects_all_on_release_without_moving_its_press_caret() { } #[test] +/// Focus that arrives while the button is still down, without a press event on that frame, +/// keeps the caret until release. +#[test] +fn focus_a_frame_after_the_press_does_not_select_all_until_release() { + let mut f = Field::new(); + let draw = |ui: &mut egui::Ui| numeric_fixture(ui, 0); + f.frame(vec![], &draw); + let at = f.text_point(field_text(0), 2, 7.0); + f.frame(vec![Event::PointerMoved(at), pointer_button(at, true)], &draw); + let pressed = f.selection(); + f.frame(vec![Event::PointerMoved(at)], &draw); + assert_eq!(f.selection(), pressed, "holding the button does not select the whole field"); + assert!(pressed.is_some_and(|(a, b)| a == b)); + f.frame(vec![pointer_button(at, false)], &draw); + assert_eq!(f.selection(), Some((0, field_text(0).len()))); +} + fn keyboard_focus_keeps_selecting_the_entire_numeric_field() { for mode in 0..4 { let mut f = Field::new(); diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index e03c4a968..929f3fad2 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -314,11 +314,19 @@ fn select_all(ui: &Ui, id: egui::Id, text: &str) { /// double-click. A pointer press keeps its caret so dragging selects from the pressed position. fn select_all_on_focus(ui: &Ui, resp: &Response, text: &str) { let pending = resp.id.with("select-all-on-focus-click"); - let (pressed, released) = - ui.input(|i| (i.pointer.button_pressed(egui::PointerButton::Primary), i.pointer.button_released(egui::PointerButton::Primary))); + let (pressed, released, down) = ui.input(|i| { + ( + i.pointer.button_pressed(egui::PointerButton::Primary), + i.pointer.button_released(egui::PointerButton::Primary), + i.pointer.button_down(egui::PointerButton::Primary), + ) + }); + // Focus can arrive the frame after the press, when `button_pressed` is already false + // and the button is still down. Defer select-all until release so a drag keeps its caret. + let defer = pressed || (down && resp.hovered()); if resp.gained_focus() { ui.data_mut(|d| { - if pressed { + if defer { d.insert_temp(pending, true); } else { d.remove_temp::(pending); @@ -329,6 +337,9 @@ fn select_all_on_focus(ui: &Ui, resp: &Response, text: &str) { if resp.lost_focus() { ui.data_mut(|d| d.remove_temp::(pending)); } + if resp.gained_focus() && defer { + return; + } if !(resp.gained_focus() && !pressed || resp.double_clicked() || clicked_focus) { return; }