Skip to content

Commit

Permalink
feat(list): support line alignment (#599)
Browse files Browse the repository at this point in the history
The `List` widget now respects the alignment of `Line`s and renders them as expected.
  • Loading branch information
orhun committed Nov 23, 2023
1 parent 18e19f6 commit c862aa5
Showing 1 changed file with 166 additions and 2 deletions.
168 changes: 166 additions & 2 deletions src/widgets/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use unicode_width::UnicodeWidthStr;

use crate::{
buffer::Buffer,
layout::{Corner, Rect},
layout::{Alignment, Corner, Rect},
style::{Style, Styled},
text::Text,
widgets::{Block, HighlightSpacing, StatefulWidget, Widget},
Expand Down Expand Up @@ -286,7 +286,14 @@ impl<'a> StatefulWidget for List<'a> {
} else {
(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 {
buf.set_style(area, self.highlight_style);
Expand Down Expand Up @@ -333,6 +340,7 @@ mod tests {
use super::*;
use crate::{
assert_buffer_eq,
prelude::Alignment,
style::{Color, Modifier, Stylize},
text::{Line, Span},
widgets::{Borders, StatefulWidget, Widget},
Expand Down Expand Up @@ -1065,4 +1073,160 @@ mod tests {
.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);
}
}

0 comments on commit c862aa5

Please sign in to comment.