[gnome-shell/wip/exalm/gestures: 8/12] workspaceAnimation: Extract WorkspaceAnimation
- From: Alexander Mikhaylenko <alexm src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/wip/exalm/gestures: 8/12] workspaceAnimation: Extract WorkspaceAnimation
- Date: Sat, 26 Oct 2019 13:05:55 +0000 (UTC)
commit 4d8ff79eb8ca123c06873c6f4071ca8a5d9b5d18
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 | 468 ++++++++++++++++++++++++--------------------
1 file changed, 251 insertions(+), 217 deletions(-)
---
diff --git a/js/ui/workspaceAnimation.js b/js/ui/workspaceAnimation.js
index b2aa52b670..24a34221e8 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;
- }
- }
- }
+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();
- _getPositionForDirection(direction, fromWs, toWs) {
- let xDest = 0, yDest = 0;
+ this.connect('destroy', this._onDestroy.bind(this));
- 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;
+ 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);
+ this._surroundings[dir] = info;
+ this._container.add_actor(info.actor);
info.actor.raise_top();
info.actor.set_position(x, y);
}
- switchData.movingWindowBin.raise_top();
+ this._movingWindowBin.raise_top();
for (let i = 0; i < windows.length; i++) {
let actor = windows[i];
@@ -170,22 +81,22 @@ var WorkspaceAnimationController = class {
let record = { window: actor,
parent: actor.get_parent() };
- if (this.movingWindow && window == this.movingWindow) {
- switchData.movingWindow = record;
- switchData.windows.push(switchData.movingWindow);
- actor.reparent(switchData.movingWindowBin);
+ if (this._controller.movingWindow && window == this._controller.movingWindow) {
+ this._movingWindow = record;
+ this._windows.push(this._movingWindow);
+ actor.reparent(this._movingWindowBin);
} else if (window.get_workspace().index() == from) {
- switchData.windows.push(record);
- actor.reparent(switchData.curGroup);
+ this._windows.push(record);
+ actor.reparent(this._curGroup);
} 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;
- switchData.windows.push(record);
+ this._windows.push(record);
actor.reparent(info.actor);
visible = true;
break;
@@ -195,20 +106,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.reparent(w.parent);
@@ -217,44 +130,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)
@@ -263,36 +202,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)
@@ -311,22 +357,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);
@@ -339,37 +384,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) {
@@ -379,30 +417,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: 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]