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
|
@ -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;
|
||||
|
|
64
src/lib.rs
64
src/lib.rs
|
@ -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`].
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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)]
|
||||
|
|
Loading…
Reference in a new issue