druid/widget/
widget.rs

1// Copyright 2018 The Druid Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::num::NonZeroU64;
16use std::ops::{Deref, DerefMut};
17
18use super::prelude::*;
19use crate::debug_state::DebugState;
20use crate::widget::Axis;
21
22/// A unique identifier for a single [`Widget`].
23///
24/// `WidgetId`s are generated automatically for all widgets that participate
25/// in layout. More specifically, each [`WidgetPod`] has a unique `WidgetId`.
26///
27/// These ids are used internally to route events, and can be used to communicate
28/// between widgets, by submitting a command (as with [`EventCtx::submit_command`])
29/// and passing a `WidgetId` as the [`Target`].
30///
31/// A widget can retrieve its id via methods on the various contexts, such as
32/// [`LifeCycleCtx::widget_id`].
33///
34/// ## Explicit `WidgetId`s.
35///
36/// Sometimes, you may want to know a widget's id when constructing the widget.
37/// You can give a widget an _explicit_ id by wrapping it in an [`IdentityWrapper`]
38/// widget, or by using the [`WidgetExt::with_id`] convenience method.
39///
40/// If you set a `WidgetId` directly, you are responsible for ensuring that it
41/// is unique in time. That is: only one widget can exist with a given id at a
42/// given time.
43///
44/// [`Target`]: crate::Target
45/// [`WidgetPod`]: crate::WidgetPod
46/// [`WidgetExt::with_id`]: super::WidgetExt::with_id
47/// [`IdentityWrapper`]: super::IdentityWrapper
48// this is NonZeroU64 because we regularly store Option<WidgetId>
49#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
50pub struct WidgetId(NonZeroU64);
51
52/// The trait implemented by all widgets.
53///
54/// All appearance and behavior for a widget is encapsulated in an
55/// object that implements this trait.
56///
57/// The trait is parametrized by a type (`T`) for associated data.
58/// All trait methods are provided with access to this data, and
59/// in the case of [`event`] the reference is mutable, so that events
60/// can directly update the data.
61///
62/// Whenever the application data changes, the framework traverses
63/// the widget hierarchy with an [`update`] method. The framework
64/// needs to know whether the data has actually changed or not, which
65/// is why `T` has a [`Data`] bound.
66///
67/// All the trait methods are provided with a corresponding context.
68/// The widget can request things and cause actions by calling methods
69/// on that context.
70///
71/// In addition, all trait methods are provided with an environment
72/// ([`Env`]).
73///
74/// Container widgets will generally not call `Widget` methods directly
75/// on their child widgets, but rather will own their widget wrapped in
76/// a [`WidgetPod`], and call the corresponding method on that. The
77/// `WidgetPod` contains state and logic for these traversals. On the
78/// other hand, particularly light-weight containers might contain their
79/// child `Widget` directly (when no layout or event flow logic is
80/// needed), and in those cases will call these methods.
81///
82/// As a general pattern, container widgets will call the corresponding
83/// `WidgetPod` method on all their children. The `WidgetPod` applies
84/// logic to determine whether to recurse, as needed.
85///
86/// [`event`]: Widget::event
87/// [`update`]: Widget::update
88/// [`WidgetPod`]: crate::WidgetPod
89pub trait Widget<T> {
90    /// Handle an event.
91    ///
92    /// A number of different events (in the [`Event`] enum) are handled in this
93    /// method call. A widget can handle these events in a number of ways:
94    /// requesting things from the [`EventCtx`], mutating the data, or submitting
95    /// a [`Command`].
96    ///
97    /// [`Command`]: crate::Command
98    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env);
99
100    /// Handle a life cycle notification.
101    ///
102    /// This method is called to notify your widget of certain special events,
103    /// (available in the [`LifeCycle`] enum) that are generally related to
104    /// changes in the widget graph or in the state of your specific widget.
105    ///
106    /// A widget is not expected to mutate the application state in response
107    /// to these events, but only to update its own internal state as required;
108    /// if a widget needs to mutate data, it can submit a [`Command`] that will
109    /// be executed at the next opportunity.
110    ///
111    /// [`Command`]: crate::Command
112    fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env);
113
114    /// Update the widget's appearance in response to a change in the app's
115    /// [`Data`] or [`Env`].
116    ///
117    /// This method is called whenever the data or environment changes.
118    /// When the appearance of the widget needs to be updated in response to
119    /// these changes, you can call [`request_paint`] or [`request_layout`] on
120    /// the provided [`UpdateCtx`] to schedule calls to [`paint`] and [`layout`]
121    /// as required.
122    ///
123    /// The previous value of the data is provided in case the widget wants to
124    /// compute a fine-grained delta; you should try to only request a new
125    /// layout or paint pass if it is actually required.
126    ///
127    /// To determine if the [`Env`] has changed, you can call [`env_changed`]
128    /// on the provided [`UpdateCtx`]; you can then call [`env_key_changed`]
129    /// with any keys that are used in your widget, to see if they have changed;
130    /// you can then request layout or paint as needed.
131    ///
132    /// [`env_changed`]: UpdateCtx::env_changed
133    /// [`env_key_changed`]: UpdateCtx::env_key_changed
134    /// [`request_paint`]: UpdateCtx::request_paint
135    /// [`request_layout`]: UpdateCtx::request_layout
136    /// [`layout`]: Widget::layout
137    /// [`paint`]: Widget::paint
138    fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env);
139
140    /// Compute layout.
141    ///
142    /// A leaf widget should determine its size (subject to the provided
143    /// constraints) and return it.
144    ///
145    /// A container widget will recursively call [`WidgetPod::layout`] on its
146    /// child widgets, providing each of them an appropriate box constraint,
147    /// compute layout, then call [`set_origin`] on each of its children.
148    /// Finally, it should return the size of the container. The container
149    /// can recurse in any order, which can be helpful to, for example, compute
150    /// the size of non-flex widgets first, to determine the amount of space
151    /// available for the flex widgets.
152    ///
153    /// For efficiency, a container should only invoke layout of a child widget
154    /// once, though there is nothing enforcing this.
155    ///
156    /// The layout strategy is strongly inspired by Flutter.
157    ///
158    /// [`WidgetPod::layout`]: crate::WidgetPod::layout
159    /// [`set_origin`]: crate::WidgetPod::set_origin
160    fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size;
161
162    /// Paint the widget appearance.
163    ///
164    /// The [`PaintCtx`] derefs to something that implements the [`RenderContext`]
165    /// trait, which exposes various methods that the widget can use to paint
166    /// its appearance.
167    ///
168    /// Container widgets can paint a background before recursing to their
169    /// children, or annotations (for example, scrollbars) by painting
170    /// afterwards. In addition, they can apply masks and transforms on
171    /// the render context, which is especially useful for scrolling.
172    fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env);
173
174    #[doc(hidden)]
175    /// Get the identity of the widget; this is basically only implemented by
176    /// `IdentityWrapper`. Widgets should not implement this on their own.
177    fn id(&self) -> Option<WidgetId> {
178        None
179    }
180
181    #[doc(hidden)]
182    /// Get the (verbose) type name of the widget for debugging purposes.
183    /// You should not override this method.
184    fn type_name(&self) -> &'static str {
185        std::any::type_name::<Self>()
186    }
187
188    #[doc(hidden)]
189    /// Get the (abridged) type name of the widget for debugging purposes.
190    /// You should not override this method.
191    fn short_type_name(&self) -> &'static str {
192        let name = self.type_name();
193        name.split('<')
194            .next()
195            .unwrap_or(name)
196            .split("::")
197            .last()
198            .unwrap_or(name)
199    }
200
201    #[doc(hidden)]
202    /// From the current data, get a best-effort description of the state of
203    /// this widget and its children for debugging purposes.
204    fn debug_state(&self, data: &T) -> DebugState {
205        #![allow(unused_variables)]
206        DebugState {
207            display_name: self.short_type_name().to_string(),
208            ..Default::default()
209        }
210    }
211
212    /// Computes max intrinsic/preferred dimension of a widget on the provided axis.
213    ///
214    /// Max intrinsic/preferred dimension is the dimension the widget could take, provided infinite
215    /// constraint on that axis.
216    ///
217    /// If axis == Axis::Horizontal, widget is being asked to calculate max intrinsic width.
218    /// If axis == Axis::Vertical, widget is being asked to calculate max intrinsic height.
219    ///
220    /// Box constraints must be honored in intrinsics computation.
221    ///
222    /// AspectRatioBox is an example where constraints are honored. If height is finite, max intrinsic
223    /// width is *height * ratio*.
224    /// Only when height is infinite, child's max intrinsic width is calculated.
225    ///
226    /// Intrinsic is a *could-be* value. It's the value a widget *could* have given infinite constraints.
227    /// This does not mean the value returned by layout() would be the same.
228    ///
229    /// This method **must** return a finite value.
230    fn compute_max_intrinsic(
231        &mut self,
232        axis: Axis,
233        ctx: &mut LayoutCtx,
234        bc: &BoxConstraints,
235        data: &T,
236        env: &Env,
237    ) -> f64 {
238        match axis {
239            Axis::Horizontal => self.layout(ctx, bc, data, env).width,
240            Axis::Vertical => self.layout(ctx, bc, data, env).height,
241        }
242    }
243}
244
245impl WidgetId {
246    /// Allocate a new, unique `WidgetId`.
247    ///
248    /// All widgets are assigned ids automatically; you should only create
249    /// an explicit id if you need to know it ahead of time, for instance
250    /// if you want two sibling widgets to know each others' ids.
251    ///
252    /// You must ensure that a given `WidgetId` is only ever used for one
253    /// widget at a time.
254    pub fn next() -> WidgetId {
255        use crate::shell::Counter;
256        static WIDGET_ID_COUNTER: Counter = Counter::new();
257        WidgetId(WIDGET_ID_COUNTER.next_nonzero())
258    }
259
260    /// Create a reserved `WidgetId`, suitable for reuse.
261    ///
262    /// The caller is responsible for ensuring that this ID is in fact assigned
263    /// to a single widget at any time, or your code may become haunted.
264    ///
265    /// The actual inner representation of the returned `WidgetId` will not
266    /// be the same as the raw value that is passed in; it will be
267    /// `u64::max_value() - raw`.
268    #[allow(unsafe_code)]
269    pub const fn reserved(raw: u16) -> WidgetId {
270        let id = u64::max_value() - raw as u64;
271        // safety: by construction this can never be zero.
272        WidgetId(unsafe { std::num::NonZeroU64::new_unchecked(id) })
273    }
274
275    pub(crate) fn to_raw(self) -> u64 {
276        self.0.into()
277    }
278}
279
280impl<T> Widget<T> for Box<dyn Widget<T>> {
281    fn event(&mut self, ctx: &mut EventCtx, event: &Event, data: &mut T, env: &Env) {
282        self.deref_mut().event(ctx, event, data, env)
283    }
284
285    fn lifecycle(&mut self, ctx: &mut LifeCycleCtx, event: &LifeCycle, data: &T, env: &Env) {
286        self.deref_mut().lifecycle(ctx, event, data, env);
287    }
288
289    fn update(&mut self, ctx: &mut UpdateCtx, old_data: &T, data: &T, env: &Env) {
290        self.deref_mut().update(ctx, old_data, data, env);
291    }
292
293    fn layout(&mut self, ctx: &mut LayoutCtx, bc: &BoxConstraints, data: &T, env: &Env) -> Size {
294        self.deref_mut().layout(ctx, bc, data, env)
295    }
296
297    fn paint(&mut self, ctx: &mut PaintCtx, data: &T, env: &Env) {
298        self.deref_mut().paint(ctx, data, env);
299    }
300
301    fn id(&self) -> Option<WidgetId> {
302        self.deref().id()
303    }
304
305    fn type_name(&self) -> &'static str {
306        self.deref().type_name()
307    }
308
309    fn debug_state(&self, data: &T) -> DebugState {
310        self.deref().debug_state(data)
311    }
312
313    fn compute_max_intrinsic(
314        &mut self,
315        axis: Axis,
316        ctx: &mut LayoutCtx,
317        bc: &BoxConstraints,
318        data: &T,
319        env: &Env,
320    ) -> f64 {
321        self.deref_mut()
322            .compute_max_intrinsic(axis, ctx, bc, data, env)
323    }
324}