feat(ui): Select Other — pick what lies under something else (#503)

Hold the left button still (long-press on touch) or right-click › Select Other… to list everything under the cursor: sketch points and curves ("Line 3", "Point (arc 2 centre)"), vertices, edges, faces, bodies, profiles, planes and axes, filtered like a click and by the open dialog's active input. Hovering a row lights it up; clicking selects it (Shift/Ctrl add) or feeds the dialog. A repeat click on the same spot with nothing changed selects the next thing there; a double-click keeps its own actions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
bherbruckandClaude Opus 5.5 authored and GitHub committed 2026-10-12 22:56:57 -04:00
1 parent 1fd4a2f782
commit 4aaa5457b3
13 files changed
+952 -76

No files matched your search

+12
View File
@@ -164,6 +164,8 @@ pub struct MenuState {
pub confirm: Option<crate::delete::Pending>,
/// The Properties window: title and measurements.
pub props: Option<(String, Value)>,
/// The viewport menu opened over something: it offers Select Other.
pub over_geometry: bool,
}
#[derive(Clone, Debug, PartialEq)]
@@ -346,6 +348,11 @@ fn viewport_items(app: &SolveApp) -> Vec<Item> {
v.push(act("ui.repeat", &format!("Repeat {label}"), "redo"));
v.push(Item::sep());
}
// Everything under the cursor, for what lies beneath something else.
if app.menu.over_geometry {
v.push(act("ui.selectOther", "Select Other…", "").key("Hold click"));
v.push(Item::sep());
}
if app.session.active_sketch.is_some() {
v.extend(sketch_entity_items(app));
v.push(Item::sep());
@@ -726,6 +733,9 @@ pub fn run_item(app: &mut SolveApp, item: &Item, at: Pos2) {
app.start(&id);
}
}
"ui.selectOther" => {
crate::select_other::open(app, at);
}
"ui.delete" if item.params.is_some() => crate::delete::request(app, p),
"ui.delete" => delete(app),
"ui.finishSketch" => app.finish_sketch(),
@@ -1161,6 +1171,8 @@ fn commit_rename(app: &mut SolveApp, r: &Rename) {
/// Open the viewport menu at a screen position. A right-click on something not selected
/// selects it first, so the menu is about what is under the cursor.
pub fn open(app: &mut SolveApp, at: Pos2) {
app.menu.over_geometry = app.viewport.hover.is_some();
crate::select_other::close(app);
if app.session.active_sketch.is_none()
&& let Some(h) = app.viewport.hover.clone()
&& let Some((_, sel)) = crate::viewport::candidate(app, &[h])