native_windows_gui2/controls/
extern_canvas.rs

1use winapi::um::winuser::{
2    WS_CAPTION, WS_CLIPCHILDREN, WS_CLIPSIBLINGS, WS_DISABLED, WS_MAXIMIZE, WS_MAXIMIZEBOX,
3    WS_MINIMIZE, WS_MINIMIZEBOX, WS_OVERLAPPEDWINDOW, WS_SYSMENU, WS_THICKFRAME, WS_VISIBLE,
4};
5
6use super::{ControlBase, ControlHandle};
7use crate::win32::base_helper::check_hwnd;
8use crate::win32::window_helper as wh;
9use crate::{Icon, NwgError};
10
11const NOT_BOUND: &'static str = "ExternCanvas is not yet bound to a winapi object";
12const BAD_HANDLE: &'static str = "INTERNAL ERROR: ExternCanvas handle is not HWND!";
13
14bitflags! {
15
16    /**
17        The extern canvas flags.
18
19        Note that the window flags only applies if the the extern canvas is a top level window (it has no parents).
20
21        Window flags:
22        * MAIN_WINDOW: Combine all the top level system window decoration: A title, a system menu, a resizable frame, and the close, minimize, maximize buttons
23        * WINDOW:  A window with a title, a system menu, a close button, and a non resizable border.
24        * MINIMIZE_BOX: Includes a minimize button
25        * MAXIMIZE_BOX: Includes a maximize button
26        * SYS_MENU: Includes a system menu when the user right click the window header
27        * MAXIMIZED: Create the window as maximized
28        * MINIMIZED: Create the window as minimized
29        * RESIZABLE: Add a resizable border
30
31        General flags:
32        * VISIBLE: Show the window right away
33    */
34    pub struct ExternCanvasFlags: u32 {
35        const NONE = 0;
36        const MAIN_WINDOW = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_THICKFRAME | WS_MAXIMIZEBOX;
37        const WINDOW = WS_CAPTION | WS_SYSMENU;
38        const MINIMIZE_BOX = WS_MINIMIZEBOX;
39        const MAXIMIZE_BOX = WS_MAXIMIZEBOX;
40        const SYS_MENU = WS_SYSMENU;
41        const VISIBLE = WS_VISIBLE;
42        const DISABLED = WS_DISABLED;
43        const MAXIMIZED = WS_MAXIMIZE;
44        const MINIMIZED = WS_MINIMIZE;
45        const RESIZABLE = WS_THICKFRAME | WS_MAXIMIZEBOX;
46    }
47}
48
49/**
50    An `ExternCanvas` is a window/children control that is painted to by an external API (such as OpenGL, Vulkan or DirectX).
51
52    When building a `ExternCanvas`, leaving the parent field empty will create a window-like canvas. If a parent is set, the canvas will be a children control (like a button).
53    When used as a child, `ExternCanvas` can be used as a way to add highly dynamic controls to a NWG application (ex: a video player).
54
55    Requires the `extern-canvas` feature.
56
57    As a top level window, the extern canvas has the same features as the window control.
58    As a children control, resize and move events cannot be triggered and window parameters
59    are not visible.
60
61    **Builder parameters:**
62      * `flags`: The window flags. See `ExternCanvasFlags`
63      * `ex_flags`: A combination of win32 window extended flags. Unlike `flags`, ex_flags must be used straight from winapi
64      * `title`: The text in the window title bar
65      * `size`: The default size of the window
66      * `position`: The default position of the window in the desktop
67      * `icon`: The window icon
68      * `parent`: Logical parent of the window, unlike children controls, this is NOT required.
69
70    **Control events:**
71      * `OnInit`: The window was created
72      * `MousePress(_)`: Generic mouse press events on the button
73      * `OnMouseMove`: Generic mouse mouse event
74      * `OnMouseWheel`: Generic mouse wheel event
75      * `OnPaint`: Generic on paint event
76      * `OnKeyPress`: Generic key press
77      * `OnKeyRelease`: Generic ket release
78      * `OnResize`: When the window is resized
79      * `OnResizeBegin`: Just before the window begins being resized by the user
80      * `OnResizeEnd`: Just after the user stops resizing the window
81      * `OnWindowMaximize`: When the window is maximized
82      * `OnWindowMinimize`: When the window is minimized
83      * `OnMove`: When the window is moved by the user
84      * `OnMinMaxInfo`: When the size or position of the window is about to change and the size of the windows must be restricted
85
86*/
87#[derive(Default)]
88pub struct ExternCanvas {
89    pub handle: ControlHandle,
90}
91
92impl ExternCanvas {
93    pub fn builder<'a>() -> ExternCanvasBuilder<'a> {
94        ExternCanvasBuilder {
95            title: "New Canvas",
96            size: (500, 500),
97            position: (300, 300),
98            flags: None,
99            ex_flags: 0,
100            icon: None,
101            parent: None,
102        }
103    }
104
105    /// Invalidate the whole drawing region. For canvas that are children control, this should be called in the paint event.
106    pub fn invalidate(&self) {
107        use std::ptr;
108        use winapi::um::winuser::InvalidateRect;
109
110        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
111        unsafe {
112            InvalidateRect(handle, ptr::null(), 1);
113        }
114    }
115
116    /// Return the icon of the window
117    pub fn icon(&self) -> Option<Icon> {
118        use winapi::um::winnt::HANDLE;
119        use winapi::um::winuser::WM_GETICON;
120
121        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
122
123        let icon_handle = wh::send_message(handle, WM_GETICON, 0, 0);
124        if icon_handle == 0 {
125            None
126        } else {
127            Some(Icon {
128                handle: icon_handle as HANDLE,
129                owned: false,
130            })
131        }
132    }
133
134    /// Set the icon in the window
135    /// - icon: The new icon. If None, the icon is removed
136    pub fn set_icon(&self, icon: Option<&Icon>) {
137        use std::ptr;
138        use winapi::shared::minwindef::LPARAM;
139        use winapi::um::winuser::WM_SETICON;
140
141        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
142
143        let image_handle = icon.map(|i| i.handle).unwrap_or(ptr::null_mut());
144        wh::send_message(handle, WM_SETICON, 0, image_handle as LPARAM);
145    }
146
147    /// Return true if the control currently has the keyboard focus
148    pub fn focus(&self) -> bool {
149        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
150        wh::get_focus(handle)
151    }
152
153    /// Set the keyboard focus on the button
154    pub fn set_focus(&self) {
155        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
156
157        wh::set_focus(handle);
158    }
159
160    /// Return true if the control user can interact with the control, return false otherwise
161    pub fn enabled(&self) -> bool {
162        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
163        wh::get_window_enabled(handle)
164    }
165
166    /// Enable or disable the control
167    pub fn set_enabled(&self, v: bool) {
168        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
169        wh::set_window_enabled(handle, v)
170    }
171
172    /// Return true if the control is visible to the user. Will return true even if the
173    /// control is outside of the parent client view (ex: at the position (10000, 10000))
174    pub fn visible(&self) -> bool {
175        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
176        wh::get_window_visibility(handle)
177    }
178
179    /// Show or hide the control to the user
180    pub fn set_visible(&self, v: bool) {
181        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
182        wh::set_window_visibility(handle, v)
183    }
184
185    /// Return the size of the button in the parent window
186    pub fn size(&self) -> (u32, u32) {
187        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
188        wh::get_window_size(handle)
189    }
190
191    /// Return the physical size of canvas in pixels considering the dpi scale
192    pub fn physical_size(&self) -> (u32, u32) {
193        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
194        wh::get_window_physical_size(handle)
195    }
196
197    /// Set the size of the button in the parent window
198    pub fn set_size(&self, x: u32, y: u32) {
199        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
200        wh::set_window_size(handle, x, y, true)
201    }
202
203    /// Return the position of the button in the parent window
204    pub fn position(&self) -> (i32, i32) {
205        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
206        wh::get_window_position(handle)
207    }
208
209    /// Set the position of the button in the parent window
210    pub fn set_position(&self, x: i32, y: i32) {
211        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
212        wh::set_window_position(handle, x, y)
213    }
214
215    /// Return window title
216    pub fn text(&self) -> String {
217        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
218        wh::get_window_text(handle)
219    }
220
221    /// Set the window title
222    pub fn set_text<'a>(&self, v: &'a str) {
223        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
224        wh::set_window_text(handle, v)
225    }
226
227    /// Winapi class name used during control creation
228    pub fn class_name(&self) -> &'static str {
229        "NWG_EXTERN_CANVAS"
230    }
231
232    // Winapi base flags used during window creation
233    pub fn flags(&self) -> u32 {
234        WS_OVERLAPPEDWINDOW | WS_VISIBLE
235    }
236
237    /// Winapi flags required by the control
238    pub fn forced_flags(&self) -> u32 {
239        WS_CLIPCHILDREN | WS_CLIPSIBLINGS
240    }
241}
242
243impl Drop for ExternCanvas {
244    fn drop(&mut self) {
245        self.handle.destroy();
246    }
247}
248
249#[cfg(feature = "raw-win-handle")]
250use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, windows::WindowsHandle};
251
252#[cfg(feature = "raw-win-handle")]
253unsafe impl HasRawWindowHandle for ExternCanvas {
254    fn raw_window_handle(&self) -> RawWindowHandle {
255        use winapi::um::winuser::GWL_HINSTANCE;
256
257        match self.handle {
258            ControlHandle::Hwnd(hwnd) => {
259                let hinstance = wh::get_window_long(hwnd, GWL_HINSTANCE);
260
261                RawWindowHandle::Windows(WindowsHandle {
262                    hwnd: hwnd as _,
263                    hinstance: hinstance as _,
264                    ..WindowsHandle::empty()
265                })
266            }
267            // Not a valid window handle, so return an empty handle
268            _ => RawWindowHandle::Windows(WindowsHandle::empty()),
269        }
270    }
271}
272
273pub struct ExternCanvasBuilder<'a> {
274    title: &'a str,
275    size: (i32, i32),
276    position: (i32, i32),
277    flags: Option<ExternCanvasFlags>,
278    ex_flags: u32,
279    icon: Option<&'a Icon>,
280    parent: Option<ControlHandle>,
281}
282
283impl<'a> ExternCanvasBuilder<'a> {
284    pub fn flags(mut self, flags: ExternCanvasFlags) -> ExternCanvasBuilder<'a> {
285        self.flags = Some(flags);
286        self
287    }
288
289    pub fn ex_flags(mut self, flags: u32) -> ExternCanvasBuilder<'a> {
290        self.ex_flags = flags;
291        self
292    }
293
294    pub fn title(mut self, text: &'a str) -> ExternCanvasBuilder<'a> {
295        self.title = text;
296        self
297    }
298
299    pub fn size(mut self, size: (i32, i32)) -> ExternCanvasBuilder<'a> {
300        self.size = size;
301        self
302    }
303
304    pub fn position(mut self, pos: (i32, i32)) -> ExternCanvasBuilder<'a> {
305        self.position = pos;
306        self
307    }
308
309    pub fn icon(mut self, ico: Option<&'a Icon>) -> ExternCanvasBuilder<'a> {
310        self.icon = ico;
311        self
312    }
313
314    pub fn parent<C: Into<ControlHandle>>(mut self, p: Option<C>) -> ExternCanvasBuilder<'a> {
315        self.parent = p.map(|p2| p2.into());
316        self
317    }
318
319    pub fn build(self, out: &mut ExternCanvas) -> Result<(), NwgError> {
320        use winapi::um::winuser::WS_CHILD;
321
322        let mut flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
323
324        // Remove window flags if a parent is set
325        if self.parent.is_some() {
326            flags &= !(WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_THICKFRAME | WS_MAXIMIZEBOX);
327            flags |= WS_CHILD;
328        }
329
330        *out = Default::default();
331
332        out.handle = ControlBase::build_hwnd()
333            .class_name(out.class_name())
334            .forced_flags(out.forced_flags())
335            .flags(flags)
336            .ex_flags(self.ex_flags)
337            .size(self.size)
338            .position(self.position)
339            .text(self.title)
340            .parent(self.parent)
341            .build()?;
342
343        if self.icon.is_some() {
344            out.set_icon(self.icon);
345        }
346
347        Ok(())
348    }
349}