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