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<Rc<str>>,
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 text_content_rc(&self) -> Option<Rc<str>> {
72 self.text_content.clone()
73 }
74
75 pub fn graphics_layer(&self) -> Option<GraphicsLayer> {
76 self.graphics_layer
77 }
78
79 pub fn with_chain_guard(mut self, handle: ModifierChainHandle) -> Self {
80 self.chain_guard = Some(Rc::new(ChainGuard { _handle: handle }));
81 self
82 }
83
84 pub fn clear(&mut self) {
86 self.draw_commands.clear();
87 self.pointer_inputs.clear();
88 self.click_handlers.clear();
89 self.clip_to_bounds = false;
90 self.text_content = None;
91 self.graphics_layer = None;
92 self.chain_guard = None;
93 }
94}
95
96impl fmt::Debug for ModifierNodeSlices {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 f.debug_struct("ModifierNodeSlices")
99 .field("draw_commands", &self.draw_commands.len())
100 .field("pointer_inputs", &self.pointer_inputs.len())
101 .field("click_handlers", &self.click_handlers.len())
102 .field("clip_to_bounds", &self.clip_to_bounds)
103 .field("text_content", &self.text_content)
104 .field("graphics_layer", &self.graphics_layer)
105 .finish()
106 }
107}
108
109pub fn collect_modifier_slices(chain: &ModifierNodeChain) -> ModifierNodeSlices {
111 let mut slices = ModifierNodeSlices::default();
112 collect_modifier_slices_into(chain, &mut slices);
113 slices
114}
115
116pub fn collect_modifier_slices_into(chain: &ModifierNodeChain, slices: &mut ModifierNodeSlices) {
118 slices.clear();
119
120 chain.for_each_node_with_capability(NodeCapabilities::POINTER_INPUT, |_ref, node| {
121 let _any = node.as_any();
122
123 if let Some(handler) = node
128 .as_pointer_input_node()
129 .and_then(|n| n.pointer_input_handler())
130 {
131 slices.pointer_inputs.push(handler);
132 }
133 });
134
135 let background_color = RefCell::new(None);
137 let corner_shape = RefCell::new(None);
138
139 chain.for_each_node_with_capability(NodeCapabilities::DRAW, |_ref, node| {
140 let any = node.as_any();
141
142 if let Some(bg_node) = any.downcast_ref::<BackgroundNode>() {
144 *background_color.borrow_mut() = Some(bg_node.color());
145 if bg_node.shape().is_some() {
148 *corner_shape.borrow_mut() = bg_node.shape();
149 }
150 }
151
152 if let Some(shape_node) = any.downcast_ref::<CornerShapeNode>() {
154 *corner_shape.borrow_mut() = Some(shape_node.shape());
155 }
156
157 if let Some(commands) = any.downcast_ref::<DrawCommandNode>() {
159 slices
160 .draw_commands
161 .extend(commands.commands().iter().cloned());
162 }
163
164 if let Some(draw_node) = node.as_draw_node() {
168 if let Some(closure) = draw_node.create_draw_closure() {
169 slices.draw_commands.push(DrawCommand::Overlay(closure));
171 } else {
172 use cranpose_ui_graphics::{DrawScope as _, DrawScopeDefault};
174 let mut scope = DrawScopeDefault::new(crate::modifier::Size {
175 width: 0.0,
176 height: 0.0,
177 });
178 draw_node.draw(&mut scope);
179 let primitives = scope.into_primitives();
180 if !primitives.is_empty() {
181 let draw_cmd = Rc::new(move |_size: crate::modifier::Size| primitives.clone());
182 slices.draw_commands.push(DrawCommand::Overlay(draw_cmd));
183 }
184 }
185 }
186
187 if let Some(layer_node) = any.downcast_ref::<GraphicsLayerNode>() {
189 slices.graphics_layer = Some(layer_node.layer());
190 }
191
192 if any.is::<ClipToBoundsNode>() {
193 slices.clip_to_bounds = true;
194 }
195 });
196
197 let mut padding = EdgeInsets::default();
199 chain.for_each_node_with_capability(NodeCapabilities::LAYOUT, |_ref, node| {
200 let any = node.as_any();
201 if let Some(padding_node) = any.downcast_ref::<PaddingNode>() {
202 let p = padding_node.padding();
203 padding.left += p.left;
204 padding.top += p.top;
205 padding.right += p.right;
206 padding.bottom += p.bottom;
207 }
208 });
209
210 chain.for_each_node_with_capability(NodeCapabilities::LAYOUT, |_ref, node| {
212 let any = node.as_any();
213 if let Some(text_node) = any.downcast_ref::<TextModifierNode>() {
214 slices.text_content = Some(text_node.text_arc());
216 }
217 if let Some(text_field_node) = any.downcast_ref::<TextFieldModifierNode>() {
219 let text = text_field_node.text();
220 slices.text_content = Some(Rc::from(text));
221
222 text_field_node.set_content_offset(padding.left);
224 text_field_node.set_content_y_offset(padding.top);
225
226 }
229 });
230
231 if let Some(color) = background_color.into_inner() {
233 let shape = corner_shape.into_inner();
234
235 let draw_cmd = Rc::new(move |size: crate::modifier::Size| {
236 use crate::modifier::{Brush, Rect};
237 use cranpose_ui_graphics::DrawPrimitive;
238
239 let brush = Brush::solid(color);
240 let rect = Rect {
241 x: 0.0,
242 y: 0.0,
243 width: size.width,
244 height: size.height,
245 };
246
247 if let Some(shape) = shape {
248 let radii = shape.resolve(size.width, size.height);
249 vec![DrawPrimitive::RoundRect { rect, brush, radii }]
250 } else {
251 vec![DrawPrimitive::Rect { rect, brush }]
252 }
253 });
254
255 slices
256 .draw_commands
257 .insert(0, DrawCommand::Behind(draw_cmd));
258 }
259}
260
261pub fn collect_slices_from_modifier(modifier: &Modifier) -> ModifierNodeSlices {
263 let mut handle = ModifierChainHandle::new();
264 let _ = handle.update(modifier);
265 collect_modifier_slices(handle.chain()).with_chain_guard(handle)
266}