[gnome-shell] Animate workspace view switches



commit 5a6c9f176ea470a8153796aad7a740dad59eba57
Author: Florian Müllner <fmuellner src gnome org>
Date:   Mon Feb 15 14:29:34 2010 +0100

    Animate workspace view switches
    
    Replace the hard switch between linear and mosaic workspace view
    with a an animated transition between the views.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=610191

 js/ui/overview.js       |    3 -
 js/ui/workspace.js      |    3 +
 js/ui/workspacesView.js |  193 ++++++++++++++++++++++++++++++++++++----------
 3 files changed, 154 insertions(+), 45 deletions(-)
---
diff --git a/js/ui/overview.js b/js/ui/overview.js
index 8bf7d93..3a5278a 100644
--- a/js/ui/overview.js
+++ b/js/ui/overview.js
@@ -263,9 +263,6 @@ Overview.prototype = {
         // Show new workspacesView
         this._group.add_actor(this._workspaces.actor);
         this._dash.actor.raise(this._workspaces.actor);
-
-        // Set new position and scale to workspaces.
-        this.emit('showing');
     },
 
     _recalculateGridSizes: function () {
diff --git a/js/ui/workspace.js b/js/ui/workspace.js
index 4c58a49..8b570a4 100644
--- a/js/ui/workspace.js
+++ b/js/ui/workspace.js
@@ -1212,6 +1212,9 @@ Workspace.prototype = {
         cloneWidth = this.scale * clone.actor.scale_x * cloneWidth;
         cloneHeight = this.scale * clone.actor.scale_y * cloneHeight;
 
+        if (!this._windowOverlaysGroup.visible)
+            this._windowOverlaysGroup.show();
+
         if (overlay) {
             overlay.updatePositions(cloneX, cloneY, cloneWidth, cloneHeight);
             overlay.fadeIn();
diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js
index dc7947c..bdf786e 100644
--- a/js/ui/workspacesView.js
+++ b/js/ui/workspacesView.js
@@ -55,7 +55,10 @@ GenericWorkspacesView.prototype = {
                 let node = this.actor.get_theme_node();
                 let [a, spacing] = node.get_length('spacing', false);
                 this._spacing = spacing;
-                this._positionWorkspaces();
+                if (Main.overview.animationInProgress)
+                    this._positionWorkspaces();
+                else
+                    this._transitionWorkspaces();
             }));
 
         this._width = width;
@@ -248,6 +251,10 @@ GenericWorkspacesView.prototype = {
         throw new Error("Not implemented");
     },
 
+    _transitionWorkspaces: function() {
+        throw new Error("Not implemented");
+    },
+
     _positionWorkspaces: function() {
         throw new Error("Not implemented");
     },
@@ -326,6 +333,54 @@ MosaicView.prototype = {
         }
     },
 
