|
|
|
@ -1,3 +1,17 @@
|
|
|
|
|
//! A [`Canvas`] and a collection of [`Shape`]s.
|
|
|
|
|
//!
|
|
|
|
|
//! The [`Canvas`] is a blank space on which you can draw anything manually or use one of the
|
|
|
|
|
//! predefined [`Shape`]s.
|
|
|
|
|
//!
|
|
|
|
|
//! The available shapes are:
|
|
|
|
|
//!
|
|
|
|
|
//! - [`Circle`]: A basic circle
|
|
|
|
|
//! - [`Line`]: A line between two points
|
|
|
|
|
//! - [`Map`]: A world map
|
|
|
|
|
//! - [`Points`]: A scatter of points
|
|
|
|
|
//! - [`Rectangle`]: A basic rectangle
|
|
|
|
|
//!
|
|
|
|
|
//! You can also implement your own custom [`Shape`]s.
|
|
|
|
|
mod circle;
|
|
|
|
|
mod line;
|
|
|
|
|
mod map;
|
|
|
|
@ -18,8 +32,14 @@ pub use self::{
|
|
|
|
|
};
|
|
|
|
|
use crate::{prelude::*, symbols, text::Line as TextLine, widgets::Block};
|
|
|
|
|
|
|
|
|
|
/// Interface for all shapes that may be drawn on a Canvas widget.
|
|
|
|
|
/// Something that can be drawn on a [`Canvas`].
|
|
|
|
|
///
|
|
|
|
|
/// You may implement your own canvas custom widgets by implementing this trait.
|
|
|
|
|
pub trait Shape {
|
|
|
|
|
/// Draws this [`Shape`] using the given [`Painter`].
|
|
|
|
|
///
|
|
|
|
|
/// This is the only method required to implement a custom widget that can be drawn on a
|
|
|
|
|
/// [`Canvas`].
|
|
|
|
|
fn draw(&self, painter: &mut Painter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -37,10 +57,10 @@ pub struct Label<'a> {
|
|
|
|
|
/// multiple shapes on the canvas in specific order.
|
|
|
|
|
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
|
|
|
|
struct Layer {
|
|
|
|
|
// a string of characters representing the grid. This will be wrapped to the width of the grid
|
|
|
|
|
// A string of characters representing the grid. This will be wrapped to the width of the grid
|
|
|
|
|
// when rendering
|
|
|
|
|
string: String,
|
|
|
|
|
// colors for foreground and background
|
|
|
|
|
// Colors for foreground and background of each cell
|
|
|
|
|
colors: Vec<(Color, Color)>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -55,16 +75,18 @@ trait Grid: Debug {
|
|
|
|
|
fn width(&self) -> u16;
|
|
|
|
|
/// Get the height of the grid in number of terminal rows
|
|
|
|
|
fn height(&self) -> u16;
|
|
|
|
|
/// Get the resolution of the grid in number of dots. This doesn't have to be the same as the
|
|
|
|
|
/// number of rows and columns of the grid. For example, a grid of Braille patterns will have a
|
|
|
|
|
/// resolution of 2x4 dots per cell. This means that a grid of 10x10 cells will have a
|
|
|
|
|
/// resolution of 20x40 dots.
|
|
|
|
|
/// Get the resolution of the grid in number of dots.
|
|
|
|
|
///
|
|
|
|
|
/// This doesn't have to be the same as the number of rows and columns of the grid. For example,
|
|
|
|
|
/// a grid of Braille patterns will have a resolution of 2x4 dots per cell. This means that a
|
|
|
|
|
/// grid of 10x10 cells will have a resolution of 20x40 dots.
|
|
|
|
|
fn resolution(&self) -> (f64, f64);
|
|
|
|
|
/// Paint a point of the grid. The point is expressed in number of dots starting at the origin
|
|
|
|
|
/// of the grid in the top left corner. Note that this is not the same as the (x, y) coordinates
|
|
|
|
|
/// of the canvas.
|
|
|
|
|
/// Paint a point of the grid.
|
|
|
|
|
///
|
|
|
|
|
/// The point is expressed in number of dots starting at the origin of the grid in the top left
|
|
|
|
|
/// corner. Note that this is not the same as the `(x, y)` coordinates of the canvas.
|
|
|
|
|
fn paint(&mut self, x: usize, y: usize, color: Color);
|
|
|
|
|
/// Save the current state of the grid as a layer to be rendered
|
|
|
|
|
/// Save the current state of the [`Grid`] as a layer to be rendered
|
|
|
|
|
fn save(&self) -> Layer;
|
|
|
|
|
/// Reset the grid to its initial state
|
|
|
|
|
fn reset(&mut self);
|
|
|
|
@ -81,12 +103,12 @@ trait Grid: Debug {
|
|
|
|
|
/// to set the individual color of each dot in the braille pattern.
|
|
|
|
|
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
|
|
|
|
struct BrailleGrid {
|
|
|
|
|
/// width of the grid in number of terminal columns
|
|
|
|
|
/// Width of the grid in number of terminal columns
|
|
|
|
|
width: u16,
|
|
|
|
|
/// height of the grid in number of terminal rows
|
|
|
|
|
/// Height of the grid in number of terminal rows
|
|
|
|
|
height: u16,
|
|
|
|
|
/// represents the unicode braille patterns. Will take a value between 0x2800 and 0x28FF
|
|
|
|
|
/// this is converted to a utf16 string when converting to a layer. See
|
|
|
|
|
/// Represents the unicode braille patterns. Will take a value between `0x2800` and `0x28FF`
|
|
|
|
|
/// this is converted to an utf16 string when converting to a layer. See
|
|
|
|
|
/// <https://en.wikipedia.org/wiki/Braille_Patterns> for more info.
|
|
|
|
|
utf16_code_points: Vec<u16>,
|
|
|
|
|
/// The color of each cell only supports foreground colors for now as there's no way to
|
|
|
|
@ -152,11 +174,11 @@ impl Grid for BrailleGrid {
|
|
|
|
|
/// when you want to draw shapes with a low resolution.
|
|
|
|
|
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
|
|
|
|
struct CharGrid {
|
|
|
|
|
/// width of the grid in number of terminal columns
|
|
|
|
|
/// Width of the grid in number of terminal columns
|
|
|
|
|
width: u16,
|
|
|
|
|
/// height of the grid in number of terminal rows
|
|
|
|
|
/// Height of the grid in number of terminal rows
|
|
|
|
|
height: u16,
|
|
|
|
|
/// represents a single character for each cell
|
|
|
|
|
/// Represents a single character for each cell
|
|
|
|
|
cells: Vec<char>,
|
|
|
|
|
/// The color of each cell
|
|
|
|
|
colors: Vec<Color>,
|
|
|
|
@ -232,17 +254,17 @@ impl Grid for CharGrid {
|
|
|
|
|
/// character for each cell.
|
|
|
|
|
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash)]
|
|
|
|
|
struct HalfBlockGrid {
|
|
|
|
|
/// width of the grid in number of terminal columns
|
|
|
|
|
/// Width of the grid in number of terminal columns
|
|
|
|
|
width: u16,
|
|
|
|
|
/// height of the grid in number of terminal rows
|
|
|
|
|
/// Height of the grid in number of terminal rows
|
|
|
|
|
height: u16,
|
|
|
|
|
/// represents a single color for each "pixel" arranged in column, row order
|
|
|
|
|
/// Represents a single color for each "pixel" arranged in column, row order
|
|
|
|
|
pixels: Vec<Vec<Color>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl HalfBlockGrid {
|
|
|
|
|
/// Create a new HalfBlockGrid with the given width and height measured in terminal columns and
|
|
|
|
|
/// rows respectively.
|
|
|
|
|
/// Create a new `HalfBlockGrid` with the given width and height measured in terminal columns
|
|
|
|
|
/// and rows respectively.
|
|
|
|
|
fn new(width: u16, height: u16) -> HalfBlockGrid {
|
|
|
|
|
HalfBlockGrid {
|
|
|
|
|
width,
|
|
|
|
@ -346,33 +368,39 @@ pub struct Painter<'a, 'b> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a, 'b> Painter<'a, 'b> {
|
|
|
|
|
/// Convert the (x, y) coordinates to location of a point on the grid
|
|
|
|
|
/// Convert the `(x, y)` coordinates to location of a point on the grid
|
|
|
|
|
///
|
|
|
|
|
/// (x, y) coordinates are expressed in the coordinate system of the canvas. The origin is in
|
|
|
|
|
/// the lower left corner of the canvas (unlike most other coordinates in Ratatui where the
|
|
|
|
|
/// origin is the upper left corner). The x and y bounds of the canvas define the specific area
|
|
|
|
|
/// of some coordinate system that will be drawn on the canvas. The resolution of the grid is
|
|
|
|
|
/// used to convert the (x, y) coordinates to the location of a point on the grid.
|
|
|
|
|
/// `(x, y)` coordinates are expressed in the coordinate system of the canvas. The origin is in
|
|
|
|
|
/// the lower left corner of the canvas (unlike most other coordinates in `Ratatui` where the
|
|
|
|
|
/// origin is the upper left corner). The `x` and `y` bounds of the canvas define the specific
|
|
|
|
|
/// area of some coordinate system that will be drawn on the canvas. The resolution of the grid
|
|
|
|
|
/// is used to convert the `(x, y)` coordinates to the location of a point on the grid.
|
|
|
|
|
///
|
|
|
|
|
/// The grid coordinates are expressed in the coordinate system of the grid. The origin is in
|
|
|
|
|
/// the top left corner of the grid. The x and y bounds of the grid are always [0, width - 1]
|
|
|
|
|
/// and [0, height - 1] respectively. The resolution of the grid is used to convert the (x, y)
|
|
|
|
|
/// coordinates to the location of a point on the grid.
|
|
|
|
|
/// the top left corner of the grid. The x and y bounds of the grid are always `[0, width - 1]`
|
|
|
|
|
/// and `[0, height - 1]` respectively. The resolution of the grid is used to convert the
|
|
|
|
|
/// `(x, y)` coordinates to the location of a point on the grid.
|
|
|
|
|
///
|
|
|
|
|
/// # Examples
|
|
|
|
|
///
|
|
|
|
|
/// # Examples:
|
|
|
|
|
/// ```
|
|
|
|
|
/// use ratatui::{prelude::*, widgets::canvas::*};
|
|
|
|
|
///
|
|
|
|
|
/// let mut ctx = Context::new(2, 2, [1.0, 2.0], [0.0, 2.0], symbols::Marker::Braille);
|
|
|
|
|
/// let mut painter = Painter::from(&mut ctx);
|
|
|
|
|
///
|
|
|
|
|
/// let point = painter.get_point(1.0, 0.0);
|
|
|
|
|
/// assert_eq!(point, Some((0, 7)));
|
|
|
|
|
///
|
|
|
|
|
/// let point = painter.get_point(1.5, 1.0);
|
|
|
|
|
/// assert_eq!(point, Some((1, 3)));
|
|
|
|
|
///
|
|
|
|
|
/// let point = painter.get_point(0.0, 0.0);
|
|
|
|
|
/// assert_eq!(point, None);
|
|
|
|
|
///
|
|
|
|
|
/// let point = painter.get_point(2.0, 2.0);
|
|
|
|
|
/// assert_eq!(point, Some((3, 0)));
|
|
|
|
|
///
|
|
|
|
|
/// let point = painter.get_point(1.0, 2.0);
|
|
|
|
|
/// assert_eq!(point, Some((0, 0)));
|
|
|
|
|
/// ```
|
|
|
|
@ -396,13 +424,14 @@ impl<'a, 'b> Painter<'a, 'b> {
|
|
|
|
|
|
|
|
|
|
/// Paint a point of the grid
|
|
|
|
|
///
|
|
|
|
|
/// # Examples:
|
|
|
|
|
/// # Example
|
|
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
|
/// use ratatui::{prelude::*, widgets::canvas::*};
|
|
|
|
|
///
|
|
|
|
|
/// let mut ctx = Context::new(1, 1, [0.0, 2.0], [0.0, 2.0], symbols::Marker::Braille);
|
|
|
|
|
/// let mut painter = Painter::from(&mut ctx);
|
|
|
|
|
/// let cell = painter.paint(1, 3, Color::Red);
|
|
|
|
|
/// painter.paint(1, 3, Color::Red);
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn paint(&mut self, x: usize, y: usize, color: Color) {
|
|
|
|
|
self.context.grid.paint(x, y, color);
|
|
|
|
@ -419,7 +448,7 @@ impl<'a, 'b> From<&'a mut Context<'b>> for Painter<'a, 'b> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Holds the state of the Canvas when painting to it.
|
|
|
|
|
/// Holds the state of the [`Canvas`] when painting to it.
|
|
|
|
|
///
|
|
|
|
|
/// This is used by the [`Canvas`] widget to draw shapes on the grid. It can be useful to think of
|
|
|
|
|
/// this as similar to the [`Frame`] struct that is used to draw widgets on the terminal.
|
|
|
|
@ -437,14 +466,14 @@ pub struct Context<'a> {
|
|
|
|
|
|
|
|
|
|
impl<'a> Context<'a> {
|
|
|
|
|
/// Create a new Context with the given width and height measured in terminal columns and rows
|
|
|
|
|
/// respectively. The x and y bounds define the specific area of some coordinate system that
|
|
|
|
|
/// respectively. The `x` and `y` bounds define the specific area of some coordinate system that
|
|
|
|
|
/// will be drawn on the canvas. The marker defines the type of points used to draw the shapes.
|
|
|
|
|
///
|
|
|
|
|
/// Applications should not use this directly but rather use the [`Canvas`] widget. This will be
|
|
|
|
|
/// created by the [`Canvas::paint`] moethod and passed to the closure that is used to draw on
|
|
|
|
|
/// created by the [`Canvas::paint`] method and passed to the closure that is used to draw on
|
|
|
|
|
/// the canvas.
|
|
|
|
|
///
|
|
|
|
|
/// The x and y bounds should be specified as left/right and bottom/top respectively. For
|
|
|
|
|
/// The `x` and `y` bounds should be specified as left/right and bottom/top respectively. For
|
|
|
|
|
/// example, if you want to draw a map of the world, you might want to use the following bounds:
|
|
|
|
|
///
|
|
|
|
|
/// ```
|
|
|
|
@ -485,7 +514,7 @@ impl<'a> Context<'a> {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Draw any object that may implement the Shape trait
|
|
|
|
|
/// Draw the given [`Shape`] in this context
|
|
|
|
|
pub fn draw<S>(&mut self, shape: &S)
|
|
|
|
|
where
|
|
|
|
|
S: Shape,
|
|
|
|
@ -495,16 +524,23 @@ impl<'a> Context<'a> {
|
|
|
|
|
shape.draw(&mut painter);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Save the existing state of the grid as a layer to be rendered and reset the grid to its
|
|
|
|
|
/// initial state for the next layer.
|
|
|
|
|
/// Save the existing state of the grid as a layer.
|
|
|
|
|
///
|
|
|
|
|
/// Save the existing state as a layer to be rendered and reset the grid to its initial
|
|
|
|
|
/// state for the next layer.
|
|
|
|
|
///
|
|
|
|
|
/// 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.
|
|
|
|
|
pub fn layer(&mut self) {
|
|
|
|
|
self.layers.push(self.grid.save());
|
|
|
|
|
self.grid.reset();
|
|
|
|
|
self.dirty = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Print a string on the canvas at the given position. Note that the text is always printed
|
|
|
|
|
/// on top of the canvas and is not affected by the layers.
|
|
|
|
|
/// Print a [`Text`] on the [`Canvas`] at the given position.
|
|
|
|
|
///
|
|
|
|
|
/// Note that the text is always printed on top of the canvas and is **not** affected by the
|
|
|
|
|
/// layers.
|
|
|
|
|
pub fn print<T>(&mut self, x: f64, y: f64, line: T)
|
|
|
|
|
where
|
|
|
|
|
T: Into<TextLine<'a>>,
|
|
|
|
@ -516,7 +552,7 @@ impl<'a> Context<'a> {
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Push the last layer if necessary
|
|
|
|
|
/// Save the last layer if necessary
|
|
|
|
|
fn finish(&mut self) {
|
|
|
|
|
if self.dirty {
|
|
|
|
|
self.layer();
|
|
|
|
@ -619,15 +655,22 @@ impl<'a, F> Canvas<'a, F>
|
|
|
|
|
where
|
|
|
|
|
F: Fn(&mut Context),
|
|
|
|
|
{
|
|
|
|
|
/// Set the block that will be rendered around the canvas
|
|
|
|
|
/// Wraps the canvas with a custom [`Block`] widget.
|
|
|
|
|
///
|
|
|
|
|
/// This is a fluent setter method which must be chained or used as it consumes self
|
|
|
|
|
#[must_use = "method moves the value of self and returns the modified value"]
|
|
|
|
|
pub fn block(mut self, block: Block<'a>) -> Canvas<'a, F> {
|
|
|
|
|
self.block = Some(block);
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Define the viewport of the canvas.
|
|
|
|
|
///
|
|
|
|
|
/// If you were to "zoom" to a certain part of the world you may want to choose different
|
|
|
|
|
/// bounds.
|
|
|
|
|
///
|
|
|
|
|
/// This is a fluent setter method which must be chained or used as it consumes self
|
|
|
|
|
#[must_use = "method moves the value of self and returns the modified value"]
|
|
|
|
|
pub fn x_bounds(mut self, bounds: [f64; 2]) -> Canvas<'a, F> {
|
|
|
|
|
self.x_bounds = bounds;
|
|
|
|
|
self
|
|
|
|
@ -637,31 +680,48 @@ where
|
|
|
|
|
///
|
|
|
|
|
/// If you were to "zoom" to a certain part of the world you may want to choose different
|
|
|
|
|
/// bounds.
|
|
|
|
|
///
|
|
|
|
|
/// This is a fluent setter method which must be chained or used as it consumes self
|
|
|
|
|
#[must_use = "method moves the value of self and returns the modified value"]
|
|
|
|
|
pub fn y_bounds(mut self, bounds: [f64; 2]) -> Canvas<'a, F> {
|
|
|
|
|
self.y_bounds = bounds;
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Store the closure that will be used to draw to the Canvas
|
|
|
|
|
/// Store the closure that will be used to draw to the [`Canvas`]
|
|
|
|
|
///
|
|
|
|
|
/// This is a fluent setter method which must be chained or used as it consumes self
|
|
|
|
|
#[must_use = "method moves the value of self and returns the modified value"]
|
|
|
|
|
pub fn paint(mut self, f: F) -> Canvas<'a, F> {
|
|
|
|
|
self.paint_func = Some(f);
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Change the background color of the canvas
|
|
|
|
|
/// Change the background [`Color`] of the entire canvas
|
|
|
|
|
///
|
|
|
|
|
/// This is a fluent setter method which must be chained or used as it consumes self
|
|
|
|
|
#[must_use = "method moves the value of self and returns the modified value"]
|
|
|
|
|
pub fn background_color(mut self, color: Color) -> Canvas<'a, F> {
|
|
|
|
|
self.background_color = color;
|
|
|
|
|
self
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Change the type of points used to draw the shapes. By default the braille patterns are used
|
|
|
|
|
/// as they provide a more fine grained result but you might want to use the simple dot or
|
|
|
|
|
/// block instead if the targeted terminal does not support those symbols.
|
|
|
|
|
/// Change the type of points used to draw the shapes.
|
|
|
|
|
///
|
|
|
|
|
/// The HalfBlock marker is useful when you want to draw shapes with a higher resolution than a
|
|
|
|
|
/// CharGrid but lower than a BrailleGrid. This grid type supports a foreground and background
|
|
|
|
|
/// color for each terminal cell. This allows for more flexibility than the BrailleGrid which
|
|
|
|
|
/// only supports a single foreground color for each 2x4 dots cell.
|
|
|
|
|
/// By default the [`Braille`] patterns are used as they provide a more fine grained result,
|
|
|
|
|
/// but you might want to use the simple [`Dot`] or [`Block`] instead if the targeted terminal
|
|
|
|
|
/// does not support those symbols.
|
|
|
|
|
///
|
|
|
|
|
/// The [`HalfBlock`] marker is useful when you want to draw shapes with a higher resolution
|
|
|
|
|
/// than with a grid of characters (e.g. with [`Block`] or [`Dot`]) but lower than with
|
|
|
|
|
/// [`Braille`]. This grid type supports a foreground and background color for each terminal
|
|
|
|
|
/// cell. This allows for more flexibility than the BrailleGrid which only supports a single
|
|
|
|
|
/// foreground color for each 2x4 dots cell.
|
|
|
|
|
///
|
|
|
|
|
/// [`Braille`]: crate::symbols::Marker::Braille
|
|
|
|
|
/// [`HalfBlock`]: crate::symbols::Marker::HalfBlock
|
|
|
|
|
/// [`Dot`]: crate::symbols::Marker::Dot
|
|
|
|
|
/// [`Block`]: crate::symbols::Marker::Block
|
|
|
|
|
///
|
|
|
|
|
/// # Examples
|
|
|
|
|
///
|
|
|
|
@ -671,12 +731,15 @@ where
|
|
|
|
|
/// Canvas::default()
|
|
|
|
|
/// .marker(symbols::Marker::Braille)
|
|
|
|
|
/// .paint(|ctx| {});
|
|
|
|
|
///
|
|
|
|
|
/// Canvas::default()
|
|
|
|
|
/// .marker(symbols::Marker::HalfBlock)
|
|
|
|
|
/// .paint(|ctx| {});
|
|
|
|
|
///
|
|
|
|
|
/// Canvas::default()
|
|
|
|
|
/// .marker(symbols::Marker::Dot)
|
|
|
|
|
/// .paint(|ctx| {});
|
|
|
|
|
///
|
|
|
|
|
/// Canvas::default()
|
|
|
|
|
/// .marker(symbols::Marker::Block)
|
|
|
|
|
/// .paint(|ctx| {});
|
|
|
|
|