Skip to content

Commit fff39b2

Browse files
committed
default icon
1 parent 348208d commit fff39b2

7 files changed

Lines changed: 201 additions & 20 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/desktop/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ dioxus-asset-resolver = { workspace = true, features = ["native"] }
2121
generational-box = { workspace = true }
2222
dioxus-devtools = { workspace = true, optional = true }
2323

24+
anyhow = { workspace = true }
25+
image = { workspace = true }
2426
serde = { workspace = true }
2527
serde_json = { workspace = true }
2628
thiserror = { workspace = true }
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
use anyhow::Result;
2+
use image::load_from_memory;
3+
use image::GenericImageView;
4+
use image::ImageReader;
5+
use std::path::Path;
6+
7+
/// Trait that creates icons for various types
8+
pub trait DioxusIconTrait {
9+
fn get_icon() -> Result<Self>
10+
where
11+
Self: Sized;
12+
fn from_memory(value: &[u8]) -> Result<Self>
13+
where
14+
Self: Sized;
15+
fn path<P: AsRef<Path>>(path: P, size: Option<(u32, u32)>) -> Result<Self>
16+
where
17+
Self: Sized;
18+
}
19+
20+
// preferably this would have platform specific implementations, not just for windows
21+
#[cfg(not(target_os = "windows"))]
22+
static DEFAULT_ICON: &[u8] = include_bytes!("./assets/default_icon.png");
23+
24+
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
25+
use crate::trayicon::DioxusTrayIcon;
26+
27+
fn load_image_from_memory(value: &[u8]) -> Result<(Vec<u8>, u32, u32)> {
28+
let img = load_from_memory(value)?;
29+
let rgba = img.to_rgba8();
30+
let (width, height) = img.dimensions();
31+
Ok((rgba.to_vec(), width, height))
32+
}
33+
34+
fn load_image_from_path<P: AsRef<Path>>(path: P) -> Result<(Vec<u8>, u32, u32)> {
35+
let img = ImageReader::open(path)?.decode()?;
36+
let rgba = img.to_rgba8();
37+
let (width, height) = img.dimensions();
38+
Ok((rgba.to_vec(), width, height))
39+
}
40+
41+
#[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))]
42+
impl DioxusIconTrait for DioxusTrayIcon {
43+
fn get_icon() -> Result<Self>
44+
where
45+
Self: Sized,
46+
{
47+
#[cfg(target_os = "windows")]
48+
#[cfg(any(target_os = "linux", target_os = "macos"))]
49+
{
50+
let (img, width, height) = load_image_from_memory(DEFAULT_ICON)?;
51+
DioxusTrayIcon::from_rgba(img, width, height).map_err(Into::into)
52+
}
53+
#[cfg(target_os = "windows")]
54+
DioxusTrayIcon::from_resource(32512, None).map_err(Into::into)
55+
}
56+
57+
fn from_memory(value: &[u8]) -> Result<Self>
58+
where
59+
Self: Sized,
60+
{
61+
let (icon, width, height) = load_image_from_memory(value)?;
62+
DioxusTrayIcon::from_rgba(icon, width, height).map_err(Into::into)
63+
}
64+
65+
fn path<P: AsRef<Path>>(path: P, size: Option<(u32, u32)>) -> Result<Self>
66+
where
67+
Self: Sized,
68+
{
69+
let (img, width, height) = load_image_from_path(path)?;
70+
if let Some((width, height)) = size {
71+
Ok(DioxusTrayIcon::from_rgba(img, width, height)?)
72+
} else {
73+
Ok(DioxusTrayIcon::from_rgba(img, width, height)?)
74+
}
75+
}
76+
}
77+
78+
#[cfg(not(any(target_os = "ios", target_os = "android")))]
79+
use crate::menubar::DioxusMenuIcon;
80+
81+
#[cfg(not(any(target_os = "ios", target_os = "android")))]
82+
impl DioxusIconTrait for DioxusMenuIcon {
83+
fn get_icon() -> Result<Self>
84+
where
85+
Self: Sized,
86+
{
87+
#[cfg(not(target_os = "windows"))]
88+
{
89+
let (img, width, height) = load_image_from_memory(DEFAULT_ICON)?;
90+
DioxusMenuIcon::from_rgba(img, width, height).map_err(Into::into)
91+
}
92+
#[cfg(target_os = "windows")]
93+
DioxusMenuIcon::from_resource(32512, None).map_err(Into::into)
94+
}
95+
96+
fn from_memory(value: &[u8]) -> Result<Self>
97+
where
98+
Self: Sized,
99+
{
100+
let (icon, width, height) = load_image_from_memory(value)?;
101+
DioxusMenuIcon::from_rgba(icon, width, height).map_err(Into::into)
102+
}
103+
104+
fn path<P: AsRef<Path>>(path: P, size: Option<(u32, u32)>) -> Result<Self>
105+
where
106+
Self: Sized,
107+
{
108+
let (img, width, height) = load_image_from_path(path)?;
109+
if let Some((width, height)) = size {
110+
Ok(DioxusMenuIcon::from_rgba(img, width, height)?)
111+
} else {
112+
Ok(DioxusMenuIcon::from_rgba(img, width, height)?)
113+
}
114+
}
115+
}
116+
117+
use tao::window::Icon;
118+
119+
#[cfg(target_os = "windows")]
120+
use tao::platform::windows::IconExtWindows;
121+
122+
impl DioxusIconTrait for Icon {
123+
fn get_icon() -> Result<Self>
124+
where
125+
Self: Sized,
126+
{
127+
#[cfg(not(target_os = "windows"))]
128+
{
129+
let (img, width, height) = load_image_from_memory(DEFAULT_ICON)?;
130+
Icon::from_rgba(img, width, height).map_err(Into::into)
131+
}
132+
#[cfg(target_os = "windows")]
133+
Icon::from_resource(32512, None).map_err(Into::into)
134+
}
135+
136+
fn from_memory(value: &[u8]) -> Result<Self>
137+
where
138+
Self: Sized,
139+
{
140+
let (icon, width, height) = load_image_from_memory(value)?;
141+
Icon::from_rgba(icon, width, height).map_err(Into::into)
142+
}
143+
144+
fn path<P: AsRef<Path>>(path: P, size: Option<(u32, u32)>) -> Result<Self>
145+
where
146+
Self: Sized,
147+
{
148+
let (img, width, height) = load_image_from_path(path)?;
149+
if let Some((width, height)) = size {
150+
Ok(Icon::from_rgba(img, width, height)?)
151+
} else {
152+
Ok(Icon::from_rgba(img, width, height)?)
153+
}
154+
}
155+
}
156+
157+
/// Provides the default icon of the app
158+
pub fn default_icon<T: DioxusIconTrait>() -> Result<T> {
159+
T::get_icon()
160+
}
161+
162+
/// Helper function to load image from include_bytes!("image.png")
163+
pub fn icon_from_memory<T: DioxusIconTrait>(value: &[u8]) -> Result<T> {
164+
T::from_memory(value)
165+
}
166+
167+
/// Helper function to load image from path
168+
pub fn icon_from_path<T: DioxusIconTrait, P: AsRef<Path>>(
169+
path: P,
170+
size: Option<(u32, u32)>,
171+
) -> Result<T> {
172+
T::path(path, size)
173+
}

