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