From d830badc571502fe3ea713531f0f98d6042bc8ed Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 1 Sep 2015 19:48:11 +0800 Subject: [PATCH 1/4] Add role property for MenuItem --- atom/browser/api/atom_api_menu.cc | 5 ++ atom/browser/api/atom_api_menu.h | 1 + atom/browser/api/lib/menu-item.coffee | 3 +- atom/browser/api/lib/menu.coffee | 1 + atom/browser/default_app/main.js | 29 +++++----- atom/browser/ui/atom_menu_model.cc | 13 +++++ atom/browser/ui/atom_menu_model.h | 6 ++ atom/browser/ui/cocoa/atom_menu_controller.h | 13 ----- atom/browser/ui/cocoa/atom_menu_controller.mm | 56 +++++++++++++++---- 9 files changed, 89 insertions(+), 38 deletions(-) diff --git a/atom/browser/api/atom_api_menu.cc b/atom/browser/api/atom_api_menu.cc index 356a4d4ce49..9bd724a9612 100644 --- a/atom/browser/api/atom_api_menu.cc +++ b/atom/browser/api/atom_api_menu.cc @@ -107,6 +107,10 @@ void Menu::SetSublabel(int index, const base::string16& sublabel) { model_->SetSublabel(index, sublabel); } +void Menu::SetRole(int index, const base::string16& role) { + model_->SetRole(index, role); +} + void Menu::Clear() { model_->Clear(); } @@ -154,6 +158,7 @@ void Menu::BuildPrototype(v8::Isolate* isolate, .SetMethod("insertSubMenu", &Menu::InsertSubMenuAt) .SetMethod("setIcon", &Menu::SetIcon) .SetMethod("setSublabel", &Menu::SetSublabel) + .SetMethod("setRole", &Menu::SetRole) .SetMethod("clear", &Menu::Clear) .SetMethod("getIndexOfCommandId", &Menu::GetIndexOfCommandId) .SetMethod("getItemCount", &Menu::GetItemCount) diff --git a/atom/browser/api/atom_api_menu.h b/atom/browser/api/atom_api_menu.h index 33aafbc45d3..0d93c0d46be 100644 --- a/atom/browser/api/atom_api_menu.h +++ b/atom/browser/api/atom_api_menu.h @@ -73,6 +73,7 @@ class Menu : public mate::Wrappable, Menu* menu); void SetIcon(int index, const gfx::Image& image); void SetSublabel(int index, const base::string16& sublabel); + void SetRole(int index, const base::string16& role); void Clear(); int GetIndexOfCommandId(int command_id); int GetItemCount() const; diff --git a/atom/browser/api/lib/menu-item.coffee b/atom/browser/api/lib/menu-item.coffee index d8e896f8ffa..7663b32996d 100644 --- a/atom/browser/api/lib/menu-item.coffee +++ b/atom/browser/api/lib/menu-item.coffee @@ -9,12 +9,13 @@ class MenuItem constructor: (options) -> 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? throw new Error('Invalid submenu') if @type is 'submenu' and @submenu?.constructor isnt Menu @overrideReadOnlyProperty 'type', 'normal' + @overrideReadOnlyProperty 'role' @overrideReadOnlyProperty 'accelerator' @overrideReadOnlyProperty 'icon' @overrideReadOnlyProperty 'submenu' diff --git a/atom/browser/api/lib/menu.coffee b/atom/browser/api/lib/menu.coffee index af70af740aa..35335ffcebe 100644 --- a/atom/browser/api/lib/menu.coffee +++ b/atom/browser/api/lib/menu.coffee @@ -115,6 +115,7 @@ Menu::insert = (pos, item) -> @setSublabel pos, item.sublabel if item.sublabel? @setIcon pos, item.icon if item.icon? + @setRole pos, item.role if item.role? # Make menu accessable to items. item.overrideReadOnlyProperty 'menu', this diff --git a/atom/browser/default_app/main.js b/atom/browser/default_app/main.js index fd3a6b596b6..99c4ad03320 100644 --- a/atom/browser/default_app/main.js +++ b/atom/browser/default_app/main.js @@ -44,13 +44,14 @@ app.once('ready', function() { submenu: [ { label: 'About Electron', - selector: 'orderFrontStandardAboutPanel:' + role: 'about' }, { type: 'separator' }, { label: 'Services', + role: 'services', submenu: [] }, { @@ -59,16 +60,16 @@ app.once('ready', function() { { label: 'Hide Electron', accelerator: 'Command+H', - selector: 'hide:' + role: 'hide' }, { label: 'Hide Others', accelerator: 'Command+Shift+H', - selector: 'hideOtherApplications:' + role: 'hideothers:' }, { label: 'Show All', - selector: 'unhideAllApplications:' + role: 'unhide:' }, { type: 'separator' @@ -86,12 +87,12 @@ app.once('ready', function() { { label: 'Undo', accelerator: 'Command+Z', - selector: 'undo:' + role: 'undo' }, { label: 'Redo', accelerator: 'Shift+Command+Z', - selector: 'redo:' + role: 'redo' }, { type: 'separator' @@ -99,22 +100,22 @@ app.once('ready', function() { { label: 'Cut', accelerator: 'Command+X', - selector: 'cut:' + role: 'cut' }, { label: 'Copy', accelerator: 'Command+C', - selector: 'copy:' + role: 'copy' }, { label: 'Paste', accelerator: 'Command+V', - selector: 'paste:' + role: 'paste' }, { label: 'Select All', accelerator: 'Command+A', - selector: 'selectAll:' + role: 'selectall' }, ] }, @@ -152,28 +153,30 @@ app.once('ready', function() { }, { label: 'Window', + role: 'window', submenu: [ { label: 'Minimize', accelerator: 'Command+M', - selector: 'performMiniaturize:' + role: 'minimize' }, { label: 'Close', accelerator: 'Command+W', - selector: 'performClose:' + role: 'close' }, { type: 'separator' }, { label: 'Bring All to Front', - selector: 'arrangeInFront:' + role: 'front' }, ] }, { label: 'Help', + role: 'help', submenu: [ { label: 'Learn More', diff --git a/atom/browser/ui/atom_menu_model.cc b/atom/browser/ui/atom_menu_model.cc index 7d2d5e1b6a1..9add7a22715 100644 --- a/atom/browser/ui/atom_menu_model.cc +++ b/atom/browser/ui/atom_menu_model.cc @@ -4,6 +4,8 @@ #include "atom/browser/ui/atom_menu_model.h" +#include "base/stl_util.h" + namespace atom { AtomMenuModel::AtomMenuModel(Delegate* delegate) @@ -14,6 +16,17 @@ AtomMenuModel::AtomMenuModel(Delegate* delegate) 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() { ui::SimpleMenuModel::MenuClosed(); FOR_EACH_OBSERVER(Observer, observers_, MenuClosed()); diff --git a/atom/browser/ui/atom_menu_model.h b/atom/browser/ui/atom_menu_model.h index 42e0e5dbc53..fe19a8dc251 100644 --- a/atom/browser/ui/atom_menu_model.h +++ b/atom/browser/ui/atom_menu_model.h @@ -5,6 +5,8 @@ #ifndef ATOM_BROWSER_UI_ATOM_MENU_MODEL_H_ #define ATOM_BROWSER_UI_ATOM_MENU_MODEL_H_ +#include + #include "base/observer_list.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 RemoveObserver(Observer* obs) { observers_.RemoveObserver(obs); } + void SetRole(int index, const base::string16& role); + base::string16 GetRoleAt(int index); + // ui::SimpleMenuModel: void MenuClosed() override; private: Delegate* delegate_; // weak ref. + std::map roles_; ObserverList observers_; DISALLOW_COPY_AND_ASSIGN(AtomMenuModel); diff --git a/atom/browser/ui/cocoa/atom_menu_controller.h b/atom/browser/ui/cocoa/atom_menu_controller.h index da10e1b0ba6..f8c48aa5dcb 100644 --- a/atom/browser/ui/cocoa/atom_menu_controller.h +++ b/atom/browser/ui/cocoa/atom_menu_controller.h @@ -59,17 +59,4 @@ class MenuModel; @end -// Exposed only for unit testing, do not call directly. -@interface AtomMenuController (PrivateExposedForTesting) -- (BOOL)validateUserInterfaceItem:(id)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_ diff --git a/atom/browser/ui/cocoa/atom_menu_controller.mm b/atom/browser/ui/cocoa/atom_menu_controller.mm index d799a9f9b64..e3aa78aa248 100644 --- a/atom/browser/ui/cocoa/atom_menu_controller.mm +++ b/atom/browser/ui/cocoa/atom_menu_controller.mm @@ -8,16 +8,36 @@ #include "atom/browser/ui/atom_menu_model.h" #include "base/logging.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/platform_accelerator_cocoa.h" #include "ui/base/l10n/l10n_util_mac.h" #include "ui/events/cocoa/cocoa_event_utils.h" #include "ui/gfx/image/image.h" -@interface AtomMenuController (Private) -- (void)addSeparatorToMenu:(NSMenu*)menu - atIndex:(int)index; -@end +namespace { + +struct Role { + 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 @@ -101,7 +121,9 @@ // associated with the entry in the model identified by |modelIndex|. - (void)addItemToMenu:(NSMenu*)menu atIndex:(NSInteger)index - fromModel:(ui::MenuModel*)model { + fromModel:(ui::MenuModel*)ui_model { + atom::AtomMenuModel* model = static_cast(ui_model); + base::string16 label16 = model->GetLabelAt(index); NSString* label = l10n_util::FixUpWindowsStyleLabel(label16); base::scoped_nsobject item( @@ -124,13 +146,13 @@ [submenu setTitle:[item title]]; [item setSubmenu:submenu]; - // Hack to set window and help menu. - if ([[item title] isEqualToString:@"Window"] && [submenu numberOfItems] > 0) + // Set submenu's role. + base::string16 role = model->GetRoleAt(index); + if (role == base::ASCIIToUTF16("window")) [NSApp setWindowsMenu:submenu]; - else if ([[item title] isEqualToString:@"Help"]) + else if (role == base::ASCIIToUTF16("help")) [NSApp setHelpMenu:submenu]; - if ([[item title] isEqualToString:@"Services"] && - [submenu numberOfItems] == 0) + if (role == base::ASCIIToUTF16("services")) [NSApp setServicesMenu:submenu]; } else { // 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 // in validation of the menu items. [item setTag:index]; - [item setTarget:self]; NSValue* modelObject = [NSValue valueWithPointer:model]; [item setRepresentedObject:modelObject]; // Retains |modelObject|. ui::Accelerator accelerator; @@ -153,6 +174,19 @@ 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]; } From 7d07f10c253e3872c7c39e7a032acdf54316f477 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 1 Sep 2015 23:34:32 +0800 Subject: [PATCH 2/4] Assign actions for roles on Windows and Linux --- atom/browser/api/lib/browser-window.coffee | 6 ++++++ atom/browser/api/lib/menu-item.coffee | 19 ++++++++++++++++--- atom/browser/api/lib/menu.coffee | 3 ++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/atom/browser/api/lib/browser-window.coffee b/atom/browser/api/lib/browser-window.coffee index 68b5932a93b..283e99641f4 100644 --- a/atom/browser/api/lib/browser-window.coffee +++ b/atom/browser/api/lib/browser-window.coffee @@ -62,6 +62,12 @@ BrowserWindow::loadUrl = -> @webContents.loadUrl.apply @webContents, arguments BrowserWindow::send = -> @webContents.send.apply @webContents, arguments # Be compatible with old API. +BrowserWindow::undo = -> @webContents.undo() +BrowserWindow::redo = -> @webContents.redo() +BrowserWindow::cut = -> @webContents.cut() +BrowserWindow::copy = -> @webContents.copy() +BrowserWindow::paste = -> @webContents.paste() +BrowserWindow::selectAll = -> @webContents.selectAll() BrowserWindow::restart = -> @webContents.reload() BrowserWindow::getUrl = -> @webContents.getUrl() BrowserWindow::reload = -> @webContents.reload.apply @webContents, arguments diff --git a/atom/browser/api/lib/menu-item.coffee b/atom/browser/api/lib/menu-item.coffee index 7663b32996d..cfefeec4edb 100644 --- a/atom/browser/api/lib/menu-item.coffee +++ b/atom/browser/api/lib/menu-item.coffee @@ -3,6 +3,17 @@ v8Util = process.atomBinding 'v8_util' nextCommandId = 0 +# Maps role to methods of webContents +rolesMap = + undo: 'undo' + redo: 'redo' + cut: 'cut' + copy: 'copy' + paste: 'paste' + selectall: 'selectAll' + minimize: 'minimize' + close: 'close' + class MenuItem @types = ['normal', 'separator', 'submenu', 'checkbox', 'radio'] @@ -28,12 +39,14 @@ class MenuItem throw new Error("Unknown menu type #{@type}") if MenuItem.types.indexOf(@type) is -1 @commandId = ++nextCommandId - @click = => + @click = (focusedWindow) => # Manually flip the checked flags when clicked. @checked = !@checked if @type in ['checkbox', 'radio'] - if typeof click is 'function' - click this, BrowserWindow.getFocusedWindow() + if @role and rolesMap[@role] and process.platform isnt 'darwin' + focusedWindow?[rolesMap[@role]]() + else if typeof click is 'function' + click this, focusedWindow else if typeof @selector is 'string' Menu.sendActionToFirstResponder @selector diff --git a/atom/browser/api/lib/menu.coffee b/atom/browser/api/lib/menu.coffee index 35335ffcebe..3595ba1fe7d 100644 --- a/atom/browser/api/lib/menu.coffee +++ b/atom/browser/api/lib/menu.coffee @@ -67,7 +67,8 @@ Menu::_init = -> isCommandIdVisible: (commandId) => @commandsMap[commandId]?.visible getAcceleratorForCommandId: (commandId) => @commandsMap[commandId]?.accelerator getIconForCommandId: (commandId) => @commandsMap[commandId]?.icon - executeCommand: (commandId) => @commandsMap[commandId]?.click() + executeCommand: (commandId) => + @commandsMap[commandId]?.click BrowserWindow.getFocusedWindow() menuWillShow: => # Make sure radio groups have at least one menu item seleted. for id, group of @groupsMap From 009b27f5f153ee8ae46508af98456c06e4a1b471 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Tue, 1 Sep 2015 23:34:56 +0800 Subject: [PATCH 3/4] Unify the menu of default app --- atom/browser/default_app/main.js | 395 +++++++++++++------------------ 1 file changed, 165 insertions(+), 230 deletions(-) diff --git a/atom/browser/default_app/main.js b/atom/browser/default_app/main.js index 99c4ad03320..4d854dc35cb 100644 --- a/atom/browser/default_app/main.js +++ b/atom/browser/default_app/main.js @@ -36,241 +36,176 @@ app.once('ready', function() { if (Menu.getApplicationMenu()) return; - var template; + var template = [ + { + label: 'Edit', + submenu: [ + { + label: 'Undo', + accelerator: 'CmdOrCtrl+Z', + role: 'undo' + }, + { + label: 'Redo', + accelerator: 'Shift+CmdOrCtrl+Z', + role: 'redo' + }, + { + type: 'separator' + }, + { + label: 'Cut', + accelerator: 'CmdOrCtrl+X', + role: 'cut' + }, + { + label: 'Copy', + accelerator: 'CmdOrCtrl+C', + role: 'copy' + }, + { + label: 'Paste', + accelerator: 'CmdOrCtrl+V', + role: 'paste' + }, + { + label: 'Select All', + accelerator: 'CmdOrCtrl+A', + role: 'selectall' + }, + ] + }, + { + label: 'View', + submenu: [ + { + label: 'Reload', + accelerator: 'CmdOrCtrl+R', + click: function(item, focusedWindow) { + if (focusedWindow) + focusedWindow.reload(); + } + }, + { + label: 'Toggle Full Screen', + accelerator: (function() { + if (process.platform == 'darwin') + return 'Ctrl+Command+F'; + else + return 'F11'; + })(), + click: function(item, focusedWindow) { + if (focusedWindow) + focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); + } + }, + { + label: 'Toggle Developer Tools', + accelerator: (function() { + if (process.platform == 'darwin') + return 'Alt+Command+I'; + else + return 'Ctrl+Shift+I'; + })(), + click: function(item, focusedWindow) { + if (focusedWindow) + focusedWindow.toggleDevTools(); + } + }, + ] + }, + { + label: 'Window', + role: 'window', + submenu: [ + { + label: 'Minimize', + accelerator: 'CmdOrCtrl+M', + role: 'minimize' + }, + { + label: 'Close', + accelerator: 'CmdOrCtrl+W', + role: 'close' + }, + ] + }, + { + label: 'Help', + role: 'help', + submenu: [ + { + label: 'Learn More', + click: function() { require('shell').openExternal('http://electron.atom.io') } + }, + { + label: 'Documentation', + click: function() { require('shell').openExternal('https://github.com/atom/electron/tree/master/docs#readme') } + }, + { + label: 'Community Discussions', + click: function() { require('shell').openExternal('https://discuss.atom.io/c/electron') } + }, + { + label: 'Search Issues', + click: function() { require('shell').openExternal('https://github.com/atom/electron/issues') } + } + ] + }, + ]; + if (process.platform == 'darwin') { - template = [ + template.unshift({ + label: 'Electron', + submenu: [ + { + label: 'About Electron', + role: 'about' + }, + { + type: 'separator' + }, + { + label: 'Services', + role: 'services', + submenu: [] + }, + { + type: 'separator' + }, + { + label: 'Hide Electron', + accelerator: 'Command+H', + role: 'hide' + }, + { + label: 'Hide Others', + accelerator: 'Command+Shift+H', + role: 'hideothers:' + }, + { + label: 'Show All', + role: 'unhide:' + }, + { + type: 'separator' + }, + { + label: 'Quit', + accelerator: 'Command+Q', + click: function() { app.quit(); } + }, + ] + }); + template[3].submenu.push( { - label: 'Electron', - submenu: [ - { - label: 'About Electron', - role: 'about' - }, - { - type: 'separator' - }, - { - label: 'Services', - role: 'services', - submenu: [] - }, - { - type: 'separator' - }, - { - label: 'Hide Electron', - accelerator: 'Command+H', - role: 'hide' - }, - { - label: 'Hide Others', - accelerator: 'Command+Shift+H', - role: 'hideothers:' - }, - { - label: 'Show All', - role: 'unhide:' - }, - { - type: 'separator' - }, - { - label: 'Quit', - accelerator: 'Command+Q', - click: function() { app.quit(); } - }, - ] + type: 'separator' }, { - label: 'Edit', - submenu: [ - { - label: 'Undo', - accelerator: 'Command+Z', - role: 'undo' - }, - { - label: 'Redo', - accelerator: 'Shift+Command+Z', - role: 'redo' - }, - { - type: 'separator' - }, - { - label: 'Cut', - accelerator: 'Command+X', - role: 'cut' - }, - { - label: 'Copy', - accelerator: 'Command+C', - role: 'copy' - }, - { - label: 'Paste', - accelerator: 'Command+V', - role: 'paste' - }, - { - label: 'Select All', - accelerator: 'Command+A', - role: 'selectall' - }, - ] - }, - { - label: 'View', - submenu: [ - { - label: 'Reload', - accelerator: 'Command+R', - click: function() { - var focusedWindow = BrowserWindow.getFocusedWindow(); - if (focusedWindow) - focusedWindow.reload(); - } - }, - { - label: 'Toggle Full Screen', - accelerator: 'Ctrl+Command+F', - click: function() { - var focusedWindow = BrowserWindow.getFocusedWindow(); - if (focusedWindow) - focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); - } - }, - { - label: 'Toggle Developer Tools', - accelerator: 'Alt+Command+I', - click: function() { - var focusedWindow = BrowserWindow.getFocusedWindow(); - if (focusedWindow) - focusedWindow.toggleDevTools(); - } - }, - ] - }, - { - label: 'Window', - role: 'window', - submenu: [ - { - label: 'Minimize', - accelerator: 'Command+M', - role: 'minimize' - }, - { - label: 'Close', - accelerator: 'Command+W', - role: 'close' - }, - { - type: 'separator' - }, - { - label: 'Bring All to Front', - role: 'front' - }, - ] - }, - { - label: 'Help', - role: 'help', - submenu: [ - { - label: 'Learn More', - click: function() { require('shell').openExternal('http://electron.atom.io') } - }, - { - label: 'Documentation', - click: function() { require('shell').openExternal('https://github.com/atom/electron/tree/master/docs#readme') } - }, - { - label: 'Community Discussions', - click: function() { require('shell').openExternal('https://discuss.atom.io/c/electron') } - }, - { - label: 'Search Issues', - click: function() { require('shell').openExternal('https://github.com/atom/electron/issues') } - } - ] + label: 'Bring All to Front', + role: 'front' } - ]; - } else { - template = [ - { - label: '&File', - submenu: [ - { - label: '&Open', - accelerator: 'Ctrl+O', - }, - { - label: '&Close', - accelerator: 'Ctrl+W', - click: function() { - var focusedWindow = BrowserWindow.getFocusedWindow(); - if (focusedWindow) - focusedWindow.close(); - } - }, - ] - }, - { - label: '&View', - submenu: [ - { - label: '&Reload', - accelerator: 'Ctrl+R', - click: function() { - var focusedWindow = BrowserWindow.getFocusedWindow(); - if (focusedWindow) - focusedWindow.reload(); - } - }, - { - label: 'Toggle &Full Screen', - accelerator: 'F11', - click: function() { - var focusedWindow = BrowserWindow.getFocusedWindow(); - if (focusedWindow) - focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); - } - }, - { - label: 'Toggle &Developer Tools', - accelerator: 'Shift+Ctrl+I', - click: function() { - var focusedWindow = BrowserWindow.getFocusedWindow(); - if (focusedWindow) - focusedWindow.toggleDevTools(); - } - }, - ] - }, - { - label: 'Help', - submenu: [ - { - label: 'Learn More', - click: function() { require('shell').openExternal('http://electron.atom.io') } - }, - { - label: 'Documentation', - click: function() { require('shell').openExternal('https://github.com/atom/electron/tree/master/docs#readme') } - }, - { - label: 'Community Discussions', - click: function() { require('shell').openExternal('https://discuss.atom.io/c/electron') } - }, - { - label: 'Search Issues', - click: function() { require('shell').openExternal('https://github.com/atom/electron/issues') } - } - ] - } - ]; + ); } var menu = Menu.buildFromTemplate(template); From 6bce5b560bc04b9caa720bb89a73f3096a0ac120 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Wed, 2 Sep 2015 09:19:18 +0800 Subject: [PATCH 4/4] docs: The "role" attribute of MenuItem --- docs/api/dialog.md | 3 +- docs/api/menu-item.md | 33 ++++++- docs/api/menu.md | 201 ++++++++++++++++++++++++------------------ 3 files changed, 147 insertions(+), 90 deletions(-) diff --git a/docs/api/dialog.md b/docs/api/dialog.md index d00540e62df..0fadfa37f80 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -48,12 +48,13 @@ selected when you want to limit the user to a specific type. For example: ] } ``` + The `extensions` array should contain extensions without wildcards or dots (e.g. `'png'` is good but `'.png'` and `'*.png'` are bad). To show all files, use the `'*'` wildcard (no other wildcard is supported). If a `callback` is passed, the API call will be asynchronous and the result -wil be passed via `callback(filenames)` +will be passed via `callback(filenames)` **Note:** On Windows and Linux an open dialog can not be both a file selector and a directory selector, so if you set `properties` to diff --git a/docs/api/menu-item.md b/docs/api/menu-item.md index 71b69b850a6..89524d9f235 100644 --- a/docs/api/menu-item.md +++ b/docs/api/menu-item.md @@ -12,9 +12,10 @@ Create a new `MenuItem` with the following method: ### new MenuItem(options) * `options` Object - * `click` Function - Callback when the menu item is clicked - * `selector` String - Call the selector of first responder when clicked (OS - X only) + * `click` Function - Will be called with `click(menuItem, browserWindow)` when + the menu item is clicked + * `role` String - Define the action of the menu item, when specified the + `click` property will be ignored * `type` String - Can be `normal`, `separator`, `submenu`, `checkbox` or `radio` * `label` String @@ -30,3 +31,29 @@ Create a new `MenuItem` with the following method: as a reference to this item by the position attribute. * `position` String - This field allows fine-grained definition of the specific location within a given menu. + +When creating menu items, it is recommended to specify `role` instead of +manually implementing the behavior if there is matching action, so menu can have +best native experience. + +The `role` property can have following values: + +* `undo` +* `redo` +* `cut` +* `copy` +* `paste` +* `selectall` +* `minimize` - Minimize current window +* `close` - Close current window + +On OS X `role` can also have following additional values: + +* `about` - Map to the `orderFrontStandardAboutPanel` action +* `hide` - Map to the `hide` action +* `hideothers` - Map to the `hideOtherApplications` action +* `unhide` - Map to the `unhideAllApplications` action +* `front` - Map to the `arrangeInFront` action +* `window` - The submenu is a "Window" menu +* `help` - The submenu is a "Help" menu +* `services` - The submenu is a "Services" menu diff --git a/docs/api/menu.md b/docs/api/menu.md index 1d6e4135157..49d6361bcf0 100644 --- a/docs/api/menu.md +++ b/docs/api/menu.md @@ -35,68 +35,20 @@ window.addEventListener('contextmenu', function (e) { An example of creating the application menu in the render process with the simple template API: -**Note to Window and Linux users** the `selector` member of each menu item is a -Mac-only [Accelerator option](https://github.com/atom/electron/blob/master/docs/api/accelerator.md). - -```html - - ``` ## Class: Menu @@ -242,29 +272,26 @@ Linux, here are some notes on making your app's menu more native-like. ### Standard Menus On OS X there are many system defined standard menus, like the `Services` and -`Windows` menus. To make your menu a standard menu, you can just set your menu's -label to one of following and Electron will recognize them and make them +`Windows` menus. To make your menu a standard menu, you should set your menu's +`role` to one of following and Electron will recognize them and make them become standard menus: -* `Window` -* `Help` -* `Services` +* `window` +* `help` +* `services` ### Standard Menu Item Actions -OS X has provided standard actions for some menu items (which are called -`selector`s), like `About xxx`, `Hide xxx`, and `Hide Others`. To set the action -of a menu item to a standard action, you can set the `selector` attribute of the -menu item. +OS X has provided standard actions for some menu items, like `About xxx`, +`Hide xxx`, and `Hide Others`. To set the action of a menu item to a standard +action, you should set the `role` attribute of the menu item. ### Main Menu's Name On OS X the label of application menu's first item is always your app's name, no matter what label you set. To change it you have to change your app's name -by modifying your app bundle's `Info.plist` file. See -[About Information Property List Files](https://developer.apple.com/library/ios/documentation/general/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html) -for more information. - +by modifying your app bundle's `Info.plist` file. See [About Information +Property List Files][AboutInformationPropertyListFiles] for more information. ## Menu Item Position @@ -339,3 +366,5 @@ Menu: - 2 - 3 ``` + +[AboutInformationPropertyListFiles]: https://developer.apple.com/library/ios/documentation/general/Reference/InfoPlistKeyReference/Articles/AboutInformationPropertyListFiles.html