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:
parent
b0c7dac56b
commit
1f31e80c47
139
js/ui/overlay.js
139
js/ui/overlay.js
@ -218,6 +218,31 @@ Overlay.prototype = {
|
|||||||
|
|
||||||
//// Public methods ////
|
//// 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() {
|
show : function() {
|
||||||
if (this.visible)
|
if (this.visible)
|
||||||
return;
|
return;
|
||||||
@ -262,26 +287,29 @@ Overlay.prototype = {
|
|||||||
global.window_group.hide();
|
global.window_group.hide();
|
||||||
this._group.show();
|
this._group.show();
|
||||||
|
|
||||||
// Try to make the menu not too visible behind the empty space between
|
// Create a zoom out effect. First scale the overlay group up and
|
||||||
// the workspace previews by sliding in its clipping rectangle.
|
// position it so that the active workspace fills up the whole screen,
|
||||||
// We want to finish drawing the Dash just before the top workspace fully
|
// then transform the group to its normal dimensions and position.
|
||||||
// slides in on the top. Which means that we have more time to wait before
|
// The opposite transition is used in hide().
|
||||||
// drawing the dash if the active workspace is displayed on the bottom of
|
this._group.scaleX = this._group.scaleY = this.getZoomedInScale();
|
||||||
// the workspaces grid, and almost no time to wait if it is displayed in the top
|
[this._group.x, this._group.y] = this.getZoomedInPosition();
|
||||||
// row of the workspaces grid. The calculations used below try to roughly
|
Tweener.addTween(this._group,
|
||||||
// capture the animation ratio for when workspaces are covering the top of the overlay
|
{ x: 0,
|
||||||
// vs. when workspaces are already below the top of the overlay, and apply it
|
y: 0,
|
||||||
// to clipping the dash. The clipping is removed in this._showDone().
|
scaleX: 1,
|
||||||
this._dash.actor.set_clip(0, 0,
|
scaleY: 1,
|
||||||
this._workspaces.getFullSizeX(),
|
transition: 'easeOutQuad',
|
||||||
this._dash.actor.height);
|
|
||||||
Tweener.addTween(this._dash.actor,
|
|
||||||
{ clipWidthRight: this._dash._width + WORKSPACE_GRID_PADDING + this._workspaces.getWidthToTopActiveWorkspace(),
|
|
||||||
time: ANIMATION_TIME,
|
time: ANIMATION_TIME,
|
||||||
transition: "easeOutQuad",
|
|
||||||
onComplete: this._showDone,
|
onComplete: this._showDone,
|
||||||
onCompleteScope: this
|
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');
|
this.emit('showing');
|
||||||
@ -297,27 +325,29 @@ Overlay.prototype = {
|
|||||||
this._hideInProgress = true;
|
this._hideInProgress = true;
|
||||||
if (this._activeDisplayPane != null)
|
if (this._activeDisplayPane != null)
|
||||||
this._activeDisplayPane.close();
|
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();
|
this._workspaces.hide();
|
||||||
|
|
||||||
// Try to make the menu not too visible behind the empty space between
|
// Create a zoom in effect by transforming the overlay group so that
|
||||||
// the workspace previews by sliding in its clipping rectangle.
|
// the active workspace fills up the whole screen. The opposite
|
||||||
// The logic used is the same as described in this.show(). If the active workspace
|
// transition is used in show().
|
||||||
// is displayed in the top row, than almost full animation time is needed for it
|
let scale = this.getZoomedInScale();
|
||||||
// to reach the top of the overlay and cover the Dash fully, while if the
|
let [posX, posY] = this.getZoomedInPosition();
|
||||||
// active workspace is in the lower row, than the top left workspace reaches the
|
Tweener.addTween(this._group,
|
||||||
// top of the overlay sooner as it is moving out of the way.
|
{ x: posX,
|
||||||
// The clipping is removed in this._hideDone().
|
y: posY,
|
||||||
this._dash.actor.set_clip(0, 0,
|
scaleX: scale,
|
||||||
this._dash.actor.width + WORKSPACE_GRID_PADDING + this._workspaces.getWidthToTopActiveWorkspace(),
|
scaleY: scale,
|
||||||
this._dash.actor.height);
|
transition: 'easeOutQuad',
|
||||||
Tweener.addTween(this._dash.actor,
|
|
||||||
{ clipWidthRight: this._workspaces.getFullSizeX() + this._workspaces.getWidthToTopActiveWorkspace() - global.screen_width,
|
|
||||||
time: ANIMATION_TIME,
|
time: ANIMATION_TIME,
|
||||||
transition: "easeOutQuad",
|
|
||||||
onComplete: this._hideDone,
|
onComplete: this._hideDone,
|
||||||
onCompleteScope: this
|
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');
|
this.emit('hiding');
|
||||||
@ -347,21 +377,10 @@ Overlay.prototype = {
|
|||||||
|
|
||||||
//// Private methods ////
|
//// 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() {
|
_showDone: function() {
|
||||||
if (this._hideInProgress)
|
if (this._hideInProgress)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._dash.actor.raise_top();
|
|
||||||
this._dash.actor.remove_clip();
|
|
||||||
|
|
||||||
this.animationInProgress = false;
|
this.animationInProgress = false;
|
||||||
|
|
||||||
this.emit('shown');
|
this.emit('shown');
|
||||||
@ -375,7 +394,6 @@ Overlay.prototype = {
|
|||||||
this._workspaces.destroy();
|
this._workspaces.destroy();
|
||||||
this._workspaces = null;
|
this._workspaces = null;
|
||||||
|
|
||||||
this._dash.actor.remove_clip();
|
|
||||||
this._dash.hide();
|
this._dash.hide();
|
||||||
this._group.hide();
|
this._group.hide();
|
||||||
|
|
||||||
@ -388,36 +406,3 @@ Overlay.prototype = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
Signals.addSignalMethods(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);
|
|
||||||
}
|
|
||||||
|
@ -590,46 +590,48 @@ Workspace.prototype = {
|
|||||||
|
|
||||||
// Animate the full-screen to overlay transition.
|
// Animate the full-screen to overlay transition.
|
||||||
zoomToOverlay : function() {
|
zoomToOverlay : function() {
|
||||||
// Move the workspace into size/position
|
this.actor.set_position(this.gridX, this.gridY);
|
||||||
this.actor.set_position(this.fullSizeX, this.fullSizeY);
|
this.actor.set_scale(this.scale, this.scale);
|
||||||
|
|
||||||
this.updateInOverlay();
|
// 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;
|
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
|
// Animates the return from overlay mode
|
||||||
zoomFromOverlay : function() {
|
zoomFromOverlay : function() {
|
||||||
this.leavingOverlay = true;
|
this.leavingOverlay = true;
|
||||||
|
|
||||||
this._hideAllIcons();
|
this._hideAllIcons();
|
||||||
|
|
||||||
Tweener.addTween(this.actor,
|
Main.overlay.connect('hidden', Lang.bind(this,
|
||||||
{ x: this.fullSizeX,
|
this._doneLeavingOverlay));
|
||||||
y: this.fullSizeY,
|
|
||||||
scale_x: 1.0,
|
|
||||||
scale_y: 1.0,
|
|
||||||
time: Overlay.ANIMATION_TIME,
|
|
||||||
transition: "easeOutQuad",
|
|
||||||
onComplete: this._doneLeavingOverlay,
|
|
||||||
onCompleteScope: this
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// 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++) {
|
for (let i = 1; i < this._windows.length; i++) {
|
||||||
let clone = this._windows[i];
|
let clone = this._windows[i];
|
||||||
Tweener.addTween(clone.actor,
|
Tweener.addTween(clone.actor,
|
||||||
@ -906,9 +908,15 @@ Workspaces.prototype = {
|
|||||||
let lastWorkspace = this._workspaces[this._workspaces.length - 1];
|
let lastWorkspace = this._workspaces[this._workspaces.length - 1];
|
||||||
lastWorkspace.updateRemovable(true);
|
lastWorkspace.updateRemovable(true);
|
||||||
|
|
||||||
// Position/scale the desktop windows and their children
|
// Position/scale the desktop windows and their children after the
|
||||||
for (let w = 0; w < this._workspaces.length; w++)
|
// workspaces have been created. This cannot be done first because
|
||||||
this._workspaces[w].zoomToOverlay();
|
// 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
|
// Track changes to the number of workspaces
|
||||||
this._nWorkspacesNotifyId =
|
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() {
|
hide : function() {
|
||||||
let global = Shell.Global.get();
|
let global = Shell.Global.get();
|
||||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||||
@ -982,35 +979,17 @@ Workspaces.prototype = {
|
|||||||
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
global.window_manager.disconnect(this._switchWorkspaceNotifyId);
|
||||||
},
|
},
|
||||||
|
|
||||||
getFullSizeX : function() {
|
getScale : function() {
|
||||||
return this._workspaces[0].fullSizeX;
|
return this._workspaces[0].scale;
|
||||||
},
|
},
|
||||||
|
|
||||||
// If j-th workspace in the i-th row is active, returns the full width
|
// Get the grid position of the active workspace.
|
||||||
// of j workspaces including empty space if i = 1, or the width of one
|
getActiveWorkspacePosition : function() {
|
||||||
// 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() {
|
|
||||||
let global = Shell.Global.get();
|
let global = Shell.Global.get();
|
||||||
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
let activeWorkspaceIndex = global.screen.get_active_workspace_index();
|
||||||
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
|
let activeWorkspace = this._workspaces[activeWorkspaceIndex];
|
||||||
|
|
||||||
if (activeWorkspace.gridRow == 0)
|
return [activeWorkspace.gridX, activeWorkspace.gridY];
|
||||||
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();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Assign grid positions to workspaces. We can't just do a simple
|
// 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() {
|
_workspacesChanged : function() {
|
||||||
@ -1167,28 +1138,49 @@ Workspaces.prototype = {
|
|||||||
Tweener.registerSpecialPropertyModifier("workspace_relative", _workspaceRelativeModifier, _workspaceRelativeGet);
|
Tweener.registerSpecialPropertyModifier("workspace_relative", _workspaceRelativeModifier, _workspaceRelativeGet);
|
||||||
|
|
||||||
function _workspaceRelativeModifier(workspace) {
|
function _workspaceRelativeModifier(workspace) {
|
||||||
let endX, endY;
|
let [startX, startY] = Main.overlay.getPosition();
|
||||||
|
let overlayPosX, overlayPosY, overlayScale;
|
||||||
|
|
||||||
if (!workspace)
|
if (!workspace)
|
||||||
return [];
|
return [];
|
||||||
|
|
||||||
if (workspace.leavingOverlay) {
|
if (workspace.leavingOverlay) {
|
||||||
endX = workspace.fullSizeX;
|
let [zoomedInX, zoomedInY] = Main.overlay.getZoomedInPosition();
|
||||||
endY = workspace.fullSizeY;
|
overlayPosX = { begin: startX, end: zoomedInX };
|
||||||
|
overlayPosY = { begin: startY, end: zoomedInY };
|
||||||
|
overlayScale = { begin: Main.overlay.getScale(),
|
||||||
|
end: Main.overlay.getZoomedInScale() };
|
||||||
} else {
|
} else {
|
||||||
endX = workspace.gridX;
|
overlayPosX = { begin: startX, end: 0 };
|
||||||
endY = workspace.gridY;
|
overlayPosY = { begin: startY, end: 0 };
|
||||||
|
overlayScale = { begin: Main.overlay.getScale(), end: 1 };
|
||||||
}
|
}
|
||||||
|
|
||||||
return [ { name: "x",
|
return [ { name: "x",
|
||||||
parameters: { begin: workspace.actor.x, end: endX,
|
parameters: { workspacePos: workspace.gridX,
|
||||||
cur: function() { return workspace.actor.x; } } },
|
overlayPos: overlayPosX,
|
||||||
|
overlayScale: overlayScale } },
|
||||||
{ name: "y",
|
{ name: "y",
|
||||||
parameters: { begin: workspace.actor.y, end: endY,
|
parameters: { workspacePos: workspace.gridY,
|
||||||
cur: function() { return workspace.actor.y; } } }
|
overlayPos: overlayPosY,
|
||||||
|
overlayScale: overlayScale } }
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _workspaceRelativeGet(begin, end, time, params) {
|
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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user