Skip to content

Commit 307c2eb

Browse files
authored
feat(core): set macOS app icon in development (#4385)
1 parent c7d13a1 commit 307c2eb

File tree

6 files changed

+97
-6
lines changed

6 files changed

+97
-6
lines changed

.changes/dev-dock-icon.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"tauri-codegen": patch
3+
"tauri-macros": patch
4+
"tauri": patch
5+
---
6+
7+
Set the application icon in development mode on macOS.

core/tauri-codegen/src/context.rs

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -192,21 +192,23 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
192192
// handle default window icons for Windows targets
193193
#[cfg(windows)]
194194
let default_window_icon = {
195-
let mut icon_path = find_icon(
195+
let icon_path = find_icon(
196196
&config,
197197
&config_parent,
198198
|i| i.ends_with(".ico"),
199199
"icons/icon.ico",
200200
);
201-
if !icon_path.exists() {
202-
icon_path = find_icon(
201+
if icon_path.exists() {
202+
ico_icon(&root, &out_dir, icon_path)?
203+
} else {
204+
let icon_path = find_icon(
203205
&config,
204206
&config_parent,
205207
|i| i.ends_with(".png"),
206208
"icons/icon.png",
207209
);
210+
png_icon(&root, &out_dir, icon_path)?
208211
}
209-
ico_icon(&root, &out_dir, icon_path)?
210212
};
211213
#[cfg(target_os = "linux")]
212214
let default_window_icon = {
@@ -221,6 +223,29 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
221223
#[cfg(not(any(windows, target_os = "linux")))]
222224
let default_window_icon = quote!(None);
223225

226+
#[cfg(target_os = "macos")]
227+
let app_icon = if dev {
228+
let mut icon_path = find_icon(
229+
&config,
230+
&config_parent,
231+
|i| i.ends_with(".icns"),
232+
"icons/icon.png",
233+
);
234+
if !icon_path.exists() {
235+
icon_path = find_icon(
236+
&config,
237+
&config_parent,
238+
|i| i.ends_with(".png"),
239+
"icons/icon.png",
240+
);
241+
}
242+
raw_icon(&out_dir, icon_path)?
243+
} else {
244+
quote!(None)
245+
};
246+
#[cfg(not(target_os = "macos"))]
247+
let app_icon = quote!(None);
248+
224249
let package_name = if let Some(product_name) = &config.package.product_name {
225250
quote!(#product_name.to_string())
226251
} else {
@@ -353,6 +378,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
353378
#config,
354379
::std::sync::Arc::new(#assets),
355380
#default_window_icon,
381+
#app_icon,
356382
#system_tray_icon,
357383
#package_info,
358384
#info_plist,
@@ -403,6 +429,35 @@ fn ico_icon<P: AsRef<Path>>(
403429
Ok(icon)
404430
}
405431

432+
#[cfg(target_os = "macos")]
433+
fn raw_icon<P: AsRef<Path>>(out_dir: &Path, path: P) -> Result<TokenStream, EmbeddedAssetsError> {
434+
use std::fs::File;
435+
use std::io::Write;
436+
437+
let path = path.as_ref();
438+
let bytes = std::fs::read(&path)
439+
.unwrap_or_else(|_| panic!("failed to read icon {}", path.display()))
440+
.to_vec();
441+
442+
let out_path = out_dir.join(path.file_name().unwrap());
443+
let mut out_file = File::create(&out_path).map_err(|error| EmbeddedAssetsError::AssetWrite {
444+
path: out_path.clone(),
445+
error,
446+
})?;
447+
448+
out_file
449+
.write_all(&bytes)
450+
.map_err(|error| EmbeddedAssetsError::AssetWrite {
451+
path: path.to_owned(),
452+
error,
453+
})?;
454+
455+
let out_path = out_path.display().to_string();
456+
457+
let icon = quote!(Some(include_bytes!(#out_path).to_vec()));
458+
Ok(icon)
459+
}
460+
406461
fn png_icon<P: AsRef<Path>>(
407462
root: &TokenStream,
408463
out_dir: &Path,
@@ -445,7 +500,6 @@ fn png_icon<P: AsRef<Path>>(
445500
Ok(icon)
446501
}
447502

448-
#[cfg(any(windows, target_os = "linux"))]
449503
fn find_icon<F: Fn(&&String) -> bool>(
450504
config: &Config,
451505
config_parent: &Path,

core/tauri/src/app.rs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1490,7 +1490,29 @@ fn on_event_loop_event<R: Runtime, F: FnMut(&AppHandle<R>, RunEvent) + 'static>(
14901490
label,
14911491
event: event.into(),
14921492
},
1493-
RuntimeRunEvent::Ready => RunEvent::Ready,
1493+
RuntimeRunEvent::Ready => {
1494+
// set the app icon in development
1495+
#[cfg(all(dev, target_os = "macos"))]
1496+
unsafe {
1497+
use cocoa::{
1498+
appkit::NSImage,
1499+
base::{id, nil},
1500+
foundation::NSData,
1501+
};
1502+
use objc::*;
1503+
if let Some(icon) = app_handle.manager.inner.app_icon.clone() {
1504+
let ns_app: id = msg_send![class!(NSApplication), sharedApplication];
1505+
let data = NSData::dataWithBytes_length_(
1506+
nil,
1507+
icon.as_ptr() as *const std::os::raw::c_void,
1508+
icon.len() as u64,
1509+
);
1510+
let app_icon = NSImage::initWithData_(NSImage::alloc(nil), data);
1511+
let _: () = msg_send![ns_app, setApplicationIconImage: app_icon];
1512+
}
1513+
}
1514+
RunEvent::Ready
1515+
}
14941516
RuntimeRunEvent::Resumed => RunEvent::Resumed,
14951517
RuntimeRunEvent::MainEventsCleared => RunEvent::MainEventsCleared,
14961518
RuntimeRunEvent::UserEvent(t) => t.into(),

core/tauri/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ pub struct Context<A: Assets> {
445445
pub(crate) config: Config,
446446
pub(crate) assets: Arc<A>,
447447
pub(crate) default_window_icon: Option<Icon>,
448+
pub(crate) app_icon: Option<Vec<u8>>,
448449
pub(crate) system_tray_icon: Option<Icon>,
449450
pub(crate) package_info: PackageInfo,
450451
pub(crate) _info_plist: (),
@@ -458,6 +459,7 @@ impl<A: Assets> fmt::Debug for Context<A> {
458459
let mut d = f.debug_struct("Context");
459460
d.field("config", &self.config)
460461
.field("default_window_icon", &self.default_window_icon)
462+
.field("app_icon", &self.app_icon)
461463
.field("system_tray_icon", &self.system_tray_icon)
462464
.field("package_info", &self.package_info)
463465
.field("pattern", &self.pattern);
@@ -548,6 +550,7 @@ impl<A: Assets> Context<A> {
548550
config: Config,
549551
assets: Arc<A>,
550552
default_window_icon: Option<Icon>,
553+
app_icon: Option<Vec<u8>>,
551554
system_tray_icon: Option<Icon>,
552555
package_info: PackageInfo,
553556
info_plist: (),
@@ -558,6 +561,7 @@ impl<A: Assets> Context<A> {
558561
config,
559562
assets,
560563
default_window_icon,
564+
app_icon,
561565
system_tray_icon,
562566
package_info,
563567
_info_plist: info_plist,

core/tauri/src/manager.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ pub struct InnerWindowManager<R: Runtime> {
206206
config: Arc<Config>,
207207
assets: Arc<dyn Assets>,
208208
default_window_icon: Option<Icon>,
209+
pub(crate) app_icon: Option<Vec<u8>>,
209210

210211
package_info: PackageInfo,
211212
/// The webview protocols protocols available to all windows.
@@ -231,6 +232,7 @@ impl<R: Runtime> fmt::Debug for InnerWindowManager<R> {
231232
.field("state", &self.state)
232233
.field("config", &self.config)
233234
.field("default_window_icon", &self.default_window_icon)
235+
.field("app_icon", &self.app_icon)
234236
.field("package_info", &self.package_info)
235237
.field("menu", &self.menu)
236238
.field("pattern", &self.pattern)
@@ -303,6 +305,7 @@ impl<R: Runtime> WindowManager<R> {
303305
config: Arc::new(context.config),
304306
assets: context.assets,
305307
default_window_icon: context.default_window_icon,
308+
app_icon: context.app_icon,
306309
package_info: context.package_info,
307310
pattern: context.pattern,
308311
uri_scheme_protocols,

core/tauri/src/test/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub fn mock_context<A: Assets>(assets: A) -> crate::Context<A> {
6767
},
6868
assets: Arc::new(assets),
6969
default_window_icon: None,
70+
app_icon: None,
7071
system_tray_icon: None,
7172
package_info: crate::PackageInfo {
7273
name: "test".into(),

0 commit comments

Comments
 (0)