Merge pull request from electron/menu-callback

Add callback parameter for Menu.popup
This commit is contained in:
Cheng Zhao 2018-01-02 16:20:58 +09:00 committed by GitHub
commit 249bd41e11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 69 additions and 80 deletions

View file

@ -40,7 +40,6 @@ void Menu::AfterInit(v8::Isolate* isolate) {
delegate.Get("getAcceleratorForCommandId", &get_accelerator_);
delegate.Get("executeCommand", &execute_command_);
delegate.Get("menuWillShow", &menu_will_show_);
delegate.Get("menuClosed", &menu_closed_);
}
bool Menu::IsCommandIdChecked(int command_id) const {
@ -76,10 +75,6 @@ void Menu::MenuWillShow(ui::SimpleMenuModel* source) {
menu_will_show_.Run();
}
void Menu::MenuClosed(ui::SimpleMenuModel* source) {
menu_closed_.Run();
}
void Menu::InsertItemAt(
int index, int command_id, const base::string16& label) {
model_->InsertItemAt(index, command_id, label);

View file

@ -52,9 +52,9 @@ class Menu : public mate::TrackableObject<Menu>,
ui::Accelerator* accelerator) const override;
void ExecuteCommand(int command_id, int event_flags) override;
void MenuWillShow(ui::SimpleMenuModel* source) override;
void MenuClosed(ui::SimpleMenuModel* source) override;
virtual void PopupAt(Window* window, int x, int y, int positioning_item) = 0;
virtual void PopupAt(Window* window, int x, int y, int positioning_item,
const base::Closure& callback) = 0;
virtual void ClosePopupAt(int32_t window_id) = 0;
std::unique_ptr<AtomMenuModel> model_;
@ -94,7 +94,6 @@ class Menu : public mate::TrackableObject<Menu>,
base::Callback<v8::Local<v8::Value>(int, bool)> get_accelerator_;
base::Callback<void(v8::Local<v8::Value>, int)> execute_command_;
base::Callback<void()> menu_will_show_;
base::Callback<void()> menu_closed_;
DISALLOW_COPY_AND_ASSIGN(Menu);
};

View file

@ -22,18 +22,20 @@ class MenuMac : public Menu {
protected:
MenuMac(v8::Isolate* isolate, v8::Local<v8::Object> wrapper);
void PopupAt(Window* window, int x, int y, int positioning_item) override;
void PopupAt(Window* window, int x, int y, int positioning_item,
const base::Closure& callback) override;
void PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
int32_t window_id,
int x,
int y,
int positioning_item);
int positioning_item,
base::Closure callback);
void ClosePopupAt(int32_t window_id) override;
private:
friend class Menu;
static void SendActionToFirstResponder(const std::string& action);
void OnClosed(int32_t window_id, base::Closure callback);
scoped_nsobject<AtomMenuController> menu_controller_;

View file

