ferroui_controls/
window_icon.rs1use crate::platform::{IPlatformIconLoader, IWindowIconImpl};
2use ferroui_base::media::imaging::Bitmap;
3use ferroui_base::{FerroLocator, LocatorExtensions};
4use std::io;
5use std::rc::Rc;
6
7pub 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 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 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 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 pub fn platform_impl(&self) -> Rc<dyn IWindowIconImpl> {
44 self.platform_impl.clone()
45 }
46
47 pub fn save(&self, stream: &mut dyn io::Write) -> io::Result<()> {
49 self.platform_impl.save(stream)
50 }
51}
52
53impl 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}