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