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); +}