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::TextStyle;
14use crate::text_field_modifier_node::TextFieldModifierNode;
15use crate::text_modifier_node::TextModifierNode;
16use cranpose_ui_graphics::EdgeInsets;
17use std::cell::RefCell;
18
19use super::{ModifierChainHandle, Point};
20
21#[derive(Default)]
23pub struct ModifierNodeSlices {
24 draw_commands: Vec<DrawCommand>,
25 pointer_inputs: Vec<Rc<dyn Fn(PointerEvent)>>,
26 click_handlers: Vec<Rc<dyn Fn(Point)>>,
27 clip_to_bounds: bool,
28 text_content: Option<Rc<str>>,
29 text_style: Option<TextStyle>,
30 graphics_layer: Option<GraphicsLayer>,
31 chain_guard: Option<Rc<ChainGuard>>,
32}
33
34struct ChainGuard {
35 _handle: ModifierChainHandle,
36}
37
38impl Clone for ModifierNodeSlices {
39 fn clone(&self) -> Self {
40 Self {
41 draw_commands: self.draw_commands.clone(),
42 pointer_inputs: self.pointer_inputs.clone(),
43 click_handlers: self.click_handlers.clone(),
44 clip_to_bounds: self.clip_to_bounds,
45 text_content: self.text_content.clone(),
46 text_style: self.text_style.clone(),
47 graphics_layer: self.graphics_layer,
48 chain_guard: self.chain_guard.clone(),
49 }
50 }
51}
52
53impl ModifierNodeSlices {
54 pub fn draw_commands(&self) -> &[DrawCommand] {
55 &self.draw_commands
56 }
57
58 pub fn pointer_inputs(&self) -> &[Rc<dyn Fn(PointerEvent)>] {
59 &self.pointer_inputs
60 }
61
62 pub fn click_handlers(&self) -> &[Rc<dyn Fn(Point)>] {
63 &self.click_handlers
64 }
65
66 pub fn clip_to_bounds(&self) -> bool {
67 self.clip_to_bounds
68 }
69
70 pub fn text_content(&self) -> Option<&str> {
71 self.text_content.as_deref()
72 }
73
74 pub fn text_content_rc(&self) -> Option<Rc<str>> {
75 self.text_content.clone()
76 }
77
78 pub fn text_style(&self) -> Option<&TextStyle> {
79 self.text_style.as_ref()
80 }
81
82 pub fn graphics_layer(&self) -> Option<GraphicsLayer> {
83 self.graphics_layer
84 }
85
86 pub fn with_chain_guard(mut self, handle: ModifierChainHandle) -> Self {
87 self.chain_guard = Some(Rc::new(ChainGuard { _handle: handle }));
88 self
89 }
90
91 pub fn clear(&mut self) {
93 self.draw_commands.clear();
94 self.pointer_inputs.clear();
95 self.click_handlers.clear();
96 self.clip_to_bounds = false;
97 self.text_content = None;
98 self.text_style = None;
99 self.graphics_layer = None;
100 self.chain_guard = None;
101 }
102}
103
104impl fmt::Debug for ModifierNodeSlices {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 f.debug_struct("ModifierNodeSlices")
107 .field("draw_commands", &self.draw_commands.len())
108 .field("pointer_inputs", &self.pointer_inputs.len())
109 .field("click_handlers", &self.click_handlers.len())
110 .field("clip_to_bounds", &self.clip_to_bounds)
111 .field("text_content", &self.text_content)
112 .field("text_style", &self.text_style)
113 .field("graphics_layer", &self.graphics_layer)
114 .finish()
115 }
116}
117
118pub fn collect_modifier_slices(chain: &ModifierNodeChain) -> ModifierNodeSlices {
120 let mut slices = ModifierNodeSlices::default();
121 collect_modifier_slices_into(chain, &mut slices);
122 slices
123}
124
125pub fn collect_modifier_slices_into(chain: &ModifierNodeChain, slices: &mut ModifierNodeSlices) {
127 slices.clear();
128
129 chain.for_each_node_with_capability(NodeCapabilities::POINTER_INPUT, |_ref, node| {
130 let _any = node.as_any();
131
132 if let Some(handler) = node
137 .as_pointer_input_node()
138 .and_then(|n| n.pointer_input_handler())
139 {
140 slices.pointer_inputs.push(handler);
141 }
142 });
143
144 let background_color = RefCell::new(None);
146 let corner_shape = RefCell::new(None);
147
148 chain.for_each_node_with_capability(NodeCapabilities::DRAW, |_ref, node| {
149 let any = node.as_any();
150
151 if let Some(bg_node) = any.downcast_ref::<BackgroundNode>() {
153 *background_color.borrow_mut() = Some(bg_node.color());
154 if bg_node.shape().is_some() {
157 *corner_shape.borrow_mut() = bg_node.shape();
158 }
159 }
160
161 if let Some(shape_node) = any.downcast_ref::<CornerShapeNode>() {
163 *corner_shape.borrow_mut() = Some(shape_node.shape());
164 }
165
166 if let Some(commands) = any.downcast_ref::<DrawCommandNode>() {
168 slices
169 .draw_commands
170 .extend(commands.commands().iter().cloned());
171 }
172
173 if let Some(draw_node) = node.as_draw_node() {
177 if let Some(closure) = draw_node.create_draw_closure() {
178 slices.draw_commands.push(DrawCommand::Overlay(closure));
180 } else {
181 use cranpose_ui_graphics::{DrawScope as _, DrawScopeDefault};
183 let mut scope = DrawScopeDefault::new(crate::modifier::Size {
184 width: 0.0,
185 height: 0.0,
186 });
187 draw_node.draw(&mut scope);
188 let primitives = scope.into_primitives();
189 if !primitives.is_empty() {
190 let draw_cmd = Rc::new(move |_size: crate::modifier::Size| primitives.clone());
191 slices.draw_commands.push(DrawCommand::Overlay(draw_cmd));
192 }
193 }
194 }
195
196 if let Some(layer_node) = any.downcast_ref::<GraphicsLayerNode>() {
198 slices.graphics_layer = Some(layer_node.layer());
199 }
200
201 if any.is::<ClipToBoundsNode>() {
202 slices.clip_to_bounds = true;
203 }
204 });
205
206 let mut padding = EdgeInsets::default();
208 chain.for_each_node_with_capability(NodeCapabilities::LAYOUT, |_ref, node| {
209 let any = node.as_any();
210 if let Some(padding_node) = any.downcast_ref::<PaddingNode>() {
211 let p = padding_node.padding();
212 padding.left += p.left;
213 padding.top += p.top;
214 padding.right += p.right;
215 padding.bottom += p.bottom;
216 }
217 });
218
219 chain.for_each_node_with_capability(NodeCapabilities::LAYOUT, |_ref, node| {
221 let any = node.as_any();
222 if let Some(text_node) = any.downcast_ref::<TextModifierNode>() {
223 slices.text_content = Some(text_node.text_arc());
225 slices.text_style = Some(text_node.style().clone());
226 }
227 if let Some(text_field_node) = any.downcast_ref::<TextFieldModifierNode>() {
229 let text = text_field_node.text();
230 slices.text_content = Some(Rc::from(text));
231
232 text_field_node.set_content_offset(padding.left);
234 text_field_node.set_content_y_offset(padding.top);
235
236 }
239 });
240
241 if let Some(color) = background_color.into_inner() {
243 let shape = corner_shape.into_inner();
244
245 let draw_cmd = Rc::new(move |size: crate::modifier::Size| {
246 use crate::modifier::{Brush, Rect};
247 use cranpose_ui_graphics::DrawPrimitive;
248
249 let brush = Brush::solid(color);
250 let rect = Rect {
251 x: 0.0,
252 y: 0.0,
253 width: size.width,
254 height: size.height,
255 };
256
257 if let Some(shape) = shape {
258 let radii = shape.resolve(size.width, size.height);
259 vec![DrawPrimitive::RoundRect { rect, brush, radii }]
260 } else {
261 vec![DrawPrimitive::Rect { rect, brush }]
262 }
263 });
264
265 slices
266 .draw_commands
267 .insert(0, DrawCommand::Behind(draw_cmd));
268 }
269}
270
271pub fn collect_slices_from_modifier(modifier: &Modifier) -> ModifierNodeSlices {
273 let mut handle = ModifierChainHandle::new();
274 let _ = handle.update(modifier);
275 collect_modifier_slices(handle.chain()).with_chain_guard(handle)
276}