feat: allow MenuItems to work optionally when hidden (#16853)

* feat: allow MenuItems to work optionally when hidden

* fix: actually include forward_declaration
This commit is contained in:
Shelley Vohr 2019-02-28 17:00:54 +00:00 committed by GitHub
parent 6d55498cc7
commit 544d8a423c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 44 additions and 2 deletions

View file

@ -40,6 +40,7 @@ void Menu::AfterInit(v8::Isolate* isolate) {
delegate.Get("isCommandIdChecked", &is_checked_); delegate.Get("isCommandIdChecked", &is_checked_);
delegate.Get("isCommandIdEnabled", &is_enabled_); delegate.Get("isCommandIdEnabled", &is_enabled_);
delegate.Get("isCommandIdVisible", &is_visible_); delegate.Get("isCommandIdVisible", &is_visible_);
delegate.Get("shouldCommandIdWorkWhenHidden", &works_when_hidden_);
delegate.Get("getAcceleratorForCommandId", &get_accelerator_); delegate.Get("getAcceleratorForCommandId", &get_accelerator_);
delegate.Get("shouldRegisterAcceleratorForCommandId", delegate.Get("shouldRegisterAcceleratorForCommandId",
&should_register_accelerator_); &should_register_accelerator_);
@ -65,6 +66,12 @@ bool Menu::IsCommandIdVisible(int command_id) const {
return is_visible_.Run(GetWrapper(), command_id); return is_visible_.Run(GetWrapper(), command_id);
} }
bool Menu::ShouldCommandIdWorkWhenHidden(int command_id) const {
v8::Locker locker(isolate());
v8::HandleScope handle_scope(isolate());
return works_when_hidden_.Run(GetWrapper(), command_id);
}
bool Menu::GetAcceleratorForCommandIdWithParams( bool Menu::GetAcceleratorForCommandIdWithParams(
int command_id, int command_id,
bool use_default_accelerator, bool use_default_accelerator,
@ -181,6 +188,10 @@ bool Menu::IsVisibleAt(int index) const {
return model_->IsVisibleAt(index); return model_->IsVisibleAt(index);
} }
bool Menu::WorksWhenHiddenAt(int index) const {
return model_->WorksWhenHiddenAt(index);
}
void Menu::OnMenuWillClose() { void Menu::OnMenuWillClose() {
Emit("menu-will-close"); Emit("menu-will-close");
} }
@ -212,6 +223,7 @@ void Menu::BuildPrototype(v8::Isolate* isolate,
.SetMethod("getAcceleratorTextAt", &Menu::GetAcceleratorTextAt) .SetMethod("getAcceleratorTextAt", &Menu::GetAcceleratorTextAt)
.SetMethod("isItemCheckedAt", &Menu::IsItemCheckedAt) .SetMethod("isItemCheckedAt", &Menu::IsItemCheckedAt)
.SetMethod("isEnabledAt", &Menu::IsEnabledAt) .SetMethod("isEnabledAt", &Menu::IsEnabledAt)
.SetMethod("worksWhenHiddenAt", &Menu::WorksWhenHiddenAt)
.SetMethod("isVisibleAt", &Menu::IsVisibleAt) .SetMethod("isVisibleAt", &Menu::IsVisibleAt)
.SetMethod("popupAt", &Menu::PopupAt) .SetMethod("popupAt", &Menu::PopupAt)
.SetMethod("closePopupAt", &Menu::ClosePopupAt); .SetMethod("closePopupAt", &Menu::ClosePopupAt);

View file

@ -47,6 +47,7 @@ class Menu : public mate::TrackableObject<Menu>,
bool IsCommandIdChecked(int command_id) const override; bool IsCommandIdChecked(int command_id) const override;
bool IsCommandIdEnabled(int command_id) const override; bool IsCommandIdEnabled(int command_id) const override;
bool IsCommandIdVisible(int command_id) const override; bool IsCommandIdVisible(int command_id) const override;
bool ShouldCommandIdWorkWhenHidden(int command_id) const override;
bool GetAcceleratorForCommandIdWithParams( bool GetAcceleratorForCommandIdWithParams(
int command_id, int command_id,
bool use_default_accelerator, bool use_default_accelerator,
@ -96,11 +97,13 @@ class Menu : public mate::TrackableObject<Menu>,
bool IsItemCheckedAt(int index) const; bool IsItemCheckedAt(int index) const;
bool IsEnabledAt(int index) const; bool IsEnabledAt(int index) const;
bool IsVisibleAt(int index) const; bool IsVisibleAt(int index) const;
bool WorksWhenHiddenAt(int index) const;
// Stored delegate methods. // Stored delegate methods.
base::Callback<bool(v8::Local<v8::Value>, int)> is_checked_; base::Callback<bool(v8::Local<v8::Value>, int)> is_checked_;
base::Callback<bool(v8::Local<v8::Value>, int)> is_enabled_; base::Callback<bool(v8::Local<v8::Value>, int)> is_enabled_;
base::Callback<bool(v8::Local<v8::Value>, int)> is_visible_; base::Callback<bool(v8::Local<v8::Value>, int)> is_visible_;
base::Callback<bool(v8::Local<v8::Value>, int)> works_when_hidden_;
base::Callback<v8::Local<v8::Value>(v8::Local<v8::Value>, int, bool)> base::Callback<v8::Local<v8::Value>(v8::Local<v8::Value>, int, bool)>
get_accelerator_; get_accelerator_;
base::Callback<bool(v8::Local<v8::Value>, int)> should_register_accelerator_; base::Callback<bool(v8::Local<v8::Value>, int)> should_register_accelerator_;

View file

@ -46,6 +46,13 @@ typedef NS_ENUM(NSInteger, AVAuthorizationStatusMac) {
AVAuthorizationStatusAuthorizedMac = 3, AVAuthorizationStatusAuthorizedMac = 3,
}; };
@interface NSMenuItem (HighSierraSDK)
@property(atomic, readwrite)
BOOL allowsKeyEquivalentWhenHidden API_AVAILABLE(macosx(10.13));
- (void)setAllowsKeyEquivalentWhenHidden:(BOOL)arg1
API_AVAILABLE(macosx(10.13));
@end
@interface AVCaptureDevice (MojaveSDK) @interface AVCaptureDevice (MojaveSDK)
+ (void)requestAccessForMediaType:(AVMediaType)mediaType + (void)requestAccessForMediaType:(AVMediaType)mediaType
completionHandler:(void (^)(BOOL granted))handler completionHandler:(void (^)(BOOL granted))handler

View file

@ -51,6 +51,13 @@ bool AtomMenuModel::ShouldRegisterAcceleratorAt(int index) const {
return true; return true;
} }
bool AtomMenuModel::WorksWhenHiddenAt(int index) const {
if (delegate_) {
return delegate_->ShouldCommandIdWorkWhenHidden(GetCommandIdAt(index));
}
return true;
}
void AtomMenuModel::MenuWillClose() { void AtomMenuModel::MenuWillClose() {
ui::SimpleMenuModel::MenuWillClose(); ui::SimpleMenuModel::MenuWillClose();
for (Observer& observer : observers_) { for (Observer& observer : observers_) {

View file

@ -27,6 +27,8 @@ class AtomMenuModel : public ui::SimpleMenuModel {
virtual bool ShouldRegisterAcceleratorForCommandId( virtual bool ShouldRegisterAcceleratorForCommandId(
int command_id) const = 0; int command_id) const = 0;
virtual bool ShouldCommandIdWorkWhenHidden(int command_id) const = 0;
private: private:
// ui::SimpleMenuModel::Delegate: // ui::SimpleMenuModel::Delegate:
bool GetAcceleratorForCommandId( bool GetAcceleratorForCommandId(
@ -57,6 +59,7 @@ class AtomMenuModel : public ui::SimpleMenuModel {
bool use_default_accelerator, bool use_default_accelerator,
ui::Accelerator* accelerator) const; ui::Accelerator* accelerator) const;
bool ShouldRegisterAcceleratorAt(int index) const; bool ShouldRegisterAcceleratorAt(int index) const;
bool WorksWhenHiddenAt(int index) const;
// ui::SimpleMenuModel: // ui::SimpleMenuModel:
void MenuWillClose() override; void MenuWillClose() override;

View file

@ -5,6 +5,7 @@
#import "atom/browser/ui/cocoa/atom_menu_controller.h" #import "atom/browser/ui/cocoa/atom_menu_controller.h"
#include "atom/browser/mac/atom_application.h"
#include "atom/browser/ui/atom_menu_model.h" #include "atom/browser/ui/atom_menu_model.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/sys_string_conversions.h" #include "base/strings/sys_string_conversions.h"
@ -291,6 +292,11 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
[item setKeyEquivalentModifierMask:modifier_mask]; [item setKeyEquivalentModifierMask:modifier_mask];
} }
if (@available(macOS 10.13, *)) {
[(id)item
setAllowsKeyEquivalentWhenHidden:(model->WorksWhenHiddenAt(index))];
}
// Set menu item's role. // Set menu item's role.
[item setTarget:self]; [item setTarget:self];
if (!role.empty()) { if (!role.empty()) {
@ -307,8 +313,7 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
} }
// Called before the menu is to be displayed to update the state (enabled, // Called before the menu is to be displayed to update the state (enabled,
// radio, etc) of each item in the menu. Also will update the title if // radio, etc) of each item in the menu.
// the item is marked as "dynamic".
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
SEL action = [item action]; SEL action = [item action];
if (action != @selector(itemSelected:)) if (action != @selector(itemSelected:))

View file

@ -24,6 +24,7 @@ See [`Menu`](menu.md) for examples.
* `icon` ([NativeImage](native-image.md) | String) (optional) * `icon` ([NativeImage](native-image.md) | String) (optional)
* `enabled` Boolean (optional) - If false, the menu item will be greyed out and * `enabled` Boolean (optional) - If false, the menu item will be greyed out and
unclickable. unclickable.
* `acceleratorWorksWhenHidden` Boolean (optional) - default is `true`, and when `false` will prevent the accelerator from triggering the item if the item is not visible`. _macOS_
* `visible` Boolean (optional) - If false, the menu item will be entirely hidden. * `visible` Boolean (optional) - If false, the menu item will be entirely hidden.
* `checked` Boolean (optional) - Should only be specified for `checkbox` or `radio` type * `checked` Boolean (optional) - Should only be specified for `checkbox` or `radio` type
menu items. menu items.
@ -48,6 +49,8 @@ See [`Menu`](menu.md) for examples.
the placement of their containing group after the containing group of the item the placement of their containing group after the containing group of the item
with the specified label. with the specified label.
**Note:** `acceleratorWorksWhenHidden` is specified as being macOS-only because accelerators always work when items are hidden on Windows and Linux. The option is exposed to users to give them the option to turn it off, as this is possible in native macOS development. This property is only usable on macOS High Sierra 10.13 or newer.
### Roles ### Roles
Roles allow menu items to have predefined behaviors. Roles allow menu items to have predefined behaviors.

View file

@ -36,6 +36,7 @@ const MenuItem = function (options) {
this.overrideProperty('enabled', true) this.overrideProperty('enabled', true)
this.overrideProperty('visible', true) this.overrideProperty('visible', true)
this.overrideProperty('checked', false) this.overrideProperty('checked', false)
this.overrideProperty('acceleratorWorksWhenHidden', true)
this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role)) this.overrideProperty('registerAccelerator', roles.shouldRegisterAccelerator(this.role))
if (!MenuItem.types.includes(this.type)) { if (!MenuItem.types.includes(this.type)) {

View file

@ -17,6 +17,7 @@ Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype)
const delegate = { const delegate = {
isCommandIdChecked: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].checked : undefined, isCommandIdChecked: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].checked : undefined,
isCommandIdEnabled: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].enabled : undefined, isCommandIdEnabled: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].enabled : undefined,
shouldCommandIdWorkWhenHidden: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].acceleratorWorksWhenHidden : undefined,
isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined, isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined,
getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => { getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => {
const command = menu.commandsMap[id] const command = menu.commandsMap[id]