Skip to main content

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;
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            entity_identifiers: &cx.entity_identifiers,
141        };
142
143        if let Some(parent_node) = get_access_node(&mut access_context, &mut cx.views, parent_id) {
144            let parent_node = parent_node.node_builder;
145            let node = Node::default();
146
147            cx.tree_updates.push(Some(TreeUpdate {
148                nodes: vec![(parent_node_id, parent_node), (node_id, node)],
149                tree: None,
150                tree_id: accesskit::TreeId::ROOT,
151                focus: cx.focused.accesskit_id(),
152            }));
153        }
154
155        cx.models.insert(id, HashMap::default());
156
157        let handle = Handle { current: id, entity: id, p: Default::default(), cx };
158
159        handle.cx.with_current(handle.entity, content);
160
161        handle
162    }
163
164    /// Specifies a name for the view type which can be used as an element selector in css.
165    ///
166    /// # Example
167    /// ```
168    /// # use vizia_core::prelude::*;
169    /// pub struct CustomView{}
170    ///
171    /// impl CustomView {
172    ///     pub fn new(cx: &mut Context) -> Handle<Self> {
173    ///         Self{}.build(cx, |_|{})
174    ///     }
175    /// }
176    ///
177    /// impl View for CustomView {
178    ///     fn element(&self) -> Option<&'static str> {
179    ///         Some("custom_view")
180    ///     }
181    /// }
182    /// ```
183    /// Then in css:
184    /// ```css
185    /// custom_view {
186    ///     background-color: red;
187    /// }
188    /// ```
189    fn element(&self) -> Option<&'static str> {
190        None
191    }
192
193    /// Handles any events received by the view.
194    ///
195    /// # Example
196    /// ```
197    /// # use vizia_core::prelude::*;
198    /// pub struct CustomView{}
199    ///
200    /// impl CustomView {
201    ///     pub fn new(cx: &mut Context) -> Handle<Self> {
202    ///         Self{}.build(cx, |_|{})
203    ///     }
204    /// }
205    ///
206    /// impl View for CustomView {
207    ///     fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
208    ///         event.map(|window_event, meta| match window_event{
209    ///             WindowEvent::MouseDown(_) => {
210    ///                 if meta.target == cx.current() {
211    ///                     // Emit a `WindowClose` event when this view is clicked on.
212    ///                     cx.emit(WindowEvent::WindowClose);
213    ///                 }
214    ///             }
215    ///
216    ///             _=> {}
217    ///         });
218    ///     }
219    /// }
220    /// ```
221    #[allow(unused_variables)]
222    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {}
223
224    /// Provides custom drawing for the view.
225    ///
226    /// Usually the look of a view is determined by the style and layout properties of the view. However, the `draw` method of
227    /// the `View` trait can be used to provide completely custom drawing for the view. The properties of the view can be accessed
228    /// through the provided [`DrawContext`] and the provided [`Canvas`] can be used to draw custom paths.
229    ///
230    /// # Example
231    /// ```
232    /// # use vizia_core::prelude::*;
233    /// # use vizia_core::vg;
234    /// pub struct CustomView{}
235    ///
236    /// impl CustomView {
237    ///     pub fn new(cx: &mut Context) -> Handle<Self> {
238    ///         Self{}.build(cx, |_|{})
239    ///     }
240    /// }
241    ///
242    /// impl View for CustomView {
243    ///     fn draw(&self, cx: &mut DrawContext, canvas: &mut Canvas) {
244    ///         // Get the bounding box of the current view.
245    ///         let bounds = cx.bounds();
246    ///
247    ///         // Create a new `Path` from the `vg` module.
248    ///         let mut path = vg::Path::new();
249    ///         // Add a rectangle to the path with the dimensions of the view bounds.
250    ///         path.rect(bounds.x, bounds.y, bounds.w, bounds.h);
251    ///         // Fill the path onto the canvas with a red color.
252    ///         canvas.fill_path(&mut path, &vg::Paint::color(Color::red().into()));
253    ///     }
254    /// }
255    /// ```
256    fn draw(&self, cx: &mut DrawContext, canvas: &Canvas) {
257        let bounds = cx.bounds();
258
259        //Skip widgets with no width or no height
260        if bounds.w == 0.0 || bounds.h == 0.0 {
261            return;
262        }
263
264        cx.draw_background(canvas);
265        cx.draw_shadows(canvas);
266
267        cx.draw_border(canvas);
268
269        cx.draw_outline(canvas);
270
271        cx.draw_text(canvas);
272    }
273
274    #[allow(unused_variables)]
275    /// Provides a way to configure the accessibility features of a view.
276    fn accessibility(&self, cx: &mut AccessContext, node: &mut AccessNode) {}
277}
278
279impl<T: View> ViewHandler for T
280where
281    T: std::marker::Sized + View + 'static,
282{
283    fn element(&self) -> Option<&'static str> {
284        <T as View>::element(self)
285    }
286
287    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
288        <T as View>::event(self, cx, event);
289    }
290
291    fn draw(&self, cx: &mut DrawContext, canvas: &Canvas) {
292        <T as View>::draw(self, cx, canvas);
293    }
294
295    fn accessibility(&self, cx: &mut AccessContext, node: &mut AccessNode) {
296        <T as View>::accessibility(self, cx, node);
297    }
298
299    fn as_any_ref(&self) -> &dyn Any {
300        self
301    }
302
303    fn as_any_mut(&mut self) -> &mut dyn Any {
304        self
305    }
306}