Skip to main content

qframe/widget/context/
mod.rs

1//! Contexts passed to widgets while measuring, painting and handling events, and the frame
2//! state the runtime routes input with.
3
4mod animation;
5mod event;
6mod floating;
7mod frame;
8mod paint;
9
10pub(crate) use event::Effects;
11pub use event::EventCx;
12pub(crate) use floating::Grounds;
13pub(crate) use frame::{FocusRequest, Frame, Interaction, LayerRecord, MeasureKey};
14pub use paint::PaintCx;
15
16use crate::env::Env;
17use crate::geometry::Size;
18use crate::widget::{IdMap, LayoutProps, Node, WidgetId};
19
20/// Measuring context.
21pub struct MeasureCx<'a> {
22    env: &'a Env,
23    layout: LayoutProps,
24    /// The sizes measured so far in the frame being painted, when measuring for one.
25    measures: Option<&'a mut IdMap<MeasureKey, Size>>,
26}
27
28impl<'a> MeasureCx<'a> {
29    pub(crate) fn new(env: &'a Env) -> Self {
30        Self { env, layout: LayoutProps::default(), measures: None }
31    }
32
33    /// A context that remembers every size it measures in `measures`, the store of the frame
34    /// being painted, and answers from it when the same node is offered the same space again.
35    pub(crate) fn for_frame(env: &'a Env, measures: &'a mut IdMap<MeasureKey, Size>) -> Self {
36        Self { env, layout: LayoutProps::default(), measures: Some(measures) }
37    }
38
39    /// The environment.
40    #[must_use]
41    pub fn env(&self) -> &Env {
42        self.env
43    }
44
45    /// Layout properties of the widget being measured.
46    #[must_use]
47    pub fn layout(&self) -> LayoutProps {
48        self.layout
49    }
50
51    /// Measures a child node, including its padding.
52    pub fn measure_child<M: 'static>(&mut self, node: &Node<M>, available: Size) -> Size {
53        // Nodes a container never exposed through `children_mut` keep the root id; they are not
54        // told apart reliably, so they are measured every time.
55        let key = (node.id != WidgetId::ROOT).then(|| (node.id, std::ptr::from_ref(node).addr(), available));
56        if let (Some(key), Some(measures)) = (key, self.measures.as_deref())
57            && let Some(size) = measures.get(&key)
58        {
59            return *size;
60        }
61        let size = self.measure_uncached(node, available);
62        if let (Some(key), Some(measures)) = (key, self.measures.as_deref_mut()) {
63            measures.insert(key, size);
64        }
65        size
66    }
67
68    fn measure_uncached<M: 'static>(&mut self, node: &Node<M>, available: Size) -> Size {
69        let padding = node.layout.padding;
70        let inner = Size::new(
71            available.width.saturating_sub(padding.horizontal()),
72            available.height.saturating_sub(padding.vertical()),
73        );
74        let saved = std::mem::replace(&mut self.layout, node.layout);
75        let size = node.widget.measure(self, inner);
76        self.layout = saved;
77        Size::new(
78            size.width.saturating_add(padding.horizontal()).min(available.width),
79            size.height.saturating_add(padding.vertical()).min(available.height),
80        )
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use std::cell::Cell;
87    use std::rc::Rc;
88
89    use super::{MeasureCx, PaintCx};
90    use crate::geometry::{Rect, Size};
91    use crate::runtime::{App, Command, Harness};
92    use crate::style::CellStyle;
93    use crate::widget::{View, Widget};
94    use crate::widgets::Text;
95
96    /// Text that counts how often it is measured.
97    struct Counted(Rc<Cell<u32>>);
98
99    impl Widget<()> for Counted {
100        fn measure(&self, _cx: &mut MeasureCx<'_>, available: Size) -> Size {
101            self.0.set(self.0.get() + 1);
102            Size::new(available.width.min(7), available.height.min(1))
103        }
104
105        fn paint(&self, cx: &mut PaintCx<'_>, area: Rect) {
106            cx.text(area.x, area.y, "counted", CellStyle::default(), area.width);
107        }
108    }
109
110    /// A leaf four containers deep, and two siblings that share a name by mistake.
111    struct Nested(Rc<Cell<u32>>);
112
113    impl App for Nested {
114        type Msg = ();
115        fn update(&mut self, (): ()) -> Command<()> {
116            Command::none()
117        }
118        fn view(&self, ui: &mut View<'_, ()>) {
119            ui.column(|ui| {
120                ui.row(|ui| {
121                    ui.column(|ui| {
122                        ui.row(|ui| {
123                            ui.add(Counted(Rc::clone(&self.0)));
124                        });
125                    });
126                });
127                ui.row(|ui| {
128                    ui.add(Text::new("a")).id("same");
129                    ui.add(Text::new("longer sibling")).id("same");
130                })
131                .gap(1);
132            });
133        }
134    }
135
136    /// Draws its text limited to a number of cells.
137    struct Limited(&'static str, u16);
138
139    impl Widget<()> for Limited {
140        fn measure(&self, _cx: &mut MeasureCx<'_>, available: Size) -> Size {
141            Size::new(available.width, available.height.min(1))
142        }
143
144        fn paint(&self, cx: &mut PaintCx<'_>, area: Rect) {
145            let used = cx.text(area.x, area.y, self.0, CellStyle::default(), self.1);
146            cx.text(area.x + 10, area.y, &used.to_string(), CellStyle::default(), 3);
147        }
148    }
149
150    struct Texts;
151
152    impl App for Texts {
153        type Msg = ();
154        fn update(&mut self, (): ()) -> Command<()> {
155            Command::none()
156        }
157        fn view(&self, ui: &mut View<'_, ()>) {
158            ui.add(Limited("deploys", 4));
159            ui.add(Limited("fade\u{301}d", 4));
160            ui.add(Limited("ok", 4));
161            ui.add(Limited("界ab", 2));
162        }
163    }
164
165    #[test]
166    fn text_stops_at_its_limit_and_keeps_marks_on_the_last_character() {
167        let h = Harness::new(Texts, 14, 4);
168        assert_eq!(h.screen(), "depl      4\nfade\u{301}      4\nok        2\n界        2\n");
169    }
170
171    #[test]
172    fn a_frame_measures_each_node_once_per_offered_space() {
173        let count = Rc::new(Cell::new(0));
174        let mut h = Harness::new(Nested(Rc::clone(&count)), 20, 3);
175        assert_eq!(h.screen(), "counted\na longer sibling\n\n");
176        count.set(0);
177        h.render();
178        // Containers measure their children again before painting them; without remembering
179        // sizes the leaf was measured again at every level above it. What remains are the
180        // different spaces the levels offer.
181        assert!(count.get() <= 5, "measured {} times in one frame, 10 without remembering", count.get());
182    }
183}