diff --git a/Cargo.toml b/Cargo.toml index fe116a5..c6329e1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ exclude = ["/examples", "/assets", ".DS_Store", "/.github", "/.changes", "/webvi tauri = { version = "1.5" } serde = { version = "1.0", features = ["derive"] } lazy_static = "1.4" +time = "0.3.28" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winuser"] } diff --git a/src/lib.rs b/src/lib.rs index f33583f..24dfba9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,13 +4,8 @@ use tauri::{ plugin::Builder, plugin::Plugin, plugin::TauriPlugin, Invoke, Manager, Runtime, State, Window, }; -#[cfg(target_os = "linux")] -use std::{ - sync::{mpsc, Mutex}, - time::Duration, -}; - mod menu_item; + use menu_item::MenuItem; mod keymap; @@ -71,13 +66,11 @@ impl ContextMenu { #[cfg(target_os = "linux")] fn show_context_menu( &self, - app_context: State<'_, os::AppContext>, window: Window, pos: Option, items: Option>, ) { - let context_menu = Arc::new(self.clone()); - os::show_context_menu(context_menu, app_context, window, pos, items); + os::show_context_menu(window, pos, items); } #[cfg(any(target_os = "macos", target_os = "windows"))] @@ -102,19 +95,6 @@ impl Plugin for ContextMenu { } } -#[cfg(target_os = "linux")] -#[tauri::command] -fn show_context_menu( - app_context: State<'_, os::AppContext>, - manager: State<'_, ContextMenu>, - window: Window, - pos: Option, - items: Option>, -) { - manager.show_context_menu(app_context, window, pos, items); -} - -#[cfg(any(target_os = "macos", target_os = "windows"))] #[tauri::command] fn show_context_menu( manager: State<'_, ContextMenu>, @@ -124,47 +104,11 @@ fn show_context_menu( ) { manager.show_context_menu(window, pos, items); } - -#[cfg(any(target_os = "macos", target_os = "windows"))] -pub fn init() -> TauriPlugin { - Builder::new("context_menu") - .invoke_handler(tauri::generate_handler![show_context_menu]) - .setup(|app| { - app.manage(ContextMenu::::default()); - Ok(()) - }) - .build() -} - -#[cfg(target_os = "linux")] pub fn init() -> TauriPlugin { - use tauri::async_runtime; - - let (tx, rx) = mpsc::channel::(); - let rx = Arc::new(Mutex::new(rx)); - - let rx = rx.clone(); - glib::timeout_add_local(Duration::from_millis(100), move || { - let rx = rx.lock().unwrap(); - if let Ok(cmd) = rx.try_recv() { - match cmd { - os::GtkThreadCommand::ShowContextMenu { pos, items, window } => { - async_runtime::block_on(async { - os::on_context_menu::(pos, items, window).await; - }); - } - } - } - glib::Continue(true) - }); - Builder::new("context_menu") .invoke_handler(tauri::generate_handler![show_context_menu]) .setup(|app| { app.manage(ContextMenu::::default()); - app.manage(os::AppContext { - tx: Arc::new(Mutex::new(tx)), - }); Ok(()) }) .build() diff --git a/src/linux.rs b/src/linux.rs index 56ae805..fcc5525 100644 --- a/src/linux.rs +++ b/src/linux.rs @@ -1,137 +1,103 @@ use gdk::{keys::Key, Display, ModifierType}; -use glib::clone; use gtk::{prelude::*, traits::WidgetExt, AccelFlags, AccelGroup, Menu, MenuItem as GtkMenuItem}; -use std::{ - any::Any, - mem, - sync::{mpsc::Sender, Arc, Mutex}, -}; -use tauri::{Runtime, State, Window}; +use std::{mem, thread::sleep, time}; +use tauri::{Runtime, Window}; use crate::keymap::{get_key_map, get_mod_map}; -use crate::{ContextMenu, MenuItem, Position}; +use crate::{MenuItem, Position}; -pub struct AppContext { - pub tx: Arc>>, -} - -pub enum GtkThreadCommand { - ShowContextMenu { - pos: Option, - items: Option>, - window: Arc>>, - }, -} - -pub async fn on_context_menu( +pub fn on_context_menu( pos: Option, items: Option>, - window: Arc>>, + window: Window, ) { - let window_clone = window.clone(); + // Create and show the context menu + let gtk_window = window.gtk_window().unwrap(); - // Delay the display of the context menu to ensure the window is ready - glib::timeout_add_local(std::time::Duration::from_millis(100), move || { - let window_mutex = window_clone.lock().unwrap(); - if let Some(window) = window_mutex.downcast_ref::>() { - // Create and show the context menu - // Create a new menu. - let menu = Menu::new(); - let gtk_window = window.gtk_window().unwrap(); - - // Check if the window is realized - if !gtk_window.is_realized() { - gtk_window.realize(); - } + // Check if the window is realized + if !gtk_window.is_realized() { + gtk_window.realize(); + } - if let Some(menu_items) = items.clone() { - for item in menu_items.iter() { - append_menu_item(window, >k_window, &menu, item); - } - } + // Create a new menu. + let menu = Menu::new(); + if let Some(menu_items) = items { + for item in menu_items.iter() { + append_menu_item(&window, >k_window, &menu, item); + } + } - let keep_alive = menu.clone(); - let window_clone = window.clone(); - menu.connect_hide(clone!(@weak keep_alive => move |_| { - window_clone.emit("menu-did-close", ()).unwrap(); - drop(keep_alive); - })); - - let (mut x, mut y) = match pos { - Some(ref position) => (position.x as i32, position.y as i32), - None => { - if let Some(display) = Display::default() { - if let Some(seat) = display.default_seat() { - let pointer = seat.pointer(); - let (_screen, x, y) = match pointer { - Some(p) => p.position(), - None => { - eprintln!("Failed to get pointer position"); - (display.default_screen(), 0, 0) - } - }; - (x, y) - } else { - eprintln!("Failed to get default seat"); - (0, 0) + let (mut x, mut y) = match pos { + Some(ref position) => (position.x as i32, position.y as i32), + None => { + if let Some(display) = Display::default() { + if let Some(seat) = display.default_seat() { + let pointer = seat.pointer(); + let (_screen, x, y) = match pointer { + Some(p) => p.position(), + None => { + eprintln!("Failed to get pointer position"); + (display.default_screen(), 0, 0) } - } else { - eprintln!("Failed to get default display"); - (0, 0) - } + }; + (x, y) + } else { + eprintln!("Failed to get default seat"); + (0, 0) } - }; - - let is_absolute = if let Some(position) = pos.clone() { - position.is_absolute } else { - Some(false) - }; - if is_absolute.unwrap_or(true) { - // Adjust x and y if the coordinates are not relative to the window - let window_position = window.outer_position().unwrap(); - x -= window_position.x; - y -= window_position.y; + eprintln!("Failed to get default display"); + (0, 0) } - - // Show the context menu at the specified position. - let gdk_window = gtk_window.window().unwrap(); - let rect = &gdk::Rectangle::new(x, y, 0, 0); - let mut event = gdk::Event::new(gdk::EventType::ButtonPress); - event.set_device( - gdk_window - .display() - .default_seat() - .and_then(|d| d.pointer()) - .as_ref(), - ); - menu.popup_at_rect( - &gdk_window, - rect, - gdk::Gravity::NorthWest, - gdk::Gravity::NorthWest, - Some(&event), - ); } + }; - glib::Continue(false) + let is_absolute = if let Some(position) = pos.clone() { + position.is_absolute + } else { + Some(false) + }; + if is_absolute.unwrap_or(true) { + // Adjust x and y if the coordinates are not relative to the window + let window_position = window.outer_position().unwrap(); + x -= window_position.x; + y -= window_position.y; + } + + // Required otherwise the menu doesn't show properly + sleep(time::Duration::from_millis(100)); + + // Delay the display of the context menu to ensure the window is ready + glib::idle_add_local(move || { + // Show the context menu at the specified position. + let gdk_window = gtk_window.window().unwrap(); + let rect = &gdk::Rectangle::new(x, y, 0, 0); + let mut event = gdk::Event::new(gdk::EventType::ButtonPress); + event.set_device( + gdk_window + .display() + .default_seat() + .and_then(|d| d.pointer()) + .as_ref(), + ); + menu.show_all(); + menu.popup_at_rect( + &gdk_window, + rect, + gdk::Gravity::NorthWest, + gdk::Gravity::NorthWest, + Some(&event), + ); + Continue(false) }); } pub fn show_context_menu( - _context_menu: Arc>, - app_context: State<'_, AppContext>, window: Window, pos: Option, items: Option>, ) { - let tx = app_context.tx.lock().unwrap(); // Lock the mutex to access the sender - tx.send(GtkThreadCommand::ShowContextMenu { - pos, - items, - window: Arc::new(Mutex::new(Box::new(window) as Box)), - }) - .expect("Failed to send command to GTK thread"); + on_context_menu(pos, items, window); } fn append_menu_item( @@ -169,7 +135,6 @@ fn append_menu_item( // Add the Box to the MenuItem menu_item.add(&hbox); - hbox.show_all(); // Handle enabled/disabled state if item.disabled.unwrap_or(false) { @@ -212,7 +177,6 @@ fn append_menu_item( } menu.append(&menu_item); - menu_item.show(); } }