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