From 5f1a37f0db779cd4287ca7523b02e40e92a82543 Mon Sep 17 00:00:00 2001 From: Josh McKinney Date: Sat, 15 Apr 2023 08:40:28 -0700 Subject: [PATCH] fix(canvas)!: use full block for Marker::Block (#133) --- Cargo.toml | 1 + examples/canvas.rs | 17 ++++++ src/symbols.rs | 2 + src/widgets/canvas/mod.rs | 108 +++++++++++++++++++++++++++++++++++++- 4 files changed, 126 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 041826b4..41821a26 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ serde = { version = "1", optional = true, features = ["derive"]} [dev-dependencies] rand = "0.8" argh = "0.1" +indoc = "2.0" [[example]] name = "barchart" diff --git a/examples/canvas.rs b/examples/canvas.rs index bec2df4c..a86ae185 100644 --- a/examples/canvas.rs +++ b/examples/canvas.rs @@ -7,6 +7,7 @@ use ratatui::{ backend::{Backend, CrosstermBackend}, layout::{Constraint, Direction, Layout, Rect}, style::{Color, Style}, + symbols::Marker, text::Span, widgets::{ canvas::{Canvas, Map, MapResolution, Rectangle}, @@ -29,6 +30,8 @@ struct App { vy: f64, dir_x: bool, dir_y: bool, + tick_count: u64, + marker: Marker, } impl App { @@ -48,10 +51,22 @@ impl App { vy: 1.0, dir_x: true, dir_y: true, + tick_count: 0, + marker: Marker::Dot, } } fn on_tick(&mut self) { + self.tick_count += 1; + // only change marker every 4 ticks (1s) to avoid stroboscopic effect + if (self.tick_count % 4) == 0 { + self.marker = match self.marker { + Marker::Dot => Marker::Block, + Marker::Block => Marker::Bar, + Marker::Bar => Marker::Braille, + Marker::Braille => Marker::Dot, + }; + } if self.ball.x < self.playground.left() as f64 || self.ball.x + self.ball.width > self.playground.right() as f64 { @@ -155,6 +170,7 @@ fn ui(f: &mut Frame, app: &App) { .split(f.size()); let canvas = Canvas::default() .block(Block::default().borders(Borders::ALL).title("World")) + .marker(app.marker) .paint(|ctx| { ctx.draw(&Map { color: Color::White, @@ -171,6 +187,7 @@ fn ui(f: &mut Frame, app: &App) { f.render_widget(canvas, chunks[0]); let canvas = Canvas::default() .block(Block::default().borders(Borders::ALL).title("Pong")) + .marker(app.marker) .paint(|ctx| { ctx.draw(&app.ball); }) diff --git a/src/symbols.rs b/src/symbols.rs index 040e77f6..273fd59d 100644 --- a/src/symbols.rs +++ b/src/symbols.rs @@ -228,6 +228,8 @@ pub enum Marker { Dot, /// One point per cell in shape of a block Block, + /// One point per cell in the shape of a bar + Bar, /// Up to 8 points per cell Braille, } diff --git a/src/widgets/canvas/mod.rs b/src/widgets/canvas/mod.rs index 91af2b7e..035d431f 100644 --- a/src/widgets/canvas/mod.rs +++ b/src/widgets/canvas/mod.rs @@ -260,9 +260,13 @@ impl<'a> Context<'a> { y_bounds: [f64; 2], marker: symbols::Marker, ) -> Context<'a> { + let dot = symbols::DOT.chars().next().unwrap(); + let block = symbols::block::FULL.chars().next().unwrap(); + let bar = symbols::bar::HALF.chars().next().unwrap(); let grid: Box = match marker { - symbols::Marker::Dot => Box::new(CharGrid::new(width, height, '•')), - symbols::Marker::Block => Box::new(CharGrid::new(width, height, '▄')), + symbols::Marker::Dot => Box::new(CharGrid::new(width, height, dot)), + symbols::Marker::Block => Box::new(CharGrid::new(width, height, block)), + symbols::Marker::Bar => Box::new(CharGrid::new(width, height, bar)), symbols::Marker::Braille => Box::new(BrailleGrid::new(width, height)), }; Context { @@ -508,3 +512,103 @@ where } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::{buffer::Cell, symbols::Marker}; + use indoc::indoc; + + // helper to test the canvas checks that drawing a vertical and horizontal line + // results in the expected output + fn test_marker(marker: Marker, expected: &str) { + let area = Rect::new(0, 0, 5, 5); + let mut cell = Cell::default(); + cell.set_char('x'); + let mut buf = Buffer::filled(area, &cell); + let horizontal_line = Line { + x1: 0.0, + y1: 0.0, + x2: 10.0, + y2: 0.0, + color: Color::Reset, + }; + let vertical_line = Line { + x1: 0.0, + y1: 0.0, + x2: 0.0, + y2: 10.0, + color: Color::Reset, + }; + Canvas::default() + .marker(marker) + .paint(|ctx| { + ctx.draw(&vertical_line); + ctx.draw(&horizontal_line); + }) + .x_bounds([0.0, 10.0]) + .y_bounds([0.0, 10.0]) + .render(area, &mut buf); + assert_eq!(buf, Buffer::with_lines(expected.lines().collect())); + } + + #[test] + fn test_bar_marker() { + test_marker( + Marker::Bar, + indoc!( + " + ▄xxxx + ▄xxxx + ▄xxxx + ▄xxxx + ▄▄▄▄▄" + ), + ); + } + + #[test] + fn test_block_marker() { + test_marker( + Marker::Block, + indoc!( + " + █xxxx + █xxxx + █xxxx + █xxxx + █████" + ), + ); + } + + #[test] + fn test_braille_marker() { + test_marker( + Marker::Braille, + indoc!( + " + ⡇xxxx + ⡇xxxx + ⡇xxxx + ⡇xxxx + ⣇⣀⣀⣀⣀" + ), + ); + } + + #[test] + fn test_dot_marker() { + test_marker( + Marker::Dot, + indoc!( + " + •xxxx + •xxxx + •xxxx + •xxxx + •••••" + ), + ); + } +}