refactor: improve tray icon/menu implementation.

This commit is contained in:
Timofey Gelazoniya 2024-09-06 02:06:53 +03:00
parent 559b0e843c
commit 0a4c10de34
Signed by: zeldon
GPG Key ID: 047886915281DD2A
2 changed files with 65 additions and 47 deletions

View File

@ -27,7 +27,6 @@ fn main() {
// Hide the console window // Hide the console window
winuser::ShowWindow(wincon::GetConsoleWindow(), winuser::SW_HIDE); winuser::ShowWindow(wincon::GetConsoleWindow(), winuser::SW_HIDE);
} }
std::env::set_var("RUST_LOG", "trace"); std::env::set_var("RUST_LOG", "trace");
pretty_env_logger::init(); pretty_env_logger::init();

View File

@ -12,7 +12,10 @@ use tray_icon::{
menu::{Menu, MenuEvent, MenuItem}, menu::{Menu, MenuEvent, MenuItem},
TrayIcon, TrayIconBuilder, TrayIcon, TrayIconBuilder,
}; };
use winapi::um::{wincon::GetConsoleWindow, winuser::{self, ShowWindow, SW_SHOW}}; use winapi::um::{
wincon::GetConsoleWindow,
winuser::{self, ShowWindow, SW_SHOW},
};
const BATTERY_UPDATE_INTERVAL: std::time::Duration = std::time::Duration::from_secs(60); const BATTERY_UPDATE_INTERVAL: std::time::Duration = std::time::Duration::from_secs(60);
const DEVICE_FETCH_INTERVAL: std::time::Duration = std::time::Duration::from_secs(5); const DEVICE_FETCH_INTERVAL: std::time::Duration = std::time::Duration::from_secs(5);
@ -39,12 +42,59 @@ impl MemoryDevice {
} }
} }
pub struct TrayInner {
tray_icon: Rc<Mutex<Option<TrayIcon>>>,
console_state: Arc<Mutex<bool>>,
menu_items: Arc<Mutex<Vec<MenuItem>>>,
}
impl TrayInner {
fn new() -> Self {
Self {
tray_icon: Rc::new(Mutex::new(None)),
console_state: Arc::new(Mutex::new(false)),
menu_items: Arc::new(Mutex::new(Vec::new())),
}
}
fn create_menu(&self) -> Menu {
let tray_menu = Menu::new();
let show_console_item = MenuItem::new("Show Log Window", true, None);
let quit_item = MenuItem::new("Exit", true, None);
let mut menu_items = self.menu_items.lock().unwrap();
menu_items.push(show_console_item);
menu_items.push(quit_item);
tray_menu
.append_items(&[&menu_items[0], &menu_items[1]])
.unwrap();
tray_menu
}
fn build_tray(
tray_icon: &Rc<Mutex<Option<TrayIcon>>>,
tray_menu: &Menu,
icon: tray_icon::Icon,
) {
let tray_builder = TrayIconBuilder::new()
.with_menu(Box::new(tray_menu.clone()))
.with_tooltip("Service is running")
.with_icon(icon)
.build();
match tray_builder {
Ok(tray) => *tray_icon.lock().unwrap() = Some(tray),
Err(err) => error!("Failed to create tray icon: {}", err),
}
}
}
pub struct TrayApp { pub struct TrayApp {
device_manager: Arc<Mutex<DeviceManager>>, device_manager: Arc<Mutex<DeviceManager>>,
devices: Arc<Mutex<HashMap<u32, MemoryDevice>>>, devices: Arc<Mutex<HashMap<u32, MemoryDevice>>>,
tray_icon: Rc<Mutex<Option<TrayIcon>>>, tray_inner: TrayInner,
console_state: Arc<Mutex<bool>>,
menu_items: Arc<Mutex<Vec<MenuItem>>>
} }
impl TrayApp { impl TrayApp {
@ -52,16 +102,14 @@ impl TrayApp {
Self { Self {
device_manager: Arc::new(Mutex::new(DeviceManager::new())), device_manager: Arc::new(Mutex::new(DeviceManager::new())),
devices: Arc::new(Mutex::new(HashMap::new())), devices: Arc::new(Mutex::new(HashMap::new())),
tray_icon: Rc::new(Mutex::new(None)), tray_inner: TrayInner::new(),
console_state: Arc::new(Mutex::new(false)),
menu_items: Arc::new(Mutex::new(Vec::new()))
} }
} }
pub fn run(&self) { pub fn run(&self) {
let icon = Self::create_icon(); let icon = Self::create_icon();
let event_loop = EventLoopBuilder::new().build(); let event_loop = EventLoopBuilder::new().build();
let tray_menu = self.create_menu(); let tray_menu = self.tray_inner.create_menu();
let (sender, receiver) = mpsc::channel(); let (sender, receiver) = mpsc::channel();
@ -82,20 +130,6 @@ impl TrayApp {
tray_icon::Icon::from_rgba(rgba, width, height).expect("Failed to create icon") tray_icon::Icon::from_rgba(rgba, width, height).expect("Failed to create icon")
} }
fn create_menu(&self) -> Menu {
let tray_menu = Menu::new();
let show_console_item = MenuItem::new("Show Log Window", true, None);
let quit_item = MenuItem::new("Exit", true, None);
let mut menu_items = self.menu_items.lock().unwrap();
menu_items.push(show_console_item);
menu_items.push(quit_item);
tray_menu.append_items(&[&menu_items[0], &menu_items[1]]).unwrap();
tray_menu
}
fn spawn_device_fetch_thread(&self, tx: mpsc::Sender<Vec<u32>>) { fn spawn_device_fetch_thread(&self, tx: mpsc::Sender<Vec<u32>>) {
let devices = Arc::clone(&self.devices); let devices = Arc::clone(&self.devices);
let device_manager = Arc::clone(&self.device_manager); let device_manager = Arc::clone(&self.device_manager);
@ -125,7 +159,7 @@ impl TrayApp {
} }
} }
} }
if !connected_devices.is_empty() { if !connected_devices.is_empty() {
tx.send(connected_devices).unwrap(); tx.send(connected_devices).unwrap();
} }
@ -151,11 +185,11 @@ impl TrayApp {
tray_menu: Menu, tray_menu: Menu,
rx: mpsc::Receiver<Vec<u32>>, rx: mpsc::Receiver<Vec<u32>>,
) { ) {
let tray_icon = Rc::clone(&self.tray_icon);
let devices = Arc::clone(&self.devices); let devices = Arc::clone(&self.devices);
let device_manager = Arc::clone(&self.device_manager); let device_manager = Arc::clone(&self.device_manager);
let console_state = Arc::clone(&self.console_state); let tray_icon = Rc::clone(&self.tray_inner.tray_icon);
let menu_items = Arc::clone(&self.menu_items); let console_state = Arc::clone(&self.tray_inner.console_state);
let menu_items = Arc::clone(&self.tray_inner.menu_items);
let menu_channel = MenuEvent::receiver(); let menu_channel = MenuEvent::receiver();
@ -169,7 +203,9 @@ impl TrayApp {
} }
if let tao::event::Event::NewEvents(tao::event::StartCause::Init) = event { if let tao::event::Event::NewEvents(tao::event::StartCause::Init) = event {
Self::build_tray(&tray_icon, &tray_menu, icon.clone()); // We create the icon once the event loop is actually running
// to prevent issues like https://github.com/tauri-apps/tray-icon/issues/90
TrayInner::build_tray(&tray_icon, &tray_menu, icon.clone());
} }
if let Ok(event) = menu_channel.try_recv() { if let Ok(event) = menu_channel.try_recv() {
@ -182,12 +218,12 @@ impl TrayApp {
let mut visible = console_state.lock().unwrap(); let mut visible = console_state.lock().unwrap();
if *visible { if *visible {
unsafe {ShowWindow(GetConsoleWindow(), winuser::SW_HIDE)}; unsafe { ShowWindow(GetConsoleWindow(), winuser::SW_HIDE) };
show_console_item.set_text("Show Log Window"); show_console_item.set_text("Show Log Window");
trace!("hiding log window"); trace!("hiding log window");
*visible = false; *visible = false;
} else { } else {
unsafe {ShowWindow(GetConsoleWindow(), SW_SHOW)}; unsafe { ShowWindow(GetConsoleWindow(), SW_SHOW) };
show_console_item.set_text("Hide Log Window"); show_console_item.set_text("Hide Log Window");
trace!("showing log window"); trace!("showing log window");
*visible = true *visible = true
@ -201,23 +237,6 @@ impl TrayApp {
}); });
} }
fn build_tray(
tray_icon: &Rc<Mutex<Option<TrayIcon>>>,
tray_menu: &Menu,
icon: tray_icon::Icon,
) {
let tray_builder = TrayIconBuilder::new()
.with_menu(Box::new(tray_menu.clone()))
.with_tooltip("Service is running")
.with_icon(icon)
.build();
match tray_builder {
Ok(tray) => *tray_icon.lock().unwrap() = Some(tray),
Err(err) => error!("Failed to create tray icon: {}", err),
}
}
fn update( fn update(
devices: &Arc<Mutex<HashMap<u32, MemoryDevice>>>, devices: &Arc<Mutex<HashMap<u32, MemoryDevice>>>,
manager: &Arc<Mutex<DeviceManager>>, manager: &Arc<Mutex<DeviceManager>>,