refactor: ginify Menu (#22916)
This commit is contained in:
parent
1d158399a6
commit
6159066c26
14 changed files with 241 additions and 150 deletions
|
@ -10,43 +10,51 @@ const { Menu } = bindings;
|
||||||
let applicationMenu = null;
|
let applicationMenu = null;
|
||||||
let groupIdIndex = 0;
|
let groupIdIndex = 0;
|
||||||
|
|
||||||
Object.setPrototypeOf(Menu.prototype, EventEmitter.prototype);
|
|
||||||
|
|
||||||
// Menu Delegate.
|
|
||||||
// This object should hold no reference to |Menu| to avoid cyclic reference.
|
|
||||||
const delegate = {
|
|
||||||
isCommandIdChecked: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].checked : undefined,
|
|
||||||
isCommandIdEnabled: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].enabled : undefined,
|
|
||||||
shouldCommandIdWorkWhenHidden: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].acceleratorWorksWhenHidden : undefined,
|
|
||||||
isCommandIdVisible: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].visible : undefined,
|
|
||||||
getAcceleratorForCommandId: (menu, id, useDefaultAccelerator) => {
|
|
||||||
const command = menu.commandsMap[id];
|
|
||||||
if (!command) return;
|
|
||||||
if (command.accelerator != null) return command.accelerator;
|
|
||||||
if (useDefaultAccelerator) return command.getDefaultRoleAccelerator();
|
|
||||||
},
|
|
||||||
shouldRegisterAcceleratorForCommandId: (menu, id) => menu.commandsMap[id] ? menu.commandsMap[id].registerAccelerator : undefined,
|
|
||||||
executeCommand: (menu, event, id) => {
|
|
||||||
const command = menu.commandsMap[id];
|
|
||||||
if (!command) return;
|
|
||||||
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents());
|
|
||||||
},
|
|
||||||
menuWillShow: (menu) => {
|
|
||||||
// Ensure radio groups have at least one menu item selected
|
|
||||||
for (const id of Object.keys(menu.groupsMap)) {
|
|
||||||
const found = menu.groupsMap[id].find(item => item.checked) || null;
|
|
||||||
if (!found) v8Util.setHiddenValue(menu.groupsMap[id][0], 'checked', true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Instance Methods */
|
/* Instance Methods */
|
||||||
|
|
||||||
Menu.prototype._init = function () {
|
Menu.prototype._init = function () {
|
||||||
this.commandsMap = {};
|
this.commandsMap = {};
|
||||||
this.groupsMap = {};
|
this.groupsMap = {};
|
||||||
this.items = [];
|
this.items = [];
|
||||||
this.delegate = delegate;
|
};
|
||||||
|
|
||||||
|
Menu.prototype._isCommandIdChecked = function (id) {
|
||||||
|
return this.commandsMap[id] ? this.commandsMap[id].checked : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
Menu.prototype._isCommandIdEnabled = function (id) {
|
||||||
|
return this.commandsMap[id] ? this.commandsMap[id].enabled : undefined;
|
||||||
|
};
|
||||||
|
Menu.prototype._shouldCommandIdWorkWhenHidden = function (id) {
|
||||||
|
return this.commandsMap[id] ? this.commandsMap[id].acceleratorWorksWhenHidden : undefined;
|
||||||
|
};
|
||||||
|
Menu.prototype._isCommandIdVisible = function (id) {
|
||||||
|
return this.commandsMap[id] ? this.commandsMap[id].visible : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
Menu.prototype._getAcceleratorForCommandId = function (id, useDefaultAccelerator) {
|
||||||
|
const command = this.commandsMap[id];
|
||||||
|
if (!command) return;
|
||||||
|
if (command.accelerator != null) return command.accelerator;
|
||||||
|
if (useDefaultAccelerator) return command.getDefaultRoleAccelerator();
|
||||||
|
};
|
||||||
|
|
||||||
|
Menu.prototype._shouldRegisterAcceleratorForCommandId = function (id) {
|
||||||
|
return this.commandsMap[id] ? this.commandsMap[id].registerAccelerator : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
Menu.prototype._executeCommand = function (event, id) {
|
||||||
|
const command = this.commandsMap[id];
|
||||||
|
if (!command) return;
|
||||||
|
command.click(event, TopLevelWindow.getFocusedWindow(), webContents.getFocusedWebContents());
|
||||||
|
};
|
||||||
|
|
||||||
|
Menu.prototype._menuWillShow = function () {
|
||||||
|
// Ensure radio groups have at least one menu item selected
|
||||||
|
for (const id of Object.keys(this.groupsMap)) {
|
||||||
|
const found = this.groupsMap[id].find(item => item.checked) || null;
|
||||||
|
if (!found) v8Util.setHiddenValue(this.groupsMap[id][0], 'checked', true);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Menu.prototype.popup = function (options = {}) {
|
Menu.prototype.popup = function (options = {}) {
|
||||||
|
|
|
@ -93,4 +93,5 @@ gpu_notify_when_dxdiag_request_fails.patch
|
||||||
feat_allow_embedders_to_add_observers_on_created_hunspell.patch
|
feat_allow_embedders_to_add_observers_on_created_hunspell.patch
|
||||||
feat_add_onclose_to_messageport.patch
|
feat_add_onclose_to_messageport.patch
|
||||||
gin_allow_passing_an_objecttemplate_to_objecttemplatebuilder.patch
|
gin_allow_passing_an_objecttemplate_to_objecttemplatebuilder.patch
|
||||||
|
gin_forward_args_when_dispatching.patch
|
||||||
fix_undo_redo_broken_in_webviews.patch
|
fix_undo_redo_broken_in_webviews.patch
|
||||||
|
|
94
patches/chromium/gin_forward_args_when_dispatching.patch
Normal file
94
patches/chromium/gin_forward_args_when_dispatching.patch
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Jeremy Apthorp <jeremya@chromium.org>
|
||||||
|
Date: Wed, 1 Apr 2020 01:06:45 +0000
|
||||||
|
Subject: gin: forward args when dispatching
|
||||||
|
|
||||||
|
This allows passing arguments with move-only semantics.
|
||||||
|
|
||||||
|
Change-Id: I852eb343398e6f763abfe2a44e623f28353d79a5
|
||||||
|
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2129029
|
||||||
|
Commit-Queue: Jeremy Apthorp <jeremya@chromium.org>
|
||||||
|
Reviewed-by: Jeremy Roman <jbroman@chromium.org>
|
||||||
|
Auto-Submit: Jeremy Apthorp <jeremya@chromium.org>
|
||||||
|
Cr-Commit-Position: refs/heads/master@{#755196}
|
||||||
|
|
||||||
|
diff --git a/gin/converter_unittest.cc b/gin/converter_unittest.cc
|
||||||
|
index 6f9205f06a3e4747e70149c440c4548e54045001..162572385bdd8a54612eb64414fe80e0f9d619d1 100644
|
||||||
|
--- a/gin/converter_unittest.cc
|
||||||
|
+++ b/gin/converter_unittest.cc
|
||||||
|
@@ -12,6 +12,7 @@
|
||||||
|
#include "base/stl_util.h"
|
||||||
|
#include "base/strings/string16.h"
|
||||||
|
#include "base/strings/utf_string_conversions.h"
|
||||||
|
+#include "gin/function_template.h"
|
||||||
|
#include "gin/handle.h"
|
||||||
|
#include "gin/public/isolate_holder.h"
|
||||||
|
#include "gin/test/v8_test.h"
|
||||||
|
@@ -224,4 +225,45 @@ TEST_F(ConverterTest, VectorOfWrappables) {
|
||||||
|
EXPECT_THAT(out_value2, testing::ContainerEq(vector));
|
||||||
|
}
|
||||||
|
|
||||||
|
+namespace {
|
||||||
|
+
|
||||||
|
+class MoveOnlyObject {
|
||||||
|
+ public:
|
||||||
|
+ MoveOnlyObject() = default;
|
||||||
|
+ MoveOnlyObject(const MoveOnlyObject&) = delete;
|
||||||
|
+ MoveOnlyObject& operator=(const MoveOnlyObject&) = delete;
|
||||||
|
+
|
||||||
|
+ MoveOnlyObject(MoveOnlyObject&&) noexcept = default;
|
||||||
|
+ MoveOnlyObject& operator=(MoveOnlyObject&&) noexcept = default;
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+} // namespace
|
||||||
|
+
|
||||||
|
+template <>
|
||||||
|
+struct Converter<MoveOnlyObject> {
|
||||||
|
+ static v8::Local<v8::Value> ToV8(v8::Isolate* isolate, MoveOnlyObject in) {
|
||||||
|
+ return v8::Undefined(isolate);
|
||||||
|
+ }
|
||||||
|
+ static bool FromV8(v8::Isolate* isolate,
|
||||||
|
+ v8::Local<v8::Value> val,
|
||||||
|
+ MoveOnlyObject* out) {
|
||||||
|
+ *out = MoveOnlyObject();
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+TEST_F(ConverterTest, MoveOnlyParameters) {
|
||||||
|
+ v8::Isolate* isolate = instance_->isolate();
|
||||||
|
+ v8::HandleScope handle_scope(isolate);
|
||||||
|
+
|
||||||
|
+ auto receives_move_only_obj = [](MoveOnlyObject obj) {};
|
||||||
|
+ auto func_templ = gin::CreateFunctionTemplate(
|
||||||
|
+ isolate, base::BindRepeating(receives_move_only_obj));
|
||||||
|
+
|
||||||
|
+ v8::Local<v8::Context> context = instance_->isolate()->GetCurrentContext();
|
||||||
|
+ auto func = func_templ->GetFunction(context).ToLocalChecked();
|
||||||
|
+ v8::Local<v8::Value> argv[] = {v8::Undefined(isolate)};
|
||||||
|
+ func->Call(context, v8::Undefined(isolate), 1, argv).ToLocalChecked();
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
} // namespace gin
|
||||||
|
diff --git a/gin/function_template.h b/gin/function_template.h
|
||||||
|
index 7edcc9e20dfa6367dde5f58237cca5f4ca637f7a..8c641d934fdeebb9a90f6eb49960e4fe06217913 100644
|
||||||
|
--- a/gin/function_template.h
|
||||||
|
+++ b/gin/function_template.h
|
||||||
|
@@ -166,14 +166,15 @@ class Invoker<std::index_sequence<indices...>, ArgTypes...>
|
||||||
|
template <typename ReturnType>
|
||||||
|
void DispatchToCallback(
|
||||||
|
base::RepeatingCallback<ReturnType(ArgTypes...)> callback) {
|
||||||
|
- args_->Return(callback.Run(ArgumentHolder<indices, ArgTypes>::value...));
|
||||||
|
+ args_->Return(
|
||||||
|
+ callback.Run(std::move(ArgumentHolder<indices, ArgTypes>::value)...));
|
||||||
|
}
|
||||||
|
|
||||||
|
// In C++, you can declare the function foo(void), but you can't pass a void
|
||||||
|
// expression to foo. As a result, we must specialize the case of Callbacks
|
||||||
|
// that have the void return type.
|
||||||
|
void DispatchToCallback(base::RepeatingCallback<void(ArgTypes...)> callback) {
|
||||||
|
- callback.Run(ArgumentHolder<indices, ArgTypes>::value...);
|
||||||
|
+ callback.Run(std::move(ArgumentHolder<indices, ArgTypes>::value)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
|
@ -155,6 +155,9 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
|
||||||
// |media_list_sources|.
|
// |media_list_sources|.
|
||||||
if (!webrtc::DxgiDuplicatorController::Instance()->GetDeviceNames(
|
if (!webrtc::DxgiDuplicatorController::Instance()->GetDeviceNames(
|
||||||
&device_names)) {
|
&device_names)) {
|
||||||
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||||
|
v8::Locker locker(isolate);
|
||||||
|
v8::HandleScope scope(isolate);
|
||||||
gin_helper::CallMethod(this, "_onerror", "Failed to get sources.");
|
gin_helper::CallMethod(this, "_onerror", "Failed to get sources.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -184,10 +187,14 @@ void DesktopCapturer::UpdateSourcesList(DesktopMediaList* list) {
|
||||||
std::back_inserter(captured_sources_));
|
std::back_inserter(captured_sources_));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!capture_window_ && !capture_screen_)
|
if (!capture_window_ && !capture_screen_) {
|
||||||
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||||
|
v8::Locker locker(isolate);
|
||||||
|
v8::HandleScope scope(isolate);
|
||||||
gin_helper::CallMethod(this, "_onfinished", captured_sources_,
|
gin_helper::CallMethod(this, "_onfinished", captured_sources_,
|
||||||
fetch_window_icons_);
|
fetch_window_icons_);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
gin::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate) {
|
gin::Handle<DesktopCapturer> DesktopCapturer::Create(v8::Isolate* isolate) {
|
||||||
|
|
|
@ -16,21 +16,13 @@
|
||||||
#include "shell/common/gin_helper/object_template_builder.h"
|
#include "shell/common/gin_helper/object_template_builder.h"
|
||||||
#include "shell/common/node_includes.h"
|
#include "shell/common/node_includes.h"
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
// We need this map to keep references to currently opened menus.
|
|
||||||
// Without this menus would be destroyed by js garbage collector
|
|
||||||
// even when they are still displayed.
|
|
||||||
std::map<uint32_t, v8::Global<v8::Object>> g_menus;
|
|
||||||
|
|
||||||
} // unnamed namespace
|
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
|
gin::WrapperInfo Menu::kWrapperInfo = {gin::kEmbedderNativeGin};
|
||||||
|
|
||||||
Menu::Menu(gin::Arguments* args) : model_(new ElectronMenuModel(this)) {
|
Menu::Menu(gin::Arguments* args) : model_(new ElectronMenuModel(this)) {
|
||||||
InitWithArgs(args);
|
|
||||||
model_->AddObserver(this);
|
model_->AddObserver(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,74 +32,82 @@ Menu::~Menu() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::AfterInit(v8::Isolate* isolate) {
|
bool InvokeBoolMethod(const Menu* menu,
|
||||||
gin::Dictionary wrappable(isolate, GetWrapper());
|
const char* method,
|
||||||
gin::Dictionary delegate(nullptr);
|
int command_id,
|
||||||
if (!wrappable.Get("delegate", &delegate))
|
bool default_value = false) {
|
||||||
return;
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||||
|
v8::HandleScope scope(isolate);
|
||||||
delegate.Get("isCommandIdChecked", &is_checked_);
|
// We need to cast off const here because GetWrapper() is non-const, but
|
||||||
delegate.Get("isCommandIdEnabled", &is_enabled_);
|
// ui::SimpleMenuModel::Delegate's methods are const.
|
||||||
delegate.Get("isCommandIdVisible", &is_visible_);
|
v8::Local<v8::Value> val = gin_helper::CallMethod(
|
||||||
delegate.Get("shouldCommandIdWorkWhenHidden", &works_when_hidden_);
|
isolate, const_cast<Menu*>(menu), method, command_id);
|
||||||
delegate.Get("getAcceleratorForCommandId", &get_accelerator_);
|
bool ret = false;
|
||||||
delegate.Get("shouldRegisterAcceleratorForCommandId",
|
return gin::ConvertFromV8(isolate, val, &ret) ? ret : default_value;
|
||||||
&should_register_accelerator_);
|
|
||||||
delegate.Get("executeCommand", &execute_command_);
|
|
||||||
delegate.Get("menuWillShow", &menu_will_show_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Menu::IsCommandIdChecked(int command_id) const {
|
bool Menu::IsCommandIdChecked(int command_id) const {
|
||||||
v8::Locker locker(isolate());
|
return InvokeBoolMethod(this, "_isCommandIdChecked", command_id);
|
||||||
v8::HandleScope handle_scope(isolate());
|
|
||||||
return is_checked_.Run(GetWrapper(), command_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Menu::IsCommandIdEnabled(int command_id) const {
|
bool Menu::IsCommandIdEnabled(int command_id) const {
|
||||||
v8::Locker locker(isolate());
|
return InvokeBoolMethod(this, "_isCommandIdEnabled", command_id);
|
||||||
v8::HandleScope handle_scope(isolate());
|
|
||||||
return is_enabled_.Run(GetWrapper(), command_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Menu::IsCommandIdVisible(int command_id) const {
|
bool Menu::IsCommandIdVisible(int command_id) const {
|
||||||
v8::Locker locker(isolate());
|
return InvokeBoolMethod(this, "_isCommandIdVisible", command_id);
|
||||||
v8::HandleScope handle_scope(isolate());
|
|
||||||
return is_visible_.Run(GetWrapper(), command_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Menu::ShouldCommandIdWorkWhenHidden(int command_id) const {
|
bool Menu::ShouldCommandIdWorkWhenHidden(int command_id) const {
|
||||||
v8::Locker locker(isolate());
|
return InvokeBoolMethod(this, "_shouldCommandIdWorkWhenHidden", command_id);
|
||||||
v8::HandleScope handle_scope(isolate());
|
|
||||||
return works_when_hidden_.Run(GetWrapper(), command_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Menu::GetAcceleratorForCommandIdWithParams(
|
bool Menu::GetAcceleratorForCommandIdWithParams(
|
||||||
int command_id,
|
int command_id,
|
||||||
bool use_default_accelerator,
|
bool use_default_accelerator,
|
||||||
ui::Accelerator* accelerator) const {
|
ui::Accelerator* accelerator) const {
|
||||||
v8::Locker locker(isolate());
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||||
v8::HandleScope handle_scope(isolate());
|
v8::HandleScope scope(isolate);
|
||||||
v8::Local<v8::Value> val =
|
v8::Local<v8::Value> val = gin_helper::CallMethod(
|
||||||
get_accelerator_.Run(GetWrapper(), command_id, use_default_accelerator);
|
isolate, const_cast<Menu*>(this), "_getAcceleratorForCommandId",
|
||||||
return gin::ConvertFromV8(isolate(), val, accelerator);
|
command_id, use_default_accelerator);
|
||||||
|
return gin::ConvertFromV8(isolate, val, accelerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Menu::ShouldRegisterAcceleratorForCommandId(int command_id) const {
|
bool Menu::ShouldRegisterAcceleratorForCommandId(int command_id) const {
|
||||||
v8::Locker locker(isolate());
|
return InvokeBoolMethod(this, "_shouldRegisterAcceleratorForCommandId",
|
||||||
v8::HandleScope handle_scope(isolate());
|
command_id);
|
||||||
return should_register_accelerator_.Run(GetWrapper(), command_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::ExecuteCommand(int command_id, int flags) {
|
void Menu::ExecuteCommand(int command_id, int flags) {
|
||||||
v8::Locker locker(isolate());
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||||
v8::HandleScope handle_scope(isolate());
|
v8::HandleScope scope(isolate);
|
||||||
execute_command_.Run(GetWrapper(), CreateEventFromFlags(flags), command_id);
|
gin_helper::CallMethod(isolate, const_cast<Menu*>(this), "_executeCommand",
|
||||||
|
CreateEventFromFlags(flags), command_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::OnMenuWillShow(ui::SimpleMenuModel* source) {
|
void Menu::OnMenuWillShow(ui::SimpleMenuModel* source) {
|
||||||
v8::Locker locker(isolate());
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||||
v8::HandleScope handle_scope(isolate());
|
v8::HandleScope scope(isolate);
|
||||||
menu_will_show_.Run(GetWrapper());
|
gin_helper::CallMethod(isolate, const_cast<Menu*>(this), "_menuWillShow");
|
||||||
|
}
|
||||||
|
|
||||||
|
base::OnceClosure Menu::BindSelfToClosure(base::OnceClosure callback) {
|
||||||
|
// return ((callback, ref) => { callback() }).bind(null, callback, this)
|
||||||
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
||||||
|
v8::Locker locker(isolate);
|
||||||
|
v8::HandleScope scope(isolate);
|
||||||
|
v8::Local<v8::Object> self;
|
||||||
|
if (GetWrapper(isolate).ToLocal(&self)) {
|
||||||
|
v8::Global<v8::Value> ref(isolate, self);
|
||||||
|
return base::BindOnce(
|
||||||
|
[](base::OnceClosure callback, v8::Global<v8::Value> ref) {
|
||||||
|
std::move(callback).Run();
|
||||||
|
},
|
||||||
|
std::move(callback), std::move(ref));
|
||||||
|
} else {
|
||||||
|
return base::DoNothing();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::InsertItemAt(int index,
|
void Menu::InsertItemAt(int index,
|
||||||
|
@ -208,32 +208,20 @@ bool Menu::WorksWhenHiddenAt(int index) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::OnMenuWillClose() {
|
void Menu::OnMenuWillClose() {
|
||||||
g_menus.erase(weak_map_id());
|
Unpin();
|
||||||
Emit("menu-will-close");
|
Emit("menu-will-close");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Menu::OnMenuWillShow() {
|
void Menu::OnMenuWillShow() {
|
||||||
v8::HandleScope scope(isolate());
|
Pin(v8::Isolate::GetCurrent());
|
||||||
g_menus[weak_map_id()] = v8::Global<v8::Object>(isolate(), GetWrapper());
|
|
||||||
Emit("menu-will-show");
|
Emit("menu-will-show");
|
||||||
}
|
}
|
||||||
|
|
||||||
base::OnceClosure Menu::BindSelfToClosure(base::OnceClosure callback) {
|
|
||||||
// return ((callback, ref) => { callback() }).bind(null, callback, this)
|
|
||||||
v8::Global<v8::Value> ref(isolate(), GetWrapper());
|
|
||||||
return base::BindOnce(
|
|
||||||
[](base::OnceClosure callback, v8::Global<v8::Value> ref) {
|
|
||||||
std::move(callback).Run();
|
|
||||||
},
|
|
||||||
std::move(callback), std::move(ref));
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void Menu::BuildPrototype(v8::Isolate* isolate,
|
v8::Local<v8::ObjectTemplate> Menu::FillObjectTemplate(
|
||||||
v8::Local<v8::FunctionTemplate> prototype) {
|
v8::Isolate* isolate,
|
||||||
prototype->SetClassName(gin::StringToV8(isolate, "Menu"));
|
v8::Local<v8::ObjectTemplate> templ) {
|
||||||
gin_helper::Destroyable::MakeDestroyable(isolate, prototype);
|
return gin::ObjectTemplateBuilder(isolate, "Menu", templ)
|
||||||
gin_helper::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
|
||||||
.SetMethod("insertItem", &Menu::InsertItemAt)
|
.SetMethod("insertItem", &Menu::InsertItemAt)
|
||||||
.SetMethod("insertCheckItem", &Menu::InsertCheckItemAt)
|
.SetMethod("insertCheckItem", &Menu::InsertCheckItemAt)
|
||||||
.SetMethod("insertRadioItem", &Menu::InsertRadioItemAt)
|
.SetMethod("insertRadioItem", &Menu::InsertRadioItemAt)
|
||||||
|
@ -256,7 +244,8 @@ void Menu::BuildPrototype(v8::Isolate* isolate,
|
||||||
.SetMethod("worksWhenHiddenAt", &Menu::WorksWhenHiddenAt)
|
.SetMethod("worksWhenHiddenAt", &Menu::WorksWhenHiddenAt)
|
||||||
.SetMethod("isVisibleAt", &Menu::IsVisibleAt)
|
.SetMethod("isVisibleAt", &Menu::IsVisibleAt)
|
||||||
.SetMethod("popupAt", &Menu::PopupAt)
|
.SetMethod("popupAt", &Menu::PopupAt)
|
||||||
.SetMethod("closePopupAt", &Menu::ClosePopupAt);
|
.SetMethod("closePopupAt", &Menu::ClosePopupAt)
|
||||||
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
@ -272,12 +261,9 @@ void Initialize(v8::Local<v8::Object> exports,
|
||||||
v8::Local<v8::Context> context,
|
v8::Local<v8::Context> context,
|
||||||
void* priv) {
|
void* priv) {
|
||||||
v8::Isolate* isolate = context->GetIsolate();
|
v8::Isolate* isolate = context->GetIsolate();
|
||||||
Menu::SetConstructor(isolate, base::BindRepeating(&Menu::New));
|
|
||||||
|
|
||||||
gin_helper::Dictionary dict(isolate, exports);
|
gin_helper::Dictionary dict(isolate, exports);
|
||||||
dict.Set(
|
dict.Set("Menu", Menu::GetConstructor(context));
|
||||||
"Menu",
|
|
||||||
Menu::GetConstructor(isolate)->GetFunction(context).ToLocalChecked());
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
dict.SetMethod("setApplicationMenu", &Menu::SetApplicationMenu);
|
dict.SetMethod("setApplicationMenu", &Menu::SetApplicationMenu);
|
||||||
dict.SetMethod("sendActionToFirstResponder",
|
dict.SetMethod("sendActionToFirstResponder",
|
||||||
|
|
|
@ -11,21 +11,30 @@
|
||||||
#include "base/callback.h"
|
#include "base/callback.h"
|
||||||
#include "gin/arguments.h"
|
#include "gin/arguments.h"
|
||||||
#include "shell/browser/api/electron_api_top_level_window.h"
|
#include "shell/browser/api/electron_api_top_level_window.h"
|
||||||
|
#include "shell/browser/event_emitter_mixin.h"
|
||||||
#include "shell/browser/ui/electron_menu_model.h"
|
#include "shell/browser/ui/electron_menu_model.h"
|
||||||
#include "shell/common/gin_helper/trackable_object.h"
|
#include "shell/common/gin_helper/constructible.h"
|
||||||
|
#include "shell/common/gin_helper/pinnable.h"
|
||||||
|
|
||||||
namespace electron {
|
namespace electron {
|
||||||
|
|
||||||
namespace api {
|
namespace api {
|
||||||
|
|
||||||
class Menu : public gin_helper::TrackableObject<Menu>,
|
class Menu : public gin::Wrappable<Menu>,
|
||||||
|
public gin_helper::EventEmitterMixin<Menu>,
|
||||||
|
public gin_helper::Constructible<Menu>,
|
||||||
|
public gin_helper::Pinnable<Menu>,
|
||||||
public ElectronMenuModel::Delegate,
|
public ElectronMenuModel::Delegate,
|
||||||
public ElectronMenuModel::Observer {
|
public ElectronMenuModel::Observer {
|
||||||
public:
|
public:
|
||||||
static gin_helper::WrappableBase* New(gin::Arguments* args);
|
// gin_helper::Constructible
|
||||||
|
static gin::Handle<Menu> New(gin::Arguments* args);
|
||||||
|
static v8::Local<v8::ObjectTemplate> FillObjectTemplate(
|
||||||
|
v8::Isolate*,
|
||||||
|
v8::Local<v8::ObjectTemplate>);
|
||||||
|
|
||||||
static void BuildPrototype(v8::Isolate* isolate,
|
// gin::Wrappable
|
||||||
v8::Local<v8::FunctionTemplate> prototype);
|
static gin::WrapperInfo kWrapperInfo;
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
// Set the global menubar.
|
// Set the global menubar.
|
||||||
|
@ -41,8 +50,9 @@ class Menu : public gin_helper::TrackableObject<Menu>,
|
||||||
explicit Menu(gin::Arguments* args);
|
explicit Menu(gin::Arguments* args);
|
||||||
~Menu() override;
|
~Menu() override;
|
||||||
|
|
||||||
// gin_helper::Wrappable:
|
// Returns a new callback which keeps references of the JS wrapper until the
|
||||||
void AfterInit(v8::Isolate* isolate) override;
|
// passed |callback| is called.
|
||||||
|
base::OnceClosure BindSelfToClosure(base::OnceClosure callback);
|
||||||
|
|
||||||
// ui::SimpleMenuModel::Delegate:
|
// ui::SimpleMenuModel::Delegate:
|
||||||
bool IsCommandIdChecked(int command_id) const override;
|
bool IsCommandIdChecked(int command_id) const override;
|
||||||
|
@ -71,11 +81,6 @@ class Menu : public gin_helper::TrackableObject<Menu>,
|
||||||
void OnMenuWillClose() override;
|
void OnMenuWillClose() override;
|
||||||
void OnMenuWillShow() override;
|
void OnMenuWillShow() override;
|
||||||
|
|
||||||
protected:
|
|
||||||
// Returns a new callback which keeps references of the JS wrapper until the
|
|
||||||
// passed |callback| is called.
|
|
||||||
base::OnceClosure BindSelfToClosure(base::OnceClosure callback);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void InsertItemAt(int index, int command_id, const base::string16& label);
|
void InsertItemAt(int index, int command_id, const base::string16& label);
|
||||||
void InsertSeparatorAt(int index);
|
void InsertSeparatorAt(int index);
|
||||||
|
@ -107,19 +112,6 @@ class Menu : public gin_helper::TrackableObject<Menu>,
|
||||||
bool IsVisibleAt(int index) const;
|
bool IsVisibleAt(int index) const;
|
||||||
bool WorksWhenHiddenAt(int index) const;
|
bool WorksWhenHiddenAt(int index) const;
|
||||||
|
|
||||||
// Stored delegate methods.
|
|
||||||
base::RepeatingCallback<bool(v8::Local<v8::Value>, int)> is_checked_;
|
|
||||||
base::RepeatingCallback<bool(v8::Local<v8::Value>, int)> is_enabled_;
|
|
||||||
base::RepeatingCallback<bool(v8::Local<v8::Value>, int)> is_visible_;
|
|
||||||
base::RepeatingCallback<bool(v8::Local<v8::Value>, int)> works_when_hidden_;
|
|
||||||
base::RepeatingCallback<v8::Local<v8::Value>(v8::Local<v8::Value>, int, bool)>
|
|
||||||
get_accelerator_;
|
|
||||||
base::RepeatingCallback<bool(v8::Local<v8::Value>, int)>
|
|
||||||
should_register_accelerator_;
|
|
||||||
base::RepeatingCallback<void(v8::Local<v8::Value>, v8::Local<v8::Value>, int)>
|
|
||||||
execute_command_;
|
|
||||||
base::RepeatingCallback<void(v8::Local<v8::Value>)> menu_will_show_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(Menu);
|
DISALLOW_COPY_AND_ASSIGN(Menu);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -177,8 +177,11 @@ void Menu::SendActionToFirstResponder(const std::string& action) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
gin_helper::WrappableBase* Menu::New(gin::Arguments* args) {
|
gin::Handle<Menu> Menu::New(gin::Arguments* args) {
|
||||||
return new MenuMac(args);
|
auto handle =
|
||||||
|
gin::CreateHandle(args->isolate(), static_cast<Menu*>(new MenuMac(args)));
|
||||||
|
gin_helper::CallMethod(args->isolate(), handle.get(), "_init");
|
||||||
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|
|
@ -84,8 +84,11 @@ void MenuViews::OnClosed(int32_t window_id, base::OnceClosure callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
gin_helper::WrappableBase* Menu::New(gin::Arguments* args) {
|
gin::Handle<Menu> Menu::New(gin::Arguments* args) {
|
||||||
return new MenuViews(args);
|
auto handle = gin::CreateHandle(args->isolate(),
|
||||||
|
static_cast<Menu*>(new MenuViews(args)));
|
||||||
|
gin_helper::CallMethod(args->isolate(), handle.get(), "_init");
|
||||||
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace api
|
} // namespace api
|
||||||
|
|
|
@ -687,7 +687,6 @@ void TopLevelWindow::SetMenu(v8::Isolate* isolate, v8::Local<v8::Value> value) {
|
||||||
gin::Handle<Menu> menu;
|
gin::Handle<Menu> menu;
|
||||||
v8::Local<v8::Object> object;
|
v8::Local<v8::Object> object;
|
||||||
if (value->IsObject() && value->ToObject(context).ToLocal(&object) &&
|
if (value->IsObject() && value->ToObject(context).ToLocal(&object) &&
|
||||||
gin::V8ToString(isolate, object->GetConstructorName()) == "Menu" &&
|
|
||||||
gin::ConvertFromV8(isolate, value, &menu) && !menu.IsEmpty()) {
|
gin::ConvertFromV8(isolate, value, &menu) && !menu.IsEmpty()) {
|
||||||
menu_.Reset(isolate, menu.ToV8());
|
menu_.Reset(isolate, menu.ToV8());
|
||||||
window_->SetMenu(menu->model());
|
window_->SetMenu(menu->model());
|
||||||
|
|
|
@ -21,7 +21,7 @@ v8::Local<v8::Value> CallMethodWithArgs(v8::Isolate* isolate,
|
||||||
// Use node::MakeCallback to call the callback, and it will also run pending
|
// Use node::MakeCallback to call the callback, and it will also run pending
|
||||||
// tasks in Node.js.
|
// tasks in Node.js.
|
||||||
v8::MaybeLocal<v8::Value> ret = node::MakeCallback(
|
v8::MaybeLocal<v8::Value> ret = node::MakeCallback(
|
||||||
isolate, obj, method, args->size(), &args->front(), {0, 0});
|
isolate, obj, method, args->size(), args->data(), {0, 0});
|
||||||
// If the JS function throws an exception (doesn't return a value) the result
|
// If the JS function throws an exception (doesn't return a value) the result
|
||||||
// of MakeCallback will be empty and therefore ToLocal will be false, in this
|
// of MakeCallback will be empty and therefore ToLocal will be false, in this
|
||||||
// case we need to return "false" as that indicates that the event emitter did
|
// case we need to return "false" as that indicates that the event emitter did
|
||||||
|
|
|
@ -69,11 +69,11 @@ v8::Local<v8::Value> CallMethod(v8::Isolate* isolate,
|
||||||
gin::Wrappable<T>* object,
|
gin::Wrappable<T>* object,
|
||||||
const char* method_name,
|
const char* method_name,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
v8::HandleScope scope(isolate);
|
v8::EscapableHandleScope scope(isolate);
|
||||||
v8::Local<v8::Object> v8_object;
|
v8::Local<v8::Object> v8_object;
|
||||||
if (object->GetWrapper(isolate).ToLocal(&v8_object))
|
if (object->GetWrapper(isolate).ToLocal(&v8_object))
|
||||||
return CustomEmit(isolate, v8_object, method_name,
|
return scope.Escape(CustomEmit(isolate, v8_object, method_name,
|
||||||
std::forward<Args>(args)...);
|
std::forward<Args>(args)...));
|
||||||
else
|
else
|
||||||
return v8::Local<v8::Value>();
|
return v8::Local<v8::Value>();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ class Wrappable : public WrappableBase {
|
||||||
template <typename Sig>
|
template <typename Sig>
|
||||||
static void SetConstructor(v8::Isolate* isolate,
|
static void SetConstructor(v8::Isolate* isolate,
|
||||||
const base::Callback<Sig>& constructor) {
|
const base::Callback<Sig>& constructor) {
|
||||||
v8::Local<v8::FunctionTemplate> templ = CreateFunctionTemplate(
|
v8::Local<v8::FunctionTemplate> templ = gin_helper::CreateFunctionTemplate(
|
||||||
isolate, base::Bind(&internal::InvokeNew<Sig>, constructor));
|
isolate, base::Bind(&internal::InvokeNew<Sig>, constructor));
|
||||||
templ->InstanceTemplate()->SetInternalFieldCount(1);
|
templ->InstanceTemplate()->SetInternalFieldCount(1);
|
||||||
T::BuildPrototype(isolate, templ);
|
T::BuildPrototype(isolate, templ);
|
||||||
|
|
6
spec-main/ambient.d.ts
vendored
6
spec-main/ambient.d.ts
vendored
|
@ -2,10 +2,8 @@ declare let standardScheme: string;
|
||||||
|
|
||||||
declare namespace Electron {
|
declare namespace Electron {
|
||||||
interface Menu {
|
interface Menu {
|
||||||
delegate: {
|
_executeCommand(event: any, id: number): void;
|
||||||
executeCommand(menu: Menu, event: any, id: number): void;
|
_menuWillShow(): void;
|
||||||
menuWillShow(menu: Menu): void;
|
|
||||||
};
|
|
||||||
getAcceleratorTextAt(index: number): string;
|
getAcceleratorTextAt(index: number): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ describe('MenuItems', () => {
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
}]);
|
}]);
|
||||||
menu.delegate.executeCommand(menu, {}, menu.items[0].commandId);
|
menu._executeCommand({}, menu.items[0].commandId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ describe('MenuItems', () => {
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
expect(menu.items[0].checked).to.be.false('menu item checked');
|
expect(menu.items[0].checked).to.be.false('menu item checked');
|
||||||
menu.delegate.executeCommand(menu, {}, menu.items[0].commandId);
|
menu._executeCommand({}, menu.items[0].commandId);
|
||||||
expect(menu.items[0].checked).to.be.true('menu item checked');
|
expect(menu.items[0].checked).to.be.true('menu item checked');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -70,9 +70,9 @@ describe('MenuItems', () => {
|
||||||
type: 'radio'
|
type: 'radio'
|
||||||
}]);
|
}]);
|
||||||
|
|
||||||
menu.delegate.executeCommand(menu, {}, menu.items[0].commandId);
|
menu._executeCommand({}, menu.items[0].commandId);
|
||||||
expect(menu.items[0].checked).to.be.true('menu item checked');
|
expect(menu.items[0].checked).to.be.true('menu item checked');
|
||||||
menu.delegate.executeCommand(menu, {}, menu.items[0].commandId);
|
menu._executeCommand({}, menu.items[0].commandId);
|
||||||
expect(menu.items[0].checked).to.be.true('menu item checked');
|
expect(menu.items[0].checked).to.be.true('menu item checked');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ describe('MenuItems', () => {
|
||||||
|
|
||||||
it('at least have one item checked in each group', () => {
|
it('at least have one item checked in each group', () => {
|
||||||
const menu = Menu.buildFromTemplate(template);
|
const menu = Menu.buildFromTemplate(template);
|
||||||
menu.delegate.menuWillShow(menu);
|
menu._menuWillShow();
|
||||||
|
|
||||||
const groups = findRadioGroups(template);
|
const groups = findRadioGroups(template);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue