Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from Apr 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/remove-tray-icon-mobile.md
@@ -0,0 +1,6 @@
---
"tauri-codegen": patch
"tauri": patch
---

Refactor the `Context` conditional fields and only parse the tray icon on desktop.
108 changes: 64 additions & 44 deletions core/tauri-codegen/src/context.rs
Expand Up @@ -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 {
Expand Down Expand Up @@ -247,15 +257,15 @@ 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,
&config_parent,
|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
Expand All @@ -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)
}
};

Expand All @@ -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 {
Expand All @@ -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")]
Expand Down Expand Up @@ -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!();

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
)))
let with_shell_scope_code = quote!();

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>>(
Expand Down Expand Up @@ -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)
}

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

Expand Down Expand Up @@ -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)
}

Expand Down
29 changes: 24 additions & 5 deletions core/tauri/src/lib.rs
Expand Up @@ -552,6 +552,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: (),
Expand All @@ -566,9 +567,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()
Expand Down Expand Up @@ -613,12 +617,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
Expand Down Expand Up @@ -657,25 +663,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
Expand Down
16 changes: 11 additions & 5 deletions core/tauri/src/manager.rs
Expand Up @@ -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,
Expand All @@ -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()
}
}

Expand Down Expand Up @@ -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,
Expand Down
41 changes: 41 additions & 0 deletions core/tauri/src/scope/shell.rs
Expand Up @@ -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)]
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions core/tauri/src/test/mod.rs
Expand Up @@ -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(),
Expand Down