macro_rules! widget_set { ( $(#[$skip:meta])* $($invalid:ident)::+ = $($tt:tt)* ) => { ... }; ( $(#[$skip:meta])* when = $($invalid:tt)* ) => { ... }; ( $wgt_mut:ident; $($tt:tt)* ) => { ... }; ( $wgt_borrow_mut:expr; $($tt:tt)* ) => { ... }; }
Expand description
Sets properties and when condition on a widget builder.
§Examples
let mut wgt = Wgt::widget_new();
if flag {
widget_set! {
&mut wgt;
enabled = false;
}
}
widget_set! {
&mut wgt;
id = "wgt";
}
let wgt = wgt.widget_build();
In the example above the widget will always build with custom id
, but only will set enabled = false
when flag
is true
.
Note that properties are designed to have a default neutral value that behaves as if unset, in the example case you could more easily write:
let wgt = Wgt! {
enabled = !flag;
id = "wgt";
};
You should use this macro only in contexts where a widget will be build in steps, or in very hot code paths where a widget has many properties and only some will be non-default per instance.
§Property Assign
Properties can be assigned using the property = value;
syntax, this expands to a call to the property method, either
directly implemented on the widget or from a trait.
id = "name";
background_color = colors::BLUE;
The example above is equivalent to:
wgt.id("name");
wgt.background_color(colors::BLUE);
Note that id
is an intrinsic property inherited from WidgetBase
, but background_color
is an extension property declared
by a property
function. Extension properties require &mut self
access to the widget, intrinsic properties only require &self
,
this is done so that IDEs that use a different style for mutable methods highlight the properties that are not intrinsic to the widget.
§Path Assign
A full or partial path can be used to specify exactly what extension property will be set:
self::background_color = colors::BLUE;
In the example above self::background_color
specify that an extension property that is imported in the self
module must be set,
even if the widget gets an intrinsic background_color
property the extension property will still be used.
The example above is equivalent to:
self::background_color::background_color(&mut wgt, colors::BLUE);
§Named Assign
Properties can have multiple parameters, multiple parameters can be set using the struct init syntax:
border = {
widths: 1,
sides: colors::RED,
};
Note that just like in struct init the parameters don’t need to be in order:
border = {
sides: colors::RED,
widths: 1,
};
Internally each property method has auxiliary methods that validate the member names and construct the property using sorted params, therefore accepting any parameter order. Note each parameter is evaluated in the order they appear, even if they are assigned in a different order after.
let mut eval_order = vec![];
border = {
sides: {
eval_order.push("sides");
colors::RED
},
widths: {
eval_order.push("widths");
1
},
};
assert_eq!(eval_order, vec!["sides", "widths"]);
§Unnamed Assign Multiple
Properties with multiple parameters don’t need to be set using the named syntax:
border = 1, colors::RED;
The example above is equivalent to:
wgt.border(1, colors::RED);
§Shorthand Assign
Is a variable with the same name as a property is in context the = name
can be omitted:
let id = "name";
let background_color = colors::BLUE;
let widths = 1;
let wgt = zng_app::widget::base::WidgetBase! {
id;
self::background_color;
border = {
widths,
sides: colors::RED,
};
};
Note that the shorthand syntax also works for path properties and parameter names.
The above is equivalent to:
let id = "name";
let background_color = colors::BLUE;
let widths = 1;
let wgt = zng_app::widget::base::WidgetBase! {
id = id;
self::background_color = background_color;
border = {
widths: widths,
sides: colors::RED,
};
};
§Property Unset
All properties can be assigned to an special value unset!
, that removes a property, when the widget is build the
unset property will not be instantiated:
border = unset!;
The example above is equivalent to:
wgt.unset_border();
Each property method generates an auxiliary unset_property
method, the unset is registered in the widget builder using the current
importance, in widget_intrinsic
they only unset already inherited default assigns, in instances it unsets all inherited or
previous assigns, see WidgetBuilder::push_unset
for more details.
§Generic Properties
Generic properties need a turbofish annotation on assign:
value::<f32> = 1.0;
§When
Conditional property assigns can be setup using when
blocks. A when
block has a bool
expression and property assigns,
when the expression is true
each property has the assigned value, unless it is overridden by a later when
block.
background_color = colors::RED;
when *#is_pressed {
background_color = colors::GREEN;
}
§When Condition
The when
block defines a condition expression, in the example above this is *#is_pressed
. The expression can be any Rust expression
that results in a bool
value, you can reference properties in it using the #
token followed by the property name or path and you
can reference variables in it using the #{var}
syntax. If a property or var is referenced the when
block is dynamic, updating all
assigned properties when the expression result changes.
§Property Reference
The most common when
expression reference is a property, in the example above the is_pressed
property is instantiated for the widget
and it controls when the background is set to green. Note that a reference to the value is inserted in the expression
so an extra deref *
is required. A property can also be referenced with a path, #properties::is_pressed
also works.
The syntax seen so far is actually a shorthand way to reference the first input of a property, the full syntax is #is_pressed.0
or
#is_pressed.state
. You can use the extended syntax to reference inputs of properties with more than one input, the input can be
reference by tuple-style index or by name. Note that if the value it self is a tuple or struct
you need to use the extended syntax
to reference a member of the value, #foo.0.0
or #foo.0.name
. Methods have no ambiguity, #foo.name()
is the same as #foo.0.name()
.
Not all properties can be referenced in when
conditions, only inputs of type impl IntoVar<T>
and impl IntoValue<T>
are
allowed, attempting to reference a different kind of input generates a compile error.
§Variable Reference
Other variable can also be referenced, context variables or any locally declared variable can be referenced. Like with properties
the variable value is inserted in the expression as a reference so you may need to deref in case the var is a simple Copy
value.
context_var! {
pub static FOO_VAR: Vec<&'static str> = vec![];
pub static BAR_VAR: bool = false;
}
background_color = colors::RED;
when !*#{BAR_VAR} && #{FOO_VAR}.contains(&"green") {
background_color = colors::GREEN;
}
§When Assigns
Inside the when
block a list of property assigns is expected, most properties can be assigned, but impl IntoValue<T>
properties cannot,
you also cannot unset!
in when assigns, a compile time error happens if the property cannot be assigned.
On instantiation a single instance of the property will be generated, the parameters will track the when expression state and update
to the value assigned when it is true
. When no block is true
the value assigned to the property outside when
blocks is used, or the property default value. When more then one block is true
the last one sets the value.
§Default Values
A when assign can be defined by a property without setting a default value, during instantiation if the property declaration has a default value it is used, or if the property was later assigned a value it is used as default, if it is not possible to generate a default value the property is not instantiated and the when assign is not used.
The same apply for properties referenced in the condition expression, note that all is_state
properties have a default value so
it is more rare that a default value is not available. If a condition property cannot be generated the entire when block is ignored.