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}