native_windows_gui/controls/
button.rs

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