Skip to main content

lv_tui/widgets/
label.rs

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
8/// 文本标签 widget
9pub 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        // Split by newlines and render each segment as a separate line
45        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        // Label 无子节点,无需操作
92    }
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}