diff options
| author | Ellie Huxtable <ellie@elliehuxtable.com> | 2023-03-31 22:57:37 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-31 22:57:37 +0100 |
| commit | a515b06bcb556c1be2d0fc3095cd778d413fe40d (patch) | |
| tree | 4b544de9aa53d6976177c08b91aa3943ef4d9e92 /src/ratatui/widgets/barchart.rs | |
| parent | feat: add github action to test the nix builds (#833) (diff) | |
| download | atuin-a515b06bcb556c1be2d0fc3095cd778d413fe40d.zip | |
Vendor ratatui temporarily (#835)
* Vendor ratatui temporarily
Once https://github.com/tui-rs-revival/ratatui/pull/114 has been merged,
we can undo this! But otherwise we can't publish to crates.io with a git
dependency.
* make tests pass
* Shush.
* these literally just fail in nix, nowhere else
idk how to work with nix properly, and they're also not our tests
Diffstat (limited to 'src/ratatui/widgets/barchart.rs')
| -rw-r--r-- | src/ratatui/widgets/barchart.rs | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/src/ratatui/widgets/barchart.rs b/src/ratatui/widgets/barchart.rs new file mode 100644 index 00000000..13f386a4 --- /dev/null +++ b/src/ratatui/widgets/barchart.rs @@ -0,0 +1,219 @@ +use crate::ratatui::{ + buffer::Buffer, + layout::Rect, + style::Style, + symbols, + widgets::{Block, Widget}, +}; +use std::cmp::min; +use unicode_width::UnicodeWidthStr; + +/// Display multiple bars in a single widgets +/// +/// # Examples +/// +/// ``` +/// # use ratatui::widgets::{Block, Borders, BarChart}; +/// # use ratatui::style::{Style, Color, Modifier}; +/// BarChart::default() +/// .block(Block::default().title("BarChart").borders(Borders::ALL)) +/// .bar_width(3) +/// .bar_gap(1) +/// .bar_style(Style::default().fg(Color::Yellow).bg(Color::Red)) +/// .value_style(Style::default().fg(Color::Red).add_modifier(Modifier::BOLD)) +/// .label_style(Style::default().fg(Color::White)) +/// .data(&[("B0", 0), ("B1", 2), ("B2", 4), ("B3", 3)]) +/// .max(4); +/// ``` +#[derive(Debug, Clone)] +pub struct BarChart<'a> { + /// Block to wrap the widget in + block: Option<Block<'a>>, + /// The width of each bar + bar_width: u16, + /// The gap between each bar + bar_gap: u16, + /// Set of symbols used to display the data + bar_set: symbols::bar::Set, + /// Style of the bars + bar_style: Style, + /// Style of the values printed at the bottom of each bar + value_style: Style, + /// Style of the labels printed under each bar + label_style: Style, + /// Style for the widget + style: Style, + /// Slice of (label, value) pair to plot on the chart + data: &'a [(&'a str, u64)], + /// Value necessary for a bar to reach the maximum height (if no value is specified, + /// the maximum value in the data is taken as reference) + max: Option<u64>, + /// Values to display on the bar (computed when the data is passed to the widget) + values: Vec<String>, +} + +impl<'a> Default for BarChart<'a> { + fn default() -> BarChart<'a> { + BarChart { + block: None, + max: None, + data: &[], + values: Vec::new(), + bar_style: Style::default(), + bar_width: 1, + bar_gap: 1, + bar_set: symbols::bar::NINE_LEVELS, + value_style: Default::default(), + label_style: Default::default(), + style: Default::default(), + } + } +} + +impl<'a> BarChart<'a> { + pub fn data(mut self, data: &'a [(&'a str, u64)]) -> BarChart<'a> { + self.data = data; + self.values = Vec::with_capacity(self.data.len()); + for &(_, v) in self.data { + self.values.push(format!("{}", v)); + } + self + } + + pub fn block(mut self, block: Block<'a>) -> BarChart<'a> { + self.block = Some(block); + self + } + + pub fn max(mut self, max: u64) -> BarChart<'a> { + self.max = Some(max); + self + } + + pub fn bar_style(mut self, style: Style) -> BarChart<'a> { + self.bar_style = style; + self + } + + pub fn bar_width(mut self, width: u16) -> BarChart<'a> { + self.bar_width = width; + self + } + + pub fn bar_gap(mut self, gap: u16) -> BarChart<'a> { + self.bar_gap = gap; + self + } + + pub fn bar_set(mut self, bar_set: symbols::bar::Set) -> BarChart<'a> { + self.bar_set = bar_set; + self + } + + pub fn value_style(mut self, style: Style) -> BarChart<'a> { + self.value_style = style; + self + } + + pub fn label_style(mut self, style: Style) -> BarChart<'a> { + self.label_style = style; + self + } + + pub fn style(mut self, style: Style) -> BarChart<'a> { + self.style = style; + self + } +} + +impl<'a> Widget for BarChart<'a> { + fn render(mut self, area: Rect, buf: &mut Buffer) { + buf.set_style(area, self.style); + + let chart_area = match self.block.take() { + Some(b) => { + let inner_area = b.inner(area); + b.render(area, buf); + inner_area + } + None => area, + }; + + if chart_area.height < 2 { + return; + } + + let max = self + .max + .unwrap_or_else(|| self.data.iter().map(|t| t.1).max().unwrap_or_default()); + let max_index = min( + (chart_area.width / (self.bar_width + self.bar_gap)) as usize, + self.data.len(), + ); + let mut data = self + .data + .iter() + .take(max_index) + .map(|&(l, v)| { + ( + l, + v * u64::from(chart_area.height - 1) * 8 / std::cmp::max(max, 1), + ) + }) + .collect::<Vec<(&str, u64)>>(); + for j in (0..chart_area.height - 1).rev() { + for (i, d) in data.iter_mut().enumerate() { + let symbol = match d.1 { + 0 => self.bar_set.empty, + 1 => self.bar_set.one_eighth, + 2 => self.bar_set.one_quarter, + 3 => self.bar_set.three_eighths, + 4 => self.bar_set.half, + 5 => self.bar_set.five_eighths, + 6 => self.bar_set.three_quarters, + 7 => self.bar_set.seven_eighths, + _ => self.bar_set.full, + }; + + for x in 0..self.bar_width { + buf.get_mut( + chart_area.left() + i as u16 * (self.bar_width + self.bar_gap) + x, + chart_area.top() + j, + ) + .set_symbol(symbol) + .set_style(self.bar_style); + } + + if d.1 > 8 { + d.1 -= 8; + } else { + d.1 = 0; + } + } + } + + for (i, &(label, value)) in self.data.iter().take(max_index).enumerate() { + if value != 0 { + let value_label = &self.values[i]; + let width = value_label.width() as u16; + if width < self.bar_width { + buf.set_string( + chart_area.left() + + i as u16 * (self.bar_width + self.bar_gap) + + (self.bar_width - width) / 2, + chart_area.bottom() - 2, + value_label, + self.value_style, + ); + } + } + buf.set_stringn( + chart_area.left() + i as u16 * (self.bar_width + self.bar_gap), + chart_area.bottom() - 1, + label, + self.bar_width as usize, + self.label_style, + ); + } + } +} |
