Skip to main content

flux_tui/components/
label.rs

1use std::num::NonZero;
2use unicode_width::UnicodeWidthStr;
3
4use crate::canvas::*;
5
6use super::*;
7
8/// Represents a single text label
9///
10/// Design (optional Border):
11/// ```text
12/// ┌─────┐
13/// │Label│
14/// └─────┘
15/// ```
16pub struct Label {
17	has_focus: bool,
18	text: String,
19	text_alignment: HorizontalAlignment,
20	base: WidgetBase,
21	focus_color: Option<Color>,
22}
23
24impl Default for Label {
25	fn default() -> Self {
26		let mut s = Self {
27			has_focus: false,
28			text: String::default(),
29			text_alignment: HorizontalAlignment::Center,
30			base: WidgetBase::default(),
31			focus_color: None,
32		};
33		s.base.constraints.y.min = NonZero::<TSize>::MIN;
34		s.base.constraints.y.max = Size::Fixed(NonZero::<TSize>::MIN);
35		s
36	}
37}
38
39impl Label {
40	/// Sets the alignment of the inner text
41	pub fn set_text_alignment(&mut self, alignment: HorizontalAlignment) {
42		self.text_alignment = alignment;
43	}
44
45	/// Replaces the inner text
46	pub fn set_text(&mut self, text: &str) {
47		self.text.clear();
48		self.text.push_str(text);
49		let old = self.base.constraints.x.max;
50		self.base.constraints.x.max =
51			Size::Fixed(NonZero::new(self.text.width() as TSize).unwrap_or(NonZero::<TSize>::MIN));
52		if old != self.base.constraints.x.max {
53			self.provoke_changed_property(WindowProperty::Size);
54		}
55	}
56
57	/// Gets the inner text
58	pub fn get_text(&self) -> &str {
59		&self.text
60	}
61
62	pub fn set_focus_color(&mut self, color: Option<Color>) {
63		self.focus_color = color;
64	}
65}
66
67impl Window for Label {
68	fn render(&self, canvas: &mut Canvas) {
69		let fg = match self.base.enabled {
70			true => match self.has_focus {
71				true => self.focus_color,
72				false => None,
73			},
74			false => self.base.colors.disabled,
75		}
76		.or(self.base.colors.base_fg);
77
78		let mut row = canvas.get_row_variable_width(TSize::MIN).unwrap();
79		let text_w = self.text.width() as TSize;
80		match self.text_alignment {
81			HorizontalAlignment::Left => {}
82			HorizontalAlignment::Center => {
83				row.skip(row.width().checked_sub(text_w).unwrap_or_default() / 2);
84			}
85			HorizontalAlignment::Right => {
86				row.skip(row.width().checked_sub(text_w).unwrap_or_default());
87			}
88		}
89
90		row.add_string(
91			&self.text,
92			fg,
93			self.base.colors.base_bg,
94			Style::default(),
95			Some(VariableWidthGlyphRow::DOT3_REPLACEMENT),
96		)
97		.unwrap()
98	}
99
100	fn handle_event(&mut self, event: &mut WindowEvent) {
101		match event.raw() {
102			Event::FocusGained => self.has_focus = true,
103			Event::FocusLost => self.has_focus = false,
104			_ => {}
105		}
106	}
107
108	fn is_enabled(&self) -> bool {
109		self.base.enabled
110	}
111}
112
113impl WindowLayout for Label {
114	fn desired_size(&self, available_size: TPoint) -> TPoint {
115		self.base.desired_size(available_size)
116	}
117
118	fn alignment(&self) -> (super::HorizontalAlignment, super::VerticalAlignment) {
119		self.base.alignment
120	}
121
122	fn margin(&self) -> Thickness {
123		self.base.margin
124	}
125
126	fn border(&self) -> BorderStyle {
127		self.base.border
128	}
129
130	fn is_visible(&self) -> bool {
131		self.base.visibility
132	}
133}
134
135impl HasWindowUID for Label {
136	fn uid(&self) -> WindowUID {
137		self.base.uid
138	}
139}
140
141impl Widget for Label {
142	fn set_alignment(&mut self, horizontal: HorizontalAlignment, vertical: VerticalAlignment) {
143		self.base.alignment = (horizontal, vertical);
144		self.provoke_changed_property(WindowProperty::Alignment);
145	}
146
147	fn set_visibility(&mut self, visibility: bool) {
148		self.base.visibility = visibility;
149		self.provoke_changed_property(WindowProperty::IsVisible);
150	}
151
152	fn set_width(&mut self, width: super::Size) {
153		self.base.size.x = width;
154		self.provoke_changed_property(WindowProperty::Size);
155	}
156
157	fn set_height(&mut self, _: super::Size) {}
158
159	fn set_margin(&mut self, margin: Thickness) {
160		self.base.margin = margin;
161		self.provoke_changed_property(WindowProperty::Margin);
162	}
163
164	fn set_border(&mut self, border: BorderStyle) {
165		self.base.border = border;
166		self.provoke_changed_property(WindowProperty::Border);
167	}
168
169	fn set_enabled_state(&mut self, is_enabled: bool) {
170		self.base.enabled = is_enabled;
171	}
172
173	fn set_width_constraint(&mut self, width: SizeConstraint) {
174		self.base.constraints.x = width;
175		self.provoke_changed_property(WindowProperty::Size);
176	}
177
178	fn set_height_constraint(&mut self, _: SizeConstraint) {}
179}
180
181impl WidgetColors for Label {
182	fn set_disabled_color(&mut self, color: Option<Color>) {
183		self.base.colors.disabled = color;
184	}
185
186	fn set_base_fg_color(&mut self, color: Option<Color>) {
187		self.base.colors.base_fg = color;
188	}
189
190	fn set_base_bg_color(&mut self, color: Option<Color>) {
191		self.base.colors.base_bg = color;
192	}
193}