graphic design tool
git clone https://git.lucas.co/cce-designer.git
feat: the row menu reads out the parameter's raw type string
`Type:` under `Control:` — the type string exactly as the template or
the file wrote it (`slider:-2:2`, `choice:UV,Icosphere,Cube`, `string`
for an absent one), where Control is the kind that string parses to.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CLAUDE.md | 4 +++-
src/app.rs | 6 +++++-
src/main.rs | 26 +++++++++++++-------------
3 files changed, 21 insertions(+), 15 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index 1606cf1..f5860b3 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -721,7 +721,9 @@ parameter's name, what a `ch()` path spells, with `Label:` under it only
when the template gives one (the pane shows the name otherwise, and a
Label row repeating it would claim there is one); `Control:` is the
kind the template gave it (`ParamKind::name` — slider, float3,
-attribute…) and `Value:` what its text holds right now
+attribute…), `Type:` the raw type string that kind parsed from
+(`slider:-2:2`, `choice:UV,Icosphere,Cube`, `string` for an absent one)
+exactly as the template or the file wrote it, and `Value:` what its text holds right now
(`ParamDef::value_type` — float, integer, vec3, boolean, enum, string,
expression, or invalid with the reason; a programmer's terms, by
request), `Expression:` the row's expression FLAG as `true` / `false`
diff --git a/src/app.rs b/src/app.rs
index cf16271..eecf20b 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -4466,7 +4466,10 @@ impl State {
/// expression), left out for a parameter no template names. A control
/// with a range adds `Range: lo..hi`, with its step when one is set
/// (`ParamDef::range`, the pane's own numbers); a choice adds
- /// `Options: a, b, c`. Ahead of `Range:`, `Min:` / `Max:` / `Step:`
+ /// `Options: a, b, c`. `Type:` is the raw type STRING under
+ /// `Control:` — `slider:-2:2`, `choice:UV,Icosphere,Cube`, or `string`
+ /// for an absent one — exactly as the template or the file wrote it,
+ /// where Control is the kind that string parses to. Ahead of `Range:`, `Min:` / `Max:` / `Step:`
/// are what the template DECLARES (`ParamDef::declared_range`, an
/// inline `slider:-2:2` included), `none` where it declares nothing —
/// so Range is the clamp the pane applies and the three above it say
@@ -4493,6 +4496,7 @@ impl State {
options.push(format!("Label: {label}"));
}
options.push(format!("Control: {control}"));
+ options.push(format!("Type: {}", param.map(|p| p.ty()).unwrap_or("?")));
options.push(format!("Value: {value}"));
options.push(format!("Expression: {is_expr}"));
if let Some(d) = self.template_default(dir, child, pname) {
diff --git a/src/main.rs b/src/main.rs
index e083ccb..6d71737 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -7756,7 +7756,7 @@ mod tests {
assert!(!state.viewport_menu_open());
assert_eq!(
state.param_menu_actions,
- vec![ParamMenuAction::Info; 9].into_iter().chain([ParamMenuAction::Separator, ParamMenuAction::CopyParameter, ParamMenuAction::Separator, ParamMenuAction::EditExpression]).collect::<Vec<_>>(),
+ vec![ParamMenuAction::Info; 10].into_iter().chain([ParamMenuAction::Separator, ParamMenuAction::CopyParameter, ParamMenuAction::Separator, ParamMenuAction::EditExpression]).collect::<Vec<_>>(),
"nothing copied yet, and the row holds a value"
);
// The header rows read the parameter out: its name, its control's
@@ -7767,15 +7767,15 @@ mod tests {
// row appears.
let shown = cce_ui::widget::context_menu::options();
assert_eq!(
- &shown[..10],
+ &shown[..11],
&[
- "Name: Radius".to_string(), "Control: slider".to_string(), "Value: float".to_string(), "Expression: false".to_string(),
- "Default: 0.5".to_string(), "Min: none".to_string(), "Max: none".to_string(), "Step: none".to_string(),
- "Range: 0..2".to_string(), "-".to_string(),
+ "Name: Radius".to_string(), "Control: slider".to_string(), "Type: slider".to_string(), "Value: float".to_string(),
+ "Expression: false".to_string(), "Default: 0.5".to_string(), "Min: none".to_string(), "Max: none".to_string(),
+ "Step: none".to_string(), "Range: 0..2".to_string(), "-".to_string(),
]
);
let (_, _, headers) = state.param_menu_rows(sphere, "Radius");
- assert_eq!(headers, 9);
+ assert_eq!(headers, 10);
// The headers are the rows before the separator; each one is
// looked up by its readout, not its position.
let headers_of = |state: &State, pname: &str| -> Vec<String> {
@@ -7786,7 +7786,7 @@ mod tests {
// An inline range, a spinbox's range and step, a choice's options,
// and a conditional row's condition.
let rows = headers_of(&state, "Center X");
- for want in ["Min: -2", "Max: 2", "Step: none", "Range: -2..2"] {
+ for want in ["Type: slider:-2:2", "Min: -2", "Max: 2", "Step: none", "Range: -2..2"] {
assert!(rows.contains(&want.to_string()), "{want} missing from {rows:?}");
}
let rows = headers_of(&state, "Rows");
@@ -7794,14 +7794,14 @@ mod tests {
assert!(rows.contains(&want.to_string()), "{want} missing from {rows:?}");
}
let rows = headers_of(&state, "Method");
- for want in ["Default: UV", "Options: UV, Icosphere, Cube"] {
+ for want in ["Type: choice:UV,Icosphere,Cube", "Default: UV", "Options: UV, Icosphere, Cube"] {
assert!(rows.contains(&want.to_string()), "{want} missing from {rows:?}");
}
- assert_eq!(headers_of(&state, "Color"), vec!["Name: Color", "Control: toggle", "Value: boolean", "Expression: false", "Default: true"], "a toggle has neither a range nor options");
+ assert_eq!(headers_of(&state, "Color"), vec!["Name: Color", "Control: toggle", "Type: toggle", "Value: boolean", "Expression: false", "Default: true"], "a toggle has neither a range nor options");
// A parameter no template names has no default row; one with a
// label shows it under the name.
state.apply_action(McpAction::AddParam { slot: sphere, name: "Extra".into(), param_type: "float".into(), default: "3".into() }, &mut redraw).unwrap();
- assert_eq!(headers_of(&state, "Extra"), vec!["Name: Extra", "Control: float", "Value: float", "Expression: false"]);
+ assert_eq!(headers_of(&state, "Extra"), vec!["Name: Extra", "Control: float", "Type: float", "Value: float", "Expression: false"]);
state.current_dir_mut().children[sphere].params.iter_mut().find(|p| p.name == "Extra").unwrap().label = "Extra Size".into();
assert_eq!(headers_of(&state, "Extra")[..2], ["Name: Extra".to_string(), "Label: Extra Size".to_string()]);
// Inside a subnet instance the SUBNET template's override is the
@@ -7856,11 +7856,11 @@ mod tests {
// the value is now an expression. Method is a choice holding an
// enum, Rows a spinbox holding an integer.
let (rows, _, _) = state.param_menu_rows(ball, "Radius");
- assert_eq!(&rows[1..4], &["Control: slider".to_string(), "Value: expression".to_string(), "Expression: true".to_string()]);
+ assert_eq!(&rows[1..5], &["Control: slider".to_string(), "Type: slider".to_string(), "Value: expression".to_string(), "Expression: true".to_string()]);
let (rows, _, _) = state.param_menu_rows(ball, "Method");
- assert_eq!(&rows[1..3], &["Control: choice".to_string(), "Value: enum".to_string()]);
+ assert_eq!((&rows[1], &rows[3]), (&"Control: choice".to_string(), &"Value: enum".to_string()));
let (rows, _, _) = state.param_menu_rows(ball, "Rows");
- assert_eq!(&rows[1..3], &["Control: spinbox".to_string(), "Value: integer".to_string()]);
+ assert_eq!((&rows[1], &rows[3]), (&"Control: spinbox".to_string(), &"Value: integer".to_string()));
// And a reference typed straight into a row (or scripted) becomes one.
state.apply_action(McpAction::SetParam { slot: ball, name: "Rows".into(), value: "chi(\"../sphere1/Rows\") * 2".into() }, &mut redraw).unwrap();