From e53c9ac7c96889a5c30df72bd522c4be86db975f Mon Sep 17 00:00:00 2001 From: Nico Just Date: Sat, 5 Jun 2021 23:53:44 +0200 Subject: [PATCH 1/4] Use ksni on linux --- Cargo.toml | 3 +- examples/linux.rs | 9 +--- src/api/linux/mod.rs | 101 +++++++++++++++++++++++++++++++++---------- 3 files changed, 80 insertions(+), 33 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2bd6d03..17eabb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,8 +13,7 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [target.'cfg(target_os="linux")'.dependencies] -libappindicator = "0.5" # Tray icon -gtk = "0.9" +ksni = "0.1.3" [target.'cfg(target_os="windows")'.dependencies] winapi = { version = "0.3", features = ["shellapi", "libloaderapi", "errhandlingapi", "impl-default"] } diff --git a/examples/linux.rs b/examples/linux.rs index 361efda..9989c69 100644 --- a/examples/linux.rs +++ b/examples/linux.rs @@ -1,10 +1,6 @@ use tray_item::TrayItem; -use gtk; fn main() { - - gtk::init().unwrap(); - let mut tray = TrayItem::new("Tray Example", "accessories-calculator").unwrap(); tray.add_label("Tray Label").unwrap(); @@ -14,9 +10,8 @@ fn main() { }).unwrap(); tray.add_menu_item("Quit", || { - gtk::main_quit(); + std::process::exit(0); }).unwrap(); - gtk::main(); - + std::io::stdin().read_line(&mut String::new()).unwrap(); } diff --git a/src/api/linux/mod.rs b/src/api/linux/mod.rs index f82ebc3..a203a81 100644 --- a/src/api/linux/mod.rs +++ b/src/api/linux/mod.rs @@ -1,53 +1,106 @@ use crate::TIError; -use gtk::prelude::*; -use libappindicator::{AppIndicator, AppIndicatorStatus}; +use ksni::{ + menu::StandardItem, + Handle, +}; +use std::sync::Arc; + +struct TrayItem { + label: String, + action: Option> +} + +struct Tray { + title: String, + icon: String, + actions: Vec +} pub struct TrayItemLinux { - tray: AppIndicator, - menu: gtk::Menu, + tray: Handle +} + +impl ksni::Tray for Tray { + fn title(&self) -> String { + self.title.clone() + } + + fn icon_name(&self) -> String { + self.icon.clone() + } + + fn menu(&self) -> Vec> { + self.actions.iter().map(|item| { + let action = item.action.clone(); + if let Some(action) = action { + StandardItem { + label: item.label.clone(), + activate: Box::new(move |_| { + action(); + }), + ..Default::default() + } + .into() + } else { + StandardItem { + label: item.label.clone(), + enabled: false, + ..Default::default() + } + .into() + } + }).collect() + } } impl TrayItemLinux { pub fn new(title: &str, icon: &str) -> Result { - let mut t = Self { - tray: AppIndicator::new(title, icon), - menu: gtk::Menu::new(), - }; + let svc = ksni::TrayService::new(Tray { + title: title.to_string(), + icon: icon.to_string(), + actions: vec![] + }); - t.set_icon(icon)?; + let handle = svc.handle(); + svc.spawn(); - Ok(t) + Ok(Self { + tray: handle + }) } pub fn set_icon(&mut self, icon: &str) -> Result<(), TIError> { - self.tray.set_icon(icon); - self.tray.set_status(AppIndicatorStatus::Active); + self.tray.update(|tray| { + tray.icon = icon.to_string() + }); Ok(()) } pub fn add_label(&mut self, label: &str) -> Result<(), TIError> { - let item = gtk::MenuItem::with_label(label.as_ref()); - item.set_sensitive(false); - self.menu.append(&item); - self.menu.show_all(); - self.tray.set_menu(&mut self.menu); + self.tray.update(move |tray| { + tray.actions.push(TrayItem { + label: label.to_string(), + action: None + }); + }); Ok(()) } pub fn add_menu_item(&mut self, label: &str, cb: F) -> Result<(), TIError> - where - F: Fn() -> () + Send + Sync + 'static, + where F: Fn() -> () + Send + Sync + 'static, { - let item = gtk::MenuItem::with_label(label.as_ref()); - item.connect_activate(move |_| { + let action = Arc::new(move ||{ cb(); }); - self.menu.append(&item); - self.menu.show_all(); - self.tray.set_menu(&mut self.menu); + self.tray.update(move |tray| { + tray.actions.push(TrayItem { + label: label.to_string(), + action: Some(action.clone()) + }); + }); Ok(()) } } From db2ee1bed63a4d30fe9e367775048226e402ddb3 Mon Sep 17 00:00:00 2001 From: Nico Just Date: Sun, 6 Jun 2021 14:18:26 +0200 Subject: [PATCH 2/4] Added IconSource to be able to load icon via include_bytes on linux --- examples/linux.rs | 4 ++-- src/api/linux/mod.rs | 35 ++++++++++++++++++++++++----------- src/lib.rs | 15 +++++++++++++-- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/examples/linux.rs b/examples/linux.rs index 9989c69..4cc46fb 100644 --- a/examples/linux.rs +++ b/examples/linux.rs @@ -1,7 +1,7 @@ -use tray_item::TrayItem; +use tray_item::{TrayItem, IconSource}; fn main() { - let mut tray = TrayItem::new("Tray Example", "accessories-calculator").unwrap(); + let mut tray = TrayItem::new("Tray Example", IconSource::Resource("accessories-calculator")).unwrap(); tray.add_label("Tray Label").unwrap(); diff --git a/src/api/linux/mod.rs b/src/api/linux/mod.rs index a203a81..b6e6821 100644 --- a/src/api/linux/mod.rs +++ b/src/api/linux/mod.rs @@ -1,8 +1,5 @@ -use crate::TIError; -use ksni::{ - menu::StandardItem, - Handle, -}; +use crate::{TIError, IconSource}; +use ksni::{menu::StandardItem, Handle, Icon}; use std::sync::Arc; struct TrayItem { @@ -12,7 +9,7 @@ struct TrayItem { struct Tray { title: String, - icon: String, + icon: IconSource, actions: Vec } @@ -26,7 +23,23 @@ impl ksni::Tray for Tray { } fn icon_name(&self) -> String { - self.icon.clone() + match &self.icon { + IconSource::Resource(name) => name.to_string(), + IconSource::Data{..} => String::new(), + } + } + + fn icon_pixmap(&self) -> Vec { + match &self.icon { + IconSource::Resource(_) => vec![], + IconSource::Data{data, height, width} => { + vec![Icon { + width: *height, + height: *width, + data: data.clone() + }] + }, + } } fn menu(&self) -> Vec> { @@ -54,10 +67,10 @@ impl ksni::Tray for Tray { } impl TrayItemLinux { - pub fn new(title: &str, icon: &str) -> Result { + pub fn new(title: &str, icon: IconSource) -> Result { let svc = ksni::TrayService::new(Tray { title: title.to_string(), - icon: icon.to_string(), + icon, actions: vec![] }); @@ -69,9 +82,9 @@ impl TrayItemLinux { }) } - pub fn set_icon(&mut self, icon: &str) -> Result<(), TIError> { + pub fn set_icon(&mut self, icon: IconSource) -> Result<(), TIError> { self.tray.update(|tray| { - tray.icon = icon.to_string() + tray.icon = icon.clone() }); Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 71f6600..ecc73ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,20 @@ pub use error::TIError; pub struct TrayItem(api::TrayItemImpl); +#[derive(Clone)] +pub enum IconSource { + Resource(&'static str), + #[cfg(target_os = "linux")] + Data { + height: i32, + width: i32, + data: Vec + }, +} + impl TrayItem { - pub fn new(title: &str, icon: &str) -> Result { + pub fn new(title: &str, icon: IconSource) -> Result { Ok( Self( @@ -16,7 +27,7 @@ impl TrayItem { } - pub fn set_icon(&mut self, icon: &str) -> Result<(), TIError> { + pub fn set_icon(&mut self, icon: IconSource) -> Result<(), TIError> { self.0.set_icon(icon) From d45cc1673c272d99e71336f1830909a1419850bb Mon Sep 17 00:00:00 2001 From: "nico.just" Date: Sun, 6 Jun 2021 14:39:06 +0200 Subject: [PATCH 3/4] Added IconSource to windows and macos api --- examples/windows.rs | 6 +++--- src/api/macos/mod.rs | 9 +++++---- src/api/windows/mod.rs | 11 ++++------- src/lib.rs | 12 ++++++++++++ 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/examples/windows.rs b/examples/windows.rs index eb2129b..8c9111f 100644 --- a/examples/windows.rs +++ b/examples/windows.rs @@ -1,8 +1,8 @@ -use tray_item::TrayItem; +use tray_item::{TrayItem, IconSource}; fn main() { - let mut tray = TrayItem::new("Tray Example", "name-of-icon-in-rc-file").unwrap(); + let mut tray = TrayItem::new("Tray Example", IconSource::Resource("name-of-icon-in-rc-file")).unwrap(); tray.add_label("Tray Label").unwrap(); @@ -15,7 +15,7 @@ fn main() { std::process::exit(0); }).unwrap(); - tray.set_icon("another-name-from-rc-file").unwrap(); + tray.set_icon(IconSource::Resource("another-name-from-rc-file")).unwrap(); std::io::stdin().read_line(&mut String::new()).unwrap(); diff --git a/src/api/macos/mod.rs b/src/api/macos/mod.rs index 2c1f7d4..57bed10 100644 --- a/src/api/macos/mod.rs +++ b/src/api/macos/mod.rs @@ -1,4 +1,4 @@ -use crate::TIError; +use crate::{TIError, IconSource}; use cocoa::{ appkit::{ NSApp, NSApplication, NSApplicationActivateIgnoringOtherApps, NSImage, NSMenu, NSMenuItem, @@ -22,10 +22,11 @@ pub struct TrayItemMacOS { } impl TrayItemMacOS { - pub fn new(title: &str, icon: &str) -> Result { + pub fn new(title: &str, icon: IconSource) -> Result { unsafe { let pool = NSAutoreleasePool::new(nil); + let icon = icon.as_str(); let icon = Some(icon).filter(|icon| !icon.is_empty()); let icon = icon.map(|icon_name| { let icon_name = NSString::alloc(nil).init_str(icon_name); @@ -46,9 +47,9 @@ impl TrayItemMacOS { } } - pub fn set_icon(&mut self, icon: &str) -> Result<(), TIError> { + pub fn set_icon(&mut self, icon: IconSource) -> Result<(), TIError> { unsafe { - let icon_name = NSString::alloc(nil).init_str(icon); + let icon_name = NSString::alloc(nil).init_str(icon.as_str()); self.icon = Some(NSImage::imageNamed_(NSImage::alloc(nil), icon_name)); } Ok(()) diff --git a/src/api/windows/mod.rs b/src/api/windows/mod.rs index 73ebf60..265fcc1 100644 --- a/src/api/windows/mod.rs +++ b/src/api/windows/mod.rs @@ -1,7 +1,7 @@ // Most of this code is taken from https://github.com/qdot/systray-rs/blob/master/src/api/win32/mod.rs // Open source is great :) -use crate::TIError; +use crate::{TIError, IconSource}; use std::{ self, cell::RefCell, @@ -47,7 +47,7 @@ pub struct TrayItemWindows { impl TrayItemWindows { - pub fn new(title: &str, icon: &str) -> Result { + pub fn new(title: &str, icon: IconSource) -> Result { let entries = Arc::new(Mutex::new(Vec::new())); let (tx, rx) = channel(); @@ -135,13 +135,10 @@ impl TrayItemWindows { w.set_icon(icon)?; Ok(w) - } - pub fn set_icon(&self, icon: &str) -> Result<(), TIError> { - - self.set_icon_from_resource(icon) - + pub fn set_icon(&self, icon: IconSource) -> Result<(), TIError> { + self.set_icon_from_resource(icon.as_str()) } pub fn add_label(&mut self, label: &str) -> Result<(), TIError> { diff --git a/src/lib.rs b/src/lib.rs index ecc73ad..d3fecab 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,18 @@ pub enum IconSource { }, } +impl IconSource { + pub fn as_str(&self) -> &str { + match self { + IconSource::Resource(res) => { + res + }, + #[allow(unreachable_patterns)] + _ => unimplemented!() + } + } +} + impl TrayItem { pub fn new(title: &str, icon: IconSource) -> Result { From 68c6afd4dc05ecbb051f2149b54934be9c9db8a5 Mon Sep 17 00:00:00 2001 From: Nico Just Date: Sun, 6 Jun 2021 20:55:44 +0200 Subject: [PATCH 4/4] Fixed macos example --- examples/macos.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/macos.rs b/examples/macos.rs index 3ef4e9a..e96b1ec 100644 --- a/examples/macos.rs +++ b/examples/macos.rs @@ -1,8 +1,8 @@ -use tray_item::TrayItem; +use tray_item::{TrayItem, IconSource}; fn main() { - let mut tray = TrayItem::new("Tray Example", "").unwrap(); + let mut tray = TrayItem::new("Tray Example", IconSource::Resource("")).unwrap(); tray.add_label("Tray Label").unwrap();