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::{DrawScope, PointerIcon};
14use cranpose_ui_layout::{Alignment, HorizontalAlignment, IntrinsicSize, VerticalAlignment};
15
16use crate::{
17 draw::DrawCommand,
18 modifier::{
19 BlendMode, Brush, 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
617#[derive(Debug, Clone, PartialEq)]
621pub struct BorderElement {
622 width: f32,
623 brush: Brush,
624 shape: RoundedCornerShape,
625}
626
627impl BorderElement {
628 pub fn new(width: f32, brush: Brush, shape: RoundedCornerShape) -> Self {
629 Self {
630 width,
631 brush,
632 shape,
633 }
634 }
635
636 pub(crate) fn command(&self) -> DrawCommand {
638 let Self {
639 width,
640 brush,
641 shape,
642 } = self.clone();
643 DrawCommand::Overlay(Rc::new(move |scope| {
644 let size = scope.size();
645 if width <= 0.0 || size.width <= 0.0 || size.height <= 0.0 {
646 return;
647 }
648 let radii = shape.resolve(size.width, size.height);
649 let half = width / 2.0;
650 if width * 2.0 >= size.width.min(size.height) {
651 scope.draw_round_rect(brush.clone(), radii);
653 return;
654 }
655 let inset = |radius: f32| (radius - half).max(0.0);
656 scope.draw_round_rect_at_stroked(
657 cranpose_ui_graphics::Rect {
658 x: half,
659 y: half,
660 width: size.width - width,
661 height: size.height - width,
662 },
663 brush.clone(),
664 cranpose_ui_graphics::CornerRadii {
665 top_left: inset(radii.top_left),
666 top_right: inset(radii.top_right),
667 bottom_right: inset(radii.bottom_right),
668 bottom_left: inset(radii.bottom_left),
669 },
670 cranpose_ui_graphics::Stroke::new(width),
671 );
672 }))
673 }
674}
675
676impl Hash for BorderElement {
677 fn hash<H: Hasher>(&self, state: &mut H) {
678 hash_f32_value(state, self.width);
679 std::mem::discriminant(&self.brush).hash(state);
680 if let Brush::Solid(color) = self.brush {
681 hash_f32_value(state, color.0);
682 hash_f32_value(state, color.1);
683 hash_f32_value(state, color.2);
684 hash_f32_value(state, color.3);
685 }
686 let radii = self.shape.radii();
687 hash_f32_value(state, radii.top_left);
688 hash_f32_value(state, radii.top_right);
689 hash_f32_value(state, radii.bottom_right);
690 hash_f32_value(state, radii.bottom_left);
691 }
692}
693
694impl ModifierNodeElement for BorderElement {
695 type Node = DrawCommandNode;
696
697 fn create(&self) -> Self::Node {
698 DrawCommandNode::new(vec![self.command()])
699 }
700
701 fn update(&self, node: &mut Self::Node) {
702 node.commands = vec![self.command()];
703 }
704
705 fn capabilities(&self) -> NodeCapabilities {
706 NodeCapabilities::DRAW
707 }
708}
709
710pub struct GraphicsLayerNode {
711 layer: GraphicsLayer,
712 layer_resolver: Option<Rc<dyn Fn() -> GraphicsLayer>>,
713 node_id: Rc<Cell<Option<NodeId>>>,
714 state: NodeState,
715}
716
717impl GraphicsLayerNode {
718 pub fn new(layer: GraphicsLayer) -> Self {
719 Self {
720 layer,
721 layer_resolver: None,
722 node_id: Rc::new(Cell::new(None)),
723 state: NodeState::new(),
724 }
725 }
726
727 pub fn new_lazy(layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) -> Self {
728 Self {
729 layer: GraphicsLayer::default(),
730 layer_resolver: Some(layer_resolver),
731 node_id: Rc::new(Cell::new(None)),
732 state: NodeState::new(),
733 }
734 }
735
736 #[cfg(test)]
737 pub fn layer(&self) -> GraphicsLayer {
738 if let Some(resolve) = self.layer_resolver(0) {
739 resolve()
740 } else {
741 self.layer.clone()
742 }
743 }
744
745 pub fn layer_snapshot(&self) -> GraphicsLayer {
746 self.layer.clone()
747 }
748
749 pub(crate) fn layer_resolver(
750 &self,
751 modifier_index: usize,
752 ) -> Option<Rc<dyn Fn() -> GraphicsLayer>> {
753 self.layer_resolver.as_ref().map(|resolve| {
754 let resolve = resolve.clone();
755 let node_id = Rc::clone(&self.node_id);
756 Rc::new(move || {
757 if let Some(node_id) = node_id.get() {
758 let scope =
759 crate::render_state::DrawObservationScope::new(node_id, modifier_index, 0);
760 crate::render_state::observe_draw_reads(scope, || resolve())
761 } else {
762 resolve()
763 }
764 }) as Rc<dyn Fn() -> GraphicsLayer>
765 })
766 }
767
768 fn set_static(&mut self, layer: GraphicsLayer) {
769 let changed = self.layer != layer || self.layer_resolver.is_some();
770 self.layer = layer;
771 self.layer_resolver = None;
772 if changed && let Some(node_id) = self.node_id.get() {
773 crate::render_state::schedule_draw_repass(node_id);
774 }
775 }
776
777 fn set_lazy(&mut self, layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) {
778 let changed = self
779 .layer_resolver
780 .as_ref()
781 .is_none_or(|current| !Rc::ptr_eq(current, &layer_resolver));
782 self.layer_resolver = Some(layer_resolver);
783 if changed && let Some(node_id) = self.node_id.get() {
784 crate::render_state::schedule_draw_repass(node_id);
785 }
786 }
787}
788
789impl DelegatableNode for GraphicsLayerNode {
790 fn node_state(&self) -> &NodeState {
791 &self.state
792 }
793}
794
795impl ModifierNode for GraphicsLayerNode {
796 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
797 attach_draw_observer(&self.node_id, context);
798 }
799
800 fn on_detach(&mut self) {
801 detach_draw_observer(&self.node_id);
802 }
803}
804
805impl std::fmt::Debug for GraphicsLayerNode {
806 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
807 f.debug_struct("GraphicsLayerNode")
808 .field("layer", &self.layer)
809 .field("lazy", &self.layer_resolver.is_some())
810 .finish()
811 }
812}
813
814#[derive(Debug, Clone, PartialEq)]
815pub struct GraphicsLayerElement {
816 layer: GraphicsLayer,
817}
818
819impl GraphicsLayerElement {
820 pub fn new(layer: GraphicsLayer) -> Self {
821 Self { layer }
822 }
823}
824
825impl Hash for GraphicsLayerElement {
826 fn hash<H: Hasher>(&self, state: &mut H) {
827 hash_graphics_layer(state, &self.layer);
828 }
829}
830
831impl ModifierNodeElement for GraphicsLayerElement {
832 type Node = GraphicsLayerNode;
833
834 fn create(&self) -> Self::Node {
835 GraphicsLayerNode::new(self.layer.clone())
836 }
837
838 fn update(&self, node: &mut Self::Node) {
839 node.set_static(self.layer.clone());
840 }
841
842 fn capabilities(&self) -> NodeCapabilities {
843 NodeCapabilities::DRAW
844 }
845}
846
847#[derive(Clone)]
848pub struct LazyGraphicsLayerElement {
849 layer_resolver: Rc<dyn Fn() -> GraphicsLayer>,
850}
851
852impl LazyGraphicsLayerElement {
853 pub fn new(layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) -> Self {
854 Self { layer_resolver }
855 }
856}
857
858impl std::fmt::Debug for LazyGraphicsLayerElement {
859 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
860 f.debug_struct("LazyGraphicsLayerElement")
861 .field("resolver", &"<closure>")
862 .finish()
863 }
864}
865
866impl PartialEq for LazyGraphicsLayerElement {
867 fn eq(&self, other: &Self) -> bool {
868 Rc::ptr_eq(&self.layer_resolver, &other.layer_resolver)
869 }
870}
871
872impl Eq for LazyGraphicsLayerElement {}
873
874impl Hash for LazyGraphicsLayerElement {
875 fn hash<H: Hasher>(&self, state: &mut H) {
876 let ptr = Rc::as_ptr(&self.layer_resolver) as *const ();
877 ptr.hash(state);
878 }
879}
880
881impl ModifierNodeElement for LazyGraphicsLayerElement {
882 type Node = GraphicsLayerNode;
883
884 fn create(&self) -> Self::Node {
885 GraphicsLayerNode::new_lazy(self.layer_resolver.clone())
886 }
887
888 fn update(&self, node: &mut Self::Node) {
889 node.set_lazy(self.layer_resolver.clone());
890 }
891
892 fn capabilities(&self) -> NodeCapabilities {
893 NodeCapabilities::DRAW
894 }
895
896 fn always_update(&self) -> bool {
897 true
898 }
899
900 fn auto_invalidate_on_update(&self) -> bool {
901 false
902 }
903}
904
905#[derive(Debug)]
909pub struct SizeNode {
910 min_width: Option<f32>,
911 max_width: Option<f32>,
912 min_height: Option<f32>,
913 max_height: Option<f32>,
914 enforce_incoming: bool,
915 state: NodeState,
916}
917
918impl SizeNode {
919 pub fn new(
920 min_width: Option<f32>,
921 max_width: Option<f32>,
922 min_height: Option<f32>,
923 max_height: Option<f32>,
924 enforce_incoming: bool,
925 ) -> Self {
926 Self {
927 min_width,
928 max_width,
929 min_height,
930 max_height,
931 enforce_incoming,
932 state: NodeState::new(),
933 }
934 }
935
936 fn target_constraints(&self, density: f32) -> Constraints {
939 use cranpose_ui_layout::round_to_px;
940
941 let max_width = self
942 .max_width
943 .map_or(f32::INFINITY, |v| round_to_px(v, density).max(0.0));
944 let max_height = self
945 .max_height
946 .map_or(f32::INFINITY, |v| round_to_px(v, density).max(0.0));
947
948 let min_width = self.min_width.map_or(0.0, |v| {
949 let clamped = round_to_px(v, density).clamp(0.0, max_width);
950 if clamped == f32::INFINITY {
951 0.0
952 } else {
953 clamped
954 }
955 });
956
957 let min_height = self.min_height.map_or(0.0, |v| {
958 let clamped = round_to_px(v, density).clamp(0.0, max_height);
959 if clamped == f32::INFINITY {
960 0.0
961 } else {
962 clamped
963 }
964 });
965
966 Constraints {
967 min_width,
968 max_width,
969 min_height,
970 max_height,
971 }
972 }
973
974 pub fn min_width(&self) -> Option<f32> {
975 self.min_width
976 }
977
978 pub fn max_width(&self) -> Option<f32> {
979 self.max_width
980 }
981
982 pub fn min_height(&self) -> Option<f32> {
983 self.min_height
984 }
985
986 pub fn max_height(&self) -> Option<f32> {
987 self.max_height
988 }
989
990 pub fn enforce_incoming(&self) -> bool {
991 self.enforce_incoming
992 }
993}
994
995impl DelegatableNode for SizeNode {
996 fn node_state(&self) -> &NodeState {
997 &self.state
998 }
999}
1000
1001impl_layout_modifier_node!(SizeNode, invalidate = InvalidationKind::Layout);
1002
1003impl LayoutModifierNode for SizeNode {
1004 fn measure(
1005 &self,
1006 context: &mut dyn ModifierNodeContext,
1007 measurable: &dyn Measurable,
1008 constraints: Constraints,
1009 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1010 let target = self.target_constraints(context.density());
1011
1012 let wrapped_constraints = if self.enforce_incoming {
1013 Constraints {
1014 min_width: target
1015 .min_width
1016 .max(constraints.min_width)
1017 .min(constraints.max_width),
1018 max_width: target
1019 .max_width
1020 .min(constraints.max_width)
1021 .max(constraints.min_width),
1022 min_height: target
1023 .min_height
1024 .max(constraints.min_height)
1025 .min(constraints.max_height),
1026 max_height: target
1027 .max_height
1028 .min(constraints.max_height)
1029 .max(constraints.min_height),
1030 }
1031 } else {
1032 let resolved_min_width = if self.min_width.is_some() {
1033 target.min_width
1034 } else {
1035 constraints.min_width.min(target.max_width)
1036 };
1037 let resolved_max_width = if self.max_width.is_some() {
1038 target.max_width
1039 } else {
1040 constraints.max_width.max(target.min_width)
1041 };
1042 let resolved_min_height = if self.min_height.is_some() {
1043 target.min_height
1044 } else {
1045 constraints.min_height.min(target.max_height)
1046 };
1047 let resolved_max_height = if self.max_height.is_some() {
1048 target.max_height
1049 } else {
1050 constraints.max_height.max(target.min_height)
1051 };
1052
1053 Constraints {
1054 min_width: resolved_min_width,
1055 max_width: resolved_max_width,
1056 min_height: resolved_min_height,
1057 max_height: resolved_max_height,
1058 }
1059 };
1060
1061 let placeable = measurable.measure(wrapped_constraints);
1062 let measured_width = placeable.width();
1063 let measured_height = placeable.height();
1064
1065 let result_width = if self.min_width.is_some()
1066 && self.max_width.is_some()
1067 && self.min_width == self.max_width
1068 && target.min_width >= wrapped_constraints.min_width
1069 && target.min_width <= wrapped_constraints.max_width
1070 {
1071 target.min_width
1072 } else {
1073 measured_width
1074 };
1075
1076 let result_height = if self.min_height.is_some()
1077 && self.max_height.is_some()
1078 && self.min_height == self.max_height
1079 && target.min_height >= wrapped_constraints.min_height
1080 && target.min_height <= wrapped_constraints.max_height
1081 {
1082 target.min_height
1083 } else {
1084 measured_height
1085 };
1086
1087 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
1088 width: result_width,
1089 height: result_height,
1090 })
1091 }
1092
1093 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, density: f32) -> f32 {
1094 size_intrinsic(
1095 self.target_constraints(density),
1096 SizeAxis::Width,
1097 self.enforce_incoming,
1098 height,
1099 |h| measurable.min_intrinsic_width(h),
1100 )
1101 }
1102
1103 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, density: f32) -> f32 {
1104 size_intrinsic(
1105 self.target_constraints(density),
1106 SizeAxis::Width,
1107 self.enforce_incoming,
1108 height,
1109 |h| measurable.max_intrinsic_width(h),
1110 )
1111 }
1112
1113 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, density: f32) -> f32 {
1114 size_intrinsic(
1115 self.target_constraints(density),
1116 SizeAxis::Height,
1117 self.enforce_incoming,
1118 width,
1119 |w| measurable.min_intrinsic_height(w),
1120 )
1121 }
1122
1123 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, density: f32) -> f32 {
1124 size_intrinsic(
1125 self.target_constraints(density),
1126 SizeAxis::Height,
1127 self.enforce_incoming,
1128 width,
1129 |w| measurable.max_intrinsic_height(w),
1130 )
1131 }
1132}
1133
1134#[derive(Debug, Clone, PartialEq)]
1138pub struct SizeElement {
1139 min_width: Option<f32>,
1140 max_width: Option<f32>,
1141 min_height: Option<f32>,
1142 max_height: Option<f32>,
1143 enforce_incoming: bool,
1144}
1145
1146impl SizeElement {
1147 pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
1148 Self {
1149 min_width: width,
1150 max_width: width,
1151 min_height: height,
1152 max_height: height,
1153 enforce_incoming: true,
1154 }
1155 }
1156
1157 pub fn with_constraints(
1158 min_width: Option<f32>,
1159 max_width: Option<f32>,
1160 min_height: Option<f32>,
1161 max_height: Option<f32>,
1162 enforce_incoming: bool,
1163 ) -> Self {
1164 Self {
1165 min_width,
1166 max_width,
1167 min_height,
1168 max_height,
1169 enforce_incoming,
1170 }
1171 }
1172}
1173
1174impl Hash for SizeElement {
1175 fn hash<H: Hasher>(&self, state: &mut H) {
1176 hash_option_f32(state, self.min_width);
1177 hash_option_f32(state, self.max_width);
1178 hash_option_f32(state, self.min_height);
1179 hash_option_f32(state, self.max_height);
1180 self.enforce_incoming.hash(state);
1181 }
1182}
1183
1184impl ModifierNodeElement for SizeElement {
1185 type Node = SizeNode;
1186
1187 fn create(&self) -> Self::Node {
1188 SizeNode::new(
1189 self.min_width,
1190 self.max_width,
1191 self.min_height,
1192 self.max_height,
1193 self.enforce_incoming,
1194 )
1195 }
1196
1197 fn update(&self, node: &mut Self::Node) {
1198 if node.min_width != self.min_width
1199 || node.max_width != self.max_width
1200 || node.min_height != self.min_height
1201 || node.max_height != self.max_height
1202 || node.enforce_incoming != self.enforce_incoming
1203 {
1204 node.min_width = self.min_width;
1205 node.max_width = self.max_width;
1206 node.min_height = self.min_height;
1207 node.max_height = self.max_height;
1208 node.enforce_incoming = self.enforce_incoming;
1209 }
1210 }
1211
1212 fn capabilities(&self) -> NodeCapabilities {
1213 NodeCapabilities::LAYOUT
1214 }
1215
1216 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
1217 Some(InvalidationKind::Layout)
1218 }
1219}
1220
1221use std::cell::RefCell;
1222
1223use cranpose_foundation::DRAG_THRESHOLD;
1224
1225pub struct ClickableNode {
1226 on_press: Option<Rc<dyn Fn(Point)>>,
1227 on_click: Rc<dyn Fn(Point)>,
1228 state: NodeState,
1229 press_position: Rc<RefCell<Option<Point>>>,
1230 cached_handler: Rc<dyn Fn(PointerEvent)>,
1231}
1232
1233impl std::fmt::Debug for ClickableNode {
1234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1235 f.debug_struct("ClickableNode").finish()
1236 }
1237}
1238
1239impl ClickableNode {
1240 pub fn new(on_click: impl Fn(Point) + 'static) -> Self {
1241 Self::with_handler(Rc::new(on_click))
1242 }
1243
1244 pub fn with_handler(on_click: Rc<dyn Fn(Point)>) -> Self {
1245 Self::with_handlers(None, on_click)
1246 }
1247
1248 pub fn with_handlers(on_press: Option<Rc<dyn Fn(Point)>>, on_click: Rc<dyn Fn(Point)>) -> Self {
1249 let press_position = Rc::new(RefCell::new(None));
1250 let cached_handler =
1251 Self::create_handler(on_press.clone(), on_click.clone(), press_position.clone());
1252 Self {
1253 on_press,
1254 on_click,
1255 state: NodeState::new(),
1256 press_position,
1257 cached_handler,
1258 }
1259 }
1260
1261 fn create_handler(
1262 on_press: Option<Rc<dyn Fn(Point)>>,
1263 on_click: Rc<dyn Fn(Point)>,
1264 press_position: Rc<RefCell<Option<Point>>>,
1265 ) -> Rc<dyn Fn(PointerEvent)> {
1266 Rc::new(move |event: PointerEvent| {
1267 if event.id != 0 {
1268 return;
1269 }
1270
1271 if event.is_consumed() {
1272 *press_position.borrow_mut() = None;
1273 return;
1274 }
1275
1276 match event.kind {
1277 PointerEventKind::Down => {
1278 *press_position.borrow_mut() = Some(event.travelled_to());
1279 if let Some(on_press) = on_press.as_ref() {
1280 on_press(event.position);
1281 }
1282 }
1283 PointerEventKind::Move => {}
1284 PointerEventKind::Up => {
1285 let press_pos_value = *press_position.borrow();
1286
1287 let should_click = if let Some(press_pos) = press_pos_value {
1288 let travelled_to = event.travelled_to();
1289 let dx = travelled_to.x - press_pos.x;
1290 let dy = travelled_to.y - press_pos.y;
1291 let distance = (dx * dx + dy * dy).sqrt();
1292 distance <= DRAG_THRESHOLD
1293 } else {
1294 true
1295 };
1296
1297 *press_position.borrow_mut() = None;
1298
1299 if should_click {
1300 on_click(Point {
1301 x: event.position.x,
1302 y: event.position.y,
1303 });
1304 event.consume();
1305 }
1306 }
1307 PointerEventKind::Cancel => {
1308 *press_position.borrow_mut() = None;
1309 }
1310 PointerEventKind::Scroll
1311 | PointerEventKind::Zoom
1312 | PointerEventKind::RotaryScrollPre
1313 | PointerEventKind::RotaryScroll
1314 | PointerEventKind::Enter
1315 | PointerEventKind::Exit => {}
1316 }
1317 })
1318 }
1319
1320 pub fn handler(&self) -> Rc<dyn Fn(Point)> {
1321 self.on_click.clone()
1322 }
1323}
1324
1325impl DelegatableNode for ClickableNode {
1326 fn node_state(&self) -> &NodeState {
1327 &self.state
1328 }
1329}
1330
1331impl ModifierNode for ClickableNode {
1332 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1333 context.invalidate(cranpose_foundation::InvalidationKind::PointerInput);
1334 }
1335
1336 fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
1337 Some(self)
1338 }
1339
1340 fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
1341 Some(self)
1342 }
1343}
1344
1345impl PointerInputNode for ClickableNode {
1346 fn on_pointer_event(
1347 &mut self,
1348 _context: &mut dyn ModifierNodeContext,
1349 event: &PointerEvent,
1350 ) -> bool {
1351 (self.cached_handler)(event.clone());
1352 event.is_consumed()
1353 }
1354
1355 fn hit_test(&self, _x: f32, _y: f32) -> bool {
1356 true
1357 }
1358
1359 fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
1360 Some(self.cached_handler.clone())
1361 }
1362}
1363
1364#[derive(Clone)]
1366pub struct ClickableElement {
1367 on_press: Option<Rc<dyn Fn(Point)>>,
1368 on_click: Rc<dyn Fn(Point)>,
1369}
1370
1371impl ClickableElement {
1372 pub fn new(on_click: impl Fn(Point) + 'static) -> Self {
1373 Self {
1374 on_press: None,
1375 on_click: Rc::new(on_click),
1376 }
1377 }
1378
1379 pub fn with_handler(on_click: Rc<dyn Fn(Point)>) -> Self {
1380 Self {
1381 on_press: None,
1382 on_click,
1383 }
1384 }
1385
1386 pub fn with_handlers(on_press: Rc<dyn Fn(Point)>, on_click: Rc<dyn Fn(Point)>) -> Self {
1387 Self {
1388 on_press: Some(on_press),
1389 on_click,
1390 }
1391 }
1392}
1393
1394impl std::fmt::Debug for ClickableElement {
1395 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1396 f.debug_struct("ClickableElement").finish()
1397 }
1398}
1399
1400impl PartialEq for ClickableElement {
1401 fn eq(&self, _other: &Self) -> bool {
1402 true
1403 }
1404}
1405
1406impl Eq for ClickableElement {}
1407
1408impl Hash for ClickableElement {
1409 fn hash<H: Hasher>(&self, state: &mut H) {
1410 "clickable".hash(state);
1411 }
1412}
1413
1414impl ModifierNodeElement for ClickableElement {
1415 type Node = ClickableNode;
1416
1417 fn create(&self) -> Self::Node {
1418 ClickableNode::with_handlers(self.on_press.clone(), self.on_click.clone())
1419 }
1420
1421 fn update(&self, node: &mut Self::Node) {
1422 node.on_press.clone_from(&self.on_press);
1423 node.on_click.clone_from(&self.on_click);
1424 node.cached_handler = ClickableNode::create_handler(
1425 node.on_press.clone(),
1426 node.on_click.clone(),
1427 node.press_position.clone(),
1428 );
1429 }
1430
1431 fn capabilities(&self) -> NodeCapabilities {
1432 NodeCapabilities::POINTER_INPUT
1433 }
1434
1435 fn always_update(&self) -> bool {
1436 true
1437 }
1438}
1439
1440#[derive(Debug)]
1446pub struct PointerIconNode {
1447 icon: PointerIcon,
1448 state: NodeState,
1449}
1450
1451impl PointerIconNode {
1452 pub fn new(icon: PointerIcon) -> Self {
1454 Self {
1455 icon,
1456 state: NodeState::new(),
1457 }
1458 }
1459
1460 pub fn icon(&self) -> &PointerIcon {
1462 &self.icon
1463 }
1464}
1465
1466impl DelegatableNode for PointerIconNode {
1467 fn node_state(&self) -> &NodeState {
1468 &self.state
1469 }
1470}
1471
1472impl ModifierNode for PointerIconNode {
1473 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1474 context.invalidate(cranpose_foundation::InvalidationKind::PointerInput);
1475 }
1476
1477 fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
1478 Some(self)
1479 }
1480
1481 fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
1482 Some(self)
1483 }
1484}
1485
1486impl PointerInputNode for PointerIconNode {
1487 fn on_pointer_event(
1488 &mut self,
1489 _context: &mut dyn ModifierNodeContext,
1490 _event: &PointerEvent,
1491 ) -> bool {
1492 false
1493 }
1494
1495 fn hit_test(&self, _x: f32, _y: f32) -> bool {
1496 true
1497 }
1498
1499 fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
1500 None
1501 }
1502}
1503
1504#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1506pub struct PointerIconElement {
1507 icon: PointerIcon,
1508}
1509
1510impl PointerIconElement {
1511 pub fn new(icon: PointerIcon) -> Self {
1513 Self { icon }
1514 }
1515}
1516
1517impl ModifierNodeElement for PointerIconElement {
1518 type Node = PointerIconNode;
1519
1520 fn create(&self) -> Self::Node {
1521 PointerIconNode::new(self.icon.clone())
1522 }
1523
1524 fn update(&self, node: &mut Self::Node) {
1525 node.icon = self.icon.clone();
1526 }
1527
1528 fn capabilities(&self) -> NodeCapabilities {
1529 NodeCapabilities::POINTER_INPUT
1530 }
1531}
1532
1533#[derive(Debug)]
1535pub struct AlphaNode {
1536 alpha: f32,
1537 state: NodeState,
1538}
1539
1540impl AlphaNode {
1541 pub fn new(alpha: f32) -> Self {
1542 Self {
1543 alpha: alpha.clamp(0.0, 1.0),
1544 state: NodeState::new(),
1545 }
1546 }
1547}
1548
1549impl DelegatableNode for AlphaNode {
1550 fn node_state(&self) -> &NodeState {
1551 &self.state
1552 }
1553}
1554
1555impl_draw_modifier_node!(AlphaNode);
1556
1557#[derive(Debug, Clone, PartialEq)]
1559pub struct AlphaElement {
1560 alpha: f32,
1561}
1562
1563impl AlphaElement {
1564 pub fn new(alpha: f32) -> Self {
1565 Self {
1566 alpha: alpha.clamp(0.0, 1.0),
1567 }
1568 }
1569}
1570
1571impl Hash for AlphaElement {
1572 fn hash<H: Hasher>(&self, state: &mut H) {
1573 hash_f32_value(state, self.alpha);
1574 }
1575}
1576
1577impl ModifierNodeElement for AlphaElement {
1578 type Node = AlphaNode;
1579
1580 fn create(&self) -> Self::Node {
1581 AlphaNode::new(self.alpha)
1582 }
1583
1584 fn update(&self, node: &mut Self::Node) {
1585 let new_alpha = self.alpha.clamp(0.0, 1.0);
1586 if (node.alpha - new_alpha).abs() > f32::EPSILON {
1587 node.alpha = new_alpha;
1588 }
1589 }
1590
1591 fn capabilities(&self) -> NodeCapabilities {
1592 NodeCapabilities::DRAW
1593 }
1594}
1595
1596#[derive(Debug)]
1597pub struct ClipToBoundsNode {
1598 state: NodeState,
1599}
1600
1601impl ClipToBoundsNode {
1602 pub fn new() -> Self {
1603 Self {
1604 state: NodeState::new(),
1605 }
1606 }
1607}
1608
1609impl DelegatableNode for ClipToBoundsNode {
1610 fn node_state(&self) -> &NodeState {
1611 &self.state
1612 }
1613}
1614
1615impl_draw_modifier_node!(ClipToBoundsNode);
1616
1617#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1618pub struct ClipToBoundsElement;
1619
1620impl ClipToBoundsElement {
1621 pub fn new() -> Self {
1622 Self
1623 }
1624}
1625
1626impl ModifierNodeElement for ClipToBoundsElement {
1627 type Node = ClipToBoundsNode;
1628
1629 fn create(&self) -> Self::Node {
1630 ClipToBoundsNode::new()
1631 }
1632
1633 fn update(&self, _node: &mut Self::Node) {}
1634
1635 fn capabilities(&self) -> NodeCapabilities {
1636 NodeCapabilities::DRAW
1637 }
1638}
1639
1640pub trait WindowRectSink {
1641 fn set(&self, coordinates: cranpose_ui_graphics::WindowCoordinates);
1642}
1643
1644impl WindowRectSink for Cell<cranpose_ui_graphics::Rect> {
1645 fn set(&self, coordinates: cranpose_ui_graphics::WindowCoordinates) {
1646 Cell::set(self, coordinates.bounds());
1647 }
1648}
1649
1650impl WindowRectSink for Cell<cranpose_ui_graphics::WindowCoordinates> {
1651 fn set(&self, coordinates: cranpose_ui_graphics::WindowCoordinates) {
1652 Cell::set(self, coordinates);
1653 }
1654}
1655
1656struct StateWindowRectSink(cranpose_core::MutableState<cranpose_ui_graphics::Rect>);
1657
1658impl WindowRectSink for StateWindowRectSink {
1659 fn set(&self, coordinates: cranpose_ui_graphics::WindowCoordinates) {
1660 self.0.set(coordinates.bounds());
1661 }
1662}
1663
1664pub struct WindowRectReporterNode {
1665 sink: Rc<dyn WindowRectSink>,
1666 state: NodeState,
1667}
1668
1669impl WindowRectReporterNode {
1670 pub(crate) fn new(sink: Rc<dyn WindowRectSink>) -> Self {
1671 Self {
1672 sink,
1673 state: NodeState::new(),
1674 }
1675 }
1676
1677 pub(crate) fn window_rect_sink(&self) -> Rc<dyn WindowRectSink> {
1678 self.sink.clone()
1679 }
1680}
1681
1682impl DelegatableNode for WindowRectReporterNode {
1683 fn node_state(&self) -> &NodeState {
1684 &self.state
1685 }
1686}
1687
1688impl_layout_modifier_node!(WindowRectReporterNode);
1689
1690impl LayoutModifierNode for WindowRectReporterNode {
1691 fn measure(
1692 &self,
1693 _context: &mut dyn ModifierNodeContext,
1694 measurable: &dyn Measurable,
1695 constraints: Constraints,
1696 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1697 measure_pass_through(measurable, constraints, |_| (0.0, 0.0))
1698 }
1699}
1700
1701#[derive(Clone)]
1702pub struct WindowRectReporterElement {
1703 sink: Rc<dyn WindowRectSink>,
1704}
1705
1706impl WindowRectReporterElement {
1707 pub fn new(sink: Rc<Cell<cranpose_ui_graphics::Rect>>) -> Self {
1708 Self { sink }
1709 }
1710
1711 pub fn from_coordinates(sink: Rc<Cell<cranpose_ui_graphics::WindowCoordinates>>) -> Self {
1713 Self { sink }
1714 }
1715
1716 pub fn from_state(sink: cranpose_core::MutableState<cranpose_ui_graphics::Rect>) -> Self {
1717 Self {
1718 sink: Rc::new(StateWindowRectSink(sink)),
1719 }
1720 }
1721}
1722
1723impl std::fmt::Debug for WindowRectReporterElement {
1724 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1725 f.debug_struct("WindowRectReporterElement").finish()
1726 }
1727}
1728
1729impl PartialEq for WindowRectReporterElement {
1730 fn eq(&self, other: &Self) -> bool {
1731 Rc::ptr_eq(&self.sink, &other.sink)
1732 }
1733}
1734
1735impl Eq for WindowRectReporterElement {}
1736
1737impl Hash for WindowRectReporterElement {
1738 fn hash<H: Hasher>(&self, state: &mut H) {
1739 std::ptr::hash(Rc::as_ptr(&self.sink).cast::<()>(), state);
1740 }
1741}
1742
1743impl_sink_reporter_element!(WindowRectReporterElement, WindowRectReporterNode);
1744
1745pub(crate) struct SelectableTextNode {
1746 sink: Rc<crate::selection_container::SelectableGeometry>,
1747 state: NodeState,
1748}
1749
1750impl SelectableTextNode {
1751 pub(crate) fn new(sink: Rc<crate::selection_container::SelectableGeometry>) -> Self {
1752 Self {
1753 sink,
1754 state: NodeState::new(),
1755 }
1756 }
1757
1758 pub(crate) fn geometry(&self) -> &crate::selection_container::SelectableGeometry {
1759 &self.sink
1760 }
1761}
1762
1763impl DelegatableNode for SelectableTextNode {
1764 fn node_state(&self) -> &NodeState {
1765 &self.state
1766 }
1767}
1768
1769impl_layout_modifier_node!(SelectableTextNode);
1770
1771impl LayoutModifierNode for SelectableTextNode {
1772 fn measure(
1773 &self,
1774 _context: &mut dyn ModifierNodeContext,
1775 measurable: &dyn Measurable,
1776 constraints: Constraints,
1777 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1778 measure_pass_through(measurable, constraints, |_| (0.0, 0.0))
1779 }
1780}
1781
1782#[derive(Clone)]
1783pub(crate) struct SelectableTextElement {
1784 sink: Rc<crate::selection_container::SelectableGeometry>,
1785}
1786
1787impl SelectableTextElement {
1788 pub(crate) fn new(sink: Rc<crate::selection_container::SelectableGeometry>) -> Self {
1789 Self { sink }
1790 }
1791}
1792
1793impl std::fmt::Debug for SelectableTextElement {
1794 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1795 f.debug_struct("SelectableTextElement").finish()
1796 }
1797}
1798
1799impl PartialEq for SelectableTextElement {
1800 fn eq(&self, other: &Self) -> bool {
1801 Rc::ptr_eq(&self.sink, &other.sink)
1802 }
1803}
1804
1805impl Eq for SelectableTextElement {}
1806
1807impl Hash for SelectableTextElement {
1808 fn hash<H: Hasher>(&self, state: &mut H) {
1809 std::ptr::hash(Rc::as_ptr(&self.sink), state);
1810 }
1811}
1812
1813impl_sink_reporter_element!(SelectableTextElement, SelectableTextNode);
1814
1815pub trait SizeSink {
1816 fn set(&self, size: Size);
1817}
1818
1819impl SizeSink for Cell<Size> {
1820 fn set(&self, size: Size) {
1821 Cell::set(self, size);
1822 }
1823}
1824
1825struct StateSizeSink(cranpose_core::MutableState<Size>);
1826
1827impl SizeSink for StateSizeSink {
1828 fn set(&self, size: Size) {
1829 self.0.set(size);
1830 }
1831}
1832
1833pub struct SizeReporterNode {
1834 sink: Rc<dyn SizeSink>,
1835 state: NodeState,
1836 #[cfg(debug_assertions)]
1837 oscillation: Cell<(Size, Size, u32)>,
1838}
1839
1840impl SizeReporterNode {
1841 pub fn new(sink: Rc<dyn SizeSink>) -> Self {
1842 Self {
1843 sink,
1844 state: NodeState::new(),
1845 #[cfg(debug_assertions)]
1846 oscillation: Cell::new((Size::default(), Size::default(), 0)),
1847 }
1848 }
1849
1850 #[cfg(debug_assertions)]
1851 fn check_oscillation(&self, size: Size) {
1852 const ALTERNATION_CEILING: u32 = 64;
1853 let (last, second_last, count) = self.oscillation.get();
1854 let count = if size == second_last && size != last {
1855 count + 1
1856 } else if size == last {
1857 count
1858 } else {
1859 0
1860 };
1861 assert!(
1862 count <= ALTERNATION_CEILING,
1863 "size-reactive feedback loop: this node's measured size has \
1864 alternated between {last:?} and {size:?} for {count} passes — \
1865 its content's size depends on the size it reports (the \
1866 onSizeChanged self-reference hazard). Break the cycle by making \
1867 the reported size feed only content that does not change this \
1868 node's own measured size."
1869 );
1870 self.oscillation.set((size, last, count));
1871 }
1872}
1873
1874impl DelegatableNode for SizeReporterNode {
1875 fn node_state(&self) -> &NodeState {
1876 &self.state
1877 }
1878}
1879
1880impl_layout_modifier_node!(SizeReporterNode);
1881
1882impl LayoutModifierNode for SizeReporterNode {
1883 fn measure(
1884 &self,
1885 _context: &mut dyn ModifierNodeContext,
1886 measurable: &dyn Measurable,
1887 constraints: Constraints,
1888 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1889 measure_pass_through(measurable, constraints, |size| {
1890 #[cfg(debug_assertions)]
1891 self.check_oscillation(size);
1892 self.sink.set(size);
1893 (0.0, 0.0)
1894 })
1895 }
1896}
1897
1898#[derive(Clone)]
1899pub struct SizeReporterElement {
1900 sink: Rc<dyn SizeSink>,
1901}
1902
1903impl SizeReporterElement {
1904 pub fn new(sink: Rc<Cell<Size>>) -> Self {
1905 Self { sink }
1906 }
1907
1908 pub fn from_state(sink: cranpose_core::MutableState<Size>) -> Self {
1909 Self {
1910 sink: Rc::new(StateSizeSink(sink)),
1911 }
1912 }
1913}
1914
1915impl std::fmt::Debug for SizeReporterElement {
1916 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1917 f.debug_struct("SizeReporterElement").finish()
1918 }
1919}
1920
1921impl PartialEq for SizeReporterElement {
1922 fn eq(&self, other: &Self) -> bool {
1923 std::ptr::addr_eq(Rc::as_ptr(&self.sink), Rc::as_ptr(&other.sink))
1924 }
1925}
1926
1927impl Hash for SizeReporterElement {
1928 fn hash<H: Hasher>(&self, state: &mut H) {
1929 (Rc::as_ptr(&self.sink) as *const () as usize).hash(state);
1930 }
1931}
1932
1933impl_sink_reporter_element!(SizeReporterElement, SizeReporterNode);
1934
1935pub struct DrawCommandNode {
1936 commands: Vec<DrawCommand>,
1937 node_id: Cell<Option<NodeId>>,
1938 state: NodeState,
1939}
1940
1941impl DrawCommandNode {
1942 pub fn new(commands: Vec<DrawCommand>) -> Self {
1943 Self {
1944 commands,
1945 node_id: Cell::new(None),
1946 state: NodeState::new(),
1947 }
1948 }
1949
1950 #[cfg(test)]
1951 pub fn commands(&self) -> &[DrawCommand] {
1952 &self.commands
1953 }
1954
1955 pub(crate) fn observed_commands(
1956 &self,
1957 modifier_index: usize,
1958 ) -> impl Iterator<Item = DrawCommand> + '_ {
1959 let node_id = self.node_id.get();
1960 self.commands
1961 .iter()
1962 .cloned()
1963 .enumerate()
1964 .map(move |(index, command)| {
1965 observe_draw_command(command, node_id, modifier_index, index)
1966 })
1967 }
1968}
1969
1970impl DelegatableNode for DrawCommandNode {
1971 fn node_state(&self) -> &NodeState {
1972 &self.state
1973 }
1974}
1975
1976impl ModifierNode for DrawCommandNode {
1977 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1978 attach_draw_observer(&self.node_id, context);
1979 }
1980
1981 fn on_detach(&mut self) {
1982 detach_draw_observer(&self.node_id);
1983 }
1984
1985 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
1986 Some(self)
1987 }
1988
1989 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
1990 Some(self)
1991 }
1992}
1993
1994impl DrawModifierNode for DrawCommandNode {}
1995
1996fn observe_draw_command(
1997 command: DrawCommand,
1998 node_id: Option<NodeId>,
1999 modifier_index: usize,
2000 command_index: usize,
2001) -> DrawCommand {
2002 let Some(node_id) = node_id else {
2003 return command;
2004 };
2005 let observation =
2006 crate::render_state::DrawObservationScope::new(node_id, modifier_index, command_index);
2007 match command {
2008 DrawCommand::Behind(draw) => DrawCommand::Behind(Rc::new(move |scope| {
2009 crate::render_state::observe_draw_reads(observation, || draw(scope));
2010 })),
2011 DrawCommand::WithContent(draw) => DrawCommand::WithContent(Rc::new(move |scope| {
2012 crate::render_state::observe_draw_reads(observation, || draw(scope));
2013 })),
2014 DrawCommand::Overlay(draw) => DrawCommand::Overlay(Rc::new(move |scope| {
2015 crate::render_state::observe_draw_reads(observation, || draw(scope));
2016 })),
2017 }
2018}
2019
2020fn draw_command_tag(cmd: &DrawCommand) -> u8 {
2021 match cmd {
2022 DrawCommand::Behind(_) => 0,
2023 DrawCommand::WithContent(_) => 1,
2024 DrawCommand::Overlay(_) => 2,
2025 }
2026}
2027
2028fn draw_command_closure_identity(cmd: &DrawCommand) -> *const () {
2029 match cmd {
2030 DrawCommand::Behind(f) | DrawCommand::WithContent(f) | DrawCommand::Overlay(f) => {
2031 Rc::as_ptr(f) as *const ()
2032 }
2033 }
2034}
2035
2036#[derive(Clone)]
2037pub struct DrawCommandElement {
2038 commands: Vec<DrawCommand>,
2039}
2040
2041impl DrawCommandElement {
2042 pub fn new(command: DrawCommand) -> Self {
2043 Self {
2044 commands: vec![command],
2045 }
2046 }
2047
2048 pub fn from_commands(commands: Vec<DrawCommand>) -> Self {
2049 Self { commands }
2050 }
2051}
2052
2053impl std::fmt::Debug for DrawCommandElement {
2054 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2055 f.debug_struct("DrawCommandElement")
2056 .field("commands", &self.commands.len())
2057 .finish()
2058 }
2059}
2060
2061impl PartialEq for DrawCommandElement {
2062 fn eq(&self, other: &Self) -> bool {
2063 if self.commands.len() != other.commands.len() {
2064 return false;
2065 }
2066 self.commands
2067 .iter()
2068 .zip(other.commands.iter())
2069 .all(|(a, b)| {
2070 draw_command_tag(a) == draw_command_tag(b)
2071 && draw_command_closure_identity(a) == draw_command_closure_identity(b)
2072 })
2073 }
2074}
2075
2076impl Eq for DrawCommandElement {}
2077
2078impl std::hash::Hash for DrawCommandElement {
2079 fn hash<H: Hasher>(&self, state: &mut H) {
2080 "draw_commands".hash(state);
2081 self.commands.len().hash(state);
2082 for command in &self.commands {
2083 draw_command_tag(command).hash(state);
2084 (draw_command_closure_identity(command) as usize).hash(state);
2085 }
2086 }
2087}
2088
2089impl ModifierNodeElement for DrawCommandElement {
2090 type Node = DrawCommandNode;
2091
2092 fn create(&self) -> Self::Node {
2093 DrawCommandNode::new(self.commands.clone())
2094 }
2095
2096 fn update(&self, node: &mut Self::Node) {
2097 node.commands.clone_from(&self.commands);
2098 }
2099
2100 fn capabilities(&self) -> NodeCapabilities {
2101 NodeCapabilities::DRAW
2102 }
2103}
2104
2105#[derive(Debug)]
2109pub struct OffsetNode {
2110 x: f32,
2111 y: f32,
2112 rtl_aware: bool,
2113 state: NodeState,
2114}
2115
2116impl OffsetNode {
2117 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
2118 Self {
2119 x,
2120 y,
2121 rtl_aware,
2122 state: NodeState::new(),
2123 }
2124 }
2125
2126 pub fn offset(&self) -> Point {
2127 Point {
2128 x: self.x,
2129 y: self.y,
2130 }
2131 }
2132
2133 pub fn device_offset(&self, density: f32) -> Point {
2136 use cranpose_ui_layout::round_to_px;
2137 Point {
2138 x: round_to_px(self.x, density),
2139 y: round_to_px(self.y, density),
2140 }
2141 }
2142
2143 pub fn rtl_aware(&self) -> bool {
2144 self.rtl_aware
2145 }
2146}
2147
2148impl DelegatableNode for OffsetNode {
2149 fn node_state(&self) -> &NodeState {
2150 &self.state
2151 }
2152}
2153
2154impl_layout_modifier_node!(OffsetNode, invalidate = InvalidationKind::Layout);
2155
2156impl LayoutModifierNode for OffsetNode {
2157 fn measure(
2158 &self,
2159 context: &mut dyn ModifierNodeContext,
2160 measurable: &dyn Measurable,
2161 constraints: Constraints,
2162 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2163 let offset = self.device_offset(context.density());
2164 measure_pass_through(measurable, constraints, |_| (offset.x, offset.y))
2165 }
2166
2167 forward_intrinsics_to_child!();
2168}
2169
2170#[derive(Debug, Clone, PartialEq)]
2174pub struct OffsetElement {
2175 x: f32,
2176 y: f32,
2177 rtl_aware: bool,
2178}
2179
2180impl OffsetElement {
2181 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
2182 Self { x, y, rtl_aware }
2183 }
2184}
2185
2186impl Hash for OffsetElement {
2187 fn hash<H: Hasher>(&self, state: &mut H) {
2188 hash_f32_value(state, self.x);
2189 hash_f32_value(state, self.y);
2190 self.rtl_aware.hash(state);
2191 }
2192}
2193
2194impl ModifierNodeElement for OffsetElement {
2195 type Node = OffsetNode;
2196
2197 fn create(&self) -> Self::Node {
2198 OffsetNode::new(self.x, self.y, self.rtl_aware)
2199 }
2200
2201 fn update(&self, node: &mut Self::Node) {
2202 if node.x != self.x || node.y != self.y || node.rtl_aware != self.rtl_aware {
2203 node.x = self.x;
2204 node.y = self.y;
2205 node.rtl_aware = self.rtl_aware;
2206 }
2207 }
2208
2209 fn capabilities(&self) -> NodeCapabilities {
2210 NodeCapabilities::LAYOUT
2211 }
2212
2213 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
2214 Some(InvalidationKind::Layout)
2215 }
2216}
2217
2218#[derive(Debug)]
2226pub struct FractionalOffsetNode {
2227 x_fraction: f32,
2228 y_fraction: f32,
2229 state: NodeState,
2230}
2231
2232impl FractionalOffsetNode {
2233 pub fn new(x_fraction: f32, y_fraction: f32) -> Self {
2234 Self {
2235 x_fraction,
2236 y_fraction,
2237 state: NodeState::new(),
2238 }
2239 }
2240
2241 pub fn fractions(&self) -> Point {
2242 Point {
2243 x: self.x_fraction,
2244 y: self.y_fraction,
2245 }
2246 }
2247}
2248
2249impl DelegatableNode for FractionalOffsetNode {
2250 fn node_state(&self) -> &NodeState {
2251 &self.state
2252 }
2253}
2254
2255impl_layout_modifier_node!(FractionalOffsetNode, invalidate = InvalidationKind::Layout);
2256
2257impl LayoutModifierNode for FractionalOffsetNode {
2258 fn measure(
2259 &self,
2260 _context: &mut dyn ModifierNodeContext,
2261 measurable: &dyn Measurable,
2262 constraints: Constraints,
2263 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2264 measure_pass_through(measurable, constraints, |size| {
2265 (self.x_fraction * size.width, self.y_fraction * size.height)
2266 })
2267 }
2268
2269 forward_intrinsics_to_child!();
2270}
2271
2272#[derive(Debug, Clone, PartialEq)]
2274pub struct FractionalOffsetElement {
2275 x_fraction: f32,
2276 y_fraction: f32,
2277}
2278
2279impl FractionalOffsetElement {
2280 pub fn new(x_fraction: f32, y_fraction: f32) -> Self {
2281 Self {
2282 x_fraction,
2283 y_fraction,
2284 }
2285 }
2286}
2287
2288impl Hash for FractionalOffsetElement {
2289 fn hash<H: Hasher>(&self, state: &mut H) {
2290 "fractional_offset".hash(state);
2291 hash_f32_value(state, self.x_fraction);
2292 hash_f32_value(state, self.y_fraction);
2293 }
2294}
2295
2296impl ModifierNodeElement for FractionalOffsetElement {
2297 type Node = FractionalOffsetNode;
2298
2299 fn create(&self) -> Self::Node {
2300 FractionalOffsetNode::new(self.x_fraction, self.y_fraction)
2301 }
2302
2303 fn update(&self, node: &mut Self::Node) {
2304 if node.x_fraction != self.x_fraction || node.y_fraction != self.y_fraction {
2305 node.x_fraction = self.x_fraction;
2306 node.y_fraction = self.y_fraction;
2307 }
2308 }
2309
2310 fn capabilities(&self) -> NodeCapabilities {
2311 NodeCapabilities::LAYOUT
2312 }
2313
2314 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
2315 Some(InvalidationKind::Layout)
2316 }
2317}
2318
2319#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2321pub enum FillDirection {
2322 Horizontal,
2323 Vertical,
2324 Both,
2325}
2326
2327#[derive(Debug)]
2331pub struct FillNode {
2332 direction: FillDirection,
2333 fraction: f32,
2334 state: NodeState,
2335}
2336
2337impl FillNode {
2338 pub fn new(direction: FillDirection, fraction: f32) -> Self {
2339 Self {
2340 direction,
2341 fraction,
2342 state: NodeState::new(),
2343 }
2344 }
2345
2346 pub fn direction(&self) -> FillDirection {
2347 self.direction
2348 }
2349
2350 pub fn fraction(&self) -> f32 {
2351 self.fraction
2352 }
2353}
2354
2355impl DelegatableNode for FillNode {
2356 fn node_state(&self) -> &NodeState {
2357 &self.state
2358 }
2359}
2360
2361impl_layout_modifier_node!(FillNode, invalidate = InvalidationKind::Layout);
2362
2363impl LayoutModifierNode for FillNode {
2364 fn measure(
2365 &self,
2366 context: &mut dyn ModifierNodeContext,
2367 measurable: &dyn Measurable,
2368 constraints: Constraints,
2369 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2370 let density = context.density();
2371 let (fill_width, child_min_width, child_max_width) = if self.direction
2372 != FillDirection::Vertical
2373 && constraints.max_width != f32::INFINITY
2374 {
2375 let width =
2376 cranpose_ui_layout::round_to_px(constraints.max_width * self.fraction, density)
2377 .clamp(constraints.min_width, constraints.max_width);
2378 (width, width, width)
2379 } else {
2380 (
2381 constraints.max_width,
2382 constraints.min_width,
2383 constraints.max_width,
2384 )
2385 };
2386
2387 let (fill_height, child_min_height, child_max_height) = if self.direction
2388 != FillDirection::Horizontal
2389 && constraints.max_height != f32::INFINITY
2390 {
2391 let height =
2392 cranpose_ui_layout::round_to_px(constraints.max_height * self.fraction, density)
2393 .clamp(constraints.min_height, constraints.max_height);
2394 (height, height, height)
2395 } else {
2396 (
2397 constraints.max_height,
2398 constraints.min_height,
2399 constraints.max_height,
2400 )
2401 };
2402
2403 let fill_constraints = Constraints {
2404 min_width: child_min_width,
2405 max_width: child_max_width,
2406 min_height: child_min_height,
2407 max_height: child_max_height,
2408 };
2409
2410 let placeable = measurable.measure(fill_constraints);
2411
2412 let result_width = if self.direction != FillDirection::Vertical
2413 && constraints.max_width != f32::INFINITY
2414 {
2415 fill_width
2416 } else {
2417 placeable.width()
2418 };
2419
2420 let result_height = if self.direction != FillDirection::Horizontal
2421 && constraints.max_height != f32::INFINITY
2422 {
2423 fill_height
2424 } else {
2425 placeable.height()
2426 };
2427
2428 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
2429 width: result_width,
2430 height: result_height,
2431 })
2432 }
2433
2434 forward_intrinsics_to_child!();
2435}
2436
2437#[derive(Debug, Clone, PartialEq)]
2441pub struct FillElement {
2442 direction: FillDirection,
2443 fraction: f32,
2444}
2445
2446impl FillElement {
2447 pub fn width(fraction: f32) -> Self {
2448 Self {
2449 direction: FillDirection::Horizontal,
2450 fraction,
2451 }
2452 }
2453
2454 pub fn height(fraction: f32) -> Self {
2455 Self {
2456 direction: FillDirection::Vertical,
2457 fraction,
2458 }
2459 }
2460
2461 pub fn size(fraction: f32) -> Self {
2462 Self {
2463 direction: FillDirection::Both,
2464 fraction,
2465 }
2466 }
2467}
2468
2469impl Hash for FillElement {
2470 fn hash<H: Hasher>(&self, state: &mut H) {
2471 self.direction.hash(state);
2472 hash_f32_value(state, self.fraction);
2473 }
2474}
2475
2476impl ModifierNodeElement for FillElement {
2477 type Node = FillNode;
2478
2479 fn create(&self) -> Self::Node {
2480 FillNode::new(self.direction, self.fraction)
2481 }
2482
2483 fn update(&self, node: &mut Self::Node) {
2484 if node.direction != self.direction || node.fraction != self.fraction {
2485 node.direction = self.direction;
2486 node.fraction = self.fraction;
2487 }
2488 }
2489
2490 fn capabilities(&self) -> NodeCapabilities {
2491 NodeCapabilities::LAYOUT
2492 }
2493}
2494
2495#[derive(Debug)]
2496pub struct WeightNode {
2497 weight: f32,
2498 fill: bool,
2499 state: NodeState,
2500}
2501
2502impl WeightNode {
2503 pub fn new(weight: f32, fill: bool) -> Self {
2504 Self {
2505 weight,
2506 fill,
2507 state: NodeState::new(),
2508 }
2509 }
2510
2511 pub fn layout_weight(&self) -> LayoutWeight {
2512 LayoutWeight {
2513 weight: self.weight,
2514 fill: self.fill,
2515 }
2516 }
2517}
2518
2519impl DelegatableNode for WeightNode {
2520 fn node_state(&self) -> &NodeState {
2521 &self.state
2522 }
2523}
2524
2525impl ModifierNode for WeightNode {
2526 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2527 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2528 }
2529}
2530
2531#[derive(Debug, Clone, PartialEq)]
2532pub struct WeightElement {
2533 weight: f32,
2534 fill: bool,
2535}
2536
2537impl WeightElement {
2538 pub fn new(weight: f32, fill: bool) -> Self {
2539 Self { weight, fill }
2540 }
2541}
2542
2543impl Hash for WeightElement {
2544 fn hash<H: Hasher>(&self, state: &mut H) {
2545 hash_f32_value(state, self.weight);
2546 self.fill.hash(state);
2547 }
2548}
2549
2550impl ModifierNodeElement for WeightElement {
2551 type Node = WeightNode;
2552
2553 fn create(&self) -> Self::Node {
2554 WeightNode::new(self.weight, self.fill)
2555 }
2556
2557 fn update(&self, node: &mut Self::Node) {
2558 if node.weight != self.weight || node.fill != self.fill {
2559 node.weight = self.weight;
2560 node.fill = self.fill;
2561 }
2562 }
2563
2564 fn capabilities(&self) -> NodeCapabilities {
2565 NodeCapabilities::LAYOUT
2566 }
2567}
2568
2569#[derive(Debug)]
2570pub struct AlignmentNode {
2571 box_alignment: Option<Alignment>,
2572 column_alignment: Option<HorizontalAlignment>,
2573 row_alignment: Option<VerticalAlignment>,
2574 row_baseline: bool,
2575 state: NodeState,
2576}
2577
2578impl AlignmentNode {
2579 pub fn new(
2580 box_alignment: Option<Alignment>,
2581 column_alignment: Option<HorizontalAlignment>,
2582 row_alignment: Option<VerticalAlignment>,
2583 row_baseline: bool,
2584 ) -> Self {
2585 Self {
2586 box_alignment,
2587 column_alignment,
2588 row_alignment,
2589 row_baseline,
2590 state: NodeState::new(),
2591 }
2592 }
2593
2594 pub fn box_alignment(&self) -> Option<Alignment> {
2595 self.box_alignment
2596 }
2597
2598 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
2599 self.column_alignment
2600 }
2601
2602 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
2603 self.row_alignment
2604 }
2605
2606 pub fn row_baseline(&self) -> bool {
2608 self.row_baseline
2609 }
2610}
2611
2612impl DelegatableNode for AlignmentNode {
2613 fn node_state(&self) -> &NodeState {
2614 &self.state
2615 }
2616}
2617
2618impl ModifierNode for AlignmentNode {
2619 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2620 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2621 }
2622}
2623
2624#[derive(Debug, Clone, PartialEq)]
2625pub struct AlignmentElement {
2626 box_alignment: Option<Alignment>,
2627 column_alignment: Option<HorizontalAlignment>,
2628 row_alignment: Option<VerticalAlignment>,
2629 row_baseline: bool,
2630}
2631
2632impl AlignmentElement {
2633 pub fn box_alignment(alignment: Alignment) -> Self {
2634 Self {
2635 box_alignment: Some(alignment),
2636 column_alignment: None,
2637 row_alignment: None,
2638 row_baseline: false,
2639 }
2640 }
2641
2642 pub fn column_alignment(alignment: HorizontalAlignment) -> Self {
2643 Self {
2644 box_alignment: None,
2645 column_alignment: Some(alignment),
2646 row_alignment: None,
2647 row_baseline: false,
2648 }
2649 }
2650
2651 pub fn row_alignment(alignment: VerticalAlignment) -> Self {
2652 Self {
2653 box_alignment: None,
2654 column_alignment: None,
2655 row_alignment: Some(alignment),
2656 row_baseline: false,
2657 }
2658 }
2659
2660 pub fn row_baseline() -> Self {
2662 Self {
2663 box_alignment: None,
2664 column_alignment: None,
2665 row_alignment: None,
2666 row_baseline: true,
2667 }
2668 }
2669}
2670
2671impl Hash for AlignmentElement {
2672 fn hash<H: Hasher>(&self, state: &mut H) {
2673 if let Some(alignment) = self.box_alignment {
2674 state.write_u8(1);
2675 hash_alignment(state, alignment);
2676 } else {
2677 state.write_u8(0);
2678 }
2679 if let Some(alignment) = self.column_alignment {
2680 state.write_u8(1);
2681 hash_horizontal_alignment(state, alignment);
2682 } else {
2683 state.write_u8(0);
2684 }
2685 if let Some(alignment) = self.row_alignment {
2686 state.write_u8(1);
2687 hash_vertical_alignment(state, alignment);
2688 } else {
2689 state.write_u8(0);
2690 }
2691 self.row_baseline.hash(state);
2692 }
2693}
2694
2695impl ModifierNodeElement for AlignmentElement {
2696 type Node = AlignmentNode;
2697
2698 fn create(&self) -> Self::Node {
2699 AlignmentNode::new(
2700 self.box_alignment,
2701 self.column_alignment,
2702 self.row_alignment,
2703 self.row_baseline,
2704 )
2705 }
2706
2707 fn update(&self, node: &mut Self::Node) {
2708 if node.box_alignment != self.box_alignment {
2709 node.box_alignment = self.box_alignment;
2710 }
2711 if node.column_alignment != self.column_alignment {
2712 node.column_alignment = self.column_alignment;
2713 }
2714 if node.row_alignment != self.row_alignment {
2715 node.row_alignment = self.row_alignment;
2716 }
2717 node.row_baseline = self.row_baseline;
2718 }
2719
2720 fn capabilities(&self) -> NodeCapabilities {
2721 NodeCapabilities::LAYOUT
2722 }
2723}
2724
2725#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2726pub enum IntrinsicAxis {
2727 Width,
2728 Height,
2729}
2730
2731#[derive(Debug)]
2732pub struct IntrinsicSizeNode {
2733 axis: IntrinsicAxis,
2734 size: IntrinsicSize,
2735 state: NodeState,
2736}
2737
2738impl IntrinsicSizeNode {
2739 pub fn new(axis: IntrinsicAxis, size: IntrinsicSize) -> Self {
2740 Self {
2741 axis,
2742 size,
2743 state: NodeState::new(),
2744 }
2745 }
2746
2747 pub fn axis(&self) -> IntrinsicAxis {
2748 self.axis
2749 }
2750
2751 pub fn intrinsic_size(&self) -> IntrinsicSize {
2752 self.size
2753 }
2754}
2755
2756impl DelegatableNode for IntrinsicSizeNode {
2757 fn node_state(&self) -> &NodeState {
2758 &self.state
2759 }
2760}
2761
2762impl ModifierNode for IntrinsicSizeNode {
2763 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2764 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2765 }
2766}
2767
2768#[derive(Debug, Clone, PartialEq)]
2769pub struct IntrinsicSizeElement {
2770 axis: IntrinsicAxis,
2771 size: IntrinsicSize,
2772}
2773
2774impl IntrinsicSizeElement {
2775 pub fn width(size: IntrinsicSize) -> Self {
2776 Self {
2777 axis: IntrinsicAxis::Width,
2778 size,
2779 }
2780 }
2781
2782 pub fn height(size: IntrinsicSize) -> Self {
2783 Self {
2784 axis: IntrinsicAxis::Height,
2785 size,
2786 }
2787 }
2788}
2789
2790impl Hash for IntrinsicSizeElement {
2791 fn hash<H: Hasher>(&self, state: &mut H) {
2792 state.write_u8(match self.axis {
2793 IntrinsicAxis::Width => 0,
2794 IntrinsicAxis::Height => 1,
2795 });
2796 state.write_u8(match self.size {
2797 IntrinsicSize::Min => 0,
2798 IntrinsicSize::Max => 1,
2799 });
2800 }
2801}
2802
2803impl ModifierNodeElement for IntrinsicSizeElement {
2804 type Node = IntrinsicSizeNode;
2805
2806 fn create(&self) -> Self::Node {
2807 IntrinsicSizeNode::new(self.axis, self.size)
2808 }
2809
2810 fn update(&self, node: &mut Self::Node) {
2811 if node.axis != self.axis {
2812 node.axis = self.axis;
2813 }
2814 if node.size != self.size {
2815 node.size = self.size;
2816 }
2817 }
2818
2819 fn capabilities(&self) -> NodeCapabilities {
2820 NodeCapabilities::LAYOUT
2821 }
2822}
2823
2824#[cfg(test)]
2825#[path = "tests/modifier_nodes_tests.rs"]
2826mod tests;