winio 0.11.0

Single-threaded async GUI runtime based on compio.
Documentation
use std::ops::Deref;

use plotters::prelude::{Color as _, *};
use plotters_backend::DrawingErrorKind;
use winio::prelude::*;

use crate::{Error, Result};

pub struct PlottersPage {
    window: Child<TabViewItem>,
    canvas: Child<Canvas>,
}

#[derive(Debug)]
pub enum PlottersPageEvent {}

#[derive(Debug)]
pub enum PlottersPageMessage {}

impl Component for PlottersPage {
    type Error = Error;
    type Event = PlottersPageEvent;
    type Init<'a> = ();
    type Message = PlottersPageMessage;

    async fn init(_init: Self::Init<'_>, _sender: &ComponentSender<Self>) -> Result<Self> {
        init! {
            window: TabViewItem = (()) => {
                text: "Plotters",
            },
            canvas: Canvas = (&window),
        }

        Ok(Self { window, canvas })
    }

    fn render(&mut self, _sender: &ComponentSender<Self>) -> Result<()> {
        let size = self.window.size()?;
        self.canvas.set_rect(size.into())?;

        let root = WinioCanvasBackend::new(&mut self.canvas)?.into_drawing_area();

        let fore = if ColorTheme::current()? == ColorTheme::Dark {
            WHITE
        } else {
            BLACK
        };
        const FONT: &str = "sans-serif";

        let mut chart = ChartBuilder::on(&root)
            .caption("y=x^2", (FONT, 50).into_font().color(&fore))
            .margin(10)
            .x_label_area_size(30)
            .y_label_area_size(30)
            .build_cartesian_2d(-1f32..1f32, -0.1f32..1f32)?;
        chart
            .configure_mesh()
            .axis_style(fore)
            .light_line_style(fore.mix(0.1))
            .bold_line_style(fore.mix(0.2))
            .label_style(FONT.into_font().color(&fore))
            .draw()?;
        chart
            .draw_series(LineSeries::new(
                (-50..=50).map(|x| x as f32 / 50.0).map(|x| (x, x * x)),
                &RED,
            ))?
            .label("y = x^2")
            .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RED));

        chart
            .configure_series_labels()
            .background_style(TRANSPARENT)
            .border_style(fore)
            .label_font(FONT.into_font().color(&fore))
            .draw()?;
        root.present()?;

        Ok(())
    }
}

impl Deref for PlottersPage {
    type Target = TabViewItem;

    fn deref(&self) -> &Self::Target {
        &self.window
    }
}

impl From<DrawingAreaErrorKind<winio::Error>> for Error {
    fn from(value: DrawingAreaErrorKind<winio::Error>) -> Self {
        match value {
            DrawingAreaErrorKind::BackendError(DrawingErrorKind::DrawingError(e)) => e.into(),
            DrawingAreaErrorKind::BackendError(DrawingErrorKind::FontError(e)) => {
                Error::Io(std::io::Error::other(e))
            }
            _ => Error::Io(std::io::Error::other(value)),
        }
    }
}