Add role property for MenuItem

This commit is contained in:
Cheng Zhao 2015-09-01 19:48:11 +08:00
parent dd2ed559cf
commit d830badc57
9 changed files with 89 additions and 38 deletions

View file

@ -107,6 +107,10 @@ void Menu::SetSublabel(int index, const base::string16& sublabel) {
model_->SetSublabel(index, sublabel); model_->SetSublabel(index, sublabel);
} }
void Menu::SetRole(int index, const base::string16& role) {
model_->SetRole(index, role);
}
void Menu::Clear() { void Menu::Clear() {
model_->Clear(); model_->Clear();
} }
@ -154,6 +158,7 @@ void Menu::BuildPrototype(v8::Isolate* isolate,
.SetMethod("insertSubMenu", &Menu::InsertSubMenuAt) .SetMethod("insertSubMenu", &Menu::InsertSubMenuAt)
.SetMethod("setIcon", &Menu::SetIcon) .SetMethod("setIcon", &Menu::SetIcon)
.SetMethod("setSublabel", &Menu::SetSublabel) .SetMethod("setSublabel", &Menu::SetSublabel)
.SetMethod("setRole", &Menu::SetRole)
.SetMethod("clear", &Menu::Clear) .SetMethod("clear", &Menu::Clear)
.SetMethod("getIndexOfCommandId", &Menu::GetIndexOfCommandId) .SetMethod("getIndexOfCommandId", &Menu::GetIndexOfCommandId)
.SetMethod("getItemCount", &Menu::GetItemCount) .SetMethod("getItemCount", &Menu::GetItemCount)

View file

@ -73,6 +73,7 @@ class Menu : public mate::Wrappable,
Menu* menu); Menu* menu);
void SetIcon(int index, const gfx::Image& image); void SetIcon(int index, const gfx::Image& image);
void SetSublabel(int index, const base::string16& sublabel); void SetSublabel(int index, const base::string16& sublabel);
void SetRole(int index, const base::string16& role);
void Clear(); void Clear();
int GetIndexOfCommandId(int command_id); int GetIndexOfCommandId(int command_id);
int GetItemCount() const; int GetItemCount() const;

View file

@ -9,12 +9,13 @@ class MenuItem
constructor: (options) -> constructor: (options) ->
Menu = require 'menu' Menu = require 'menu'
{click, @selector, @type, @label, @sublabel, @accelerator, @icon, @enabled, @visible, @checked, @submenu} = options {click, @selector, @type, @role, @label, @sublabel, @accelerator, @icon, @enabled, @visible, @checked, @submenu} = options
@type = 'submenu' if not @type? and @submenu? @type = 'submenu' if not @type? and @submenu?
throw new Error('Invalid submenu') if @type is 'submenu' and @submenu?.constructor isnt Menu throw new Error('Invalid submenu') if @type is 'submenu' and @submenu?.constructor isnt Menu
@overrideReadOnlyProperty 'type', 'normal' @overrideReadOnlyProperty 'type', 'normal'
@overrideReadOnlyProperty 'role'
@overrideReadOnlyProperty 'accelerator' @overrideReadOnlyProperty 'accelerator'
@overrideReadOnlyProperty 'icon' @overrideReadOnlyProperty 'icon'
@overrideReadOnlyProperty 'submenu' @overrideReadOnlyProperty 'submenu'

View file

@ -115,6 +115,7 @@ Menu::insert = (pos, item) ->
@setSublabel pos, item.sublabel if item.sublabel? @setSublabel pos, item.sublabel if item.sublabel?
@setIcon pos, item.icon if item.icon? @setIcon pos, item.icon if item.icon?
@setRole pos, item.role if item.role?
# Make menu accessable to items. # Make menu accessable to items.
item.overrideReadOnlyProperty 'menu', this item.overrideReadOnlyProperty 'menu', this

View file

