Skip to content

Commit

Permalink
Merge pull request #19 from SilverLuke/main
Browse files Browse the repository at this point in the history
Refactoring the linux version and removed the unnecessary GtkThreadCommand
  • Loading branch information
c2r0b authored Dec 19, 2023
2 parents 091efb3 + 80f80c2 commit ededa72
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 169 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand Down
60 changes: 2 additions & 58 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -71,13 +66,11 @@ impl<R: Runtime> ContextMenu<R> {
#[cfg(target_os = "linux")]
fn show_context_menu(
&self,
app_context: State<'_, os::AppContext>,
window: Window<R>,
pos: Option<Position>,
items: Option<Vec<MenuItem>>,
) {
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"))]
Expand All @@ -102,19 +95,6 @@ impl<R: Runtime> Plugin<R> for ContextMenu<R> {
}
}

#[cfg(target_os = "linux")]
#[tauri::command]
fn show_context_menu<R: Runtime>(
app_context: State<'_, os::AppContext>,
manager: State<'_, ContextMenu<R>>,
window: Window<R>,
pos: Option<Position>,
items: Option<Vec<MenuItem>>,
) {
manager.show_context_menu(app_context, window, pos, items);
}

#[cfg(any(target_os = "macos", target_os = "windows"))]
#[tauri::command]
fn show_context_menu<R: Runtime>(
manager: State<'_, ContextMenu<R>>,
Expand All @@ -124,47 +104,11 @@ fn show_context_menu<R: Runtime>(
) {
manager.show_context_menu(window, pos, items);
}

#[cfg(any(target_os = "macos", target_os = "windows"))]
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("context_menu")
.invoke_handler(tauri::generate_handler![show_context_menu])
.setup(|app| {
app.manage(ContextMenu::<R>::default());
Ok(())
})
.build()
}

#[cfg(target_os = "linux")]
pub fn init<R: Runtime>() -> TauriPlugin<R> {
use tauri::async_runtime;

let (tx, rx) = mpsc::channel::<os::GtkThreadCommand>();
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::<R>(pos, items, window).await;
});
}
}
}
glib::Continue(true)
});

Builder::new("context_menu")
.invoke_handler(tauri::generate_handler![show_context_menu])
.setup(|app| {
app.manage(ContextMenu::<R>::default());
app.manage(os::AppContext {
tx: Arc::new(Mutex::new(tx)),
});
Ok(())
})
.build()
Expand Down
186 changes: 75 additions & 111 deletions src/linux.rs
Original file line number Diff line number Diff line change
@@ -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<Mutex<Sender<GtkThreadCommand>>>,
}

pub enum GtkThreadCommand {
ShowContextMenu {
pos: Option<Position>,
items: Option<Vec<MenuItem>>,
window: Arc<Mutex<Box<dyn Any + Send>>>,
},
}

pub async fn on_context_menu<R: Runtime>(
pub fn on_context_menu<R: Runtime>(
pos: Option<Position>,
items: Option<Vec<MenuItem>>,
window: Arc<Mutex<Box<dyn Any + Send>>>,
window: Window<R>,
) {
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::<Window<R>>() {
// 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, &gtk_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, &gtk_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<R: Runtime>(
_context_menu: Arc<ContextMenu<R>>,
app_context: State<'_, AppContext>,
window: Window<R>,
pos: Option<Position>,
items: Option<Vec<MenuItem>>,
) {
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<dyn Any + Send>)),
})
.expect("Failed to send command to GTK thread");
on_context_menu(pos, items, window);
}

fn append_menu_item<R: Runtime>(
Expand Down Expand Up @@ -169,7 +135,6 @@ fn append_menu_item<R: Runtime>(

// Add the Box to the MenuItem
menu_item.add(&hbox);
hbox.show_all();

// Handle enabled/disabled state
if item.disabled.unwrap_or(false) {
Expand Down Expand Up @@ -212,7 +177,6 @@ fn append_menu_item<R: Runtime>(
}

menu.append(&menu_item);
menu_item.show();
}
}

Expand Down

0 comments on commit ededa72

Please sign in to comment.