vizia_core/
view.rs

1//! Views are used to visually present model data and to act as controls which, when interacted with, send events to mutate model data.
2//!
3//! # Example
4//! The `Label` view is used to display a text string:
5//!
6//! ```no_run
7//! # use vizia_core::prelude::*;
8//! # use vizia_winit::application::Application;
9//! Application::new(|cx|{
10//!     Label::new(cx, "Hello World");
11//! })
12//! .run();
13//! ```
14
15use crate::accessibility::IntoNode;
16use crate::prelude::*;
17use crate::systems::get_access_node;
18use std::any::{Any, TypeId};
19mod handle;
20pub use handle::Handle;
21use hashbrown::HashMap;
22
23use crate::events::ViewHandler;
24use accesskit::{Node, TreeUpdate};
25
26/// A view is any object which can be displayed on the screen.
27///
28/// # Creating a Custom View
29///
30/// To create a custom view, first define a struct with any view-specific state.
31/// ```
32/// # use vizia_core::prelude::*;
33/// pub struct CustomView {
34///     count: i32,
35/// }
36/// ```
37///
38/// Next, implement the constructor for the custom view. Typically, the constructor will take `&mut Context` as the first argument
39/// and return a [`Handle`] to the view.
40/// ```
41/// # use vizia_core::prelude::*;
42/// pub struct CustomView {
43///     count: i32,
44/// }
45///
46/// impl CustomView {
47///     pub fn new(cx: &mut Context, count: i32) -> Handle<Self> {
48///         Self {
49///             count,
50///         }.build(cx, |cx|{
51///             // If we want the view to contain other views we can build those here.
52///         })
53///     }
54/// }
55///
56/// # impl View for CustomView {}
57/// ```
58///
59/// The `build` method above is provided by the `View` trait, which we must implement for any custom view.
60/// ```
61/// # use vizia_core::prelude::*;
62/// pub struct CustomView {
63///     count: i32,
64/// }
65///
66/// impl CustomView {
67///     pub fn new(cx: &mut Context, count: i32) -> Handle<Self> {
68///         Self {
69///             count,
70///         }.build(cx, |cx|{
71///             // If we want the view to contain other views we can build those here.
72///         })
73///     }
74/// }
75///
76/// impl View for CustomView {
77///
78/// }
79/// ```
80///
81/// The `View` trait contains methods, which can be optionally overridden, for assigning an element name, handling events, and performing custom drawing.
82pub trait View: 'static + Sized {
83    /// Builds the view into the tree and returns a handle which can be used to apply style and layout modifiers to the view.
84    ///
85    /// Typically this method is called within the constructor of a view, for example:
86    /// ```
87    /// # use vizia_core::prelude::*;
88    /// pub struct CustomView{}
89    ///
90    /// impl CustomView {
91    ///     pub fn new(cx: &mut Context) -> Handle<Self> {
92    ///         Self{}.build(cx, |_|{})
93    ///     }
94    /// }
95    /// # impl View for CustomView {}
96    /// ```
97    /// The `content` closure allows for a view to be built from other views. For example, a custom view could encapsulate a
98    /// pair of labels:
99    /// ```
100    /// # use vizia_core::prelude::*;
101    /// pub struct CustomView{}
102    ///
103    /// impl CustomView {
104    ///     pub fn new(cx: &mut Context) -> Handle<Self> {
105    ///         Self{}.build(cx, |cx|{
106    ///             Label::new(cx, "Hello");
107    ///             Label::new(cx, "World");
108    ///         })
109    ///     }
110    /// }
111    /// # impl View for CustomView {}
112    /// ```
113    fn build<F>(self, cx: &mut Context, content: F) -> Handle<Self>
114    where
115        F: FnOnce(&mut Context),
116    {
117        let id = cx.entity_manager.create();
118        let current = cx.current();
119        cx.tree.add(id, current).expect("Failed to add to tree");
120        cx.cache.add(id);
121        cx.style.add(id);
122        cx.needs_redraw(id);
123
124        if let Some(element) = self.element() {
125            cx.style.element.insert(id, fxhash::hash32(element));
126        }
127
128        cx.views.insert(id, Box::new(self));
129
130        let parent_id = cx.tree.get_layout_parent(id).unwrap();
131        let parent_node_id = parent_id.accesskit_id();
132        let node_id = id.accesskit_id();
133
134        let mut access_context = AccessContext {
135            current: id,
136            tree: &cx.tree,
137            cache: &cx.cache,
138            style: &cx.style,
139            text_context: &mut cx.text_context,
140        };
141
142        if let Some(parent_node) = get_access_node(&mut access_context, &mut cx.views, parent_id) {
143            let parent_node = parent_node.node_builder;
144            let node = Node::default();
145
146            cx.tree_updates.push(Some(TreeUpdate {
147                nodes: vec![(parent_node_id, parent_node), (node_id, node)],
148                tree: None,
149                focus: cx.focused.accesskit_id(),
150            }));
151        }
152
153        cx.models.insert(id, HashMap::default());
154        cx.stores.insert(id, HashMap::default());
155
156        let handle = Handle { current: id, entity: id, p: Default::default(), cx };
157
158        handle.cx.with_current(handle.entity, content);
159
160        handle
161    }
162
163    /// Specifies a name for the view type which can be used as an element selector in css.
164    ///
165    /// # Example
166    /// ```
167    /// # use vizia_core::prelude::*;
168    /// pub struct CustomView{}
169    ///
170    /// impl CustomView {
171    ///     pub fn new(cx: &mut Context) -> Handle<Self> {
172    ///         Self{}.build(cx, |_|{})
173    ///     }
174    /// }
175    ///
176    /// impl View for CustomView {
177    ///     fn element(&self) -> Option<&'static str> {
178    ///         Some("custom_view")
179    ///     }
180    /// }
181    /// ```
182    /// Then in css:
183    /// ```css
184    /// custom_view {
185    ///     background-color: red;
186    /// }
187    /// ```
188    fn element(&self) -> Option<&'static str> {
189        None
190    }
191
192    /// Handles any events received by the view.
193    ///
194    /// # Example
195    /// ```
196    /// # use vizia_core::prelude::*;
197    /// pub struct CustomView{}
198    ///
199    /// impl CustomView {
200    ///     pub fn new(cx: &mut Context) -> Handle<Self> {
201    ///         Self{}.build(cx, |_|{})
202    ///     }
203    /// }
204    ///
205    /// impl View for CustomView {
206    ///     fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
207    ///         event.map(|window_event, meta| match window_event{
208    ///             WindowEvent::MouseDown(_) => {
209    ///                 if meta.target == cx.current() {
210    ///                     // Emit a `WindowClose` event when this view is clicked on.
211    ///                     cx.emit(WindowEvent::WindowClose);
212    ///                 }
213    ///             }
214    ///
215    ///             _=> {}
216    ///         });
217    ///     }
218    /// }
219    /// ```
220    #[allow(unused_variables)]
221    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {}
222
223    /// Provides custom drawing for the view.
224    ///
225    /// Usually the look of a view is determined by the style and layout properties of the view. However, the `draw` method of
226    /// the `View` trait can be used to provide completely custom drawing for the view. The properties of the view can be accessed
227    /// through the provided [`DrawContext`] and the provided [`Canvas`] can be used to draw custom paths.
228    ///
229    /// # Example
230    /// ```
231    /// # use vizia_core::prelude::*;
232    /// # use vizia_core::vg;
233    /// pub struct CustomView{}
234    ///
235    /// impl CustomView {
236    ///     pub fn new(cx: &mut Context) -> Handle<Self> {
237    ///         Self{}.build(cx, |_|{})
238    ///     }
239    /// }
240    ///
241    /// impl View for CustomView {
242    ///     fn draw(&self, cx: &mut DrawContext, canvas: &mut Canvas) {
243    ///         // Get the bounding box of the current view.
244    ///         let bounds = cx.bounds();
245    ///
246    ///         // Create a new `Path` from the `vg` module.
247    ///         let mut path = vg::Path::new();
248    ///         // Add a rectangle to the path with the dimensions of the view bounds.
249    ///         path.rect(bounds.x, bounds.y, bounds.w, bounds.h);
250    ///         // Fill the path onto the canvas with a red color.
251    ///         canvas.fill_path(&mut path, &vg::Paint::color(Color::red().into()));
252    ///     }
253    /// }
254    /// ```
255    fn draw(&self, cx: &mut DrawContext, canvas: &Canvas) {
256        let bounds = cx.bounds();
257
258        //Skip widgets with no width or no height
259        if bounds.w == 0.0 || bounds.h == 0.0 {
260            return;
261        }
262
263        cx.draw_background(canvas);
264        cx.draw_shadows(canvas);
265
266        cx.draw_border(canvas);
267
268        cx.draw_outline(canvas);
269
270        cx.draw_text(canvas);
271    }
272
273    #[allow(unused_variables)]
274    /// Provides a way to configure the accessibility features of a view.
275    fn accessibility(&self, cx: &mut AccessContext, node: &mut AccessNode) {}
276}
277
278impl<T: View> ViewHandler for T
279where
280    T: std::marker::Sized + View + 'static,
281{
282    fn element(&self) -> Option<&'static str> {
283        <T as View>::element(self)
284    }
285
286    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
287        <T as View>::event(self, cx, event);
288    }
289
290    fn draw(&self, cx: &mut DrawContext, canvas: &Canvas) {
291        <T as View>::draw(self, cx, canvas);
292    }
293
294    fn accessibility(&self, cx: &mut AccessContext, node: &mut AccessNode) {
295        <T as View>::accessibility(self, cx, node);
296    }
297
298    fn as_any_ref(&self) -> &dyn Any {
299        self
300    }
301
302    fn as_any_mut(&mut self) -> &mut dyn Any {
303        self
304    }
305
306    fn id(&self) -> std::any::TypeId {
307        TypeId::of::<T>()
308    }
309}