Add MacOS menu (#1583)

* feat: added MacOS menu

* fix: ran fmt

* extracted function into variable

* idiomatic formatting

* Set the default menu only during app startup

* Don't set the activation policy in the menu init

Co-authored-by: Artur Kovacs <kovacs.artur.barnabas@gmail.com>
This commit is contained in:
Casper Rogild Storm 2021-04-24 16:56:46 +02:00 committed by GitHub
parent 1c4d6e7613
commit e8cdf8b092
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 123 additions and 0 deletions

View file

@ -24,6 +24,7 @@
- On Windows, added `WindowExtWindows::set_enable` to allow creating modal popup windows. - On Windows, added `WindowExtWindows::set_enable` to allow creating modal popup windows.
- On macOS, emit `RedrawRequested` events immediately while the window is being resized. - On macOS, emit `RedrawRequested` events immediately while the window is being resized.
- Implement `Default`, `Hash`, and `Eq` for `LogicalPosition`, `PhysicalPosition`, `LogicalSize`, and `PhysicalSize`. - Implement `Default`, `Hash`, and `Eq` for `LogicalPosition`, `PhysicalPosition`, `LogicalSize`, and `PhysicalSize`.
- On macOS, initialize the Menu Bar with minimal defaults.
# 0.24.0 (2020-12-09) # 0.24.0 (2020-12-09)

View file

@ -25,6 +25,7 @@ use crate::{
platform_impl::platform::{ platform_impl::platform::{
event::{EventProxy, EventWrapper}, event::{EventProxy, EventWrapper},
event_loop::{post_dummy_event, PanicInfo}, event_loop::{post_dummy_event, PanicInfo},
menu,
observer::{CFRunLoopGetMain, CFRunLoopWakeUp, EventLoopWaker}, observer::{CFRunLoopGetMain, CFRunLoopWakeUp, EventLoopWaker},
util::{IdRef, Never}, util::{IdRef, Never},
window::get_window_id, window::get_window_id,
@ -274,6 +275,9 @@ impl AppState {
pub fn launched() { pub fn launched() {
HANDLER.set_ready(); HANDLER.set_ready();
HANDLER.waker().start(); HANDLER.waker().start();
// The menubar initialization should be before the `NewEvents` event, to allow overriding
// of the default menu in the event
menu::initialize();
HANDLER.set_in_callback(true); HANDLER.set_in_callback(true);
HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents( HANDLER.handle_nonuser_event(EventWrapper::StaticEvent(Event::NewEvents(
StartCause::Init, StartCause::Init,

View file

@ -0,0 +1,117 @@
use cocoa::appkit::{
NSApp, NSApplication, NSApplicationActivationPolicyRegular, NSEventModifierFlags, NSMenu,
NSMenuItem,
};
use cocoa::base::{nil, selector};
use cocoa::foundation::{NSAutoreleasePool, NSProcessInfo, NSString};
use objc::{
rc::autoreleasepool,
runtime::{Object, Sel},
};
struct KeyEquivalent<'a> {
key: &'a str,
masks: Option<NSEventModifierFlags>,
}
pub fn initialize() {
autoreleasepool(|| unsafe {
let menubar = NSMenu::new(nil).autorelease();
let app_menu_item = NSMenuItem::new(nil).autorelease();
menubar.addItem_(app_menu_item);
let app = NSApp();
app.setMainMenu_(menubar);
let app_menu = NSMenu::new(nil);
let process_name = NSProcessInfo::processInfo(nil).processName();
// About menu item
let about_item_prefix = NSString::alloc(nil).init_str("About ");
let about_item_title = about_item_prefix.stringByAppendingString_(process_name);
let about_item = menu_item(
about_item_title,
selector("orderFrontStandardAboutPanel:"),
None,
);
// Seperator menu item
let sep_first = NSMenuItem::separatorItem(nil);
// Hide application menu item
let hide_item_prefix = NSString::alloc(nil).init_str("Hide ");
let hide_item_title = hide_item_prefix.stringByAppendingString_(process_name);
let hide_item = menu_item(
hide_item_title,
selector("hide:"),
Some(KeyEquivalent {
key: "h",
masks: None,
}),
);
// Hide other applications menu item
let hide_others_item_title = NSString::alloc(nil).init_str("Hide Others");
let hide_others_item = menu_item(
hide_others_item_title,
selector("hideOtherApplications:"),
Some(KeyEquivalent {
key: "h",
masks: Some(
NSEventModifierFlags::NSAlternateKeyMask
| NSEventModifierFlags::NSCommandKeyMask,
),
}),
);
// Show applications menu item
let show_all_item_title = NSString::alloc(nil).init_str("Show All");
let show_all_item = menu_item(
show_all_item_title,
selector("unhideAllApplications:"),
None,
);
// Seperator menu item
let sep = NSMenuItem::separatorItem(nil);
// Quit application menu item
let quit_item_prefix = NSString::alloc(nil).init_str("Quit ");
let quit_item_title = quit_item_prefix.stringByAppendingString_(process_name);
let quit_item = menu_item(
quit_item_title,
selector("terminate:"),
Some(KeyEquivalent {
key: "q",
masks: None,
}),
);
app_menu.addItem_(about_item);
app_menu.addItem_(sep_first);
app_menu.addItem_(hide_item);
app_menu.addItem_(hide_others_item);
app_menu.addItem_(show_all_item);
app_menu.addItem_(sep);
app_menu.addItem_(quit_item);
app_menu_item.setSubmenu_(app_menu);
});
}
fn menu_item(
title: *mut Object,
selector: Sel,
key_equivalent: Option<KeyEquivalent<'_>>,
) -> *mut Object {
unsafe {
let (key, masks) = match key_equivalent {
Some(ke) => (NSString::alloc(nil).init_str(ke.key), ke.masks),
None => (NSString::alloc(nil).init_str(""), None),
};
let item = NSMenuItem::alloc(nil).initWithTitle_action_keyEquivalent_(title, selector, key);
if let Some(masks) = masks {
item.setKeyEquivalentModifierMask_(masks)
}
item
}
}

View file

@ -7,6 +7,7 @@ mod app_state;
mod event; mod event;
mod event_loop; mod event_loop;
mod ffi; mod ffi;
mod menu;
mod monitor; mod monitor;
mod observer; mod observer;
mod util; mod util;