Skip to content

Commit

Permalink
feat(linux) add with_temp_icon_dir builder extension (#452)
Browse files Browse the repository at this point in the history
* linux: Defer writing files to the `SystemTrayBuilder::build` call

* linux: Add an argument to `temp_icon_path` for a custom directory

* linux: Add `temp_icon_dir` field to the `SystemTrayBuilder`

* linux: Add `with_temp_icon_dir` builder method

* linux: Use `with_temp_icon_dir` in `system_tray` example

* Add changes file

* Format with cargo fmt

Rather than rust analyzer
  • Loading branch information
Beanow committed Jun 26, 2022
1 parent 86e1e81 commit ce209d3
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 28 deletions.
6 changes: 6 additions & 0 deletions .changes/linux-custom-temp-icon-dir.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"tao": minor
---

On Linux, adds `SystemTrayBuilderExtLinux::with_temp_icon_dir` which sets a custom temp icon dir to store generated icon files.
This may be useful when the application requires icons to be stored in a specific location, such as when running in a Flatpak sandbox.
9 changes: 9 additions & 0 deletions examples/system_tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#[cfg(any(target_os = "windows", target_os = "linux", target_os = "macos"))]
#[cfg(any(feature = "tray", all(target_os = "linux", feature = "ayatana")))]
fn main() {
#[cfg(target_os = "linux")]
use tao::platform::linux::SystemTrayBuilderExtLinux;
#[cfg(target_os = "macos")]
use tao::platform::macos::{CustomMenuItemExtMacOS, NativeImage, SystemTrayBuilderExtMacOS};
use tao::{
Expand All @@ -30,6 +32,13 @@ fn main() {

let icon = load_icon(std::path::Path::new(path));

#[cfg(target_os = "linux")]
let system_tray = SystemTrayBuilder::new(icon, Some(tray_menu))
.with_temp_icon_dir(std::path::Path::new("/tmp/tao-examples"))
.build(&event_loop)
.unwrap();

#[cfg(not(target_os = "linux"))]
let system_tray = SystemTrayBuilder::new(icon, Some(tray_menu))
.build(&event_loop)
.unwrap();
Expand Down
23 changes: 23 additions & 0 deletions src/platform/linux.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0

#![cfg(target_os = "linux")]

#[cfg(feature = "tray")]
use crate::system_tray::SystemTrayBuilder;
#[cfg(feature = "tray")]
use std::path::Path;

#[cfg(feature = "tray")]
pub trait SystemTrayBuilderExtLinux {
/// Sets a custom temp icon dir to store generated icon files.
fn with_temp_icon_dir<P: AsRef<Path>>(self, p: P) -> Self;
}

#[cfg(feature = "tray")]
impl SystemTrayBuilderExtLinux for SystemTrayBuilder {
fn with_temp_icon_dir<P: AsRef<Path>>(mut self, p: P) -> Self {
self.0.temp_icon_dir = Some(p.as_ref().to_path_buf());
self
}
}
2 changes: 2 additions & 0 deletions src/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
//! - `ios`
//! - `macos`
//! - `unix`
//! - `linux`
//! - `windows`
//!
//! And the following platform-specific module:
Expand All @@ -20,6 +21,7 @@

pub mod android;
pub mod ios;
pub mod linux;
pub mod macos;
pub mod run_return;
pub mod unix;
Expand Down
70 changes: 42 additions & 28 deletions src/platform_impl/linux/system_tray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,54 +16,58 @@ use libappindicator::{AppIndicator, AppIndicatorStatus};
use super::{menu::Menu, window::WindowRequest, WindowId};

pub struct SystemTrayBuilder {
pub(crate) temp_icon_dir: Option<PathBuf>,
tray_menu: Option<Menu>,
app_indicator: AppIndicator,
path: PathBuf,
icon: Icon,
}

impl SystemTrayBuilder {
#[inline]
pub fn new(icon: Icon, tray_menu: Option<Menu>) -> Self {
let (parent_path, icon_path) =
temp_icon_path().expect("Failed to create a temp folder for icon");
icon.inner.write_to_png(&icon_path);

let mut app_indicator = AppIndicator::new("tao application", "");
app_indicator.set_icon_theme_path(&parent_path.to_string_lossy());
app_indicator.set_icon_full(&icon_path.to_string_lossy(), "icon");

Self {
temp_icon_dir: None,
tray_menu,
app_indicator,
path: icon_path,
icon,
}
}

#[inline]
pub fn build<T: 'static>(
mut self,
self,
window_target: &EventLoopWindowTarget<T>,
) -> Result<RootSystemTray, OsError> {
let mut app_indicator = AppIndicator::new("tao application", "");

let (parent_path, icon_path) =
temp_icon_path(self.temp_icon_dir.as_ref()).expect("Failed to create a temp folder for icon");

self.icon.inner.write_to_png(&icon_path);

app_indicator.set_icon_theme_path(&parent_path.to_string_lossy());
app_indicator.set_icon_full(&icon_path.to_string_lossy(), "icon");

let sender = window_target.p.window_requests_tx.clone();

if let Some(tray_menu) = self.tray_menu.clone() {
let menu = &mut tray_menu.into_gtkmenu(&sender, &AccelGroup::new(), WindowId::dummy());

self.app_indicator.set_menu(menu);
app_indicator.set_menu(menu);
menu.show_all();
}

self.app_indicator.set_status(AppIndicatorStatus::Active);
app_indicator.set_status(AppIndicatorStatus::Active);

Ok(RootSystemTray(SystemTray {
app_indicator: self.app_indicator,
temp_icon_dir: self.temp_icon_dir,
app_indicator,
sender,
path: self.path,
path: icon_path,
}))
}
}

pub struct SystemTray {
temp_icon_dir: Option<PathBuf>,
app_indicator: AppIndicator,
sender: Sender<(WindowId, WindowRequest)>,
path: PathBuf,
Expand All @@ -72,7 +76,7 @@ pub struct SystemTray {
impl SystemTray {
pub fn set_icon(&mut self, icon: Icon) {
let (parent_path, icon_path) =
temp_icon_path().expect("Failed to create a temp folder for icon");
temp_icon_path(self.temp_icon_dir.as_ref()).expect("Failed to create a temp folder for icon");
icon.inner.write_to_png(&icon_path);

self
Expand Down Expand Up @@ -101,28 +105,38 @@ impl Drop for SystemTray {
}
}

fn temp_icon_path() -> std::io::Result<(PathBuf, PathBuf)> {
let mut parent_path = dirs_next::runtime_dir().unwrap_or_else(|| std::env::temp_dir());
/// Generates an icon path in one of the following dirs:
/// 1. If `temp_icon_dir` is `Some` use that.
/// 2. `$XDG_RUNTIME_DIR/tao`
/// 3. `/tmp/tao`
fn temp_icon_path(temp_icon_dir: Option<&PathBuf>) -> std::io::Result<(PathBuf, PathBuf)> {
let parent_path = match temp_icon_dir.as_ref() {
Some(path) => path.to_path_buf(),
None => dirs_next::runtime_dir()
.unwrap_or_else(|| std::env::temp_dir())
.join("tao"),
};

parent_path.push("tao");
std::fs::create_dir_all(&parent_path)?;
let mut icon_path = parent_path.clone();
icon_path.push(format!("tray-icon-{}.png", uuid::Uuid::new_v4()));
let icon_path = parent_path.join(format!("tray-icon-{}.png", uuid::Uuid::new_v4()));
Ok((parent_path, icon_path))
}

#[test]
fn temp_icon_path_prefers_runtime() {
fn temp_icon_path_preference_order() {
let runtime_dir = option_env!("XDG_RUNTIME_DIR");
let override_dir = PathBuf::from("/tmp/tao-tests");

let (dir1, _file1) = temp_icon_path().unwrap();
let (dir1, _file1) = temp_icon_path(Some(&override_dir)).unwrap();
let (dir2, _file1) = temp_icon_path(None).unwrap();
std::env::remove_var("XDG_RUNTIME_DIR");
let (dir2, _file2) = temp_icon_path().unwrap();
let (dir3, _file2) = temp_icon_path(None).unwrap();

assert_eq!(dir1, override_dir);
if let Some(runtime_dir) = runtime_dir {
std::env::set_var("XDG_RUNTIME_DIR", runtime_dir);
assert_eq!(dir1, PathBuf::from(format!("{}/tao", runtime_dir)));
assert_eq!(dir2, PathBuf::from(format!("{}/tao", runtime_dir)));
}

assert_eq!(dir2, PathBuf::from("/tmp/tao"));
assert_eq!(dir3, PathBuf::from("/tmp/tao"));
}

0 comments on commit ce209d3

Please sign in to comment.