workspaceAnimation: Use a workspace strip
Currently, the workspace swipe transition only has one workspace in each direction. This works until you try to do multiple swipes in quick succession. The second swipe would continue the existing transition, which only has 2 or 3 workspaces in it, and will hit a wall. To prevent this, take all workspaces and arrange them into a column or row, depending on the layout, and use that as a transition. For the transition that happens when focusing a window on another workspace (for example, via Alt+Tab), still use only two workspaces instead of all of them. Since we don't support layouts other than single rows/columns anymore, diagonal transitions aren't supported anymore, and will be shown as horizontal or vertical instead. Since nw alt-tab and gesture transitions are different, don't allow to do both at once, that is, disable swipe tracker when a programmatic transition is going. This will also conveniently cancel a gesture transition if a programmatic one is initiated while a gesture is in progress. Fixes https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2612 https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326
This commit is contained in:
parent
856a4d27e0
commit
7bdcc503cb
@ -26,6 +26,10 @@ class WorkspaceGroup extends Clutter.Actor {
|
|||||||
this._syncStacking.bind(this));
|
this._syncStacking.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get workspace() {
|
||||||
|
return this._workspace;
|
||||||
|
}
|
||||||
|
|
||||||
_shouldShowWindow(window) {
|
_shouldShowWindow(window) {
|
||||||
if (!window.showing_on_its_workspace())
|
if (!window.showing_on_its_workspace())
|
||||||
return false;
|
return false;
|
||||||
@ -123,7 +127,7 @@ var WorkspaceAnimationController = class {
|
|||||||
Main.overview.connect('showing', () => {
|
Main.overview.connect('showing', () => {
|
||||||
if (this._switchData) {
|
if (this._switchData) {
|
||||||
if (this._switchData.gestureActivated)
|
if (this._switchData.gestureActivated)
|
||||||
this._switchWorkspaceStop();
|
this._finishWorkspaceSwitch(this._switchData);
|
||||||
this._swipeTracker.enabled = false;
|
this._swipeTracker.enabled = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -139,57 +143,25 @@ var WorkspaceAnimationController = class {
|
|||||||
this._swipeTracker = swipeTracker;
|
this._swipeTracker = swipeTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
_getPositionForDirection(direction, fromWs, toWs) {
|
_prepareWorkspaceSwitch(workspaceIndices) {
|
||||||
let xDest = 0, yDest = 0;
|
|
||||||
|
|
||||||
const oldWsIsFullscreen = fromWs.list_windows().some(w => w.is_fullscreen());
|
|
||||||
const 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.
|
|
||||||
const 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)
|
if (this._switchData)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const workspaceManager = global.workspace_manager;
|
const workspaceManager = global.workspace_manager;
|
||||||
const curWs = workspaceManager.get_workspace_by_index(from);
|
const vertical = workspaceManager.layout_rows === -1;
|
||||||
|
const nWorkspaces = workspaceManager.get_n_workspaces();
|
||||||
|
const activeWorkspaceIndex = workspaceManager.get_active_workspace_index();
|
||||||
|
|
||||||
const switchData = {};
|
const switchData = {};
|
||||||
|
|
||||||
this._switchData = switchData;
|
this._switchData = switchData;
|
||||||
switchData.curGroup = new WorkspaceGroup(curWs, this.movingWindow);
|
|
||||||
switchData.movingWindowBin = new Clutter.Actor();
|
switchData.movingWindowBin = new Clutter.Actor();
|
||||||
switchData.movingWindow = null;
|
switchData.movingWindow = null;
|
||||||
switchData.surroundings = {};
|
switchData.workspaceGroups = [];
|
||||||
switchData.gestureActivated = false;
|
switchData.gestureActivated = false;
|
||||||
switchData.inProgress = false;
|
switchData.inProgress = false;
|
||||||
|
|
||||||
switchData.container = new Clutter.Actor();
|
switchData.container = new Clutter.Actor();
|
||||||
switchData.container.add_child(switchData.curGroup);
|
|
||||||
switchData.backgroundGroup = new Clutter.Actor();
|
switchData.backgroundGroup = new Clutter.Actor();
|
||||||
|
|
||||||
for (const monitor of Main.layoutManager.monitors)
|
for (const monitor of Main.layoutManager.monitors)
|
||||||
@ -199,28 +171,44 @@ var WorkspaceAnimationController = class {
|
|||||||
Main.uiGroup.insert_child_above(switchData.container, switchData.backgroundGroup);
|
Main.uiGroup.insert_child_above(switchData.container, switchData.backgroundGroup);
|
||||||
Main.uiGroup.insert_child_above(switchData.movingWindowBin, switchData.container);
|
Main.uiGroup.insert_child_above(switchData.movingWindowBin, switchData.container);
|
||||||
|
|
||||||
for (const dir of Object.values(Meta.MotionDirection)) {
|
let x = 0;
|
||||||
let ws = null;
|
let y = 0;
|
||||||
|
|
||||||
if (to < 0)
|
if (!workspaceIndices)
|
||||||
ws = curWs.get_neighbor(dir);
|
workspaceIndices = [...Array(nWorkspaces).keys()];
|
||||||
else if (dir === direction)
|
|
||||||
ws = workspaceManager.get_workspace_by_index(to);
|
|
||||||
|
|
||||||
if (ws === null || ws === curWs) {
|
for (const i of workspaceIndices) {
|
||||||
switchData.surroundings[dir] = null;
|
const ws = workspaceManager.get_workspace_by_index(i);
|
||||||
continue;
|
const fullscreen = ws.list_windows().some(w => w.is_fullscreen());
|
||||||
|
|
||||||
|
if (y > 0 && vertical && !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.
|
||||||
|
y -= Main.panel.height;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [x, y] = this._getPositionForDirection(dir, curWs, ws);
|
|
||||||
const group = new WorkspaceGroup(ws, this.movingWindow);
|
const group = new WorkspaceGroup(ws, this.movingWindow);
|
||||||
switchData.surroundings[dir] = group;
|
|
||||||
|
switchData.workspaceGroups.push(group);
|
||||||
switchData.container.add_child(group);
|
switchData.container.add_child(group);
|
||||||
switchData.container.set_child_above_sibling(group, null);
|
switchData.container.set_child_above_sibling(group, null);
|
||||||
|
|
||||||
group.set_position(x, y);
|
group.set_position(x, y);
|
||||||
|
|
||||||
|
if (vertical)
|
||||||
|
y += global.screen_height;
|
||||||
|
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
||||||
|
x -= global.screen_width;
|
||||||
|
else
|
||||||
|
x += global.screen_width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeGroup = this._findWorkspaceGroupByIndex(activeWorkspaceIndex);
|
||||||
|
if (vertical)
|
||||||
|
switchData.container.y = -activeGroup.y;
|
||||||
|
else
|
||||||
|
switchData.container.x = -activeGroup.x;
|
||||||
|
|
||||||
if (this.movingWindow) {
|
if (this.movingWindow) {
|
||||||
const windowActor = this.movingWindow.get_compositor_private();
|
const windowActor = this.movingWindow.get_compositor_private();
|
||||||
|
|
||||||
@ -257,79 +245,73 @@ var WorkspaceAnimationController = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
animateSwitch(from, to, direction, onComplete) {
|
animateSwitch(from, to, direction, onComplete) {
|
||||||
this._prepareWorkspaceSwitch(from, to, direction);
|
this._swipeTracker.enabled = false;
|
||||||
|
|
||||||
|
let workspaceIndices = [];
|
||||||
|
|
||||||
|
switch (direction) {
|
||||||
|
case Meta.MotionDirection.UP:
|
||||||
|
case Meta.MotionDirection.LEFT:
|
||||||
|
case Meta.MotionDirection.UP_LEFT:
|
||||||
|
case Meta.MotionDirection.UP_RIGHT:
|
||||||
|
workspaceIndices = [to, from];
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Meta.MotionDirection.DOWN:
|
||||||
|
case Meta.MotionDirection.RIGHT:
|
||||||
|
case Meta.MotionDirection.DOWN_LEFT:
|
||||||
|
case Meta.MotionDirection.DOWN_RIGHT:
|
||||||
|
workspaceIndices = [from, to];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL &&
|
||||||
|
direction !== Meta.MotionDirection.UP &&
|
||||||
|
direction !== Meta.MotionDirection.DOWN)
|
||||||
|
workspaceIndices.reverse();
|
||||||
|
|
||||||
|
this._prepareWorkspaceSwitch(workspaceIndices);
|
||||||
this._switchData.inProgress = true;
|
this._switchData.inProgress = true;
|
||||||
|
|
||||||
const workspaceManager = global.workspace_manager;
|
const fromGroup = this._findWorkspaceGroupByIndex(from);
|
||||||
const fromWs = workspaceManager.get_workspace_by_index(from);
|
const toGroup = this._findWorkspaceGroupByIndex(to);
|
||||||
const toWs = workspaceManager.get_workspace_by_index(to);
|
|
||||||
|
|
||||||
let [xDest, yDest] = this._getPositionForDirection(direction, fromWs, toWs);
|
this._switchData.container.x = -fromGroup.x;
|
||||||
|
this._switchData.container.y = -fromGroup.y;
|
||||||
/* @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;
|
|
||||||
|
|
||||||
this._switchData.container.ease({
|
this._switchData.container.ease({
|
||||||
x: xDest,
|
x: -toGroup.x,
|
||||||
y: yDest,
|
y: -toGroup.y,
|
||||||
duration: WINDOW_ANIMATION_TIME,
|
duration: WINDOW_ANIMATION_TIME,
|
||||||
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
this._finishWorkspaceSwitch(this._switchData);
|
this._finishWorkspaceSwitch(this._switchData);
|
||||||
onComplete();
|
onComplete();
|
||||||
|
this._swipeTracker.enabled = true;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_directionForProgress(progress) {
|
_getProgressForWorkspace(workspaceGroup) {
|
||||||
if (global.workspace_manager.layout_rows === -1) {
|
if (global.workspace_manager.layout_rows === -1)
|
||||||
return progress > 0
|
return workspaceGroup.y / global.screen_height;
|
||||||
? Meta.MotionDirection.DOWN
|
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
||||||
: Meta.MotionDirection.UP;
|
return -workspaceGroup.x / global.screen_width;
|
||||||
} else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) {
|
|
||||||
return progress > 0
|
|
||||||
? Meta.MotionDirection.LEFT
|
|
||||||
: Meta.MotionDirection.RIGHT;
|
|
||||||
} else {
|
|
||||||
return progress > 0
|
|
||||||
? Meta.MotionDirection.RIGHT
|
|
||||||
: Meta.MotionDirection.LEFT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_getProgressRange() {
|
|
||||||
if (!this._switchData)
|
|
||||||
return [0, 0];
|
|
||||||
|
|
||||||
let lower = 0;
|
|
||||||
let upper = 0;
|
|
||||||
|
|
||||||
const horiz = global.workspace_manager.layout_rows !== -1;
|
|
||||||
let baseDistance;
|
|
||||||
if (horiz)
|
|
||||||
baseDistance = global.screen_width;
|
|
||||||
else
|
else
|
||||||
baseDistance = global.screen_height;
|
return workspaceGroup.x / global.screen_width;
|
||||||
|
|
||||||
let direction = this._directionForProgress(-1);
|
|
||||||
let group = this._switchData.surroundings[direction];
|
|
||||||
if (group !== null) {
|
|
||||||
const distance = horiz ? group.x : group.y;
|
|
||||||
lower = -Math.abs(distance) / baseDistance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
direction = this._directionForProgress(1);
|
_findWorkspaceGroupByIndex(index) {
|
||||||
group = this._switchData.surroundings[direction];
|
return this._switchData.workspaceGroups.find(g => g.workspace.index() === index);
|
||||||
if (group !== null) {
|
|
||||||
const distance = horiz ? group.x : group.y;
|
|
||||||
upper = Math.abs(distance) / baseDistance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return [lower, upper];
|
_findClosestWorkspaceGroup(progress) {
|
||||||
|
const distances = this._switchData.workspaceGroups.map(g => {
|
||||||
|
const workspaceProgress = this._getProgressForWorkspace(g);
|
||||||
|
return Math.abs(workspaceProgress - progress);
|
||||||
|
});
|
||||||
|
const index = distances.indexOf(Math.min(...distances));
|
||||||
|
return this._switchData.workspaceGroups[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
_switchWorkspaceBegin(tracker, monitor) {
|
_switchWorkspaceBegin(tracker, monitor) {
|
||||||
@ -343,15 +325,10 @@ var WorkspaceAnimationController = class {
|
|||||||
? Clutter.Orientation.HORIZONTAL
|
? Clutter.Orientation.HORIZONTAL
|
||||||
: Clutter.Orientation.VERTICAL;
|
: Clutter.Orientation.VERTICAL;
|
||||||
|
|
||||||
const activeWorkspace = workspaceManager.get_active_workspace();
|
const baseDistance = horiz ? global.screen_width : global.screen_height;
|
||||||
|
|
||||||
let baseDistance;
|
|
||||||
if (horiz)
|
|
||||||
baseDistance = global.screen_width;
|
|
||||||
else
|
|
||||||
baseDistance = global.screen_height;
|
|
||||||
|
|
||||||
let progress;
|
let progress;
|
||||||
|
let cancelProgress;
|
||||||
if (this._switchData && this._switchData.gestureActivated) {
|
if (this._switchData && this._switchData.gestureActivated) {
|
||||||
this._switchData.container.remove_all_transitions();
|
this._switchData.container.remove_all_transitions();
|
||||||
if (!horiz)
|
if (!horiz)
|
||||||
@ -360,41 +337,36 @@ var WorkspaceAnimationController = class {
|
|||||||
progress = this._switchData.container.x / baseDistance;
|
progress = this._switchData.container.x / baseDistance;
|
||||||
else
|
else
|
||||||
progress = -this._switchData.container.x / baseDistance;
|
progress = -this._switchData.container.x / baseDistance;
|
||||||
|
|
||||||
|
const wsGroup = this._findClosestWorkspaceGroup(progress);
|
||||||
|
cancelProgress = this._getProgressForWorkspaceGroup(wsGroup);
|
||||||
} else {
|
} else {
|
||||||
this._prepareWorkspaceSwitch(activeWorkspace.index(), -1);
|
this._prepareWorkspaceSwitch();
|
||||||
progress = 0;
|
|
||||||
|
const activeIndex = workspaceManager.get_active_workspace_index();
|
||||||
|
const wsGroup = this._findWorkspaceGroupByIndex(activeIndex);
|
||||||
|
|
||||||
|
progress = cancelProgress = this._getProgressForWorkspace(wsGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
const points = [];
|
const points = this._switchData.workspaceGroups.map(this._getProgressForWorkspace);
|
||||||
const [lower, upper] = this._getProgressRange();
|
|
||||||
|
|
||||||
if (lower !== 0)
|
tracker.confirmSwipe(baseDistance, points, progress, cancelProgress);
|
||||||
points.push(lower);
|
|
||||||
|
|
||||||
points.push(0);
|
|
||||||
|
|
||||||
if (upper !== 0)
|
|
||||||
points.push(upper);
|
|
||||||
|
|
||||||
tracker.confirmSwipe(baseDistance, points, progress, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_switchWorkspaceUpdate(tracker, progress) {
|
_switchWorkspaceUpdate(tracker, progress) {
|
||||||
if (!this._switchData)
|
if (!this._switchData)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const direction = this._directionForProgress(progress);
|
|
||||||
const group = this._switchData.surroundings[direction];
|
|
||||||
let xPos = 0;
|
let xPos = 0;
|
||||||
let yPos = 0;
|
let yPos = 0;
|
||||||
if (group) {
|
|
||||||
if (global.workspace_manager.layout_rows === -1)
|
if (global.workspace_manager.layout_rows === -1)
|
||||||
yPos = -Math.round(progress * global.screen_height);
|
yPos = -Math.round(progress * global.screen_height);
|
||||||
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
||||||
xPos = Math.round(progress * global.screen_width);
|
xPos = Math.round(progress * global.screen_width);
|
||||||
else
|
else
|
||||||
xPos = -Math.round(progress * global.screen_width);
|
xPos = -Math.round(progress * global.screen_width);
|
||||||
}
|
|
||||||
|
|
||||||
this._switchData.container.set_position(xPos, yPos);
|
this._switchData.container.set_position(xPos, yPos);
|
||||||
}
|
}
|
||||||
@ -403,40 +375,24 @@ var WorkspaceAnimationController = class {
|
|||||||
if (!this._switchData)
|
if (!this._switchData)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const workspaceManager = global.workspace_manager;
|
const newGroup = this._findClosestWorkspaceGroup(endProgress);
|
||||||
const activeWorkspace = workspaceManager.get_active_workspace();
|
|
||||||
let newWs = activeWorkspace;
|
|
||||||
let xDest = 0;
|
|
||||||
let yDest = 0;
|
|
||||||
if (endProgress !== 0) {
|
|
||||||
const direction = this._directionForProgress(endProgress);
|
|
||||||
newWs = activeWorkspace.get_neighbor(direction);
|
|
||||||
xDest = -this._switchData.surroundings[direction].x;
|
|
||||||
yDest = -this._switchData.surroundings[direction].y;
|
|
||||||
}
|
|
||||||
|
|
||||||
const switchData = this._switchData;
|
const switchData = this._switchData;
|
||||||
switchData.gestureActivated = true;
|
switchData.gestureActivated = true;
|
||||||
|
|
||||||
this._switchData.container.ease({
|
this._switchData.container.ease({
|
||||||
x: xDest,
|
x: -newGroup.x,
|
||||||
y: yDest,
|
y: -newGroup.y,
|
||||||
duration,
|
duration,
|
||||||
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
mode: Clutter.AnimationMode.EASE_OUT_CUBIC,
|
||||||
onComplete: () => {
|
onComplete: () => {
|
||||||
if (!newWs.active)
|
if (!newGroup.workspace.active)
|
||||||
newWs.activate(global.get_current_time());
|
newGroup.workspace.activate(global.get_current_time());
|
||||||
this._finishWorkspaceSwitch(switchData);
|
this._finishWorkspaceSwitch(switchData);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_switchWorkspaceStop() {
|
|
||||||
this._switchData.container.x = 0;
|
|
||||||
this._switchData.container.y = 0;
|
|
||||||
this._finishWorkspaceSwitch(this._switchData);
|
|
||||||
}
|
|
||||||
|
|
||||||
get gestureActive() {
|
get gestureActive() {
|
||||||
return this._switchData !== null && this._switchData.gestureActivated;
|
return this._switchData !== null && this._switchData.gestureActivated;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user