[gnome-shell/wip/swarm: 1/3] appDisplay: Animate appIcon for new window of apps



commit 1b89694f07d66d2ccde1f59d1e56fffad83e5f44
Author: Carlos Soriano <carlos soriano89 gmail com>
Date:   Tue Jun 17 21:31:53 2014 +0200

    appDisplay: Animate appIcon for new window of apps
    
    Following design mockups, animate the icons on AllView, FrequentView,
    Dash and Search to zoom out when opening a new window of the app or when
    the app is not running and the user execute it.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=734726

 js/ui/appDisplay.js |   32 +++++++++++++++++++-------------
 js/ui/iconGrid.js   |   49 +++++++++++++++++++++++++++++++++++++++++++++++++
 js/ui/search.js     |    9 +++++++++
 3 files changed, 77 insertions(+), 13 deletions(-)
---
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
index 356c2e0..d9efcbf 100644
--- a/js/ui/appDisplay.js
+++ b/js/ui/appDisplay.js
@@ -1651,13 +1651,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() {
@@ -1711,19 +1705,28 @@ 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.app.state == Shell.AppState.STOPPED || openNewWindow)
+            this.animateLaunch();
 
-        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();
     },
 
+    animateLaunch: function() {
+        this.icon.animateOut();
+    },
+
     shellWorkspaceLaunch : function(params) {
         params = Params.parse(params, { workspace: -1,
                                         timestamp: 0 });
@@ -1805,6 +1808,9 @@ 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.app.state == Shell.AppState.STOPPED)
+                        this._source.animateLaunch();
+
                     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 bf17ae5..c1d8e16 100644
--- a/js/ui/iconGrid.js
+++ b/js/ui/iconGrid.js
@@ -30,6 +30,9 @@ const AnimationDirection = {
     OUT: 1
 };
 
+const APPICON_ANIMATION_OUT_SCALE = 3;
+const APPICON_ANIMATION_OUT_TIME = 0.25;
+
 const BaseIcon = new Lang.Class({
     Name: 'BaseIcon',
 
@@ -187,9 +190,55 @@ const BaseIcon = new Lang.Class({
 
     _onIconThemeChanged: function() {
         this._createIconTexture(this.iconSize);
+    },
+
+    animateOut: function() {
+        // Animate only the child instead of the entire actor, so the
+        // styles like hover and running are not applied while
+        // animating.
+        zoomOutActor(this.actor.child);
     }
 });
 
+function clamp(value, min, max) {
+    return Math.max(Math.min(value, max), min);
+};
+
+function zoomOutActor(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 scaledWidth = width * APPICON_ANIMATION_OUT_SCALE;
+    let scaledHeight = height * APPICON_ANIMATION_OUT_SCALE;
+    let scaledX = x - (scaledWidth - width) / 2;
+    let scaledY = y - (scaledHeight - height) / 2;
+    let containedX = clamp(scaledX, monitor.x, monitor.x + monitor.width - scaledWidth);
+    let containedY = clamp(scaledY, monitor.y, monitor.y + monitor.height - scaledHeight);
+
+    Tweener.addTween(actorClone,
+                     { time: APPICON_ANIMATION_OUT_TIME,
+                       scale_x: APPICON_ANIMATION_OUT_SCALE,
+                       scale_y: APPICON_ANIMATION_OUT_SCALE,
+                       translation_x: containedX - scaledX,
+                       translation_y: containedY - scaledY,
+                       opacity: 0,
+                       transition: 'easeOutQuad',
+                       onComplete: function() {
+                           actorClone.destroy();
+                       }
+                    });
+}
+
 const IconGrid = new Lang.Class({
     Name: 'IconGrid',
 
diff --git a/js/ui/search.js b/js/ui/search.js
index 3046d01..f33b8ad 100644
--- a/js/ui/search.js
+++ b/js/ui/search.js
@@ -6,6 +6,7 @@ const Gio = imports.gi.Gio;
 const Gtk = imports.gi.Gtk;
 const Meta = imports.gi.Meta;
 const Signals = imports.signals;
+const Shell = imports.gi.Shell;
 const St = imports.gi.St;
 const Atk = imports.gi.Atk;
 
@@ -417,6 +418,7 @@ const ListSearchResults = new Lang.Class({
         this.providerIcon.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
         this.providerIcon.connect('clicked', Lang.bind(this,
             function() {
+                this.providerIcon.animateOut();
                 provider.launchSearch(this._terms);
                 Main.overview.toggle();
             }));
@@ -725,5 +727,12 @@ const ProviderIcon = new Lang.Class({
                                  gicon: provider.appInfo.get_icon() });
         this._content.add_actor(icon);
         this._content.add_actor(this.moreIcon);
+    },
+
+    animateOut: function() {
+        let appSys = Shell.AppSystem.get_default();
+        let app = appSys.lookup_app(this.provider.appInfo.get_id());
+        if (app.state == Shell.AppState.STOPPED)
+            IconGrid.zoomOutActor(this._content);
     }
 });


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]