native_windows_gui/controls/
image_frame.rs

1use winapi::um::winuser::{WS_VISIBLE, WS_DISABLED};
2use winapi::um::wingdi::DeleteObject;
3use winapi::shared::windef::HBRUSH;
4use crate::win32::{
5    base_helper::check_hwnd,  
6    window_helper as wh,
7    resources_helper as rh
8};
9use super::{ControlBase, ControlHandle};
10use crate::{Bitmap, Icon, NwgError, RawEventHandler, unbind_raw_event_handler};
11use std::cell::RefCell;
12
13const NOT_BOUND: &'static str = "ImageFrame is not yet bound to a winapi object";
14const BAD_HANDLE: &'static str = "INTERNAL ERROR: ImageFrame handle is not HWND!";
15
16
17bitflags! {
18    pub struct ImageFrameFlags: u32 {
19        const VISIBLE = WS_VISIBLE;
20        const DISABLED = WS_DISABLED;
21    }
22}
23
24/**
25An image frame is a control that displays a `Bitmap` or a `Icon` image resource.
26
27ImageFrame is not behind any features.
28
29**Builder parameters:**
30  * `parent`:           **Required.** The image frame parent container.
31  * `size`:             The image frame size.
32  * `position`:         The image frame position.
33  * `flags`:            A combination of the ImageFrameFlags values.
34  * `ex_flags`: A combination of win32 window extended flags. Unlike `flags`, ex_flags must be used straight from winapi
35  * `background_color`: The background color of the image frame. Used if the image is smaller than the control
36  * `bitmap`:           A bitmap to display. If this value is set, icon is ignored.
37  * `icon`:             An icon to display
38
39**Control events:**
40  * `OnImageFrameClick`: When the image frame is clicked once by the user
41  * `OnImageFrameDoubleClick`: When the image frame is clicked twice rapidly by the user
42  * `MousePress(_)`: Generic mouse press events on the button
43  * `OnMouseMove`: Generic mouse mouse event
44  * `OnMouseWheel`: Generic mouse wheel event
45
46```rust
47use native_windows_gui as nwg;
48fn build_frame(button: &mut nwg::ImageFrame, window: &nwg::Window, ico: &nwg::Icon) {
49    nwg::ImageFrame::builder()
50        .parent(window)
51        .build(button);
52}
53```
54*/
55#[derive(Default)]
56pub struct ImageFrame {
57    pub handle: ControlHandle,
58    background_brush: Option<HBRUSH>,
59    handler0: RefCell<Option<RawEventHandler>>,
60}
61
62impl ImageFrame {
63
64    pub fn builder<'a>() -> ImageFrameBuilder<'a> {
65        ImageFrameBuilder {
66            size: (100, 100),
67            position: (0, 0),
68            flags: None,
69            ex_flags: 0,
70            bitmap: None,
71            icon: None,
72            parent: None,
73            background_color: None
74        }
75    }
76
77    /// Sets the bitmap image of the image frame. Replace the current bitmap or icon.
78    /// Set `image` to `None` to remove the image
79    pub fn set_bitmap<'a>(&self, image: Option<&'a Bitmap>) {
80        use winapi::um::winuser::{STM_SETIMAGE, IMAGE_BITMAP};
81        use winapi::shared::minwindef::{WPARAM, LPARAM};
82
83        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
84
85        let image_handle = image.map(|i| i.handle as LPARAM).unwrap_or(0);
86        let prev_img = wh::send_message(handle, STM_SETIMAGE, IMAGE_BITMAP as WPARAM, image_handle);
87        if prev_img != 0 {
88            unsafe { DeleteObject(prev_img as _); }
89        }
90    }
91
92    /// Sets the bitmap image of the image frame. Replace the current bitmap or icon.
93    /// Set `image` to `None` to remove the image
94    pub fn set_icon<'a>(&self, image: Option<&'a Icon>) {
95        use winapi::um::winuser::{STM_SETIMAGE, IMAGE_ICON};
96        use winapi::shared::minwindef::{WPARAM, LPARAM};
97
98        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
99
100        let image_handle = image.map(|i| i.handle as LPARAM).unwrap_or(0);
101        let prev_img = wh::send_message(handle, STM_SETIMAGE, IMAGE_ICON as WPARAM, image_handle);
102        if prev_img != 0 {
103            unsafe { DeleteObject(prev_img as _); }
104        }
105    }
106
107    /// Returns the current image in the image frame.
108    /// If the image frame has a bitmap, the value will be returned in `bitmap`
109    /// If the image frame has a icon, the value will be returned in `icon`
110    pub fn image<'a>(&self, bitmap: &mut Option<Bitmap>, icon: &mut Option<Icon>) {
111        use winapi::um::winuser::{STM_GETIMAGE, IMAGE_BITMAP, IMAGE_ICON};
112        use winapi::shared::minwindef::WPARAM;
113        use winapi::shared::windef::HBITMAP;
114        use winapi::um::winnt::HANDLE;
115
116        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
117        let bitmap_handle = wh::send_message(handle, STM_GETIMAGE, IMAGE_BITMAP as WPARAM, 0);
118        let icon_handle = wh::send_message(handle, STM_GETIMAGE, IMAGE_ICON as WPARAM, 0);
119
120        *bitmap = None;
121        *icon = None;
122
123        if bitmap_handle != 0 && rh::is_bitmap(bitmap_handle as HBITMAP) {
124            *bitmap = Some(Bitmap { handle: bitmap_handle as HANDLE, owned: false });
125        } else if icon_handle != 0 {
126            *icon = Some(Icon { handle: icon_handle as HANDLE, owned: false });
127        }
128    }
129
130    /// Return true if the control user can interact with the control, return false otherwise
131    pub fn enabled(&self) -> bool {
132        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
133        unsafe { wh::get_window_enabled(handle) }
134    }
135
136    /// Enable or disable the control
137    pub fn set_enabled(&self, v: bool) {
138        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
139        unsafe { wh::set_window_enabled(handle, v) }
140    }
141
142    /// Return true if the control is visible to the user. Will return true even if the 
143    /// control is outside of the parent client view (ex: at the position (10000, 10000))
144    pub fn visible(&self) -> bool {
145        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
146        unsafe { wh::get_window_visibility(handle) }
147    }
148
149    /// Show or hide the control to the user
150    pub fn set_visible(&self, v: bool) {
151        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
152        unsafe { wh::set_window_visibility(handle, v) }
153    }
154
155    /// Return the size of the image frame in the parent window
156    pub fn size(&self) -> (u32, u32) {
157        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
158        unsafe { wh::get_window_size(handle) }
159    }
160
161    /// Set the size of the image frame in the parent window
162    pub fn set_size(&self, x: u32, y: u32) {
163        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
164        unsafe { wh::set_window_size(handle, x, y, false) }
165    }
166
167    /// Return the position of the image frame in the parent window
168    pub fn position(&self) -> (i32, i32) {
169        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
170        unsafe { wh::get_window_position(handle) }
171    }
172
173    /// Set the position of the image frame in the parent window
174    pub fn set_position(&self, x: i32, y: i32) {
175        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
176        unsafe { wh::set_window_position(handle, x, y) }
177    }
178
179    /// Winapi class name used during control creation
180    pub fn class_name(&self) -> &'static str {
181        "STATIC"
182    }
183
184    /// Winapi base flags used during window creation
185    pub fn flags(&self) -> u32 {
186        WS_VISIBLE
187    }
188
189    /// Winapi flags required by the control
190    pub fn forced_flags(&self) -> u32 {
191        use winapi::um::winuser::{SS_NOTIFY, WS_CHILD, SS_CENTERIMAGE};
192
193        WS_CHILD | SS_NOTIFY | SS_CENTERIMAGE
194    }
195
196    /// Change the label background color to transparent.
197    /// Change the checkbox background color.
198    fn hook_background_color(&mut self, c: [u8; 3]) {
199        use crate::bind_raw_event_handler_inner;
200        use winapi::um::winuser::{WM_CTLCOLORSTATIC};
201        use winapi::shared::{basetsd::UINT_PTR, windef::{HWND}, minwindef::LRESULT};
202        use winapi::um::wingdi::{CreateSolidBrush, RGB};
203
204        if self.handle.blank() { panic!("{}", NOT_BOUND); }
205        let handle = self.handle.hwnd().expect(BAD_HANDLE);
206
207        let parent_handle = ControlHandle::Hwnd(wh::get_window_parent(handle));
208        let brush = unsafe { CreateSolidBrush(RGB(c[0], c[1], c[2])) };
209        self.background_brush = Some(brush);
210        
211        let handler = bind_raw_event_handler_inner(&parent_handle, handle as UINT_PTR, move |_hwnd, msg, _w, l| {
212            match msg {
213                WM_CTLCOLORSTATIC => {
214                    let child = l as HWND;
215                    if child == handle {
216                        return Some(brush as LRESULT);
217                    }
218                },
219                _ => {}
220            }
221
222            None
223        });
224
225        *self.handler0.borrow_mut() = Some(handler.unwrap());
226    }
227
228}
229
230impl Drop for ImageFrame {
231    fn drop(&mut self) {
232        let handler = self.handler0.borrow();
233        if let Some(h) = handler.as_ref() {
234            drop(unbind_raw_event_handler(h));
235        }
236
237        if let Some(bg) = self.background_brush {
238            unsafe { DeleteObject(bg as _); }
239        }
240
241        self.handle.destroy();
242    }
243}
244
245pub struct ImageFrameBuilder<'a> {
246    size: (i32, i32),
247    position: (i32, i32),
248    flags: Option<ImageFrameFlags>,
249    ex_flags: u32,
250    bitmap: Option<&'a Bitmap>,
251    icon: Option<&'a Icon>,
252    parent: Option<ControlHandle>,
253    background_color: Option<[u8; 3]>,
254}
255
256impl<'a> ImageFrameBuilder<'a> {
257
258    pub fn flags(mut self, flags: ImageFrameFlags) -> ImageFrameBuilder<'a> {
259        self.flags = Some(flags);
260        self
261    }
262
263    pub fn ex_flags(mut self, flags: u32) -> ImageFrameBuilder<'a> {
264        self.ex_flags = flags;
265        self
266    }
267
268    pub fn size(mut self, size: (i32, i32)) -> ImageFrameBuilder<'a> {
269        self.size = size;
270        self
271    }
272
273    pub fn position(mut self, pos: (i32, i32)) -> ImageFrameBuilder<'a> {
274        self.position = pos;
275        self
276    }
277
278    pub fn bitmap(mut self, bit: Option<&'a Bitmap>) -> ImageFrameBuilder<'a> {
279        self.bitmap = bit;
280        self
281    }
282
283    pub fn icon(mut self, ico: Option<&'a Icon>) -> ImageFrameBuilder<'a> {
284        self.icon = ico;
285        self
286    }
287
288    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ImageFrameBuilder<'a> {
289        self.parent = Some(p.into());
290        self
291    }
292
293    pub fn background_color(mut self, color: Option<[u8;3]>) -> ImageFrameBuilder<'a> {
294        self.background_color = color;
295        self
296    }
297
298    pub fn build(self, out: &mut ImageFrame) -> Result<(), NwgError> {
299        use winapi::um::winuser::{SS_BITMAP, SS_ICON};
300
301        let mut flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());
302        if self.icon.is_some() {
303            flags |= SS_ICON;
304        } else {
305            flags |= SS_BITMAP;
306        }
307        
308        let parent = match self.parent {
309            Some(p) => Ok(p),
310            None => Err(NwgError::no_parent("ImageFrame"))
311        }?;
312
313        *out = Default::default();
314
315        out.handle = ControlBase::build_hwnd()
316            .class_name(out.class_name())
317            .forced_flags(out.forced_flags())
318            .flags(flags)
319            .ex_flags(self.ex_flags)
320            .size(self.size)
321            .position(self.position)
322            .parent(Some(parent))
323            .build()?;
324
325        if self.bitmap.is_some() {
326            out.set_bitmap(self.bitmap);
327        } else if self.icon.is_some() {
328            out.set_icon(self.icon);
329        }
330
331        if self.background_color.is_some() {
332            out.hook_background_color(self.background_color.unwrap());
333        }
334
335        Ok(())
336    }
337
338}
339
340impl PartialEq for ImageFrame {
341    fn eq(&self, other: &Self) -> bool {
342        self.handle == other.handle
343    }
344}