1use crate::component::{Component, EventCx, LayoutCx, MeasureCx};
2use crate::event::Event;
3use crate::geom::{Rect, Size};
4use crate::layout::Constraint;
5use crate::render::RenderCx;
6use crate::style::{Style, TextWrap};
7
8pub struct Label {
10 text: String,
11 style: Style,
12 id: Option<String>,
13 class: Option<String>,
14}
15
16impl Label {
17 pub fn new(text: impl Into<String>) -> Self {
18 Self {
19 text: text.into(),
20 style: Style::default(),
21 id: None,
22 class: None,
23 }
24 }
25
26 pub fn style(mut self, style: Style) -> Self {
27 self.style = style;
28 self
29 }
30
31 pub fn id(mut self, id: impl Into<String>) -> Self {
32 self.id = Some(id.into());
33 self
34 }
35
36 pub fn class(mut self, class: impl Into<String>) -> Self {
37 self.class = Some(class.into());
38 self
39 }
40}
41
42impl Component for Label {
43 fn render(&self, cx: &mut RenderCx) {
44 let segments: Vec<&str> = self.text.split('\n').collect();
46 for (i, seg) in segments.iter().enumerate() {
47 if i > 0 {
48 cx.cursor.y = cx.cursor.y.saturating_add(1);
49 cx.cursor.x = cx.rect.x;
50 }
51 match cx.wrap {
52 TextWrap::None => {
53 let tw: u16 = seg.chars().map(|c| unicode_width::UnicodeWidthChar::width(c).unwrap_or(0) as u16).sum();
54 cx.cursor.x = cx.cursor.x.saturating_add(cx.align_offset(tw));
55 cx.text(seg);
56 }
57 TextWrap::Char => cx.line(seg),
58 }
59 }
60 }
61
62 fn measure(&self, constraint: Constraint, _cx: &mut MeasureCx) -> Size {
63 let max_w = constraint.max.width.max(1);
64 let mut line_width: u16 = 0;
65 let mut lines: u16 = 1;
66 let mut total_w: u16 = 0;
67 for c in self.text.chars() {
68 if c == '\n' {
69 lines += 1;
70 total_w = total_w.max(line_width);
71 line_width = 0;
72 continue;
73 }
74 let w = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0) as u16;
75 if line_width + w > max_w {
76 lines += 1;
77 total_w = total_w.max(line_width);
78 line_width = w;
79 } else {
80 line_width += w;
81 }
82 }
83 total_w = total_w.max(line_width);
84 Size {
85 width: max_w.min(total_w),
86 height: lines,
87 }
88 }
89
90 fn layout(&mut self, _rect: Rect, _cx: &mut LayoutCx) {
91 }
93
94 fn style(&self) -> Style {
95 self.style.clone()
96 }
97
98 fn id(&self) -> Option<&str> {
99 self.id.as_deref()
100 }
101
102 fn class(&self) -> Option<&str> {
103 self.class.as_deref()
104 }
105
106 fn focusable(&self) -> bool {
107 false
108 }
109
110 fn event(&mut self, _event: &Event, _cx: &mut EventCx) {}
111}