From 5122b06839d8fab75414eb6ad8ea7f9316d522c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=BCllner?= Date: Thu, 8 Feb 2018 10:38:04 +0000 Subject: [PATCH] altTab: Update the switcher when apps and/or windows get stopped/closed Make sure that the items from the applications switcher and the windows switcher are removed when the related applications get stopped, or some of the associated windows closed. https://bugzilla.gnome.org/show_bug.cgi?id=620106 --- js/ui/altTab.js | 70 +++++++++++++++++++++++++++++++++++++++--- js/ui/switcherPopup.js | 7 ++++- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/js/ui/altTab.js b/js/ui/altTab.js index 273bfd36a..a10e1433e 100644 --- a/js/ui/altTab.js +++ b/js/ui/altTab.js @@ -252,7 +252,7 @@ var AppSwitcherPopup = new Lang.Class({ let appIcon = this._items[this._selectedIndex]; if (this._currentWindow < 0) appIcon.app.activate_window(appIcon.cachedWindows[0], timestamp); - else + else if (appIcon.cachedWindows[this._currentWindow]) Main.activateWindow(appIcon.cachedWindows[this._currentWindow], timestamp); this.parent(); @@ -343,13 +343,18 @@ var AppSwitcherPopup = new Lang.Class({ }) }); this._thumbnails = null; - this._switcherList._items[this._selectedIndex].remove_accessible_state (Atk.StateType.EXPANDED); + if (this._switcherList._items[this._selectedIndex]) + this._switcherList._items[this._selectedIndex].remove_accessible_state (Atk.StateType.EXPANDED); }, _createThumbnails : function() { this._thumbnails = new ThumbnailList (this._items[this._selectedIndex].cachedWindows); this._thumbnails.connect('item-activated', Lang.bind(this, this._windowActivated)); this._thumbnails.connect('item-entered', Lang.bind(this, this._windowEntered)); + this._thumbnails.actor.connect('destroy', () => { + this._thumbnails = null; + this._thumbnailsFocused = false; + }); this.actor.add_actor(this._thumbnails.actor); @@ -645,6 +650,10 @@ var AppSwitcher = new Lang.Class({ _onDestroy: function() { if (this._mouseTimeOutId != 0) Mainloop.source_remove(this._mouseTimeOutId); + + this.icons.forEach(icon => { + icon.app.disconnect(icon._stateChangedId); + }); }, _setIconSize: function() { @@ -745,7 +754,7 @@ var AppSwitcher = new Lang.Class({ // show a dim arrow, but show a bright arrow when they are // highlighted. highlight : function(n, justOutline) { - if (this._curApp != -1) { + if (this.icons[this._curApp]) { if (this.icons[this._curApp].cachedWindows.length == 1) this._arrows[this._curApp].hide(); else @@ -767,6 +776,11 @@ var AppSwitcher = new Lang.Class({ this.icons.push(appIcon); let item = this.addItem(appIcon.actor, appIcon.label); + appIcon._stateChangedId = appIcon.app.connect('notify::state', app => { + if (app.state != Shell.AppState.RUNNING) + this._removeIcon(app); + }); + let n = this._arrows.length; let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' }); arrow.connect('repaint', function() { SwitcherPopup.drawArrow(arrow, St.Side.BOTTOM); }); @@ -777,7 +791,25 @@ var AppSwitcher = new Lang.Class({ arrow.hide(); else item.add_accessible_state (Atk.StateType.EXPANDABLE); - } + }, + + _removeIcon: function(app) { + let index = this.icons.findIndex(icon => { + return icon.app == app; + }); + if (index === -1) + return; + + this.icons.splice(index, 1); + this.removeItem(index); + + if (this._curApp == index) + this._curApp = SwitcherPopup.mod(index, this.icons.length); + if (this.icons.length > 0) + this.highlight(this._curApp); + else + this.actor.destroy(); + }, }); var ThumbnailList = new Lang.Class({ @@ -816,6 +848,8 @@ var ThumbnailList = new Lang.Class({ } } + + this.actor.connect('destroy', this._onDestroy.bind(this)); }, addClones : function (availHeight) { @@ -840,12 +874,38 @@ var ThumbnailList = new Lang.Class({ let clone = _createWindowClone(mutterWindow, thumbnailSize); this._thumbnailBins[i].set_height(binHeight); this._thumbnailBins[i].add_actor(clone); + + clone._destroyId = mutterWindow.connect('destroy', Lang.bind(this, this._removeThumbnail, clone)); this._clones.push(clone); } // Make sure we only do this once this._thumbnailBins = new Array(); - } + }, + + _removeThumbnail: function(source, clone) { + let index = this._clones.indexOf(clone); + if (index === -1) + return; + + this._clones.splice(index, 1); + this._windows.splice(index, 1); + this._labels.splice(index, 1); + this.removeItem(index); + + if (this._clones.length > 0) + this.highlight(SwitcherPopup.mod(index, this._clones.length)); + else + this.actor.destroy(); + }, + + _onDestroy: function() { + this._clones.forEach(clone => { + if (clone.source) + clone.source.disconnect(clone._destroyId); + }); + }, + }); var WindowIcon = new Lang.Class({ diff --git a/js/ui/switcherPopup.js b/js/ui/switcherPopup.js index 0e1f710df..9a29ce2c2 100644 --- a/js/ui/switcherPopup.js +++ b/js/ui/switcherPopup.js @@ -418,6 +418,11 @@ var SwitcherList = new Lang.Class({ return bbox; }, + removeItem: function(index) { + let item = this._items.splice(index, 1); + item[0].destroy(); + }, + _onItemClicked: function (index) { this._itemActivated(index); }, @@ -432,7 +437,7 @@ var SwitcherList = new Lang.Class({ }, highlight: function(index, justOutline) { - if (this._highlighted != -1) { + if (this._items[this._highlighted]) { this._items[this._highlighted].remove_style_pseudo_class('outlined'); this._items[this._highlighted].remove_style_pseudo_class('selected'); }