[gnome-shell/wip/overviewTransitions: 4/4] workspace: Fade in instead of zoom to return desktop



commit 96a1fdc9e1a4a43fab214708bb36da1e25fd51a3
Author: Carlos Soriano <carlos soriano89 gmail com>
Date:   Mon Jul 14 19:06:08 2014 +0200

    workspace: Fade in instead of zoom to return desktop
    
    The zooming animation of the windows looks nice when animating
    from the workspace display page, but looks weird from others pages
    like apps page or search page since the windows comes from nowhere
    with a initial position not known for the user.
    
    Instead of that just fade the desktop with the windows in its
    original position.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=732901

 js/ui/overview.js       |    2 +-
 js/ui/viewSelector.js   |   34 ++++++--
 js/ui/workspace.js      |  211 +++++++++++++++++++++++++++++++++++++++--------
 js/ui/workspacesView.js |   77 ++++++++++++++----
 4 files changed, 265 insertions(+), 59 deletions(-)
---
diff --git a/js/ui/overview.js b/js/ui/overview.js
index fc9f055..50e42e6 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -623,7 +623,7 @@ const Overview = new Lang.Class({
         this.animationInProgress = true;
         this.visibleTarget = false;
 
-        this.viewSelector.zoomFromOverview();
+        this.viewSelector.animateFromOverview();
 
         // Make other elements fade out.
         Tweener.addTween(this._stack,
diff --git a/js/ui/viewSelector.js b/js/ui/viewSelector.js
index 76cc5c9..e71b8e9 100644
--- a/js/ui/viewSelector.js
+++ b/js/ui/viewSelector.js
@@ -296,29 +296,38 @@ const ViewSelector = new Lang.Class({
     },
 
     _toggleAppsPage: function() {
+        this.showingAppsToOverview = !this._showAppsButton.checked;
         Main.overview.show();
         this._showAppsButton.checked = !this._showAppsButton.checked;
     },
 
     showApps: function() {
+        this.showingAppsToOverview = true;
         Main.overview.show();
         this._showAppsButton.checked = true;
     },
 
     show: function() {
         this.reset();
-
-        this._workspacesDisplay.show();
+        this._workspacesDisplay.show(this.showingAppsToOverview);
         this._activePage = null;
-        this._showPage(this._workspacesPage);
+        if (this.showingAppsToOverview)
+            this._showPage(this._appsPage);
+        else
+            this._showPage(this._workspacesPage);
 
         if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
             Main.overview.fadeOutDesktop();
     },
 
-    zoomFromOverview: function() {
-        this._showAppsButton.checked = false;
-        this._workspacesDisplay.zoomFromOverview();
+    animateFromOverview: function() {
+        let showingAppsFromOverview = this._activePage != this._workspacesPage;
+        if (showingAppsFromOverview) {
+            // Let workspace.js manage the animation of the windows clones.
+            this._showAppsButton.checked = false;
+            this._workspacesPage.visible = true;
+        }
+        this._workspacesDisplay.animateFromOverview(showingAppsFromOverview);
 
         if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
             Main.overview.fadeInDesktop();
@@ -365,7 +374,18 @@ const ViewSelector = new Lang.Class({
         Tweener.addTween(this._activePage,
             { opacity: 255,
               time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
-              transition: 'easeOutQuad'
+              transition: 'easeOutQuad',
+              onComplete: Lang.bind(this,
+                function() {
+                    // If we were animating from the desktop view to the appsPage,
+                    // previously we showed the workspacePage to allow the windows to animate,
+                    // but now we no longer want to show it given that we are now
+                    // on the apps page or search page.
+                    if (this.showingAppsToOverview) {
+                        this.showingAppsToOverview = false;
+                        this._workspacesPage.visible = false;
+                    }
+                })
             });
     },
 
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
index ec012c0..760f3ae 100644
--- a/js/ui/workspace.js
+++ b/js/ui/workspace.js
@@ -17,6 +17,7 @@ const Main = imports.ui.main;
 const Overview = imports.ui.overview;
 const Tweener = imports.ui.tweener;
 const WindowManager = imports.ui.windowManager;
+const OverviewControls = imports.ui.overviewControls;
 
 const WINDOW_DND_SIZE = 256;
 
@@ -34,6 +35,8 @@ const DRAGGING_WINDOW_OPACITY = 100;
 const LAYOUT_SCALE_WEIGHT = 1;
 const LAYOUT_SPACE_WEIGHT = 0.1;
 
+const WINDOW_ANIMATION_MAX_NUMBER_BLENDING = 3;
+
 function _interpolate(start, end, step) {
     return start + (end - start) * step;
 }
@@ -1258,10 +1261,11 @@ const Workspace = new Lang.Class({
             return;
         }
 
-        // We will reposition windows when enter again overview anyway.
+        // We will reposition windows anyway when enter again overview or when ending the windows
+        // animations whith fade animation.
         // In this way we avoid unwanted animations of windows repositioning while
-        // animating overview
-        if (this.leavingOverview)
+        // animating overview.
+        if (this.leavingOverview || this.animatingWindowsFade)
             return;
 
         let initialPositioning = flags & WindowPositionFlags.INITIAL;
@@ -1562,14 +1566,149 @@ const Workspace = new Lang.Class({
         return false;
     },
 
-    // Animate the full-screen to Overview transition.
-    zoomToOverview : function() {
+    fadeToOverview: function() {
+        // We don't want to reposition windows while animating in this way.
+        this.animatingWindowsFade = true;
+
+        // We'll use the OverviewControls.SIDE_CONTROLS_ANIMATION_TIME as max
+        // time instead of Overview.ANIMATION_TIME because we are going to
+        // show the apps page, and that's the time it lasts to show it and
+        // hide the windows page, making windows clones opacity zero. In this way
+        // we avoid cutting the animation at the middle.
+        let windowBaseTime;
+        if (this._windows.length > WINDOW_ANIMATION_MAX_NUMBER_BLENDING) {
+            // Reserve one "slot" for the animation of the windows from
+            // WINDOW_ANIMATION_MAX_NUMBER_BLENDING to this._windows.length
+            windowBaseTime = OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / 
(WINDOW_ANIMATION_MAX_NUMBER_BLENDING + 1);
+        } else {
+            windowBaseTime = OverviewControls.SIDE_CONTROLS_ANIMATION_TIME / this._windows.length;
+        }
+        let onCompleteWindowsAnimation =  Lang.bind(this, function() {
+            this.animatingWindowsFade = false;
+            // Position and scale the windows in case the user use workspacePage
+            // after using appsPage
+            this._recalculateWindowPositions(WindowPositionFlags.INITIAL);
+            // Given that viewSelector uses the same time to hide the windows
+            // we can restore the opacity of windows clones here withouth flickering
+            for (let i = 0; i < this._windows.length; i++) {
+                let clone = this._windows[i];
+                clone.actor.opacity = 255;
+            }
+        });
+        // Animate gradually the most WINDOW_ANIMATION_MAX_NUMBER_BLENDING accesed windows hoping that
+        // those covers most of the screen so we can achieve a smooth transition.
+        for (let i = 0; i < Math.min(this._windows.length, WINDOW_ANIMATION_MAX_NUMBER_BLENDING); i++) {
+            let onComplete = null;
+            if (i == this._windows.lenght - 1)
+               onComplete = onCompleteWindowsAnimation;
+            this._fadeWindowToOverview(i, windowBaseTime * (i + 1), onComplete);
+        }
+
+        // Animate the rest of the windows from WINDOW_ANIMATION_MAX_NUMBER_BLENDING to this._windows.length
+        // at the same time.
+        for (let i = WINDOW_ANIMATION_MAX_NUMBER_BLENDING; i < this._windows.length; i++) {
+            let onComplete = null;
+            if (i == this._windows.length - 1)
+               onComplete = onCompleteWindowsAnimation;
+            this._fadeWindowToOverview(i, windowBaseTime * (WINDOW_ANIMATION_MAX_NUMBER_BLENDING + 1), 
onComplete);
+        }
+    },
+
+    _fadeWindowToOverview: function(index, time, onComplete) {
+        let clone = this._windows[index];
+        let overlay = this._windowOverlays[index];
+
+        if (overlay)
+            overlay.hide();
+
+        let [origX, origY] = clone.getOriginalPosition();
+        Tweener.addTween(clone.actor,
+                         { time: time,
+                           opacity: 0,
+                           transition: 'easeOutQuad',
+                           onComplete: Lang.bind(this,
+                                function() {
+                                    if (onComplete)
+                                        onComplete();
+                                })
+                        });
+    },
+
+    fadeFromOverview: function() {
+        this.leavingOverview = true;
+
+        for (let i = 0; i < this._windows.length; i++) {
+            let clone = this._windows[i];
+            Tweener.removeTweens(clone.actor);
+        }
+
+        if (this._repositionWindowsId > 0) {
+            Mainloop.source_remove(this._repositionWindowsId);
+            this._repositionWindowsId = 0;
+        }
+        this._overviewHiddenId = Main.overview.connect('hidden', Lang.bind(this,
+                                                                           this._doneLeavingOverview));
+
+        if (this.metaWorkspace != null && this.metaWorkspace != global.screen.get_active_workspace())
+            return;
+
+        let windowBaseTime;
+        if (this._windows.length > WINDOW_ANIMATION_MAX_NUMBER_BLENDING) {
+            // Reserve one "slot" for the animation of the windows from
+            // WINDOW_ANIMATION_MAX_NUMBER_BLENDING to this._windows.length
+            windowBaseTime = Overview.ANIMATION_TIME / (WINDOW_ANIMATION_MAX_NUMBER_BLENDING + 1);
+        } else {
+            windowBaseTime = Overview.ANIMATION_TIME / this._windows.length;
+        }
+        // Animate gradually the most WINDOW_ANIMATION_MAX_NUMBER_BLENDING accesed windows hoping that
+        // those covers most of the screen so we can achieve a smooth transition.
+        for (let i = 0; i < Math.min(this._windows.length, WINDOW_ANIMATION_MAX_NUMBER_BLENDING); i++)
+            this._fadeWindowFromOverview(i, windowBaseTime * (i + 1));
+
+        // Animate the rest of the windows from WINDOW_ANIMATION_MAX_NUMBER_BLENDING to this._windows.length
+        // at the same time.
+        for (let i = WINDOW_ANIMATION_MAX_NUMBER_BLENDING; i < this._windows.length; i++)
+            this._fadeWindowFromOverview(i, windowBaseTime * (WINDOW_ANIMATION_MAX_NUMBER_BLENDING + 1));
+    },
+
+    _fadeWindowFromOverview: function(index, time) {
+        // Reverse order of windows, so the most upper in the stack is animated first
+        let reverseStackWindow = this._windows.slice();
+        reverseStackWindow.reverse();
+        let reverseStackOverlays = this._windowOverlays.slice();
+        reverseStackOverlays.reverse();
+
+        let clone = reverseStackWindow[index];
+        let overlay = reverseStackOverlays[index];
+
+        if (overlay)
+            overlay.hide();
+
+        if (clone.metaWindow.showing_on_its_workspace()) {
+            let [origX, origY] = clone.getOriginalPosition();
+            clone.actor.scale_x = 1;
+            clone.actor.scale_y = 1;
+            clone.actor.x = origX;
+            clone.actor.y = origY;
+            clone.actor.opacity = 0;
+            Tweener.addTween(clone.actor,
+                             { time: time,
+                               opacity: 255,
+                               transition: 'easeOutQuad' });
+        } else {
+            // The window is hidden
+            clone.actor.scale_x = 0;
+            clone.actor.scale_y = 0;
+            clone.actor.opacity = 0;
+        }
+    },
+
+    zoomToOverview: function() {
         // Position and scale the windows.
         this._recalculateWindowPositions(WindowPositionFlags.ANIMATE | WindowPositionFlags.INITIAL);
     },
 
-    // Animates the return from Overview mode
-    zoomFromOverview : function() {
+    zoomFromOverview: function() {
         let currentWorkspace = global.screen.get_active_workspace();
 
         this.leavingOverview = true;
@@ -1590,35 +1729,37 @@ const Workspace = new Lang.Class({
             return;
 
         // Position and scale the windows.
-        for (let i = 0; i < this._windows.length; i++) {
-            let clone = this._windows[i];
-            let overlay = this._windowOverlays[i];
-
-            if (overlay)
-                overlay.hide();
+        for (let i = 0; i < this._windows.length; i++)
+           this._zoomWindowFromOverview(i);
+    },
 
-            if (clone.metaWindow.showing_on_its_workspace()) {
-                let [origX, origY] = clone.getOriginalPosition();
-
-                Tweener.addTween(clone.actor,
-                                 { x: origX,
-                                   y: origY,
-                                   scale_x: 1.0,
-                                   scale_y: 1.0,
-                                   time: Overview.ANIMATION_TIME,
-                                   opacity: 255,
-                                   transition: 'easeOutQuad'
-                                 });
-            } else {
-                // The window is hidden, make it shrink and fade it out
-                Tweener.addTween(clone.actor,
-                                 { scale_x: 0,
-                                   scale_y: 0,
-                                   opacity: 0,
-                                   time: Overview.ANIMATION_TIME,
-                                   transition: 'easeOutQuad'
-                                 });
-            }
+    _zoomWindowFromOverview: function(index) {
+        let clone = this._windows[index];
+        let overlay = this._windowOverlays[index];
+
+        if (overlay)
+            overlay.hide();
+
+        if (clone.metaWindow.showing_on_its_workspace()) {
+            let [origX, origY] = clone.getOriginalPosition();
+            Tweener.addTween(clone.actor,
+                             { x: origX,
+                               y: origY,
+                               scale_x: 1.0,
+                               scale_y: 1.0,
+                               time: Overview.ANIMATION_TIME,
+                               opacity: 255,
+                               transition: 'easeOutQuad'
+                             });
+        } else {
+            // The window is hidden, make it shrink and fade it out
+            Tweener.addTween(clone.actor,
+                             { scale_x: 0,
+                               scale_y: 0,
+                               opacity: 0,
+                               time: Overview.ANIMATION_TIME,
+                               transition: 'easeOutQuad'
+                             });
         }
     },
 
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index 61df6ab..eed0f6b 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -21,6 +21,11 @@ const WORKSPACE_SWITCH_TIME = 0.25;
 // Note that mutter has a compile-time limit of 36
 const MAX_WORKSPACES = 16;
 
+const AnimationType = {
+    ZOOM: 0,
+    FADE: 1
+};
+
 const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides';
 
 const WorkspacesViewBase = new Lang.Class({
@@ -142,17 +147,25 @@ const WorkspacesView = new Lang.Class({
         return this._workspaces[active];
     },
 
-    zoomToOverview: function() {
-        for (let w = 0; w < this._workspaces.length; w++)
-            this._workspaces[w].zoomToOverview();
+    animateToOverview: function(animationType) {
+        for (let w = 0; w < this._workspaces.length; w++) {
+            if (animationType == AnimationType.ZOOM)
+                this._workspaces[w].zoomToOverview();
+            else
+                this._workspaces[w].fadeToOverview();
+        }
         this._updateWorkspaceActors(false);
     },
 
-    zoomFromOverview: function() {
+    animateFromOverview: function(animationType) {
         this.actor.remove_clip();
 
-        for (let w = 0; w < this._workspaces.length; w++)
-            this._workspaces[w].zoomFromOverview();
+        for (let w = 0; w < this._workspaces.length; w++) {
+            if (animationType == AnimationType.ZOOM)
+                this._workspaces[w].zoomFromOverview();
+            else
+                this._workspaces[w].fadeFromOverview();
+        }
     },
 
     syncStacking: function(stackIndices) {
@@ -365,12 +378,18 @@ const ExtraWorkspaceView = new Lang.Class({
         this._workspace.setActualGeometry(this._actualGeometry);
     },
 
-    zoomToOverview: function() {
-        this._workspace.zoomToOverview();
+    animateToOverview: function(animationType) {
+        if (animationType == AnimationType.ZOOM)
+            this._workspace.zoomToOverview();
+        else
+            this._workspace.fadeToOverview();
     },
 
-    zoomFromOverview: function() {
-        this._workspace.zoomFromOverview();
+    animateFromOverview: function(animationType) {
+        if (animationType == AnimationType.ZOOM)
+            this._workspace.zoomFromOverview();
+        else
+            this._workspace.fadeFromOverview();
     },
 
     syncStacking: function(stackIndices) {
@@ -446,6 +465,8 @@ const WorkspacesDisplay = new Lang.Class({
         this._switchWorkspaceNotifyId = 0;
 
         this._notifyOpacityId = 0;
+        this._notifyVisibilityId = 0;
+
         this._scrollEventId = 0;
 
         this._fullGeometry = null;
@@ -462,10 +483,16 @@ const WorkspacesDisplay = new Lang.Class({
         return this._getPrimaryView().actor.navigate_focus(from, direction, false);
     },
 
-    show: function() {
+    show: function(showingApps) {
         this._updateWorkspacesViews();
-        for (let i = 0; i < this._workspacesViews.length; i++)
-            this._workspacesViews[i].zoomToOverview();
+        for (let i = 0; i < this._workspacesViews.length; i++) {
+            let animationType;
+            if (showingApps && i == this._primaryIndex)
+                animationType = AnimationType.FADE;
+            else
+                animationType = AnimationType.ZOOM;
+            this._workspacesViews[i].animateToOverview(animationType);
+        }
 
         this._restackedNotifyId =
             Main.overview.connect('windows-restacked',
@@ -474,9 +501,15 @@ const WorkspacesDisplay = new Lang.Class({
             this._scrollEventId = Main.overview.connect('scroll-event', Lang.bind(this, 
this._onScrollEvent));
     },
 
-    zoomFromOverview: function() {
-        for (let i = 0; i < this._workspacesViews.length; i++)
-            this._workspacesViews[i].zoomFromOverview();
+    animateFromOverview: function(showingApps) {
+        for (let i = 0; i < this._workspacesViews.length; i++) {
+            let animationType;
+            if (showingApps && i == this._primaryIndex)
+                animationType = AnimationType.FADE;
+            else
+                animationType = AnimationType.ZOOM;
+            this._workspacesViews[i].animateFromOverview(animationType);
+        }
     },
 
     hide: function() {
@@ -561,6 +594,10 @@ const WorkspacesDisplay = new Lang.Class({
             oldParent.disconnect(this._notifyOpacityId);
         this._notifyOpacityId = 0;
 
+        if (oldParent && this._notifyVisibilityId)
+            oldParent.disconnect(this._notifyVisibilityId);
+        this._notifyVisibilityId = 0;
+
         Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this,
             function() {
                 let newParent = this.actor.get_parent();
@@ -579,6 +616,14 @@ const WorkspacesDisplay = new Lang.Class({
                         primaryView.actor.opacity = opacity;
                         primaryView.actor.visible = opacity != 0;
                     }));
+
+                this._notifyVisibilityId = newParent.connect('notify::visible',
+                    Lang.bind(this, function() {
+                        let primaryView = this._getPrimaryView();
+                        if (!primaryView)
+                            return;
+                        primaryView.actor.visible = this.actor.get_parent().visibile;
+                    }));
         }));
     },
 


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