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_space<H: Hasher>(state: &mut H, space: Option<&cranpose_ui_graphics::space::LayerSpace>) {
41 use cranpose_ui_graphics::space::LayerSpace;
42 match space {
43 None => state.write_u8(0),
44 Some(LayerSpace::Scene(_)) => state.write_u8(1),
45 Some(LayerSpace::Surface(surface)) => {
46 state.write_u8(2);
47 state.write_u64(surface.mesh.id());
48 }
49 Some(LayerSpace::Transform(_)) => state.write_u8(3),
50 }
51}
52
53fn hash_graphics_layer<H: Hasher>(state: &mut H, layer: &GraphicsLayer) {
54 hash_f32_value(state, layer.alpha);
55 hash_f32_value(state, layer.scale);
56 hash_f32_value(state, layer.scale_x);
57 hash_f32_value(state, layer.scale_y);
58 hash_f32_value(state, layer.rotation_x);
59 hash_f32_value(state, layer.rotation_y);
60 hash_f32_value(state, layer.rotation_z);
61 hash_f32_value(state, layer.camera_distance);
62 hash_f32_value(state, layer.transform_origin.pivot_fraction_x);
63 hash_f32_value(state, layer.transform_origin.pivot_fraction_y);
64 hash_f32_value(state, layer.translation_x);
65 hash_f32_value(state, layer.translation_y);
66 hash_space(state, layer.space.as_deref());
67 hash_f32_value(state, layer.shadow_elevation);
68 hash_f32_value(state, layer.ambient_shadow_color.r());
69 hash_f32_value(state, layer.ambient_shadow_color.g());
70 hash_f32_value(state, layer.ambient_shadow_color.b());
71 hash_f32_value(state, layer.ambient_shadow_color.a());
72 hash_f32_value(state, layer.spot_shadow_color.r());
73 hash_f32_value(state, layer.spot_shadow_color.g());
74 hash_f32_value(state, layer.spot_shadow_color.b());
75 hash_f32_value(state, layer.spot_shadow_color.a());
76 match layer.shape {
77 crate::modifier::LayerShape::Rectangle => {
78 state.write_u8(0);
79 }
80 crate::modifier::LayerShape::Rounded(shape) => {
81 state.write_u8(1);
82 let radii = shape.radii();
83 hash_f32_value(state, radii.top_left);
84 hash_f32_value(state, radii.top_right);
85 hash_f32_value(state, radii.bottom_right);
86 hash_f32_value(state, radii.bottom_left);
87 }
88 }
89 state.write_u8(layer.clip as u8);
90 match &layer.color_filter {
91 Some(ColorFilter::Tint(color)) => {
92 state.write_u8(1);
93 hash_f32_value(state, color.r());
94 hash_f32_value(state, color.g());
95 hash_f32_value(state, color.b());
96 hash_f32_value(state, color.a());
97 }
98 Some(ColorFilter::Modulate(color)) => {
99 state.write_u8(2);
100 hash_f32_value(state, color.r());
101 hash_f32_value(state, color.g());
102 hash_f32_value(state, color.b());
103 hash_f32_value(state, color.a());
104 }
105 Some(ColorFilter::Matrix(matrix)) => {
106 state.write_u8(3);
107 for &value in matrix.iter() {
108 hash_f32_value(state, value);
109 }
110 }
111 None => state.write_u8(0),
112 }
113 state.write_u8(layer.render_effect.is_some() as u8);
114 state.write_u8(layer.backdrop_effect.is_some() as u8);
115 let compositing_tag = match layer.compositing_strategy {
116 CompositingStrategy::Auto => 0,
117 CompositingStrategy::Offscreen => 1,
118 CompositingStrategy::ModulateAlpha => 2,
119 };
120 state.write_u8(compositing_tag);
121 let blend_tag = match layer.blend_mode {
122 BlendMode::Clear => 0,
123 BlendMode::Src => 1,
124 BlendMode::Dst => 2,
125 BlendMode::SrcOver => 3,
126 BlendMode::DstOver => 4,
127 BlendMode::SrcIn => 5,
128 BlendMode::DstIn => 6,
129 BlendMode::SrcOut => 7,
130 BlendMode::DstOut => 8,
131 BlendMode::SrcAtop => 9,
132 BlendMode::DstAtop => 10,
133 BlendMode::Xor => 11,
134 BlendMode::Plus => 12,
135 BlendMode::Modulate => 13,
136 BlendMode::Screen => 14,
137 BlendMode::Overlay => 15,
138 BlendMode::Darken => 16,
139 BlendMode::Lighten => 17,
140 BlendMode::ColorDodge => 18,
141 BlendMode::ColorBurn => 19,
142 BlendMode::HardLight => 20,
143 BlendMode::SoftLight => 21,
144 BlendMode::Difference => 22,
145 BlendMode::Exclusion => 23,
146 BlendMode::Multiply => 24,
147 BlendMode::Hue => 25,
148 BlendMode::Saturation => 26,
149 BlendMode::Color => 27,
150 BlendMode::Luminosity => 28,
151 };
152 state.write_u8(blend_tag);
153}
154
155fn hash_horizontal_alignment<H: Hasher>(state: &mut H, alignment: HorizontalAlignment) {
156 let tag = match alignment {
157 HorizontalAlignment::Start => 0,
158 HorizontalAlignment::CenterHorizontally => 1,
159 HorizontalAlignment::End => 2,
160 };
161 state.write_u8(tag);
162}
163
164fn hash_vertical_alignment<H: Hasher>(state: &mut H, alignment: VerticalAlignment) {
165 let tag = match alignment {
166 VerticalAlignment::Top => 0,
167 VerticalAlignment::CenterVertically => 1,
168 VerticalAlignment::Bottom => 2,
169 };
170 state.write_u8(tag);
171}
172
173fn hash_alignment<H: Hasher>(state: &mut H, alignment: Alignment) {
174 hash_horizontal_alignment(state, alignment.horizontal);
175 hash_vertical_alignment(state, alignment.vertical);
176}
177
178macro_rules! impl_layout_modifier_node {
179 ($ty:ty) => {
180 impl ModifierNode for $ty {
181 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
182 Some(self)
183 }
184
185 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
186 Some(self)
187 }
188 }
189 };
190 ($ty:ty, invalidate = $invalidation:expr) => {
191 impl ModifierNode for $ty {
192 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
193 context.invalidate($invalidation);
194 }
195
196 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
197 Some(self)
198 }
199
200 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
201 Some(self)
202 }
203 }
204 };
205}
206pub(crate) use impl_layout_modifier_node;
207
208macro_rules! impl_draw_modifier_node {
209 ($ty:ty) => {
210 impl ModifierNode for $ty {
211 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
212 context.invalidate(InvalidationKind::Draw);
213 }
214
215 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
216 Some(self)
217 }
218
219 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
220 Some(self)
221 }
222 }
223
224 impl DrawModifierNode for $ty {}
225 };
226}
227
228macro_rules! impl_sink_reporter_element {
229 ($element:ty, $node:ty) => {
230 impl ModifierNodeElement for $element {
231 type Node = $node;
232
233 fn create(&self) -> Self::Node {
234 <$node>::new(self.sink.clone())
235 }
236
237 fn update(&self, node: &mut Self::Node) {
238 node.sink = self.sink.clone();
239 }
240
241 fn capabilities(&self) -> NodeCapabilities {
242 NodeCapabilities::LAYOUT
243 }
244 }
245 };
246}
247
248fn size_axis_hold(
253 (min, max): (Option<f32>, Option<f32>),
254 target: f32,
255 size: f32,
256 wrapped: Option<cranpose_ui_layout::AxisHold>,
257) -> Option<cranpose_ui_layout::AxisHold> {
258 match (min, max) {
259 (Some(min), Some(max)) if min == max && size == target => {
260 Some(cranpose_ui_layout::AxisHold::sized(target))
261 }
262 (None, None) => wrapped,
263 _ => None,
264 }
265}
266
267fn fill_axis_hold(
270 fills: bool,
271 (max, size): (f32, f32),
272 (fraction, density): (f32, f32),
273 wrapped: Option<cranpose_ui_layout::AxisHold>,
274) -> Option<cranpose_ui_layout::AxisHold> {
275 use cranpose_ui_layout::{AxisHold, BoundRange};
276 if !fills {
277 return wrapped;
278 }
279 if max == f32::INFINITY {
280 let wrapped = wrapped?;
281 return Some(AxisHold {
282 min: wrapped.min,
283 max: wrapped.max.intersect(BoundRange::exactly(f32::INFINITY))?,
284 });
285 }
286 (size == cranpose_ui_layout::round_to_px(max * fraction, density)).then_some(AxisHold {
287 min: BoundRange::up_to(size),
288 max: BoundRange::exactly(max),
289 })
290}
291
292fn measure_pass_through(
293 measurable: &dyn Measurable,
294 constraints: Constraints,
295 offset: impl FnOnce(Size) -> (f32, f32),
296) -> cranpose_ui_layout::LayoutModifierMeasureResult {
297 let placeable = measurable.measure(constraints);
298 let size = Size {
299 width: placeable.width(),
300 height: placeable.height(),
301 };
302 let (x, y) = offset(size);
303 cranpose_ui_layout::LayoutModifierMeasureResult::new(size, x, y)
304}
305
306fn attach_draw_observer(
307 node_id_cell: &Cell<Option<NodeId>>,
308 context: &mut dyn ModifierNodeContext,
309) {
310 node_id_cell.set(context.node_id());
311 context.invalidate(InvalidationKind::Draw);
312}
313
314fn detach_draw_observer(node_id_cell: &Cell<Option<NodeId>>) {
315 if let Some(node_id) = node_id_cell.replace(None) {
316 crate::render_state::clear_draw_observations_for_node(node_id);
317 }
318}
319
320enum SizeAxis {
321 Width,
322 Height,
323}
324
325fn size_intrinsic(
326 target: Constraints,
327 axis: SizeAxis,
328 enforce_incoming: bool,
329 cross: f32,
330 intrinsic: impl FnOnce(f32) -> f32,
331) -> f32 {
332 let (target_min, target_max, cross_min, cross_max) = match axis {
333 SizeAxis::Width => (
334 target.min_width,
335 target.max_width,
336 target.min_height,
337 target.max_height,
338 ),
339 SizeAxis::Height => (
340 target.min_height,
341 target.max_height,
342 target.min_width,
343 target.max_width,
344 ),
345 };
346 if target_min == target_max && target_max != f32::INFINITY {
347 target_max
348 } else {
349 let child_cross = if enforce_incoming {
350 cross
351 } else {
352 cross.clamp(cross_min, cross_max)
353 };
354 intrinsic(child_cross).clamp(target_min, target_max)
355 }
356}
357
358#[derive(Debug)]
360pub struct PaddingNode {
361 padding: EdgeInsets,
362 state: NodeState,
363}
364
365impl PaddingNode {
366 pub fn new(padding: EdgeInsets) -> Self {
368 Self {
369 padding,
370 state: NodeState::new(),
371 }
372 }
373
374 pub fn padding(&self) -> EdgeInsets {
376 self.padding
377 }
378}
379
380impl DelegatableNode for PaddingNode {
381 fn node_state(&self) -> &NodeState {
382 &self.state
383 }
384}
385
386impl_layout_modifier_node!(PaddingNode, invalidate = InvalidationKind::Layout);
387
388impl LayoutModifierNode for PaddingNode {
389 fn measure_hold(
390 &self,
391 density: f32,
392 _constraints: Constraints,
393 size: Size,
394 wrapped: cranpose_ui_layout::WrappedHold,
395 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
396 let inner = wrapped.hold?;
397 let padding = device_padding(self.padding, density);
398 let horizontal = padding.horizontal_sum();
399 let vertical = padding.vertical_sum();
400 if size.width != wrapped.size.width + horizontal
402 || size.height != wrapped.size.height + vertical
403 {
404 return None;
405 }
406 cranpose_ui_layout::ConstraintsHold {
407 width: inner.width.outset(horizontal),
408 height: inner.height.outset(vertical),
409 }
410 .intersect(cranpose_ui_layout::ConstraintsHold::sized(
411 size.width,
412 size.height,
413 ))
414 }
415
416 fn measure(
417 &self,
418 context: &mut dyn ModifierNodeContext,
419 measurable: &dyn Measurable,
420 constraints: Constraints,
421 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
422 let padding = device_padding(self.padding, context.density());
423 let horizontal_padding = padding.horizontal_sum();
424 let vertical_padding = padding.vertical_sum();
425
426 let inner_constraints = Constraints {
427 min_width: (constraints.min_width - horizontal_padding).max(0.0),
428 max_width: (constraints.max_width - horizontal_padding).max(0.0),
429 min_height: (constraints.min_height - vertical_padding).max(0.0),
430 max_height: (constraints.max_height - vertical_padding).max(0.0),
431 };
432
433 let inner_placeable = measurable.measure(inner_constraints);
434 let inner_width = inner_placeable.width();
435 let inner_height = inner_placeable.height();
436
437 let (width, height) = constraints.constrain(
438 inner_width + horizontal_padding,
439 inner_height + vertical_padding,
440 );
441
442 cranpose_ui_layout::LayoutModifierMeasureResult::new(
443 Size { width, height },
444 padding.left,
445 padding.top,
446 )
447 }
448
449 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, density: f32) -> f32 {
450 let padding = device_padding(self.padding, density);
451 let inner_height = (height - padding.vertical_sum()).max(0.0);
452 measurable.min_intrinsic_width(inner_height) + padding.horizontal_sum()
453 }
454
455 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, density: f32) -> f32 {
456 let padding = device_padding(self.padding, density);
457 let inner_height = (height - padding.vertical_sum()).max(0.0);
458 measurable.max_intrinsic_width(inner_height) + padding.horizontal_sum()
459 }
460
461 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, density: f32) -> f32 {
462 let padding = device_padding(self.padding, density);
463 let inner_width = (width - padding.horizontal_sum()).max(0.0);
464 measurable.min_intrinsic_height(inner_width) + padding.vertical_sum()
465 }
466
467 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, density: f32) -> f32 {
468 let padding = device_padding(self.padding, density);
469 let inner_width = (width - padding.horizontal_sum()).max(0.0);
470 measurable.max_intrinsic_height(inner_width) + padding.vertical_sum()
471 }
472}
473
474pub(crate) fn device_padding(padding: EdgeInsets, density: f32) -> EdgeInsets {
477 use cranpose_ui_layout::round_to_px;
478 EdgeInsets {
479 left: round_to_px(padding.left, density),
480 top: round_to_px(padding.top, density),
481 right: round_to_px(padding.right, density),
482 bottom: round_to_px(padding.bottom, density),
483 }
484}
485
486#[derive(Debug, Clone, PartialEq)]
488pub struct PaddingElement {
489 padding: EdgeInsets,
490}
491
492impl PaddingElement {
493 pub fn new(padding: EdgeInsets) -> Self {
495 Self { padding }
496 }
497}
498
499impl Hash for PaddingElement {
500 fn hash<H: Hasher>(&self, state: &mut H) {
501 hash_f32_value(state, self.padding.left);
502 hash_f32_value(state, self.padding.top);
503 hash_f32_value(state, self.padding.right);
504 hash_f32_value(state, self.padding.bottom);
505 }
506}
507
508impl ModifierNodeElement for PaddingElement {
509 type Node = PaddingNode;
510
511 fn create(&self) -> Self::Node {
512 PaddingNode::new(self.padding)
513 }
514
515 fn update(&self, node: &mut Self::Node) {
516 if node.padding != self.padding {
517 node.padding = self.padding;
518 }
519 }
520
521 fn capabilities(&self) -> NodeCapabilities {
522 NodeCapabilities::LAYOUT
523 }
524}
525
526#[derive(Debug)]
528pub struct BackgroundNode {
529 color: Color,
530 shape: Option<RoundedCornerShape>,
531 state: NodeState,
532}
533
534impl BackgroundNode {
535 pub fn new(color: Color) -> Self {
537 Self {
538 color,
539 shape: None,
540 state: NodeState::new(),
541 }
542 }
543
544 pub fn color(&self) -> Color {
546 self.color
547 }
548
549 pub fn shape(&self) -> Option<RoundedCornerShape> {
551 self.shape
552 }
553}
554
555impl DelegatableNode for BackgroundNode {
556 fn node_state(&self) -> &NodeState {
557 &self.state
558 }
559}
560
561impl_draw_modifier_node!(BackgroundNode);
562
563#[derive(Debug, Clone, PartialEq)]
565pub struct BackgroundElement {
566 color: Color,
567}
568
569impl BackgroundElement {
570 pub fn new(color: Color) -> Self {
572 Self { color }
573 }
574}
575
576impl Hash for BackgroundElement {
577 fn hash<H: Hasher>(&self, state: &mut H) {
578 hash_f32_value(state, self.color.0);
579 hash_f32_value(state, self.color.1);
580 hash_f32_value(state, self.color.2);
581 hash_f32_value(state, self.color.3);
582 }
583}
584
585impl ModifierNodeElement for BackgroundElement {
586 type Node = BackgroundNode;
587
588 fn create(&self) -> Self::Node {
589 BackgroundNode::new(self.color)
590 }
591
592 fn update(&self, node: &mut Self::Node) {
593 if node.color != self.color {
594 node.color = self.color;
595 }
596 }
597
598 fn capabilities(&self) -> NodeCapabilities {
599 NodeCapabilities::DRAW
600 }
601}
602
603#[derive(Debug)]
605pub struct CornerShapeNode {
606 shape: RoundedCornerShape,
607 state: NodeState,
608}
609
610impl CornerShapeNode {
611 pub fn new(shape: RoundedCornerShape) -> Self {
613 Self {
614 shape,
615 state: NodeState::new(),
616 }
617 }
618
619 pub fn shape(&self) -> RoundedCornerShape {
621 self.shape
622 }
623}
624
625impl DelegatableNode for CornerShapeNode {
626 fn node_state(&self) -> &NodeState {
627 &self.state
628 }
629}
630
631impl_draw_modifier_node!(CornerShapeNode);
632
633#[derive(Debug, Clone, PartialEq)]
635pub struct CornerShapeElement {
636 shape: RoundedCornerShape,
637}
638
639impl CornerShapeElement {
640 pub fn new(shape: RoundedCornerShape) -> Self {
642 Self { shape }
643 }
644}
645
646impl Hash for CornerShapeElement {
647 fn hash<H: Hasher>(&self, state: &mut H) {
648 let radii = self.shape.radii();
649 hash_f32_value(state, radii.top_left);
650 hash_f32_value(state, radii.top_right);
651 hash_f32_value(state, radii.bottom_right);
652 hash_f32_value(state, radii.bottom_left);
653 }
654}
655
656impl ModifierNodeElement for CornerShapeElement {
657 type Node = CornerShapeNode;
658
659 fn create(&self) -> Self::Node {
660 CornerShapeNode::new(self.shape)
661 }
662
663 fn update(&self, node: &mut Self::Node) {
664 if node.shape != self.shape {
665 node.shape = self.shape;
666 }
667 }
668
669 fn capabilities(&self) -> NodeCapabilities {
670 NodeCapabilities::DRAW
671 }
672}
673
674#[derive(Debug, Clone, PartialEq)]
678pub struct BorderElement {
679 width: f32,
680 brush: Brush,
681 shape: RoundedCornerShape,
682}
683
684impl BorderElement {
685 pub fn new(width: f32, brush: Brush, shape: RoundedCornerShape) -> Self {
686 Self {
687 width,
688 brush,
689 shape,
690 }
691 }
692
693 pub(crate) fn command(&self) -> DrawCommand {
695 let Self {
696 width,
697 brush,
698 shape,
699 } = self.clone();
700 DrawCommand::Overlay(Rc::new(move |scope| {
701 let size = scope.size();
702 if width <= 0.0 || size.width <= 0.0 || size.height <= 0.0 {
703 return;
704 }
705 let radii = shape.resolve(size.width, size.height);
706 let half = width / 2.0;
707 if width * 2.0 >= size.width.min(size.height) {
708 scope.draw_round_rect(brush.clone(), radii);
710 return;
711 }
712 let inset = |radius: f32| (radius - half).max(0.0);
713 scope.draw_round_rect_at_stroked(
714 cranpose_ui_graphics::Rect {
715 x: half,
716 y: half,
717 width: size.width - width,
718 height: size.height - width,
719 },
720 brush.clone(),
721 cranpose_ui_graphics::CornerRadii {
722 top_left: inset(radii.top_left),
723 top_right: inset(radii.top_right),
724 bottom_right: inset(radii.bottom_right),
725 bottom_left: inset(radii.bottom_left),
726 },
727 cranpose_ui_graphics::Stroke::new(width),
728 );
729 }))
730 }
731}
732
733impl Hash for BorderElement {
734 fn hash<H: Hasher>(&self, state: &mut H) {
735 hash_f32_value(state, self.width);
736 std::mem::discriminant(&self.brush).hash(state);
737 if let Brush::Solid(color) = self.brush {
738 hash_f32_value(state, color.0);
739 hash_f32_value(state, color.1);
740 hash_f32_value(state, color.2);
741 hash_f32_value(state, color.3);
742 }
743 let radii = self.shape.radii();
744 hash_f32_value(state, radii.top_left);
745 hash_f32_value(state, radii.top_right);
746 hash_f32_value(state, radii.bottom_right);
747 hash_f32_value(state, radii.bottom_left);
748 }
749}
750
751impl ModifierNodeElement for BorderElement {
752 type Node = DrawCommandNode;
753
754 fn create(&self) -> Self::Node {
755 DrawCommandNode::new(vec![self.command()])
756 }
757
758 fn update(&self, node: &mut Self::Node) {
759 node.commands = vec![self.command()];
760 }
761
762 fn capabilities(&self) -> NodeCapabilities {
763 NodeCapabilities::DRAW
764 }
765}
766
767pub struct GraphicsLayerNode {
768 layer: GraphicsLayer,
769 layer_resolver: Option<Rc<dyn Fn() -> GraphicsLayer>>,
770 node_id: Rc<Cell<Option<NodeId>>>,
771 state: NodeState,
772}
773
774impl GraphicsLayerNode {
775 pub fn new(layer: GraphicsLayer) -> Self {
776 Self {
777 layer,
778 layer_resolver: None,
779 node_id: Rc::new(Cell::new(None)),
780 state: NodeState::new(),
781 }
782 }
783
784 pub fn new_lazy(layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) -> Self {
785 Self {
786 layer: GraphicsLayer::default(),
787 layer_resolver: Some(layer_resolver),
788 node_id: Rc::new(Cell::new(None)),
789 state: NodeState::new(),
790 }
791 }
792
793 #[cfg(test)]
794 pub fn layer(&self) -> GraphicsLayer {
795 if let Some(resolve) = self.layer_resolver(0) {
796 resolve()
797 } else {
798 self.layer.clone()
799 }
800 }
801
802 pub fn layer_snapshot(&self) -> GraphicsLayer {
803 self.layer.clone()
804 }
805
806 pub(crate) fn layer_resolver(
807 &self,
808 modifier_index: usize,
809 ) -> Option<Rc<dyn Fn() -> GraphicsLayer>> {
810 self.layer_resolver.as_ref().map(|resolve| {
811 let resolve = resolve.clone();
812 let node_id = Rc::clone(&self.node_id);
813 Rc::new(move || {
814 if let Some(node_id) = node_id.get() {
815 let scope = crate::render_state::DrawObservationScope::layer_properties(
816 node_id,
817 modifier_index,
818 );
819 crate::render_state::observe_draw_reads(scope, || resolve())
820 } else {
821 resolve()
822 }
823 }) as Rc<dyn Fn() -> GraphicsLayer>
824 })
825 }
826
827 fn set_static(&mut self, layer: GraphicsLayer) {
828 let changed = self.layer != layer || self.layer_resolver.is_some();
829 self.layer = layer;
830 self.layer_resolver = None;
831 if changed && let Some(node_id) = self.node_id.get() {
832 crate::render_state::schedule_draw_repass(node_id);
833 }
834 }
835
836 fn set_lazy(&mut self, layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) {
837 let changed = self
838 .layer_resolver
839 .as_ref()
840 .is_none_or(|current| !Rc::ptr_eq(current, &layer_resolver));
841 self.layer_resolver = Some(layer_resolver);
842 if changed && let Some(node_id) = self.node_id.get() {
843 crate::render_state::schedule_draw_repass(node_id);
844 }
845 }
846}
847
848impl DelegatableNode for GraphicsLayerNode {
849 fn node_state(&self) -> &NodeState {
850 &self.state
851 }
852}
853
854impl ModifierNode for GraphicsLayerNode {
855 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
856 attach_draw_observer(&self.node_id, context);
857 }
858
859 fn on_detach(&mut self) {
860 detach_draw_observer(&self.node_id);
861 }
862}
863
864impl std::fmt::Debug for GraphicsLayerNode {
865 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
866 f.debug_struct("GraphicsLayerNode")
867 .field("layer", &self.layer)
868 .field("lazy", &self.layer_resolver.is_some())
869 .finish()
870 }
871}
872
873#[derive(Debug, Clone, PartialEq)]
874pub struct GraphicsLayerElement {
875 layer: GraphicsLayer,
876}
877
878impl GraphicsLayerElement {
879 pub fn new(layer: GraphicsLayer) -> Self {
880 Self { layer }
881 }
882}
883
884impl Hash for GraphicsLayerElement {
885 fn hash<H: Hasher>(&self, state: &mut H) {
886 hash_graphics_layer(state, &self.layer);
887 }
888}
889
890impl ModifierNodeElement for GraphicsLayerElement {
891 type Node = GraphicsLayerNode;
892
893 fn create(&self) -> Self::Node {
894 GraphicsLayerNode::new(self.layer.clone())
895 }
896
897 fn update(&self, node: &mut Self::Node) {
898 node.set_static(self.layer.clone());
899 }
900
901 fn capabilities(&self) -> NodeCapabilities {
902 NodeCapabilities::DRAW
903 }
904}
905
906#[derive(Clone)]
907pub struct LazyGraphicsLayerElement {
908 layer_resolver: Rc<dyn Fn() -> GraphicsLayer>,
909}
910
911impl LazyGraphicsLayerElement {
912 pub fn new(layer_resolver: Rc<dyn Fn() -> GraphicsLayer>) -> Self {
913 Self { layer_resolver }
914 }
915}
916
917impl std::fmt::Debug for LazyGraphicsLayerElement {
918 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
919 f.debug_struct("LazyGraphicsLayerElement")
920 .field("resolver", &"<closure>")
921 .finish()
922 }
923}
924
925impl PartialEq for LazyGraphicsLayerElement {
926 fn eq(&self, other: &Self) -> bool {
927 Rc::ptr_eq(&self.layer_resolver, &other.layer_resolver)
928 }
929}
930
931impl Eq for LazyGraphicsLayerElement {}
932
933impl Hash for LazyGraphicsLayerElement {
934 fn hash<H: Hasher>(&self, state: &mut H) {
935 let ptr = Rc::as_ptr(&self.layer_resolver) as *const ();
936 ptr.hash(state);
937 }
938}
939
940impl ModifierNodeElement for LazyGraphicsLayerElement {
941 type Node = GraphicsLayerNode;
942
943 fn create(&self) -> Self::Node {
944 GraphicsLayerNode::new_lazy(self.layer_resolver.clone())
945 }
946
947 fn update(&self, node: &mut Self::Node) {
948 node.set_lazy(self.layer_resolver.clone());
949 }
950
951 fn capabilities(&self) -> NodeCapabilities {
952 NodeCapabilities::DRAW
953 }
954
955 fn always_update(&self) -> bool {
956 true
957 }
958
959 fn auto_invalidate_on_update(&self) -> bool {
960 false
961 }
962}
963
964#[derive(Debug)]
968pub struct SizeNode {
969 min_width: Option<f32>,
970 max_width: Option<f32>,
971 min_height: Option<f32>,
972 max_height: Option<f32>,
973 enforce_incoming: bool,
974 rounded: Cell<Option<(f32, Constraints)>>,
977 state: NodeState,
978}
979
980impl SizeNode {
981 pub fn new(
985 min_width: Option<f32>,
986 max_width: Option<f32>,
987 min_height: Option<f32>,
988 max_height: Option<f32>,
989 enforce_incoming: bool,
990 ) -> Self {
991 Self {
992 min_width,
993 max_width,
994 min_height,
995 max_height,
996 enforce_incoming,
997 rounded: Cell::new(None),
998 state: NodeState::new(),
999 }
1000 }
1001
1002 fn target_constraints(&self, density: f32) -> Constraints {
1005 if let Some((rounded_for, target)) = self.rounded.get()
1006 && rounded_for == density
1007 {
1008 return target;
1009 }
1010 let target = self.round_targets(density);
1011 self.rounded.set(Some((density, target)));
1012 target
1013 }
1014
1015 fn round_targets(&self, density: f32) -> Constraints {
1016 use cranpose_ui_layout::round_to_px;
1017
1018 let max_width = self
1019 .max_width
1020 .map_or(f32::INFINITY, |v| round_to_px(v, density).max(0.0));
1021 let max_height = self
1022 .max_height
1023 .map_or(f32::INFINITY, |v| round_to_px(v, density).max(0.0));
1024
1025 let min_width = self.min_width.map_or(0.0, |v| {
1026 let clamped = round_to_px(v, density).clamp(0.0, max_width);
1027 if clamped == f32::INFINITY {
1028 0.0
1029 } else {
1030 clamped
1031 }
1032 });
1033
1034 let min_height = self.min_height.map_or(0.0, |v| {
1035 let clamped = round_to_px(v, density).clamp(0.0, max_height);
1036 if clamped == f32::INFINITY {
1037 0.0
1038 } else {
1039 clamped
1040 }
1041 });
1042
1043 Constraints {
1044 min_width,
1045 max_width,
1046 min_height,
1047 max_height,
1048 }
1049 }
1050
1051 pub fn min_width(&self) -> Option<f32> {
1053 self.min_width
1054 }
1055
1056 pub fn max_width(&self) -> Option<f32> {
1058 self.max_width
1059 }
1060
1061 pub fn min_height(&self) -> Option<f32> {
1063 self.min_height
1064 }
1065
1066 pub fn max_height(&self) -> Option<f32> {
1068 self.max_height
1069 }
1070
1071 pub fn enforce_incoming(&self) -> bool {
1073 self.enforce_incoming
1074 }
1075}
1076
1077impl DelegatableNode for SizeNode {
1078 fn node_state(&self) -> &NodeState {
1079 &self.state
1080 }
1081}
1082
1083impl_layout_modifier_node!(SizeNode, invalidate = InvalidationKind::Layout);
1084
1085impl LayoutModifierNode for SizeNode {
1086 fn measure_hold(
1087 &self,
1088 density: f32,
1089 _constraints: Constraints,
1090 size: Size,
1091 wrapped: cranpose_ui_layout::WrappedHold,
1092 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
1093 if !self.enforce_incoming {
1094 return None;
1095 }
1096 let target = self.target_constraints(density);
1097 Some(cranpose_ui_layout::ConstraintsHold {
1098 width: size_axis_hold(
1099 (self.min_width, self.max_width),
1100 target.min_width,
1101 size.width,
1102 wrapped.hold.map(|hold| hold.width),
1103 )?,
1104 height: size_axis_hold(
1105 (self.min_height, self.max_height),
1106 target.min_height,
1107 size.height,
1108 wrapped.hold.map(|hold| hold.height),
1109 )?,
1110 })
1111 }
1112
1113 fn measure(
1114 &self,
1115 context: &mut dyn ModifierNodeContext,
1116 measurable: &dyn Measurable,
1117 constraints: Constraints,
1118 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1119 let target = self.target_constraints(context.density());
1120
1121 let wrapped_constraints = if self.enforce_incoming {
1122 Constraints {
1123 min_width: target
1124 .min_width
1125 .max(constraints.min_width)
1126 .min(constraints.max_width),
1127 max_width: target
1128 .max_width
1129 .min(constraints.max_width)
1130 .max(constraints.min_width),
1131 min_height: target
1132 .min_height
1133 .max(constraints.min_height)
1134 .min(constraints.max_height),
1135 max_height: target
1136 .max_height
1137 .min(constraints.max_height)
1138 .max(constraints.min_height),
1139 }
1140 } else {
1141 let resolved_min_width = if self.min_width.is_some() {
1142 target.min_width
1143 } else {
1144 constraints.min_width.min(target.max_width)
1145 };
1146 let resolved_max_width = if self.max_width.is_some() {
1147 target.max_width
1148 } else {
1149 constraints.max_width.max(target.min_width)
1150 };
1151 let resolved_min_height = if self.min_height.is_some() {
1152 target.min_height
1153 } else {
1154 constraints.min_height.min(target.max_height)
1155 };
1156 let resolved_max_height = if self.max_height.is_some() {
1157 target.max_height
1158 } else {
1159 constraints.max_height.max(target.min_height)
1160 };
1161
1162 Constraints {
1163 min_width: resolved_min_width,
1164 max_width: resolved_max_width,
1165 min_height: resolved_min_height,
1166 max_height: resolved_max_height,
1167 }
1168 };
1169
1170 let placeable = measurable.measure(wrapped_constraints);
1171 let measured_width = placeable.width();
1172 let measured_height = placeable.height();
1173
1174 let result_width = if self.min_width.is_some()
1175 && self.max_width.is_some()
1176 && self.min_width == self.max_width
1177 && target.min_width >= wrapped_constraints.min_width
1178 && target.min_width <= wrapped_constraints.max_width
1179 {
1180 target.min_width
1181 } else {
1182 measured_width
1183 };
1184
1185 let result_height = if self.min_height.is_some()
1186 && self.max_height.is_some()
1187 && self.min_height == self.max_height
1188 && target.min_height >= wrapped_constraints.min_height
1189 && target.min_height <= wrapped_constraints.max_height
1190 {
1191 target.min_height
1192 } else {
1193 measured_height
1194 };
1195
1196 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
1197 width: result_width,
1198 height: result_height,
1199 })
1200 }
1201
1202 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, density: f32) -> f32 {
1203 size_intrinsic(
1204 self.target_constraints(density),
1205 SizeAxis::Width,
1206 self.enforce_incoming,
1207 height,
1208 |h| measurable.min_intrinsic_width(h),
1209 )
1210 }
1211
1212 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, density: f32) -> f32 {
1213 size_intrinsic(
1214 self.target_constraints(density),
1215 SizeAxis::Width,
1216 self.enforce_incoming,
1217 height,
1218 |h| measurable.max_intrinsic_width(h),
1219 )
1220 }
1221
1222 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, density: f32) -> f32 {
1223 size_intrinsic(
1224 self.target_constraints(density),
1225 SizeAxis::Height,
1226 self.enforce_incoming,
1227 width,
1228 |w| measurable.min_intrinsic_height(w),
1229 )
1230 }
1231
1232 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, density: f32) -> f32 {
1233 size_intrinsic(
1234 self.target_constraints(density),
1235 SizeAxis::Height,
1236 self.enforce_incoming,
1237 width,
1238 |w| measurable.max_intrinsic_height(w),
1239 )
1240 }
1241}
1242
1243#[derive(Debug, Clone, PartialEq)]
1247pub struct SizeElement {
1248 min_width: Option<f32>,
1249 max_width: Option<f32>,
1250 min_height: Option<f32>,
1251 max_height: Option<f32>,
1252 enforce_incoming: bool,
1253}
1254
1255impl SizeElement {
1256 pub fn new(width: Option<f32>, height: Option<f32>) -> Self {
1259 Self {
1260 min_width: width,
1261 max_width: width,
1262 min_height: height,
1263 max_height: height,
1264 enforce_incoming: true,
1265 }
1266 }
1267
1268 pub fn with_constraints(
1272 min_width: Option<f32>,
1273 max_width: Option<f32>,
1274 min_height: Option<f32>,
1275 max_height: Option<f32>,
1276 enforce_incoming: bool,
1277 ) -> Self {
1278 Self {
1279 min_width,
1280 max_width,
1281 min_height,
1282 max_height,
1283 enforce_incoming,
1284 }
1285 }
1286}
1287
1288impl Hash for SizeElement {
1289 fn hash<H: Hasher>(&self, state: &mut H) {
1290 hash_option_f32(state, self.min_width);
1291 hash_option_f32(state, self.max_width);
1292 hash_option_f32(state, self.min_height);
1293 hash_option_f32(state, self.max_height);
1294 self.enforce_incoming.hash(state);
1295 }
1296}
1297
1298impl ModifierNodeElement for SizeElement {
1299 type Node = SizeNode;
1300
1301 fn create(&self) -> Self::Node {
1302 SizeNode::new(
1303 self.min_width,
1304 self.max_width,
1305 self.min_height,
1306 self.max_height,
1307 self.enforce_incoming,
1308 )
1309 }
1310
1311 fn update(&self, node: &mut Self::Node) {
1312 if node.min_width != self.min_width
1313 || node.max_width != self.max_width
1314 || node.min_height != self.min_height
1315 || node.max_height != self.max_height
1316 || node.enforce_incoming != self.enforce_incoming
1317 {
1318 node.min_width = self.min_width;
1319 node.max_width = self.max_width;
1320 node.min_height = self.min_height;
1321 node.max_height = self.max_height;
1322 node.enforce_incoming = self.enforce_incoming;
1323 node.rounded.set(None);
1324 }
1325 }
1326
1327 fn capabilities(&self) -> NodeCapabilities {
1328 NodeCapabilities::LAYOUT
1329 }
1330
1331 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
1332 Some(InvalidationKind::Layout)
1333 }
1334}
1335
1336use cranpose_foundation::DRAG_THRESHOLD;
1337
1338#[derive(Clone, Copy, Debug)]
1340enum ClickPress {
1341 Idle,
1342 Pressed(Point, cranpose_foundation::PointerId),
1344 Cancelled,
1347}
1348
1349pub struct ClickableNode {
1354 on_press: Option<Rc<dyn Fn(Point)>>,
1355 on_click: Rc<dyn Fn(Point)>,
1356 state: NodeState,
1357 press_position: Rc<Cell<ClickPress>>,
1358 cached_handler: Rc<dyn Fn(PointerEvent)>,
1359}
1360
1361impl std::fmt::Debug for ClickableNode {
1362 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1363 f.debug_struct("ClickableNode").finish()
1364 }
1365}
1366
1367impl ClickableNode {
1368 pub fn new(on_click: impl Fn(Point) + 'static) -> Self {
1370 Self::with_handler(Rc::new(on_click))
1371 }
1372
1373 pub fn with_handler(on_click: Rc<dyn Fn(Point)>) -> Self {
1375 Self::with_handlers(None, on_click)
1376 }
1377
1378 pub fn with_handlers(on_press: Option<Rc<dyn Fn(Point)>>, on_click: Rc<dyn Fn(Point)>) -> Self {
1381 let press_position = Rc::new(Cell::new(ClickPress::Idle));
1382 let cached_handler =
1383 Self::create_handler(on_press.clone(), on_click.clone(), press_position.clone());
1384 Self {
1385 on_press,
1386 on_click,
1387 state: NodeState::new(),
1388 press_position,
1389 cached_handler,
1390 }
1391 }
1392
1393 fn create_handler(
1394 on_press: Option<Rc<dyn Fn(Point)>>,
1395 on_click: Rc<dyn Fn(Point)>,
1396 press_position: Rc<Cell<ClickPress>>,
1397 ) -> Rc<dyn Fn(PointerEvent)> {
1398 Rc::new(move |event: PointerEvent| {
1399 if matches!(press_position.get(), ClickPress::Pressed(_, holder) if holder != event.id)
1400 {
1401 return;
1402 }
1403
1404 if event.is_consumed() {
1405 press_position.set(ClickPress::Idle);
1406 return;
1407 }
1408
1409 match event.kind {
1410 PointerEventKind::Down => {
1411 press_position.set(ClickPress::Pressed(event.travelled_to(), event.id));
1412 if let Some(on_press) = on_press.as_ref() {
1413 on_press(event.position);
1414 }
1415 }
1416 PointerEventKind::Move => {}
1417 PointerEventKind::Up => {
1418 let should_click = match press_position.replace(ClickPress::Idle) {
1419 ClickPress::Pressed(press_pos, _) => {
1420 let travelled_to = event.travelled_to();
1421 let dx = travelled_to.x - press_pos.x;
1422 let dy = travelled_to.y - press_pos.y;
1423 let distance = (dx * dx + dy * dy).sqrt();
1424 distance <= DRAG_THRESHOLD
1425 }
1426 ClickPress::Idle => event.id == 0,
1430 ClickPress::Cancelled => false,
1431 };
1432
1433 if should_click {
1434 on_click(Point {
1435 x: event.position.x,
1436 y: event.position.y,
1437 });
1438 event.consume();
1439 }
1440 }
1441 PointerEventKind::Cancel => {
1442 press_position.set(ClickPress::Cancelled);
1443 }
1444 _ => {}
1445 }
1446 })
1447 }
1448
1449 pub fn handler(&self) -> Rc<dyn Fn(Point)> {
1451 self.on_click.clone()
1452 }
1453}
1454
1455impl DelegatableNode for ClickableNode {
1456 fn node_state(&self) -> &NodeState {
1457 &self.state
1458 }
1459}
1460
1461impl ModifierNode for ClickableNode {
1462 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1463 context.invalidate(cranpose_foundation::InvalidationKind::PointerInput);
1464 }
1465
1466 fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
1467 Some(self)
1468 }
1469
1470 fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
1471 Some(self)
1472 }
1473}
1474
1475impl PointerInputNode for ClickableNode {
1476 fn on_pointer_event(
1477 &mut self,
1478 _context: &mut dyn ModifierNodeContext,
1479 event: &PointerEvent,
1480 ) -> bool {
1481 (self.cached_handler)(event.clone());
1482 event.is_consumed()
1483 }
1484
1485 fn hit_test(&self, _x: f32, _y: f32) -> bool {
1486 true
1487 }
1488
1489 fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
1490 Some(self.cached_handler.clone())
1491 }
1492}
1493
1494#[derive(Clone)]
1496pub struct ClickableElement {
1497 on_press: Option<Rc<dyn Fn(Point)>>,
1498 on_click: Rc<dyn Fn(Point)>,
1499}
1500
1501impl ClickableElement {
1502 pub fn new(on_click: impl Fn(Point) + 'static) -> Self {
1504 Self {
1505 on_press: None,
1506 on_click: Rc::new(on_click),
1507 }
1508 }
1509
1510 pub fn with_handler(on_click: Rc<dyn Fn(Point)>) -> Self {
1512 Self {
1513 on_press: None,
1514 on_click,
1515 }
1516 }
1517
1518 pub fn with_handlers(on_press: Rc<dyn Fn(Point)>, on_click: Rc<dyn Fn(Point)>) -> Self {
1521 Self {
1522 on_press: Some(on_press),
1523 on_click,
1524 }
1525 }
1526}
1527
1528impl std::fmt::Debug for ClickableElement {
1529 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1530 f.debug_struct("ClickableElement").finish()
1531 }
1532}
1533
1534impl PartialEq for ClickableElement {
1535 fn eq(&self, _other: &Self) -> bool {
1536 true
1537 }
1538}
1539
1540impl Eq for ClickableElement {}
1541
1542impl Hash for ClickableElement {
1543 fn hash<H: Hasher>(&self, state: &mut H) {
1544 "clickable".hash(state);
1545 }
1546}
1547
1548impl ModifierNodeElement for ClickableElement {
1549 type Node = ClickableNode;
1550
1551 fn create(&self) -> Self::Node {
1552 ClickableNode::with_handlers(self.on_press.clone(), self.on_click.clone())
1553 }
1554
1555 fn update(&self, node: &mut Self::Node) {
1556 node.on_press.clone_from(&self.on_press);
1557 node.on_click.clone_from(&self.on_click);
1558 node.cached_handler = ClickableNode::create_handler(
1559 node.on_press.clone(),
1560 node.on_click.clone(),
1561 node.press_position.clone(),
1562 );
1563 }
1564
1565 fn capabilities(&self) -> NodeCapabilities {
1566 NodeCapabilities::POINTER_INPUT
1567 }
1568
1569 fn always_update(&self) -> bool {
1570 true
1571 }
1572}
1573
1574#[derive(Debug)]
1580pub struct PointerIconNode {
1581 icon: PointerIcon,
1582 state: NodeState,
1583}
1584
1585impl PointerIconNode {
1586 pub fn new(icon: PointerIcon) -> Self {
1588 Self {
1589 icon,
1590 state: NodeState::new(),
1591 }
1592 }
1593
1594 pub fn icon(&self) -> &PointerIcon {
1596 &self.icon
1597 }
1598}
1599
1600impl DelegatableNode for PointerIconNode {
1601 fn node_state(&self) -> &NodeState {
1602 &self.state
1603 }
1604}
1605
1606impl ModifierNode for PointerIconNode {
1607 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
1608 context.invalidate(cranpose_foundation::InvalidationKind::PointerInput);
1609 }
1610
1611 fn as_pointer_input_node(&self) -> Option<&dyn PointerInputNode> {
1612 Some(self)
1613 }
1614
1615 fn as_pointer_input_node_mut(&mut self) -> Option<&mut dyn PointerInputNode> {
1616 Some(self)
1617 }
1618}
1619
1620impl PointerInputNode for PointerIconNode {
1621 fn on_pointer_event(
1622 &mut self,
1623 _context: &mut dyn ModifierNodeContext,
1624 _event: &PointerEvent,
1625 ) -> bool {
1626 false
1627 }
1628
1629 fn hit_test(&self, _x: f32, _y: f32) -> bool {
1630 true
1631 }
1632
1633 fn pointer_input_handler(&self) -> Option<Rc<dyn Fn(PointerEvent)>> {
1634 None
1635 }
1636}
1637
1638#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1640pub struct PointerIconElement {
1641 icon: PointerIcon,
1642}
1643
1644impl PointerIconElement {
1645 pub fn new(icon: PointerIcon) -> Self {
1647 Self { icon }
1648 }
1649}
1650
1651impl ModifierNodeElement for PointerIconElement {
1652 type Node = PointerIconNode;
1653
1654 fn create(&self) -> Self::Node {
1655 PointerIconNode::new(self.icon.clone())
1656 }
1657
1658 fn update(&self, node: &mut Self::Node) {
1659 node.icon = self.icon.clone();
1660 }
1661
1662 fn capabilities(&self) -> NodeCapabilities {
1663 NodeCapabilities::POINTER_INPUT
1664 }
1665}
1666
1667#[derive(Debug)]
1669pub struct AlphaNode {
1670 alpha: f32,
1671 state: NodeState,
1672}
1673
1674impl AlphaNode {
1675 pub fn new(alpha: f32) -> Self {
1677 Self {
1678 alpha: alpha.clamp(0.0, 1.0),
1679 state: NodeState::new(),
1680 }
1681 }
1682}
1683
1684impl DelegatableNode for AlphaNode {
1685 fn node_state(&self) -> &NodeState {
1686 &self.state
1687 }
1688}
1689
1690impl_draw_modifier_node!(AlphaNode);
1691
1692#[derive(Debug, Clone, PartialEq)]
1694pub struct AlphaElement {
1695 alpha: f32,
1696}
1697
1698impl AlphaElement {
1699 pub fn new(alpha: f32) -> Self {
1702 Self {
1703 alpha: alpha.clamp(0.0, 1.0),
1704 }
1705 }
1706}
1707
1708impl Hash for AlphaElement {
1709 fn hash<H: Hasher>(&self, state: &mut H) {
1710 hash_f32_value(state, self.alpha);
1711 }
1712}
1713
1714impl ModifierNodeElement for AlphaElement {
1715 type Node = AlphaNode;
1716
1717 fn create(&self) -> Self::Node {
1718 AlphaNode::new(self.alpha)
1719 }
1720
1721 fn update(&self, node: &mut Self::Node) {
1722 let new_alpha = self.alpha.clamp(0.0, 1.0);
1723 if (node.alpha - new_alpha).abs() > f32::EPSILON {
1724 node.alpha = new_alpha;
1725 }
1726 }
1727
1728 fn capabilities(&self) -> NodeCapabilities {
1729 NodeCapabilities::DRAW
1730 }
1731}
1732
1733#[derive(Debug)]
1734pub struct ClipToBoundsNode {
1735 state: NodeState,
1736}
1737
1738impl ClipToBoundsNode {
1739 pub fn new() -> Self {
1740 Self {
1741 state: NodeState::new(),
1742 }
1743 }
1744}
1745
1746impl DelegatableNode for ClipToBoundsNode {
1747 fn node_state(&self) -> &NodeState {
1748 &self.state
1749 }
1750}
1751
1752impl_draw_modifier_node!(ClipToBoundsNode);
1753
1754#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1755pub struct ClipToBoundsElement;
1756
1757impl ClipToBoundsElement {
1758 pub fn new() -> Self {
1759 Self
1760 }
1761}
1762
1763impl ModifierNodeElement for ClipToBoundsElement {
1764 type Node = ClipToBoundsNode;
1765
1766 fn create(&self) -> Self::Node {
1767 ClipToBoundsNode::new()
1768 }
1769
1770 fn update(&self, _node: &mut Self::Node) {}
1771
1772 fn capabilities(&self) -> NodeCapabilities {
1773 NodeCapabilities::DRAW
1774 }
1775}
1776
1777pub trait WindowRectSink {
1778 fn set(&self, coordinates: cranpose_ui_graphics::WindowCoordinates);
1779}
1780
1781impl WindowRectSink for Cell<cranpose_ui_graphics::Rect> {
1782 fn set(&self, coordinates: cranpose_ui_graphics::WindowCoordinates) {
1783 Cell::set(self, coordinates.bounds());
1784 }
1785}
1786
1787impl WindowRectSink for Cell<cranpose_ui_graphics::WindowCoordinates> {
1788 fn set(&self, coordinates: cranpose_ui_graphics::WindowCoordinates) {
1789 Cell::set(self, coordinates);
1790 }
1791}
1792
1793struct StateWindowRectSink(cranpose_core::MutableState<cranpose_ui_graphics::Rect>);
1794
1795impl WindowRectSink for StateWindowRectSink {
1796 fn set(&self, coordinates: cranpose_ui_graphics::WindowCoordinates) {
1797 self.0.set(coordinates.bounds());
1798 }
1799}
1800
1801pub struct WindowRectReporterNode {
1802 sink: Rc<dyn WindowRectSink>,
1803 state: NodeState,
1804}
1805
1806impl WindowRectReporterNode {
1807 pub(crate) fn new(sink: Rc<dyn WindowRectSink>) -> Self {
1808 Self {
1809 sink,
1810 state: NodeState::new(),
1811 }
1812 }
1813
1814 pub(crate) fn window_rect_sink(&self) -> Rc<dyn WindowRectSink> {
1815 self.sink.clone()
1816 }
1817}
1818
1819impl DelegatableNode for WindowRectReporterNode {
1820 fn node_state(&self) -> &NodeState {
1821 &self.state
1822 }
1823}
1824
1825impl_layout_modifier_node!(WindowRectReporterNode);
1826
1827impl LayoutModifierNode for WindowRectReporterNode {
1828 fn measure(
1829 &self,
1830 _context: &mut dyn ModifierNodeContext,
1831 measurable: &dyn Measurable,
1832 constraints: Constraints,
1833 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1834 measure_pass_through(measurable, constraints, |_| (0.0, 0.0))
1835 }
1836}
1837
1838#[derive(Clone)]
1839pub struct WindowRectReporterElement {
1840 sink: Rc<dyn WindowRectSink>,
1841}
1842
1843impl WindowRectReporterElement {
1844 pub fn new(sink: Rc<Cell<cranpose_ui_graphics::Rect>>) -> Self {
1845 Self { sink }
1846 }
1847
1848 pub fn from_coordinates(sink: Rc<Cell<cranpose_ui_graphics::WindowCoordinates>>) -> Self {
1850 Self { sink }
1851 }
1852
1853 pub fn from_state(sink: cranpose_core::MutableState<cranpose_ui_graphics::Rect>) -> Self {
1854 Self {
1855 sink: Rc::new(StateWindowRectSink(sink)),
1856 }
1857 }
1858}
1859
1860impl std::fmt::Debug for WindowRectReporterElement {
1861 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1862 f.debug_struct("WindowRectReporterElement").finish()
1863 }
1864}
1865
1866impl PartialEq for WindowRectReporterElement {
1867 fn eq(&self, other: &Self) -> bool {
1868 Rc::ptr_eq(&self.sink, &other.sink)
1869 }
1870}
1871
1872impl Eq for WindowRectReporterElement {}
1873
1874impl Hash for WindowRectReporterElement {
1875 fn hash<H: Hasher>(&self, state: &mut H) {
1876 std::ptr::hash(Rc::as_ptr(&self.sink).cast::<()>(), state);
1877 }
1878}
1879
1880impl_sink_reporter_element!(WindowRectReporterElement, WindowRectReporterNode);
1881
1882pub(crate) struct SelectableTextNode {
1883 sink: Rc<crate::selection_container::SelectableGeometry>,
1884 state: NodeState,
1885}
1886
1887impl SelectableTextNode {
1888 pub(crate) fn new(sink: Rc<crate::selection_container::SelectableGeometry>) -> Self {
1889 Self {
1890 sink,
1891 state: NodeState::new(),
1892 }
1893 }
1894
1895 pub(crate) fn geometry(&self) -> &crate::selection_container::SelectableGeometry {
1896 &self.sink
1897 }
1898}
1899
1900impl DelegatableNode for SelectableTextNode {
1901 fn node_state(&self) -> &NodeState {
1902 &self.state
1903 }
1904}
1905
1906impl_layout_modifier_node!(SelectableTextNode);
1907
1908impl LayoutModifierNode for SelectableTextNode {
1909 fn measure(
1910 &self,
1911 _context: &mut dyn ModifierNodeContext,
1912 measurable: &dyn Measurable,
1913 constraints: Constraints,
1914 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
1915 measure_pass_through(measurable, constraints, |_| (0.0, 0.0))
1916 }
1917}
1918
1919#[derive(Clone)]
1920pub(crate) struct SelectableTextElement {
1921 sink: Rc<crate::selection_container::SelectableGeometry>,
1922}
1923
1924impl SelectableTextElement {
1925 pub(crate) fn new(sink: Rc<crate::selection_container::SelectableGeometry>) -> Self {
1926 Self { sink }
1927 }
1928}
1929
1930impl std::fmt::Debug for SelectableTextElement {
1931 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1932 f.debug_struct("SelectableTextElement").finish()
1933 }
1934}
1935
1936impl PartialEq for SelectableTextElement {
1937 fn eq(&self, other: &Self) -> bool {
1938 Rc::ptr_eq(&self.sink, &other.sink)
1939 }
1940}
1941
1942impl Eq for SelectableTextElement {}
1943
1944impl Hash for SelectableTextElement {
1945 fn hash<H: Hasher>(&self, state: &mut H) {
1946 std::ptr::hash(Rc::as_ptr(&self.sink), state);
1947 }
1948}
1949
1950impl_sink_reporter_element!(SelectableTextElement, SelectableTextNode);
1951
1952pub trait SizeSink {
1953 fn set(&self, size: Size);
1954}
1955
1956impl SizeSink for Cell<Size> {
1957 fn set(&self, size: Size) {
1958 Cell::set(self, size);
1959 }
1960}
1961
1962struct StateSizeSink(cranpose_core::MutableState<Size>);
1963
1964impl SizeSink for StateSizeSink {
1965 fn set(&self, size: Size) {
1966 self.0.set(size);
1967 }
1968}
1969
1970pub struct SizeReporterNode {
1971 sink: Rc<dyn SizeSink>,
1972 state: NodeState,
1973 #[cfg(debug_assertions)]
1974 oscillation: Cell<(Size, Size, u32)>,
1975}
1976
1977impl SizeReporterNode {
1978 pub fn new(sink: Rc<dyn SizeSink>) -> Self {
1979 Self {
1980 sink,
1981 state: NodeState::new(),
1982 #[cfg(debug_assertions)]
1983 oscillation: Cell::new((Size::default(), Size::default(), 0)),
1984 }
1985 }
1986
1987 #[cfg(debug_assertions)]
1988 fn check_oscillation(&self, size: Size) {
1989 const ALTERNATION_CEILING: u32 = 64;
1990 let (last, second_last, count) = self.oscillation.get();
1991 let count = if size == second_last && size != last {
1992 count + 1
1993 } else if size == last {
1994 count
1995 } else {
1996 0
1997 };
1998 assert!(
1999 count <= ALTERNATION_CEILING,
2000 "size-reactive feedback loop: this node's measured size has \
2001 alternated between {last:?} and {size:?} for {count} passes — \
2002 its content's size depends on the size it reports (the \
2003 onSizeChanged self-reference hazard). Break the cycle by making \
2004 the reported size feed only content that does not change this \
2005 node's own measured size."
2006 );
2007 self.oscillation.set((size, last, count));
2008 }
2009}
2010
2011impl DelegatableNode for SizeReporterNode {
2012 fn node_state(&self) -> &NodeState {
2013 &self.state
2014 }
2015}
2016
2017impl_layout_modifier_node!(SizeReporterNode);
2018
2019impl LayoutModifierNode for SizeReporterNode {
2020 fn measure(
2021 &self,
2022 _context: &mut dyn ModifierNodeContext,
2023 measurable: &dyn Measurable,
2024 constraints: Constraints,
2025 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2026 measure_pass_through(measurable, constraints, |size| {
2027 #[cfg(debug_assertions)]
2028 self.check_oscillation(size);
2029 self.sink.set(size);
2030 (0.0, 0.0)
2031 })
2032 }
2033}
2034
2035#[derive(Clone)]
2036pub struct SizeReporterElement {
2037 sink: Rc<dyn SizeSink>,
2038}
2039
2040impl SizeReporterElement {
2041 pub fn new(sink: Rc<Cell<Size>>) -> Self {
2042 Self { sink }
2043 }
2044
2045 pub fn from_state(sink: cranpose_core::MutableState<Size>) -> Self {
2046 Self {
2047 sink: Rc::new(StateSizeSink(sink)),
2048 }
2049 }
2050}
2051
2052impl std::fmt::Debug for SizeReporterElement {
2053 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2054 f.debug_struct("SizeReporterElement").finish()
2055 }
2056}
2057
2058impl PartialEq for SizeReporterElement {
2059 fn eq(&self, other: &Self) -> bool {
2060 std::ptr::addr_eq(Rc::as_ptr(&self.sink), Rc::as_ptr(&other.sink))
2061 }
2062}
2063
2064impl Hash for SizeReporterElement {
2065 fn hash<H: Hasher>(&self, state: &mut H) {
2066 (Rc::as_ptr(&self.sink) as *const () as usize).hash(state);
2067 }
2068}
2069
2070impl_sink_reporter_element!(SizeReporterElement, SizeReporterNode);
2071
2072pub struct DrawCommandNode {
2073 commands: Vec<DrawCommand>,
2074 node_id: Cell<Option<NodeId>>,
2075 state: NodeState,
2076}
2077
2078impl DrawCommandNode {
2079 pub fn new(commands: Vec<DrawCommand>) -> Self {
2080 Self {
2081 commands,
2082 node_id: Cell::new(None),
2083 state: NodeState::new(),
2084 }
2085 }
2086
2087 #[cfg(test)]
2088 pub fn commands(&self) -> &[DrawCommand] {
2089 &self.commands
2090 }
2091
2092 pub(crate) fn observed_commands(
2093 &self,
2094 modifier_index: usize,
2095 ) -> impl Iterator<Item = DrawCommand> + '_ {
2096 let node_id = self.node_id.get();
2097 self.commands
2098 .iter()
2099 .cloned()
2100 .enumerate()
2101 .map(move |(index, command)| {
2102 observe_draw_command(command, node_id, modifier_index, index)
2103 })
2104 }
2105}
2106
2107impl DelegatableNode for DrawCommandNode {
2108 fn node_state(&self) -> &NodeState {
2109 &self.state
2110 }
2111}
2112
2113impl ModifierNode for DrawCommandNode {
2114 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2115 attach_draw_observer(&self.node_id, context);
2116 }
2117
2118 fn on_detach(&mut self) {
2119 detach_draw_observer(&self.node_id);
2120 }
2121
2122 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
2123 Some(self)
2124 }
2125
2126 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
2127 Some(self)
2128 }
2129}
2130
2131impl DrawModifierNode for DrawCommandNode {}
2132
2133fn observe_draw_command(
2134 command: DrawCommand,
2135 node_id: Option<NodeId>,
2136 modifier_index: usize,
2137 command_index: usize,
2138) -> DrawCommand {
2139 let Some(node_id) = node_id else {
2140 return command;
2141 };
2142 let observation =
2143 crate::render_state::DrawObservationScope::new(node_id, modifier_index, command_index);
2144 match command {
2145 DrawCommand::Behind(draw) => DrawCommand::Behind(Rc::new(move |scope| {
2146 crate::render_state::observe_draw_reads(observation, || draw(scope));
2147 })),
2148 DrawCommand::WithContent(draw) => DrawCommand::WithContent(Rc::new(move |scope| {
2149 crate::render_state::observe_draw_reads(observation, || draw(scope));
2150 })),
2151 DrawCommand::Overlay(draw) => DrawCommand::Overlay(Rc::new(move |scope| {
2152 crate::render_state::observe_draw_reads(observation, || draw(scope));
2153 })),
2154 }
2155}
2156
2157fn draw_command_tag(cmd: &DrawCommand) -> u8 {
2158 match cmd {
2159 DrawCommand::Behind(_) => 0,
2160 DrawCommand::WithContent(_) => 1,
2161 DrawCommand::Overlay(_) => 2,
2162 }
2163}
2164
2165fn draw_command_closure_identity(cmd: &DrawCommand) -> *const () {
2166 match cmd {
2167 DrawCommand::Behind(f) | DrawCommand::WithContent(f) | DrawCommand::Overlay(f) => {
2168 Rc::as_ptr(f) as *const ()
2169 }
2170 }
2171}
2172
2173#[derive(Clone)]
2174pub struct DrawCommandElement {
2175 commands: Vec<DrawCommand>,
2176}
2177
2178impl DrawCommandElement {
2179 pub fn new(command: DrawCommand) -> Self {
2180 Self {
2181 commands: vec![command],
2182 }
2183 }
2184
2185 pub fn from_commands(commands: Vec<DrawCommand>) -> Self {
2186 Self { commands }
2187 }
2188}
2189
2190impl std::fmt::Debug for DrawCommandElement {
2191 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2192 f.debug_struct("DrawCommandElement")
2193 .field("commands", &self.commands.len())
2194 .finish()
2195 }
2196}
2197
2198impl PartialEq for DrawCommandElement {
2199 fn eq(&self, other: &Self) -> bool {
2200 if self.commands.len() != other.commands.len() {
2201 return false;
2202 }
2203 self.commands
2204 .iter()
2205 .zip(other.commands.iter())
2206 .all(|(a, b)| {
2207 draw_command_tag(a) == draw_command_tag(b)
2208 && draw_command_closure_identity(a) == draw_command_closure_identity(b)
2209 })
2210 }
2211}
2212
2213impl Eq for DrawCommandElement {}
2214
2215impl std::hash::Hash for DrawCommandElement {
2216 fn hash<H: Hasher>(&self, state: &mut H) {
2217 "draw_commands".hash(state);
2218 self.commands.len().hash(state);
2219 for command in &self.commands {
2220 draw_command_tag(command).hash(state);
2221 (draw_command_closure_identity(command) as usize).hash(state);
2222 }
2223 }
2224}
2225
2226impl ModifierNodeElement for DrawCommandElement {
2227 type Node = DrawCommandNode;
2228
2229 fn create(&self) -> Self::Node {
2230 DrawCommandNode::new(self.commands.clone())
2231 }
2232
2233 fn update(&self, node: &mut Self::Node) {
2234 node.commands.clone_from(&self.commands);
2235 }
2236
2237 fn capabilities(&self) -> NodeCapabilities {
2238 NodeCapabilities::DRAW
2239 }
2240}
2241
2242#[derive(Debug)]
2246pub struct OffsetNode {
2247 x: f32,
2248 y: f32,
2249 rtl_aware: bool,
2250 layout_direction: crate::LayoutDirection,
2251 state: NodeState,
2252}
2253
2254impl OffsetNode {
2255 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
2258 Self {
2259 x,
2260 y,
2261 rtl_aware,
2262 layout_direction: crate::LayoutDirection::Ltr,
2263 state: NodeState::new(),
2264 }
2265 }
2266
2267 pub fn offset(&self) -> Point {
2270 Point {
2271 x: if self.rtl_aware && self.layout_direction.is_rtl() {
2272 -self.x
2273 } else {
2274 self.x
2275 },
2276 y: self.y,
2277 }
2278 }
2279
2280 pub fn device_offset(&self, density: f32) -> Point {
2283 use cranpose_ui_layout::round_to_px;
2284 let offset = self.offset();
2285 Point {
2286 x: round_to_px(offset.x, density),
2287 y: round_to_px(offset.y, density),
2288 }
2289 }
2290
2291 pub fn rtl_aware(&self) -> bool {
2293 self.rtl_aware
2294 }
2295
2296 pub(crate) fn set_layout_direction(&mut self, direction: crate::LayoutDirection) {
2297 self.layout_direction = direction;
2298 }
2299}
2300
2301impl DelegatableNode for OffsetNode {
2302 fn node_state(&self) -> &NodeState {
2303 &self.state
2304 }
2305}
2306
2307impl_layout_modifier_node!(OffsetNode, invalidate = InvalidationKind::Layout);
2308
2309impl LayoutModifierNode for OffsetNode {
2310 fn measure_hold(
2311 &self,
2312 _density: f32,
2313 _constraints: Constraints,
2314 _size: Size,
2315 wrapped: cranpose_ui_layout::WrappedHold,
2316 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
2317 wrapped.hold
2318 }
2319
2320 fn measure(
2321 &self,
2322 context: &mut dyn ModifierNodeContext,
2323 measurable: &dyn Measurable,
2324 constraints: Constraints,
2325 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2326 let offset = self.device_offset(context.density());
2327 measure_pass_through(measurable, constraints, |_| (offset.x, offset.y))
2328 }
2329}
2330
2331#[derive(Debug, Clone, PartialEq)]
2335pub struct OffsetElement {
2336 x: f32,
2337 y: f32,
2338 rtl_aware: bool,
2339}
2340
2341impl OffsetElement {
2342 pub fn new(x: f32, y: f32, rtl_aware: bool) -> Self {
2345 Self { x, y, rtl_aware }
2346 }
2347}
2348
2349impl Hash for OffsetElement {
2350 fn hash<H: Hasher>(&self, state: &mut H) {
2351 hash_f32_value(state, self.x);
2352 hash_f32_value(state, self.y);
2353 self.rtl_aware.hash(state);
2354 }
2355}
2356
2357impl ModifierNodeElement for OffsetElement {
2358 type Node = OffsetNode;
2359
2360 fn create(&self) -> Self::Node {
2361 OffsetNode::new(self.x, self.y, self.rtl_aware)
2362 }
2363
2364 fn update(&self, node: &mut Self::Node) {
2365 if node.x != self.x || node.y != self.y || node.rtl_aware != self.rtl_aware {
2366 node.x = self.x;
2367 node.y = self.y;
2368 node.rtl_aware = self.rtl_aware;
2369 }
2370 }
2371
2372 fn capabilities(&self) -> NodeCapabilities {
2373 NodeCapabilities::LAYOUT
2374 }
2375
2376 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
2377 Some(InvalidationKind::Layout)
2378 }
2379}
2380
2381#[derive(Debug)]
2389pub struct FractionalOffsetNode {
2390 x_fraction: f32,
2391 y_fraction: f32,
2392 state: NodeState,
2393}
2394
2395impl FractionalOffsetNode {
2396 pub fn new(x_fraction: f32, y_fraction: f32) -> Self {
2399 Self {
2400 x_fraction,
2401 y_fraction,
2402 state: NodeState::new(),
2403 }
2404 }
2405
2406 pub fn fractions(&self) -> Point {
2409 Point {
2410 x: self.x_fraction,
2411 y: self.y_fraction,
2412 }
2413 }
2414}
2415
2416impl DelegatableNode for FractionalOffsetNode {
2417 fn node_state(&self) -> &NodeState {
2418 &self.state
2419 }
2420}
2421
2422impl_layout_modifier_node!(FractionalOffsetNode, invalidate = InvalidationKind::Layout);
2423
2424impl LayoutModifierNode for FractionalOffsetNode {
2425 fn measure_hold(
2426 &self,
2427 _density: f32,
2428 _constraints: Constraints,
2429 _size: Size,
2430 wrapped: cranpose_ui_layout::WrappedHold,
2431 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
2432 wrapped.hold
2433 }
2434
2435 fn measure(
2436 &self,
2437 _context: &mut dyn ModifierNodeContext,
2438 measurable: &dyn Measurable,
2439 constraints: Constraints,
2440 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2441 measure_pass_through(measurable, constraints, |size| {
2442 (self.x_fraction * size.width, self.y_fraction * size.height)
2443 })
2444 }
2445}
2446
2447#[derive(Debug, Clone, PartialEq)]
2449pub struct FractionalOffsetElement {
2450 x_fraction: f32,
2451 y_fraction: f32,
2452}
2453
2454impl FractionalOffsetElement {
2455 pub fn new(x_fraction: f32, y_fraction: f32) -> Self {
2458 Self {
2459 x_fraction,
2460 y_fraction,
2461 }
2462 }
2463}
2464
2465impl Hash for FractionalOffsetElement {
2466 fn hash<H: Hasher>(&self, state: &mut H) {
2467 "fractional_offset".hash(state);
2468 hash_f32_value(state, self.x_fraction);
2469 hash_f32_value(state, self.y_fraction);
2470 }
2471}
2472
2473impl ModifierNodeElement for FractionalOffsetElement {
2474 type Node = FractionalOffsetNode;
2475
2476 fn create(&self) -> Self::Node {
2477 FractionalOffsetNode::new(self.x_fraction, self.y_fraction)
2478 }
2479
2480 fn update(&self, node: &mut Self::Node) {
2481 if node.x_fraction != self.x_fraction || node.y_fraction != self.y_fraction {
2482 node.x_fraction = self.x_fraction;
2483 node.y_fraction = self.y_fraction;
2484 }
2485 }
2486
2487 fn capabilities(&self) -> NodeCapabilities {
2488 NodeCapabilities::LAYOUT
2489 }
2490
2491 fn update_invalidation_kind(&self) -> Option<InvalidationKind> {
2492 Some(InvalidationKind::Layout)
2493 }
2494}
2495
2496#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2498pub enum FillDirection {
2499 Horizontal,
2501 Vertical,
2503 Both,
2505}
2506
2507#[derive(Debug)]
2511pub struct FillNode {
2512 direction: FillDirection,
2513 fraction: f32,
2514 state: NodeState,
2515}
2516
2517impl FillNode {
2518 pub fn new(direction: FillDirection, fraction: f32) -> Self {
2520 Self {
2521 direction,
2522 fraction,
2523 state: NodeState::new(),
2524 }
2525 }
2526
2527 pub fn direction(&self) -> FillDirection {
2529 self.direction
2530 }
2531
2532 pub fn fraction(&self) -> f32 {
2534 self.fraction
2535 }
2536
2537 fn filled(&self, extent: f32, other: FillDirection, density: f32) -> f32 {
2540 if self.direction == other || !extent.is_finite() {
2541 return extent;
2542 }
2543 cranpose_ui_layout::round_to_px(extent * self.fraction, density)
2544 }
2545}
2546
2547impl DelegatableNode for FillNode {
2548 fn node_state(&self) -> &NodeState {
2549 &self.state
2550 }
2551}
2552
2553impl_layout_modifier_node!(FillNode, invalidate = InvalidationKind::Layout);
2554
2555impl LayoutModifierNode for FillNode {
2556 fn measure_hold(
2560 &self,
2561 density: f32,
2562 constraints: Constraints,
2563 size: Size,
2564 wrapped: cranpose_ui_layout::WrappedHold,
2565 ) -> Option<cranpose_ui_layout::ConstraintsHold> {
2566 Some(cranpose_ui_layout::ConstraintsHold {
2567 width: fill_axis_hold(
2568 self.direction != FillDirection::Vertical,
2569 (constraints.max_width, size.width),
2570 (self.fraction, density),
2571 wrapped.hold.map(|hold| hold.width),
2572 )?,
2573 height: fill_axis_hold(
2574 self.direction != FillDirection::Horizontal,
2575 (constraints.max_height, size.height),
2576 (self.fraction, density),
2577 wrapped.hold.map(|hold| hold.height),
2578 )?,
2579 })
2580 }
2581
2582 fn measure(
2583 &self,
2584 context: &mut dyn ModifierNodeContext,
2585 measurable: &dyn Measurable,
2586 constraints: Constraints,
2587 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
2588 let density = context.density();
2589 let (fill_width, child_min_width, child_max_width) = if self.direction
2590 != FillDirection::Vertical
2591 && constraints.max_width != f32::INFINITY
2592 {
2593 let width =
2594 cranpose_ui_layout::round_to_px(constraints.max_width * self.fraction, density)
2595 .clamp(constraints.min_width, constraints.max_width);
2596 (width, width, width)
2597 } else {
2598 (
2599 constraints.max_width,
2600 constraints.min_width,
2601 constraints.max_width,
2602 )
2603 };
2604
2605 let (fill_height, child_min_height, child_max_height) = if self.direction
2606 != FillDirection::Horizontal
2607 && constraints.max_height != f32::INFINITY
2608 {
2609 let height =
2610 cranpose_ui_layout::round_to_px(constraints.max_height * self.fraction, density)
2611 .clamp(constraints.min_height, constraints.max_height);
2612 (height, height, height)
2613 } else {
2614 (
2615 constraints.max_height,
2616 constraints.min_height,
2617 constraints.max_height,
2618 )
2619 };
2620
2621 let fill_constraints = Constraints {
2622 min_width: child_min_width,
2623 max_width: child_max_width,
2624 min_height: child_min_height,
2625 max_height: child_max_height,
2626 };
2627
2628 let placeable = measurable.measure(fill_constraints);
2629
2630 let result_width = if self.direction != FillDirection::Vertical
2631 && constraints.max_width != f32::INFINITY
2632 {
2633 fill_width
2634 } else {
2635 placeable.width()
2636 };
2637
2638 let result_height = if self.direction != FillDirection::Horizontal
2639 && constraints.max_height != f32::INFINITY
2640 {
2641 fill_height
2642 } else {
2643 placeable.height()
2644 };
2645
2646 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
2647 width: result_width,
2648 height: result_height,
2649 })
2650 }
2651
2652 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, density: f32) -> f32 {
2653 measurable.min_intrinsic_width(self.filled(height, FillDirection::Horizontal, density))
2654 }
2655
2656 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, density: f32) -> f32 {
2657 measurable.max_intrinsic_width(self.filled(height, FillDirection::Horizontal, density))
2658 }
2659
2660 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, density: f32) -> f32 {
2661 measurable.min_intrinsic_height(self.filled(width, FillDirection::Vertical, density))
2662 }
2663
2664 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, density: f32) -> f32 {
2665 measurable.max_intrinsic_height(self.filled(width, FillDirection::Vertical, density))
2666 }
2667}
2668
2669#[derive(Debug, Clone, PartialEq)]
2673pub struct FillElement {
2674 direction: FillDirection,
2675 fraction: f32,
2676}
2677
2678impl FillElement {
2679 pub fn width(fraction: f32) -> Self {
2681 Self {
2682 direction: FillDirection::Horizontal,
2683 fraction,
2684 }
2685 }
2686
2687 pub fn height(fraction: f32) -> Self {
2689 Self {
2690 direction: FillDirection::Vertical,
2691 fraction,
2692 }
2693 }
2694
2695 pub fn size(fraction: f32) -> Self {
2697 Self {
2698 direction: FillDirection::Both,
2699 fraction,
2700 }
2701 }
2702}
2703
2704impl Hash for FillElement {
2705 fn hash<H: Hasher>(&self, state: &mut H) {
2706 self.direction.hash(state);
2707 hash_f32_value(state, self.fraction);
2708 }
2709}
2710
2711impl ModifierNodeElement for FillElement {
2712 type Node = FillNode;
2713
2714 fn create(&self) -> Self::Node {
2715 FillNode::new(self.direction, self.fraction)
2716 }
2717
2718 fn update(&self, node: &mut Self::Node) {
2719 if node.direction != self.direction || node.fraction != self.fraction {
2720 node.direction = self.direction;
2721 node.fraction = self.fraction;
2722 }
2723 }
2724
2725 fn capabilities(&self) -> NodeCapabilities {
2726 NodeCapabilities::LAYOUT
2727 }
2728}
2729
2730#[derive(Debug)]
2731pub struct WeightNode {
2732 weight: f32,
2733 fill: bool,
2734 state: NodeState,
2735}
2736
2737impl WeightNode {
2738 pub fn new(weight: f32, fill: bool) -> Self {
2739 Self {
2740 weight,
2741 fill,
2742 state: NodeState::new(),
2743 }
2744 }
2745
2746 pub fn layout_weight(&self) -> LayoutWeight {
2747 LayoutWeight {
2748 weight: self.weight,
2749 fill: self.fill,
2750 }
2751 }
2752}
2753
2754impl DelegatableNode for WeightNode {
2755 fn node_state(&self) -> &NodeState {
2756 &self.state
2757 }
2758}
2759
2760impl ModifierNode for WeightNode {
2761 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2762 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2763 }
2764}
2765
2766#[derive(Debug, Clone, PartialEq)]
2767pub struct WeightElement {
2768 weight: f32,
2769 fill: bool,
2770}
2771
2772impl WeightElement {
2773 pub fn new(weight: f32, fill: bool) -> Self {
2774 Self { weight, fill }
2775 }
2776}
2777
2778impl Hash for WeightElement {
2779 fn hash<H: Hasher>(&self, state: &mut H) {
2780 hash_f32_value(state, self.weight);
2781 self.fill.hash(state);
2782 }
2783}
2784
2785impl ModifierNodeElement for WeightElement {
2786 type Node = WeightNode;
2787
2788 fn create(&self) -> Self::Node {
2789 WeightNode::new(self.weight, self.fill)
2790 }
2791
2792 fn update(&self, node: &mut Self::Node) {
2793 if node.weight != self.weight || node.fill != self.fill {
2794 node.weight = self.weight;
2795 node.fill = self.fill;
2796 }
2797 }
2798
2799 fn capabilities(&self) -> NodeCapabilities {
2800 NodeCapabilities::LAYOUT
2801 }
2802}
2803
2804#[derive(Debug)]
2805pub struct AlignmentNode {
2806 box_alignment: Option<Alignment>,
2807 column_alignment: Option<HorizontalAlignment>,
2808 row_alignment: Option<VerticalAlignment>,
2809 row_baseline: bool,
2810 state: NodeState,
2811}
2812
2813impl AlignmentNode {
2814 pub fn new(
2815 box_alignment: Option<Alignment>,
2816 column_alignment: Option<HorizontalAlignment>,
2817 row_alignment: Option<VerticalAlignment>,
2818 row_baseline: bool,
2819 ) -> Self {
2820 Self {
2821 box_alignment,
2822 column_alignment,
2823 row_alignment,
2824 row_baseline,
2825 state: NodeState::new(),
2826 }
2827 }
2828
2829 pub fn box_alignment(&self) -> Option<Alignment> {
2830 self.box_alignment
2831 }
2832
2833 pub fn column_alignment(&self) -> Option<HorizontalAlignment> {
2834 self.column_alignment
2835 }
2836
2837 pub fn row_alignment(&self) -> Option<VerticalAlignment> {
2838 self.row_alignment
2839 }
2840
2841 pub fn row_baseline(&self) -> bool {
2843 self.row_baseline
2844 }
2845}
2846
2847impl DelegatableNode for AlignmentNode {
2848 fn node_state(&self) -> &NodeState {
2849 &self.state
2850 }
2851}
2852
2853impl ModifierNode for AlignmentNode {
2854 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
2855 context.invalidate(cranpose_foundation::InvalidationKind::Layout);
2856 }
2857}
2858
2859#[derive(Debug, Clone, PartialEq)]
2860pub struct AlignmentElement {
2861 box_alignment: Option<Alignment>,
2862 column_alignment: Option<HorizontalAlignment>,
2863 row_alignment: Option<VerticalAlignment>,
2864 row_baseline: bool,
2865}
2866
2867impl AlignmentElement {
2868 pub fn box_alignment(alignment: Alignment) -> Self {
2869 Self {
2870 box_alignment: Some(alignment),
2871 column_alignment: None,
2872 row_alignment: None,
2873 row_baseline: false,
2874 }
2875 }
2876
2877 pub fn column_alignment(alignment: HorizontalAlignment) -> Self {
2878 Self {
2879 box_alignment: None,
2880 column_alignment: Some(alignment),
2881 row_alignment: None,
2882 row_baseline: false,
2883 }
2884 }
2885
2886 pub fn row_alignment(alignment: VerticalAlignment) -> Self {
2887 Self {
2888 box_alignment: None,
2889 column_alignment: None,
2890 row_alignment: Some(alignment),
2891 row_baseline: false,
2892 }
2893 }
2894
2895 pub fn row_baseline() -> Self {
2897 Self {
2898 box_alignment: None,
2899 column_alignment: None,
2900 row_alignment: None,
2901 row_baseline: true,
2902 }
2903 }
2904}
2905
2906impl Hash for AlignmentElement {
2907 fn hash<H: Hasher>(&self, state: &mut H) {
2908 if let Some(alignment) = self.box_alignment {
2909 state.write_u8(1);
2910 hash_alignment(state, alignment);
2911 } else {
2912 state.write_u8(0);
2913 }
2914 if let Some(alignment) = self.column_alignment {
2915 state.write_u8(1);
2916 hash_horizontal_alignment(state, alignment);
2917 } else {
2918 state.write_u8(0);
2919 }
2920 if let Some(alignment) = self.row_alignment {
2921 state.write_u8(1);
2922 hash_vertical_alignment(state, alignment);
2923 } else {
2924 state.write_u8(0);
2925 }
2926 self.row_baseline.hash(state);
2927 }
2928}
2929
2930impl ModifierNodeElement for AlignmentElement {
2931 type Node = AlignmentNode;
2932
2933 fn create(&self) -> Self::Node {
2934 AlignmentNode::new(
2935 self.box_alignment,
2936 self.column_alignment,
2937 self.row_alignment,
2938 self.row_baseline,
2939 )
2940 }
2941
2942 fn update(&self, node: &mut Self::Node) {
2943 if node.box_alignment != self.box_alignment {
2944 node.box_alignment = self.box_alignment;
2945 }
2946 if node.column_alignment != self.column_alignment {
2947 node.column_alignment = self.column_alignment;
2948 }
2949 if node.row_alignment != self.row_alignment {
2950 node.row_alignment = self.row_alignment;
2951 }
2952 node.row_baseline = self.row_baseline;
2953 }
2954
2955 fn capabilities(&self) -> NodeCapabilities {
2956 NodeCapabilities::LAYOUT
2957 }
2958}
2959
2960#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
2961pub enum IntrinsicAxis {
2962 Width,
2963 Height,
2964}
2965
2966#[derive(Debug)]
2967pub struct IntrinsicSizeNode {
2968 axis: IntrinsicAxis,
2969 size: IntrinsicSize,
2970 state: NodeState,
2971}
2972
2973impl IntrinsicSizeNode {
2974 pub fn new(axis: IntrinsicAxis, size: IntrinsicSize) -> Self {
2975 Self {
2976 axis,
2977 size,
2978 state: NodeState::new(),
2979 }
2980 }
2981
2982 fn content_extent(&self, measurable: &dyn Measurable, constraints: Constraints) -> f32 {
2986 let extent = match (self.axis, self.size) {
2987 (IntrinsicAxis::Width, IntrinsicSize::Min) => {
2988 measurable.min_intrinsic_width(constraints.max_height)
2989 }
2990 (IntrinsicAxis::Width, IntrinsicSize::Max) => {
2991 measurable.max_intrinsic_width(constraints.max_height)
2992 }
2993 (IntrinsicAxis::Height, IntrinsicSize::Min) => {
2994 measurable.min_intrinsic_height(constraints.max_width)
2995 }
2996 (IntrinsicAxis::Height, IntrinsicSize::Max) => {
2997 measurable.max_intrinsic_height(constraints.max_width)
2998 }
2999 };
3000 extent.max(0.0)
3001 }
3002
3003 fn intrinsic_width(&self, measurable: &dyn Measurable, height: f32) -> Option<f32> {
3006 (self.axis == IntrinsicAxis::Width).then(|| match self.size {
3007 IntrinsicSize::Min => measurable.min_intrinsic_width(height),
3008 IntrinsicSize::Max => measurable.max_intrinsic_width(height),
3009 })
3010 }
3011
3012 fn intrinsic_height(&self, measurable: &dyn Measurable, width: f32) -> Option<f32> {
3013 (self.axis == IntrinsicAxis::Height).then(|| match self.size {
3014 IntrinsicSize::Min => measurable.min_intrinsic_height(width),
3015 IntrinsicSize::Max => measurable.max_intrinsic_height(width),
3016 })
3017 }
3018}
3019
3020impl DelegatableNode for IntrinsicSizeNode {
3021 fn node_state(&self) -> &NodeState {
3022 &self.state
3023 }
3024}
3025
3026impl_layout_modifier_node!(IntrinsicSizeNode, invalidate = InvalidationKind::Layout);
3027
3028impl LayoutModifierNode for IntrinsicSizeNode {
3033 fn measure(
3034 &self,
3035 _context: &mut dyn ModifierNodeContext,
3036 measurable: &dyn Measurable,
3037 constraints: Constraints,
3038 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
3039 let extent = self.content_extent(measurable, constraints);
3040 let content = match self.axis {
3041 IntrinsicAxis::Width => {
3042 let width = extent.max(constraints.min_width).min(constraints.max_width);
3043 Constraints {
3044 min_width: width,
3045 max_width: width,
3046 ..constraints
3047 }
3048 }
3049 IntrinsicAxis::Height => {
3050 let height = extent
3051 .max(constraints.min_height)
3052 .min(constraints.max_height);
3053 Constraints {
3054 min_height: height,
3055 max_height: height,
3056 ..constraints
3057 }
3058 }
3059 };
3060 let placeable = measurable.measure(content);
3061 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size {
3062 width: placeable.width(),
3063 height: placeable.height(),
3064 })
3065 }
3066
3067 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, _density: f32) -> f32 {
3068 self.intrinsic_width(measurable, height)
3069 .unwrap_or_else(|| measurable.min_intrinsic_width(height))
3070 }
3071
3072 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, _density: f32) -> f32 {
3073 self.intrinsic_width(measurable, height)
3074 .unwrap_or_else(|| measurable.max_intrinsic_width(height))
3075 }
3076
3077 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, _density: f32) -> f32 {
3078 self.intrinsic_height(measurable, width)
3079 .unwrap_or_else(|| measurable.min_intrinsic_height(width))
3080 }
3081
3082 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, _density: f32) -> f32 {
3083 self.intrinsic_height(measurable, width)
3084 .unwrap_or_else(|| measurable.max_intrinsic_height(width))
3085 }
3086}
3087
3088#[derive(Debug, Clone, PartialEq)]
3089pub struct IntrinsicSizeElement {
3090 axis: IntrinsicAxis,
3091 size: IntrinsicSize,
3092}
3093
3094impl IntrinsicSizeElement {
3095 pub fn width(size: IntrinsicSize) -> Self {
3096 Self {
3097 axis: IntrinsicAxis::Width,
3098 size,
3099 }
3100 }
3101
3102 pub fn height(size: IntrinsicSize) -> Self {
3103 Self {
3104 axis: IntrinsicAxis::Height,
3105 size,
3106 }
3107 }
3108}
3109
3110impl Hash for IntrinsicSizeElement {
3111 fn hash<H: Hasher>(&self, state: &mut H) {
3112 state.write_u8(match self.axis {
3113 IntrinsicAxis::Width => 0,
3114 IntrinsicAxis::Height => 1,
3115 });
3116 state.write_u8(match self.size {
3117 IntrinsicSize::Min => 0,
3118 IntrinsicSize::Max => 1,
3119 });
3120 }
3121}
3122
3123impl ModifierNodeElement for IntrinsicSizeElement {
3124 type Node = IntrinsicSizeNode;
3125
3126 fn create(&self) -> Self::Node {
3127 IntrinsicSizeNode::new(self.axis, self.size)
3128 }
3129
3130 fn update(&self, node: &mut Self::Node) {
3131 if node.axis != self.axis {
3132 node.axis = self.axis;
3133 }
3134 if node.size != self.size {
3135 node.size = self.size;
3136 }
3137 }
3138
3139 fn capabilities(&self) -> NodeCapabilities {
3140 NodeCapabilities::LAYOUT
3141 }
3142}
3143
3144#[cfg(test)]
3145#[path = "tests/modifier_nodes_tests.rs"]
3146mod tests;