Browse Source

fix: correctly enable and disable windows on Windows and Linux (backport: 4-0-x) (#15256)

* fix: correctly enable and disable windows

* Move has_child_modal_ to NativeWindowViews

* Track modal children as an int instead of a bool

* Use correct types

* Move Increment/DecrementChildModals to NativeWindowViews

* Use parent() in NativeWindowViews

* Add test for not enabling disabled parent when modal child closes
trop[bot] 6 years ago
parent
commit
f30c382d41

+ 32 - 14
atom/browser/native_window_views.cc

@@ -344,7 +344,7 @@ bool NativeWindowViews::IsFocused() {
 void NativeWindowViews::Show() {
   if (is_modal() && NativeWindow::parent() &&
       !widget()->native_widget_private()->IsVisible())
-    NativeWindow::parent()->SetEnabled(false);
+    static_cast<NativeWindowViews*>(parent())->IncrementChildModals();
 
   widget()->native_widget_private()->ShowWithWindowState(GetRestoredState());
 
@@ -369,7 +369,7 @@ void NativeWindowViews::ShowInactive() {
 
 void NativeWindowViews::Hide() {
   if (is_modal() && NativeWindow::parent())
-    NativeWindow::parent()->SetEnabled(true);
+    static_cast<NativeWindowViews*>(parent())->DecrementChildModals();
 
   widget()->Hide();
 
@@ -393,16 +393,34 @@ bool NativeWindowViews::IsEnabled() {
 #endif
 }
 
+void NativeWindowViews::IncrementChildModals() {
+  num_modal_children_++;
+  SetEnabledInternal(ShouldBeEnabled());
+}
+
+void NativeWindowViews::DecrementChildModals() {
+  if (num_modal_children_ > 0) {
+    num_modal_children_--;
+  }
+  SetEnabledInternal(ShouldBeEnabled());
+}
+
 void NativeWindowViews::SetEnabled(bool enable) {
-  // Handle multiple calls of SetEnabled correctly.
-  if (enable) {
-    --disable_count_;
-    if (disable_count_ != 0)
-      return;
-  } else {
-    ++disable_count_;
-    if (disable_count_ != 1)
-      return;
+  if (enable != is_enabled_) {
+    is_enabled_ = enable;
+    SetEnabledInternal(ShouldBeEnabled());
+  }
+}
+
+bool NativeWindowViews::ShouldBeEnabled() {
+  return is_enabled_ && (num_modal_children_ == 0);
+}
+
+void NativeWindowViews::SetEnabledInternal(bool enable) {
+  if (enable && IsEnabled()) {
+    return;
+  } else if (!enable && !IsEnabled()) {
+    return;
   }
 
 #if defined(OS_WIN)
@@ -1161,10 +1179,10 @@ void NativeWindowViews::OnWidgetBoundsChanged(views::Widget* changed_widget,
 }
 
 void NativeWindowViews::DeleteDelegate() {
-  if (is_modal() && NativeWindow::parent()) {
-    auto* parent = NativeWindow::parent();
+  if (is_modal() && this->parent()) {
+    auto* parent = this->parent();
     // Enable parent window after current window gets closed.
-    parent->SetEnabled(true);
+    static_cast<NativeWindowViews*>(parent)->DecrementChildModals();
     // Focus on parent window.
     parent->Focus(true);
   }

+ 12 - 2
atom/browser/native_window_views.h

@@ -134,6 +134,9 @@ class NativeWindowViews : public NativeWindow,
 
   void UpdateDraggableRegions(std::unique_ptr<SkRegion> region);
 
+  void IncrementChildModals();
+  void DecrementChildModals();
+
 #if defined(OS_WIN)
   void SetIcon(HICON small_icon, HICON app_icon);
 #elif defined(USE_X11)
@@ -190,6 +193,10 @@ class NativeWindowViews : public NativeWindow,
                                         LPARAM l_param);
 #endif
 
+  // Enable/disable:
+  bool ShouldBeEnabled();
+  void SetEnabledInternal(bool enabled);
+
   // NativeWindow:
   void HandleKeyboardEvent(
       content::WebContents*,
@@ -273,8 +280,11 @@ class NativeWindowViews : public NativeWindow,
   // has to been explicitly provided.
   std::unique_ptr<SkRegion> draggable_region_;  // used in custom drag.
 
-  // How many times the Disable has been called.
-  int disable_count_ = 0;
+  // Whether the window should be enabled based on user calls to SetEnabled()
+  bool is_enabled_ = true;
+  // How many modal children this window has;
+  // used to determine enabled state
+  unsigned int num_modal_children_ = 0;
 
   bool use_content_size_ = false;
   bool movable_ = true;

+ 10 - 1
spec/api-browser-window-spec.js

@@ -2974,7 +2974,7 @@ describe('BrowserWindow module', () => {
         c.show()
         assert.strictEqual(w.isEnabled(), false)
       })
-      it('enables parent window when closed', (done) => {
+      it('re-enables an enabled parent window when closed', (done) => {
         c.once('closed', () => {
           assert.strictEqual(w.isEnabled(), true)
           done()
@@ -2982,6 +2982,15 @@ describe('BrowserWindow module', () => {
         c.show()
         c.close()
       })
+      it('does not re-enable a disabled parent window when closed', (done) => {
+        c.once('closed', () => {
+          assert.strictEqual(w.isEnabled(), false)
+          done()
+        })
+        w.setEnabled(false)
+        c.show()
+        c.close()
+      })
       it('disables parent window recursively', () => {
         const c2 = new BrowserWindow({ show: false, parent: w, modal: true })
         c.show()