From 5f6706ac33df60b774742a3dd9eaf6dfdd4354e4 Mon Sep 17 00:00:00 2001 From: Emmanuel Kimmerlin Date: Fri, 24 Aug 2018 23:33:27 +0200 Subject: [PATCH] feat: BrowserWindow.getNormalBounds() (#13290) * First commit * Add Mac support (1st attempt) * Add Mac support (2nd attempt) * Simplify tests * Restore window state ! * Looking at other tests, seems minimize, maximize, fullscreen are skipped when in CI * Fix Mac tests * Restore tests in CI * Fix typo * widget getRestoredBounds not working on Mac !! * widget getRestoredBounds not working on Mac !! * Add IsNormal function * Add IsNormal * IsNormal => isNormal * Deactivate fullscreen on Mac. Do not receive leave-fullscreen event * Set default original_frame_ * Set default original_frame_ * Fix Mac --- atom/browser/api/atom_api_top_level_window.cc | 10 ++ atom/browser/api/atom_api_top_level_window.h | 2 + atom/browser/native_window.cc | 4 + atom/browser/native_window.h | 2 + atom/browser/native_window_mac.h | 2 + atom/browser/native_window_mac.mm | 34 +++++- atom/browser/native_window_views.cc | 4 + atom/browser/native_window_views.h | 1 + docs/api/browser-window.md | 10 ++ spec/api-browser-window-spec.js | 108 ++++++++++++++++++ 10 files changed, 176 insertions(+), 1 deletion(-) diff --git a/atom/browser/api/atom_api_top_level_window.cc b/atom/browser/api/atom_api_top_level_window.cc index c8db6b0bb468a..2c536a55df2d3 100644 --- a/atom/browser/api/atom_api_top_level_window.cc +++ b/atom/browser/api/atom_api_top_level_window.cc @@ -365,6 +365,14 @@ gfx::Rect TopLevelWindow::GetBounds() { return window_->GetBounds(); } +bool TopLevelWindow::IsNormal() { + return window_->IsNormal(); +} + +gfx::Rect TopLevelWindow::GetNormalBounds() { + return window_->GetNormalBounds(); +} + void TopLevelWindow::SetContentBounds(const gfx::Rect& bounds, mate::Arguments* args) { bool animate = false; @@ -967,6 +975,8 @@ void TopLevelWindow::BuildPrototype(v8::Isolate* isolate, .SetMethod("isFullScreen", &TopLevelWindow::IsFullscreen) .SetMethod("setBounds", &TopLevelWindow::SetBounds) .SetMethod("getBounds", &TopLevelWindow::GetBounds) + .SetMethod("isNormal", &TopLevelWindow::IsNormal) + .SetMethod("getNormalBounds", &TopLevelWindow::GetNormalBounds) .SetMethod("setSize", &TopLevelWindow::SetSize) .SetMethod("getSize", &TopLevelWindow::GetSize) .SetMethod("setContentBounds", &TopLevelWindow::SetContentBounds) diff --git a/atom/browser/api/atom_api_top_level_window.h b/atom/browser/api/atom_api_top_level_window.h index ac4c7670a544a..3f7c3f7cedf26 100644 --- a/atom/browser/api/atom_api_top_level_window.h +++ b/atom/browser/api/atom_api_top_level_window.h @@ -110,6 +110,8 @@ class TopLevelWindow : public mate::TrackableObject, std::vector GetContentSize(); void SetContentBounds(const gfx::Rect& bounds, mate::Arguments* args); gfx::Rect GetContentBounds(); + bool IsNormal(); + gfx::Rect GetNormalBounds(); void SetMinimumSize(int width, int height); std::vector GetMinimumSize(); void SetMaximumSize(int width, int height); diff --git a/atom/browser/native_window.cc b/atom/browser/native_window.cc index 4ff2bcfcabfd3..6ad8cd96fe49c 100644 --- a/atom/browser/native_window.cc +++ b/atom/browser/native_window.cc @@ -208,6 +208,10 @@ gfx::Rect NativeWindow::GetContentBounds() { return WindowBoundsToContentBounds(GetBounds()); } +bool NativeWindow::IsNormal() { + return !IsMinimized() && !IsMaximized() && !IsFullscreen(); +} + void NativeWindow::SetSizeConstraints( const extensions::SizeConstraints& window_constraints) { extensions::SizeConstraints content_constraints(GetContentSizeConstraints()); diff --git a/atom/browser/native_window.h b/atom/browser/native_window.h index 35b67e2dd2306..eb5c95cb3e3ec 100644 --- a/atom/browser/native_window.h +++ b/atom/browser/native_window.h @@ -87,6 +87,8 @@ class NativeWindow : public base::SupportsUserData, virtual gfx::Size GetContentSize(); virtual void SetContentBounds(const gfx::Rect& bounds, bool animate = false); virtual gfx::Rect GetContentBounds(); + virtual bool IsNormal(); + virtual gfx::Rect GetNormalBounds() = 0; virtual void SetSizeConstraints( const extensions::SizeConstraints& size_constraints); virtual extensions::SizeConstraints GetSizeConstraints() const; diff --git a/atom/browser/native_window_mac.h b/atom/browser/native_window_mac.h index 44a78198f44cd..3d8f6a932f1ae 100644 --- a/atom/browser/native_window_mac.h +++ b/atom/browser/native_window_mac.h @@ -52,6 +52,8 @@ class NativeWindowMac : public NativeWindow { bool IsFullscreen() const override; void SetBounds(const gfx::Rect& bounds, bool animate = false) override; gfx::Rect GetBounds() override; + bool IsNormal() override; + gfx::Rect GetNormalBounds() override; void SetContentSizeConstraints( const extensions::SizeConstraints& size_constraints) override; void SetResizable(bool resizable) override; diff --git a/atom/browser/native_window_mac.mm b/atom/browser/native_window_mac.mm index 004263ec12770..060f6fe4dcc32 100644 --- a/atom/browser/native_window_mac.mm +++ b/atom/browser/native_window_mac.mm @@ -469,6 +469,8 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { // Default content view. SetContentView(new views::View()); AddContentViewLayers(); + + original_frame_ = [window_ frame]; } NativeWindowMac::~NativeWindowMac() { @@ -594,6 +596,9 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { if (IsMaximized()) return; + // Take note of the current window size + if (IsNormal()) + original_frame_ = [window_ frame]; [window_ zoom:nil]; } @@ -618,6 +623,12 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { } void NativeWindowMac::Minimize() { + if (IsMinimized()) + return; + + // Take note of the current window size + if (IsNormal()) + original_frame_ = [window_ frame]; [window_ miniaturize:nil]; } @@ -633,6 +644,9 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { if (fullscreen == IsFullscreen()) return; + // Take note of the current window size + if (IsNormal()) + original_frame_ = [window_ frame]; [window_ toggleFullScreenMode:nil]; } @@ -668,6 +682,23 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { return bounds; } +bool NativeWindowMac::IsNormal() { + return NativeWindow::IsNormal() && !IsSimpleFullScreen(); +} + +gfx::Rect NativeWindowMac::GetNormalBounds() { + if (IsNormal()) { + return GetBounds(); + } + NSRect frame = original_frame_; + gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame)); + NSScreen* screen = [[NSScreen screens] firstObject]; + bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame)); + return bounds; + // Works on OS_WIN ! + // return widget()->GetRestoredBounds(); +} + void NativeWindowMac::SetContentSizeConstraints( const extensions::SizeConstraints& size_constraints) { auto convertSize = [this](const gfx::Size& size) { @@ -860,7 +891,8 @@ void ViewDidMoveToSuperview(NSView* self, SEL _cmd) { is_simple_fullscreen_ = true; // Take note of the current window size - original_frame_ = [window frame]; + if (IsNormal()) + original_frame_ = [window_ frame]; simple_fullscreen_options_ = [NSApp currentSystemPresentationOptions]; simple_fullscreen_mask_ = [window styleMask]; diff --git a/atom/browser/native_window_views.cc b/atom/browser/native_window_views.cc index 0dba39942f92d..0efb7aaf9f537 100644 --- a/atom/browser/native_window_views.cc +++ b/atom/browser/native_window_views.cc @@ -562,6 +562,10 @@ gfx::Size NativeWindowViews::GetContentSize() { return content_view() ? content_view()->size() : gfx::Size(); } +gfx::Rect NativeWindowViews::GetNormalBounds() { + return widget()->GetRestoredBounds(); +} + void NativeWindowViews::SetContentSizeConstraints( const extensions::SizeConstraints& size_constraints) { NativeWindow::SetContentSizeConstraints(size_constraints); diff --git a/atom/browser/native_window_views.h b/atom/browser/native_window_views.h index ef1e4413ca879..e73c5694b8b78 100644 --- a/atom/browser/native_window_views.h +++ b/atom/browser/native_window_views.h @@ -67,6 +67,7 @@ class NativeWindowViews : public NativeWindow, gfx::Rect GetBounds() override; gfx::Rect GetContentBounds() override; gfx::Size GetContentSize() override; + gfx::Rect GetNormalBounds() override; void SetContentSizeConstraints( const extensions::SizeConstraints& size_constraints) override; void SetResizable(bool resizable) override; diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 8ff6ff41428eb..1595e2c8635a6 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -816,6 +816,10 @@ Simple fullscreen mode emulates the native fullscreen behavior found in versions Returns `Boolean` - Whether the window is in simple (pre-Lion) fullscreen mode. +#### `win.isNormal()` + +Returns `Boolean` - Whether the window is in normal state (not maximized, not minimized, not in fullscreen mode). + #### `win.setAspectRatio(aspectRatio[, extraSize])` _macOS_ * `aspectRatio` Float - The aspect ratio to maintain for some portion of the @@ -878,6 +882,12 @@ the supplied bounds. Returns [`Rectangle`](structures/rectangle.md) +#### `win.getNormalBounds()` + +Returns [`Rectangle`](structures/rectangle.md) - Contains the window bounds of the normal state + +**Note:** whatever the current state of the window : maximized, minimized or in fullscreen, this function always returns the position and size of the window in normal state. In normal state, getBounds and getNormalBounds returns the same [`Rectangle`](structures/rectangle.md). + #### `win.setEnabled(enable)` * `enable` Boolean diff --git a/spec/api-browser-window-spec.js b/spec/api-browser-window-spec.js index c591097f4a865..658a253d543bb 100644 --- a/spec/api-browser-window-spec.js +++ b/spec/api-browser-window-spec.js @@ -558,6 +558,114 @@ describe('BrowserWindow module', () => { }) }) + describe(`BrowserWindow.getNormalBounds()`, () => { + describe(`Normal state`, () => { + it(`checks normal bounds after resize`, (done) => { + const size = [300, 400] + w.once('resize', () => { + assertBoundsEqual(w.getNormalBounds(), w.getBounds()) + done() + }) + w.setSize(size[0], size[1]) + }) + it(`checks normal bounds after move`, (done) => { + const pos = [10, 10] + w.once('move', () => { + assertBoundsEqual(w.getNormalBounds(), w.getBounds()) + done() + }) + w.setPosition(pos[0], pos[1]) + }) + }) + describe(`Maximized state`, () => { + before(function () { + if (isCI) { + this.skip() + } + }) + it(`checks normal bounds when maximized`, (done) => { + const bounds = w.getBounds() + w.once('maximize', () => { + assertBoundsEqual(w.getNormalBounds(), bounds) + done() + }) + w.show() + w.maximize() + }) + it(`checks normal bounds when unmaximized`, (done) => { + const bounds = w.getBounds() + w.once('maximize', () => { + w.unmaximize() + }) + w.once('unmaximize', () => { + assertBoundsEqual(w.getNormalBounds(), bounds) + done() + }) + w.show() + w.maximize() + }) + }) + describe(`Minimized state`, () => { + before(function () { + if (isCI) { + this.skip() + } + }) + it(`checks normal bounds when minimized`, (done) => { + const bounds = w.getBounds() + w.once('minimize', () => { + assertBoundsEqual(w.getNormalBounds(), bounds) + done() + }) + w.show() + w.minimize() + }) + it(`checks normal bounds when restored`, (done) => { + const bounds = w.getBounds() + w.once('minimize', () => { + w.restore() + }) + w.once('restore', () => { + assertBoundsEqual(w.getNormalBounds(), bounds) + done() + }) + w.show() + w.minimize() + }) + }) + describe(`Fullscreen state`, () => { + before(function () { + if (isCI) { + this.skip() + } + if (process.platform === 'darwin') { + this.skip() + } + }) + it(`checks normal bounds when fullscreen'ed`, (done) => { + const bounds = w.getBounds() + w.once('enter-full-screen', () => { + assertBoundsEqual(w.getNormalBounds(), bounds) + done() + }) + w.show() + w.setFullScreen(true) + }) + it(`checks normal bounds when unfullscreen'ed`, (done) => { + const bounds = w.getBounds() + w.once('enter-full-screen', () => { + w.setFullScreen(false) + }) + w.once('leave-full-screen', () => { + assertBoundsEqual(w.getNormalBounds(), bounds) + done() + }) + w.show() + w.setFullScreen(true) + }) + }) + }) + describe('BrowserWindow.setProgressBar(progress)', () => { it('sets the progress', () => { assert.doesNotThrow(() => {