Skip to main content

native_windows_gui2/controls/
button.rs

1use super::{ControlBase, ControlHandle};
2use crate::win32::{base_helper::check_hwnd, resources_helper as rh, window_helper as wh};
3use crate::{Bitmap, Font, Icon, NwgError};
4use winapi::um::winuser::{
5    BS_BITMAP, BS_ICON, BS_NOTIFY, WS_CHILD, WS_DISABLED, WS_TABSTOP, WS_VISIBLE,
6};
7
8const NOT_BOUND: &'static str = "Button is not yet bound to a winapi object";
9const BAD_HANDLE: &'static str = "INTERNAL ERROR: Button handle is not HWND!";
10
11bitflags! {
12    /**
13        The button flags
14
15        * NONE:     No flags. Equivalent to a invisible blank button.
16        * VISIBLE:  The button is immediatly visible after creation
17        * DISABLED: The button cannot be interacted with by the user. It also has a grayed out look.
18        * BITMAP:   The button will display a bitmap image with no text. Must have a bitmap or else it will only show text.
19        * ICON:     The button will display a icon image with no text. Must have a icon or else it will only show text.
20        * NOTIFY:   Enable the `OnButtonDoubleClick` event
21        * TAB_STOP: The control can be selected using tab navigation
22    */
23    pub struct ButtonFlags: u32 {
24        const NONE = 0;
25        const VISIBLE = WS_VISIBLE;
26        const DISABLED = WS_DISABLED;
27        const ICON = BS_ICON;
28        const BITMAP = BS_BITMAP;
29        const NOTIFY = BS_NOTIFY;
30        const TAB_STOP = WS_TABSTOP;
31    }
32}
33
34/**
35A push button is a rectangle containing an application-defined text label.
36Use `ImageButton` if you need to have a button that ONLY contains an icon or a bitmap.
37
38Button is not behind any features.
39
40**Builder parameters:**
41  * `parent`:   **Required.** The button parent container.
42  * `text`:     The button text.
43  * `size`:     The button size.
44  * `position`: The button position.
45  * `enabled`:  If the button can be used by the user. It also has a grayed out look if disabled.
46  * `flags`:    A combination of the ButtonFlags values.
47  * `ex_flags`: A combination of win32 window extended flags. Unlike `flags`, ex_flags must be used straight from winapi
48  * `font`:     The font used for the button text
49  * `bitmap`:   A bitmap to display next to the button text. If this value is set, icon is ignored.
50  * `icon`:     An icon to display next to the button text
51  * `focus`:    The control receive focus after being created
52
53**Control events:**
54  * `OnButtonClick`: When the button is clicked once by the user
55  * `OnButtonDoubleClick`: When the button is clicked twice rapidly by the user
56  * `MousePress(_)`: Generic mouse press events on the button
57  * `OnMouseMove`: Generic mouse mouse event
58  * `OnMouseWheel`: Generic mouse wheel event
59
60```rust
61use native_windows_gui2 as nwg;
62fn build_button(button: &mut nwg::Button, window: &nwg::Window, font: &nwg::Font) {
63    nwg::Button::builder()
64        .text("Hello")
65        .flags(nwg::ButtonFlags::VISIBLE)
66        .font(Some(font))
67        .parent(window)
68        .build(button);
69}
70```
71
72*/
73#[derive(Default, Eq, PartialEq)]
74pub struct Button {
75    pub handle: ControlHandle,
76}
77
78impl Button {
79    pub fn builder<'a>() -> ButtonBuilder<'a> {
80        ButtonBuilder {
81            text: "Button",
82            size: (100, 25),
83            position: (0, 0),
84            enabled: true,
85            flags: None,
86            ex_flags: 0,
87            font: None,
88            parent: None,
89            bitmap: None,
90            icon: None,
91            focus: false,
92        }
93    }
94
95    /// Simulate a user click
96    pub fn click(&self) {
97        use winapi::um::winuser::BM_CLICK;
98        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
99        wh::send_message(handle, BM_CLICK, 0, 0);
100    }
101
102    /// Sets the bitmap image of the button. Replace the current bitmap or icon.
103    /// Set `image` to `None` to remove the image
104    pub fn set_bitmap<'a>(&self, image: Option<&'a Bitmap>) {
105        use winapi::shared::minwindef::{LPARAM, WPARAM};
106        use winapi::um::winuser::{BM_SETIMAGE, IMAGE_BITMAP};
107
108        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
109
110        let image_handle = image.map(|i| i.handle as LPARAM).unwrap_or(0);
111        wh::send_message(handle, BM_SETIMAGE, IMAGE_BITMAP as WPARAM, image_handle);
112    }
113
114    /// Sets the bitmap image of the button. Replace the current bitmap or icon.
115    /// Set `image` to `None` to remove the image
116    pub fn set_icon<'a>(&self, image: Option<&'a Icon>) {
117        use winapi::shared::minwindef::{LPARAM, WPARAM};
118        use winapi::um::winuser::{BM_SETIMAGE, IMAGE_ICON};
119
120        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
121
122        let image_handle = image.map(|i| i.handle as LPARAM).unwrap_or(0);
123        wh::send_message(handle, BM_SETIMAGE, IMAGE_ICON as WPARAM, image_handle);
124    }
125
126    /// Returns the current image in the button.
127    /// If the button has a bitmap, the value will be returned in `bitmap`
128    /// If the button has a icon, the value will be returned in `icon`
129    pub fn image<'a>(&self, bitmap: &mut Option<Bitmap>, icon: &mut Option<Icon>) {
130        use winapi::shared::minwindef::WPARAM;
131        use winapi::shared::windef::HBITMAP;
132        use winapi::um::winnt::HANDLE;
133        use winapi::um::winuser::{BM_GETIMAGE, IMAGE_BITMAP, IMAGE_ICON};
134
135        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
136
137        let bitmap_handle = wh::send_message(handle, BM_GETIMAGE, IMAGE_BITMAP as WPARAM, 0);
138        let icon_handle = wh::send_message(handle, BM_GETIMAGE, IMAGE_ICON as WPARAM, 0);
139
140        *bitmap = None;
141        *icon = None;
142
143        if bitmap_handle != 0 && rh::is_bitmap(bitmap_handle as HBITMAP) {
144            *bitmap = Some(Bitmap {
145                handle: bitmap_handle as HANDLE,
146                owned: false,
147            });
148        } else if icon_handle != 0 {
149            *icon = Some(Icon {
150                handle: icon_handle as HANDLE,
151                owned: false,
152            });
153        }
154    }
155
156    /// Returns the font of the control
157    pub fn font(&self) -> Option<Font> {
158        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
159
160        let font_handle = wh::get_window_font(handle);
161        if font_handle.is_null() {
162            None
163        } else {
164            Some(Font {
165                handle: font_handle,
166            })
167        }
168    }
169
170    /// Sets the font of the control
171    pub fn set_font(&self, font: Option<&Font>) {
172        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
173
174        wh::set_window_font(handle, font.map(|f| f.handle), true);
175    }
176
177    /// Returns true if the control currently has the keyboard focus
178    pub fn focus(&self) -> bool {
179        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
180        wh::get_focus(handle)
181    }
182
183    /// Sets the keyboard focus on the button.
184    pub fn set_focus(&self) {
185        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
186
187        wh::set_focus(handle);
188    }
189
190    /// Returns true if the control user can interact with the control, return false otherwise
191    pub fn enabled(&self) -> bool {
192        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
193        wh::get_window_enabled(handle)
194    }
195
196    /// Enable or disable the control
197    pub fn set_enabled(&self, v: bool) {
198        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
199        wh::set_window_enabled(handle, v)
200    }
201
202    /// Returns true if the control is visible to the user. Will return true even if the
203    /// control is outside of the parent client view (ex: at the position (10000, 10000))
204    pub fn visible(&self) -> bool {
205        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
206        wh::get_window_visibility(handle)
207    }
208
209    /// Show or hide the control to the user
210    pub fn set_visible(&self, v: bool) {
211        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
212        wh::set_window_visibility(handle, v)
213    }
214
215    /// Returns the size of the button in the parent window
216    pub fn size(&self) -> (u32, u32) {
217        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
218        wh::get_window_size(handle)
219    }
220
221    /// Sets the size of the button in the parent window
222    pub fn set_size(&self, x: u32, y: u32) {
223        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
224        wh::set_window_size(handle, x, y, false)
225    }
226
227    /// Returns the position of the button in the parent window
228    pub fn position(&self) -> (i32, i32) {
229        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
230        wh::get_window_position(handle)
231    }
232
233    /// Sets the position of the button in the parent window
234    pub fn set_position(&self, x: i32, y: i32) {
235        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
236        wh::set_window_position(handle, x, y)
237    }
238
239    /// Returns the button label
240    pub fn text(&self) -> String {
241        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
242        wh::get_window_text(handle)
243    }
244
245    /// Sets the button label
246    pub fn set_text<'a>(&self, v: &'a str) {
247        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
248        wh::set_window_text(handle, v)
249    }
250
251    /// Winapi class name used during control creation
252    pub fn class_name(&self) -> &'static str {
253        "BUTTON"
254    }
255
256    /// Winapi base flags used during window creation
257    pub fn flags(&self) -> u32 {
258        WS_VISIBLE | WS_TABSTOP | BS_NOTIFY
259    }
260
261    /// Winapi flags required by the control
262    pub fn forced_flags(&self) -> u32 {
263        WS_CHILD
264    }
265}
266
267impl Drop for Button {
268    fn drop(&mut self) {
269        self.handle.destroy();
270    }
271}
272
273pub struct ButtonBuilder<'a> {
274    text: &'a str,
275    size: (i32, i32),
276    position: (i32, i32),
277    enabled: bool,
278    flags: Option<ButtonFlags>,
279    ex_flags: u32,
280    font: Option<&'a Font>,
281    bitmap: Option<&'a Bitmap>,
282    icon: Option<&'a Icon>,
283    parent: Option<ControlHandle>,
284    focus: bool,
285}
286
287impl<'a> ButtonBuilder<'a> {
288    pub fn flags(mut self, flags: ButtonFlags) -> ButtonBuilder<'a> {
289        self.flags = Some(flags);
290        self
291    }
292
293    pub fn ex_flags(mut self, flags: u32) -> ButtonBuilder<'a> {
294        self.ex_flags = flags;
295        self
296    }
297
298    pub fn text(mut self, text: &'a str) -> ButtonBuilder<'a> {
299        self.text = text;
300        self
301    }
302
303    pub fn size(mut self, size: (i32, i32)) -> ButtonBuilder<'a> {
304        self.size = size;
305        self
306    }
307
308    pub fn position(mut self, pos: (i32, i32)) -> ButtonBuilder<'a> {
309        self.position = pos;
310        self
311    }
312
313    pub fn enabled(mut self, e: bool) -> ButtonBuilder<'a> {
314        self.enabled = e;
315        self
316    }
317
318    pub fn font(mut self, font: Option<&'a Font>) -> ButtonBuilder<'a> {
319        self.font = font;
320        self
321    }
322
323    pub fn bitmap(mut self, bit: Option<&'a Bitmap>) -> ButtonBuilder<'a> {
324        self.bitmap = bit;
325        self
326    }
327
328    pub fn icon(mut self, ico: Option<&'a Icon>) -> ButtonBuilder<'a> {
329        self.icon = ico;
330        self
331    }
332
333    pub fn focus(mut self, focus: bool) -> ButtonBuilder<'a> {
334        self.focus = focus;
335        self
336    }
337
338    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ButtonBuilder<'a> {
339        self.parent = Some(p.into());
340        self
341    }
342
343    pub fn build(self, out: &mut Button) -> Result<(), NwgError> {
344        let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
345
346        let parent = match self.parent {
347            Some(p) => Ok(p),
348            None => Err(NwgError::no_parent("Button")),
349        }?;
350
351        // Drop the old object
352        *out = Button::default();
353
354        out.handle = ControlBase::build_hwnd()
355            .class_name(out.class_name())
356            .forced_flags(out.forced_flags())
357            .flags(flags)
358            .ex_flags(self.ex_flags)
359            .size(self.size)
360            .position(self.position)
361            .text(self.text)
362            .parent(Some(parent))
363            .build()?;
364
365        if self.font.is_some() {
366            out.set_font(self.font);
367        } else {
368            out.set_font(Font::global_default().as_ref());
369        }
370
371        out.set_enabled(self.enabled);
372
373        if self.bitmap.is_some() {
374            out.set_bitmap(self.bitmap);
375        } else if self.icon.is_some() {
376            out.set_icon(self.icon);
377        }
378
379        if self.focus {
380            out.set_focus();
381        }
382
383        Ok(())
384    }
385}