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}