git.lucas.co / cce-designer
graphic design tool
git clone https://git.lucas.co/cce-designer.git

commit9446366978233283e9443a87977d9ed3386e39b0
parentb927af15d9
authorLucas Galante <lsgalante12@gmail.com>
date2026-09-25 12:34
fix: the scene starts at the network plate's edge

cursor_in_viewport carved out the old column layout -- left of
splitter1_x + SPLITTER_W, above the network content's top -- which the
floating plates stopped following, so a band right of the network plate
and a strip along the top belonged to nothing and a right-click there
opened no menu. It is now the viewport's own rect minus the network
plates and the floating panes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

 src/app.rs  | 21 +++++++++++++++------
 src/main.rs | 33 +++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/src/app.rs b/src/app.rs
index f68c413..32ac643 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -2675,12 +2675,21 @@ impl State {
         // asked, and the spreadsheet's own header-click sort never fired.
         // Same for the right-click menu and the pinch zoom, which gate on
         // this test as well.
-        let node_area_y = self.positions[CONTENT_IDX].1;
-        !self.over_floating_pane()
-            && self.cursor_x >= self.content_right_x()
-            && self.cursor_x < self.splitter_layout.splitter2_x
-            && self.cursor_y >= node_area_y
-            && self.cursor_y < self.height - STATUS_H
+        //
+        // Bounded by the viewport's OWN rect, minus the network plates by
+        // their laid-out rects. Until 2026-09-25 this carved out the old
+        // column layout instead — left of `splitter1_x + SPLITTER_W`, above
+        // the network content's top — which the floating plates stopped
+        // following long ago, so a band right of the network plate and a
+        // strip along the top were neither the network's nor the scene's,
+        // and a right-click there opened nothing. The circular pane is
+        // excluded by its callers (`in_circle_network_pane`): its rect is
+        // the circle's bounding box, whose corners are scene.
+        let (px, py) = (self.cursor_x, self.cursor_y);
+        let inside = |(x, y, w, h): (f32, f32, f32, f32)| w > 0.0 && h > 0.0 && px >= x && px < x + w && py >= y && py < y + h;
+        let over_network = !self.circular_network_pane
+            && [NETWORK_PANEL_IDX, crate::slots::NETWORK_PANEL2_IDX].iter().any(|&idx| inside(self.positions[idx]));
+        inside(self.positions[VIEWPORT_IDX]) && !over_network && !self.over_floating_pane()
     }
 
     // --- Pane edge-resize hotspots. Each is the single source of truth for its zone:
diff --git a/src/main.rs b/src/main.rs
index a504f62..225ec77 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -471,6 +471,39 @@ mod tests {
     /// before any widget is asked. So the toolkit's header-click sort
     /// (tested in cce-ui) never fired in this app: clicking a header
     /// hovered it, tinted it, and did nothing.
+    /// The scene is everything in the viewport's rect that no plate covers
+    /// — right up to the network plate's edge. The test used to carve out
+    /// the old COLUMN layout, so the band between the floating plate's right
+    /// edge and `splitter1_x + SPLITTER_W`, and the strip above the network
+    /// content, were nobody's: a right-click there opened no menu.
+    #[test]
+    fn the_scene_starts_at_the_network_plates_edge() {
+        use crate::slots::{NETWORK_PANEL_IDX, VIEWPORT_IDX};
+        use crate::window::{LocalPosition, WindowEvent};
+        use cce_ui::widget::{ElementState, MouseButton};
+        let mut state = State::new(false);
+        state.resize(1600.0, 900.0, 1.0);
+        state.network_plate = true;
+        state.splitter_layout.splitter1_x = 700.0;
+        state.rebuild_positions();
+        state.apply_layout();
+        let (nx, ny, nw, nh) = state.positions[NETWORK_PANEL_IDX];
+        assert!(nw > 0.0 && nx + nw < 690.0, "the plate ends short of the old column split");
+        let at = |state: &mut State, x: f32, y: f32| {
+            state.handle_event(&WindowEvent::CursorMoved { position: LocalPosition { x: x as f64, y: y as f64 } });
+            state.cursor_in_viewport()
+        };
+        assert!(!at(&mut state, nx + nw - 4.0, ny + nh * 0.5), "the plate is the network's");
+        assert!(at(&mut state, nx + nw + 4.0, ny + nh * 0.5), "just right of the plate is scene");
+        let (vx, vy, vw, _) = state.positions[VIEWPORT_IDX];
+        assert!(at(&mut state, vx + vw * 0.5, vy + 4.0), "the strip along the top is scene");
+
+        // And the right press there opens the viewport's menu.
+        at(&mut state, nx + nw + 4.0, ny + nh * 0.5);
+        state.handle_event(&WindowEvent::MouseInput { state: ElementState::Pressed, button: MouseButton::Right });
+        assert!(state.viewport_menu_open(), "a right-click beside the plate opens the viewport menu");
+    }
+
     #[test]
     fn spreadsheet_header_press_reaches_the_widget_not_the_camera() {
         use crate::slots::{SPREADSHEET_IDX, SPREADSHEET_MENUBAR_IDX, VIEWPORT_IDX};