Skip to content

Commit 4ce8e22

Browse files
authored
refactor(core): use Icon for tray icons (#4342)
1 parent 6e16679 commit 4ce8e22

File tree

25 files changed

+451
-330
lines changed

25 files changed

+451
-330
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"tauri-build": patch
3+
---
4+
5+
Do not copy the tray icon to the output directory on Linux since it is embedded in the binary.

.changes/core-remove-tray-icon.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"tauri": patch
3+
"tauri-codegen": patch
4+
---
5+
6+
**Breaking change:** The `TrayIcon` enum has been removed and now `Icon` is used instead.
7+
This allows you to use more image formats and use embedded icons on Linux.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"cli.rs": patch
3+
"cli.js": patch
4+
---
5+
6+
Removed the tray icon from the Debian and AppImage bundles since they are embedded in the binary now.

.changes/runtime-icon-refactor.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri-runtime": patch
3+
"tauri-runtime-wry": patch
4+
---
5+
6+
Removed `TrayIcon` and renamed `WindowIcon` to `Icon`, a shared type for both icons.

core/tauri-build/src/lib.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,12 +266,8 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
266266
)?;
267267
}
268268

269-
#[allow(unused_mut)]
269+
#[allow(unused_mut, clippy::redundant_clone)]
270270
let mut resources = config.tauri.bundle.resources.clone().unwrap_or_default();
271-
#[cfg(target_os = "linux")]
272-
if let Some(tray) = config.tauri.system_tray {
273-
resources.push(tray.icon_path.display().to_string());
274-
}
275271
#[cfg(windows)]
276272
if let Some(fixed_webview2_runtime_path) = &config.tauri.bundle.windows.webview_fixed_runtime_path
277273
{

core/tauri-codegen/Cargo.toml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,7 @@ brotli = { version = "3", optional = true, default-features = false, features =
2626
regex = { version = "1.5.6", optional = true }
2727
uuid = { version = "1", features = [ "v4" ] }
2828
semver = "1"
29-
30-
[target."cfg(windows)".dependencies]
3129
ico = "0.1"
32-
33-
[target."cfg(target_os = \"linux\")".dependencies]
3430
png = "0.17"
3531

3632
[features]

core/tauri-codegen/src/context.rs

Lines changed: 25 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
177177
_ => unimplemented!(),
178178
};
179179

180-
#[cfg(any(windows, target_os = "linux"))]
181180
let out_dir = {
182181
let out_dir = std::env::var("OUT_DIR")
183182
.map_err(|_| EmbeddedAssetsError::OutDir)
@@ -193,12 +192,20 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
193192
// handle default window icons for Windows targets
194193
#[cfg(windows)]
195194
let default_window_icon = {
196-
let icon_path = find_icon(
195+
let mut icon_path = find_icon(
197196
&config,
198197
&config_parent,
199198
|i| i.ends_with(".ico"),
200199
"icons/icon.ico",
201200
);
201+
if !icon_path.exists() {
202+
icon_path = find_icon(
203+
&config,
204+
&config_parent,
205+
|i| i.ends_with(".png"),
206+
"icons/icon.png",
207+
);
208+
}
202209
ico_icon(&root, &out_dir, icon_path)?
203210
};
204211
#[cfg(target_os = "linux")]
@@ -234,49 +241,22 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
234241
}
235242
);
236243

237-
#[cfg(target_os = "linux")]
238244
let system_tray_icon = if let Some(tray) = &config.tauri.system_tray {
239-
let mut system_tray_icon_path = tray.icon_path.clone();
240-
system_tray_icon_path.set_extension("png");
241-
if dev {
242-
let system_tray_icon_path = config_parent
243-
.join(system_tray_icon_path)
244-
.display()
245-
.to_string();
246-
quote!(Some(#root::TrayIcon::File(::std::path::PathBuf::from(#system_tray_icon_path))))
245+
let system_tray_icon_path = tray.icon_path.clone();
246+
let ext = system_tray_icon_path.extension();
247+
if ext.map_or(false, |e| e == "ico") {
248+
ico_icon(&root, &out_dir, system_tray_icon_path)?
249+
} else if ext.map_or(false, |e| e == "png") {
250+
png_icon(&root, &out_dir, system_tray_icon_path)?
247251
} else {
248-
let system_tray_icon_file_path = system_tray_icon_path.to_string_lossy().to_string();
249-
quote!(
250-
Some(
251-
#root::TrayIcon::File(
252-
#root::api::path::resolve_path(
253-
&#config,
254-
&#package_info,
255-
&Default::default(),
256-
#system_tray_icon_file_path,
257-
Some(#root::api::path::BaseDirectory::Resource)
258-
).expect("failed to resolve resource dir")
259-
)
260-
)
261-
)
252+
quote!(compile_error!(
253+
"The tray icon extension must be either `.ico` or `.png`."
254+
))
262255
}
263256
} else {
264257
quote!(None)
265258
};
266259

267-
#[cfg(not(target_os = "linux"))]
268-
let system_tray_icon = if let Some(tray) = &config.tauri.system_tray {
269-
let mut system_tray_icon_path = tray.icon_path.clone();
270-
system_tray_icon_path.set_extension(if cfg!(windows) { "ico" } else { "png" });
271-
let system_tray_icon_path = config_parent
272-
.join(system_tray_icon_path)
273-
.display()
274-
.to_string();
275-
quote!(Some(#root::TrayIcon::Raw(include_bytes!(#system_tray_icon_path).to_vec())))
276-
} else {
277-
quote!(None)
278-
};
279-
280260
#[cfg(target_os = "macos")]
281261
let info_plist = {
282262
if dev {
@@ -367,7 +347,6 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
367347
)))
368348
}
369349

370-
#[cfg(windows)]
371350
fn ico_icon<P: AsRef<Path>>(
372351
root: &TokenStream,
373352
out_dir: &Path,
@@ -378,14 +357,14 @@ fn ico_icon<P: AsRef<Path>>(
378357

379358
let path = path.as_ref();
380359
let bytes = std::fs::read(&path)
381-
.unwrap_or_else(|_| panic!("failed to read window icon {}", path.display()))
360+
.unwrap_or_else(|_| panic!("failed to read icon {}", path.display()))
382361
.to_vec();
383362
let icon_dir = ico::IconDir::read(std::io::Cursor::new(bytes))
384-
.unwrap_or_else(|_| panic!("failed to parse window icon {}", path.display()));
363+
.unwrap_or_else(|_| panic!("failed to parse icon {}", path.display()));
385364
let entry = &icon_dir.entries()[0];
386365
let rgba = entry
387366
.decode()
388-
.unwrap_or_else(|_| panic!("failed to decode window icon {}", path.display()))
367+
.unwrap_or_else(|_| panic!("failed to decode icon {}", path.display()))
389368
.rgba_data()
390369
.to_vec();
391370
let width = entry.width();
@@ -410,7 +389,6 @@ fn ico_icon<P: AsRef<Path>>(
410389
Ok(icon)
411390
}
412391

413-
#[cfg(target_os = "linux")]
414392
fn png_icon<P: AsRef<Path>>(
415393
root: &TokenStream,
416394
out_dir: &Path,
@@ -421,12 +399,12 @@ fn png_icon<P: AsRef<Path>>(
421399

422400
let path = path.as_ref();
423401
let bytes = std::fs::read(&path)
424-
.unwrap_or_else(|_| panic!("failed to read window icon {}", path.display()))
402+
.unwrap_or_else(|_| panic!("failed to read icon {}", path.display()))
425403
.to_vec();
426404
let decoder = png::Decoder::new(std::io::Cursor::new(bytes));
427405
let mut reader = decoder
428406
.read_info()
429-
.unwrap_or_else(|_| panic!("failed to read window icon {}", path.display()));
407+
.unwrap_or_else(|_| panic!("failed to read icon {}", path.display()));
430408
let mut buffer: Vec<u8> = Vec::new();
431409
while let Ok(Some(row)) = reader.next_row() {
432410
buffer.extend(row.data());
@@ -459,7 +437,7 @@ fn find_icon<F: Fn(&&String) -> bool>(
459437
config_parent: &Path,
460438
predicate: F,
461439
default: &str,
462-
) -> String {
440+
) -> PathBuf {
463441
let icon_path = config
464442
.tauri
465443
.bundle
@@ -468,7 +446,7 @@ fn find_icon<F: Fn(&&String) -> bool>(
468446
.find(|i| predicate(i))
469447
.cloned()
470448
.unwrap_or_else(|| default.to_string());
471-
config_parent.join(icon_path).display().to_string()
449+
config_parent.join(icon_path)
472450
}
473451

474452
#[cfg(feature = "shell-scope")]

core/tauri-runtime-wry/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ exclude = [ ".license_template", "CHANGELOG.md", "/target" ]
1313
readme = "README.md"
1414

1515
[dependencies]
16-
wry = { version = "0.18.1", default-features = false, features = [ "file-drop", "protocol" ] }
16+
wry = { version = "0.18.3", default-features = false, features = [ "file-drop", "protocol" ] }
1717
tauri-runtime = { version = "0.7.0", path = "../tauri-runtime" }
1818
tauri-utils = { version = "1.0.0-rc.9", path = "../tauri-utils" }
1919
uuid = { version = "1", features = [ "v4" ] }

core/tauri-runtime-wry/src/lib.rs

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use tauri_runtime::{
1616
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size},
1717
CursorIcon, DetachedWindow, FileDropEvent, JsEventListenerKey, PendingWindow, WindowEvent,
1818
},
19-
Dispatch, Error, EventLoopProxy, ExitRequestedEventAction, Result, RunEvent, RunIteration,
20-
Runtime, RuntimeHandle, UserAttentionType, UserEvent, WindowIcon,
19+
Dispatch, Error, EventLoopProxy, ExitRequestedEventAction, Icon, Result, RunEvent, RunIteration,
20+
Runtime, RuntimeHandle, UserAttentionType, UserEvent,
2121
};
2222

2323
use tauri_runtime::window::MenuEvent;
@@ -486,9 +486,9 @@ fn icon_err<E: std::error::Error + Send + Sync + 'static>(e: E) -> Error {
486486
Error::InvalidIcon(Box::new(e))
487487
}
488488

489-
impl TryFrom<WindowIcon> for WryIcon {
489+
impl TryFrom<Icon> for WryIcon {
490490
type Error = Error;
491-
fn try_from(icon: WindowIcon) -> std::result::Result<Self, Self::Error> {
491+
fn try_from(icon: Icon) -> std::result::Result<Self, Self::Error> {
492492
WryWindowIcon::from_rgba(icon.rgba, icon.width, icon.height)
493493
.map(Self)
494494
.map_err(icon_err)
@@ -885,7 +885,7 @@ impl WindowBuilder for WindowBuilderWrapper {
885885
self
886886
}
887887

888-
fn icon(mut self, icon: WindowIcon) -> Result<Self> {
888+
fn icon(mut self, icon: Icon) -> Result<Self> {
889889
self.inner = self
890890
.inner
891891
.with_window_icon(Some(WryIcon::try_from(icon)?.0));
@@ -1092,7 +1092,7 @@ pub enum WebviewEvent {
10921092
pub enum TrayMessage {
10931093
UpdateItem(u16, MenuUpdate),
10941094
UpdateMenu(SystemTrayMenu),
1095-
UpdateIcon(TrayIcon),
1095+
UpdateIcon(Icon),
10961096
#[cfg(target_os = "macos")]
10971097
UpdateIconAsTemplate(bool),
10981098
Close,
@@ -1480,7 +1480,7 @@ impl<T: UserEvent> Dispatch<T> for WryDispatcher<T> {
14801480
)
14811481
}
14821482

1483-
fn set_icon(&self, icon: WindowIcon) -> Result<()> {
1483+
fn set_icon(&self, icon: Icon) -> Result<()> {
14841484
send_user_message(
14851485
&self.context,
14861486
Message::Window(
@@ -1953,33 +1953,26 @@ impl<T: UserEvent> Runtime<T> for Wry<T> {
19531953

19541954
#[cfg(feature = "system-tray")]
19551955
fn system_tray(&self, system_tray: SystemTray) -> Result<Self::TrayHandler> {
1956-
let icon = system_tray
1957-
.icon
1958-
.expect("tray icon not set")
1959-
.into_platform_icon();
1956+
let icon = TrayIcon::try_from(system_tray.icon.expect("tray icon not set"))?;
19601957

19611958
let mut items = HashMap::new();
19621959

1963-
#[cfg(target_os = "macos")]
1964-
let tray = SystemTrayBuilder::new(
1965-
icon,
1960+
#[allow(unused_mut)]
1961+
let mut tray_builder = SystemTrayBuilder::new(
1962+
icon.0,
19661963
system_tray
19671964
.menu
19681965
.map(|menu| to_wry_context_menu(&mut items, menu)),
1969-
)
1970-
.with_icon_as_template(system_tray.icon_as_template)
1971-
.build(&self.event_loop)
1972-
.map_err(|e| Error::SystemTray(Box::new(e)))?;
1966+
);
19731967

1974-
#[cfg(not(target_os = "macos"))]
1975-
let tray = SystemTrayBuilder::new(
1976-
icon,
1977-
system_tray
1978-
.menu
1979-
.map(|menu| to_wry_context_menu(&mut items, menu)),
1980-
)
1981-
.build(&self.event_loop)
1982-
.map_err(|e| Error::SystemTray(Box::new(e)))?;
1968+
#[cfg(target_os = "macos")]
1969+
{
1970+
tray_builder = tray_builder.with_icon_as_template(system_tray.icon_as_template);
1971+
}
1972+
1973+
let tray = tray_builder
1974+
.build(&self.event_loop)
1975+
.map_err(|e| Error::SystemTray(Box::new(e)))?;
19831976

19841977
*self.tray_context.items.lock().unwrap() = items;
19851978
*self.tray_context.tray.lock().unwrap() = Some(Arc::new(Mutex::new(tray)));
@@ -2530,7 +2523,9 @@ fn handle_user_message<T: UserEvent>(
25302523
}
25312524
TrayMessage::UpdateIcon(icon) => {
25322525
if let Some(tray) = &*tray_context.tray.lock().unwrap() {
2533-
tray.lock().unwrap().set_icon(icon.into_platform_icon());
2526+
if let Ok(icon) = TrayIcon::try_from(icon) {
2527+
tray.lock().unwrap().set_icon(icon.0);
2528+
}
25342529
}
25352530
}
25362531
#[cfg(target_os = "macos")]

core/tauri-runtime-wry/src/system_tray.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ pub use tauri_runtime::{
77
Menu, MenuEntry, MenuItem, MenuUpdate, Submenu, SystemTrayMenu, SystemTrayMenuEntry,
88
SystemTrayMenuItem, TrayHandle,
99
},
10-
SystemTrayEvent, TrayIcon,
10+
Icon, SystemTrayEvent,
1111
};
1212
pub use wry::application::{
1313
event::TrayEvent,
1414
event_loop::EventLoopProxy,
1515
menu::{
1616
ContextMenu as WryContextMenu, CustomMenuItem as WryCustomMenuItem, MenuItem as WryMenuItem,
1717
},
18+
system_tray::Icon as WryTrayIcon,
1819
};
1920

2021
#[cfg(target_os = "macos")]
@@ -35,13 +36,25 @@ pub type SystemTrayEventHandler = Box<dyn Fn(&SystemTrayEvent) + Send>;
3536
pub type SystemTrayEventListeners = Arc<Mutex<HashMap<Uuid, Arc<SystemTrayEventHandler>>>>;
3637
pub type SystemTrayItems = Arc<Mutex<HashMap<u16, WryCustomMenuItem>>>;
3738

39+
/// Wrapper around a [`wry::application::system_tray::Icon`] that can be created from an [`WindowIcon`].
40+
pub struct TrayIcon(pub(crate) WryTrayIcon);
41+
42+
impl TryFrom<Icon> for TrayIcon {
43+
type Error = Error;
44+
fn try_from(icon: Icon) -> std::result::Result<Self, Self::Error> {
45+
WryTrayIcon::from_rgba(icon.rgba, icon.width, icon.height)
46+
.map(Self)
47+
.map_err(crate::icon_err)
48+
}
49+
}
50+
3851
#[derive(Debug, Clone)]
3952
pub struct SystemTrayHandle<T: UserEvent> {
4053
pub(crate) proxy: EventLoopProxy<super::Message<T>>,
4154
}
4255

4356
impl<T: UserEvent> TrayHandle for SystemTrayHandle<T> {
44-
fn set_icon(&self, icon: TrayIcon) -> Result<()> {
57+
fn set_icon(&self, icon: Icon) -> Result<()> {
4558
self
4659
.proxy
4760
.send_event(Message::Tray(TrayMessage::UpdateIcon(icon)))

0 commit comments

Comments
 (0)