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, NavAdvance};
13#[allow(unused)] use kas_macros as macros;
14use kas_macros::autoimpl;
15
16/// The Widget trait
17///
18/// The primary widget trait covers event handling over super trait [`Tile`]
19/// which governs layout, drawing, child enumeration and identification.
20/// Most methods of `Widget` are hidden and only for use within the Kas library.
21///
22/// `Widget` is dyn-safe given a type parameter, e.g. `dyn Widget<Data = ()>`.
23/// [`Tile`] is dyn-safe without a type parameter. [`Node`] is a dyn-safe
24/// abstraction over a `&dyn Widget<Data = T>` plus a `&T` data parameter.
25///
26/// # Widget lifecycle
27///
28/// 1.  The widget is configured ([`Events::configure`]) and immediately updated
29///     ([`Events::update`]).
30/// 2.  The widget has its size-requirements checked by calling
31///     [`Layout::size_rules`] for each axis.
32/// 3.  [`Layout::set_rect`] is called to position elements. This may use data
33///     cached by `size_rules`.
34/// 4.  The widget is updated again after any data change (see [`ConfigCx::update`]).
35/// 5.  The widget is ready for event-handling and drawing
36///     ([`Events::handle_event`], [`Layout::try_probe`], [`Layout::draw`]).
37///
38/// Widgets are responsible for ensuring that their children may observe this
39/// lifecycle. Usually this simply involves inclusion of the child in layout
40/// operations. Steps of the lifecycle may be postponed until a widget becomes
41/// visible.
42///
43/// # Implementing Widget
44///
45/// To implement a widget, use the [`#widget`] macro within an
46/// [`impl_self`](macros::impl_self), [`impl_scope!`](macros::impl_scope) or
47/// [`impl_anon!`](macros::impl_anon) macro.
48/// **This is the only supported method of implementing `Widget`.**
49///
50/// Explicit (partial) implementations of [`Widget`], [`Layout`], [`Tile`] and [`Events`]
51/// are optional. The [`#widget`] macro completes implementations.
52///
53/// Synopsis:
54/// ```ignore
55/// #[impl_self]
56/// mod MyWidget {
57///     #[widget {
58///         // macro properties (all optional)
59///         Data = T;
60///     }]
61///     #[layout(self.foo)]
62///     struct MyWidget {
63///         core: widget_core!(),
64///         #[widget] foo: impl Widget<Data = T> = make_foo(),
65///         // ...
66///     }
67///
68///     // Optional implementations:
69///     impl Layout for Self { /* ... */ }
70///     impl Events for Self { /* ... */ }
71///     impl Self { /* ... */ }
72/// }
73/// ```
74///
75/// Details may be categorised as follows:
76///
77/// -   **Data**: the type [`Widget::Data`] must be specified exactly once, but
78///     this type may be given in any of three locations: as a property of the
79///     [`#widget`] macro or as [`Widget::Data`].
80/// -   **Core** methods of [`Tile`] are *always* implemented via the [`#widget`]
81///     macro, whether or not an `impl Tile { ... }` item is present.
82/// -   **Introspection** methods [`Tile::child_indices`], [`Tile::get_child`]
83///     and [`Widget::child_node`] are implemented by the [`#widget`] macro
84///     in most cases: child widgets embedded within a layout descriptor or
85///     included as fields marked with `#[widget]` are enumerated.
86/// -   **Introspection** methods [`Tile::find_child_index`] and
87///     [`Events::make_child_id`] have default implementations which *usually*
88///     suffice.
89/// -   **Layout** is specified either via [layout syntax](macros::widget#layout-1)
90///     or via implementation of at least [`Layout::size_rules`] and
91///     [`Layout::draw`] (optionally also `set_rect`, `nav_next`, `translation`
92///     and [`Tile::probe`]).
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    /// Do not implement this method directly!
150    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
151    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
152    fn _configure(&mut self, cx: &mut ConfigCx, data: &Self::Data, id: Id);
153
154    /// Internal method: update recursively
155    ///
156    /// Do not implement this method directly!
157    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
158    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
159    fn _update(&mut self, cx: &mut ConfigCx, data: &Self::Data);
160
161    /// Internal method: send recursively
162    ///
163    /// Do not implement this method directly!
164    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
165    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
166    fn _send(&mut self, cx: &mut EventCx, data: &Self::Data, id: Id, event: Event) -> IsUsed;
167
168    /// Internal method: replay recursively
169    ///
170    /// Traverses the widget tree to `id`, then unwinds.
171    /// It is expected that some message is available on the stack.
172    ///
173    /// Do not implement this method directly!
174    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
175    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
176    fn _replay(&mut self, cx: &mut EventCx, data: &Self::Data, id: Id);
177
178    /// Internal method: search for the previous/next navigation target
179    ///
180    /// `focus`: the current focus or starting point.
181    ///
182    /// Do not implement this method directly!
183    #[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
184    #[cfg_attr(docsrs, doc(cfg(internal_doc)))]
185    fn _nav_next(
186        &mut self,
187        cx: &mut ConfigCx,
188        data: &Self::Data,
189        focus: Option<&Id>,
190        advance: NavAdvance,
191    ) -> Option<Id>;
192}