Preferences (File menu): theme, grid, perspective, auto-project face edges, body selection; all kept between runs
Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01AS5Tn3xieewUSg3uxrMVdL
This commit is contained in:
1 parent
967bee179a
commit
7a0cc85392
3 files changed
+63
-8
No files matched your search
@@ -78,6 +78,8 @@ pub enum Kind {
|
||||
Sweep {
|
||||
operation: usize,
|
||||
},
|
||||
/// Application preferences (applied as they change; kept between runs).
|
||||
Preferences,
|
||||
/// Physical material (and with it the appearance) of bodies.
|
||||
Material {
|
||||
index: usize,
|
||||
@@ -286,6 +288,10 @@ impl Dialog {
|
||||
Some(d)
|
||||
}
|
||||
|
||||
pub fn preferences() -> Dialog {
|
||||
Dialog::new(Kind::Preferences, vec![])
|
||||
}
|
||||
|
||||
pub fn rename(feature: u64, name: &str) -> Dialog {
|
||||
Dialog::new(Kind::Rename { feature, name: name.to_string() }, vec![])
|
||||
}
|
||||
@@ -495,6 +501,7 @@ fn title(k: &Kind) -> &'static str {
|
||||
Kind::Mirror => "MIRROR",
|
||||
Kind::Measure { .. } => "MEASURE",
|
||||
Kind::Material { .. } => "PHYSICAL MATERIAL",
|
||||
Kind::Preferences => "PREFERENCES",
|
||||
Kind::Section { .. } => "SECTION ANALYSIS",
|
||||
Kind::PatternRect { .. } => "RECTANGULAR PATTERN",
|
||||
Kind::PatternCirc { .. } => "CIRCULAR PATTERN",
|
||||
@@ -592,6 +599,30 @@ fn update_measure(app: &SolveApp, d: &mut Dialog) {
|
||||
}
|
||||
}
|
||||
|
||||
/// The preferences, applied as they change.
|
||||
fn preferences_rows(app: &mut SolveApp, ui: &mut egui::Ui) {
|
||||
ui.label("Theme");
|
||||
let mut theme = usize::from(!app.ui.dark);
|
||||
combo(ui, "pref_theme", &["Dark", "Light"], &mut theme);
|
||||
app.ui.dark = theme == 0;
|
||||
ui.end_row();
|
||||
ui.label("Grid");
|
||||
ui.checkbox(&mut app.ui.show_grid, "");
|
||||
ui.end_row();
|
||||
ui.label("Perspective view");
|
||||
ui.checkbox(&mut app.ui.perspective, "");
|
||||
ui.end_row();
|
||||
ui.label("Project face edges into new sketches");
|
||||
let mut auto = app.session.auto_project;
|
||||
if ui.checkbox(&mut auto, "").changed() {
|
||||
let _ = app.run("sketch.auto_project", json!({ "value": auto }));
|
||||
}
|
||||
ui.end_row();
|
||||
ui.label("Click selects whole bodies");
|
||||
ui.checkbox(&mut app.ui.pick_bodies, "");
|
||||
ui.end_row();
|
||||
}
|
||||
|
||||
fn fmt_mm(v: &Value) -> String {
|
||||
v.as_f64().map(|x| format!("{x:.3} mm")).unwrap_or_default()
|
||||
}
|
||||
@@ -755,6 +786,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
}
|
||||
Kind::Mirror => {}
|
||||
Kind::Measure { result, .. } => measure_rows(ui, result.as_ref()),
|
||||
Kind::Preferences => preferences_rows(app, ui),
|
||||
Kind::Material { index } => {
|
||||
let names: Vec<&str> = solvecraft_engine::doc::MATERIALS.iter().map(|(n, _)| *n).collect();
|
||||
ui.label("Material");
|
||||
@@ -888,7 +920,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
ui.horizontal(|ui| {
|
||||
if !matches!(d.kind, Kind::Sketch | Kind::Params { .. } | Kind::Measure { .. })
|
||||
if !matches!(d.kind, Kind::Sketch | Kind::Params { .. } | Kind::Measure { .. } | Kind::Preferences)
|
||||
&& ui
|
||||
.add(
|
||||
egui::Button::new(
|
||||
@@ -901,7 +933,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
{
|
||||
ok = true;
|
||||
}
|
||||
let close = if matches!(d.kind, Kind::Params { .. } | Kind::Measure { .. }) { "Close" } else { "Cancel" };
|
||||
let close = if matches!(d.kind, Kind::Params { .. } | Kind::Measure { .. } | Kind::Preferences) { "Close" } else { "Cancel" };
|
||||
if ui.add(egui::Button::new(close).min_size(vec2(70.0, 24.0))).clicked() {
|
||||
cancel = true;
|
||||
}
|
||||
@@ -914,7 +946,7 @@ pub fn show(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
let (enter_free, esc) = ctx.input(|i| (i.key_pressed(egui::Key::Enter), i.key_pressed(egui::Key::Escape)));
|
||||
let nothing_focused = ctx.memory(|m| m.focused().is_none());
|
||||
let canvas_enter = enter_free && ctx.memory(|m| m.had_focus_last_frame(egui::Id::new("sc_canvas_value")));
|
||||
let enter_applies = !matches!(d.kind, Kind::Sketch | Kind::Params { .. } | Kind::Measure { .. } | Kind::ConfirmDelete { .. });
|
||||
let enter_applies = !matches!(d.kind, Kind::Sketch | Kind::Params { .. } | Kind::Measure { .. } | Kind::ConfirmDelete { .. } | Kind::Preferences);
|
||||
if enter_applies && (enter || canvas_enter || (enter_free && nothing_focused)) {
|
||||
ok = true;
|
||||
}
|
||||
@@ -1300,7 +1332,7 @@ fn dialog_commands(app: &SolveApp, d: &Dialog) -> Result<Vec<(String, Value)>, S
|
||||
Kind::EditParam { name, expr } => ("ChangeParameterCommand", json!({"name": name, "expression": expr})),
|
||||
Kind::Rename { feature, name } => ("FusionRenameTimelineEntryCommand", json!({"feature": feature, "name": name})),
|
||||
Kind::ConfirmDelete { feature, .. } => ("FusionDeleteCommand", json!({ "features": [feature.to_string()] })),
|
||||
Kind::Sketch | Kind::Params { .. } | Kind::Measure { .. } => return Ok(Vec::new()),
|
||||
Kind::Sketch | Kind::Params { .. } | Kind::Measure { .. } | Kind::Preferences => return Ok(Vec::new()),
|
||||
};
|
||||
let mut params = params;
|
||||
if let Value::Object(m) = &mut params {
|
||||
|
||||
@@ -159,16 +159,35 @@ impl SolveApp {
|
||||
|
||||
/// Preferences kept between runs (JSON), for the host to store.
|
||||
pub fn prefs(&self) -> String {
|
||||
json!({ "dark": self.ui.dark }).to_string()
|
||||
json!({
|
||||
"dark": self.ui.dark,
|
||||
"grid": self.ui.show_grid,
|
||||
"perspective": self.ui.perspective,
|
||||
"pick_bodies": self.ui.pick_bodies,
|
||||
"auto_project": self.session.auto_project,
|
||||
})
|
||||
.to_string()
|
||||
}
|
||||
|
||||
/// Restore preferences saved with [`SolveApp::prefs`].
|
||||
pub fn load_prefs(&mut self, prefs: &str) {
|
||||
if let Ok(v) = serde_json::from_str::<Value>(prefs)
|
||||
&& let Some(d) = v.get("dark").and_then(Value::as_bool)
|
||||
{
|
||||
let Ok(v) = serde_json::from_str::<Value>(prefs) else { return };
|
||||
let flag = |k: &str| v.get(k).and_then(Value::as_bool);
|
||||
if let Some(d) = flag("dark") {
|
||||
self.ui.dark = d;
|
||||
}
|
||||
if let Some(g) = flag("grid") {
|
||||
self.ui.show_grid = g;
|
||||
}
|
||||
if let Some(p) = flag("perspective") {
|
||||
self.ui.perspective = p;
|
||||
}
|
||||
if let Some(b) = flag("pick_bodies") {
|
||||
self.ui.pick_bodies = b;
|
||||
}
|
||||
if let Some(a) = flag("auto_project") {
|
||||
self.session.auto_project = a;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_control(mut self, rx: Receiver<ControlRequest>) -> Self {
|
||||
|
||||
@@ -159,6 +159,10 @@ fn file_menu(app: &mut SolveApp, ctx: &egui::Context) {
|
||||
close = true;
|
||||
}
|
||||
ui.separator();
|
||||
if item(ui, "Preferences…", "") {
|
||||
app.dialog = Some(crate::dialogs::Dialog::preferences());
|
||||
close = true;
|
||||
}
|
||||
if item(ui, "Quit", "") {
|
||||
app.quit_requested = true;
|
||||
close = true;
|
||||
|
||||
Reference in new issue
Block a user