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}