@ -44,13 +44,14 @@ app.once('ready', function() {
submenu: [ submenu: [
{ {
label: 'About Electron', label: 'About Electron',
selector: 'orderFrontStandardAboutPanel:' role: 'about'
}, },
{ {
type: 'separator' type: 'separator'
}, },
{ {
label: 'Services', label: 'Services',
role: 'services',
submenu: [] submenu: []
}, },
{ {
@ -59,16 +60,16 @@ app.once('ready', function() {
{ {
label: 'Hide Electron', label: 'Hide Electron',
accelerator: 'Command+H', accelerator: 'Command+H',
selector: 'hide:' role: 'hide'
}, },
{ {
label: 'Hide Others', label: 'Hide Others',
accelerator: 'Command+Shift+H', accelerator: 'Command+Shift+H',
selector: 'hideOtherApplications:' role: 'hideothers:'
}, },
{ {
label: 'Show All', label: 'Show All',
selector: 'unhideAllApplications:' role: 'unhide:'
}, },
{ {
type: 'separator' type: 'separator'
@ -86,12 +87,12 @@ app.once('ready', function() {
{ {
label: 'Undo', label: 'Undo',
accelerator: 'Command+Z', accelerator: 'Command+Z',
selector: 'undo:' role: 'undo'
}, },
{ {
label: 'Redo', label: 'Redo',
accelerator: 'Shift+Command+Z', accelerator: 'Shift+Command+Z',
selector: 'redo:' role: 'redo'
}, },
{ {
type: 'separator' type: 'separator'
@ -99,22 +100,22 @@ app.once('ready', function() {
{ {
label: 'Cut', label: 'Cut',
accelerator: 'Command+X', accelerator: 'Command+X',
selector: 'cut:' role: 'cut'
}, },
{ {
label: 'Copy', label: 'Copy',
accelerator: 'Command+C', accelerator: 'Command+C',
selector: 'copy:' role: 'copy'
}, },
{ {
label: 'Paste', label: 'Paste',
accelerator: 'Command+V', accelerator: 'Command+V',
selector: 'paste:' role: 'paste'
}, },
{ {
label: 'Select All', label: 'Select All',
accelerator: 'Command+A', accelerator: 'Command+A',
selector: 'selectAll:' role: 'selectall'
}, },
] ]
}, },
@ -152,28 +153,30 @@ app.once('ready', function() {
}, },
{ {
label: 'Window', label: 'Window',
role: 'window',
submenu: [ submenu: [
{ {
label: 'Minimize', label: 'Minimize',
accelerator: 'Command+M', accelerator: 'Command+M',
selector: 'performMiniaturize:' role: 'minimize'
}, },
{ {
label: 'Close', label: 'Close',
accelerator: 'Command+W', accelerator: 'Command+W',
selector: 'performClose:' role: 'close'
}, },
{ {
type: 'separator' type: 'separator'
}, },
{ {
label: 'Bring All to Front', label: 'Bring All to Front',
selector: 'arrangeInFront:' role: 'front'
}, },
] ]
}, },
{ {
label: 'Help', label: 'Help',
role: 'help',
submenu: [ submenu: [
{ {
label: 'Learn More', label: 'Learn More',

View file

@ -4,6 +4,8 @@
#include "atom/browser/ui/atom_menu_model.h" #include "atom/browser/ui/atom_menu_model.h"
#include "base/stl_util.h"
namespace atom { namespace atom {
AtomMenuModel::AtomMenuModel(Delegate* delegate) AtomMenuModel::AtomMenuModel(Delegate* delegate)
@ -14,6 +16,17 @@ AtomMenuModel::AtomMenuModel(Delegate* delegate)
AtomMenuModel::~AtomMenuModel() { AtomMenuModel::~AtomMenuModel() {
} }
void AtomMenuModel::SetRole(int index, const base::string16& role) {
roles_[index] = role;
}
base::string16 AtomMenuModel::GetRoleAt(int index) {
if (ContainsKey(roles_, index))
return roles_[index];
else
return base::string16();
}
void AtomMenuModel::MenuClosed() { void AtomMenuModel::MenuClosed() {
ui::SimpleMenuModel::MenuClosed(); ui::SimpleMenuModel::MenuClosed();
FOR_EACH_OBSERVER(Observer, observers_, MenuClosed()); FOR_EACH_OBSERVER(Observer, observers_, MenuClosed());

View file

@ -5,6 +5,8 @@
#ifndef ATOM_BROWSER_UI_ATOM_MENU_MODEL_H_ #ifndef ATOM_BROWSER_UI_ATOM_MENU_MODEL_H_
#define ATOM_BROWSER_UI_ATOM_MENU_MODEL_H_ #define ATOM_BROWSER_UI_ATOM_MENU_MODEL_H_
#include <map>
#include "base/observer_list.h" #include "base/observer_list.h"
#include "ui/base/models/simple_menu_model.h" #include "ui/base/models/simple_menu_model.h"
@ -31,12 +33,16 @@ class AtomMenuModel : public ui::SimpleMenuModel {
void AddObserver(Observer* obs) { observers_.AddObserver(obs); } void AddObserver(Observer* obs) { observers_.AddObserver(obs); }
void RemoveObserver(Observer* obs) { observers_.RemoveObserver(obs); } void RemoveObserver(Observer* obs) { observers_.RemoveObserver(obs); }
void SetRole(int index, const base::string16& role);
base::string16 GetRoleAt(int index);
// ui::SimpleMenuModel: // ui::SimpleMenuModel:
void MenuClosed() override; void MenuClosed() override;
private: private:
Delegate* delegate_; // weak ref. Delegate* delegate_; // weak ref.
std::map<int, base::string16> roles_;
ObserverList<Observer> observers_; ObserverList<Observer> observers_;
DISALLOW_COPY_AND_ASSIGN(AtomMenuModel); DISALLOW_COPY_AND_ASSIGN(AtomMenuModel);

View file

@ -59,17 +59,4 @@ class MenuModel;
@end @end
// Exposed only for unit testing, do not call directly.
@interface AtomMenuController (PrivateExposedForTesting)
- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item;
@end
// Protected methods that subclassers can override.
@interface AtomMenuController (Protected)
- (void)addItemToMenu:(NSMenu*)menu
atIndex:(NSInteger)index
fromModel:(ui::MenuModel*)model;
- (NSMenu*)menuFromModel:(ui::MenuModel*)model;
@end
#endif // ATOM_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_ #endif // ATOM_BROWSER_UI_COCOA_ATOM_MENU_CONTROLLER_H_

View file

@ -8,16 +8,36 @@
#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"
#include "base/strings/utf_string_conversions.h"
#include "ui/base/accelerators/accelerator.h" #include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/platform_accelerator_cocoa.h" #include "ui/base/accelerators/platform_accelerator_cocoa.h"
#include "ui/base/l10n/l10n_util_mac.h" #include "ui/base/l10n/l10n_util_mac.h"
#include "ui/events/cocoa/cocoa_event_utils.h" #include "ui/events/cocoa/cocoa_event_utils.h"
#include "ui/gfx/image/image.h" #include "ui/gfx/image/image.h"
@interface AtomMenuController (Private) namespace {
- (void)addSeparatorToMenu:(NSMenu*)menu
atIndex:(int)index; struct Role {
@end SEL selector;
const char* role;
};
Role kRolesMap[] = {
{ @selector(orderFrontStandardAboutPanel:), "about" },
{ @selector(hide:), "hide" },
{ @selector(hideOtherApplications:), "hideothers" },
{ @selector(unhideAllApplications:), "unhide" },
{ @selector(arrangeInFront:), "front" },
{ @selector(undo:), "undo" },
{ @selector(redo:), "redo" },
{ @selector(cut:), "cut" },
{ @selector(copy:), "copy" },
{ @selector(paste:), "paste" },
{ @selector(selectAll:), "selectall" },
{ @selector(performMiniaturize:), "minimize" },
{ @selector(performClose:), "close" },
};
} // namespace
@implementation AtomMenuController @implementation AtomMenuController
@ -101,7 +121,9 @@
// associated with the entry in the model identified by |modelIndex|. // associated with the entry in the model identified by |modelIndex|.
- (void)addItemToMenu:(NSMenu*)menu - (void)addItemToMenu:(NSMenu*)menu
atIndex:(NSInteger)index atIndex:(NSInteger)index
fromModel:(ui::MenuModel*)model { fromModel:(ui::MenuModel*)ui_model {
atom::AtomMenuModel* model = static_cast<atom::AtomMenuModel*>(ui_model);
base::string16 label16 = model->GetLabelAt(index); base::string16 label16 = model->GetLabelAt(index);
NSString* label = l10n_util::FixUpWindowsStyleLabel(label16); NSString* label = l10n_util::FixUpWindowsStyleLabel(label16);
base::scoped_nsobject<NSMenuItem> item( base::scoped_nsobject<NSMenuItem> item(
@ -124,13 +146,13 @@
[submenu setTitle:[item title]]; [submenu setTitle:[item title]];
[item setSubmenu:submenu]; [item setSubmenu:submenu];
// Hack to set window and help menu. // Set submenu's role.
if ([[item title] isEqualToString:@"Window"] && [submenu numberOfItems] > 0) base::string16 role = model->GetRoleAt(index);
if (role == base::ASCIIToUTF16("window"))
[NSApp setWindowsMenu:submenu]; [NSApp setWindowsMenu:submenu];
else if ([[item title] isEqualToString:@"Help"]) else if (role == base::ASCIIToUTF16("help"))
[NSApp setHelpMenu:submenu]; [NSApp setHelpMenu:submenu];
if ([[item title] isEqualToString:@"Services"] && if (role == base::ASCIIToUTF16("services"))
[submenu numberOfItems] == 0)
[NSApp setServicesMenu:submenu]; [NSApp setServicesMenu:submenu];
} else { } else {
// The MenuModel works on indexes so we can't just set the command id as the // The MenuModel works on indexes so we can't just set the command id as the
@ -139,7 +161,6 @@
// model. Setting the target to |self| allows this class to participate // model. Setting the target to |self| allows this class to participate
// in validation of the menu items. // in validation of the menu items.
[item setTag:index]; [item setTag:index];
[item setTarget:self];
NSValue* modelObject = [NSValue valueWithPointer:model]; NSValue* modelObject = [NSValue valueWithPointer:model];
[item setRepresentedObject:modelObject]; // Retains |modelObject|. [item setRepresentedObject:modelObject]; // Retains |modelObject|.
ui::Accelerator accelerator; ui::Accelerator accelerator;
@ -153,6 +174,19 @@
platformAccelerator->modifier_mask()]; platformAccelerator->modifier_mask()];
} }
} }
// Set menu item's role.
base::string16 role = model->GetRoleAt(index);
if (role.empty()) {
[item setTarget:self];
} else {
for (const Role& pair : kRolesMap) {
if (role == base::ASCIIToUTF16(pair.role)) {
[item setAction:pair.selector];
break;
}
}
}
} }
[menu insertItem:item atIndex:index]; [menu insertItem:item atIndex:index];
} }