packages/desktop/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod android_sync_lock;
99
mod app;
1010
mod assets;
1111
mod config;
12+
mod default_icon;
1213
mod desktop_context;
1314
mod document;
1415
mod edits;
@@ -26,6 +27,8 @@ mod shortcut;
2627
mod waker;
2728
mod webview;
2829

30+
pub use default_icon::{default_icon, icon_from_memory, icon_from_path};
31+
2932
// mobile shortcut is only supported on mobile platforms
3033
#[cfg(any(target_os = "ios", target_os = "android"))]
3134
mod mobile_shortcut;

packages/desktop/src/menubar.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ pub type DioxusMenu = muda::Menu;
55
#[cfg(any(target_os = "ios", target_os = "android"))]
66
pub type DioxusMenu = ();
77

8+
#[cfg(not(any(target_os = "ios", target_os = "android")))]
9+
pub type DioxusMenuIcon = muda::Icon;
10+
#[cfg(any(target_os = "ios", target_os = "android"))]
11+
pub type DioxusMenuIcon = ();
12+
813
/// Initializes the menu bar for the window.
914
#[allow(unused)]
1015
pub fn init_menu_bar(menu: &DioxusMenu, window: &Window) {

packages/desktop/src/trayicon.rs

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,23 @@ pub type DioxusTray = ();
2828
pub fn init_tray_icon(menu: DioxusTrayMenu, icon: Option<DioxusTrayIcon>) -> DioxusTray {
2929
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
3030
{
31-
let builder = tray_icon::TrayIconBuilder::new()
31+
32+
let icon = icon.map(Ok).unwrap_or_else(|| crate::default_icon());
33+
34+
let tray = tray_icon::TrayIconBuilder::new()
3235
.with_menu(Box::new(menu))
33-
.with_menu_on_left_click(false)
34-
.with_icon(match icon {
35-
Some(value) => value,
36-
None => tray_icon::Icon::from_rgba(
37-
include_bytes!("./assets/default_icon.bin").to_vec(),
38-
460,
39-
460,
40-
)
41-
.expect("image parse failed"),
42-
});
36+
.with_menu_on_left_click(false);
37+
38+
let tray = match icon {
39+
Ok(icon) => tray.with_icon(icon),
40+
Err(err) => {
41+
tracing::trace!("No tray icon: {:?}", err);
42+
tray
43+
}
44+
};
45+
46+
provide_context(tray.build().expect("tray icon builder failed"))
4347

44-
provide_context(builder.build().expect("tray icon builder failed"))
4548
}
4649
}
4750

packages/desktop/src/webview.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -234,14 +234,7 @@ impl WebviewInstance {
234234

235235
// We assume that if the icon is None in cfg, then the user just didnt set it
236236
if cfg.window.window.window_icon.is_none() {
237-
window = window.with_window_icon(Some(
238-
tao::window::Icon::from_rgba(
239-
include_bytes!("./assets/default_icon.bin").to_vec(),
240-
460,
241-
460,
242-
)
243-
.expect("image parse failed"),
244-
));
237+
window = window.with_window_icon(crate::default_icon().ok());
245238
}
246239

247240
let window = Arc::new(window.build(&shared.target).unwrap());

0 commit comments

Comments
 (0)