Browse Source

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
Emmanuel Kimmerlin 6 years ago
parent
commit
5f6706ac33

+ 10 - 0
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)

+ 2 - 0
atom/browser/api/atom_api_top_level_window.h

@@ -110,6 +110,8 @@ class TopLevelWindow : public mate::TrackableObject<TopLevelWindow>,
   std::vector<int> 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<int> GetMinimumSize();
   void SetMaximumSize(int width, int height);

+ 4 - 0
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());

+ 2 - 0
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;

+ 2 - 0
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;

+ 33 - 1
atom/browser/native_window_mac.mm

@@ -469,6 +469,8 @@ NativeWindowMac::NativeWindowMac(const mate::Dictionary& options,
   // Default content view.
   SetContentView(new views::View());
   AddContentViewLayers();
+
+  original_frame_ = [window_ frame];
 }
 
 NativeWindowMac::~NativeWindowMac() {
@@ -594,6 +596,9 @@ void NativeWindowMac::Maximize() {
   if (IsMaximized())
     return;
 
+  // Take note of the current window size
+  if (IsNormal())
+    original_frame_ = [window_ frame];
   [window_ zoom:nil];
 }
 
@@ -618,6 +623,12 @@ bool NativeWindowMac::IsMaximized() {
 }
 
 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 NativeWindowMac::SetFullScreen(bool fullscreen) {
   if (fullscreen == IsFullscreen())
     return;
 
+  // Take note of the current window size
+  if (IsNormal())
+    original_frame_ = [window_ frame];
   [window_ toggleFullScreenMode:nil];
 }
 
@@ -668,6 +682,23 @@ gfx::Rect NativeWindowMac::GetBounds() {
   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 NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
     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];

+ 4 - 0
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);

+ 1 - 0
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;

+ 10 - 0
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

+ 108 - 0
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(() => {