forked from 1b-agent/opencadstudio-upstream
Wire up the Properties panel's Parameters section
PropValue::ParamRow/ParamAddRow, the PropParamInput/Commit/Delete/AddNew messages, their command_driver handlers (on_prop_param_commit/_delete/ _add_new, resolve_named_parameter_edit), and their render functions all already existed — but nothing ever constructed the "Parameters" section that would route data to them, so the whole inline add/rename/redefine/ delete workflow was unreachable from the UI. Adds that missing section to the no-selection page in refresh_properties, alongside a new PropValue::ParamsVisibilityToggle header row (mirrors the existing show_constraint_values setting) for toggling whether constraint pills show their driven value/parameter name. Users can now manage named parameters inline in the Properties panel (live resolved values, add/rename/redefine/delete) as an alternative to the separate PARAMETERS modal, which continues to work unchanged. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
753df67ee4
commit
ed2e0fbc4a
5 files changed
+154
-2
No files matched your search
@@ -7676,6 +7676,74 @@ mod sketch_constraint_undo_tests {
|
||||
}
|
||||
}
|
||||
|
||||
/// The `PropParamInput`/`PropParamCommit`/`PropParamDelete`/`PropParamAddNew`
|
||||
/// handlers, `PropValue::ParamRow`/`ParamAddRow`/`ParamsVisibilityToggle`,
|
||||
/// and their render functions already existed — nothing built the
|
||||
/// Properties panel's "Parameters" section that would ever construct or
|
||||
/// route to them, so the whole inline add/rename/redefine/delete workflow
|
||||
/// was unreachable. These tests exercise it end to end now that the section
|
||||
/// is wired up in `refresh_properties`.
|
||||
#[cfg(test)]
|
||||
mod properties_panel_parameters_tests {
|
||||
use super::*;
|
||||
|
||||
/// Finds a `PropValue::ParamRow`'s name across every rendered Properties
|
||||
/// panel section — the panel is a rebuilt-on-demand snapshot
|
||||
/// (`refresh_properties`), not a live view, so this only sees whatever
|
||||
/// it was last rebuilt with.
|
||||
fn param_row_names(app: &OpenCADStudio) -> Vec<String> {
|
||||
app.tabs[app.active_tab]
|
||||
.properties
|
||||
.sections
|
||||
.iter()
|
||||
.flat_map(|s| &s.props)
|
||||
.filter_map(|p| match &p.value {
|
||||
crate::scene::model::object::PropValue::ParamRow { name, .. } => Some(name.clone()),
|
||||
_ => None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_selection_page_shows_every_named_parameter() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let _ = app.automation_op(r#"{"op":"new"}"#);
|
||||
let _ = app.tabs[app.active_tab].scene.named_parameters_mut().set("hole_dia", "12");
|
||||
app.refresh_properties();
|
||||
assert_eq!(param_row_names(&app), vec!["hole_dia".to_string()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_rename_redefine_and_delete_a_parameter_through_the_panel() {
|
||||
use crate::ui::window::named_parameters::ParamField;
|
||||
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
let _ = app.automation_op(r#"{"op":"new"}"#);
|
||||
|
||||
// Add: the first row is auto-named "param1", and the panel reflects
|
||||
// it immediately (on_prop_param_add_new refreshes internally).
|
||||
let _ = app.update(Message::PropParamAddNew);
|
||||
assert_eq!(param_row_names(&app), vec!["param1".to_string()]);
|
||||
let index = app.tabs[app.active_tab].scene.named_parameters().iter().position(|p| p.name == "param1").unwrap();
|
||||
|
||||
// Rename param1 -> hole_dia (commit-on-submit, not per keystroke).
|
||||
let _ = app.update(Message::PropParamInput { index, field: ParamField::Name, value: "hole_dia".to_string() });
|
||||
assert_eq!(param_row_names(&app), vec!["param1".to_string()], "typing alone must not commit");
|
||||
let _ = app.update(Message::PropParamCommit { index, field: ParamField::Name });
|
||||
assert_eq!(param_row_names(&app), vec!["hole_dia".to_string()]);
|
||||
|
||||
// Redefine its formula.
|
||||
let _ = app.update(Message::PropParamInput { index, field: ParamField::Formula, value: "8".to_string() });
|
||||
let _ = app.update(Message::PropParamCommit { index, field: ParamField::Formula });
|
||||
assert_eq!(app.tabs[app.active_tab].scene.named_parameters().resolve("hole_dia"), Ok(8.0));
|
||||
|
||||
// Delete: gone from both the table and the panel.
|
||||
let _ = app.update(Message::PropParamDelete(index));
|
||||
assert!(!app.tabs[app.active_tab].scene.named_parameters().contains("hole_dia"));
|
||||
assert!(param_row_names(&app).is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, not(target_arch = "wasm32")))]
|
||||
mod delobj_tests {
|
||||
use super::*;
|
||||
|
||||
@@ -471,6 +471,42 @@ impl OpenCADStudio {
|
||||
},
|
||||
],
|
||||
},
|
||||
// Document-wide named-parameter table embedded here
|
||||
// instead of only the separate PARAMETERS modal —
|
||||
// see `PropValue::ParamRow`'s doc comment. Belongs
|
||||
// on the no-selection (drawing-level) page, not a
|
||||
// per-entity one: a parameter isn't owned by any
|
||||
// one entity.
|
||||
PropSection {
|
||||
title: t!("Parameters").into_owned(),
|
||||
props: {
|
||||
let mut props: Vec<Property> = vec![Property {
|
||||
label: String::new(),
|
||||
field: "show_named_parameters",
|
||||
value: PropValue::ParamsVisibilityToggle(self.show_constraint_values),
|
||||
}];
|
||||
props.extend(scene
|
||||
.named_parameters()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, p)| Property {
|
||||
label: String::new(),
|
||||
field: "named_parameter",
|
||||
value: PropValue::ParamRow {
|
||||
index,
|
||||
name: p.name.clone(),
|
||||
formula: p.source.clone(),
|
||||
resolved: scene.named_parameters().resolve(&p.name).map_err(|e| e.to_string()),
|
||||
},
|
||||
}));
|
||||
props.push(Property {
|
||||
label: String::new(),
|
||||
field: "named_parameter_add",
|
||||
value: PropValue::ParamAddRow,
|
||||
});
|
||||
props
|
||||
},
|
||||
},
|
||||
];
|
||||
ui::PropertiesPanel {
|
||||
title: t!("No selection").into_owned(),
|
||||
@@ -3231,6 +3267,9 @@ fn make_sections_read_only(
|
||||
PropValue::EntityLink { handles, .. } => format!("{} entity link(s)", handles.len()),
|
||||
PropValue::ParamRow { name, formula, .. } => format!("{name} = {formula}"),
|
||||
PropValue::ParamAddRow => String::new(),
|
||||
PropValue::ParamsVisibilityToggle(value) => {
|
||||
if *value { t!("Shown") } else { t!("Hidden") }.into_owned()
|
||||
}
|
||||
};
|
||||
property.field = "locked_read_only";
|
||||
property.value = PropValue::ReadOnly(text);
|
||||
|
||||
@@ -80,6 +80,11 @@ pub enum PropValue {
|
||||
},
|
||||
/// The trailing "+ Add parameter" row in the Parameters section.
|
||||
ParamAddRow,
|
||||
/// The Parameters section's leading header row (no-selection page only):
|
||||
/// a global on/off toggle for whether any constraint pill in the
|
||||
/// viewport shows its driven value/parameter-name text. Mirrors
|
||||
/// `App::show_constraint_values`.
|
||||
ParamsVisibilityToggle(bool),
|
||||
}
|
||||
|
||||
/// A single property row in the Properties panel.
|
||||
|
||||
@@ -750,7 +750,8 @@ impl Scene {
|
||||
| PropValue::FieldLwVaries { .. }
|
||||
| PropValue::EntityLink { .. }
|
||||
| PropValue::ParamRow { .. }
|
||||
| PropValue::ParamAddRow => continue,
|
||||
| PropValue::ParamAddRow
|
||||
| PropValue::ParamsVisibilityToggle(_) => continue,
|
||||
}
|
||||
};
|
||||
out.push(choice(prop.field, prop.label, editor));
|
||||
@@ -887,7 +888,8 @@ impl Scene {
|
||||
| PropValue::FieldLwVaries { .. }
|
||||
| PropValue::EntityLink { .. }
|
||||
| PropValue::ParamRow { .. }
|
||||
| PropValue::ParamAddRow => return None,
|
||||
| PropValue::ParamAddRow
|
||||
| PropValue::ParamsVisibilityToggle(_) => return None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -872,6 +872,7 @@ impl PropertiesPanel {
|
||||
self.render_param_row(*index, name, formula, resolved)
|
||||
}
|
||||
PropValue::ParamAddRow => render_param_add_row(),
|
||||
PropValue::ParamsVisibilityToggle(value) => render_params_visibility_toggle_row(*value),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1973,6 +1974,43 @@ fn render_param_add_row<'a>() -> Element<'a, Message> {
|
||||
container(btn).width(Length::Fill).into()
|
||||
}
|
||||
|
||||
// ── Parameters section: leading global visibility toggle ───────────────────
|
||||
|
||||
/// The Parameters section's leading header row (no-selection page): a
|
||||
/// global on/off toggle for whether any constraint pill in the viewport
|
||||
/// shows its driven value/parameter-name text — lives next to the
|
||||
/// named-parameter table it governs.
|
||||
fn render_params_visibility_toggle_row<'a>(value: bool) -> Element<'a, Message> {
|
||||
let btn_label = if value { t!("Shown").into_owned() } else { t!("Hidden").into_owned() };
|
||||
let btn = button(
|
||||
row![
|
||||
crate::ui::icons::semantic(crate::ui::icons::layer_visible(value), 13.0),
|
||||
text(btn_label).size(FONT_SZ),
|
||||
]
|
||||
.spacing(6)
|
||||
.align_y(iced::Center),
|
||||
)
|
||||
.on_press(Message::ShowConstraintValuesChanged(!value))
|
||||
.style(move |theme: &Theme, status| {
|
||||
let palette = theme.palette();
|
||||
let pair = match status {
|
||||
button::Status::Hovered | button::Status::Pressed => palette.background.weak,
|
||||
_ => palette.background.base,
|
||||
};
|
||||
button::Style {
|
||||
background: Some(Background::Color(pair.color)),
|
||||
border: Border { color: palette.background.neutral.color, width: 1.0, radius: 2.0.into() },
|
||||
text_color: pair.text,
|
||||
..Default::default()
|
||||
}
|
||||
})
|
||||
.padding([4, 8])
|
||||
.width(Length::Fill);
|
||||
container(row![text(t!("Show values").into_owned()).size(FONT_SZ).width(Length::Fill), btn].align_y(iced::Center))
|
||||
.width(Length::Fill)
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Build a label | widget property row.
|
||||
fn prop_row_widget<'a>(label: &'a str, widget: Element<'a, Message>) -> Element<'a, Message> {
|
||||
prop_row_with_active(label, widget, false)
|
||||
|
||||
Reference in new issue
Block a user