fix(text): truncate based on alignment (#1432)

This is a follow-up PR to https://github.com/ratatui/ratatui/pull/987,
which implemented alignment-aware truncation for the `Line` widget.
However, the truncation only checked the `Line::alignment` field, and
any alignment inherited from a parent's `Text::alignment` field would
not be used.

This commit updates the truncation of `Line` to depend both on the
individual `Line::alignment`, and on any alignment inherited from the
parent's `Text::alignment`.

Co-authored-by: Josh McKinney <joshka@users.noreply.github.com>
This commit is contained in:
Eric Lunderberg 2024-10-19 11:16:44 -05:00 committed by GitHub
parent 381ec75329
commit a52ee82fc7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 48 additions and 19 deletions

21
src/text/line.rs Normal file → Executable file
View file

@ -687,6 +687,21 @@ impl Widget for Line<'_> {
impl WidgetRef for Line<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
self.render_with_alignment(area, buf, None);
}
}
impl Line<'_> {
/// An internal implementation method for `WidgetRef::render_ref`
///
/// Allows the parent widget to define a default alignment, to be
/// used if `Line::alignment` is `None`.
pub(crate) fn render_with_alignment(
&self,
area: Rect,
buf: &mut Buffer,
parent_alignment: Option<Alignment>,
) {
let area = area.intersection(buf.area);
if area.is_empty() {
return;
@ -699,10 +714,12 @@ impl WidgetRef for Line<'_> {
buf.set_style(area, self.style);
let alignment = self.alignment.or(parent_alignment);
let area_width = usize::from(area.width);
let can_render_complete_line = line_width <= area_width;
if can_render_complete_line {
let indent_width = match self.alignment {
let indent_width = match alignment {
Some(Alignment::Center) => (area_width.saturating_sub(line_width)) / 2,
Some(Alignment::Right) => area_width.saturating_sub(line_width),
Some(Alignment::Left) | None => 0,
@ -713,7 +730,7 @@ impl WidgetRef for Line<'_> {
} else {
// There is not enough space to render the whole line. As the right side is truncated by
// the area width, only truncate the left.
let skip_width = match self.alignment {
let skip_width = match alignment {
Some(Alignment::Center) => (line_width.saturating_sub(area_width)) / 2,
Some(Alignment::Right) => line_width.saturating_sub(area_width),
Some(Alignment::Left) | None => 0,

46
src/text/text.rs Normal file → Executable file
View file

@ -735,23 +735,8 @@ impl WidgetRef for Text<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
let area = area.intersection(buf.area);
buf.set_style(area, self.style);
for (line, row) in self.iter().zip(area.rows()) {
let line_width = line.width() as u16;
let x_offset = match (self.alignment, line.alignment) {
(Some(Alignment::Center), None) => area.width.saturating_sub(line_width) / 2,
(Some(Alignment::Right), None) => area.width.saturating_sub(line_width),
_ => 0,
};
let line_area = Rect {
x: area.x + x_offset,
y: row.y,
width: area.width - x_offset,
height: 1,
};
line.render(line_area, buf);
for (line, line_area) in self.iter().zip(area.rows()) {
line.render_with_alignment(line_area, buf, self.alignment);
}
}
}
@ -1196,6 +1181,33 @@ mod tests {
assert_eq!(buf, Buffer::with_lines([" foo "]));
}
#[test]
fn render_right_aligned_with_truncation() {
let text = Text::from("123456789").alignment(Alignment::Right);
let area = Rect::new(0, 0, 5, 1);
let mut buf = Buffer::empty(area);
text.render(area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["56789"]));
}
#[test]
fn render_centered_odd_with_truncation() {
let text = Text::from("123456789").alignment(Alignment::Center);
let area = Rect::new(0, 0, 5, 1);
let mut buf = Buffer::empty(area);
text.render(area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["34567"]));
}
#[test]
fn render_centered_even_with_truncation() {
let text = Text::from("123456789").alignment(Alignment::Center);
let area = Rect::new(0, 0, 6, 1);
let mut buf = Buffer::empty(area);
text.render(area, &mut buf);
assert_eq!(buf, Buffer::with_lines(["234567"]));
}
#[test]
fn render_one_line_right() {
let text = Text::from(vec![