native_windows_gui/controls/
plotters.rs

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