[gnome-shell] workspace: Fade in instead of zoom to return desktop



commit 687e1ebf69bfe44d179ec4c426b8fd2791302b90
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 other pages
    like apps page or search page since the windows come from nowhere
    with an initial position not known to 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      |  207 +++++++++++++++++++++++++++++++++++++++--------
 js/ui/workspacesView.js |   63 +++++++++++----
 4 files changed, 247 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 bf2d733..80c3035 100644
--- a/js/ui/viewSelector.js
+++ b/js/ui/viewSelector.js
@@ -260,6 +260,16 @@ const ViewSelector = new Lang.Class({
                     this._stageKeyPressId = 0;
                 }
             }));
+        Main.overview.connect('shown', Lang.bind(this,
+            function() {
+                // If we were animating from the desktop view to the
+                // apps page the workspace page was visible, allowing
+                // 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._activePage != this._workspacesPage)
+                    this._workspacesPage.opacity = 0;
+            }));
 
         Main.wm.addKeybinding('toggle-application-view',
                               new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }),
@@ -295,29 +305,36 @@ const ViewSelector = new Lang.Class({
     },
 
     _toggleAppsPage: function() {
-        Main.overview.show();
         this._showAppsButton.checked = !this._showAppsButton.checked;
+        Main.overview.show();
     },
 
     showApps: function() {
-        Main.overview.show();
         this._showAppsButton.checked = true;
+        Main.overview.show();
     },
 
     show: function() {
         this.reset();
-
-        this._workspacesDisplay.show();
+        this._workspacesDisplay.show(this._showAppsButton.checked);
         this._activePage = null;
-        this._showPage(this._workspacesPage);
+        if (this._showAppsButton.checked)
+            this._showPage(this._appsPage);
+        else
+            this._showPage(this._workspacesPage);
 
         if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
             Main.overview.fadeOutDesktop();
     },
 
-    zoomFromOverview: function() {
+    animateFromOverview: function() {
+        // Make sure workspace page is fully visible to allow
+        // workspace.js do the animation of the windows
+        this._workspacesPage.opacity = 255;
+
+        this._workspacesDisplay.animateFromOverview(this._activePage != this._workspacesPage);
+
         this._showAppsButton.checked = false;
-        this._workspacesDisplay.zoomFromOverview();
 
         if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows())
             Main.overview.fadeInDesktop();
@@ -369,6 +386,9 @@ const ViewSelector = new Lang.Class({
     },
 
     _showPage: function(page) {
+        if (!Main.overview.visible)
+            return;
+
         if (page == this._activePage)
             return;
 
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
index 5fd055b..7e157ad 100644
--- a/js/ui/workspace.js
+++ b/js/ui/workspace.js
@@ -34,6 +34,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 +1260,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 +1565,141 @@ 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;
+        this._overviewShownId = Main.overview.connect('shown', Lang.bind(this,
+                                                                         this._doneShowingOverview));
+        if (this._windows.length == 0)
+            return;
+
+        if (this.metaWorkspace != null && this.metaWorkspace != global.screen.get_active_workspace())
+            return;
+
+        // Special case maximized windows, since it doesn't make sense
+        // to animate windows below in the stack
+        let topMaximizedWindow;
+        // It is ok to treat the case where there is no maximized
+        // window as if the bottom-most window was maximized given that
+        // it won't affect the result of the animation
+        for (topMaximizedWindow = this._windows.length - 1; topMaximizedWindow > 0; topMaximizedWindow--) {
+            let metaWindow = this._windows[topMaximizedWindow].metaWindow;
+            if (metaWindow.maximized_horizontally && metaWindow.maximized_vertically)
+                break;
+        }
+
+        let nTimeSlots = Math.min(WINDOW_ANIMATION_MAX_NUMBER_BLENDING + 1, this._windows.length - 
topMaximizedWindow);
+        let windowBaseTime = Overview.ANIMATION_TIME / nTimeSlots;
+
+        let topIndex = this._windows.length - 1;
+        for (let i = 0; i < this._windows.length; i++) {
+            if (i < topMaximizedWindow) {
+                // below top-most maximized window, don't animate
+                let overlay = this._windowOverlays[i];
+                if (overlay)
+                    overlay.hide();
+                this._windows[i].actor.opacity = 0;
+            } else {
+                let fromTop = topIndex - i;
+                let time;
+                if (fromTop < nTimeSlots) // animate top-most windows gradually
+                    time = windowBaseTime * (nTimeSlots - fromTop);
+                else
+                    time = windowBaseTime;
+
+                this._windows[i].actor.opacity = 255;
+                this._fadeWindow(i, time, 0);
+            }
+        }
+    },
+
+    fadeFromOverview: function() {
+        this.leavingOverview = true;
+        this._overviewHiddenId = Main.overview.connect('hidden', Lang.bind(this,
+                                                                           this._doneLeavingOverview));
+        if (this._windows.length == 0)
+            return;
+
+        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;
+        }
+
+        if (this.metaWorkspace != null && this.metaWorkspace != global.screen.get_active_workspace())
+            return;
+
+        // Special case maximized windows, since it doesn't make sense
+        // to animate windows below in the stack
+        let topMaximizedWindow;
+        // It is ok to treat the case where there is no maximized
+        // window as if the bottom-most window was maximized given that
+        // it won't affect the result of the animation
+        for (topMaximizedWindow = this._windows.length - 1; topMaximizedWindow > 0; topMaximizedWindow--) {
+            let metaWindow = this._windows[topMaximizedWindow].metaWindow;
+            if (metaWindow.maximized_horizontally && metaWindow.maximized_vertically)
+                break;
+        }
+
+        let nTimeSlots = Math.min(WINDOW_ANIMATION_MAX_NUMBER_BLENDING + 1, this._windows.length - 
topMaximizedWindow);
+        let windowBaseTime = Overview.ANIMATION_TIME / nTimeSlots;
+
+        let topIndex = this._windows.length - 1;
+        for (let i = 0; i < this._windows.length; i++) {
+            if (i < topMaximizedWindow) {
+                // below top-most maximized window, don't animate
+                let overlay = this._windowOverlays[i];
+                if (overlay)
+                    overlay.hide();
+                this._windows[i].actor.opacity = 0;
+            } else {
+                let fromTop = topIndex - i;
+                let time;
+                if (fromTop < nTimeSlots) // animate top-most windows gradually
+                    time = windowBaseTime * (fromTop + 1);
+                else
+                    time = windowBaseTime * nTimeSlots;
+
+                this._windows[i].actor.opacity = 0;
+                this._fadeWindow(i, time, 255);
+            }
+        }
+    },
+
+    _fadeWindow: function(index, time, opacity) {
+        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();
+            clone.actor.scale_x = 1;
+            clone.actor.scale_y = 1;
+            clone.actor.x = origX;
+            clone.actor.y = origY;
+            Tweener.addTween(clone.actor,
+                             { time: time,
+                               opacity: opacity,
+                               transition: 'easeOutQuad'
+                             });
+        } else {
+            // The window is hidden
+            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 +1720,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'
+                             });
         }
     },
 
