Skip to main content

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}