Merge branch 'feat/macOS-ns-menu' into dev

This commit is contained in:
amrbashir 2022-11-27 12:28:36 +02:00
commit c09eab7ef2
No known key found for this signature in database
GPG key ID: BBD7A47A2003FF33
4 changed files with 75 additions and 23 deletions

View file

@ -209,6 +209,9 @@ pub trait ContextMenu {
/// (the click which triggered the menu to be shown).
#[cfg(target_os = "macos")]
fn show_context_menu_for_nsview(&self, view: cocoa::base::id, x: f64, y: f64);
#[cfg(target_os = "macos")]
fn ns_menu(&self) -> *mut std::ffi::c_void;
}
/// Describes a menu event emitted when a menu item is activated

View file

@ -46,6 +46,11 @@ impl ContextMenu for Menu {
fn show_context_menu_for_nsview(&self, view: cocoa::base::id, x: f64, y: f64) {
self.0.show_context_menu_for_nsview(view, x, y)
}
#[cfg(target_os = "macos")]
fn ns_menu(&self) -> *mut std::ffi::c_void {
self.0.ns_menu()
}
}
impl Menu {

View file

@ -27,7 +27,7 @@ static BLOCK_PTR: &str = "mudaMenuItemBlockPtr";
/// A generic child in a menu
///
/// Be careful when cloning this item and treat it as read-only
#[derive(Debug, Default, PartialEq, Eq)]
#[derive(Debug, PartialEq, Eq)]
#[allow(dead_code)]
struct MenuChild {
// shared fields between submenus and menu items
@ -50,6 +50,25 @@ struct MenuChild {
// submenu fields
children: Option<Vec<Rc<RefCell<MenuChild>>>>,
ns_menus: HashMap<u32, Vec<id>>,
ns_menu: (u32, id),
}
impl Default for MenuChild {
fn default() -> Self {
Self {
type_: Default::default(),
id: Default::default(),
text: Default::default(),
enabled: Default::default(),
ns_menu_items: Default::default(),
accelerator: Default::default(),
predefined_item_type: Default::default(),
checked: Default::default(),
children: Default::default(),
ns_menus: Default::default(),
ns_menu: (0, 0 as _),
}
}
}
impl MenuChild {
@ -242,6 +261,10 @@ impl Menu {
msg_send![self.ns_menu, popUpMenuPositioningItem: nil atLocation: location inView: view]
}
}
pub fn ns_menu(&self) -> *mut std::ffi::c_void {
self.ns_menu as _
}
}
#[derive(Clone)]
@ -254,6 +277,7 @@ impl Submenu {
text: strip_mnemonic(text),
enabled,
children: Some(Vec::new()),
ns_menu: (COUNTER.next(), unsafe { NSMenu::alloc(nil).autorelease() }),
..Default::default()
})))
}
@ -335,6 +359,10 @@ impl Submenu {
ns_menu.addItem_(ns_menu_item);
}
}
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(self_.ns_menu.0);
self_.ns_menu.1.addItem_(ns_menu_item);
self_.children.as_mut().unwrap().push(item_child);
}
AddOp::Insert(position) => {
@ -344,6 +372,10 @@ impl Submenu {
let () = msg_send![ns_menu, insertItem: ns_menu_item atIndex: position as NSInteger];
}
}
let ns_menu_item: *mut Object = item.make_ns_item_for_menu(self_.ns_menu.0);
let () = msg_send![ self_.ns_menu.1, insertItem: ns_menu_item atIndex: position as NSInteger];
self_
.children
.as_mut()
@ -355,29 +387,39 @@ impl Submenu {
}
pub fn remove(&self, item: &dyn crate::MenuItemExt) -> crate::Result<()> {
let mut child = self.0.borrow_mut();
let mut self_ = self.0.borrow_mut();
let item_child: Rc<RefCell<MenuChild>> = item.get_child();
let child: Rc<RefCell<MenuChild>> = item.get_child();
// get a list of instances of the specified NSMenuItem in this menu
if let Some(ns_menu_items) = item_child.borrow_mut().ns_menu_items.remove(&child.id) {
if let Some(ns_menu_items) = child.borrow_mut().ns_menu_items.remove(&self_.id) {
// remove each NSMenuItem from the NSMenu
unsafe {
for item in ns_menu_items {
for menus in child.ns_menus.values() {
for menus in self_.ns_menus.values() {
for &ns_menu in menus {
let () = msg_send![ns_menu, removeItem: item];
}
}
let () = msg_send![self_.ns_menu.1, removeItem: item];
}
}
}
if let Some(ns_menu_items) = child.borrow_mut().ns_menu_items.remove(&self_.ns_menu.0) {
unsafe {
for item in ns_menu_items {
let () = msg_send![self_.ns_menu.1, removeItem: item];
}
}
}
// remove the item from our internal list of children
let children = child.children.as_mut().unwrap();
let children = self_.children.as_mut().unwrap();
let index = children
.iter()
.position(|e| e == &item_child)
.position(|e| e == &child)
.ok_or(crate::Error::NotAChildOfThisMenu)?;
children.remove(index);
@ -422,29 +464,26 @@ impl Submenu {
}
pub fn show_context_menu_for_nsview(&self, view: id, x: f64, y: f64) {
// TODO: this needs to work even if it hasn't already been added to a menu
if let Some(ns_menus) = self.0.borrow().ns_menus.get(&1) {
unsafe {
let window: id = msg_send![view, window];
let scale_factor: CGFloat = msg_send![window, backingScaleFactor];
let view_point = NSPoint::new(x / scale_factor, y / scale_factor);
let view_rect: NSRect = msg_send![view, frame];
let location = NSPoint::new(view_point.x, view_rect.size.height - view_point.y);
msg_send![ns_menus[0], popUpMenuPositioningItem: nil atLocation: location inView: view]
}
msg_send![self.0.borrow().ns_menu.1, popUpMenuPositioningItem: nil atLocation: location inView: view]
}
}
pub fn set_windows_menu_for_nsapp(&self) {
if let Some(ns_menus) = self.0.borrow().ns_menus.get(&1) {
unsafe { NSApp().setWindowsMenu_(ns_menus[0]) }
}
unsafe { NSApp().setWindowsMenu_(self.0.borrow().ns_menu.1) }
}
pub fn set_help_menu_for_nsapp(&self) {
if let Some(ns_menus) = self.0.borrow().ns_menus.get(&1) {
unsafe { msg_send![NSApp(), setHelpMenu: ns_menus[0]] }
unsafe { msg_send![NSApp(), setHelpMenu: self.0.borrow().ns_menu.1] }
}
pub fn ns_menu(&self) -> *mut std::ffi::c_void {
self.0.borrow().ns_menu.1 as _
}
}

View file

@ -54,6 +54,11 @@ impl ContextMenu for Submenu {
fn show_context_menu_for_nsview(&self, view: cocoa::base::id, x: f64, y: f64) {
self.0.show_context_menu_for_nsview(view, x, y)
}
#[cfg(target_os = "macos")]
fn ns_menu(&self) -> *mut std::ffi::c_void {
self.0.ns_menu()
}
}
impl Submenu {