@@ -1657,6 +1789,11 @@ const Workspace = new Lang.Class({
         this.leavingOverview = false;
     },
 
+    _doneShowingOverview: function() {
+        this._animatingWindowsFade = false;
+        this._recalculateWindowPositions(WindowPositionFlags.INITIAL);
+    },
+
     // Tests if @actor belongs to this workspaces and monitor
     _isMyWindow : function (actor) {
         let win = actor.meta_window;
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index 61df6ab..004ac81 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) {
@@ -462,10 +481,16 @@ const WorkspacesDisplay = new Lang.Class({
         return this._getPrimaryView().actor.navigate_focus(from, direction, false);
     },
 
-    show: function() {
+    show: function(fadeOnPrimary) {
         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 (fadeOnPrimary && i == this._primaryIndex)
+                animationType = AnimationType.FADE;
+            else
+                animationType = AnimationType.ZOOM;
+            this._workspacesViews[i].animateToOverview(animationType);
+        }
 
         this._restackedNotifyId =
             Main.overview.connect('windows-restacked',
@@ -474,9 +499,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(fadeOnPrimary) {
+        for (let i = 0; i < this._workspacesViews.length; i++) {
+            let animationType;
+            if (fadeOnPrimary && i == this._primaryIndex)
+                animationType = AnimationType.FADE;
+            else
+                animationType = AnimationType.ZOOM;
+            this._workspacesViews[i].animateFromOverview(animationType);
+        }
     },
 
     hide: function() {


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