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