workspaceAnimation: Support multiple monitors
Currently, there's one animation for the whole canvas. While it looks fine with just one screen, it causes windows to move between screens when switching workspaces. Instead, have a separate animation on each screen, and sync their progress so that at any given time the progress "fraction" is the same between all screens. Clip all animations to their screens so that the windows don't leak to other screens. If a window is placed between every screen, can end up in multiple animations, in that case each part is still animated separately. Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1213 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/605
This commit is contained in:
parent
46d5bccfc6
commit
b1afb946b2
@ -4,17 +4,19 @@
|
|||||||
const { Clutter, GObject, Meta, Shell } = imports.gi;
|
const { Clutter, GObject, Meta, Shell } = imports.gi;
|
||||||
|
|
||||||
const Main = imports.ui.main;
|
const Main = imports.ui.main;
|
||||||
|
const Layout = imports.ui.layout;
|
||||||
const SwipeTracker = imports.ui.swipeTracker;
|
const SwipeTracker = imports.ui.swipeTracker;
|
||||||
|
|
||||||
const WINDOW_ANIMATION_TIME = 250;
|
const WINDOW_ANIMATION_TIME = 250;
|
||||||
|
|
||||||
const WorkspaceGroup = GObject.registerClass(
|
const WorkspaceGroup = GObject.registerClass(
|
||||||
class WorkspaceGroup extends Clutter.Actor {
|
class WorkspaceGroup extends Clutter.Actor {
|
||||||
_init(controller, workspace) {
|
_init(controller, workspace, monitor) {
|
||||||
super._init();
|
super._init();
|
||||||
|
|
||||||
this._controller = controller;
|
this._controller = controller;
|
||||||
this._workspace = workspace;
|
this._workspace = workspace;
|
||||||
|
this._monitor = monitor;
|
||||||
this._windows = [];
|
this._windows = [];
|
||||||
|
|
||||||
this._refreshWindows();
|
this._refreshWindows();
|
||||||
@ -28,6 +30,11 @@ class WorkspaceGroup extends Clutter.Actor {
|
|||||||
if (window.get_workspace() !== this._workspace)
|
if (window.get_workspace() !== this._workspace)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
let geometry = global.display.get_monitor_geometry(this._monitor.index);
|
||||||
|
let [intersects, intersection_] = window.get_frame_rect().intersect(geometry);
|
||||||
|
if (!intersects)
|
||||||
|
return false;
|
||||||
|
|
||||||
if (!window.showing_on_its_workspace())
|
if (!window.showing_on_its_workspace())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -51,8 +58,8 @@ class WorkspaceGroup extends Clutter.Actor {
|
|||||||
for (let window of windows) {
|
for (let window of windows) {
|
||||||
let clone = new Clutter.Clone({
|
let clone = new Clutter.Clone({
|
||||||
source: window,
|
source: window,
|
||||||
x: window.x,
|
x: window.x - this._monitor.x,
|
||||||
y: window.y,
|
y: window.y - this._monitor.y,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.add_actor(clone);
|
this.add_actor(clone);
|
||||||
@ -105,19 +112,35 @@ const WorkspaceAnimation = GObject.registerClass({
|
|||||||
|
|
||||||
this._controller = controller;
|
this._controller = controller;
|
||||||
this._movingWindow = null;
|
this._movingWindow = null;
|
||||||
this._surroundings = {};
|
this._monitors = [];
|
||||||
this._progress = 0;
|
this._progress = 0;
|
||||||
|
|
||||||
this._container = new Clutter.Actor();
|
|
||||||
|
|
||||||
this.add_actor(this._container);
|
|
||||||
global.window_group.add_actor(this);
|
global.window_group.add_actor(this);
|
||||||
|
|
||||||
let workspaceManager = global.workspace_manager;
|
let workspaceManager = global.workspace_manager;
|
||||||
let curWs = workspaceManager.get_workspace_by_index(from);
|
let curWs = workspaceManager.get_workspace_by_index(from);
|
||||||
|
|
||||||
this._curGroup = new WorkspaceGroup(controller, curWs);
|
for (let monitor of Main.layoutManager.monitors) {
|
||||||
this._container.add_actor(this._curGroup);
|
let record = {
|
||||||
|
index: monitor.index,
|
||||||
|
clipBin: new Clutter.Actor({
|
||||||
|
x_expand: true,
|
||||||
|
y_expand: true,
|
||||||
|
clip_to_allocation: true,
|
||||||
|
}),
|
||||||
|
container: new Clutter.Actor(),
|
||||||
|
surroundings: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
let constraint = new Layout.MonitorConstraint({ index: monitor.index });
|
||||||
|
record.clipBin.add_constraint(constraint);
|
||||||
|
|
||||||
|
record.clipBin.add_actor(record.container);
|
||||||
|
|
||||||
|
this.add_actor(record.clipBin);
|
||||||
|
|
||||||
|
record.curGroup = new WorkspaceGroup(controller, curWs, monitor);
|
||||||
|
record.container.add_actor(record.curGroup);
|
||||||
|
|
||||||
for (let dir of Object.values(Meta.MotionDirection)) {
|
for (let dir of Object.values(Meta.MotionDirection)) {
|
||||||
let ws = null;
|
let ws = null;
|
||||||
@ -128,24 +151,28 @@ const WorkspaceAnimation = GObject.registerClass({
|
|||||||
ws = workspaceManager.get_workspace_by_index(to);
|
ws = workspaceManager.get_workspace_by_index(to);
|
||||||
|
|
||||||
if (ws === null || ws === curWs) {
|
if (ws === null || ws === curWs) {
|
||||||
this._surroundings[dir] = null;
|
record.surroundings[dir] = null;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [x, y] = this._getPositionForDirection(dir, curWs, ws);
|
let [x, y] = this._getPositionForDirection(dir, curWs, ws,
|
||||||
|
monitor.index);
|
||||||
let info = {
|
let info = {
|
||||||
index: ws.index(),
|
index: ws.index(),
|
||||||
actor: new WorkspaceGroup(controller, ws),
|
actor: new WorkspaceGroup(controller, ws, monitor),
|
||||||
xDest: x,
|
xDest: x,
|
||||||
yDest: y,
|
yDest: y,
|
||||||
};
|
};
|
||||||
this._surroundings[dir] = info;
|
record.surroundings[dir] = info;
|
||||||
this._container.add_actor(info.actor);
|
record.container.add_actor(info.actor);
|
||||||
this._container.set_child_above_sibling(info.actor, null);
|
record.container.set_child_above_sibling(info.actor, null);
|
||||||
|
|
||||||
info.actor.set_position(x, y);
|
info.actor.set_position(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._monitors.push(record);
|
||||||
|
}
|
||||||
|
|
||||||
if (this._controller.movingWindow) {
|
if (this._controller.movingWindow) {
|
||||||
let actor = this._controller.movingWindow.get_compositor_private();
|
let actor = this._controller.movingWindow.get_compositor_private();
|
||||||
let container = new Clutter.Actor();
|
let container = new Clutter.Actor();
|
||||||
@ -168,6 +195,8 @@ const WorkspaceAnimation = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
|
|
||||||
_onDestroy() {
|
_onDestroy() {
|
||||||
|
this._monitors = [];
|
||||||
|
|
||||||
if (this._movingWindow) {
|
if (this._movingWindow) {
|
||||||
let record = this._movingWindow;
|
let record = this._movingWindow;
|
||||||
record.window.disconnect(record.windowDestroyId);
|
record.window.disconnect(record.windowDestroyId);
|
||||||
@ -179,34 +208,39 @@ const WorkspaceAnimation = GObject.registerClass({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getPositionForDirection(direction, fromWs, toWs) {
|
_getPositionForDirection(direction, fromWs, toWs, monitor) {
|
||||||
let xDest = 0, yDest = 0;
|
let xDest = 0, yDest = 0;
|
||||||
|
|
||||||
let oldWsIsFullscreen = fromWs.list_windows().some(w => w.is_fullscreen());
|
let condition = w => w.get_monitor() === monitor && w.is_fullscreen();
|
||||||
let newWsIsFullscreen = toWs.list_windows().some(w => w.is_fullscreen());
|
|
||||||
|
let oldWsIsFullscreen = fromWs.list_windows().some(condition);
|
||||||
|
let newWsIsFullscreen = toWs.list_windows().some(condition);
|
||||||
|
|
||||||
|
let geometry = Main.layoutManager.monitors[monitor];
|
||||||
|
|
||||||
// We have to shift windows up or down by the height of the panel to prevent having a
|
// 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
|
// visible gap between the windows while switching workspaces. Since fullscreen windows
|
||||||
// hide the panel, they don't need to be shifted up or down.
|
// hide the panel, they don't need to be shifted up or down.
|
||||||
let shiftHeight = Main.panel.height;
|
let shiftHeight = monitor === Main.layoutManager.primaryIndex
|
||||||
|
? Main.panel.height : 0;
|
||||||
|
|
||||||
if (direction === Meta.MotionDirection.UP ||
|
if (direction === Meta.MotionDirection.UP ||
|
||||||
direction === Meta.MotionDirection.UP_LEFT ||
|
direction === Meta.MotionDirection.UP_LEFT ||
|
||||||
direction === Meta.MotionDirection.UP_RIGHT)
|
direction === Meta.MotionDirection.UP_RIGHT)
|
||||||
yDest = -global.screen_height + (oldWsIsFullscreen ? 0 : shiftHeight);
|
yDest = -geometry.height + (oldWsIsFullscreen ? 0 : shiftHeight);
|
||||||
else if (direction === Meta.MotionDirection.DOWN ||
|
else if (direction === Meta.MotionDirection.DOWN ||
|
||||||
direction === Meta.MotionDirection.DOWN_LEFT ||
|
direction === Meta.MotionDirection.DOWN_LEFT ||
|
||||||
direction === Meta.MotionDirection.DOWN_RIGHT)
|
direction === Meta.MotionDirection.DOWN_RIGHT)
|
||||||
yDest = global.screen_height - (newWsIsFullscreen ? 0 : shiftHeight);
|
yDest = geometry.height - (newWsIsFullscreen ? 0 : shiftHeight);
|
||||||
|
|
||||||
if (direction === Meta.MotionDirection.LEFT ||
|
if (direction === Meta.MotionDirection.LEFT ||
|
||||||
direction === Meta.MotionDirection.UP_LEFT ||
|
direction === Meta.MotionDirection.UP_LEFT ||
|
||||||
direction === Meta.MotionDirection.DOWN_LEFT)
|
direction === Meta.MotionDirection.DOWN_LEFT)
|
||||||
xDest = -global.screen_width;
|
xDest = -geometry.width;
|
||||||
else if (direction === Meta.MotionDirection.RIGHT ||
|
else if (direction === Meta.MotionDirection.RIGHT ||
|
||||||
direction === Meta.MotionDirection.UP_RIGHT ||
|
direction === Meta.MotionDirection.UP_RIGHT ||
|
||||||
direction === Meta.MotionDirection.DOWN_RIGHT)
|
direction === Meta.MotionDirection.DOWN_RIGHT)
|
||||||
xDest = global.screen_width;
|
xDest = geometry.width;
|
||||||
|
|
||||||
return [xDest, yDest];
|
return [xDest, yDest];
|
||||||
}
|
}
|
||||||
@ -244,21 +278,24 @@ const WorkspaceAnimation = GObject.registerClass({
|
|||||||
this._progress = progress;
|
this._progress = progress;
|
||||||
|
|
||||||
let direction = this.directionForProgress(progress);
|
let direction = this.directionForProgress(progress);
|
||||||
|
|
||||||
|
for (let monitorData of this._monitors) {
|
||||||
let xPos = 0;
|
let xPos = 0;
|
||||||
let yPos = 0;
|
let yPos = 0;
|
||||||
|
|
||||||
if (global.workspace_manager.layout_rows === -1)
|
if (global.workspace_manager.layout_rows === -1)
|
||||||
yPos = -Math.round(progress * this._getDistance(direction));
|
yPos = -Math.round(progress * this._getDistance(monitorData, direction));
|
||||||
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL)
|
||||||
xPos = Math.round(progress * this._getDistance(direction));
|
xPos = Math.round(progress * this._getDistance(monitorData, direction));
|
||||||
else
|
else
|
||||||
xPos = -Math.round(progress * this._getDistance(direction));
|
xPos = -Math.round(progress * this._getDistance(monitorData, direction));
|
||||||
|
|
||||||
this._container.set_position(xPos, yPos);
|
monitorData.container.set_position(xPos, yPos);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_getDistance(direction) {
|
_getDistance(monitorData, direction) {
|
||||||
let info = this._surroundings[direction];
|
let info = monitorData.surroundings[direction];
|
||||||
if (!info)
|
if (!info)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -276,19 +313,30 @@ const WorkspaceAnimation = GObject.registerClass({
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
getProgressRange() {
|
getProgressRange(monitor) {
|
||||||
|
let monitorData = null;
|
||||||
|
for (let data of this._monitors) {
|
||||||
|
if (data.index === monitor) {
|
||||||
|
monitorData = data;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!monitorData)
|
||||||
|
return 0;
|
||||||
|
|
||||||
let baseDistance;
|
let baseDistance;
|
||||||
if (global.workspace_manager.layout_rows !== -1)
|
if (global.workspace_manager.layout_rows !== -1)
|
||||||
baseDistance = global.screen_width;
|
baseDistance = Main.layoutManager.monitors[monitor].width;
|
||||||
else
|
else
|
||||||
baseDistance = global.screen_height;
|
baseDistance = Main.layoutManager.monitors[monitor].height;
|
||||||
|
|
||||||
let direction = this.directionForProgress(-1);
|
let direction = this.directionForProgress(-1);
|
||||||
let distance = this._getDistance(direction);
|
let distance = this._getDistance(monitorData, direction);
|
||||||
let lower = -distance / baseDistance;
|
let lower = -distance / baseDistance;
|
||||||
|
|
||||||
direction = this.directionForProgress(1);
|
direction = this.directionForProgress(1);
|
||||||
distance = this._getDistance(direction);
|
distance = this._getDistance(monitorData, direction);
|
||||||
let upper = distance / baseDistance;
|
let upper = distance / baseDistance;
|
||||||
|
|
||||||
return [lower, upper];
|
return [lower, upper];
|
||||||
@ -335,6 +383,7 @@ var WorkspaceAnimationController = class {
|
|||||||
this._inProgress = false;
|
this._inProgress = false;
|
||||||
this._gestureActivated = false;
|
this._gestureActivated = false;
|
||||||
this.movingWindow = null;
|
this.movingWindow = null;
|
||||||
|
this._monitor = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
animateSwitchWorkspace(from, to, direction, onComplete) {
|
animateSwitchWorkspace(from, to, direction, onComplete) {
|
||||||
@ -368,9 +417,9 @@ var WorkspaceAnimationController = class {
|
|||||||
|
|
||||||
let baseDistance;
|
let baseDistance;
|
||||||
if (horiz)
|
if (horiz)
|
||||||
baseDistance = global.screen_width;
|
baseDistance = Main.layoutManager.monitors[monitor].width;
|
||||||
else
|
else
|
||||||
baseDistance = global.screen_height;
|
baseDistance = Main.layoutManager.monitors[monitor].height;
|
||||||
|
|
||||||
let progress;
|
let progress;
|
||||||
if (this._gestureActivated) {
|
if (this._gestureActivated) {
|
||||||
@ -381,7 +430,8 @@ var WorkspaceAnimationController = class {
|
|||||||
progress = 0;
|
progress = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
let [lower, upper] = this._animation.getProgressRange();
|
this._monitor = monitor;
|
||||||
|
let [lower, upper] = this._animation.getProgressRange(monitor);
|
||||||
if (progress < 0)
|
if (progress < 0)
|
||||||
progress *= -lower;
|
progress *= -lower;
|
||||||
else if (progress > 0)
|
else if (progress > 0)
|
||||||
@ -401,7 +451,7 @@ var WorkspaceAnimationController = class {
|
|||||||
|
|
||||||
_switchWorkspaceUpdate(tracker, progress) {
|
_switchWorkspaceUpdate(tracker, progress) {
|
||||||
// Translate the progress into [-1;1] range
|
// Translate the progress into [-1;1] range
|
||||||
let [lower, upper] = this._animation.getProgressRange();
|
let [lower, upper] = this._animation.getProgressRange(this._monitor);
|
||||||
if (progress < 0)
|
if (progress < 0)
|
||||||
progress /= -lower;
|
progress /= -lower;
|
||||||
else if (progress > 0)
|
else if (progress > 0)
|
||||||
|
Loading…
Reference in New Issue
Block a user