slt/context/widgets_display/
text.rs1use super::*;
2use crate::KeyMap;
3
4impl Context {
5 pub fn text(&mut self, s: impl Into<String>) -> &mut Self {
16 let content = s.into();
17 let default_fg = self
18 .rollback
19 .text_color_stack
20 .iter()
21 .rev()
22 .find_map(|c| *c)
23 .unwrap_or(self.theme.text);
24 self.commands.push(Command::Text {
25 content,
26 cursor_offset: None,
27 style: Style::new().fg(default_fg),
28 grow: 0,
29 align: Align::Start,
30 wrap: false,
31 truncate: false,
32 margin: Margin::default(),
33 constraints: Constraints::default(),
34 });
35 self.rollback.last_text_idx = Some(self.commands.len() - 1);
36 self
37 }
38
39 #[allow(clippy::print_stderr)]
45 pub fn link(&mut self, text: impl Into<String>, url: impl Into<String>) -> &mut Self {
46 let url_str = url.into();
47 let focused = self.register_focusable();
48 let (_interaction_id, response) = self.begin_widget_interaction(focused);
49
50 let activated = response.clicked || self.consume_activation_keys(focused);
51
52 if activated {
53 if let Err(e) = open_url(&url_str) {
54 eprintln!("[slt] failed to open URL: {e}");
55 }
56 }
57
58 let style = if focused {
59 Style::new()
60 .fg(self.theme.primary)
61 .bg(self.theme.surface_hover)
62 .underline()
63 .bold()
64 } else if response.hovered {
65 Style::new()
66 .fg(self.theme.accent)
67 .bg(self.theme.surface_hover)
68 .underline()
69 } else {
70 Style::new().fg(self.theme.primary).underline()
71 };
72
73 self.commands.push(Command::Link {
74 text: text.into(),
75 url: url_str,
76 style,
77 margin: Margin::default(),
78 constraints: Constraints::default(),
79 });
80 self.rollback.last_text_idx = Some(self.commands.len() - 1);
81 self
82 }
83
84 #[deprecated(since = "0.15.4", note = "use ui.text(s).wrap() instead")]
92 pub fn text_wrap(&mut self, s: impl Into<String>) -> &mut Self {
93 let content = s.into();
94 let default_fg = self
95 .rollback
96 .text_color_stack
97 .iter()
98 .rev()
99 .find_map(|c| *c)
100 .unwrap_or(self.theme.text);
101 self.commands.push(Command::Text {
102 content,
103 cursor_offset: None,
104 style: Style::new().fg(default_fg),
105 grow: 0,
106 align: Align::Start,
107 wrap: true,
108 truncate: false,
109 margin: Margin::default(),
110 constraints: Constraints::default(),
111 });
112 self.rollback.last_text_idx = Some(self.commands.len() - 1);
113 self
114 }
115
116 pub fn timer_display(&mut self, elapsed: std::time::Duration) -> &mut Self {
120 let total_centis = elapsed.as_millis() / 10;
121 let centis = total_centis % 100;
122 let total_seconds = total_centis / 100;
123 let seconds = total_seconds % 60;
124 let minutes = (total_seconds / 60) % 60;
125 let hours = total_seconds / 3600;
126
127 let content = if hours > 0 {
128 format!("{hours:02}:{minutes:02}:{seconds:02}.{centis:02}")
129 } else {
130 format!("{minutes:02}:{seconds:02}.{centis:02}")
131 };
132
133 self.commands.push(Command::Text {
134 content,
135 cursor_offset: None,
136 style: Style::new().fg(self.theme.text),
137 grow: 0,
138 align: Align::Start,
139 wrap: false,
140 truncate: false,
141 margin: Margin::default(),
142 constraints: Constraints::default(),
143 });
144 self.rollback.last_text_idx = Some(self.commands.len() - 1);
145 self
146 }
147
148 pub fn help_from_keymap(&mut self, keymap: &KeyMap) -> Response {
150 let pairs: Vec<(&str, &str)> = keymap
151 .visible_bindings()
152 .map(|binding| (binding.display.as_str(), binding.description.as_str()))
153 .collect();
154 self.help(&pairs)
155 }
156
157 pub fn bold(&mut self) -> &mut Self {
161 self.modify_last_style(|s| s.modifiers |= Modifiers::BOLD);
162 self
163 }
164
165 pub fn dim(&mut self) -> &mut Self {
170 let text_dim = self.theme.text_dim;
171 self.modify_last_style(|s| {
172 s.modifiers |= Modifiers::DIM;
173 if s.fg.is_none() {
174 s.fg = Some(text_dim);
175 }
176 });
177 self
178 }
179
180 pub fn italic(&mut self) -> &mut Self {
182 self.modify_last_style(|s| s.modifiers |= Modifiers::ITALIC);
183 self
184 }
185
186 pub fn underline(&mut self) -> &mut Self {
188 self.modify_last_style(|s| s.modifiers |= Modifiers::UNDERLINE);
189 self
190 }
191
192 pub fn reversed(&mut self) -> &mut Self {
194 self.modify_last_style(|s| s.modifiers |= Modifiers::REVERSED);
195 self
196 }
197
198 pub fn strikethrough(&mut self) -> &mut Self {
200 self.modify_last_style(|s| s.modifiers |= Modifiers::STRIKETHROUGH);
201 self
202 }
203
204 pub fn fg(&mut self, color: Color) -> &mut Self {
206 self.modify_last_style(|s| s.fg = Some(color));
207 self
208 }
209
210 pub fn bg(&mut self, color: Color) -> &mut Self {
212 self.modify_last_style(|s| s.bg = Some(color));
213 self
214 }
215
216 pub fn gradient(&mut self, from: Color, to: Color) -> &mut Self {
218 if let Some(idx) = self.rollback.last_text_idx {
219 let replacement = match &self.commands[idx] {
220 Command::Text {
221 content,
222 style,
223 wrap,
224 align,
225 margin,
226 constraints,
227 ..
228 } => {
229 let chars: Vec<char> = content.chars().collect();
230 let len = chars.len();
231 let denom = len.saturating_sub(1).max(1) as f32;
232 let segments = chars
233 .into_iter()
234 .enumerate()
235 .map(|(i, ch)| {
236 let mut seg_style = *style;
237 seg_style.fg = Some(from.blend(to, i as f32 / denom));
238 (ch.to_string(), seg_style)
239 })
240 .collect();
241
242 Some(Command::RichText {
243 segments,
244 wrap: *wrap,
245 align: *align,
246 margin: *margin,
247 constraints: *constraints,
248 })
249 }
250 _ => None,
251 };
252
253 if let Some(command) = replacement {
254 self.commands[idx] = command;
255 }
256 }
257
258 self
259 }
260
261 pub fn group_hover_fg(&mut self, color: Color) -> &mut Self {
263 let apply_group_style = self
264 .rollback
265 .group_stack
266 .last()
267 .map(|name| self.is_group_hovered(name) || self.is_group_focused(name))
268 .unwrap_or(false);
269 if apply_group_style {
270 self.modify_last_style(|s| s.fg = Some(color));
271 }
272 self
273 }
274
275 pub fn group_hover_bg(&mut self, color: Color) -> &mut Self {
277 let apply_group_style = self
278 .rollback
279 .group_stack
280 .last()
281 .map(|name| self.is_group_hovered(name) || self.is_group_focused(name))
282 .unwrap_or(false);
283 if apply_group_style {
284 self.modify_last_style(|s| s.bg = Some(color));
285 }
286 self
287 }
288
289 pub fn styled(&mut self, s: impl Into<String>, style: Style) -> &mut Self {
294 self.styled_with_cursor(s, style, None)
295 }
296
297 pub(crate) fn styled_with_cursor(
298 &mut self,
299 s: impl Into<String>,
300 style: Style,
301 cursor_offset: Option<usize>,
302 ) -> &mut Self {
303 self.commands.push(Command::Text {
304 content: s.into(),
305 cursor_offset,
306 style,
307 grow: 0,
308 align: Align::Start,
309 wrap: false,
310 truncate: false,
311 margin: Margin::default(),
312 constraints: Constraints::default(),
313 });
314 self.rollback.last_text_idx = Some(self.commands.len() - 1);
315 self
316 }
317
318 pub fn wrap(&mut self) -> &mut Self {
320 if let Some(idx) = self.rollback.last_text_idx {
321 if let Command::Text { wrap, .. } = &mut self.commands[idx] {
322 *wrap = true;
323 }
324 }
325 self
326 }
327
328 pub fn truncate(&mut self) -> &mut Self {
331 if let Some(idx) = self.rollback.last_text_idx {
332 if let Command::Text { truncate, .. } = &mut self.commands[idx] {
333 *truncate = true;
334 }
335 }
336 self
337 }
338
339 fn modify_last_style(&mut self, f: impl FnOnce(&mut Style)) {
340 if let Some(idx) = self.rollback.last_text_idx {
341 match &mut self.commands[idx] {
342 Command::Text { style, .. } | Command::Link { style, .. } => f(style),
343 _ => {}
344 }
345 }
346 }
347
348 fn modify_last_constraints(&mut self, f: impl FnOnce(&mut Constraints)) {
349 if let Some(idx) = self.rollback.last_text_idx {
350 match &mut self.commands[idx] {
351 Command::Text { constraints, .. } | Command::Link { constraints, .. } => {
352 f(constraints)
353 }
354 _ => {}
355 }
356 }
357 }
358
359 fn modify_last_margin(&mut self, f: impl FnOnce(&mut Margin)) {
360 if let Some(idx) = self.rollback.last_text_idx {
361 match &mut self.commands[idx] {
362 Command::Text { margin, .. } | Command::Link { margin, .. } => f(margin),
363 _ => {}
364 }
365 }
366 }
367
368 pub fn grow(&mut self, value: u16) -> &mut Self {
375 if let Some(idx) = self.rollback.last_text_idx {
376 if let Command::Text { grow, .. } = &mut self.commands[idx] {
377 *grow = value;
378 }
379 }
380 self
381 }
382
383 pub fn align(&mut self, align: Align) -> &mut Self {
385 if let Some(idx) = self.rollback.last_text_idx {
386 if let Command::Text {
387 align: text_align, ..
388 } = &mut self.commands[idx]
389 {
390 *text_align = align;
391 }
392 }
393 self
394 }
395
396 pub fn text_center(&mut self) -> &mut Self {
400 self.align(Align::Center)
401 }
402
403 pub fn text_right(&mut self) -> &mut Self {
406 self.align(Align::End)
407 }
408
409 pub fn w(&mut self, value: u32) -> &mut Self {
416 self.modify_last_constraints(|c| {
417 c.min_width = Some(value);
418 c.max_width = Some(value);
419 });
420 self
421 }
422
423 pub fn h(&mut self, value: u32) -> &mut Self {
427 self.modify_last_constraints(|c| {
428 c.min_height = Some(value);
429 c.max_height = Some(value);
430 });
431 self
432 }
433
434 pub fn min_w(&mut self, value: u32) -> &mut Self {
436 self.modify_last_constraints(|c| c.min_width = Some(value));
437 self
438 }
439
440 pub fn max_w(&mut self, value: u32) -> &mut Self {
442 self.modify_last_constraints(|c| c.max_width = Some(value));
443 self
444 }
445
446 pub fn min_h(&mut self, value: u32) -> &mut Self {
448 self.modify_last_constraints(|c| c.min_height = Some(value));
449 self
450 }
451
452 pub fn max_h(&mut self, value: u32) -> &mut Self {
454 self.modify_last_constraints(|c| c.max_height = Some(value));
455 self
456 }
457
458 pub fn m(&mut self, value: u32) -> &mut Self {
462 self.modify_last_margin(|m| *m = Margin::all(value));
463 self
464 }
465
466 pub fn mx(&mut self, value: u32) -> &mut Self {
468 self.modify_last_margin(|m| {
469 m.left = value;
470 m.right = value;
471 });
472 self
473 }
474
475 pub fn my(&mut self, value: u32) -> &mut Self {
477 self.modify_last_margin(|m| {
478 m.top = value;
479 m.bottom = value;
480 });
481 self
482 }
483
484 pub fn mt(&mut self, value: u32) -> &mut Self {
486 self.modify_last_margin(|m| m.top = value);
487 self
488 }
489
490 pub fn mr(&mut self, value: u32) -> &mut Self {
492 self.modify_last_margin(|m| m.right = value);
493 self
494 }
495
496 pub fn mb(&mut self, value: u32) -> &mut Self {
498 self.modify_last_margin(|m| m.bottom = value);
499 self
500 }
501
502 pub fn ml(&mut self, value: u32) -> &mut Self {
504 self.modify_last_margin(|m| m.left = value);
505 self
506 }
507
508 pub fn spacer(&mut self) -> &mut Self {
512 self.commands.push(Command::Spacer { grow: 1 });
513 self.rollback.last_text_idx = None;
514 self
515 }
516}