cranpose_ui/modifier/
slices.rs1use std::fmt;
2use std::rc::Rc;
3
4use cranpose_foundation::{ModifierNodeChain, NodeCapabilities, PointerEvent};
5use cranpose_ui_graphics::GraphicsLayer;
6
7use crate::draw::DrawCommand;
8use crate::modifier::Modifier;
9use crate::modifier_nodes::{
10 BackgroundNode, ClipToBoundsNode, CornerShapeNode, DrawCommandNode, GraphicsLayerNode,
11 PaddingNode,
12};
13use crate::text_field_modifier_node::TextFieldModifierNode;
14use crate::text_modifier_node::TextModifierNode;
15use cranpose_ui_graphics::EdgeInsets;
16use std::cell::RefCell;
17
18use super::{ModifierChainHandle, Point};
19
20#[derive(Default)]
22pub struct ModifierNodeSlices {
23 draw_commands: Vec<DrawCommand>,
24 pointer_inputs: Vec<Rc<dyn Fn(PointerEvent)>>,
25 click_handlers: Vec<Rc<dyn Fn(Point)>>,
26 clip_to_bounds: bool,
27 text_content: Option<String>,
28 graphics_layer: Option<GraphicsLayer>,
29 chain_guard: Option<Rc<ChainGuard>>,
30}
31
32struct ChainGuard {
33 _handle: ModifierChainHandle,
34}
35
36impl Clone for ModifierNodeSlices {
37 fn clone(&self) -> Self {
38 Self {
39 draw_commands: self.draw_commands.clone(),
40 pointer_inputs: self.pointer_inputs.clone(),
41 click_handlers: self.click_handlers.clone(),
42 clip_to_bounds: self.clip_to_bounds,
43 text_content: self.text_content.clone(),
44 graphics_layer: self.graphics_layer,
45 chain_guard: self.chain_guard.clone(),
46 }
47 }
48}
49
50impl ModifierNodeSlices {
51 pub fn draw_commands(&self) -> &[DrawCommand] {
52 &self.draw_commands
53 }
54
55 pub fn pointer_inputs(&self) -> &[Rc<dyn Fn(PointerEvent)>] {
56 &self.pointer_inputs
57 }
58
59 pub fn click_handlers(&self) -> &[Rc<dyn Fn(Point)>] {
60 &self.click_handlers
61 }
62
63 pub fn clip_to_bounds(&self) -> bool {
64 self.clip_to_bounds
65 }
66
67 pub fn text_content(&self) -> Option<&str> {
68 self.text_content.as_deref()
69 }
70
71 pub fn graphics_layer(&self) -> Option<GraphicsLayer> {
72 self.graphics_layer
73 }
74
75 pub fn with_chain_guard(mut self, handle: ModifierChainHandle) -> Self {
76 self.chain_guard = Some(Rc::new(ChainGuard { _handle: handle }));
77 self
78 }
79}
80
81impl fmt::Debug for ModifierNodeSlices {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 f.debug_struct("ModifierNodeSlices")
84 .field("draw_commands", &self.draw_commands.len())
85 .field("pointer_inputs", &self.pointer_inputs.len())
86 .field("click_handlers", &self.click_handlers.len())
87 .field("clip_to_bounds", &self.clip_to_bounds)
88 .field("text_content", &self.text_content)
89 .field("graphics_layer", &self.graphics_layer)
90 .finish()
91 }
92}
93
94pub fn collect_modifier_slices(chain: &ModifierNodeChain) -> ModifierNodeSlices {
96 let mut slices = ModifierNodeSlices::default();
97
98 chain.for_each_node_with_capability(NodeCapabilities::POINTER_INPUT, |_ref, node| {
99 let _any = node.as_any();
100
101 if let Some(handler) = node
106 .as_pointer_input_node()
107 .and_then(|n| n.pointer_input_handler())
108 {
109 slices.pointer_inputs.push(handler);
110 }
111 });
112
113 let background_color = RefCell::new(None);
115 let corner_shape = RefCell::new(None);
116
117 chain.for_each_node_with_capability(NodeCapabilities::DRAW, |_ref, node| {
118 let any = node.as_any();
119
120 if let Some(bg_node) = any.downcast_ref::<BackgroundNode>() {
122 *background_color.borrow_mut() = Some(bg_node.color());
123 if bg_node.shape().is_some() {
126 *corner_shape.borrow_mut() = bg_node.shape();
127 }
128 }
129
130 if let Some(shape_node) = any.downcast_ref::<CornerShapeNode>() {
132 *corner_shape.borrow_mut() = Some(shape_node.shape());
133 }
134
135 if let Some(commands) = any.downcast_ref::<DrawCommandNode>() {
137 slices
138 .draw_commands
139 .extend(commands.commands().iter().cloned());
140 }
141
142 if let Some(draw_node) = node.as_draw_node() {
146 if let Some(closure) = draw_node.create_draw_closure() {
147 slices.draw_commands.push(DrawCommand::Overlay(closure));
149 } else {
150 use cranpose_ui_graphics::{DrawScope as _, DrawScopeDefault};
152 let mut scope = DrawScopeDefault::new(crate::modifier::Size {
153 width: 0.0,
154 height: 0.0,
155 });
156 draw_node.draw(&mut scope);
157 let primitives = scope.into_primitives();
158 if !primitives.is_empty() {
159 let draw_cmd = Rc::new(move |_size: crate::modifier::Size| primitives.clone());
160 slices.draw_commands.push(DrawCommand::Overlay(draw_cmd));
161 }
162 }
163 }
164
165 if let Some(layer_node) = any.downcast_ref::<GraphicsLayerNode>() {
167 slices.graphics_layer = Some(layer_node.layer());
168 }
169
170 if any.is::<ClipToBoundsNode>() {
171 slices.clip_to_bounds = true;
172 }
173 });
174
175 let mut padding = EdgeInsets::default();
177 chain.for_each_node_with_capability(NodeCapabilities::LAYOUT, |_ref, node| {
178 let any = node.as_any();
179 if let Some(padding_node) = any.downcast_ref::<PaddingNode>() {
180 let p = padding_node.padding();
181 padding.left += p.left;
182 padding.top += p.top;
183 padding.right += p.right;
184 padding.bottom += p.bottom;
185 }
186 });
187
188 chain.for_each_node_with_capability(NodeCapabilities::LAYOUT, |_ref, node| {
190 let any = node.as_any();
191 if let Some(text_node) = any.downcast_ref::<TextModifierNode>() {
192 slices.text_content = Some(text_node.text().to_string());
194 }
195 if let Some(text_field_node) = any.downcast_ref::<TextFieldModifierNode>() {
197 let text = text_field_node.text();
198 slices.text_content = Some(text.clone());
199
200 text_field_node.set_content_offset(padding.left);
202 text_field_node.set_content_y_offset(padding.top);
203
204 }
207 });
208
209 if let Some(color) = background_color.into_inner() {
211 let shape = corner_shape.into_inner();
212
213 let draw_cmd = Rc::new(move |size: crate::modifier::Size| {
214 use crate::modifier::{Brush, Rect};
215 use cranpose_ui_graphics::DrawPrimitive;
216
217 let brush = Brush::solid(color);
218 let rect = Rect {
219 x: 0.0,
220 y: 0.0,
221 width: size.width,
222 height: size.height,
223 };
224
225 if let Some(shape) = shape {
226 let radii = shape.resolve(size.width, size.height);
227 vec![DrawPrimitive::RoundRect { rect, brush, radii }]
228 } else {
229 vec![DrawPrimitive::Rect { rect, brush }]
230 }
231 });
232
233 slices
234 .draw_commands
235 .insert(0, DrawCommand::Behind(draw_cmd));
236 }
237
238 slices
239}
240
241pub fn collect_slices_from_modifier(modifier: &Modifier) -> ModifierNodeSlices {
243 let mut handle = ModifierChainHandle::new();
244 let _ = handle.update(modifier);
245 collect_modifier_slices(handle.chain()).with_chain_guard(handle)
246}