mirror of
https://github.com/italicsjenga/muda.git
synced 2024-12-23 20:11:29 +11:00
feat: add menu hide, show and remove (#8)
This commit is contained in:
parent
6b98160e49
commit
0201895d74
|
@ -1,4 +1,4 @@
|
||||||
# muda
|
# muda
|
||||||
|
|
||||||
Menu Utilities for Desktop Applications.
|
Menu Utilities for Desktop Applications.
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
64
src/lib.rs
64
src/lib.rs
|
@ -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`].
|
||||||
|
|
|
@ -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();
|
||||||
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);
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
Loading…
Reference in a new issue