mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-01-31 00:35:19 +01:00
refactor(core): read tray icon only on desktop, refactor Context (#6719)
This commit is contained in:
committed by
GitHub
parent
86488a6ad8
commit
ae102980fc
6
.changes/remove-tray-icon-mobile.md
Normal file
6
.changes/remove-tray-icon-mobile.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"tauri-codegen": patch
|
||||
"tauri": patch
|
||||
---
|
||||
|
||||
Refactor the `Context` conditional fields and only parse the tray icon on desktop.
|
||||
@@ -125,6 +125,16 @@ enum Target {
|
||||
Ios,
|
||||
}
|
||||
|
||||
impl Target {
|
||||
fn is_mobile(&self) -> bool {
|
||||
matches!(self, Target::Android | Target::Ios)
|
||||
}
|
||||
|
||||
fn is_desktop(&self) -> bool {
|
||||
!self.is_mobile()
|
||||
}
|
||||
}
|
||||
|
||||
/// Build a `tauri::Context` for including in application code.
|
||||
pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsError> {
|
||||
let ContextData {
|
||||
@@ -247,7 +257,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
"icons/icon.ico",
|
||||
);
|
||||
if icon_path.exists() {
|
||||
ico_icon(&root, &out_dir, icon_path)?
|
||||
ico_icon(&root, &out_dir, icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
|
||||
} else {
|
||||
let icon_path = find_icon(
|
||||
&config,
|
||||
@@ -255,7 +265,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
|i| i.ends_with(".png"),
|
||||
"icons/icon.png",
|
||||
);
|
||||
png_icon(&root, &out_dir, icon_path)?
|
||||
png_icon(&root, &out_dir, icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
|
||||
}
|
||||
} else if target == Target::Linux {
|
||||
// handle default window icons for Linux targets
|
||||
@@ -265,9 +275,9 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
|i| i.ends_with(".png"),
|
||||
"icons/icon.png",
|
||||
);
|
||||
png_icon(&root, &out_dir, icon_path)?
|
||||
png_icon(&root, &out_dir, icon_path).map(|i| quote!(::std::option::Option::Some(#i)))?
|
||||
} else {
|
||||
quote!(None)
|
||||
quote!(::std::option::Option::None)
|
||||
}
|
||||
};
|
||||
|
||||
@@ -288,7 +298,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
}
|
||||
raw_icon(&out_dir, icon_path)?
|
||||
} else {
|
||||
quote!(None)
|
||||
quote!(::std::option::Option::None)
|
||||
};
|
||||
|
||||
let package_name = if let Some(product_name) = &config.package.product_name {
|
||||
@@ -312,20 +322,26 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
}
|
||||
);
|
||||
|
||||
let system_tray_icon = if let Some(tray) = &config.tauri.system_tray {
|
||||
let system_tray_icon_path = config_parent.join(&tray.icon_path);
|
||||
let ext = system_tray_icon_path.extension();
|
||||
if ext.map_or(false, |e| e == "ico") {
|
||||
ico_icon(&root, &out_dir, system_tray_icon_path)?
|
||||
} else if ext.map_or(false, |e| e == "png") {
|
||||
png_icon(&root, &out_dir, system_tray_icon_path)?
|
||||
let with_system_tray_icon_code = if target.is_desktop() {
|
||||
if let Some(tray) = &config.tauri.system_tray {
|
||||
let system_tray_icon_path = config_parent.join(&tray.icon_path);
|
||||
let ext = system_tray_icon_path.extension();
|
||||
if ext.map_or(false, |e| e == "ico") {
|
||||
ico_icon(&root, &out_dir, system_tray_icon_path)
|
||||
.map(|i| quote!(context.set_system_tray_icon(#i);))?
|
||||
} else if ext.map_or(false, |e| e == "png") {
|
||||
png_icon(&root, &out_dir, system_tray_icon_path)
|
||||
.map(|i| quote!(context.set_system_tray_icon(#i);))?
|
||||
} else {
|
||||
quote!(compile_error!(
|
||||
"The tray icon extension must be either `.ico` or `.png`."
|
||||
))
|
||||
}
|
||||
} else {
|
||||
quote!(compile_error!(
|
||||
"The tray icon extension must be either `.ico` or `.png`."
|
||||
))
|
||||
quote!()
|
||||
}
|
||||
} else {
|
||||
quote!(None)
|
||||
quote!()
|
||||
};
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
@@ -409,50 +425,52 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
|
||||
};
|
||||
|
||||
#[cfg(feature = "shell-scope")]
|
||||
let shell_scope_config = {
|
||||
let with_shell_scope_code = {
|
||||
use regex::Regex;
|
||||
use tauri_utils::config::ShellAllowlistOpen;
|
||||
|
||||
let shell_scopes = get_allowed_clis(&root, &config.tauri.allowlist.shell.scope);
|
||||
|
||||
let shell_scope_open = match &config.tauri.allowlist.shell.open {
|
||||
ShellAllowlistOpen::Flag(false) => quote!(::std::option::Option::None),
|
||||
ShellAllowlistOpen::Flag(true) => {
|
||||
quote!(::std::option::Option::Some(#root::regex::Regex::new(r#"^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+"#).unwrap()))
|
||||
}
|
||||
let shell_scope_constructor = match &config.tauri.allowlist.shell.open {
|
||||
ShellAllowlistOpen::Flag(false) => quote!(#root::ShellScopeConfig::new().skip_validation()),
|
||||
ShellAllowlistOpen::Flag(true) => quote!(#root::ShellScopeConfig::new()),
|
||||
ShellAllowlistOpen::Validate(regex) => match Regex::new(regex) {
|
||||
Ok(_) => quote!(::std::option::Option::Some(#root::regex::Regex::new(#regex).unwrap())),
|
||||
Ok(_) => {
|
||||
quote!(#root::ShellScopeConfig::with_validator(#root::regex::Regex::new(#regex).unwrap()))
|
||||
}
|
||||
Err(error) => {
|
||||
let error = error.to_string();
|
||||
quote!({
|
||||
compile_error!(#error);
|
||||
::std::option::Option::Some(#root::regex::Regex::new(#regex).unwrap())
|
||||
#root::ShellScopeConfig::with_validator(#root::regex::Regex::new(#regex).unwrap())
|
||||
})
|
||||
}
|
||||
},
|
||||
_ => panic!("unknown shell open format, unable to prepare"),
|
||||
};
|
||||
let shell_scope = quote!(#shell_scope_constructor.set_allowed_commands(#shell_scopes));
|
||||
|
||||
quote!(#root::ShellScopeConfig {
|
||||
open: #shell_scope_open,
|
||||
scopes: #shell_scopes
|
||||
})
|
||||
quote!(context.set_shell_scope(#shell_scope);)
|
||||
};
|
||||
|
||||
#[cfg(not(feature = "shell-scope"))]
|
||||
let shell_scope_config = quote!();
|
||||
let with_shell_scope_code = quote!();
|
||||
|
||||
Ok(quote!(#root::Context::new(
|
||||
#config,
|
||||
::std::sync::Arc::new(#assets),
|
||||
#default_window_icon,
|
||||
#app_icon,
|
||||
#system_tray_icon,
|
||||
#package_info,
|
||||
#info_plist,
|
||||
#pattern,
|
||||
#shell_scope_config
|
||||
)))
|
||||
Ok(quote!({
|
||||
#[allow(unused_mut, clippy::let_and_return)]
|
||||
let mut context = #root::Context::new(
|
||||
#config,
|
||||
::std::sync::Arc::new(#assets),
|
||||
#default_window_icon,
|
||||
#app_icon,
|
||||
#package_info,
|
||||
#info_plist,
|
||||
#pattern,
|
||||
);
|
||||
#with_system_tray_icon_code
|
||||
#with_shell_scope_code
|
||||
context
|
||||
}))
|
||||
}
|
||||
|
||||
fn ico_icon<P: AsRef<Path>>(
|
||||
@@ -483,7 +501,7 @@ fn ico_icon<P: AsRef<Path>>(
|
||||
|
||||
let out_path = out_path.display().to_string();
|
||||
|
||||
let icon = quote!(Some(#root::Icon::Rgba { rgba: include_bytes!(#out_path).to_vec(), width: #width, height: #height }));
|
||||
let icon = quote!(#root::Icon::Rgba { rgba: include_bytes!(#out_path).to_vec(), width: #width, height: #height });
|
||||
Ok(icon)
|
||||
}
|
||||
|
||||
@@ -501,7 +519,9 @@ fn raw_icon<P: AsRef<Path>>(out_dir: &Path, path: P) -> Result<TokenStream, Embe
|
||||
|
||||
let out_path = out_path.display().to_string();
|
||||
|
||||
let icon = quote!(Some(include_bytes!(#out_path).to_vec()));
|
||||
let icon = quote!(::std::option::Option::Some(
|
||||
include_bytes!(#out_path).to_vec()
|
||||
));
|
||||
Ok(icon)
|
||||
}
|
||||
|
||||
@@ -533,7 +553,7 @@ fn png_icon<P: AsRef<Path>>(
|
||||
|
||||
let out_path = out_path.display().to_string();
|
||||
|
||||
let icon = quote!(Some(#root::Icon::Rgba { rgba: include_bytes!(#out_path).to_vec(), width: #width, height: #height }));
|
||||
let icon = quote!(#root::Icon::Rgba { rgba: include_bytes!(#out_path).to_vec(), width: #width, height: #height });
|
||||
Ok(icon)
|
||||
}
|
||||
|
||||
|
||||
@@ -539,6 +539,7 @@ pub struct Context<A: Assets> {
|
||||
pub(crate) assets: Arc<A>,
|
||||
pub(crate) default_window_icon: Option<Icon>,
|
||||
pub(crate) app_icon: Option<Vec<u8>>,
|
||||
#[cfg(desktop)]
|
||||
pub(crate) system_tray_icon: Option<Icon>,
|
||||
pub(crate) package_info: PackageInfo,
|
||||
pub(crate) _info_plist: (),
|
||||
@@ -553,9 +554,12 @@ impl<A: Assets> fmt::Debug for Context<A> {
|
||||
d.field("config", &self.config)
|
||||
.field("default_window_icon", &self.default_window_icon)
|
||||
.field("app_icon", &self.app_icon)
|
||||
.field("system_tray_icon", &self.system_tray_icon)
|
||||
.field("package_info", &self.package_info)
|
||||
.field("pattern", &self.pattern);
|
||||
|
||||
#[cfg(desktop)]
|
||||
d.field("system_tray_icon", &self.system_tray_icon);
|
||||
|
||||
#[cfg(shell_scope)]
|
||||
d.field("shell_scope", &self.shell_scope);
|
||||
d.finish()
|
||||
@@ -600,12 +604,14 @@ impl<A: Assets> Context<A> {
|
||||
}
|
||||
|
||||
/// The icon to use on the system tray UI.
|
||||
#[cfg(desktop)]
|
||||
#[inline(always)]
|
||||
pub fn system_tray_icon(&self) -> Option<&Icon> {
|
||||
self.system_tray_icon.as_ref()
|
||||
}
|
||||
|
||||
/// A mutable reference to the icon to use on the system tray UI.
|
||||
#[cfg(desktop)]
|
||||
#[inline(always)]
|
||||
pub fn system_tray_icon_mut(&mut self) -> &mut Option<Icon> {
|
||||
&mut self.system_tray_icon
|
||||
@@ -644,25 +650,38 @@ impl<A: Assets> Context<A> {
|
||||
assets: Arc<A>,
|
||||
default_window_icon: Option<Icon>,
|
||||
app_icon: Option<Vec<u8>>,
|
||||
system_tray_icon: Option<Icon>,
|
||||
package_info: PackageInfo,
|
||||
info_plist: (),
|
||||
pattern: Pattern,
|
||||
#[cfg(shell_scope)] shell_scope: scope::ShellScopeConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
config,
|
||||
assets,
|
||||
default_window_icon,
|
||||
app_icon,
|
||||
system_tray_icon,
|
||||
#[cfg(desktop)]
|
||||
system_tray_icon: None,
|
||||
package_info,
|
||||
_info_plist: info_plist,
|
||||
pattern,
|
||||
#[cfg(shell_scope)]
|
||||
shell_scope,
|
||||
shell_scope: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the app tray icon.
|
||||
#[cfg(desktop)]
|
||||
#[inline(always)]
|
||||
pub fn set_system_tray_icon(&mut self, icon: Icon) {
|
||||
self.system_tray_icon.replace(icon);
|
||||
}
|
||||
|
||||
/// Sets the app shell scope.
|
||||
#[cfg(shell_scope)]
|
||||
#[inline(always)]
|
||||
pub fn set_shell_scope(&mut self, scope: scope::ShellScopeConfig) {
|
||||
self.shell_scope = scope;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: expand these docs
|
||||
|
||||
@@ -219,6 +219,7 @@ pub struct InnerWindowManager<R: Runtime> {
|
||||
assets: Arc<dyn Assets>,
|
||||
pub(crate) default_window_icon: Option<Icon>,
|
||||
pub(crate) app_icon: Option<Vec<u8>>,
|
||||
#[cfg(desktop)]
|
||||
pub(crate) tray_icon: Option<Icon>,
|
||||
|
||||
package_info: PackageInfo,
|
||||
@@ -240,17 +241,21 @@ pub struct InnerWindowManager<R: Runtime> {
|
||||
|
||||
impl<R: Runtime> fmt::Debug for InnerWindowManager<R> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("InnerWindowManager")
|
||||
.field("plugins", &self.plugins)
|
||||
let mut d = f.debug_struct("InnerWindowManager");
|
||||
|
||||
d.field("plugins", &self.plugins)
|
||||
.field("state", &self.state)
|
||||
.field("config", &self.config)
|
||||
.field("default_window_icon", &self.default_window_icon)
|
||||
.field("app_icon", &self.app_icon)
|
||||
.field("tray_icon", &self.tray_icon)
|
||||
.field("package_info", &self.package_info)
|
||||
.field("menu", &self.menu)
|
||||
.field("pattern", &self.pattern)
|
||||
.finish()
|
||||
.field("pattern", &self.pattern);
|
||||
|
||||
#[cfg(desktop)]
|
||||
d.field("tray_icon", &self.tray_icon);
|
||||
|
||||
d.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,6 +327,7 @@ impl<R: Runtime> WindowManager<R> {
|
||||
assets: context.assets,
|
||||
default_window_icon: context.default_window_icon,
|
||||
app_icon: context.app_icon,
|
||||
#[cfg(desktop)]
|
||||
tray_icon: context.system_tray_icon,
|
||||
package_info: context.package_info,
|
||||
pattern: context.pattern,
|
||||
|
||||
@@ -12,6 +12,8 @@ use regex::Regex;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
const DEFAULT_OPEN_REGEX: &str = r#"^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+"#;
|
||||
|
||||
/// Allowed representation of `Execute` command arguments.
|
||||
#[derive(Debug, Clone, serde::Deserialize)]
|
||||
#[serde(untagged, deny_unknown_fields)]
|
||||
@@ -67,6 +69,45 @@ pub struct ScopeConfig {
|
||||
pub scopes: HashMap<String, ScopeAllowedCommand>,
|
||||
}
|
||||
|
||||
impl Default for ScopeConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
open: Some(Regex::new(DEFAULT_OPEN_REGEX).unwrap()),
|
||||
scopes: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ScopeConfig {
|
||||
/// Creates a new scope configuration with the default validation regex ^((mailto:\w+)|(tel:\w+)|(https?://\w+)).+.
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Creates a new scope configuration with the specified validation regex.
|
||||
pub fn with_validator(regex: Regex) -> Self {
|
||||
Self {
|
||||
open: Some(regex),
|
||||
scopes: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unsets the validator regex, allowing any path to be opened.
|
||||
pub fn skip_validation(mut self) -> Self {
|
||||
self.open = None;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the commands that are allowed to be executed.
|
||||
pub fn set_allowed_commands<I: IntoIterator<Item = (String, ScopeAllowedCommand)>>(
|
||||
mut self,
|
||||
scopes: I,
|
||||
) -> Self {
|
||||
self.scopes = scopes.into_iter().collect();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// A configured scoped shell command.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScopeAllowedCommand {
|
||||
|
||||
@@ -60,6 +60,7 @@ pub fn mock_context<A: Assets>(assets: A) -> crate::Context<A> {
|
||||
assets: Arc::new(assets),
|
||||
default_window_icon: None,
|
||||
app_icon: None,
|
||||
#[cfg(desktop)]
|
||||
system_tray_icon: None,
|
||||
package_info: crate::PackageInfo {
|
||||
name: "test".into(),
|
||||
|
||||
Reference in New Issue
Block a user