1use std::{cell::RefCell, fmt, rc::Rc};
5
6use cranpose_core::{MemoryApplier, NodeError, NodeId};
7use cranpose_foundation::SemanticsConfiguration;
8use cranpose_ui_graphics::{ProjectiveTransform, layer_transform::layer_transform_to_window};
9
10use super::{SemanticsRole, SemanticsText};
11use crate::{
12 modifier::{Modifier, ModifierNodeSlices, Point, ResolvedModifiers, Size},
13 subcompose_layout::SubcomposeLayoutNode,
14 widgets::nodes::{LayoutNode, LayoutState},
15};
16
17#[derive(Debug, Clone)]
19pub struct LayoutNodeData {
20 #[cfg(feature = "inspection")]
22 pub source_trace: Rc<[cranpose_core::source_trace::SourceLocation]>,
23 pub modifier: Modifier,
25 pub resolved_modifiers: ResolvedModifiers,
27 pub modifier_slices: Rc<ModifierNodeSlices>,
30 pub semantics: Option<Rc<SemanticsConfiguration>>,
32 pub kind: LayoutNodeKind,
34 pub z_index: f32,
37}
38
39impl LayoutNodeData {
40 pub fn new(
42 modifier: Modifier,
43 resolved_modifiers: ResolvedModifiers,
44 modifier_slices: Rc<ModifierNodeSlices>,
45 semantics: Option<Rc<SemanticsConfiguration>>,
46 kind: LayoutNodeKind,
47 ) -> Self {
48 Self {
49 #[cfg(feature = "inspection")]
50 source_trace: Rc::default(),
51 modifier,
52 resolved_modifiers,
53 modifier_slices,
54 semantics,
55 kind,
56 z_index: 0.0,
57 }
58 }
59
60 pub fn semantics(&self) -> Option<&SemanticsConfiguration> {
63 self.semantics.as_deref()
64 }
65
66 pub fn resolved_modifiers(&self) -> ResolvedModifiers {
68 self.resolved_modifiers
69 }
70
71 pub fn modifier_slices(&self) -> &ModifierNodeSlices {
74 &self.modifier_slices
75 }
76}
77
78#[derive(Clone)]
85pub enum LayoutNodeKind {
86 Layout,
88 Subcompose,
90 Spacer,
92 Button {
94 on_click: Rc<RefCell<dyn FnMut()>>,
96 },
97 Unknown,
99}
100
101impl fmt::Debug for LayoutNodeKind {
102 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103 match self {
104 Self::Layout => f.write_str("Layout"),
105 Self::Subcompose => f.write_str("Subcompose"),
106 Self::Spacer => f.write_str("Spacer"),
107 Self::Button { .. } => f.write_str("Button"),
108 Self::Unknown => f.write_str("Unknown"),
109 }
110 }
111}
112
113pub(super) fn snapshot_node_data(
114 applier: &mut MemoryApplier,
115 node_id: NodeId,
116 top_left: Point,
117 size: Size,
118 parent_transform: ProjectiveTransform,
119) -> Result<(LayoutNodeData, ProjectiveTransform), NodeError> {
120 let info = runtime_metadata_for(applier, node_id)?;
121 let kind = layout_kind_from_metadata(node_id, &info);
122 let RuntimeNodeMetadata {
123 modifier,
124 resolved_modifiers,
125 modifier_slices,
126 semantics,
127 z_index,
128 ..
129 } = info;
130
131 let window_transform = modifier_slices
132 .graphics_layer()
133 .map_or(parent_transform, |layer| {
134 layer_transform_to_window(
135 parent_transform,
136 top_left,
137 modifier_slices.layer_bounds(size),
138 &layer,
139 )
140 });
141 modifier_slices.publish_window_geometry(top_left, window_transform, size);
142
143 let data = LayoutNodeData {
144 #[cfg(feature = "inspection")]
145 source_trace: applier
146 .with_node::<LayoutNode, _>(node_id, |node| Rc::clone(&node.source_trace))
147 .unwrap_or_default(),
148 modifier,
149 resolved_modifiers,
150 modifier_slices,
151 semantics,
152 kind,
153 z_index,
154 };
155 Ok((data, window_transform))
156}
157
158#[derive(Clone)]
159struct RuntimeNodeMetadata {
160 modifier: Modifier,
161 resolved_modifiers: ResolvedModifiers,
162 modifier_slices: Rc<ModifierNodeSlices>,
163 semantics: Option<Rc<SemanticsConfiguration>>,
164 role: SemanticsRole,
165 z_index: f32,
167 button_handler: Option<Rc<RefCell<dyn FnMut()>>>,
168}
169
170impl Default for RuntimeNodeMetadata {
171 fn default() -> Self {
172 Self {
173 modifier: Modifier::empty(),
174 resolved_modifiers: ResolvedModifiers::default(),
175 modifier_slices: Rc::default(),
176 semantics: None,
177 role: SemanticsRole::Unknown,
178 button_handler: None,
179 z_index: 0.0,
180 }
181 }
182}
183
184pub(super) fn role_from_modifier_slices(modifier_slices: &ModifierNodeSlices) -> SemanticsRole {
185 modifier_slices
186 .annotated_text()
187 .map_or(SemanticsRole::Layout, |text| SemanticsRole::Text {
188 value: SemanticsText(Rc::clone(&text)),
189 })
190}
191
192fn runtime_metadata_for(
193 applier: &mut MemoryApplier,
194 node_id: NodeId,
195) -> Result<RuntimeNodeMetadata, NodeError> {
196 if let Ok(meta) = applier.with_node::<LayoutNode, _>(node_id, |layout| {
197 let modifier = layout.modifier.clone();
198 let resolved_modifiers = layout.resolved_modifiers();
199 let modifier_slices = layout.modifier_slices_snapshot();
200 let role = role_from_modifier_slices(&modifier_slices);
201 let z_index = modifier_slices.z_index() + layout.with_layout_state(LayoutState::z_index);
202
203 RuntimeNodeMetadata {
204 modifier,
205 resolved_modifiers,
206 modifier_slices,
207 semantics: layout.semantics_configuration().map(Rc::new),
208 role,
209 button_handler: None,
210 z_index,
211 }
212 }) {
213 return Ok(meta);
214 }
215
216 if let Ok((modifier, resolved_modifiers, modifier_slices, semantics, z_index)) =
217 applier.with_node::<SubcomposeLayoutNode, _>(node_id, |node| {
218 let modifier_slices = node.modifier_slices_snapshot();
219 let placed = node.layout_parts().layout_state().borrow().z_index();
220 let z_index = modifier_slices.z_index() + placed;
221 (
222 node.modifier(),
223 node.resolved_modifiers(),
224 modifier_slices,
225 node.semantics_configuration().map(Rc::new),
226 z_index,
227 )
228 })
229 {
230 return Ok(RuntimeNodeMetadata {
231 modifier,
232 resolved_modifiers,
233 modifier_slices,
234 semantics,
235 role: SemanticsRole::Subcompose,
236 button_handler: None,
237 z_index,
238 });
239 }
240 Ok(RuntimeNodeMetadata::default())
241}
242
243fn layout_kind_from_metadata(_node_id: NodeId, info: &RuntimeNodeMetadata) -> LayoutNodeKind {
244 match &info.role {
245 SemanticsRole::Layout => LayoutNodeKind::Layout,
246 SemanticsRole::Subcompose => LayoutNodeKind::Subcompose,
247 SemanticsRole::Text { .. } => LayoutNodeKind::Layout,
248 SemanticsRole::Spacer => LayoutNodeKind::Spacer,
249 SemanticsRole::Button => {
250 let handler = info
251 .button_handler
252 .as_ref()
253 .cloned()
254 .unwrap_or_else(|| Rc::new(RefCell::new(|| {})));
255 LayoutNodeKind::Button { on_click: handler }
256 }
257 SemanticsRole::Unknown => LayoutNodeKind::Unknown,
258 }
259}