qframe/widget/context/
mod.rs1mod 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
18pub struct MeasureCx<'a> {
20 env: &'a Env,
21 layout: LayoutProps,
22 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 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 #[must_use]
39 pub fn env(&self) -> &Env {
40 self.env
41 }
42
43 #[must_use]
45 pub fn layout(&self) -> LayoutProps {
46 self.layout
47 }
48
49 pub fn measure_child<M: 'static>(&mut self, node: &Node<M>, available: Size) -> Size {
51 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 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 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 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 assert!(count.get() <= 5, "measured {} times in one frame, 10 without remembering", count.get());
180 }
181}