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