feat: add menu hide, show and remove (#8)

This commit is contained in:
Amr Bashir 2022-06-07 18:32:10 +02:00 committed by GitHub
parent 6b98160e49
commit 0201895d74
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 173 additions and 19 deletions

View file

@ -13,10 +13,10 @@ fn main() {
let mut event_loop_builder = EventLoopBuilder::new(); let mut event_loop_builder = EventLoopBuilder::new();
let mut menu_bar = Menu::new(); let mut menu_bar = Menu::new();
let menu_bar_c = menu_bar.clone();
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
{ {
let menu_bar_c = menu_bar.clone();
event_loop_builder.with_msg_hook(move |msg| { event_loop_builder.with_msg_hook(move |msg| {
use windows_sys::Win32::UI::WindowsAndMessaging::{TranslateAcceleratorW, MSG}; use windows_sys::Win32::UI::WindowsAndMessaging::{TranslateAcceleratorW, MSG};
unsafe { unsafe {
@ -26,6 +26,7 @@ fn main() {
} }
}); });
} }
#[allow(unused_mut)] #[allow(unused_mut)]
let mut event_loop = event_loop_builder.build(); let mut event_loop = event_loop_builder.build();
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
@ -51,6 +52,11 @@ fn main() {
menu_bar.init_for_hwnd(_window2.hwnd() as _); menu_bar.init_for_hwnd(_window2.hwnd() as _);
} }
#[cfg(target_os = "macos")]
{
menu_bar.init_for_nsapp();
}
let menu_channel = menu_event_receiver(); let menu_channel = menu_event_receiver();
let mut open_item_disabled = false; let mut open_item_disabled = false;
let mut counter = 0; let mut counter = 0;

View file

@ -123,6 +123,10 @@ impl Menu {
/// ## Safety: /// ## Safety:
/// ///
/// This should be called before anything is added to the window. /// This should be called before anything is added to the window.
///
/// ## Panics:
///
/// Panics if the gtk event loop hasn't been initialized on the thread.
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn init_for_gtk_window<W>(&self, w: &W) -> std::rc::Rc<gtk::Box> pub fn init_for_gtk_window<W>(&self, w: &W) -> std::rc::Rc<gtk::Box>
where where
@ -168,11 +172,69 @@ impl Menu {
self.0.haccel() self.0.haccel()
} }
/// Adds this menu to NSApp. /// Removes this menu from a [`gtk::Window`]
///
/// ## Panics:
///
/// Panics if the window doesn't have a menu created by this crate.
#[cfg(target_os = "linux")]
pub fn remove_for_gtk_window<W>(&self, w: &W)
where
W: gtk::prelude::IsA<gtk::Container>,
W: gtk::prelude::IsA<gtk::Window>,
{
self.0.remove_for_gtk_window(w)
}
/// Removes this menu from a win32 window
#[cfg(target_os = "windows")]
pub fn remove_for_hwnd(&self, hwnd: isize) {
self.0.remove_for_hwnd(hwnd)
}
/// Hides this menu from a [`gtk::Window`]
#[cfg(target_os = "linux")]
pub fn hide_for_gtk_window<W>(&self, w: &W)
where
W: gtk::prelude::IsA<gtk::Container>,
W: gtk::prelude::IsA<gtk::Window>,
{
self.0.hide_for_gtk_window(w)
}
/// Hides this menu from a win32 window
#[cfg(target_os = "windows")]
pub fn hide_for_hwnd(&self, hwnd: isize) {
self.0.hide_for_hwnd(hwnd)
}
/// Shows this menu from a [`gtk::Window`]
#[cfg(target_os = "linux")]
pub fn show_for_gtk_window<W>(&self, w: &W)
where
W: gtk::prelude::IsA<gtk::Container>,
W: gtk::prelude::IsA<gtk::Window>,
{
self.0.show_for_gtk_window(w)
}
/// Shows this menu from a win32 window
#[cfg(target_os = "windows")]
pub fn show_for_hwnd(&self, hwnd: isize) {
self.0.show_for_hwnd(hwnd)
}
/// Adds this menu to an NSApp.
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
pub fn init_for_nsapp(&self) { pub fn init_for_nsapp(&self) {
self.0.init_for_nsapp() self.0.init_for_nsapp()
} }
/// Removes this menu from an NSApp.
#[cfg(target_os = "macos")]
pub fn remove_for_nsapp(&self) {
self.0.remove_for_nsapp()
}
} }
/// This is a submenu within another [`Submenu`] or [`Menu`]. /// This is a submenu within another [`Submenu`] or [`Menu`].

