native_windows_gui2/controls/
image_frame.rs

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