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}