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