Compare commits

...

4 Commits

Author SHA1 Message Date
Mario Sanchez Prada
043f042cec altTab: Close the list of thumbnails in the switcher when closing an app
This makes sure that no thumbnails list is left open and empty, since both
the app's icon and all its thumbnails will be gone once the app is closed.

https://bugzilla.gnome.org/show_bug.cgi?id=620106
2017-12-01 21:30:26 +00:00
Mario Sanchez Prada
815302b4ac altTab: Don't activate an app it the icon is no longer available
https://bugzilla.gnome.org/show_bug.cgi?id=620106
2017-12-01 21:30:17 +00:00
Mario Sanchez Prada
e608d4f26b altTab: Use arrow functions for callbacks
https://bugzilla.gnome.org/show_bug.cgi?id=620106
2017-12-01 21:29:49 +00:00
Florian Müllner
77c20e3b8e [appSwitcher] Add shortcut to quit application
Allow to quit the currently selected application by hitting Alt-q,
similar to the OS X switcher.

https://bugzilla.gnome.org/show_bug.cgi?id=620106
2017-11-30 16:58:18 +00:00
2 changed files with 103 additions and 28 deletions

View File

@ -156,6 +156,17 @@ var AppSwitcherPopup = new Lang.Class({
this._items[this._selectedIndex].cachedWindows.length); this._items[this._selectedIndex].cachedWindows.length);
}, },
_quitApplication: function(appIndex) {
let appIcon = this._items[appIndex];
if (!appIcon)
return;
// Make sure the list of thumbnails is hidden before quitting the
// application, not to keep an empty list of windows showing up.
this._select(appIndex, null, false);
appIcon.app.request_quit();
},
_keyPressHandler: function(keysym, action) { _keyPressHandler: function(keysym, action) {
if (action == Meta.KeyBindingAction.SWITCH_GROUP) { if (action == Meta.KeyBindingAction.SWITCH_GROUP) {
if (!this._thumbnailsFocused) if (!this._thumbnailsFocused)
@ -175,6 +186,8 @@ var AppSwitcherPopup = new Lang.Class({
this._select(this._selectedIndex, this._nextWindow()); this._select(this._selectedIndex, this._nextWindow());
else if (keysym == Clutter.Up) else if (keysym == Clutter.Up)
this._select(this._selectedIndex, null, true); this._select(this._selectedIndex, null, true);
else if (keysym == Clutter.q)
this._quitApplication(this._selectedIndex);
else else
return Clutter.EVENT_PROPAGATE; return Clutter.EVENT_PROPAGATE;
} else { } else {
@ -184,6 +197,8 @@ var AppSwitcherPopup = new Lang.Class({
this._select(this._next()); this._select(this._next());
else if (keysym == Clutter.Down) else if (keysym == Clutter.Down)
this._select(this._selectedIndex, 0); this._select(this._selectedIndex, 0);
else if (keysym == Clutter.q)
this._quitApplication(this._selectedIndex);
else else
return Clutter.EVENT_PROPAGATE; return Clutter.EVENT_PROPAGATE;
} }
@ -250,11 +265,12 @@ var AppSwitcherPopup = new Lang.Class({
_finish : function(timestamp) { _finish : function(timestamp) {
let appIcon = this._items[this._selectedIndex]; let appIcon = this._items[this._selectedIndex];
if (this._currentWindow < 0) if (appIcon) {
appIcon.app.activate_window(appIcon.cachedWindows[0], timestamp); if (this._currentWindow < 0)
else appIcon.app.activate_window(appIcon.cachedWindows[0], timestamp);
Main.activateWindow(appIcon.cachedWindows[this._currentWindow], timestamp); else if (appIcon.cachedWindows[this._currentWindow])
Main.activateWindow(appIcon.cachedWindows[this._currentWindow], timestamp);
}
this.parent(); this.parent();
}, },
@ -317,8 +333,7 @@ var AppSwitcherPopup = new Lang.Class({
} else if (this._items[this._selectedIndex].cachedWindows.length > 1 && } else if (this._items[this._selectedIndex].cachedWindows.length > 1 &&
!forceAppFocus) { !forceAppFocus) {
this._thumbnailTimeoutId = Mainloop.timeout_add ( this._thumbnailTimeoutId = Mainloop.timeout_add (
THUMBNAIL_POPUP_TIME, THUMBNAIL_POPUP_TIME, () => { this._timeoutPopupThumbnails(); });
Lang.bind(this, this._timeoutPopupThumbnails));
GLib.Source.set_name_by_id(this._thumbnailTimeoutId, '[gnome-shell] this._timeoutPopupThumbnails'); GLib.Source.set_name_by_id(this._thumbnailTimeoutId, '[gnome-shell] this._timeoutPopupThumbnails');
} }
}, },
@ -337,10 +352,11 @@ var AppSwitcherPopup = new Lang.Class({
{ opacity: 0, { opacity: 0,
time: THUMBNAIL_FADE_TIME, time: THUMBNAIL_FADE_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() { onComplete: () => {
thumbnailsActor.destroy(); thumbnailsActor.destroy();
this.thumbnailsVisible = false; this.thumbnailsVisible = false;
}) },
onCompleteScope: this
}); });
this._thumbnails = null; this._thumbnails = null;
this._switcherList._items[this._selectedIndex].remove_accessible_state (Atk.StateType.EXPANDED); this._switcherList._items[this._selectedIndex].remove_accessible_state (Atk.StateType.EXPANDED);
@ -348,8 +364,12 @@ var AppSwitcherPopup = new Lang.Class({
_createThumbnails : function() { _createThumbnails : function() {
this._thumbnails = new ThumbnailList (this._items[this._selectedIndex].cachedWindows); this._thumbnails = new ThumbnailList (this._items[this._selectedIndex].cachedWindows);
this._thumbnails.connect('item-activated', Lang.bind(this, this._windowActivated)); this._thumbnails.connect('item-activated', (thumbnailList, n) => { this._windowActivated(thumbnailList, n); });
this._thumbnails.connect('item-entered', Lang.bind(this, this._windowEntered)); this._thumbnails.connect('item-entered', (thumbnailList, n) => { this._windowEntered(thumbnailList, n); });
this._thumbnails.actor.connect('destroy', () => {
this._thumbnails = null;
this._thumbnailsFocused = false;
});
this.actor.add_actor(this._thumbnails.actor); this.actor.add_actor(this._thumbnails.actor);
@ -362,7 +382,8 @@ var AppSwitcherPopup = new Lang.Class({
{ opacity: 255, { opacity: 255,
time: THUMBNAIL_FADE_TIME, time: THUMBNAIL_FADE_TIME,
transition: 'easeOutQuad', transition: 'easeOutQuad',
onComplete: Lang.bind(this, function () { this.thumbnailsVisible = true; }) onComplete: () => { this.thumbnailsVisible = true; },
onCompleteScope: this
}); });
this._switcherList._items[this._selectedIndex].add_accessible_state (Atk.StateType.EXPANDED); this._switcherList._items[this._selectedIndex].add_accessible_state (Atk.StateType.EXPANDED);
@ -389,9 +410,8 @@ var CyclerHighlight = new Lang.Class({
this.actor.add_constraint(constraint); this.actor.add_constraint(constraint);
this.actor.connect('notify::allocation', this.actor.connect('notify::allocation', () => { this._onAllocationChanged(); });
Lang.bind(this, this._onAllocationChanged)); this.actor.connect('destroy', () => { this._onDestroy(); });
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
}, },
set window(w) { set window(w) {
@ -449,7 +469,7 @@ var CyclerPopup = new Lang.Class({
// We don't show an actual popup, so just provide what SwitcherPopup // We don't show an actual popup, so just provide what SwitcherPopup
// expects instead of inheriting from SwitcherList // expects instead of inheriting from SwitcherList
this._switcherList = { actor: new St.Widget(), this._switcherList = { actor: new St.Widget(),
highlight: Lang.bind(this, this._highlightItem), highlight: (index, justOutline) => { this._highlightItem(index, justOutline); },
connect: function() {} }; connect: function() {} };
}, },
@ -639,12 +659,15 @@ var AppSwitcher = new Lang.Class({
this._altTabPopup = altTabPopup; this._altTabPopup = altTabPopup;
this._mouseTimeOutId = 0; this._mouseTimeOutId = 0;
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', () => { this._onDestroy(); });
}, },
_onDestroy: function() { _onDestroy: function() {
if (this._mouseTimeOutId != 0) if (this._mouseTimeOutId != 0)
Mainloop.source_remove(this._mouseTimeOutId); Mainloop.source_remove(this._mouseTimeOutId);
for (let i = 0; i < this.icons.length; i++)
this.icons[i].app.disconnect(this.icons[i]._stateChangedId);
}, },
_setIconSize: function() { _setIconSize: function() {
@ -719,12 +742,11 @@ var AppSwitcher = new Lang.Class({
if (this._mouseTimeOutId != 0) if (this._mouseTimeOutId != 0)
Mainloop.source_remove(this._mouseTimeOutId); Mainloop.source_remove(this._mouseTimeOutId);
if (this._altTabPopup.thumbnailsVisible) { if (this._altTabPopup.thumbnailsVisible) {
this._mouseTimeOutId = Mainloop.timeout_add(APP_ICON_HOVER_TIMEOUT, this._mouseTimeOutId = Mainloop.timeout_add(APP_ICON_HOVER_TIMEOUT, () => {
Lang.bind(this, function () { this._enterItem(index);
this._enterItem(index); this._mouseTimeOutId = 0;
this._mouseTimeOutId = 0; return GLib.SOURCE_REMOVE;
return GLib.SOURCE_REMOVE; });
}));
GLib.Source.set_name_by_id(this._mouseTimeOutId, '[gnome-shell] this._enterItem'); GLib.Source.set_name_by_id(this._mouseTimeOutId, '[gnome-shell] this._enterItem');
} else } else
this._itemEntered(index); this._itemEntered(index);
@ -767,6 +789,11 @@ var AppSwitcher = new Lang.Class({
this.icons.push(appIcon); this.icons.push(appIcon);
let item = this.addItem(appIcon.actor, appIcon.label); 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 n = this._arrows.length;
let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' }); let arrow = new St.DrawingArea({ style_class: 'switcher-arrow' });
arrow.connect('repaint', function() { SwitcherPopup.drawArrow(arrow, St.Side.BOTTOM); }); arrow.connect('repaint', function() { SwitcherPopup.drawArrow(arrow, St.Side.BOTTOM); });
@ -777,7 +804,22 @@ var AppSwitcher = new Lang.Class({
arrow.hide(); arrow.hide();
else else
item.add_accessible_state (Atk.StateType.EXPANDABLE); item.add_accessible_state (Atk.StateType.EXPANDABLE);
} },
_removeIcon: function(app) {
for (let i = 0; i < this.icons.length; i++)
if (this.icons[i].app == app) {
this.icons.splice(i, 1);
this.removeItem(i);
if (this._curApp == i)
this._curApp = SwitcherPopup.mod(i, this.icons.length);
if (this.icons.length > 0)
this.highlight(this._curApp);
else
this.actor.destroy();
return;
}
},
}); });
var ThumbnailList = new Lang.Class({ var ThumbnailList = new Lang.Class({
@ -816,6 +858,8 @@ var ThumbnailList = new Lang.Class({
} }
} }
this.actor.connect('destroy', () => { this._onDestroy(); });
}, },
addClones : function (availHeight) { addClones : function (availHeight) {
@ -840,12 +884,38 @@ var ThumbnailList = new Lang.Class({
let clone = _createWindowClone(mutterWindow, thumbnailSize); let clone = _createWindowClone(mutterWindow, thumbnailSize);
this._thumbnailBins[i].set_height(binHeight); this._thumbnailBins[i].set_height(binHeight);
this._thumbnailBins[i].add_actor(clone); this._thumbnailBins[i].add_actor(clone);
clone._destroyId = mutterWindow.connect('destroy', () => { this._removeThumbnail(clone); });
this._clones.push(clone); this._clones.push(clone);
} }
// Make sure we only do this once // Make sure we only do this once
this._thumbnailBins = new Array(); this._thumbnailBins = new Array();
} },
_removeThumbnail: function(source, clone) {
for (let i = 0; i < this._clones.length; i++)
if (this._clones[i] == clone) {
this._clones.splice(i, 1);
this._windows.splice(i, 1);
this._labels.splice(i, 1);
this.removeItem(i);
if (this._clones.length > 0)
this.highlight(SwitcherPopup.mod(i, this._clones.length));
else
this.actor.destroy();
return;
}
},
_onDestroy: function() {
for (let i = 0; i < this._clones.length; i++) {
if (this._clones[i].source)
this._clones[i].source.disconnect(this._clones[i]._destroyId);
}
},
}); });
var WindowIcon = new Lang.Class({ var WindowIcon = new Lang.Class({

View File

@ -418,6 +418,11 @@ var SwitcherList = new Lang.Class({
return bbox; return bbox;
}, },
removeItem: function(index) {
let item = this._items.splice(index, 1);
item[0].destroy();
},
_onItemClicked: function (index) { _onItemClicked: function (index) {
this._itemActivated(index); this._itemActivated(index);
}, },
@ -432,7 +437,7 @@ var SwitcherList = new Lang.Class({
}, },
highlight: function(index, justOutline) { 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('outlined');
this._items[this._highlighted].remove_style_pseudo_class('selected'); this._items[this._highlighted].remove_style_pseudo_class('selected');
} }