vizia_core/binding/mod.rs
1//! Data binding provides a way to link views to model data so that view properties update when data changes.
2//!
3//! # Example
4//! First we declare some data for our application:
5//! ```
6//! # use vizia_core::prelude::*;
7//!
8//!
9//! struct AppData {
10//! count: i32,
11//! }
12//!
13//! ```
14//! Next we'll declare some events which will be sent by views to modify the data. Data binding in vizia is one-way, events are sent up the tree
15//! to the app data to mutate it and updated values are sent to observers, such as a [`Binding`] view.
16//! ```
17//! enum AppEvent {
18//! Increment,
19//! Decrement,
20//! }
21//! ```
22//! Then we implement the [`Model`](crate::model::Model) trait on our data, which allows us to modify the it in response to an [`Event`](crate::events::Event):
23//! ```
24//! # use vizia_core::prelude::*;
25//!
26//!
27//! struct AppData {
28//! count: i32,
29//! }
30//!
31//! enum AppEvent {
32//! Increment,
33//! Decrement,
34//! }
35//!
36//! impl Model for AppData {
37//! fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
38//! event.map(|app_event, _| match app_event {
39//! AppEvent::Increment => {
40//! self.count += 1;
41//! }
42//!
43//! AppEvent::Decrement => {
44//! self.count -= 1;
45//! }
46//! });
47//! }
48//! }
49//! ```
50//! This trait also allows data to be built into the application [Tree](crate::prelude::Tree):
51//! ```ignore
52//! # use vizia_core::prelude::*;
53//!
54//! # use vizia_winit::application::Application;
55//!
56//! struct AppData {
57//! count: i32,
58//! }
59//!
60//! impl Model for AppData {}
61//!
62//! fn main() {
63//! Application::new(|cx|{
64//! AppData {
65//! count: 0,
66//! }.build(cx);
67//! }).run();
68//! }
69//! ```
70//! A [`Binding`] view is one way in which data can be used by views. It observes a signal and rebuilds whenever the signal changes:
71//! ```ignore
72//! # use vizia_core::prelude::*;
73//!
74//! # use vizia_winit::application::Application;
75//!
76//! struct AppData {
77//! count: Signal<i32>,
78//! }
79//!
80//! impl Model for AppData {
81//! fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
82//! event.map(|app_event, _| match app_event {
83//! AppEvent::Increment => {
84//! self.count.update(|count| *count += 1);
85//! }
86//!
87//! AppEvent::Decrement => {
88//! self.count.update(|count| *count -= 1);
89//! }
90//! });
91//! }
92//! }
93//!
94//! enum AppEvent {
95//! Increment,
96//! Decrement,
97//! }
98//!
99//! fn main() {
100//! Application::new(|cx|{
101//! let count = Signal::new(0);
102//! AppData {
103//! count,
104//! }.build(cx);
105//!
106//! Binding::new(cx, count, |cx|{
107//! Label::new(cx, count.get().to_string());
108//! });
109//!
110//! Button::new(cx, |cx|{
111//! Label::new(cx, "Increment")
112//! })
113//! .on_press(|cx| cx.emit(AppEvent::Increment));
114//!
115//! Button::new(cx, |cx|{
116//! Label::new(cx, "Decrement")
117//! })
118//! .on_press(|cx| cx.emit(AppEvent::Decrement));
119//! }).run();
120//! }
121//! ```
122//! Note, the button does not need to be bound to the data to send an event to it. By default events will propagate up the tree.
123//!
124//! Completely rebuilding the `Label` when the data changes is unnecessary in this case. Instead we can pass a signal directly to
125//! the view constructor so only the relevant property updates.
126//! ```ignore
127//! # use vizia_core::prelude::*;
128//! # use vizia_winit::application::Application;
129//!
130//! fn main() {
131//! Application::new(|cx|{
132//! let count = Signal::new(0);
133//!
134//! Label::new(cx, count);
135//!
136//! Button::new(cx, |cx|{
137//! Label::new(cx, "Increment")
138//! })
139//! .on_press(move |_| count.update(|v| *v += 1));
140//!
141//! Button::new(cx, |cx|{
142//! Label::new(cx, "Decrement")
143//! })
144//! .on_press(move |_| count.update(|v| *v -= 1));
145//! }).run();
146//! }
147//! ```
148//!
149//! Note that even though the `count` value is `i32`, the label accepts it because it implements `ToString` and is converted internally.
150//! If the data is the wrong type and cannot be converted internally, use a mapped signal or a custom formatter.
151mod handler;
152pub(crate) use handler::BindingHandler;
153
154mod res;
155pub use res::*;
156
157#[allow(clippy::module_inception)]
158mod binding;
159pub use binding::Binding;