overview: Do not zoom the desktop background

While scaling the desktop background with the window previews represents
workspaces quite intuitively, the approach is not without problems.
As window previews in the overview behave quite differently to "real"
windows, the representation of workspaces as miniature versions of
"real" workspaces is flawed. The scaling also makes the transitions
to and from the overview much more visually expensive, without adding
much benefit.
Leaving the background in place provides more visual stability to the
transitions and emphasizes the distinctive behavior of elements in the
overview.

https://bugzilla.gnome.org/show_bug.cgi?id=634948
This commit is contained in:
Florian Müllner 2010-07-15 16:21:32 +02:00
parent 1ea488bb3d
commit f24e567dc4
4 changed files with 112 additions and 152 deletions

View File

@ -257,10 +257,6 @@ StTooltip StLabel {
/* Overview */ /* Overview */
.overview {
background-color: #111;
}
.new-workspace-area { .new-workspace-area {
border: 2px solid rgba(255, 255, 255, 0.8); border: 2px solid rgba(255, 255, 255, 0.8);
border-radius: 10px; border-radius: 10px;

View File

@ -1,6 +1,7 @@
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
const Clutter = imports.gi.Clutter; const Clutter = imports.gi.Clutter;
const Meta = imports.gi.Meta;
const Mainloop = imports.mainloop; const Mainloop = imports.mainloop;
const Signals = imports.signals; const Signals = imports.signals;
const Lang = imports.lang; const Lang = imports.lang;
@ -172,7 +173,16 @@ function Overview() {
Overview.prototype = { Overview.prototype = {
_init : function() { _init : function() {
this._group = new St.Group({ style_class: 'overview' }); // The actual global.background_actor is inside global.window_group,
// which is hidden when displaying the overview, so we display a clone.
this._background = new Clutter.Clone({ source: global.background_actor });
this._background.hide();
global.overlay_group.add_actor(this._background);
this._desktopFade = new St.Bin();
global.overlay_group.add_actor(this._desktopFade);
this._group = new St.Group({ name: 'overview' });
this._group._delegate = this; this._group._delegate = this;
this._group.connect('destroy', Lang.bind(this, this._group.connect('destroy', Lang.bind(this,
function() { function() {
@ -209,10 +219,6 @@ Overview.prototype = {
reactive: true }); reactive: true });
this._group.add_actor(this._transparentBackground); this._group.add_actor(this._transparentBackground);
// Background color for the Overview
this._backOver = new St.Label();
this._group.add_actor(this._backOver);
this._group.hide(); this._group.hide();
global.overlay_group.add_actor(this._group); global.overlay_group.add_actor(this._group);
@ -238,6 +244,20 @@ Overview.prototype = {
this.workspaces = null; this.workspaces = null;
}, },
_getDesktopClone: function() {
let windows = global.get_window_actors().filter(function(w) {
return w.meta_window.get_window_type() == Meta.WindowType.DESKTOP;
});
if (windows.length == 0)
return null;
let clone = new Clutter.Clone({ source: windows[0].get_texture() });
clone.source.connect('destroy', Lang.bind(this, function() {
clone.destroy();
}));
return clone;
},
_onViewChanged: function() { _onViewChanged: function() {
if (!this.visible) if (!this.visible)
return; return;
@ -314,11 +334,6 @@ Overview.prototype = {
this._workspacesBarWidth = this._workspacesWidth; this._workspacesBarWidth = this._workspacesWidth;
this._workspacesBarY = primary.height - displayGridRowHeight; this._workspacesBarY = primary.height - displayGridRowHeight;
// The parent (this._group) is positioned at the top left of the primary monitor
// while this._backOver occupies the entire screen.
this._backOver.set_position(- primary.x, - primary.y);
this._backOver.set_size(global.screen_width, global.screen_height);
this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING, this._paneContainer.set_position(this._dash.actor.x + this._dash.actor.width + DEFAULT_PADDING,
this._workspacesY); this._workspacesY);
// Dynamic width // Dynamic width
@ -456,6 +471,19 @@ Overview.prototype = {
this._group.add_actor(this._workspacesBar); this._group.add_actor(this._workspacesBar);
this._workspacesBar.raise(this.workspaces.actor); this._workspacesBar.raise(this.workspaces.actor);
if (!this._desktopFade.child)
this._desktopFade.child = this._getDesktopClone();
if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) {
this._desktopFade.opacity = 255;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 0,
time: ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
// All the the actors in the window group are completely obscured, // All the the actors in the window group are completely obscured,
// hiding the group holding them while the Overview is displayed greatly // hiding the group holding them while the Overview is displayed greatly
// increases performance of the Overview especially when there are many // increases performance of the Overview especially when there are many
@ -465,6 +493,7 @@ Overview.prototype = {
// clones of them, this would obviously no longer be necessary. // clones of them, this would obviously no longer be necessary.
global.window_group.hide(); global.window_group.hide();
this._group.show(); this._group.show();
this._background.show();
// Create a zoom out effect. First scale the Overview group up and // Create a zoom out effect. First scale the Overview group up and
// position it so that the active workspace fills up the whole screen, // position it so that the active workspace fills up the whole screen,
@ -502,6 +531,16 @@ Overview.prototype = {
this.animationInProgress = true; this.animationInProgress = true;
this._hideInProgress = true; this._hideInProgress = true;
if (!this.workspaces.getActiveWorkspace().hasMaximizedWindows()) {
this._desktopFade.opacity = 0;
this._desktopFade.show();
Tweener.addTween(this._desktopFade,
{ opacity: 255,
time: ANIMATION_TIME,
transition: 'easeOutQuad' });
}
if (this._activeDisplayPane != null) if (this._activeDisplayPane != null)
this._activeDisplayPane.close(); this._activeDisplayPane.close();
this.workspaces.hide(); this.workspaces.hide();
@ -559,6 +598,7 @@ Overview.prototype = {
return; return;
this.animationInProgress = false; this.animationInProgress = false;
this._desktopFade.hide();
this._coverPane.lower_bottom(); this._coverPane.lower_bottom();
this.emit('shown'); this.emit('shown');
@ -576,6 +616,8 @@ Overview.prototype = {
this._workspacesManager = null; this._workspacesManager = null;
this._dash.hide(); this._dash.hide();
this._desktopFade.hide();
this._background.hide();
this._group.hide(); this._group.hide();
this.visible = false; this.visible = false;

View File

@ -140,7 +140,10 @@ WindowClone.prototype = {
if (this.inDrag || this._zooming) if (this.inDrag || this._zooming)
// We'll fix up the stack after the drag/zooming // We'll fix up the stack after the drag/zooming
return; return;
this.actor.raise(this._stackAbove); if (this._stackAbove == null)
this.actor.lower_bottom();
else
this.actor.raise(this._stackAbove);
}, },
destroy: function () { destroy: function () {
@ -247,11 +250,13 @@ WindowClone.prototype = {
this.emit('zoom-end'); this.emit('zoom-end');
this.actor.reparent(this._origParent); this.actor.reparent(this._origParent);
if (this._stackAbove == null)
this.actor.lower_bottom();
// If the workspace has been destroyed while we were reparented to // If the workspace has been destroyed while we were reparented to
// the stage, _stackAbove will be unparented and we can't raise our // the stage, _stackAbove will be unparented and we can't raise our
// actor above it - as we are bound to be destroyed anyway in that // actor above it - as we are bound to be destroyed anyway in that
// case, we can skip that step // case, we can skip that step
if (this._stackAbove && this._stackAbove.get_parent()) else if (this._stackAbove.get_parent())
this.actor.raise(this._stackAbove); this.actor.raise(this._stackAbove);
[this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition(); [this.actor.x, this.actor.y] = this._zoomLocalOrig.getPosition();
@ -283,82 +288,20 @@ WindowClone.prototype = {
// We may not have a parent if DnD completed successfully, in // We may not have a parent if DnD completed successfully, in
// which case our clone will shortly be destroyed and replaced // which case our clone will shortly be destroyed and replaced
// with a new one on the target workspace. // with a new one on the target workspace.
if (this.actor.get_parent() != null) if (this.actor.get_parent() != null) {
this.actor.raise(this._stackAbove); if (this._stackAbove == null)
this.actor.lower_bottom();
else
this.actor.raise(this._stackAbove);
}
this.emit('drag-end'); this.emit('drag-end');
} }
}; };
Signals.addSignalMethods(WindowClone.prototype); Signals.addSignalMethods(WindowClone.prototype);
function DesktopClone(window) {
this._init(window);
}
DesktopClone.prototype = {
_init : function(window) {
this.actor = new Clutter.Group({ reactive: true });
let background = new Clutter.Clone({ source: global.background_actor });
this.actor.add_actor(background);
if (window) {
this._desktop = new Clutter.Clone({ source: window.get_texture() });
this.actor.add_actor(this._desktop);
this._desktop.hide();
} else {
this._desktop = null;
}
this.actor.connect('button-release-event',
Lang.bind(this, this._onButtonRelease));
},
zoomFromOverview: function(fadeInIcons) {
if (this._desktop == null)
return;
if (fadeInIcons) {
this._desktop.opacity = 0;
this._desktop.show();
Tweener.addTween(this._desktop,
{ opacity: 255,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad' });
}
},
zoomToOverview: function(fadeOutIcons) {
if (this._desktop == null)
return;
if (fadeOutIcons) {
this._desktop.opacity = 255;
this._desktop.show();
Tweener.addTween(this._desktop,
{ opacity: 0,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this,
function() {
this._desktop.hide();
})
});
} else {
this._desktop.hide();
}
},
_onButtonRelease : function (actor, event) {
this.emit('selected', event.get_time());
}
};
Signals.addSignalMethods(DesktopClone.prototype);
/** /**
* @windowClone: Corresponding window clone * @windowClone: Corresponding window clone
* @parentActor: The actor which will be the parent of all overlay items * @parentActor: The actor which will be the parent of all overlay items
@ -559,7 +502,6 @@ WindowOverlay.prototype = {
this._parentActor.queue_relayout(); this._parentActor.queue_relayout();
} }
}; };
Signals.addSignalMethods(WindowOverlay.prototype); Signals.addSignalMethods(WindowOverlay.prototype);
const WindowPositionFlags = { const WindowPositionFlags = {
@ -583,10 +525,20 @@ Workspace.prototype = {
// Without this the drop area will be overlapped. // Without this the drop area will be overlapped.
this._windowOverlaysGroup.set_size(0, 0); this._windowOverlaysGroup.set_size(0, 0);
this.actor = new Clutter.Group(); this.actor = new Clutter.Group({ reactive: true });
this.actor._delegate = this; this.actor._delegate = this;
this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this.actor.connect('button-release-event', Lang.bind(this,
function(actor, event) {
// Only switch to the workspace when there's no application
// windows open. The problem is that it's too easy to miss
// an app window and get the wrong one focused.
if (this._windows.length == 0) {
this.metaWorkspace.activate(event.get_time());
Main.overview.hide();
}
}));
// Items in _windowOverlaysGroup should not be scaled, so we don't // Items in _windowOverlaysGroup should not be scaled, so we don't
// add them to this.actor, but to its parent whenever it changes // add them to this.actor, but to its parent whenever it changes
@ -602,35 +554,10 @@ Workspace.prototype = {
let windows = global.get_window_actors().filter(this._isMyWindow, this); let windows = global.get_window_actors().filter(this._isMyWindow, this);
// Find the desktop window
for (let i = 0; i < windows.length; i++) {
if (windows[i].meta_window.get_window_type() == Meta.WindowType.DESKTOP) {
this._desktop = new DesktopClone(windows[i]);
break;
}
}
// If there wasn't one, fake it
if (!this._desktop)
this._desktop = new DesktopClone();
this._desktop.connect('selected',
Lang.bind(this,
function(clone, time) {
// Only switch to the workspace when there's no application windows
// open (we always have one window for the desktop). The problem
// is that it's too easy to miss an app window and get the wrong
// one focused.
if (this._windows.length == 1) {
this.metaWorkspace.activate(time);
Main.overview.hide();
}
}));
this.actor.add_actor(this._desktop.actor);
// Create clones for remaining windows that should be // Create clones for remaining windows that should be
// visible in the Overview // visible in the Overview
this._windows = [this._desktop]; this._windows = [];
this._windowOverlays = [ null ]; this._windowOverlays = [];
for (let i = 0; i < windows.length; i++) { for (let i = 0; i < windows.length; i++) {
if (this._isOverviewWindow(windows[i])) { if (this._isOverviewWindow(windows[i])) {
this._addWindowClone(windows[i]); this._addWindowClone(windows[i]);
@ -745,10 +672,10 @@ Workspace.prototype = {
// FIXME: do something cooler-looking using clutter-cairo // FIXME: do something cooler-looking using clutter-cairo
this._frame = new Clutter.Rectangle({ color: FRAME_COLOR }); this._frame = new Clutter.Rectangle({ color: FRAME_COLOR });
this.actor.add_actor(this._frame); this.actor.add_actor(this._frame);
this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x, this._frame.set_position(- FRAME_SIZE / this.actor.scale_x,
this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y); - FRAME_SIZE / this.actor.scale_y);
this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, this._frame.set_size(this.actor.width + 2 * FRAME_SIZE / this.actor.scale_x,
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); this.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
this._frame.lower_bottom(); this._frame.lower_bottom();
this._framePosHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFramePosition)); this._framePosHandler = this.actor.connect('notify::scale-x', Lang.bind(this, this._updateFramePosition));
@ -768,14 +695,14 @@ Workspace.prototype = {
* Set the workspace (desktop) reactive * Set the workspace (desktop) reactive
**/ **/
setReactive: function(reactive) { setReactive: function(reactive) {
this._desktop.actor.reactive = reactive; this.actor.reactive = reactive;
}, },
_updateFramePosition : function() { _updateFramePosition : function() {
this._frame.set_position(this._desktop.actor.x - FRAME_SIZE / this.actor.scale_x, this._frame.set_position(- FRAME_SIZE / this.actor.scale_x,
this._desktop.actor.y - FRAME_SIZE / this.actor.scale_y); - FRAME_SIZE / this.actor.scale_y);
this._frame.set_size(this._desktop.actor.width + 2 * FRAME_SIZE / this.actor.scale_x, this._frame.set_size(this.actor.width + 2 * FRAME_SIZE / this.actor.scale_x,
this._desktop.actor.height + 2 * FRAME_SIZE / this.actor.scale_y); this.actor.height + 2 * FRAME_SIZE / this.actor.scale_y);
}, },
_isCloneVisible: function(clone) { _isCloneVisible: function(clone) {
@ -786,7 +713,7 @@ Workspace.prototype = {
* _getVisibleClones: * _getVisibleClones:
* *
* Returns a list WindowClone objects where the clone isn't filtered * Returns a list WindowClone objects where the clone isn't filtered
* out by any application filter. The clone for the desktop is excluded. * out by any application filter.
* The returned array will always be newly allocated; it is not in any * The returned array will always be newly allocated; it is not in any
* defined order, and thus it's convenient to call .sort() with your * defined order, and thus it's convenient to call .sort() with your
* choice of sorting function. * choice of sorting function.
@ -794,7 +721,7 @@ Workspace.prototype = {
_getVisibleClones: function() { _getVisibleClones: function() {
let visible = []; let visible = [];
for (let i = 1; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
if (!this._isCloneVisible(clone)) if (!this._isCloneVisible(clone))
@ -806,7 +733,7 @@ Workspace.prototype = {
}, },
_resetCloneVisibility: function () { _resetCloneVisibility: function () {
for (let i = 1; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
let overlay = this._windowOverlays[i]; let overlay = this._windowOverlays[i];
@ -1005,9 +932,9 @@ Workspace.prototype = {
let buttonOuterHeight, captionHeight; let buttonOuterHeight, captionHeight;
let buttonOuterWidth = 0; let buttonOuterWidth = 0;
if (this._windowOverlays[1]) { if (this._windowOverlays[0]) {
[buttonOuterHeight, captionHeight] = this._windowOverlays[1].chromeHeights(); [buttonOuterHeight, captionHeight] = this._windowOverlays[0].chromeHeights();
buttonOuterWidth = this._windowOverlays[1].chromeWidth() / this.scale; buttonOuterWidth = this._windowOverlays[0].chromeWidth() / this.scale;
} else } else
[buttonOuterHeight, captionHeight] = [0, 0]; [buttonOuterHeight, captionHeight] = [0, 0];
buttonOuterHeight /= this.scale; buttonOuterHeight /= this.scale;
@ -1126,8 +1053,6 @@ Workspace.prototype = {
}, },
syncStacking: function(stackIndices) { syncStacking: function(stackIndices) {
let desktopClone = this._windows[0];
let visibleClones = this._getVisibleClones(); let visibleClones = this._getVisibleClones();
visibleClones.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; }); visibleClones.sort(function (a, b) { return stackIndices[a.metaWindow.get_stable_sequence()] - stackIndices[b.metaWindow.get_stable_sequence()]; });
@ -1135,7 +1060,7 @@ Workspace.prototype = {
let clone = visibleClones[i]; let clone = visibleClones[i];
let metaWindow = clone.metaWindow; let metaWindow = clone.metaWindow;
if (i == 0) { if (i == 0) {
clone.setStackAbove(desktopClone.actor); clone.setStackAbove(null);
} else { } else {
let previousClone = visibleClones[i - 1]; let previousClone = visibleClones[i - 1];
clone.setStackAbove(previousClone.actor); clone.setStackAbove(previousClone.actor);
@ -1170,7 +1095,7 @@ Workspace.prototype = {
}, },
_fadeInAllOverlays: function() { _fadeInAllOverlays: function() {
for (let i = 1; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
let overlay = this._windowOverlays[i]; let overlay = this._windowOverlays[i];
if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows)) if (this._showOnlyWindows != null && !(clone.metaWindow in this._showOnlyWindows))
@ -1180,7 +1105,7 @@ Workspace.prototype = {
}, },
_hideAllOverlays: function() { _hideAllOverlays: function() {
for (let i = 1; i< this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let overlay = this._windowOverlays[i]; let overlay = this._windowOverlays[i];
overlay.hide(); overlay.hide();
} }
@ -1307,8 +1232,8 @@ Workspace.prototype = {
}, },
// check for maximized windows on the workspace // check for maximized windows on the workspace
_haveMaximizedWindows: function() { hasMaximizedWindows: function() {
for (let i = 1; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let metaWindow = this._windows[i].metaWindow; let metaWindow = this._windows[i].metaWindow;
if (metaWindow.showing_on_its_workspace() && if (metaWindow.showing_on_its_workspace() &&
metaWindow.maximized_horizontally && metaWindow.maximized_horizontally &&
@ -1329,12 +1254,6 @@ Workspace.prototype = {
else else
this.positionWindows(WindowPositionFlags.ZOOM); this.positionWindows(WindowPositionFlags.ZOOM);
let active = global.screen.get_active_workspace();
let fadeInIcons = (Main.overview.animationInProgress &&
active == this.metaWorkspace &&
!this._haveMaximizedWindows());
this._desktop.zoomToOverview(fadeInIcons);
this._visible = true; this._visible = true;
}, },
@ -1352,7 +1271,7 @@ Workspace.prototype = {
this._doneLeavingOverview)); this._doneLeavingOverview));
// Position and scale the windows. // Position and scale the windows.
for (let i = 1; i < this._windows.length; i++) { for (let i = 0; i < this._windows.length; i++) {
let clone = this._windows[i]; let clone = this._windows[i];
clone.zoomFromOverview(); clone.zoomFromOverview();
@ -1381,11 +1300,6 @@ Workspace.prototype = {
} }
} }
let active = global.screen.get_active_workspace();
let fadeOutIcons = (active == this.metaWorkspace &&
!this._haveMaximizedWindows());
this._desktop.zoomFromOverview(fadeOutIcons);
this._visible = false; this._visible = false;
}, },
@ -1449,7 +1363,7 @@ Workspace.prototype = {
// Don't let the user try to select this workspace as it's // Don't let the user try to select this workspace as it's
// making its exit. // making its exit.
this._desktop.reactive = false; this.actor.reactive = false;
}, },
destroy : function() { destroy : function() {
@ -1473,7 +1387,7 @@ Workspace.prototype = {
// their parent (this.actor), but we might have a zoomed window // their parent (this.actor), but we might have a zoomed window
// which has been reparented to the stage - _windows[0] holds // which has been reparented to the stage - _windows[0] holds
// the desktop window, which is never reparented // the desktop window, which is never reparented
for (let w = 1; w < this._windows.length; w++) for (let w = 0; w < this._windows.length; w++)
this._windows[w].destroy(); this._windows[w].destroy();
this._windows = []; this._windows = [];
}, },
@ -1532,7 +1446,7 @@ Workspace.prototype = {
}, },
_onShowOverlayClose: function (windowOverlay) { _onShowOverlayClose: function (windowOverlay) {
for (let i = 1; i < this._windowOverlays.length; i++) { for (let i = 0; i < this._windowOverlays.length; i++) {
let overlay = this._windowOverlays[i]; let overlay = this._windowOverlays[i];
if (overlay == windowOverlay) if (overlay == windowOverlay)
continue; continue;

View File

@ -117,6 +117,11 @@ GenericWorkspacesView.prototype = {
} }
}, },
getActiveWorkspace: function() {
let active = global.screen.get_active_workspace_index();
return this._workspaces[active];
},
_clearApplicationWindowSelection: function(reposition) { _clearApplicationWindowSelection: function(reposition) {
if (this._windowSelectionAppId == null) if (this._windowSelectionAppId == null)
return; return;
@ -826,7 +831,7 @@ SingleView.prototype = {
if (index < 0 || index >= global.n_workspaces) if (index < 0 || index >= global.n_workspaces)
return; return;
let dragActor = this._workspaces[index]._desktop.actor; let dragActor = this._workspaces[index].actor;
if (draggable) { if (draggable) {
this._workspaces[index].actor.reactive = true; this._workspaces[index].actor.reactive = true;
@ -856,6 +861,9 @@ SingleView.prototype = {
// start dragging the active workspace // start dragging the active workspace
_onButtonPress: function(actor, event) { _onButtonPress: function(actor, event) {
if (actor != event.get_source())
return;
if (this._dragIndex == -1) if (this._dragIndex == -1)
return; return;