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