[
  {
    "path": ".github/dependabot.yml",
    "content": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where the package manifests are located.\n# Please see the documentation for all configuration options:\n# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\n\nversion: 2\nupdates:\n- package-ecosystem: cargo\n  directory: \"/\"\n  schedule:\n    interval: \"daily\"\n  target-branch: \"master\"\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "name: Test and Build\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  build:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      matrix:\n        os: [ubuntu-latest, windows-latest, macOS-latest]\n        rust: [stable]\n    steps:\n      - uses: actions/checkout@master\n      - name: Install Rust toolchain\n        uses: actions-rs/toolchain@v1\n        with:\n          toolchain: ${{ matrix.rust }}\n          components: rustfmt\n          override: true\n      - name: Install dependencies\n        run: |\n          if [ \"$RUNNER_OS\" == \"Linux\" ]; then\n              sudo apt-get -qq update\n              sudo apt-get install -y libxkbcommon-dev\n          fi\n        shell: bash\n      - name: Verify versions\n        run: rustc --version && rustup --version && cargo --version\n      - name: Cargo Build\n        run: cargo build --verbose\n      - name: Build example\n        run: cargo build --examples\n        continue-on-error: true\n      - name: Run tests\n        run: cargo test --verbose\n      - name: Check code style\n        run: cargo fmt -- --check\n"
  },
  {
    "path": ".gitignore",
    "content": "target/*\nCargo.lock\n.cargo/*\nexamples/split-chart/dist/*\n.idea\n.DS_store\n.history"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"plotters-iced\"\nversion = \"0.11.0\"\ndescription = \"Iced backend for Plotters\"\nreadme = \"README.md\"\nlicense = \"MIT\"\nedition = \"2021\"\nresolver = \"2\"\nhomepage = \"https://github.com/Joylei/plotters-iced\"\nrepository = \"https://github.com/Joylei/plotters-iced.git\"\ndocumentation = \"https://docs.rs/crate/plotters-iced/\"\nkeywords = [\"plotters\", \"chart\", \"plot\", \"iced\", \"backend\"]\ncategories = [\"visualization\"]\nauthors = [\"Joylei <leingliu@gmail.com>\"]\n\n[workspace]\nmembers = [\".\", \"examples/split-chart\"]\n\n[dependencies]\nplotters = { version = \"0.3\", default-features = false }\nplotters-backend = \"0.3\"\niced_widget = { version = \"0.13\", features = [\"canvas\"] }\niced_graphics = \"0.13\"\nonce_cell = \"1\"\n\n[dev-dependencies]\nplotters = { version = \"0.3\", default-features = false, features = [\n    \"chrono\",\n    \"area_series\",\n    \"line_series\",\n    \"point_series\",\n] }\niced = { version = \"0.13\", features = [\"canvas\", \"tokio\"] }\nchrono = { version = \"0.4\", default-features = false }\nrand = \"0.8\"\ntokio = { version = \"1\", features = [\"rt\"], default-features = false }\n\n[target.'cfg(not(target_arch = \"wasm32\"))'.dev-dependencies]\nsysinfo = { version = \"0.30\", default-features = false }\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022, Joylei <leingliu@gmail.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# plotters-iced\n[![Test and Build](https://github.com/joylei/plotters-iced/workflows/Test%20and%20Build/badge.svg?branch=master)](https://github.com/joylei/plotters-iced/actions?query=workflow%3A%22Test+and+Build%22)\n[![Documentation](https://docs.rs/plotters-iced/badge.svg)](https://docs.rs/plotters-iced)\n[![Crates.io](https://img.shields.io/crates/v/plotters-iced.svg)](https://crates.io/crates/plotters-iced)\n[![License](https://img.shields.io/crates/l/plotters-iced.svg)](https://github.com/joylei/plotters-iced/blob/master/LICENSE)\n\nThis is an implementation of an Iced backend for Plotters, for both native and wasm applications.\n\nThis backend has been optimized as for speed. Note that some specific plotting features supported in the Bitmap backend may not be implemented there, though.\n\n## Showcase\n\n![CPU Monitor Example](./images/plotter_iced_demo.png)\n\n![WASM Example](./images/split-chart-web.png)\n\n## What is Plotters?\n\nPlotters is an extensible Rust drawing library that can be used to plot data on nice-looking graphs, rendering them through a plotting backend (eg. to a Bitmap image raw buffer, to your GUI backend, to an SVG file, etc.).\n\n**For more details on Plotters, please check the following links:**\n\n- For an introduction of Plotters, see: [Plotters on Crates.io](https://crates.io/crates/plotters);\n- Check the main repository on [GitHub](https://github.com/38/plotters);\n- You can also visit the Plotters [homepage](https://plotters-rs.github.io/);\n\n## How to install?\n\nInclude `plotters-iced` in your `Cargo.toml` dependencies:\n\n```toml\n[dependencies]\nplotters-iced = \"0.11\"\niced = { version = \"0.13\", features = [\"canvas\", \"tokio\"] }\nplotters=\"0.3\"\n```\n\n## How to use?\n\nFirst, import `Chart` and `ChartWidget`:\n\n```rust,ignore\nuse plotters_iced::{Chart, ChartWidget, DrawingBackend, ChartBuilder};\n```\n\nThen, derive `Chart` trait and build your chart, and let `plotters-iced` takes care the rest:\n\n```rust,ignore\nstruct MyChart;\nimpl Chart<Message> for MyChart {\n    type State = ();\n    fn build_chart<DB:DrawingBackend>(&self, state: &Self::State, builder: ChartBuilder<DB>) {\n        //build your chart here, please refer to plotters for more details\n    }\n}\n```\n\nFinally, render your chart view:\n\n```rust,ignore\nimpl MyChart {\n    fn view(&mut self)->Element<Message> {\n        ChartWidget::new(self)\n            .width(Length::Fixed(200))\n            .height(Length::Fixed(200))\n            .into()\n    }\n}\n```\n\n_If you are looking for a full example of an implementation, please check [cpu-monitor.rs](./examples/cpu-monitor.rs)._\n\n## How to run the examples?\n\n### Example #1: `cpu-monitor`\n\nThis example samples your CPU load every second, and renders it in a real-time chart:\n\n```sh\ncargo run --release --example cpu-monitor\n```\n\nFrom this example, you'll learn:\n\n- how to build charts by `plotters-iced`\n- how to feed data to charts\n- how to make layouts of charts responsive\n- how to use fonts with charts\n\n### Example #2: `split-chart`\n\nThis example shows you how to split drawing area.\n\n- run the native version\n\n```sh\ncargo run --release --example split-chart\n```\n\n- run the web version\n\n```sh\ncd examples/split-chart\ntrunk serve\n```\n\n## Are there any limitations?\n\n### Limitation #1: No image rendering\n\nNo image rendering for native and wasm applications.\n\n### Limitation #2: Limited text rendering for native applications\n\nOnly TTF font family are supported for text rendering, which is a limitation of `Iced`, please look at  [cpu-monitor.rs](./examples/cpu-monitor.rs). As well, font transforms are not supported,which is also a limitation of `Iced`.\n\n## Credits\n\n- [plotters-conrod](https://github.com/valeriansaliou/plotters-conrod)"
  },
  {
    "path": "examples/cpu-monitor.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n\nextern crate iced;\nextern crate plotters;\nextern crate sysinfo;\n\nuse chrono::{DateTime, Utc};\nuse iced::{\n    alignment::{Horizontal, Vertical},\n    font,\n    widget::{\n        canvas::{Cache, Frame, Geometry},\n        Column, Container, Row, Scrollable, Space, Text,\n    },\n    Alignment, Element, Font, Length, Size, Task,\n};\nuse plotters::prelude::ChartBuilder;\nuse plotters_backend::DrawingBackend;\nuse plotters_iced::{Chart, ChartWidget, Renderer};\nuse std::{\n    collections::VecDeque,\n    time::{Duration, Instant},\n};\nuse sysinfo::{CpuRefreshKind, RefreshKind, System};\n\nconst PLOT_SECONDS: usize = 60; //1 min\nconst TITLE_FONT_SIZE: u16 = 22;\nconst SAMPLE_EVERY: Duration = Duration::from_millis(1000);\n\nconst FONT_BOLD: Font = Font {\n    family: font::Family::Name(\"Noto Sans\"),\n    weight: font::Weight::Bold,\n    ..Font::DEFAULT\n};\n\nfn main() {\n    iced::application(\"CPU Monitor Example\", State::update, State::view)\n        .antialiasing(true)\n        .default_font(Font::with_name(\"Noto Sans\"))\n        .subscription(|_| {\n            const FPS: u64 = 50;\n            iced::time::every(Duration::from_millis(1000 / FPS)).map(|_| Message::Tick)\n        })\n        .run_with(State::new)\n        .unwrap();\n}\n\n#[derive(Debug)]\nenum Message {\n    /// message that cause charts' data lazily updated\n    Tick,\n    FontLoaded(Result<(), font::Error>),\n}\n\nstruct State {\n    chart: SystemChart,\n}\n\nimpl State {\n    fn new() -> (Self, Task<Message>) {\n        (\n            Self {\n                chart: Default::default(),\n            },\n            Task::batch([\n                font::load(include_bytes!(\"./fonts/notosans-regular.ttf\").as_slice())\n                    .map(Message::FontLoaded),\n                font::load(include_bytes!(\"./fonts/notosans-bold.ttf\").as_slice())\n                    .map(Message::FontLoaded),\n            ]),\n        )\n    }\n\n    fn update(&mut self, message: Message) {\n        match message {\n            Message::Tick => {\n                self.chart.update();\n            }\n            _ => {}\n        }\n    }\n\n    fn view(&self) -> Element<'_, Message> {\n        let content = Column::new()\n            .spacing(20)\n            .align_x(Alignment::Start)\n            .width(Length::Fill)\n            .height(Length::Fill)\n            .push(\n                Text::new(\"Iced test chart\")\n                    .size(TITLE_FONT_SIZE)\n                    .font(FONT_BOLD),\n            )\n            .push(self.chart.view());\n\n        Container::new(content)\n            //.style(style::Container)\n            .padding(5)\n            .center_x(Length::Fill)\n            .center_y(Length::Fill)\n            .into()\n    }\n}\n\nstruct SystemChart {\n    sys: System,\n    last_sample_time: Instant,\n    items_per_row: usize,\n    processors: Vec<CpuUsageChart>,\n    chart_height: f32,\n}\n\nimpl Default for SystemChart {\n    fn default() -> Self {\n        Self {\n            sys: System::new_with_specifics(\n                RefreshKind::new().with_cpu(CpuRefreshKind::new().with_cpu_usage()),\n            ),\n            last_sample_time: Instant::now(),\n            items_per_row: 3,\n            processors: Default::default(),\n            chart_height: 300.0,\n        }\n    }\n}\n\nimpl SystemChart {\n    #[inline]\n    fn is_initialized(&self) -> bool {\n        !self.processors.is_empty()\n    }\n\n    #[inline]\n    fn should_update(&self) -> bool {\n        !self.is_initialized() || self.last_sample_time.elapsed() > SAMPLE_EVERY\n    }\n\n    fn update(&mut self) {\n        if !self.should_update() {\n            return;\n        }\n        //eprintln!(\"refresh...\");\n\n        self.sys.refresh_cpu();\n        self.last_sample_time = Instant::now();\n        let now = Utc::now();\n        let data = self.sys.cpus().iter().map(|v| v.cpu_usage() as i32);\n\n        //check if initialized\n        if !self.is_initialized() {\n            eprintln!(\"init...\");\n            let mut processors: Vec<_> = data\n                .map(|percent| CpuUsageChart::new(vec![(now, percent)].into_iter()))\n                .collect();\n            self.processors.append(&mut processors);\n        } else {\n            //eprintln!(\"update...\");\n            for (percent, p) in data.zip(self.processors.iter_mut()) {\n                p.push_data(now, percent);\n            }\n        }\n    }\n\n    fn view(&self) -> Element<Message> {\n        if !self.is_initialized() {\n            Text::new(\"Loading...\")\n                .align_x(Horizontal::Center)\n                .align_y(Vertical::Center)\n                .into()\n        } else {\n            let mut col = Column::new()\n                .width(Length::Fill)\n                .height(Length::Shrink)\n                .align_x(Alignment::Center);\n\n            let chart_height = self.chart_height;\n            let mut idx = 0;\n            for chunk in self.processors.chunks(self.items_per_row) {\n                let mut row = Row::new()\n                    .spacing(15)\n                    .padding(20)\n                    .width(Length::Fill)\n                    .height(Length::Shrink)\n                    .align_y(Alignment::Center);\n                for item in chunk {\n                    row = row.push(item.view(idx, chart_height));\n                    idx += 1;\n                }\n                while idx % self.items_per_row != 0 {\n                    row = row.push(Space::new(Length::Fill, Length::Fixed(50.0)));\n                    idx += 1;\n                }\n                col = col.push(row);\n            }\n\n            Scrollable::new(col).height(Length::Shrink).into()\n        }\n    }\n}\n\nstruct CpuUsageChart {\n    cache: Cache,\n    data_points: VecDeque<(DateTime<Utc>, i32)>,\n    limit: Duration,\n}\n\nimpl CpuUsageChart {\n    fn new(data: impl Iterator<Item = (DateTime<Utc>, i32)>) -> Self {\n        let data_points: VecDeque<_> = data.collect();\n        Self {\n            cache: Cache::new(),\n            data_points,\n            limit: Duration::from_secs(PLOT_SECONDS as u64),\n        }\n    }\n\n    fn push_data(&mut self, time: DateTime<Utc>, value: i32) {\n        let cur_ms = time.timestamp_millis();\n        self.data_points.push_front((time, value));\n        loop {\n            if let Some((time, _)) = self.data_points.back() {\n                let diff = Duration::from_millis((cur_ms - time.timestamp_millis()) as u64);\n                if diff > self.limit {\n                    self.data_points.pop_back();\n                    continue;\n                }\n            }\n            break;\n        }\n        self.cache.clear();\n    }\n\n    fn view(&self, idx: usize, chart_height: f32) -> Element<Message> {\n        Column::new()\n            .width(Length::Fill)\n            .height(Length::Shrink)\n            .spacing(5)\n            .align_x(Alignment::Center)\n            .push(Text::new(format!(\"Processor {}\", idx)))\n            .push(ChartWidget::new(self).height(Length::Fixed(chart_height)))\n            .into()\n    }\n}\n\nimpl Chart<Message> for CpuUsageChart {\n    type State = ();\n    // fn update(\n    //     &mut self,\n    //     event: Event,\n    //     bounds: Rectangle,\n    //     cursor: Cursor,\n    // ) -> (event::Status, Option<Message>) {\n    //     self.cache.clear();\n    //     (event::Status::Ignored, None)\n    // }\n\n    #[inline]\n    fn draw<R: Renderer, F: Fn(&mut Frame)>(\n        &self,\n        renderer: &R,\n        bounds: Size,\n        draw_fn: F,\n    ) -> Geometry {\n        renderer.draw_cache(&self.cache, bounds, draw_fn)\n    }\n\n    fn build_chart<DB: DrawingBackend>(&self, _state: &Self::State, mut chart: ChartBuilder<DB>) {\n        use plotters::prelude::*;\n\n        const PLOT_LINE_COLOR: RGBColor = RGBColor(0, 175, 255);\n\n        // Acquire time range\n        let newest_time = self\n            .data_points\n            .front()\n            .unwrap_or(&(DateTime::from_timestamp(0, 0).unwrap(), 0))\n            .0;\n        let oldest_time = newest_time - chrono::Duration::seconds(PLOT_SECONDS as i64);\n        let mut chart = chart\n            .x_label_area_size(0)\n            .y_label_area_size(28)\n            .margin(20)\n            .build_cartesian_2d(oldest_time..newest_time, 0..100)\n            .expect(\"failed to build chart\");\n\n        chart\n            .configure_mesh()\n            .bold_line_style(plotters::style::colors::BLUE.mix(0.1))\n            .light_line_style(plotters::style::colors::BLUE.mix(0.05))\n            .axis_style(ShapeStyle::from(plotters::style::colors::BLUE.mix(0.45)).stroke_width(1))\n            .y_labels(10)\n            .y_label_style(\n                (\"sans-serif\", 15)\n                    .into_font()\n                    .color(&plotters::style::colors::BLUE.mix(0.65))\n                    .transform(FontTransform::Rotate90),\n            )\n            .y_label_formatter(&|y: &i32| format!(\"{}%\", y))\n            .draw()\n            .expect(\"failed to draw chart mesh\");\n\n        chart\n            .draw_series(\n                AreaSeries::new(\n                    self.data_points.iter().map(|x| (x.0, x.1)),\n                    0,\n                    PLOT_LINE_COLOR.mix(0.175),\n                )\n                .border_style(ShapeStyle::from(PLOT_LINE_COLOR).stroke_width(2)),\n            )\n            .expect(\"failed to draw chart data\");\n    }\n}\n"
  },
  {
    "path": "examples/large-data.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n\nextern crate iced;\nextern crate plotters;\nextern crate rand;\nextern crate tokio;\n\nuse chrono::{DateTime, Utc};\nuse iced::{\n    font,\n    widget::{\n        canvas::{Cache, Frame, Geometry},\n        Column, Container, Text,\n    },\n    Alignment, Element, Font, Length, Size, Task,\n};\nuse plotters::prelude::ChartBuilder;\nuse plotters_backend::DrawingBackend;\nuse plotters_iced::{\n    sample::lttb::{DataPoint, LttbSource},\n    Chart, ChartWidget, Renderer,\n};\nuse rand::Rng;\nuse std::time::Duration;\nuse std::{collections::VecDeque, time::Instant};\n\nconst TITLE_FONT_SIZE: u16 = 22;\n\nconst FONT_BOLD: Font = Font {\n    family: font::Family::Name(\"Noto Sans\"),\n    weight: font::Weight::Bold,\n    ..Font::DEFAULT\n};\n\nfn main() {\n    iced::application(\"Large Data Example\", State::update, State::view)\n        .antialiasing(true)\n        .default_font(Font::with_name(\"Noto Sans\"))\n        .run_with(State::new)\n        .unwrap();\n}\n\nstruct Wrapper<'a>(&'a DateTime<Utc>, &'a f32);\n\nimpl DataPoint for Wrapper<'_> {\n    #[inline]\n    fn x(&self) -> f64 {\n        self.0.timestamp() as f64\n    }\n    #[inline]\n    fn y(&self) -> f64 {\n        *self.1 as f64\n    }\n}\n\n#[derive(Debug)]\nenum Message {\n    FontLoaded(Result<(), font::Error>),\n    DataLoaded(Vec<(DateTime<Utc>, f32)>),\n    Sampled(Vec<(DateTime<Utc>, f32)>),\n}\n\nstruct State {\n    chart: Option<ExampleChart>,\n}\n\nimpl State {\n    fn new() -> (Self, Task<Message>) {\n        (\n            Self { chart: None },\n            Task::batch([\n                font::load(include_bytes!(\"./fonts/notosans-regular.ttf\").as_slice())\n                    .map(Message::FontLoaded),\n                font::load(include_bytes!(\"./fonts/notosans-bold.ttf\").as_slice())\n                    .map(Message::FontLoaded),\n                Task::perform(tokio::task::spawn_blocking(generate_data), |data| {\n                    Message::DataLoaded(data.unwrap())\n                }),\n            ]),\n        )\n    }\n\n    fn update(&mut self, message: Message) -> Task<Message> {\n        match message {\n            Message::DataLoaded(data) => Task::perform(\n                tokio::task::spawn_blocking(move || {\n                    let now = Instant::now();\n                    let sampled: Vec<_> = (&data[..])\n                        .cast(|v| Wrapper(&v.0, &v.1))\n                        .lttb(1000)\n                        .map(|w| (*w.0, *w.1))\n                        .collect();\n                    dbg!(now.elapsed().as_millis());\n                    sampled\n                }),\n                |data| Message::Sampled(data.unwrap()),\n            ),\n            Message::Sampled(sampled) => {\n                self.chart = Some(ExampleChart::new(sampled.into_iter()));\n                Task::none()\n            }\n            _ => Task::none(),\n        }\n    }\n\n    fn view(&self) -> Element<'_, Message> {\n        let content = Column::new()\n            .spacing(20)\n            .align_x(Alignment::Start)\n            .width(Length::Fill)\n            .height(Length::Fill)\n            .push(\n                Text::new(\"Iced test chart\")\n                    .size(TITLE_FONT_SIZE)\n                    .font(FONT_BOLD),\n            )\n            .push(match self.chart {\n                Some(ref chart) => chart.view(),\n                None => Text::new(\"Loading...\").into(),\n            });\n\n        Container::new(content)\n            .padding(5)\n            .center_x(Length::Fill)\n            .center_y(Length::Fill)\n            .into()\n    }\n}\n\nstruct ExampleChart {\n    cache: Cache,\n    data_points: VecDeque<(DateTime<Utc>, f32)>,\n}\n\nimpl ExampleChart {\n    fn new(data: impl Iterator<Item = (DateTime<Utc>, f32)>) -> Self {\n        let data_points: VecDeque<_> = data.collect();\n        Self {\n            cache: Cache::new(),\n            data_points,\n        }\n    }\n\n    fn view(&self) -> Element<Message> {\n        let chart = ChartWidget::new(self)\n            .width(Length::Fill)\n            .height(Length::Fill);\n\n        chart.into()\n    }\n}\n\nimpl Chart<Message> for ExampleChart {\n    type State = ();\n    // fn update(\n    //     &mut self,\n    //     event: Event,\n    //     bounds: Rectangle,\n    //     cursor: Cursor,\n    // ) -> (event::Status, Option<Message>) {\n    //     self.cache.clear();\n    //     (event::Status::Ignored, None)\n    // }\n\n    #[inline]\n    fn draw<R: Renderer, F: Fn(&mut Frame)>(\n        &self,\n        renderer: &R,\n        bounds: Size,\n        draw_fn: F,\n    ) -> Geometry {\n        renderer.draw_cache(&self.cache, bounds, draw_fn)\n    }\n\n    fn build_chart<DB: DrawingBackend>(&self, _state: &Self::State, mut chart: ChartBuilder<DB>) {\n        use plotters::prelude::*;\n\n        const PLOT_LINE_COLOR: RGBColor = RGBColor(0, 175, 255);\n\n        // Acquire time range\n        let newest_time = self\n            .data_points\n            .back()\n            .unwrap()\n            .0\n            .checked_add_signed(chrono::Duration::from_std(Duration::from_secs(10)).unwrap())\n            .unwrap();\n        //let oldest_time = newest_time - chrono::Duration::seconds(PLOT_SECONDS as i64);\n        let oldest_time = self\n            .data_points\n            .front()\n            .unwrap()\n            .0\n            .checked_sub_signed(chrono::Duration::from_std(Duration::from_secs(10)).unwrap())\n            .unwrap();\n        //dbg!(&newest_time);\n        //dbg!(&oldest_time);\n        let mut chart = chart\n            .x_label_area_size(0)\n            .y_label_area_size(28)\n            .margin(20)\n            .build_cartesian_2d(oldest_time..newest_time, -10.0_f32..110.0_f32)\n            .expect(\"failed to build chart\");\n\n        chart\n            .configure_mesh()\n            .bold_line_style(plotters::style::colors::BLUE.mix(0.1))\n            .light_line_style(plotters::style::colors::BLUE.mix(0.05))\n            .axis_style(ShapeStyle::from(plotters::style::colors::BLUE.mix(0.45)).stroke_width(1))\n            .y_labels(10)\n            .y_label_style(\n                (\"Noto Sans\", 15)\n                    .into_font()\n                    .color(&plotters::style::colors::BLUE.mix(0.65))\n                    .transform(FontTransform::Rotate90),\n            )\n            .y_label_formatter(&|y| format!(\"{}\", y))\n            .draw()\n            .expect(\"failed to draw chart mesh\");\n\n        chart\n            .draw_series(\n                AreaSeries::new(\n                    self.data_points.iter().cloned(),\n                    0_f32,\n                    PLOT_LINE_COLOR.mix(0.175),\n                )\n                .border_style(ShapeStyle::from(PLOT_LINE_COLOR).stroke_width(2)),\n            )\n            .expect(\"failed to draw chart data\");\n    }\n}\n\nfn generate_data() -> Vec<(DateTime<Utc>, f32)> {\n    let total = 10_000_000;\n    let mut data = Vec::new();\n    let mut rng = rand::thread_rng();\n    let time_range = (24 * 3600 * 30) as f32;\n    let interval = (3600 * 12) as f32;\n    let start = Utc::now()\n        .checked_sub_signed(\n            chrono::Duration::from_std(Duration::from_secs_f32(time_range)).unwrap(),\n        )\n        .unwrap();\n    while data.len() < total {\n        let secs = rng.gen_range(0.1..time_range);\n        let time = start\n            .checked_sub_signed(chrono::Duration::from_std(Duration::from_secs_f32(secs)).unwrap())\n            .unwrap();\n\n        let value =\n            (((secs % interval) - interval / 2.0) / (interval / 2.0) * std::f32::consts::PI).sin()\n                * 50_f32\n                + 50_f32;\n        data.push((time, value));\n    }\n    data.sort_by_cached_key(|x| x.0);\n    //dbg!(&data[..100]);\n    data\n}\n"
  },
  {
    "path": "examples/mouse_events.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Grey <grey@openrobotics.org>\n// License: MIT\n\nuse iced::{\n    event,\n    mouse::Cursor,\n    widget::{\n        canvas::{self, Cache, Frame, Geometry},\n        Column, Container, Text,\n    },\n    Alignment, Element, Length, Point, Size,\n};\nuse plotters::{\n    coord::{types::RangedCoordf32, ReverseCoordTranslate},\n    prelude::*,\n};\nuse plotters_iced::{Chart, ChartWidget, Renderer};\nuse std::cell::RefCell;\n\n#[derive(Default)]\nstruct State {\n    chart: ArtChart,\n}\n\nimpl State {\n    fn update(&mut self, message: Message) {\n        match message {\n            Message::MouseEvent(event, point) => {\n                self.chart.set_current_position(point);\n                match event {\n                    iced::mouse::Event::ButtonPressed(iced::mouse::Button::Left) => {\n                        self.chart.set_down(true);\n                    }\n                    iced::mouse::Event::ButtonReleased(iced::mouse::Button::Left) => {\n                        self.chart.set_down(false);\n                    }\n                    _ => {\n                        // Do nothing\n                    }\n                }\n            }\n        }\n    }\n\n    fn view(&self) -> Element<Message> {\n        let content = Column::new()\n            .spacing(20)\n            .width(Length::Fill)\n            .height(Length::Fill)\n            .push(Text::new(\"Click below!\").size(20))\n            .push(self.chart.view())\n            .align_x(Alignment::Center)\n            .padding(15);\n\n        Container::new(content)\n            .padding(5)\n            .center_x(Length::Fill)\n            .center_y(Length::Fill)\n            .into()\n    }\n}\n\n#[derive(Default)]\nstruct ArtChart {\n    cache: Cache,\n    points: Vec<(f32, f32)>,\n    lines: Vec<((f32, f32), (f32, f32))>,\n    is_down: bool,\n    current_position: Option<(f32, f32)>,\n    initial_down_position: Option<(f32, f32)>,\n    spec: RefCell<Option<Cartesian2d<RangedCoordf32, RangedCoordf32>>>,\n}\n\nimpl ArtChart {\n    fn view(&self) -> Element<Message> {\n        let chart = ChartWidget::new(self)\n            .width(Length::Fill)\n            .height(Length::Fill);\n\n        chart.into()\n    }\n\n    fn set_current_position(&mut self, p: Point) {\n        if let Some(spec) = self.spec.borrow().as_ref() {\n            self.current_position = spec.reverse_translate((p.x as i32, p.y as i32));\n            self.cache.clear();\n        }\n    }\n\n    fn nearby(p0: (f32, f32), p1: (f32, f32)) -> bool {\n        let delta = (p1.0 - p0.0, p1.1 - p0.1);\n        (delta.0 * delta.0 + delta.1 * delta.1).sqrt() <= 1.0\n    }\n\n    fn set_down(&mut self, new_is_down: bool) {\n        if !self.is_down && new_is_down {\n            self.initial_down_position = self.current_position;\n        }\n\n        if self.is_down && !new_is_down {\n            if let Some((initial_p, current_p)) =\n                self.initial_down_position.zip(self.current_position)\n            {\n                if Self::nearby(initial_p, current_p) {\n                    self.points.push(current_p);\n                } else {\n                    self.lines.push((initial_p, current_p));\n                }\n            }\n        }\n\n        self.is_down = new_is_down;\n    }\n}\n\nimpl Chart<Message> for ArtChart {\n    type State = ();\n    fn draw<R: Renderer, F: Fn(&mut Frame)>(\n        &self,\n        renderer: &R,\n        bounds: Size,\n        draw_fn: F,\n    ) -> Geometry {\n        renderer.draw_cache(&self.cache, bounds, draw_fn)\n    }\n\n    fn build_chart<DB: DrawingBackend>(&self, _state: &Self::State, mut builder: ChartBuilder<DB>) {\n        use plotters::style::colors;\n\n        const POINT_COLOR: RGBColor = colors::RED;\n        const LINE_COLOR: RGBColor = colors::BLUE;\n        const HOVER_COLOR: RGBColor = colors::YELLOW;\n        const PREVIEW_COLOR: RGBColor = colors::GREEN;\n\n        let mut chart = builder\n            .x_label_area_size(28_i32)\n            .y_label_area_size(28_i32)\n            .margin(20_i32)\n            .build_cartesian_2d(0_f32..100_f32, 0_f32..100_f32)\n            .expect(\"Failed to build chart\");\n\n        chart\n            .configure_mesh()\n            .bold_line_style(colors::BLACK.mix(0.1))\n            .light_line_style(colors::BLACK.mix(0.05))\n            .axis_style(ShapeStyle::from(colors::BLACK.mix(0.45)).stroke_width(1))\n            .y_labels(10)\n            .y_label_style(\n                (\"sans-serif\", 15)\n                    .into_font()\n                    .color(&colors::BLACK.mix(0.65))\n                    .transform(FontTransform::Rotate90),\n            )\n            .y_label_formatter(&|y| format!(\"{}\", y))\n            .draw()\n            .expect(\"Failed to draw chart mesh\");\n\n        chart\n            .draw_series(\n                self.points\n                    .iter()\n                    .map(|p| Circle::new(*p, 5_i32, POINT_COLOR.filled())),\n            )\n            .expect(\"Failed to draw points\");\n\n        for line in &self.lines {\n            chart\n                .draw_series(LineSeries::new(\n                    vec![line.0, line.1].into_iter(),\n                    LINE_COLOR.filled(),\n                ))\n                .expect(\"Failed to draw line\");\n        }\n\n        if self.is_down {\n            if let Some((initial_p, current_p)) =\n                self.initial_down_position.zip(self.current_position)\n            {\n                if Self::nearby(initial_p, current_p) {\n                    chart\n                        .draw_series(std::iter::once(Circle::new(\n                            current_p,\n                            5_i32,\n                            PREVIEW_COLOR.filled(),\n                        )))\n                        .expect(\"Failed to draw preview point\");\n                } else {\n                    chart\n                        .draw_series(LineSeries::new(\n                            vec![initial_p, current_p].into_iter(),\n                            PREVIEW_COLOR.filled(),\n                        ))\n                        .expect(\"Failed to draw preview line\");\n                }\n            }\n        } else if let Some(current_p) = self.current_position {\n            chart\n                .draw_series(std::iter::once(Circle::new(\n                    current_p,\n                    5_i32,\n                    HOVER_COLOR.filled(),\n                )))\n                .expect(\"Failed to draw hover point\");\n        }\n\n        *self.spec.borrow_mut() = Some(chart.as_coord_spec().clone());\n    }\n\n    fn update(\n        &self,\n        _state: &mut Self::State,\n        event: canvas::Event,\n        bounds: iced::Rectangle,\n        cursor: Cursor,\n    ) -> (event::Status, Option<Message>) {\n        if let Cursor::Available(point) = cursor {\n            match event {\n                canvas::Event::Mouse(evt) if bounds.contains(point) => {\n                    let p_origin = bounds.position();\n                    let p = point - p_origin;\n                    return (\n                        event::Status::Captured,\n                        Some(Message::MouseEvent(evt, Point::new(p.x, p.y))),\n                    );\n                }\n                _ => {}\n            }\n        }\n        (event::Status::Ignored, None)\n    }\n}\n\n#[derive(Debug)]\nenum Message {\n    MouseEvent(iced::mouse::Event, iced::Point),\n}\n\nfn main() -> iced::Result {\n    iced::application(\"Art\", State::update, State::view)\n        .antialiasing(true)\n        .run()\n}\n"
  },
  {
    "path": "examples/split-chart/Cargo.toml",
    "content": "[package]\nname = \"split-chart\"\nversion = \"0.1.0\"\nauthors = [\"Joylei <leingliu@gmail.com>\"]\nedition = \"2021\"\npublish = false\n\n[dependencies]\niced = { version = \"0.13\", features = [\"canvas\"] }\nplotters-iced = { path = \"../../\" }\nplotters = { version = \"0.3\", default-features = false, features = [\n    \"chrono\",\n    \"area_series\",\n    \"line_series\",\n    \"point_series\",\n] }\n\n[target.'cfg(target_arch = \"wasm32\")'.dependencies]\niced.version = \"0.13\"\niced.features = [\"canvas\", \"debug\", \"webgl\"]\n\nconsole_error_panic_hook = \"0.1\"\nconsole_log = \"1.0\"\n"
  },
  {
    "path": "examples/split-chart/index.html",
    "content": "<!DOCTYPE html>\n<html>\n\n<head>\n    <meta content=\"text/html; charset=utf-8\" charset=\"utf-8\" http-equiv=\"Content-Type\">\n    <meta name=\"description\" content=\"\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n    <title>split chart example</title>\n    <base data-trunk-public-url />\n</head>\n\n<body>\n    <link data-trunk rel=\"rust\" href=\"Cargo.toml\" data-wasm-opt=\"z\" data-bin=\"split-chart\" />\n    <!-- see split-chart.rs for details -->\n</body>\n\n</html>"
  },
  {
    "path": "examples/split-chart/src/main.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n\n/*!\n- run the native version\n\n```sh\ncargo run --release --example split-chart\n```\n\n- run the web version with [trunk](https://trunkrs.dev/)\n\n```sh\ncd examples\ntrunk serve\n```\n\n*/\n\nextern crate iced;\nextern crate plotters;\n\nuse iced::{\n    widget::{Column, Container, Text},\n    window, Alignment, Element, Length,\n};\nuse plotters::{coord::Shift, prelude::*};\nuse plotters_backend::DrawingBackend;\nuse plotters_iced::{plotters_backend, Chart, ChartWidget, DrawingArea};\n\nconst TITLE_FONT_SIZE: u16 = 22;\n\n// antialiasing issue: https://github.com/iced-rs/iced/issues/1159\n\nfn main() {\n    #[cfg(target_arch = \"wasm32\")]\n    {\n        console_log::init().expect(\"Initialize logger\");\n        std::panic::set_hook(Box::new(console_error_panic_hook::hook));\n    }\n    let app = iced::application(\"Split Chart Example\", State::update, State::view)\n        .antialiasing(cfg!(not(target_arch = \"wasm32\")))\n        .subscription(|_| window::frames().map(|_| Message::Tick));\n    app.run().unwrap();\n}\n\n#[allow(unused)]\n#[derive(Debug)]\nenum Message {\n    Tick,\n}\n\n#[derive(Default)]\nstruct State {\n    chart: MyChart,\n}\n\nimpl State {\n    fn update(&mut self, _message: Message) {}\n\n    fn view(&self) -> Element<'_, Message> {\n        let content = Column::new()\n            .spacing(20)\n            .align_x(Alignment::Start)\n            .width(Length::Fill)\n            .height(Length::Fill)\n            .push(Text::new(\"Iced test chart\").size(TITLE_FONT_SIZE))\n            .push(self.chart.view());\n\n        Container::new(content)\n            .padding(5)\n            .center_x(Length::Fill)\n            .center_y(Length::Fill)\n            .into()\n    }\n}\n\n#[allow(unused)]\n#[derive(Default)]\nstruct MyChart;\n\nimpl MyChart {\n    fn view(&self) -> Element<Message> {\n        let chart = ChartWidget::new(self)\n            .width(Length::Fill)\n            .height(Length::Fill);\n\n        chart.into()\n    }\n}\n\nimpl Chart<Message> for MyChart {\n    type State = ();\n    // leave it empty\n    fn build_chart<DB: DrawingBackend>(&self, _state: &Self::State, _builder: ChartBuilder<DB>) {}\n\n    fn draw_chart<DB: DrawingBackend>(&self, _state: &Self::State, root: DrawingArea<DB, Shift>) {\n        let children = root.split_evenly((2, 2));\n        for (i, area) in children.iter().enumerate() {\n            let builder = ChartBuilder::on(area);\n            draw_chart(builder, i + 1);\n        }\n    }\n}\n\nfn draw_chart<DB: DrawingBackend>(mut chart: ChartBuilder<DB>, power: usize) {\n    let mut chart = chart\n        .margin(30)\n        .caption(format!(\"y=x^{}\", power), (\"sans-serif\", 22))\n        .x_label_area_size(30)\n        .y_label_area_size(30)\n        .build_cartesian_2d(-1f32..1f32, -1.2f32..1.2f32)\n        .unwrap();\n\n    chart\n        .configure_mesh()\n        .x_labels(3)\n        .y_labels(3)\n        // .y_label_style(\n        //     (\"sans-serif\", 15)\n        //         .into_font()\n        //         .color(&plotters::style::colors::BLACK.mix(0.8))\n        //         .transform(FontTransform::RotateAngle(30.0)),\n        // )\n        .draw()\n        .unwrap();\n\n    chart\n        .draw_series(LineSeries::new(\n            (-50..=50)\n                .map(|x| x as f32 / 50.0)\n                .map(|x| (x, x.powf(power as f32))),\n            &RED,\n        ))\n        .unwrap();\n}\n"
  },
  {
    "path": "src/backend/mod.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n\nuse std::collections::HashSet;\n\nuse iced_graphics::core::text::Paragraph;\nuse iced_widget::{\n    canvas,\n    core::{\n        alignment::{Horizontal, Vertical},\n        font, text, Font, Size,\n    },\n    text::Shaping,\n};\nuse once_cell::unsync::Lazy;\nuse plotters_backend::{\n    text_anchor,\n    //FontTransform,\n    BackendColor,\n    BackendCoord,\n    BackendStyle,\n    BackendTextStyle,\n    DrawingBackend,\n    DrawingErrorKind,\n    FontFamily,\n    FontStyle,\n};\n\nuse crate::error::Error;\nuse crate::utils::{cvt_color, cvt_stroke, CvtPoint};\n\n/// The Iced drawing backend\npub(crate) struct IcedChartBackend<'a, B> {\n    frame: &'a mut canvas::Frame,\n    backend: &'a B,\n    shaping: Shaping,\n}\n\nimpl<'a, B> IcedChartBackend<'a, B>\nwhere\n    B: text::Renderer<Font = Font>,\n{\n    pub fn new(frame: &'a mut canvas::Frame, backend: &'a B, shaping: Shaping) -> Self {\n        Self {\n            frame,\n            backend,\n            shaping,\n        }\n    }\n}\n\nimpl<'a, B> DrawingBackend for IcedChartBackend<'a, B>\nwhere\n    B: text::Renderer<Font = Font>,\n{\n    type ErrorType = Error;\n\n    fn get_size(&self) -> (u32, u32) {\n        let Size { width, height } = self.frame.size();\n        (width as u32, height as u32)\n    }\n\n    fn ensure_prepared(&mut self) -> Result<(), DrawingErrorKind<Error>> {\n        Ok(())\n    }\n\n    fn present(&mut self) -> Result<(), DrawingErrorKind<Error>> {\n        Ok(())\n    }\n\n    #[inline]\n    fn draw_pixel(\n        &mut self,\n        point: BackendCoord,\n        color: BackendColor,\n    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {\n        if color.alpha == 0.0 {\n            return Ok(());\n        }\n        self.frame\n            .fill_rectangle(point.cvt_point(), Size::new(1.0, 1.0), cvt_color(&color));\n        Ok(())\n    }\n\n    #[inline]\n    fn draw_line<S: BackendStyle>(\n        &mut self,\n        from: BackendCoord,\n        to: BackendCoord,\n        style: &S,\n    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {\n        if style.color().alpha == 0.0 {\n            return Ok(());\n        }\n        let line = canvas::Path::line(from.cvt_point(), to.cvt_point());\n        self.frame.stroke(&line, cvt_stroke(style));\n        Ok(())\n    }\n\n    #[inline]\n    fn draw_rect<S: BackendStyle>(\n        &mut self,\n        upper_left: BackendCoord,\n        bottom_right: BackendCoord,\n        style: &S,\n        fill: bool,\n    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {\n        if style.color().alpha == 0.0 {\n            return Ok(());\n        }\n        let height = (bottom_right.1 - upper_left.1) as f32;\n        let width = (bottom_right.0 - upper_left.0) as f32;\n        let upper_left = upper_left.cvt_point();\n        if fill {\n            self.frame.fill_rectangle(\n                upper_left,\n                Size::new(width, height),\n                cvt_color(&style.color()),\n            );\n        } else {\n            let rect = canvas::Path::rectangle(upper_left, Size::new(width, height));\n            self.frame.stroke(&rect, cvt_stroke(style));\n        }\n\n        Ok(())\n    }\n\n    #[inline]\n    fn draw_path<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(\n        &mut self,\n        path: I,\n        style: &S,\n    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {\n        if style.color().alpha == 0.0 {\n            return Ok(());\n        }\n        let path = canvas::Path::new(move |builder| {\n            for (i, point) in path.into_iter().enumerate() {\n                if i > 0 {\n                    builder.line_to(point.cvt_point());\n                } else {\n                    builder.move_to(point.cvt_point());\n                }\n            }\n        });\n\n        self.frame.stroke(&path, cvt_stroke(style));\n        Ok(())\n    }\n\n    #[inline]\n    fn draw_circle<S: BackendStyle>(\n        &mut self,\n        center: BackendCoord,\n        radius: u32,\n        style: &S,\n        fill: bool,\n    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {\n        if style.color().alpha == 0.0 {\n            return Ok(());\n        }\n\n        let circle = canvas::Path::circle(center.cvt_point(), radius as f32);\n\n        if fill {\n            self.frame.fill(&circle, cvt_color(&style.color()));\n        } else {\n            self.frame.stroke(&circle, cvt_stroke(style));\n        }\n\n        Ok(())\n    }\n\n    #[inline]\n    fn fill_polygon<S: BackendStyle, I: IntoIterator<Item = BackendCoord>>(\n        &mut self,\n        vert: I,\n        style: &S,\n    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {\n        if style.color().alpha == 0.0 {\n            return Ok(());\n        }\n        let path = canvas::Path::new(move |builder| {\n            for (i, point) in vert.into_iter().enumerate() {\n                if i > 0 {\n                    builder.line_to(point.cvt_point());\n                } else {\n                    builder.move_to(point.cvt_point());\n                }\n            }\n            builder.close();\n        });\n        self.frame.fill(&path, cvt_color(&style.color()));\n        Ok(())\n    }\n\n    #[inline]\n    fn draw_text<S: BackendTextStyle>(\n        &mut self,\n        text: &str,\n        style: &S,\n        pos: BackendCoord,\n    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {\n        if style.color().alpha == 0.0 {\n            return Ok(());\n        }\n        let horizontal_alignment = match style.anchor().h_pos {\n            text_anchor::HPos::Left => Horizontal::Left,\n            text_anchor::HPos::Right => Horizontal::Right,\n            text_anchor::HPos::Center => Horizontal::Center,\n        };\n        let vertical_alignment = match style.anchor().v_pos {\n            text_anchor::VPos::Top => Vertical::Top,\n            text_anchor::VPos::Center => Vertical::Center,\n            text_anchor::VPos::Bottom => Vertical::Bottom,\n        };\n        let font = style_to_font(style);\n        let pos = pos.cvt_point();\n\n        //let (w, h) = self.estimate_text_size(text, style)?;\n        let text = canvas::Text {\n            content: text.to_owned(),\n            position: pos,\n            color: cvt_color(&style.color()),\n            size: (style.size() as f32).into(),\n            line_height: Default::default(),\n            font,\n            horizontal_alignment,\n            vertical_alignment,\n            shaping: self.shaping,\n        };\n        //TODO: fix rotation until text rotation is supported by Iced\n        // let rotate = match style.transform() {\n        //     FontTransform::None => None,\n        //     FontTransform::Rotate90 => Some(90.0),\n        //     FontTransform::Rotate180 => Some(180.0),\n        //     FontTransform::Rotate270 => Some(270.0),\n        //     FontTransform::RotateAngle(angle) => Some(angle),\n        // };\n        // if let Some(rotate) = rotate {\n        //     dbg!(rotate);\n        //     self.frame.with_save(move |frame| {\n        //         frame.fill_text(text);\n        //         frame.translate(Vector::new(pos.x + w as f32 / 2.0, pos.y + h as f32 / 2.0));\n        //         let angle = 2.0 * std::f32::consts::PI * rotate / 360.0;\n        //         frame.rotate(angle);\n        //     });\n        // } else {\n        //     self.frame.fill_text(text);\n        // }\n        self.frame.fill_text(text);\n\n        Ok(())\n    }\n\n    #[inline]\n    fn estimate_text_size<S: BackendTextStyle>(\n        &self,\n        text: &str,\n        style: &S,\n    ) -> Result<(u32, u32), DrawingErrorKind<Self::ErrorType>> {\n        let font = style_to_font(style);\n        let bounds = self.frame.size();\n        let horizontal_alignment = match style.anchor().h_pos {\n            text_anchor::HPos::Left => Horizontal::Left,\n            text_anchor::HPos::Right => Horizontal::Right,\n            text_anchor::HPos::Center => Horizontal::Center,\n        };\n        let vertical_alignment = match style.anchor().v_pos {\n            text_anchor::VPos::Top => Vertical::Top,\n            text_anchor::VPos::Center => Vertical::Center,\n            text_anchor::VPos::Bottom => Vertical::Bottom,\n        };\n\n        let p = B::Paragraph::with_text(iced_widget::core::text::Text {\n            content: text,\n            bounds,\n            size: self.backend.default_size(),\n            line_height: Default::default(),\n            font,\n            horizontal_alignment,\n            vertical_alignment,\n            shaping: self.shaping,\n            wrapping: iced_widget::core::text::Wrapping::Word,\n        });\n        let size = p.min_bounds();\n        Ok((size.width as u32, size.height as u32))\n    }\n\n    #[inline]\n    fn blit_bitmap(\n        &mut self,\n        _pos: BackendCoord,\n        (_iw, _ih): (u32, u32),\n        _src: &[u8],\n    ) -> Result<(), DrawingErrorKind<Self::ErrorType>> {\n        // Not supported yet (rendering ignored)\n        // Notice: currently Iced has limitations, because widgets are not rendered in the order of creation, and different primitives go to different render pipelines.\n\n        Ok(())\n    }\n}\n\nfn style_to_font<S: BackendTextStyle>(style: &S) -> Font {\n    // iced font family requires static str\n    static mut FONTS: Lazy<HashSet<String>> = Lazy::new(HashSet::new);\n\n    Font {\n        family: match style.family() {\n            FontFamily::Serif => font::Family::Serif,\n            FontFamily::SansSerif => font::Family::SansSerif,\n            FontFamily::Monospace => font::Family::Monospace,\n            FontFamily::Name(s) => {\n                let s = unsafe {\n                    if !FONTS.contains(s) {\n                        FONTS.insert(String::from(s));\n                    }\n                    FONTS.get(s).unwrap().as_str()\n                };\n                font::Family::Name(s)\n            }\n        },\n        weight: match style.style() {\n            FontStyle::Bold => font::Weight::Bold,\n            _ => font::Weight::Normal,\n        },\n        ..Font::DEFAULT\n    }\n}\n"
  },
  {
    "path": "src/chart.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n\nuse iced_widget::canvas::Cache;\nuse iced_widget::core::event::Status;\nuse iced_widget::core::mouse::Interaction;\nuse iced_widget::core::Rectangle;\nuse iced_widget::{\n    canvas::{Event, Frame, Geometry},\n    core::{mouse::Cursor, Size},\n};\nuse plotters::{chart::ChartBuilder, coord::Shift, drawing::DrawingArea};\nuse plotters_backend::DrawingBackend;\n\n/// graphics renderer\npub trait Renderer {\n    /// draw frame\n    fn draw<F: Fn(&mut Frame)>(&self, bounds: Size, draw_fn: F) -> Geometry;\n\n    /// draw frame with cache\n    fn draw_cache<F: Fn(&mut Frame)>(&self, cache: &Cache, bounds: Size, draw_fn: F) -> Geometry;\n}\n\nimpl<Message, C: ?Sized> Chart<Message> for &C\nwhere\n    C: Chart<Message>,\n{\n    type State = C::State;\n    #[inline]\n    fn build_chart<DB: DrawingBackend>(&self, state: &Self::State, builder: ChartBuilder<DB>) {\n        C::build_chart(self, state, builder);\n    }\n    #[inline]\n    fn draw_chart<DB: DrawingBackend>(&self, state: &Self::State, root: DrawingArea<DB, Shift>) {\n        C::draw_chart(self, state, root);\n    }\n    #[inline]\n    fn draw<R: Renderer, F: Fn(&mut Frame)>(&self, renderer: &R, size: Size, f: F) -> Geometry {\n        C::draw(self, renderer, size, f)\n    }\n    #[inline]\n    fn update(\n        &self,\n        state: &mut Self::State,\n        event: Event,\n        bounds: Rectangle,\n        cursor: Cursor,\n    ) -> (Status, Option<Message>) {\n        C::update(self, state, event, bounds, cursor)\n    }\n    #[inline]\n    fn mouse_interaction(\n        &self,\n        state: &Self::State,\n        bounds: Rectangle,\n        cursor: Cursor,\n    ) -> Interaction {\n        C::mouse_interaction(self, state, bounds, cursor)\n    }\n}\n\n/// Chart View Model\n///\n/// ## Example\n/// ```rust,ignore\n/// use plotters::prelude::*;\n/// use plotters_iced::{Chart,ChartWidget};\n/// struct MyChart;\n/// impl Chart<Message> for MyChart {\n///     type State = ();\n///     fn build_chart<DB:DrawingBackend>(&self, state: &Self::State, builder: ChartBuilder<DB>) {\n///         //build your chart here, please refer to plotters for more details\n///     }\n/// }\n///\n/// impl MyChart {\n///     fn view(&mut self)->Element<Message> {\n///         ChartWidget::new(self)\n///             .width(Length::Fixed(200))\n///             .height(Length::Fixed(200))\n///             .into()\n///     }\n/// }\n/// ```\npub trait Chart<Message> {\n    /// state data of chart\n    type State: Default + 'static;\n\n    /// draw chart with [`ChartBuilder`]\n    ///\n    /// for simple chart, you impl this method\n    fn build_chart<DB: DrawingBackend>(&self, state: &Self::State, builder: ChartBuilder<DB>);\n\n    /// override this method if you want more freedom of drawing area\n    ///\n    /// ## Example\n    /// ```rust,ignore\n    /// use plotters::prelude::*;\n    /// use plotters_iced::{Chart,ChartWidget};\n    ///\n    /// struct MyChart{}\n    ///\n    /// impl Chart<Message> for MyChart {\n    ///     // leave it empty\n    ///     fn build_chart<DB: DrawingBackend>(&self, state: &Self::State, builder: ChartBuilder<DB>){}\n    ///     fn draw_chart<DB: DrawingBackend>(&self, state: &Self::State, root: DrawingArea<DB, Shift>){\n    ///          let children = root.split_evenly((3,3));\n    ///          for (area, color) in children.into_iter().zip(0..) {\n    ///                area.fill(&Palette99::pick(color)).unwrap();\n    ///          }\n    ///      }\n    /// }\n    /// ```\n    #[inline]\n    fn draw_chart<DB: DrawingBackend>(&self, state: &Self::State, root: DrawingArea<DB, Shift>) {\n        let builder = ChartBuilder::on(&root);\n        self.build_chart(state, builder);\n    }\n\n    /// draw on [`iced_widget::canvas::Canvas`]\n    ///\n    /// override this method if you want to use [`iced_widget::canvas::Cache`]\n    ///\n    /// ## Example\n    /// ```rust,ignore\n    ///\n    /// impl Chart<Message> for CpuUsageChart {\n    ///\n    ///       #[inline]\n    ///       fn draw<R: Renderer, F: Fn(&mut Frame)>(&self, renderer: &R, bounds: Size, draw_fn: F) -> Geometry {\n    ///            R::draw_cache(renderer, &self.cache, size, draw_fn)\n    ///       }\n    ///      //...\n    /// }\n    /// ```\n    #[inline]\n    fn draw<R: Renderer, F: Fn(&mut Frame)>(&self, renderer: &R, size: Size, f: F) -> Geometry {\n        R::draw(renderer, size, f)\n    }\n\n    /// react on event\n    #[allow(unused_variables)]\n    #[inline]\n    #[allow(unused)]\n    fn update(\n        &self,\n        state: &mut Self::State,\n        event: Event,\n        bounds: Rectangle,\n        cursor: Cursor,\n    ) -> (Status, Option<Message>) {\n        (Status::Ignored, None)\n    }\n\n    /// Returns the current mouse interaction of the [`Chart`]\n    #[inline]\n    #[allow(unused)]\n    fn mouse_interaction(\n        &self,\n        state: &Self::State,\n        bounds: Rectangle,\n        cursor: Cursor,\n    ) -> Interaction {\n        Interaction::Idle\n    }\n}\n"
  },
  {
    "path": "src/error.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n\nuse std::error::Error as StdError;\nuse std::fmt;\n\n#[derive(Debug)]\n/// Indicates that some error occurred within the Iced backend\npub enum Error {}\n\nimpl fmt::Display for Error {\n    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {\n        write!(fmt, \"{self:?}\")\n    }\n}\n\nimpl StdError for Error {}\n"
  },
  {
    "path": "src/lib.rs",
    "content": "#![doc = include_str!(\"../README.md\")]\n#![warn(missing_docs)]\n\npub extern crate plotters_backend;\n\n#[doc(no_inline)]\npub use plotters::{chart::ChartBuilder, drawing::DrawingArea};\n#[doc(no_inline)]\npub use plotters_backend::DrawingBackend;\n\n#[doc(inline)]\npub use chart::Chart;\n#[doc(inline)]\npub use chart::Renderer;\n#[doc(inline)]\npub use error::Error;\npub use widget::ChartWidget;\n\nmod backend;\nmod chart;\nmod error;\nmod renderer;\n/// data point sampling\npub mod sample;\nmod utils;\nmod widget;\n"
  },
  {
    "path": "src/renderer.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n\nuse iced_widget::{\n    canvas::{Cache, Frame, Geometry},\n    core::{Layout, Size, Vector},\n    text::Shaping,\n};\nuse plotters::prelude::DrawingArea;\n\nuse crate::backend::IcedChartBackend;\nuse crate::Chart;\n\n/// Graphics Renderer\npub trait Renderer:\n    iced_widget::core::Renderer + iced_widget::core::text::Renderer + iced_graphics::geometry::Renderer\n{\n    /// draw a [Chart]\n    fn draw_chart<Message, C>(\n        &mut self,\n        state: &C::State,\n        chart: &C,\n        layout: Layout<'_>,\n        shaping: Shaping,\n    ) where\n        C: Chart<Message>;\n}\n\nimpl crate::chart::Renderer for iced_widget::renderer::Renderer {\n    fn draw<F: Fn(&mut Frame)>(&self, size: Size, f: F) -> Geometry {\n        let mut frame = Frame::new(self, size);\n        f(&mut frame);\n        frame.into_geometry()\n    }\n\n    fn draw_cache<F: Fn(&mut Frame)>(&self, cache: &Cache, size: Size, f: F) -> Geometry {\n        cache.draw(self, size, f)\n    }\n}\n\nimpl Renderer for iced_widget::renderer::Renderer {\n    fn draw_chart<Message, C>(\n        &mut self,\n        state: &C::State,\n        chart: &C,\n        layout: Layout<'_>,\n        shaping: Shaping,\n    ) where\n        C: Chart<Message>,\n    {\n        let bounds = layout.bounds();\n        if bounds.width < 1.0 || bounds.height < 1.0 {\n            return;\n        }\n        let geometry = chart.draw(self, bounds.size(), |frame| {\n            let backend = IcedChartBackend::new(frame, self, shaping);\n            let root: DrawingArea<_, _> = backend.into();\n            chart.draw_chart(state, root);\n        });\n        let translation = Vector::new(bounds.x, bounds.y);\n        iced_widget::core::Renderer::with_translation(self, translation, |renderer| {\n            iced_graphics::geometry::Renderer::draw_geometry(renderer, geometry);\n        });\n    }\n}\n"
  },
  {
    "path": "src/sample/lttb.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n//\n\n//! Largest-Triangle-Three-Buckets algorithm (LTTB)\n//!\n//! ## Known limitations\n//! - X-values must be in a strictly increasing order\n\n// original version: https://github.com/sveinn-steinarsson/flot-downsample\n// modified based on https://github.com/jeromefroe/lttb-rs\n\n/// data point for [`LttbSource`]\npub trait DataPoint {\n    /// x value for sampling, must be in a strictly increasing order\n    fn x(&self) -> f64;\n    /// y value for sampling\n    fn y(&self) -> f64;\n}\n\nimpl<D: DataPoint> DataPoint for &D {\n    #[inline]\n    fn x(&self) -> f64 {\n        (*self).x()\n    }\n    #[inline]\n    fn y(&self) -> f64 {\n        (*self).y()\n    }\n}\n\n/// data source for lttb sampling\n///\n/// ## Known limitations\n/// - X-values must be in a strictly increasing order\npub trait LttbSource {\n    /// data item of [`LttbSource`]\n    type Item;\n\n    /// length of [`LttbSource`]\n    fn len(&self) -> usize;\n\n    /// is [`LttbSource`] empty\n    #[inline]\n    fn is_empty(&self) -> bool {\n        self.len() == 0\n    }\n\n    /// data item at  index `i`\n    fn item_at(&self, i: usize) -> Self::Item;\n\n    /// map data item to another type.\n    /// - if the data item type of [`LttbSource`] is not [`DataPoint`], lttb sampling can be used after casting\n    fn cast<T, F>(self, f: F) -> Cast<Self, T, F>\n    where\n        Self: Sized,\n        T: DataPoint,\n        F: Fn(Self::Item) -> T,\n    {\n        Cast { s: self, f }\n    }\n\n    /// lttb sampling\n    fn lttb(self, threshold: usize) -> LttbIterator<Self>\n    where\n        Self: Sized,\n        Self::Item: DataPoint,\n    {\n        let is_sample = !(threshold >= self.len() || threshold < 3);\n        let every = if is_sample {\n            ((self.len() - 2) as f64) / ((threshold - 2) as f64)\n        } else {\n            0_f64\n        };\n        LttbIterator {\n            source: self,\n            is_sample,\n            idx: 0,\n            a: 0,\n            threshold,\n            every,\n        }\n    }\n}\n\n/// map data item to another type\npub struct Cast<S, T, F>\nwhere\n    S: LttbSource,\n    T: DataPoint,\n    F: Fn(S::Item) -> T,\n{\n    s: S,\n    f: F,\n}\n\nimpl<S, T, F> LttbSource for Cast<S, T, F>\nwhere\n    S: LttbSource,\n    T: DataPoint,\n    F: Fn(S::Item) -> T,\n{\n    type Item = T;\n    #[inline]\n    fn len(&self) -> usize {\n        self.s.len()\n    }\n\n    #[inline]\n    fn item_at(&self, i: usize) -> Self::Item {\n        let item = self.s.item_at(i);\n        (self.f)(item)\n    }\n}\n\nimpl<'a, S: LttbSource> LttbSource for &'a S {\n    type Item = S::Item;\n    #[inline]\n    fn len(&self) -> usize {\n        (*self).len()\n    }\n\n    #[inline]\n    fn item_at(&self, i: usize) -> Self::Item {\n        (*self).item_at(i)\n    }\n}\n\n/// iterator for [`LttbSource`]\npub struct LttbIterator<S: LttbSource> {\n    source: S,\n    is_sample: bool,\n    idx: usize,\n    threshold: usize,\n    every: f64,\n    a: usize,\n}\n\nimpl<S: LttbSource> LttbIterator<S>\nwhere\n    S::Item: DataPoint,\n{\n    fn next_no_sample(&mut self) -> Option<S::Item> {\n        if self.idx < self.source.len() {\n            let item = self.source.item_at(self.idx);\n            self.idx += 1;\n            Some(item)\n        } else {\n            None\n        }\n    }\n\n    fn next_sample(&mut self) -> Option<S::Item> {\n        if self.idx < self.threshold {\n            if self.idx == 0 {\n                self.idx += 1;\n                Some(self.source.item_at(0))\n            } else if self.idx + 1 == self.threshold {\n                self.idx += 1;\n                Some(self.source.item_at(self.source.len() - 1))\n            } else {\n                let every = self.every;\n                let i = self.idx - 1;\n                // Calculate point average for next bucket (containing c).\n                let mut avg_x = 0f64;\n                let mut avg_y = 0f64;\n\n                let avg_range_start = (((i + 1) as f64) * every) as usize + 1;\n\n                let mut end = (((i + 2) as f64) * every) as usize + 1;\n                if end >= self.source.len() {\n                    end = self.source.len();\n                }\n                let avg_range_end = end;\n\n                let avg_range_length = (avg_range_end - avg_range_start) as f64;\n\n                for i in 0..(avg_range_end - avg_range_start) {\n                    let idx = avg_range_start + i;\n                    let item = self.source.item_at(idx);\n                    avg_x += item.x();\n                    avg_y += item.y();\n                }\n                avg_x /= avg_range_length;\n                avg_y /= avg_range_length;\n\n                // Get the range for this bucket.\n                let range_offs = ((i as f64) * every) as usize + 1;\n                let range_to = (((i + 1) as f64) * every) as usize + 1;\n\n                // Point a.\n                let item = self.source.item_at(self.a);\n                let point_a_x = item.x();\n                let point_a_y = item.y();\n\n                let mut max_area = -1f64;\n                let mut next_a = range_offs;\n                for i in 0..(range_to - range_offs) {\n                    let idx = range_offs + i;\n\n                    // Calculate triangle area over three buckets.\n                    let item = self.source.item_at(idx);\n                    let area = ((point_a_x - avg_x) * (item.y() - point_a_y)\n                        - (point_a_x - item.x()) * (avg_y - point_a_y))\n                        .abs()\n                        * 0.5;\n                    if area > max_area {\n                        max_area = area;\n                        next_a = idx; // Next a is this b.\n                    }\n                }\n\n                let item = self.source.item_at(next_a); // Pick this point from the bucket.\n                self.a = next_a; // This a is the next a (chosen b).\n                self.idx += 1;\n                Some(item)\n            }\n        } else {\n            None\n        }\n    }\n\n    #[inline]\n    fn remaining(&self) -> usize {\n        if self.is_sample {\n            self.threshold - self.idx\n        } else {\n            self.source.len() - self.idx\n        }\n    }\n}\n\nimpl<S: LttbSource> Iterator for LttbIterator<S>\nwhere\n    S::Item: DataPoint,\n{\n    type Item = S::Item;\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.is_sample {\n            self.next_sample()\n        } else {\n            self.next_no_sample()\n        }\n    }\n\n    #[inline]\n    fn size_hint(&self) -> (usize, Option<usize>) {\n        let size = self.remaining();\n        (size, Some(size))\n    }\n}\n\nimpl<S: LttbSource> ExactSizeIterator for LttbIterator<S>\nwhere\n    S::Item: DataPoint,\n{\n    #[inline]\n    fn len(&self) -> usize {\n        self.remaining()\n    }\n}\n\nimpl<'a, T> LttbSource for &'a [T] {\n    type Item = &'a T;\n    #[inline]\n    fn len(&self) -> usize {\n        (*self).len()\n    }\n\n    #[inline]\n    fn item_at(&self, i: usize) -> Self::Item {\n        &self[i]\n    }\n}\n\nimpl<T: Clone> LttbSource for [T] {\n    type Item = T;\n    #[inline]\n    fn len(&self) -> usize {\n        (*self).len()\n    }\n\n    #[inline]\n    fn item_at(&self, i: usize) -> Self::Item {\n        self[i].clone()\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[derive(Debug, PartialEq, Clone)]\n    pub struct DataPoint {\n        pub x: f64,\n        pub y: f64,\n    }\n\n    impl DataPoint {\n        pub fn new(x: f64, y: f64) -> Self {\n            DataPoint { x, y }\n        }\n    }\n\n    impl super::DataPoint for DataPoint {\n        fn x(&self) -> f64 {\n            self.x\n        }\n\n        fn y(&self) -> f64 {\n            self.y\n        }\n    }\n\n    #[test]\n    fn lttb_test() {\n        let mut dps = vec![];\n        dps.push(DataPoint::new(0.0, 10.0));\n        dps.push(DataPoint::new(1.0, 12.0));\n        dps.push(DataPoint::new(2.0, 8.0));\n        dps.push(DataPoint::new(3.0, 10.0));\n        dps.push(DataPoint::new(4.0, 12.0));\n\n        let mut expected = vec![];\n        expected.push(DataPoint::new(0.0, 10.0));\n        expected.push(DataPoint::new(2.0, 8.0));\n        expected.push(DataPoint::new(4.0, 12.0));\n\n        let result: Vec<DataPoint> = dps.as_slice().lttb(3).cloned().collect();\n\n        assert_eq!(expected, result);\n    }\n}\n"
  },
  {
    "path": "src/sample.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n//\n\npub mod lttb;\n"
  },
  {
    "path": "src/utils.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n\nuse iced_widget::canvas;\nuse iced_widget::core::{Color, Point};\nuse plotters_backend::{BackendColor, BackendCoord, BackendStyle};\n\npub(crate) trait AndExt {\n    fn and<F: Fn(Self) -> Self>(self, f: F) -> Self\n    where\n        Self: Sized;\n}\n\nimpl<T> AndExt for T {\n    #[inline(always)]\n    fn and<F: Fn(Self) -> Self>(self, f: F) -> Self\n    where\n        Self: Sized,\n    {\n        f(self)\n    }\n}\n\n#[inline]\npub(crate) fn cvt_color(color: &BackendColor) -> Color {\n    let ((r, g, b), a) = (color.rgb, color.alpha);\n    Color::from_rgba8(r, g, b, a as f32)\n}\n\n#[inline]\npub(crate) fn cvt_stroke<S: BackendStyle>(style: &S) -> canvas::Stroke {\n    canvas::Stroke::default()\n        .with_color(cvt_color(&style.color()))\n        .with_width(style.stroke_width() as f32)\n}\n\npub(crate) trait CvtPoint {\n    fn cvt_point(self) -> Point;\n}\n\nimpl CvtPoint for BackendCoord {\n    #[inline]\n    fn cvt_point(self) -> Point {\n        Point::new(self.0 as f32, self.1 as f32)\n    }\n}\n\nimpl CvtPoint for [f64; 2] {\n    #[inline]\n    fn cvt_point(self) -> Point {\n        Point::new(self[0] as f32, self[1] as f32)\n    }\n}\n"
  },
  {
    "path": "src/widget.rs",
    "content": "// plotters-iced\n//\n// Iced backend for Plotters\n// Copyright: 2022, Joylei <leingliu@gmail.com>\n// License: MIT\n\nuse core::marker::PhantomData;\n\nuse iced_widget::{\n    canvas::Event,\n    core::{\n        event,\n        mouse::Cursor,\n        renderer::Style,\n        widget::{tree, Tree},\n        Element, Layout, Length, Rectangle, Shell, Size, Widget,\n    },\n    text::Shaping,\n};\n\nuse crate::renderer::Renderer;\n\nuse super::Chart;\n\n/// Chart container, turns [`Chart`]s to [`Widget`]s\npub struct ChartWidget<'a, Message, Theme, Renderer, C>\nwhere\n    C: Chart<Message>,\n{\n    chart: C,\n    width: Length,\n    height: Length,\n    shaping: Shaping,\n    _marker: PhantomData<&'a (Renderer, Theme, Message)>,\n}\n\nimpl<'a, Message, Theme, Renderer, C> ChartWidget<'a, Message, Theme, Renderer, C>\nwhere\n    C: Chart<Message> + 'a,\n{\n    /// create a new [`ChartWidget`]\n    pub fn new(chart: C) -> Self {\n        Self {\n            chart,\n            width: Length::Fill,\n            height: Length::Fill,\n            shaping: Default::default(),\n            _marker: Default::default(),\n        }\n    }\n\n    /// set width\n    pub fn width(mut self, width: Length) -> Self {\n        self.width = width;\n        self\n    }\n\n    /// set height\n    pub fn height(mut self, height: Length) -> Self {\n        self.height = height;\n        self\n    }\n\n    /// set text shaping\n    pub fn text_shaping(mut self, shaping: Shaping) -> Self {\n        self.shaping = shaping;\n        self\n    }\n}\n\nimpl<'a, Message, Theme, Renderer, C> Widget<Message, Theme, Renderer>\n    for ChartWidget<'a, Message, Theme, Renderer, C>\nwhere\n    C: Chart<Message>,\n    Renderer: self::Renderer,\n{\n    fn size(&self) -> Size<Length> {\n        Size::new(self.width, self.height)\n    }\n\n    fn tag(&self) -> tree::Tag {\n        struct Tag<T>(T);\n        tree::Tag::of::<Tag<C::State>>()\n    }\n\n    fn state(&self) -> tree::State {\n        tree::State::new(C::State::default())\n    }\n\n    #[inline]\n    fn layout(\n        &self,\n        _tree: &mut Tree,\n        _renderer: &Renderer,\n        limits: &iced_widget::core::layout::Limits,\n    ) -> iced_widget::core::layout::Node {\n        let size = limits.resolve(self.width, self.height, Size::ZERO);\n        iced_widget::core::layout::Node::new(size)\n    }\n\n    #[inline]\n    fn draw(\n        &self,\n        tree: &Tree,\n        renderer: &mut Renderer,\n        _theme: &Theme,\n        _style: &Style,\n        layout: Layout<'_>,\n        _cursor_position: Cursor,\n        _viewport: &Rectangle,\n    ) {\n        let state = tree.state.downcast_ref::<C::State>();\n        renderer.draw_chart(state, &self.chart, layout, self.shaping);\n    }\n\n    #[inline]\n    fn on_event(\n        &mut self,\n        tree: &mut Tree,\n        event: iced_widget::core::Event,\n        layout: Layout<'_>,\n        cursor: Cursor,\n        _renderer: &Renderer,\n        _clipboard: &mut dyn iced_widget::core::Clipboard,\n        shell: &mut Shell<'_, Message>,\n        _rectangle: &Rectangle,\n    ) -> event::Status {\n        let bounds = layout.bounds();\n        let canvas_event = match event {\n            iced_widget::core::Event::Mouse(mouse_event) => Some(Event::Mouse(mouse_event)),\n            iced_widget::core::Event::Keyboard(keyboard_event) => {\n                Some(Event::Keyboard(keyboard_event))\n            }\n            _ => None,\n        };\n        if let Some(canvas_event) = canvas_event {\n            let state = tree.state.downcast_mut::<C::State>();\n\n            let (event_status, message) = self.chart.update(state, canvas_event, bounds, cursor);\n\n            if let Some(message) = message {\n                shell.publish(message);\n            }\n            return event_status;\n        }\n        event::Status::Ignored\n    }\n\n    fn mouse_interaction(\n        &self,\n        tree: &Tree,\n        layout: Layout<'_>,\n        cursor: Cursor,\n        _viewport: &Rectangle,\n        _renderer: &Renderer,\n    ) -> iced_widget::core::mouse::Interaction {\n        let state = tree.state.downcast_ref::<C::State>();\n        let bounds = layout.bounds();\n        self.chart.mouse_interaction(state, bounds, cursor)\n    }\n}\n\nimpl<'a, Message, Theme, Renderer, C> From<ChartWidget<'a, Message, Theme, Renderer, C>>\n    for Element<'a, Message, Theme, Renderer>\nwhere\n    Message: 'a,\n    C: Chart<Message> + 'a,\n    Renderer: self::Renderer,\n{\n    fn from(widget: ChartWidget<'a, Message, Theme, Renderer, C>) -> Self {\n        Element::new(widget)\n    }\n}\n"
  }
]