use unicode_width::UnicodeWidthStr; use crate::{ prelude::*, text::StyledGrapheme, widgets::{ reflow::{LineComposer, LineTruncator, WordWrapper, WrappedLine}, Block, Widget, }, }; fn get_line_offset(line_width: u16, text_area_width: u16, alignment: Alignment) -> u16 { match alignment { Alignment::Center => (text_area_width / 2).saturating_sub(line_width / 2), Alignment::Right => text_area_width.saturating_sub(line_width), Alignment::Left => 0, } } /// A widget to display some text. /// /// # Example /// /// ``` /// use ratatui::{prelude::*, widgets::*}; /// /// let text = vec![ /// Line::from(vec![ /// Span::raw("First"), /// Span::styled("line", Style::new().green().italic()), /// ".".into(), /// ]), /// Line::from("Second line".red()), /// "Third line".into(), /// ]; /// Paragraph::new(text) /// .block(Block::new().title("Paragraph").borders(Borders::ALL)) /// .style(Style::new().white().on_black()) /// .alignment(Alignment::Center) /// .wrap(Wrap { trim: true }); /// ``` #[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] pub struct Paragraph<'a> { /// A block to wrap the widget in block: Option>, /// Widget style style: Style, /// How to wrap the text wrap: Option, /// The text to display text: Text<'a>, /// Scroll scroll: (u16, u16), /// Alignment of the text alignment: Alignment, } /// Describes how to wrap text across lines. /// /// ## Examples /// /// ``` /// use ratatui::{prelude::*, widgets::*}; /// /// let bullet_points = Text::from( /// r#"Some indented points: /// - First thing goes here and is long so that it wraps /// - Here is another point that is long enough to wrap"#, /// ); /// /// // With leading spaces trimmed (window width of 30 chars): /// Paragraph::new(bullet_points.clone()).wrap(Wrap { trim: true }); /// // Some indented points: /// // - First thing goes here and is /// // long so that it wraps /// // - Here is another point that /// // is long enough to wrap /// /// // But without trimming, indentation is preserved: /// Paragraph::new(bullet_points).wrap(Wrap { trim: false }); /// // Some indented points: /// // - First thing goes here /// // and is long so that it wraps /// // - Here is another point /// // that is long enough to wrap /// ``` #[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] pub struct Wrap { /// Should leading whitespace be trimmed pub trim: bool, } type Horizontal = u16; type Vertical = u16; impl<'a> Paragraph<'a> { /// Creates a new [`Paragraph`] widget with the given text. /// /// The `text` parameter can be a [`Text`] or any type that can be converted into a [`Text`]. By /// default, the text is styled with [`Style::default()`], not wrapped, and aligned to the left. /// /// # Examples /// /// ```rust /// # use ratatui::{prelude::*, widgets::*}; /// let paragraph = Paragraph::new("Hello, world!"); /// let paragraph = Paragraph::new(String::from("Hello, world!")); /// let paragraph = Paragraph::new(Text::raw("Hello, world!")); /// let paragraph = Paragraph::new(Text::styled("Hello, world!", Style::default())); /// let paragraph = Paragraph::new(Line::from(vec!["Hello, ".into(), "world!".red()])); /// ``` pub fn new(text: T) -> Paragraph<'a> where T: Into>, { Paragraph { block: None, style: Style::default(), wrap: None, text: text.into(), scroll: (0, 0), alignment: Alignment::Left, } } /// Surrounds the [`Paragraph`] widget with a [`Block`]. /// /// # Example /// /// ```rust /// # use ratatui::{prelude::*, widgets::*}; /// let paragraph = Paragraph::new("Hello, world!") /// .block(Block::default().title("Paragraph").borders(Borders::ALL)); /// ``` #[must_use = "method moves the value of self and returns the modified value"] pub fn block(mut self, block: Block<'a>) -> Paragraph<'a> { self.block = Some(block); self } /// Sets the style of the entire widget. /// /// `style` accepts any type that is convertible to [`Style`] (e.g. [`Style`], [`Color`], or /// your own type that implements [`Into