cranpose_ui/
text_modifier_node.rs1use cranpose_foundation::{
23 Constraints, DelegatableNode, DrawModifierNode, DrawScope, InvalidationKind,
24 LayoutModifierNode, Measurable, MeasurementProxy, ModifierNode, ModifierNodeContext,
25 ModifierNodeElement, NodeCapabilities, NodeState, SemanticsConfiguration, SemanticsNode, Size,
26};
27use std::hash::{Hash, Hasher};
28
29#[derive(Debug)]
39pub struct TextModifierNode {
40 text: String,
41 state: NodeState,
42}
43
44impl TextModifierNode {
45 pub fn new(text: String) -> Self {
46 Self {
47 text,
48 state: NodeState::new(),
49 }
50 }
51
52 pub fn text(&self) -> &str {
53 &self.text
54 }
55
56 fn measure_text_content(&self) -> Size {
58 let metrics = crate::text::measure_text(&self.text);
59 Size {
60 width: metrics.width,
61 height: metrics.height,
62 }
63 }
64}
65
66impl DelegatableNode for TextModifierNode {
67 fn node_state(&self) -> &NodeState {
68 &self.state
69 }
70}
71
72impl ModifierNode for TextModifierNode {
73 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
74 context.invalidate(InvalidationKind::Layout);
76 context.invalidate(InvalidationKind::Draw);
77 context.invalidate(InvalidationKind::Semantics);
78 }
79
80 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
81 Some(self)
82 }
83
84 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
85 Some(self)
86 }
87
88 fn as_semantics_node(&self) -> Option<&dyn SemanticsNode> {
89 Some(self)
90 }
91
92 fn as_semantics_node_mut(&mut self) -> Option<&mut dyn SemanticsNode> {
93 Some(self)
94 }
95
96 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
97 Some(self)
98 }
99
100 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
101 Some(self)
102 }
103}
104
105impl LayoutModifierNode for TextModifierNode {
106 fn measure(
107 &self,
108 _context: &mut dyn ModifierNodeContext,
109 _measurable: &dyn Measurable,
110 constraints: Constraints,
111 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
112 let text_size = self.measure_text_content();
114
115 let width = text_size
117 .width
118 .clamp(constraints.min_width, constraints.max_width);
119 let height = text_size
120 .height
121 .clamp(constraints.min_height, constraints.max_height);
122
123 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size { width, height })
127 }
128
129 fn min_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
130 self.measure_text_content().width
131 }
132
133 fn max_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
134 self.measure_text_content().width
135 }
136
137 fn min_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
138 self.measure_text_content().height
139 }
140
141 fn max_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
142 self.measure_text_content().height
143 }
144
145 fn create_measurement_proxy(&self) -> Option<Box<dyn MeasurementProxy>> {
146 Some(Box::new(TextMeasurementProxy {
147 text: self.text.clone(),
148 }))
149 }
150}
151
152struct TextMeasurementProxy {
157 text: String,
158}
159
160impl TextMeasurementProxy {
161 fn measure_text_content(&self) -> Size {
164 let metrics = crate::text::measure_text(&self.text);
165 Size {
166 width: metrics.width,
167 height: metrics.height,
168 }
169 }
170}
171
172impl MeasurementProxy for TextMeasurementProxy {
173 fn measure_proxy(
174 &self,
175 _context: &mut dyn ModifierNodeContext,
176 _measurable: &dyn Measurable,
177 constraints: Constraints,
178 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
179 let text_size = self.measure_text_content();
181
182 let width = text_size
184 .width
185 .clamp(constraints.min_width, constraints.max_width);
186 let height = text_size
187 .height
188 .clamp(constraints.min_height, constraints.max_height);
189
190 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size { width, height })
192 }
193
194 fn min_intrinsic_width_proxy(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
195 self.measure_text_content().width
196 }
197
198 fn max_intrinsic_width_proxy(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
199 self.measure_text_content().width
200 }
201
202 fn min_intrinsic_height_proxy(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
203 self.measure_text_content().height
204 }
205
206 fn max_intrinsic_height_proxy(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
207 self.measure_text_content().height
208 }
209}
210
211impl DrawModifierNode for TextModifierNode {
212 fn draw(&self, _draw_scope: &mut dyn DrawScope) {
213 }
222}
223
224impl SemanticsNode for TextModifierNode {
225 fn merge_semantics(&self, config: &mut SemanticsConfiguration) {
226 config.content_description = Some(self.text.clone());
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq)]
240pub struct TextModifierElement {
241 text: String,
242}
243
244impl TextModifierElement {
245 pub fn new(text: String) -> Self {
246 Self { text }
247 }
248}
249
250impl Hash for TextModifierElement {
251 fn hash<H: Hasher>(&self, state: &mut H) {
252 self.text.hash(state);
253 }
254}
255
256impl ModifierNodeElement for TextModifierElement {
257 type Node = TextModifierNode;
258
259 fn create(&self) -> Self::Node {
260 TextModifierNode::new(self.text.clone())
261 }
262
263 fn update(&self, node: &mut Self::Node) {
264 if node.text != self.text {
265 node.text = self.text.clone();
266 }
272 }
273
274 fn capabilities(&self) -> NodeCapabilities {
275 NodeCapabilities::LAYOUT | NodeCapabilities::DRAW | NodeCapabilities::SEMANTICS
277 }
278}