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

@ -1,4 +1,4 @@
# muda
# muda
Menu Utilities for Desktop Applications.

View file

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

View file

@ -123,6 +123,10 @@ impl Menu {
/// ## Safety:
///
/// 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")]
pub fn init_for_gtk_window<W>(&self, w: &W) -> std::rc::Rc<gtk::Box>
where
@ -168,11 +172,69 @@ impl Menu {
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")]
pub fn init_for_nsapp(&self) {
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`].

View file

@ -1,10 +1,9 @@
mod accelerator;
use crate::counter::Counter;
use accelerator::{to_gtk_accelerator, to_gtk_menemenoic};
use gtk::{prelude::*, Orientation};
use std::{cell::RefCell, rc::Rc};
use self::accelerator::{to_gtk_accelerator, to_gtk_menemenoic};
use std::{cell::RefCell, collections::HashMap, rc::Rc};
static COUNTER: Counter = Counter::new();
@ -36,7 +35,7 @@ struct InnerMenu {
// multiple times, and thus can't be used in multiple windows, entry
// 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.
gtk_items: Vec<(gtk::MenuBar, Rc<gtk::Box>)>,
gtk_items: HashMap<isize, (Option<gtk::MenuBar>, Rc<gtk::Box>)>,
accel_group: gtk::AccelGroup,
}
@ -47,7 +46,7 @@ impl Menu {
pub fn new() -> Self {
Self(Rc::new(RefCell::new(InnerMenu {
entries: Vec::new(),
gtk_items: Vec::new(),
gtk_items: HashMap::new(),
accel_group: gtk::AccelGroup::new(),
})))
}
@ -74,21 +73,82 @@ impl Menu {
W: IsA<gtk::Window>,
{
let mut inner = self.0.borrow_mut();
let menu_bar = gtk::MenuBar::new();
add_entries_to_menu(&menu_bar, &inner.entries, &inner.accel_group);
// 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 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);
let vbox = gtk::Box::new(Orientation::Vertical, 0);
vbox.pack_start(&menu_bar, false, false, 0);
w.add(&vbox);
vbox.pack_start(menu_bar.as_ref().unwrap(), false, false, 0);
vbox.show_all();
let vbox = Rc::new(vbox);
let vbox_c = Rc::clone(&vbox);
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);
}
}
pub fn remove_for_nsapp(&self) {
unsafe {
NSApp().setMainMenu_(std::ptr::null_mut());
}
}
}
#[derive(Debug, Clone)]

View file

@ -9,7 +9,7 @@ use util::{decode_wide, encode_wide, LOWORD};
use windows_sys::Win32::{
Foundation::{HWND, LPARAM, LRESULT, WPARAM},
UI::{
Shell::{DefSubclassProc, SetWindowSubclass},
Shell::{DefSubclassProc, RemoveWindowSubclass, SetWindowSubclass},
WindowsAndMessaging::{
AppendMenuW, CreateAcceleratorTableW, CreateMenu, EnableMenuItem, GetMenuItemInfoW,
SetMenu, SetMenuItemInfoW, ACCEL, HACCEL, HMENU, MENUITEMINFOW, MFS_DISABLED,
@ -21,6 +21,7 @@ use windows_sys::Win32::{
use self::accelerator::parse_accelerator;
static COUNTER: Counter = Counter::new_with_start(563);
const MENU_SUBCLASS_ID: usize = 232;
struct InnerMenu {
hmenu: HMENU,
@ -64,7 +65,7 @@ impl Menu {
pub fn init_for_hwnd(&self, hwnd: isize) {
unsafe {
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 _)
};
}
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)]