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