35159f
From a518c9f57e5fe9c6b5ece5c6cb0534a83f0b2f2d Mon Sep 17 00:00:00 2001
35159f
From: Ray Strode <rstrode@redhat.com>
35159f
Date: Mon, 15 Jul 2019 13:52:58 -0400
35159f
Subject: [PATCH 1/8] appDisplay: Don't leak duplicate items in AppView
35159f
35159f
If an icon already exists in an app view with the same id, the
35159f
duplicate is not added on a call to addItem.  Unfortunately,
35159f
since it's not added, the icon actor gets orphaned and leaked.
35159f
35159f
This commit address the problem by introducing a new hasItem
35159f
method and disallowing callers to call addItem with a duplicate
35159f
in the first place.
35159f
35159f
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
35159f
---
35159f
 js/ui/appDisplay.js | 15 ++++++++++++---
35159f
 1 file changed, 12 insertions(+), 3 deletions(-)
35159f
35159f
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
35159f
index a07db6573..fa22f47e0 100644
35159f
--- a/js/ui/appDisplay.js
35159f
+++ b/js/ui/appDisplay.js
35159f
@@ -143,10 +143,14 @@ class BaseAppView {
35159f
         return this._allItems;
35159f
     }
35159f
 
35159f
+    hasItem(id) {
35159f
+        return this._items[id] !== undefined;
35159f
+    }
35159f
+
35159f
     addItem(icon) {
35159f
         let id = icon.id;
35159f
-        if (this._items[id] !== undefined)
35159f
-            return;
35159f
+	if (this.hasItem(id))
35159f
+            throw new Error(`icon with id ${id} already added to view`)
35159f
 
35159f
         this._allItems.push(icon);
35159f
         this._items[id] = icon;
35159f
@@ -386,6 +390,8 @@ var AllView = class AllView extends BaseAppView {
35159f
 
35159f
         let folders = this._folderSettings.get_strv('folder-children');
35159f
         folders.forEach(id => {
35159f
+            if (this.hasItem(id))
35159f
+                return;
35159f
             let path = this._folderSettings.path + 'folders/' + id + '/';
35159f
             let icon = new FolderIcon(id, path, this);
35159f
             icon.connect('name-changed', this._itemNameChanged.bind(this));
35159f
@@ -1165,7 +1171,10 @@ var FolderIcon = class FolderIcon {
35159f
         let excludedApps = this._folder.get_strv('excluded-apps');
35159f
         let appSys = Shell.AppSystem.get_default();
35159f
         let addAppId = appId => {
35159f
-            if (excludedApps.indexOf(appId) >= 0)
35159f
+            if (this.view.hasItem(appId))
35159f
+                return;
35159f
+
35159f
+            if (excludedApps.includes(appId))
35159f
                 return;
35159f
 
35159f
             let app = appSys.lookup_app(appId);
35159f
-- 
35159f
2.23.0
35159f
35159f
35159f
From 2b6aa9aed98c4854c2ad015879ddcb8d2bf91e9e Mon Sep 17 00:00:00 2001
35159f
From: Ray Strode <rstrode@redhat.com>
35159f
Date: Mon, 22 Jul 2019 11:06:30 -0400
35159f
Subject: [PATCH 2/8] iconGrid: Clear meta_later callback on destruction
35159f
35159f
The IconGrid code sometimes sets up a callback to be invoked
35159f
later right before being destroyed.
35159f
35159f
This commit adds a destroy handler to cancel the callback.
35159f
35159f
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
35159f
---
35159f
 js/ui/iconGrid.js | 16 ++++++++++++++--
35159f
 1 file changed, 14 insertions(+), 2 deletions(-)
35159f
35159f
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
35159f
index d51a443e8..1f05e67f3 100644
35159f
--- a/js/ui/iconGrid.js
35159f
+++ b/js/ui/iconGrid.js
35159f
@@ -210,6 +210,8 @@ var IconGrid = GObject.registerClass({
35159f
         this.rightPadding = 0;
35159f
         this.leftPadding = 0;
35159f
 
35159f
+        this._updateIconSizesLaterId = 0;
35159f
+
35159f
         this._items = [];
35159f
         this._clonesAnimating = [];
35159f
         // Pulled from CSS, but hardcode some defaults here
35159f
@@ -227,6 +229,14 @@ var IconGrid = GObject.registerClass({
35159f
 
35159f
         this.connect('actor-added', this._childAdded.bind(this));
35159f
         this.connect('actor-removed', this._childRemoved.bind(this));
35159f
+        this.connect('destroy', this._onDestroy.bind(this));
35159f
+    }
35159f
+
35159f
+    _onDestroy() {
35159f
+        if (this._updateIconSizesLaterId) {
35159f
+            Meta.later_remove (this._updateIconSizesLaterId);
35159f
+            this._updateIconSizesLaterId = 0;
35159f
+        }
35159f
     }
35159f
 
35159f
     _keyFocusIn(actor) {
35159f
@@ -757,12 +767,14 @@ var IconGrid = GObject.registerClass({
35159f
 
35159f
             this._updateSpacingForSize(availWidth, availHeight);
35159f
         }
35159f
-        Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
35159f
-                       this._updateIconSizes.bind(this));
35159f
+        if (!this._updateIconSizesLaterId)
35159f
+            this._updateIconSizesLaterId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
35159f
+                                                          this._updateIconSizes.bind(this));
35159f
     }
35159f
 
35159f
     // Note that this is ICON_SIZE as used by BaseIcon, not elsewhere in IconGrid; it's a bit messed up
35159f
     _updateIconSizes() {
35159f
+        this._updateIconSizesLaterId = 0;
35159f
         let scale = Math.min(this._fixedHItemSize, this._fixedVItemSize) / Math.max(this._hItemSize, this._vItemSize);
35159f
         let newIconSize = Math.floor(ICON_SIZE * scale);
35159f
         for (let i in this._items) {
35159f
-- 
35159f
2.23.0
35159f
35159f
35159f
From 14a2650548a5104d6a3ec7a1174a23264d79030a Mon Sep 17 00:00:00 2001
35159f
From: Ray Strode <rstrode@redhat.com>
35159f
Date: Mon, 22 Jul 2019 11:02:10 -0400
35159f
Subject: [PATCH 3/8] appDisplay: Add AppFolderPopup destroy handler
35159f
35159f
At the moment AppFolderPopup calls popdown on destruction,
35159f
which leads to open-state-changed getting emitted after
35159f
the actor associated with the popup is destroyed.
35159f
35159f
This commit handles ungrabbing and closing from an
35159f
actor destroy handler to side-step the open-state-changed
35159f
signal.
35159f
35159f
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
35159f
---
35159f
 js/ui/appDisplay.js | 9 +++++++++
35159f
 1 file changed, 9 insertions(+)
35159f
35159f
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
35159f
index fa22f47e0..b75d095d5 100644
35159f
--- a/js/ui/appDisplay.js
35159f
+++ b/js/ui/appDisplay.js
35159f
@@ -1329,6 +1329,15 @@ var AppFolderPopup = class AppFolderPopup {
35159f
         });
35159f
         this._grabHelper.addActor(Main.layoutManager.overviewGroup);
35159f
         this.actor.connect('key-press-event', this._onKeyPress.bind(this));
35159f
+        this.actor.connect('destroy', this._onDestroy.bind(this));
35159f
+    }
35159f
+
35159f
+    _onDestroy() {
35159f
+        if (this._isOpen) {
35159f
+            this._isOpen = false;
35159f
+            this._grabHelper.ungrab({ actor: this.actor });
35159f
+            this._grabHelper = null;
35159f
+        }
35159f
     }
35159f
 
35159f
     _onKeyPress(actor, event) {
35159f
-- 
35159f
2.23.0
35159f
35159f
35159f
From c9fcb2d23141694ffa2182df20ba75687b01dacc Mon Sep 17 00:00:00 2001
35159f
From: Ray Strode <rstrode@redhat.com>
35159f
Date: Thu, 18 Jul 2019 10:06:38 -0400
35159f
Subject: [PATCH 4/8] appDisplay: Clear AllView reference to current popup when
35159f
 destroyed
35159f
35159f
AllView contains a reference to the current popup that lingers after
35159f
the popup is destroyed.
35159f
35159f
This commit fixes that, by explicitly nullifying when appropriate.
35159f
35159f
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
35159f
---
35159f
 js/ui/appDisplay.js | 18 +++++++++++++++++-
35159f
 1 file changed, 17 insertions(+), 1 deletion(-)
35159f
35159f
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
35159f
index b75d095d5..dabf63bfd 100644
35159f
--- a/js/ui/appDisplay.js
35159f
+++ b/js/ui/appDisplay.js
35159f
@@ -300,6 +300,7 @@ var AllView = class AllView extends BaseAppView {
35159f
         this._eventBlocker.add_action(this._clickAction);
35159f
 
35159f
         this._displayingPopup = false;
35159f
+        this._currentPopupDestroyId = 0;
35159f
 
35159f
         this._availWidth = 0;
35159f
         this._availHeight = 0;
35159f
@@ -589,7 +590,22 @@ var AllView = class AllView extends BaseAppView {
35159f
         this._stack.add_actor(popup.actor);
35159f
         popup.connect('open-state-changed', (popup, isOpen) => {
35159f
             this._eventBlocker.reactive = isOpen;
35159f
-            this._currentPopup = isOpen ? popup : null;
35159f
+
35159f
+            if (this._currentPopup) {
35159f
+                this._currentPopup.actor.disconnect(this._currentPopupDestroyId);
35159f
+                this._currentPopupDestroyId = 0;
35159f
+            }
35159f
+
35159f
+            this._currentPopup = null;
35159f
+
35159f
+            if (isOpen) {
35159f
+                this._currentPopup = popup;
35159f
+                this._currentPopupDestroyId = popup.actor.connect('destroy', () => {
35159f
+                    this._currentPopup = null;
35159f
+                    this._currentPopupDestroyId = 0;
35159f
+                    this._eventBlocker.reactive = false;
35159f
+                });
35159f
+            }
35159f
             this._updateIconOpacities(isOpen);
35159f
             if(!isOpen)
35159f
                 this._closeSpaceForPopup();
35159f
-- 
35159f
2.23.0
35159f
35159f
35159f
From b7a3fd7fa4527ba9411dcd18debe6ccf88c34dc0 Mon Sep 17 00:00:00 2001
35159f
From: Ray Strode <rstrode@redhat.com>
35159f
Date: Mon, 22 Jul 2019 10:57:57 -0400
35159f
Subject: [PATCH 5/8] appDisplay: Add destroy handler for FolderIcon
35159f
35159f
It is important that the FolderView of a FolderIcon always
35159f
gets destroyed before the AppFolderPopup, since the view
35159f
may or may not be in the popup, and the view should
35159f
get cleaned up exactly once in either case.
35159f
35159f
This commit adds a destroy handler on FolderIcon to ensure
35159f
things get taken down in the right order, and to make sure
35159f
the view isn't leaked if it's not yet part of the popup.
35159f
35159f
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
35159f
---
35159f
 js/ui/appDisplay.js | 8 ++++++++
35159f
 1 file changed, 8 insertions(+)
35159f
35159f
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
35159f
index dabf63bfd..5a8f4f1bf 100644
35159f
--- a/js/ui/appDisplay.js
35159f
+++ b/js/ui/appDisplay.js
35159f
@@ -1156,6 +1156,7 @@ var FolderIcon = class FolderIcon {
35159f
             this.view.actor.vscroll.adjustment.value = 0;
35159f
             this._openSpaceForPopup();
35159f
         });
35159f
+        this.actor.connect('destroy', this.onDestroy.bind(this));
35159f
         this.actor.connect('notify::mapped', () => {
35159f
             if (!this.actor.mapped && this._popup)
35159f
                 this._popup.popdown();
35159f
@@ -1165,6 +1166,13 @@ var FolderIcon = class FolderIcon {
35159f
         this._redisplay();
35159f
     }
35159f
 
35159f
+    onDestroy() {
35159f
+        this.view.actor.destroy();
35159f
+
35159f
+        if (this._popup)
35159f
+            this._popup.actor.destroy();
35159f
+    }
35159f
+
35159f
     getAppIds() {
35159f
         return this.view.getAllItems().map(item => item.id);
35159f
     }
35159f
-- 
35159f
2.23.0
35159f
35159f
35159f
From a90d7a97d21ffa596747cc8ecd0e3f500cb8a77c Mon Sep 17 00:00:00 2001
35159f
From: Ray Strode <rstrode@redhat.com>
35159f
Date: Thu, 18 Jul 2019 14:49:30 -0400
35159f
Subject: [PATCH 6/8] appDisplay: Stop watching FolderIcon parent view when
35159f
 destroyed
35159f
35159f
When a FolderIcon is opened, it asks the parent view to allocate
35159f
space for it, which takes time.  Eventually, the space-ready
35159f
signal is emitted on the view and the icon can make use of the new
35159f
space with its popup.  If the icon gets destroyed in the
35159f
interim, though, space-ready signal handler still fires.
35159f
35159f
This commit disconnects the signal handler so it doesn't get called
35159f
on a destroyed icon.
35159f
35159f
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
35159f
---
35159f
 js/ui/appDisplay.js | 10 ++++++++--
35159f
 1 file changed, 8 insertions(+), 2 deletions(-)
35159f
35159f
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
35159f
index 5a8f4f1bf..062ff222c 100644
35159f
--- a/js/ui/appDisplay.js
35159f
+++ b/js/ui/appDisplay.js
35159f
@@ -1169,6 +1169,11 @@ var FolderIcon = class FolderIcon {
35159f
     onDestroy() {
35159f
         this.view.actor.destroy();
35159f
 
35159f
+        if (this._spaceReadySignalId) {
35159f
+            this._parentView.disconnect(this._spaceReadySignalId);
35159f
+            this._spaceReadySignalId = 0;
35159f
+        }
35159f
+
35159f
         if (this._popup)
35159f
             this._popup.actor.destroy();
35159f
     }
35159f
@@ -1240,8 +1245,9 @@ var FolderIcon = class FolderIcon {
35159f
     }
35159f
 
35159f
     _openSpaceForPopup() {
35159f
-        let id = this._parentView.connect('space-ready', () => {
35159f
-            this._parentView.disconnect(id);
35159f
+        this._spaceReadySignalId = this._parentView.connect('space-ready', () => {
35159f
+            this._parentView.disconnect(this._spaceReadySignalId);
35159f
+            this._spaceReadySignalId = 0;
35159f
             this._popup.popup();
35159f
             this._updatePopupPosition();
35159f
         });
35159f
-- 
35159f
2.23.0
35159f
35159f
35159f
From b57ab33dadf0f31c5bf2c800806593e94784050c Mon Sep 17 00:00:00 2001
35159f
From: Ray Strode <rstrode@redhat.com>
35159f
Date: Thu, 18 Jul 2019 10:19:13 -0400
35159f
Subject: [PATCH 7/8] appDisplay: Add open method to FolderIcon
35159f
35159f
At the moment the only way to open a folder icon is to click on it;
35159f
there's no API to open the icon programmatically.
35159f
35159f
This commits adds an open method and makes the click handler use
35159f
it.
35159f
35159f
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
35159f
---
35159f
 js/ui/appDisplay.js | 12 +++++++-----
35159f
 1 file changed, 7 insertions(+), 5 deletions(-)
35159f
35159f
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
35159f
index 062ff222c..c0c6e3663 100644
35159f
--- a/js/ui/appDisplay.js
35159f
+++ b/js/ui/appDisplay.js
35159f
@@ -1151,11 +1151,7 @@ var FolderIcon = class FolderIcon {
35159f
 
35159f
         this.view = new FolderView();
35159f
 
35159f
-        this.actor.connect('clicked', () => {
35159f
-            this._ensurePopup();
35159f
-            this.view.actor.vscroll.adjustment.value = 0;
35159f
-            this._openSpaceForPopup();
35159f
-        });
35159f
+        this.actor.connect('clicked', this.open.bind(this));
35159f
         this.actor.connect('destroy', this.onDestroy.bind(this));
35159f
         this.actor.connect('notify::mapped', () => {
35159f
             if (!this.actor.mapped && this._popup)
35159f
@@ -1178,6 +1174,12 @@ var FolderIcon = class FolderIcon {
35159f
             this._popup.actor.destroy();
35159f
     }
35159f
 
35159f
+    open() {
35159f
+        this._ensurePopup();
35159f
+        this.view.actor.vscroll.adjustment.value = 0;
35159f
+        this._openSpaceForPopup();
35159f
+    }
35159f
+
35159f
     getAppIds() {
35159f
         return this.view.getAllItems().map(item => item.id);
35159f
     }
35159f
-- 
35159f
2.23.0
35159f
35159f
35159f
From baacab7922a56957d041aa59944c419b82e7a7e1 Mon Sep 17 00:00:00 2001
35159f
From: Ray Strode <rstrode@redhat.com>
35159f
Date: Thu, 18 Jul 2019 11:13:27 -0400
35159f
Subject: [PATCH 8/8] appDisplay: Keep popup open on refresh
35159f
35159f
If the list of applications is refreshed we currently close
35159f
the open app folder.
35159f
35159f
This commit adds logic to reopen the app folder on reload.
35159f
35159f
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/628
35159f
---
35159f
 js/ui/appDisplay.js | 15 +++++++++++++++
35159f
 1 file changed, 15 insertions(+)
35159f
35159f
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
35159f
index c0c6e3663..7fad02cd0 100644
35159f
--- a/js/ui/appDisplay.js
35159f
+++ b/js/ui/appDisplay.js
35159f
@@ -345,6 +345,21 @@ var AllView = class AllView extends BaseAppView {
35159f
         super.removeAll();
35159f
     }
35159f
 
35159f
+    _redisplay() {
35159f
+        let openFolderId = null;
35159f
+        if (this._displayingPopup && this._currentPopup)
35159f
+            openFolderId = this._currentPopup._source.id;
35159f
+
35159f
+        super._redisplay();
35159f
+
35159f
+        if (openFolderId) {
35159f
+            let [folderToReopen] = this.folderIcons.filter(folder => folder.id == openFolderId);
35159f
+
35159f
+            if (folderToReopen)
35159f
+                folderToReopen.open();
35159f
+        }
35159f
+    }
35159f
+
35159f
     _itemNameChanged(item) {
35159f
         // If an item's name changed, we can pluck it out of where it's
35159f
         // supposed to be and reinsert it where it's sorted.
35159f
-- 
35159f
2.23.0
35159f