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