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