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