From 4b9e3195460919b2e0a5798e97b1031dceaf10be Mon Sep 17 00:00:00 2001 From: Cong Liu Date: Wed, 20 Jul 2016 13:24:51 +0800 Subject: [PATCH] [osx] update menuitems after setting main menu On Mac, the system will add several menu items to Edit menu, like `Emoji & Symbols` or `Start Dictation...`. These items are added after setting main menu. This patch will update menu items after setting main menu. Then developers can iterate items under edit menu and remove corresponding items. fixed #2812 --- nw.gypi | 2 + src/api/base/base_mac.h | 10 ++++ src/api/base/base_mac.mm | 53 +++++++++++++++++++ src/api/menu/menu.h | 4 ++ src/api/menu/menu_mac.mm | 7 +++ src/api/menuitem/menuitem.h | 6 +++ src/api/menuitem/menuitem_mac.mm | 73 ++++++++++++++++++++++++++ src/api/nw_current_window_internal.idl | 9 +++- src/api/nw_object.idl | 1 + src/api/nw_object_api.cc | 11 ++++ src/api/nw_object_api.h | 13 +++++ src/api/nw_window_api.cc | 5 +- src/api/nw_window_api.h | 4 +- src/api/object_manager.cc | 4 +- src/nw_content_mac.h | 4 +- src/nw_content_mac.mm | 33 +++++++++++- src/resources/api_nw_menu.js | 9 ++-- src/resources/api_nw_menuitem.js | 10 ++-- src/resources/api_nw_object.js | 3 ++ src/resources/api_nw_window.js | 16 +++++- 20 files changed, 260 insertions(+), 17 deletions(-) create mode 100644 src/api/base/base_mac.h create mode 100644 src/api/base/base_mac.mm diff --git a/nw.gypi b/nw.gypi index 04d8269204..dfb32b8016 100644 --- a/nw.gypi +++ b/nw.gypi @@ -145,6 +145,8 @@ }], ['OS=="mac"', { 'sources': [ + 'src/api/base/base_mac.h', + 'src/api/base/base_mac.mm', 'src/api/nw_window_api_mac.mm', 'src/api/nw_menu_api_mac.mm', 'src/api/menuitem/menuitem_mac.mm', diff --git a/src/api/base/base_mac.h b/src/api/base/base_mac.h new file mode 100644 index 0000000000..59057577fd --- /dev/null +++ b/src/api/base/base_mac.h @@ -0,0 +1,10 @@ +#ifndef CONTENT_NW_SRC_API_BASE_BASE_MAC_H_ +#define CONTENT_NW_SRC_API_BASE_BASE_MAC_H_ + +#import + +@interface NSObject (AssociatedObject) +@property (nonatomic, assign) void* associatedObject; +@end + +#endif // CONTENT_NW_SRC_API_BASE_BASE_MAC_H_ \ No newline at end of file diff --git a/src/api/base/base_mac.mm b/src/api/base/base_mac.mm new file mode 100644 index 0000000000..537b7d7a2f --- /dev/null +++ b/src/api/base/base_mac.mm @@ -0,0 +1,53 @@ +#import "content/nw/src/api/base/base_mac.h" +#import + +@interface CppWrapper : NSObject { + void* _obj; +} ++ (id) createFromCppObject:(void*) obj; +- (id) initWithCppObject:(void*) obj; +- (void*) obj; +@end + +@implementation CppWrapper + ++ (id) createFromCppObject:(void*) obj { + return [[CppWrapper alloc] initWithCppObject:obj]; +} + +- (id) initWithCppObject:(void*) obj { + _obj = obj; + return self; +} + +- (void*) obj { + return _obj; +} + +@end + +@implementation NSObject (AssociatedObject) +@dynamic associatedObject; + +- (void)setAssociatedObject:(void*)object { + objc_setAssociatedObject(self, @selector(associatedObject), [CppWrapper createFromCppObject:object], OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void*)associatedObject { + CppWrapper* wrapper = objc_getAssociatedObject(self, @selector(associatedObject)); + return wrapper == nil ? nil : [wrapper obj]; +} + +// - (void)setAssociatedCppObject:(void*)obj { +// [self setAssociatedObject: [CppWrapper createFromCppObject:obj]]; +// } + +// - (void*)associatedCppObject { +// id obj = [self associatedObject]; +// if ([obj isKindOfClass: [CppWrapper class]]) { +// return [(CppWrapper*)obj obj]; +// } +// return nullptr; +// } + +@end \ No newline at end of file diff --git a/src/api/menu/menu.h b/src/api/menu/menu.h index 04a79d62c5..7258d6941d 100644 --- a/src/api/menu/menu.h +++ b/src/api/menu/menu.h @@ -98,6 +98,10 @@ class Menu : public Base { const base::ListValue& arguments, content::RenderFrameHost* rvh = nullptr) override; +#if defined(OS_MACOSX) + static Menu* GetMenuFromNative(NSMenu* menu); +#endif + #if defined(OS_WIN) || defined(OS_LINUX) void UpdateKeys(views::FocusManager *focus_manager); ui::NwMenuModel* model() { return menu_model_.get(); } diff --git a/src/api/menu/menu_mac.mm b/src/api/menu/menu_mac.mm index fb34aa1591..d9b69f1e01 100644 --- a/src/api/menu/menu_mac.mm +++ b/src/api/menu/menu_mac.mm @@ -27,18 +27,25 @@ #include "content/public/browser/web_contents.h" #include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/render_frame_host.h" +#include "content/nw/src/api/base/base_mac.h" #include "content/nw/src/api/object_manager.h" #include "content/nw/src/api/menu/menu_delegate_mac.h" #include "content/nw/src/api/menuitem/menuitem.h" namespace nw { +// static +Menu* Menu::GetMenuFromNative(NSMenu* menu) { + return (Menu*)[menu associatedObject]; +} + void Menu::Create(const base::DictionaryValue& option) { menu_ = [[NSMenu alloc] initWithTitle:@"NW Menu"]; [menu_ setAutoenablesItems:NO]; [menu_ setAllowsContextMenuPlugIns:NO]; menu_delegate_ = [[NWMenuDelegate alloc] initWithMenu:this]; [menu_ setDelegate:menu_delegate_]; + [menu_ setAssociatedObject: this]; } void Menu::Destroy() { diff --git a/src/api/menuitem/menuitem.h b/src/api/menuitem/menuitem.h index e8289ece5e..3a1f68eca9 100644 --- a/src/api/menuitem/menuitem.h +++ b/src/api/menuitem/menuitem.h @@ -70,6 +70,11 @@ class MenuItem : public Base { const base::ListValue& arguments, base::ListValue* result) override; +#if defined(OS_MACOSX) + static std::unique_ptr CreateFromNative(NSMenuItem* menu_item, Menu* menu, int index); + static MenuItem* GetMenuItemFromNative(NSMenuItem* menu_item); +#endif + #if defined(OS_WIN) || defined(OS_LINUX) bool AcceleratorPressed(const ui::Accelerator& accelerator) override; bool CanHandleAccelerators() const override; @@ -104,6 +109,7 @@ class MenuItem : public Base { NSMenuItem* menu_item_; MenuItemDelegate* delegate_; bool iconIsTemplate; + bool native_; #elif defined(OS_WIN) || defined(OS_LINUX) friend class MenuDelegate; diff --git a/src/api/menuitem/menuitem_mac.mm b/src/api/menuitem/menuitem_mac.mm index dae813bb1d..6830e07907 100644 --- a/src/api/menuitem/menuitem_mac.mm +++ b/src/api/menuitem/menuitem_mac.mm @@ -20,8 +20,10 @@ #include "content/nw/src/api/menuitem/menuitem.h" +#include "base/strings/sys_string_conversions.h" #include "base/values.h" #import +#include "content/nw/src/api/base/base_mac.h" #include "content/nw/src/api/object_manager.h" #include "content/nw/src/api/menu/menu.h" #include "content/nw/src/api/menuitem/menuitem_delegate_mac.h" @@ -33,6 +35,10 @@ std::string type; option.GetString("type", &type); type_ = type; + native_ = false; + option.GetBoolean("native", &native_); + + if (native_) return; if (type == "separator") { menu_item_ = [NSMenuItem separatorItem]; @@ -97,6 +103,71 @@ if (option.GetInteger("submenu", &menu_id)) SetSubmenu(object_manager()->GetApiObject(menu_id)); } + + [menu_item_ setAssociatedObject: this]; +} + +// static +std::unique_ptr MenuItem::CreateFromNative(NSMenuItem* menu_item, Menu* menu, int index) { + std::unique_ptr options(new base::DictionaryValue()); + + options->SetBoolean("native", true); + + std::string type("normal"); + if ([menu_item isSeparatorItem]) { + type = "separator"; + } if ([menu_item state] == NSOnState) { + type = "checkbox"; + } + options->SetString("type", type); + + options->SetBoolean("checked", [menu_item state] == NSOnState); + + options->SetString("label", base::SysNSStringToUTF8([menu_item title])); + + if ([menu_item image] != nil) { + options->SetString("icon", ""); + options->SetBoolean("iconIsTemplate", [[menu_item image] isTemplate]); + } + + options->SetString("tooltip", base::SysNSStringToUTF8([menu_item toolTip])); + + options->SetBoolean("enabled", [menu_item isEnabled]); + + NSUInteger mask = [menu_item keyEquivalentModifierMask]; + if (mask != 0) { + std::stringstream s; + std::vector modifiers; + if (mask & NSCommandKeyMask) modifiers.push_back("cmd"); + if (mask & NSControlKeyMask) modifiers.push_back("ctrl"); + if (mask & NSAlternateKeyMask) modifiers.push_back("alt"); + if (mask & NSShiftKeyMask) modifiers.push_back("shift"); + std::copy(modifiers.begin(), modifiers.end(), std::ostream_iterator(s, "+")); + std::string str = s.str(); + str.erase(str.length()-1); + options->SetString("modifiers", str); + } + + NSString* key = [menu_item keyEquivalent]; + if (key != nil) { + options->SetString("key", base::SysNSStringToUTF8(key)); + } + + int menuitem_id = ObjectManager::AllocateId(); + options->SetInteger("id", menuitem_id); + + ObjectManager* manager = menu->object_manager(); + manager->OnAllocateObject(menuitem_id, "MenuItem", *options, menu->extension_id_); + MenuItem* item = reinterpret_cast(manager->GetApiObject(menuitem_id)); + item->menu_item_ = menu_item; + [menu_item setAssociatedObject: item]; + + return options; +} + +// static +MenuItem* MenuItem::GetMenuItemFromNative(NSMenuItem* menu_item) { + return (MenuItem*)[menu_item associatedObject]; } void MenuItem::OnClick() { @@ -110,6 +181,8 @@ } void MenuItem::Destroy() { + if (native_) return; + [menu_item_ release]; [delegate_ release]; } diff --git a/src/api/nw_current_window_internal.idl b/src/api/nw_current_window_internal.idl index 33207133dc..a1d1dfac56 100644 --- a/src/api/nw_current_window_internal.idl +++ b/src/api/nw_current_window_internal.idl @@ -14,6 +14,13 @@ namespace nw.currentWindowInternal { [nodoc] DOMString? datatype; [nodoc] long? quality; }; + + dictionary MenuPatch { + long menu; + long index; + object option; + }; + interface Functions { static void close(optional boolean force); static void showDevToolsInternal(optional ShowDevToolsCallback callback); @@ -27,7 +34,7 @@ namespace nw.currentWindowInternal { static bool isKioskInternal(); static void capturePageInternal(optional CapturePageOptions options, optional CapturePageCallback callback); static void clearMenu(); - static void setMenu(long id); + static MenuPatch[] setMenu(long id); static void reloadIgnoringCache(); static double getZoom(); static void setZoom(double level); diff --git a/src/api/nw_object.idl b/src/api/nw_object.idl index 02d4a1fe50..e95ceccbd3 100644 --- a/src/api/nw_object.idl +++ b/src/api/nw_object.idl @@ -6,6 +6,7 @@ [implemented_in="content/nw/src/api/nw_object_api.h"] namespace nw.Obj { interface Functions { + static long allocateId(); static void create(long id, DOMString type, object options); static void destroy(long id); static void callObjectMethod(long id, DOMString type, DOMString method, any[] arguments); diff --git a/src/api/nw_object_api.cc b/src/api/nw_object_api.cc index 86f2c895c7..0e19b0952e 100644 --- a/src/api/nw_object_api.cc +++ b/src/api/nw_object_api.cc @@ -12,6 +12,17 @@ namespace extensions { +NwObjAllocateIdFunction::NwObjAllocateIdFunction() { +} + +NwObjAllocateIdFunction::~NwObjAllocateIdFunction() { +} + +bool NwObjAllocateIdFunction::RunNWSync(base::ListValue* response, std::string* error) { + response->AppendInteger(nw::ObjectManager::AllocateId()); + return true; +} + NwObjCreateFunction::NwObjCreateFunction() { } diff --git a/src/api/nw_object_api.h b/src/api/nw_object_api.h index d6ff488b7c..ebe5e92865 100644 --- a/src/api/nw_object_api.h +++ b/src/api/nw_object_api.h @@ -7,6 +7,19 @@ namespace extensions { +class NwObjAllocateIdFunction : public NWSyncExtensionFunction { + public: + NwObjAllocateIdFunction(); + bool RunNWSync(base::ListValue* response, std::string* error) override; + + protected: + ~NwObjAllocateIdFunction() override; + + DECLARE_EXTENSION_FUNCTION("nw.Obj.allocateId", UNKNOWN) + private: + DISALLOW_COPY_AND_ASSIGN(NwObjAllocateIdFunction); +}; + class NwObjCreateFunction : public NWSyncExtensionFunction { public: NwObjCreateFunction(); diff --git a/src/api/nw_window_api.cc b/src/api/nw_window_api.cc index 0e84c39dfe..013b9381c6 100644 --- a/src/api/nw_window_api.cc +++ b/src/api/nw_window_api.cc @@ -349,7 +349,7 @@ NwCurrentWindowInternalSetMenuFunction::NwCurrentWindowInternalSetMenuFunction() NwCurrentWindowInternalSetMenuFunction::~NwCurrentWindowInternalSetMenuFunction() { } -bool NwCurrentWindowInternalSetMenuFunction::RunAsync() { +bool NwCurrentWindowInternalSetMenuFunction::RunNWSync(base::ListValue* response, std::string* error) { int id = 0; EXTENSION_FUNCTION_VALIDATE(args_->GetInteger(0, &id)); AppWindow* window = getAppWindow(this); @@ -362,7 +362,7 @@ bool NwCurrentWindowInternalSetMenuFunction::RunAsync() { window->menu_ = menu; #if defined(OS_MACOSX) - NWChangeAppMenu(menu); + response->Append(NWChangeAppMenu(menu)); #endif #if defined(OS_LINUX) || defined(OS_WIN) @@ -377,6 +377,7 @@ bool NwCurrentWindowInternalSetMenuFunction::RunAsync() { native_app_window_views->layout_(); native_app_window_views->SchedulePaint(); menu->UpdateKeys( native_app_window_views->widget()->GetFocusManager() ); + response->Append(std::unique_ptr(new base::ListValue())); #endif return true; } diff --git a/src/api/nw_window_api.h b/src/api/nw_window_api.h index 7607b93969..4dd66a6079 100644 --- a/src/api/nw_window_api.h +++ b/src/api/nw_window_api.h @@ -105,7 +105,7 @@ class NwCurrentWindowInternalClearMenuFunction : public AsyncExtensionFunction { DISALLOW_COPY_AND_ASSIGN(NwCurrentWindowInternalClearMenuFunction); }; -class NwCurrentWindowInternalSetMenuFunction : public AsyncExtensionFunction { +class NwCurrentWindowInternalSetMenuFunction : public NWSyncExtensionFunction { public: NwCurrentWindowInternalSetMenuFunction(); @@ -113,7 +113,7 @@ class NwCurrentWindowInternalSetMenuFunction : public AsyncExtensionFunction { ~NwCurrentWindowInternalSetMenuFunction() override; // ExtensionFunction: - bool RunAsync() override; + bool RunNWSync(base::ListValue* response, std::string* error) override; DECLARE_EXTENSION_FUNCTION("nw.currentWindowInternal.setMenu", UNKNOWN) private: diff --git a/src/api/object_manager.cc b/src/api/object_manager.cc index 9e7bf56dd0..080421f531 100644 --- a/src/api/object_manager.cc +++ b/src/api/object_manager.cc @@ -51,7 +51,7 @@ using extensions::EventRouter; namespace nw { IDMap nw::ObjectManager::objects_registry_; -int nw::ObjectManager::next_object_id_ = 1; +int nw::ObjectManager::next_object_id_ = 0; ObjectManager* ObjectManager::Get(content::BrowserContext* context) { return ObjectManagerFactory::GetForBrowserContext(context); @@ -80,7 +80,7 @@ Base* ObjectManager::GetApiObject(int id) { // static int ObjectManager::AllocateId() { - return next_object_id_++; + return ++next_object_id_; } void ObjectManager::OnAllocateObject(int object_id, diff --git a/src/nw_content_mac.h b/src/nw_content_mac.h index 1a032ac9bb..91f7162101 100644 --- a/src/nw_content_mac.h +++ b/src/nw_content_mac.h @@ -1,6 +1,8 @@ #ifndef _CONTENT_NW_CONTENT_MAC_H #define _CONTENT_NW_CONTENT_MAC_H +#include "base/values.h" + namespace nw { class Menu; } @@ -9,6 +11,6 @@ namespace extensions { class NativeAppWindow; } -void NWChangeAppMenu(nw::Menu* menu); +std::unique_ptr NWChangeAppMenu(nw::Menu* menu); void NWSetNSWindowShowInTaskbar(extensions::NativeAppWindow* win, bool show); #endif diff --git a/src/nw_content_mac.mm b/src/nw_content_mac.mm index 494dc0017a..56427e2a58 100644 --- a/src/nw_content_mac.mm +++ b/src/nw_content_mac.mm @@ -3,10 +3,13 @@ #import #include "content/nw/src/api/menu/menu.h" +#include "content/nw/src/api/menuitem/menuitem.h" #include "extensions/browser/app_window/native_app_window.h" #import "ui/gfx/mac/nswindow_frame_controls.h" +#include "chrome/grit/generated_resources.h" +#include "ui/base/l10n/l10n_util_mac.h" -void NWChangeAppMenu(nw::Menu* menu) { +std::unique_ptr NWChangeAppMenu(nw::Menu* menu) { NSMenu *main_menu; if (menu == nil) { @@ -16,6 +19,34 @@ void NWChangeAppMenu(nw::Menu* menu) { } [NSApp setMainMenu:main_menu]; + + std::unique_ptr items(new base::ListValue()); + + if (menu != nil) { + NSString* editMenuTitle = l10n_util::GetNSStringWithFixup(IDS_EDIT_MENU_MAC); + NSInteger editMenuIndex = [main_menu indexOfItemWithTitle:editMenuTitle]; + if (editMenuIndex != -1) { + NSMenuItem* editMenuItem = [main_menu itemAtIndex:editMenuIndex]; + if (editMenuItem != nil && [editMenuItem hasSubmenu]) { + NSMenu* editMenu = [editMenuItem submenu]; + nw::Menu* nwEditMenu = nw::Menu::GetMenuFromNative(editMenu); + NSArray* itemList = [editMenu itemArray]; + for(int i = 0; i < [editMenu numberOfItems]; i++) { + NSMenuItem* nativeItem = [itemList objectAtIndex:i]; + if (!nw::MenuItem::GetMenuItemFromNative(nativeItem)) { + std::unique_ptr options = nw::MenuItem::CreateFromNative(nativeItem, nwEditMenu, i); + std::unique_ptr menuPatch(new base::DictionaryValue); + menuPatch->SetInteger("menu", editMenuIndex); + menuPatch->SetInteger("index", i); + menuPatch->Set("option", std::move(options)); + items->Append(std::move(menuPatch)); + } + } + } + } + } + + return items; } void NWSetNSWindowShowInTaskbar(extensions::NativeAppWindow* win, bool show) { diff --git a/src/resources/api_nw_menu.js b/src/resources/api_nw_menu.js index a9da574aa8..2149245298 100644 --- a/src/resources/api_nw_menu.js +++ b/src/resources/api_nw_menu.js @@ -3,7 +3,6 @@ var forEach = require('utils').forEach; var nw_binding = require('binding').Binding.create('nw.Menu'); var nwNative = requireNative('nw_natives'); var sendRequest = require('sendRequest'); -var contextMenuNatives = requireNative('context_menus'); var messagingNatives = requireNative('messaging_natives'); var Event = require('event_bindings').Event; @@ -17,7 +16,7 @@ function Menu (option) { if (option.type != 'contextmenu' && option.type != 'menubar') throw new TypeError('Invalid menu type: ' + option.type); - var id = contextMenuNatives.GetNextContextMenuId(); + var id = nw.Obj.allocateId(); option.generatedId = id; this.id = id; @@ -39,12 +38,14 @@ Menu.prototype.__defineSetter__('items', function(val) { Menu.prototype.append = function(menu_item) { privates(this).items.push(menu_item); - nw.Obj.callObjectMethod(this.id, 'Menu', 'Append', [ menu_item.id ]); + if (!menu_item.native) + nw.Obj.callObjectMethod(this.id, 'Menu', 'Append', [ menu_item.id ]); }; Menu.prototype.insert = function(menu_item, i) { privates(this).items.splice(i, 0, menu_item); - nw.Obj.callObjectMethod(this.id, 'Menu', 'Insert', [ menu_item.id, i ]); + if (!menu_item.native) + nw.Obj.callObjectMethod(this.id, 'Menu', 'Insert', [ menu_item.id, i ]); } Menu.prototype.remove = function(menu_item) { diff --git a/src/resources/api_nw_menuitem.js b/src/resources/api_nw_menuitem.js index 3a9e556688..102a50f867 100644 --- a/src/resources/api_nw_menuitem.js +++ b/src/resources/api_nw_menuitem.js @@ -3,7 +3,6 @@ var forEach = require('utils').forEach; var nw_binding = require('binding').Binding.create('nw.Menu'); var nwNative = requireNative('nw_natives'); var sendRequest = require('sendRequest'); -var contextMenuNatives = requireNative('context_menus'); var messagingNatives = requireNative('messaging_natives'); var Event = require('event_bindings').Event; var util = nw.require('util'); @@ -79,7 +78,7 @@ function MenuItem(option) { }; } - var id = contextMenuNatives.GetNextContextMenuId(); + var id = option.id || nw.Obj.allocateId(); this.id = id; privates(this).option = option; @@ -96,7 +95,8 @@ function MenuItem(option) { if (!option.hasOwnProperty('modifiers')) option.modifiers = ""; - nw.Obj.create(id, 'MenuItem', option); + if (!option.native) + nw.Obj.create(id, 'MenuItem', option); messagingNatives.BindToGC(this, nw.Obj.destroy.bind(undefined, id), -1); } @@ -202,4 +202,8 @@ MenuItem.prototype.__defineSetter__('submenu', function(val) { nw.Obj.callObjectMethod(this.id, 'MenuItem', 'SetSubmenu', [ val.id ]); }); +MenuItem.prototype.__defineGetter__('native', function() { + return this.handleGetter('native'); +}); + exports.binding = MenuItem; \ No newline at end of file diff --git a/src/resources/api_nw_object.js b/src/resources/api_nw_object.js index 91588ed6ec..d7b47470d7 100644 --- a/src/resources/api_nw_object.js +++ b/src/resources/api_nw_object.js @@ -5,6 +5,9 @@ var sendRequest = require('sendRequest'); nw_binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; + apiFunctions.setHandleRequest('allocateId', function() { + return sendRequest.sendRequestSync(this.name, arguments, this.definition.parameters, {})[0]; + }); apiFunctions.setHandleRequest('create', function() { return sendRequest.sendRequestSync(this.name, arguments, this.definition.parameters, {}); }); diff --git a/src/resources/api_nw_window.js b/src/resources/api_nw_window.js index 33065c900d..284be2d13a 100644 --- a/src/resources/api_nw_window.js +++ b/src/resources/api_nw_window.js @@ -161,6 +161,9 @@ nw_internal.registerCustomHook(function(bindingsAPI) { apiFunctions.setHandleRequest('setPrintSettingsInternal', function() { return sendRequest.sendRequestSync('nw.currentWindowInternal.setPrintSettingsInternal', arguments, this.definition.parameters, {})[0]; }); + apiFunctions.setHandleRequest('setMenu', function() { + return sendRequest.sendRequestSync('nw.currentWindowInternal.setMenu', arguments, this.definition.parameters, {})[0]; + }); }); nw_binding.registerCustomHook(function(bindingsAPI) { @@ -590,6 +593,7 @@ nw_binding.registerCustomHook(function(bindingsAPI) { }, set: function(menu) { if(!menu) { + privates(this).menu = null; currentNWWindowInternal.clearMenu(); return; } @@ -597,7 +601,17 @@ nw_binding.registerCustomHook(function(bindingsAPI) { throw new TypeError('Only menu of type "menubar" can be used as this.window menu'); privates(this).menu = menu; - currentNWWindowInternal.setMenu(menu.id); + var menuPatch = currentNWWindowInternal.setMenu(menu.id); + if (menuPatch.length) { + menuPatch.forEach((patch)=>{ + let menuIndex = patch.menu; + let itemIndex = patch.index; + let menuToPatch = menu.items[menuIndex]; + if (menuToPatch && menuToPatch.submenu) { + menuToPatch.submenu.insert(new nw.MenuItem(patch.option), itemIndex); + } + }); + } } }); Object.defineProperty(NWWindow.prototype, 'window', {