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};
28use std::rc::Rc;
29
30#[derive(Debug)]
40pub struct TextModifierNode {
41 text: Rc<str>,
42 state: NodeState,
43}
44
45impl TextModifierNode {
46 pub fn new(text: Rc<str>) -> Self {
47 Self {
48 text,
49 state: NodeState::new(),
50 }
51 }
52
53 pub fn text(&self) -> &str {
54 &self.text
55 }
56
57 pub fn text_arc(&self) -> Rc<str> {
58 self.text.clone()
59 }
60
61 fn measure_text_content(&self) -> Size {
63 let metrics = crate::text::measure_text(&self.text);
64 Size {
65 width: metrics.width,
66 height: metrics.height,
67 }
68 }
69}
70
71impl DelegatableNode for TextModifierNode {
72 fn node_state(&self) -> &NodeState {
73 &self.state
74 }
75}
76
77impl ModifierNode for TextModifierNode {
78 fn on_attach(&mut self, context: &mut dyn ModifierNodeContext) {
79 context.invalidate(InvalidationKind::Layout);
81 context.invalidate(InvalidationKind::Draw);
82 context.invalidate(InvalidationKind::Semantics);
83 }
84
85 fn as_draw_node(&self) -> Option<&dyn DrawModifierNode> {
86 Some(self)
87 }
88
89 fn as_draw_node_mut(&mut self) -> Option<&mut dyn DrawModifierNode> {
90 Some(self)
91 }
92
93 fn as_semantics_node(&self) -> Option<&dyn SemanticsNode> {
94 Some(self)
95 }
96
97 fn as_semantics_node_mut(&mut self) -> Option<&mut dyn SemanticsNode> {
98 Some(self)
99 }
100
101 fn as_layout_node(&self) -> Option<&dyn LayoutModifierNode> {
102 Some(self)
103 }
104
105 fn as_layout_node_mut(&mut self) -> Option<&mut dyn LayoutModifierNode> {
106 Some(self)
107 }
108}
109
110impl LayoutModifierNode for TextModifierNode {
111 fn measure(
112 &self,
113 _context: &mut dyn ModifierNodeContext,
114 _measurable: &dyn Measurable,
115 constraints: Constraints,
116 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
117 let text_size = self.measure_text_content();
119
120 let width = text_size
122 .width
123 .clamp(constraints.min_width, constraints.max_width);
124 let height = text_size
125 .height
126 .clamp(constraints.min_height, constraints.max_height);
127
128 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size { width, height })
132 }
133
134 fn min_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
135 self.measure_text_content().width
136 }
137
138 fn max_intrinsic_width(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
139 self.measure_text_content().width
140 }
141
142 fn min_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
143 self.measure_text_content().height
144 }
145
146 fn max_intrinsic_height(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
147 self.measure_text_content().height
148 }
149
150 fn create_measurement_proxy(&self) -> Option<Box<dyn MeasurementProxy>> {
151 Some(Box::new(TextMeasurementProxy {
152 text: self.text.clone(),
153 }))
154 }
155}
156
157struct TextMeasurementProxy {
162 text: Rc<str>,
163}
164
165impl TextMeasurementProxy {
166 fn measure_text_content(&self) -> Size {
169 let metrics = crate::text::measure_text(&self.text);
170 Size {
171 width: metrics.width,
172 height: metrics.height,
173 }
174 }
175}
176
177impl MeasurementProxy for TextMeasurementProxy {
178 fn measure_proxy(
179 &self,
180 _context: &mut dyn ModifierNodeContext,
181 _measurable: &dyn Measurable,
182 constraints: Constraints,
183 ) -> cranpose_ui_layout::LayoutModifierMeasureResult {
184 let text_size = self.measure_text_content();
186
187 let width = text_size
189 .width
190 .clamp(constraints.min_width, constraints.max_width);
191 let height = text_size
192 .height
193 .clamp(constraints.min_height, constraints.max_height);
194
195 cranpose_ui_layout::LayoutModifierMeasureResult::with_size(Size { width, height })
197 }
198
199 fn min_intrinsic_width_proxy(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
200 self.measure_text_content().width
201 }
202
203 fn max_intrinsic_width_proxy(&self, _measurable: &dyn Measurable, _height: f32) -> f32 {
204 self.measure_text_content().width
205 }
206
207 fn min_intrinsic_height_proxy(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
208 self.measure_text_content().height
209 }
210
211 fn max_intrinsic_height_proxy(&self, _measurable: &dyn Measurable, _width: f32) -> f32 {
212 self.measure_text_content().height
213 }
214}
215
216impl DrawModifierNode for TextModifierNode {
217 fn draw(&self, _draw_scope: &mut dyn DrawScope) {
218 }
227}
228
229impl SemanticsNode for TextModifierNode {
230 fn merge_semantics(&self, config: &mut SemanticsConfiguration) {
231 config.content_description = Some(self.text.to_string());
233 }
234}
235
236#[derive(Debug, Clone, PartialEq, Eq)]
245pub struct TextModifierElement {
246 text: Rc<str>,
247}
248
249impl TextModifierElement {
250 pub fn new(text: Rc<str>) -> Self {
251 Self { text }
252 }
253}
254
255impl Hash for TextModifierElement {
256 fn hash<H: Hasher>(&self, state: &mut H) {
257 self.text.hash(state);
258 }
259}
260
261impl ModifierNodeElement for TextModifierElement {
262 type Node = TextModifierNode;
263
264 fn create(&self) -> Self::Node {
265 TextModifierNode::new(self.text.clone())
266 }
267
268 fn update(&self, node: &mut Self::Node) {
269 if node.text != self.text {
270 node.text = self.text.clone();
271 }
277 }
278
279 fn capabilities(&self) -> NodeCapabilities {
280 NodeCapabilities::LAYOUT | NodeCapabilities::DRAW | NodeCapabilities::SEMANTICS
282 }
283}