suzy/widget/content.rs
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use crate::dims::Rect;
6use crate::platform::{
7 DefaultPlatform, DefaultRenderPlatform, RenderPlatform,
8};
9use crate::pointer::PointerEvent;
10
11use super::{
12 WidgetChildReceiver, WidgetExtra, WidgetGraphicReceiver, WidgetInit,
13};
14
15/// This trait provides the "glue" between the data you define in custom
16/// widgets and the behavior suzy defines for widgets. There are three
17/// required methods: `init`, `children`, and `graphics`.
18///
19/// The `init` method is the primary point for registering the `watch`
20/// functions that define the behavior of a widget. See the
21/// [watch](../watch/index.html) module for more information.
22///
23/// The methods `children` and `graphics` should be fairly straightforward
24/// to implement: they provide a simple "internal iterator" format which
25/// allows suzy to iterate over the children and graphics a custom widget
26/// contains.
27///
28/// Custom widgets may contain any number of graphics and child widgets, or
29/// none of either.
30///
31/// For example, if a custom widget contains two buttons as children:
32///
33/// ```rust
34/// # use suzy::widget::*;
35/// # use suzy::selectable::SelectableIgnored;
36/// # type ButtonContent = SelectableIgnored<()>;
37/// use suzy::widgets::Button;
38///
39/// struct MyWidgetData {
40/// button_one: Button<ButtonContent>,
41/// button_two: Button<ButtonContent>,
42/// }
43///
44/// impl WidgetContent for MyWidgetData {
45/// // ...
46/// # fn init(_init: impl WidgetInit<Self>) {}
47/// # fn graphics(&mut self, _receiver: impl WidgetGraphicReceiver) {}
48///
49/// fn children(&mut self, mut receiver: impl WidgetChildReceiver) {
50/// receiver.child(&mut self.button_one);
51/// receiver.child(&mut self.button_two);
52/// }
53/// }
54/// ```
55///
56/// Or, if the custom widget only has a single graphic:
57///
58/// ```rust
59/// # use suzy::widget::*;
60/// # type MyGraphic = ();
61///
62/// struct MyWidgetData {
63/// graphic: MyGraphic,
64/// }
65///
66/// impl WidgetContent for MyWidgetData {
67/// // ...
68/// # fn init(_init: impl WidgetInit<Self>) {}
69/// # fn children(&mut self, _receiver: impl WidgetChildReceiver) {}
70///
71/// fn graphics(&mut self, mut receiver: impl WidgetGraphicReceiver) {
72/// receiver.graphic(&mut self.graphic);
73/// }
74/// }
75/// ```
76///
77pub trait WidgetContent<P = DefaultRenderPlatform>
78where
79 P: RenderPlatform + ?Sized,
80 Self: 'static,
81{
82 /// This method provides a convient place to register functions which
83 /// watch values and update parts of the widget when they change.
84 fn init(init: impl WidgetInit<Self, P>);
85
86 /// Use this method to specify the children a custom widget contains.
87 ///
88 /// Call `receiver.child` for each child.
89 fn children(&mut self, receiver: impl WidgetChildReceiver<P>);
90
91 /// Use this method to specify the graphics a custom widget contains.
92 ///
93 /// Call `receiver.graphic` for each graphic.
94 fn graphics(&mut self, receiver: impl WidgetGraphicReceiver<P>);
95
96 /// Override this method to define a custom shape for the widget.
97 ///
98 /// This is used by e.g. Button to test if it should handle a pointer
99 /// event. The default is a standard rectangular test.
100 fn hittest(&self, extra: &mut WidgetExtra<'_>, point: (f32, f32)) -> bool {
101 extra.contains(point)
102 }
103
104 /// Override this method to handle pointer events directly by a custom
105 /// widget.
106 ///
107 /// Return true if this successfully handled the event.
108 fn pointer_event(
109 &mut self,
110 extra: &mut WidgetExtra<'_>,
111 event: &mut PointerEvent,
112 ) -> bool {
113 let _unused = (extra, event);
114 false
115 }
116
117 /// This is the same as `pointer_event`, except that it runs before
118 /// passing the event to children, rather than after. This is only
119 /// recomended for special cases.
120 fn pointer_event_before(
121 &mut self,
122 extra: &mut WidgetExtra<'_>,
123 event: &mut PointerEvent,
124 ) -> bool {
125 let _unused = (extra, event);
126 false
127 }
128
129 /// This is a convience function to create and run an App with this
130 /// content as the only initial root widget.
131 fn run_as_app() -> !
132 where
133 Self: Default + WidgetContent<DefaultRenderPlatform>,
134 {
135 run_widget_as_app::<Self>()
136 }
137}
138
139impl<P: RenderPlatform> WidgetContent<P> for () {
140 fn init(_init: impl WidgetInit<Self, P>) {}
141 fn children(&mut self, _receiver: impl WidgetChildReceiver<P>) {}
142 fn graphics(&mut self, _receiver: impl WidgetGraphicReceiver<P>) {}
143}
144
145fn run_widget_as_app<T>() -> !
146where
147 T: Default + WidgetContent<DefaultRenderPlatform>,
148{
149 use crate::app::{App, AppBuilder};
150 use crate::window::WindowSettings;
151
152 let name = std::any::type_name::<T>().rsplit("::").next().unwrap();
153 let (_, title) =
154 name.chars()
155 .fold((false, String::new()), |(prev, mut title), ch| {
156 if prev && ch.is_uppercase() {
157 title.push(' ');
158 }
159 title.push(ch);
160 (ch.is_lowercase(), title)
161 });
162 let mut builder = AppBuilder::default();
163 builder.set_title(title);
164 let app: App<DefaultPlatform> = builder.build();
165 let (app, _) = app.with(|current| {
166 current.add_root(super::Widget::<T>::default);
167 });
168 app.run();
169}