From 9a9762b4ca7119d83deaff532858bb1c2e46ddc0 Mon Sep 17 00:00:00 2001 From: Nathaniel Ledford Date: Thu, 29 Oct 2026 14:57:27 -0400 Subject: [PATCH] LR-BEHAV-TITLEBAR: the title-bar handle is not drawn while a dropdown is open in the dialog --- crates/ui-egui/src/dialogs.rs | 4 +++- crates/ui-egui/src/tests/titlebar.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index ad7ce7a82..48877e706 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -255,7 +255,9 @@ fn below_title_bar(screen: egui::Rect, band: Option) -> egui::Rect { /// handle is the band on either side of it, and isn't run while the pointer is on the dialog (each /// handle records whether the window may move, and one the pointer isn't in would say yes). fn title_bar_handle(ctx: &egui::Context, band: egui::Rect, dialog: egui::Rect) { - if ctx.pointer_hover_pos().is_some_and(|p| dialog.contains(p)) { + // (egui lifts an area that wasn't there the frame before above the rest: a handle drawn again as the pointer leaves + // the dialog would sit over an open dropdown and take the press meant for its item) + if egui::Popup::is_any_open(ctx) || ctx.pointer_hover_pos().is_some_and(|p| dialog.contains(p)) { return; } let sides = diff --git a/crates/ui-egui/src/tests/titlebar.rs b/crates/ui-egui/src/tests/titlebar.rs index a8eaa33f1..f8c032f03 100644 --- a/crates/ui-egui/src/tests/titlebar.rs +++ b/crates/ui-egui/src/tests/titlebar.rs @@ -298,3 +298,31 @@ fn a_dialog_inside_the_bar_keeps_the_window_from_moving() { assert!(hover(&mut h, spot), "the bar beside the dialog moves the window"); assert!(!hover(&mut h, egui::pos2(window.center().x, bar.center().y)), "the dialog over the bar does not"); } + +// Scenario: with a dropdown open in the dialog, the bar's handle stays out of the way: egui lifts an area that was not +// there the frame before above the others, so a handle drawn again as the pointer leaves the dialog would take the +// press meant for the dropdown's item +#[test] +fn the_title_bar_handle_stays_below_a_dropdown_open_in_the_dialog() { + let mut h = with_export_dialog(integrated()); + let spot = empty_spot(&h); + let handle_at = |h: &mut Headless| { + h.events.push(egui::Event::PointerMoved(spot)); + play(h); + let handles = [0usize, 1].map(|i| egui::LayerId::new(egui::Order::Foreground, egui::Id::unique("dialog-title-bar").with(i))); + h.view.ctx.memory(|m| handles.iter().any(|layer| m.areas().visible_last_frame(layer))) + }; + assert!(handle_at(&mut h), "without a dropdown the bar beside the dialog is the handle's"); + let r = h.request("ui.clickWidget", json!({"id": "combo:exportPreset"}), T); + assert_eq!(r["ok"], true, "{r}"); + play(&mut h); + assert!(egui::Popup::is_any_open(&h.view.ctx), "the preset dropdown is open"); + assert!(!handle_at(&mut h), "with one open, no handle sits above it"); + assert!(egui::Popup::is_any_open(&h.view.ctx), "(and the pointer leaving the dialog did not close it)"); + // once the dropdown is closed the handle is back, so the bar beside the dialog moves the window again + h.request("ui.key", json!({"key": "Escape"}), T); + play(&mut h); + assert!(!egui::Popup::is_any_open(&h.view.ctx), "Escape closes the dropdown"); + assert!(h.app.ui.dialog.is_some(), "and leaves the dialog"); + assert!(handle_at(&mut h), "the handle is back"); +}