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