Skip to content

Commit 7a4044b

Browse files
[macos] Add support for setting icon & template at same time
Calling set_icon and then set_icon_as_template in sequence cause a flicker as they both run on the main thread and update the UI. This exposes a single function to do both at once, preventing the flicker.
1 parent 67c7418 commit 7a4044b

File tree

7 files changed

+106
-1
lines changed

7 files changed

+106
-1
lines changed

.changes/changed.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'tauri': 'patch:feat'
3+
---
4+
5+
Add macos support for setting the icon and icon template state in the same step of the main thread, to prevent flickering.

crates/tauri/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ const PLUGINS: &[(&str, &[(&str, bool)])] = &[
219219
("set_visible", true),
220220
("set_temp_dir_path", true),
221221
("set_icon_as_template", true),
222+
("set_icon_with_as_template", true),
222223
("set_show_menu_on_left_click", true),
223224
],
224225
),

crates/tauri/permissions/tray/autogenerated/reference.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ Default permissions for the plugin, which enables all commands.
1414
- `allow-set-visible`
1515
- `allow-set-temp-dir-path`
1616
- `allow-set-icon-as-template`
17+
- `allow-set-icon-with-as-template`
1718
- `allow-set-show-menu-on-left-click`
1819

1920
## Permission Table
@@ -158,6 +159,32 @@ Denies the set_icon_as_template command without any pre-configured scope.
158159
<tr>
159160
<td>
160161

162+
`core:tray:allow-set-icon-with-as-template`
163+
164+
</td>
165+
<td>
166+
167+
Enables the set_icon_with_as_template command without any pre-configured scope.
168+
169+
</td>
170+
</tr>
171+
172+
<tr>
173+
<td>
174+
175+
`core:tray:deny-set-icon-with-as-template`
176+
177+
</td>
178+
<td>
179+
180+
Denies the set_icon_with_as_template command without any pre-configured scope.
181+
182+
</td>
183+
</tr>
184+
185+
<tr>
186+
<td>
187+
161188
`core:tray:allow-set-menu`
162189

163190
</td>

crates/tauri/scripts/bundle.global.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/tauri/src/tray/mod.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -576,6 +576,38 @@ impl<R: Runtime> TrayIcon<R> {
576576
Ok(())
577577
}
578578

579+
/// Sets the tray icon and template status atomically. **macOS only**.
580+
///
581+
/// On macOS, calling `set_icon` followed by `set_icon_as_template` causes a visible
582+
/// flicker as the icon is rendered twice. This method sets both atomically to prevent that.
583+
///
584+
/// ## Platform-specific:
585+
///
586+
/// - **Linux / Windows:** Falls back to calling `set_icon`.
587+
pub fn set_icon_with_as_template(
588+
&self,
589+
icon: Option<Image<'_>>,
590+
#[allow(unused)] is_template: bool,
591+
) -> crate::Result<()> {
592+
#[cfg(target_os = "macos")]
593+
{
594+
let tray_icon = match icon {
595+
Some(i) => Some(i.try_into()?),
596+
None => None,
597+
};
598+
run_item_main_thread!(self, |self_: Self| {
599+
self_
600+
.inner
601+
.set_icon_with_as_template(tray_icon, is_template)
602+
})??;
603+
}
604+
#[cfg(not(target_os = "macos"))]
605+
{
606+
self.set_icon(icon)?;
607+
}
608+
Ok(())
609+
}
610+
579611
/// Disable or enable showing the tray menu on left click.
580612
///
581613
///

crates/tauri/src/tray/plugin.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,24 @@ fn set_icon_as_template<R: Runtime>(
203203
tray.set_icon_as_template(as_template)
204204
}
205205

206+
#[command(root = "crate")]
207+
fn set_icon_with_as_template<R: Runtime>(
208+
app: AppHandle<R>,
209+
webview: Webview<R>,
210+
rid: ResourceId,
211+
icon: Option<JsImage>,
212+
as_template: bool,
213+
) -> crate::Result<()> {
214+
let resources_table = app.resources_table();
215+
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
216+
let webview_resources_table = webview.resources_table();
217+
let icon = match icon {
218+
Some(i) => Some(i.into_img(&webview_resources_table)?.as_ref().clone()),
219+
None => None,
220+
};
221+
tray.set_icon_with_as_template(icon, as_template)
222+
}
223+
206224
#[command(root = "crate")]
207225
fn set_show_menu_on_left_click<R: Runtime>(
208226
app: AppHandle<R>,
@@ -228,6 +246,7 @@ pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
228246
set_visible,
229247
set_temp_dir_path,
230248
set_icon_as_template,
249+
set_icon_with_as_template,
231250
set_show_menu_on_left_click,
232251
])
233252
.build()

packages/api/src/tray.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,27 @@ export class TrayIcon extends Resource {
296296
})
297297
}
298298

299+
/**
300+
* Sets a new tray icon and template status atomically. **macOS only**.
301+
*
302+
* Note that you may need the `image-ico` or `image-png` Cargo features to use this API.
303+
* To enable it, change your Cargo.toml file:
304+
* ```toml
305+
* [dependencies]
306+
* tauri = { version = "...", features = ["...", "image-png"] }
307+
* ```
308+
*/
309+
async setIconWithAsTemplate(
310+
icon: string | Image | Uint8Array | ArrayBuffer | number[] | null,
311+
asTemplate: boolean
312+
): Promise<void> {
313+
let trayIcon = null
314+
if (icon) {
315+
trayIcon = transformImage(icon)
316+
}
317+
return invoke('plugin:tray|set_icon_with_as_template', { rid: this.rid, icon: trayIcon, asTemplate })
318+
}
319+
299320
/**
300321
* Disable or enable showing the tray menu on left click.
301322
*

0 commit comments

Comments
 (0)