plotters_iced2/
widget.rs

1// plotters-iced
2//
3// Iced backend for Plotters
4// Copyright: 2022, Joylei <leingliu@gmail.com>
5// License: MIT
6
7use core::marker::PhantomData;
8
9use iced_widget::{
10    canvas::Event,
11    core::{
12        Element, Layout, Length, Rectangle, Shell, Size, Widget,
13        mouse::Cursor,
14        renderer::Style,
15        widget::{Tree, tree},
16    },
17    text::Shaping,
18};
19
20use crate::renderer::Renderer;
21
22use super::Chart;
23
24/// Chart container, turns [`Chart`]s to [`Widget`]s
25pub struct ChartWidget<'a, Message, Theme, Renderer, C>
26where
27    C: Chart<Message>,
28{
29    chart: C,
30    width: Length,
31    height: Length,
32    shaping: Shaping,
33    _marker: PhantomData<&'a (Renderer, Theme, Message)>,
34}
35
36impl<'a, Message, Theme, Renderer, C> ChartWidget<'a, Message, Theme, Renderer, C>
37where
38    C: Chart<Message> + 'a,
39{
40    /// create a new [`ChartWidget`]
41    pub fn new(chart: C) -> Self {
42        Self {
43            chart,
44            width: Length::Fill,
45            height: Length::Fill,
46            shaping: Shaping::default(),
47            _marker: PhantomData,
48        }
49    }
50
51    /// set width
52    #[must_use]
53    pub fn width(mut self, width: Length) -> Self {
54        self.width = width;
55        self
56    }
57
58    /// set height
59    #[must_use]
60    pub fn height(mut self, height: Length) -> Self {
61        self.height = height;
62        self
63    }
64
65    /// set text shaping
66    #[must_use]
67    pub fn text_shaping(mut self, shaping: Shaping) -> Self {
68        self.shaping = shaping;
69        self
70    }
71}
72
73impl<Message, Theme, Renderer, C> Widget<Message, Theme, Renderer>
74    for ChartWidget<'_, Message, Theme, Renderer, C>
75where
76    C: Chart<Message>,
77    Renderer: self::Renderer,
78{
79    fn size(&self) -> Size<Length> {
80        Size::new(self.width, self.height)
81    }
82
83    fn tag(&self) -> tree::Tag {
84        struct Tag<T>(T);
85        tree::Tag::of::<Tag<C::State>>()
86    }
87
88    fn state(&self) -> tree::State {
89        tree::State::new(C::State::default())
90    }
91
92    #[inline]
93    fn layout(
94        &mut self,
95        _tree: &mut Tree,
96        _renderer: &Renderer,
97        limits: &iced_widget::core::layout::Limits,
98    ) -> iced_widget::core::layout::Node {
99        let size = limits.resolve(self.width, self.height, Size::ZERO);
100        iced_widget::core::layout::Node::new(size)
101    }
102
103    #[inline]
104    fn draw(
105        &self,
106        tree: &Tree,
107        renderer: &mut Renderer,
108        _theme: &Theme,
109        _style: &Style,
110        layout: Layout<'_>,
111        _cursor_position: Cursor,
112        _viewport: &Rectangle,
113    ) {
114        let state = tree.state.downcast_ref::<C::State>();
115        renderer.draw_chart(state, &self.chart, layout, self.shaping);
116    }
117
118    #[inline]
119    fn update(
120        &mut self,
121        tree: &mut Tree,
122        event: &iced_graphics::core::Event,
123        layout: Layout<'_>,
124        cursor: Cursor,
125        _renderer: &Renderer,
126        _clipboard: &mut dyn iced_widget::core::Clipboard,
127        shell: &mut Shell<'_, Message>,
128        _rectangle: &Rectangle,
129    ) {
130        let bounds = layout.bounds();
131        let canvas_event = if matches!(event, Event::Mouse(_) | Event::Keyboard(_)) {
132            Some(event)
133        } else {
134            None
135        };
136        if let Some(canvas_event) = canvas_event {
137            let state = tree.state.downcast_mut::<C::State>();
138
139            if let (_, Some(message)) = self.chart.update(state, canvas_event, bounds, cursor) {
140                shell.publish(message);
141            }
142        }
143    }
144
145    fn mouse_interaction(
146        &self,
147        tree: &Tree,
148        layout: Layout<'_>,
149        cursor: Cursor,
150        _viewport: &Rectangle,
151        _renderer: &Renderer,
152    ) -> iced_widget::core::mouse::Interaction {
153        let state = tree.state.downcast_ref::<C::State>();
154        let bounds = layout.bounds();
155        self.chart.mouse_interaction(state, bounds, cursor)
156    }
157}
158
159impl<'a, Message, Theme, Renderer, C> From<ChartWidget<'a, Message, Theme, Renderer, C>>
160    for Element<'a, Message, Theme, Renderer>
161where
162    Message: 'a,
163    C: Chart<Message> + 'a,
164    Renderer: self::Renderer,
165{
166    fn from(widget: ChartWidget<'a, Message, Theme, Renderer, C>) -> Self {
167        Element::new(widget)
168    }
169}