Skip to main content

dioxus_desktop/
default_icon.rs

1use anyhow::Result;
2use image::load_from_memory;
3use image::GenericImageView;
4use image::ImageReader;
5use std::path::Path;
6
7/// Pre-decoded RGBA bytes of the bundled fallback icon.
8const FALLBACK_ICON_RGBA: &[u8] = include_bytes!("./assets/default_icon.bin");
9const FALLBACK_ICON_WIDTH: u32 = 460;
10const FALLBACK_ICON_HEIGHT: u32 = 460;
11
12/// Trait that creates icons for various types
13pub trait DioxusIconTrait {
14    fn get_icon() -> Result<Self>
15    where
16        Self: Sized;
17    fn from_memory(value: &[u8]) -> Result<Self>
18    where
19        Self: Sized;
20    fn path<P: AsRef<Path>>(path: P, size: Option<(u32, u32)>) -> Result<Self>
21    where
22        Self: Sized;
23}
24
25#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
26use crate::trayicon::DioxusTrayIcon;
27
28fn load_image_from_memory(value: &[u8]) -> Result<(Vec<u8>, u32, u32)> {
29    let img = load_from_memory(value)?;
30    let rgba = img.to_rgba8();
31    let (width, height) = img.dimensions();
32    Ok((rgba.to_vec(), width, height))
33}
34
35fn load_image_from_path<P: AsRef<Path>>(path: P) -> Result<(Vec<u8>, u32, u32)> {
36    let img = ImageReader::open(path)?.decode()?;
37    let rgba = img.to_rgba8();
38    let (width, height) = img.dimensions();
39    Ok((rgba.to_vec(), width, height))
40}
41
42#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
43impl DioxusIconTrait for DioxusTrayIcon {
44    fn get_icon() -> Result<Self>
45    where
46        Self: Sized,
47    {
48        #[cfg(target_os = "windows")]
49        if let Ok(icon) = DioxusTrayIcon::from_resource(32512, None) {
50            return Ok(icon);
51        }
52        DioxusTrayIcon::from_rgba(
53            FALLBACK_ICON_RGBA.to_vec(),
54            FALLBACK_ICON_WIDTH,
55            FALLBACK_ICON_HEIGHT,
56        )
57        .map_err(Into::into)
58    }
59
60    fn from_memory(value: &[u8]) -> Result<Self>
61    where
62        Self: Sized,
63    {
64        let (icon, width, height) = load_image_from_memory(value)?;
65        DioxusTrayIcon::from_rgba(icon, width, height).map_err(Into::into)
66    }
67
68    fn path<P: AsRef<Path>>(path: P, size: Option<(u32, u32)>) -> Result<Self>
69    where
70        Self: Sized,
71    {
72        let (img, width, height) = load_image_from_path(path)?;
73        if let Some((width, height)) = size {
74            Ok(DioxusTrayIcon::from_rgba(img, width, height)?)
75        } else {
76            Ok(DioxusTrayIcon::from_rgba(img, width, height)?)
77        }
78    }
79}
80
81#[cfg(not(any(target_os = "ios", target_os = "android")))]
82use crate::menubar::DioxusMenuIcon;
83
84#[cfg(not(any(target_os = "ios", target_os = "android")))]
85impl DioxusIconTrait for DioxusMenuIcon {
86    fn get_icon() -> Result<Self>
87    where
88        Self: Sized,
89    {
90        #[cfg(target_os = "windows")]
91        if let Ok(icon) = DioxusMenuIcon::from_resource(32512, None) {
92            return Ok(icon);
93        }
94        DioxusMenuIcon::from_rgba(
95            FALLBACK_ICON_RGBA.to_vec(),
96            FALLBACK_ICON_WIDTH,
97            FALLBACK_ICON_HEIGHT,
98        )
99        .map_err(Into::into)
100    }
101
102    fn from_memory(value: &[u8]) -> Result<Self>
103    where
104        Self: Sized,
105    {
106        let (icon, width, height) = load_image_from_memory(value)?;
107        DioxusMenuIcon::from_rgba(icon, width, height).map_err(Into::into)
108    }
109
110    fn path<P: AsRef<Path>>(path: P, size: Option<(u32, u32)>) -> Result<Self>
111    where
112        Self: Sized,
113    {
114        let (img, width, height) = load_image_from_path(path)?;
115        if let Some((width, height)) = size {
116            Ok(DioxusMenuIcon::from_rgba(img, width, height)?)
117        } else {
118            Ok(DioxusMenuIcon::from_rgba(img, width, height)?)
119        }
120    }
121}
122
123use tao::window::Icon;
124
125#[cfg(target_os = "windows")]
126use tao::platform::windows::IconExtWindows;
127
128impl DioxusIconTrait for Icon {
129    fn get_icon() -> Result<Self>
130    where
131        Self: Sized,
132    {
133        #[cfg(target_os = "windows")]
134        if let Ok(icon) = Icon::from_resource(32512, None) {
135            return Ok(icon);
136        }
137        Icon::from_rgba(
138            FALLBACK_ICON_RGBA.to_vec(),
139            FALLBACK_ICON_WIDTH,
140            FALLBACK_ICON_HEIGHT,
141        )
142        .map_err(Into::into)
143    }
144
145    fn from_memory(value: &[u8]) -> Result<Self>
146    where
147        Self: Sized,
148    {
149        let (icon, width, height) = load_image_from_memory(value)?;
150        Icon::from_rgba(icon, width, height).map_err(Into::into)
151    }
152
153    fn path<P: AsRef<Path>>(path: P, size: Option<(u32, u32)>) -> Result<Self>
154    where
155        Self: Sized,
156    {
157        let (img, width, height) = load_image_from_path(path)?;
158        if let Some((width, height)) = size {
159            Ok(Icon::from_rgba(img, width, height)?)
160        } else {
161            Ok(Icon::from_rgba(img, width, height)?)
162        }
163    }
164}
165
166/// Provides the default icon of the app.
167///
168/// On Windows this prefers the icon embedded as resource id `IDI::APPLICATION`
169/// (32512) by `dx`'s bundler, falling back to a generic Dioxus icon when the
170/// resource is missing (e.g. when running with plain `cargo run`). On all
171/// other platforms the bundled fallback icon is returned directly.
172pub fn default_icon<T: DioxusIconTrait>() -> Result<T> {
173    T::get_icon()
174}
175
176/// Helper function to load image from include_bytes!("image.png")
177pub fn icon_from_memory<T: DioxusIconTrait>(value: &[u8]) -> Result<T> {
178    T::from_memory(value)
179}
180
181/// Helper function to load image from path
182pub fn icon_from_path<T: DioxusIconTrait, P: AsRef<Path>>(
183    path: P,
184    size: Option<(u32, u32)>,
185) -> Result<T> {
186    T::path(path, size)
187}