Skip to main content

ferroui_controls/
window_icon.rs

1use crate::platform::{IPlatformIconLoader, IWindowIconImpl};
2use ferroui_base::media::imaging::Bitmap;
3use ferroui_base::{FerroLocator, LocatorExtensions};
4use std::io;
5use std::rc::Rc;
6
7/// Represents an icon for a window.
8pub struct WindowIcon {
9    platform_impl: Rc<dyn IWindowIconImpl>,
10}
11
12impl WindowIcon {
13    fn loader() -> Rc<dyn IPlatformIconLoader> {
14        FerroLocator::current().get_required_service::<dyn IPlatformIconLoader>()
15    }
16
17    /// Creates an icon from a bitmap.
18    ///
19    /// # Panics
20    /// Panics when no platform icon loader is registered.
21    pub fn from_bitmap(bitmap: &Bitmap) -> Rc<WindowIcon> {
22        Rc::new(WindowIcon { platform_impl: Self::loader().load_icon_from_bitmap(bitmap.platform_impl().item()) })
23    }
24
25    /// Loads an icon from a file.
26    ///
27    /// # Panics
28    /// Panics when no platform icon loader is registered.
29    pub fn from_file(file_name: &str) -> io::Result<Rc<WindowIcon>> {
30        Ok(Rc::new(WindowIcon { platform_impl: Self::loader().load_icon_from_file(file_name)? }))
31    }
32
33    /// Loads an icon from a stream.
34    ///
35    /// # Panics
36    /// Panics when no platform icon loader is registered.
37    pub fn from_stream(stream: &mut dyn io::Read) -> io::Result<Rc<WindowIcon>> {
38        Ok(Rc::new(WindowIcon { platform_impl: Self::loader().load_icon_from_stream(stream)? }))
39    }
40
41    /// The platform implementation of the icon (internal upstream: for the
42    /// windowing classes).
43    pub fn platform_impl(&self) -> Rc<dyn IWindowIconImpl> {
44        self.platform_impl.clone()
45    }
46
47    /// Writes the icon to a stream.
48    pub fn save(&self, stream: &mut dyn io::Write) -> io::Result<()> {
49        self.platform_impl.save(stream)
50    }
51}
52
53/// Icons are compared by identity, like the references of the reference
54/// implementation.
55impl PartialEq for WindowIcon {
56    fn eq(&self, other: &Self) -> bool {
57        std::ptr::eq(self, other)
58    }
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use ferroui_base::platform::IBitmapImpl;
65    use std::cell::RefCell;
66
67    struct TestIconImpl(Vec<u8>);
68
69    impl IWindowIconImpl for TestIconImpl {
70        fn save(&self, output_stream: &mut dyn io::Write) -> io::Result<()> {
71            output_stream.write_all(&self.0)
72        }
73    }
74
75    #[derive(Default)]
76    struct TestIconLoader {
77        files: RefCell<Vec<String>>,
78    }
79
80    impl IPlatformIconLoader for TestIconLoader {
81        fn load_icon_from_file(&self, file_name: &str) -> io::Result<Rc<dyn IWindowIconImpl>> {
82            self.files.borrow_mut().push(file_name.to_string());
83            Ok(Rc::new(TestIconImpl(file_name.as_bytes().to_vec())))
84        }
85
86        fn load_icon_from_stream(&self, stream: &mut dyn io::Read) -> io::Result<Rc<dyn IWindowIconImpl>> {
87            let mut data = Vec::new();
88            stream.read_to_end(&mut data)?;
89            Ok(Rc::new(TestIconImpl(data)))
90        }
91
92        fn load_icon_from_bitmap(&self, _bitmap: std::sync::Arc<ferroui_base::platform::SharedBitmapImpl>) -> Rc<dyn IWindowIconImpl> {
93            Rc::new(TestIconImpl(b"bitmap".to_vec()))
94        }
95    }
96
97    fn with_loader(test: impl FnOnce(Rc<TestIconLoader>)) {
98        let scope = FerroLocator::enter_scope();
99        let loader = Rc::new(TestIconLoader::default());
100        let service: Rc<dyn IPlatformIconLoader> = loader.clone();
101        FerroLocator::current_mutable().bind::<dyn IPlatformIconLoader>().to_constant(service);
102        test(loader);
103        scope.dispose();
104    }
105
106    #[test]
107    fn icon_from_stream_saves_what_the_loader_produced() {
108        with_loader(|_| {
109            let mut source: &[u8] = b"icon data";
110            let icon = WindowIcon::from_stream(&mut source).unwrap();
111
112            let mut saved = Vec::new();
113            icon.save(&mut saved).unwrap();
114
115            assert_eq!(b"icon data".to_vec(), saved);
116        });
117    }
118
119    #[test]
120    fn icon_from_file_asks_the_loader_for_the_file() {
121        with_loader(|loader| {
122            let icon = WindowIcon::from_file("app.ico").unwrap();
123
124            let mut saved = Vec::new();
125            icon.save(&mut saved).unwrap();
126
127            assert_eq!(vec!["app.ico".to_string()], *loader.files.borrow());
128            assert_eq!(b"app.ico".to_vec(), saved);
129        });
130    }
131
132    #[test]
133    fn icons_are_compared_by_identity() {
134        with_loader(|_| {
135            let a = WindowIcon::from_file("a.ico").unwrap();
136            let b = WindowIcon::from_file("a.ico").unwrap();
137
138            assert!(Some(a.clone()) == Some(a.clone()));
139            assert!(Some(a) != Some(b));
140        });
141    }
142}