Skip to main content

native_windows_gui2/controls/
window.rs

1use winapi::um::winuser::{
2    WS_CAPTION, WS_CLIPCHILDREN, WS_DISABLED, WS_EX_ACCEPTFILES, WS_EX_TOPMOST, WS_MAXIMIZE,
3    WS_MAXIMIZEBOX, WS_MINIMIZE, WS_MINIMIZEBOX, WS_OVERLAPPEDWINDOW, WS_POPUP, WS_SYSMENU,
4    WS_THICKFRAME, WS_VISIBLE,
5};
6
7use super::{ControlBase, ControlHandle};
8use crate::win32::base_helper::check_hwnd;
9use crate::win32::window_helper as wh;
10use crate::{Icon, NwgError};
11
12const NOT_BOUND: &'static str = "Window is not yet bound to a winapi object";
13const BAD_HANDLE: &'static str = "INTERNAL ERROR: Window handle is not HWND!";
14
15bitflags! {
16
17    /**
18        The window flags.
19
20        Example: `WindowFlags::MAIN_WINDOW | WindowFlags::VISIBLE`
21
22        Window flags:
23        * MAIN_WINDOW: Combine all the top level system window decoration: A title, a system menu, a resizable frame, and the close, minimize, maximize buttons
24        * WINDOW:  A window with a title, a system menu, a close button, and a non resizable border.
25        * MINIMIZE_BOX: Includes a minimize button
26        * MAXIMIZE_BOX: Includes a maximize button
27        * SYS_MENU: Includes a system menu when the user right click the window header
28        * MAXIMIZED: Create the window as maximized
29        * MINIMIZED: Create the window as minimized
30        * RESIZABLE: Add a resizable border
31        * VISIBLE: Show the window right away
32    */
33    pub struct WindowFlags: u32 {
34        const MAIN_WINDOW = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_THICKFRAME | WS_MAXIMIZEBOX;
35        const WINDOW = WS_CAPTION | WS_SYSMENU;
36        const MINIMIZE_BOX = WS_MINIMIZEBOX;
37        const MAXIMIZE_BOX = WS_MAXIMIZEBOX;
38        const SYS_MENU = WS_SYSMENU;
39        const VISIBLE = WS_VISIBLE;
40        const DISABLED = WS_DISABLED;
41        const MAXIMIZED = WS_MAXIMIZE;
42        const MINIMIZED = WS_MINIMIZE;
43        const RESIZABLE = WS_THICKFRAME | WS_MAXIMIZEBOX;
44        const POPUP = WS_POPUP;
45    }
46}
47
48/**
49    A basic top level window. At least one top level window is required to make a NWG application.
50
51    Windows can be heavily customized using the window flags. If your application don't need a visible window
52    (ex: a system tray app), use `MessageWindow` instead.
53
54    **Builder parameters:**
55      * `flags`:       The window flags. See `WindowFlags`
56      * `ex_flags`:    A combination of win32 window extended flags. Unlike `flags`, ex_flags must be used straight from winapi
57      * `title`:       The text in the window title bar
58      * `size`:        The default size of the window
59      * `position`:    The default position of the window in the desktop
60      * `icon`:        The window icon
61      * `accept_file`: If the window should accept files by drag & drop
62      * `maximized`:   If the window should be maximized at creation
63      * `minimized`:   If the window should be minimized at creation
64      * `center`:      Center the window in the current monitor based on its size. If `true`, this overrides `position`
65      * `topmost`:     If the window should always be on top of other system window
66      * `parent`:      Logical parent of the window, unlike children controls, this is NOT required.
67
68    **Control events:**
69      * `OnInit`: The window was created
70      * `MousePress(_)`: Generic mouse press events on the button
71      * `OnMouseMove`: Generic mouse mouse event
72      * `OnMouseWheel`: Generic mouse wheel event
73      * `OnPaint`: Generic on paint event
74      * `OnKeyPress`: Generic key press
75      * `OnKeyRelease`: Generic ket release
76      * `OnResize`: When the window is resized
77      * `OnResizeBegin`: Just before the window begins being resized by the user
78      * `OnResizeEnd`: Just after the user stops resizing the window
79      * `OnWindowMaximize`: When the window is maximized
80      * `OnWindowMinimize`: When the window is minimized
81      * `OnMove`: When the window is moved by the user
82      * `OnFileDrop`: When a file is dropped in the window (only raised if accept_file is set)
83      * `OnMinMaxInfo`: When the size or position of the window is about to change and the size of the windows must be restricted
84
85*/
86#[derive(Default, PartialEq, Eq)]
87pub struct Window {
88    pub handle: ControlHandle,
89}
90
91impl Window {
92    pub fn builder<'a>() -> WindowBuilder<'a> {
93        WindowBuilder {
94            title: "New Window",
95            size: (500, 500),
96            position: (300, 300),
97            accept_files: false,
98            topmost: false,
99            center: false,
100            maximized: false,
101            minimized: false,
102            flags: None,
103            ex_flags: 0,
104            icon: None,
105            parent: None,
106        }
107    }
108
109    /// Maximize the window
110    pub fn maximize(&self) {
111        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
112        wh::maximize_window(handle);
113    }
114
115    /// Minimize the window
116    pub fn minimize(&self) {
117        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
118        wh::minimize_window(handle);
119    }
120
121    /// Restore a minimized/maximized window
122    pub fn restore(&self) {
123        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
124        wh::restore_window(handle);
125    }
126
127    /// Force the window to refraw iteself and all its children
128    pub fn invalidate(&self) {
129        use winapi::um::winuser::InvalidateRect;
130
131        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
132        unsafe {
133            InvalidateRect(handle, ::std::ptr::null(), 1);
134        }
135    }
136
137    /// Close the window as if the user clicked the X button.
138    pub fn close(&self) {
139        use winapi::um::winuser::WM_CLOSE;
140
141        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
142        wh::post_message(handle, WM_CLOSE, 0, 0);
143    }
144
145    /// Return the icon of the window
146    pub fn icon(&self) -> Option<Icon> {
147        use winapi::um::winnt::HANDLE;
148        use winapi::um::winuser::WM_GETICON;
149
150        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
151        let handle = wh::send_message(handle, WM_GETICON, 0, 0);
152        if handle == 0 {
153            None
154        } else {
155            Some(Icon {
156                handle: handle as HANDLE,
157                owned: false,
158            })
159        }
160    }
161
162    /// Set the icon in the window
163    /// - icon: The new icon. If None, the icon is removed
164    pub fn set_icon(&self, icon: Option<&Icon>) {
165        use std::ptr;
166        use winapi::shared::minwindef::LPARAM;
167        use winapi::um::winuser::WM_SETICON;
168
169        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
170
171        let image_handle = icon.map(|i| i.handle).unwrap_or(ptr::null_mut());
172        wh::send_message(handle, WM_SETICON, 0, image_handle as LPARAM);
173    }
174
175    /// Return true if the control currently has the keyboard focus
176    pub fn focus(&self) -> bool {
177        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
178        wh::get_focus(handle)
179    }
180
181    /// Set the keyboard focus on the button
182    pub fn set_focus(&self) {
183        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
184
185        wh::set_focus(handle);
186    }
187
188    /// Return true if the control user can interact with the control, return false otherwise
189    pub fn enabled(&self) -> bool {
190        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
191        wh::get_window_enabled(handle)
192    }
193
194    /// Enable or disable the control
195    pub fn set_enabled(&self, v: bool) {
196        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
197        wh::set_window_enabled(handle, v)
198    }
199
200    /// Return true if the control is visible to the user. Will return true even if the
201    /// control is outside of the parent client view (ex: at the position (10000, 10000))
202    pub fn visible(&self) -> bool {
203        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
204        wh::get_window_visibility(handle)
205    }
206
207    /// Show or hide the control to the user
208    pub fn set_visible(&self, v: bool) {
209        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
210        wh::set_window_visibility(handle, v)
211    }
212
213    /// Return the size of the button in the parent window
214    pub fn size(&self) -> (u32, u32) {
215        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
216        wh::get_window_size(handle)
217    }
218
219    /// Set the size of the button in the parent window
220    pub fn set_size(&self, x: u32, y: u32) {
221        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
222        wh::set_window_size(handle, x, y, true)
223    }
224
225    /// Return the position of the button in the parent window
226    pub fn position(&self) -> (i32, i32) {
227        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
228        wh::get_window_position(handle)
229    }
230
231    /// Set the position of the button in the parent window
232    pub fn set_position(&self, x: i32, y: i32) {
233        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
234        wh::set_window_position(handle, x, y)
235    }
236
237    /// Return window title
238    pub fn text(&self) -> String {
239        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
240        wh::get_window_text(handle)
241    }
242
243    /// Set the window title
244    pub fn set_text<'a>(&self, v: &'a str) {
245        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
246        wh::set_window_text(handle, v)
247    }
248
249    /// Winapi class name used during control creation
250    pub fn class_name(&self) -> &'static str {
251        "NativeWindowsGuiWindow"
252    }
253
254    // Winapi base flags used during window creation
255    pub fn flags(&self) -> u32 {
256        WS_OVERLAPPEDWINDOW | WS_VISIBLE
257    }
258
259    /// Winapi flags required by the control
260    pub fn forced_flags(&self) -> u32 {
261        WS_CLIPCHILDREN
262    }
263}
264
265impl Drop for Window {
266    fn drop(&mut self) {
267        self.handle.destroy();
268    }
269}
270
271#[cfg(feature = "raw-win-handle")]
272use raw_window_handle::{HasRawWindowHandle, RawWindowHandle, windows::WindowsHandle};
273
274#[cfg(feature = "raw-win-handle")]
275unsafe impl HasRawWindowHandle for Window {
276    fn raw_window_handle(&self) -> RawWindowHandle {
277        use winapi::um::winuser::GWL_HINSTANCE;
278        match self.handle {
279            ControlHandle::Hwnd(hwnd) => {
280                let hinstance = wh::get_window_long(hwnd, GWL_HINSTANCE);
281
282                RawWindowHandle::Windows(WindowsHandle {
283                    hwnd: hwnd as _,
284                    hinstance: hinstance as _,
285                    ..WindowsHandle::empty()
286                })
287            }
288            // Not a valid window handle, so return an empty handle
289            _ => RawWindowHandle::Windows(WindowsHandle::empty()),
290        }
291    }
292}
293
294pub struct WindowBuilder<'a> {
295    title: &'a str,
296    size: (i32, i32),
297    position: (i32, i32),
298    accept_files: bool,
299    center: bool,
300    topmost: bool,
301    maximized: bool,
302    minimized: bool,
303    flags: Option<WindowFlags>,
304    ex_flags: u32,
305    icon: Option<&'a Icon>,
306    parent: Option<ControlHandle>,
307}
308
309impl<'a> WindowBuilder<'a> {
310    pub fn flags(mut self, flags: WindowFlags) -> WindowBuilder<'a> {
311        self.flags = Some(flags);
312        self
313    }
314
315    pub fn ex_flags(mut self, flags: u32) -> WindowBuilder<'a> {
316        self.ex_flags = flags;
317        self
318    }
319
320    pub fn title(mut self, text: &'a str) -> WindowBuilder<'a> {
321        self.title = text;
322        self
323    }
324
325    pub fn size(mut self, size: (i32, i32)) -> WindowBuilder<'a> {
326        self.size = size;
327        self
328    }
329
330    pub fn position(mut self, pos: (i32, i32)) -> WindowBuilder<'a> {
331        self.position = pos;
332        self
333    }
334
335    pub fn icon(mut self, ico: Option<&'a Icon>) -> WindowBuilder<'a> {
336        self.icon = ico;
337        self
338    }
339
340    pub fn accept_files(mut self, accept_files: bool) -> WindowBuilder<'a> {
341        self.accept_files = accept_files;
342        self
343    }
344
345    pub fn topmost(mut self, topmost: bool) -> WindowBuilder<'a> {
346        self.topmost = topmost;
347        self
348    }
349
350    pub fn center(mut self, center: bool) -> WindowBuilder<'a> {
351        self.center = center;
352        self
353    }
354
355    pub fn maximized(mut self, maximized: bool) -> WindowBuilder<'a> {
356        self.maximized = maximized;
357        self
358    }
359
360    pub fn minimized(mut self, minimized: bool) -> WindowBuilder<'a> {
361        self.minimized = minimized;
362        self
363    }
364
365    pub fn parent<C: Into<ControlHandle>>(mut self, p: Option<C>) -> WindowBuilder<'a> {
366        self.parent = p.map(|p2| p2.into());
367        self
368    }
369
370    pub fn build(self, out: &mut Window) -> Result<(), NwgError> {
371        use crate::win32::high_dpi::physical_to_logical;
372
373        let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
374
375        let mut ex_flags = self.ex_flags;
376        if self.topmost {
377            ex_flags |= WS_EX_TOPMOST;
378        }
379        if self.accept_files {
380            ex_flags |= WS_EX_ACCEPTFILES;
381        }
382
383        *out = Default::default();
384
385        out.handle = ControlBase::build_hwnd()
386            .class_name(out.class_name())
387            .forced_flags(out.forced_flags())
388            .ex_flags(ex_flags)
389            .flags(flags)
390            .size(self.size)
391            .position(self.position)
392            .text(self.title)
393            .parent(self.parent)
394            .build()?;
395
396        if self.icon.is_some() {
397            out.set_icon(self.icon);
398        }
399
400        if self.center {
401            let [left, top, right, bottom] =
402                crate::Monitor::monitor_rect_from_window(out as &Window);
403            let (m_width, m_height) = physical_to_logical(right - left, bottom - top);
404            let (width, height) = self.size;
405
406            let x = left + ((m_width - width) / 2);
407            let y = top + ((m_height - height) / 2);
408
409            out.set_position(x, y);
410        } else if self.maximized {
411            out.maximize();
412        } else if self.minimized {
413            out.minimize();
414        }
415
416        Ok(())
417    }
418}