dash: Animate item and size changes

In general, all changes in the shell interface should be backed
by animations to give the interface a more natural feel and provide
feedback of what's happening. Currently the dash violates that
principle, as items simply appear/disappear or change size abruptly,
so add animations for application list and icon size changes.

https://bugzilla.gnome.org/show_bug.cgi?id=636156
This commit is contained in:
Florian Müllner 2011-01-21 12:06:13 +01:00
parent 29e97a5f88
commit d6020f1402

View File

@ -14,8 +14,11 @@ const AppFavorites = imports.ui.appFavorites;
const DND = imports.ui.dnd; const DND = imports.ui.dnd;
const IconGrid = imports.ui.iconGrid; const IconGrid = imports.ui.iconGrid;
const Main = imports.ui.main; const Main = imports.ui.main;
const Tweener = imports.ui.tweener;
const Workspace = imports.ui.workspace; const Workspace = imports.ui.workspace;
const DASH_ANIMATION_TIME = 0.2;
// A container like StBin, but taking the child's scale into account // A container like StBin, but taking the child's scale into account
// when requesting a size // when requesting a size
function DashItemContainer() { function DashItemContainer() {
@ -34,6 +37,8 @@ DashItemContainer.prototype = {
this.actor._delegate = this; this.actor._delegate = this;
this.child = null; this.child = null;
this._childScale = 1;
this._childOpacity = 255;
}, },
_allocate: function(actor, box, flags) { _allocate: function(actor, box, flags) {
@ -90,6 +95,67 @@ DashItemContainer.prototype = {
this.child = actor; this.child = actor;
this.actor.add_actor(this.child); this.actor.add_actor(this.child);
},
animateIn: function() {
if (this.child == null)
return;
this.childScale = 0;
this.childOpacity = 0;
Tweener.addTween(this,
{ childScale: 1.0,
childOpacity: 255,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad'
});
},
animateOutAndDestroy: function() {
if (this.child == null) {
this.actor.destroy();
return;
}
this.childScale = 1.0;
Tweener.addTween(this,
{ childScale: 0.0,
childOpacity: 0,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad',
onComplete: Lang.bind(this, function() {
this.actor.destroy();
})
});
},
set childScale(scale) {
this._childScale = scale;
if (this.child == null)
return;
this.child.set_scale_with_gravity(scale, scale,
Clutter.Gravity.CENTER);
this.actor.queue_relayout();
},
get childScale() {
return this._childScale;
},
set childOpacity(opacity) {
this._childOpacity = opacity;
if (this.child == null)
return;
this.child.set_opacity(opacity);
this.actor.queue_redraw();
},
get childOpacity() {
return this._childOpacity;
} }
}; };
@ -113,6 +179,12 @@ RemoveFavoriteIcon.prototype = {
this._iconBin._delegate = this; this._iconBin._delegate = this;
this.setChild(this._iconBin); this.setChild(this._iconBin);
this.hiding = false;
},
animateOutAndDestroy: function() {
DashItemContainer.prototype.animateOutAndDestroy.call(this);
this.hiding = true;
}, },
_createIcon: function(size) { _createIcon: function(size) {
@ -181,6 +253,7 @@ Dash.prototype = {
this._dragPlaceholder = null; this._dragPlaceholder = null;
this._dragPlaceholderPos = -1; this._dragPlaceholderPos = -1;
this._animatingPlaceholdersCount = 0;
this._favRemoveTarget = null; this._favRemoveTarget = null;
this._box = new St.BoxLayout({ name: 'dash', this._box = new St.BoxLayout({ name: 'dash',
@ -234,9 +307,15 @@ Dash.prototype = {
_onDragEnd: function() { _onDragEnd: function() {
this._clearDragPlaceholder(); this._clearDragPlaceholder();
if (this._favRemoveTarget) { if (this._favRemoveTarget) {
this._favRemoveTarget.actor.destroy(); this._favRemoveTarget.actor.hide();
this._adjustIconSize(); this._adjustIconSize();
this._favRemoveTarget.actor.show();
this._favRemoveTarget.animateOutAndDestroy();
this._favRemoveTarget.actor.connect('destroy', Lang.bind(this,
function() {
this._favRemoveTarget = null; this._favRemoveTarget = null;
}));
} }
DND.removeMonitor(this._dragMonitor); DND.removeMonitor(this._dragMonitor);
}, },
@ -261,6 +340,7 @@ Dash.prototype = {
this._favRemoveTarget.icon.setIconSize(this._iconSize); this._favRemoveTarget.icon.setIconSize(this._iconSize);
this._box.add(this._favRemoveTarget.actor); this._box.add(this._favRemoveTarget.actor);
this._adjustIconSize(); this._adjustIconSize();
this._favRemoveTarget.animateIn();
} }
let favRemoveHovered = false; let favRemoveHovered = false;
@ -343,11 +423,29 @@ Dash.prototype = {
if (newIconSize == this._iconSize) if (newIconSize == this._iconSize)
return; return;
let oldIconSize = this._iconSize;
this._iconSize = newIconSize; this._iconSize = newIconSize;
let scale = oldIconSize / newIconSize;
for (let i = 0; i < iconChildren.length; i++) { for (let i = 0; i < iconChildren.length; i++) {
let icon = iconChildren[i]._delegate.child._delegate.icon; let icon = iconChildren[i]._delegate.child._delegate.icon;
// Set the new size immediately, to keep the icons' sizes
// in sync with this._iconSize
icon.setIconSize(this._iconSize); icon.setIconSize(this._iconSize);
let [targetWidth, targetHeight] = icon.icon.get_size();
// Scale the icon's texture to the previous size and
// tween to the new size
icon.icon.set_size(icon.icon.width * scale,
icon.icon.height * scale);
Tweener.addTween(icon.icon,
{ width: targetWidth,
height: targetHeight,
time: DASH_ANIMATION_TIME,
transition: 'easeOutQuad'
});
} }
}, },
@ -452,15 +550,33 @@ Dash.prototype = {
this._box.insert_actor(addedItems[i].item.actor, this._box.insert_actor(addedItems[i].item.actor,
addedItems[i].pos); addedItems[i].pos);
// Hide removed actors to not take them into account
// when adjusting the icon size ...
for (let i = 0; i < removedActors.length; i++) for (let i = 0; i < removedActors.length; i++)
removedActors[i].destroy(); removedActors[i].hide();
// ... and do the same for the remove target if necessary
if (this._favRemoveTarget && this._favRemoveTarget.hiding)
this._favRemoveTarget.actor.hide();
this._adjustIconSize(); this._adjustIconSize();
if (this._favRemoveTarget && this._favRemoveTarget.hiding)
this._favRemoveTarget.actor.show();
for (let i = 0; i < removedActors.length; i++) {
removedActors[i].show();
let item = removedActors[i]._delegate;
item.animateOutAndDestroy();
}
for (let i = 0; i < addedItems.length; i++)
addedItems[i].item.animateIn();
}, },
_clearDragPlaceholder: function() { _clearDragPlaceholder: function() {
if (this._dragPlaceholder) { if (this._dragPlaceholder) {
this._dragPlaceholder.actor.destroy(); this._dragPlaceholder.animateOutAndDestroy();
this._dragPlaceholder = null; this._dragPlaceholder = null;
this._dragPlaceholderPos = -1; this._dragPlaceholderPos = -1;
} }
@ -482,7 +598,8 @@ Dash.prototype = {
let favPos = favorites.indexOf(app); let favPos = favorites.indexOf(app);
let numChildren = this._box.get_children().length; let children = this._box.get_children();
let numChildren = children.length;
let boxHeight = this._box.height; let boxHeight = this._box.height;
// Keep the placeholder out of the index calculation; assuming that // Keep the placeholder out of the index calculation; assuming that
@ -496,17 +613,49 @@ Dash.prototype = {
let pos = Math.round(y * numChildren / boxHeight); let pos = Math.round(y * numChildren / boxHeight);
if (pos != this._dragPlaceholderPos && pos <= numFavorites) { if (pos != this._dragPlaceholderPos && pos <= numFavorites) {
if (this._animatingPlaceholdersCount > 0) {
let appChildren = children.filter(function(actor) {
return actor._delegate &&
actor._delegate.child &&
actor._delegate.child._delegate &&
actor._delegate.child._delegate.app;
});
this._dragPlaceholderPos = children.indexOf(appChildren[pos]);
} else {
this._dragPlaceholderPos = pos; this._dragPlaceholderPos = pos;
if (this._dragPlaceholder) }
this._dragPlaceholder.actor.destroy();
// Don't allow positioning before or after self // Don't allow positioning before or after self
if (favPos != -1 && (pos == favPos || pos == favPos + 1)) if (favPos != -1 && (pos == favPos || pos == favPos + 1)) {
if (this._dragPlaceholder) {
this._dragPlaceholder.animateOutAndDestroy();
this._animatingPlaceholdersCount++;
this._dragPlaceholder.actor.connect('destroy',
Lang.bind(this, function() {
this._animatingPlaceholdersCount--;
}));
}
this._dragPlaceholder = null;
return DND.DragMotionResult.CONTINUE; return DND.DragMotionResult.CONTINUE;
}
// If the placeholder already exists, we just move
// it, but if we are adding it, expand its size in
// an animation
let fadeIn;
if (this._dragPlaceholder) {
this._dragPlaceholder.actor.destroy();
fadeIn = false;
} else {
fadeIn = true;
}
this._dragPlaceholder = new DragPlaceholderItem(); this._dragPlaceholder = new DragPlaceholderItem();
this._box.insert_actor(this._dragPlaceholder.actor, this._box.insert_actor(this._dragPlaceholder.actor,
this._dragPlaceholderPos); this._dragPlaceholderPos);
if (fadeIn)
this._dragPlaceholder.animateIn();
} }
let srcIsFavorite = (favPos != -1); let srcIsFavorite = (favPos != -1);
@ -540,6 +689,10 @@ Dash.prototype = {
let favPos = 0; let favPos = 0;
let children = this._box.get_children(); let children = this._box.get_children();
for (let i = 0; i < this._dragPlaceholderPos; i++) { for (let i = 0; i < this._dragPlaceholderPos; i++) {
if (this._dragPlaceholder &&
children[i] == this._dragPlaceholder.actor)
continue;
let childId = children[i]._delegate.child._delegate.app.get_id(); let childId = children[i]._delegate.child._delegate.app.get_id();
if (childId == id) if (childId == id)
continue; continue;