1use std::{
4 any::Any,
5 borrow::Cow,
6 rc::Rc,
7};
8
9use freya_engine::prelude::{
10 ClipOp,
11 ParagraphBuilder,
12 ParagraphStyle,
13 SkParagraph,
14 SkRect,
15};
16use torin::prelude::{
17 Area,
18 Size2D,
19 SizeModel,
20};
21
22use crate::{
23 data::{
24 AccessibilityData,
25 EffectData,
26 LayoutData,
27 StyleState,
28 TextStyleData,
29 },
30 diff_key::DiffKey,
31 element::{
32 ClipContext,
33 Element,
34 ElementExt,
35 EventHandlers,
36 LayoutContext,
37 RenderContext,
38 },
39 elements::paragraph::ParagraphPaintExt,
40 layers::Layer,
41 prelude::{
42 AccessibilityExt,
43 ContainerExt,
44 EffectExt,
45 EventHandlersExt,
46 KeyExt,
47 LayerExt,
48 LayoutExt,
49 MaybeExt,
50 Span,
51 TextAlign,
52 TextOverflowExt,
53 TextStyleExt,
54 },
55 text_cache::CachedParagraph,
56 tree::DiffModifies,
57};
58
59pub fn label() -> Label {
70 Label::default()
71}
72
73impl From<&str> for Element {
74 fn from(value: &str) -> Self {
75 label().text(value.to_string()).into()
76 }
77}
78
79impl From<String> for Element {
80 fn from(value: String) -> Self {
81 label().text(value).into()
82 }
83}
84
85pub enum TextWidth {
87 Fit,
89 Max,
91}
92
93#[derive(PartialEq, Clone)]
94pub struct LabelElement {
95 pub text: Cow<'static, str>,
96 pub accessibility: AccessibilityData,
97 pub text_style_data: TextStyleData,
98 pub layout: LayoutData,
99 pub event_handlers: EventHandlers,
100 pub max_lines: Option<usize>,
101 pub line_height: Option<f32>,
102 pub relative_layer: Layer,
103 pub effect: Option<EffectData>,
104}
105
106impl Default for LabelElement {
107 fn default() -> Self {
108 let mut accessibility = AccessibilityData::default();
109 accessibility.builder.set_role(accesskit::Role::Label);
110 Self {
111 text: Default::default(),
112 accessibility,
113 text_style_data: Default::default(),
114 layout: Default::default(),
115 event_handlers: Default::default(),
116 max_lines: None,
117 line_height: None,
118 relative_layer: Layer::default(),
119 effect: None,
120 }
121 }
122}
123
124impl ElementExt for LabelElement {
125 fn changed(&self, other: &Rc<dyn ElementExt>) -> bool {
126 let Some(label) = (other.as_ref() as &dyn Any).downcast_ref::<LabelElement>() else {
127 return false;
128 };
129 self != label
130 }
131
132 fn diff(&self, other: &Rc<dyn ElementExt>) -> DiffModifies {
133 let Some(label) = (other.as_ref() as &dyn Any).downcast_ref::<LabelElement>() else {
134 return DiffModifies::all();
135 };
136
137 let mut diff = DiffModifies::empty();
138
139 if self.text != label.text {
140 diff.insert(DiffModifies::STYLE);
141 diff.insert(DiffModifies::LAYOUT);
142 diff.insert(DiffModifies::ACCESSIBILITY);
143 }
144
145 if self.accessibility != label.accessibility {
146 diff.insert(DiffModifies::ACCESSIBILITY);
147 }
148
149 if self.relative_layer != label.relative_layer {
150 diff.insert(DiffModifies::LAYER);
151 }
152
153 if self.effect != label.effect {
154 diff.insert(DiffModifies::EFFECT);
155 }
156
157 if self.text_style_data != label.text_style_data
158 || self.line_height != label.line_height
159 || self.max_lines != label.max_lines
160 {
161 diff.insert(DiffModifies::TEXT_STYLE);
162 diff.insert(DiffModifies::LAYOUT);
163 }
164 if self.layout != label.layout {
165 diff.insert(DiffModifies::LAYOUT);
166 }
167
168 if self.event_handlers != label.event_handlers {
169 diff.insert(DiffModifies::EVENT_HANDLERS);
170 }
171
172 diff
173 }
174
175 fn layout(&'_ self) -> Cow<'_, LayoutData> {
176 Cow::Borrowed(&self.layout)
177 }
178
179 fn effect(&'_ self) -> Option<Cow<'_, EffectData>> {
180 self.effect.as_ref().map(Cow::Borrowed)
181 }
182
183 fn style(&'_ self) -> Cow<'_, StyleState> {
184 Cow::Owned(StyleState::default())
185 }
186
187 fn is_transparent(&self) -> bool {
188 false
189 }
190
191 fn text_style(&'_ self) -> Cow<'_, TextStyleData> {
192 Cow::Borrowed(&self.text_style_data)
193 }
194
195 fn accessibility(&'_ self) -> Cow<'_, AccessibilityData> {
196 Cow::Borrowed(&self.accessibility)
197 }
198
199 fn finish_accessibility(&self, builder: &mut accesskit::Node) {
200 builder.set_value(self.text.clone());
201 }
202
203 fn layer(&self) -> Layer {
204 self.relative_layer
205 }
206
207 fn events_handlers(&'_ self) -> Option<Cow<'_, EventHandlers>> {
208 Some(Cow::Borrowed(&self.event_handlers))
209 }
210
211 fn measure(&self, context: LayoutContext) -> Option<(Size2D, Rc<dyn Any>)> {
212 let content_area_size =
213 (*context.area_size - context.torin_node.padding.into()).max(Size2D::zero());
214 let cached_paragraph = CachedParagraph {
215 text_style_state: context.text_style_state,
216 spans: &[Span::new(&*self.text)],
217 max_lines: self.max_lines,
218 line_height: self.line_height,
219 width: content_area_size.width,
220 };
221 let paragraph = context
222 .text_cache
223 .utilize(context.node_id, &cached_paragraph)
224 .unwrap_or_else(|| {
225 let build = |fill_area: Area| {
226 let mut paragraph_style = ParagraphStyle::default();
227
228 if let Some(ellipsis) = context.text_style_state.text_overflow.get_ellipsis() {
229 paragraph_style.set_ellipsis(ellipsis);
230 }
231
232 let text_style = context.text_style_state.to_text_style(
233 context.fallback_fonts,
234 context.scale_factor,
235 self.line_height,
236 fill_area,
237 );
238 paragraph_style.set_text_style(&text_style);
239 paragraph_style.set_max_lines(self.max_lines);
240 paragraph_style.set_text_align(context.text_style_state.text_align.into());
241
242 let mut paragraph_builder =
243 ParagraphBuilder::new(¶graph_style, &*context.font_collection);
244
245 paragraph_builder.push_style(&text_style);
246 paragraph_builder.add_text(&self.text);
247
248 let mut paragraph = paragraph_builder.build();
249 paragraph.layout(
250 if self.max_lines == Some(1)
251 && context.text_style_state.text_align == TextAlign::default()
252 && !paragraph_style.ellipsized()
253 {
254 f32::MAX
255 } else {
256 content_area_size.width + 1.0
257 },
258 );
259 paragraph
260 };
261
262 let mut paragraph = build(Area::default());
263
264 if context.text_style_state.color.as_color().is_none() {
265 paragraph = build(paragraph.fill_area());
266 }
267
268 context
269 .text_cache
270 .insert(context.node_id, &cached_paragraph, paragraph)
271 });
272
273 let size = Size2D::new(paragraph.longest_line(), paragraph.height())
274 .max(Size2D::zero())
275 .with_gaps(&context.torin_node.padding);
276
277 Some((size, paragraph))
278 }
279
280 fn should_hook_measurement(&self) -> bool {
281 true
282 }
283
284 fn should_measure_inner_children(&self) -> bool {
285 false
286 }
287
288 fn clip(&self, context: ClipContext) {
289 let area = context.visible_area;
290 context.canvas.clip_rect(
291 SkRect::new(area.min_x(), area.min_y(), area.max_x(), area.max_y()),
292 ClipOp::Intersect,
293 true,
294 );
295 }
296
297 fn render(&self, context: RenderContext) {
298 let layout_data = context.layout_node.data.as_ref().unwrap();
299 let paragraph = layout_data.downcast_ref::<SkParagraph>().unwrap();
300
301 let origin = context.layout_node.inner_area.origin;
302 paragraph.paint_at(context.canvas, origin.cast_unit());
303 }
304}
305
306impl From<Label> for Element {
307 fn from(value: Label) -> Self {
308 Element::Element {
309 key: value.key,
310 element: Rc::new(value.element),
311 elements: vec![],
312 }
313 }
314}
315
316impl KeyExt for Label {
317 fn key_mut(&mut self) -> &mut DiffKey {
318 &mut self.key
319 }
320}
321
322impl EventHandlersExt for Label {
323 fn event_handlers_mut(&mut self) -> &mut EventHandlers {
324 &mut self.element.event_handlers
325 }
326}
327
328impl AccessibilityExt for Label {
329 fn accessibility_data_mut(&mut self) -> &mut AccessibilityData {
330 &mut self.element.accessibility
331 }
332}
333
334impl TextOverflowExt for Label {}
335
336impl TextStyleExt for Label {
337 fn text_style_data_mut(&mut self) -> &mut TextStyleData {
338 &mut self.element.text_style_data
339 }
340}
341
342impl EffectExt for Label {
343 fn effect_mut(&mut self) -> &mut EffectData {
344 self.element.effect.get_or_insert_with(EffectData::default)
345 }
346}
347
348impl LayerExt for Label {
349 fn layer_mut(&mut self) -> &mut Layer {
350 &mut self.element.relative_layer
351 }
352}
353
354impl MaybeExt for Label {}
355
356#[derive(Default, Clone)]
357pub struct Label {
358 key: DiffKey,
359 element: LabelElement,
360}
361
362impl Label {
363 pub fn try_downcast(element: &dyn ElementExt) -> Option<LabelElement> {
364 (element as &dyn Any)
365 .downcast_ref::<LabelElement>()
366 .cloned()
367 }
368
369 pub fn text(mut self, text: impl Into<Cow<'static, str>>) -> Self {
371 let text = text.into();
372 self.element.text = text;
373 self
374 }
375
376 pub fn max_lines(mut self, max_lines: impl Into<Option<usize>>) -> Self {
378 self.element.max_lines = max_lines.into();
379 self
380 }
381
382 pub fn line_height(mut self, line_height: impl Into<Option<f32>>) -> Self {
384 self.element.line_height = line_height.into();
385 self
386 }
387}
388
389impl LayoutExt for Label {
390 fn layout_mut(&mut self) -> &mut LayoutData {
391 &mut self.element.layout
392 }
393}
394
395impl ContainerExt for Label {}