qframe/widget/mod.rs
1//! The widget model: the [`Widget`] trait, view nodes, layout properties and the contexts
2//! widgets measure, paint and handle events with.
3//!
4//! An application's `view` builds a fresh tree of [`Node`]s every frame through [`View`].
5//! The runtime assigns every node a [`WidgetId`], lays the tree out while painting it, and
6//! keeps the tree until the next frame so input can reach the widgets that were on screen.
7
8mod context;
9mod flex;
10mod id;
11mod idle;
12mod mapped;
13#[cfg(test)]
14mod mapped_rules;
15mod memory;
16#[cfg(test)]
17mod natural_rules;
18mod place;
19mod pointer_shape;
20#[cfg(test)]
21mod sized_rules;
22mod view;
23mod wrap;
24#[cfg(test)]
25mod wrap_rules;
26
27use std::any::{Any, type_name};
28
29pub use context::{EventCx, MeasureCx, PaintCx};
30pub use id::WidgetId;
31pub use pointer_shape::PointerShape;
32pub use view::{NodeMut, View};
33
34pub(crate) use context::{Effects, FocusRequest, Frame, Grounds, Interaction, LayerRecord};
35pub(crate) use flex::{Axis, Flex};
36pub(crate) use id::{IdMap, Key};
37pub(crate) use idle::{IdleScope, IdleWatch};
38pub(crate) use mapped::Reached;
39pub(crate) use memory::Memory;
40
41use mapped::Mapped;
42
43use crate::event::Event;
44use crate::geometry::{Padding, Rect, Size};
45use crate::keymap::Scope;
46
47/// The size `widget` takes when drawn with `env` and allowed up to `available`: what the layout
48/// asks the widget itself, so the answer is exactly the cells it will cover. For deciding a
49/// layout outside `view`, where there is no [`View`], such as where to split a list from its
50/// detail in `update` from the width of the buttons that must fit. Pass
51/// [`Size::MAX`](crate::geometry::Size::MAX) for the widget's natural size.
52///
53/// A widget that fills whatever it is given, such as a key hint bar, answers with `available`.
54///
55/// ```
56/// use qframe::env::Env;
57/// use qframe::geometry::Size;
58/// use qframe::widget::natural_size;
59/// use qframe::widgets::Button;
60///
61/// let env = Env::builtin();
62/// let save = natural_size(&Button::<()>::new("Save"), &env, Size::MAX);
63/// assert_eq!(save.height, 1);
64/// assert!(save.width > 4, "the label and the padding around it");
65/// ```
66#[must_use]
67pub fn natural_size<Msg>(widget: &impl Widget<Msg>, env: &crate::env::Env, available: Size) -> Size {
68 widget.measure(&mut MeasureCx::new(env), available)
69}
70
71/// Something that can be laid out, painted and interacted with.
72///
73/// Widgets are plain values created in `view` every frame. Anything that must survive between
74/// frames and is not application data (a cursor position, a scroll offset) is kept in runtime
75/// memory through [`PaintCx::memory`] and [`EventCx::memory`].
76pub trait Widget<Msg>: 'static {
77 /// The size the widget wants when it may use up to `available`.
78 fn measure(&self, cx: &mut MeasureCx<'_>, available: Size) -> Size;
79
80 /// Draws the widget into `area`.
81 fn paint(&self, cx: &mut PaintCx<'_>, area: Rect);
82
83 /// Draws the widget's overlay after the whole view was painted, when it asked for one with
84 /// [`PaintCx::request_overlay`]. `anchor` is the area the widget was painted in.
85 fn paint_overlay(&self, _cx: &mut PaintCx<'_>, _anchor: Rect) {}
86
87 /// Handles input. Returns `true` when the event was used; unused key and scroll events
88 /// bubble to the parent widget.
89 fn event(&self, _cx: &mut EventCx<'_, Msg>, _event: &Event) -> bool {
90 false
91 }
92
93 /// Whether the widget can take keyboard focus.
94 fn focusable(&self) -> bool {
95 false
96 }
97
98 /// Child nodes, for widgets that contain other widgets.
99 fn children(&self) -> &[Node<Msg>] {
100 &[]
101 }
102
103 /// Mutable child nodes, used to assign ids.
104 fn children_mut(&mut self) -> &mut [Node<Msg>] {
105 &mut []
106 }
107}
108
109/// A widget as a node stores it: one that can also be told apart by its type, so the tree walks
110/// recognise the node of a part built with another message type.
111pub(crate) trait StoredWidget<Msg>: Widget<Msg> + Any {}
112
113impl<Msg, W: Widget<Msg>> StoredWidget<Msg> for W {}
114
115/// A widget whose children are built with a closure, see [`View::add_with`].
116pub trait Container<Msg>: Widget<Msg> {
117 /// Receives the children built for this widget.
118 fn set_children(&mut self, children: Vec<Node<Msg>>);
119}
120
121/// How much space a node takes along one axis.
122#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
123pub enum Length {
124 /// As much as the widget measures.
125 #[default]
126 Auto,
127 /// Exactly this many cells.
128 Cells(u16),
129 /// A share of the space left after `Auto` and `Cells` siblings, by weight.
130 Fill(u16),
131}
132
133/// Where children sit along an axis when there is room left.
134#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
135pub enum Align {
136 /// At the start.
137 #[default]
138 Start,
139 /// In the middle.
140 Center,
141 /// At the end.
142 End,
143}
144
145/// Layout properties of a node.
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
147pub struct LayoutProps {
148 /// Width.
149 pub width: Length,
150 /// Height.
151 pub height: Length,
152 /// Space kept free inside the node.
153 pub padding: Padding,
154 /// Cells between children of a row or column.
155 pub gap: u16,
156 /// Placement of children along the main axis of a row or column, or both axes of a stack.
157 pub justify: Align,
158 /// Placement of children across the main axis.
159 pub align: Align,
160}
161
162/// One widget in the view tree with its layout properties.
163pub struct Node<Msg> {
164 pub(crate) key: Key,
165 pub(crate) id: WidgetId,
166 pub(crate) type_name: &'static str,
167 pub(crate) layout: LayoutProps,
168 pub(crate) persistent: bool,
169 /// `Some(true)` makes the node a text selection region, `Some(false)` keeps selection out.
170 pub(crate) selectable: Option<bool>,
171 /// Keymap actions the node answers while focus is inside it, see [`NodeMut::on_action`].
172 pub(crate) actions: Vec<FocusAction<Msg>>,
173 pub(crate) widget: Box<dyn StoredWidget<Msg>>,
174}
175
176/// A keymap action a node answers with its own message while focus is inside it.
177pub(crate) struct FocusAction<Msg> {
178 pub(crate) asked: Asked,
179 pub(crate) message: Box<dyn Fn() -> Msg>,
180}
181
182/// What a node answers with a [`FocusAction`]: a keymap action, or one of the clipboard keys.
183#[derive(Debug, Clone, PartialEq, Eq)]
184pub(crate) enum Asked {
185 /// The keymap action of this name in this scope, see [`NodeMut::on_action`].
186 Action(Scope, String),
187 /// A clipboard key, see [`NodeMut::on_clipboard`].
188 Clipboard(ClipboardKey),
189}
190
191/// One of the three keys that cut, copy and paste, for a node to claim with
192/// [`NodeMut::on_clipboard`].
193///
194/// They are the keys a text field cuts, copies and pastes its text with, which a list of other
195/// things, such as a file manager's rows, gives its own meaning to while it has focus.
196#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197#[non_exhaustive]
198pub enum ClipboardKey {
199 /// Ctrl+X, the key a text field cuts with.
200 Cut,
201 /// The keys of the global keymap action `copy`, Ctrl+C unless rebound.
202 Copy,
203 /// The keys of the global keymap action `paste`, Ctrl+V unless rebound.
204 Paste,
205}
206
207impl<Msg: 'static> Node<Msg> {
208 pub(crate) fn new<W: Widget<Msg>>(widget: W, index: usize) -> Self {
209 Self {
210 key: Key::Index(index),
211 id: WidgetId::ROOT,
212 type_name: type_name::<W>(),
213 layout: LayoutProps::default(),
214 persistent: false,
215 selectable: None,
216 actions: Vec::new(),
217 widget: Box::new(widget),
218 }
219 }
220
221 /// The node's id; valid once the view has been built.
222 #[must_use]
223 pub fn id(&self) -> WidgetId {
224 self.id
225 }
226
227 /// The node's layout properties.
228 #[must_use]
229 pub fn layout(&self) -> LayoutProps {
230 self.layout
231 }
232
233 /// The message this node sends for the keymap action `action` of `scope` while focus is
234 /// inside it; see [`NodeMut::on_action`].
235 pub(crate) fn answer_action(&self, scope: Scope, action: &str) -> Option<Msg> {
236 self.answer(|asked| matches!(asked, Asked::Action(s, a) if *s == scope && a == action))
237 }
238
239 /// The message this node sends for the clipboard key `key` while focus is inside it; see
240 /// [`NodeMut::on_clipboard`].
241 pub(crate) fn answer_clipboard(&self, key: ClipboardKey) -> Option<Msg> {
242 self.answer(|asked| *asked == Asked::Clipboard(key))
243 }
244
245 fn answer(&self, wanted: impl Fn(&Asked) -> bool) -> Option<Msg> {
246 self.actions.iter().find(|answer| wanted(&answer.asked)).map(|answer| (answer.message)())
247 }
248
249 /// Gives this node and its descendants their ids.
250 pub(crate) fn assign_ids(&mut self, parent: WidgetId) {
251 self.id = parent.child(&self.key, self.type_name);
252 let id = self.id;
253 if let Some(mapped) = (&mut *self.widget as &mut dyn Any).downcast_mut::<Mapped<Msg>>() {
254 mapped.assign_ids(id);
255 return;
256 }
257 for child in self.widget.children_mut() {
258 child.assign_ids(id);
259 }
260 }
261
262 /// Finds the node with `id` in this subtree, also inside parts built with another message
263 /// type.
264 pub(crate) fn find(&self, id: WidgetId) -> Option<Box<dyn Reached<Msg> + '_>> {
265 if self.id == id {
266 return Some(Box::new(self));
267 }
268 if let Some(mapped) = self.mapped() {
269 return mapped.find(id);
270 }
271 self.widget.children().iter().find_map(|child| child.find(id))
272 }
273
274 /// How many focusable widgets this subtree holds, also inside parts built with another
275 /// message type.
276 pub(crate) fn count_focusable(&self) -> usize {
277 let own = usize::from(self.widget.focusable());
278 match self.mapped() {
279 Some(mapped) => own + mapped.count_focusable(),
280 None => own + self.widget.children().iter().map(Self::count_focusable).sum::<usize>(),
281 }
282 }
283
284 /// The part built with another message type this node holds, if it is one.
285 fn mapped(&self) -> Option<&Mapped<Msg>> {
286 (&*self.widget as &dyn Any).downcast_ref::<Mapped<Msg>>()
287 }
288}