cranpose_ui/modifier/
minimum_interactive.rs1use cranpose_foundation::{
5 Constraints, DelegatableNode, InvalidationKind, LayoutModifierNode, Measurable, ModifierNode,
6 ModifierNodeContext, ModifierNodeElement, NodeCapabilities, NodeState, Size,
7};
8use cranpose_ui_layout::{
9 HorizontalAlignment, LayoutModifierMeasureResult, VerticalAlignment, round_to_px,
10};
11
12use super::Modifier;
13
14pub const MINIMUM_INTERACTIVE_SIZE: f32 = 48.0;
17
18pub fn minimum_interactive_placement(
22 constraints: Constraints,
23 content: Size,
24 density: f32,
25) -> LayoutModifierMeasureResult {
26 let minimum = round_to_px(MINIMUM_INTERACTIVE_SIZE, density);
27 let width = content.width.max(
28 minimum
29 .max(constraints.min_width)
30 .min(constraints.max_width),
31 );
32 let height = content.height.max(
33 minimum
34 .max(constraints.min_height)
35 .min(constraints.max_height),
36 );
37 LayoutModifierMeasureResult::new(
38 Size { width, height },
39 HorizontalAlignment::CenterHorizontally.align(width, content.width, density),
40 VerticalAlignment::CenterVertically.align(height, content.height, density),
41 )
42}
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
46pub struct MinimumInteractiveElement;
47
48pub struct MinimumInteractiveNode {
50 state: NodeState,
51}
52
53impl DelegatableNode for MinimumInteractiveNode {
54 fn node_state(&self) -> &NodeState {
55 &self.state
56 }
57}
58
59crate::modifier_nodes::impl_layout_modifier_node!(
60 MinimumInteractiveNode,
61 invalidate = InvalidationKind::Layout
62);
63
64impl LayoutModifierNode for MinimumInteractiveNode {
65 fn measure(
66 &self,
67 context: &mut dyn ModifierNodeContext,
68 measurable: &dyn Measurable,
69 constraints: Constraints,
70 ) -> LayoutModifierMeasureResult {
71 let placeable = measurable.measure(constraints);
72 minimum_interactive_placement(
73 constraints,
74 Size {
75 width: placeable.width(),
76 height: placeable.height(),
77 },
78 context.density(),
79 )
80 }
81
82 fn min_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, density: f32) -> f32 {
83 measurable
84 .min_intrinsic_width(height)
85 .max(round_to_px(MINIMUM_INTERACTIVE_SIZE, density))
86 }
87
88 fn max_intrinsic_width(&self, measurable: &dyn Measurable, height: f32, density: f32) -> f32 {
89 measurable
90 .max_intrinsic_width(height)
91 .max(round_to_px(MINIMUM_INTERACTIVE_SIZE, density))
92 }
93
94 fn min_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, density: f32) -> f32 {
95 measurable
96 .min_intrinsic_height(width)
97 .max(round_to_px(MINIMUM_INTERACTIVE_SIZE, density))
98 }
99
100 fn max_intrinsic_height(&self, measurable: &dyn Measurable, width: f32, density: f32) -> f32 {
101 measurable
102 .max_intrinsic_height(width)
103 .max(round_to_px(MINIMUM_INTERACTIVE_SIZE, density))
104 }
105}
106
107impl ModifierNodeElement for MinimumInteractiveElement {
108 type Node = MinimumInteractiveNode;
109
110 fn create(&self) -> Self::Node {
111 MinimumInteractiveNode {
112 state: NodeState::new(),
113 }
114 }
115
116 fn update(&self, _node: &mut Self::Node) {}
117
118 fn capabilities(&self) -> NodeCapabilities {
119 NodeCapabilities::LAYOUT
120 }
121}
122
123impl Modifier {
124 pub fn minimum_interactive_component_size(self) -> Self {
130 self.then(Self::with_element(MinimumInteractiveElement))
131 }
132}
133
134#[cfg(test)]
135#[path = "tests/minimum_interactive_tests.rs"]
136mod tests;