From e9ba26f50ed2a38ada19469951c1eeaf346ef152 Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Wed, 7 Nov 2018 09:40:38 -0800 Subject: [PATCH] feat: allow registering multiple shortcuts (#15542) This PR allows for multiple global shortcuts to be registered such that triggering any of them calls the same callback. --- atom/browser/api/atom_api_global_shortcut.cc | 27 ++++++++++++++++++++ atom/browser/api/atom_api_global_shortcut.h | 4 +++ docs/api/global-shortcut.md | 11 ++++++++ spec/api-global-shortcut-spec.js | 21 +++++++++++++-- 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/atom/browser/api/atom_api_global_shortcut.cc b/atom/browser/api/atom_api_global_shortcut.cc index 4fc7532d9e34..9f9102b07e45 100644 --- a/atom/browser/api/atom_api_global_shortcut.cc +++ b/atom/browser/api/atom_api_global_shortcut.cc @@ -5,6 +5,7 @@ #include "atom/browser/api/atom_api_global_shortcut.h" #include +#include #include "atom/common/native_mate_converters/accelerator_converter.h" #include "atom/common/native_mate_converters/callback.h" @@ -38,6 +39,24 @@ void GlobalShortcut::OnKeyPressed(const ui::Accelerator& accelerator) { accelerator_callback_map_[accelerator].Run(); } +bool GlobalShortcut::RegisterAll( + const std::vector& accelerators, + const base::Closure& callback) { + std::vector registered; + for (auto& accelerator : accelerators) { + GlobalShortcutListener* listener = GlobalShortcutListener::GetInstance(); + if (!listener->RegisterAccelerator(accelerator, this)) { + // unregister all shortcuts if any failed + UnregisterSome(registered); + return false; + } + + registered.push_back(accelerator); + accelerator_callback_map_[accelerator] = callback; + } + return true; +} + bool GlobalShortcut::Register(const ui::Accelerator& accelerator, const base::Closure& callback) { if (!GlobalShortcutListener::GetInstance()->RegisterAccelerator(accelerator, @@ -58,6 +77,13 @@ void GlobalShortcut::Unregister(const ui::Accelerator& accelerator) { this); } +void GlobalShortcut::UnregisterSome( + const std::vector& accelerators) { + for (auto& accelerator : accelerators) { + Unregister(accelerator); + } +} + bool GlobalShortcut::IsRegistered(const ui::Accelerator& accelerator) { return ContainsKey(accelerator_callback_map_, accelerator); } @@ -77,6 +103,7 @@ void GlobalShortcut::BuildPrototype(v8::Isolate* isolate, v8::Local prototype) { prototype->SetClassName(mate::StringToV8(isolate, "GlobalShortcut")); mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate()) + .SetMethod("registerAll", &GlobalShortcut::RegisterAll) .SetMethod("register", &GlobalShortcut::Register) .SetMethod("isRegistered", &GlobalShortcut::IsRegistered) .SetMethod("unregister", &GlobalShortcut::Unregister) diff --git a/atom/browser/api/atom_api_global_shortcut.h b/atom/browser/api/atom_api_global_shortcut.h index b023aec4a843..edcbad3db01e 100644 --- a/atom/browser/api/atom_api_global_shortcut.h +++ b/atom/browser/api/atom_api_global_shortcut.h @@ -7,6 +7,7 @@ #include #include +#include #include "atom/browser/api/trackable_object.h" #include "base/callback.h" @@ -33,10 +34,13 @@ class GlobalShortcut : public extensions::GlobalShortcutListener::Observer, private: typedef std::map AcceleratorCallbackMap; + bool RegisterAll(const std::vector& accelerators, + const base::Closure& callback); bool Register(const ui::Accelerator& accelerator, const base::Closure& callback); bool IsRegistered(const ui::Accelerator& accelerator); void Unregister(const ui::Accelerator& accelerator); + void UnregisterSome(const std::vector& accelerators); void UnregisterAll(); // GlobalShortcutListener::Observer implementation. diff --git a/docs/api/global-shortcut.md b/docs/api/global-shortcut.md index 6632f851de10..fe7363aaeb3c 100644 --- a/docs/api/global-shortcut.md +++ b/docs/api/global-shortcut.md @@ -54,6 +54,17 @@ When the accelerator is already taken by other applications, this call will silently fail. This behavior is intended by operating systems, since they don't want applications to fight for global shortcuts. +### `globalShortcut.registerAll(accelerators, callback)` + +* `accelerators` String[] - an array of [Accelerator](accelerator.md)s. +* `callback` Function + +Registers a global shortcut of all `accelerator` items in `accelerators`. The `callback` is called when any of the registered shortcuts are pressed by the user. + +When a given accelerator is already taken by other applications, this call will +silently fail. This behavior is intended by operating systems, since they don't +want applications to fight for global shortcuts. + ### `globalShortcut.isRegistered(accelerator)` * `accelerator` [Accelerator](accelerator.md) diff --git a/spec/api-global-shortcut-spec.js b/spec/api-global-shortcut-spec.js index 04abee452da3..ac8a555bb7d6 100644 --- a/spec/api-global-shortcut-spec.js +++ b/spec/api-global-shortcut-spec.js @@ -18,8 +18,8 @@ describe('globalShortcut module', () => { globalShortcut.unregisterAll() }) - it('can register and unregister accelerators', () => { - const accelerator = 'CommandOrControl+A+B+C' + it('can register and unregister single accelerators', () => { + const accelerator = 'CmdOrCtrl+A+B+C' expect(globalShortcut.isRegistered(accelerator)).to.be.false() globalShortcut.register(accelerator, () => {}) @@ -33,4 +33,21 @@ describe('globalShortcut module', () => { globalShortcut.unregisterAll() expect(globalShortcut.isRegistered(accelerator)).to.be.false() }) + + it('can register and unregister multiple accelerators', () => { + const accelerators = ['CmdOrCtrl+X', 'CmdOrCtrl+Y'] + + expect(globalShortcut.isRegistered(accelerators[0])).to.be.false() + expect(globalShortcut.isRegistered(accelerators[1])).to.be.false() + + globalShortcut.registerAll(accelerators, () => {}) + + expect(globalShortcut.isRegistered(accelerators[0])).to.be.true() + expect(globalShortcut.isRegistered(accelerators[1])).to.be.true() + + globalShortcut.unregisterAll() + + expect(globalShortcut.isRegistered(accelerators[0])).to.be.false() + expect(globalShortcut.isRegistered(accelerators[1])).to.be.false() + }) })