[gnome-shell/wip/exalm/gestures: 17/21] workspaceAnimation: Extract WorkspaceAnimation



commit 712caa00c07692689cd4aac5ae45b2ba63e0677f
Author: Alexander Mikhaylenko <alexm gnome org>
Date:   Thu Jul 4 20:58:05 2019 +0500

    workspaceAnimation: Extract WorkspaceAnimation
    
    Simplify the logic a bit. Introduce WorkspaceAnimation class that reparents
    the windows from current, surrounding and destination workspaces and manages
    them. Expose 'progress' property and have WorkspaceAnimationController animate
    it instead of animating everything separately.
    
    https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/605

 js/ui/workspaceAnimation.js | 476 ++++++++++++++++++++++++--------------------
 1 file changed, 255 insertions(+), 221 deletions(-)
---
diff --git a/js/ui/workspaceAnimation.js b/js/ui/workspaceAnimation.js
index 6ae7038f65..ded7e09663 100644
--- a/js/ui/workspaceAnimation.js
+++ b/js/ui/workspaceAnimation.js
@@ -1,131 +1,42 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 /* exported WorkspaceAnimationController */
 
-const { Clutter, Meta, Shell } = imports.gi;
+const { Clutter, GObject, Meta, Shell } = imports.gi;
 
 const Main = imports.ui.main;
 const SwipeTracker = imports.ui.swipeTracker;
 
 var WINDOW_ANIMATION_TIME = 250;
 
