refactor(core): read tray icon only on desktop, refactor Context (#6719)

This commit is contained in:
Lucas Fernandes Nogueira
2023-04-19 15:44:10 -07:00
committed by GitHub
parent 86488a6ad8
commit ae102980fc
6 changed files with 146 additions and 53 deletions

View File

@@ -0,0 +1,6 @@
---
"tauri-codegen": patch
"tauri": patch
---
Refactor the `Context` conditional fields and only parse the tray icon on desktop.

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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(),