Expand description
Variables API.
The Var<T> struct represents an observable value. The IntoVar<T> trait is the primary property input
kind and the reason properties inputs are so versatile. Variables can be a simple value, a shared reference to a value or
a contextual value, some variables are also derived from others and update when the source variable update.
Properties and widgets can subscribe to a variable to update when the variable value changes, this features enables most of the dynamic UI behavior, from binding one widget to another to animation.
§Value
The simplest variable kind is const_var, it represents an unchanging value that is shared by cloning. All values of types
that implement VarValue automatically convert IntoVar<T> to const var, For this reason you don’t usually need
to write const_var(_) when setting properties.
use zng::prelude::*;
fn foo(size: impl IntoVar<layout::Size>) {
let size = size.into_var();
assert!(size.capabilities().is_const());
assert!(size.capabilities().is_always_read_only());
}
foo(layout::Size::new(10, 10));
foo((10, 10));
foo(10);The example above declares a const Var<Size> 3 times with equal value. The (10, 10) and 10 values are type conversions
implemented by the Size type. Type conversions are very easy to implement with the help of the impl_from_and_into_var! macro,
most of the types used by properties implement conversions that enable a form of shorthand syntax.
§Share & Modify
The var variable represents a shared value that can be modified.
The example below declares a button that grows taller every click. The variable is shared between the height property and the click handler. On click the height is increased, this schedules an update that applies the new value and notifies all subscribers.
use zng::prelude::*;
let height = var(2.em());
Button! {
child = Text!("Taller!");
on_click = hn!(height, |_| {
// clone `height` reference for the handler.
height.set(height.get() + 10); // request an update to a new value.
});
layout::align = layout::Align::CENTER;
layout::height; // set the height (shorthand, variable is same name as property)
}Note that variable updates don’t happen immediately, in the handler above the variable is still the previous value after the set call,
this is done so that all widgets in a single update react to the same value. The variable values is updated at the end of the current update.
use zng::prelude::*;
let number = var(0u8);
Button! {
child = Text!("Test");
on_click = async_hn!(number, |_| {
assert_eq!(number.get(), 0);
number.set(1);
assert_eq!(number.get(), 0);
task::yield_now().await;
assert_eq!(number.get(), 1);
});
}The example above demonstrates the delayed update of a variable.
If multiple widgets set the same variable on the same update only
the last value set will be used, widgets update in parallel by default so it is difficult to predict who is the last. The modify
method can be used register a closure that can modify the value, this closure will observe the partially updated value that may already be
modified by other widgets.
The example below demonstrates how the modify closure observes a value that was just set in the same update cycle.
use zng::prelude::*;
let foo = var(0u8);
Wgt! {
widget::on_init = async_hn!(foo, |_| {
foo.set(1);
assert_eq!(0, foo.get());
foo.modify(|m| {
assert_eq!(1, **m);
**m = 2;
});
assert_eq!(0, foo.get());
foo.wait_update().await;
assert_eq!(2, foo.get());
println!("test ok");
});
}§Mapping
Variables can be mapped to other value types, when the source variable updates the mapping closure re-evaluates and the mapped variable updates, all in the same update cycle, that is both variable will be flagged new at the same time. Mapping can also be bidirectional.
The example below demonstrates a button that updates an integer variable that is mapped to a text.
use zng::prelude::*;
let count = var(0u32);
Button! {
child = Text!(count.map(|i| match i {
0 => Txt::from("Click Me!"),
1 => Txt::from("Clicked 1 time!"),
n => formatx!("Clicked {n} times!"),
}));
on_click = hn!(|_| {
count.set(count.get() + 1);
});
}§Binding
Two existing variables can be bound, such that one variable update sets the other. The example below rewrites the mapping
demo to use a bind_map instead.
use zng::prelude::*;
let count = var(0u32);
let label = var(Txt::from("Click Me!"));
count
.bind_map(&label, |i| match i {
1 => Txt::from("Clicked 1 time!"),
n => formatx!("Clicked {n} times!"),
})
.perm();
Button! {
child = Text!(label);
on_click = hn!(|_| {
count.set(count.get() + 1);
});
}Note that unlike a map the initial value of the output variable is not updated, only subsequent ones. You can use
set_from to update the initial value too.
§Animating
Animation is implemented using variables, at the lowest level VARS.animate is used to register a closure to be
called every frame, the closure can set any number of variables, at a higher level the Var::ease and Var::chase methods
can be used to animate the value of a variable.
The example below uses Var::easing to animate the window background:
use zng::prelude::*;
let color = var(colors::AZURE.darken(30.pct()));
Window! {
widget::background_color = color.easing(500.ms(), easing::linear);
child = Button! {
layout::align = layout::Align::TOP;
on_click = hn!(|_| {
let mut c = color::Hsla::from(color.get());
c.hue += 60.0;
color.set(c);
});
child = Text!("Change background color");
};
}Variables can only be operated by a single animation, when a newer animation or modify affects a variable older animations can no longer
affect it, see VARS.animate for more details.
§Response
The ResponseVar<T> is a specialized variable that represents the result of an async task. You can use .await directly
in any async handler, but a response var lets you plug a query directly into a property. You can use task::respond to convert
any future into a response var, and you can use wait_rsp to convert a response var to a future.
use zng::prelude::*;
let rsp = task::respond(async {
let url = "https://raw.githubusercontent.com/git/git-scm.com/main/MIT-LICENSE.txt";
match task::http::get_txt(url).await {
Ok(t) => t,
Err(e) => formatx!("{e}"),
}
});
SelectableText!(rsp.map(|r| {
use zng::var::Response::*;
match r {
Waiting => Txt::from("loading.."),
Done(t) => t.clone(),
}
}))The example above creates a response var from a download future and maps the response to a widget.
A response var is paired with a ResponderVar<T>, you can create a response channel using the response_var function.
§Merge
The merge_var! and expr_var! macros can be used to declare a variable that merges multiple other variable values.
The example below demonstrates the two macros.
use zng::prelude::*;
let a = var(10u32);
let b = var(1u32);
// let merge = expr_var!({
// let a = *#{a};
// let b = *#{b.clone()};
// formatx!("{a} + {b} = {}", a + b)
// });
let merge = merge_var!(a, b.clone(), |&a, &b| { formatx!("{a} + {b} = {}", a + b) });
Button! {
child = Text!(merge);
on_click = hn!(|_| b.set(b.get() + 1));
}§Contextual
The ContextVar<T> type represents a variable that has context depend value, meaning they can produce a different value depending
on where they are used. Context vars are declared using the context_var! macro.
The example below declares a context var and a property that sets it. The context var is then used in two texts with two different contexts, the first text will show “Text!”, the second will show “Stack!”.
use zng::prelude::*;
context_var! {
static FOO_VAR: Txt = "";
}
#[zng::widget::property(CONTEXT, default(FOO_VAR))]
pub fn foo(child: impl IntoUiNode, foo: impl IntoVar<Txt>) -> UiNode {
zng::widget::node::with_context_var(child, FOO_VAR, foo)
}
fn demo() -> UiNode {
Stack! {
direction = StackDirection::top_to_bottom();
spacing = 5;
foo = "Stack!";
children = ui_vec![
Text! {
txt = FOO_VAR;
foo = "Text!";
},
Text!(FOO_VAR),
];
}
}Context variables have all the same capabilities of other variables if the example if foo is set to a var
the context var will be editable, and if FOO_VAR is mapped the mapping variable is also contextual.
§Full API
See zng_var for the full var API.
Modules§
- animation
- Var animation types and functions.
Macros§
- context_
var - Declares new
ContextVar<T>static items. - expr_
var - New variable from an expression with interpolated vars.
- flat_
expr_ var - New variable from an expression with interpolated vars that produces another variable.
- impl_
from_ and_ into_ var - Implements
T: IntoVar<U>,T: IntoValue<U>and optionallyU: From<T>without boilerplate. - merge_
var - Initializes a new
Var<T>with value made by merging multiple other variables. - when_
var - Initializes a new conditional var.
Structs§
- AnyVar
- Variable of any type.
- AnyWhen
VarBuilder - Type erased
when_var!manual builder. - ArcEq
- Arc value that implements equality by pointer comparison.
- BoxAny
VarValue - Small box for
AnyVarValuevalues. - Context
Init Handle - Identifies the unique context a
contextual_varis in. - Context
Var - Represents a named contextual variable.
- Merge
VarBuilder - Build a
merge_var!from any number of input vars of the same typeI. - Observable
Vec - Represents a
Vec<T>that tracks changes when used inside a variable. - OnVar
Args - Arguments for a var event handler.
- Responder
Var - Represents a read-write variable used to notify the completion of an async operation.
- Response
Var - Represents a read-only variable used to listen to a one time signal that an async operation has completed.
- VARS
- Variable updates and animation service.
- Var
- Variable of type
T. - VarCapability
- Kinds of interactions allowed by a
Var<T>in the current update. - VarEq
- Represents a
Var<T>as a value inside another variable. - VarHandle
- Handle to a variable or animation hook.
- VarHandles
- Represents a collection of var handles.
- VarHook
Args - Arguments for
Var::hook. - VarInstance
Tag - Unique identifier of a share variable, while it is alive.
- VarModify
- Mutable reference to a variable value.
- VarUpdate
Id - Represents the last time a variable was mutated or the current update cycle.
- Weak
AnyVar - Weak reference to a
AnyVar. - WeakVar
- Weak reference to a
Var<T>.
Enums§
- Response
- Raw value in a
ResponseVar. - VecChange
- Represents a change in a
ObservableVec.
Traits§
- AnyVar
Subscribe - Extension method to subscribe any widget to a variable.
- AnyVar
Value - Represents any variable value.
- Into
Value - A property value that is not a variable but can be inspected.
- IntoVar
- A value-to-var conversion that consumes the value.
- VarLayout
- Extension methods to layout var values.
- VarSubscribe
- Extension methods to subscribe any widget to a variable or app handlers to a variable.
- VarValue
- Represents a type that can be a
Var<T>value.
Functions§
- any_
var_ derived - New read/write shared reference type-erased variable that has initial value derived from
source. - const_
var - New immutable variable that stores the
valuedirectly. - contextual_
var - Create a contextualized variable.
- response_
done_ var - New
ResponseVarin the done state. - response_
var - New paired
ResponderVarandResponseVarin the waiting state. - var
- New read/write shared reference variable.
- var_
default - New read/write shared reference variable with default initial value.
- var_
from - New read/write shared reference variable from any type that can convert into it.
- var_
getter - Variable for getter properties (
get_*,actual_*). - var_
state - Variable for state properties (
is_*,has_*).