Bug 584609 - Zoom the whole overlay when showing or hiding

Instead of only transforming the active workspace, create a
zooming effect when showing or hiding the overlay. This makes
the transitions simpler: the workspaces are now fixed to the
overlay actor group and will not slide over the Dash.

overlay.js: Add zoom animations, fade in/out Dash during those,
    remove obsolete Dash clipping and stacking logic, add public
    get[Scale|Position]() and getZoomedIn[Scale|Position]()
    functions.
workspaces.js: Remove zoom animations, add fade animations for
    the remove button, add helper functions for the overlay
    zooming, keep the movement of windows linear to that of
    their workspaces, remove the updatePosition() and
    updateInOverlay() functions and fullSize variables that
    were left from the old overlay design.
This commit is contained in:
Sander Dijkhuis 2009-08-11 00:31:39 +02:00
parent b0c7dac56b
commit 1f31e80c47
2 changed files with 138 additions and 161 deletions

View File

@ -218,6 +218,31 @@ Overlay.prototype = {
//// Public methods ////
// Returns the scale the overlay has when we just start zooming out
// to overview mode. That is, when just the active workspace is showing.
getZoomedInScale : function() {
return 1 / this._workspaces.getScale();
},
// Returns the position the overlay has when we just start zooming out
// to overview mode. That is, when just the active workspace is showing.
getZoomedInPosition : function() {
let [posX, posY] = this._workspaces.getActiveWorkspacePosition();
let scale = this.getZoomedInScale();
return [- posX * scale, - posY * scale];
},
// Returns the current scale of the overlay.
getScale : function() {
return this._group.scaleX;
},
// Returns the current position of the overlay.
getPosition : function() {
return [this._group.x, this._group.y];
},
show : function() {
if (this.visible)
return;
@ -262,26 +287,29 @@ Overlay.prototype = {
global.window_group.hide();
this._group.show();
// Try to make the menu not too visible behind the empty space between
// the workspace previews by sliding in its clipping rectangle.
// We want to finish drawing the Dash just before the top workspace fully
// slides in on the top. Which means that we have more time to wait before
// drawing the dash if the active workspace is displayed on the bottom of
// the workspaces grid, and almost no time to wait if it is displayed in the top
// row of the workspaces grid. The calculations used below try to roughly
// capture the animation ratio for when workspaces are covering the top of the overlay
// vs. when workspaces are already below the top of the overlay, and apply it
// to clipping the dash. The clipping is removed in this._showDone().
this._dash.actor.set_clip(0, 0,
this._workspaces.getFullSizeX(),
this._dash.actor.height);
Tweener.addTween(this._dash.actor,
{ clipWidthRight: this._dash._width + WORKSPACE_GRID_PADDING + this._workspaces.getWidthToTopActiveWorkspace(),
// Create a zoom out effect. First scale the overlay group up and
// position it so that the active workspace fills up the whole screen,
// then transform the group to its normal dimensions and position.
// The opposite transition is used in hide().
this._group.scaleX = this._group.scaleY = this.getZoomedInScale();
[this._group.x, this._group.y] = this.getZoomedInPosition();
Tweener.addTween(this._group,
{ x: 0,
y: 0,
scaleX: 1,
scaleY: 1,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._showDone,
onCompleteScope: this
});
// Make Dash fade in so that it doesn't appear to big.
this._dash.actor.opacity = 0;
Tweener.addTween(this._dash.actor,
{ opacity: 255,
transition: 'easeOutQuad',
time: ANIMATION_TIME
});
this.emit('showing');
@ -297,27 +325,29 @@ Overlay.prototype = {
this._hideInProgress = true;
if (this._activeDisplayPane != null)
this._activeDisplayPane.close();
// lower the panes, so that workspaces display is on top while sliding out
this._dash.actor.lower(this._workspaces.actor);
this._workspaces.hide();
// Try to make the menu not too visible behind the empty space between
// the workspace previews by sliding in its clipping rectangle.
// The logic used is the same as described in this.show(). If the active workspace
// is displayed in the top row, than almost full animation time is needed for it
// to reach the top of the overlay and cover the Dash fully, while if the
// active workspace is in the lower row, than the top left workspace reaches the
// top of the overlay sooner as it is moving out of the way.
// The clipping is removed in this._hideDone().
this._dash.actor.set_clip(0, 0,
this._dash.actor.width + WORKSPACE_GRID_PADDING + this._workspaces.getWidthToTopActiveWorkspace(),
this._dash.actor.height);
Tweener.addTween(this._dash.actor,
{ clipWidthRight: this._workspaces.getFullSizeX() + this._workspaces.getWidthToTopActiveWorkspace() - global.screen_width,
// Create a zoom in effect by transforming the overlay group so that
// the active workspace fills up the whole screen. The opposite
// transition is used in show().
let scale = this.getZoomedInScale();
let [posX, posY] = this.getZoomedInPosition();
Tweener.addTween(this._group,
{ x: posX,
y: posY,
scaleX: scale,
scaleY: scale,
transition: 'easeOutQuad',
time: ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._hideDone,
onCompleteScope: this
});
// Make Dash fade out so that it doesn't appear to big.
Tweener.addTween(this._dash.actor,
{ opacity: 0,
transition: 'easeOutQuad',
time: ANIMATION_TIME
});
this.emit('hiding');
@ -347,21 +377,10 @@ Overlay.prototype = {
//// Private methods ////
// Raises the Dash to the top, so that we can tell if the pointer is above one of its items.
// We need to do this once the workspaces are shown because the workspaces actor currently covers
// the whole screen, regardless of where the workspaces are actually displayed.
//
// Once we rework the workspaces actor to only cover the area it actually needs, we can
// remove this workaround. Also http://bugzilla.openedhand.com/show_bug.cgi?id=1513 requests being
// able to pick only a reactive actor at a certain position, rather than any actor. Being able
// to do that would allow us to not have to raise the Dash.
_showDone: function() {
if (this._hideInProgress)
return;
this._dash.actor.raise_top();
this._dash.actor.remove_clip();
this.animationInProgress = false;
this.emit('shown');
@ -375,7 +394,6 @@ Overlay.prototype = {
this._workspaces.destroy();
this._workspaces = null;
this._dash.actor.remove_clip();
this._dash.hide();
this._group.hide();
@ -388,36 +406,3 @@ Overlay.prototype = {
}
};
Signals.addSignalMethods(Overlay.prototype);
Tweener.registerSpecialProperty("clipHeightBottom", _clipHeightBottomGet, _clipHeightBottomSet);
function _clipHeightBottomGet(actor) {
let [xOffset, yOffset, clipWidth, clipHeight] = actor.get_clip();
return clipHeight;
}
function _clipHeightBottomSet(actor, clipHeight) {
actor.set_clip(0, 0, actor.width, clipHeight);
}
Tweener.registerSpecialProperty("clipHeightTop", _clipHeightTopGet, _clipHeightTopSet);
function _clipHeightTopGet(actor) {
let [xOffset, yOffset, clipWidth, clipHeight] = actor.get_clip();
return clipHeight;
}
function _clipHeightTopSet(actor, clipHeight) {
actor.set_clip(0, actor.height - clipHeight, actor.width, clipHeight);
}
Tweener.registerSpecialProperty("clipWidthRight", _clipWidthRightGet, _clipWidthRightSet);
function _clipWidthRightGet(actor) {
let [xOffset, yOffset, clipWidth, clipHeight] = actor.get_clip();
return clipWidth;
}
function _clipWidthRightSet(actor, clipWidth) {
actor.set_clip(0, 0, clipWidth, actor.height);
}

View File

@ -590,46 +590,48 @@ Workspace.prototype = {
// Animate the full-screen to overlay transition.
zoomToOverlay : function() {
// Move the workspace into size/position
this.actor.set_position(this.fullSizeX, this.fullSizeY);
this.updateInOverlay();
this.actor.set_position(this.gridX, this.gridY);
this.actor.set_scale(this.scale, this.scale);
// Position and scale the windows.
this.positionWindows(true);
// Fade in the remove button if available, so that it doesn't appear
// too abrubtly and doesn't start at a too big size.
if (this._removeButton) {
Tweener.removeTweens(this._removeButton);
this._removeButton.opacity = 0;
Tweener.addTween(this._removeButton,
{ opacity: 255,
time: Overlay.ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
this._visible = true;
},
// Animates the display of a workspace and its windows to have the current dimensions and position.
updateInOverlay : function() {
Tweener.addTween(this.actor,
{ x: this.gridX,
y: this.gridY,
scale_x: this.scale,
scale_y: this.scale,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
// Likewise for each of the windows in the workspace.
this.positionWindows(true);
},
// Animates the return from overlay mode
zoomFromOverlay : function() {
this.leavingOverlay = true;
this._hideAllIcons();
Tweener.addTween(this.actor,
{ x: this.fullSizeX,
y: this.fullSizeY,
scale_x: 1.0,
scale_y: 1.0,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: this._doneLeavingOverlay,
onCompleteScope: this
});
Main.overlay.connect('hidden', Lang.bind(this,
this._doneLeavingOverlay));
// Fade out the remove button if available, so that it doesn't
// disappear too abrubtly and doesn't become too big.
if (this._removeButton) {
Tweener.removeTweens(this._removeButton);
Tweener.addTween(this._removeButton,
{ opacity: 0,
time: Overlay.ANIMATION_TIME,
transition: 'easeOutQuad'
});
}
// Position and scale the windows.
for (let i = 1; i < this._windows.length; i++) {
let clone = this._windows[i];
Tweener.addTween(clone.actor,
@ -906,9 +908,15 @@ Workspaces.prototype = {
let lastWorkspace = this._workspaces[this._workspaces.length - 1];
lastWorkspace.updateRemovable(true);
// Position/scale the desktop windows and their children
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverlay();
// Position/scale the desktop windows and their children after the
// workspaces have been created. This cannot be done first because
// window movement depends on the Workspaces object being accessible
// as an Overlay member.
Main.overlay.connect('showing',
Lang.bind(this, function() {
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].zoomToOverlay();
}));
// Track changes to the number of workspaces
this._nWorkspacesNotifyId =
@ -945,17 +953,6 @@ Workspaces.prototype = {
}
},
// Updates position of the workspaces display based on the new coordinates.
// Preserves the old value for the coordinate, if the passed value is null.
updatePosition : function(x, y) {
if (x != null)
this._x = x;
if (y != null)
this._y = y;
this._updateInOverlay();
},
hide : function() {
let global = Shell.Global.get();
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
@ -982,35 +979,17 @@ Workspaces.prototype = {
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
},
getFullSizeX : function() {
return this._workspaces[0].fullSizeX;
getScale : function() {
return this._workspaces[0].scale;
},
// If j-th workspace in the i-th row is active, returns the full width
// of j workspaces including empty space if i = 1, or the width of one
// workspace.
// Used in overlay.js to determine when it is ok to remove the sideshow
// during animations for entering and leaving the overlay.
getWidthToTopActiveWorkspace : function() {
// Get the grid position of the active workspace.
getActiveWorkspacePosition : function() {
let global = Shell.Global.get();
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
if (activeWorkspace.gridRow == 0)
return (activeWorkspace.gridCol + 1) * global.screen_width + activeWorkspace.gridCol * GRID_SPACING;
else
return global.screen_width;
},
// Updates the workspaces display based on the current dimensions and position.
_updateInOverlay : function() {
let global = Shell.Global.get();
this._positionWorkspaces(global);
// Position/scale the desktop windows and their children
for (let w = 0; w < this._workspaces.length; w++)
this._workspaces[w].updateInOverlay();
return [activeWorkspace.gridX, activeWorkspace.gridY];
},
// Assign grid positions to workspaces. We can't just do a simple
@ -1063,14 +1042,6 @@ Workspaces.prototype = {
}
}
}
// Now figure out their full-size coordinates
for (let w = 0; w < this._workspaces.length; w++) {
let workspace = this._workspaces[w];
workspace.fullSizeX = (workspace.gridCol - activeWorkspace.gridCol) * (global.screen_width + GRID_SPACING);
workspace.fullSizeY = (workspace.gridRow - activeWorkspace.gridRow) * (global.screen_height + GRID_SPACING);
}
},
_workspacesChanged : function() {
@ -1167,28 +1138,49 @@ Workspaces.prototype = {
Tweener.registerSpecialPropertyModifier("workspace_relative", _workspaceRelativeModifier, _workspaceRelativeGet);
function _workspaceRelativeModifier(workspace) {
let endX, endY;
let [startX, startY] = Main.overlay.getPosition();
let overlayPosX, overlayPosY, overlayScale;
if (!workspace)
return [];
if (workspace.leavingOverlay) {
endX = workspace.fullSizeX;
endY = workspace.fullSizeY;
let [zoomedInX, zoomedInY] = Main.overlay.getZoomedInPosition();
overlayPosX = { begin: startX, end: zoomedInX };
overlayPosY = { begin: startY, end: zoomedInY };
overlayScale = { begin: Main.overlay.getScale(),
end: Main.overlay.getZoomedInScale() };
} else {
endX = workspace.gridX;
endY = workspace.gridY;
overlayPosX = { begin: startX, end: 0 };
overlayPosY = { begin: startY, end: 0 };
overlayScale = { begin: Main.overlay.getScale(), end: 1 };
}
return [ { name: "x",
parameters: { begin: workspace.actor.x, end: endX,
cur: function() { return workspace.actor.x; } } },
parameters: { workspacePos: workspace.gridX,
overlayPos: overlayPosX,
overlayScale: overlayScale } },
{ name: "y",
parameters: { begin: workspace.actor.y, end: endY,
cur: function() { return workspace.actor.y; } } }
parameters: { workspacePos: workspace.gridY,
overlayPos: overlayPosY,
overlayScale: overlayScale } }
];
}
function _workspaceRelativeGet(begin, end, time, params) {
return (begin + params.begin) + time * (end + params.end - (begin + params.begin)) - params.cur();
let curOverlayPos = (1 - time) * params.overlayPos.begin +
time * params.overlayPos.end;
let curOverlayScale = (1 - time) * params.overlayScale.begin +
time * params.overlayScale.end;
// Calculate the screen position of the window.
let screen = (1 - time) *
((begin + params.workspacePos) * params.overlayScale.begin +
params.overlayPos.begin) +
time *
((end + params.workspacePos) * params.overlayScale.end +
params.overlayPos.end);
// Return the workspace coordinates.
return (screen - curOverlayPos) / curOverlayScale - params.workspacePos;
}