From b858d3e34aca97cc40c71cf141c31eafa1509492 Mon Sep 17 00:00:00 2001 From: Nigel Ihza Firdausy <106657905+Ni-zav@users.noreply.github.com> Date: Wed, 14 Oct 2026 23:14:57 +0700 Subject: [PATCH 1/2] fix(ui): keep search-field text drags out of titlebar window gestures (#1646) --- crates/ui-egui/src/panels/topbar.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/ui-egui/src/panels/topbar.rs b/crates/ui-egui/src/panels/topbar.rs index 03810aca9..d29f5a4c1 100644 --- a/crates/ui-egui/src/panels/topbar.rs +++ b/crates/ui-egui/src/panels/topbar.rs @@ -10,7 +10,7 @@ use crate::widgets::{icon_button, register}; /// The bar doubles as the window's title bar: dragging its empty space moves the window and a /// double-click zooms or restores it. Registered before the bar's widgets so they win the click. -fn window_handle(app: &LightcraftApp, ui: &mut egui::Ui, content: Rect, margin_left: f32, margin_right: f32) { +fn window_handle(app: &LightcraftApp, ui: &mut egui::Ui, content: Rect, margin_left: f32, margin_right: f32, search_rect: Rect) { use crate::titlebar::{Gesture, WindowState, command_for}; let bar = Rect::from_min_max(pos2(content.left() - margin_left, content.top()), pos2(content.right() + margin_right, content.bottom())); register(ui.ctx(), "region:titlebar", bar); @@ -27,9 +27,12 @@ fn window_handle(app: &LightcraftApp, ui: &mut egui::Ui, content: Rect, margin_l ctx.data_mut(|d| d.insert_temp(memo, on_widget)); } let on_widget = ctx.data(|d| d.get_temp::(memo)).unwrap_or(false); - let gesture = if resp.double_clicked_by(egui::PointerButton::Primary) { + // The search field is drawn after this handle. Its hit-test response may not exist + // yet when the press is recorded; the press origin still identifies text selection. + let on_search = ui.input(|i| i.pointer.press_origin().is_some_and(|p| search_rect.contains(p))); + let gesture = if resp.double_clicked_by(egui::PointerButton::Primary) && !on_search { Some(Gesture::DoubleClicked) - } else if resp.drag_started_by(egui::PointerButton::Primary) && !on_widget { + } else if resp.drag_started_by(egui::PointerButton::Primary) && !on_widget && !on_search { Some(Gesture::DragStarted) } else { None @@ -50,9 +53,6 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { .frame(egui::Frame::NONE.fill(t.chrome).inner_margin(egui::Margin { left, right: 12, top: 0, bottom: 0 })) .show(ui, |ui| { let full = ui.max_rect(); - if app.integrated_titlebar { - window_handle(app, ui, full, left as f32, 12.0); - } // room for the filter icon and the six icons on the right let mut sw = 640.0f32.min(full.width() - 540.0).max(200.0); if !app.native_menu { @@ -61,6 +61,10 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { sw = sw.min(2.0 * (full.center().x - menus_right)).max(200.0); } let search_left = full.center().x - sw / 2.0; + let sr = Rect::from_center_size(pos2(full.center().x, full.center().y), vec2(sw, 28.0)); + if app.integrated_titlebar { + window_handle(app, ui, full, left as f32, 12.0, sr); + } ui.horizontal_centered(|ui| { ui.spacing_mut().item_spacing.x = 4.0; if icon_button(ui, "sidebar", Icon::Sidebar, vec2(30.0, 30.0), app.ui.left_panel, true, crate::i18n::tr("Show/hide My Photos panel")) @@ -81,7 +85,6 @@ pub fn show(app: &mut LightcraftApp, ui: &mut egui::Ui) { } }); // search field (centred on the window) - let sr = Rect::from_center_size(pos2(full.center().x, full.center().y), vec2(sw, 28.0)); let id = crate::text_field::id("field:search"); if std::mem::take(&mut app.ui.focus_search) { ui.memory_mut(|m| m.request_focus(id)); From 44629a0df11c8651d6c46bfaf9423f3924e723ab Mon Sep 17 00:00:00 2001 From: Nigel Ihza Firdausy <106657905+Ni-zav@users.noreply.github.com> Date: Wed, 14 Oct 2026 23:15:16 +0700 Subject: [PATCH 2/2] test(ui): regress search text dragging without moving the app (#1646) --- crates/ui-egui/src/tests_titlebar.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/ui-egui/src/tests_titlebar.rs b/crates/ui-egui/src/tests_titlebar.rs index 40eba4b13..40d70c452 100644 --- a/crates/ui-egui/src/tests_titlebar.rs +++ b/crates/ui-egui/src/tests_titlebar.rs @@ -104,6 +104,20 @@ fn double_click_in_the_search_field_does_not_zoom() { assert!(h.window_commands.is_empty(), "{:?}", h.window_commands); } +// Scenario: dragging within the search field selects text rather than moving the window (#1646) +#[test] +fn dragging_within_search_does_not_move_the_window() { + let mut h = integrated(); + let r = widget(&h, "field:search"); + let x = r.left() + r.width() * 0.35; + h.request("ui.click", json!({"x": x, "y": r.center().y}), T); + h.request("ui.text", json!({"text": "landscape"}), T); + play(&mut h); + h.request("ui.drag", json!({"x": x, "y": r.center().y, "toX": x + 60.0, "toY": r.center().y}), T); + play(&mut h); + assert!(h.window_commands.is_empty(), "{:?}", h.window_commands); +} + // Scenario: a window with its native title bar (Windows, Linux) leaves the bar alone #[test] fn without_the_integrated_title_bar_the_bar_is_not_a_window_handle() {