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