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