1use std::{
2 cell::Cell,
3 hash::{Hash, Hasher},
4 rc::Rc,
5};
6
7use cranpose_core::NodeId;
8use cranpose_foundation::{
9 Constraints, DelegatableNode, DrawModifierNode, DrawScope, InvalidationKind,
10 LayoutModifierNode, Measurable, ModifierNode, ModifierNodeContext, ModifierNodeElement,
11 NodeCapabilities, NodeState, PointerEvent, PointerEventKind, PointerInputNode, Size,
12};
13use cranpose_ui_graphics::PointerIcon;
14use cranpose_ui_layout::{Alignment, HorizontalAlignment, IntrinsicSize, VerticalAlignment};
15
16use crate::{
17 draw::DrawCommand,
18 modifier::{
19 BlendMode, Color, ColorFilter, CompositingStrategy, EdgeInsets, GraphicsLayer,
20 LayoutWeight, Point, RoundedCornerShape,
21 },
22};
23
24fn hash_f32_value<H: Hasher>(state: &mut H, value: f32) {
25 state.write_u32(value.to_bits());
26}
27
28fn hash_option_f32<H: Hasher>(state: &mut H, value: Option<f32>) {
29 match value {
30 Some(v) => {
31 state.write_u8(1);
32 hash_f32_value(state, v);
33 }
34 None => state.write_u8(0),
35 }
36}
37
38fn hash_graphics_layer<H: Hasher>(state: &mut H, layer: &GraphicsLayer) {
39 hash_f32_value(state, layer.alpha);
40 hash_f32_value(state, layer.scale);
41 hash_f32_value(state, layer.scale_x);
42 hash_f32_value(state, layer.scale_y);
43 hash_f32_value(state, layer.rotation_x);
44 hash_f32_value(state, layer.rotation_y);
45 hash_f32_value(state, layer.rotation_z);
46 hash_f32_value(state, layer.camera_distance);
47 hash_f32_value(state, layer.transform_origin.pivot_fraction_x);
48 hash_f32_value(state, layer.transform_origin.pivot_fraction_y);
49 hash_f32_value(state, layer.translation_x);
50 hash_f32_value(state, layer.translation_y);
51 hash_f32_value(state, layer.shadow_elevation);
52 hash_f32_value(state, layer.ambient_shadow_color.r());
53 hash_f32_value(state, layer.ambient_shadow_color.g());
54 hash_f32_value(state, layer.ambient_shadow_color.b());
55 hash_f32_value(state, layer.ambient_shadow_color.a());
56 hash_f32_value(state, layer.spot_shadow_color.r());
57 hash_f32_value(state, layer.spot_shadow_color.g());
58 hash_f32_value(state, layer.spot_shadow_color.b());
59 hash_f32_value(state, layer.spot_shadow_color.a());
60 match layer.shape {
61 crate::modifier::LayerShape::Rectangle => {
62 state.write_u8(0);
63 }
64 crate::modifier::LayerShape::Rounded(shape) => {
65 state.write_u8(1);
66 let radii = shape.radii();
67 hash_f32_value(state, radii.top_left);
68 hash_f32_value(state, radii.top_right);
69 hash_f32_value(state, radii.bottom_right);
70 hash_f32_value(state, radii.bottom_left);
71 }
72 }
73 state.write_u8(layer.clip as u8);
74 match layer.color_filter {
75 Some(ColorFilter::Tint(color)) => {
76 state.write_u8(1);
77 hash_f32_value(state, color.r());
78 hash_f32_value(state, color.g());
79 hash_f32_value(state, color.b());
80 hash_f32_value(state, color.a());
81 }
82 Some(ColorFilter::Modulate(color)) => {
83 state.write_u8(2);
84 hash_f32_value(state, color.r());
85 hash_f32_value(state, color.g());
86 hash_f32_value(state, color.b());
87 hash_f32_value(state, color.a());
88 }
89 Some(ColorFilter::Matrix(matrix)) => {
90 state.write_u8(3);
91 for value in matrix {
92 hash_f32_value(state, value);
93 }
94 }
95 None => state.write_u8(0),
96 }
97 state.write_u8(layer.render_effect.is_some() as u8);
98 state.write_u8(layer.backdrop_effect.is_some() as u8);
99 let compositing_tag = match layer.compositing_strategy {
100 CompositingStrategy::Auto => 0,
101 CompositingStrategy::Offscreen => 1,
102 CompositingStrategy::ModulateAlpha => 2,
103 };
104 state.write_u8(compositing_tag);
105 let blend_tag = match layer.blend_mode {
106 BlendMode::Clear => 0,
107 BlendMode::Src => 1,
108 BlendMode::Dst => 2,
109 BlendMode::SrcOver => 3,
110 BlendMode::DstOver => 4,
111 BlendMode::SrcIn => 5,
112 BlendMode::DstIn => 6,
113 BlendMode::SrcOut => 7,
114 BlendMode::DstOut => 8,
115 BlendMode::SrcAtop => 9,
116 BlendMode::DstAtop => 10,
117 BlendMode::Xor => 11,
118 BlendMode::Plus => 12,
119 BlendMode::Modulate => 13,
120 BlendMode::Screen => 14,
121 BlendMode::Overlay => 15,
122 BlendMode::Darken => 16,
123 BlendMode::Lighten => 17,
124 BlendMode::ColorDodge => 18,
125 BlendMode::ColorBurn => 19,
126 BlendMode::HardLight => 20,
127 BlendMode::SoftLight => 21,
128 BlendMode::Difference => 22,
129 BlendMode::Exclusion => 23,
130 BlendMode::Multiply => 24,
131 BlendMode::Hue => 25,
132 BlendMode::Saturation => 26,
133 BlendMode::Color => 27,
134 BlendMode::Luminosity => 28,
135 };
136 state.write_u8(blend_tag);
137}
138
139fn hash_horizontal_alignment<H: Hasher>(state: &mut H, alignment: HorizontalAlignment) {
140 let tag = match alignment {
141 HorizontalAlignment::Start => 0,
142 HorizontalAlignment::CenterHorizontally => 1,
143 HorizontalAlignment::End => 2,
144 };
145 state.write_u8(tag);
146}
147
148fn hash_vertical_alignment<H: Hasher>(state: &mut H, alignment: VerticalAlignment) {
149 let tag = match alignment {
150 VerticalAlignment::Top => 0,
151 VerticalAlignment::CenterVertically => 1,
152 VerticalAlignment::Bottom => 2,
153 };
154 state.write_u8(tag);
155}
156
157fn hash_alignment<H: Hasher>(state: &mut H, alignment: Alignment) {
158 hash_horizontal_alignment(state, alignment.horizontal);
159 hash_vertical_alignment(state, alignment.vertical);
160}
161
162macro_rules! impl_layout_modifier_node {
163 ($ty:ty) => {
164 impl ModifierNode for $ty {
165 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
166 Some(self)
167 }
168
169 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
170 Some(self)
171 }
172 }
173 };
174 ($ty:ty, invalidate = $invalidation:expr) => {
175 impl ModifierNode for $ty {
176 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
177 context.invalidate($invalidation);
178 }
179
180 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
181 Some(self)
182 }
183
184 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
185 Some(self)
186 }
187 }
188 };
189}
190pub(crate) use impl_layout_modifier_node;
191
192macro_rules! impl_draw_modifier_node {
193 ($ty:ty) => {
194 impl ModifierNode for $ty {
195 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
196 context.invalidate(InvalidationKind::Draw);
197 }
198
199 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
200 Some(self)
201 }
202
203 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
204 Some(self)
205 }
206 }
207
208 impl DrawModifierNode for $ty {
209 fn draw(&self, _draw_scope: &mut dyn DrawScope) {}
210 }
211 };
212}
213
214macro_rules! forward_intrinsics_to_child {
215 () => {
216 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
217 measurable.min_intrinsic_width(height)
218 }
219
220 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
221 measurable.max_intrinsic_width(height)
222 }
223
224 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
225 measurable.min_intrinsic_height(width)
226 }
227
228 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
229 measurable.max_intrinsic_height(width)
230 }
231 };
232}
233
234macro_rules! impl_sink_reporter_element {
235 ($element:ty, $node:ty) => {
236 impl ModifierNodeElement for $element {
237 type Node = $node;
238
239 fn create(&self) -> Self::Node {
240 <$node>::new(self.sink.clone())
241 }
242
243 fn update(&self, node: &mut Self::Node) {
244 node.sink = self.sink.clone();
245 }
246
247 fn capabilities(&self) -> NodeCapabilities {
248 NodeCapabilities::LAYOUT
249 }
250 }
251 };
252}
253
254fn measure_pass_through(
255 measurable: &dyn Measurable,
256 constraints: Constraints,
257 offset: impl FnOnce(Size) -> (f32, f32),
258) -> cranpose_ui_layout::LayoutModifierMeasureResult {
259 let placeable = measurable.measure(constraints);
260 let size = Size {
261 width: placeable.width(),
262 height: placeable.height(),
263 };
264 let (x, y) = offset(size);
265 cranpose_ui_layout::LayoutModifierMeasureResult::new(size, x, y)
266}
267
268fn attach_draw_observer(
269 node_id_cell: &Cell<Option<NodeId>>,
270 context: &mut dyn ModifierNodeContext,
271) {
272 node_id_cell.set(context.node_id());
273 context.invalidate(InvalidationKind::Draw);
274}
275
276fn detach_draw_observer(node_id_cell: &Cell<Option<NodeId>>) {
277 if let Some(node_id) = node_id_cell.replace(None) {
278 crate::render_state::clear_draw_observations_for_node(node_id);
279 }
280}
281
282enum SizeAxis {
283 Width,
284 Height,
285}
286
287fn size_intrinsic(
288 target: Constraints,
289 axis: SizeAxis,
290 enforce_incoming: bool,
291 cross: f32,
292 intrinsic: impl FnOnce(f32) -> f32,
293) -> f32 {
294 let (target_min, target_max, cross_min, cross_max) = match axis {
295 SizeAxis::Width => (
296 target.min_width,
297 target.max_width,
298 target.min_height,
299 target.max_height,
300 ),
301 SizeAxis::Height => (
302 target.min_height,
303 target.max_height,
304 target.min_width,
305 target.max_width,
306 ),
307 };
308 if target_min == target_max && target_max != f32::INFINITY {
309 target_max
310 } else {
311 let child_cross = if enforce_incoming {
312 cross
313 } else {
314 cross.clamp(cross_min, cross_max)
315 };
316 intrinsic(child_cross).clamp(target_min, target_max)
317 }
318}
319
320#[derive(Debug)]
322pub struct PaddingNode {
323 padding: EdgeInsets,
324 state: NodeState,
325}
326
327impl PaddingNode {
328 pub fn new(padding: EdgeInsets) -> Self {
329 Self {
330 padding,
331 state: NodeState::new(),
332 }
333 }
334
335 pub fn padding(&self) -> EdgeInsets {
336 self.padding
337 }
338}
339
340impl DelegatableNode for PaddingNode {
341 fn node_state(&self) -> &NodeState {
342 &self.state
343 }
344}
345
346impl_layout_modifier_node!(PaddingNode, invalidate = InvalidationKind::Layout);
347
348impl LayoutModifierNode for PaddingNode {
349 fn measure(
350 &self,
351 _context: &mut dyn ModifierNodeContext,
352 measurable: &dyn Measurable,
353 constraints: Constraints,
354 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
355 let horizontal_padding = self.padding.horizontal_sum();
356 let vertical_padding = self.padding.vertical_sum();
357
358 let inner_constraints = Constraints {
359 min_width: (constraints.min_width - horizontal_padding).max(0.0),
360 max_width: (constraints.max_width - horizontal_padding).max(0.0),
361 min_height: (constraints.min_height - vertical_padding).max(0.0),
362 max_height: (constraints.max_height - vertical_padding).max(0.0),
363 };
364
365 let inner_placeable = measurable.measure(inner_constraints);
366 let inner_width = inner_placeable.width();
367 let inner_height = inner_placeable.height();
368
369 let (width, height) = constraints.constrain(
370 inner_width + horizontal_padding,
371 inner_height + vertical_padding,
372 );
373
374 cranpose_ui_layout::LayoutModifierMeasureResult::new(
375 Size { width, height },
376 self.padding.left,
377 self.padding.top,
378 )
379 }
380
381 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
382 let vertical_padding = self.padding.vertical_sum();
383 let inner_height = (height - vertical_padding).max(0.0);
384 let inner_width = measurable.min_intrinsic_width(inner_height);
385 inner_width + self.padding.horizontal_sum()
386 }
387
388 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
389 let vertical_padding = self.padding.vertical_sum();
390 let inner_height = (height - vertical_padding).max(0.0);
391 let inner_width = measurable.max_intrinsic_width(inner_height);
392 inner_width + self.padding.horizontal_sum()
393 }
394
395 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
396 let horizontal_padding = self.padding.horizontal_sum();
397 let inner_width = (width - horizontal_padding).max(0.0);
398 let inner_height = measurable.min_intrinsic_height(inner_width);
399 inner_height + self.padding.vertical_sum()
400 }
401
402 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
403 let horizontal_padding = self.padding.horizontal_sum();
404 let inner_width = (width - horizontal_padding).max(0.0);
405 let inner_height = measurable.max_intrinsic_height(inner_width);
406 inner_height + self.padding.vertical_sum()
407 }
408}
409
410#[derive(Debug, Clone, PartialEq)]
412pub struct PaddingElement {
413 padding: EdgeInsets,
414}
415
416impl PaddingElement {
417 pub fn new(padding: EdgeInsets) -> Self {
418 Self { padding }
419 }
420}
421
422impl Hash for PaddingElement {
423 fn hash<H: Hasher>(&self, state: &mut H) {
424 hash_f32_value(state, self.padding.left);
425 hash_f32_value(state, self.padding.top);
426 hash_f32_value(state, self.padding.right);
427 hash_f32_value(state, self.padding.bottom);
428 }
429}
430
431impl ModifierNodeElement for PaddingElement {
432 type Node = PaddingNode;
433
434 fn create(&self) -> Self::Node {
435 PaddingNode::new(self.padding)
436 }
437
438 fn update(&self, node: &mut Self::Node) {
439 if node.padding != self.padding {
440 node.padding = self.padding;
441 }
442 }
443
444 fn capabilities(&self) -> NodeCapabilities {
445 NodeCapabilities::LAYOUT
446 }
447}
448
449#[derive(Debug)]
451pub struct BackgroundNode {
452 color: Color,
453 shape: Option<RoundedCornerShape>,
454 state: NodeState,
455}
456
457impl BackgroundNode {
458 pub fn new(color: Color) -> Self {
459 Self {
460 color,
461 shape: None,
462 state: NodeState::new(),
463 }
464 }
465
466 pub fn color(&self) -> Color {
467 self.color
468 }
469
470 pub fn shape(&self) -> Option<RoundedCornerShape> {
471 self.shape
472 }
473}
474
475impl DelegatableNode for BackgroundNode {
476 fn node_state(&self) -> &NodeState {
477 &self.state
478 }
479}
480
481impl_draw_modifier_node!(BackgroundNode);
482
483#[derive(Debug, Clone, PartialEq)]
485pub struct BackgroundElement {
486 color: Color,
487}
488
489impl BackgroundElement {
490 pub fn new(color: Color) -> Self {
491 Self { color }
492 }
493}
494
495impl Hash for BackgroundElement {
496 fn hash<H: Hasher>(&self, state: &mut H) {
497 hash_f32_value(state, self.color.0);
498 hash_f32_value(state, self.color.1);
499 hash_f32_value(state, self.color.2);
500 hash_f32_value(state, self.color.3);
501 }
502}
503
504impl ModifierNodeElement for BackgroundElement {
505 type Node = BackgroundNode;
506
507 fn create(&self) -> Self::Node {
508 BackgroundNode::new(self.color)
509 }
510
511 fn update(&self, node: &mut Self::Node) {
512 if node.color != self.color {
513 node.color = self.color;
514 }
515 }
516
517 fn capabilities(&self) -> NodeCapabilities {
518 NodeCapabilities::DRAW
519 }
520}
521
522#[derive(Debug)]
524pub struct CornerShapeNode {
525 shape: RoundedCornerShape,
526 state: NodeState,
527}
528
529impl CornerShapeNode {
530 pub fn new(shape: RoundedCornerShape) -> Self {
531 Self {
532 shape,
533 state: NodeState::new(),
534 }
535 }
536
537 pub fn shape(&self) -> RoundedCornerShape {
538 self.shape
539 }
540}
541
542impl DelegatableNode for CornerShapeNode {
543 fn node_state(&self) -> &NodeState {
544 &self.state
545 }
546}
547
548impl_draw_modifier_node!(CornerShapeNode);
549
550#[derive(Debug, Clone, PartialEq)]
552pub struct CornerShapeElement {
553 shape: RoundedCornerShape,
554}
555
556impl CornerShapeElement {
557 pub fn new(shape: RoundedCornerShape) -> Self {
558 Self { shape }
559 }
560}
561
562impl Hash for CornerShapeElement {
563 fn hash<H: Hasher>(&self, state: &mut H) {
564 let radii = self.shape.radii();
565 hash_f32_value(state, radii.top_left);
566 hash_f32_value(state, radii.top_right);
567 hash_f32_value(state, radii.bottom_right);
568 hash_f32_value(state, radii.bottom_left);
569 }
570}
571
572impl ModifierNodeElement for CornerShapeElement {
573 type Node = CornerShapeNode;
574
575 fn create(&self) -> Self::Node {
576 CornerShapeNode::new(self.shape)
577 }
578
579 fn update(&self, node: &mut Self::Node) {
580 if node.shape != self.shape {
581 node.shape = self.shape;
582 }
583 }
584
585 fn capabilities(&self) -> NodeCapabilities {
586 NodeCapabilities::DRAW
587 }
588}
589
590pub struct GraphicsLayerNode {
591 layer: GraphicsLayer,
592 layer_resolver: Option<Rc<dyn Fn() -> GraphicsLayer>>,
593 node_id: Rc<Cell<Option<NodeId>>>,
594 state: NodeState,
595}
596
597impl GraphicsLayerNode {
598 pub fn new(layer: GraphicsLayer) -> Self {
599 Self {
600 layer,
601 layer_resolver: None,
602 node_id: Rc::new(Cell::new(None)),
603 state: NodeState::new(),
604 }
605 }
606
607 pub fn new_lazy(layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) -> Self {
608 Self {
609 layer: GraphicsLayer::default(),
610 layer_resolver: Some(layer_resolver),
611 node_id: Rc::new(Cell::new(None)),
612 state: NodeState::new(),
613 }
614 }
615
616 #[cfg(test)]
617 pub fn layer(&self) -> GraphicsLayer {
618 if let Some(resolve) = self.layer_resolver() {
619 resolve()
620 } else {
621 self.layer.clone()
622 }
623 }
624
625 pub fn layer_snapshot(&self) -> GraphicsLayer {
626 self.layer.clone()
627 }
628
629 pub fn layer_resolver(&self) -> Option<Rc<dyn Fn() -> GraphicsLayer>> {
630 self.layer_resolver.as_ref().map(|resolve| {
631 let resolve = resolve.clone();
632 let node_id = Rc::clone(&self.node_id);
633 Rc::new(move || {
634 if let Some(node_id) = node_id.get() {
635 let scope = crate::render_state::DrawObservationScope::new(node_id, usize::MAX);
636 crate::render_state::observe_draw_reads(scope, || resolve())
637 } else {
638 resolve()
639 }
640 }) as Rc<dyn Fn() -> GraphicsLayer>
641 })
642 }
643
644 fn set_static(&mut self, layer: GraphicsLayer) {
645 let changed = self.layer != layer || self.layer_resolver.is_some();
646 self.layer = layer;
647 self.layer_resolver = None;
648 if changed && let Some(node_id) = self.node_id.get() {
649 crate::render_state::schedule_draw_repass(node_id);
650 }
651 }
652
653 fn set_lazy(&mut self, layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) {
654 let changed = self
655 .layer_resolver
656 .as_ref()
657 .is_none_or(|current| !Rc::ptr_eq(current, &layer_resolver));
658 self.layer_resolver = Some(layer_resolver);
659 if changed && let Some(node_id) = self.node_id.get() {
660 crate::render_state::schedule_draw_repass(node_id);
661 }
662 }
663}
664
665impl DelegatableNode for GraphicsLayerNode {
666 fn node_state(&self) -> &NodeState {
667 &self.state
668 }
669}
670
671impl ModifierNode for GraphicsLayerNode {
672 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
673 attach_draw_observer(&self.node_id, context);
674 }
675
676 fn on_detach(&mut self) {
677 detach_draw_observer(&self.node_id);
678 }
679}
680
681impl std::fmt::Debug for GraphicsLayerNode {
682 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
683 f.debug_struct("GraphicsLayerNode")
684 .field("layer", &self.layer)
685 .field("lazy", &self.layer_resolver.is_some())
686 .finish()
687 }
688}
689
690#[derive(Debug, Clone, PartialEq)]
691pub struct GraphicsLayerElement {
692 layer: GraphicsLayer,
693}
694
695impl GraphicsLayerElement {
696 pub fn new(layer: GraphicsLayer) -> Self {
697 Self { layer }
698 }
699}
700
701impl Hash for GraphicsLayerElement {
702 fn hash<H: Hasher>(&self, state: &mut H) {
703 hash_graphics_layer(state, &self.layer);
704 }
705}
706
707impl ModifierNodeElement for GraphicsLayerElement {
708 type Node = GraphicsLayerNode;
709
710 fn create(&self) -> Self::Node {
711 GraphicsLayerNode::new(self.layer.clone())
712 }
713
714 fn update(&self, node: &mut Self::Node) {
715 node.set_static(self.layer.clone());
716 }
717
718 fn capabilities(&self) -> NodeCapabilities {
719 NodeCapabilities::DRAW
720 }
721}
722
723#[derive(Clone)]
724pub struct LazyGraphicsLayerElement {
725 layer_resolver: Rc<dyn Fn() -> GraphicsLayer>,
726}
727
728impl LazyGraphicsLayerElement {
729 pub fn new(layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) -> Self {
730 Self { layer_resolver }
731 }
732}
733
734impl std::fmt::Debug for LazyGraphicsLayerElement {
735 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
736 f.debug_struct("LazyGraphicsLayerElement")
737 .field("resolver", &"<closure>")
738 .finish()
739 }
740}
741
742impl PartialEq for LazyGraphicsLayerElement {
743 fn eq(&self, other: &Self) -> bool {
744 Rc::ptr_eq(&self.layer_resolver, &other.layer_resolver)
745 }
746}
747
748impl Eq for LazyGraphicsLayerElement {}
749
750impl Hash for LazyGraphicsLayerElement {
751 fn hash<H: Hasher>(&self, state: &mut H) {
752 let ptr = Rc::as_ptr(&self.layer_resolver) as *const ();
753 ptr.hash(state);
754 }
755}
756
757impl ModifierNodeElement for LazyGraphicsLayerElement {
758 type Node = GraphicsLayerNode;
759
760 fn create(&self) -> Self::Node {
761 GraphicsLayerNode::new_lazy(self.layer_resolver.clone())
762 }
763
764 fn update(&self, node: &mut Self::Node) {
765 node.set_lazy(self.layer_resolver.clone());
766 }
767
768 fn capabilities(&self) -> NodeCapabilities {
769 NodeCapabilities::DRAW
770 }
771
772 fn always_update(&self) -> bool {
773 true
774 }
775
776 fn auto_invalidate_on_update(&self) -> bool {
777 false
778 }
779}
780
781#[derive(Debug)]
785pub struct SizeNode {
786 min_width: Option<f32>,
787 max_width: Option<f32>,
788 min_height: Option<f32>,
789 max_height: Option<f32>,
790 enforce_incoming: bool,
791 state: NodeState,
792}
793
794impl SizeNode {
795 pub fn new(
796 min_width: Option<f32>,
797 max_width: Option<f32>,
798 min_height: Option<f32>,
799 max_height: Option<f32>,
800 enforce_incoming: bool,
801 ) -> Self {
802 Self {
803 min_width,
804 max_width,
805 min_height,
806 max_height,
807 enforce_incoming,
808 state: NodeState::new(),
809 }
810 }
811
812 fn target_constraints(&self) -> Constraints {
813 let max_width = self.max_width.map(|v| v.max(0.0)).unwrap_or(f32::INFINITY);
814 let max_height = self.max_height.map(|v| v.max(0.0)).unwrap_or(f32::INFINITY);
815
816 let min_width = self
817 .min_width
818 .map(|v| {
819 let clamped = v.clamp(0.0, max_width);
820 if clamped == f32::INFINITY {
821 0.0
822 } else {
823 clamped
824 }
825 })
826 .unwrap_or(0.0);
827
828 let min_height = self
829 .min_height
830 .map(|v| {
831 let clamped = v.clamp(0.0, max_height);
832 if clamped == f32::INFINITY {
833 0.0
834 } else {
835 clamped
836 }
837 })
838 .unwrap_or(0.0);
839
840 Constraints {
841 min_width,
842 max_width,
843 min_height,
844 max_height,
845 }
846 }
847
848 pub fn min_width(&self) -> Option<f32> {
849 self.min_width
850 }
851
852 pub fn max_width(&self) -> Option<f32> {
853 self.max_width
854 }
855
856 pub fn min_height(&self) -> Option<f32> {
857 self.min_height
858 }
859
860 pub fn max_height(&self) -> Option<f32> {
861 self.max_height
862 }
863
864 pub fn enforce_incoming(&self) -> bool {
865 self.enforce_incoming
866 }
867}
868
869impl DelegatableNode for SizeNode {
870 fn node_state(&self) -> &NodeState {
871 &self.state
872 }
873}
874
875impl_layout_modifier_node!(SizeNode, invalidate = InvalidationKind::Layout);
876
877impl LayoutModifierNode for SizeNode {
878 fn measure(
879 &self,
880 _context: &mut dyn ModifierNodeContext,
881 measurable: &dyn Measurable,
882 constraints: Constraints,
883 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
884 let target = self.target_constraints();
885
886 let wrapped_constraints = if self.enforce_incoming {
887 Constraints {
888 min_width: target
889 .min_width
890 .max(constraints.min_width)
891 .min(constraints.max_width),
892 max_width: target
893 .max_width
894 .min(constraints.max_width)
895 .max(constraints.min_width),
896 min_height: target
897 .min_height
898 .max(constraints.min_height)
899 .min(constraints.max_height),
900 max_height: target
901 .max_height
902 .min(constraints.max_height)
903 .max(constraints.min_height),
904 }
905 } else {
906 let resolved_min_width = if self.min_width.is_some() {
907 target.min_width
908 } else {
909 constraints.min_width.min(target.max_width)
910 };
911 let resolved_max_width = if self.max_width.is_some() {
912 target.max_width
913 } else {
914 constraints.max_width.max(target.min_width)
915 };
916 let resolved_min_height = if self.min_height.is_some() {
917 target.min_height
918 } else {
919 constraints.min_height.min(target.max_height)
920 };
921 let resolved_max_height = if self.max_height.is_some() {
922 target.max_height
923 } else {
924 constraints.max_height.max(target.min_height)
925 };
926
927 Constraints {
928 min_width: resolved_min_width,
929 max_width: resolved_max_width,
930 min_height: resolved_min_height,
931 max_height: resolved_max_height,
932 }
933 };
934
935 let placeable = measurable.measure(wrapped_constraints);
936 let measured_width = placeable.width();
937 let measured_height = placeable.height();
938
939 let result_width = if self.min_width.is_some()
940 && self.max_width.is_some()
941 && self.min_width == self.max_width
942 && target.min_width >= wrapped_constraints.min_width
943 && target.min_width <= wrapped_constraints.max_width
944 {
945 target.min_width
946 } else {
947 measured_width
948 };
949
950 let result_height = if self.min_height.is_some()
951 && self.max_height.is_some()
952 && self.min_height == self.max_height
953 && target.min_height >= wrapped_constraints.min_height
954 && target.min_height <= wrapped_constraints.max_height
955 {
956 target.min_height
957 } else {
958 measured_height
959 };
960
961 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
962 width: result_width,
963 height: result_height,
964 })
965 }
966
967 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
968 size_intrinsic(
969 self.target_constraints(),
970 SizeAxis::Width,
971 self.enforce_incoming,
972 height,
973 |h| measurable.min_intrinsic_width(h),
974 )
975 }
976
977 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
978 size_intrinsic(
979 self.target_constraints(),
980 SizeAxis::Width,
981 self.enforce_incoming,
982 height,
983 |h| measurable.max_intrinsic_width(h),
984 )
985 }
986
987 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
988 size_intrinsic(
989 self.target_constraints(),
990 SizeAxis::Height,
991 self.enforce_incoming,
992 width,
993 |w| measurable.min_intrinsic_height(w),
994 )
995 }
996
997 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
998 size_intrinsic(
999 self.target_constraints(),
1000 SizeAxis::Height,
1001 self.enforce_incoming,
1002 width,
1003 |w| measurable.max_intrinsic_height(w),
1004 )
1005 }
1006}
1007
1008#[derive(Debug, Clone, PartialEq)]
1012pub struct SizeElement {
1013 min_width: Option<f32>,
1014 max_width: Option<f32>,
1015 min_height: Option<f32>,
1016 max_height: Option<f32>,
1017 enforce_incoming: bool,
1018}
1019
1020impl SizeElement {
1021 pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
1022 Self {
1023 min_width: width,
1024 max_width: width,
1025 min_height: height,
1026 max_height: height,
1027 enforce_incoming: true,
1028 }
1029 }
1030
1031 pub fn with_constraints(
1032 min_width: Option<f32>,
1033 max_width: Option<f32>,
1034 min_height: Option<f32>,
1035 max_height: Option<f32>,
1036 enforce_incoming: bool,
1037 ) -> Self {
1038 Self {
1039 min_width,
1040 max_width,
1041 min_height,
1042 max_height,
1043 enforce_incoming,
1044 }
1045 }
1046}
1047
1048impl Hash for SizeElement {
1049 fn hash<H: Hasher>(&self, state: &mut H) {
1050 hash_option_f32(state, self.min_width);
1051 hash_option_f32(state, self.max_width);
1052 hash_option_f32(state, self.min_height);
1053 hash_option_f32(state, self.max_height);
1054 self.enforce_incoming.hash(state);
1055 }
1056}
1057
1058impl ModifierNodeElement for SizeElement {
1059 type Node = SizeNode;
1060
1061 fn create(&self) -> Self::Node {
1062 SizeNode::new(
1063 self.min_width,
1064 self.max_width,
1065 self.min_height,
1066 self.max_height,
1067 self.enforce_incoming,
1068 )
1069 }
1070
1071 fn update(&self, node: &mut Self::Node) {
1072 if node.min_width != self.min_width
1073 || node.max_width != self.max_width
1074 || node.min_height != self.min_height
1075 || node.max_height != self.max_height
1076 || node.enforce_incoming != self.enforce_incoming
1077 {
1078 node.min_width = self.min_width;
1079 node.max_width = self.max_width;
1080 node.min_height = self.min_height;
1081 node.max_height = self.max_height;
1082 node.enforce_incoming = self.enforce_incoming;
1083 }
1084 }
1085
1086 fn capabilities(&self) -> NodeCapabilities {
1087 NodeCapabilities::LAYOUT
1088 }
1089
1090 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
1091 Some(InvalidationKind::Layout)
1092 }
1093}
1094
1095use std::cell::RefCell;
1096
1097use cranpose_foundation::DRAG_THRESHOLD;
1098
1099pub struct ClickableNode {
1100 on_press: Option<Rc<dyn Fn(Point)>>,
1101 on_click: Rc<dyn Fn(Point)>,
1102 state: NodeState,
1103 press_position: Rc<RefCell<Option<Point>>>,
1104 cached_handler: Rc<dyn Fn(PointerEvent)>,
1105}
1106
1107impl std::fmt::Debug for ClickableNode {
1108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1109 f.debug_struct("ClickableNode").finish()
1110 }
1111}
1112
1113impl ClickableNode {
1114 pub fn new(on_click: impl Fn(Point) + 'static) -> Self {
1115 Self::with_handler(Rc::new(on_click))
1116 }
1117
1118 pub fn with_handler(on_click: Rc<dyn Fn(Point)>) -> Self {
1119 Self::with_handlers(None, on_click)
1120 }
1121
1122 pub fn with_handlers(on_press: Option<Rc<dyn Fn(Point)>>, on_click: Rc<dyn Fn(Point)>) -> Self {
1123 let press_position = Rc::new(RefCell::new(None));
1124 let cached_handler =
1125 Self::create_handler(on_press.clone(), on_click.clone(), press_position.clone());
1126 Self {
1127 on_press,
1128 on_click,
1129 state: NodeState::new(),
1130 press_position,
1131 cached_handler,
1132 }
1133 }
1134
1135 fn create_handler(
1136 on_press: Option<Rc<dyn Fn(Point)>>,
1137 on_click: Rc<dyn Fn(Point)>,
1138 press_position: Rc<RefCell<Option<Point>>>,
1139 ) -> Rc<dyn Fn(PointerEvent)> {
1140 Rc::new(move |event: PointerEvent| {
1141 if event.id != 0 {
1142 return;
1143 }
1144
1145 if event.is_consumed() {
1146 *press_position.borrow_mut() = None;
1147 return;
1148 }
1149
1150 match event.kind {
1151 PointerEventKind::Down => {
1152 *press_position.borrow_mut() = Some(event.travelled_to());
1153 if let Some(on_press) = on_press.as_ref() {
1154 on_press(event.position);
1155 }
1156 }
1157 PointerEventKind::Move => {}
1158 PointerEventKind::Up => {
1159 let press_pos_value = *press_position.borrow();
1160
1161 let should_click = if let Some(press_pos) = press_pos_value {
1162 let travelled_to = event.travelled_to();
1163 let dx = travelled_to.x - press_pos.x;
1164 let dy = travelled_to.y - press_pos.y;
1165 let distance = (dx * dx + dy * dy).sqrt();
1166 distance <= DRAG_THRESHOLD
1167 } else {
1168 true
1169 };
1170
1171 *press_position.borrow_mut() = None;
1172
1173 if should_click {
1174 on_click(Point {
1175 x: event.position.x,
1176 y: event.position.y,
1177 });
1178 event.consume();
1179 }
1180 }
1181 PointerEventKind::Cancel => {
1182 *press_position.borrow_mut() = None;
1183 }
1184 PointerEventKind::Scroll
1185 | PointerEventKind::Zoom
1186 | PointerEventKind::RotaryScrollPre
1187 | PointerEventKind::RotaryScroll
1188 | PointerEventKind::Enter
1189 | PointerEventKind::Exit => {}
1190 }
1191 })
1192 }
1193
1194 pub fn handler(&self) -> Rc<dyn Fn(Point)> {
1195 self.on_click.clone()
1196 }
1197}
1198
1199impl DelegatableNode for ClickableNode {
1200 fn node_state(&self) -> &NodeState {
1201 &self.state
1202 }
1203}
1204
1205impl ModifierNode for ClickableNode {
1206 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1207 context.invalidate(cranpose_foundation::InvalidationKind::PointerInput);
1208 }
1209
1210 fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
1211 Some(self)
1212 }
1213
1214 fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
1215 Some(self)
1216 }
1217}
1218
1219impl PointerInputNode for ClickableNode {
1220 fn on_pointer_event(
1221 &mut self,
1222 _context: &mut dyn ModifierNodeContext,
1223 event: &PointerEvent,
1224 ) -> bool {
1225 (self.cached_handler)(event.clone());
1226 event.is_consumed()
1227 }
1228
1229 fn hit_test(&self, _x: f32, _y: f32) -> bool {
1230 true
1231 }
1232
1233 fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
1234 Some(self.cached_handler.clone())
1235 }
1236}
1237
1238#[derive(Clone)]
1240pub struct ClickableElement {
1241 on_press: Option<Rc<dyn Fn(Point)>>,
1242 on_click: Rc<dyn Fn(Point)>,
1243}
1244
1245impl ClickableElement {
1246 pub fn new(on_click: impl Fn(Point) + 'static) -> Self {
1247 Self {
1248 on_press: None,
1249 on_click: Rc::new(on_click),
1250 }
1251 }
1252
1253 pub fn with_handler(on_click: Rc<dyn Fn(Point)>) -> Self {
1254 Self {
1255 on_press: None,
1256 on_click,
1257 }
1258 }
1259
1260 pub fn with_handlers(on_press: Rc<dyn Fn(Point)>, on_click: Rc<dyn Fn(Point)>) -> Self {
1261 Self {
1262 on_press: Some(on_press),
1263 on_click,
1264 }
1265 }
1266}
1267
1268impl std::fmt::Debug for ClickableElement {
1269 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1270 f.debug_struct("ClickableElement").finish()
1271 }
1272}
1273
1274impl PartialEq for ClickableElement {
1275 fn eq(&self, _other: &Self) -> bool {
1276 true
1277 }
1278}
1279
1280impl Eq for ClickableElement {}
1281
1282impl Hash for ClickableElement {
1283 fn hash<H: Hasher>(&self, state: &mut H) {
1284 "clickable".hash(state);
1285 }
1286}
1287
1288impl ModifierNodeElement for ClickableElement {
1289 type Node = ClickableNode;
1290
1291 fn create(&self) -> Self::Node {
1292 ClickableNode::with_handlers(self.on_press.clone(), self.on_click.clone())
1293 }
1294
1295 fn update(&self, node: &mut Self::Node) {
1296 node.on_press = self.on_press.clone();
1297 node.on_click = self.on_click.clone();
1298 node.cached_handler = ClickableNode::create_handler(
1299 node.on_press.clone(),
1300 node.on_click.clone(),
1301 node.press_position.clone(),
1302 );
1303 }
1304
1305 fn capabilities(&self) -> NodeCapabilities {
1306 NodeCapabilities::POINTER_INPUT
1307 }
1308
1309 fn always_update(&self) -> bool {
1310 true
1311 }
1312}
1313
1314#[derive(Debug)]
1320pub struct PointerIconNode {
1321 icon: PointerIcon,
1322 state: NodeState,
1323}
1324
1325impl PointerIconNode {
1326 pub fn new(icon: PointerIcon) -> Self {
1328 Self {
1329 icon,
1330 state: NodeState::new(),
1331 }
1332 }
1333
1334 pub fn icon(&self) -> &PointerIcon {
1336 &self.icon
1337 }
1338}
1339
1340impl DelegatableNode for PointerIconNode {
1341 fn node_state(&self) -> &NodeState {
1342 &self.state
1343 }
1344}
1345
1346impl ModifierNode for PointerIconNode {
1347 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1348 context.invalidate(cranpose_foundation::InvalidationKind::PointerInput);
1349 }
1350
1351 fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
1352 Some(self)
1353 }
1354
1355 fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
1356 Some(self)
1357 }
1358}
1359
1360impl PointerInputNode for PointerIconNode {
1361 fn on_pointer_event(
1362 &mut self,
1363 _context: &mut dyn ModifierNodeContext,
1364 _event: &PointerEvent,
1365 ) -> bool {
1366 false
1367 }
1368
1369 fn hit_test(&self, _x: f32, _y: f32) -> bool {
1370 true
1371 }
1372
1373 fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
1374 None
1375 }
1376}
1377
1378#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1380pub struct PointerIconElement {
1381 icon: PointerIcon,
1382}
1383
1384impl PointerIconElement {
1385 pub fn new(icon: PointerIcon) -> Self {
1387 Self { icon }
1388 }
1389}
1390
1391impl ModifierNodeElement for PointerIconElement {
1392 type Node = PointerIconNode;
1393
1394 fn create(&self) -> Self::Node {
1395 PointerIconNode::new(self.icon.clone())
1396 }
1397
1398 fn update(&self, node: &mut Self::Node) {
1399 node.icon = self.icon.clone();
1400 }
1401
1402 fn capabilities(&self) -> NodeCapabilities {
1403 NodeCapabilities::POINTER_INPUT
1404 }
1405}
1406
1407#[derive(Debug)]
1409pub struct AlphaNode {
1410 alpha: f32,
1411 state: NodeState,
1412}
1413
1414impl AlphaNode {
1415 pub fn new(alpha: f32) -> Self {
1416 Self {
1417 alpha: alpha.clamp(0.0, 1.0),
1418 state: NodeState::new(),
1419 }
1420 }
1421}
1422
1423impl DelegatableNode for AlphaNode {
1424 fn node_state(&self) -> &NodeState {
1425 &self.state
1426 }
1427}
1428
1429impl_draw_modifier_node!(AlphaNode);
1430
1431#[derive(Debug, Clone, PartialEq)]
1433pub struct AlphaElement {
1434 alpha: f32,
1435}
1436
1437impl AlphaElement {
1438 pub fn new(alpha: f32) -> Self {
1439 Self {
1440 alpha: alpha.clamp(0.0, 1.0),
1441 }
1442 }
1443}
1444
1445impl Hash for AlphaElement {
1446 fn hash<H: Hasher>(&self, state: &mut H) {
1447 hash_f32_value(state, self.alpha);
1448 }
1449}
1450
1451impl ModifierNodeElement for AlphaElement {
1452 type Node = AlphaNode;
1453
1454 fn create(&self) -> Self::Node {
1455 AlphaNode::new(self.alpha)
1456 }
1457
1458 fn update(&self, node: &mut Self::Node) {
1459 let new_alpha = self.alpha.clamp(0.0, 1.0);
1460 if (node.alpha - new_alpha).abs() > f32::EPSILON {
1461 node.alpha = new_alpha;
1462 }
1463 }
1464
1465 fn capabilities(&self) -> NodeCapabilities {
1466 NodeCapabilities::DRAW
1467 }
1468}
1469
1470#[derive(Debug)]
1471pub struct ClipToBoundsNode {
1472 state: NodeState,
1473}
1474
1475impl ClipToBoundsNode {
1476 pub fn new() -> Self {
1477 Self {
1478 state: NodeState::new(),
1479 }
1480 }
1481}
1482
1483impl DelegatableNode for ClipToBoundsNode {
1484 fn node_state(&self) -> &NodeState {
1485 &self.state
1486 }
1487}
1488
1489impl_draw_modifier_node!(ClipToBoundsNode);
1490
1491#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1492pub struct ClipToBoundsElement;
1493
1494impl ClipToBoundsElement {
1495 pub fn new() -> Self {
1496 Self
1497 }
1498}
1499
1500impl ModifierNodeElement for ClipToBoundsElement {
1501 type Node = ClipToBoundsNode;
1502
1503 fn create(&self) -> Self::Node {
1504 ClipToBoundsNode::new()
1505 }
1506
1507 fn update(&self, _node: &mut Self::Node) {}
1508
1509 fn capabilities(&self) -> NodeCapabilities {
1510 NodeCapabilities::DRAW
1511 }
1512}
1513
1514pub trait WindowRectSink {
1515 fn set(&self, rect: cranpose_ui_graphics::Rect);
1516}
1517
1518impl WindowRectSink for Cell<cranpose_ui_graphics::Rect> {
1519 fn set(&self, rect: cranpose_ui_graphics::Rect) {
1520 Cell::set(self, rect);
1521 }
1522}
1523
1524struct StateWindowRectSink(cranpose_core::MutableState<cranpose_ui_graphics::Rect>);
1525
1526impl WindowRectSink for StateWindowRectSink {
1527 fn set(&self, rect: cranpose_ui_graphics::Rect) {
1528 self.0.set(rect);
1529 }
1530}
1531
1532pub struct WindowRectReporterNode {
1533 sink: Rc<dyn WindowRectSink>,
1534 state: NodeState,
1535}
1536
1537impl WindowRectReporterNode {
1538 pub(crate) fn new(sink: Rc<dyn WindowRectSink>) -> Self {
1539 Self {
1540 sink,
1541 state: NodeState::new(),
1542 }
1543 }
1544
1545 pub(crate) fn window_rect_sink(&self) -> Rc<dyn WindowRectSink> {
1546 self.sink.clone()
1547 }
1548}
1549
1550impl DelegatableNode for WindowRectReporterNode {
1551 fn node_state(&self) -> &NodeState {
1552 &self.state
1553 }
1554}
1555
1556impl_layout_modifier_node!(WindowRectReporterNode);
1557
1558impl LayoutModifierNode for WindowRectReporterNode {
1559 fn measure(
1560 &self,
1561 _context: &mut dyn ModifierNodeContext,
1562 measurable: &dyn Measurable,
1563 constraints: Constraints,
1564 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1565 measure_pass_through(measurable, constraints, |_| (0.0, 0.0))
1566 }
1567}
1568
1569#[derive(Clone)]
1570pub struct WindowRectReporterElement {
1571 sink: Rc<dyn WindowRectSink>,
1572}
1573
1574impl WindowRectReporterElement {
1575 pub fn new(sink: Rc<Cell<cranpose_ui_graphics::Rect>>) -> Self {
1576 Self { sink }
1577 }
1578
1579 pub fn from_state(sink: cranpose_core::MutableState<cranpose_ui_graphics::Rect>) -> Self {
1580 Self {
1581 sink: Rc::new(StateWindowRectSink(sink)),
1582 }
1583 }
1584}
1585
1586impl std::fmt::Debug for WindowRectReporterElement {
1587 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1588 f.debug_struct("WindowRectReporterElement").finish()
1589 }
1590}
1591
1592impl PartialEq for WindowRectReporterElement {
1593 fn eq(&self, other: &Self) -> bool {
1594 Rc::ptr_eq(&self.sink, &other.sink)
1595 }
1596}
1597
1598impl Eq for WindowRectReporterElement {}
1599
1600impl Hash for WindowRectReporterElement {
1601 fn hash<H: Hasher>(&self, state: &mut H) {
1602 std::ptr::hash(Rc::as_ptr(&self.sink).cast::<()>(), state);
1603 }
1604}
1605
1606impl_sink_reporter_element!(WindowRectReporterElement, WindowRectReporterNode);
1607
1608pub trait SizeSink {
1609 fn set(&self, size: Size);
1610}
1611
1612impl SizeSink for Cell<Size> {
1613 fn set(&self, size: Size) {
1614 Cell::set(self, size);
1615 }
1616}
1617
1618struct StateSizeSink(cranpose_core::MutableState<Size>);
1619
1620impl SizeSink for StateSizeSink {
1621 fn set(&self, size: Size) {
1622 self.0.set(size);
1623 }
1624}
1625
1626pub struct SizeReporterNode {
1627 sink: Rc<dyn SizeSink>,
1628 state: NodeState,
1629 #[cfg(debug_assertions)]
1630 oscillation: Cell<(Size, Size, u32)>,
1631}
1632
1633impl SizeReporterNode {
1634 pub fn new(sink: Rc<dyn SizeSink>) -> Self {
1635 Self {
1636 sink,
1637 state: NodeState::new(),
1638 #[cfg(debug_assertions)]
1639 oscillation: Cell::new((Size::default(), Size::default(), 0)),
1640 }
1641 }
1642
1643 #[cfg(debug_assertions)]
1644 fn check_oscillation(&self, size: Size) {
1645 const ALTERNATION_CEILING: u32 = 64;
1646 let (last, second_last, count) = self.oscillation.get();
1647 let count = if size == second_last && size != last {
1648 count + 1
1649 } else if size == last {
1650 count
1651 } else {
1652 0
1653 };
1654 assert!(
1655 count <= ALTERNATION_CEILING,
1656 "size-reactive feedback loop: this node's measured size has \
1657 alternated between {last:?} and {size:?} for {count} passes — \
1658 its content's size depends on the size it reports (the \
1659 onSizeChanged self-reference hazard). Break the cycle by making \
1660 the reported size feed only content that does not change this \
1661 node's own measured size."
1662 );
1663 self.oscillation.set((size, last, count));
1664 }
1665}
1666
1667impl DelegatableNode for SizeReporterNode {
1668 fn node_state(&self) -> &NodeState {
1669 &self.state
1670 }
1671}
1672
1673impl_layout_modifier_node!(SizeReporterNode);
1674
1675impl LayoutModifierNode for SizeReporterNode {
1676 fn measure(
1677 &self,
1678 _context: &mut dyn ModifierNodeContext,
1679 measurable: &dyn Measurable,
1680 constraints: Constraints,
1681 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1682 measure_pass_through(measurable, constraints, |size| {
1683 #[cfg(debug_assertions)]
1684 self.check_oscillation(size);
1685 self.sink.set(size);
1686 (0.0, 0.0)
1687 })
1688 }
1689}
1690
1691#[derive(Clone)]
1692pub struct SizeReporterElement {
1693 sink: Rc<dyn SizeSink>,
1694}
1695
1696impl SizeReporterElement {
1697 pub fn new(sink: Rc<Cell<Size>>) -> Self {
1698 Self { sink }
1699 }
1700
1701 pub fn from_state(sink: cranpose_core::MutableState<Size>) -> Self {
1702 Self {
1703 sink: Rc::new(StateSizeSink(sink)),
1704 }
1705 }
1706}
1707
1708impl std::fmt::Debug for SizeReporterElement {
1709 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1710 f.debug_struct("SizeReporterElement").finish()
1711 }
1712}
1713
1714impl PartialEq for SizeReporterElement {
1715 fn eq(&self, other: &Self) -> bool {
1716 std::ptr::addr_eq(Rc::as_ptr(&self.sink), Rc::as_ptr(&other.sink))
1717 }
1718}
1719
1720impl Hash for SizeReporterElement {
1721 fn hash<H: Hasher>(&self, state: &mut H) {
1722 (Rc::as_ptr(&self.sink) as *const () as usize).hash(state);
1723 }
1724}
1725
1726impl_sink_reporter_element!(SizeReporterElement, SizeReporterNode);
1727
1728pub struct DrawCommandNode {
1729 commands: Vec<DrawCommand>,
1730 node_id: Cell<Option<NodeId>>,
1731 state: NodeState,
1732}
1733
1734impl DrawCommandNode {
1735 pub fn new(commands: Vec<DrawCommand>) -> Self {
1736 Self {
1737 commands,
1738 node_id: Cell::new(None),
1739 state: NodeState::new(),
1740 }
1741 }
1742
1743 #[cfg(test)]
1744 pub fn commands(&self) -> &[DrawCommand] {
1745 &self.commands
1746 }
1747
1748 pub(crate) fn observed_commands(&self) -> Vec<DrawCommand> {
1749 let node_id = self.node_id.get();
1750 self.commands
1751 .iter()
1752 .cloned()
1753 .enumerate()
1754 .map(|(index, command)| observe_draw_command(command, node_id, index))
1755 .collect()
1756 }
1757}
1758
1759impl DelegatableNode for DrawCommandNode {
1760 fn node_state(&self) -> &NodeState {
1761 &self.state
1762 }
1763}
1764
1765impl ModifierNode for DrawCommandNode {
1766 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1767 attach_draw_observer(&self.node_id, context);
1768 }
1769
1770 fn on_detach(&mut self) {
1771 detach_draw_observer(&self.node_id);
1772 }
1773
1774 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
1775 Some(self)
1776 }
1777
1778 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
1779 Some(self)
1780 }
1781}
1782
1783impl DrawModifierNode for DrawCommandNode {
1784 fn draw(&self, _draw_scope: &mut dyn DrawScope) {}
1785}
1786
1787fn observe_draw_command(
1788 command: DrawCommand,
1789 node_id: Option<NodeId>,
1790 command_index: usize,
1791) -> DrawCommand {
1792 let Some(node_id) = node_id else {
1793 return command;
1794 };
1795 let observation = crate::render_state::DrawObservationScope::new(node_id, command_index);
1796 match command {
1797 DrawCommand::Behind(draw) => DrawCommand::Behind(Rc::new(move |scope| {
1798 crate::render_state::observe_draw_reads(observation, || draw(scope))
1799 })),
1800 DrawCommand::WithContent(draw) => DrawCommand::WithContent(Rc::new(move |scope| {
1801 crate::render_state::observe_draw_reads(observation, || draw(scope))
1802 })),
1803 DrawCommand::Overlay(draw) => DrawCommand::Overlay(Rc::new(move |scope| {
1804 crate::render_state::observe_draw_reads(observation, || draw(scope))
1805 })),
1806 }
1807}
1808
1809fn draw_command_tag(cmd: &DrawCommand) -> u8 {
1810 match cmd {
1811 DrawCommand::Behind(_) => 0,
1812 DrawCommand::WithContent(_) => 1,
1813 DrawCommand::Overlay(_) => 2,
1814 }
1815}
1816
1817fn draw_command_closure_identity(cmd: &DrawCommand) -> *const () {
1818 match cmd {
1819 DrawCommand::Behind(f) | DrawCommand::WithContent(f) | DrawCommand::Overlay(f) => {
1820 Rc::as_ptr(f) as *const ()
1821 }
1822 }
1823}
1824
1825#[derive(Clone)]
1826pub struct DrawCommandElement {
1827 commands: Vec<DrawCommand>,
1828}
1829
1830impl DrawCommandElement {
1831 pub fn new(command: DrawCommand) -> Self {
1832 Self {
1833 commands: vec![command],
1834 }
1835 }
1836
1837 pub fn from_commands(commands: Vec<DrawCommand>) -> Self {
1838 Self { commands }
1839 }
1840}
1841
1842impl std::fmt::Debug for DrawCommandElement {
1843 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1844 f.debug_struct("DrawCommandElement")
1845 .field("commands", &self.commands.len())
1846 .finish()
1847 }
1848}
1849
1850impl PartialEq for DrawCommandElement {
1851 fn eq(&self, other: &Self) -> bool {
1852 if self.commands.len() != other.commands.len() {
1853 return false;
1854 }
1855 self.commands
1856 .iter()
1857 .zip(other.commands.iter())
1858 .all(|(a, b)| {
1859 draw_command_tag(a) == draw_command_tag(b)
1860 && draw_command_closure_identity(a) == draw_command_closure_identity(b)
1861 })
1862 }
1863}
1864
1865impl Eq for DrawCommandElement {}
1866
1867impl std::hash::Hash for DrawCommandElement {
1868 fn hash<H: Hasher>(&self, state: &mut H) {
1869 "draw_commands".hash(state);
1870 self.commands.len().hash(state);
1871 for command in &self.commands {
1872 draw_command_tag(command).hash(state);
1873 (draw_command_closure_identity(command) as usize).hash(state);
1874 }
1875 }
1876}
1877
1878impl ModifierNodeElement for DrawCommandElement {
1879 type Node = DrawCommandNode;
1880
1881 fn create(&self) -> Self::Node {
1882 DrawCommandNode::new(self.commands.clone())
1883 }
1884
1885 fn update(&self, node: &mut Self::Node) {
1886 node.commands = self.commands.clone();
1887 }
1888
1889 fn capabilities(&self) -> NodeCapabilities {
1890 NodeCapabilities::DRAW
1891 }
1892}
1893
1894#[derive(Debug)]
1898pub struct OffsetNode {
1899 x: f32,
1900 y: f32,
1901 rtl_aware: bool,
1902 state: NodeState,
1903}
1904
1905impl OffsetNode {
1906 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
1907 Self {
1908 x,
1909 y,
1910 rtl_aware,
1911 state: NodeState::new(),
1912 }
1913 }
1914
1915 pub fn offset(&self) -> Point {
1916 Point {
1917 x: self.x,
1918 y: self.y,
1919 }
1920 }
1921
1922 pub fn rtl_aware(&self) -> bool {
1923 self.rtl_aware
1924 }
1925}
1926
1927impl DelegatableNode for OffsetNode {
1928 fn node_state(&self) -> &NodeState {
1929 &self.state
1930 }
1931}
1932
1933impl_layout_modifier_node!(OffsetNode, invalidate = InvalidationKind::Layout);
1934
1935impl LayoutModifierNode for OffsetNode {
1936 fn measure(
1937 &self,
1938 _context: &mut dyn ModifierNodeContext,
1939 measurable: &dyn Measurable,
1940 constraints: Constraints,
1941 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1942 measure_pass_through(measurable, constraints, |_| (self.x, self.y))
1943 }
1944
1945 forward_intrinsics_to_child!();
1946}
1947
1948#[derive(Debug, Clone, PartialEq)]
1952pub struct OffsetElement {
1953 x: f32,
1954 y: f32,
1955 rtl_aware: bool,
1956}
1957
1958impl OffsetElement {
1959 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
1960 Self { x, y, rtl_aware }
1961 }
1962}
1963
1964impl Hash for OffsetElement {
1965 fn hash<H: Hasher>(&self, state: &mut H) {
1966 hash_f32_value(state, self.x);
1967 hash_f32_value(state, self.y);
1968 self.rtl_aware.hash(state);
1969 }
1970}
1971
1972impl ModifierNodeElement for OffsetElement {
1973 type Node = OffsetNode;
1974
1975 fn create(&self) -> Self::Node {
1976 OffsetNode::new(self.x, self.y, self.rtl_aware)
1977 }
1978
1979 fn update(&self, node: &mut Self::Node) {
1980 if node.x != self.x || node.y != self.y || node.rtl_aware != self.rtl_aware {
1981 node.x = self.x;
1982 node.y = self.y;
1983 node.rtl_aware = self.rtl_aware;
1984 }
1985 }
1986
1987 fn capabilities(&self) -> NodeCapabilities {
1988 NodeCapabilities::LAYOUT
1989 }
1990
1991 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
1992 Some(InvalidationKind::Layout)
1993 }
1994}
1995
1996#[derive(Debug)]
2004pub struct FractionalOffsetNode {
2005 x_fraction: f32,
2006 y_fraction: f32,
2007 state: NodeState,
2008}
2009
2010impl FractionalOffsetNode {
2011 pub fn new(x_fraction: f32, y_fraction: f32) -> Self {
2012 Self {
2013 x_fraction,
2014 y_fraction,
2015 state: NodeState::new(),
2016 }
2017 }
2018
2019 pub fn fractions(&self) -> Point {
2020 Point {
2021 x: self.x_fraction,
2022 y: self.y_fraction,
2023 }
2024 }
2025}
2026
2027impl DelegatableNode for FractionalOffsetNode {
2028 fn node_state(&self) -> &NodeState {
2029 &self.state
2030 }
2031}
2032
2033impl_layout_modifier_node!(FractionalOffsetNode, invalidate = InvalidationKind::Layout);
2034
2035impl LayoutModifierNode for FractionalOffsetNode {
2036 fn measure(
2037 &self,
2038 _context: &mut dyn ModifierNodeContext,
2039 measurable: &dyn Measurable,
2040 constraints: Constraints,
2041 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2042 measure_pass_through(measurable, constraints, |size| {
2043 (self.x_fraction * size.width, self.y_fraction * size.height)
2044 })
2045 }
2046
2047 forward_intrinsics_to_child!();
2048}
2049
2050#[derive(Debug, Clone, PartialEq)]
2052pub struct FractionalOffsetElement {
2053 x_fraction: f32,
2054 y_fraction: f32,
2055}
2056
2057impl FractionalOffsetElement {
2058 pub fn new(x_fraction: f32, y_fraction: f32) -> Self {
2059 Self {
2060 x_fraction,
2061 y_fraction,
2062 }
2063 }
2064}
2065
2066impl Hash for FractionalOffsetElement {
2067 fn hash<H: Hasher>(&self, state: &mut H) {
2068 "fractional_offset".hash(state);
2069 hash_f32_value(state, self.x_fraction);
2070 hash_f32_value(state, self.y_fraction);
2071 }
2072}
2073
2074impl ModifierNodeElement for FractionalOffsetElement {
2075 type Node = FractionalOffsetNode;
2076
2077 fn create(&self) -> Self::Node {
2078 FractionalOffsetNode::new(self.x_fraction, self.y_fraction)
2079 }
2080
2081 fn update(&self, node: &mut Self::Node) {
2082 if node.x_fraction != self.x_fraction || node.y_fraction != self.y_fraction {
2083 node.x_fraction = self.x_fraction;
2084 node.y_fraction = self.y_fraction;
2085 }
2086 }
2087
2088 fn capabilities(&self) -> NodeCapabilities {
2089 NodeCapabilities::LAYOUT
2090 }
2091
2092 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
2093 Some(InvalidationKind::Layout)
2094 }
2095}
2096
2097#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2099pub enum FillDirection {
2100 Horizontal,
2101 Vertical,
2102 Both,
2103}
2104
2105#[derive(Debug)]
2109pub struct FillNode {
2110 direction: FillDirection,
2111 fraction: f32,
2112 state: NodeState,
2113}
2114
2115impl FillNode {
2116 pub fn new(direction: FillDirection, fraction: f32) -> Self {
2117 Self {
2118 direction,
2119 fraction,
2120 state: NodeState::new(),
2121 }
2122 }
2123
2124 pub fn direction(&self) -> FillDirection {
2125 self.direction
2126 }
2127
2128 pub fn fraction(&self) -> f32 {
2129 self.fraction
2130 }
2131}
2132
2133impl DelegatableNode for FillNode {
2134 fn node_state(&self) -> &NodeState {
2135 &self.state
2136 }
2137}
2138
2139impl_layout_modifier_node!(FillNode, invalidate = InvalidationKind::Layout);
2140
2141impl LayoutModifierNode for FillNode {
2142 fn measure(
2143 &self,
2144 _context: &mut dyn ModifierNodeContext,
2145 measurable: &dyn Measurable,
2146 constraints: Constraints,
2147 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2148 let (fill_width, child_min_width, child_max_width) = if self.direction
2149 != FillDirection::Vertical
2150 && constraints.max_width != f32::INFINITY
2151 {
2152 let width = (constraints.max_width * self.fraction)
2153 .round()
2154 .clamp(constraints.min_width, constraints.max_width);
2155 (width, width, width)
2156 } else {
2157 (
2158 constraints.max_width,
2159 constraints.min_width,
2160 constraints.max_width,
2161 )
2162 };
2163
2164 let (fill_height, child_min_height, child_max_height) = if self.direction
2165 != FillDirection::Horizontal
2166 && constraints.max_height != f32::INFINITY
2167 {
2168 let height = (constraints.max_height * self.fraction)
2169 .round()
2170 .clamp(constraints.min_height, constraints.max_height);
2171 (height, height, height)
2172 } else {
2173 (
2174 constraints.max_height,
2175 constraints.min_height,
2176 constraints.max_height,
2177 )
2178 };
2179
2180 let fill_constraints = Constraints {
2181 min_width: child_min_width,
2182 max_width: child_max_width,
2183 min_height: child_min_height,
2184 max_height: child_max_height,
2185 };
2186
2187 let placeable = measurable.measure(fill_constraints);
2188
2189 let result_width = if self.direction != FillDirection::Vertical
2190 && constraints.max_width != f32::INFINITY
2191 {
2192 fill_width
2193 } else {
2194 placeable.width()
2195 };
2196
2197 let result_height = if self.direction != FillDirection::Horizontal
2198 && constraints.max_height != f32::INFINITY
2199 {
2200 fill_height
2201 } else {
2202 placeable.height()
2203 };
2204
2205 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
2206 width: result_width,
2207 height: result_height,
2208 })
2209 }
2210
2211 forward_intrinsics_to_child!();
2212}
2213
2214#[derive(Debug, Clone, PartialEq)]
2218pub struct FillElement {
2219 direction: FillDirection,
2220 fraction: f32,
2221}
2222
2223impl FillElement {
2224 pub fn width(fraction: f32) -> Self {
2225 Self {
2226 direction: FillDirection::Horizontal,
2227 fraction,
2228 }
2229 }
2230
2231 pub fn height(fraction: f32) -> Self {
2232 Self {
2233 direction: FillDirection::Vertical,
2234 fraction,
2235 }
2236 }
2237
2238 pub fn size(fraction: f32) -> Self {
2239 Self {
2240 direction: FillDirection::Both,
2241 fraction,
2242 }
2243 }
2244}
2245
2246impl Hash for FillElement {
2247 fn hash<H: Hasher>(&self, state: &mut H) {
2248 self.direction.hash(state);
2249 hash_f32_value(state, self.fraction);
2250 }
2251}
2252
2253impl ModifierNodeElement for FillElement {
2254 type Node = FillNode;
2255
2256 fn create(&self) -> Self::Node {
2257 FillNode::new(self.direction, self.fraction)
2258 }
2259
2260 fn update(&self, node: &mut Self::Node) {
2261 if node.direction != self.direction || node.fraction != self.fraction {
2262 node.direction = self.direction;
2263 node.fraction = self.fraction;
2264 }
2265 }
2266
2267 fn capabilities(&self) -> NodeCapabilities {
2268 NodeCapabilities::LAYOUT
2269 }
2270}
2271
2272#[derive(Debug)]
2273pub struct WeightNode {
2274 weight: f32,
2275 fill: bool,
2276 state: NodeState,
2277}
2278
2279impl WeightNode {
2280 pub fn new(weight: f32, fill: bool) -> Self {
2281 Self {
2282 weight,
2283 fill,
2284 state: NodeState::new(),
2285 }
2286 }
2287
2288 pub fn layout_weight(&self) -> LayoutWeight {
2289 LayoutWeight {
2290 weight: self.weight,
2291 fill: self.fill,
2292 }
2293 }
2294}
2295
2296impl DelegatableNode for WeightNode {
2297 fn node_state(&self) -> &NodeState {
2298 &self.state
2299 }
2300}
2301
2302impl ModifierNode for WeightNode {
2303 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2304 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2305 }
2306}
2307
2308#[derive(Debug, Clone, PartialEq)]
2309pub struct WeightElement {
2310 weight: f32,
2311 fill: bool,
2312}
2313
2314impl WeightElement {
2315 pub fn new(weight: f32, fill: bool) -> Self {
2316 Self { weight, fill }
2317 }
2318}
2319
2320impl Hash for WeightElement {
2321 fn hash<H: Hasher>(&self, state: &mut H) {
2322 hash_f32_value(state, self.weight);
2323 self.fill.hash(state);
2324 }
2325}
2326
2327impl ModifierNodeElement for WeightElement {
2328 type Node = WeightNode;
2329
2330 fn create(&self) -> Self::Node {
2331 WeightNode::new(self.weight, self.fill)
2332 }
2333
2334 fn update(&self, node: &mut Self::Node) {
2335 if node.weight != self.weight || node.fill != self.fill {
2336 node.weight = self.weight;
2337 node.fill = self.fill;
2338 }
2339 }
2340
2341 fn capabilities(&self) -> NodeCapabilities {
2342 NodeCapabilities::LAYOUT
2343 }
2344}
2345
2346#[derive(Debug)]
2347pub struct AlignmentNode {
2348 box_alignment: Option<Alignment>,
2349 column_alignment: Option<HorizontalAlignment>,
2350 row_alignment: Option<VerticalAlignment>,
2351 state: NodeState,
2352}
2353
2354impl AlignmentNode {
2355 pub fn new(
2356 box_alignment: Option<Alignment>,
2357 column_alignment: Option<HorizontalAlignment>,
2358 row_alignment: Option<VerticalAlignment>,
2359 ) -> Self {
2360 Self {
2361 box_alignment,
2362 column_alignment,
2363 row_alignment,
2364 state: NodeState::new(),
2365 }
2366 }
2367
2368 pub fn box_alignment(&self) -> Option<Alignment> {
2369 self.box_alignment
2370 }
2371
2372 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
2373 self.column_alignment
2374 }
2375
2376 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
2377 self.row_alignment
2378 }
2379}
2380
2381impl DelegatableNode for AlignmentNode {
2382 fn node_state(&self) -> &NodeState {
2383 &self.state
2384 }
2385}
2386
2387impl ModifierNode for AlignmentNode {
2388 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2389 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2390 }
2391}
2392
2393#[derive(Debug, Clone, PartialEq)]
2394pub struct AlignmentElement {
2395 box_alignment: Option<Alignment>,
2396 column_alignment: Option<HorizontalAlignment>,
2397 row_alignment: Option<VerticalAlignment>,
2398}
2399
2400impl AlignmentElement {
2401 pub fn box_alignment(alignment: Alignment) -> Self {
2402 Self {
2403 box_alignment: Some(alignment),
2404 column_alignment: None,
2405 row_alignment: None,
2406 }
2407 }
2408
2409 pub fn column_alignment(alignment: HorizontalAlignment) -> Self {
2410 Self {
2411 box_alignment: None,
2412 column_alignment: Some(alignment),
2413 row_alignment: None,
2414 }
2415 }
2416
2417 pub fn row_alignment(alignment: VerticalAlignment) -> Self {
2418 Self {
2419 box_alignment: None,
2420 column_alignment: None,
2421 row_alignment: Some(alignment),
2422 }
2423 }
2424}
2425
2426impl Hash for AlignmentElement {
2427 fn hash<H: Hasher>(&self, state: &mut H) {
2428 if let Some(alignment) = self.box_alignment {
2429 state.write_u8(1);
2430 hash_alignment(state, alignment);
2431 } else {
2432 state.write_u8(0);
2433 }
2434 if let Some(alignment) = self.column_alignment {
2435 state.write_u8(1);
2436 hash_horizontal_alignment(state, alignment);
2437 } else {
2438 state.write_u8(0);
2439 }
2440 if let Some(alignment) = self.row_alignment {
2441 state.write_u8(1);
2442 hash_vertical_alignment(state, alignment);
2443 } else {
2444 state.write_u8(0);
2445 }
2446 }
2447}
2448
2449impl ModifierNodeElement for AlignmentElement {
2450 type Node = AlignmentNode;
2451
2452 fn create(&self) -> Self::Node {
2453 AlignmentNode::new(
2454 self.box_alignment,
2455 self.column_alignment,
2456 self.row_alignment,
2457 )
2458 }
2459
2460 fn update(&self, node: &mut Self::Node) {
2461 if node.box_alignment != self.box_alignment {
2462 node.box_alignment = self.box_alignment;
2463 }
2464 if node.column_alignment != self.column_alignment {
2465 node.column_alignment = self.column_alignment;
2466 }
2467 if node.row_alignment != self.row_alignment {
2468 node.row_alignment = self.row_alignment;
2469 }
2470 }
2471
2472 fn capabilities(&self) -> NodeCapabilities {
2473 NodeCapabilities::LAYOUT
2474 }
2475}
2476
2477#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2478pub enum IntrinsicAxis {
2479 Width,
2480 Height,
2481}
2482
2483#[derive(Debug)]
2484pub struct IntrinsicSizeNode {
2485 axis: IntrinsicAxis,
2486 size: IntrinsicSize,
2487 state: NodeState,
2488}
2489
2490impl IntrinsicSizeNode {
2491 pub fn new(axis: IntrinsicAxis, size: IntrinsicSize) -> Self {
2492 Self {
2493 axis,
2494 size,
2495 state: NodeState::new(),
2496 }
2497 }
2498
2499 pub fn axis(&self) -> IntrinsicAxis {
2500 self.axis
2501 }
2502
2503 pub fn intrinsic_size(&self) -> IntrinsicSize {
2504 self.size
2505 }
2506}
2507
2508impl DelegatableNode for IntrinsicSizeNode {
2509 fn node_state(&self) -> &NodeState {
2510 &self.state
2511 }
2512}
2513
2514impl ModifierNode for IntrinsicSizeNode {
2515 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2516 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2517 }
2518}
2519
2520#[derive(Debug, Clone, PartialEq)]
2521pub struct IntrinsicSizeElement {
2522 axis: IntrinsicAxis,
2523 size: IntrinsicSize,
2524}
2525
2526impl IntrinsicSizeElement {
2527 pub fn width(size: IntrinsicSize) -> Self {
2528 Self {
2529 axis: IntrinsicAxis::Width,
2530 size,
2531 }
2532 }
2533
2534 pub fn height(size: IntrinsicSize) -> Self {
2535 Self {
2536 axis: IntrinsicAxis::Height,
2537 size,
2538 }
2539 }
2540}
2541
2542impl Hash for IntrinsicSizeElement {
2543 fn hash<H: Hasher>(&self, state: &mut H) {
2544 state.write_u8(match self.axis {
2545 IntrinsicAxis::Width => 0,
2546 IntrinsicAxis::Height => 1,
2547 });
2548 state.write_u8(match self.size {
2549 IntrinsicSize::Min => 0,
2550 IntrinsicSize::Max => 1,
2551 });
2552 }
2553}
2554
2555impl ModifierNodeElement for IntrinsicSizeElement {
2556 type Node = IntrinsicSizeNode;
2557
2558 fn create(&self) -> Self::Node {
2559 IntrinsicSizeNode::new(self.axis, self.size)
2560 }
2561
2562 fn update(&self, node: &mut Self::Node) {
2563 if node.axis != self.axis {
2564 node.axis = self.axis;
2565 }
2566 if node.size != self.size {
2567 node.size = self.size;
2568 }
2569 }
2570
2571 fn capabilities(&self) -> NodeCapabilities {
2572 NodeCapabilities::LAYOUT
2573 }
2574}
2575
2576#[cfg(test)]
2577#[path = "tests/modifier_nodes_tests.rs"]
2578mod tests;