@ -27,14 +27,15 @@ MenuMac::MenuMac(v8::Isolate* isolate, v8::Local<v8::Object> wrapper)
weak_factory_(this) {
}
void MenuMac::PopupAt(Window* window, int x, int y, int positioning_item) {
void MenuMac::PopupAt(Window* window, int x, int y, int positioning_item,
const base::Closure& callback) {
NativeWindow* native_window = window->window();
if (!native_window)
return;
auto popup = base::Bind(&MenuMac::PopupOnUI, weak_factory_.GetWeakPtr(),
native_window->GetWeakPtr(), window->ID(), x, y,
positioning_item);
positioning_item, callback);
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, popup);
}
@ -42,7 +43,8 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
int32_t window_id,
int x,
int y,
int positioning_item) {
int positioning_item,
base::Closure callback) {
if (!native_window)
return;
brightray::InspectableWebContents* web_contents =
@ -50,8 +52,8 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
if (!web_contents)
return;
auto close_callback = base::Bind(&MenuMac::ClosePopupAt,
weak_factory_.GetWeakPtr(), window_id);
auto close_callback = base::Bind(
&MenuMac::OnClosed, weak_factory_.GetWeakPtr(), window_id, callback);
popup_controllers_[window_id] = base::scoped_nsobject<AtomMenuController>(
[[AtomMenuController alloc] initWithModel:model()
useDefaultAccelerator:NO]);
@ -108,14 +110,25 @@ void MenuMac::PopupOnUI(const base::WeakPtr<NativeWindow>& native_window,
}
void MenuMac::ClosePopupAt(int32_t window_id) {
auto it = popup_controllers_.find(window_id);
if (it != popup_controllers_.end()) {
popup_controllers_.erase(it);
auto controller = popup_controllers_.find(window_id);
if (controller != popup_controllers_.end()) {
// Close the controller for the window.
[controller->second cancel];
} else if (window_id == -1) {
popup_controllers_.clear();
// Or just close all opened controllers.
for (auto it = popup_controllers_.begin();
it != popup_controllers_.end();) {
// The iterator is invalidated after the call.
[(it++)->second cancel];
}
}
}
void MenuMac::OnClosed(int32_t window_id, base::Closure callback) {
popup_controllers_.erase(window_id);
callback.Run();
}
// static
void Menu::SetApplicationMenu(Menu* base_menu) {
MenuMac* menu = static_cast<MenuMac*>(base_menu);

View file

@ -6,7 +6,8 @@
#include "atom/browser/native_window_views.h"
#include "atom/browser/unresponsive_suppressor.h"
#include "content/public/browser/render_widget_host_view.h"
#include "brightray/browser/inspectable_web_contents.h"
#include "brightray/browser/inspectable_web_contents_view.h"
#include "ui/display/screen.h"
using views::MenuRunner;
@ -20,23 +21,22 @@ MenuViews::MenuViews(v8::Isolate* isolate, v8::Local<v8::Object> wrapper)
weak_factory_(this) {
}
void MenuViews::PopupAt(Window* window, int x, int y, int positioning_item) {
void MenuViews::PopupAt(Window* window, int x, int y, int positioning_item,
const base::Closure& callback) {
NativeWindow* native_window = static_cast<NativeWindow*>(window->window());
if (!native_window)
return;
content::WebContents* web_contents = native_window->web_contents();
auto* web_contents = native_window->inspectable_web_contents();
if (!web_contents)
return;
content::RenderWidgetHostView* view = web_contents->GetRenderWidgetHostView();
if (!view)
return;
// (-1, -1) means showing on mouse location.
gfx::Point location;
if (x == -1 || y == -1) {
location = display::Screen::GetScreen()->GetCursorScreenPoint();
} else {
gfx::Point origin = view->GetViewBounds().origin();
auto* view = web_contents->GetView()->GetWebView();
gfx::Point origin = view->bounds().origin();
location = gfx::Point(origin.x() + x, origin.y() + y);
}
@ -48,7 +48,7 @@ void MenuViews::PopupAt(Window* window, int x, int y, int positioning_item) {
// Show the menu.
int32_t window_id = window->ID();
auto close_callback = base::Bind(
&MenuViews::OnClosed, weak_factory_.GetWeakPtr(), window_id);
&MenuViews::OnClosed, weak_factory_.GetWeakPtr(), window_id, callback);
menu_runners_[window_id] = std::unique_ptr<MenuRunner>(new MenuRunner(
model(), flags, close_callback));
menu_runners_[window_id]->RunMenuAt(
@ -73,8 +73,9 @@ void MenuViews::ClosePopupAt(int32_t window_id) {
}
}
void MenuViews::OnClosed(int32_t window_id) {
void MenuViews::OnClosed(int32_t window_id, base::Closure callback) {
menu_runners_.erase(window_id);
callback.Run();
}
// static

View file

@ -21,11 +21,12 @@ class MenuViews : public Menu {
MenuViews(v8::Isolate* isolate, v8::Local<v8::Object> wrapper);
protected:
void PopupAt(Window* window, int x, int y, int positioning_item) override;
void PopupAt(Window* window, int x, int y, int positioning_item,
const base::Closure& callback) override;
void ClosePopupAt(int32_t window_id) override;
private:
void OnClosed(int32_t window_id);
void OnClosed(int32_t window_id, base::Closure callback);
// window ID -> open context menu
std::map<int32_t, std::unique_ptr<views::MenuRunner>> menu_runners_;

View file

@ -12,12 +12,9 @@
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/accelerators/platform_accelerator_cocoa.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "content/public/browser/browser_thread.h"
#include "ui/events/cocoa/cocoa_event_utils.h"
#include "ui/gfx/image/image.h"
using content::BrowserThread;
namespace {
struct Role {
@ -118,8 +115,9 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
- (void)cancel {
if (isMenuOpen_) {
[menu_ cancelTracking];
model_->MenuWillClose();
isMenuOpen_ = NO;
model_->MenuWillClose();
closeCallback.Run();
}
}
@ -334,11 +332,7 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
if (isMenuOpen_) {
isMenuOpen_ = NO;
model_->MenuWillClose();
// Post async task so that itemSelected runs before the close callback
// deletes the controller from the map which deallocates it
if (!closeCallback.is_null())
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, closeCallback);
closeCallback.Run();
}
}

View file

@ -59,7 +59,7 @@ will become properties of the constructed menu items.
The `menu` object has the following instance methods:
#### `menu.popup([browserWindow, options])`
#### `menu.popup([browserWindow, options, callback])`
* `browserWindow` [BrowserWindow](browser-window.md) (optional) - Default is the focused window.
* `options` Object (optional)
@ -70,6 +70,7 @@ The `menu` object has the following instance methods:
* `positioningItem` Number (optional) _macOS_ - The index of the menu item to
be positioned under the mouse cursor at the specified coordinates. Default
is -1.
* `callback` Function (optional) - Called when menu is closed.
Pops up this menu as a context menu in the [`BrowserWindow`](browser-window.md).
@ -114,10 +115,6 @@ can have a submenu.
Objects created with `new Menu` or returned by `Menu.buildFromTemplate` emit
the following events:
#### Event: 'closed'
Emitted when the menu is closed.
## Examples
The `Menu` class is only available in the main process, but you can also use it

View file

@ -39,9 +39,6 @@ Menu.prototype._init = function () {
const found = this.groupsMap[id].find(item => item.checked) || null
if (!found) v8Util.setHiddenValue(this.groupsMap[id][0], 'checked', true)
}
},
menuClosed: () => {
this.emit('closed')
}
}
}
@ -49,18 +46,26 @@ Menu.prototype._init = function () {
Menu.prototype.popup = function (window, x, y, positioningItem) {
let [newX, newY, newPosition, newWindow] = [x, y, positioningItem, window]
let opts
let callback
// menu.popup(x, y, positioningItem)
if (window != null && !(window instanceof BrowserWindow)) {
[newPosition, newY, newX, newWindow] = [y, x, window, null]
}
// menu.popup([w], x, y, callback)
if (typeof newPosition === 'function') {
callback = newPosition
}
// menu.popup({})
if (window != null && window.constructor === Object) {
opts = window
callback = arguments[1]
// menu.popup(window, {})
} else if (x && typeof x === 'object') {
opts = x
callback = arguments[2]
}
if (opts) {
@ -68,6 +73,9 @@ Menu.prototype.popup = function (window, x, y, positioningItem) {
newY = opts.y
newPosition = opts.positioningItem
}
if (typeof callback !== 'function') {
callback = () => {}
}
// set defaults
if (typeof newX !== 'number') newX = -1
@ -88,7 +96,7 @@ Menu.prototype.popup = function (window, x, y, positioningItem) {
}
}
this.popupAt(newWindow, newX, newY, newPosition)
this.popupAt(newWindow, newX, newY, newPosition, callback)
return { browserWindow: newWindow, x: newX, y: newY, position: newPosition }
}

View file

@ -280,8 +280,7 @@ WebContents.prototype._init = function () {
this.on('pepper-context-menu', function (event, params, callback) {
// Access Menu via electron.Menu to prevent circular require.
const menu = electron.Menu.buildFromTemplate(params.menu)
menu.once('closed', callback)
menu.popup(event.sender.getOwnerBrowserWindow(), params.x, params.y)
menu.popup(event.sender.getOwnerBrowserWindow(), params.x, params.y, callback)
})
// The devtools requests the webContents to reload.

View file

@ -323,36 +323,15 @@ describe('Menu module', () => {
assert.equal(x, 100)
assert.equal(y, 101)
})
})
describe('Menu.closePopup()', () => {
let w = null
let menu
beforeEach((done) => {
w = new BrowserWindow({show: false, width: 200, height: 200})
menu = Menu.buildFromTemplate([
{
label: '1'
}
])
w.loadURL('data:text/html,<html>teszt</html>')
w.webContents.on('dom-ready', () => {
done()
})
it('calls the callback', (done) => {
menu.popup({}, () => done())
menu.closePopup()
})
afterEach(() => {
return closeWindow(w).then(() => { w = null })
})
it('emits closed event', (done) => {
menu.popup(w, {x: 100, y: 100})
menu.on('closed', () => {
done()
})
menu.closePopup(w)
it('works with old style', (done) => {
menu.popup(w, 100, 101, () => done())
menu.closePopup()
})
})

View file

@ -68,10 +68,11 @@
// npm run test -match=menu
const moduleMatch = process.env.npm_config_match
? new RegExp(process.env.npm_config_match, 'g')
: /.*/gi
: null
walker.on('file', (file) => {
if (/-spec\.js$/.test(file) && moduleMatch.test(file) && !file.includes(crashSpec)) {
if (/-spec\.js$/.test(file) && !file.includes(crashSpec) &&
(!moduleMatch || moduleMatch.test(file))) {
mocha.addFile(file)
}
})