kas_core/core/widget.rs
1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License in the LICENSE-APACHE file or at:
4// https://www.apache.org/licenses/LICENSE-2.0
5
6//! Widget and Events traits
7
8#[allow(unused)] use super::{Events, Layout};
9use super::{Node, Tile};
10use crate::Id;
11#[allow(unused)] use crate::event::EventState;
12use crate::event::{ConfigCx, Event, EventCx, IsUsed};
13use crate::geom::{Coord, Offset, Rect, Size};
14use crate::theme::{DrawCx, SizeCx};
15#[allow(unused)] use kas_macros as macros;
16use kas_macros::autoimpl;
17
18/// The Widget trait
19///
20/// The primary widget trait covers event handling over super trait [`Tile`]
21/// which governs layout, drawing, child enumeration and identification.
22/// Most methods of `Widget` are hidden and only for use within the Kas library.
23///
24/// `Widget` is dyn-safe given a type parameter, e.g. `dyn Widget<Data = ()>`.
25/// [`Tile`] is dyn-safe without a type parameter. [`Node`] is a dyn-safe
26/// abstraction over a `&dyn Widget<Data = T>` plus a `&T` data parameter.
27///
28/// # Widget lifecycle
29///
30/// Widget methods have a specified call order:
31///
32/// 1. The widget is configured (see [`Events#configuration`])
33/// 2. The widget is updated ([`Events#update`])
34/// 3. The widget is sized (see [`Layout#sizing`])
35/// 4. The widget is ready for other methods to be called
36///
37/// Configuration, update and sizing may be repeated at any time (see above
38/// linked documentation).
39///
40/// Widgets are responsible for ensuring that their children may observe this
41/// lifecycle. Usually this simply involves inclusion of the child in layout
42/// operations. Steps of the lifecycle may be postponed until a widget becomes
43/// visible.
44///
45/// # Implementing Widget
46///
47/// To implement a widget, use the [`#widget`] macro within an
48/// [`impl_self`](macros::impl_self), [`impl_scope!`](macros::impl_scope) or
49/// [`impl_anon!`](macros::impl_anon) macro.
50/// **This is the only supported method of implementing `Widget`.**
51///
52/// Explicit (partial) implementations of [`Widget`], [`Layout`], [`Tile`] and [`Events`]
53/// are optional. The [`#widget`] macro completes implementations.
54///
55/// Synopsis:
56/// ```ignore
57/// #[impl_self]
58/// mod MyWidget {
59/// #[widget {
60/// // macro properties (all optional)
61/// Data = T;
62/// }]
63/// #[layout(self.foo)]
64/// struct MyWidget {
65/// core: widget_core!(),
66/// #[widget] foo: impl Widget<Data = T> = make_foo(),
67/// // ...
68/// }
69///
70/// // Optional implementations:
71/// impl Layout for Self { /* ... */ }
72/// impl Events for Self { /* ... */ }
73/// impl Self { /* ... */ }
74/// }
75/// ```
76///
77/// Details may be categorised as follows:
78///
79/// - **Data**: the type [`Widget::Data`] must be specified exactly once, but
80/// this type may be given in any of three locations: as a property of the
81/// [`#widget`] macro or as [`Widget::Data`].
82/// - **Core** methods of [`Tile`] are *always* implemented via the [`#widget`]
83/// macro, whether or not an `impl Tile { ... }` item is present.
84/// - **Introspection** methods [`Tile::child_indices`], [`Tile::get_child`]
85/// and [`Widget::child_node`] are implemented by the [`#widget`] macro
86/// in most cases: child widgets embedded within a layout descriptor or
87/// included as fields marked with `#[widget]` are enumerated.
88/// - **Introspection** methods [`Tile::find_child_index`] and
89/// [`Events::make_child_id`] have default implementations which *usually*
90/// suffice.
91/// - **Layout** is specified either via [`layout`](macro@crate::layout) macro
92/// or via implementation of at least [`Layout`].
93///- **Event handling** is optional, implemented through [`Events`].
94///
95/// For examples, check the source code of widgets in the widgets library
96/// or [examples apps](https://github.com/kas-gui/kas/tree/master/examples).
97/// (Check that the code uses the same Kas version since the widget traits are
98/// not yet stable.)
99///
100/// [`#widget`]: macros::widget
101#[autoimpl(for<T: trait + ?Sized> &'_ mut T, Box<T>)]
102pub trait Widget: Tile {
103 /// Input data type
104 ///
105 /// Widget expects data of this type to be provided by reference when
106 /// calling any event-handling operation on this widget.
107 ///
108 /// Type `Data` should be specified either here (`impl Widget { ... }`) or
109 /// in `impl Events { ... }`. Alternatively, if the widget has no children
110 /// and no explicit `impl Events` or `impl Widget`, then `Data = ()` is
111 /// assumed; or, if the prior conditions are met and `#[collection]` is used
112 /// on some field, then `Data = <#field_ty as ::kas::Collection>::Data` is
113 /// assumed.
114 ///
115 /// [`#widget`]: macros::widget
116 //
117 // SAFETY: the unsafe_node feature requires Data: Sized.
118 type Data: Sized;
119
120 /// Erase type
121 ///
122 /// This method is implemented by the `#[widget]` macro.
123 fn as_node<'a>(&'a mut self, data: &'a Self::Data) -> Node<'a> {
124 let _ = data;
125 unimplemented!() // make rustdoc show that this is a provided method
126 }
127
128 /// Access a child as a [`Node`], if available
129 ///
130 /// This method is the `mut` version of [`Tile::get_child`] but which also
131 /// pairs the returned widget with its input `data`. It is expected to
132 /// succeed where [`Tile::get_child`] succeeds.
133 ///
134 /// Valid `index` values may be discovered by calling
135 /// [`Tile::child_indices`], [`Tile::find_child_index`] or
136 /// [`Tile::nav_next`]. The `index`-to-child mapping is not
137 /// required to remain fixed; use an [`Id`] to track a widget over time.
138 ///
139 /// This method must be implemented explicitly when [`Tile::get_child`] is.
140 /// It might also need to be implemented explicitly to map `data`, though
141 /// usually the `#[widget]` attribute on children specifies this mapping.
142 fn child_node<'n>(&'n mut self, data: &'n Self::Data, index: usize) -> Option<Node<'n>> {
143 let _ = (data, index);
144 unimplemented!() // make rustdoc show that this is a provided method
145 }
146
147 /// Internal method: configure recursively
148 ///
149 /// Requires status: none.
150 ///
151 /// Do not implement this method directly!
152 #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
153 #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
154 fn _configure(&mut self, cx: &mut ConfigCx, data: &Self::Data, id: Id);
155
156 /// Internal method: update recursively
157 ///
158 /// Requires status: configured.
159 ///
160 /// Do not implement this method directly!
161 #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
162 #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
163 fn _update(&mut self, cx: &mut ConfigCx, data: &Self::Data);
164
165 /// Internal method: send recursively
166 ///
167 /// Requires status: configured and sized.
168 ///
169 /// Do not implement this method directly!
170 #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
171 #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
172 fn _send(&mut self, cx: &mut EventCx, data: &Self::Data, id: Id, event: Event) -> IsUsed;
173
174 /// Internal method: replay recursively
175 ///
176 /// Traverses the widget tree to `id`, then unwinds with standard handling
177 /// of event state.
178 ///
179 /// If a message is being sent, it must already be on the stack. If the
180 /// target is not found, unsent messages are dropped.
181 ///
182 /// Requires status: configured.
183 ///
184 /// Do not implement this method directly!
185 #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
186 #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
187 fn _replay(&mut self, cx: &mut EventCx, data: &Self::Data, id: Id);
188}
189
190/// Layout routines for scrollable content
191///
192/// A `Viewport` supports content larger than its assigned `rect` (the `rect`
193/// passed to [`Layout::set_rect`]). This `rect` is considered the viewport
194/// through which content may be viewed (approximately: see
195/// [`Self::draw_with_offset`]).
196///
197/// If the parent widget supports scrolling over contents implementing
198/// `Viewport`, it should call [`Viewport::draw_with_offset`] instead of
199/// [`Layout::draw`].
200///
201/// It is intended that the widget implementing this trait is the child of some
202/// parent widget which supports scrolling through event handling and provision
203/// of a scroll offset, and that this parent uses the methods of this trait
204/// where applicable (see below). In case the parent does not support scrolling,
205/// the widget should remain usable (but with only a subset of content being
206/// accessible).
207pub trait Viewport: Widget {
208 /// Get content size
209 ///
210 /// When the content size is larger than the viewport, content becomes
211 /// scrollable with a maximum offset of `content_size - viewport_size`.
212 ///
213 /// # Calling
214 ///
215 /// This method is called during sizing.
216 fn content_size(&self) -> Size;
217
218 /// Set the scroll offset
219 ///
220 /// The `viewport` and `offset` parameters are the same as those of
221 /// [`Viewport::draw_with_offset`].
222 ///
223 /// # Calling
224 ///
225 /// This method should be called immediately after [`Layout::set_rect`]
226 /// (unless it's known that the implementation doesn't use this method).
227 ///
228 /// # Implementation
229 ///
230 /// This method only needs to do anything in cases where only a subset of
231 /// content is prepared.
232 fn set_offset(&mut self, cx: &mut SizeCx, viewport: Rect, offset: Offset) {
233 let _ = (cx, viewport, offset);
234 }
235
236 /// Update the scroll offset
237 ///
238 /// The `viewport` and `offset` parameters are the same as those of
239 /// [`Viewport::draw_with_offset`].
240 ///
241 /// # Calling
242 ///
243 /// This method should be called whenever the scroll offset changes to allow
244 /// preparation of content (unless it's known that the implementation
245 /// doesn't use this method). It must be called before drawing and event
246 /// handling operations using this new `offset`.
247 ///
248 /// # Implementation
249 ///
250 /// This method only needs to do anything in cases where only a subset of
251 /// content is prepared.
252 fn update_offset(
253 &mut self,
254 cx: &mut ConfigCx,
255 data: &Self::Data,
256 viewport: Rect,
257 offset: Offset,
258 ) {
259 let _ = (cx, data, viewport, offset);
260 }
261
262 /// Draw with a scroll offset
263 ///
264 /// Drawing should be clamped to the given `viewport`. This `viewport` may
265 /// be the same as [`Layout::rect`] but is allowed to be slightly different;
266 /// for example `EditBox` passes a larger [`Rect`] to allow drawing in the
267 /// margin allocated between its frame and content.
268 ///
269 /// The `offset` should be the same as that used by the parent widget
270 /// in [`Tile::translation`].
271 ///
272 /// Effectively, content is drawn at position `self.rect().pos - offset`
273 /// but clamped to `viewport`.
274 ///
275 /// # Calling
276 ///
277 /// This method should be called instead of [`Layout::draw`] by compatible
278 /// parent widgets.
279 ///
280 /// # Implementation
281 ///
282 /// ## Method modification
283 ///
284 /// The `#[widget]` macro injects a call to [`DrawCx::set_id`] into this
285 /// method where possible, allowing correct detection of disabled and
286 /// highlight states.
287 ///
288 /// This method modification should never cause issues (besides the implied
289 /// limitation that widgets cannot easily detect a parent's state while
290 /// being drawn).
291 fn draw_with_offset(&self, draw: DrawCx, viewport: Rect, offset: Offset);
292
293 /// Probe a coordinate for a widget's [`Id`]
294 ///
295 /// # Calling
296 ///
297 /// This method should be called instead of [`Tile::try_probe`].
298 ///
299 /// # Implementation
300 ///
301 /// The default implementation will normally suffice. It calls
302 /// [`Events::probe`] with `coord + offset` when
303 /// `self.rect().contains(coord)`.
304 fn try_probe_with_offset(&self, coord: Coord, offset: Offset) -> Option<Id> {
305 let _ = (coord, offset);
306 unimplemented!() // make rustdoc show that this is a provided method
307 }
308}