diff options
| author | Conrad Ludgate <conradludgate@gmail.com> | 2023-03-23 09:19:29 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-23 09:19:29 +0000 |
| commit | ba1d615f5e5d904a3bd7a7f5f0ce336b89995aea (patch) | |
| tree | db84efdef064426a9c2e32b897bb39c06fe4145e /src/tui/text.rs | |
| parent | Allow changing search_mode during interactive search (#586) (diff) | |
| download | atuin-ba1d615f5e5d904a3bd7a7f5f0ce336b89995aea.zip | |
chore: remove tui vendoring (#804)
Diffstat (limited to 'src/tui/text.rs')
| -rw-r--r-- | src/tui/text.rs | 428 |
1 files changed, 0 insertions, 428 deletions
diff --git a/src/tui/text.rs b/src/tui/text.rs deleted file mode 100644 index e8a5aa73..00000000 --- a/src/tui/text.rs +++ /dev/null @@ -1,428 +0,0 @@ -//! Primitives for styled text. -//! -//! A terminal UI is at its root a lot of strings. In order to make it accessible and stylish, -//! those strings may be associated to a set of styles. `tui` has three ways to represent them: -//! - A single line string where all graphemes have the same style is represented by a [`Span`]. -//! - A single line string where each grapheme may have its own style is represented by [`Spans`]. -//! - A multiple line string where each grapheme may have its own style is represented by a -//! [`Text`]. -//! -//! These types form a hierarchy: [`Spans`] is a collection of [`Span`] and each line of [`Text`] -//! is a [`Spans`]. -//! -//! Keep it mind that a lot of widgets will use those types to advertise what kind of string is -//! supported for their properties. Moreover, `tui` provides convenient `From` implementations so -//! that you can start by using simple `String` or `&str` and then promote them to the previous -//! primitives when you need additional styling capabilities. -//! -//! For example, for the [`crate::widgets::Block`] widget, all the following calls are valid to set -//! its `title` property (which is a [`Spans`] under the hood): -//! -//! ```rust -//! # use tui::widgets::Block; -//! # use tui::text::{Span, Spans}; -//! # use tui::style::{Color, Style}; -//! // A simple string with no styling. -//! // Converted to Spans(vec![ -//! // Span { content: Cow::Borrowed("My title"), style: Style { .. } } -//! // ]) -//! let block = Block::default().title("My title"); -//! -//! // A simple string with a unique style. -//! // Converted to Spans(vec![ -//! // Span { content: Cow::Borrowed("My title"), style: Style { fg: Some(Color::Yellow), .. } -//! // ]) -//! let block = Block::default().title( -//! Span::styled("My title", Style::default().fg(Color::Yellow)) -//! ); -//! -//! // A string with multiple styles. -//! // Converted to Spans(vec![ -//! // Span { content: Cow::Borrowed("My"), style: Style { fg: Some(Color::Yellow), .. } }, -//! // Span { content: Cow::Borrowed(" title"), .. } -//! // ]) -//! let block = Block::default().title(vec![ -//! Span::styled("My", Style::default().fg(Color::Yellow)), -//! Span::raw(" title"), -//! ]); -//! ``` -use crate::tui::style::Style; -use std::borrow::Cow; -use unicode_segmentation::UnicodeSegmentation; -use unicode_width::UnicodeWidthStr; - -/// A grapheme associated to a style. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct StyledGrapheme<'a> { - pub symbol: &'a str, - pub style: Style, -} - -/// A string where all graphemes have the same style. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct Span<'a> { - pub content: Cow<'a, str>, - pub style: Style, -} - -impl<'a> Span<'a> { - /// Create a span with no style. - /// - /// ## Examples - /// - /// ```rust - /// # use tui::text::Span; - /// Span::raw("My text"); - /// Span::raw(String::from("My text")); - /// ``` - pub fn raw<T>(content: T) -> Span<'a> - where - T: Into<Cow<'a, str>>, - { - Span { - content: content.into(), - style: Style::default(), - } - } - - /// Create a span with a style. - /// - /// # Examples - /// - /// ```rust - /// # use tui::text::Span; - /// # use tui::style::{Color, Modifier, Style}; - /// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC); - /// Span::styled("My text", style); - /// Span::styled(String::from("My text"), style); - /// ``` - pub fn styled<T>(content: T, style: Style) -> Span<'a> - where - T: Into<Cow<'a, str>>, - { - Span { - content: content.into(), - style, - } - } - - /// Returns the width of the content held by this span. - pub fn width(&self) -> usize { - self.content.width() - } - - /// Returns an iterator over the graphemes held by this span. - /// - /// `base_style` is the [`Style`] that will be patched with each grapheme [`Style`] to get - /// the resulting [`Style`]. - /// - /// ## Examples - /// - /// ```rust - /// # use tui::text::{Span, StyledGrapheme}; - /// # use tui::style::{Color, Modifier, Style}; - /// # use std::iter::Iterator; - /// let style = Style::default().fg(Color::Yellow); - /// let span = Span::styled("Text", style); - /// let style = Style::default().fg(Color::Green).bg(Color::Black); - /// let styled_graphemes = span.styled_graphemes(style); - /// assert_eq!( - /// vec![ - /// StyledGrapheme { - /// symbol: "T", - /// style: Style { - /// fg: Some(Color::Yellow), - /// bg: Some(Color::Black), - /// add_modifier: Modifier::empty(), - /// sub_modifier: Modifier::empty(), - /// }, - /// }, - /// StyledGrapheme { - /// symbol: "e", - /// style: Style { - /// fg: Some(Color::Yellow), - /// bg: Some(Color::Black), - /// add_modifier: Modifier::empty(), - /// sub_modifier: Modifier::empty(), - /// }, - /// }, - /// StyledGrapheme { - /// symbol: "x", - /// style: Style { - /// fg: Some(Color::Yellow), - /// bg: Some(Color::Black), - /// add_modifier: Modifier::empty(), - /// sub_modifier: Modifier::empty(), - /// }, - /// }, - /// StyledGrapheme { - /// symbol: "t", - /// style: Style { - /// fg: Some(Color::Yellow), - /// bg: Some(Color::Black), - /// add_modifier: Modifier::empty(), - /// sub_modifier: Modifier::empty(), - /// }, - /// }, - /// ], - /// styled_graphemes.collect::<Vec<StyledGrapheme>>() - /// ); - /// ``` - pub fn styled_graphemes( - &'a self, - base_style: Style, - ) -> impl Iterator<Item = StyledGrapheme<'a>> { - UnicodeSegmentation::graphemes(self.content.as_ref(), true) - .map(move |g| StyledGrapheme { - symbol: g, - style: base_style.patch(self.style), - }) - .filter(|s| s.symbol != "\n") - } -} - -impl<'a> From<String> for Span<'a> { - fn from(s: String) -> Span<'a> { - Span::raw(s) - } -} - -impl<'a> From<&'a str> for Span<'a> { - fn from(s: &'a str) -> Span<'a> { - Span::raw(s) - } -} - -/// A string composed of clusters of graphemes, each with their own style. -#[derive(Debug, Clone, PartialEq, Default, Eq)] -pub struct Spans<'a>(pub Vec<Span<'a>>); - -impl<'a> Spans<'a> { - /// Returns the width of the underlying string. - /// - /// ## Examples - /// - /// ```rust - /// # use tui::text::{Span, Spans}; - /// # use tui::style::{Color, Style}; - /// let spans = Spans::from(vec![ - /// Span::styled("My", Style::default().fg(Color::Yellow)), - /// Span::raw(" text"), - /// ]); - /// assert_eq!(7, spans.width()); - /// ``` - pub fn width(&self) -> usize { - self.0.iter().map(Span::width).sum() - } -} - -impl<'a> From<String> for Spans<'a> { - fn from(s: String) -> Spans<'a> { - Spans(vec![Span::from(s)]) - } -} - -impl<'a> From<&'a str> for Spans<'a> { - fn from(s: &'a str) -> Spans<'a> { - Spans(vec![Span::from(s)]) - } -} - -impl<'a> From<Vec<Span<'a>>> for Spans<'a> { - fn from(spans: Vec<Span<'a>>) -> Spans<'a> { - Spans(spans) - } -} - -impl<'a> From<Span<'a>> for Spans<'a> { - fn from(span: Span<'a>) -> Spans<'a> { - Spans(vec![span]) - } -} - -impl<'a> From<Spans<'a>> for String { - fn from(line: Spans<'a>) -> String { - line.0.iter().fold(String::new(), |mut acc, s| { - acc.push_str(s.content.as_ref()); - acc - }) - } -} - -/// A string split over multiple lines where each line is composed of several clusters, each with -/// their own style. -/// -/// A [`Text`], like a [`Span`], can be constructed using one of the many `From` implementations -/// or via the [`Text::raw`] and [`Text::styled`] methods. Helpfully, [`Text`] also implements -/// [`core::iter::Extend`] which enables the concatenation of several [`Text`] blocks. -/// -/// ```rust -/// # use tui::text::Text; -/// # use tui::style::{Color, Modifier, Style}; -/// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC); -/// -/// // An initial two lines of `Text` built from a `&str` -/// let mut text = Text::from("The first line\nThe second line"); -/// assert_eq!(2, text.height()); -/// -/// // Adding two more unstyled lines -/// text.extend(Text::raw("These are two\nmore lines!")); -/// assert_eq!(4, text.height()); -/// -/// // Adding a final two styled lines -/// text.extend(Text::styled("Some more lines\nnow with more style!", style)); -/// assert_eq!(6, text.height()); -/// ``` -#[derive(Debug, Clone, PartialEq, Default, Eq)] -pub struct Text<'a> { - pub lines: Vec<Spans<'a>>, -} - -impl<'a> Text<'a> { - /// Create some text (potentially multiple lines) with no style. - /// - /// ## Examples - /// - /// ```rust - /// # use tui::text::Text; - /// Text::raw("The first line\nThe second line"); - /// Text::raw(String::from("The first line\nThe second line")); - /// ``` - pub fn raw<T>(content: T) -> Text<'a> - where - T: Into<Cow<'a, str>>, - { - Text { - lines: match content.into() { - Cow::Borrowed(s) => s.lines().map(Spans::from).collect(), - Cow::Owned(s) => s.lines().map(|l| Spans::from(l.to_owned())).collect(), - }, - } - } - - /// Create some text (potentially multiple lines) with a style. - /// - /// # Examples - /// - /// ```rust - /// # use tui::text::Text; - /// # use tui::style::{Color, Modifier, Style}; - /// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC); - /// Text::styled("The first line\nThe second line", style); - /// Text::styled(String::from("The first line\nThe second line"), style); - /// ``` - pub fn styled<T>(content: T, style: Style) -> Text<'a> - where - T: Into<Cow<'a, str>>, - { - let mut text = Text::raw(content); - text.patch_style(style); - text - } - - /// Returns the max width of all the lines. - /// - /// ## Examples - /// - /// ```rust - /// use tui::text::Text; - /// let text = Text::from("The first line\nThe second line"); - /// assert_eq!(15, text.width()); - /// ``` - pub fn width(&self) -> usize { - self.lines - .iter() - .map(Spans::width) - .max() - .unwrap_or_default() - } - - /// Returns the height. - /// - /// ## Examples - /// - /// ```rust - /// use tui::text::Text; - /// let text = Text::from("The first line\nThe second line"); - /// assert_eq!(2, text.height()); - /// ``` - pub fn height(&self) -> usize { - self.lines.len() - } - - /// Apply a new style to existing text. - /// - /// # Examples - /// - /// ```rust - /// # use tui::text::Text; - /// # use tui::style::{Color, Modifier, Style}; - /// let style = Style::default().fg(Color::Yellow).add_modifier(Modifier::ITALIC); - /// let mut raw_text = Text::raw("The first line\nThe second line"); - /// let styled_text = Text::styled(String::from("The first line\nThe second line"), style); - /// assert_ne!(raw_text, styled_text); - /// - /// raw_text.patch_style(style); - /// assert_eq!(raw_text, styled_text); - /// ``` - pub fn patch_style(&mut self, style: Style) { - for line in &mut self.lines { - for span in &mut line.0 { - span.style = span.style.patch(style); - } - } - } -} - -impl<'a> From<String> for Text<'a> { - fn from(s: String) -> Text<'a> { - Text::raw(s) - } -} - -impl<'a> From<&'a str> for Text<'a> { - fn from(s: &'a str) -> Text<'a> { - Text::raw(s) - } -} - -impl<'a> From<Cow<'a, str>> for Text<'a> { - fn from(s: Cow<'a, str>) -> Text<'a> { - Text::raw(s) - } -} - -impl<'a> From<Span<'a>> for Text<'a> { - fn from(span: Span<'a>) -> Text<'a> { - Text { - lines: vec![Spans::from(span)], - } - } -} - -impl<'a> From<Spans<'a>> for Text<'a> { - fn from(spans: Spans<'a>) -> Text<'a> { - Text { lines: vec![spans] } - } -} - -impl<'a> From<Vec<Spans<'a>>> for Text<'a> { - fn from(lines: Vec<Spans<'a>>) -> Text<'a> { - Text { lines } - } -} - -impl<'a> IntoIterator for Text<'a> { - type Item = Spans<'a>; - type IntoIter = std::vec::IntoIter<Self::Item>; - - fn into_iter(self) -> Self::IntoIter { - self.lines.into_iter() - } -} - -impl<'a> Extend<Spans<'a>> for Text<'a> { - fn extend<T: IntoIterator<Item = Spans<'a>>>(&mut self, iter: T) { - self.lines.extend(iter); - } -} |
