From c4cd0a5f31581518501a1ecda93875277ff943d0 Mon Sep 17 00:00:00 2001 From: Florian Dehau Date: Sun, 27 Sep 2020 16:43:56 +0200 Subject: [PATCH] fix(widgets/chart): use the correct style to draw legend and axis titles Before this change, the style of the points drawn in the graph area could reused to draw the title of the axis and the legend. Now the style of these components put on top of the graph area is solely based on the widget style. --- src/widgets/chart.rs | 48 ++++++-- tests/widgets_chart.rs | 253 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 290 insertions(+), 11 deletions(-) diff --git a/src/widgets/chart.rs b/src/widgets/chart.rs index 72c59e3..cd50783 100644 --- a/src/widgets/chart.rs +++ b/src/widgets/chart.rs @@ -333,7 +333,7 @@ impl<'a> Chart<'a> { if let Some(ref title) = self.y_axis.title { let w = title.width() as u16; if w + 1 < layout.graph_area.width && layout.graph_area.height > 2 { - layout.title_y = Some((x + 1, area.top())); + layout.title_y = Some((x, area.top())); } } @@ -367,6 +367,11 @@ impl<'a> Chart<'a> { impl<'a> Widget for Chart<'a> { fn render(mut self, area: Rect, buf: &mut Buffer) { buf.set_style(area, self.style); + // Sample the style of the entire widget. This sample will be used to reset the style of + // the cells that are part of the components put on top of the grah area (i.e legend and + // axis names). + let original_style = buf.get(area.left(), area.top()).style(); + let chart_area = match self.block.take() { Some(b) => { let inner_area = b.inner(area); @@ -382,16 +387,6 @@ impl<'a> Widget for Chart<'a> { return; } - if let Some((x, y)) = layout.title_x { - let title = self.x_axis.title.unwrap(); - buf.set_spans(x, y, &title, graph_area.right().saturating_sub(x)); - } - - if let Some((x, y)) = layout.title_y { - let title = self.y_axis.title.unwrap(); - buf.set_spans(x, y, &title, graph_area.right().saturating_sub(x)); - } - if let Some(y) = layout.label_x { let labels = self.x_axis.labels.unwrap(); let total_width = labels.iter().map(Span::width).sum::() as u16; @@ -471,6 +466,7 @@ impl<'a> Widget for Chart<'a> { } if let Some(legend_area) = layout.legend_area { + buf.set_style(legend_area, original_style); Block::default() .borders(Borders::ALL) .render(legend_area, buf); @@ -483,6 +479,36 @@ impl<'a> Widget for Chart<'a> { ); } } + + if let Some((x, y)) = layout.title_x { + let title = self.x_axis.title.unwrap(); + let width = graph_area.right().saturating_sub(x); + buf.set_style( + Rect { + x, + y, + width, + height: 1, + }, + original_style, + ); + buf.set_spans(x, y, &title, width); + } + + if let Some((x, y)) = layout.title_y { + let title = self.y_axis.title.unwrap(); + let width = graph_area.right().saturating_sub(x); + buf.set_style( + Rect { + x, + y, + width, + height: 1, + }, + original_style, + ); + buf.set_spans(x, y, &title, width); + } } } diff --git a/tests/widgets_chart.rs b/tests/widgets_chart.rs index 164118f..1e92166 100644 --- a/tests/widgets_chart.rs +++ b/tests/widgets_chart.rs @@ -1,5 +1,6 @@ use tui::{ backend::TestBackend, + buffer::Buffer, layout::Rect, style::{Color, Style}, symbols, @@ -124,3 +125,255 @@ fn widgets_chart_can_have_empty_datasets() { }) .unwrap(); } + +#[test] +fn widgets_chart_can_have_a_legend() { + let backend = TestBackend::new(60, 30); + let mut terminal = Terminal::new(backend).unwrap(); + terminal + .draw(|f| { + let datasets = vec![ + Dataset::default() + .name("Dataset 1") + .style(Style::default().fg(Color::Blue)) + .data(&[ + (0.0, 0.0), + (10.0, 1.0), + (20.0, 2.0), + (30.0, 3.0), + (40.0, 4.0), + (50.0, 5.0), + (60.0, 6.0), + (70.0, 7.0), + (80.0, 8.0), + (90.0, 9.0), + (100.0, 10.0), + ]) + .graph_type(Line), + Dataset::default() + .name("Dataset 2") + .style(Style::default().fg(Color::Green)) + .data(&[ + (0.0, 10.0), + (10.0, 9.0), + (20.0, 8.0), + (30.0, 7.0), + (40.0, 6.0), + (50.0, 5.0), + (60.0, 4.0), + (70.0, 3.0), + (80.0, 2.0), + (90.0, 1.0), + (100.0, 0.0), + ]) + .graph_type(Line), + ]; + let chart = Chart::new(datasets) + .style(Style::default().bg(Color::White)) + .block(Block::default().title("Chart Test").borders(Borders::ALL)) + .x_axis( + Axis::default() + .bounds([0.0, 100.0]) + .title(Span::styled("X Axis", Style::default().fg(Color::Yellow))) + .labels(create_labels(&["0.0", "50.0", "100.0"])), + ) + .y_axis( + Axis::default() + .bounds([0.0, 10.0]) + .title("Y Axis") + .labels(create_labels(&["0.0", "5.0", "10.0"])), + ); + f.render_widget( + chart, + Rect { + x: 0, + y: 0, + width: 60, + height: 30, + }, + ); + }) + .unwrap(); + let mut expected = Buffer::with_lines(vec![ + "┌Chart Test────────────────────────────────────────────────┐", + "│10.0│Y Axis ┌─────────┐│", + "│ │ •• │Dataset 1││", + "│ │ •• │Dataset 2││", + "│ │ •• └─────────┘│", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ ••• •• │", + "│ │ ••• │", + "│5.0 │ •• •• │", + "│ │ •• •• │", + "│ │ ••• •• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│ │ •• ••• │", + "│ │ •• •• │", + "│ │ •• •• │", + "│0.0 │• X Axis│", + "│ └─────────────────────────────────────────────────────│", + "│ 0.0 50.0 100.0 │", + "└──────────────────────────────────────────────────────────┘", + ]); + + // Set expected backgound color + for row in 0..30 { + for col in 0..60 { + expected.get_mut(col, row).set_bg(Color::White); + } + } + + // Set expected colors of the first dataset + let line1 = vec![ + (48, 5), + (49, 5), + (46, 6), + (47, 6), + (44, 7), + (45, 7), + (42, 8), + (43, 8), + (40, 9), + (41, 9), + (38, 10), + (39, 10), + (36, 11), + (37, 11), + (34, 12), + (35, 12), + (33, 13), + (30, 14), + (31, 14), + (28, 15), + (29, 15), + (25, 16), + (26, 16), + (27, 16), + (23, 17), + (24, 17), + (21, 18), + (22, 18), + (19, 19), + (20, 19), + (17, 20), + (18, 20), + (15, 21), + (16, 21), + (13, 22), + (14, 22), + (11, 23), + (12, 23), + (9, 24), + (10, 24), + (7, 25), + (8, 25), + (6, 26), + ]; + let legend1 = vec![ + (49, 2), + (50, 2), + (51, 2), + (52, 2), + (53, 2), + (54, 2), + (55, 2), + (56, 2), + (57, 2), + ]; + for (col, row) in line1 { + expected.get_mut(col, row).set_fg(Color::Blue); + } + for (col, row) in legend1 { + expected.get_mut(col, row).set_fg(Color::Blue); + } + + // Set expected colors of the second dataset + let line2 = vec![ + (8, 2), + (9, 2), + (10, 3), + (11, 3), + (12, 4), + (13, 4), + (14, 5), + (15, 5), + (16, 6), + (17, 6), + (18, 7), + (19, 7), + (20, 8), + (21, 8), + (22, 9), + (23, 9), + (24, 10), + (25, 10), + (26, 11), + (27, 11), + (28, 12), + (29, 12), + (30, 12), + (31, 13), + (32, 13), + (33, 14), + (34, 14), + (35, 15), + (36, 15), + (37, 16), + (38, 16), + (39, 17), + (40, 17), + (41, 18), + (42, 18), + (43, 19), + (44, 19), + (45, 20), + (46, 20), + (47, 21), + (48, 21), + (49, 22), + (50, 22), + (51, 23), + (52, 23), + (53, 23), + (54, 24), + (55, 24), + (56, 25), + (57, 25), + ]; + let legend2 = vec![ + (49, 3), + (50, 3), + (51, 3), + (52, 3), + (53, 3), + (54, 3), + (55, 3), + (56, 3), + (57, 3), + ]; + for (col, row) in line2 { + expected.get_mut(col, row).set_fg(Color::Green); + } + for (col, row) in legend2 { + expected.get_mut(col, row).set_fg(Color::Green); + } + + // Set expected colors of the x axis + let x_axis_title = vec![(53, 26), (54, 26), (55, 26), (56, 26), (57, 26), (58, 26)]; + for (col, row) in x_axis_title { + expected.get_mut(col, row).set_fg(Color::Yellow); + } + + terminal.backend().assert_buffer(&expected); +}