From 6a023dc4fe43605a8a03453f62005d0ce7fcdb22 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Thu, 16 Feb 2017 10:58:02 -0800 Subject: [PATCH] Add Menu.closePopup API on macOS --- atom/browser/api/atom_api_menu.cc | 3 ++- atom/browser/api/atom_api_menu.h | 1 + atom/browser/api/atom_api_menu_mac.h | 10 ++++++++-- atom/browser/api/atom_api_menu_mac.mm | 21 +++++++++++++++------ atom/browser/api/atom_api_window.h | 3 ++- atom/browser/ui/cocoa/atom_menu_controller.h | 4 ++++ atom/browser/ui/cocoa/atom_menu_controller.mm | 8 +++++++- lib/browser/api/menu.js | 9 +++++++++ 8 files changed, 48 insertions(+), 11 deletions(-) diff --git a/atom/browser/api/atom_api_menu.cc b/atom/browser/api/atom_api_menu.cc index 627ce60..da208b3 100644 --- a/atom/browser/api/atom_api_menu.cc +++ b/atom/browser/api/atom_api_menu.cc @@ -176,7 +176,8 @@ void Menu::BuildPrototype(v8::Isolate* isolate, .SetMethod("isItemCheckedAt", &Menu::IsItemCheckedAt) .SetMethod("isEnabledAt", &Menu::IsEnabledAt) .SetMethod("isVisibleAt", &Menu::IsVisibleAt) - .SetMethod("popupAt", &Menu::PopupAt); + .SetMethod("popupAt", &Menu::PopupAt) + .SetMethod("closePopupAt", &Menu::ClosePopupAt); } } // namespace api diff --git a/atom/browser/api/atom_api_menu.h b/atom/browser/api/atom_api_menu.h index 772b2f3..59312e1 100644 --- a/atom/browser/api/atom_api_menu.h +++ b/atom/browser/api/atom_api_menu.h @@ -55,6 +55,7 @@ class Menu : public mate::TrackableObject, virtual void PopupAt(Window* window, int x, int y, int positioning_item, bool async) = 0; + virtual void ClosePopupAt(int32_t window_id) = 0; std::unique_ptr model_; Menu* parent_; diff --git a/atom/browser/api/atom_api_menu_mac.h b/atom/browser/api/atom_api_menu_mac.h index 0c5c7f7..7c41cd7 100644 --- a/atom/browser/api/atom_api_menu_mac.h +++ b/atom/browser/api/atom_api_menu_mac.h @@ -7,10 +7,13 @@ #include "atom/browser/api/atom_api_menu.h" +#include #include #import "atom/browser/ui/cocoa/atom_menu_controller.h" +using base::scoped_nsobject; + namespace atom { namespace api { @@ -22,9 +25,12 @@ class MenuMac : public Menu { void PopupAt( Window* window, int x, int y, int positioning_item, bool async) override; void PopupOnUI(const base::WeakPtr& native_window, - int x, int y, int positioning_item, bool async); + int32_t window_id, int x, int y, int positioning_item, + bool async); + void ClosePopupAt(int32_t window_id) override; - base::scoped_nsobject menu_controller_; + scoped_nsobject menu_controller_; + std::map> popup_controllers_; private: friend class Menu; diff --git a/atom/browser/api/atom_api_menu_mac.mm b/atom/browser/api/atom_api_menu_mac.mm index 84a6b8d..9741202 100644 --- a/atom/browser/api/atom_api_menu_mac.mm +++ b/atom/browser/api/atom_api_menu_mac.mm @@ -34,8 +34,8 @@ void MenuMac::PopupAt( return; auto popup = base::Bind(&MenuMac::PopupOnUI, weak_factory_.GetWeakPtr(), - native_window->GetWeakPtr(), x, y, positioning_item, - async); + native_window->GetWeakPtr(), window->ID(), x, y, + positioning_item, async); if (async) BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, popup); else @@ -43,7 +43,8 @@ void MenuMac::PopupAt( } void MenuMac::PopupOnUI(const base::WeakPtr& native_window, - int x, int y, int positioning_item, bool async) { + int32_t window_id, int x, int y, int positioning_item, + bool async) { if (!native_window) return; brightray::InspectableWebContents* web_contents = @@ -51,10 +52,12 @@ void MenuMac::PopupOnUI(const base::WeakPtr& native_window, if (!web_contents) return; - base::scoped_nsobject menu_controller( - [[AtomMenuController alloc] initWithModel:model_.get() + auto close_callback = base::Bind(&MenuMac::ClosePopupAt, + weak_factory_.GetWeakPtr(), window_id); + popup_controllers_[window_id] = base::scoped_nsobject( + [[AtomMenuController alloc] initWithModel:model() useDefaultAccelerator:NO]); - NSMenu* menu = [menu_controller menu]; + NSMenu* menu = [popup_controllers_[window_id] menu]; NSView* view = web_contents->GetView()->GetNativeView(); // Which menu item to show. @@ -91,6 +94,7 @@ void MenuMac::PopupOnUI(const base::WeakPtr& native_window, if (async) { + [popup_controllers_[window_id] setCloseCallback:close_callback]; // Make sure events can be pumped while the menu is up. base::MessageLoop::ScopedNestableTaskAllower allow( base::MessageLoop::current()); @@ -109,9 +113,14 @@ void MenuMac::PopupOnUI(const base::WeakPtr& native_window, // Don't emit unresponsive event when showing menu. atom::UnresponsiveSuppressor suppressor; [menu popUpMenuPositioningItem:item atLocation:position inView:view]; + close_callback.Run(); } } +void MenuMac::ClosePopupAt(int32_t window_id) { + popup_controllers_.erase(window_id); +} + // static void Menu::SetApplicationMenu(Menu* base_menu) { MenuMac* menu = static_cast(base_menu); diff --git a/atom/browser/api/atom_api_window.h b/atom/browser/api/atom_api_window.h index 9908fee..80af78a 100644 --- a/atom/browser/api/atom_api_window.h +++ b/atom/browser/api/atom_api_window.h @@ -51,6 +51,8 @@ class Window : public mate::TrackableObject, NativeWindow* window() const { return window_.get(); } + int32_t ID() const; + protected: Window(v8::Isolate* isolate, v8::Local wrapper, const mate::Dictionary& options); @@ -202,7 +204,6 @@ class Window : public mate::TrackableObject, void SetVibrancy(mate::Arguments* args); - int32_t ID() const; v8::Local WebContents(v8::Isolate* isolate); // Remove this window from parent window's |child_windows_|. diff --git a/atom/browser/ui/cocoa/atom_menu_controller.h b/atom/browser/ui/cocoa/atom_menu_controller.h index af0b276..a230437 100644 --- a/atom/browser/ui/cocoa/atom_menu_controller.h +++ b/atom/browser/ui/cocoa/atom_menu_controller.h @@ -8,6 +8,7 @@ #import +#include "base/callback.h" #include "base/mac/scoped_nsobject.h" #include "base/strings/string16.h" @@ -27,6 +28,7 @@ class AtomMenuModel; base::scoped_nsobject menu_; BOOL isMenuOpen_; BOOL useDefaultAccelerator_; + base::Callback closeCallback; } @property(nonatomic, assign) atom::AtomMenuModel* model; @@ -35,6 +37,8 @@ class AtomMenuModel; // to the contents of the model after calling this will not be noticed. - (id)initWithModel:(atom::AtomMenuModel*)model useDefaultAccelerator:(BOOL)use; +- (void)setCloseCallback:(const base::Callback&)callback; + // Populate current NSMenu with |model|. - (void)populateWithModel:(atom::AtomMenuModel*)model; diff --git a/atom/browser/ui/cocoa/atom_menu_controller.mm b/atom/browser/ui/cocoa/atom_menu_controller.mm index 6bdaa4c..2286b6b 100644 --- a/atom/browser/ui/cocoa/atom_menu_controller.mm +++ b/atom/browser/ui/cocoa/atom_menu_controller.mm @@ -71,6 +71,10 @@ Role kRolesMap[] = { [super dealloc]; } +- (void)setCloseCallback:(const base::Callback&)callback { + closeCallback = callback; +} + - (void)populateWithModel:(atom::AtomMenuModel*)model { if (!menu_) return; @@ -265,8 +269,10 @@ Role kRolesMap[] = { - (void)menuDidClose:(NSMenu*)menu { if (isMenuOpen_) { - model_->MenuWillClose(); isMenuOpen_ = NO; + model_->MenuWillClose(); + if (!closeCallback.is_null()) + closeCallback.Run(); } } diff --git a/lib/browser/api/menu.js b/lib/browser/api/menu.js index e4fda6d..30f3df7 100644 --- a/lib/browser/api/menu.js +++ b/lib/browser/api/menu.js @@ -174,6 +174,15 @@ Menu.prototype.popup = function (window, x, y, positioningItem) { this.popupAt(window, x, y, positioningItem, asyncPopup) } +Menu.prototype.closePopup = function (window) { + if (window == null || window.constructor !== BrowserWindow) { + window = BrowserWindow.getFocusedWindow() + } + if (window != null) { + this.closePopupAt(window.id) + } +} + Menu.prototype.append = function (item) { return this.insert(this.getItemCount(), item) } -- 2.7.4