feat(list): support line alignment (#599)

The `List` widget now respects the alignment of `Line`s and renders them as expected.
This commit is contained in:
Orhun Parmaksız 2023-11-23 20:52:12 +01:00 committed by GitHub
parent 18e19f6ce6
commit c862aa5e9e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -2,7 +2,7 @@ use unicode_width::UnicodeWidthStr;
use crate::{ use crate::{
buffer::Buffer, buffer::Buffer,
layout::{Corner, Rect}, layout::{Alignment, Corner, Rect},
style::{Style, Styled}, style::{Style, Styled},
text::Text, text::Text,
widgets::{Block, HighlightSpacing, StatefulWidget, Widget}, widgets::{Block, HighlightSpacing, StatefulWidget, Widget},
@ -286,7 +286,14 @@ impl<'a> StatefulWidget for List<'a> {
} else { } else {
(x, list_area.width) (x, list_area.width)
}; };
buf.set_line(elem_x, y + j as u16, line, max_element_width); let x_offset = match line.alignment {
Some(Alignment::Center) => {
(area.width / 2).saturating_sub(line.width() as u16 / 2)
}
Some(Alignment::Right) => area.width.saturating_sub(line.width() as u16),
_ => 0,
};
buf.set_line(elem_x + x_offset, y + j as u16, line, max_element_width);
} }
if is_selected { if is_selected {
buf.set_style(area, self.highlight_style); buf.set_style(area, self.highlight_style);
@ -333,6 +340,7 @@ mod tests {
use super::*; use super::*;
use crate::{ use crate::{
assert_buffer_eq, assert_buffer_eq,
prelude::Alignment,
style::{Color, Modifier, Stylize}, style::{Color, Modifier, Stylize},
text::{Line, Span}, text::{Line, Span},
widgets::{Borders, StatefulWidget, Widget}, widgets::{Borders, StatefulWidget, Widget},
@ -1065,4 +1073,160 @@ mod tests {
.remove_modifier(Modifier::DIM) .remove_modifier(Modifier::DIM)
) )
} }
#[test]
fn test_render_list_with_alignment() {
let items = [
Line::from("Left").alignment(Alignment::Left),
Line::from("Center").alignment(Alignment::Center),
Line::from("Right").alignment(Alignment::Right),
]
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(items);
let buffer = render_widget(list, 10, 5);
let expected = Buffer::with_lines(vec![
"Left ",
" Center ",
" Right",
" ",
" ",
]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_render_list_alignment_odd_line_odd_area() {
let items = [
Line::from("Odd").alignment(Alignment::Left),
Line::from("Even").alignment(Alignment::Center),
Line::from("Width").alignment(Alignment::Right),
]
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(items);
let buffer = render_widget(list, 7, 5);
let expected =
Buffer::with_lines(vec!["Odd ", " Even ", " Width", " ", " "]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_render_list_alignment_even_line_even_area() {
let items = [
Line::from("Odd").alignment(Alignment::Left),
Line::from("Even").alignment(Alignment::Center),
Line::from("Width").alignment(Alignment::Right),
]
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(items);
let buffer = render_widget(list, 6, 4);
let expected = Buffer::with_lines(vec!["Odd ", " Even ", " Width", " "]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_render_list_alignment_odd_line_even_area() {
let items = [
Line::from("Odd").alignment(Alignment::Left),
Line::from("Even").alignment(Alignment::Center),
Line::from("Width").alignment(Alignment::Right),
]
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(items);
let buffer = render_widget(list, 8, 4);
let expected = Buffer::with_lines(vec!["Odd ", " Even ", " Width", " "]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_render_list_alignment_even_line_odd_area() {
let items = [
Line::from("Odd").alignment(Alignment::Left),
Line::from("Even").alignment(Alignment::Center),
Line::from("Width").alignment(Alignment::Right),
]
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(items);
let buffer = render_widget(list, 6, 5);
let expected = Buffer::with_lines(vec!["Odd ", " Even ", " Width", " ", " "]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_render_list_alignment_zero_line_width() {
let items = [Line::from("This line has zero width").alignment(Alignment::Center)]
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(items);
let buffer = render_widget(list, 0, 5);
let expected = Buffer::with_lines(vec!["", "", "", "", ""]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_render_list_alignment_zero_area_width() {
let items = [Line::from("Text").alignment(Alignment::Left)]
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(items);
// assert_buffer_eq! doesn't handle zero height buffers so we call this test manually
// rather than using render_widget
let mut buffer = Buffer::empty(Rect::new(0, 0, 4, 1));
Widget::render(list, Rect::new(0, 0, 4, 0), &mut buffer);
let expected = Buffer::with_lines(vec![" "]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_render_list_alignment_line_less_than_width() {
let items = [Line::from("Small").alignment(Alignment::Center)]
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(items);
let buffer = render_widget(list, 10, 5);
let expected = Buffer::with_lines(vec![
" Small ",
" ",
" ",
" ",
" ",
]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_render_list_alignment_line_equal_to_width() {
let items = [Line::from("Exact").alignment(Alignment::Left)]
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(items);
let buffer = render_widget(list, 5, 3);
let expected = Buffer::with_lines(vec!["Exact", " ", " "]);
assert_buffer_eq!(buffer, expected);
}
#[test]
fn test_render_list_alignment_line_greater_than_width() {
let items = [Line::from("Large line").alignment(Alignment::Left)]
.into_iter()
.map(ListItem::new)
.collect::<Vec<ListItem>>();
let list = List::new(items);
let buffer = render_widget(list, 5, 3);
let expected = Buffer::with_lines(vec!["Large", " ", " "]);
assert_buffer_eq!(buffer, expected);
}
} }