feat: implement UnicodeWidthStr for Text/Line/Span (#2030)

You can now calculate the width of any Text/Line/Span using the
UnicodeWidthStr trait instead of the width method on the type. This also
makes it possible to use the width_cjk() method if needed.
This commit is contained in:
Josh McKinney 2025-08-01 10:50:09 -07:00 committed by GitHub
parent 5e75278499
commit 8188ed3950
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 46 additions and 3 deletions

View File

@ -7,6 +7,7 @@ use alloc::vec::Vec;
use core::fmt; use core::fmt;
use unicode_truncate::UnicodeTruncateStr; use unicode_truncate::UnicodeTruncateStr;
use unicode_width::UnicodeWidthStr;
use crate::buffer::Buffer; use crate::buffer::Buffer;
use crate::layout::{Alignment, Rect}; use crate::layout::{Alignment, Rect};
@ -435,8 +436,9 @@ impl<'a> Line<'a> {
/// let line = Line::from(vec!["Hello".blue(), " world!".green()]); /// let line = Line::from(vec!["Hello".blue(), " world!".green()]);
/// assert_eq!(12, line.width()); /// assert_eq!(12, line.width());
/// ``` /// ```
#[must_use]
pub fn width(&self) -> usize { pub fn width(&self) -> usize {
self.spans.iter().map(Span::width).sum() UnicodeWidthStr::width(self)
} }
/// Returns an iterator over the graphemes held by this line. /// Returns an iterator over the graphemes held by this line.
@ -562,6 +564,16 @@ impl<'a> Line<'a> {
} }
} }
impl UnicodeWidthStr for Line<'_> {
fn width(&self) -> usize {
self.spans.iter().map(UnicodeWidthStr::width).sum()
}
fn width_cjk(&self) -> usize {
self.spans.iter().map(UnicodeWidthStr::width_cjk).sum()
}
}
impl<'a> IntoIterator for Line<'a> { impl<'a> IntoIterator for Line<'a> {
type Item = Span<'a>; type Item = Span<'a>;
type IntoIter = alloc::vec::IntoIter<Span<'a>>; type IntoIter = alloc::vec::IntoIter<Span<'a>>;

View File

@ -269,7 +269,7 @@ impl<'a> Span<'a> {
/// Returns the unicode width of the content held by this span. /// Returns the unicode width of the content held by this span.
pub fn width(&self) -> usize { pub fn width(&self) -> usize {
self.content.width() UnicodeWidthStr::width(self)
} }
/// Returns an iterator over the graphemes held by this span. /// Returns an iterator over the graphemes held by this span.
@ -376,6 +376,16 @@ impl<'a> Span<'a> {
} }
} }
impl UnicodeWidthStr for Span<'_> {
fn width(&self) -> usize {
self.content.width()
}
fn width_cjk(&self) -> usize {
self.content.width_cjk()
}
}
impl<'a, T> From<T> for Span<'a> impl<'a, T> From<T> for Span<'a>
where where
T: Into<Cow<'a, str>>, T: Into<Cow<'a, str>>,

View File

@ -5,6 +5,8 @@ use alloc::vec;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::fmt; use core::fmt;
use unicode_width::UnicodeWidthStr;
use crate::buffer::Buffer; use crate::buffer::Buffer;
use crate::layout::{Alignment, Rect}; use crate::layout::{Alignment, Rect};
use crate::style::{Style, Styled}; use crate::style::{Style, Styled};
@ -284,7 +286,7 @@ impl<'a> Text<'a> {
/// assert_eq!(15, text.width()); /// assert_eq!(15, text.width());
/// ``` /// ```
pub fn width(&self) -> usize { pub fn width(&self) -> usize {
self.iter().map(Line::width).max().unwrap_or_default() UnicodeWidthStr::width(self)
} }
/// Returns the height. /// Returns the height.
@ -559,6 +561,25 @@ impl<'a> Text<'a> {
} }
} }
impl UnicodeWidthStr for Text<'_> {
/// Returns the max width of all the lines.
fn width(&self) -> usize {
self.lines
.iter()
.map(UnicodeWidthStr::width)
.max()
.unwrap_or_default()
}
fn width_cjk(&self) -> usize {
self.lines
.iter()
.map(UnicodeWidthStr::width_cjk)
.max()
.unwrap_or_default()
}
}
impl<'a> IntoIterator for Text<'a> { impl<'a> IntoIterator for Text<'a> {
type Item = Line<'a>; type Item = Line<'a>;
type IntoIter = alloc::vec::IntoIter<Self::Item>; type IntoIter = alloc::vec::IntoIter<Self::Item>;