zng/var.rs
1//! Variables API.
2//!
3//! The [`Var<T>`] trait represents an observable value. The [`IntoVar<T>`] trait is the primary property input
4//! kind and the reason setting properties is so versatile. Variables can be a simple value, a shared reference to a value or
5//! a contextual value, some variables are also derived from others and update when the source variable update.
6//!
7//! Properties and widgets can subscribe to a variable to update when the variable value changes, this features enables most
8//! of the dynamic UI behavior, from binding one widget to another to animation.
9//!
10//! # Value
11//!
12//! The simplest variable is [`LocalVar<T>`], it represents an unchanging value that is shared by cloning. All values of types
13//! that implement [`VarValue`] automatically convert `IntoVar<T>` to this variable type. For this reason you don't usually need
14//! to write it.
15//!
16//! ```
17//! use zng::prelude::*;
18//!
19//! fn local(size: impl IntoVar<layout::Size>) {
20//! let size = size.into_var();
21//! assert!(size.capabilities().is_always_static());
22//! assert!(size.capabilities().is_always_read_only());
23//! }
24//!
25//! local(layout::Size::new(10, 10));
26//! local((10, 10));
27//! local(10);
28//! ```
29//!
30//! The example above declares a `LocalVar<Size>` 3 times with equal value. The `(10, 10)` and `10` values are type conversions
31//! implemented by the `Size` type. Type conversions are very easy to implement with the help of the [`impl_from_and_into_var!`] macro,
32//! most of the types used by properties implement conversions that enable a form of shorthand syntax.
33//!
34//! # Share & Modify
35//!
36//! The [`ArcVar<T>`] variable represents a shared value that can be modified, the [`var`] function instantiates it.
37//!
38//! The example below declares a button that grows taller every click. The variable is shared between the height property
39//! and the click handler. On click the height is increased, this schedules an update that applies the new value and notifies
40//! all subscribers.
41//!
42//! ```
43//! use zng::prelude::*;
44//! # let _scope = APP.defaults();
45//!
46//! let height = var(2.em());
47//! # let _ =
48//! Button! {
49//! child = Text!("Taller!");
50//! on_click = hn!(height, |_| { // clone `height` reference for the handler.
51//! height.set(height.get() + 10); // request an update to a new value.
52//! });
53//! layout::align = layout::Align::CENTER;
54//! layout::height; // set the height (shorthand, variable is same name as property)
55//! }
56//! # ;
57//! ```
58//!
59//! Note that variable updates don't happen immediately, in the handler above the variable is still the previous value after the [`set`](Var::set) call,
60//! 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.
61//!
62//! ```
63//! use zng::prelude::*;
64//! # let _scope = APP.defaults();
65//!
66//! let number = var(0u8);
67//! # let _ =
68//! Button! {
69//! child = Text!("Test");
70//! on_click = async_hn!(number, |_| {
71//! assert_eq!(number.get(), 0);
72//! number.set(1);
73//! assert_eq!(number.get(), 0);
74//!
75//! task::yield_now().await;
76//! assert_eq!(number.get(), 1);
77//! });
78//! }
79//! # ;
80//! ```
81//!
82//! The example above demonstrates the delayed update of a variable.
83//!
84//! If multiple widgets set the same variable on the same update only
85//! 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`](Var::modify)
86//! method can be used register a closure that can modify the value, this closure will observe the partially updated value that may already be
87//! modified by other widgets.
88//!
89//! The example below demonstrates how the `modify` closure observes a value that was just set in the same update cycle.
90//!
91//! ```
92//! use zng::prelude::*;
93//! # let _scope = APP.defaults();
94//!
95//! let foo = var(0u8);
96//! # let _ =
97//! Wgt! {
98//! widget::on_init = async_hn!(foo, |_| {
99//! foo.set(1);
100//! assert_eq!(0, foo.get());
101//! foo.modify(|m| {
102//! assert_eq!(1, **m);
103//! *m.to_mut() = 2;
104//! });
105//! assert_eq!(0, foo.get());
106//!
107//! foo.wait_update().await;
108//! assert_eq!(2, foo.get());
109//!
110//! println!("test ok");
111//! });
112//! }
113//! # ;
114//! ```
115//!
116//! # Mapping
117//!
118//! Variables can be mapped to other types, when the source variable updates the mapping closure re-evaluates and the mapped variable
119//! updates, all in the same update cycle, that is both variable will be flagged new at the same time. Mapping can also be bidirectional.
120//!
121//! The example below demonstrates a button that updates an integer variable that is mapped to a text.
122//!
123//! ```
124//! use zng::prelude::*;
125//! # let _scope = APP.defaults();
126//!
127//! let count = var(0u32);
128//! # let _ =
129//! Button! {
130//! child = Text!(count.map(|i| match i {
131//! 0 => Txt::from("Click Me!"),
132//! 1 => Txt::from("Clicked 1 time!"),
133//! n => formatx!("Clicked {n} times!"),
134//! }));
135//! on_click = hn!(|_| {
136//! count.set(count.get() + 1);
137//! });
138//! }
139//! # ;
140//! ```
141//!
142//! Variable mapping is specialized for each variable type, a `LocalVar<T>` will just map once and return another `LocalVar<T>`
143//! for example, the `ArcVar<T>` on the example creates a new variable and a mapping binding.
144//!
145//! # Binding
146//!
147//! Two existing variables can be bound, such that one variable update sets the other. The example below rewrites the mapping
148//! demo to use a [`bind_map`](Var::bind_map) instead.
149//!
150//! ```
151//! use zng::prelude::*;
152//! # let _scope = APP.defaults();
153//!
154//! let count = var(0u32);
155//! let label = var(Txt::from("Click Me!"));
156//! count
157//! .bind_map(&label, |i| match i {
158//! 1 => Txt::from("Clicked 1 time!"),
159//! n => formatx!("Clicked {n} times!"),
160//! })
161//! .perm();
162//! # let _ =
163//! Button! {
164//! child = Text!(label);
165//! on_click = hn!(|_| {
166//! count.set(count.get() + 1);
167//! });
168//! }
169//! # ;
170//! ```
171//!
172//! Note that unlike a map the initial value of the output variable is not updated, only subsequent ones. You can use
173//! [`set_from`](Var::set_from) to update the initial value too.
174//!
175//! # Animating
176//!
177//! Animation is implemented using variables, at the lowest level [`VARS.animate`](VARS::animate) is used to register a closure to be
178//! called every frame, the closure can set any number of variables, at a higher level the [`Var::ease`] and [`Var::chase`] methods
179//! can be used to animate the value of a variable.
180//!
181//! The example below uses [`Var::easing`] to animate the window background:
182//! ```
183//! use zng::prelude::*;
184//! # let _scope = APP.defaults();
185//!
186//! let color = var(colors::AZURE.darken(30.pct()));
187//! # let _ =
188//! Window! {
189//! widget::background_color = color.easing(500.ms(), easing::linear);
190//! child = Button! {
191//! layout::align = layout::Align::TOP;
192//! on_click = hn!(|_|{
193//! let mut c = color::Hsla::from(color.get());
194//! c.hue += 60.0;
195//! color.set(c);
196//! });
197//! child = Text!("Change background color");
198//! }
199//! }
200//! # ;
201//! ```
202//!
203//! Variables can only be operated by a single animation, when a newer animation or modify affects a variable older animations can no longer
204//! affect it, see [`VARS.animate`](VARS::animate) for more details.
205//!
206//! # Response
207//!
208//! The [`ResponseVar<T>`] is a specialized variable that represents the result of an async task. You can use `.await` directly
209//! in any async handler, but a response var lets you plug a query directly into a property. You can use [`task::respond`] to convert
210//! any future into a response var, and you can use [`wait_rsp`] to convert a response var to a future.
211//!
212//! ```no_run
213//! use zng::prelude::*;
214//! # let _scope = APP.defaults();
215//!
216//! let rsp = task::respond(async {
217//! let url = "https://raw.githubusercontent.com/git/git-scm.com/main/MIT-LICENSE.txt";
218//! match task::http::get_txt(url).await {
219//! Ok(t) => t,
220//! Err(e) => formatx!("{e}"),
221//! }
222//! });
223//! # let _ =
224//! SelectableText!(rsp.map(|r| {
225//! use zng::var::Response::*;
226//! match r {
227//! Waiting => Txt::from("loading.."),
228//! Done(t) => t.clone(),
229//! }
230//! }))
231//! # ;
232//! ```
233//!
234//! The example above creates a response var from a download future and maps the response to a widget.
235//!
236//! A response var is paired with a [`ResponderVar<T>`], you can create a *response channel* using the [`response_var`] function.
237//!
238//! [`task::respond`]: crate::task::respond
239//! [`wait_rsp`]: ResponseVar::wait_rsp
240//!
241//! # Merge
242//!
243//! The [`merge_var!`] and [`expr_var!`] macros can be used to declare a variable that merges multiple other variable values.
244//!
245//! The example below demonstrates the two macros.
246//!
247//! ```
248//! use zng::prelude::*;
249//! # let _scope = APP.defaults();
250//!
251//! let a = var(10u32);
252//! let b = var(1u32);
253//!
254//! // let merge = expr_var!({
255//! // let a = *#{a};
256//! // let b = *#{b.clone()};
257//! // formatx!("{a} + {b} = {}", a + b)
258//! // });
259//! let merge = merge_var!(a, b.clone(), |&a, &b| {
260//! formatx!("{a} + {b} = {}", a + b)
261//! });
262//! # let _ =
263//! Button! {
264//! child = Text!(merge);
265//! on_click = hn!(|_| b.set(b.get() + 1));
266//! }
267//! # ;
268//! ```
269//!
270//! # Contextual
271//!
272//! The [`ContextVar<T>`] variable represents a context depend value, meaning they can produce a different value depending
273//! on where they are used. Context vars are declared using the [`context_var!`] macro.
274//!
275//! The example below declares a context var and a property that sets it. The context var is then used in two texts with
276//! two different contexts, the first text will show "Text!", the second will show "Stack!".
277//!
278//! ```
279//! # fn main() { }
280//! use zng::prelude::*;
281//!
282//! context_var! {
283//! static FOO_VAR: Txt = "";
284//! }
285//!
286//! #[zng::widget::property(CONTEXT, default(FOO_VAR))]
287//! pub fn foo(child: impl UiNode, foo: impl IntoVar<Txt>) -> impl UiNode {
288//! zng::widget::node::with_context_var(child, FOO_VAR, foo)
289//! }
290//!
291//! fn demo() -> impl UiNode {
292//! Stack! {
293//! direction = StackDirection::top_to_bottom();
294//! spacing = 5;
295//! foo = "Stack!";
296//! children = ui_vec![
297//! Text! {
298//! txt = FOO_VAR;
299//! foo = "Text!";
300//! },
301//! Text!(FOO_VAR),
302//! ]
303//! }
304//! }
305//! ```
306//!
307//! Context variables have all the same capabilities of other variables if the example if `foo` is set to a [`var`]
308//! the context var will be editable, and if `FOO_VAR` is mapped the mapping variable is also contextual.
309//!
310//! # Full API
311//!
312//! See [`zng_var`] for the full var API.
313
314pub use zng_var::types::{
315 AnyWhenVarBuilder, ArcCowVar, ArcWhenVar, ContextualizedVar, ReadOnlyVar, Response, VecChange, WeakArcVar, WeakContextualizedVar,
316 WeakReadOnlyVar, WeakWhenVar,
317};
318pub use zng_var::{
319 AnyVar, AnyVarValue, AnyWeakVar, ArcEq, ArcVar, BoxedAnyVar, BoxedAnyWeakVar, BoxedVar, BoxedWeakVar, ContextInitHandle, ContextVar,
320 IntoValue, IntoVar, LocalVar, MergeVarBuilder, ObservableVec, ReadOnlyArcVar, ReadOnlyContextVar, ResponderVar, ResponseVar,
321 TraceValueArgs, VARS, Var, VarCapability, VarHandle, VarHandles, VarHookArgs, VarModify, VarPtr, VarUpdateId, VarValue, WeakVar,
322 context_var, expr_var, getter_var, impl_from_and_into_var, merge_var, response_done_var, response_var, state_var, var, var_default,
323 var_from, when_var,
324};
325
326pub use zng_app::widget::{AnyVarSubscribe, OnVarArgs, VarLayout, VarSubscribe};
327
328/// Var animation types and functions.
329pub mod animation {
330 pub use zng_var::animation::{
331 Animation, AnimationController, AnimationHandle, ChaseAnimation, ForceAnimationController, ModifyInfo, Transition, TransitionKeyed,
332 Transitionable, WeakAnimationHandle,
333 };
334
335 /// Common easing functions.
336 pub mod easing {
337 pub use zng_var::animation::easing::{
338 Bezier, EasingFn, EasingStep, EasingTime, back, bounce, circ, cubic, cubic_bezier, ease_in, ease_in_out, ease_out, ease_out_in,
339 elastic, expo, linear, none, quad, quart, quint, reverse, reverse_out, sine, step_ceil, step_floor,
340 };
341 }
342}