1use std::fmt;
2
3use crate::{
4 buffer::Buffer,
5 enums::{Color, Modifier, Wrap},
6 geometry::{Rect, TextAlign, Vec2},
7 style::Style,
8 text::{Text, TextParser},
9 widgets::cache::Cache,
10};
11
12use super::{widget::Widget, Element};
13
14#[derive(Debug)]
71pub struct Span {
72 text: String,
73 style: Style,
74 align: TextAlign,
75 wrap: Wrap,
76 ellipsis: String,
77}
78
79impl Span {
80 #[must_use]
90 pub fn new<T>(text: T) -> Self
91 where
92 T: AsRef<str>,
93 {
94 Self {
95 text: text.as_ref().to_string(),
96 ..Default::default()
97 }
98 }
99
100 #[must_use]
111 pub fn style<T>(mut self, style: T) -> Self
112 where
113 T: Into<Style>,
114 {
115 self.style = style.into();
116 self
117 }
118
119 #[must_use]
123 pub fn fg<T>(mut self, fg: T) -> Self
124 where
125 T: Into<Option<Color>>,
126 {
127 self.style = self.style.fg(fg);
128 self
129 }
130
131 #[must_use]
135 pub fn bg<T>(mut self, bg: T) -> Self
136 where
137 T: Into<Option<Color>>,
138 {
139 self.style = self.style.bg(bg);
140 self
141 }
142
143 #[must_use]
156 pub fn modifier(mut self, modifier: Modifier) -> Self {
157 self.style = self.style.modifier(modifier);
158 self
159 }
160
161 #[must_use]
169 pub fn add_modifier(mut self, flag: Modifier) -> Self {
170 self.style = self.style.add_modifier(flag);
171 self
172 }
173
174 #[must_use]
183 pub fn remove_modifier(mut self, flag: Modifier) -> Self {
184 self.style = self.style.remove_modifier(flag);
185 self
186 }
187
188 #[must_use]
192 pub fn align(mut self, align: TextAlign) -> Self {
193 self.align = align;
194 self
195 }
196
197 #[must_use]
201 pub fn wrap(mut self, wrap: Wrap) -> Self {
202 self.wrap = wrap;
203 self
204 }
205
206 #[must_use]
210 pub fn ellipsis<T>(mut self, ellipsis: T) -> Self
211 where
212 T: AsRef<str>,
213 {
214 self.ellipsis = ellipsis.as_ref().to_string();
215 self
216 }
217}
218
219impl Widget for Span {
220 fn render(&self, buffer: &mut Buffer, rect: Rect, _cache: &mut Cache) {
221 _ = self.render_offset(buffer, rect, 0, None);
222 }
223
224 fn height(&self, size: &Vec2) -> usize {
225 match self.wrap {
226 Wrap::Letter => self.height_letter_wrap(size),
227 Wrap::Word => self.height_word_wrap(size),
228 }
229 }
230
231 fn width(&self, size: &Vec2) -> usize {
232 match self.wrap {
233 Wrap::Letter => self.width_letter_wrap(size),
234 Wrap::Word => self.width_word_wrap(size),
235 }
236 }
237}
238
239impl Text for Span {
240 fn render_offset(
241 &self,
242 buffer: &mut Buffer,
243 rect: Rect,
244 offset: usize,
245 wrap: Option<Wrap>,
246 ) -> Vec2 {
247 if rect.is_empty() {
248 return Vec2::new(0, rect.y());
249 }
250
251 let wrap = wrap.unwrap_or(self.wrap);
252 let mut chars = self.text.chars();
253 let mut parser = TextParser::new(&mut chars).wrap(wrap);
254
255 let mut pos = Vec2::new(rect.x() + offset, rect.y());
256 let mut fin_pos = pos;
257
258 let right_end = rect.x() + rect.width();
259 while pos.y <= rect.bottom() {
260 let line_len = right_end.saturating_sub(pos.x);
261 let Some((text, len)) = parser.next_line(line_len) else {
262 break;
263 };
264
265 fin_pos.x =
266 self.render_line(buffer, &rect, &parser, text, len, &pos);
267 fin_pos.y = pos.y;
268 pos.x = rect.x();
269 pos.y += 1;
270 }
271 fin_pos
272 }
273
274 fn get(&self) -> String {
275 format!("{}{}\x1b[0m", self.get_mods(), self.text)
276 }
277
278 fn get_text(&self) -> &str {
279 &self.text
280 }
281
282 fn get_mods(&self) -> String {
283 self.style.to_string()
284 }
285}
286
287impl Default for Span {
288 fn default() -> Self {
289 Self {
290 text: Default::default(),
291 style: Default::default(),
292 align: Default::default(),
293 wrap: Default::default(),
294 ellipsis: "...".to_string(),
295 }
296 }
297}
298
299impl fmt::Display for Span {
300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
301 write!(f, "{}", self.get())
302 }
303}
304
305impl Span {
306 fn render_line(
308 &self,
309 buffer: &mut Buffer,
310 rect: &Rect,
311 parser: &TextParser,
312 mut line: String,
313 mut len: usize,
314 pos: &Vec2,
315 ) -> usize {
316 if pos.y >= rect.bottom() && !parser.is_end() {
317 len += self.ellipsis.len();
318 if len > rect.width() {
319 len = rect.width();
320 let end = rect.width().saturating_sub(self.ellipsis.len());
321 line = line[..end].to_string();
322 }
323 line.push_str(&self.ellipsis);
324 }
325
326 let x = match self.align {
327 TextAlign::Left => 0,
328 TextAlign::Center => rect.width().saturating_sub(len) >> 1,
329 TextAlign::Right => rect.width().saturating_sub(len),
330 };
331 buffer.set_str_styled(line, &Vec2::new(pos.x + x, pos.y), self.style);
332 pos.x + x + len - 1
333 }
334
335 fn height_word_wrap(&self, size: &Vec2) -> usize {
337 let mut chars = self.text.chars();
338 let mut parser = TextParser::new(&mut chars);
339
340 let mut pos = Vec2::new(0, 0);
341 loop {
342 if parser.next_line(size.x).is_none() {
343 break;
344 }
345 pos.y += 1;
346 }
347 pos.y
348 }
349
350 fn width_word_wrap(&self, size: &Vec2) -> usize {
352 let mut guess =
353 Vec2::new(self.size_letter_wrap(size.y).saturating_sub(1), 0);
354
355 while self.height_word_wrap(&guess) > size.y {
356 let Some(val) = guess.x.checked_add(1) else {
357 break;
358 };
359 guess.x = val;
360 }
361 guess.x
362 }
363
364 fn height_letter_wrap(&self, size: &Vec2) -> usize {
366 self.text
367 .lines()
368 .map(|l| {
369 (l.chars().count() as f32 / size.x as f32).ceil() as usize
370 })
371 .sum()
372 }
373
374 fn width_letter_wrap(&self, size: &Vec2) -> usize {
376 let mut guess = Vec2::new(self.size_letter_wrap(size.y), 0);
377 while self.height_letter_wrap(&guess) > size.y {
378 guess.x += 1;
379 }
380 guess.x
381 }
382
383 fn size_letter_wrap(&self, size: usize) -> usize {
385 (self.text.chars().count() as f32 / size as f32).ceil() as usize
386 }
387}
388
389pub trait ToSpan {
395 fn style<T>(self, style: T) -> Span
397 where
398 T: Into<Style>;
399
400 fn fg<T>(self, fg: T) -> Span
402 where
403 T: Into<Option<Color>>;
404
405 fn bg<T>(self, bg: T) -> Span
407 where
408 T: Into<Option<Color>>;
409
410 fn modifier(self, modifier: Modifier) -> Span;
412
413 fn add_modifier(self, flag: Modifier) -> Span;
415
416 fn align(self, align: TextAlign) -> Span;
418
419 fn wrap(self, wrap: Wrap) -> Span;
421
422 fn ellipsis<T>(self, ellipsis: T) -> Span
424 where
425 T: AsRef<str>;
426
427 fn to_span(self) -> Span;
429}
430
431impl<T> ToSpan for &T
432where
433 T: std::fmt::Display,
434{
435 fn style<S>(self, style: S) -> Span
436 where
437 S: Into<Style>,
438 {
439 Span::new(self.to_string()).style(style)
440 }
441
442 fn fg<C>(self, fg: C) -> Span
443 where
444 C: Into<Option<Color>>,
445 {
446 Span::new(self.to_string()).fg(fg)
447 }
448
449 fn bg<C>(self, bg: C) -> Span
450 where
451 C: Into<Option<Color>>,
452 {
453 Span::new(self.to_string()).bg(bg)
454 }
455
456 fn modifier(self, modifier: Modifier) -> Span {
457 Span::new(self.to_string()).modifier(modifier)
458 }
459
460 fn add_modifier(self, flag: Modifier) -> Span {
461 Span::new(self.to_string()).add_modifier(flag)
462 }
463
464 fn align(self, align: TextAlign) -> Span {
465 Span::new(self.to_string()).align(align)
466 }
467
468 fn wrap(self, wrap: Wrap) -> Span {
469 Span::new(self.to_string()).wrap(wrap)
470 }
471
472 fn ellipsis<R>(self, ellipsis: R) -> Span
473 where
474 R: AsRef<str>,
475 {
476 Span::new(self.to_string()).ellipsis(ellipsis.as_ref())
477 }
478
479 fn to_span(self) -> Span {
480 Span::new(self.to_string())
481 }
482}
483
484impl<T> From<T> for Span
486where
487 T: AsRef<str>,
488{
489 fn from(value: T) -> Self {
490 Span::new(value)
491 }
492}
493
494impl<T> From<T> for Box<dyn Widget>
495where
496 T: AsRef<str>,
497{
498 fn from(value: T) -> Self {
499 Box::new(Span::new(value.as_ref()))
500 }
501}
502
503impl<T> From<T> for Box<dyn Text>
504where
505 T: AsRef<str>,
506{
507 fn from(value: T) -> Self {
508 Box::new(Span::new(value))
509 }
510}
511
512impl<T> From<T> for Element
513where
514 T: AsRef<str>,
515{
516 fn from(value: T) -> Self {
517 Element::new(Span::new(value))
518 }
519}
520
521impl From<Span> for Box<dyn Widget> {
522 fn from(value: Span) -> Self {
523 Box::new(value)
524 }
525}
526
527impl From<Span> for Box<dyn Text> {
528 fn from(value: Span) -> Self {
529 Box::new(value)
530 }
531}
532
533impl From<Span> for Element {
534 fn from(value: Span) -> Self {
535 Element::new(value)
536 }
537}