Skip to content

Commit

Permalink
feat(core): system tray, closes #157 (#1749)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed May 9, 2021
1 parent 41d5d6a commit c090927
Show file tree
Hide file tree
Showing 20 changed files with 756 additions and 383 deletions.
5 changes: 5 additions & 0 deletions .changes/tray.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

Adds system tray support.
42 changes: 42 additions & 0 deletions core/tauri-codegen/src/context.rs
Expand Up @@ -62,6 +62,47 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
quote!(None)
};

#[cfg(target_os = "linux")]
let system_tray_icon = if let Some(tray) = &config.tauri.system_tray {
let mut system_tray_icon_path = tray.icon_path.clone();
system_tray_icon_path.set_extension("png");
if dev {
let system_tray_icon_file_name = system_tray_icon_path
.file_name()
.expect("failed to get tray path file_name")
.to_string_lossy()
.to_string();
quote!(Some(
::tauri::platform::resource_dir()
.expect("failed to read resource dir")
.join(
#system_tray_icon_file_name
)
))
} else {
let system_tray_icon_path = config_parent
.join(system_tray_icon_path)
.display()
.to_string();
quote!(Some(::std::path::PathBuf::from(#system_tray_icon_path)))
}
} else {
quote!(None)
};

#[cfg(not(target_os = "linux"))]
let system_tray_icon = if let Some(tray) = &config.tauri.system_tray {
let mut system_tray_icon_path = tray.icon_path.clone();
system_tray_icon_path.set_extension(if cfg!(windows) { "ico" } else { "png" });
let system_tray_icon_path = config_parent
.join(system_tray_icon_path)
.display()
.to_string();
quote!(Some(include_bytes!(#system_tray_icon_path).to_vec()))
} else {
quote!(None)
};

let package_name = if let Some(product_name) = &config.package.product_name {
quote!(#product_name.to_string())
} else {
Expand All @@ -78,6 +119,7 @@ pub fn context_codegen(data: ContextData) -> Result<TokenStream, EmbeddedAssetsE
config: #config,
assets: ::std::sync::Arc::new(#assets),
default_window_icon: #default_window_icon,
system_tray_icon: #system_tray_icon,
package_info: #root::api::PackageInfo {
name: #package_name,
version: #package_version,
Expand Down
45 changes: 38 additions & 7 deletions core/tauri-utils/src/config.rs
Expand Up @@ -134,7 +134,7 @@ impl Default for WindowConfig {

/// The Updater configuration object.
#[derive(PartialEq, Deserialize, Debug, Clone)]
#[serde(tag = "updater", rename_all = "camelCase")]
#[serde(rename_all = "camelCase")]
pub struct UpdaterConfig {
/// Whether the updater is active or not.
#[serde(default)]
Expand Down Expand Up @@ -167,12 +167,21 @@ impl Default for UpdaterConfig {

/// Security configuration.
#[derive(PartialEq, Deserialize, Debug, Clone, Default)]
#[serde(tag = "updater", rename_all = "camelCase")]
#[serde(rename_all = "camelCase")]
pub struct SecurityConfig {
/// Content security policy to inject to HTML files with the custom protocol.
pub csp: Option<String>,
}

/// Configuration for application system tray icon.
#[derive(PartialEq, Deserialize, Debug, Clone, Default)]
#[serde(rename_all = "camelCase")]
pub struct SystemTrayConfig {
/// Path to the icon to use on the system tray.
/// Automatically set to be an `.png` on macOS and Linux, and `.ico` on Windows.
pub icon_path: PathBuf,
}

/// A CLI argument definition
#[derive(PartialEq, Deserialize, Debug, Default, Clone)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -262,7 +271,7 @@ pub struct CliArg {

/// The CLI root command definition.
#[derive(PartialEq, Deserialize, Debug, Clone)]
#[serde(tag = "cli", rename_all = "camelCase")]
#[serde(rename_all = "camelCase")]
#[allow(missing_docs)] // TODO
pub struct CliConfig {
pub description: Option<String>,
Expand Down Expand Up @@ -311,7 +320,7 @@ impl CliConfig {

/// The bundler configuration object.
#[derive(PartialEq, Deserialize, Debug)]
#[serde(tag = "bundle", rename_all = "camelCase")]
#[serde(rename_all = "camelCase")]
pub struct BundleConfig {
/// The bundle identifier.
pub identifier: String,
Expand All @@ -335,7 +344,7 @@ fn default_window_config() -> Vec<WindowConfig> {

/// The Tauri configuration object.
#[derive(PartialEq, Deserialize, Debug)]
#[serde(tag = "tauri", rename_all = "camelCase")]
#[serde(rename_all = "camelCase")]
pub struct TauriConfig {
/// The window configuration.
#[serde(default = "default_window_config")]
Expand All @@ -352,6 +361,8 @@ pub struct TauriConfig {
/// The security configuration.
#[serde(default)]
pub security: SecurityConfig,
/// System tray configuration.
pub system_tray: Option<SystemTrayConfig>,
}

impl Default for TauriConfig {
Expand All @@ -362,13 +373,14 @@ impl Default for TauriConfig {
bundle: BundleConfig::default(),
updater: UpdaterConfig::default(),
security: SecurityConfig::default(),
system_tray: None,
}
}
}

/// The Build configuration object.
#[derive(PartialEq, Deserialize, Debug)]
#[serde(tag = "build", rename_all = "camelCase")]
#[serde(rename_all = "camelCase")]
pub struct BuildConfig {
/// the devPath config.
#[serde(default = "default_dev_path")]
Expand Down Expand Up @@ -777,15 +789,33 @@ mod build {
}
}

impl ToTokens for SystemTrayConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let icon_path = self.icon_path.to_string_lossy().to_string();
let icon_path = quote! { ::std::path::PathBuf::from(#icon_path) };
literal_struct!(tokens, SystemTrayConfig, icon_path);
}
}

impl ToTokens for TauriConfig {
fn to_tokens(&self, tokens: &mut TokenStream) {
let windows = vec_lit(&self.windows, identity);
let cli = opt_lit(self.cli.as_ref());
let bundle = &self.bundle;
let updater = &self.updater;
let security = &self.security;
let system_tray = opt_lit(self.system_tray.as_ref());

literal_struct!(tokens, TauriConfig, windows, cli, bundle, updater, security);
literal_struct!(
tokens,
TauriConfig,
windows,
cli,
bundle,
updater,
security,
system_tray
);
}
}

Expand Down Expand Up @@ -880,6 +910,7 @@ mod test {
endpoints: None,
},
security: SecurityConfig { csp: None },
system_tray: None,
};

// create a build config
Expand Down
1 change: 1 addition & 0 deletions core/tauri/src/api/process.rs
Expand Up @@ -365,6 +365,7 @@ impl Command {
// tests for the commands functions.
#[cfg(test)]
mod test {
#[cfg(not(windows))]
use super::*;

#[cfg(not(windows))]
Expand Down
29 changes: 16 additions & 13 deletions core/tauri/src/endpoints/window.rs
Expand Up @@ -3,8 +3,7 @@
// SPDX-License-Identifier: MIT

#[cfg(window_create)]
use crate::sealed::ManagerBase;

use crate::runtime::{webview::WindowBuilder, Dispatch, Runtime};
use crate::{
api::config::WindowConfig,
endpoints::InvokeResponse,
Expand Down Expand Up @@ -106,17 +105,21 @@ impl Cmd {
});

let url = options.url.clone();
let pending = crate::runtime::window::PendingWindow::with_config(
options,
crate::runtime::webview::WebviewAttributes::new(url),
label.clone(),
);
window.create_new_window(pending)?.emit_others(
&crate::runtime::manager::tauri_event::<P::Event>("tauri://window-created"),
Some(WindowCreatedEvent {
label: label.to_string(),
}),
)?;
window
.create_window(label.clone(), url, |_, webview_attributes| {
(
<<<P::Runtime as Runtime>::Dispatcher as Dispatch>::WindowBuilder>::with_config(
options,
),
webview_attributes,
)
})?
.emit_others(
&crate::runtime::manager::tauri_event::<P::Event>("tauri://window-created"),
Some(WindowCreatedEvent {
label: label.to_string(),
}),
)?;
}
// Getters
Self::ScaleFactor => return Ok(window.scale_factor()?.into()),
Expand Down
3 changes: 3 additions & 0 deletions core/tauri/src/error.rs
Expand Up @@ -66,6 +66,9 @@ pub enum Error {
/// `default_path` provided to dialog API doesn't exist.
#[error("failed to setup dialog: provided default path `{0}` doesn't exist")]
DialogDefaultPathNotExists(PathBuf),
/// Encountered an error creating the app system tray,
#[error("error encountered during tray setup: {0}")]
SystemTray(Box<dyn std::error::Error + Send>),
}

impl From<serde_json::Error> for Error {
Expand Down
34 changes: 23 additions & 11 deletions core/tauri/src/lib.rs
Expand Up @@ -59,12 +59,11 @@ pub use {
Invoke, InvokeError, InvokeHandler, InvokeMessage, InvokeResolver, InvokeResponse, OnPageLoad,
PageLoadPayload, SetupHook,
},
self::runtime::app::{App, Builder, WindowMenuEvent},
self::runtime::app::{App, Builder, SystemTrayEvent, WindowMenuEvent},
self::runtime::flavors::wry::Wry,
self::runtime::menu::{CustomMenuItem, Menu, MenuId, MenuItem, SystemTrayMenuItem},
self::runtime::monitor::Monitor,
self::runtime::webview::{
CustomMenuItem, Menu, MenuItem, MenuItemId, WebviewAttributes, WindowBuilder,
},
self::runtime::webview::{WebviewAttributes, WindowBuilder},
self::runtime::window::{
export::{
dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Pixel, Position, Size},
Expand All @@ -73,6 +72,7 @@ pub use {
WindowEvent,
},
self::state::{State, StateManager},
tauri_utils::platform,
};

/// Reads the config file at compile time and generates a [`Context`] based on its content.
Expand Down Expand Up @@ -130,6 +130,14 @@ pub struct Context<A: Assets> {
/// The default window icon Tauri should use when creating windows.
pub default_window_icon: Option<Vec<u8>>,

/// The icon to use use on the system tray UI.
#[cfg(target_os = "linux")]
pub system_tray_icon: Option<PathBuf>,

/// The icon to use use on the system tray UI.
#[cfg(not(target_os = "linux"))]
pub system_tray_icon: Option<Vec<u8>>,

/// Package information.
pub package_info: crate::api::PackageInfo,
}
Expand All @@ -142,6 +150,12 @@ pub trait Params: sealed::ParamsBase {
/// The type used to determine the name of windows.
type Label: Tag;

/// The type used to determine window menu ids.
type MenuId: MenuId;

/// The type used to determine system tray menu ids.
type SystemTrayMenuId: MenuId;

/// Assets that Tauri should serve from itself.
type Assets: Assets;

Expand Down Expand Up @@ -258,8 +272,8 @@ pub(crate) mod sealed {

/// A running [`Runtime`] or a dispatcher to it.
pub enum RuntimeOrDispatch<'r, P: Params> {
/// Mutable reference to the running [`Runtime`].
Runtime(&'r mut P::Runtime),
/// Reference to the running [`Runtime`].
Runtime(&'r P::Runtime),

/// A dispatcher to the running [`Runtime`].
Dispatch(<P::Runtime as Runtime>::Dispatcher),
Expand All @@ -270,18 +284,16 @@ pub(crate) mod sealed {
/// The manager behind the [`Managed`] item.
fn manager(&self) -> &WindowManager<P>;

/// The runtime or runtime dispatcher of the [`Managed`] item.
fn runtime(&mut self) -> RuntimeOrDispatch<'_, P>;

/// Creates a new [`Window`] on the [`Runtime`] and attaches it to the [`Manager`].
fn create_new_window(
&mut self,
&self,
runtime: RuntimeOrDispatch<'_, P>,
pending: crate::PendingWindow<P>,
) -> crate::Result<crate::Window<P>> {
use crate::runtime::Dispatch;
let labels = self.manager().labels().into_iter().collect::<Vec<_>>();
let pending = self.manager().prepare_window(pending, &labels)?;
match self.runtime() {
match runtime {
RuntimeOrDispatch::Runtime(runtime) => runtime.create_window(pending),
RuntimeOrDispatch::Dispatch(mut dispatcher) => dispatcher.create_window(pending),
}
Expand Down

0 comments on commit c090927

Please sign in to comment.