forked from 1b-agent/fieldcraft-upstream
ui: Suppress/Unsuppress on datums and partitions, greyed in the browser (#291)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UhhZMBTviDQxZvwnyP89fo
This commit is contained in:
1 parent
5189554a6a
commit
84f42f6f8a
5 files changed
+71
-7
No files matched your search
@@ -888,12 +888,17 @@ fn rows(app: &mut ModelApp, ui: &mut egui::Ui, acts: &mut Vec<Act>) {
|
||||
"point" => "geometry.datum.point",
|
||||
_ => "geometry.datum",
|
||||
};
|
||||
let row = Row { depth: 2, icon: Some(icon), label: n, note: Some((*kind).into()), ..Default::default() };
|
||||
let off = app.session.model.datum_suppressed(n);
|
||||
let note = if off { format!("{kind} · suppressed") } else { (*kind).into() };
|
||||
let row = Row { depth: 2, icon: Some(icon), label: n, note: Some(note), dim: off, ..Default::default() };
|
||||
item_row(app, ui, egui::Id::new(("fc_row_datum", i)), row, Target::Def(Def::Datum, n.clone()), acts);
|
||||
}
|
||||
let partitions: Vec<(String, String)> = app.session.model.partitions().iter().map(|p| (p.name.clone(), p.datum.clone())).collect();
|
||||
for (i, (n, datum)) in partitions.iter().enumerate() {
|
||||
let row = Row { depth: 2, icon: Some("geometry.partition"), label: n, note: Some(format!("by {datum}")), ..Default::default() };
|
||||
let model = &app.session.model;
|
||||
let partitions: Vec<(String, String, bool)> =
|
||||
model.partitions().iter().map(|p| (p.name.clone(), p.datum.clone(), !model.partition_cuts(p))).collect();
|
||||
for (i, (n, datum, off)) in partitions.iter().enumerate() {
|
||||
let note = if *off { format!("by {datum} · suppressed") } else { format!("by {datum}") };
|
||||
let row = Row { depth: 2, icon: Some("geometry.partition"), label: n, note: Some(note), dim: *off, ..Default::default() };
|
||||
item_row(app, ui, egui::Id::new(("fc_row_partition", i)), row, Target::Def(Def::Partition, n.clone()), acts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,6 +384,8 @@ pub fn handle(app: &mut ModelApp, ctx: &egui::Context, req: &ControlRequest) ->
|
||||
let action = match (s("action"), s("step")) {
|
||||
(Some("edit"), _) => crate::menu::Action::Edit,
|
||||
(Some("rename"), _) => crate::menu::Action::Rename,
|
||||
(Some("suppress"), _) => crate::menu::Action::Suppress(true),
|
||||
(Some("unsuppress"), _) => crate::menu::Action::Suppress(false),
|
||||
(Some("hide"), _) => crate::menu::Action::Hide,
|
||||
(Some("show"), _) => crate::menu::Action::Show,
|
||||
(Some("isolate"), _) => crate::menu::Action::Isolate,
|
||||
@@ -393,7 +395,9 @@ pub fn handle(app: &mut ModelApp, ctx: &egui::Context, req: &ControlRequest) ->
|
||||
crate::menu::Action::Active { step: step.to_string(), active: a == "activate" }
|
||||
}
|
||||
(a, _) => {
|
||||
return err(format!("`action` {a:?}: edit, rename, hide, show, isolate, show_all, delete, or activate/deactivate with `step`"));
|
||||
return err(format!(
|
||||
"`action` {a:?}: edit, rename, suppress, unsuppress, hide, show, isolate, show_all, delete, or activate/deactivate with `step`"
|
||||
));
|
||||
}
|
||||
};
|
||||
crate::menu::apply(app, target, action);
|
||||
|
||||
@@ -45,6 +45,8 @@ pub enum Action {
|
||||
Edit,
|
||||
/// Rename a definition (`model.rename`'s form, filled in).
|
||||
Rename,
|
||||
/// Suppress a datum or partition, or bring it back.
|
||||
Suppress(bool),
|
||||
Hide,
|
||||
Show,
|
||||
Isolate,
|
||||
@@ -82,6 +84,14 @@ pub fn show(app: &ModelApp, ui: &mut egui::Ui, target: &Target) -> Option<Action
|
||||
if def.rename_kind().is_some() {
|
||||
entry(ui, true, "Rename…", "model.rename: every definition that refers to it follows", Action::Rename);
|
||||
}
|
||||
if let Some((command, suppressed)) = suppression(app, *def, name) {
|
||||
let (label, tip) = if suppressed {
|
||||
("Unsuppress", format!("{command} {{\"suppressed\": false}}: back in the model"))
|
||||
} else {
|
||||
("Suppress", format!("{command}: keep it, but leave it out of the model"))
|
||||
};
|
||||
entry(ui, true, label, &tip, Action::Suppress(!suppressed));
|
||||
}
|
||||
}
|
||||
if let Target::Seed(_) = target {
|
||||
entry(ui, true, "Edit…", "mesh.seed.edge", Action::Edit);
|
||||
@@ -157,6 +167,13 @@ pub fn apply(app: &mut ModelApp, target: Target, action: Action) {
|
||||
}
|
||||
Ok(Value::Null)
|
||||
}
|
||||
Action::Suppress(on) => match &target {
|
||||
Target::Def(def, name) => match suppression(app, *def, name) {
|
||||
Some((command, _)) => app.run(command, json!({"name": name, "suppressed": on})),
|
||||
None => Ok(Value::Null),
|
||||
},
|
||||
_ => Ok(Value::Null),
|
||||
},
|
||||
Action::Hide => app.run("view.hide", items(&target)),
|
||||
Action::Show => app.run("view.show", items(&target)),
|
||||
Action::Isolate => app.run("view.isolate", items(&target)),
|
||||
@@ -179,6 +196,17 @@ pub fn apply(app: &mut ModelApp, target: Target, action: Action) {
|
||||
};
|
||||
}
|
||||
|
||||
/// The command that suppresses `name` of `kind` (a datum or a partition), and whether it is
|
||||
/// suppressed now.
|
||||
fn suppression(app: &ModelApp, kind: Def, name: &str) -> Option<(&'static str, bool)> {
|
||||
let model = &app.session.model;
|
||||
match kind {
|
||||
Def::Datum => Some(("geometry.datum.suppress", model.datum_suppressed(name))),
|
||||
Def::Partition => Some(("geometry.partition.suppress", model.partitions().iter().find(|p| p.name == name)?.suppressed)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// `mesh.seed.edge`'s parameters that make `seed` as it is: its edit form, filled in.
|
||||
pub fn seed_params(seed: &fieldcraft_model::Seed) -> Option<Value> {
|
||||
let mut v = serde_json::to_value(seed).ok()?;
|
||||
@@ -312,6 +340,31 @@ mod tests {
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// Suppress and Unsuppress on a partition's and a datum's menu run their suppress commands:
|
||||
/// the cells merge back and come again; a suppressed datum is not drawn (#291).
|
||||
#[test]
|
||||
fn suppress_from_the_menu_merges_the_cells_back() {
|
||||
let (mut app, dir) = plate();
|
||||
let bodies = |app: &ModelApp| app.session.design.as_ref().map_or(0, |d| d.bodies.len());
|
||||
assert_eq!(bodies(&app), 2);
|
||||
apply(&mut app, Target::Def(Def::Partition, "Half".into()), Action::Suppress(true));
|
||||
assert_eq!(bodies(&app), 1, "{:?}", app.status);
|
||||
assert_eq!(suppression(&app, Def::Partition, "Half"), Some(("geometry.partition.suppress", true)));
|
||||
apply(&mut app, Target::Def(Def::Partition, "Half".into()), Action::Suppress(false));
|
||||
assert_eq!(bodies(&app), 2);
|
||||
apply(&mut app, Target::Def(Def::Datum, "Mid".into()), Action::Suppress(true));
|
||||
assert_eq!(bodies(&app), 1);
|
||||
let drawn = |app: &mut ModelApp| {
|
||||
let b = app.display().bounds;
|
||||
let mut s = fieldcraft_render::Scene::default();
|
||||
fieldcraft_render::glyphs::datums(&app.session.model, &b, &|_| true, None, &crate::theme::Tokens::dark().glyph_colors(), &mut s);
|
||||
s.labels.into_iter().map(|l| l.text).collect::<Vec<_>>()
|
||||
};
|
||||
assert_eq!(drawn(&mut app), ["P"], "the suppressed plane is not drawn");
|
||||
assert_eq!(suppression(&app, Def::Set, "x"), None);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// A screen point on datum `name`'s drawn lines.
|
||||
fn on_datum(app: &mut ModelApp, name: &str) -> egui::Pos2 {
|
||||
let bounds = app.display().bounds;
|
||||
|
||||
Reference in new issue
Block a user