chore(examples): add canvas demo app (#1578)

related #1512 

This moves the canvas example to the apps and adds some interactivity
via changing the marker by pressing enter.
This commit is contained in:
Orhun Parmaksız 2024-12-19 07:06:12 +03:00 committed by GitHub
parent 9a541981b8
commit 9721300a47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 69 additions and 38 deletions

10
Cargo.lock generated
View File

@ -316,6 +316,16 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "canvas"
version = "0.0.0"
dependencies = [
"color-eyre",
"crossterm",
"itertools 0.13.0",
"ratatui",
]
[[package]] [[package]]
name = "cargo-platform" name = "cargo-platform"
version = "0.1.9" version = "0.1.9"

View File

@ -0,0 +1,15 @@
[package]
name = "canvas"
publish = false
license.workspace = true
edition.workspace = true
rust-version.workspace = true
[dependencies]
color-eyre.workspace = true
crossterm.workspace = true
itertools.workspace = true
ratatui.workspace = true
[lints]
workspace = true

View File

@ -0,0 +1,9 @@
# Canvas demo
This example shows how to render various shapes and a map on a canvas.
To run this demo:
```shell
cargo run -p canvas
```

View File

@ -1,18 +1,13 @@
//! # [Ratatui] Canvas example /// A Ratatui example that demonstrates how to draw on a canvas.
//! ///
//! The latest version of this example is available in the [examples] folder in the repository. /// This example demonstrates how to draw various shapes such as rectangles, circles, and lines
//! /// on a canvas. It also demonstrates how to draw a map.
//! Please note that the examples are designed to be run against the `main` branch of the Github ///
//! repository. This means that you may not be able to compile with the latest release version on /// This example runs with the Ratatui library code in the branch that you are currently
//! crates.io, or the one that you have installed locally. /// reading. See the [`latest`] branch for the code which works with the most recent Ratatui
//! /// release.
//! See the [examples readme] for more information on finding examples that match the version of the ///
//! library you are using. /// [`latest`]: https://github.com/ratatui/ratatui/tree/latest
//!
//! [Ratatui]: https://github.com/ratatui/ratatui
//! [examples]: https://github.com/ratatui/ratatui/blob/main/examples
//! [examples readme]: https://github.com/ratatui/ratatui/blob/main/examples/README.md
use std::{ use std::{
io::stdout, io::stdout,
time::{Duration, Instant}, time::{Duration, Instant},
@ -29,6 +24,7 @@ use ratatui::{
layout::{Constraint, Layout, Position, Rect}, layout::{Constraint, Layout, Position, Rect},
style::{Color, Stylize}, style::{Color, Stylize},
symbols::Marker, symbols::Marker,
text::Text,
widgets::{ widgets::{
canvas::{Canvas, Circle, Map, MapResolution, Points, Rectangle}, canvas::{Canvas, Circle, Map, MapResolution, Points, Rectangle},
Block, Widget, Block, Widget,
@ -54,7 +50,6 @@ struct App {
playground: Rect, playground: Rect,
vx: f64, vx: f64,
vy: f64, vy: f64,
tick_count: u64,
marker: Marker, marker: Marker,
points: Vec<Position>, points: Vec<Position>,
is_drawing: bool, is_drawing: bool,
@ -75,7 +70,6 @@ impl App {
playground: Rect::new(10, 10, 200, 100), playground: Rect::new(10, 10, 200, 100),
vx: 1.0, vx: 1.0,
vy: 1.0, vy: 1.0,
tick_count: 0,
marker: Marker::Dot, marker: Marker::Dot,
points: vec![], points: vec![],
is_drawing: false, is_drawing: false,
@ -114,6 +108,15 @@ impl App {
KeyCode::Up | KeyCode::Char('k') => self.y -= 1.0, KeyCode::Up | KeyCode::Char('k') => self.y -= 1.0,
KeyCode::Right | KeyCode::Char('l') => self.x += 1.0, KeyCode::Right | KeyCode::Char('l') => self.x += 1.0,
KeyCode::Left | KeyCode::Char('h') => self.x -= 1.0, KeyCode::Left | KeyCode::Char('h') => self.x -= 1.0,
KeyCode::Enter => {
self.marker = match self.marker {
Marker::Dot => Marker::Braille,
Marker::Braille => Marker::Block,
Marker::Block => Marker::HalfBlock,
Marker::HalfBlock => Marker::Bar,
Marker::Bar => Marker::Dot,
};
}
_ => {} _ => {}
} }
} }
@ -130,17 +133,6 @@ impl App {
} }
fn on_tick(&mut self) { fn on_tick(&mut self) {
self.tick_count += 1;
// only change marker every 180 ticks (3s) to avoid stroboscopic effect
if (self.tick_count % 180) == 0 {
self.marker = match self.marker {
Marker::Dot => Marker::Braille,
Marker::Braille => Marker::Block,
Marker::Block => Marker::HalfBlock,
Marker::HalfBlock => Marker::Bar,
Marker::Bar => Marker::Dot,
};
}
// bounce the ball by flipping the velocity vector // bounce the ball by flipping the velocity vector
let ball = &self.ball; let ball = &self.ball;
let playground = self.playground; let playground = self.playground;
@ -154,18 +146,28 @@ impl App {
{ {
self.vy = -self.vy; self.vy = -self.vy;
} }
self.ball.x += self.vx; self.ball.x += self.vx;
self.ball.y += self.vy; self.ball.y += self.vy;
} }
fn draw(&self, frame: &mut Frame) { fn draw(&self, frame: &mut Frame) {
let header = Text::from_iter([
"Canvas Example".bold(),
"<q> Quit | <enter> Change Marker | <hjkl> Move".into(),
]);
let vertical = Layout::vertical([
Constraint::Length(header.height() as u16),
Constraint::Percentage(50),
Constraint::Percentage(50),
]);
let [text_area, up, down] = vertical.areas(frame.area());
frame.render_widget(header.centered(), text_area);
let horizontal = let horizontal =
Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]); Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]);
let vertical = Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]); let [draw, pong] = horizontal.areas(up);
let [left, right] = horizontal.areas(frame.area()); let [map, boxes] = horizontal.areas(down);
let [draw, map] = vertical.areas(left);
let [pong, boxes] = vertical.areas(right);
frame.render_widget(self.map_canvas(), map); frame.render_widget(self.map_canvas(), map);
frame.render_widget(self.draw_canvas(draw), draw); frame.render_widget(self.draw_canvas(draw), draw);

View File

@ -142,11 +142,6 @@ bench = false
name = "main" name = "main"
harness = false harness = false
[[example]]
name = "canvas"
required-features = ["crossterm"]
doc-scrape-examples = true
[[example]] [[example]]
name = "chart" name = "chart"
required-features = ["crossterm"] required-features = ["crossterm"]