forked from 1b-agent/opencadstudio-upstream
refactor(selection): split 31-field SelectionState into five sub-structs
SelectionState now holds view, gesture, input, menu and orbit sub-structs; all 450 access sites updated mechanically with no behavior change. Deletes three write-only dead fields (box_last, box_last_crossing, poly_last_crossing). Hand-rolled clears in commands and dim_viewport route through the new clear methods; bench fixture updated. Full suite: 1976 passed / 0 failed / 25 ignored.
This commit is contained in:
1 parent
8c2cfa2c8f
commit
7aafd9c710
27 files changed
+512
-469
No files matched your search
@@ -430,7 +430,7 @@ impl Scene {
|
||||
/// wrong whenever a tiled viewport is active.
|
||||
pub(super) fn active_camera_aspect(&self) -> f32 {
|
||||
if self.current_layout == "Model" {
|
||||
let (canvas_w, canvas_h) = self.selection.borrow().vp_size;
|
||||
let (canvas_w, canvas_h) = self.selection.borrow().view.vp_size;
|
||||
let tiles = self.model_tiles.borrow();
|
||||
let active = self.active_model_tile.get().min(tiles.len().saturating_sub(1));
|
||||
if let Some(tile) = tiles.get(active) {
|
||||
@@ -803,7 +803,7 @@ impl Scene {
|
||||
z: view_dir.z as f64,
|
||||
};
|
||||
entry.view_height = view_height as f64;
|
||||
let (canvas_width, canvas_height) = self.selection.borrow().vp_size;
|
||||
let (canvas_width, canvas_height) = self.selection.borrow().view.vp_size;
|
||||
let viewport_width = (upper_right.x - lower_left.x).abs() * canvas_width as f64;
|
||||
let viewport_height = (upper_right.y - lower_left.y).abs() * canvas_height as f64;
|
||||
if viewport_width > 1e-9 && viewport_height > 1e-9 {
|
||||
@@ -1556,7 +1556,7 @@ impl Scene {
|
||||
return;
|
||||
};
|
||||
|
||||
let (canvas_w, canvas_h) = self.selection.borrow().vp_size;
|
||||
let (canvas_w, canvas_h) = self.selection.borrow().view.vp_size;
|
||||
let fallback = self.last_render_aspect.get().max(0.01);
|
||||
let aspects: Vec<f32> = self
|
||||
.model_tiles
|
||||
|
||||
@@ -3068,7 +3068,7 @@ impl super::Scene {
|
||||
/// `(id, anchor, outward_screen_direction, label, is_conflicting,
|
||||
/// hover_points)` for every enabled, visible constraint whose glyph
|
||||
/// projects on-screen.
|
||||
/// `vp_size` is the full canvas size (as `SelectionState::vp_size`
|
||||
/// `vp_size` is the full canvas size (as `SelectionView::vp_size`
|
||||
/// reports it), matching what `viewport_edit_frame`/
|
||||
/// `active_model_tile_bounds` expect. Mirrors the projection
|
||||
/// `crate::app::view` builds its own render list with. The memoised
|
||||
@@ -3808,7 +3808,7 @@ mod tests {
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
),
|
||||
));
|
||||
scene.selection.borrow_mut().vp_size = (800.0, 600.0);
|
||||
scene.selection.borrow_mut().view.vp_size = (800.0, 600.0);
|
||||
let id = scene.parametric_constraint_set_mut(scope).add(
|
||||
ConstraintKind::Horizontal,
|
||||
vec![ParametricRef::whole(line)],
|
||||
@@ -3848,7 +3848,7 @@ mod tests {
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
),
|
||||
));
|
||||
scene.selection.borrow_mut().vp_size = (800.0, 600.0);
|
||||
scene.selection.borrow_mut().view.vp_size = (800.0, 600.0);
|
||||
let id = scene.parametric_constraint_set_mut(scope).add(
|
||||
ConstraintKind::Horizontal,
|
||||
vec![ParametricRef::whole(line)],
|
||||
@@ -3878,7 +3878,7 @@ mod tests {
|
||||
Vector3::new(1.0, 0.0, 0.0),
|
||||
),
|
||||
));
|
||||
scene.selection.borrow_mut().vp_size = (800.0, 600.0);
|
||||
scene.selection.borrow_mut().view.vp_size = (800.0, 600.0);
|
||||
let id = scene.parametric_constraint_set_mut(scope).add(
|
||||
ConstraintKind::Horizontal,
|
||||
vec![ParametricRef::whole(line)],
|
||||
|
||||
@@ -2,9 +2,29 @@ use iced::time::Instant;
|
||||
use iced::Point;
|
||||
|
||||
/// Mouse / selection interaction state for the viewport.
|
||||
///
|
||||
/// Split (#32) into five sub-structs by family so a per-frame clone can copy
|
||||
/// only the family it needs and gesture clears touch one family at a time.
|
||||
/// The `RefCell` around this struct (`Scene::selection`) is untouched.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SelectionState {
|
||||
pub view: SelectionView,
|
||||
pub gesture: SelectionGesture,
|
||||
pub input: SelectionInput,
|
||||
pub menu: SelectionMenu,
|
||||
pub orbit: SelectionOrbit,
|
||||
}
|
||||
|
||||
/// View-dependent snapshot: the canvas size selection geometry is expressed in.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SelectionView {
|
||||
pub vp_size: (f32, f32),
|
||||
}
|
||||
|
||||
/// One box-or-lasso gesture: anchor/current corners, crossing sense, and the
|
||||
/// in-progress polygon. Cleared together by [`SelectionGesture::clear_left`].
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SelectionGesture {
|
||||
pub box_anchor: Option<Point>,
|
||||
/// World point under the box-selection anchor, so the anchor can be
|
||||
/// re-projected to screen when the camera zooms/pans mid-drag instead of
|
||||
@@ -12,14 +32,12 @@ pub struct SelectionState {
|
||||
/// (#234)
|
||||
pub box_anchor_world: Option<glam::DVec3>,
|
||||
pub box_current: Option<Point>,
|
||||
pub box_last: Option<(Point, Point)>,
|
||||
pub box_crossing: bool,
|
||||
/// Set when a Window / Crossing selection keyword fixed the sense of the
|
||||
/// box being drawn. Dragging normally decides it from the direction the
|
||||
/// corner travels, which would immediately overwrite what the user just
|
||||
/// asked for, so that derivation stands down while this holds.
|
||||
pub box_crossing_locked: bool,
|
||||
pub box_last_crossing: bool,
|
||||
/// A preview-only selection marquee `(anchor, current, crossing)` in canvas
|
||||
/// pixels, drawn identically to a real box-selection (green crossing fill /
|
||||
/// blue window fill) but with NO hit-test behaviour. Commands that pick a
|
||||
@@ -29,14 +47,25 @@ pub struct SelectionState {
|
||||
pub poly_active: bool,
|
||||
pub poly_points: Vec<Point>,
|
||||
pub poly_crossing: bool,
|
||||
pub poly_last_crossing: bool,
|
||||
/// Canvas position the right-click context menu is anchored at while it
|
||||
/// is open. `None` = closed. Its rows are rebuilt from app state every
|
||||
/// frame (`ui::popup::context_menu::build_context_menu`).
|
||||
pub context_menu: Option<Point>,
|
||||
/// Transient UI state of the open context menu (expanded submenu,
|
||||
/// keyboard highlight). Reset every time the menu opens.
|
||||
pub context_menu_ui: ContextMenuUi,
|
||||
}
|
||||
|
||||
impl SelectionGesture {
|
||||
/// End the in-progress box/lasso gesture (left-button release path).
|
||||
pub fn clear_left(&mut self) {
|
||||
self.box_anchor = None;
|
||||
self.box_anchor_world = None;
|
||||
self.box_current = None;
|
||||
self.box_crossing = false;
|
||||
self.box_crossing_locked = false;
|
||||
self.poly_active = false;
|
||||
self.poly_points.clear();
|
||||
self.poly_crossing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Raw button/pointer state: press positions, drag flags, orbit pivot input.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SelectionInput {
|
||||
pub last_move_pos: Option<Point>,
|
||||
pub left_down: bool,
|
||||
pub left_press_pos: Option<Point>,
|
||||
@@ -47,10 +76,6 @@ pub struct SelectionState {
|
||||
pub right_press_time: Option<Instant>,
|
||||
pub right_dragging: bool,
|
||||
pub right_last_pos: Option<Point>,
|
||||
/// World point the current orbit drag revolves around (selection or model
|
||||
/// centre), captured when the drag starts so it stays fixed for the whole
|
||||
/// gesture. `None` when no orbit is in progress. (#229)
|
||||
pub orbit_pivot: Option<glam::DVec3>,
|
||||
/// While a command is active, a right-click acts as Enter; the *next*
|
||||
/// consecutive right-click opens the context menu instead. This tracks
|
||||
/// whether the previous right-click already fired Enter. Reset by any
|
||||
@@ -66,6 +91,52 @@ pub struct SelectionState {
|
||||
pub zoom_dir_out: bool,
|
||||
}
|
||||
|
||||
impl SelectionInput {
|
||||
/// Release the left button (click-commit path).
|
||||
pub fn clear_left_buttons(&mut self) {
|
||||
self.left_down = false;
|
||||
self.left_press_pos = None;
|
||||
self.left_press_time = None;
|
||||
self.left_dragging = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Right-click context-menu state.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SelectionMenu {
|
||||
/// Canvas position the right-click context menu is anchored at while it
|
||||
/// is open. `None` = closed. Its rows are rebuilt from app state every
|
||||
/// frame (`ui::popup::context_menu::build_context_menu`).
|
||||
pub open_at: Option<Point>,
|
||||
/// Transient UI state of the open context menu (expanded submenu,
|
||||
/// keyboard highlight). Reset every time the menu opens.
|
||||
pub ui: ContextMenuUi,
|
||||
}
|
||||
|
||||
impl SelectionMenu {
|
||||
/// Open the context menu at `at`, discarding the previous menu's
|
||||
/// expanded-submenu / highlight state.
|
||||
pub fn open(&mut self, at: Point) {
|
||||
self.open_at = Some(at);
|
||||
self.ui = ContextMenuUi::default();
|
||||
}
|
||||
|
||||
/// Close the context menu (no-op when it is not open).
|
||||
pub fn close(&mut self) {
|
||||
self.open_at = None;
|
||||
self.ui = ContextMenuUi::default();
|
||||
}
|
||||
}
|
||||
|
||||
/// Orbit-drag state.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct SelectionOrbit {
|
||||
/// World point the current orbit drag revolves around (selection or model
|
||||
/// centre), captured when the drag starts so it stays fixed for the whole
|
||||
/// gesture. `None` when no orbit is in progress. (#229)
|
||||
pub pivot: Option<glam::DVec3>,
|
||||
}
|
||||
|
||||
/// Transient state of the open right-click context menu.
|
||||
#[derive(Clone, Default)]
|
||||
pub struct ContextMenuUi {
|
||||
@@ -78,36 +149,12 @@ pub struct ContextMenuUi {
|
||||
}
|
||||
|
||||
impl SelectionState {
|
||||
/// Open the context menu at `at`, discarding the previous menu's
|
||||
/// expanded-submenu / highlight state.
|
||||
pub fn open_context_menu(&mut self, at: Point) {
|
||||
self.context_menu = Some(at);
|
||||
self.context_menu_ui = ContextMenuUi::default();
|
||||
}
|
||||
|
||||
/// Close the context menu (no-op when it is not open).
|
||||
pub fn close_context_menu(&mut self) {
|
||||
self.context_menu = None;
|
||||
self.context_menu_ui = ContextMenuUi::default();
|
||||
}
|
||||
|
||||
/// End every left-button selection gesture without disturbing the previous
|
||||
/// completed-window record or command-owned preview marquee. Grip editing
|
||||
/// owns the left button while engaged and calls this before/after placement
|
||||
/// so a small pointer move cannot also arm a box or lasso selection.
|
||||
pub fn clear_left_selection_gesture(&mut self) {
|
||||
self.left_down = false;
|
||||
self.left_press_pos = None;
|
||||
self.left_press_time = None;
|
||||
self.left_dragging = false;
|
||||
self.box_anchor = None;
|
||||
self.box_anchor_world = None;
|
||||
self.box_current = None;
|
||||
self.box_crossing = false;
|
||||
self.box_crossing_locked = false;
|
||||
self.poly_active = false;
|
||||
self.poly_points.clear();
|
||||
self.poly_crossing = false;
|
||||
self.input.clear_left_buttons();
|
||||
self.gesture.clear_left();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ impl Scene {
|
||||
self.interim_wire = None;
|
||||
// Drop any point-picked window marquee (STRETCH) so it doesn't linger
|
||||
// after the command ends before the next mouse move. (#291)
|
||||
self.selection.borrow_mut().preview_box = None;
|
||||
self.selection.borrow_mut().gesture.preview_box = None;
|
||||
}
|
||||
|
||||
pub fn wire_models_for(&self, handles: &[codec::Handle]) -> Vec<WireModel> {
|
||||
|
||||
@@ -4213,7 +4213,7 @@ impl Scene {
|
||||
// `CursorMoved` handler — the cube overlay sits above the shader
|
||||
// and would otherwise mask the move event from `Program::update`.
|
||||
let hover_region = show_interaction.then(|| self.viewcube_hover.get()).flatten();
|
||||
self.selection.borrow_mut().vp_size = (bounds.width, bounds.height);
|
||||
self.selection.borrow_mut().view.vp_size = (bounds.width, bounds.height);
|
||||
if bounds.height > 0.0 {
|
||||
self.set_render_aspect(bounds.width / bounds.height);
|
||||
self.set_render_pixel_scale(bounds.width, bounds.height);
|
||||
|
||||
Reference in new issue
Block a user