1use cranpose_core::NodeId;
64use cranpose_foundation::{
65 Constraints, DelegatableNode, DrawModifierNode, DrawScope, LayoutModifierNode, Measurable,
66 ModifierNode, ModifierNodeContext, ModifierNodeElement, NodeCapabilities, NodeState,
67 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
219const LAZY_GRAPHICS_LAYER_SCOPE_ID: usize = 1;
220
221#[derive(Debug)]
227pub struct PaddingNode {
228 padding: EdgeInsets,
229 state: NodeState,
230}
231
232impl PaddingNode {
233 pub fn new(padding: EdgeInsets) -> Self {
234 Self {
235 padding,
236 state: NodeState::new(),
237 }
238 }
239
240 pub fn padding(&self) -> EdgeInsets {
241 self.padding
242 }
243}
244
245impl DelegatableNode for PaddingNode {
246 fn node_state(&self) -> &NodeState {
247 &self.state
248 }
249}
250
251impl ModifierNode for PaddingNode {
252 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
253 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
254 }
255
256 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
257 Some(self)
258 }
259
260 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
261 Some(self)
262 }
263}
264
265impl LayoutModifierNode for PaddingNode {
266 fn measure(
267 &self,
268 _context: &mut dyn ModifierNodeContext,
269 measurable: &dyn Measurable,
270 constraints: Constraints,
271 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
272 let horizontal_padding = self.padding.horizontal_sum();
274 let vertical_padding = self.padding.vertical_sum();
275
276 let inner_constraints = Constraints {
278 min_width: (constraints.min_width - horizontal_padding).max(0.0),
279 max_width: (constraints.max_width - horizontal_padding).max(0.0),
280 min_height: (constraints.min_height - vertical_padding).max(0.0),
281 max_height: (constraints.max_height - vertical_padding).max(0.0),
282 };
283
284 let inner_placeable = measurable.measure(inner_constraints);
286 let inner_width = inner_placeable.width();
287 let inner_height = inner_placeable.height();
288
289 let (width, height) = constraints.constrain(
290 inner_width + horizontal_padding,
291 inner_height + vertical_padding,
292 );
293
294 cranpose_ui_layout::LayoutModifierMeasureResult::new(
296 Size { width, height },
297 self.padding.left, self.padding.top, )
300 }
301
302 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
303 let vertical_padding = self.padding.vertical_sum();
304 let inner_height = (height - vertical_padding).max(0.0);
305 let inner_width = measurable.min_intrinsic_width(inner_height);
306 inner_width + self.padding.horizontal_sum()
307 }
308
309 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
310 let vertical_padding = self.padding.vertical_sum();
311 let inner_height = (height - vertical_padding).max(0.0);
312 let inner_width = measurable.max_intrinsic_width(inner_height);
313 inner_width + self.padding.horizontal_sum()
314 }
315
316 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
317 let horizontal_padding = self.padding.horizontal_sum();
318 let inner_width = (width - horizontal_padding).max(0.0);
319 let inner_height = measurable.min_intrinsic_height(inner_width);
320 inner_height + self.padding.vertical_sum()
321 }
322
323 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
324 let horizontal_padding = self.padding.horizontal_sum();
325 let inner_width = (width - horizontal_padding).max(0.0);
326 let inner_height = measurable.max_intrinsic_height(inner_width);
327 inner_height + self.padding.vertical_sum()
328 }
329}
330
331#[derive(Debug, Clone, PartialEq)]
333pub struct PaddingElement {
334 padding: EdgeInsets,
335}
336
337impl PaddingElement {
338 pub fn new(padding: EdgeInsets) -> Self {
339 Self { padding }
340 }
341}
342
343impl Hash for PaddingElement {
344 fn hash<H: Hasher>(&self, state: &mut H) {
345 hash_f32_value(state, self.padding.left);
346 hash_f32_value(state, self.padding.top);
347 hash_f32_value(state, self.padding.right);
348 hash_f32_value(state, self.padding.bottom);
349 }
350}
351
352impl ModifierNodeElement for PaddingElement {
353 type Node = PaddingNode;
354
355 fn create(&self) -> Self::Node {
356 PaddingNode::new(self.padding)
357 }
358
359 fn update(&self, node: &mut Self::Node) {
360 if node.padding != self.padding {
361 node.padding = self.padding;
362 }
363 }
364
365 fn capabilities(&self) -> NodeCapabilities {
366 NodeCapabilities::LAYOUT
367 }
368}
369
370#[derive(Debug)]
376pub struct BackgroundNode {
377 color: Color,
378 shape: Option<RoundedCornerShape>,
379 state: NodeState,
380}
381
382impl BackgroundNode {
383 pub fn new(color: Color) -> Self {
384 Self {
385 color,
386 shape: None,
387 state: NodeState::new(),
388 }
389 }
390
391 pub fn color(&self) -> Color {
392 self.color
393 }
394
395 pub fn shape(&self) -> Option<RoundedCornerShape> {
396 self.shape
397 }
398}
399
400impl DelegatableNode for BackgroundNode {
401 fn node_state(&self) -> &NodeState {
402 &self.state
403 }
404}
405
406impl ModifierNode for BackgroundNode {
407 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
408 context.invalidate(cranpose_foundation::InvalidationKind::Draw);
409 }
410
411 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
412 Some(self)
413 }
414
415 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
416 Some(self)
417 }
418}
419
420impl DrawModifierNode for BackgroundNode {
421 fn draw(&self, _draw_scope: &mut dyn DrawScope) {
422 }
424}
425
426#[derive(Debug, Clone, PartialEq)]
428pub struct BackgroundElement {
429 color: Color,
430}
431
432impl BackgroundElement {
433 pub fn new(color: Color) -> Self {
434 Self { color }
435 }
436}
437
438impl Hash for BackgroundElement {
439 fn hash<H: Hasher>(&self, state: &mut H) {
440 hash_f32_value(state, self.color.0);
441 hash_f32_value(state, self.color.1);
442 hash_f32_value(state, self.color.2);
443 hash_f32_value(state, self.color.3);
444 }
445}
446
447impl ModifierNodeElement for BackgroundElement {
448 type Node = BackgroundNode;
449
450 fn create(&self) -> Self::Node {
451 BackgroundNode::new(self.color)
452 }
453
454 fn update(&self, node: &mut Self::Node) {
455 if node.color != self.color {
456 node.color = self.color;
457 }
458 }
459
460 fn capabilities(&self) -> NodeCapabilities {
461 NodeCapabilities::DRAW
462 }
463}
464
465#[derive(Debug)]
471pub struct CornerShapeNode {
472 shape: RoundedCornerShape,
473 state: NodeState,
474}
475
476impl CornerShapeNode {
477 pub fn new(shape: RoundedCornerShape) -> Self {
478 Self {
479 shape,
480 state: NodeState::new(),
481 }
482 }
483
484 pub fn shape(&self) -> RoundedCornerShape {
485 self.shape
486 }
487}
488
489impl DelegatableNode for CornerShapeNode {
490 fn node_state(&self) -> &NodeState {
491 &self.state
492 }
493}
494
495impl ModifierNode for CornerShapeNode {
496 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
497 context.invalidate(cranpose_foundation::InvalidationKind::Draw);
498 }
499
500 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
501 Some(self)
502 }
503
504 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
505 Some(self)
506 }
507}
508
509impl DrawModifierNode for CornerShapeNode {
510 fn draw(&self, _draw_scope: &mut dyn DrawScope) {}
511}
512
513#[derive(Debug, Clone, PartialEq)]
515pub struct CornerShapeElement {
516 shape: RoundedCornerShape,
517}
518
519impl CornerShapeElement {
520 pub fn new(shape: RoundedCornerShape) -> Self {
521 Self { shape }
522 }
523}
524
525impl Hash for CornerShapeElement {
526 fn hash<H: Hasher>(&self, state: &mut H) {
527 let radii = self.shape.radii();
528 hash_f32_value(state, radii.top_left);
529 hash_f32_value(state, radii.top_right);
530 hash_f32_value(state, radii.bottom_right);
531 hash_f32_value(state, radii.bottom_left);
532 }
533}
534
535impl ModifierNodeElement for CornerShapeElement {
536 type Node = CornerShapeNode;
537
538 fn create(&self) -> Self::Node {
539 CornerShapeNode::new(self.shape)
540 }
541
542 fn update(&self, node: &mut Self::Node) {
543 if node.shape != self.shape {
544 node.shape = self.shape;
545 }
546 }
547
548 fn capabilities(&self) -> NodeCapabilities {
549 NodeCapabilities::DRAW
550 }
551}
552
553pub struct GraphicsLayerNode {
559 layer: GraphicsLayer,
560 layer_resolver: Option<Rc<dyn Fn() -> GraphicsLayer>>,
561 lazy_scope_id: Option<usize>,
562 lazy_observer: Option<cranpose_core::SnapshotStateObserver>,
563 state: NodeState,
564}
565
566impl GraphicsLayerNode {
567 pub fn new(layer: GraphicsLayer) -> Self {
568 Self {
569 layer,
570 layer_resolver: None,
571 lazy_scope_id: None,
572 lazy_observer: None,
573 state: NodeState::new(),
574 }
575 }
576
577 pub fn new_lazy(layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) -> Self {
578 let mut node = Self {
579 layer: GraphicsLayer::default(),
580 layer_resolver: Some(layer_resolver),
581 lazy_scope_id: None,
582 lazy_observer: None,
583 state: NodeState::new(),
584 };
585 node.ensure_lazy_observation();
586 node.layer = node.layer();
587 node
588 }
589
590 pub fn layer(&self) -> GraphicsLayer {
591 if let Some(resolve) = self.layer_resolver() {
592 resolve()
593 } else {
594 self.layer.clone()
595 }
596 }
597
598 pub fn layer_snapshot(&self) -> GraphicsLayer {
599 self.layer.clone()
600 }
601
602 pub fn layer_resolver(&self) -> Option<Rc<dyn Fn() -> GraphicsLayer>> {
603 self.layer_resolver.as_ref().map(|resolve| {
604 let resolve = resolve.clone();
605 match (&self.lazy_observer, self.lazy_scope_id) {
606 (Some(observer), Some(scope_id)) => {
607 let observer = observer.clone();
608 Rc::new(move || {
609 observer.observe_reads(
610 scope_id,
611 |_| crate::request_render_invalidation(),
612 || resolve(),
613 )
614 }) as Rc<dyn Fn() -> GraphicsLayer>
615 }
616 _ => resolve,
617 }
618 })
619 }
620
621 fn set_static(&mut self, layer: GraphicsLayer) {
622 self.layer = layer;
623 self.layer_resolver = None;
624 self.clear_lazy_observation();
625 }
626
627 fn set_lazy(&mut self, layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) {
628 self.layer_resolver = Some(layer_resolver);
629 self.ensure_lazy_observation();
630 self.layer = self.layer();
631 }
632
633 fn ensure_lazy_observation(&mut self) {
634 if self.layer_resolver.is_none() {
635 self.clear_lazy_observation();
636 return;
637 }
638 if self.lazy_observer.is_some() {
639 return;
640 }
641 let observer = cranpose_core::SnapshotStateObserver::new(|callback| callback());
642 observer.start();
643 self.lazy_scope_id = Some(LAZY_GRAPHICS_LAYER_SCOPE_ID);
644 self.lazy_observer = Some(observer);
645 }
646
647 fn clear_lazy_observation(&mut self) {
648 if let Some(observer) = self.lazy_observer.take() {
649 observer.stop();
650 }
651 self.lazy_scope_id = None;
652 }
653}
654
655impl DelegatableNode for GraphicsLayerNode {
656 fn node_state(&self) -> &NodeState {
657 &self.state
658 }
659}
660
661impl ModifierNode for GraphicsLayerNode {
662 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
663 context.invalidate(cranpose_foundation::InvalidationKind::Draw);
664 }
665
666 fn on_detach(&mut self) {
667 self.clear_lazy_observation();
668 }
669}
670
671impl std::fmt::Debug for GraphicsLayerNode {
672 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
673 f.debug_struct("GraphicsLayerNode")
674 .field("layer", &self.layer)
675 .field("lazy", &self.layer_resolver.is_some())
676 .finish()
677 }
678}
679
680#[derive(Debug, Clone, PartialEq)]
682pub struct GraphicsLayerElement {
683 layer: GraphicsLayer,
684}
685
686impl GraphicsLayerElement {
687 pub fn new(layer: GraphicsLayer) -> Self {
688 Self { layer }
689 }
690}
691
692impl Hash for GraphicsLayerElement {
693 fn hash<H: Hasher>(&self, state: &mut H) {
694 hash_graphics_layer(state, &self.layer);
695 }
696}
697
698impl ModifierNodeElement for GraphicsLayerElement {
699 type Node = GraphicsLayerNode;
700
701 fn create(&self) -> Self::Node {
702 GraphicsLayerNode::new(self.layer.clone())
703 }
704
705 fn update(&self, node: &mut Self::Node) {
706 node.set_static(self.layer.clone());
707 }
708
709 fn capabilities(&self) -> NodeCapabilities {
710 NodeCapabilities::DRAW
711 }
712}
713
714#[derive(Clone)]
716pub struct LazyGraphicsLayerElement {
717 layer_resolver: Rc<dyn Fn() -> GraphicsLayer>,
718}
719
720impl LazyGraphicsLayerElement {
721 pub fn new(layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) -> Self {
722 Self { layer_resolver }
723 }
724}
725
726impl std::fmt::Debug for LazyGraphicsLayerElement {
727 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
728 f.debug_struct("LazyGraphicsLayerElement")
729 .field("resolver", &"<closure>")
730 .finish()
731 }
732}
733
734impl PartialEq for LazyGraphicsLayerElement {
735 fn eq(&self, other: &Self) -> bool {
736 Rc::ptr_eq(&self.layer_resolver, &other.layer_resolver)
737 }
738}
739
740impl Eq for LazyGraphicsLayerElement {}
741
742impl Hash for LazyGraphicsLayerElement {
743 fn hash<H: Hasher>(&self, state: &mut H) {
744 let ptr = Rc::as_ptr(&self.layer_resolver) as *const ();
745 ptr.hash(state);
746 }
747}
748
749impl ModifierNodeElement for LazyGraphicsLayerElement {
750 type Node = GraphicsLayerNode;
751
752 fn create(&self) -> Self::Node {
753 GraphicsLayerNode::new_lazy(self.layer_resolver.clone())
754 }
755
756 fn update(&self, node: &mut Self::Node) {
757 node.set_lazy(self.layer_resolver.clone());
758 }
759
760 fn capabilities(&self) -> NodeCapabilities {
761 NodeCapabilities::DRAW
762 }
763
764 fn always_update(&self) -> bool {
765 true
766 }
767}
768
769#[derive(Debug)]
777pub struct SizeNode {
778 min_width: Option<f32>,
779 max_width: Option<f32>,
780 min_height: Option<f32>,
781 max_height: Option<f32>,
782 enforce_incoming: bool,
783 state: NodeState,
784}
785
786impl SizeNode {
787 pub fn new(
788 min_width: Option<f32>,
789 max_width: Option<f32>,
790 min_height: Option<f32>,
791 max_height: Option<f32>,
792 enforce_incoming: bool,
793 ) -> Self {
794 Self {
795 min_width,
796 max_width,
797 min_height,
798 max_height,
799 enforce_incoming,
800 state: NodeState::new(),
801 }
802 }
803
804 fn target_constraints(&self) -> Constraints {
806 let max_width = self.max_width.map(|v| v.max(0.0)).unwrap_or(f32::INFINITY);
807 let max_height = self.max_height.map(|v| v.max(0.0)).unwrap_or(f32::INFINITY);
808
809 let min_width = self
810 .min_width
811 .map(|v| {
812 let clamped = v.clamp(0.0, max_width);
813 if clamped == f32::INFINITY {
814 0.0
815 } else {
816 clamped
817 }
818 })
819 .unwrap_or(0.0);
820
821 let min_height = self
822 .min_height
823 .map(|v| {
824 let clamped = v.clamp(0.0, max_height);
825 if clamped == f32::INFINITY {
826 0.0
827 } else {
828 clamped
829 }
830 })
831 .unwrap_or(0.0);
832
833 Constraints {
834 min_width,
835 max_width,
836 min_height,
837 max_height,
838 }
839 }
840
841 pub fn min_width(&self) -> Option<f32> {
842 self.min_width
843 }
844
845 pub fn max_width(&self) -> Option<f32> {
846 self.max_width
847 }
848
849 pub fn min_height(&self) -> Option<f32> {
850 self.min_height
851 }
852
853 pub fn max_height(&self) -> Option<f32> {
854 self.max_height
855 }
856
857 pub fn enforce_incoming(&self) -> bool {
858 self.enforce_incoming
859 }
860}
861
862impl DelegatableNode for SizeNode {
863 fn node_state(&self) -> &NodeState {
864 &self.state
865 }
866}
867
868impl ModifierNode for SizeNode {
869 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
870 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
871 }
872
873 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
874 Some(self)
875 }
876
877 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
878 Some(self)
879 }
880}
881
882impl LayoutModifierNode for SizeNode {
883 fn measure(
884 &self,
885 _context: &mut dyn ModifierNodeContext,
886 measurable: &dyn Measurable,
887 constraints: Constraints,
888 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
889 let target = self.target_constraints();
890
891 let wrapped_constraints = if self.enforce_incoming {
892 Constraints {
894 min_width: target
895 .min_width
896 .max(constraints.min_width)
897 .min(constraints.max_width),
898 max_width: target
899 .max_width
900 .min(constraints.max_width)
901 .max(constraints.min_width),
902 min_height: target
903 .min_height
904 .max(constraints.min_height)
905 .min(constraints.max_height),
906 max_height: target
907 .max_height
908 .min(constraints.max_height)
909 .max(constraints.min_height),
910 }
911 } else {
912 let resolved_min_width = if self.min_width.is_some() {
914 target.min_width
915 } else {
916 constraints.min_width.min(target.max_width)
917 };
918 let resolved_max_width = if self.max_width.is_some() {
919 target.max_width
920 } else {
921 constraints.max_width.max(target.min_width)
922 };
923 let resolved_min_height = if self.min_height.is_some() {
924 target.min_height
925 } else {
926 constraints.min_height.min(target.max_height)
927 };
928 let resolved_max_height = if self.max_height.is_some() {
929 target.max_height
930 } else {
931 constraints.max_height.max(target.min_height)
932 };
933
934 Constraints {
935 min_width: resolved_min_width,
936 max_width: resolved_max_width,
937 min_height: resolved_min_height,
938 max_height: resolved_max_height,
939 }
940 };
941
942 let placeable = measurable.measure(wrapped_constraints);
943 let measured_width = placeable.width();
944 let measured_height = placeable.height();
945
946 let result_width = if self.min_width.is_some()
950 && self.max_width.is_some()
951 && self.min_width == self.max_width
952 && target.min_width >= wrapped_constraints.min_width
953 && target.min_width <= wrapped_constraints.max_width
954 {
955 target.min_width
956 } else {
957 measured_width
958 };
959
960 let result_height = if self.min_height.is_some()
961 && self.max_height.is_some()
962 && self.min_height == self.max_height
963 && target.min_height >= wrapped_constraints.min_height
964 && target.min_height <= wrapped_constraints.max_height
965 {
966 target.min_height
967 } else {
968 measured_height
969 };
970
971 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
973 width: result_width,
974 height: result_height,
975 })
976 }
977
978 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
979 let target = self.target_constraints();
980 if target.min_width == target.max_width && target.max_width != f32::INFINITY {
981 target.max_width
982 } else {
983 let child_height = if self.enforce_incoming {
984 height
985 } else {
986 height.clamp(target.min_height, target.max_height)
987 };
988 measurable
989 .min_intrinsic_width(child_height)
990 .clamp(target.min_width, target.max_width)
991 }
992 }
993
994 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
995 let target = self.target_constraints();
996 if target.min_width == target.max_width && target.max_width != f32::INFINITY {
997 target.max_width
998 } else {
999 let child_height = if self.enforce_incoming {
1000 height
1001 } else {
1002 height.clamp(target.min_height, target.max_height)
1003 };
1004 measurable
1005 .max_intrinsic_width(child_height)
1006 .clamp(target.min_width, target.max_width)
1007 }
1008 }
1009
1010 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
1011 let target = self.target_constraints();
1012 if target.min_height == target.max_height && target.max_height != f32::INFINITY {
1013 target.max_height
1014 } else {
1015 let child_width = if self.enforce_incoming {
1016 width
1017 } else {
1018 width.clamp(target.min_width, target.max_width)
1019 };
1020 measurable
1021 .min_intrinsic_height(child_width)
1022 .clamp(target.min_height, target.max_height)
1023 }
1024 }
1025
1026 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
1027 let target = self.target_constraints();
1028 if target.min_height == target.max_height && target.max_height != f32::INFINITY {
1029 target.max_height
1030 } else {
1031 let child_width = if self.enforce_incoming {
1032 width
1033 } else {
1034 width.clamp(target.min_width, target.max_width)
1035 };
1036 measurable
1037 .max_intrinsic_height(child_width)
1038 .clamp(target.min_height, target.max_height)
1039 }
1040 }
1041}
1042
1043#[derive(Debug, Clone, PartialEq)]
1047pub struct SizeElement {
1048 min_width: Option<f32>,
1049 max_width: Option<f32>,
1050 min_height: Option<f32>,
1051 max_height: Option<f32>,
1052 enforce_incoming: bool,
1053}
1054
1055impl SizeElement {
1056 pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
1057 Self {
1058 min_width: width,
1059 max_width: width,
1060 min_height: height,
1061 max_height: height,
1062 enforce_incoming: true,
1063 }
1064 }
1065
1066 pub fn with_constraints(
1067 min_width: Option<f32>,
1068 max_width: Option<f32>,
1069 min_height: Option<f32>,
1070 max_height: Option<f32>,
1071 enforce_incoming: bool,
1072 ) -> Self {
1073 Self {
1074 min_width,
1075 max_width,
1076 min_height,
1077 max_height,
1078 enforce_incoming,
1079 }
1080 }
1081}
1082
1083impl Hash for SizeElement {
1084 fn hash<H: Hasher>(&self, state: &mut H) {
1085 hash_option_f32(state, self.min_width);
1086 hash_option_f32(state, self.max_width);
1087 hash_option_f32(state, self.min_height);
1088 hash_option_f32(state, self.max_height);
1089 self.enforce_incoming.hash(state);
1090 }
1091}
1092
1093impl ModifierNodeElement for SizeElement {
1094 type Node = SizeNode;
1095
1096 fn create(&self) -> Self::Node {
1097 SizeNode::new(
1098 self.min_width,
1099 self.max_width,
1100 self.min_height,
1101 self.max_height,
1102 self.enforce_incoming,
1103 )
1104 }
1105
1106 fn update(&self, node: &mut Self::Node) {
1107 if 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 node.min_width = self.min_width;
1114 node.max_width = self.max_width;
1115 node.min_height = self.min_height;
1116 node.max_height = self.max_height;
1117 node.enforce_incoming = self.enforce_incoming;
1118 }
1119 }
1120
1121 fn capabilities(&self) -> NodeCapabilities {
1122 NodeCapabilities::LAYOUT
1123 }
1124}
1125
1126use cranpose_foundation::DRAG_THRESHOLD;
1133
1134use std::cell::RefCell;
1135
1136pub struct ClickableNode {
1141 on_click: Rc<dyn Fn(Point)>,
1142 state: NodeState,
1143 press_position: Rc<RefCell<Option<Point>>>,
1145 cached_handler: Rc<dyn Fn(PointerEvent)>,
1147}
1148
1149impl std::fmt::Debug for ClickableNode {
1150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1151 f.debug_struct("ClickableNode").finish()
1152 }
1153}
1154
1155impl ClickableNode {
1156 pub fn new(on_click: impl Fn(Point) + 'static) -> Self {
1157 Self::with_handler(Rc::new(on_click))
1158 }
1159
1160 pub fn with_handler(on_click: Rc<dyn Fn(Point)>) -> Self {
1161 let press_position = Rc::new(RefCell::new(None));
1162 let cached_handler = Self::create_handler(on_click.clone(), press_position.clone());
1163 Self {
1164 on_click,
1165 state: NodeState::new(),
1166 press_position,
1167 cached_handler,
1168 }
1169 }
1170
1171 fn create_handler(
1172 handler: Rc<dyn Fn(Point)>,
1173 press_position: Rc<RefCell<Option<Point>>>,
1174 ) -> Rc<dyn Fn(PointerEvent)> {
1175 Rc::new(move |event: PointerEvent| {
1176 if event.is_consumed() {
1178 *press_position.borrow_mut() = None;
1180 return;
1181 }
1182
1183 match event.kind {
1184 PointerEventKind::Down => {
1185 *press_position.borrow_mut() = Some(Point {
1187 x: event.global_position.x,
1188 y: event.global_position.y,
1189 });
1190 }
1191 PointerEventKind::Move => {
1192 }
1194 PointerEventKind::Up => {
1195 let press_pos_value = *press_position.borrow();
1197
1198 let should_click = if let Some(press_pos) = press_pos_value {
1199 let dx = event.global_position.x - press_pos.x;
1200 let dy = event.global_position.y - press_pos.y;
1201 let distance = (dx * dx + dy * dy).sqrt();
1202 distance <= DRAG_THRESHOLD
1203 } else {
1204 true
1208 };
1209
1210 *press_position.borrow_mut() = None;
1212
1213 if should_click {
1214 handler(Point {
1215 x: event.position.x,
1216 y: event.position.y,
1217 });
1218 event.consume();
1219 }
1220 }
1221 PointerEventKind::Cancel => {
1222 *press_position.borrow_mut() = None;
1224 }
1225 PointerEventKind::Scroll | PointerEventKind::Enter | PointerEventKind::Exit => {
1226 }
1228 }
1229 })
1230 }
1231
1232 pub fn handler(&self) -> Rc<dyn Fn(Point)> {
1233 self.on_click.clone()
1234 }
1235}
1236
1237impl DelegatableNode for ClickableNode {
1238 fn node_state(&self) -> &NodeState {
1239 &self.state
1240 }
1241}
1242
1243impl ModifierNode for ClickableNode {
1244 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1245 context.invalidate(cranpose_foundation::InvalidationKind::PointerInput);
1246 }
1247
1248 fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
1249 Some(self)
1250 }
1251
1252 fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
1253 Some(self)
1254 }
1255}
1256
1257impl PointerInputNode for ClickableNode {
1258 fn on_pointer_event(
1259 &mut self,
1260 _context: &mut dyn ModifierNodeContext,
1261 event: &PointerEvent,
1262 ) -> bool {
1263 (self.cached_handler)(event.clone());
1266 event.is_consumed()
1267 }
1268
1269 fn hit_test(&self, _x: f32, _y: f32) -> bool {
1270 true
1272 }
1273
1274 fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
1275 Some(self.cached_handler.clone())
1278 }
1279}
1280
1281#[derive(Clone)]
1283pub struct ClickableElement {
1284 on_click: Rc<dyn Fn(Point)>,
1285}
1286
1287impl ClickableElement {
1288 pub fn new(on_click: impl Fn(Point) + 'static) -> Self {
1289 Self {
1290 on_click: Rc::new(on_click),
1291 }
1292 }
1293
1294 pub fn with_handler(on_click: Rc<dyn Fn(Point)>) -> Self {
1295 Self { on_click }
1296 }
1297}
1298
1299impl std::fmt::Debug for ClickableElement {
1300 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1301 f.debug_struct("ClickableElement").finish()
1302 }
1303}
1304
1305impl PartialEq for ClickableElement {
1306 fn eq(&self, _other: &Self) -> bool {
1307 true
1311 }
1312}
1313
1314impl Eq for ClickableElement {}
1315
1316impl Hash for ClickableElement {
1317 fn hash<H: Hasher>(&self, state: &mut H) {
1318 "clickable".hash(state);
1320 }
1321}
1322
1323impl ModifierNodeElement for ClickableElement {
1324 type Node = ClickableNode;
1325
1326 fn create(&self) -> Self::Node {
1327 ClickableNode::with_handler(self.on_click.clone())
1328 }
1329
1330 fn update(&self, node: &mut Self::Node) {
1336 node.on_click = self.on_click.clone();
1339 node.cached_handler =
1341 ClickableNode::create_handler(node.on_click.clone(), node.press_position.clone());
1342 }
1343
1344 fn capabilities(&self) -> NodeCapabilities {
1345 NodeCapabilities::POINTER_INPUT
1346 }
1347
1348 fn always_update(&self) -> bool {
1349 true
1351 }
1352}
1353
1354#[derive(Debug)]
1360pub struct AlphaNode {
1361 alpha: f32,
1362 state: NodeState,
1363}
1364
1365impl AlphaNode {
1366 pub fn new(alpha: f32) -> Self {
1367 Self {
1368 alpha: alpha.clamp(0.0, 1.0),
1369 state: NodeState::new(),
1370 }
1371 }
1372}
1373
1374impl DelegatableNode for AlphaNode {
1375 fn node_state(&self) -> &NodeState {
1376 &self.state
1377 }
1378}
1379
1380impl ModifierNode for AlphaNode {
1381 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1382 context.invalidate(cranpose_foundation::InvalidationKind::Draw);
1383 }
1384
1385 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
1386 Some(self)
1387 }
1388
1389 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
1390 Some(self)
1391 }
1392}
1393
1394impl DrawModifierNode for AlphaNode {
1395 fn draw(&self, _draw_scope: &mut dyn DrawScope) {}
1396}
1397
1398#[derive(Debug, Clone, PartialEq)]
1400pub struct AlphaElement {
1401 alpha: f32,
1402}
1403
1404impl AlphaElement {
1405 pub fn new(alpha: f32) -> Self {
1406 Self {
1407 alpha: alpha.clamp(0.0, 1.0),
1408 }
1409 }
1410}
1411
1412impl Hash for AlphaElement {
1413 fn hash<H: Hasher>(&self, state: &mut H) {
1414 hash_f32_value(state, self.alpha);
1415 }
1416}
1417
1418impl ModifierNodeElement for AlphaElement {
1419 type Node = AlphaNode;
1420
1421 fn create(&self) -> Self::Node {
1422 AlphaNode::new(self.alpha)
1423 }
1424
1425 fn update(&self, node: &mut Self::Node) {
1426 let new_alpha = self.alpha.clamp(0.0, 1.0);
1427 if (node.alpha - new_alpha).abs() > f32::EPSILON {
1428 node.alpha = new_alpha;
1429 }
1430 }
1431
1432 fn capabilities(&self) -> NodeCapabilities {
1433 NodeCapabilities::DRAW
1434 }
1435}
1436
1437#[derive(Debug)]
1443pub struct ClipToBoundsNode {
1444 state: NodeState,
1445}
1446
1447impl ClipToBoundsNode {
1448 pub fn new() -> Self {
1449 Self {
1450 state: NodeState::new(),
1451 }
1452 }
1453}
1454
1455impl DelegatableNode for ClipToBoundsNode {
1456 fn node_state(&self) -> &NodeState {
1457 &self.state
1458 }
1459}
1460
1461impl ModifierNode for ClipToBoundsNode {
1462 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1463 context.invalidate(cranpose_foundation::InvalidationKind::Draw);
1464 }
1465
1466 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
1467 Some(self)
1468 }
1469
1470 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
1471 Some(self)
1472 }
1473}
1474
1475impl DrawModifierNode for ClipToBoundsNode {
1476 fn draw(&self, _draw_scope: &mut dyn DrawScope) {}
1477}
1478
1479#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1481pub struct ClipToBoundsElement;
1482
1483impl ClipToBoundsElement {
1484 pub fn new() -> Self {
1485 Self
1486 }
1487}
1488
1489impl ModifierNodeElement for ClipToBoundsElement {
1490 type Node = ClipToBoundsNode;
1491
1492 fn create(&self) -> Self::Node {
1493 ClipToBoundsNode::new()
1494 }
1495
1496 fn update(&self, _node: &mut Self::Node) {}
1497
1498 fn capabilities(&self) -> NodeCapabilities {
1499 NodeCapabilities::DRAW
1500 }
1501}
1502
1503pub struct DrawCommandNode {
1509 commands: Vec<DrawCommand>,
1510 node_id: Cell<Option<NodeId>>,
1511 state: NodeState,
1512}
1513
1514impl DrawCommandNode {
1515 pub fn new(commands: Vec<DrawCommand>) -> Self {
1516 Self {
1517 commands,
1518 node_id: Cell::new(None),
1519 state: NodeState::new(),
1520 }
1521 }
1522
1523 #[cfg(test)]
1524 pub fn commands(&self) -> &[DrawCommand] {
1525 &self.commands
1526 }
1527
1528 pub(crate) fn observed_commands(&self) -> Vec<DrawCommand> {
1529 let node_id = self.node_id.get();
1530 self.commands
1531 .iter()
1532 .cloned()
1533 .enumerate()
1534 .map(|(index, command)| observe_draw_command(command, node_id, index))
1535 .collect()
1536 }
1537}
1538
1539impl DelegatableNode for DrawCommandNode {
1540 fn node_state(&self) -> &NodeState {
1541 &self.state
1542 }
1543}
1544
1545impl ModifierNode for DrawCommandNode {
1546 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1547 self.node_id.set(context.node_id());
1548 context.invalidate(cranpose_foundation::InvalidationKind::Draw);
1549 }
1550
1551 fn on_detach(&mut self) {
1552 if let Some(node_id) = self.node_id.replace(None) {
1553 crate::render_state::clear_draw_observations_for_node(node_id);
1554 }
1555 }
1556
1557 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
1558 Some(self)
1559 }
1560
1561 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
1562 Some(self)
1563 }
1564}
1565
1566impl DrawModifierNode for DrawCommandNode {
1567 fn draw(&self, _draw_scope: &mut dyn DrawScope) {}
1568}
1569
1570fn observe_draw_command(
1571 command: DrawCommand,
1572 node_id: Option<NodeId>,
1573 command_index: usize,
1574) -> DrawCommand {
1575 let Some(node_id) = node_id else {
1576 return command;
1577 };
1578 let scope = crate::render_state::DrawObservationScope::new(node_id, command_index);
1579 match command {
1580 DrawCommand::Behind(draw) => DrawCommand::Behind(Rc::new(move |size| {
1581 crate::render_state::observe_draw_reads(scope, || draw(size))
1582 })),
1583 DrawCommand::WithContent(draw) => DrawCommand::WithContent(Rc::new(move |size| {
1584 crate::render_state::observe_draw_reads(scope, || draw(size))
1585 })),
1586 DrawCommand::Overlay(draw) => DrawCommand::Overlay(Rc::new(move |size| {
1587 crate::render_state::observe_draw_reads(scope, || draw(size))
1588 })),
1589 }
1590}
1591
1592fn draw_command_tag(cmd: &DrawCommand) -> u8 {
1593 match cmd {
1594 DrawCommand::Behind(_) => 0,
1595 DrawCommand::WithContent(_) => 1,
1596 DrawCommand::Overlay(_) => 2,
1597 }
1598}
1599
1600fn draw_command_closure_identity(cmd: &DrawCommand) -> *const () {
1601 match cmd {
1602 DrawCommand::Behind(f) | DrawCommand::WithContent(f) | DrawCommand::Overlay(f) => {
1603 Rc::as_ptr(f) as *const ()
1604 }
1605 }
1606}
1607
1608#[derive(Clone)]
1610pub struct DrawCommandElement {
1611 commands: Vec<DrawCommand>,
1612}
1613
1614impl DrawCommandElement {
1615 pub fn new(command: DrawCommand) -> Self {
1616 Self {
1617 commands: vec![command],
1618 }
1619 }
1620
1621 pub fn from_commands(commands: Vec<DrawCommand>) -> Self {
1622 Self { commands }
1623 }
1624}
1625
1626impl std::fmt::Debug for DrawCommandElement {
1627 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1628 f.debug_struct("DrawCommandElement")
1629 .field("commands", &self.commands.len())
1630 .finish()
1631 }
1632}
1633
1634impl PartialEq for DrawCommandElement {
1635 fn eq(&self, other: &Self) -> bool {
1636 if self.commands.len() != other.commands.len() {
1637 return false;
1638 }
1639 self.commands
1640 .iter()
1641 .zip(other.commands.iter())
1642 .all(|(a, b)| {
1643 draw_command_tag(a) == draw_command_tag(b)
1644 && draw_command_closure_identity(a) == draw_command_closure_identity(b)
1645 })
1646 }
1647}
1648
1649impl Eq for DrawCommandElement {}
1650
1651impl std::hash::Hash for DrawCommandElement {
1652 fn hash<H: Hasher>(&self, state: &mut H) {
1653 "draw_commands".hash(state);
1654 self.commands.len().hash(state);
1655 for command in &self.commands {
1656 draw_command_tag(command).hash(state);
1657 (draw_command_closure_identity(command) as usize).hash(state);
1658 }
1659 }
1660}
1661
1662impl ModifierNodeElement for DrawCommandElement {
1663 type Node = DrawCommandNode;
1664
1665 fn create(&self) -> Self::Node {
1666 DrawCommandNode::new(self.commands.clone())
1667 }
1668
1669 fn update(&self, node: &mut Self::Node) {
1670 node.commands = self.commands.clone();
1671 }
1672
1673 fn capabilities(&self) -> NodeCapabilities {
1674 NodeCapabilities::DRAW
1675 }
1676}
1677
1678#[derive(Debug)]
1686pub struct OffsetNode {
1687 x: f32,
1688 y: f32,
1689 rtl_aware: bool,
1690 state: NodeState,
1691}
1692
1693impl OffsetNode {
1694 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
1695 Self {
1696 x,
1697 y,
1698 rtl_aware,
1699 state: NodeState::new(),
1700 }
1701 }
1702
1703 pub fn offset(&self) -> Point {
1704 Point {
1705 x: self.x,
1706 y: self.y,
1707 }
1708 }
1709
1710 pub fn rtl_aware(&self) -> bool {
1711 self.rtl_aware
1712 }
1713}
1714
1715impl DelegatableNode for OffsetNode {
1716 fn node_state(&self) -> &NodeState {
1717 &self.state
1718 }
1719}
1720
1721impl ModifierNode for OffsetNode {
1722 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1723 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
1724 }
1725
1726 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
1727 Some(self)
1728 }
1729
1730 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
1731 Some(self)
1732 }
1733}
1734
1735impl LayoutModifierNode for OffsetNode {
1736 fn measure(
1737 &self,
1738 _context: &mut dyn ModifierNodeContext,
1739 measurable: &dyn Measurable,
1740 constraints: Constraints,
1741 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1742 let placeable = measurable.measure(constraints);
1744
1745 cranpose_ui_layout::LayoutModifierMeasureResult::new(
1747 Size {
1748 width: placeable.width(),
1749 height: placeable.height(),
1750 },
1751 self.x, self.y, )
1754 }
1755
1756 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
1757 measurable.min_intrinsic_width(height)
1758 }
1759
1760 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
1761 measurable.max_intrinsic_width(height)
1762 }
1763
1764 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
1765 measurable.min_intrinsic_height(width)
1766 }
1767
1768 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
1769 measurable.max_intrinsic_height(width)
1770 }
1771}
1772
1773#[derive(Debug, Clone, PartialEq)]
1777pub struct OffsetElement {
1778 x: f32,
1779 y: f32,
1780 rtl_aware: bool,
1781}
1782
1783impl OffsetElement {
1784 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
1785 Self { x, y, rtl_aware }
1786 }
1787}
1788
1789impl Hash for OffsetElement {
1790 fn hash<H: Hasher>(&self, state: &mut H) {
1791 hash_f32_value(state, self.x);
1792 hash_f32_value(state, self.y);
1793 self.rtl_aware.hash(state);
1794 }
1795}
1796
1797impl ModifierNodeElement for OffsetElement {
1798 type Node = OffsetNode;
1799
1800 fn create(&self) -> Self::Node {
1801 OffsetNode::new(self.x, self.y, self.rtl_aware)
1802 }
1803
1804 fn update(&self, node: &mut Self::Node) {
1805 if node.x != self.x || node.y != self.y || node.rtl_aware != self.rtl_aware {
1806 node.x = self.x;
1807 node.y = self.y;
1808 node.rtl_aware = self.rtl_aware;
1809 }
1810 }
1811
1812 fn capabilities(&self) -> NodeCapabilities {
1813 NodeCapabilities::LAYOUT
1814 }
1815}
1816
1817#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1823pub enum FillDirection {
1824 Horizontal,
1825 Vertical,
1826 Both,
1827}
1828
1829#[derive(Debug)]
1833pub struct FillNode {
1834 direction: FillDirection,
1835 fraction: f32,
1836 state: NodeState,
1837}
1838
1839impl FillNode {
1840 pub fn new(direction: FillDirection, fraction: f32) -> Self {
1841 Self {
1842 direction,
1843 fraction,
1844 state: NodeState::new(),
1845 }
1846 }
1847
1848 pub fn direction(&self) -> FillDirection {
1849 self.direction
1850 }
1851
1852 pub fn fraction(&self) -> f32 {
1853 self.fraction
1854 }
1855}
1856
1857impl DelegatableNode for FillNode {
1858 fn node_state(&self) -> &NodeState {
1859 &self.state
1860 }
1861}
1862
1863impl ModifierNode for FillNode {
1864 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1865 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
1866 }
1867
1868 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
1869 Some(self)
1870 }
1871
1872 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
1873 Some(self)
1874 }
1875}
1876
1877impl LayoutModifierNode for FillNode {
1878 fn measure(
1879 &self,
1880 _context: &mut dyn ModifierNodeContext,
1881 measurable: &dyn Measurable,
1882 constraints: Constraints,
1883 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1884 let (fill_width, child_min_width, child_max_width) = if self.direction
1886 != FillDirection::Vertical
1887 && constraints.max_width != f32::INFINITY
1888 {
1889 let width = (constraints.max_width * self.fraction)
1890 .round()
1891 .clamp(constraints.min_width, constraints.max_width);
1892 (width, width, width)
1894 } else {
1895 (
1896 constraints.max_width,
1897 constraints.min_width,
1898 constraints.max_width,
1899 )
1900 };
1901
1902 let (fill_height, child_min_height, child_max_height) = if self.direction
1903 != FillDirection::Horizontal
1904 && constraints.max_height != f32::INFINITY
1905 {
1906 let height = (constraints.max_height * self.fraction)
1907 .round()
1908 .clamp(constraints.min_height, constraints.max_height);
1909 (height, height, height)
1911 } else {
1912 (
1913 constraints.max_height,
1914 constraints.min_height,
1915 constraints.max_height,
1916 )
1917 };
1918
1919 let fill_constraints = Constraints {
1920 min_width: child_min_width,
1921 max_width: child_max_width,
1922 min_height: child_min_height,
1923 max_height: child_max_height,
1924 };
1925
1926 let placeable = measurable.measure(fill_constraints);
1927
1928 let result_width = if self.direction != FillDirection::Vertical
1932 && constraints.max_width != f32::INFINITY
1933 {
1934 fill_width
1935 } else {
1936 placeable.width()
1937 };
1938
1939 let result_height = if self.direction != FillDirection::Horizontal
1940 && constraints.max_height != f32::INFINITY
1941 {
1942 fill_height
1943 } else {
1944 placeable.height()
1945 };
1946
1947 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
1948 width: result_width,
1949 height: result_height,
1950 })
1951 }
1952
1953 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
1954 measurable.min_intrinsic_width(height)
1955 }
1956
1957 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
1958 measurable.max_intrinsic_width(height)
1959 }
1960
1961 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
1962 measurable.min_intrinsic_height(width)
1963 }
1964
1965 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
1966 measurable.max_intrinsic_height(width)
1967 }
1968}
1969
1970#[derive(Debug, Clone, PartialEq)]
1974pub struct FillElement {
1975 direction: FillDirection,
1976 fraction: f32,
1977}
1978
1979impl FillElement {
1980 pub fn width(fraction: f32) -> Self {
1981 Self {
1982 direction: FillDirection::Horizontal,
1983 fraction,
1984 }
1985 }
1986
1987 pub fn height(fraction: f32) -> Self {
1988 Self {
1989 direction: FillDirection::Vertical,
1990 fraction,
1991 }
1992 }
1993
1994 pub fn size(fraction: f32) -> Self {
1995 Self {
1996 direction: FillDirection::Both,
1997 fraction,
1998 }
1999 }
2000}
2001
2002impl Hash for FillElement {
2003 fn hash<H: Hasher>(&self, state: &mut H) {
2004 self.direction.hash(state);
2005 hash_f32_value(state, self.fraction);
2006 }
2007}
2008
2009impl ModifierNodeElement for FillElement {
2010 type Node = FillNode;
2011
2012 fn create(&self) -> Self::Node {
2013 FillNode::new(self.direction, self.fraction)
2014 }
2015
2016 fn update(&self, node: &mut Self::Node) {
2017 if node.direction != self.direction || node.fraction != self.fraction {
2018 node.direction = self.direction;
2019 node.fraction = self.fraction;
2020 }
2021 }
2022
2023 fn capabilities(&self) -> NodeCapabilities {
2024 NodeCapabilities::LAYOUT
2025 }
2026}
2027
2028#[derive(Debug)]
2034pub struct WeightNode {
2035 weight: f32,
2036 fill: bool,
2037 state: NodeState,
2038}
2039
2040impl WeightNode {
2041 pub fn new(weight: f32, fill: bool) -> Self {
2042 Self {
2043 weight,
2044 fill,
2045 state: NodeState::new(),
2046 }
2047 }
2048
2049 pub fn layout_weight(&self) -> LayoutWeight {
2050 LayoutWeight {
2051 weight: self.weight,
2052 fill: self.fill,
2053 }
2054 }
2055}
2056
2057impl DelegatableNode for WeightNode {
2058 fn node_state(&self) -> &NodeState {
2059 &self.state
2060 }
2061}
2062
2063impl ModifierNode for WeightNode {
2064 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2065 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2066 }
2067}
2068
2069#[derive(Debug, Clone, PartialEq)]
2071pub struct WeightElement {
2072 weight: f32,
2073 fill: bool,
2074}
2075
2076impl WeightElement {
2077 pub fn new(weight: f32, fill: bool) -> Self {
2078 Self { weight, fill }
2079 }
2080}
2081
2082impl Hash for WeightElement {
2083 fn hash<H: Hasher>(&self, state: &mut H) {
2084 hash_f32_value(state, self.weight);
2085 self.fill.hash(state);
2086 }
2087}
2088
2089impl ModifierNodeElement for WeightElement {
2090 type Node = WeightNode;
2091
2092 fn create(&self) -> Self::Node {
2093 WeightNode::new(self.weight, self.fill)
2094 }
2095
2096 fn update(&self, node: &mut Self::Node) {
2097 if node.weight != self.weight || node.fill != self.fill {
2098 node.weight = self.weight;
2099 node.fill = self.fill;
2100 }
2101 }
2102
2103 fn capabilities(&self) -> NodeCapabilities {
2104 NodeCapabilities::LAYOUT
2105 }
2106}
2107
2108#[derive(Debug)]
2114pub struct AlignmentNode {
2115 box_alignment: Option<Alignment>,
2116 column_alignment: Option<HorizontalAlignment>,
2117 row_alignment: Option<VerticalAlignment>,
2118 state: NodeState,
2119}
2120
2121impl AlignmentNode {
2122 pub fn new(
2123 box_alignment: Option<Alignment>,
2124 column_alignment: Option<HorizontalAlignment>,
2125 row_alignment: Option<VerticalAlignment>,
2126 ) -> Self {
2127 Self {
2128 box_alignment,
2129 column_alignment,
2130 row_alignment,
2131 state: NodeState::new(),
2132 }
2133 }
2134
2135 pub fn box_alignment(&self) -> Option<Alignment> {
2136 self.box_alignment
2137 }
2138
2139 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
2140 self.column_alignment
2141 }
2142
2143 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
2144 self.row_alignment
2145 }
2146}
2147
2148impl DelegatableNode for AlignmentNode {
2149 fn node_state(&self) -> &NodeState {
2150 &self.state
2151 }
2152}
2153
2154impl ModifierNode for AlignmentNode {
2155 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2156 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2157 }
2158}
2159
2160#[derive(Debug, Clone, PartialEq)]
2162pub struct AlignmentElement {
2163 box_alignment: Option<Alignment>,
2164 column_alignment: Option<HorizontalAlignment>,
2165 row_alignment: Option<VerticalAlignment>,
2166}
2167
2168impl AlignmentElement {
2169 pub fn box_alignment(alignment: Alignment) -> Self {
2170 Self {
2171 box_alignment: Some(alignment),
2172 column_alignment: None,
2173 row_alignment: None,
2174 }
2175 }
2176
2177 pub fn column_alignment(alignment: HorizontalAlignment) -> Self {
2178 Self {
2179 box_alignment: None,
2180 column_alignment: Some(alignment),
2181 row_alignment: None,
2182 }
2183 }
2184
2185 pub fn row_alignment(alignment: VerticalAlignment) -> Self {
2186 Self {
2187 box_alignment: None,
2188 column_alignment: None,
2189 row_alignment: Some(alignment),
2190 }
2191 }
2192}
2193
2194impl Hash for AlignmentElement {
2195 fn hash<H: Hasher>(&self, state: &mut H) {
2196 if let Some(alignment) = self.box_alignment {
2197 state.write_u8(1);
2198 hash_alignment(state, alignment);
2199 } else {
2200 state.write_u8(0);
2201 }
2202 if let Some(alignment) = self.column_alignment {
2203 state.write_u8(1);
2204 hash_horizontal_alignment(state, alignment);
2205 } else {
2206 state.write_u8(0);
2207 }
2208 if let Some(alignment) = self.row_alignment {
2209 state.write_u8(1);
2210 hash_vertical_alignment(state, alignment);
2211 } else {
2212 state.write_u8(0);
2213 }
2214 }
2215}
2216
2217impl ModifierNodeElement for AlignmentElement {
2218 type Node = AlignmentNode;
2219
2220 fn create(&self) -> Self::Node {
2221 AlignmentNode::new(
2222 self.box_alignment,
2223 self.column_alignment,
2224 self.row_alignment,
2225 )
2226 }
2227
2228 fn update(&self, node: &mut Self::Node) {
2229 if node.box_alignment != self.box_alignment {
2230 node.box_alignment = self.box_alignment;
2231 }
2232 if node.column_alignment != self.column_alignment {
2233 node.column_alignment = self.column_alignment;
2234 }
2235 if node.row_alignment != self.row_alignment {
2236 node.row_alignment = self.row_alignment;
2237 }
2238 }
2239
2240 fn capabilities(&self) -> NodeCapabilities {
2241 NodeCapabilities::LAYOUT
2242 }
2243}
2244
2245#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2250pub enum IntrinsicAxis {
2251 Width,
2252 Height,
2253}
2254
2255#[derive(Debug)]
2257pub struct IntrinsicSizeNode {
2258 axis: IntrinsicAxis,
2259 size: IntrinsicSize,
2260 state: NodeState,
2261}
2262
2263impl IntrinsicSizeNode {
2264 pub fn new(axis: IntrinsicAxis, size: IntrinsicSize) -> Self {
2265 Self {
2266 axis,
2267 size,
2268 state: NodeState::new(),
2269 }
2270 }
2271
2272 pub fn axis(&self) -> IntrinsicAxis {
2273 self.axis
2274 }
2275
2276 pub fn intrinsic_size(&self) -> IntrinsicSize {
2277 self.size
2278 }
2279}
2280
2281impl DelegatableNode for IntrinsicSizeNode {
2282 fn node_state(&self) -> &NodeState {
2283 &self.state
2284 }
2285}
2286
2287impl ModifierNode for IntrinsicSizeNode {
2288 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2289 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2290 }
2291}
2292
2293#[derive(Debug, Clone, PartialEq)]
2295pub struct IntrinsicSizeElement {
2296 axis: IntrinsicAxis,
2297 size: IntrinsicSize,
2298}
2299
2300impl IntrinsicSizeElement {
2301 pub fn width(size: IntrinsicSize) -> Self {
2302 Self {
2303 axis: IntrinsicAxis::Width,
2304 size,
2305 }
2306 }
2307
2308 pub fn height(size: IntrinsicSize) -> Self {
2309 Self {
2310 axis: IntrinsicAxis::Height,
2311 size,
2312 }
2313 }
2314}
2315
2316impl Hash for IntrinsicSizeElement {
2317 fn hash<H: Hasher>(&self, state: &mut H) {
2318 state.write_u8(match self.axis {
2319 IntrinsicAxis::Width => 0,
2320 IntrinsicAxis::Height => 1,
2321 });
2322 state.write_u8(match self.size {
2323 IntrinsicSize::Min => 0,
2324 IntrinsicSize::Max => 1,
2325 });
2326 }
2327}
2328
2329impl ModifierNodeElement for IntrinsicSizeElement {
2330 type Node = IntrinsicSizeNode;
2331
2332 fn create(&self) -> Self::Node {
2333 IntrinsicSizeNode::new(self.axis, self.size)
2334 }
2335
2336 fn update(&self, node: &mut Self::Node) {
2337 if node.axis != self.axis {
2338 node.axis = self.axis;
2339 }
2340 if node.size != self.size {
2341 node.size = self.size;
2342 }
2343 }
2344
2345 fn capabilities(&self) -> NodeCapabilities {
2346 NodeCapabilities::LAYOUT
2347 }
2348}
2349
2350#[cfg(test)]
2351#[path = "tests/modifier_nodes_tests.rs"]
2352mod tests;