From ebef4677203b210126a664036527d7f5515c5618 Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Fri, 1 May 2026 21:12:14 +0300 Subject: [PATCH] fix: hide sheet viewport border and improve crossing selection Two layout-related fixes: 1. Sheet viewport (id=1) was rendered as a grey rectangle border in paper-space layouts. It is the paper boundary, not a user viewport, and should not appear. Now filtered via is_content_viewport() before tessellation. The redundant id==1 color branch is removed. 2. Box crossing-selection (right-to-left drag) only detected wires whose vertex points fell inside the selection box. Large entities like viewport frames could be visually crossed without any corner inside the box and would not be selected. Added segment-box edge intersection tests so any wire segment that crosses the selection rectangle boundary is included. Fixes part of #10 Co-Authored-By: Claude Sonnet 4.6 --- src/scene/hit_test.rs | 19 ++++++++++++++++++- src/scene/mod.rs | 9 ++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/scene/hit_test.rs b/src/scene/hit_test.rs index 9a45660e5..02a62aa0c 100644 --- a/src/scene/hit_test.rs +++ b/src/scene/hit_test.rs @@ -57,7 +57,9 @@ pub fn click_hit<'a>( /// - **Window mode** (`crossing = false`, left→right drag): /// ALL projected points must lie inside the box. /// - **Crossing mode** (`crossing = true`, right→left drag): -/// ANY projected point inside the box is sufficient. +/// ANY projected point inside the box, OR any wire segment crosses the box +/// boundary (so large entities like viewport frames are caught even when +/// no corner falls inside the selection rectangle). pub fn box_hit<'a>( corner_a: Point, corner_b: Point, @@ -78,6 +80,12 @@ pub fn box_hit<'a>( let inside = |sp: Point| sp.x >= min_x && sp.x <= max_x && sp.y >= min_y && sp.y <= max_y; + // Box corners for segment-intersection tests (crossing mode only). + let box_tl = Point { x: min_x, y: min_y }; + let box_tr = Point { x: max_x, y: min_y }; + let box_bl = Point { x: min_x, y: max_y }; + let box_br = Point { x: max_x, y: max_y }; + wires .iter() .filter_map(|wire| { @@ -92,7 +100,16 @@ pub fn box_hit<'a>( .collect(); let hit = if crossing { + // A wire is hit when any vertex is inside the box OR any of its + // segments crosses one of the four box edges. screen.iter().any(|&sp| inside(sp)) + || screen.windows(2).any(|seg| { + let (a, b) = (seg[0], seg[1]); + segments_intersect(a, b, box_tl, box_tr) + || segments_intersect(a, b, box_tr, box_br) + || segments_intersect(a, b, box_br, box_bl) + || segments_intersect(a, b, box_bl, box_tl) + }) } else { screen.iter().all(|&sp| inside(sp)) }; diff --git a/src/scene/mod.rs b/src/scene/mod.rs index e4cb4c01e..1fc10ccb1 100644 --- a/src/scene/mod.rs +++ b/src/scene/mod.rs @@ -3084,12 +3084,15 @@ fn tessellate_entity( let sel = selected.contains(&h); if let EntityType::Viewport(vp) = e { + // The sheet viewport (overall/id=1) is never shown — it represents the + // paper boundary, not a user-defined content window. + if !Scene::is_content_viewport(vp) { + return vec![]; + } let is_active = active_viewport == Some(h); let is_locked = vp.status.locked; - let color = if sel && vp.id != 1 { + let color = if sel { [1.0, 1.0, 1.0, 1.0] - } else if vp.id == 1 { - [0.40, 0.40, 0.40, 1.0] } else if is_active { [1.0, 0.90, 0.20, 1.0] } else if is_locked {