-var WorkspaceAnimationController = class {
-    constructor() {
-        this._shellwm = global.window_manager;
-        this._movingWindow = null;
-
-        this._switchData = null;
-        this._shellwm.connect('kill-switch-workspace', shellwm => {
-            if (this._switchData) {
-                if (this._switchData.inProgress)
-                    this._switchWorkspaceDone(shellwm);
-                else if (!this._switchData.gestureActivated)
-                    this._finishWorkspaceSwitch(this._switchData);
-            }
-        });
-
-        global.display.connect('restacked', this._syncStacking.bind(this));
-
-        Main.overview.connect('showing', () => {
-            if (this._switchData) {
-                if (this._switchData.gestureActivated)
-                    this._switchWorkspaceStop();
-                this._swipeTracker.enabled = false;
-            }
-        });
-        Main.overview.connect('hiding', () => {
-            this._swipeTracker.enabled = true;
-        });
-
-        let allowedModes = Shell.ActionMode.NORMAL;
-        let swipeTracker = new SwipeTracker.SwipeTracker(global.stage, allowedModes, false, false);
-        swipeTracker.connect('begin', this._switchWorkspaceBegin.bind(this));
-        swipeTracker.connect('update', this._switchWorkspaceUpdate.bind(this));
-        swipeTracker.connect('end', this._switchWorkspaceEnd.bind(this));
-        this._swipeTracker = swipeTracker;
-    }
-
-    _syncStacking() {
-        if (this._switchData == null)
-            return;
-
-        let windows = global.get_window_actors();
-        let lastCurSibling = null;
-        let lastDirSibling = [];
-        for (let i = 0; i < windows.length; i++) {
-            if (windows[i].get_parent() == this._switchData.curGroup) {
-                this._switchData.curGroup.set_child_above_sibling(windows[i], lastCurSibling);
-                lastCurSibling = windows[i];
-            } else {
-                for (let dir of Object.values(Meta.MotionDirection)) {
-                    let info = this._switchData.surroundings[dir];
-                    if (!info || windows[i].get_parent() != info.actor)
-                        continue;
-
-                    let sibling = lastDirSibling[dir];
-                    if (sibling == undefined)
-                        sibling = null;
-
-                    info.actor.set_child_above_sibling(windows[i], sibling);
-                    lastDirSibling[dir] = windows[i];
-                    break;
-                }
-            }
-        }
-    }
-
-    _getPositionForDirection(direction, fromWs, toWs) {
-        let xDest = 0, yDest = 0;
-
-        let oldWsIsFullscreen = fromWs.list_windows().some(w => w.is_fullscreen());
-        let newWsIsFullscreen = toWs.list_windows().some(w => w.is_fullscreen());
-
-        // We have to shift windows up or down by the height of the panel to prevent having a
-        // visible gap between the windows while switching workspaces. Since fullscreen windows
-        // hide the panel, they don't need to be shifted up or down.
-        let shiftHeight = Main.panel.height;
-
-        if (direction == Meta.MotionDirection.UP ||
-            direction == Meta.MotionDirection.UP_LEFT ||
-            direction == Meta.MotionDirection.UP_RIGHT)
-            yDest = -global.screen_height + (oldWsIsFullscreen ? 0 : shiftHeight);
-        else if (direction == Meta.MotionDirection.DOWN ||
-            direction == Meta.MotionDirection.DOWN_LEFT ||
-            direction == Meta.MotionDirection.DOWN_RIGHT)
-            yDest = global.screen_height - (newWsIsFullscreen ? 0 : shiftHeight);
-
-        if (direction == Meta.MotionDirection.LEFT ||
-            direction == Meta.MotionDirection.UP_LEFT ||
-            direction == Meta.MotionDirection.DOWN_LEFT)
-            xDest = -global.screen_width;
-        else if (direction == Meta.MotionDirection.RIGHT ||
-                 direction == Meta.MotionDirection.UP_RIGHT ||
-                 direction == Meta.MotionDirection.DOWN_RIGHT)
-            xDest = global.screen_width;
-
-        return [xDest, yDest];
-    }
-
-    _prepareWorkspaceSwitch(from, to, direction) {
-        if (this._switchData)
-            return;
+var WorkspaceAnimation = GObject.registerClass({
+    Properties: {
+        'progress': GObject.ParamSpec.double(
+            'progress', 'progress', 'progress',
+            GObject.ParamFlags.READWRITE,
+            -1, 1, 0),
+    },
+}, class WorkspaceAnimation extends Clutter.Actor {
+    _init(controller, from, to, direction) {
+        super._init();
+
+        this.connect('destroy', this._onDestroy.bind(this));
+
+        this._controller = controller;
+        this._curGroup = new Clutter.Actor();
+        this._movingWindowBin = new Clutter.Actor();
+        this._windows = [];
+        this._surroundings = {};
+        this._progress = 0;
 
         let wgroup = global.window_group;
         let windows = global.get_window_actors();
-        let switchData = {};
-
-        this._switchData = switchData;
-        switchData.curGroup = new Clutter.Actor();
-        switchData.movingWindowBin = new Clutter.Actor();
-        switchData.windows = [];
-        switchData.surroundings = {};
-        switchData.gestureActivated = false;
-        switchData.inProgress = false;
 
-        switchData.container = new Clutter.Actor();
-        switchData.container.add_actor(switchData.curGroup);
+        this._container = new Clutter.Actor();
+        this._container.add_actor(this._curGroup);
 
-        wgroup.add_actor(switchData.movingWindowBin);
-        wgroup.add_actor(switchData.container);
+        this.add_actor(this._container);
+        wgroup.add_actor(this);
+        wgroup.add_actor(this._movingWindowBin);
 
         let workspaceManager = global.workspace_manager;
         let curWs = workspaceManager.get_workspace_by_index(from);
@@ -139,7 +50,7 @@ var WorkspaceAnimationController = class {
                 ws = workspaceManager.get_workspace_by_index(to);
 
             if (ws == null || ws == curWs) {
-                switchData.surroundings[dir] = null;
+                this._surroundings[dir] = null;
                 continue;
             }
 
@@ -148,14 +59,14 @@ var WorkspaceAnimationController = class {
                          actor: new Clutter.Actor(),
                          xDest: x,
                          yDest: y };
-            switchData.surroundings[dir] = info;
-            switchData.container.add_actor(info.actor);
-            switchData.container.set_child_above_sibling(info.actor, null);
+            this._surroundings[dir] = info;
+            this._container.add_actor(info.actor);
+            this._container.set_child_above_sibling(info.actor, null);
 
             info.actor.set_position(x, y);
         }
 
-        wgroup.set_child_above_sibling(switchData.movingWindowBin, null);
+        wgroup.set_child_above_sibling(this._movingWindowBin, null);
 
         for (let i = 0; i < windows.length; i++) {
             let actor = windows[i];
@@ -170,25 +81,25 @@ var WorkspaceAnimationController = class {
             let record = { window: actor,
                            parent: actor.get_parent() };
 
-            if (this.movingWindow && window == this.movingWindow) {
+            if (this._controller.movingWindow && window == this._controller.movingWindow) {
                 record.parent.remove_child(actor);
-                switchData.movingWindow = record;
-                switchData.windows.push(switchData.movingWindow);
-                switchData.movingWindowBin.add_child(actor);
+                this._movingWindow = record;
+                this._windows.push(this._movingWindow);
+                this._movingWindowBin.add_child(actor);
             } else if (window.get_workspace().index() == from) {
                 record.parent.remove_child(actor)
-                switchData.windows.push(record);
-                switchData.curGroup.add_child(actor);
+                this._windows.push(record);
+                this._curGroup.add_child(actor);
             } else {
                 let visible = false;
                 for (let dir of Object.values(Meta.MotionDirection)) {
-                    let info = switchData.surroundings[dir];
+                    let info = this._surroundings[dir];
 
                     if (!info || info.index != window.get_workspace().index())
                         continue;
 
                     record.parent.remove_child(actor);
-                    switchData.windows.push(record);
+                    this._windows.push(record);
                     info.actor.add_child(actor);
                     visible = true;
                     break;
@@ -198,20 +109,22 @@ var WorkspaceAnimationController = class {
             }
         }
 
-        for (let i = 0; i < switchData.windows.length; i++) {
-            let w = switchData.windows[i];
+        for (let i = 0; i < this._windows.length; i++) {
+            let w = this._windows[i];
 
             w.windowDestroyId = w.window.connect('destroy', () => {
-                switchData.windows.splice(switchData.windows.indexOf(w), 1);
+                this._windows.splice(this._windows.indexOf(w), 1);
             });
         }
+
+        global.display.connect('restacked', this._syncStacking.bind(this));
     }
 
-    _finishWorkspaceSwitch(switchData) {
-        this._switchData = null;
+    _onDestroy() {
+        this.remove_all_transitions();
 
-        for (let i = 0; i < switchData.windows.length; i++) {
-            let w = switchData.windows[i];
+        for (let i = 0; i < this._windows.length; i++) {
+            let w = this._windows[i];
 
             w.window.disconnect(w.windowDestroyId);
             w.window.get_parent().remove_child(w.window);
@@ -221,44 +134,70 @@ var WorkspaceAnimationController = class {
                 global.workspace_manager.get_active_workspace())
                 w.window.hide();
         }
-        switchData.container.destroy();
-        switchData.movingWindowBin.destroy();
 
-        this.movingWindow = null;
+        this._container.destroy();
+        this._movingWindowBin.destroy();
     }
 
-    animateSwitchWorkspace(shellwm, from, to, direction) {
-        this._prepareWorkspaceSwitch(from, to, direction);
-        this._switchData.inProgress = true;
+    _getPositionForDirection(direction, fromWs, toWs) {
+        let xDest = 0, yDest = 0;
 
-        let workspaceManager = global.workspace_manager;
-        let fromWs = workspaceManager.get_workspace_by_index(from);
-        let toWs = workspaceManager.get_workspace_by_index(to);
+        let oldWsIsFullscreen = fromWs.list_windows().some(w => w.is_fullscreen());
+        let newWsIsFullscreen = toWs.list_windows().some(w => w.is_fullscreen());
 
-        let [xDest, yDest] = this._getPositionForDirection(direction, fromWs, toWs);
+        // We have to shift windows up or down by the height of the panel to prevent having a
+        // visible gap between the windows while switching workspaces. Since fullscreen windows
+        // hide the panel, they don't need to be shifted up or down.
+        let shiftHeight = Main.panel.height;
 
-        /* @direction is the direction that the "camera" moves, so the
-         * screen contents have to move one screen's worth in the
-         * opposite direction.
-         */
-        xDest = -xDest;
-        yDest = -yDest;
+        if (direction == Meta.MotionDirection.UP ||
+            direction == Meta.MotionDirection.UP_LEFT ||
+            direction == Meta.MotionDirection.UP_RIGHT)
+            yDest = -global.screen_height + (oldWsIsFullscreen ? 0 : shiftHeight);
+        else if (direction == Meta.MotionDirection.DOWN ||
+            direction == Meta.MotionDirection.DOWN_LEFT ||
+            direction == Meta.MotionDirection.DOWN_RIGHT)
+            yDest = global.screen_height - (newWsIsFullscreen ? 0 : shiftHeight);
 
-        this._switchData.container.ease({
-            x: xDest,
-            y: yDest,
-            duration: WINDOW_ANIMATION_TIME,
-            mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
-            onComplete: () => this._switchWorkspaceDone(shellwm),
-        });
+        if (direction == Meta.MotionDirection.LEFT ||
+            direction == Meta.MotionDirection.UP_LEFT ||
+            direction == Meta.MotionDirection.DOWN_LEFT)
+            xDest = -global.screen_width;
+        else if (direction == Meta.MotionDirection.RIGHT ||
+                 direction == Meta.MotionDirection.UP_RIGHT ||
+                 direction == Meta.MotionDirection.DOWN_RIGHT)
+            xDest = global.screen_width;
+
+        return [xDest, yDest];
     }
 
-    _switchWorkspaceDone(shellwm) {
-        this._finishWorkspaceSwitch(this._switchData);
-        shellwm.completed_switch_workspace();
+    _syncStacking() {
+        let windows = global.get_window_actors();
+        let lastCurSibling = null;
+        let lastDirSibling = [];
+        for (let i = 0; i < windows.length; i++) {
+            if (windows[i].get_parent() == this._curGroup) {
+                this._curGroup.set_child_above_sibling(windows[i], lastCurSibling);
+                lastCurSibling = windows[i];
+            } else {
+                for (let dir of Object.values(Meta.MotionDirection)) {
+                    let info = this._surroundings[dir];
+                    if (!info || windows[i].get_parent() != info.actor)
+                        continue;
+
+                    let sibling = lastDirSibling[dir];
+                    if (sibling == undefined)
+                        sibling = null;
+
+                    info.actor.set_child_above_sibling(windows[i], sibling);
+                    lastDirSibling[dir] = windows[i];
+                    break;
+                }
+            }
+        }
     }
 
-    _directionForProgress(progress) {
+    directionForProgress(progress) {
         if (global.workspace_manager.layout_rows == -1)
             return progress > 0 ? Meta.MotionDirection.DOWN : Meta.MotionDirection.UP;
         else if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
@@ -267,36 +206,143 @@ var WorkspaceAnimationController = class {
             return progress > 0 ? Meta.MotionDirection.RIGHT : Meta.MotionDirection.LEFT;
     }
 
-    _getProgressRange() {
-        if (!this._switchData)
-            return [0, 0];
+    progressForDirection(dir) {
+        if (global.workspace_manager.layout_rows == -1)
+            return dir == Meta.MotionDirection.DOWN ? 1 : -1;
+        else if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
+            return dir == Meta.MotionDirection.LEFT ? 1 : -1;
+        else
+            return dir == Meta.MotionDirection.RIGHT ? 1 : -1;
+    }
+
+    get progress() {
+        return this._progress;
+    }
 
-        let lower = 0;
-        let upper = 0;
+    set progress(progress) {
+        this._progress = progress;
 
-        let horiz = global.workspace_manager.layout_rows != -1;
+        let direction = this.directionForProgress(progress);
+        let xPos = 0;
+        let yPos = 0;
+
+        if (global.workspace_manager.layout_rows == -1)
+            yPos = -Math.round(progress * this._getDistance(direction));
+        else if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
+            xPos = Math.round(progress * this._getDistance(direction));
+        else
+            xPos = -Math.round(progress * this._getDistance(direction));
+
+        this._container.set_position(xPos, yPos);
+    }
+
+    _getDistance(direction) {
+        let info = this._surroundings[direction];
+        if (!info)
+            return 0;
+
+        switch (direction) {
+        case Meta.MotionDirection.UP:
+            return -info.yDest;
+        case Meta.MotionDirection.DOWN:
+            return info.yDest;
+        case Meta.MotionDirection.LEFT:
+            return -info.xDest;
+        case Meta.MotionDirection.RIGHT:
+            return info.xDest;
+        }
+
+        return 0;
+    }
+
+    getProgressRange() {
         let baseDistance;
-        if (horiz)
+        if (global.workspace_manager.layout_rows != -1)
             baseDistance = global.screen_width;
         else
             baseDistance = global.screen_height;
 
-        let direction = this._directionForProgress(-1);
-        let info = this._switchData.surroundings[direction];
-        if (info != null) {
-            let distance = horiz ? info.xDest : info.yDest;
-            lower = -Math.abs(distance) / baseDistance;
-        }
+        let direction = this.directionForProgress(-1);
+        let distance = this._getDistance(direction);
+        let lower = -distance / baseDistance;
 
-        direction = this._directionForProgress(1);
-        info = this._switchData.surroundings[direction];
-        if (info != null) {
-            let distance = horiz ? info.xDest : info.yDest;
-            upper = Math.abs(distance) / baseDistance;
-        }
+        direction = this.directionForProgress(1);
+        distance = this._getDistance(direction);
+        let upper = distance / baseDistance;
 
         return [lower, upper];
     }
+});
+
+var WorkspaceAnimationController = class {
+    constructor() {
+        this._shellwm = global.window_manager;
+        this._blockAnimations = false;
+        this._movingWindow = null;
+        this._inProgress = false;
+        this._gestureActivated = false;
+        this._animation = null;
+
+        this._shellwm.connect('kill-switch-workspace', shellwm => {
+            if (this._animation) {
+                if (this._inProgress)
+                    this._switchWorkspaceDone(shellwm);
+                else if (!this._gestureActivated)
+                    this._finishWorkspaceSwitch();
+            }
+        });
+
+        Main.overview.connect('showing', () => {
+            if (this._gestureActivated)
+                this._switchWorkspaceStop();
+
+            this._swipeTracker.enabled = false;
+        });
+        Main.overview.connect('hiding', () => {
+            this._swipeTracker.enabled = true;
+        });
+
+        let allowedModes = Shell.ActionMode.NORMAL;
+        let swipeTracker = new SwipeTracker.SwipeTracker(global.stage, allowedModes, false, false);
+        swipeTracker.connect('begin', this._switchWorkspaceBegin.bind(this));
+        swipeTracker.connect('update', this._switchWorkspaceUpdate.bind(this));
+        swipeTracker.connect('end', this._switchWorkspaceEnd.bind(this));
+        this._swipeTracker = swipeTracker;
+    }
+
+    _prepareWorkspaceSwitch(from, to, direction) {
+        if (this._animation)
+            return;
+
+        this._animation = new WorkspaceAnimation(this, from, to, direction);
+    }
+
+    _finishWorkspaceSwitch() {
+        if (this._animation)
+            this._animation.destroy();
+        this._animation = null;
+        this._inProgress = false;
+        this._gestureActivated = false;
+        this.movingWindow = null;
+    }
+
+    animateSwitchWorkspace(shellwm, from, to, direction) {
+        this._prepareWorkspaceSwitch(from, to, direction);
+        this._inProgress = true;
+
+        let progress = this._animation.progressForDirection(direction);
+
+        this._animation.ease_property('progress', progress, {
+            duration: WINDOW_ANIMATION_TIME,
+            mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
+            onComplete: () => this._switchWorkspaceDone(shellwm),
+        });
+    }
+
+    _switchWorkspaceDone(shellwm) {
+        this._finishWorkspaceSwitch();
+        shellwm.completed_switch_workspace();
+    }
 
     _switchWorkspaceBegin(tracker, monitor) {
         if (Meta.prefs_get_workspaces_only_on_primary() && monitor != Main.layoutManager.primaryIndex)
@@ -315,22 +361,21 @@ var WorkspaceAnimationController = class {
             baseDistance = global.screen_height;
 
         let progress;
-        if (this._switchData && this._switchData.gestureActivated) {
-            this._switchData.container.remove_all_transitions();
-            if (!horiz)
-                progress = -this._switchData.container.y / baseDistance;
-            else if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL)
-                progress = this._switchData.container.x / baseDistance;
-            else
-                progress = -this._switchData.container.x / baseDistance;
+        if (this._gestureActivated) {
+            this._animation.remove_all_transitions();
+            progress = this._animation.progress;
         } else {
             this._prepareWorkspaceSwitch(activeWorkspace.index(), -1);
             progress = 0;
         }
 
-        let points = [];
-        let [lower, upper] = this._getProgressRange();
+        let [lower, upper] = this._animation.getProgressRange();
+        if (progress < 0)
+            progress *= -lower;
+        else if (progress > 0)
+            progress *= upper;
 
+        let points = [];
         if (lower != 0)
             points.push(lower);
 
@@ -343,37 +388,30 @@ var WorkspaceAnimationController = class {
     }
 
     _switchWorkspaceUpdate(_tracker, progress) {
-        if (!this._switchData)
-            return;
-
-        let direction = this._directionForProgress(progress);
-        let info = this._switchData.surroundings[direction];
-        let xPos = 0;
-        let yPos = 0;
-        if (info) {
-            if (global.workspace_manager.layout_rows == -1)
-                yPos = Math.round(Math.abs(progress) * -info.yDest);
-            else
-                xPos = Math.round(Math.abs(progress) * -info.xDest);
-        }
-
-        this._switchData.container.set_position(xPos, yPos);
+        // Translate the progress into [-1;1] range
+        let [lower, upper] = this._animation.getProgressRange();
+        if (progress < 0)
+            progress /= -lower;
+        else if (progress > 0)
+            progress /= upper;
+
+        if (this._animation)
+            this._animation.progress = progress;
     }
 
     _switchWorkspaceEnd(_tracker, duration, endProgress) {
-        if (!this._switchData)
+        if (!this._animation)
             return;
 
+        // Translate the progress into [-1;1] range
+        endProgress = Math.sign(endProgress);
+
         let workspaceManager = global.workspace_manager;
         let activeWorkspace = workspaceManager.get_active_workspace();
         let newWs = activeWorkspace;
-        let xDest = 0;
-        let yDest = 0;
         if (endProgress != 0) {
-            let direction = this._directionForProgress(endProgress);
+            let direction = this._animation.directionForProgress(endProgress);
             newWs = activeWorkspace.get_neighbor(direction);
-            xDest = -this._switchData.surroundings[direction].xDest;
-            yDest = -this._switchData.surroundings[direction].yDest;
         }
 
         if (duration == 0) {
@@ -383,30 +421,26 @@ var WorkspaceAnimationController = class {
             return;
         }
 
-        let switchData = this._switchData;
-        switchData.gestureActivated = true;
+        this._gestureActivated = true;
 
-        this._switchData.container.ease({
-            x: xDest,
-            y: yDest,
+        this._animation.ease_property('progress', endProgress, {
             duration,
             mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
             onComplete: () => {
                 if (newWs != activeWorkspace)
                     newWs.activate(global.get_current_time());
-                this._finishWorkspaceSwitch(switchData);
+                this._finishWorkspaceSwitch();
             },
         });
     }
 
     _switchWorkspaceStop() {
-        this._switchData.container.x = 0;
-        this._switchData.container.y = 0;
-        this._finishWorkspaceSwitch(this._switchData);
+        this._animation.progress = 0;
+        this._finishWorkspaceSwitch();
     }
 
     isAnimating() {
-        return this._switchData != null;
+        return this._animation != null;
     }
 
     set movingWindow(movingWindow) {


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