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