[gnome-shell/wip/swarm: 7/9] Search: Animate new window of apps in AllView and FrequentView
- From: Carlos Soriano <csoriano src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/swarm: 7/9] Search: Animate new window of apps in AllView and FrequentView
- Date: Wed, 13 Aug 2014 15:52:53 +0000 (UTC)
commit 2b9f90256bb357d9378b5a6abd4b92f2be5c1229
Author: Carlos Soriano <carlos soriano89 gmail com>
Date: Thu Jun 26 21:36:15 2014 +0200
Search: Animate new window of apps in AllView and FrequentView
Following design mockups, animate the icons on AllView and FrequentView
to zoom out when opening a new window of the app or when the app is not
running and the user execute it.
js/ui/appDisplay.js | 53 +++++++++++++++++++-------------
js/ui/iconGrid.js | 83 ++++++++++++++++++++++++++++++++++++++++++++++-----
2 files changed, 107 insertions(+), 29 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index f6fdfb4..22693b2 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -483,7 +483,7 @@ const AllView = new Lang.Class({
apps.forEach(Lang.bind(this, function(appId) {
let app = appSys.lookup_app(appId);
- let icon = new AppIcon(app);
+ let icon = new AppIcon(app, { animateOnNewWindow: true });
this.addItem(icon);
}));
@@ -783,7 +783,7 @@ const FrequentView = new Lang.Class({
for (let i = 0; i < mostUsed.length; i++) {
if (!mostUsed[i].get_app_info().should_show())
continue;
- let appIcon = new AppIcon(mostUsed[i]);
+ let appIcon = new AppIcon(mostUsed[i], { animateOnNewWindow: true });
this._grid.addItem(appIcon, -1);
}
},
@@ -1530,7 +1530,7 @@ Signals.addSignalMethods(AppFolderPopup.prototype);
const AppIcon = new Lang.Class({
Name: 'AppIcon',
- _init : function(app, iconParams) {
+ _init : function(app, params) {
this.app = app;
this.id = app.get_id();
this.name = app.get_name();
@@ -1543,12 +1543,15 @@ const AppIcon = new Lang.Class({
y_fill: true });
this.actor._delegate = this;
- if (!iconParams)
- iconParams = {};
+ if (!params)
+ params = {};
+
+ this._params = Params.parse(params, { animateOnNewWindow: false,
+ createIcon: Lang.bind(this, this._createIcon),
+ setSizeManually: true },
+ true);
- iconParams['createIcon'] = Lang.bind(this, this._createIcon);
- iconParams['setSizeManually'] = true;
- this.icon = new IconGrid.BaseIcon(app.get_name(), iconParams);
+ this.icon = new IconGrid.BaseIcon(app.get_name(), this._params);
this.actor.set_child(this.icon.actor);
this.actor.label_actor = this.icon.label;
@@ -1647,13 +1650,7 @@ const AppIcon = new Lang.Class({
_onClicked: function(actor, button) {
this._removeMenuTimeout();
-
- if (button == 0 || button == 1) {
- this._onActivate(Clutter.get_current_event());
- } else if (button == 2) {
- this.app.open_new_window(-1);
- Main.overview.hide();
- }
+ this.activate(button);
},
_onKeyboardPopupMenu: function() {
@@ -1707,19 +1704,29 @@ const AppIcon = new Lang.Class({
this.emit('menu-state-changed', false);
},
- _onActivate: function (event) {
- let modifiers = event.get_state();
+ activate: function (button) {
+ let event = Clutter.get_current_event();
+ let modifiers = event ? event.get_state() : 0;
+ let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK &&
+ this.app.state == Shell.AppState.RUNNING ||
+ button && button == 2;
+
+ if (this._params.animateOnNewWindow &&
+ (this.app.state == Shell.AppState.STOPPED || openNewWindow))
+ this.animateOut();
- if (modifiers & Clutter.ModifierType.CONTROL_MASK
- && this.app.state == Shell.AppState.RUNNING) {
+ if (openNewWindow)
this.app.open_new_window(-1);
- } else {
+ else
this.app.activate();
- }
Main.overview.hide();
},
+ animateOut: function() {
+ this.icon.animateOut();
+ },
+
shellWorkspaceLaunch : function(params) {
params = Params.parse(params, { workspace: -1,
timestamp: 0 });
@@ -1808,6 +1815,10 @@ const AppIconMenu = new Lang.Class({
if (this._source.app.can_open_new_window()) {
this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
this._newWindowMenuItem.connect('activate', Lang.bind(this, function() {
+ if (this._source.params.animateOnNewWindow &&
+ this._source.app.state == Shell.AppState.STOPPED)
+ this._source.animateOut();
+
this._source.app.open_new_window(-1);
this.emit('activate-window', null);
}));
diff --git a/js/ui/iconGrid.js b/js/ui/iconGrid.js
index 841415b..2ad5a7e 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -35,13 +35,17 @@ const AnimationDirection = {
OUT: 1
};
+const APPICON_ANIMATION_OUT_SCALE = 3;
+const APPICON_ANIMATION_OUT_TIME = 0.25;
+
const BaseIcon = new Lang.Class({
Name: 'BaseIcon',
_init : function(label, params) {
params = Params.parse(params, { createIcon: null,
setSizeManually: false,
- showLabel: true });
+ showLabel: true },
+ true);
let styleClass = 'overview-icon';
if (params.showLabel)
@@ -58,23 +62,23 @@ const BaseIcon = new Lang.Class({
this._spacing = 0;
- let box = new Shell.GenericContainer();
- box.connect('allocate', Lang.bind(this, this._allocate));
- box.connect('get-preferred-width',
+ this._box = new Shell.GenericContainer();
+ this._box.connect('allocate', Lang.bind(this, this._allocate));
+ this._box.connect('get-preferred-width',
Lang.bind(this, this._getPreferredWidth));
- box.connect('get-preferred-height',
+ this._box.connect('get-preferred-height',
Lang.bind(this, this._getPreferredHeight));
- this.actor.set_child(box);
+ this.actor.set_child(this._box);
this.iconSize = ICON_SIZE;
this._iconBin = new St.Bin({ x_align: St.Align.MIDDLE,
y_align: St.Align.MIDDLE });
- box.add_actor(this._iconBin);
+ this._box.add_actor(this._iconBin);
if (params.showLabel) {
this.label = new St.Label({ text: label });
- box.add_actor(this.label);
+ this._box.add_actor(this.label);
} else {
this.label = null;
}
@@ -192,9 +196,72 @@ const BaseIcon = new Lang.Class({
_onIconThemeChanged: function() {
this._createIconTexture(this.iconSize);
+ },
+
+ animateOut: function() {
+ // Animate only the container box instead of the entire actor,
+ // so the styles like hover and running are not applied while
+ // animating.
+ actorZoomOut(this._box);
}
});
+function actorZoomOut(actor) {
+ let actorClone = new Clutter.Clone({ source: actor,
+ reactive: false });
+ let [width, height] = actor.get_transformed_size();
+ let [x, y] = actor.get_transformed_position();
+ actorClone.set_size(width, height);
+ actorClone.set_position(x, y);
+ actorClone.opacity = 255;
+ actorClone.set_pivot_point(0.5, 0.5);
+
+ Main.uiGroup.add_actor(actorClone);
+
+ // Avoid monitor edges to not zoom outside the current monitor
+ let monitor = Main.layoutManager.findMonitorForActor(actor);
+ let finalWidth = width * APPICON_ANIMATION_OUT_SCALE;
+ let finalHeight = height * APPICON_ANIMATION_OUT_SCALE;
+ // Assume pivot point at 0.5, 0.5
+ let finalX = x - (finalWidth - width) / 2;
+ let finalY = y - (finalHeight - height) / 2;
+ let [vecX, vecY] = vectorToBeContainedInActor(finalX, finalY, finalWidth, finalHeight, monitor);
+
+ Tweener.addTween(actorClone,
+ { time: APPICON_ANIMATION_OUT_TIME,
+ scale_x: APPICON_ANIMATION_OUT_SCALE,
+ scale_y: APPICON_ANIMATION_OUT_SCALE,
+ translation_x: vecX,
+ translation_y: vecY,
+ opacity: 0,
+ transition: 'easeOutQuad',
+ onComplete: function() {
+ actorClone.destroy();
+ }
+ });
+}
+
+function vectorToBeContainedInActor(x, y, width, height, actor) {
+ // Avoid actors bigger than the screen, since they cannot be contained
+ if (width > actor.width || height > actor.height)
+ return undefined;
+
+ let vecX = 0;
+ let vecY = 0;
+
+ if (x < actor.x)
+ vecX = actor.x - x;
+ else if (x + width > actor.x + actor.width)
+ vecX = actor.x + actor.width - (x + width);
+
+ if (y < actor.y)
+ vecY = actor.y - y;
+ else if (y + height > actor.y + actor.height)
+ vecY = actor.y + actor.height - (y + height);
+
+ return [vecX, vecY];
+}
+
const IconGrid = new Lang.Class({
Name: 'IconGrid',
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]