diff --git a/src/backend/crossterm.rs b/src/backend/crossterm.rs index ec59ac93..5c3707c9 100644 --- a/src/backend/crossterm.rs +++ b/src/backend/crossterm.rs @@ -304,7 +304,6 @@ impl From for Color { /// The `ModifierDiff` struct is used to calculate the difference between two `Modifier` /// values. This is useful when updating the terminal display, as it allows for more /// efficient updates by only sending the necessary changes. -#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] struct ModifierDiff { pub from: Modifier, pub to: Modifier, diff --git a/src/backend/termion.rs b/src/backend/termion.rs index a69082b4..2815a57b 100644 --- a/src/backend/termion.rs +++ b/src/backend/termion.rs @@ -209,16 +209,13 @@ where self.writer.flush() } } -#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] struct Fg(Color); -#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] struct Bg(Color); /// The `ModifierDiff` struct is used to calculate the difference between two `Modifier` /// values. This is useful when updating the terminal display, as it allows for more /// efficient updates by only sending the necessary changes. -#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, Hash)] struct ModifierDiff { from: Modifier, to: Modifier, diff --git a/src/widgets/canvas.rs b/src/widgets/canvas.rs index 23791ef4..f28c0898 100644 --- a/src/widgets/canvas.rs +++ b/src/widgets/canvas.rs @@ -55,7 +55,7 @@ pub struct Label<'a> { /// /// This allows the canvas to be drawn in multiple layers. This is useful if you want to draw /// multiple shapes on the canvas in specific order. -#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] +#[derive(Debug)] struct Layer { // A string of characters representing the grid. This will be wrapped to the width of the grid // when rendering @@ -97,7 +97,7 @@ trait Grid: Debug { /// /// This grid type only supports a single foreground color for each 2x4 dots cell. There is no way /// to set the individual color of each dot in the braille pattern. -#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] +#[derive(Debug)] struct BrailleGrid { /// Width of the grid in number of terminal columns width: u16, @@ -160,7 +160,7 @@ impl Grid for BrailleGrid { /// /// This makes it possible to draw shapes with a resolution of 1x1 dots per cell. This is useful /// when you want to draw shapes with a low resolution. -#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] +#[derive(Debug)] struct CharGrid { /// Width of the grid in number of terminal columns width: u16, @@ -232,7 +232,7 @@ impl Grid for CharGrid { /// This allows for more flexibility than the `BrailleGrid` which only supports a single /// foreground color for each 2x4 dots cell, and the `CharGrid` which only supports a single /// character for each cell. -#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] +#[derive(Debug)] struct HalfBlockGrid { /// Width of the grid in number of terminal columns width: u16, diff --git a/src/widgets/chart.rs b/src/widgets/chart.rs index 8b9164ac..9d1191e4 100644 --- a/src/widgets/chart.rs +++ b/src/widgets/chart.rs @@ -400,7 +400,6 @@ impl<'a> Dataset<'a> { /// A container that holds all the infos about where to display each elements of the chart (axis, /// labels, legend, ...). -#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)] struct ChartLayout { /// Location of the title of the x axis title_x: Option<(u16, u16)>, @@ -691,50 +690,63 @@ impl<'a> Chart<'a> { /// Compute the internal layout of the chart given the area. If the area is too small some /// elements may be automatically hidden - fn layout(&self, area: Rect) -> ChartLayout { - let mut layout = ChartLayout::default(); + fn layout(&self, area: Rect) -> Option { if area.height == 0 || area.width == 0 { - return layout; + return None; } let mut x = area.left(); let mut y = area.bottom() - 1; + let mut label_x = None; if self.x_axis.labels.is_some() && y > area.top() { - layout.label_x = Some(y); + label_x = Some(y); y -= 1; } - layout.label_y = self.y_axis.labels.as_ref().and(Some(x)); + let label_y = self.y_axis.labels.as_ref().and(Some(x)); x += self.max_width_of_labels_left_of_y_axis(area, self.y_axis.labels.is_some()); + let mut axis_x = None; if self.x_axis.labels.is_some() && y > area.top() { - layout.axis_x = Some(y); + axis_x = Some(y); y -= 1; } + let mut axis_y = None; if self.y_axis.labels.is_some() && x + 1 < area.right() { - layout.axis_y = Some(x); + axis_y = Some(x); x += 1; } - if x < area.right() && y > 1 { - layout.graph_area = Rect::new(x, area.top(), area.right() - x, y - area.top() + 1); - } + let graph_width = area.right().saturating_sub(x); + let graph_height = y.saturating_sub(area.top()).saturating_add(1); + debug_assert_ne!( + graph_width, 0, + "Axis and labels should have been hidden due to the small area" + ); + debug_assert_ne!( + graph_height, 0, + "Axis and labels should have been hidden due to the small area" + ); + let graph_area = Rect::new(x, area.top(), graph_width, graph_height); + let mut title_x = None; if let Some(ref title) = self.x_axis.title { let w = title.width() as u16; - if w < layout.graph_area.width && layout.graph_area.height > 2 { - layout.title_x = Some((x + layout.graph_area.width - w, y)); + if w < graph_area.width && graph_area.height > 2 { + title_x = Some((x + graph_area.width - w, y)); } } + let mut title_y = None; 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, area.top())); + if w + 1 < graph_area.width && graph_area.height > 2 { + title_y = Some((x, area.top())); } } + let mut legend_area = None; if let Some(legend_position) = self.legend_position { let legends = self .datasets @@ -747,27 +759,25 @@ impl<'a> Chart<'a> { let [max_legend_width] = Layout::horizontal([self.hidden_legend_constraints.0]) .flex(Flex::Start) - .areas(layout.graph_area); + .areas(graph_area); let [max_legend_height] = Layout::vertical([self.hidden_legend_constraints.1]) .flex(Flex::Start) - .areas(layout.graph_area); + .areas(graph_area); if inner_width > 0 && legend_width <= max_legend_width.width && legend_height <= max_legend_height.height { - layout.legend_area = legend_position.layout( - layout.graph_area, + legend_area = legend_position.layout( + graph_area, legend_width, legend_height, - layout - .title_x + title_x .and(self.x_axis.title.as_ref()) .map(|t| t.width() as u16) .unwrap_or_default(), - layout - .title_y + title_y .and(self.y_axis.title.as_ref()) .map(|t| t.width() as u16) .unwrap_or_default(), @@ -775,7 +785,16 @@ impl<'a> Chart<'a> { } } } - layout + Some(ChartLayout { + title_x, + title_y, + label_x, + label_y, + axis_x, + axis_y, + legend_area, + graph_area, + }) } fn max_width_of_labels_left_of_y_axis(&self, area: Rect, has_y_axis: bool) -> u16 { @@ -930,21 +949,16 @@ impl WidgetRef for Chart<'_> { self.block.render_ref(area, buf); let chart_area = self.block.inner_if_some(area); - if chart_area.is_empty() { + let Some(layout) = self.layout(chart_area) else { return; - } + }; + let graph_area = layout.graph_area; // 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 layout = self.layout(chart_area); - let graph_area = layout.graph_area; - if graph_area.width < 1 || graph_area.height < 1 { - return; - } - self.render_x_labels(buf, &layout, chart_area, graph_area); self.render_y_labels(buf, &layout, chart_area, graph_area); @@ -1136,7 +1150,7 @@ mod tests { .x_axis(Axis::default().title("X axis")) .y_axis(Axis::default().title("Y axis")) .hidden_legend_constraints(case.hidden_legend_constraints); - let layout = chart.layout(case.chart_area); + let layout = chart.layout(case.chart_area).unwrap(); assert_eq!(layout.legend_area, case.legend_area); } } @@ -1208,7 +1222,7 @@ mod tests { let data_unnamed = Dataset::default(); // must not occupy a row in legend let widget = Chart::new(vec![data_named_1, data_unnamed, data_named_2]); let buffer = Buffer::empty(Rect::new(0, 0, 50, 25)); - let layout = widget.layout(buffer.area); + let layout = widget.layout(buffer.area).unwrap(); assert!(layout.legend_area.is_some()); assert_eq!(layout.legend_area.unwrap().height, 4); // 2 for borders, 2 for rows @@ -1219,7 +1233,7 @@ mod tests { let dataset = Dataset::default(); let widget = Chart::new(vec![dataset; 3]); let buffer = Buffer::empty(Rect::new(0, 0, 50, 25)); - let layout = widget.layout(buffer.area); + let layout = widget.layout(buffer.area).unwrap(); assert!(layout.legend_area.is_none()); }