graphic design tool
git clone https://git.lucas.co/cce-designer.git
fix: the dialog owns right presses while it is open
A right press inside the plate fell through to the pane beneath, whose
context menu then opened over the modal with its labels clipped by the
dialog's occluder — a menu with no legible entries. Inside the plate a
right press is now swallowed; outside it dismisses, as a left press does.
The middle button still passes through.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
src/dialog.rs | 21 ++++++++++++++++-----
src/main.rs | 33 +++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/src/dialog.rs b/src/dialog.rs
index c029a15..c7a997c 100644
--- a/src/dialog.rs
+++ b/src/dialog.rs
@@ -1830,19 +1830,30 @@ impl State {
/// A mouse button, while the dialog is open.
///
- /// `None` hands the press back to the ordinary cascade — which happens
- /// only for the buttons the dialog has no use for, so a right-click still
- /// reaches whatever is under it outside the plate. `Some(handled)` means
- /// the dialog dealt with it and nothing else should.
+ /// `None` hands the press back to the ordinary cascade — only the middle
+ /// button, which the dialog has no use for. `Some(handled)` means the
+ /// dialog dealt with it and nothing else should. A RIGHT press is the
+ /// dialog's too: inside the plate it is swallowed (nothing in the dialog
+ /// has a context menu, and until 2026-09-22 it fell through to the pane
+ /// beneath, whose menu then opened over a modal with its labels clipped
+ /// by the dialog's occluder — a menu with no legible entries), outside
+ /// it dismisses, exactly as a left press does.
pub(crate) fn dialog_mouse_input(
&mut self,
button: MouseButton,
state: ElementState,
) -> Option<bool> {
+ let (x, y) = (self.cursor_x, self.cursor_y);
+ if button == MouseButton::Right {
+ let inside = self.in_dialog_slot(DIALOG_IDX, x, y) || self.in_dialog_slot(DIALOG_PARAMS_IDX, x, y);
+ if !inside && state == ElementState::Pressed {
+ self.close_dialog();
+ }
+ return Some(true);
+ }
if button != MouseButton::Left {
return None;
}
- let (x, y) = (self.cursor_x, self.cursor_y);
// A slider drag started in the settings body ends wherever the pointer
// happens to be — including outside the plate. Ending it has to come
diff --git a/src/main.rs b/src/main.rs
index 8bfca24..903dcc6 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -8481,6 +8481,39 @@ mod tests {
assert_eq!(d.take_slider_change(), None, "over the label the wheel is the list's");
}
+ /// A right press is the dialog's while it is open: inside the plate it is
+ /// swallowed — no context menu opens for the pane beneath, which used to
+ /// come up over the modal with its labels clipped — and outside it
+ /// dismisses, as a left press does.
+ #[test]
+ fn dialog_owns_right_presses_while_open() {
+ use cce_ui::widget::{ElementState, MouseButton};
+ let mut state = State::new(false);
+ state.run_command("toggle_dialog");
+ assert!(state.dialog_visible());
+ let (dx, dy, dw, dh) = state.positions[crate::slots::DIALOG_IDX];
+ assert!(dw > 0.0 && dh > 0.0, "the dialog is laid out");
+
+ // Inside: swallowed, nothing opens, the dialog stays.
+ state.cursor_x = dx + dw * 0.5;
+ state.cursor_y = dy + dh * 0.5;
+ assert_eq!(state.dialog_mouse_input(MouseButton::Right, ElementState::Pressed), Some(true));
+ assert!(state.dialog_visible());
+ assert!(!cce_ui::widget::context_menu::is_visible(), "no menu opened over the modal");
+ assert_eq!(state.dialog_mouse_input(MouseButton::Right, ElementState::Released), Some(true));
+
+ // Outside: dismisses, and is swallowed rather than reaching the pane.
+ state.cursor_x = (dx - 20.0).max(0.0);
+ state.cursor_y = (dy - 20.0).max(0.0);
+ assert_eq!(state.dialog_mouse_input(MouseButton::Right, ElementState::Pressed), Some(true));
+ assert!(!state.dialog_visible());
+ assert!(!cce_ui::widget::context_menu::is_visible());
+
+ // The middle button is still nobody's.
+ state.run_command("toggle_dialog");
+ assert_eq!(state.dialog_mouse_input(MouseButton::Middle, ElementState::Pressed), None);
+ }
+
/// Backspace walks the query back, and the ranking follows it.
#[test]
fn dialog_backspace_widens_the_filter() {