+    _transitionWorkspaces: function() {
+        // update workspace parameters
+        this._positionWorkspaces();
+
+        let active = global.screen.get_active_workspace_index();
+        let activeWorkspace = this._workspaces[active];
+        // scale is the factor needed to translate from the new scale
+        // (this view) to the currently active scale (previous view)
+        let scale = this._workspaces[0].actor.scale_x / activeWorkspace.scale;
+
+        for (let w = 0; w < this._workspaces.length; w++) {
+            let workspace = this._workspaces[w];
+            let originX, originY;
+            let dx, dy;
+
+            // The correct transition would be a straightforward animation
+            // of each workspace's old position/scale to the new one;
+            // however, this looks overly busy, so we only use a zoom effect.
+            // Unfortunately this implies that we cannot pretend to not knowing
+            // the other view's layout at this point:
+            // We position the workspaces in the grid, which we scale up so
+            // that the active workspace fills the viewport.
+            dx = workspace.gridX - activeWorkspace.gridX;
+            dy = workspace.gridY - activeWorkspace.gridY;
+            originX = this._x + scale * dx;
+            originY = this._y + scale * dy;
+
+            workspace.actor.set_position(originX, originY);
+
+            workspace.positionWindows(Workspace.WindowPositionFlags.ANIMATE);
+            workspace.setSelected(false);
+            workspace.hideWindowsOverlays();
+
+            Tweener.addTween(workspace.actor,
+                             { x: workspace.gridX,
+                               y: workspace.gridY,
+                               scale_x: workspace.scale,
+                               scale_y: workspace.scale,
+                               time: Overview.ANIMATION_TIME,
+                               transition: 'easeOutQuad',
+                               onComplete: function() {
+                                   workspace.zoomToOverview(false);
+                                   if (workspace.metaWorkspace.index() == active)
+                                       workspace.setSelected(true);
+                             }});
+        }
+    },
+
     updateWorkspaces: function(oldNumWorkspaces, newNumWorkspaces, lostWorkspaces) {
         let oldScale = this._workspaces[0].scale;
         let oldGridWidth = Math.ceil(Math.sqrt(oldNumWorkspaces));
@@ -529,6 +584,46 @@ SingleView.prototype = {
         this._rightShadow.gridY = this._y + (this._height - this._rightShadow.height * scale) / 2;
     },
 
+    _transitionWorkspaces: function() {
+        // update workspace parameters
+        this._positionWorkspaces();
+
+        let active = global.screen.get_active_workspace_index();
+        let activeActor = this._workspaces[active].actor;
+        // scale is the factor needed to translate from the currently
+        // active scale (previous view) to the new scale (this view)
+        let scale = this._workspaces[active].scale / activeActor.scale_x;
+
+        for (let w = 0; w < this._workspaces.length; w++) {
+            let workspace = this._workspaces[w];
+            let targetX, targetY;
+
+            // The correct transition would be a straightforward animation
+            // of each workspace's old position/scale to the new one;
+            // however, this looks overly busy, so we only use a zoom effect.
+            // Therefore we scale up each workspace's distance to the active
+            // workspace, so the latter fills the viewport while the other
+            // workspaces maintain their relative position
+            targetX = this._x + scale * (workspace.actor.x - activeActor.x);
+            targetY = this._y + scale * (workspace.actor.y - activeActor.y);
+
+            workspace.positionWindows(Workspace.WindowPositionFlags.ANIMATE);
+            workspace.setSelected(false);
+            workspace._hideAllOverlays();
+
+            Tweener.addTween(workspace.actor,
+                             { x: targetX,
+                               y: targetY,
+                               scale_x: workspace.scale,
+                               scale_y: workspace.scale,
+                               time: Overview.ANIMATION_TIME,
+                               transition: 'easeOutQuad',
+                               onComplete: function() {
+                                   workspace.zoomToOverview(false);
+                             }});
+        }
+    },
+
     _scrollToActive: function(showAnimation) {
         let active = global.screen.get_active_workspace_index();
 
@@ -1228,11 +1323,9 @@ function WorkspacesControls() {
 WorkspacesControls.prototype = {
     _init: function() {
         this.actor = new St.BoxLayout({ style_class: 'workspaces-bar' });
-        this._gconf = Shell.GConf.get_default();
+        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 
-        this._toggleViewButton = null;
-        this._addButton = null;
-        this._removeButton = null;
+        this._gconf = Shell.GConf.get_default();
 
         let view = this._gconf.get_string(WORKSPACES_VIEW_KEY).toUpperCase();
         if (view in WorkspacesViewType)
@@ -1240,30 +1333,7 @@ WorkspacesControls.prototype = {
         else
             this._currentViewType = WorkspacesViewType.SINGLE;
 
-        this._nWorkspacesNotifyId =
-            global.screen.connect('notify::n-workspaces',
-                                  Lang.bind(this, this._workspacesChanged));
-
-        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
-    },
-
-    _updateToggleButtonStyle: function() {
-       if (this._currentViewType == WorkspacesViewType.SINGLE)
-            this._toggleViewButton.set_style_class_name('workspace-controls switch-mosaic');
-        else
-            this._toggleViewButton.set_style_class_name('workspace-controls switch-single');
-    },
-
-    _setView: function(view) {
-        if (this._currentViewType == view)
-            return;
-        this._currentViewType = view;
-        this._updateToggleButtonStyle();
-        this._gconf.set_string(WORKSPACES_VIEW_KEY, view);
-    },
-
-    updateControls: function(currentView) {
-        this.actor.remove_all();
+        this._currentView = null;
 
         // View switcher button
         this._toggleViewButton = new St.Button();
@@ -1279,40 +1349,79 @@ WorkspacesControls.prototype = {
         this.actor.add(this._toggleViewButton, { y_fill: false, y_align: St.Align.START });
 
         // View specific controls
-        let viewControls = currentView.createControllerBar();
-        if (!viewControls)
-            viewControls = new St.Bin();
-        this.actor.add(viewControls, { expand: true,
-                                       x_fill: true,
-                                       y_fill: true,
-                                       y_align: St.Align.MIDDLE,
-                                       x_align: St.Align.START });
+        this._viewControls = new St.Bin({ x_fill: true, y_fill: true });
+        this.actor.add(this._viewControls, { expand: true, x_fill: true });
 
         // Add/remove workspace buttons
         this._removeButton = new St.Button({ style_class: 'workspace-controls remove' });
         this._removeButton.connect('clicked', Lang.bind(this, function() {
-            currentView.removeWorkspace();
+            this._currentView.removeWorkspace();
         }));
         this.actor.add(this._removeButton, { y_fill: false,
                                              y_align: St.Align.START });
 
         this._addButton = new St.Button({ style_class: 'workspace-controls add' });
         this._addButton.connect('clicked', Lang.bind(this, function() {
-            currentView.addWorkspace()
+            this._currentView.addWorkspace()
         }));
         this._addButton._delegate = this._addButton;
         this._addButton._delegate.acceptDrop = Lang.bind(this,
             function(source, actor, x, y, time) {
-                let view = currentView;
-                return view._acceptNewWorkspaceDrop(source, actor, x, y, time);
-return false;
+                return this._currentView._acceptNewWorkspaceDrop(source, actor, x, y, time);
             });
         this.actor.add(this._addButton, { y_fill: false,
                                           y_align: St.Align.START });
 
+        this._nWorkspacesNotifyId =
+            global.screen.connect('notify::n-workspaces',
+                                  Lang.bind(this, this._workspacesChanged));
+
         this._workspacesChanged();
     },
 
+    updateControls: function(view) {
+        this._currentView = view;
+
+        let newControls = this._currentView.createControllerBar();
+        if (newControls) {
+            this._viewControls.child = newControls;
+            this._viewControls.child.opacity = 0;
+            Tweener.addTween(this._viewControls.child,
+                             { opacity: 255,
+                               time: Overview.ANIMATION_TIME,
+                               transition: 'easeOutQuad' });
+        } else {
+            if (this._viewControls.child)
+                Tweener.addTween(this._viewControls.child,
+                                 { opacity: 0,
+                                   time: Overview.ANIMATION_TIME,
+                                   transition: 'easeOutQuad',
+                                   onComplete: Lang.bind(this, function() {
+                                       this._viewControls.child.destroy();
+                                 })});
+        }
+    },
+
+    _updateToggleButtonStyle: function() {
+       if (this._currentViewType == WorkspacesViewType.SINGLE)
+            this._toggleViewButton.set_style_class_name('workspace-controls switch-mosaic');
+        else
+            this._toggleViewButton.set_style_class_name('workspace-controls switch-single');
+    },
+
+    _setView: function(view) {
+        if (this._currentViewType == view)
+            return;
+
+        if (WorkspacesViewType.SINGLE == view)
+            this._toggleViewButton.set_style_class_name('workspace-controls switch-mosaic');
+        else
+            this._toggleViewButton.set_style_class_name('workspace-controls switch-single');
+
+        this._currentViewType = view;
+        this._gconf.set_string(WORKSPACES_VIEW_KEY, view);
+    },
+
     setCanRemove: function(canRemove) {
         this._setButtonSensitivity(this._removeButton, canRemove);
     },



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