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