native_windows_gui2/controls/
plotters.rs

1use super::{ControlBase, ControlHandle};
2use crate::NwgError;
3use crate::win32::base_helper::check_hwnd;
4use crate::win32::window_helper as wh;
5use winapi::um::winuser::{WS_CHILD, WS_CLIPCHILDREN, WS_CLIPSIBLINGS, WS_VISIBLE};
6
7pub use crate::win32::plotters_d2d::{PlottersBackend, PlottersError};
8use plotters::coord::Shift;
9use plotters::prelude::DrawingArea;
10use std::ops::Deref;
11
12const NOT_BOUND: &'static str = "Plotters control is not yet bound to a winapi object";
13const BAD_HANDLE: &'static str = "INTERNAL ERROR: Plotters control handle is not HWND!";
14
15/**
16    An object that can be used as a drawing area by the plotters library.
17
18    This is needed because direct 2D needs to wrap the drawing command between a `begin_draw` and `end_draw` call
19    but it is impossible to do that within the DrawingBackend trait.
20*/
21pub struct PlottersDrawingArea<'a> {
22    inner: &'a Plotters,
23    area: DrawingArea<&'a PlottersBackend, Shift>,
24}
25
26impl<'a> PlottersDrawingArea<'a> {
27    pub fn new(inner: &'a Plotters) -> Result<PlottersDrawingArea<'a>, PlottersError> {
28        let backend = inner.d2d_backend.as_ref().unwrap();
29
30        backend.rebuild(inner.handle.hwnd().unwrap())?;
31        backend.begin_draw();
32        backend.clear();
33
34        let area = PlottersDrawingArea {
35            inner: inner,
36            area: backend.into(),
37        };
38
39        Ok(area)
40    }
41}
42
43impl<'a> Deref for PlottersDrawingArea<'a> {
44    type Target = DrawingArea<&'a PlottersBackend, Shift>;
45
46    fn deref(&self) -> &Self::Target {
47        &self.area
48    }
49}
50
51impl<'a> Drop for PlottersDrawingArea<'a> {
52    fn drop(&mut self) {
53        self.inner.d2d_backend.as_ref().unwrap().end_draw();
54    }
55}
56
57/**
58    A canvas-like control that act as a backend for the [plotters](https://docs.rs/plotters/0.3.0/plotters/) library.
59    The plotters control use direct2D to render to the canvas.
60*/
61#[derive(Default)]
62pub struct Plotters {
63    pub handle: ControlHandle,
64    d2d_backend: Option<PlottersBackend>,
65}
66
67impl Plotters {
68    pub fn builder() -> PlottersBuilder {
69        PlottersBuilder {
70            size: (500, 500),
71            position: (0, 0),
72            ex_flags: 0,
73            parent: None,
74        }
75    }
76
77    /// Prepare the plotters canvas for drawing. Returns an object that can be DrawingArea.
78    /// This method may fail if an internal error occured during the last draw call
79    pub fn draw<'a>(&'a self) -> Result<PlottersDrawingArea<'a>, PlottersError> {
80        check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
81        PlottersDrawingArea::new(self)
82    }
83
84    /// Return true if the control currently has the keyboard focus
85    pub fn focus(&self) -> bool {
86        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
87        wh::get_focus(handle)
88    }
89
90    /// Set the keyboard focus on the button
91    pub fn set_focus(&self) {
92        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
93
94        wh::set_focus(handle);
95    }
96
97    /// Return true if the control user can interact with the control, return false otherwise
98    pub fn enabled(&self) -> bool {
99        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
100        wh::get_window_enabled(handle)
101    }
102
103    /// Enable or disable the control
104    pub fn set_enabled(&self, v: bool) {
105        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
106        wh::set_window_enabled(handle, v)
107    }
108
109    /// Return true if the control is visible to the user. Will return true even if the
110    /// control is outside of the parent client view (ex: at the position (10000, 10000))
111    pub fn visible(&self) -> bool {
112        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
113        wh::get_window_visibility(handle)
114    }
115
116    /// Show or hide the control to the user
117    pub fn set_visible(&self, v: bool) {
118        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
119        wh::set_window_visibility(handle, v)
120    }
121
122    /// Return the size of the button in the parent window
123    pub fn size(&self) -> (u32, u32) {
124        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
125        wh::get_window_size(handle)
126    }
127
128    /// Return the physical size of canvas in pixels considering the dpi scale
129    pub fn physical_size(&self) -> (u32, u32) {
130        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
131        wh::get_window_physical_size(handle)
132    }
133
134    /// Set the size of the button in the parent window
135    pub fn set_size(&self, x: u32, y: u32) {
136        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
137        wh::set_window_size(handle, x, y, true)
138    }
139
140    /// Return the position of the button in the parent window
141    pub fn position(&self) -> (i32, i32) {
142        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
143        wh::get_window_position(handle)
144    }
145
146    /// Set the position of the button in the parent window
147    pub fn set_position(&self, x: i32, y: i32) {
148        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
149        wh::set_window_position(handle, x, y)
150    }
151
152    /// Winapi class name used during control creation
153    pub fn class_name(&self) -> &'static str {
154        "NWG_EXTERN_CANVAS"
155    }
156
157    // Winapi base flags used during window creation
158    pub fn flags(&self) -> u32 {
159        WS_CHILD | WS_VISIBLE
160    }
161
162    /// Winapi flags required by the control
163    pub fn forced_flags(&self) -> u32 {
164        WS_CLIPCHILDREN | WS_CLIPSIBLINGS
165    }
166}
167
168impl PartialEq for Plotters {
169    fn eq(&self, other: &Self) -> bool {
170        self.handle == other.handle
171    }
172}
173
174pub struct PlottersBuilder {
175    parent: Option<ControlHandle>,
176    size: (i32, i32),
177    position: (i32, i32),
178    ex_flags: u32,
179}
180
181impl PlottersBuilder {
182    pub fn ex_flags(mut self, flags: u32) -> PlottersBuilder {
183        self.ex_flags = flags;
184        self
185    }
186
187    pub fn size(mut self, size: (i32, i32)) -> PlottersBuilder {
188        self.size = size;
189        self
190    }
191
192    pub fn position(mut self, pos: (i32, i32)) -> PlottersBuilder {
193        self.position = pos;
194        self
195    }
196
197    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> PlottersBuilder {
198        self.parent = Some(p.into());
199        self
200    }
201
202    pub fn build(self, out: &mut Plotters) -> Result<(), NwgError> {
203        *out = Default::default();
204
205        out.handle = ControlBase::build_hwnd()
206            .class_name(out.class_name())
207            .forced_flags(out.forced_flags())
208            .flags(out.flags())
209            .ex_flags(self.ex_flags)
210            .size(self.size)
211            .position(self.position)
212            .text("")
213            .parent(self.parent)
214            .build()?;
215
216        let handle = out.handle.hwnd().unwrap();
217        match PlottersBackend::init(handle) {
218            Ok(b) => {
219                out.d2d_backend = Some(b);
220                Ok(())
221            }
222            Err(e) => {
223                *out = Default::default();
224                Err(NwgError::from(e))
225            }
226        }
227    }
228}