Animate workspace view switches

Replace the hard switch between linear and mosaic workspace view
with a an animated transition between the views.

https://bugzilla.gnome.org/show_bug.cgi?id=610191
This commit is contained in:
Florian Müllner 2010-02-15 14:29:34 +01:00
parent a9fea8248c
commit 5a6c9f176e
3 changed files with 154 additions and 45 deletions

View File

@ -263,9 +263,6 @@ Overview.prototype = {
// Show new workspacesView
this._group.add_actor(this._workspaces.actor);
this._dash.actor.raise(this._workspaces.actor);
// Set new position and scale to workspaces.
this.emit('showing');
},
_recalculateGridSizes: function () {

View File

@ -1212,6 +1212,9 @@ Workspace.prototype = {
cloneWidth = this.scale * clone.actor.scale_x * cloneWidth;
cloneHeight = this.scale * clone.actor.scale_y * cloneHeight;
if (!this._windowOverlaysGroup.visible)
this._windowOverlaysGroup.show();
if (overlay) {
overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight);
overlay.fadeIn();

View File

@ -55,7 +55,10 @@ GenericWorkspacesView.prototype = {
let node = this.actor.get_theme_node();
let [a, spacing] = node.get_length('spacing', false);
this._spacing = spacing;
this._positionWorkspaces();
if (Main.overview.animationInProgress)
this._positionWorkspaces();
else
this._transitionWorkspaces();
}));
this._width = width;
@ -248,6 +251,10 @@ GenericWorkspacesView.prototype = {
throw new Error("Not implemented");
},
_transitionWorkspaces: function() {
throw new Error("Not implemented");
},
_positionWorkspaces: function() {
throw new Error("Not implemented");
},
@ -326,6 +333,54 @@ MosaicView.prototype = {
}
},
_transitionWorkspaces: function() {
// update workspace parameters
this._positionWorkspaces();
let active = global.screen.get_active_workspace_index();
let activeWorkspace = this._workspaces[active];
// scale is the factor needed to translate from the new scale
// (this view) to the currently active scale (previous view)
let scale = this._workspaces[0].actor.scale_x / activeWorkspace.scale;
for (let w = 0; w < this._workspaces.length; w++) {
let workspace = this._workspaces[w];
let originX, originY;
let dx, dy;
// The correct transition would be a straightforward animation
// of each workspace's old position/scale to the new one;
// however, this looks overly busy, so we only use a zoom effect.
// Unfortunately this implies that we cannot pretend to not knowing
// the other view's layout at this point:
// We position the workspaces in the grid, which we scale up so
// that the active workspace fills the viewport.
dx = workspace.gridX - activeWorkspace.gridX;
dy = workspace.gridY - activeWorkspace.gridY;
originX = this._x + scale * dx;
originY = this._y + scale * dy;
workspace.actor.set_position(originX, originY);
workspace.positionWindows(Workspace.WindowPositionFlags.ANIMATE);
workspace.setSelected(false);
workspace.hideWindowsOverlays();
Tweener.addTween(workspace.actor,
{ x: workspace.gridX,
y: workspace.gridY,
scale_x: workspace.scale,
scale_y: workspace.scale,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
workspace.zoomToOverview(false);
if (workspace.metaWorkspace.index() == active)
workspace.setSelected(true);
}});
}
},
updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces, lostWorkspaces) {
let oldScale = this._workspaces[0].scale;
let oldGridWidth = Math.ceil(Math.sqrt(oldNumWorkspaces));
@ -529,6 +584,46 @@ SingleView.prototype = {
this._rightShadow.gridY = this._y + (this._height - this._rightShadow.height * scale) / 2;
},
_transitionWorkspaces: function() {
// update workspace parameters
this._positionWorkspaces();
let active = global.screen.get_active_workspace_index();
let activeActor = this._workspaces[active].actor;
// scale is the factor needed to translate from the currently
// active scale (previous view) to the new scale (this view)
let scale = this._workspaces[active].scale / activeActor.scale_x;
for (let w = 0; w < this._workspaces.length; w++) {
let workspace = this._workspaces[w];
let targetX, targetY;
// The correct transition would be a straightforward animation
// of each workspace's old position/scale to the new one;
// however, this looks overly busy, so we only use a zoom effect.
// Therefore we scale up each workspace's distance to the active
// workspace, so the latter fills the viewport while the other
// workspaces maintain their relative position
targetX = this._x + scale * (workspace.actor.x - activeActor.x);
targetY = this._y + scale * (workspace.actor.y - activeActor.y);
workspace.positionWindows(Workspace.WindowPositionFlags.ANIMATE);
workspace.setSelected(false);
workspace._hideAllOverlays();
Tweener.addTween(workspace.actor,
{ x: targetX,
y: targetY,
scale_x: workspace.scale,
scale_y: workspace.scale,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: function() {
workspace.zoomToOverview(false);
}});
}
},
_scrollToActive: function(showAnimation) {
let active = global.screen.get_active_workspace_index();
@ -1228,11 +1323,9 @@ function WorkspacesControls() {
WorkspacesControls.prototype = {
_init: function() {
this.actor = new St.BoxLayout({ style_class: 'workspaces-bar' });
this._gconf = Shell.GConf.get_default();
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
this._toggleViewButton = null;
this._addButton = null;
this._removeButton = null;
this._gconf = Shell.GConf.get_default();
let view = this._gconf.get_string(WORKSPACES_VIEW_KEY).toUpperCase();
if (view in WorkspacesViewType)
@ -1240,30 +1333,7 @@ WorkspacesControls.prototype = {
else
this._currentViewType = WorkspacesViewType.SINGLE;
this._nWorkspacesNotifyId =
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
},
_updateToggleButtonStyle: function() {
if (this._currentViewType == WorkspacesViewType.SINGLE)
this._toggleViewButton.set_style_class_name('workspace-controls switch-mosaic');
else
this._toggleViewButton.set_style_class_name('workspace-controls switch-single');
},
_setView: function(view) {
if (this._currentViewType == view)
return;
this._currentViewType = view;
this._updateToggleButtonStyle();
this._gconf.set_string(WORKSPACES_VIEW_KEY, view);
},
updateControls: function(currentView) {
this.actor.remove_all();
this._currentView = null;
// View switcher button
this._toggleViewButton = new St.Button();
@ -1279,40 +1349,79 @@ WorkspacesControls.prototype = {
this.actor.add(this._toggleViewButton, { y_fill: false, y_align: St.Align.START });
// View specific controls
let viewControls = currentView.createControllerBar();
if (!viewControls)
viewControls = new St.Bin();
this.actor.add(viewControls, { expand: true,
x_fill: true,
y_fill: true,
y_align: St.Align.MIDDLE,
x_align: St.Align.START });
this._viewControls = new St.Bin({ x_fill: true, y_fill: true });
this.actor.add(this._viewControls, { expand: true, x_fill: true });
// Add/remove workspace buttons
this._removeButton = new St.Button({ style_class: 'workspace-controls remove' });
this._removeButton.connect('clicked', Lang.bind(this, function() {
currentView.removeWorkspace();
this._currentView.removeWorkspace();
}));
this.actor.add(this._removeButton, { y_fill: false,
y_align: St.Align.START });
this._addButton = new St.Button({ style_class: 'workspace-controls add' });
this._addButton.connect('clicked', Lang.bind(this, function() {
currentView.addWorkspace()
this._currentView.addWorkspace()
}));
this._addButton._delegate = this._addButton;
this._addButton._delegate.acceptDrop = Lang.bind(this,
function(source, actor, x, y, time) {
let view = currentView;
return view._acceptNewWorkspaceDrop(source, actor, x, y, time);
return false;
return this._currentView._acceptNewWorkspaceDrop(source, actor, x, y, time);
});
this.actor.add(this._addButton, { y_fill: false,
y_align: St.Align.START });
this._nWorkspacesNotifyId =
global.screen.connect('notify::n-workspaces',
Lang.bind(this, this._workspacesChanged));
this._workspacesChanged();
},
updateControls: function(view) {
this._currentView = view;
let newControls = this._currentView.createControllerBar();
if (newControls) {
this._viewControls.child = newControls;
this._viewControls.child.opacity = 0;
Tweener.addTween(this._viewControls.child,
{ opacity: 255,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad' });
} else {
if (this._viewControls.child)
Tweener.addTween(this._viewControls.child,
{ opacity: 0,
time: Overview.ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this._viewControls.child.destroy();
})});
}
},
_updateToggleButtonStyle: function() {
if (this._currentViewType == WorkspacesViewType.SINGLE)
this._toggleViewButton.set_style_class_name('workspace-controls switch-mosaic');
else
this._toggleViewButton.set_style_class_name('workspace-controls switch-single');
},
_setView: function(view) {
if (this._currentViewType == view)
return;
if (WorkspacesViewType.SINGLE == view)
this._toggleViewButton.set_style_class_name('workspace-controls switch-mosaic');
else
this._toggleViewButton.set_style_class_name('workspace-controls switch-single');
this._currentViewType = view;
this._gconf.set_string(WORKSPACES_VIEW_KEY, view);
},
setCanRemove: function(canRemove) {
this._setButtonSensitivity(this._removeButton, canRemove);
},