Clean up Workspace animation. #567865

Use Tweener.registerSpecialPropertyModifier to handle moving windows in
straight lines on the screen even while their parent workspaces are also
moving.

Have the remove buttons track their parent workspace scale and deal with
it automatically rather than having special cases in each Workspace method.

svn path=/trunk/; revision=157
This commit is contained in:
Dan Winship 2009-01-21 20:35:20 +00:00
parent ff5f960978
commit 136ce3257d

View File

@ -122,11 +122,10 @@ Workspace.prototype = {
this._removeButton.connect('button-press-event', Lang.bind(this, this._removeSelf)); this._removeButton.connect('button-press-event', Lang.bind(this, this._removeSelf));
this.actor.add_actor(this._removeButton); this.actor.add_actor(this._removeButton);
this._adjustRemoveButton();
this._adjustRemoveButtonId = this.actor.connect('notify::scale-x', Lang.bind(this, this._adjustRemoveButton));
if (this._visible) { if (this._visible) {
this._removeButton.set_position(
this.gridX + (this._desktop.width * this.scale - this._removeButton.width) / 2,
this.gridY + (this._desktop.height * this.scale - this._removeButton.height) / 2);
this._removeButton.set_opacity(0); this._removeButton.set_opacity(0);
Tweener.addTween(this._removeButton, Tweener.addTween(this._removeButton,
{ opacity: 255, { opacity: 255,
@ -146,25 +145,32 @@ Workspace.prototype = {
onComplete: this._removeRemoveButton, onComplete: this._removeRemoveButton,
onCompleteScope: this onCompleteScope: this
}); });
} else { } else
this._removeButton.destroy(); this._removeRemoveButton();
this._removeButton = null;
}
} }
}, },
_adjustRemoveButton : function() {
this._removeButton.set_scale(1.0 / this.actor.scale_x,
1.0 / this.actor.scale_y);
this._removeButton.set_position(
(this.actor.width - this._removeButton.width / this.actor.scale_x) / 2,
(this.actor.height - this._removeButton.height / this.actor.scale_y) / 2);
},
_removeRemoveButton : function() { _removeRemoveButton : function() {
this._removeButton.destroy(); this._removeButton.destroy();
this._removeButton = null; this._removeButton = null;
this.actor.disconnect(this._adjustRemoveButtonId);
}, },
// Animate the full-screen to overlay transition. // Animate the full-screen to overlay transition.
zoomToOverlay : function() { zoomToOverlay : function() {
let global = Shell.Global.get(); let global = Shell.Global.get();
// Move the desktop into size/position // Move the workspace into size/position
this._desktop.set_position(this.fullSizeX, this.fullSizeY); this.actor.set_position(this.fullSizeX, this.fullSizeY);
Tweener.addTween(this._desktop, Tweener.addTween(this.actor,
{ x: this.gridX, { x: this.gridX,
y: this.gridY, y: this.gridY,
scale_x: this.scale, scale_x: this.scale,
@ -173,120 +179,74 @@ Workspace.prototype = {
transition: "easeOutQuad" transition: "easeOutQuad"
}); });
// Likewise for each of the windows in the workspace. This // Likewise for each of the windows in the workspace.
// would be easier if we just positioned and scaled the entire
// workspace group rather than going each window individually,
// but if we do that then the windows of the active workspace
// will trace out a curved path as they move into place, which
// looks odd. Positioning everything independently lets us
// move them in a straight line to their final destination.
for (let i = 1; i < this._windows.length; i++) { for (let i = 1; i < this._windows.length; i++) {
let window = this._windows[i]; let window = this._windows[i];
let [xCenter, yCenter, fraction] = this._computeWindowPosition(i); let [xCenter, yCenter, fraction] = this._computeWindowPosition(i);
xCenter = this.gridX + this.scale * (xCenter * global.screen_width); xCenter = xCenter * global.screen_width;
yCenter = this.gridY + this.scale * (yCenter * global.screen_height); yCenter = yCenter * global.screen_height;
let size = Math.max(window.width, window.height); let size = Math.max(window.width, window.height);
let desiredSize = global.screen_width * fraction; let desiredSize = global.screen_width * fraction;
let scale = Math.min(desiredSize / size, 1.0) * this.scale; let scale = Math.min(desiredSize / size, 1.0);
window.set_position(this.fullSizeX + window.origX, this.fullSizeY + window.origY);
Tweener.addTween(window, Tweener.addTween(window,
{ x: xCenter - 0.5 * scale * window.width, { x: xCenter - 0.5 * scale * window.width,
y: yCenter - 0.5 * scale * window.height, y: yCenter - 0.5 * scale * window.height,
scale_x: scale, scale_x: scale,
scale_y: scale, scale_y: scale,
workspace_relative: this,
time: Overlay.ANIMATION_TIME, time: Overlay.ANIMATION_TIME,
opacity: WINDOW_OPACITY, opacity: WINDOW_OPACITY,
transition: "easeOutQuad" transition: "easeOutQuad"
}); });
} }
// If the workspace is removable, animate in its removeButton
if (this._removeButton) {
this._removeButton.set_position(
this.fullSizeX + (this._desktop.width - this._removeButton.width) / 2,
this.fullSizeY + (this._desktop.height - this._removeButton.height) / 2);
this._removeButton.set_opacity(0);
Tweener.addTween(this._removeButton,
{ x: this.gridX + (this._desktop.width * this.scale - this._removeButton.width) / 2,
y: this.gridY + (this._desktop.height * this.scale - this._removeButton.height) / 2,
opacity: 255,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
}
this._visible = true; this._visible = true;
}, },
// Animates the return from overlay mode // Animates the return from overlay mode
zoomFromOverlay : function() { zoomFromOverlay : function() {
for (let i = 0; i < this._windows.length; i++) { Tweener.addTween(this.actor,
{ x: this.fullSizeX,
y: this.fullSizeY,
scale_x: 1.0,
scale_y: 1.0,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
for (let i = 1; i < this._windows.length; i++) {
let window = this._windows[i]; let window = this._windows[i];
if (window.cloneTitle) if (window.cloneTitle)
window.cloneTitle.hide(); window.cloneTitle.hide();
Tweener.addTween(window, Tweener.addTween(window,
{ x: this.fullSizeX + window.origX, { x: window.origX,
y: this.fullSizeY + window.origY, y: window.origY,
scale_x: 1.0, scale_x: 1.0,
scale_y: 1.0, scale_y: 1.0,
workspace_relative: this,
time: Overlay.ANIMATION_TIME, time: Overlay.ANIMATION_TIME,
opacity: 255, opacity: 255,
transition: "easeOutQuad" transition: "easeOutQuad"
}); });
} }
if (this._removeButton) {
Tweener.addTween(this._removeButton,
{ x: this.fullSizeX + (this._desktop.width - this._removeButton.width) / 2,
y: this.fullSizeY + (this._desktop.height - this._removeButton.height) / 2,
opacity: 0,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
}
this._visible = false; this._visible = false;
}, },
// Animates grid shrinking/expanding when a row or column // Animates grid shrinking/expanding when a row or column
// of workspaces is added or removed // of workspaces is added or removed
resizeToGrid : function (oldScale) { resizeToGrid : function (oldScale) {
let me = this; Tweener.addTween(this.actor,
let rescale = this.scale / oldScale; { x: this.gridX,
y: this.gridY,
for (let i = 0; i < this._windows.length; i++) { scale_x: this.scale,
let newX = this.gridX + (this._windows[i].x - this._desktop.x) * rescale; scale_y: this.scale,
let newY = this.gridY + (this._windows[i].y - this._desktop.y) * rescale; time: Overlay.ANIMATION_TIME,
let newWindowScale = this._windows[i].scale_x * rescale; transition: "easeOutQuad"
});
let window = this._windows[i];
Tweener.addTween(window,
{ x: newX,
y: newY,
scale_x: newWindowScale,
scale_y: newWindowScale,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad",
onComplete: function () {
me._adjustCloneTitle(window);
}
});
}
if (this._removeButton) {
// This gets layered on top of any already-running fade-out
// animation from setRemovable
Tweener.addTween(this._removeButton,
{ x: this.gridX + (this._desktop.width * this.scale - this._removeButton.width) / 2,
y: this.gridY + (this._desktop.height * this.scale - this._removeButton.height) / 2,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
}
}, },
// Animates the addition of a new (empty) workspace // Animates the addition of a new (empty) workspace
@ -294,13 +254,13 @@ Workspace.prototype = {
let global = Shell.Global.get(); let global = Shell.Global.get();
if (this.gridCol > this.gridRow) { if (this.gridCol > this.gridRow) {
this._desktop.set_position(global.screen_width, this.gridY); this.actor.set_position(global.screen_width, this.gridY);
this._desktop.set_scale(oldScale, oldScale); this.actor.set_scale(oldScale, oldScale);
} else { } else {
this._desktop.set_position(this.gridX, global.screen_height); this.actor.set_position(this.gridX, global.screen_height);
this._desktop.set_scale(this.scale, this.scale); this.actor.set_scale(this.scale, this.scale);
} }
Tweener.addTween(this._desktop, Tweener.addTween(this.actor,
{ x: this.gridX, { x: this.gridX,
y: this.gridY, y: this.gridY,
scale_x: this.scale, scale_x: this.scale,
@ -309,33 +269,19 @@ Workspace.prototype = {
transition: "easeOutQuad" transition: "easeOutQuad"
}); });
if (this._removeButton) {
this._removeButton.set_position(
this._desktop.x + (this._desktop.width * oldScale - this._removeButton.width) / 2,
this._desktop.y + (this._desktop.height * oldScale - this._removeButton.height) / 2);
this._removeButton.set_opacity(0);
Tweener.addTween(this._removeButton,
{ x: this.gridX + (this._desktop.width * this.scale - this._removeButton.width) / 2,
y: this.gridY + (this._desktop.height * this.scale - this._removeButton.height) / 2,
opacity: 255,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
}
this._visible = true; this._visible = true;
}, },
// Animates the removal of a workspace // Animates the removal of a workspace
slideOut : function(onComplete) { slideOut : function(onComplete) {
let global = Shell.Global.get(); let global = Shell.Global.get();
let destX = this._desktop.x, destY = this._desktop.y; let destX = this.actor.x, destY = this.actor.y;
if (this.gridCol > this.gridRow) if (this.gridCol > this.gridRow)
destX = global.screen_width; destX = global.screen_width;
else else
destY = global.screen_height; destY = global.screen_height;
Tweener.addTween(this._desktop, Tweener.addTween(this.actor,
{ x: destX, { x: destX,
y: destY, y: destY,
scale_x: this.scale, scale_x: this.scale,
@ -345,17 +291,6 @@ Workspace.prototype = {
onComplete: onComplete onComplete: onComplete
}); });
if (this._removeButton) {
// This gets layered on top of any already-running fade-out
// animation from setRemovable()
Tweener.addTween(this._removeButton,
{ x: destX + (this._desktop.width * this.scale - this._removeButton.width) / 2,
y: destY + (this._desktop.height * this.scale - this._removeButton.height) / 2,
time: Overlay.ANIMATION_TIME,
transition: "easeOutQuad"
});
}
this._visible = false; this._visible = false;
}, },
@ -436,10 +371,13 @@ Workspace.prototype = {
}, },
_cloneEnter: function (clone, event) { _cloneEnter: function (clone, event) {
if (Tweener.getTweenCount(this.actor))
return;
if (!clone.cloneTitle) if (!clone.cloneTitle)
this._createCloneTitle(clone); this._createCloneTitle(clone);
clone.cloneTitle.show();
this._adjustCloneTitle(clone); this._adjustCloneTitle(clone);
clone.cloneTitle.show();
if (!this._overlappedMode) if (!this._overlappedMode)
return; return;
if (clone.index != this._windows.length-1) { if (clone.index != this._windows.length-1) {
@ -449,6 +387,8 @@ Workspace.prototype = {
}, },
_cloneLeave: function (clone, event) { _cloneLeave: function (clone, event) {
if (!clone.cloneTitle)
return;
clone.cloneTitle.hide(); clone.cloneTitle.hide();
if (!this._overlappedMode) if (!this._overlappedMode)
return; return;
@ -467,7 +407,7 @@ Workspace.prototype = {
corner_radius: 5, corner_radius: 5,
padding: 4, padding: 4,
spacing: 4, spacing: 4,
orientation: Big.BoxOrientation.HORIZONTAL}); orientation: Big.BoxOrientation.HORIZONTAL});
let icon = window.meta_window.mini_icon; let icon = window.meta_window.mini_icon;
let iconTexture = new Clutter.Texture({ x: clone.x, let iconTexture = new Clutter.Texture({ x: clone.x,
@ -480,24 +420,30 @@ Workspace.prototype = {
font_name: "Sans 12", font_name: "Sans 12",
text: window.meta_window.title, text: window.meta_window.title,
ellipsize: Pango.EllipsizeMode.END}); ellipsize: Pango.EllipsizeMode.END});
box.append(title, Big.BoxPackFlags.EXPAND); box.append(title, Big.BoxPackFlags.EXPAND);
// Get and cache the expected width (just the icon), with spacing, plus title // Get and cache the expected width (just the icon), with spacing, plus title
box.fullWidth = box.width; box.fullWidth = box.width;
box.hide(); // Hidden by default, show on mouseover box.hide(); // Hidden by default, show on mouseover
clone.cloneTitle = box; clone.cloneTitle = box;
let parent = clone.get_parent(); this.actor.add_actor(box);
parent.add_actor(box);
}, },
_adjustCloneTitle : function (clone) { _adjustCloneTitle : function (clone) {
let transformed = clone.get_transformed_size();
let title = clone.cloneTitle; let title = clone.cloneTitle;
if (!title) if (!title)
return; return;
title.width = Math.min(title.fullWidth, transformed[0]);
let xoff = (transformed[0] - title.width)/2; let transformed = clone.get_transformed_size();
title.set_position(clone.x+xoff, clone.y); let cloneWidth = transformed[0];
// Set the title scale to the inverse of this.actor's scale;
// this means its scale is 1.0 with respect to the stage
// (and thus, in the same units as cloneWidth).
title.set_scale(1.0 / this.scale, 1.0 / this.scale);
title.width = Math.min(title.fullWidth, cloneWidth);
let xoff = ((cloneWidth - title.width) / 2) / this.scale;
title.set_position(clone.x + xoff, clone.y);
}, },
_activateWindow : function(w, time) { _activateWindow : function(w, time) {
@ -780,3 +726,32 @@ Workspaces.prototype = {
global.screen.append_new_workspace(false, event.get_time()); global.screen.append_new_workspace(false, event.get_time());
} }
}; };
// Create a SpecialPropertyModifier to let us move windows in a
// straight line on the screen even though their containing workspace
// is also moving.
Tweener.registerSpecialPropertyModifier("workspace_relative", _workspace_relative_modifier, _workspace_relative_get);
function _workspace_relative_modifier(workspace) {
let endX, endY;
if (workspace.actor.x == workspace.fullSizeX) {
endX = workspace.gridX;
endY = workspace.gridY;
} else {
endX = workspace.fullSizeX;
endY = workspace.fullSizeY;
}
return [ { name: "x",
parameters: { begin: workspace.actor.x, end: endX,
cur: function() { return workspace.actor.x; } } },
{ name: "y",
parameters: { begin: workspace.actor.y, end: endY,
cur: function() { return workspace.actor.y; } } }
];
}
function _workspace_relative_get(begin, end, time, params) {
return (begin + params.begin) + time * (end + params.end - (begin + params.begin)) - params.cur();
}