View file

@ -1,10 +1,9 @@
mod accelerator; mod accelerator;
use crate::counter::Counter; use crate::counter::Counter;
use accelerator::{to_gtk_accelerator, to_gtk_menemenoic};
use gtk::{prelude::*, Orientation}; use gtk::{prelude::*, Orientation};
use std::{cell::RefCell, rc::Rc}; use std::{cell::RefCell, collections::HashMap, rc::Rc};
use self::accelerator::{to_gtk_accelerator, to_gtk_menemenoic};
static COUNTER: Counter = Counter::new(); static COUNTER: Counter = Counter::new();
@ -36,7 +35,7 @@ struct InnerMenu {
// multiple times, and thus can't be used in multiple windows, entry // multiple times, and thus can't be used in multiple windows, entry
// keeps a vector of a tuple of `gtk::MenuBar` and `gtk::Box` // keeps a vector of a tuple of `gtk::MenuBar` and `gtk::Box`
// and push to it every time `Menu::init_for_gtk_window` is called. // and push to it every time `Menu::init_for_gtk_window` is called.
gtk_items: Vec<(gtk::MenuBar, Rc<gtk::Box>)>, gtk_items: HashMap<isize, (Option<gtk::MenuBar>, Rc<gtk::Box>)>,
accel_group: gtk::AccelGroup, accel_group: gtk::AccelGroup,
} }
@ -47,7 +46,7 @@ impl Menu {
pub fn new() -> Self { pub fn new() -> Self {
Self(Rc::new(RefCell::new(InnerMenu { Self(Rc::new(RefCell::new(InnerMenu {
entries: Vec::new(), entries: Vec::new(),
gtk_items: Vec::new(), gtk_items: HashMap::new(),
accel_group: gtk::AccelGroup::new(), accel_group: gtk::AccelGroup::new(),
}))) })))
} }
@ -74,21 +73,82 @@ impl Menu {
W: IsA<gtk::Window>, W: IsA<gtk::Window>,
{ {
let mut inner = self.0.borrow_mut(); let mut inner = self.0.borrow_mut();
// This is the first time this method has been called on a window
if inner.gtk_items.get(&(w.as_ptr() as _)).is_none() {
let menu_bar = gtk::MenuBar::new(); let menu_bar = gtk::MenuBar::new();
add_entries_to_menu(&menu_bar, &inner.entries, &inner.accel_group); let vbox = gtk::Box::new(Orientation::Vertical, 0);
w.add(&vbox);
inner
.gtk_items
.insert(w.as_ptr() as _, (Some(menu_bar), Rc::new(vbox)));
}
if let Some((menu_bar, vbox)) = inner.gtk_items.get(&(w.as_ptr() as _)) {
// This is NOT the first time this method has been called on a window.
// So it already contains a `gtk::Box` but it doesn't have a `gtk::MenuBar`
// because it was probably removed using `Menu::remove_for_gtk_window`
// so we only need to create the menubar
if menu_bar.is_none() {
let vbox = Rc::clone(vbox);
inner
.gtk_items
.insert(w.as_ptr() as _, (Some(gtk::MenuBar::new()), vbox));
}
}
let (menu_bar, vbox) = inner.gtk_items.get(&(w.as_ptr() as _)).unwrap();
add_entries_to_menu(
menu_bar.as_ref().unwrap(),
&inner.entries,
&inner.accel_group,
);
w.add_accel_group(&inner.accel_group); w.add_accel_group(&inner.accel_group);
let vbox = gtk::Box::new(Orientation::Vertical, 0); vbox.pack_start(menu_bar.as_ref().unwrap(), false, false, 0);
vbox.pack_start(&menu_bar, false, false, 0);
w.add(&vbox);
vbox.show_all(); vbox.show_all();
let vbox = Rc::new(vbox); Rc::clone(vbox)
let vbox_c = Rc::clone(&vbox); }
inner.gtk_items.push((menu_bar, vbox)); pub fn remove_for_gtk_window<W>(&self, w: &W)
where
W: IsA<gtk::Container>,
W: IsA<gtk::Window>,
{
let mut inner = self.0.borrow_mut();
vbox_c if let Some((menu_bar, vbox)) = inner.gtk_items.get(&(w.as_ptr() as _)) {
vbox.remove(menu_bar.as_ref().unwrap());
w.remove_accel_group(&inner.accel_group);
let vbox = Rc::clone(vbox);
inner.gtk_items.insert(w.as_ptr() as _, (None, vbox));
}
}
pub fn hide_for_gtk_window<W>(&self, w: &W)
where
W: IsA<gtk::Container>,
W: IsA<gtk::Window>,
{
if let Some((menu_bar, _)) = self.0.borrow().gtk_items.get(&(w.as_ptr() as isize)) {
if let Some(menu_bar) = menu_bar {
menu_bar.hide();
}
}
}
pub fn show_for_gtk_window<W>(&self, w: &W)
where
W: IsA<gtk::Container>,
W: IsA<gtk::Window>,
{
if let Some((menu_bar, _)) = self.0.borrow().gtk_items.get(&(w.as_ptr() as isize)) {
if let Some(menu_bar) = menu_bar {
menu_bar.show_all();
}
}
} }
} }

