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