1use crate::{
2 AnyElement, App, Bounds, Element, ElementId, GlobalElementId, InspectorElementId, IntoElement,
3 LayoutId, Pixels, Window,
4};
5use std::rc::Rc;
6
7pub fn measured_element(
12 id: impl Into<ElementId>,
13 child: impl IntoElement,
14 listener: impl Fn(&ElementId, Bounds<Pixels>, Option<&GlobalElementId>, &mut Window, &mut App)
15 + 'static,
16) -> MeasuredElement {
17 MeasuredElement {
18 id: id.into(),
19 child: Some(child.into_any_element()),
20 listener: Rc::new(listener),
21 }
22}
23
24pub struct MeasuredElement {
26 id: ElementId,
27 child: Option<AnyElement>,
28 listener: Rc<
29 dyn Fn(&ElementId, Bounds<Pixels>, Option<&GlobalElementId>, &mut Window, &mut App)
30 + 'static,
31 >,
32}
33
34impl Element for MeasuredElement {
35 type RequestLayoutState = AnyElement;
36 type PrepaintState = ();
37
38 fn id(&self) -> Option<ElementId> {
39 Some(self.id.clone())
40 }
41
42 fn source_location(&self) -> Option<&'static core::panic::Location<'static>> {
43 None
44 }
45
46 fn request_layout(
47 &mut self,
48 _id: Option<&GlobalElementId>,
49 _inspector_id: Option<&InspectorElementId>,
50 window: &mut Window,
51 cx: &mut App,
52 ) -> (LayoutId, Self::RequestLayoutState) {
53 let mut child = self.child.take().expect("measured element child missing");
54 let layout_id = child.request_layout(window, cx);
55 (layout_id, child)
56 }
57
58 fn prepaint(
59 &mut self,
60 global_id: Option<&GlobalElementId>,
61 _inspector_id: Option<&InspectorElementId>,
62 bounds: Bounds<Pixels>,
63 child: &mut Self::RequestLayoutState,
64 window: &mut Window,
65 cx: &mut App,
66 ) {
67 (self.listener)(&self.id, bounds, global_id, window, cx);
68 child.prepaint(window, cx);
69 }
70
71 fn paint(
72 &mut self,
73 _global_id: Option<&GlobalElementId>,
74 _inspector_id: Option<&InspectorElementId>,
75 _bounds: Bounds<Pixels>,
76 child: &mut Self::RequestLayoutState,
77 _prepaint: &mut Self::PrepaintState,
78 window: &mut Window,
79 cx: &mut App,
80 ) {
81 child.paint(window, cx);
82 }
83}
84
85impl IntoElement for MeasuredElement {
86 type Element = Self;
87
88 fn into_element(self) -> Self::Element {
89 self
90 }
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use crate::{
97 Empty, ParentElement as _, Styled as _, TestAppContext, VisualTestContext, div, point, px,
98 size,
99 };
100 use std::{cell::RefCell, rc::Rc};
101
102 #[derive(Clone, Debug)]
103 struct MeasuredBounds {
104 id: ElementId,
105 bounds: Bounds<Pixels>,
106 global_id: Option<String>,
107 }
108
109 #[crate::test]
110 fn measured_element_reports_nested_layout_pass_bounds(cx: &mut TestAppContext) {
111 let reported = Rc::new(RefCell::new(Vec::new()));
112 let window = cx.add_window(|_, _| Empty);
113 let mut cx = VisualTestContext::from_window(window.into(), cx);
114
115 cx.draw(
116 point(px(10.0), px(20.0)),
117 size(px(100.0), px(80.0)),
118 |_, _| {
119 let report_root = reported.clone();
120 let report_child = reported.clone();
121
122 measured_element(
123 "semantic-root".to_string(),
124 div().size_full().child(measured_element(
125 "semantic-child".to_string(),
126 div().w(px(30.0)).h(px(20.0)),
127 move |id, bounds, global_id, _, _| {
128 report_child.borrow_mut().push(MeasuredBounds {
129 id: id.clone(),
130 bounds,
131 global_id: global_id.map(ToString::to_string),
132 });
133 },
134 )),
135 move |id, bounds, global_id, _, _| {
136 report_root.borrow_mut().push(MeasuredBounds {
137 id: id.clone(),
138 bounds,
139 global_id: global_id.map(ToString::to_string),
140 });
141 },
142 )
143 },
144 );
145
146 let reported = reported.borrow();
147 let root = reported
148 .iter()
149 .find(|entry| entry.id == ElementId::from("semantic-root".to_string()))
150 .expect("root measured bounds");
151 let child = reported
152 .iter()
153 .find(|entry| entry.id == ElementId::from("semantic-child".to_string()))
154 .expect("child measured bounds");
155
156 assert_eq!(root.bounds.origin, point(px(10.0), px(20.0)));
157 assert_eq!(root.bounds.size, size(px(100.0), px(80.0)));
158 assert_eq!(child.bounds.origin, point(px(10.0), px(20.0)));
159 assert_eq!(child.bounds.size, size(px(30.0), px(20.0)));
160 assert!(
161 root.global_id
162 .as_deref()
163 .is_some_and(|id| id.contains("semantic-root"))
164 );
165 assert!(
166 child
167 .global_id
168 .as_deref()
169 .is_some_and(|id| id.contains("semantic-child"))
170 );
171 }
172}