View file

@ -41,6 +41,12 @@ impl Menu {
NSApp().setMainMenu_(self.0); NSApp().setMainMenu_(self.0);
} }
} }
pub fn remove_for_nsapp(&self) {
unsafe {
NSApp().setMainMenu_(std::ptr::null_mut());
}
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -9,7 +9,7 @@ use util::{decode_wide, encode_wide, LOWORD};
use windows_sys::Win32::{ use windows_sys::Win32::{
Foundation::{HWND, LPARAM, LRESULT, WPARAM}, Foundation::{HWND, LPARAM, LRESULT, WPARAM},
UI::{ UI::{
Shell::{DefSubclassProc, SetWindowSubclass}, Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass},
WindowsAndMessaging::{ WindowsAndMessaging::{
AppendMenuW, CreateAcceleratorTableW, CreateMenu, EnableMenuItem, GetMenuItemInfoW, AppendMenuW, CreateAcceleratorTableW, CreateMenu, EnableMenuItem, GetMenuItemInfoW,
SetMenu, SetMenuItemInfoW, ACCEL, HACCEL, HMENU, MENUITEMINFOW, MFS_DISABLED, SetMenu, SetMenuItemInfoW, ACCEL, HACCEL, HMENU, MENUITEMINFOW, MFS_DISABLED,
@ -21,6 +21,7 @@ use windows_sys::Win32::{
use self::accelerator::parse_accelerator; use self::accelerator::parse_accelerator;
static COUNTER: Counter = Counter::new_with_start(563); static COUNTER: Counter = Counter::new_with_start(563);
const MENU_SUBCLASS_ID: usize = 232;
struct InnerMenu { struct InnerMenu {
hmenu: HMENU, hmenu: HMENU,
@ -64,7 +65,7 @@ impl Menu {
pub fn init_for_hwnd(&self, hwnd: isize) { pub fn init_for_hwnd(&self, hwnd: isize) {
unsafe { unsafe {
SetMenu(hwnd, self.0.borrow().hmenu); SetMenu(hwnd, self.0.borrow().hmenu);
SetWindowSubclass(hwnd, Some(menu_subclass_proc), 22, 0); SetWindowSubclass(hwnd, Some(menu_subclass_proc), MENU_SUBCLASS_ID, 0);
}; };
} }
@ -78,6 +79,25 @@ impl Menu {
CreateAcceleratorTableW(inner.accelerators.as_ptr(), inner.accelerators.len() as _) CreateAcceleratorTableW(inner.accelerators.as_ptr(), inner.accelerators.len() as _)
}; };
} }
pub fn remove_for_hwnd(&self, hwnd: isize) {
unsafe {
RemoveWindowSubclass(hwnd, Some(menu_subclass_proc), MENU_SUBCLASS_ID);
SetMenu(hwnd, 0);
}
}
pub fn hide_for_hwnd(&self, hwnd: isize) {
unsafe {
SetMenu(hwnd, 0);
}
}
pub fn show_for_hwnd(&self, hwnd: isize) {
unsafe {
SetMenu(hwnd, self.0.borrow().hmenu);
}
}
} }
#[derive(Clone)] #[derive(Clone)]