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_or(f32::INFINITY, |v| v.max(0.0));
814 let max_height = self.max_height.map_or(f32::INFINITY, |v| v.max(0.0));
815
816 let min_width = self.min_width.map_or(0.0, |v| {
817 let clamped = v.clamp(0.0, max_width);
818 if clamped == f32::INFINITY {
819 0.0
820 } else {
821 clamped
822 }
823 });
824
825 let min_height = self.min_height.map_or(0.0, |v| {
826 let clamped = v.clamp(0.0, max_height);
827 if clamped == f32::INFINITY {
828 0.0
829 } else {
830 clamped
831 }
832 });
833
834 Constraints {
835 min_width,
836 max_width,
837 min_height,
838 max_height,
839 }
840 }
841
842 pub fn min_width(&self) -> Option<f32> {
843 self.min_width
844 }
845
846 pub fn max_width(&self) -> Option<f32> {
847 self.max_width
848 }
849
850 pub fn min_height(&self) -> Option<f32> {
851 self.min_height
852 }
853
854 pub fn max_height(&self) -> Option<f32> {
855 self.max_height
856 }
857
858 pub fn enforce_incoming(&self) -> bool {
859 self.enforce_incoming
860 }
861}
862
863impl DelegatableNode for SizeNode {
864 fn node_state(&self) -> &NodeState {
865 &self.state
866 }
867}
868
869impl_layout_modifier_node!(SizeNode, invalidate = InvalidationKind::Layout);
870
871impl LayoutModifierNode for SizeNode {
872 fn measure(
873 &self,
874 _context: &mut dyn ModifierNodeContext,
875 measurable: &dyn Measurable,
876 constraints: Constraints,
877 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
878 let target = self.target_constraints();
879
880 let wrapped_constraints = if self.enforce_incoming {
881 Constraints {
882 min_width: target
883 .min_width
884 .max(constraints.min_width)
885 .min(constraints.max_width),
886 max_width: target
887 .max_width
888 .min(constraints.max_width)
889 .max(constraints.min_width),
890 min_height: target
891 .min_height
892 .max(constraints.min_height)
893 .min(constraints.max_height),
894 max_height: target
895 .max_height
896 .min(constraints.max_height)
897 .max(constraints.min_height),
898 }
899 } else {
900 let resolved_min_width = if self.min_width.is_some() {
901 target.min_width
902 } else {
903 constraints.min_width.min(target.max_width)
904 };
905 let resolved_max_width = if self.max_width.is_some() {
906 target.max_width
907 } else {
908 constraints.max_width.max(target.min_width)
909 };
910 let resolved_min_height = if self.min_height.is_some() {
911 target.min_height
912 } else {
913 constraints.min_height.min(target.max_height)
914 };
915 let resolved_max_height = if self.max_height.is_some() {
916 target.max_height
917 } else {
918 constraints.max_height.max(target.min_height)
919 };
920
921 Constraints {
922 min_width: resolved_min_width,
923 max_width: resolved_max_width,
924 min_height: resolved_min_height,
925 max_height: resolved_max_height,
926 }
927 };
928
929 let placeable = measurable.measure(wrapped_constraints);
930 let measured_width = placeable.width();
931 let measured_height = placeable.height();
932
933 let result_width = if self.min_width.is_some()
934 && self.max_width.is_some()
935 && self.min_width == self.max_width
936 && target.min_width >= wrapped_constraints.min_width
937 && target.min_width <= wrapped_constraints.max_width
938 {
939 target.min_width
940 } else {
941 measured_width
942 };
943
944 let result_height = if self.min_height.is_some()
945 && self.max_height.is_some()
946 && self.min_height == self.max_height
947 && target.min_height >= wrapped_constraints.min_height
948 && target.min_height <= wrapped_constraints.max_height
949 {
950 target.min_height
951 } else {
952 measured_height
953 };
954
955 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
956 width: result_width,
957 height: result_height,
958 })
959 }
960
961 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
962 size_intrinsic(
963 self.target_constraints(),
964 SizeAxis::Width,
965 self.enforce_incoming,
966 height,
967 |h| measurable.min_intrinsic_width(h),
968 )
969 }
970
971 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> f32 {
972 size_intrinsic(
973 self.target_constraints(),
974 SizeAxis::Width,
975 self.enforce_incoming,
976 height,
977 |h| measurable.max_intrinsic_width(h),
978 )
979 }
980
981 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
982 size_intrinsic(
983 self.target_constraints(),
984 SizeAxis::Height,
985 self.enforce_incoming,
986 width,
987 |w| measurable.min_intrinsic_height(w),
988 )
989 }
990
991 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> f32 {
992 size_intrinsic(
993 self.target_constraints(),
994 SizeAxis::Height,
995 self.enforce_incoming,
996 width,
997 |w| measurable.max_intrinsic_height(w),
998 )
999 }
1000}
1001
1002#[derive(Debug, Clone, PartialEq)]
1006pub struct SizeElement {
1007 min_width: Option<f32>,
1008 max_width: Option<f32>,
1009 min_height: Option<f32>,
1010 max_height: Option<f32>,
1011 enforce_incoming: bool,
1012}
1013
1014impl SizeElement {
1015 pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
1016 Self {
1017 min_width: width,
1018 max_width: width,
1019 min_height: height,
1020 max_height: height,
1021 enforce_incoming: true,
1022 }
1023 }
1024
1025 pub fn with_constraints(
1026 min_width: Option<f32>,
1027 max_width: Option<f32>,
1028 min_height: Option<f32>,
1029 max_height: Option<f32>,
1030 enforce_incoming: bool,
1031 ) -> Self {
1032 Self {
1033 min_width,
1034 max_width,
1035 min_height,
1036 max_height,
1037 enforce_incoming,
1038 }
1039 }
1040}
1041
1042impl Hash for SizeElement {
1043 fn hash<H: Hasher>(&self, state: &mut H) {
1044 hash_option_f32(state, self.min_width);
1045 hash_option_f32(state, self.max_width);
1046 hash_option_f32(state, self.min_height);
1047 hash_option_f32(state, self.max_height);
1048 self.enforce_incoming.hash(state);
1049 }
1050}
1051
1052impl ModifierNodeElement for SizeElement {
1053 type Node = SizeNode;
1054
1055 fn create(&self) -> Self::Node {
1056 SizeNode::new(
1057 self.min_width,
1058 self.max_width,
1059 self.min_height,
1060 self.max_height,
1061 self.enforce_incoming,
1062 )
1063 }
1064
1065 fn update(&self, node: &mut Self::Node) {
1066 if node.min_width != self.min_width
1067 || node.max_width != self.max_width
1068 || node.min_height != self.min_height
1069 || node.max_height != self.max_height
1070 || node.enforce_incoming != self.enforce_incoming
1071 {
1072 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 }
1079
1080 fn capabilities(&self) -> NodeCapabilities {
1081 NodeCapabilities::LAYOUT
1082 }
1083
1084 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
1085 Some(InvalidationKind::Layout)
1086 }
1087}
1088
1089use std::cell::RefCell;
1090
1091use cranpose_foundation::DRAG_THRESHOLD;
1092
1093pub struct ClickableNode {
1094 on_press: Option<Rc<dyn Fn(Point)>>,
1095 on_click: Rc<dyn Fn(Point)>,
1096 state: NodeState,
1097 press_position: Rc<RefCell<Option<Point>>>,
1098 cached_handler: Rc<dyn Fn(PointerEvent)>,
1099}
1100
1101impl std::fmt::Debug for ClickableNode {
1102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1103 f.debug_struct("ClickableNode").finish()
1104 }
1105}
1106
1107impl ClickableNode {
1108 pub fn new(on_click: impl Fn(Point) + 'static) -> Self {
1109 Self::with_handler(Rc::new(on_click))
1110 }
1111
1112 pub fn with_handler(on_click: Rc<dyn Fn(Point)>) -> Self {
1113 Self::with_handlers(None, on_click)
1114 }
1115
1116 pub fn with_handlers(on_press: Option<Rc<dyn Fn(Point)>>, on_click: Rc<dyn Fn(Point)>) -> Self {
1117 let press_position = Rc::new(RefCell::new(None));
1118 let cached_handler =
1119 Self::create_handler(on_press.clone(), on_click.clone(), press_position.clone());
1120 Self {
1121 on_press,
1122 on_click,
1123 state: NodeState::new(),
1124 press_position,
1125 cached_handler,
1126 }
1127 }
1128
1129 fn create_handler(
1130 on_press: Option<Rc<dyn Fn(Point)>>,
1131 on_click: Rc<dyn Fn(Point)>,
1132 press_position: Rc<RefCell<Option<Point>>>,
1133 ) -> Rc<dyn Fn(PointerEvent)> {
1134 Rc::new(move |event: PointerEvent| {
1135 if event.id != 0 {
1136 return;
1137 }
1138
1139 if event.is_consumed() {
1140 *press_position.borrow_mut() = None;
1141 return;
1142 }
1143
1144 match event.kind {
1145 PointerEventKind::Down => {
1146 *press_position.borrow_mut() = Some(event.travelled_to());
1147 if let Some(on_press) = on_press.as_ref() {
1148 on_press(event.position);
1149 }
1150 }
1151 PointerEventKind::Move => {}
1152 PointerEventKind::Up => {
1153 let press_pos_value = *press_position.borrow();
1154
1155 let should_click = if let Some(press_pos) = press_pos_value {
1156 let travelled_to = event.travelled_to();
1157 let dx = travelled_to.x - press_pos.x;
1158 let dy = travelled_to.y - press_pos.y;
1159 let distance = (dx * dx + dy * dy).sqrt();
1160 distance <= DRAG_THRESHOLD
1161 } else {
1162 true
1163 };
1164
1165 *press_position.borrow_mut() = None;
1166
1167 if should_click {
1168 on_click(Point {
1169 x: event.position.x,
1170 y: event.position.y,
1171 });
1172 event.consume();
1173 }
1174 }
1175 PointerEventKind::Cancel => {
1176 *press_position.borrow_mut() = None;
1177 }
1178 PointerEventKind::Scroll
1179 | PointerEventKind::Zoom
1180 | PointerEventKind::RotaryScrollPre
1181 | PointerEventKind::RotaryScroll
1182 | PointerEventKind::Enter
1183 | PointerEventKind::Exit => {}
1184 }
1185 })
1186 }
1187
1188 pub fn handler(&self) -> Rc<dyn Fn(Point)> {
1189 self.on_click.clone()
1190 }
1191}
1192
1193impl DelegatableNode for ClickableNode {
1194 fn node_state(&self) -> &NodeState {
1195 &self.state
1196 }
1197}
1198
1199impl ModifierNode for ClickableNode {
1200 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1201 context.invalidate(cranpose_foundation::InvalidationKind::PointerInput);
1202 }
1203
1204 fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
1205 Some(self)
1206 }
1207
1208 fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
1209 Some(self)
1210 }
1211}
1212
1213impl PointerInputNode for ClickableNode {
1214 fn on_pointer_event(
1215 &mut self,
1216 _context: &mut dyn ModifierNodeContext,
1217 event: &PointerEvent,
1218 ) -> bool {
1219 (self.cached_handler)(event.clone());
1220 event.is_consumed()
1221 }
1222
1223 fn hit_test(&self, _x: f32, _y: f32) -> bool {
1224 true
1225 }
1226
1227 fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
1228 Some(self.cached_handler.clone())
1229 }
1230}
1231
1232#[derive(Clone)]
1234pub struct ClickableElement {
1235 on_press: Option<Rc<dyn Fn(Point)>>,
1236 on_click: Rc<dyn Fn(Point)>,
1237}
1238
1239impl ClickableElement {
1240 pub fn new(on_click: impl Fn(Point) + 'static) -> Self {
1241 Self {
1242 on_press: None,
1243 on_click: Rc::new(on_click),
1244 }
1245 }
1246
1247 pub fn with_handler(on_click: Rc<dyn Fn(Point)>) -> Self {
1248 Self {
1249 on_press: None,
1250 on_click,
1251 }
1252 }
1253
1254 pub fn with_handlers(on_press: Rc<dyn Fn(Point)>, on_click: Rc<dyn Fn(Point)>) -> Self {
1255 Self {
1256 on_press: Some(on_press),
1257 on_click,
1258 }
1259 }
1260}
1261
1262impl std::fmt::Debug for ClickableElement {
1263 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1264 f.debug_struct("ClickableElement").finish()
1265 }
1266}
1267
1268impl PartialEq for ClickableElement {
1269 fn eq(&self, _other: &Self) -> bool {
1270 true
1271 }
1272}
1273
1274impl Eq for ClickableElement {}
1275
1276impl Hash for ClickableElement {
1277 fn hash<H: Hasher>(&self, state: &mut H) {
1278 "clickable".hash(state);
1279 }
1280}
1281
1282impl ModifierNodeElement for ClickableElement {
1283 type Node = ClickableNode;
1284
1285 fn create(&self) -> Self::Node {
1286 ClickableNode::with_handlers(self.on_press.clone(), self.on_click.clone())
1287 }
1288
1289 fn update(&self, node: &mut Self::Node) {
1290 node.on_press.clone_from(&self.on_press);
1291 node.on_click.clone_from(&self.on_click);
1292 node.cached_handler = ClickableNode::create_handler(
1293 node.on_press.clone(),
1294 node.on_click.clone(),
1295 node.press_position.clone(),
1296 );
1297 }
1298
1299 fn capabilities(&self) -> NodeCapabilities {
1300 NodeCapabilities::POINTER_INPUT
1301 }
1302
1303 fn always_update(&self) -> bool {
1304 true
1305 }
1306}
1307
1308#[derive(Debug)]
1314pub struct PointerIconNode {
1315 icon: PointerIcon,
1316 state: NodeState,
1317}
1318
1319impl PointerIconNode {
1320 pub fn new(icon: PointerIcon) -> Self {
1322 Self {
1323 icon,
1324 state: NodeState::new(),
1325 }
1326 }
1327
1328 pub fn icon(&self) -> &PointerIcon {
1330 &self.icon
1331 }
1332}
1333
1334impl DelegatableNode for PointerIconNode {
1335 fn node_state(&self) -> &NodeState {
1336 &self.state
1337 }
1338}
1339
1340impl ModifierNode for PointerIconNode {
1341 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1342 context.invalidate(cranpose_foundation::InvalidationKind::PointerInput);
1343 }
1344
1345 fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
1346 Some(self)
1347 }
1348
1349 fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
1350 Some(self)
1351 }
1352}
1353
1354impl PointerInputNode for PointerIconNode {
1355 fn on_pointer_event(
1356 &mut self,
1357 _context: &mut dyn ModifierNodeContext,
1358 _event: &PointerEvent,
1359 ) -> bool {
1360 false
1361 }
1362
1363 fn hit_test(&self, _x: f32, _y: f32) -> bool {
1364 true
1365 }
1366
1367 fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
1368 None
1369 }
1370}
1371
1372#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1374pub struct PointerIconElement {
1375 icon: PointerIcon,
1376}
1377
1378impl PointerIconElement {
1379 pub fn new(icon: PointerIcon) -> Self {
1381 Self { icon }
1382 }
1383}
1384
1385impl ModifierNodeElement for PointerIconElement {
1386 type Node = PointerIconNode;
1387
1388 fn create(&self) -> Self::Node {
1389 PointerIconNode::new(self.icon.clone())
1390 }
1391
1392 fn update(&self, node: &mut Self::Node) {
1393 node.icon = self.icon.clone();
1394 }
1395
1396 fn capabilities(&self) -> NodeCapabilities {
1397 NodeCapabilities::POINTER_INPUT
1398 }
1399}
1400
1401#[derive(Debug)]
1403pub struct AlphaNode {
1404 alpha: f32,
1405 state: NodeState,
1406}
1407
1408impl AlphaNode {
1409 pub fn new(alpha: f32) -> Self {
1410 Self {
1411 alpha: alpha.clamp(0.0, 1.0),
1412 state: NodeState::new(),
1413 }
1414 }
1415}
1416
1417impl DelegatableNode for AlphaNode {
1418 fn node_state(&self) -> &NodeState {
1419 &self.state
1420 }
1421}
1422
1423impl_draw_modifier_node!(AlphaNode);
1424
1425#[derive(Debug, Clone, PartialEq)]
1427pub struct AlphaElement {
1428 alpha: f32,
1429}
1430
1431impl AlphaElement {
1432 pub fn new(alpha: f32) -> Self {
1433 Self {
1434 alpha: alpha.clamp(0.0, 1.0),
1435 }
1436 }
1437}
1438
1439impl Hash for AlphaElement {
1440 fn hash<H: Hasher>(&self, state: &mut H) {
1441 hash_f32_value(state, self.alpha);
1442 }
1443}
1444
1445impl ModifierNodeElement for AlphaElement {
1446 type Node = AlphaNode;
1447
1448 fn create(&self) -> Self::Node {
1449 AlphaNode::new(self.alpha)
1450 }
1451
1452 fn update(&self, node: &mut Self::Node) {
1453 let new_alpha = self.alpha.clamp(0.0, 1.0);
1454 if (node.alpha - new_alpha).abs() > f32::EPSILON {
1455 node.alpha = new_alpha;
1456 }
1457 }
1458
1459 fn capabilities(&self) -> NodeCapabilities {
1460 NodeCapabilities::DRAW
1461 }
1462}
1463
1464#[derive(Debug)]
1465pub struct ClipToBoundsNode {
1466 state: NodeState,
1467}
1468
1469impl ClipToBoundsNode {
1470 pub fn new() -> Self {
1471 Self {
1472 state: NodeState::new(),
1473 }
1474 }
1475}
1476
1477impl DelegatableNode for ClipToBoundsNode {
1478 fn node_state(&self) -> &NodeState {
1479 &self.state
1480 }
1481}
1482
1483impl_draw_modifier_node!(ClipToBoundsNode);
1484
1485#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1486pub struct ClipToBoundsElement;
1487
1488impl ClipToBoundsElement {
1489 pub fn new() -> Self {
1490 Self
1491 }
1492}
1493
1494impl ModifierNodeElement for ClipToBoundsElement {
1495 type Node = ClipToBoundsNode;
1496
1497 fn create(&self) -> Self::Node {
1498 ClipToBoundsNode::new()
1499 }
1500
1501 fn update(&self, _node: &mut Self::Node) {}
1502
1503 fn capabilities(&self) -> NodeCapabilities {
1504 NodeCapabilities::DRAW
1505 }
1506}
1507
1508pub trait WindowRectSink {
1509 fn set(&self, rect: cranpose_ui_graphics::Rect);
1510}
1511
1512impl WindowRectSink for Cell<cranpose_ui_graphics::Rect> {
1513 fn set(&self, rect: cranpose_ui_graphics::Rect) {
1514 Cell::set(self, rect);
1515 }
1516}
1517
1518struct StateWindowRectSink(cranpose_core::MutableState<cranpose_ui_graphics::Rect>);
1519
1520impl WindowRectSink for StateWindowRectSink {
1521 fn set(&self, rect: cranpose_ui_graphics::Rect) {
1522 self.0.set(rect);
1523 }
1524}
1525
1526pub struct WindowRectReporterNode {
1527 sink: Rc<dyn WindowRectSink>,
1528 state: NodeState,
1529}
1530
1531impl WindowRectReporterNode {
1532 pub(crate) fn new(sink: Rc<dyn WindowRectSink>) -> Self {
1533 Self {
1534 sink,
1535 state: NodeState::new(),
1536 }
1537 }
1538
1539 pub(crate) fn window_rect_sink(&self) -> Rc<dyn WindowRectSink> {
1540 self.sink.clone()
1541 }
1542}
1543
1544impl DelegatableNode for WindowRectReporterNode {
1545 fn node_state(&self) -> &NodeState {
1546 &self.state
1547 }
1548}
1549
1550impl_layout_modifier_node!(WindowRectReporterNode);
1551
1552impl LayoutModifierNode for WindowRectReporterNode {
1553 fn measure(
1554 &self,
1555 _context: &mut dyn ModifierNodeContext,
1556 measurable: &dyn Measurable,
1557 constraints: Constraints,
1558 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1559 measure_pass_through(measurable, constraints, |_| (0.0, 0.0))
1560 }
1561}
1562
1563#[derive(Clone)]
1564pub struct WindowRectReporterElement {
1565 sink: Rc<dyn WindowRectSink>,
1566}
1567
1568impl WindowRectReporterElement {
1569 pub fn new(sink: Rc<Cell<cranpose_ui_graphics::Rect>>) -> Self {
1570 Self { sink }
1571 }
1572
1573 pub fn from_state(sink: cranpose_core::MutableState<cranpose_ui_graphics::Rect>) -> Self {
1574 Self {
1575 sink: Rc::new(StateWindowRectSink(sink)),
1576 }
1577 }
1578}
1579
1580impl std::fmt::Debug for WindowRectReporterElement {
1581 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1582 f.debug_struct("WindowRectReporterElement").finish()
1583 }
1584}
1585
1586impl PartialEq for WindowRectReporterElement {
1587 fn eq(&self, other: &Self) -> bool {
1588 Rc::ptr_eq(&self.sink, &other.sink)
1589 }
1590}
1591
1592impl Eq for WindowRectReporterElement {}
1593
1594impl Hash for WindowRectReporterElement {
1595 fn hash<H: Hasher>(&self, state: &mut H) {
1596 std::ptr::hash(Rc::as_ptr(&self.sink).cast::<()>(), state);
1597 }
1598}
1599
1600impl_sink_reporter_element!(WindowRectReporterElement, WindowRectReporterNode);
1601
1602pub trait SizeSink {
1603 fn set(&self, size: Size);
1604}
1605
1606impl SizeSink for Cell<Size> {
1607 fn set(&self, size: Size) {
1608 Cell::set(self, size);
1609 }
1610}
1611
1612struct StateSizeSink(cranpose_core::MutableState<Size>);
1613
1614impl SizeSink for StateSizeSink {
1615 fn set(&self, size: Size) {
1616 self.0.set(size);
1617 }
1618}
1619
1620pub struct SizeReporterNode {
1621 sink: Rc<dyn SizeSink>,
1622 state: NodeState,
1623 #[cfg(debug_assertions)]
1624 oscillation: Cell<(Size, Size, u32)>,
1625}
1626
1627impl SizeReporterNode {
1628 pub fn new(sink: Rc<dyn SizeSink>) -> Self {
1629 Self {
1630 sink,
1631 state: NodeState::new(),
1632 #[cfg(debug_assertions)]
1633 oscillation: Cell::new((Size::default(), Size::default(), 0)),
1634 }
1635 }
1636
1637 #[cfg(debug_assertions)]
1638 fn check_oscillation(&self, size: Size) {
1639 const ALTERNATION_CEILING: u32 = 64;
1640 let (last, second_last, count) = self.oscillation.get();
1641 let count = if size == second_last && size != last {
1642 count + 1
1643 } else if size == last {
1644 count
1645 } else {
1646 0
1647 };
1648 assert!(
1649 count <= ALTERNATION_CEILING,
1650 "size-reactive feedback loop: this node's measured size has \
1651 alternated between {last:?} and {size:?} for {count} passes — \
1652 its content's size depends on the size it reports (the \
1653 onSizeChanged self-reference hazard). Break the cycle by making \
1654 the reported size feed only content that does not change this \
1655 node's own measured size."
1656 );
1657 self.oscillation.set((size, last, count));
1658 }
1659}
1660
1661impl DelegatableNode for SizeReporterNode {
1662 fn node_state(&self) -> &NodeState {
1663 &self.state
1664 }
1665}
1666
1667impl_layout_modifier_node!(SizeReporterNode);
1668
1669impl LayoutModifierNode for SizeReporterNode {
1670 fn measure(
1671 &self,
1672 _context: &mut dyn ModifierNodeContext,
1673 measurable: &dyn Measurable,
1674 constraints: Constraints,
1675 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1676 measure_pass_through(measurable, constraints, |size| {
1677 #[cfg(debug_assertions)]
1678 self.check_oscillation(size);
1679 self.sink.set(size);
1680 (0.0, 0.0)
1681 })
1682 }
1683}
1684
1685#[derive(Clone)]
1686pub struct SizeReporterElement {
1687 sink: Rc<dyn SizeSink>,
1688}
1689
1690impl SizeReporterElement {
1691 pub fn new(sink: Rc<Cell<Size>>) -> Self {
1692 Self { sink }
1693 }
1694
1695 pub fn from_state(sink: cranpose_core::MutableState<Size>) -> Self {
1696 Self {
1697 sink: Rc::new(StateSizeSink(sink)),
1698 }
1699 }
1700}
1701
1702impl std::fmt::Debug for SizeReporterElement {
1703 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1704 f.debug_struct("SizeReporterElement").finish()
1705 }
1706}
1707
1708impl PartialEq for SizeReporterElement {
1709 fn eq(&self, other: &Self) -> bool {
1710 std::ptr::addr_eq(Rc::as_ptr(&self.sink), Rc::as_ptr(&other.sink))
1711 }
1712}
1713
1714impl Hash for SizeReporterElement {
1715 fn hash<H: Hasher>(&self, state: &mut H) {
1716 (Rc::as_ptr(&self.sink) as *const () as usize).hash(state);
1717 }
1718}
1719
1720impl_sink_reporter_element!(SizeReporterElement, SizeReporterNode);
1721
1722pub struct DrawCommandNode {
1723 commands: Vec<DrawCommand>,
1724 node_id: Cell<Option<NodeId>>,
1725 state: NodeState,
1726}
1727
1728impl DrawCommandNode {
1729 pub fn new(commands: Vec<DrawCommand>) -> Self {
1730 Self {
1731 commands,
1732 node_id: Cell::new(None),
1733 state: NodeState::new(),
1734 }
1735 }
1736
1737 #[cfg(test)]
1738 pub fn commands(&self) -> &[DrawCommand] {
1739 &self.commands
1740 }
1741
1742 pub(crate) fn observed_commands(&self) -> Vec<DrawCommand> {
1743 let node_id = self.node_id.get();
1744 self.commands
1745 .iter()
1746 .cloned()
1747 .enumerate()
1748 .map(|(index, command)| observe_draw_command(command, node_id, index))
1749 .collect()
1750 }
1751}
1752
1753impl DelegatableNode for DrawCommandNode {
1754 fn node_state(&self) -> &NodeState {
1755 &self.state
1756 }
1757}
1758
1759impl ModifierNode for DrawCommandNode {
1760 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1761 attach_draw_observer(&self.node_id, context);
1762 }
1763
1764 fn on_detach(&mut self) {
1765 detach_draw_observer(&self.node_id);
1766 }
1767
1768 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
1769 Some(self)
1770 }
1771
1772 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
1773 Some(self)
1774 }
1775}
1776
1777impl DrawModifierNode for DrawCommandNode {
1778 fn draw(&self, _draw_scope: &mut dyn DrawScope) {}
1779}
1780
1781fn observe_draw_command(
1782 command: DrawCommand,
1783 node_id: Option<NodeId>,
1784 command_index: usize,
1785) -> DrawCommand {
1786 let Some(node_id) = node_id else {
1787 return command;
1788 };
1789 let observation = crate::render_state::DrawObservationScope::new(node_id, command_index);
1790 match command {
1791 DrawCommand::Behind(draw) => DrawCommand::Behind(Rc::new(move |scope| {
1792 crate::render_state::observe_draw_reads(observation, || draw(scope));
1793 })),
1794 DrawCommand::WithContent(draw) => DrawCommand::WithContent(Rc::new(move |scope| {
1795 crate::render_state::observe_draw_reads(observation, || draw(scope));
1796 })),
1797 DrawCommand::Overlay(draw) => DrawCommand::Overlay(Rc::new(move |scope| {
1798 crate::render_state::observe_draw_reads(observation, || draw(scope));
1799 })),
1800 }
1801}
1802
1803fn draw_command_tag(cmd: &DrawCommand) -> u8 {
1804 match cmd {
1805 DrawCommand::Behind(_) => 0,
1806 DrawCommand::WithContent(_) => 1,
1807 DrawCommand::Overlay(_) => 2,
1808 }
1809}
1810
1811fn draw_command_closure_identity(cmd: &DrawCommand) -> *const () {
1812 match cmd {
1813 DrawCommand::Behind(f) | DrawCommand::WithContent(f) | DrawCommand::Overlay(f) => {
1814 Rc::as_ptr(f) as *const ()
1815 }
1816 }
1817}
1818
1819#[derive(Clone)]
1820pub struct DrawCommandElement {
1821 commands: Vec<DrawCommand>,
1822}
1823
1824impl DrawCommandElement {
1825 pub fn new(command: DrawCommand) -> Self {
1826 Self {
1827 commands: vec![command],
1828 }
1829 }
1830
1831 pub fn from_commands(commands: Vec<DrawCommand>) -> Self {
1832 Self { commands }
1833 }
1834}
1835
1836impl std::fmt::Debug for DrawCommandElement {
1837 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1838 f.debug_struct("DrawCommandElement")
1839 .field("commands", &self.commands.len())
1840 .finish()
1841 }
1842}
1843
1844impl PartialEq for DrawCommandElement {
1845 fn eq(&self, other: &Self) -> bool {
1846 if self.commands.len() != other.commands.len() {
1847 return false;
1848 }
1849 self.commands
1850 .iter()
1851 .zip(other.commands.iter())
1852 .all(|(a, b)| {
1853 draw_command_tag(a) == draw_command_tag(b)
1854 && draw_command_closure_identity(a) == draw_command_closure_identity(b)
1855 })
1856 }
1857}
1858
1859impl Eq for DrawCommandElement {}
1860
1861impl std::hash::Hash for DrawCommandElement {
1862 fn hash<H: Hasher>(&self, state: &mut H) {
1863 "draw_commands".hash(state);
1864 self.commands.len().hash(state);
1865 for command in &self.commands {
1866 draw_command_tag(command).hash(state);
1867 (draw_command_closure_identity(command) as usize).hash(state);
1868 }
1869 }
1870}
1871
1872impl ModifierNodeElement for DrawCommandElement {
1873 type Node = DrawCommandNode;
1874
1875 fn create(&self) -> Self::Node {
1876 DrawCommandNode::new(self.commands.clone())
1877 }
1878
1879 fn update(&self, node: &mut Self::Node) {
1880 node.commands.clone_from(&self.commands);
1881 }
1882
1883 fn capabilities(&self) -> NodeCapabilities {
1884 NodeCapabilities::DRAW
1885 }
1886}
1887
1888#[derive(Debug)]
1892pub struct OffsetNode {
1893 x: f32,
1894 y: f32,
1895 rtl_aware: bool,
1896 state: NodeState,
1897}
1898
1899impl OffsetNode {
1900 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
1901 Self {
1902 x,
1903 y,
1904 rtl_aware,
1905 state: NodeState::new(),
1906 }
1907 }
1908
1909 pub fn offset(&self) -> Point {
1910 Point {
1911 x: self.x,
1912 y: self.y,
1913 }
1914 }
1915
1916 pub fn rtl_aware(&self) -> bool {
1917 self.rtl_aware
1918 }
1919}
1920
1921impl DelegatableNode for OffsetNode {
1922 fn node_state(&self) -> &NodeState {
1923 &self.state
1924 }
1925}
1926
1927impl_layout_modifier_node!(OffsetNode, invalidate = InvalidationKind::Layout);
1928
1929impl LayoutModifierNode for OffsetNode {
1930 fn measure(
1931 &self,
1932 _context: &mut dyn ModifierNodeContext,
1933 measurable: &dyn Measurable,
1934 constraints: Constraints,
1935 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1936 measure_pass_through(measurable, constraints, |_| (self.x, self.y))
1937 }
1938
1939 forward_intrinsics_to_child!();
1940}
1941
1942#[derive(Debug, Clone, PartialEq)]
1946pub struct OffsetElement {
1947 x: f32,
1948 y: f32,
1949 rtl_aware: bool,
1950}
1951
1952impl OffsetElement {
1953 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
1954 Self { x, y, rtl_aware }
1955 }
1956}
1957
1958impl Hash for OffsetElement {
1959 fn hash<H: Hasher>(&self, state: &mut H) {
1960 hash_f32_value(state, self.x);
1961 hash_f32_value(state, self.y);
1962 self.rtl_aware.hash(state);
1963 }
1964}
1965
1966impl ModifierNodeElement for OffsetElement {
1967 type Node = OffsetNode;
1968
1969 fn create(&self) -> Self::Node {
1970 OffsetNode::new(self.x, self.y, self.rtl_aware)
1971 }
1972
1973 fn update(&self, node: &mut Self::Node) {
1974 if node.x != self.x || node.y != self.y || node.rtl_aware != self.rtl_aware {
1975 node.x = self.x;
1976 node.y = self.y;
1977 node.rtl_aware = self.rtl_aware;
1978 }
1979 }
1980
1981 fn capabilities(&self) -> NodeCapabilities {
1982 NodeCapabilities::LAYOUT
1983 }
1984
1985 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
1986 Some(InvalidationKind::Layout)
1987 }
1988}
1989
1990#[derive(Debug)]
1998pub struct FractionalOffsetNode {
1999 x_fraction: f32,
2000 y_fraction: f32,
2001 state: NodeState,
2002}
2003
2004impl FractionalOffsetNode {
2005 pub fn new(x_fraction: f32, y_fraction: f32) -> Self {
2006 Self {
2007 x_fraction,
2008 y_fraction,
2009 state: NodeState::new(),
2010 }
2011 }
2012
2013 pub fn fractions(&self) -> Point {
2014 Point {
2015 x: self.x_fraction,
2016 y: self.y_fraction,
2017 }
2018 }
2019}
2020
2021impl DelegatableNode for FractionalOffsetNode {
2022 fn node_state(&self) -> &NodeState {
2023 &self.state
2024 }
2025}
2026
2027impl_layout_modifier_node!(FractionalOffsetNode, invalidate = InvalidationKind::Layout);
2028
2029impl LayoutModifierNode for FractionalOffsetNode {
2030 fn measure(
2031 &self,
2032 _context: &mut dyn ModifierNodeContext,
2033 measurable: &dyn Measurable,
2034 constraints: Constraints,
2035 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2036 measure_pass_through(measurable, constraints, |size| {
2037 (self.x_fraction * size.width, self.y_fraction * size.height)
2038 })
2039 }
2040
2041 forward_intrinsics_to_child!();
2042}
2043
2044#[derive(Debug, Clone, PartialEq)]
2046pub struct FractionalOffsetElement {
2047 x_fraction: f32,
2048 y_fraction: f32,
2049}
2050
2051impl FractionalOffsetElement {
2052 pub fn new(x_fraction: f32, y_fraction: f32) -> Self {
2053 Self {
2054 x_fraction,
2055 y_fraction,
2056 }
2057 }
2058}
2059
2060impl Hash for FractionalOffsetElement {
2061 fn hash<H: Hasher>(&self, state: &mut H) {
2062 "fractional_offset".hash(state);
2063 hash_f32_value(state, self.x_fraction);
2064 hash_f32_value(state, self.y_fraction);
2065 }
2066}
2067
2068impl ModifierNodeElement for FractionalOffsetElement {
2069 type Node = FractionalOffsetNode;
2070
2071 fn create(&self) -> Self::Node {
2072 FractionalOffsetNode::new(self.x_fraction, self.y_fraction)
2073 }
2074
2075 fn update(&self, node: &mut Self::Node) {
2076 if node.x_fraction != self.x_fraction || node.y_fraction != self.y_fraction {
2077 node.x_fraction = self.x_fraction;
2078 node.y_fraction = self.y_fraction;
2079 }
2080 }
2081
2082 fn capabilities(&self) -> NodeCapabilities {
2083 NodeCapabilities::LAYOUT
2084 }
2085
2086 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
2087 Some(InvalidationKind::Layout)
2088 }
2089}
2090
2091#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2093pub enum FillDirection {
2094 Horizontal,
2095 Vertical,
2096 Both,
2097}
2098
2099#[derive(Debug)]
2103pub struct FillNode {
2104 direction: FillDirection,
2105 fraction: f32,
2106 state: NodeState,
2107}
2108
2109impl FillNode {
2110 pub fn new(direction: FillDirection, fraction: f32) -> Self {
2111 Self {
2112 direction,
2113 fraction,
2114 state: NodeState::new(),
2115 }
2116 }
2117
2118 pub fn direction(&self) -> FillDirection {
2119 self.direction
2120 }
2121
2122 pub fn fraction(&self) -> f32 {
2123 self.fraction
2124 }
2125}
2126
2127impl DelegatableNode for FillNode {
2128 fn node_state(&self) -> &NodeState {
2129 &self.state
2130 }
2131}
2132
2133impl_layout_modifier_node!(FillNode, invalidate = InvalidationKind::Layout);
2134
2135impl LayoutModifierNode for FillNode {
2136 fn measure(
2137 &self,
2138 _context: &mut dyn ModifierNodeContext,
2139 measurable: &dyn Measurable,
2140 constraints: Constraints,
2141 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2142 let (fill_width, child_min_width, child_max_width) = if self.direction
2143 != FillDirection::Vertical
2144 && constraints.max_width != f32::INFINITY
2145 {
2146 let width = (constraints.max_width * self.fraction)
2147 .round()
2148 .clamp(constraints.min_width, constraints.max_width);
2149 (width, width, width)
2150 } else {
2151 (
2152 constraints.max_width,
2153 constraints.min_width,
2154 constraints.max_width,
2155 )
2156 };
2157
2158 let (fill_height, child_min_height, child_max_height) = if self.direction
2159 != FillDirection::Horizontal
2160 && constraints.max_height != f32::INFINITY
2161 {
2162 let height = (constraints.max_height * self.fraction)
2163 .round()
2164 .clamp(constraints.min_height, constraints.max_height);
2165 (height, height, height)
2166 } else {
2167 (
2168 constraints.max_height,
2169 constraints.min_height,
2170 constraints.max_height,
2171 )
2172 };
2173
2174 let fill_constraints = Constraints {
2175 min_width: child_min_width,
2176 max_width: child_max_width,
2177 min_height: child_min_height,
2178 max_height: child_max_height,
2179 };
2180
2181 let placeable = measurable.measure(fill_constraints);
2182
2183 let result_width = if self.direction != FillDirection::Vertical
2184 && constraints.max_width != f32::INFINITY
2185 {
2186 fill_width
2187 } else {
2188 placeable.width()
2189 };
2190
2191 let result_height = if self.direction != FillDirection::Horizontal
2192 && constraints.max_height != f32::INFINITY
2193 {
2194 fill_height
2195 } else {
2196 placeable.height()
2197 };
2198
2199 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
2200 width: result_width,
2201 height: result_height,
2202 })
2203 }
2204
2205 forward_intrinsics_to_child!();
2206}
2207
2208#[derive(Debug, Clone, PartialEq)]
2212pub struct FillElement {
2213 direction: FillDirection,
2214 fraction: f32,
2215}
2216
2217impl FillElement {
2218 pub fn width(fraction: f32) -> Self {
2219 Self {
2220 direction: FillDirection::Horizontal,
2221 fraction,
2222 }
2223 }
2224
2225 pub fn height(fraction: f32) -> Self {
2226 Self {
2227 direction: FillDirection::Vertical,
2228 fraction,
2229 }
2230 }
2231
2232 pub fn size(fraction: f32) -> Self {
2233 Self {
2234 direction: FillDirection::Both,
2235 fraction,
2236 }
2237 }
2238}
2239
2240impl Hash for FillElement {
2241 fn hash<H: Hasher>(&self, state: &mut H) {
2242 self.direction.hash(state);
2243 hash_f32_value(state, self.fraction);
2244 }
2245}
2246
2247impl ModifierNodeElement for FillElement {
2248 type Node = FillNode;
2249
2250 fn create(&self) -> Self::Node {
2251 FillNode::new(self.direction, self.fraction)
2252 }
2253
2254 fn update(&self, node: &mut Self::Node) {
2255 if node.direction != self.direction || node.fraction != self.fraction {
2256 node.direction = self.direction;
2257 node.fraction = self.fraction;
2258 }
2259 }
2260
2261 fn capabilities(&self) -> NodeCapabilities {
2262 NodeCapabilities::LAYOUT
2263 }
2264}
2265
2266#[derive(Debug)]
2267pub struct WeightNode {
2268 weight: f32,
2269 fill: bool,
2270 state: NodeState,
2271}
2272
2273impl WeightNode {
2274 pub fn new(weight: f32, fill: bool) -> Self {
2275 Self {
2276 weight,
2277 fill,
2278 state: NodeState::new(),
2279 }
2280 }
2281
2282 pub fn layout_weight(&self) -> LayoutWeight {
2283 LayoutWeight {
2284 weight: self.weight,
2285 fill: self.fill,
2286 }
2287 }
2288}
2289
2290impl DelegatableNode for WeightNode {
2291 fn node_state(&self) -> &NodeState {
2292 &self.state
2293 }
2294}
2295
2296impl ModifierNode for WeightNode {
2297 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2298 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2299 }
2300}
2301
2302#[derive(Debug, Clone, PartialEq)]
2303pub struct WeightElement {
2304 weight: f32,
2305 fill: bool,
2306}
2307
2308impl WeightElement {
2309 pub fn new(weight: f32, fill: bool) -> Self {
2310 Self { weight, fill }
2311 }
2312}
2313
2314impl Hash for WeightElement {
2315 fn hash<H: Hasher>(&self, state: &mut H) {
2316 hash_f32_value(state, self.weight);
2317 self.fill.hash(state);
2318 }
2319}
2320
2321impl ModifierNodeElement for WeightElement {
2322 type Node = WeightNode;
2323
2324 fn create(&self) -> Self::Node {
2325 WeightNode::new(self.weight, self.fill)
2326 }
2327
2328 fn update(&self, node: &mut Self::Node) {
2329 if node.weight != self.weight || node.fill != self.fill {
2330 node.weight = self.weight;
2331 node.fill = self.fill;
2332 }
2333 }
2334
2335 fn capabilities(&self) -> NodeCapabilities {
2336 NodeCapabilities::LAYOUT
2337 }
2338}
2339
2340#[derive(Debug)]
2341pub struct AlignmentNode {
2342 box_alignment: Option<Alignment>,
2343 column_alignment: Option<HorizontalAlignment>,
2344 row_alignment: Option<VerticalAlignment>,
2345 state: NodeState,
2346}
2347
2348impl AlignmentNode {
2349 pub fn new(
2350 box_alignment: Option<Alignment>,
2351 column_alignment: Option<HorizontalAlignment>,
2352 row_alignment: Option<VerticalAlignment>,
2353 ) -> Self {
2354 Self {
2355 box_alignment,
2356 column_alignment,
2357 row_alignment,
2358 state: NodeState::new(),
2359 }
2360 }
2361
2362 pub fn box_alignment(&self) -> Option<Alignment> {
2363 self.box_alignment
2364 }
2365
2366 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
2367 self.column_alignment
2368 }
2369
2370 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
2371 self.row_alignment
2372 }
2373}
2374
2375impl DelegatableNode for AlignmentNode {
2376 fn node_state(&self) -> &NodeState {
2377 &self.state
2378 }
2379}
2380
2381impl ModifierNode for AlignmentNode {
2382 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2383 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2384 }
2385}
2386
2387#[derive(Debug, Clone, PartialEq)]
2388pub struct AlignmentElement {
2389 box_alignment: Option<Alignment>,
2390 column_alignment: Option<HorizontalAlignment>,
2391 row_alignment: Option<VerticalAlignment>,
2392}
2393
2394impl AlignmentElement {
2395 pub fn box_alignment(alignment: Alignment) -> Self {
2396 Self {
2397 box_alignment: Some(alignment),
2398 column_alignment: None,
2399 row_alignment: None,
2400 }
2401 }
2402
2403 pub fn column_alignment(alignment: HorizontalAlignment) -> Self {
2404 Self {
2405 box_alignment: None,
2406 column_alignment: Some(alignment),
2407 row_alignment: None,
2408 }
2409 }
2410
2411 pub fn row_alignment(alignment: VerticalAlignment) -> Self {
2412 Self {
2413 box_alignment: None,
2414 column_alignment: None,
2415 row_alignment: Some(alignment),
2416 }
2417 }
2418}
2419
2420impl Hash for AlignmentElement {
2421 fn hash<H: Hasher>(&self, state: &mut H) {
2422 if let Some(alignment) = self.box_alignment {
2423 state.write_u8(1);
2424 hash_alignment(state, alignment);
2425 } else {
2426 state.write_u8(0);
2427 }
2428 if let Some(alignment) = self.column_alignment {
2429 state.write_u8(1);
2430 hash_horizontal_alignment(state, alignment);
2431 } else {
2432 state.write_u8(0);
2433 }
2434 if let Some(alignment) = self.row_alignment {
2435 state.write_u8(1);
2436 hash_vertical_alignment(state, alignment);
2437 } else {
2438 state.write_u8(0);
2439 }
2440 }
2441}
2442
2443impl ModifierNodeElement for AlignmentElement {
2444 type Node = AlignmentNode;
2445
2446 fn create(&self) -> Self::Node {
2447 AlignmentNode::new(
2448 self.box_alignment,
2449 self.column_alignment,
2450 self.row_alignment,
2451 )
2452 }
2453
2454 fn update(&self, node: &mut Self::Node) {
2455 if node.box_alignment != self.box_alignment {
2456 node.box_alignment = self.box_alignment;
2457 }
2458 if node.column_alignment != self.column_alignment {
2459 node.column_alignment = self.column_alignment;
2460 }
2461 if node.row_alignment != self.row_alignment {
2462 node.row_alignment = self.row_alignment;
2463 }
2464 }
2465
2466 fn capabilities(&self) -> NodeCapabilities {
2467 NodeCapabilities::LAYOUT
2468 }
2469}
2470
2471#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2472pub enum IntrinsicAxis {
2473 Width,
2474 Height,
2475}
2476
2477#[derive(Debug)]
2478pub struct IntrinsicSizeNode {
2479 axis: IntrinsicAxis,
2480 size: IntrinsicSize,
2481 state: NodeState,
2482}
2483
2484impl IntrinsicSizeNode {
2485 pub fn new(axis: IntrinsicAxis, size: IntrinsicSize) -> Self {
2486 Self {
2487 axis,
2488 size,
2489 state: NodeState::new(),
2490 }
2491 }
2492
2493 pub fn axis(&self) -> IntrinsicAxis {
2494 self.axis
2495 }
2496
2497 pub fn intrinsic_size(&self) -> IntrinsicSize {
2498 self.size
2499 }
2500}
2501
2502impl DelegatableNode for IntrinsicSizeNode {
2503 fn node_state(&self) -> &NodeState {
2504 &self.state
2505 }
2506}
2507
2508impl ModifierNode for IntrinsicSizeNode {
2509 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2510 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2511 }
2512}
2513
2514#[derive(Debug, Clone, PartialEq)]
2515pub struct IntrinsicSizeElement {
2516 axis: IntrinsicAxis,
2517 size: IntrinsicSize,
2518}
2519
2520impl IntrinsicSizeElement {
2521 pub fn width(size: IntrinsicSize) -> Self {
2522 Self {
2523 axis: IntrinsicAxis::Width,
2524 size,
2525 }
2526 }
2527
2528 pub fn height(size: IntrinsicSize) -> Self {
2529 Self {
2530 axis: IntrinsicAxis::Height,
2531 size,
2532 }
2533 }
2534}
2535
2536impl Hash for IntrinsicSizeElement {
2537 fn hash<H: Hasher>(&self, state: &mut H) {
2538 state.write_u8(match self.axis {
2539 IntrinsicAxis::Width => 0,
2540 IntrinsicAxis::Height => 1,
2541 });
2542 state.write_u8(match self.size {
2543 IntrinsicSize::Min => 0,
2544 IntrinsicSize::Max => 1,
2545 });
2546 }
2547}
2548
2549impl ModifierNodeElement for IntrinsicSizeElement {
2550 type Node = IntrinsicSizeNode;
2551
2552 fn create(&self) -> Self::Node {
2553 IntrinsicSizeNode::new(self.axis, self.size)
2554 }
2555
2556 fn update(&self, node: &mut Self::Node) {
2557 if node.axis != self.axis {
2558 node.axis = self.axis;
2559 }
2560 if node.size != self.size {
2561 node.size = self.size;
2562 }
2563 }
2564
2565 fn capabilities(&self) -> NodeCapabilities {
2566 NodeCapabilities::LAYOUT
2567 }
2568}
2569
2570#[cfg(test)]
2571#[path = "tests/modifier_nodes_tests.rs"]
2572mod tests;