Selection filter in the SELECT panel: select bodies (a face picks and highlights its body), faces, edges, vertices, sketch geometry
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
656502b882
commit
24627d4f0a
3 files changed
+49
-5
No files matched your search
@@ -55,6 +55,10 @@ pub struct UiState {
|
||||
pub palette_open: bool,
|
||||
/// Dark theme (the default); a preference kept between runs.
|
||||
pub dark: bool,
|
||||
/// Selection filter: kinds a click doesn't pick (`vertices`, `edges`, `faces`, `sketch`).
|
||||
pub pick_off: Vec<String>,
|
||||
/// A click on a face picks its whole body.
|
||||
pub pick_bodies: bool,
|
||||
}
|
||||
|
||||
impl Default for UiState {
|
||||
@@ -71,6 +75,8 @@ impl Default for UiState {
|
||||
show_sketches: true,
|
||||
palette_open: false,
|
||||
dark: true,
|
||||
pick_off: Vec::new(),
|
||||
pick_bodies: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ pub fn toolbar(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
let cmds: Vec<_> = specs.iter().filter(|c| c.tab == tab && c.panel == *panel).collect();
|
||||
let n = promoted(&tab, panel).min(cmds.len()).max(if cmds.is_empty() { 1 } else { 0 });
|
||||
let width = (n as f32 * 40.0).max(64.0) + 8.0;
|
||||
let enabled_panel = !cmds.is_empty();
|
||||
let enabled_panel = !cmds.is_empty() || *panel == "SELECT";
|
||||
for (i, c) in cmds.iter().take(n).enumerate() {
|
||||
let br = Rect::from_min_size(pos2(px + 4.0 + i as f32 * 40.0, top + 2.0), vec2(38.0, 38.0));
|
||||
let info = c.info(&app.session);
|
||||
@@ -305,7 +305,9 @@ pub fn toolbar(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
let area = egui::Area::new(popup_id).fixed_pos(lr.left_bottom()).order(egui::Order::Foreground).show(ui.ctx(), |ui| {
|
||||
egui::Frame::popup(ui.style()).show(ui, |ui| {
|
||||
ui.set_min_width(200.0);
|
||||
if cmds.is_empty() {
|
||||
if *panel == "SELECT" {
|
||||
selection_filter(app, ui);
|
||||
} else if cmds.is_empty() {
|
||||
ui.label(egui::RichText::new("Not available yet").color(t.text_dim));
|
||||
}
|
||||
for c in &cmds {
|
||||
@@ -335,6 +337,22 @@ pub fn toolbar(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
});
|
||||
}
|
||||
|
||||
/// The SELECT panel: what a click in the view picks.
|
||||
fn selection_filter(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
let t = Tokens::get();
|
||||
ui.label(egui::RichText::new("Selection filter").color(t.text_dim).size(11.0));
|
||||
ui.checkbox(&mut app.ui.pick_bodies, "Select bodies (a face picks its body)");
|
||||
for (k, label) in [("faces", "Faces"), ("edges", "Edges"), ("vertices", "Vertices"), ("sketch", "Sketch geometry and profiles")] {
|
||||
let mut on = !app.ui.pick_off.iter().any(|x| x == k);
|
||||
if ui.checkbox(&mut on, label).changed() {
|
||||
app.ui.pick_off.retain(|x| x != k);
|
||||
if !on {
|
||||
app.ui.pick_off.push(k.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A small drop-down caret.
|
||||
fn caret(p: &egui::Painter, c: egui::Pos2, col: Color32) {
|
||||
p.add(egui::Shape::convex_polygon(vec![pos2(c.x - 3.5, c.y - 2.0), pos2(c.x + 3.5, c.y - 2.0), pos2(c.x, c.y + 2.5)], col, Stroke::NONE));
|
||||
|
||||
@@ -449,12 +449,20 @@ pub fn candidate(app: &SolveApp, hits: &[Hit]) -> Option<(Hit, Sel)> {
|
||||
if let Some(d) = app.dialog.as_ref().filter(|d| d.wants_picks()) {
|
||||
return hits.iter().find_map(|h| d.candidate(&app.session, h).map(|s| (h.clone(), s)));
|
||||
}
|
||||
let off = |k: &str| app.ui.pick_off.iter().any(|x| x == k);
|
||||
hits.iter()
|
||||
.filter(|h| match h {
|
||||
Hit::SketchCurve { sketch, .. } => app.session.active_sketch == Some(*sketch),
|
||||
_ => true,
|
||||
Hit::SketchCurve { sketch, .. } => app.session.active_sketch == Some(*sketch) && !off("sketch"),
|
||||
Hit::SketchPoint { .. } | Hit::Profile { .. } => !off("sketch"),
|
||||
Hit::Vertex { .. } => !off("vertices") && !app.ui.pick_bodies,
|
||||
Hit::Edge { .. } => !off("edges") && !app.ui.pick_bodies,
|
||||
Hit::Face { .. } => !off("faces") || app.ui.pick_bodies,
|
||||
Hit::Plane { .. } | Hit::Axis { .. } => true,
|
||||
})
|
||||
.find_map(|h| match h {
|
||||
Hit::Face { body, .. } if app.ui.pick_bodies => Some((h.clone(), Sel::Body { name: body.clone() })),
|
||||
_ => hit_sel(h).map(|s| (h.clone(), s)),
|
||||
})
|
||||
.find_map(|h| hit_sel(h).map(|s| (h.clone(), s)))
|
||||
}
|
||||
|
||||
/// The edge a selection refers to now: by index if it still passes through the stored point,
|
||||
@@ -535,6 +543,7 @@ fn highlight_key(app: &SolveApp) -> u64 {
|
||||
serde_json::to_string(&app.highlighted()).unwrap_or_default().hash(&mut h);
|
||||
format!("{:?}", app.viewport.hover).hash(&mut h);
|
||||
app.viewport.hover_feature.hash(&mut h);
|
||||
app.ui.pick_bodies.hash(&mut h);
|
||||
crate::theme::is_dark().hash(&mut h);
|
||||
app.preview.replaced.hash(&mut h);
|
||||
app.origin_visible().hash(&mut h);
|
||||
@@ -649,6 +658,17 @@ fn build_highlight(app: &SolveApp) -> GpuScene {
|
||||
}
|
||||
// Hover (pre-highlight).
|
||||
match hover {
|
||||
// Body selection: the whole body lights up.
|
||||
Some(Hit::Face { body, .. }) if app.ui.pick_bodies && app.dialog.is_none() && app.tool.is_none() => {
|
||||
if let Some(b) = st.body(body) {
|
||||
let k = t.hover_face_lift;
|
||||
let c = (t.body.r(), t.body.g(), t.body.b());
|
||||
let m = b.mesh();
|
||||
for f in 0..b.body.face_count() {
|
||||
face_tris(&mut sc, &m, f, [c.0.saturating_add(k), c.1.saturating_add(k), c.2.saturating_add(k), 255], true);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(Hit::Face { body, index, .. })
|
||||
if !app.preview.replaced.contains(body)
|
||||
&& !sel.iter().any(|x| matches!(x, Sel::Face { body: b, index: i, .. } if b == body && i == index)) =>
|
||||
|
||||
Reference in new issue
Block a user