Skip to content

Commit 2c673d0

Browse files
Ripper53Keavon
andauthored
Add the unit, display_decimal_places, and step parameter widget macro attributes (#2706)
* UI working for spacing enum. * Implementations. * UI working for spacing enum. * Undo all changes. * unit, display_decimal_places, and step macro implementation. * Fixing tests. --------- Co-authored-by: Keavon Chambers <keavon@keavon.com>
1 parent d080c05 commit 2c673d0

File tree

4 files changed

+132
-10
lines changed

4 files changed

+132
-10
lines changed

editor/src/messages/portfolio/document/node_graph/node_properties.rs

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ pub(crate) fn property_from_type(
121121
index: usize,
122122
ty: &Type,
123123
number_options: (Option<f64>, Option<f64>, Option<(f64, f64)>),
124+
unit: Option<&str>,
125+
display_decimal_places: Option<u32>,
126+
step: Option<f64>,
124127
context: &mut NodePropertiesContext,
125128
) -> Result<Vec<LayoutGroup>, Vec<LayoutGroup>> {
126129
let Some(network) = context.network_interface.nested_network(context.selection_network_path) else {
@@ -142,6 +145,15 @@ pub(crate) fn property_from_type(
142145
number_max = Some(range_end);
143146
number_input = number_input.mode_range().min(range_start).max(range_end);
144147
}
148+
if let Some(unit) = unit {
149+
number_input = number_input.unit(unit);
150+
}
151+
if let Some(display_decimal_places) = display_decimal_places {
152+
number_input = number_input.display_decimal_places(display_decimal_places);
153+
}
154+
if let Some(step) = step {
155+
number_input = number_input.step(step);
156+
}
145157

146158
let min = |x: f64| number_min.unwrap_or(x);
147159
let max = |x: f64| number_max.unwrap_or(x);
@@ -155,15 +167,15 @@ pub(crate) fn property_from_type(
155167
// Aliased types (ambiguous values)
156168
Some("Percentage") => number_widget(default_info, number_input.percentage().min(min(0.)).max(max(100.))).into(),
157169
Some("SignedPercentage") => number_widget(default_info, number_input.percentage().min(min(-100.)).max(max(100.))).into(),
158-
Some("Angle") => number_widget(default_info, number_input.mode_range().min(min(-180.)).max(max(180.)).unit("°")).into(),
159-
Some("Multiplier") => number_widget(default_info, number_input.unit("x")).into(),
160-
Some("PixelLength") => number_widget(default_info, number_input.min(min(0.)).unit(" px")).into(),
170+
Some("Angle") => number_widget(default_info, number_input.mode_range().min(min(-180.)).max(max(180.)).unit(unit.unwrap_or("°"))).into(),
171+
Some("Multiplier") => number_widget(default_info, number_input.unit(unit.unwrap_or("x"))).into(),
172+
Some("PixelLength") => number_widget(default_info, number_input.min(min(0.)).unit(unit.unwrap_or(" px"))).into(),
161173
Some("Length") => number_widget(default_info, number_input.min(min(0.))).into(),
162174
Some("Fraction") => number_widget(default_info, number_input.mode_range().min(min(0.)).max(max(1.))).into(),
163175
Some("IntegerCount") => number_widget(default_info, number_input.int().min(min(1.))).into(),
164176
Some("SeedValue") => number_widget(default_info, number_input.int().min(min(0.))).into(),
165-
Some("Resolution") => coordinate_widget(default_info, "W", "H", " px", Some(64.)),
166-
Some("PixelSize") => coordinate_widget(default_info, "X", "Y", " px", None),
177+
Some("Resolution") => coordinate_widget(default_info, "W", "H", unit.unwrap_or(" px"), Some(64.)),
178+
Some("PixelSize") => coordinate_widget(default_info, "X", "Y", unit.unwrap_or(" px"), None),
167179

168180
// For all other types, use TypeId-based matching
169181
_ => {
@@ -249,8 +261,8 @@ pub(crate) fn property_from_type(
249261
}
250262
}
251263
Type::Generic(_) => vec![TextLabel::new("Generic type (not supported)").widget_holder()].into(),
252-
Type::Fn(_, out) => return property_from_type(node_id, index, out, number_options, context),
253-
Type::Future(out) => return property_from_type(node_id, index, out, number_options, context),
264+
Type::Fn(_, out) => return property_from_type(node_id, index, out, number_options, unit, display_decimal_places, step, context),
265+
Type::Future(out) => return property_from_type(node_id, index, out, number_options, unit, display_decimal_places, step, context),
254266
};
255267

256268
extra_widgets.push(widgets);
@@ -1395,6 +1407,9 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
13951407
};
13961408

13971409
let mut number_options = (None, None, None);
1410+
let mut display_decimal_places = None;
1411+
let mut step = None;
1412+
let mut unit_suffix = None;
13981413
let input_type = match implementation {
13991414
DocumentNodeImplementation::ProtoNode(proto_node_identifier) => 'early_return: {
14001415
if let Some(field) = graphene_core::registry::NODE_METADATA
@@ -1404,6 +1419,9 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
14041419
.and_then(|metadata| metadata.fields.get(input_index))
14051420
{
14061421
number_options = (field.number_min, field.number_max, field.number_mode_range);
1422+
display_decimal_places = field.number_display_decimal_places;
1423+
unit_suffix = field.unit;
1424+
step = field.number_step;
14071425
if let Some(ref default) = field.default_type {
14081426
break 'early_return default.clone();
14091427
}
@@ -1417,7 +1435,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
14171435
let mut input_types = implementations
14181436
.keys()
14191437
.filter_map(|item| item.inputs.get(input_index))
1420-
.filter(|ty| property_from_type(node_id, input_index, ty, number_options, context).is_ok())
1438+
.filter(|ty| property_from_type(node_id, input_index, ty, number_options, unit_suffix, display_decimal_places, step, context).is_ok())
14211439
.collect::<Vec<_>>();
14221440
input_types.sort_by_key(|ty| ty.type_name());
14231441
let input_type = input_types.first().cloned();
@@ -1431,7 +1449,7 @@ pub(crate) fn generate_node_properties(node_id: NodeId, context: &mut NodeProper
14311449
_ => context.network_interface.input_type(&InputConnector::node(node_id, input_index), context.selection_network_path).0,
14321450
};
14331451

1434-
property_from_type(node_id, input_index, &input_type, number_options, context).unwrap_or_else(|value| value)
1452+
property_from_type(node_id, input_index, &input_type, number_options, unit_suffix, display_decimal_places, step, context).unwrap_or_else(|value| value)
14351453
});
14361454

14371455
layout.extend(row);

node-graph/gcore/src/registry.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ pub struct FieldMetadata {
5454
pub number_min: Option<f64>,
5555
pub number_max: Option<f64>,
5656
pub number_mode_range: Option<(f64, f64)>,
57+
pub number_display_decimal_places: Option<u32>,
58+
pub number_step: Option<f64>,
59+
pub unit: Option<&'static str>,
5760
}
5861

5962
pub trait ChoiceTypeStatic: Sized + Copy + crate::vector::misc::AsU32 + Send + Sync {

node-graph/node-macro/src/codegen.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,41 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
163163
_ => quote!(None),
164164
})
165165
.collect();
166+
let number_display_decimal_places: Vec<_> = fields
167+
.iter()
168+
.map(|field| match field {
169+
ParsedField::Regular {
170+
number_display_decimal_places: Some(decimal_places),
171+
..
172+
}
173+
| ParsedField::Node {
174+
number_display_decimal_places: Some(decimal_places),
175+
..
176+
} => {
177+
quote!(Some(#decimal_places))
178+
}
179+
_ => quote!(None),
180+
})
181+
.collect();
182+
let number_step: Vec<_> = fields
183+
.iter()
184+
.map(|field| match field {
185+
ParsedField::Regular { number_step: Some(step), .. } | ParsedField::Node { number_step: Some(step), .. } => {
186+
quote!(Some(#step))
187+
}
188+
_ => quote!(None),
189+
})
190+
.collect();
191+
192+
let unit_suffix: Vec<_> = fields
193+
.iter()
194+
.map(|field| match field {
195+
ParsedField::Regular { unit: Some(unit), .. } | ParsedField::Node { unit: Some(unit), .. } => {
196+
quote!(Some(#unit))
197+
}
198+
_ => quote!(None),
199+
})
200+
.collect();
166201

167202
let exposed: Vec<_> = fields
168203
.iter()
@@ -375,6 +410,9 @@ pub(crate) fn generate_node_code(parsed: &ParsedNodeFn) -> syn::Result<TokenStre
375410
number_min: #number_min_values,
376411
number_max: #number_max_values,
377412
number_mode_range: #number_mode_range_values,
413+
number_display_decimal_places: #number_display_decimal_places,
414+
number_step: #number_step,
415+
unit: #unit_suffix,
378416
},
379417
)*
380418
],

node-graph/node-macro/src/parsing.rs

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use syn::punctuated::Punctuated;
77
use syn::spanned::Spanned;
88
use syn::token::{Comma, RArrow};
99
use syn::{
10-
AttrStyle, Attribute, Error, Expr, ExprTuple, FnArg, GenericParam, Ident, ItemFn, Lit, LitFloat, LitStr, Meta, Pat, PatIdent, PatType, Path, ReturnType, Type, TypeParam, WhereClause, parse_quote,
10+
AttrStyle, Attribute, Error, Expr, ExprTuple, FnArg, GenericParam, Ident, ItemFn, Lit, LitFloat, LitInt, LitStr, Meta, Pat, PatIdent, PatType, Path, ReturnType, Type, TypeParam, WhereClause,
11+
parse_quote,
1112
};
1213

1314
use crate::codegen::generate_node_code;
@@ -110,7 +111,10 @@ pub(crate) enum ParsedField {
110111
number_hard_min: Option<LitFloat>,
111112
number_hard_max: Option<LitFloat>,
112113
number_mode_range: Option<ExprTuple>,
114+
number_display_decimal_places: Option<LitInt>,
115+
number_step: Option<LitFloat>,
113116
implementations: Punctuated<Type, Comma>,
117+
unit: Option<LitStr>,
114118
},
115119
Node {
116120
pat_ident: PatIdent,
@@ -119,7 +123,10 @@ pub(crate) enum ParsedField {
119123
widget_override: ParsedWidgetOverride,
120124
input_type: Type,
121125
output_type: Type,
126+
number_display_decimal_places: Option<LitInt>,
127+
number_step: Option<LitFloat>,
122128
implementations: Punctuated<Implementation, Comma>,
129+
unit: Option<LitStr>,
123130
},
124131
}
125132
#[derive(Debug)]
@@ -466,6 +473,35 @@ fn parse_field(pat_ident: PatIdent, ty: Type, attrs: &[Attribute]) -> syn::Resul
466473
}
467474
}
468475

476+
let unit = extract_attribute(attrs, "unit")
477+
.map(|attr| attr.parse_args::<LitStr>().map_err(|e| Error::new_spanned(attr, format!("Expected a unit type as string"))))
478+
.transpose()?;
479+
480+
let number_display_decimal_places = extract_attribute(attrs, "display_decimal_places")
481+
.map(|attr| {
482+
attr.parse_args::<LitInt>().map_err(|e| {
483+
Error::new_spanned(
484+
attr,
485+
format!("Invalid `integer` for number of decimals for argument '{}': {}\nUSAGE EXAMPLE: #[display_decimal_places(2)]", ident, e),
486+
)
487+
})
488+
})
489+
.transpose()?
490+
.map(|f| {
491+
if let Err(e) = f.base10_parse::<u32>() {
492+
Err(Error::new_spanned(f, format!("Expected a `u32` for `display_decimal_places` for '{}': {}", ident, e)))
493+
} else {
494+
Ok(f)
495+
}
496+
})
497+
.transpose()?;
498+
let number_step = extract_attribute(attrs, "step")
499+
.map(|attr| {
500+
attr.parse_args::<LitFloat>()
501+
.map_err(|e| Error::new_spanned(attr, format!("Invalid `step` for argument '{}': {}\nUSAGE EXAMPLE: #[step(2.)]", ident, e)))
502+
})
503+
.transpose()?;
504+
469505
let (is_node, node_input_type, node_output_type) = parse_node_type(&ty);
470506
let description = attrs
471507
.iter()
@@ -502,7 +538,10 @@ fn parse_field(pat_ident: PatIdent, ty: Type, attrs: &[Attribute]) -> syn::Resul
502538
widget_override,
503539
input_type,
504540
output_type,
541+
number_display_decimal_places,
542+
number_step,
505543
implementations,
544+
unit,
506545
})
507546
} else {
508547
let implementations = extract_attribute(attrs, "implementations")
@@ -520,9 +559,12 @@ fn parse_field(pat_ident: PatIdent, ty: Type, attrs: &[Attribute]) -> syn::Resul
520559
number_hard_min,
521560
number_hard_max,
522561
number_mode_range,
562+
number_display_decimal_places,
563+
number_step,
523564
ty,
524565
value_source,
525566
implementations,
567+
unit,
526568
})
527569
}
528570
}
@@ -738,7 +780,10 @@ mod tests {
738780
number_hard_min: None,
739781
number_hard_max: None,
740782
number_mode_range: None,
783+
number_display_decimal_places: None,
784+
number_step: None,
741785
implementations: Punctuated::new(),
786+
unit: None,
742787
}],
743788
body: TokenStream2::new(),
744789
crate_name: FoundCrate::Itself,
@@ -790,7 +835,10 @@ mod tests {
790835
widget_override: ParsedWidgetOverride::None,
791836
input_type: parse_quote!(Footprint),
792837
output_type: parse_quote!(T),
838+
number_display_decimal_places: None,
839+
number_step: None,
793840
implementations: Punctuated::new(),
841+
unit: None,
794842
},
795843
ParsedField::Regular {
796844
pat_ident: pat_ident("translate"),
@@ -805,7 +853,10 @@ mod tests {
805853
number_hard_min: None,
806854
number_hard_max: None,
807855
number_mode_range: None,
856+
number_display_decimal_places: None,
857+
number_step: None,
808858
implementations: Punctuated::new(),
859+
unit: None,
809860
},
810861
],
811862
body: TokenStream2::new(),
@@ -860,7 +911,10 @@ mod tests {
860911
number_hard_min: None,
861912
number_hard_max: None,
862913
number_mode_range: None,
914+
number_display_decimal_places: None,
915+
number_step: None,
863916
implementations: Punctuated::new(),
917+
unit: None,
864918
}],
865919
body: TokenStream2::new(),
866920
crate_name: FoundCrate::Itself,
@@ -913,12 +967,15 @@ mod tests {
913967
number_hard_min: None,
914968
number_hard_max: None,
915969
number_mode_range: None,
970+
number_display_decimal_places: None,
971+
number_step: None,
916972
implementations: {
917973
let mut p = Punctuated::new();
918974
p.push(parse_quote!(f32));
919975
p.push(parse_quote!(f64));
920976
p
921977
},
978+
unit: None,
922979
}],
923980
body: TokenStream2::new(),
924981
crate_name: FoundCrate::Itself,
@@ -978,7 +1035,10 @@ mod tests {
9781035
number_hard_min: None,
9791036
number_hard_max: None,
9801037
number_mode_range: Some(parse_quote!((0., 100.))),
1038+
number_display_decimal_places: None,
1039+
number_step: None,
9811040
implementations: Punctuated::new(),
1041+
unit: None,
9821042
}],
9831043
body: TokenStream2::new(),
9841044
crate_name: FoundCrate::Itself,
@@ -1031,7 +1091,10 @@ mod tests {
10311091
number_hard_min: None,
10321092
number_hard_max: None,
10331093
number_mode_range: None,
1094+
number_display_decimal_places: None,
1095+
number_step: None,
10341096
implementations: Punctuated::new(),
1097+
unit: None,
10351098
}],
10361099
body: TokenStream2::new(),
10371100
crate_name: FoundCrate::Itself,

0 commit comments

Comments
 (0)