Skip to main content

native_windows_gui2/resources/
image_list.rs

1use crate::{Bitmap, Icon, NwgError};
2use std::ptr;
3use winapi::shared::windef::{HBITMAP, HICON};
4use winapi::um::commctrl::{HIMAGELIST, ImageList_AddMasked};
5
6const NOT_BOUND: &'static str = "ImageList is not yet bound to a winapi object";
7
8/**
9An image list is a collection of images of the same size, each of which can be referred to by its index.
10Image lists are used in controls such as tabs container and tree view in order to add icon next to the items.
11
12There are two kinds of image list in Winapi: masked. This is a wrapper over the masked type.
13
14Image list and the method that use them in controls are behind the "image-list" feature.
15
16**Builder parameters:**
17  * `size`:    The size size of the images in the image list. Default `(32, 32)`
18  * `initial`: The initial size (in images) of the image list. Default `5`
19  * `grow`:    The number of images by which the image list can grow when the system needs to make room for new images. Default `5`
20
21```rust
22use native_windows_gui2 as nwg;
23fn build_image_list(list: &mut nwg::ImageList) {
24    nwg::ImageList::builder()
25        .size((64, 64))
26        .initial(10)
27        .grow(1)
28        .build(list);
29}
30```
31
32*/
33pub struct ImageList {
34    pub handle: HIMAGELIST,
35    pub owned: bool,
36}
37
38impl ImageList {
39    pub fn builder() -> ImageListBuilder {
40        ImageListBuilder {
41            size: (32, 32),
42            initial: 5,
43            grow: 5,
44        }
45    }
46
47    /// Returns the size of the images in the image list
48    pub fn size(&self) -> (i32, i32) {
49        use winapi::um::commctrl::ImageList_GetIconSize;
50
51        if self.handle.is_null() {
52            panic!("{}", NOT_BOUND);
53        }
54
55        let mut size = (0, 0);
56        unsafe {
57            ImageList_GetIconSize(self.handle, &mut size.0, &mut size.1);
58        }
59
60        size
61    }
62
63    /// Sets the size of the image list. This clears all current image data.
64    pub fn set_size(&self, size: (i32, i32)) {
65        use winapi::um::commctrl::ImageList_SetIconSize;
66
67        if self.handle.is_null() {
68            panic!("{}", NOT_BOUND);
69        }
70
71        let (w, h) = size;
72        unsafe {
73            ImageList_SetIconSize(self.handle, w, h);
74        }
75    }
76
77    /// Returns the number of images in the image list
78    pub fn len(&self) -> u32 {
79        use winapi::um::commctrl::ImageList_GetImageCount;
80
81        if self.handle.is_null() {
82            panic!("{}", NOT_BOUND);
83        }
84
85        unsafe { ImageList_GetImageCount(self.handle) as u32 }
86    }
87
88    /// Adds a new bitmap to the image list. Returns the index to the image. Panics if the bitmap was not initialized
89    pub fn add_bitmap(&self, bitmap: &Bitmap) -> i32 {
90        if self.handle.is_null() {
91            panic!("{}", NOT_BOUND);
92        }
93        if bitmap.handle.is_null() {
94            panic!("Bitmap was not initialized");
95        }
96
97        unsafe { ImageList_AddMasked(self.handle, bitmap.handle as HBITMAP, 0) }
98    }
99
100    /**
101        Adds a bitmap directly from a filename. The image is resized to the image list size.
102        Returns the index to the image or an error if the image could not be loaded
103    */
104    pub fn add_bitmap_from_filename(&self, filename: &str) -> Result<i32, NwgError> {
105        if self.handle.is_null() {
106            panic!("{}", NOT_BOUND);
107        }
108
109        let (w, h) = self.size();
110        let mut bitmap = Bitmap::default();
111        Bitmap::builder()
112            .source_file(Some(filename))
113            .size(Some((w as u32, h as u32)))
114            .strict(true)
115            .build(&mut bitmap)?;
116
117        unsafe {
118            Ok(ImageList_AddMasked(
119                self.handle,
120                bitmap.handle as HBITMAP,
121                0,
122            ))
123        }
124    }
125
126    /// Adds a new icon to the image list. Returns the index to the image. Panics if the icon was not initialized
127    pub fn add_icon(&self, icon: &Icon) -> i32 {
128        use winapi::um::wingdi::DeleteObject;
129        use winapi::um::winuser::{GetIconInfo, ICONINFO};
130
131        if self.handle.is_null() {
132            panic!("{}", NOT_BOUND);
133        }
134        if icon.handle.is_null() {
135            panic!("Icon was not initialized");
136        }
137
138        // Extract the bitmap from the icon
139        // Can't use `ImageList_AddIcon` because it doesn't always guess the mask
140        unsafe {
141            let mut info: ICONINFO = ::std::mem::zeroed();
142            GetIconInfo(icon.handle as _, &mut info);
143
144            let i = ImageList_AddMasked(self.handle, info.hbmColor, 0);
145
146            DeleteObject(info.hbmMask as _);
147            DeleteObject(info.hbmColor as _);
148
149            i
150        }
151    }
152
153    /**
154        Adds a icon directly from a filename. The image is resized to the image list size.
155        Returns the index to the image or an error if the image could not be loaded
156    */
157    pub fn add_icon_from_filename(&self, filename: &str) -> Result<i32, NwgError> {
158        if self.handle.is_null() {
159            panic!("{}", NOT_BOUND);
160        }
161
162        let (w, h) = self.size();
163        let mut icon = Icon::default();
164        Icon::builder()
165            .source_file(Some(filename))
166            .size(Some((w as u32, h as u32)))
167            .strict(true)
168            .build(&mut icon)?;
169
170        Ok(self.add_icon(&icon))
171    }
172
173    /**
174        Removes the image at the specified index
175
176        When an image is removed, the indexes of the remaining images are adjusted so that the image indexes
177        always range from zero to one less than the number of images in the image list. For example, if you
178        remove the image at index 0, then image 1 becomes image 0, image 2 becomes image 1, and so on.
179    */
180    pub fn remove(&self, index: i32) {
181        use winapi::um::commctrl::ImageList_Remove;
182
183        if self.handle.is_null() {
184            panic!("{}", NOT_BOUND);
185        }
186
187        unsafe {
188            ImageList_Remove(self.handle, index);
189        }
190    }
191
192    /// Replaces an image in the image list. Panics if the bitmap was not initialized
193    pub fn replace_bitmap(&self, index: i32, bitmap: &Bitmap) {
194        use winapi::um::commctrl::ImageList_Replace;
195
196        if self.handle.is_null() {
197            panic!("{}", NOT_BOUND);
198        }
199        if bitmap.handle.is_null() {
200            panic!("Bitmap was not initialized");
201        }
202
203        unsafe {
204            ImageList_Replace(
205                self.handle,
206                index,
207                bitmap.handle as HBITMAP,
208                ptr::null_mut(),
209            );
210        }
211    }
212
213    /// Replaces an image in the image list by an icon. Panics if the icon was not initialized
214    pub fn replace_icon(&self, index: i32, icon: &Icon) {
215        use winapi::um::commctrl::ImageList_ReplaceIcon;
216
217        if self.handle.is_null() {
218            panic!("{}", NOT_BOUND);
219        }
220        if icon.handle.is_null() {
221            panic!("Icon was not initialized");
222        }
223
224        unsafe {
225            ImageList_ReplaceIcon(self.handle, index, icon.handle as HICON);
226        }
227    }
228}
229
230impl Drop for ImageList {
231    fn drop(&mut self) {
232        use winapi::um::commctrl::ImageList_Destroy;
233        unsafe {
234            if self.owned && !self.handle.is_null() {
235                ImageList_Destroy(self.handle);
236            }
237        }
238    }
239}
240
241impl Default for ImageList {
242    fn default() -> ImageList {
243        ImageList {
244            handle: ptr::null_mut(),
245            owned: false,
246        }
247    }
248}
249
250impl PartialEq for ImageList {
251    fn eq(&self, other: &Self) -> bool {
252        self.handle == other.handle
253    }
254}
255
256pub struct ImageListBuilder {
257    size: (i32, i32),
258    initial: i32,
259    grow: i32,
260}
261
262impl ImageListBuilder {
263    pub fn size(mut self, size: (i32, i32)) -> ImageListBuilder {
264        self.size = size;
265        self
266    }
267
268    pub fn initial(mut self, initial: i32) -> ImageListBuilder {
269        self.initial = initial;
270        self
271    }
272
273    pub fn grow(mut self, grow: i32) -> ImageListBuilder {
274        self.grow = grow;
275        self
276    }
277
278    pub fn build(self, list: &mut ImageList) -> Result<(), NwgError> {
279        use winapi::um::commctrl::{ILC_COLOR32, ILC_MASK, ImageList_Create};
280
281        unsafe {
282            let (w, h) = self.size;
283            let handle = ImageList_Create(w, h, ILC_COLOR32 | ILC_MASK, self.initial, self.grow);
284            if handle.is_null() {
285                return Err(NwgError::resource_create("Failed to create image list"));
286            }
287
288            list.handle = handle;
289            list.owned = true;
290        }
291
292        Ok(())
293    }
294}