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 && let Some(node_id) = self.node_id.get() {
619 crate::render_state::schedule_draw_repass(node_id);
620 }
621 }
622
623 fn set_lazy(&mut self, layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) {
624 let changed = self
625 .layer_resolver
626 .as_ref()
627 .is_none_or(|current| !Rc::ptr_eq(current, &layer_resolver));
628 self.layer_resolver = Some(layer_resolver);
629 if changed && let Some(node_id) = self.node_id.get() {
630 crate::render_state::schedule_draw_repass(node_id);
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 std::cell::RefCell;
1129
1130use cranpose_foundation::DRAG_THRESHOLD;
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 trait WindowRectSink {
1549 fn set(&self, rect: cranpose_ui_graphics::Rect);
1550}
1551
1552impl WindowRectSink for Cell<cranpose_ui_graphics::Rect> {
1553 fn set(&self, rect: cranpose_ui_graphics::Rect) {
1554 Cell::set(self, rect);
1555 }
1556}
1557
1558struct StateWindowRectSink(cranpose_core::MutableState<cranpose_ui_graphics::Rect>);
1559
1560impl WindowRectSink for StateWindowRectSink {
1561 fn set(&self, rect: cranpose_ui_graphics::Rect) {
1562 self.0.set(rect);
1563 }
1564}
1565
1566pub struct WindowRectReporterNode {
1567 sink: Rc<dyn WindowRectSink>,
1568 state: NodeState,
1569}
1570
1571impl WindowRectReporterNode {
1572 pub(crate) fn new(sink: Rc<dyn WindowRectSink>) -> Self {
1573 Self {
1574 sink,
1575 state: NodeState::new(),
1576 }
1577 }
1578
1579 pub(crate) fn window_rect_sink(&self) -> Rc<dyn WindowRectSink> {
1581 self.sink.clone()
1582 }
1583}
1584
1585impl DelegatableNode for WindowRectReporterNode {
1586 fn node_state(&self) -> &NodeState {
1587 &self.state
1588 }
1589}
1590
1591impl ModifierNode for WindowRectReporterNode {
1592 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
1593 Some(self)
1594 }
1595
1596 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
1597 Some(self)
1598 }
1599}
1600
1601impl LayoutModifierNode for WindowRectReporterNode {
1602 fn measure(
1606 &self,
1607 _context: &mut dyn ModifierNodeContext,
1608 measurable: &dyn Measurable,
1609 constraints: Constraints,
1610 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1611 let placeable = measurable.measure(constraints);
1612 cranpose_ui_layout::LayoutModifierMeasureResult::new(
1613 Size {
1614 width: placeable.width(),
1615 height: placeable.height(),
1616 },
1617 0.0,
1618 0.0,
1619 )
1620 }
1621}
1622
1623#[derive(Clone)]
1626pub struct WindowRectReporterElement {
1627 sink: Rc<dyn WindowRectSink>,
1628}
1629
1630impl WindowRectReporterElement {
1631 pub fn new(sink: Rc<Cell<cranpose_ui_graphics::Rect>>) -> Self {
1632 Self { sink }
1633 }
1634
1635 pub fn from_state(sink: cranpose_core::MutableState<cranpose_ui_graphics::Rect>) -> Self {
1636 Self {
1637 sink: Rc::new(StateWindowRectSink(sink)),
1638 }
1639 }
1640}
1641
1642impl std::fmt::Debug for WindowRectReporterElement {
1643 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1644 f.debug_struct("WindowRectReporterElement").finish()
1645 }
1646}
1647
1648impl PartialEq for WindowRectReporterElement {
1649 fn eq(&self, other: &Self) -> bool {
1650 Rc::ptr_eq(&self.sink, &other.sink)
1651 }
1652}
1653
1654impl Eq for WindowRectReporterElement {}
1655
1656impl Hash for WindowRectReporterElement {
1657 fn hash<H: Hasher>(&self, state: &mut H) {
1658 std::ptr::hash(Rc::as_ptr(&self.sink).cast::<()>(), state);
1659 }
1660}
1661
1662impl ModifierNodeElement for WindowRectReporterElement {
1663 type Node = WindowRectReporterNode;
1664
1665 fn create(&self) -> Self::Node {
1666 WindowRectReporterNode::new(self.sink.clone())
1667 }
1668
1669 fn update(&self, node: &mut Self::Node) {
1670 node.sink = self.sink.clone();
1671 }
1672
1673 fn capabilities(&self) -> NodeCapabilities {
1674 NodeCapabilities::LAYOUT
1675 }
1676}
1677
1678pub trait SizeSink {
1688 fn set(&self, size: Size);
1689}
1690
1691impl SizeSink for Cell<Size> {
1692 fn set(&self, size: Size) {
1693 Cell::set(self, size);
1694 }
1695}
1696
1697struct StateSizeSink(cranpose_core::MutableState<Size>);
1698
1699impl SizeSink for StateSizeSink {
1700 fn set(&self, size: Size) {
1701 self.0.set(size);
1702 }
1703}
1704
1705pub struct SizeReporterNode {
1711 sink: Rc<dyn SizeSink>,
1712 state: NodeState,
1713 #[cfg(debug_assertions)]
1721 oscillation: Cell<(Size, Size, u32)>,
1722}
1723
1724impl SizeReporterNode {
1725 pub fn new(sink: Rc<dyn SizeSink>) -> Self {
1726 Self {
1727 sink,
1728 state: NodeState::new(),
1729 #[cfg(debug_assertions)]
1730 oscillation: Cell::new((Size::default(), Size::default(), 0)),
1731 }
1732 }
1733
1734 #[cfg(debug_assertions)]
1735 fn check_oscillation(&self, size: Size) {
1736 const ALTERNATION_CEILING: u32 = 64;
1737 let (last, second_last, count) = self.oscillation.get();
1738 let count = if size == second_last && size != last {
1739 count + 1
1740 } else if size == last {
1741 count
1742 } else {
1743 0
1744 };
1745 assert!(
1746 count <= ALTERNATION_CEILING,
1747 "size-reactive feedback loop: this node's measured size has \
1748 alternated between {last:?} and {size:?} for {count} passes — \
1749 its content's size depends on the size it reports (the \
1750 onSizeChanged self-reference hazard). Break the cycle by making \
1751 the reported size feed only content that does not change this \
1752 node's own measured size."
1753 );
1754 self.oscillation.set((size, last, count));
1755 }
1756}
1757
1758impl DelegatableNode for SizeReporterNode {
1759 fn node_state(&self) -> &NodeState {
1760 &self.state
1761 }
1762}
1763
1764impl ModifierNode for SizeReporterNode {
1765 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
1766 Some(self)
1767 }
1768
1769 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
1770 Some(self)
1771 }
1772}
1773
1774impl LayoutModifierNode for SizeReporterNode {
1775 fn measure(
1776 &self,
1777 _context: &mut dyn ModifierNodeContext,
1778 measurable: &dyn Measurable,
1779 constraints: Constraints,
1780 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1781 let placeable = measurable.measure(constraints);
1782 let size = Size {
1783 width: placeable.width(),
1784 height: placeable.height(),
1785 };
1786 #[cfg(debug_assertions)]
1787 self.check_oscillation(size);
1788 self.sink.set(size);
1789 cranpose_ui_layout::LayoutModifierMeasureResult::new(size, 0.0, 0.0)
1790 }
1791}
1792
1793#[derive(Clone)]
1795pub struct SizeReporterElement {
1796 sink: Rc<dyn SizeSink>,
1797}
1798
1799impl SizeReporterElement {
1800 pub fn new(sink: Rc<Cell<Size>>) -> Self {
1801 Self { sink }
1802 }
1803
1804 pub fn from_state(sink: cranpose_core::MutableState<Size>) -> Self {
1807 Self {
1808 sink: Rc::new(StateSizeSink(sink)),
1809 }
1810 }
1811}
1812
1813impl std::fmt::Debug for SizeReporterElement {
1814 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1815 f.debug_struct("SizeReporterElement").finish()
1816 }
1817}
1818
1819impl PartialEq for SizeReporterElement {
1820 fn eq(&self, other: &Self) -> bool {
1821 std::ptr::addr_eq(Rc::as_ptr(&self.sink), Rc::as_ptr(&other.sink))
1822 }
1823}
1824
1825impl Hash for SizeReporterElement {
1826 fn hash<H: Hasher>(&self, state: &mut H) {
1827 (Rc::as_ptr(&self.sink) as *const () as usize).hash(state);
1828 }
1829}
1830
1831impl ModifierNodeElement for SizeReporterElement {
1832 type Node = SizeReporterNode;
1833
1834 fn create(&self) -> Self::Node {
1835 SizeReporterNode::new(self.sink.clone())
1836 }
1837
1838 fn update(&self, node: &mut Self::Node) {
1839 node.sink = self.sink.clone();
1840 }
1841
1842 fn capabilities(&self) -> NodeCapabilities {
1843 NodeCapabilities::LAYOUT
1844 }
1845}
1846
1847pub struct DrawCommandNode {
1853 commands: Vec<DrawCommand>,
1854 node_id: Cell<Option<NodeId>>,
1855 state: NodeState,
1856}
1857
1858impl DrawCommandNode {
1859 pub fn new(commands: Vec<DrawCommand>) -> Self {
1860 Self {
1861 commands,
1862 node_id: Cell::new(None),
1863 state: NodeState::new(),
1864 }
1865 }
1866
1867 #[cfg(test)]
1868 pub fn commands(&self) -> &[DrawCommand] {
1869 &self.commands
1870 }
1871
1872 pub(crate) fn observed_commands(&self) -> Vec<DrawCommand> {
1873 let node_id = self.node_id.get();
1874 self.commands
1875 .iter()
1876 .cloned()
1877 .enumerate()
1878 .map(|(index, command)| observe_draw_command(command, node_id, index))
1879 .collect()
1880 }
1881}
1882
1883impl DelegatableNode for DrawCommandNode {
1884 fn node_state(&self) -> &NodeState {
1885 &self.state
1886 }
1887}
1888
1889impl ModifierNode for DrawCommandNode {
1890 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1891 self.node_id.set(context.node_id());
1892 context.invalidate(cranpose_foundation::InvalidationKind::Draw);
1893 }
1894
1895 fn on_detach(&mut self) {
1896 if let Some(node_id) = self.node_id.replace(None) {
1897 crate::render_state::clear_draw_observations_for_node(node_id);
1898 }
1899 }
1900
1901 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
1902 Some(self)
1903 }
1904
1905 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
1906 Some(self)
1907 }
1908}
1909
1910impl DrawModifierNode for DrawCommandNode {
1911 fn draw(&self, _draw_scope: &mut dyn DrawScope) {}
1912}
1913
1914fn observe_draw_command(
1915 command: DrawCommand,
1916 node_id: Option<NodeId>,
1917 command_index: usize,
1918) -> DrawCommand {
1919 let Some(node_id) = node_id else {
1920 return command;
1921 };
1922 let observation = crate::render_state::DrawObservationScope::new(node_id, command_index);
1923 match command {
1924 DrawCommand::Behind(draw) => DrawCommand::Behind(Rc::new(move |scope| {
1925 crate::render_state::observe_draw_reads(observation, || draw(scope))
1926 })),
1927 DrawCommand::WithContent(draw) => DrawCommand::WithContent(Rc::new(move |scope| {
1928 crate::render_state::observe_draw_reads(observation, || draw(scope))
1929 })),
1930 DrawCommand::Overlay(draw) => DrawCommand::Overlay(Rc::new(move |scope| {
1931 crate::render_state::observe_draw_reads(observation, || draw(scope))
1932 })),
1933 }
1934}
1935
1936fn draw_command_tag(cmd: &DrawCommand) -> u8 {
1937 match cmd {
1938 DrawCommand::Behind(_) => 0,
1939 DrawCommand::WithContent(_) => 1,
1940 DrawCommand::Overlay(_) => 2,
1941 }
1942}
1943
1944fn draw_command_closure_identity(cmd: &DrawCommand) -> *const () {
1945 match cmd {
1946 DrawCommand::Behind(f) | DrawCommand::WithContent(f) | DrawCommand::Overlay(f) => {
1947 Rc::as_ptr(f) as *const ()
1948 }
1949 }
1950}
1951
1952#[derive(Clone)]
1954pub struct DrawCommandElement {
1955 commands: Vec<DrawCommand>,
1956}
1957
1958impl DrawCommandElement {
1959 pub fn new(command: DrawCommand) -> Self {
1960 Self {
1961 commands: vec![command],
1962 }
1963 }
1964
1965 pub fn from_commands(commands: Vec<DrawCommand>) -> Self {
1966 Self { commands }
1967 }
1968}
1969
1970impl std::fmt::Debug for DrawCommandElement {
1971 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1972 f.debug_struct("DrawCommandElement")
1973 .field("commands", &self.commands.len())
1974 .finish()
1975 }
1976}
1977
1978impl PartialEq for DrawCommandElement {
1979 fn eq(&self, other: &Self) -> bool {
1980 if self.commands.len() != other.commands.len() {
1981 return false;
1982 }
1983 self.commands
1984 .iter()
1985 .zip(other.commands.iter())
1986 .all(|(a, b)| {
1987 draw_command_tag(a) == draw_command_tag(b)
1988 && draw_command_closure_identity(a) == draw_command_closure_identity(b)
1989 })
1990 }
1991}
1992
1993impl Eq for DrawCommandElement {}
1994
1995impl std::hash::Hash for DrawCommandElement {
1996 fn hash<H: Hasher>(&self, state: &mut H) {
1997 "draw_commands".hash(state);
1998 self.commands.len().hash(state);
1999 for command in &self.commands {
2000 draw_command_tag(command).hash(state);
2001 (draw_command_closure_identity(command) as usize).hash(state);
2002 }
2003 }
2004}
2005
2006impl ModifierNodeElement for DrawCommandElement {
2007 type Node = DrawCommandNode;
2008
2009 fn create(&self) -> Self::Node {
2010 DrawCommandNode::new(self.commands.clone())
2011 }
2012
2013 fn update(&self, node: &mut Self::Node) {
2014 node.commands = self.commands.clone();
2015 }
2016
2017 fn capabilities(&self) -> NodeCapabilities {
2018 NodeCapabilities::DRAW
2019 }
2020}
2021
2022#[derive(Debug)]
2030pub struct OffsetNode {
2031 x: f32,
2032 y: f32,
2033 rtl_aware: bool,
2034 state: NodeState,
2035}
2036
2037impl OffsetNode {
2038 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
2039 Self {
2040 x,
2041 y,
2042 rtl_aware,
2043 state: NodeState::new(),
2044 }
2045 }
2046
2047 pub fn offset(&self) -> Point {
2048 Point {
2049 x: self.x,
2050 y: self.y,
2051 }
2052 }
2053
2054 pub fn rtl_aware(&self) -> bool {
2055 self.rtl_aware
2056 }
2057}
2058
2059impl DelegatableNode for OffsetNode {
2060 fn node_state(&self) -> &NodeState {
2061 &self.state
2062 }
2063}
2064
2065impl ModifierNode for OffsetNode {
2066 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2067 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2068 }
2069
2070 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
2071 Some(self)
2072 }
2073
2074 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
2075 Some(self)
2076 }
2077}
2078
2079impl LayoutModifierNode for OffsetNode {
2080 fn measure(
2081 &self,
2082 _context: &mut dyn ModifierNodeContext,
2083 measurable: &dyn Measurable,
2084 constraints: Constraints,
2085 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2086 let placeable = measurable.measure(constraints);
2088
2089 cranpose_ui_layout::LayoutModifierMeasureResult::new(
2091 Size {
2092 width: placeable.width(),
2093 height: placeable.height(),
2094 },
2095 self.x, self.y, )
2098 }
2099
2100 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
2101 measurable.min_intrinsic_width(height)
2102 }
2103
2104 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
2105 measurable.max_intrinsic_width(height)
2106 }
2107
2108 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
2109 measurable.min_intrinsic_height(width)
2110 }
2111
2112 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
2113 measurable.max_intrinsic_height(width)
2114 }
2115}
2116
2117#[derive(Debug, Clone, PartialEq)]
2121pub struct OffsetElement {
2122 x: f32,
2123 y: f32,
2124 rtl_aware: bool,
2125}
2126
2127impl OffsetElement {
2128 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
2129 Self { x, y, rtl_aware }
2130 }
2131}
2132
2133impl Hash for OffsetElement {
2134 fn hash<H: Hasher>(&self, state: &mut H) {
2135 hash_f32_value(state, self.x);
2136 hash_f32_value(state, self.y);
2137 self.rtl_aware.hash(state);
2138 }
2139}
2140
2141impl ModifierNodeElement for OffsetElement {
2142 type Node = OffsetNode;
2143
2144 fn create(&self) -> Self::Node {
2145 OffsetNode::new(self.x, self.y, self.rtl_aware)
2146 }
2147
2148 fn update(&self, node: &mut Self::Node) {
2149 if node.x != self.x || node.y != self.y || node.rtl_aware != self.rtl_aware {
2150 node.x = self.x;
2151 node.y = self.y;
2152 node.rtl_aware = self.rtl_aware;
2153 }
2154 }
2155
2156 fn capabilities(&self) -> NodeCapabilities {
2157 NodeCapabilities::LAYOUT
2158 }
2159
2160 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
2161 Some(InvalidationKind::Layout)
2162 }
2163}
2164
2165#[derive(Debug)]
2177pub struct FractionalOffsetNode {
2178 x_fraction: f32,
2179 y_fraction: f32,
2180 state: NodeState,
2181}
2182
2183impl FractionalOffsetNode {
2184 pub fn new(x_fraction: f32, y_fraction: f32) -> Self {
2185 Self {
2186 x_fraction,
2187 y_fraction,
2188 state: NodeState::new(),
2189 }
2190 }
2191
2192 pub fn fractions(&self) -> Point {
2193 Point {
2194 x: self.x_fraction,
2195 y: self.y_fraction,
2196 }
2197 }
2198}
2199
2200impl DelegatableNode for FractionalOffsetNode {
2201 fn node_state(&self) -> &NodeState {
2202 &self.state
2203 }
2204}
2205
2206impl ModifierNode for FractionalOffsetNode {
2207 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2208 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2209 }
2210
2211 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
2212 Some(self)
2213 }
2214
2215 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
2216 Some(self)
2217 }
2218}
2219
2220impl LayoutModifierNode for FractionalOffsetNode {
2221 fn measure(
2222 &self,
2223 _context: &mut dyn ModifierNodeContext,
2224 measurable: &dyn Measurable,
2225 constraints: Constraints,
2226 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2227 let placeable = measurable.measure(constraints);
2230
2231 cranpose_ui_layout::LayoutModifierMeasureResult::new(
2232 Size {
2233 width: placeable.width(),
2234 height: placeable.height(),
2235 },
2236 self.x_fraction * placeable.width(),
2237 self.y_fraction * placeable.height(),
2238 )
2239 }
2240
2241 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
2242 measurable.min_intrinsic_width(height)
2243 }
2244
2245 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
2246 measurable.max_intrinsic_width(height)
2247 }
2248
2249 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
2250 measurable.min_intrinsic_height(width)
2251 }
2252
2253 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
2254 measurable.max_intrinsic_height(width)
2255 }
2256}
2257
2258#[derive(Debug, Clone, PartialEq)]
2260pub struct FractionalOffsetElement {
2261 x_fraction: f32,
2262 y_fraction: f32,
2263}
2264
2265impl FractionalOffsetElement {
2266 pub fn new(x_fraction: f32, y_fraction: f32) -> Self {
2267 Self {
2268 x_fraction,
2269 y_fraction,
2270 }
2271 }
2272}
2273
2274impl Hash for FractionalOffsetElement {
2275 fn hash<H: Hasher>(&self, state: &mut H) {
2276 "fractional_offset".hash(state);
2277 hash_f32_value(state, self.x_fraction);
2278 hash_f32_value(state, self.y_fraction);
2279 }
2280}
2281
2282impl ModifierNodeElement for FractionalOffsetElement {
2283 type Node = FractionalOffsetNode;
2284
2285 fn create(&self) -> Self::Node {
2286 FractionalOffsetNode::new(self.x_fraction, self.y_fraction)
2287 }
2288
2289 fn update(&self, node: &mut Self::Node) {
2290 if node.x_fraction != self.x_fraction || node.y_fraction != self.y_fraction {
2291 node.x_fraction = self.x_fraction;
2292 node.y_fraction = self.y_fraction;
2293 }
2294 }
2295
2296 fn capabilities(&self) -> NodeCapabilities {
2297 NodeCapabilities::LAYOUT
2298 }
2299
2300 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
2301 Some(InvalidationKind::Layout)
2302 }
2303}
2304
2305#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2311pub enum FillDirection {
2312 Horizontal,
2313 Vertical,
2314 Both,
2315}
2316
2317#[derive(Debug)]
2321pub struct FillNode {
2322 direction: FillDirection,
2323 fraction: f32,
2324 state: NodeState,
2325}
2326
2327impl FillNode {
2328 pub fn new(direction: FillDirection, fraction: f32) -> Self {
2329 Self {
2330 direction,
2331 fraction,
2332 state: NodeState::new(),
2333 }
2334 }
2335
2336 pub fn direction(&self) -> FillDirection {
2337 self.direction
2338 }
2339
2340 pub fn fraction(&self) -> f32 {
2341 self.fraction
2342 }
2343}
2344
2345impl DelegatableNode for FillNode {
2346 fn node_state(&self) -> &NodeState {
2347 &self.state
2348 }
2349}
2350
2351impl ModifierNode for FillNode {
2352 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2353 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2354 }
2355
2356 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
2357 Some(self)
2358 }
2359
2360 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
2361 Some(self)
2362 }
2363}
2364
2365impl LayoutModifierNode for FillNode {
2366 fn measure(
2367 &self,
2368 _context: &mut dyn ModifierNodeContext,
2369 measurable: &dyn Measurable,
2370 constraints: Constraints,
2371 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2372 let (fill_width, child_min_width, child_max_width) = if self.direction
2374 != FillDirection::Vertical
2375 && constraints.max_width != f32::INFINITY
2376 {
2377 let width = (constraints.max_width * self.fraction)
2378 .round()
2379 .clamp(constraints.min_width, constraints.max_width);
2380 (width, width, width)
2382 } else {
2383 (
2384 constraints.max_width,
2385 constraints.min_width,
2386 constraints.max_width,
2387 )
2388 };
2389
2390 let (fill_height, child_min_height, child_max_height) = if self.direction
2391 != FillDirection::Horizontal
2392 && constraints.max_height != f32::INFINITY
2393 {
2394 let height = (constraints.max_height * self.fraction)
2395 .round()
2396 .clamp(constraints.min_height, constraints.max_height);
2397 (height, height, height)
2399 } else {
2400 (
2401 constraints.max_height,
2402 constraints.min_height,
2403 constraints.max_height,
2404 )
2405 };
2406
2407 let fill_constraints = Constraints {
2408 min_width: child_min_width,
2409 max_width: child_max_width,
2410 min_height: child_min_height,
2411 max_height: child_max_height,
2412 };
2413
2414 let placeable = measurable.measure(fill_constraints);
2415
2416 let result_width = if self.direction != FillDirection::Vertical
2420 && constraints.max_width != f32::INFINITY
2421 {
2422 fill_width
2423 } else {
2424 placeable.width()
2425 };
2426
2427 let result_height = if self.direction != FillDirection::Horizontal
2428 && constraints.max_height != f32::INFINITY
2429 {
2430 fill_height
2431 } else {
2432 placeable.height()
2433 };
2434
2435 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
2436 width: result_width,
2437 height: result_height,
2438 })
2439 }
2440
2441 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
2442 measurable.min_intrinsic_width(height)
2443 }
2444
2445 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
2446 measurable.max_intrinsic_width(height)
2447 }
2448
2449 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
2450 measurable.min_intrinsic_height(width)
2451 }
2452
2453 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
2454 measurable.max_intrinsic_height(width)
2455 }
2456}
2457
2458#[derive(Debug, Clone, PartialEq)]
2462pub struct FillElement {
2463 direction: FillDirection,
2464 fraction: f32,
2465}
2466
2467impl FillElement {
2468 pub fn width(fraction: f32) -> Self {
2469 Self {
2470 direction: FillDirection::Horizontal,
2471 fraction,
2472 }
2473 }
2474
2475 pub fn height(fraction: f32) -> Self {
2476 Self {
2477 direction: FillDirection::Vertical,
2478 fraction,
2479 }
2480 }
2481
2482 pub fn size(fraction: f32) -> Self {
2483 Self {
2484 direction: FillDirection::Both,
2485 fraction,
2486 }
2487 }
2488}
2489
2490impl Hash for FillElement {
2491 fn hash<H: Hasher>(&self, state: &mut H) {
2492 self.direction.hash(state);
2493 hash_f32_value(state, self.fraction);
2494 }
2495}
2496
2497impl ModifierNodeElement for FillElement {
2498 type Node = FillNode;
2499
2500 fn create(&self) -> Self::Node {
2501 FillNode::new(self.direction, self.fraction)
2502 }
2503
2504 fn update(&self, node: &mut Self::Node) {
2505 if node.direction != self.direction || node.fraction != self.fraction {
2506 node.direction = self.direction;
2507 node.fraction = self.fraction;
2508 }
2509 }
2510
2511 fn capabilities(&self) -> NodeCapabilities {
2512 NodeCapabilities::LAYOUT
2513 }
2514}
2515
2516#[derive(Debug)]
2522pub struct WeightNode {
2523 weight: f32,
2524 fill: bool,
2525 state: NodeState,
2526}
2527
2528impl WeightNode {
2529 pub fn new(weight: f32, fill: bool) -> Self {
2530 Self {
2531 weight,
2532 fill,
2533 state: NodeState::new(),
2534 }
2535 }
2536
2537 pub fn layout_weight(&self) -> LayoutWeight {
2538 LayoutWeight {
2539 weight: self.weight,
2540 fill: self.fill,
2541 }
2542 }
2543}
2544
2545impl DelegatableNode for WeightNode {
2546 fn node_state(&self) -> &NodeState {
2547 &self.state
2548 }
2549}
2550
2551impl ModifierNode for WeightNode {
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 WeightElement {
2560 weight: f32,
2561 fill: bool,
2562}
2563
2564impl WeightElement {
2565 pub fn new(weight: f32, fill: bool) -> Self {
2566 Self { weight, fill }
2567 }
2568}
2569
2570impl Hash for WeightElement {
2571 fn hash<H: Hasher>(&self, state: &mut H) {
2572 hash_f32_value(state, self.weight);
2573 self.fill.hash(state);
2574 }
2575}
2576
2577impl ModifierNodeElement for WeightElement {
2578 type Node = WeightNode;
2579
2580 fn create(&self) -> Self::Node {
2581 WeightNode::new(self.weight, self.fill)
2582 }
2583
2584 fn update(&self, node: &mut Self::Node) {
2585 if node.weight != self.weight || node.fill != self.fill {
2586 node.weight = self.weight;
2587 node.fill = self.fill;
2588 }
2589 }
2590
2591 fn capabilities(&self) -> NodeCapabilities {
2592 NodeCapabilities::LAYOUT
2593 }
2594}
2595
2596#[derive(Debug)]
2602pub struct AlignmentNode {
2603 box_alignment: Option<Alignment>,
2604 column_alignment: Option<HorizontalAlignment>,
2605 row_alignment: Option<VerticalAlignment>,
2606 state: NodeState,
2607}
2608
2609impl AlignmentNode {
2610 pub fn new(
2611 box_alignment: Option<Alignment>,
2612 column_alignment: Option<HorizontalAlignment>,
2613 row_alignment: Option<VerticalAlignment>,
2614 ) -> Self {
2615 Self {
2616 box_alignment,
2617 column_alignment,
2618 row_alignment,
2619 state: NodeState::new(),
2620 }
2621 }
2622
2623 pub fn box_alignment(&self) -> Option<Alignment> {
2624 self.box_alignment
2625 }
2626
2627 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
2628 self.column_alignment
2629 }
2630
2631 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
2632 self.row_alignment
2633 }
2634}
2635
2636impl DelegatableNode for AlignmentNode {
2637 fn node_state(&self) -> &NodeState {
2638 &self.state
2639 }
2640}
2641
2642impl ModifierNode for AlignmentNode {
2643 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2644 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2645 }
2646}
2647
2648#[derive(Debug, Clone, PartialEq)]
2650pub struct AlignmentElement {
2651 box_alignment: Option<Alignment>,
2652 column_alignment: Option<HorizontalAlignment>,
2653 row_alignment: Option<VerticalAlignment>,
2654}
2655
2656impl AlignmentElement {
2657 pub fn box_alignment(alignment: Alignment) -> Self {
2658 Self {
2659 box_alignment: Some(alignment),
2660 column_alignment: None,
2661 row_alignment: None,
2662 }
2663 }
2664
2665 pub fn column_alignment(alignment: HorizontalAlignment) -> Self {
2666 Self {
2667 box_alignment: None,
2668 column_alignment: Some(alignment),
2669 row_alignment: None,
2670 }
2671 }
2672
2673 pub fn row_alignment(alignment: VerticalAlignment) -> Self {
2674 Self {
2675 box_alignment: None,
2676 column_alignment: None,
2677 row_alignment: Some(alignment),
2678 }
2679 }
2680}
2681
2682impl Hash for AlignmentElement {
2683 fn hash<H: Hasher>(&self, state: &mut H) {
2684 if let Some(alignment) = self.box_alignment {
2685 state.write_u8(1);
2686 hash_alignment(state, alignment);
2687 } else {
2688 state.write_u8(0);
2689 }
2690 if let Some(alignment) = self.column_alignment {
2691 state.write_u8(1);
2692 hash_horizontal_alignment(state, alignment);
2693 } else {
2694 state.write_u8(0);
2695 }
2696 if let Some(alignment) = self.row_alignment {
2697 state.write_u8(1);
2698 hash_vertical_alignment(state, alignment);
2699 } else {
2700 state.write_u8(0);
2701 }
2702 }
2703}
2704
2705impl ModifierNodeElement for AlignmentElement {
2706 type Node = AlignmentNode;
2707
2708 fn create(&self) -> Self::Node {
2709 AlignmentNode::new(
2710 self.box_alignment,
2711 self.column_alignment,
2712 self.row_alignment,
2713 )
2714 }
2715
2716 fn update(&self, node: &mut Self::Node) {
2717 if node.box_alignment != self.box_alignment {
2718 node.box_alignment = self.box_alignment;
2719 }
2720 if node.column_alignment != self.column_alignment {
2721 node.column_alignment = self.column_alignment;
2722 }
2723 if node.row_alignment != self.row_alignment {
2724 node.row_alignment = self.row_alignment;
2725 }
2726 }
2727
2728 fn capabilities(&self) -> NodeCapabilities {
2729 NodeCapabilities::LAYOUT
2730 }
2731}
2732
2733#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2738pub enum IntrinsicAxis {
2739 Width,
2740 Height,
2741}
2742
2743#[derive(Debug)]
2745pub struct IntrinsicSizeNode {
2746 axis: IntrinsicAxis,
2747 size: IntrinsicSize,
2748 state: NodeState,
2749}
2750
2751impl IntrinsicSizeNode {
2752 pub fn new(axis: IntrinsicAxis, size: IntrinsicSize) -> Self {
2753 Self {
2754 axis,
2755 size,
2756 state: NodeState::new(),
2757 }
2758 }
2759
2760 pub fn axis(&self) -> IntrinsicAxis {
2761 self.axis
2762 }
2763
2764 pub fn intrinsic_size(&self) -> IntrinsicSize {
2765 self.size
2766 }
2767}
2768
2769impl DelegatableNode for IntrinsicSizeNode {
2770 fn node_state(&self) -> &NodeState {
2771 &self.state
2772 }
2773}
2774
2775impl ModifierNode for IntrinsicSizeNode {
2776 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2777 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2778 }
2779}
2780
2781#[derive(Debug, Clone, PartialEq)]
2783pub struct IntrinsicSizeElement {
2784 axis: IntrinsicAxis,
2785 size: IntrinsicSize,
2786}
2787
2788impl IntrinsicSizeElement {
2789 pub fn width(size: IntrinsicSize) -> Self {
2790 Self {
2791 axis: IntrinsicAxis::Width,
2792 size,
2793 }
2794 }
2795
2796 pub fn height(size: IntrinsicSize) -> Self {
2797 Self {
2798 axis: IntrinsicAxis::Height,
2799 size,
2800 }
2801 }
2802}
2803
2804impl Hash for IntrinsicSizeElement {
2805 fn hash<H: Hasher>(&self, state: &mut H) {
2806 state.write_u8(match self.axis {
2807 IntrinsicAxis::Width => 0,
2808 IntrinsicAxis::Height => 1,
2809 });
2810 state.write_u8(match self.size {
2811 IntrinsicSize::Min => 0,
2812 IntrinsicSize::Max => 1,
2813 });
2814 }
2815}
2816
2817impl ModifierNodeElement for IntrinsicSizeElement {
2818 type Node = IntrinsicSizeNode;
2819
2820 fn create(&self) -> Self::Node {
2821 IntrinsicSizeNode::new(self.axis, self.size)
2822 }
2823
2824 fn update(&self, node: &mut Self::Node) {
2825 if node.axis != self.axis {
2826 node.axis = self.axis;
2827 }
2828 if node.size != self.size {
2829 node.size = self.size;
2830 }
2831 }
2832
2833 fn capabilities(&self) -> NodeCapabilities {
2834 NodeCapabilities::LAYOUT
2835 }
2836}
2837
2838#[cfg(test)]
2839#[path = "tests/modifier_nodes_tests.rs"]
2840mod tests;