plotters_ratatui_backend/
widget.rs

1use plotters::coord;
2use plotters::prelude::{DrawingArea, DrawingAreaErrorKind, IntoDrawingArea};
3use ratatui::layout;
4use ratatui::prelude::Buffer;
5use ratatui::widgets::canvas::Canvas;
6use ratatui::widgets::Widget;
7
8use crate::{Error, RatatuiBackend};
9
10pub type AreaResult<T = ()> = Result<T, DrawingAreaErrorKind<Error>>;
11
12/// A ratatui widget that draws a plotters chart.
13pub struct PlottersWidget<D, E> {
14    pub draw:          D,
15    pub error_handler: E,
16}
17
18/// Creates a [ratatui widget](Widget) for drawing a plotters chart.
19pub fn widget_fn(
20    draw_fn: impl Fn(DrawingArea<RatatuiBackend, coord::Shift>) -> AreaResult,
21) -> PlottersWidget<impl Draw, impl Fn(DrawingAreaErrorKind<Error>)> {
22    PlottersWidget {
23        draw:          DrawFn(draw_fn),
24        error_handler: |err| log::error!("Plotters draw error: {err:?}"),
25    }
26}
27
28pub struct DrawFn<F>(pub F);
29
30impl<F: Fn(DrawingArea<RatatuiBackend, coord::Shift>) -> AreaResult> Draw for DrawFn<F> {
31    fn draw(&self, b: DrawingArea<RatatuiBackend, coord::Shift>) -> AreaResult { (self.0)(b) }
32}
33
34pub trait Draw {
35    fn draw(&self, b: DrawingArea<RatatuiBackend, coord::Shift>) -> AreaResult;
36}
37
38impl<D: Draw, E: Fn(DrawingAreaErrorKind<Error>)> Widget for PlottersWidget<D, E> {
39    fn render(self, area: layout::Rect, buf: &mut Buffer) {
40        let (size_x, size_y) = super::rect_to_size(area);
41        log::debug!("size: {area:?}, {size_x}, {size_y}");
42        let canvas =
43            Canvas::default().x_bounds([0.0, 1.0]).y_bounds([0.0, 1.0]).paint(move |canvas| {
44                let backend = RatatuiBackend { canvas, size: area };
45                let area = backend.into_drawing_area();
46                if let Err(err) = self.draw.draw(area) {
47                    (self.error_handler)(err);
48                }
49            });
50        canvas.render(area, buf)
51    }
52}