native_windows_gui/controls/
window.rs

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