From a64a5327e79e2c425d1bf3d419b1e22a96455c64 Mon Sep 17 00:00:00 2001 From: luxick Date: Wed, 9 Sep 2026 21:28:49 +0200 Subject: [PATCH] Fix canvas rect --- src/view.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/src/view.rs b/src/view.rs index 980ef01..15acf12 100644 --- a/src/view.rs +++ b/src/view.rs @@ -580,19 +580,19 @@ impl CanvasView { // *canvas*, and egui clips interaction rectangles to it, so the clip // has to be corrected before anything in the layer is interacted with: // otherwise the background stops responding as soon as the view is - // panned away from where it started. + // panned away from where it started. The correction is the visible + // area and not a pixel more: the layer paints above the panels, so a + // background reaching past the central panel would sit on top of the + // inspector and swallow the clicks meant for its widgets. let visible = self.to_global.inverse() * outer; - let background_rect = visible.expand(visible.width().max(visible.height())); - scene.set_clip_rect(background_rect); + scene.set_clip_rect(visible); let background = scene.interact( - background_rect, + visible, scene.id().with("background"), Sense::click_and_drag(), ); self.handle_pan_and_zoom(&scene, &background); - let visible = self.to_global.inverse() * outer; - scene.set_clip_rect(visible); ui.ctx().set_transform_layer(layer, self.to_global); if self.show_grid { @@ -1694,6 +1694,40 @@ mod tests { .drop_without_applying_deltas(); } + /// Like `pass`, but with a panel on the right the way the app puts the + /// inspector there. Reports whether the widget filling that panel was + /// clicked. + fn pass_beside_a_panel( + ctx: &egui::Context, + view: &mut CanvasView, + doc: &mut Document, + screen: Rect, + events: Vec, + ) -> bool { + let palette = Palette::dark(); + let input = egui::RawInput { + screen_rect: Some(screen), + events, + ..Default::default() + }; + let mut clicked = false; + ctx.run_ui(input, |ui| { + egui::Panel::right("inspector") + .default_size(300.0) + .show(ui, |ui| { + let size = ui.available_size(); + clicked = ui.add_sized(size, egui::Button::new("A control")).clicked(); + }); + egui::CentralPanel::default() + .frame(egui::Frame::NONE) + .show(ui, |ui| { + view.show(ui, doc, &palette); + }); + }) + .drop_without_applying_deltas(); + clicked + } + fn click(at: Pos2) -> egui::Event { egui::Event::PointerButton { pos: at, @@ -1771,6 +1805,32 @@ mod tests { ); } + #[test] + fn a_click_in_the_inspector_stays_in_the_inspector() { + let ctx = egui::Context::default(); + let mut doc = Document::new(); + doc.canvas = canvas_with_two_nodes(); + let mut view = CanvasView::new(); + view.selection.set_node("a"); + + let screen = Rect::from_min_size(Pos2::ZERO, vec2(1200.0, 900.0)); + let at = pos2(1050.0, 450.0); + let mut hit = false; + for events in [ + vec![egui::Event::PointerMoved(at)], + vec![click(at)], + vec![release(at)], + ] { + hit |= pass_beside_a_panel(&ctx, &mut view, &mut doc, screen, events); + } + + assert!(hit, "the widget in the panel never saw the click"); + assert!( + view.selection.has_node("a"), + "the canvas took a click that belonged to the panel" + ); + } + fn canvas_with_two_nodes() -> Canvas { let mut canvas = Canvas::default(); canvas.nodes.push(Node::new(