Skip to content

Commit

Permalink
feat(Paragraph): add line_count and line_width unstable helper me…
Browse files Browse the repository at this point in the history
…thods

This is an unstable feature that may be removed in the future
  • Loading branch information
TylerBloom authored Dec 7, 2023
1 parent 3ec4e24 commit 8bfd666
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 1 deletion.
8 changes: 7 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,18 @@ underline-color = ["dep:crossterm"]
#! The following features are unstable and may change in the future:

## Enable all unstable features.
unstable = ["unstable-segment-size"]
unstable = ["unstable-segment-size", "unstable-rendered-line-info"]

## Enables the [`Layout::segment_size`](crate::layout::Layout::segment_size) method which is experimental and may change in the
## future. See [Issue #536](https://github.com/ratatui-org/ratatui/issues/536) for more details.
unstable-segment-size = []

## Enables the [`Paragraph::line_count`](crate::widgets::Paragraph::line_count)
## [`Paragraph::line_width`](crate::widgets::Paragraph::line_width) methods
## which are experimental and may change in the future.
## See [Issue 293](https://github.com/ratatui-org/ratatui/issues/293) for more details.
unstable-rendered-line-info = []

[package.metadata.docs.rs]
all-features = true
# see https://doc.rust-lang.org/nightly/rustdoc/scraped-examples.html
Expand Down
113 changes: 113 additions & 0 deletions src/widgets/paragraph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,77 @@ impl<'a> Paragraph<'a> {
self.alignment = alignment;
self
}

/// Calculates the number of lines needed to fully render.
///
/// Given a max line width, this method calculates the number of lines that a paragraph will
/// need in order to be fully rendered. For paragraphs that do not use wrapping, this count is
/// simply the number of lines present in the paragraph.
///
/// # Example
///
/// ```ignore
/// # use ratatui::{prelude::*, widgets::*};
/// let paragraph = Paragraph::new("Hello World")
/// .wrap(Wrap { trim: false });
/// assert_eq!(paragraph.line_count(20), 1);
/// assert_eq!(paragraph.line_count(10), 2);
/// ```
#[stability::unstable(
feature = "rendered-line-info",
reason = "The design for text wrapping is not stable and might affect this API.",
issue = "https://github.com/ratatui-org/ratatui/issues/293"
)]
pub fn line_count(&self, width: u16) -> usize {
if width < 1 {
return 0;
}

if let Some(Wrap { trim }) = self.wrap {
let styled = self.text.lines.iter().map(|line| {
let graphemes = line
.spans
.iter()
.flat_map(|span| span.styled_graphemes(self.style));
let alignment = line.alignment.unwrap_or(self.alignment);
(graphemes, alignment)
});
let mut line_composer = WordWrapper::new(styled, width, trim);
let mut count = 0;
while line_composer.next_line().is_some() {
count += 1;
}
count
} else {
self.text.lines.len()
}
}

/// Calculates the shortest line width needed to avoid any word being wrapped or truncated.
///
/// # Example
///
/// ```ignore
/// # use ratatui::{prelude::*, widgets::*};
/// let paragraph = Paragraph::new("Hello World");
/// assert_eq!(paragraph.line_width(), 11);
///
/// let paragraph = Paragraph::new("Hello World\nhi\nHello World!!!");
/// assert_eq!(paragraph.line_width(), 14);
/// ```
#[stability::unstable(
feature = "rendered-line-info",
reason = "The design for text wrapping is not stable and might affect this API.",
issue = "https://github.com/ratatui-org/ratatui/issues/293"
)]
pub fn line_width(&self) -> usize {
self.text
.lines
.iter()
.map(|l| l.width())
.max()
.unwrap_or_default()
}
}

impl<'a> Widget for Paragraph<'a> {
Expand Down Expand Up @@ -815,4 +886,46 @@ mod test {
.remove_modifier(Modifier::DIM)
)
}

#[test]
fn widgets_paragraph_count_rendered_lines() {
let paragraph = Paragraph::new("Hello World");
assert_eq!(paragraph.line_count(20), 1);
assert_eq!(paragraph.line_count(10), 1);
let paragraph = Paragraph::new("Hello World").wrap(Wrap { trim: false });
assert_eq!(paragraph.line_count(20), 1);
assert_eq!(paragraph.line_count(10), 2);
let paragraph = Paragraph::new("Hello World").wrap(Wrap { trim: true });
assert_eq!(paragraph.line_count(20), 1);
assert_eq!(paragraph.line_count(10), 2);

let text = "Hello World ".repeat(100);
let paragraph = Paragraph::new(text.trim());
assert_eq!(paragraph.line_count(11), 1);
assert_eq!(paragraph.line_count(6), 1);
let paragraph = paragraph.wrap(Wrap { trim: false });
assert_eq!(paragraph.line_count(11), 100);
assert_eq!(paragraph.line_count(6), 200);
let paragraph = paragraph.wrap(Wrap { trim: true });
assert_eq!(paragraph.line_count(11), 100);
assert_eq!(paragraph.line_count(6), 200);
}

#[test]
fn widgets_paragraph_line_width() {
let paragraph = Paragraph::new("Hello World");
assert_eq!(paragraph.line_width(), 11);
let paragraph = Paragraph::new("Hello World").wrap(Wrap { trim: false });
assert_eq!(paragraph.line_width(), 11);
let paragraph = Paragraph::new("Hello World").wrap(Wrap { trim: true });
assert_eq!(paragraph.line_width(), 11);

let text = "Hello World ".repeat(100);
let paragraph = Paragraph::new(text);
assert_eq!(paragraph.line_width(), 1200);
let paragraph = paragraph.wrap(Wrap { trim: false });
assert_eq!(paragraph.line_width(), 1200);
let paragraph = paragraph.wrap(Wrap { trim: true });
assert_eq!(paragraph.line_width(), 1200);
}
}

0 comments on commit 8bfd666

Please sign in to comment.