From 33806e39e8a70196cc64a1a7c8a18f40e39b90db Mon Sep 17 00:00:00 2001 From: Shelley Vohr Date: Tue, 13 Apr 2021 21:29:28 +0200 Subject: [PATCH] feat: add BrowserWindow.isFocusable() --- docs/api/browser-window.md | 8 ++++++++ lib/browser/api/base-window.ts | 5 +++++ shell/browser/api/electron_api_base_window.cc | 5 +++++ shell/browser/api/electron_api_base_window.h | 1 + shell/browser/native_window.cc | 4 ++++ shell/browser/native_window.h | 1 + shell/browser/native_window_mac.h | 1 + shell/browser/native_window_mac.mm | 4 ++++ shell/browser/native_window_views.cc | 11 +++++++++++ shell/browser/native_window_views.h | 1 + spec-main/api-browser-window-spec.ts | 11 +++++++++++ 11 files changed, 52 insertions(+) diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 67b66a0f1ac46..6ec3850c7b178 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -758,6 +758,10 @@ A `Boolean` property that determines whether the window is in simple (pre-Lion) A `Boolean` property that determines whether the window is in fullscreen mode. +#### `win.focusable` _Windows_ _macOS_ + +A `Boolean` property that determines whether the window is focusable. + #### `win.visibleOnAllWorkspaces` A `Boolean` property that determines whether the window is visible on all workspaces. @@ -1676,6 +1680,10 @@ Changes whether the window can be focused. On macOS it does not remove the focus from the window. +#### `win.isFocusable()` _macOS_ _Windows_ + +Returns whether the window can be focused. + #### `win.setParentWindow(parent)` * `parent` BrowserWindow | null diff --git a/lib/browser/api/base-window.ts b/lib/browser/api/base-window.ts index 52b21aaf0b13e..28671d7c1de7c 100644 --- a/lib/browser/api/base-window.ts +++ b/lib/browser/api/base-window.ts @@ -37,6 +37,11 @@ Object.defineProperty(BaseWindow.prototype, 'simpleFullScreen', { set: function (simple) { this.setSimpleFullScreen(simple); } }); +Object.defineProperty(BaseWindow.prototype, 'focusable', { + get: function () { return this.isFocusable(); }, + set: function (focusable) { this.setFocusable(focusable); } +}); + Object.defineProperty(BaseWindow.prototype, 'kiosk', { get: function () { return this.isKiosk(); }, set: function (kiosk) { this.setKiosk(kiosk); } diff --git a/shell/browser/api/electron_api_base_window.cc b/shell/browser/api/electron_api_base_window.cc index 4a3bc35c43f5b..ba551776aecd2 100644 --- a/shell/browser/api/electron_api_base_window.cc +++ b/shell/browser/api/electron_api_base_window.cc @@ -696,6 +696,10 @@ void BaseWindow::SetFocusable(bool focusable) { return window_->SetFocusable(focusable); } +bool BaseWindow::IsFocusable() { + return window_->IsFocusable(); +} + void BaseWindow::SetMenu(v8::Isolate* isolate, v8::Local value) { auto context = isolate->GetCurrentContext(); gin::Handle menu; @@ -1243,6 +1247,7 @@ void BaseWindow::BuildPrototype(v8::Isolate* isolate, .SetMethod("setIgnoreMouseEvents", &BaseWindow::SetIgnoreMouseEvents) .SetMethod("setContentProtection", &BaseWindow::SetContentProtection) .SetMethod("setFocusable", &BaseWindow::SetFocusable) + .SetMethod("isFocusable", &BaseWindow::IsFocusable) .SetMethod("setMenu", &BaseWindow::SetMenu) .SetMethod("removeMenu", &BaseWindow::RemoveMenu) .SetMethod("setParentWindow", &BaseWindow::SetParentWindow) diff --git a/shell/browser/api/electron_api_base_window.h b/shell/browser/api/electron_api_base_window.h index 25b2c9e7c5bad..d0f2f44df165c 100644 --- a/shell/browser/api/electron_api_base_window.h +++ b/shell/browser/api/electron_api_base_window.h @@ -171,6 +171,7 @@ class BaseWindow : public gin_helper::TrackableObject, void SetIgnoreMouseEvents(bool ignore, gin_helper::Arguments* args); void SetContentProtection(bool enable); void SetFocusable(bool focusable); + bool IsFocusable(); void SetMenu(v8::Isolate* isolate, v8::Local menu); void RemoveMenu(); void SetParentWindow(v8::Local value, gin_helper::Arguments* args); diff --git a/shell/browser/native_window.cc b/shell/browser/native_window.cc index 3fa8315cb609c..426ae58324453 100644 --- a/shell/browser/native_window.cc +++ b/shell/browser/native_window.cc @@ -328,6 +328,10 @@ bool NativeWindow::IsDocumentEdited() { void NativeWindow::SetFocusable(bool focusable) {} +bool NativeWindow::IsFocusable() { + return false; +} + void NativeWindow::SetMenu(ElectronMenuModel* menu) {} void NativeWindow::SetParentWindow(NativeWindow* parent) { diff --git a/shell/browser/native_window.h b/shell/browser/native_window.h index 0da492ef9b7d0..fd0c0675db906 100644 --- a/shell/browser/native_window.h +++ b/shell/browser/native_window.h @@ -162,6 +162,7 @@ class NativeWindow : public base::SupportsUserData, virtual void SetIgnoreMouseEvents(bool ignore, bool forward) = 0; virtual void SetContentProtection(bool enable) = 0; virtual void SetFocusable(bool focusable); + virtual bool IsFocusable(); virtual void SetMenu(ElectronMenuModel* menu); virtual void SetParentWindow(NativeWindow* parent); virtual void AddBrowserView(NativeBrowserView* browser_view) = 0; diff --git a/shell/browser/native_window_mac.h b/shell/browser/native_window_mac.h index 88f65ea4201e0..8168acd6c5b2b 100644 --- a/shell/browser/native_window_mac.h +++ b/shell/browser/native_window_mac.h @@ -104,6 +104,7 @@ class NativeWindowMac : public NativeWindow, void SetIgnoreMouseEvents(bool ignore, bool forward) override; void SetContentProtection(bool enable) override; void SetFocusable(bool focusable) override; + bool IsFocusable() override; void AddBrowserView(NativeBrowserView* browser_view) override; void RemoveBrowserView(NativeBrowserView* browser_view) override; void SetTopBrowserView(NativeBrowserView* browser_view) override; diff --git a/shell/browser/native_window_mac.mm b/shell/browser/native_window_mac.mm index 99999d2ff1255..dddc13f029856 100644 --- a/shell/browser/native_window_mac.mm +++ b/shell/browser/native_window_mac.mm @@ -1072,6 +1072,10 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { [window_ setDisableKeyOrMainWindow:!focusable]; } +bool NativeWindowMac::IsFocusable() { + return ![window_ disableKeyOrMainWindow]; +} + void NativeWindowMac::AddBrowserView(NativeBrowserView* view) { [CATransaction begin]; [CATransaction setDisableActions:YES]; diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index ae21549889c0e..ff6f4f62aa4b5 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -1083,6 +1083,17 @@ void NativeWindowViews::SetFocusable(bool focusable) { #endif } +bool NativeWindowViews::IsFocusable() { + bool can_activate = widget()->widget_delegate()->CanActivate(); +#if defined(OS_WIN) + LONG ex_style = ::GetWindowLong(GetAcceleratedWidget(), GWL_EXSTYLE); + bool no_activate = ex_style & WS_EX_NOACTIVATE; + return !no_activate && can_activate; +#else + return can_activate; +#endif +} + void NativeWindowViews::SetMenu(ElectronMenuModel* menu_model) { #if defined(USE_X11) if (!features::IsUsingOzonePlatform()) { diff --git a/shell/browser/native_window_views.h b/shell/browser/native_window_views.h index 3c7aba646bb09..9f62ac1ba20bf 100644 --- a/shell/browser/native_window_views.h +++ b/shell/browser/native_window_views.h @@ -112,6 +112,7 @@ class NativeWindowViews : public NativeWindow, void SetIgnoreMouseEvents(bool ignore, bool forward) override; void SetContentProtection(bool enable) override; void SetFocusable(bool focusable) override; + bool IsFocusable() override; void SetMenu(ElectronMenuModel* menu_model) override; void AddBrowserView(NativeBrowserView* browser_view) override; void RemoveBrowserView(NativeBrowserView* browser_view) override; diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index d480c2ab522c6..089932256cc9d 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -838,6 +838,17 @@ describe('BrowserWindow module', () => { await closeWindow(w2, { assertNotWindows: false }); }); }); + + describe('BrowserWindow.isFocusable()', () => { + it('correctly returns whether a window is focusable', async () => { + const w2 = new BrowserWindow({ focusable: false }); + expect(w2.isFocusable()).to.be.false(); + + w2.setFocusable(true); + expect(w2.isFocusable()).to.be.true(); + await closeWindow(w2, { assertNotWindows: false }); + }); + }); }); describe('sizing', () => {