mirror of
https://github.com/ratatui-org/ratatui
synced 2024-11-10 07:04:17 +00:00
feat(barchart): render charts smaller than 3 lines (#532)
The bar values are not shown if the value width is equal the bar width and the bar is height is less than one line Add an internal structure `LabelInfo` which stores the reserved height for the labels (0, 1 or 2) and also whether the labels will be shown. Fixes ratatui-org#513 Signed-off-by: Ben Fekih, Hichem <hichem.f@live.de>
This commit is contained in:
parent
3bda372847
commit
301366c4fa
2 changed files with 327 additions and 244 deletions
|
@ -283,6 +283,12 @@ impl<'a> BarChart<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
struct LabelInfo {
|
||||
group_label_visible: bool,
|
||||
bar_label_visible: bool,
|
||||
height: u16,
|
||||
}
|
||||
|
||||
impl<'a> BarChart<'a> {
|
||||
/// Check the bars, which fits inside the available space and removes
|
||||
/// the bars and the groups, which are outside of the available space.
|
||||
|
@ -306,23 +312,43 @@ impl<'a> BarChart<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get the number of lines needed for the labels.
|
||||
/// Get label information.
|
||||
///
|
||||
/// The number of lines depends on whether we need to print the bar labels and/or the group
|
||||
/// labels.
|
||||
/// - If there are no labels, return 0.
|
||||
/// - If there are only bar labels, return 1.
|
||||
/// - If there are only group labels, return 1.
|
||||
/// - If there are both bar and group labels, return 2.
|
||||
fn label_height(&self) -> u16 {
|
||||
let has_group_labels = self.data.iter().any(|e| e.label.is_some());
|
||||
let has_data_labels = self
|
||||
/// height is the number of lines, which depends on whether we need to print the bar
|
||||
/// labels and/or the group labels.
|
||||
/// - If there are no labels, height is 0.
|
||||
/// - If there are only bar labels, height is 1.
|
||||
/// - If there are only group labels, height is 1.
|
||||
/// - If there are both bar and group labels, height is 2.
|
||||
fn label_info(&self, available_height: u16) -> LabelInfo {
|
||||
if available_height == 0 {
|
||||
return LabelInfo {
|
||||
group_label_visible: false,
|
||||
bar_label_visible: false,
|
||||
height: 0,
|
||||
};
|
||||
}
|
||||
|
||||
let bar_label_visible = self
|
||||
.data
|
||||
.iter()
|
||||
.any(|e| e.bars.iter().any(|e| e.label.is_some()));
|
||||
|
||||
// convert true to 1 and false to 0 and add the two values
|
||||
u16::from(has_group_labels) + u16::from(has_data_labels)
|
||||
if available_height == 1 && bar_label_visible {
|
||||
return LabelInfo {
|
||||
group_label_visible: false,
|
||||
bar_label_visible: true,
|
||||
height: 1,
|
||||
};
|
||||
}
|
||||
|
||||
let group_label_visible = self.data.iter().any(|e| e.label.is_some());
|
||||
LabelInfo {
|
||||
group_label_visible,
|
||||
bar_label_visible,
|
||||
// convert true to 1 and false to 0 and add the two values
|
||||
height: u16::from(group_label_visible) + u16::from(bar_label_visible),
|
||||
}
|
||||
}
|
||||
|
||||
/// renders the block if there is one and updates the area to the inner area
|
||||
|
@ -425,9 +451,16 @@ impl<'a> BarChart<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn render_vertical_bars(&self, buf: &mut Buffer, bars_area: Rect, max: u64) {
|
||||
fn render_vertical(self, buf: &mut Buffer, area: Rect, max: u64) {
|
||||
let label_info = self.label_info(area.height - 1);
|
||||
|
||||
let bars_area = Rect {
|
||||
height: area.height - label_info.height,
|
||||
..area
|
||||
};
|
||||
|
||||
// convert the bar values to ratatui::symbols::bar::Set
|
||||
let mut groups: Vec<Vec<u64>> = self
|
||||
let group_ticks: Vec<Vec<u64>> = self
|
||||
.data
|
||||
.iter()
|
||||
.map(|group| {
|
||||
|
@ -439,11 +472,17 @@ impl<'a> BarChart<'a> {
|
|||
})
|
||||
.collect();
|
||||
|
||||
self.render_vertical_bars(bars_area, buf, &group_ticks);
|
||||
self.render_labels_and_values(area, buf, label_info, &group_ticks);
|
||||
}
|
||||
|
||||
fn render_vertical_bars(&self, area: Rect, buf: &mut Buffer, group_ticks: &[Vec<u64>]) {
|
||||
// print all visible bars (without labels and values)
|
||||
for j in (0..bars_area.height).rev() {
|
||||
let mut bar_x = bars_area.left();
|
||||
for (group_data, group) in groups.iter_mut().zip(&self.data) {
|
||||
for (d, bar) in group_data.iter_mut().zip(&group.bars) {
|
||||
let mut bar_x = area.left();
|
||||
for (ticks, group) in group_ticks.iter().zip(&self.data) {
|
||||
for (d, bar) in ticks.iter().zip(&group.bars) {
|
||||
let mut d = *d;
|
||||
for j in (0..area.height).rev() {
|
||||
let symbol = match d {
|
||||
0 => self.bar_set.empty,
|
||||
1 => self.bar_set.one_eighth,
|
||||
|
@ -459,20 +498,16 @@ impl<'a> BarChart<'a> {
|
|||
let bar_style = self.bar_style.patch(bar.style);
|
||||
|
||||
for x in 0..self.bar_width {
|
||||
buf.get_mut(bar_x + x, bars_area.top() + j)
|
||||
buf.get_mut(bar_x + x, area.top() + j)
|
||||
.set_symbol(symbol)
|
||||
.set_style(bar_style);
|
||||
}
|
||||
|
||||
if *d > 8 {
|
||||
*d -= 8;
|
||||
} else {
|
||||
*d = 0;
|
||||
}
|
||||
bar_x += self.bar_gap + self.bar_width;
|
||||
d = d.saturating_sub(8);
|
||||
}
|
||||
bar_x += self.group_gap;
|
||||
bar_x += self.bar_gap + self.bar_width;
|
||||
}
|
||||
bar_x += self.group_gap;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -489,36 +524,42 @@ impl<'a> BarChart<'a> {
|
|||
.max(1u64)
|
||||
}
|
||||
|
||||
fn render_labels_and_values(self, area: Rect, buf: &mut Buffer, label_height: u16) {
|
||||
fn render_labels_and_values(
|
||||
self,
|
||||
area: Rect,
|
||||
buf: &mut Buffer,
|
||||
label_info: LabelInfo,
|
||||
group_ticks: &[Vec<u64>],
|
||||
) {
|
||||
// print labels and values in one go
|
||||
let mut bar_x = area.left();
|
||||
let bar_y = area.bottom() - label_height - 1;
|
||||
for mut group in self.data.into_iter() {
|
||||
let bar_y = area.bottom() - label_info.height - 1;
|
||||
for (mut group, ticks) in self.data.into_iter().zip(group_ticks) {
|
||||
if group.bars.is_empty() {
|
||||
continue;
|
||||
}
|
||||
let bars = std::mem::take(&mut group.bars);
|
||||
|
||||
// print group labels under the bars or the previous labels
|
||||
let label_max_width =
|
||||
bars.len() as u16 * (self.bar_width + self.bar_gap) - self.bar_gap;
|
||||
let group_area = Rect {
|
||||
x: bar_x,
|
||||
y: area.bottom() - 1,
|
||||
width: label_max_width,
|
||||
height: 1,
|
||||
};
|
||||
group.render_label(buf, group_area, self.label_style);
|
||||
if label_info.group_label_visible {
|
||||
let label_max_width =
|
||||
bars.len() as u16 * (self.bar_width + self.bar_gap) - self.bar_gap;
|
||||
let group_area = Rect {
|
||||
x: bar_x,
|
||||
y: area.bottom() - 1,
|
||||
width: label_max_width,
|
||||
height: 1,
|
||||
};
|
||||
group.render_label(buf, group_area, self.label_style);
|
||||
}
|
||||
|
||||
// print the bar values and numbers
|
||||
for bar in bars.into_iter() {
|
||||
bar.render_label_and_value(
|
||||
buf,
|
||||
self.bar_width,
|
||||
bar_x,
|
||||
bar_y,
|
||||
self.value_style,
|
||||
self.label_style,
|
||||
);
|
||||
for (mut bar, ticks) in bars.into_iter().zip(ticks) {
|
||||
if label_info.bar_label_visible {
|
||||
bar.render_label(buf, self.bar_width, bar_x, bar_y + 1, self.label_style);
|
||||
}
|
||||
|
||||
bar.render_value(buf, self.bar_width, bar_x, bar_y, self.value_style, *ticks);
|
||||
|
||||
bar_x += self.bar_gap + self.bar_width;
|
||||
}
|
||||
|
@ -532,18 +573,8 @@ impl<'a> Widget for BarChart<'a> {
|
|||
buf.set_style(area, self.style);
|
||||
|
||||
self.render_block(&mut area, buf);
|
||||
if area.area() == 0 {
|
||||
return;
|
||||
}
|
||||
if self.data.is_empty() {
|
||||
return;
|
||||
}
|
||||
if self.bar_width == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let label_height = self.label_height();
|
||||
if area.height <= label_height {
|
||||
if area.is_empty() || self.data.is_empty() || self.bar_width == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -558,12 +589,7 @@ impl<'a> Widget for BarChart<'a> {
|
|||
Direction::Vertical => {
|
||||
// remove invisible groups and bars, since we don't need to print them
|
||||
self.remove_invisible_groups_and_bars(area.width);
|
||||
let bars_area = Rect {
|
||||
height: area.height - label_height,
|
||||
..area
|
||||
};
|
||||
self.render_vertical_bars(buf, bars_area, max);
|
||||
self.render_labels_and_values(area, buf, label_height);
|
||||
self.render_vertical(buf, area, max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -607,7 +633,7 @@ mod tests {
|
|||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" █ ",
|
||||
"█ █ ",
|
||||
"1 2 ",
|
||||
"f b ",
|
||||
])
|
||||
);
|
||||
|
@ -629,7 +655,7 @@ mod tests {
|
|||
Buffer::with_lines(vec![
|
||||
"╔Block════════╗",
|
||||
"║ █ ║",
|
||||
"║█ █ ║",
|
||||
"║1 2 ║",
|
||||
"║f b ║",
|
||||
"╚═════════════╝",
|
||||
])
|
||||
|
@ -657,7 +683,7 @@ mod tests {
|
|||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" █ █ ",
|
||||
"█ █ █ ",
|
||||
"1 2 █ ",
|
||||
"f b b ",
|
||||
])
|
||||
);
|
||||
|
@ -672,7 +698,7 @@ mod tests {
|
|||
widget.render(buffer.area, &mut buffer);
|
||||
let mut expected = Buffer::with_lines(vec![
|
||||
" █ ",
|
||||
"█ █ ",
|
||||
"1 2 ",
|
||||
"f b ",
|
||||
]);
|
||||
for (x, y) in iproduct!([0, 2], [0, 1]) {
|
||||
|
@ -709,7 +735,7 @@ mod tests {
|
|||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" █ ",
|
||||
"█ █ ",
|
||||
"1 2 ",
|
||||
"f b ",
|
||||
])
|
||||
);
|
||||
|
@ -726,7 +752,7 @@ mod tests {
|
|||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" █ ",
|
||||
" ▄ █ ",
|
||||
" ▄ 3 ",
|
||||
"f b b ",
|
||||
])
|
||||
);
|
||||
|
@ -753,7 +779,7 @@ mod tests {
|
|||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" ",
|
||||
" ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ",
|
||||
" ▁ ▂ ▃ ▄ ▅ ▆ ▇ 8 ",
|
||||
"a b c d e f g h i ",
|
||||
])
|
||||
);
|
||||
|
@ -786,7 +812,7 @@ mod tests {
|
|||
widget.render(buffer.area, &mut buffer);
|
||||
let mut expected = Buffer::with_lines(vec![
|
||||
" █ ",
|
||||
"█ █ ",
|
||||
"1 2 ",
|
||||
"f b ",
|
||||
]);
|
||||
expected.get_mut(0, 2).set_fg(Color::Red);
|
||||
|
@ -803,7 +829,7 @@ mod tests {
|
|||
widget.render(buffer.area, &mut buffer);
|
||||
let mut expected = Buffer::with_lines(vec![
|
||||
" █ ",
|
||||
"█ █ ",
|
||||
"1 2 ",
|
||||
"f b ",
|
||||
]);
|
||||
for (x, y) in iproduct!(0..15, 0..3) {
|
||||
|
@ -812,166 +838,6 @@ mod tests {
|
|||
assert_buffer_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_render_less_than_two_rows() {
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 15, 1));
|
||||
let widget = BarChart::default().data(&[("foo", 1), ("bar", 2)]);
|
||||
widget.render(buffer.area, &mut buffer);
|
||||
assert_buffer_eq!(buffer, Buffer::empty(Rect::new(0, 0, 15, 1)));
|
||||
}
|
||||
|
||||
fn create_test_barchart<'a>() -> BarChart<'a> {
|
||||
BarChart::default()
|
||||
.group_gap(2)
|
||||
.data(BarGroup::default().label("G1".into()).bars(&[
|
||||
Bar::default().value(2),
|
||||
Bar::default().value(1),
|
||||
Bar::default().value(2),
|
||||
]))
|
||||
.data(BarGroup::default().label("G2".into()).bars(&[
|
||||
Bar::default().value(1),
|
||||
Bar::default().value(2),
|
||||
Bar::default().value(1),
|
||||
]))
|
||||
.data(BarGroup::default().label("G3".into()).bars(&[
|
||||
Bar::default().value(1),
|
||||
Bar::default().value(2),
|
||||
Bar::default().value(1),
|
||||
]))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invisible_groups_and_bars_full() {
|
||||
let chart = create_test_barchart();
|
||||
// Check that the BarChart is shown in full
|
||||
{
|
||||
let mut c = chart.clone();
|
||||
c.remove_invisible_groups_and_bars(21);
|
||||
assert_eq!(c.data.len(), 3);
|
||||
assert_eq!(c.data[2].bars.len(), 3);
|
||||
}
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 21, 3));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
let expected = Buffer::with_lines(vec![
|
||||
"█ █ █ █ ",
|
||||
"█ █ █ █ █ █ █ █ █",
|
||||
"G1 G2 G3 ",
|
||||
]);
|
||||
|
||||
assert_buffer_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invisible_groups_and_bars_missing_last_2_bars() {
|
||||
// Last 2 bars of G3 should be out of screen. (screen width is 17)
|
||||
let chart = create_test_barchart();
|
||||
|
||||
{
|
||||
let mut w = chart.clone();
|
||||
w.remove_invisible_groups_and_bars(17);
|
||||
assert_eq!(w.data.len(), 3);
|
||||
assert_eq!(w.data[2].bars.len(), 1);
|
||||
}
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 3));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
let expected = Buffer::with_lines(vec![
|
||||
"█ █ █ ",
|
||||
"█ █ █ █ █ █ █",
|
||||
"G1 G2 G",
|
||||
]);
|
||||
assert_buffer_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invisible_groups_and_bars_missing_last_group() {
|
||||
// G3 should be out of screen. (screen width is 16)
|
||||
let chart = create_test_barchart();
|
||||
|
||||
{
|
||||
let mut w = chart.clone();
|
||||
w.remove_invisible_groups_and_bars(16);
|
||||
assert_eq!(w.data.len(), 2);
|
||||
assert_eq!(w.data[1].bars.len(), 3);
|
||||
}
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 16, 3));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
let expected = Buffer::with_lines(vec![
|
||||
"█ █ █ ",
|
||||
"█ █ █ █ █ █ ",
|
||||
"G1 G2 ",
|
||||
]);
|
||||
assert_buffer_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invisible_groups_and_bars_show_only_1_bar() {
|
||||
let chart = create_test_barchart();
|
||||
|
||||
{
|
||||
let mut w = chart.clone();
|
||||
w.remove_invisible_groups_and_bars(1);
|
||||
assert_eq!(w.data.len(), 1);
|
||||
assert_eq!(w.data[0].bars.len(), 1);
|
||||
}
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 1, 3));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
let expected = Buffer::with_lines(vec!["█", "█", "G"]);
|
||||
assert_buffer_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invisible_groups_and_bars_all_bars_outside_visible_area() {
|
||||
let chart = create_test_barchart();
|
||||
|
||||
{
|
||||
let mut w = chart.clone();
|
||||
w.remove_invisible_groups_and_bars(0);
|
||||
assert_eq!(w.data.len(), 0);
|
||||
}
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 0, 3));
|
||||
// Check if the render method panics
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_label_height() {
|
||||
{
|
||||
let barchart = BarChart::default().data(
|
||||
BarGroup::default()
|
||||
.label("Group Label".into())
|
||||
.bars(&[Bar::default().value(2).label("Bar Label".into())]),
|
||||
);
|
||||
assert_eq!(barchart.label_height(), 2);
|
||||
}
|
||||
|
||||
{
|
||||
let barchart = BarChart::default().data(
|
||||
BarGroup::default()
|
||||
.label("Group Label".into())
|
||||
.bars(&[Bar::default().value(2)]),
|
||||
);
|
||||
assert_eq!(barchart.label_height(), 1);
|
||||
}
|
||||
|
||||
{
|
||||
let barchart = BarChart::default().data(
|
||||
BarGroup::default().bars(&[Bar::default().value(2).label("Bar Label".into())]),
|
||||
);
|
||||
assert_eq!(barchart.label_height(), 1);
|
||||
}
|
||||
|
||||
{
|
||||
let barchart =
|
||||
BarChart::default().data(BarGroup::default().bars(&[Bar::default().value(2)]));
|
||||
assert_eq!(barchart.label_height(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn can_be_stylized() {
|
||||
assert_eq!(
|
||||
|
@ -995,7 +861,7 @@ mod tests {
|
|||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 3, 3));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
let expected = Buffer::with_lines(vec![" █", "█ █", "G "]);
|
||||
let expected = Buffer::with_lines(vec![" █", "1 2", "G "]);
|
||||
assert_buffer_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
|
@ -1177,7 +1043,7 @@ mod tests {
|
|||
let mut buffer = Buffer::empty(Rect::new(0, 0, 3, 3));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
|
||||
let expected = Buffer::with_lines(vec![" █", "▆ █", " G "]);
|
||||
let expected = Buffer::with_lines(vec![" █", "▆ 5", " G "]);
|
||||
assert_buffer_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
|
@ -1192,7 +1058,7 @@ mod tests {
|
|||
let mut buffer = Buffer::empty(Rect::new(0, 0, 3, 3));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
|
||||
let expected = Buffer::with_lines(vec![" █", "▆ █", " G"]);
|
||||
let expected = Buffer::with_lines(vec![" █", "▆ 5", " G"]);
|
||||
assert_buffer_eq!(buffer, expected);
|
||||
}
|
||||
|
||||
|
@ -1238,4 +1104,211 @@ mod tests {
|
|||
chart.render(buffer.area, &mut buffer);
|
||||
assert_buffer_eq!(buffer, Buffer::empty(Rect::new(0, 0, 0, 10)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_line() {
|
||||
let mut group: BarGroup = (&[
|
||||
("a", 0),
|
||||
("b", 1),
|
||||
("c", 2),
|
||||
("d", 3),
|
||||
("e", 4),
|
||||
("f", 5),
|
||||
("g", 6),
|
||||
("h", 7),
|
||||
("i", 8),
|
||||
])
|
||||
.into();
|
||||
group = group.label("Group".into());
|
||||
|
||||
let chart = BarChart::default()
|
||||
.data(group)
|
||||
.bar_set(symbols::bar::NINE_LEVELS);
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 1));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
|
||||
assert_buffer_eq!(buffer, Buffer::with_lines(vec![" ▁ ▂ ▃ ▄ ▅ ▆ ▇ 8"]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_lines() {
|
||||
let mut group: BarGroup = (&[
|
||||
("a", 0),
|
||||
("b", 1),
|
||||
("c", 2),
|
||||
("d", 3),
|
||||
("e", 4),
|
||||
("f", 5),
|
||||
("g", 6),
|
||||
("h", 7),
|
||||
("i", 8),
|
||||
])
|
||||
.into();
|
||||
group = group.label("Group".into());
|
||||
|
||||
let chart = BarChart::default()
|
||||
.data(group)
|
||||
.bar_set(symbols::bar::NINE_LEVELS);
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 3));
|
||||
chart.render(Rect::new(0, 1, buffer.area.width, 2), &mut buffer);
|
||||
|
||||
assert_buffer_eq!(
|
||||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" ",
|
||||
" ▁ ▂ ▃ ▄ ▅ ▆ ▇ 8",
|
||||
"a b c d e f g h i",
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_lines() {
|
||||
let mut group: BarGroup = (&[
|
||||
("a", 0),
|
||||
("b", 1),
|
||||
("c", 2),
|
||||
("d", 3),
|
||||
("e", 4),
|
||||
("f", 5),
|
||||
("g", 6),
|
||||
("h", 7),
|
||||
("i", 8),
|
||||
])
|
||||
.into();
|
||||
group = group.label(Line::from("Group").alignment(Alignment::Center));
|
||||
|
||||
let chart = BarChart::default()
|
||||
.data(group)
|
||||
.bar_set(symbols::bar::NINE_LEVELS);
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 3));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
|
||||
assert_buffer_eq!(
|
||||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" ▁ ▂ ▃ ▄ ▅ ▆ ▇ 8",
|
||||
"a b c d e f g h i",
|
||||
" Group ",
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_lines_double_width() {
|
||||
let mut group = BarGroup::from(&[
|
||||
("a", 0),
|
||||
("b", 1),
|
||||
("c", 2),
|
||||
("d", 3),
|
||||
("e", 4),
|
||||
("f", 5),
|
||||
("g", 6),
|
||||
("h", 7),
|
||||
("i", 8),
|
||||
]);
|
||||
group = group.label(Line::from("Group").alignment(Alignment::Center));
|
||||
|
||||
let chart = BarChart::default()
|
||||
.data(group)
|
||||
.bar_width(2)
|
||||
.bar_set(symbols::bar::NINE_LEVELS);
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 26, 3));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
|
||||
assert_buffer_eq!(
|
||||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" 1▁ 2▂ 3▃ 4▄ 5▅ 6▆ 7▇ 8█",
|
||||
"a b c d e f g h i ",
|
||||
" Group ",
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn four_lines() {
|
||||
let mut group: BarGroup = (&[
|
||||
("a", 0),
|
||||
("b", 1),
|
||||
("c", 2),
|
||||
("d", 3),
|
||||
("e", 4),
|
||||
("f", 5),
|
||||
("g", 6),
|
||||
("h", 7),
|
||||
("i", 8),
|
||||
])
|
||||
.into();
|
||||
group = group.label(Line::from("Group").alignment(Alignment::Center));
|
||||
|
||||
let chart = BarChart::default()
|
||||
.data(group)
|
||||
.bar_set(symbols::bar::NINE_LEVELS);
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 4));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
|
||||
assert_buffer_eq!(
|
||||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" ▂ ▄ ▆ █",
|
||||
" ▂ ▄ ▆ 4 5 6 7 8",
|
||||
"a b c d e f g h i",
|
||||
" Group ",
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_lines_without_bar_labels() {
|
||||
let group = BarGroup::default()
|
||||
.label(Line::from("Group").alignment(Alignment::Center))
|
||||
.bars(&[
|
||||
Bar::default().value(0),
|
||||
Bar::default().value(1),
|
||||
Bar::default().value(2),
|
||||
Bar::default().value(3),
|
||||
Bar::default().value(4),
|
||||
Bar::default().value(5),
|
||||
Bar::default().value(6),
|
||||
Bar::default().value(7),
|
||||
Bar::default().value(8),
|
||||
]);
|
||||
|
||||
let chart = BarChart::default().data(group);
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 17, 3));
|
||||
chart.render(Rect::new(0, 1, buffer.area.width, 2), &mut buffer);
|
||||
|
||||
assert_buffer_eq!(
|
||||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" ",
|
||||
" ▁ ▂ ▃ ▄ ▅ ▆ ▇ 8",
|
||||
" Group ",
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_lines_with_more_bars() {
|
||||
let bars: Vec<Bar> = (0..30).map(|i| Bar::default().value(i)).collect();
|
||||
|
||||
let chart = BarChart::default().data(BarGroup::default().bars(&bars));
|
||||
|
||||
let mut buffer = Buffer::empty(Rect::new(0, 0, 59, 1));
|
||||
chart.render(buffer.area, &mut buffer);
|
||||
|
||||
assert_buffer_eq!(
|
||||
buffer,
|
||||
Buffer::with_lines(vec![
|
||||
" ▁ ▁ ▁ ▁ ▂ ▂ ▂ ▃ ▃ ▃ ▃ ▄ ▄ ▄ ▄ ▅ ▅ ▅ ▆ ▆ ▆ ▆ ▇ ▇ ▇ █",
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -139,16 +139,15 @@ impl<'a> Bar<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn render_label_and_value(
|
||||
pub(super) fn render_value(
|
||||
self,
|
||||
buf: &mut Buffer,
|
||||
max_width: u16,
|
||||
x: u16,
|
||||
y: u16,
|
||||
default_value_style: Style,
|
||||
default_label_style: Style,
|
||||
ticks: u64,
|
||||
) {
|
||||
// render the value
|
||||
if self.value != 0 {
|
||||
let value_label = if let Some(text) = self.text_value {
|
||||
text
|
||||
|
@ -157,7 +156,10 @@ impl<'a> Bar<'a> {
|
|||
};
|
||||
|
||||
let width = value_label.width() as u16;
|
||||
if width < max_width {
|
||||
const TICKS_PER_LINE: u64 = 8;
|
||||
// if we have enough space or the ticks are greater equal than 1 cell (8)
|
||||
// then print the value
|
||||
if width < max_width || (width == max_width && ticks >= TICKS_PER_LINE) {
|
||||
buf.set_string(
|
||||
x + (max_width.saturating_sub(value_label.len() as u16) >> 1),
|
||||
y,
|
||||
|
@ -166,9 +168,17 @@ impl<'a> Bar<'a> {
|
|||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// render the label
|
||||
if let Some(mut label) = self.label {
|
||||
pub(super) fn render_label(
|
||||
&mut self,
|
||||
buf: &mut Buffer,
|
||||
max_width: u16,
|
||||
x: u16,
|
||||
y: u16,
|
||||
default_label_style: Style,
|
||||
) {
|
||||
if let Some(label) = &mut self.label {
|
||||
// patch label styles
|
||||
for span in &mut label.spans {
|
||||
span.style = default_label_style.patch(span.style);
|
||||
|
@ -176,8 +186,8 @@ impl<'a> Bar<'a> {
|
|||
|
||||
buf.set_line(
|
||||
x + (max_width.saturating_sub(label.width() as u16) >> 1),
|
||||
y + 1,
|
||||
&label,
|
||||
y,
|
||||
label,
|
||||
max_width,
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue