From 1759e27ef70c758139804bbcbf75f0d9f97fe2d1 Mon Sep 17 00:00:00 2001 From: Alexander Mikhaylenko Date: Tue, 2 Jul 2019 22:28:47 +0500 Subject: [PATCH] workspaceAnimation: Split from WindowManager It's already too complex, and will get more complex in future, split it out. Update the code style. https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1326 --- js/js-resources.gresource.xml | 1 + js/ui/windowManager.js | 400 +------------------------------- js/ui/workspaceAnimation.js | 422 ++++++++++++++++++++++++++++++++++ 3 files changed, 430 insertions(+), 393 deletions(-) create mode 100644 js/ui/workspaceAnimation.js diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml index 2289a2dde..9e2adefe3 100644 --- a/js/js-resources.gresource.xml +++ b/js/js-resources.gresource.xml @@ -112,6 +112,7 @@ ui/windowManager.js ui/windowPreview.js ui/workspace.js + ui/workspaceAnimation.js ui/workspaceSwitcherPopup.js ui/workspaceThumbnail.js ui/workspacesView.js diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js index 2ff1ffdcb..4396f6000 100644 --- a/js/ui/windowManager.js +++ b/js/ui/windowManager.js @@ -14,9 +14,9 @@ const WindowMenu = imports.ui.windowMenu; const PadOsd = imports.ui.padOsd; const EdgeDragAction = imports.ui.edgeDragAction; const CloseDialog = imports.ui.closeDialog; -const SwipeTracker = imports.ui.swipeTracker; const SwitchMonitor = imports.ui.switchMonitor; const IBusManager = imports.misc.ibusManager; +const WorkspaceAnimation = imports.ui.workspaceAnimation; const { loadInterfaceXML } = imports.misc.fileUtils; @@ -561,7 +561,6 @@ var WindowManager = class { this._resizing = new Set(); this._resizePending = new Set(); this._destroying = new Set(); - this._movingWindow = null; this._dimmedWindows = []; @@ -571,15 +570,6 @@ var WindowManager = class { this._isWorkspacePrepended = false; - 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); - } - }); this._shellwm.connect('kill-window-effects', (shellwm, actor) => { this._minimizeWindowDone(shellwm, actor); this._mapWindowDone(shellwm, actor); @@ -601,7 +591,6 @@ var WindowManager = class { this._shellwm.connect('confirm-display-change', this._confirmDisplayChange.bind(this)); this._shellwm.connect('create-close-dialog', this._createCloseDialog.bind(this)); this._shellwm.connect('create-inhibit-shortcuts-dialog', this._createInhibitShortcutsDialog.bind(this)); - global.display.connect('restacked', this._syncStacking.bind(this)); this._workspaceSwitcherPopup = null; this._tilePreview = null; @@ -947,17 +936,10 @@ var WindowManager = class { Main.overview.connect('showing', () => { for (let i = 0; i < this._dimmedWindows.length; i++) this._undimWindow(this._dimmedWindows[i]); - - if (this._switchData) { - if (this._switchData.gestureActivated) - this._switchWorkspaceStop(); - this._swipeTracker.enabled = false; - } }); Main.overview.connect('hiding', () => { for (let i = 0; i < this._dimmedWindows.length; i++) this._dimWindow(this._dimmedWindows[i]); - this._swipeTracker.enabled = true; }); this._windowMenuManager = new WindowMenu.WindowMenuManager(); @@ -968,13 +950,6 @@ var WindowManager = class { global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, -1, 1); - let swipeTracker = new SwipeTracker.SwipeTracker(global.stage, - Shell.ActionMode.NORMAL, { allowDrag: false, allowScroll: 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; - let appSwitchAction = new AppSwitchAction(); appSwitchAction.connect('activated', this._switchApp.bind(this)); global.stage.add_action(appSwitchAction); @@ -1006,6 +981,9 @@ var WindowManager = class { global.display.connect('in-fullscreen-changed', updateUnfullscreenGesture); global.stage.add_action(topDragAction); + + this._workspaceAnimation = + new WorkspaceAnimation.WorkspaceAnimationController(); } _showPadOsd(display, device, settings, imagePath, editionMode, monitorIndex) { @@ -1128,8 +1106,7 @@ var WindowManager = class { } _shouldAnimate() { - return !(Main.overview.visible || - (this._switchData && this._switchData.gestureActivated)); + return !(Main.overview.visible || this._workspaceAnimation.gestureActive); } _shouldAnimateActor(actor, types) { @@ -1625,376 +1602,13 @@ var WindowManager = class { return !(this._allowedKeybindings[binding.get_name()] & Main.actionMode); } - _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; - - 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); - - wgroup.add_actor(switchData.movingWindowBin); - wgroup.add_actor(switchData.container); - - let workspaceManager = global.workspace_manager; - let curWs = workspaceManager.get_workspace_by_index(from); - - for (let dir of Object.values(Meta.MotionDirection)) { - let ws = null; - - if (to < 0) - ws = curWs.get_neighbor(dir); - else if (dir == direction) - ws = workspaceManager.get_workspace_by_index(to); - - if (ws == null || ws == curWs) { - switchData.surroundings[dir] = null; - continue; - } - - let [x, y] = this._getPositionForDirection(dir, curWs, ws); - let info = { - index: ws.index(), - 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); - - info.actor.set_position(x, y); - } - - wgroup.set_child_above_sibling(switchData.movingWindowBin, null); - - for (let i = 0; i < windows.length; i++) { - let actor = windows[i]; - let window = actor.get_meta_window(); - - if (!window.showing_on_its_workspace()) - continue; - - if (window.is_on_all_workspaces()) - continue; - - let record = { window: actor, - parent: actor.get_parent() }; - - if (this._movingWindow && window == this._movingWindow) { - record.parent.remove_child(actor); - switchData.movingWindow = record; - switchData.windows.push(switchData.movingWindow); - switchData.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); - } else { - let visible = false; - for (let dir of Object.values(Meta.MotionDirection)) { - let info = switchData.surroundings[dir]; - - if (!info || info.index != window.get_workspace().index()) - continue; - - record.parent.remove_child(actor); - switchData.windows.push(record); - info.actor.add_child(actor); - visible = true; - break; - } - - actor.visible = visible; - } - } - - for (let i = 0; i < switchData.windows.length; i++) { - let w = switchData.windows[i]; - - w.windowDestroyId = w.window.connect('destroy', () => { - switchData.windows.splice(switchData.windows.indexOf(w), 1); - }); - } - } - - _finishWorkspaceSwitch(switchData) { - this._switchData = null; - - for (let i = 0; i < switchData.windows.length; i++) { - let w = switchData.windows[i]; - - w.window.disconnect(w.windowDestroyId); - w.window.get_parent().remove_child(w.window); - w.parent.add_child(w.window); - - if (!w.window.get_meta_window().get_workspace().active) - w.window.hide(); - } - switchData.container.destroy(); - switchData.movingWindowBin.destroy(); - - this._movingWindow = null; - } - _switchWorkspace(shellwm, from, to, direction) { if (!Main.sessionMode.hasWorkspaces || !this._shouldAnimate()) { shellwm.completed_switch_workspace(); return; } - this._prepareWorkspaceSwitch(from, to, direction); - this._switchData.inProgress = true; - - let workspaceManager = global.workspace_manager; - let fromWs = workspaceManager.get_workspace_by_index(from); - let toWs = workspaceManager.get_workspace_by_index(to); - - let [xDest, yDest] = this._getPositionForDirection(direction, fromWs, toWs); - - /* @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({ - x: xDest, - y: yDest, - duration: WINDOW_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_CUBIC, - onComplete: () => this._switchWorkspaceDone(shellwm), - }); - } - - _switchWorkspaceDone(shellwm) { - this._finishWorkspaceSwitch(this._switchData); - shellwm.completed_switch_workspace(); - } - - _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) { - 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; - - let horiz = global.workspace_manager.layout_rows !== -1; - let baseDistance; - if (horiz) - 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; - } - - direction = this._directionForProgress(1); - info = this._switchData.surroundings[direction]; - if (info !== null) { - let distance = horiz ? info.xDest : info.yDest; - upper = Math.abs(distance) / baseDistance; - } - - return [lower, upper]; - } - - _switchWorkspaceBegin(tracker, monitor) { - if (Meta.prefs_get_workspaces_only_on_primary() && - monitor !== Main.layoutManager.primaryIndex) - return; - - let workspaceManager = global.workspace_manager; - let horiz = workspaceManager.layout_rows !== -1; - tracker.orientation = horiz - ? Clutter.Orientation.HORIZONTAL - : Clutter.Orientation.VERTICAL; - - let activeWorkspace = workspaceManager.get_active_workspace(); - - let baseDistance; - if (horiz) - baseDistance = global.screen_width; - else - 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; - } else { - this._prepareWorkspaceSwitch(activeWorkspace.index(), -1); - progress = 0; - } - - let points = []; - let [lower, upper] = this._getProgressRange(); - - if (lower !== 0) - points.push(lower); - - points.push(0); - - if (upper !== 0) - points.push(upper); - - tracker.confirmSwipe(baseDistance, points, progress, 0); - } - - _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(progress * global.screen_height); - else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) - xPos = Math.round(progress * global.screen_width); - else - xPos = -Math.round(progress * global.screen_width); - } - - this._switchData.container.set_position(xPos, yPos); - } - - _switchWorkspaceEnd(tracker, duration, endProgress) { - if (!this._switchData) - return; - - 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); - newWs = activeWorkspace.get_neighbor(direction); - xDest = -this._switchData.surroundings[direction].xDest; - yDest = -this._switchData.surroundings[direction].yDest; - } - - let switchData = this._switchData; - switchData.gestureActivated = true; - - this._switchData.container.ease({ - x: xDest, - y: yDest, - duration, - mode: Clutter.AnimationMode.EASE_OUT_CUBIC, - onComplete: () => { - if (!newWs.active) - this.actionMoveWorkspace(newWs); - this._finishWorkspaceSwitch(switchData); - }, - }); - } - - _switchWorkspaceStop() { - this._switchData.container.x = 0; - this._switchData.container.y = 0; - this._finishWorkspaceSwitch(this._switchData); + this._workspaceAnimation.animateSwitch(shellwm, from, to, direction); } _showTilePreview(shellwm, window, tileRect, monitorIndex) { @@ -2196,7 +1810,7 @@ var WindowManager = class { // This won't have any effect for "always sticky" windows // (like desktop windows or docks) - this._movingWindow = window; + this._workspaceAnimation.movingWindow = window; window.change_workspace(workspace); global.display.clear_mouse_mode(); diff --git a/js/ui/workspaceAnimation.js b/js/ui/workspaceAnimation.js new file mode 100644 index 000000000..096867e24 --- /dev/null +++ b/js/ui/workspaceAnimation.js @@ -0,0 +1,422 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- +/* exported WorkspaceAnimationController */ + +const { Clutter, Meta, Shell } = imports.gi; + +const Main = imports.ui.main; +const SwipeTracker = imports.ui.swipeTracker; + +const 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 swipeTracker = new SwipeTracker.SwipeTracker(global.stage, + Shell.ActionMode.NORMAL, { allowDrag: false, allowScroll: 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; + + const windows = global.get_window_actors(); + let lastCurSibling = null; + const 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 (const dir of Object.values(Meta.MotionDirection)) { + const 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; + + 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) + return; + + const wgroup = global.window_group; + const windows = global.get_window_actors(); + const 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_child(switchData.curGroup); + + wgroup.add_child(switchData.movingWindowBin); + wgroup.add_child(switchData.container); + + const workspaceManager = global.workspace_manager; + const curWs = workspaceManager.get_workspace_by_index(from); + + for (const dir of Object.values(Meta.MotionDirection)) { + let ws = null; + + if (to < 0) + ws = curWs.get_neighbor(dir); + else if (dir === direction) + ws = workspaceManager.get_workspace_by_index(to); + + if (ws === null || ws === curWs) { + switchData.surroundings[dir] = null; + continue; + } + + const [x, y] = this._getPositionForDirection(dir, curWs, ws); + const info = { + index: ws.index(), + actor: new Clutter.Actor(), + xDest: x, + yDest: y, + }; + switchData.surroundings[dir] = info; + switchData.container.add_child(info.actor); + switchData.container.set_child_above_sibling(info.actor, null); + + info.actor.set_position(x, y); + } + + wgroup.set_child_above_sibling(switchData.movingWindowBin, null); + + for (const windowActor of windows) { + const window = windowActor.get_meta_window(); + + if (!window.showing_on_its_workspace()) + continue; + + if (window.is_on_all_workspaces()) + continue; + + const record = { + windowActor, + parent: windowActor.get_parent(), + }; + + if (this.movingWindow && window === this.movingWindow) { + record.parent.remove_child(windowActor); + switchData.movingWindow = record; + switchData.windows.push(switchData.movingWindow); + switchData.movingWindowBin.add_child(windowActor); + } else if (window.get_workspace().index() === from) { + record.parent.remove_child(windowActor); + switchData.windows.push(record); + switchData.curGroup.add_child(windowActor); + } else { + let visible = false; + for (const dir of Object.values(Meta.MotionDirection)) { + const info = switchData.surroundings[dir]; + + if (!info || info.index !== window.get_workspace().index()) + continue; + + record.parent.remove_child(windowActor); + switchData.windows.push(record); + info.actor.add_child(windowActor); + visible = true; + break; + } + + windowActor.visible = visible; + } + } + + for (const record of switchData.windows) { + record.windowDestroyId = record.windowActor.connect('destroy', () => { + switchData.windows.splice(switchData.windows.indexOf(record), 1); + }); + } + } + + _finishWorkspaceSwitch(switchData) { + this._switchData = null; + + for (const record of switchData.windows) { + record.windowActor.disconnect(record.windowDestroyId); + switchData.movingWindowBin.remove_child(record.windowActor); + record.parent.add_child(record.windowActor); + + if (!record.windowActor.get_meta_window().get_workspace().active) + record.windowActor.hide(); + } + switchData.container.destroy(); + switchData.movingWindowBin.destroy(); + + this.movingWindow = null; + } + + animateSwitch(shellwm, from, to, direction) { + this._prepareWorkspaceSwitch(from, to, direction); + this._switchData.inProgress = true; + + const workspaceManager = global.workspace_manager; + const fromWs = workspaceManager.get_workspace_by_index(from); + const toWs = workspaceManager.get_workspace_by_index(to); + + let [xDest, yDest] = this._getPositionForDirection(direction, fromWs, toWs); + + /* @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({ + x: xDest, + y: yDest, + duration: WINDOW_ANIMATION_TIME, + mode: Clutter.AnimationMode.EASE_OUT_CUBIC, + onComplete: () => this._switchWorkspaceDone(shellwm), + }); + } + + _switchWorkspaceDone(shellwm) { + this._finishWorkspaceSwitch(this._switchData); + shellwm.completed_switch_workspace(); + } + + _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) { + 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 + baseDistance = global.screen_height; + + let direction = this._directionForProgress(-1); + let info = this._switchData.surroundings[direction]; + if (info !== null) { + const distance = horiz ? info.xDest : info.yDest; + lower = -Math.abs(distance) / baseDistance; + } + + direction = this._directionForProgress(1); + info = this._switchData.surroundings[direction]; + if (info !== null) { + const distance = horiz ? info.xDest : info.yDest; + upper = Math.abs(distance) / baseDistance; + } + + return [lower, upper]; + } + + _switchWorkspaceBegin(tracker, monitor) { + if (Meta.prefs_get_workspaces_only_on_primary() && + monitor !== Main.layoutManager.primaryIndex) + return; + + const workspaceManager = global.workspace_manager; + const horiz = workspaceManager.layout_rows !== -1; + tracker.orientation = horiz + ? Clutter.Orientation.HORIZONTAL + : Clutter.Orientation.VERTICAL; + + const activeWorkspace = workspaceManager.get_active_workspace(); + + let baseDistance; + if (horiz) + baseDistance = global.screen_width; + else + 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; + } else { + this._prepareWorkspaceSwitch(activeWorkspace.index(), -1); + progress = 0; + } + + const points = []; + const [lower, upper] = this._getProgressRange(); + + if (lower !== 0) + points.push(lower); + + points.push(0); + + if (upper !== 0) + points.push(upper); + + tracker.confirmSwipe(baseDistance, points, progress, 0); + } + + _switchWorkspaceUpdate(tracker, progress) { + if (!this._switchData) + return; + + const direction = this._directionForProgress(progress); + const info = this._switchData.surroundings[direction]; + let xPos = 0; + let yPos = 0; + if (info) { + if (global.workspace_manager.layout_rows === -1) + yPos = -Math.round(progress * global.screen_height); + else if (Clutter.get_default_text_direction() === Clutter.TextDirection.RTL) + xPos = Math.round(progress * global.screen_width); + else + xPos = -Math.round(progress * global.screen_width); + } + + this._switchData.container.set_position(xPos, yPos); + } + + _switchWorkspaceEnd(tracker, duration, endProgress) { + if (!this._switchData) + return; + + const workspaceManager = global.workspace_manager; + 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].xDest; + yDest = -this._switchData.surroundings[direction].yDest; + } + + const switchData = this._switchData; + switchData.gestureActivated = true; + + this._switchData.container.ease({ + x: xDest, + y: yDest, + duration, + mode: Clutter.AnimationMode.EASE_OUT_CUBIC, + onComplete: () => { + if (!newWs.active) + newWs.activate(global.get_current_time()); + this._finishWorkspaceSwitch(switchData); + }, + }); + } + + _switchWorkspaceStop() { + this._switchData.container.x = 0; + this._switchData.container.y = 0; + this._finishWorkspaceSwitch(this._switchData); + } + + get gestureActive() { + return this._switchData !== null && this._switchData.gestureActivated; + } + + set movingWindow(movingWindow) { + this._movingWindow = movingWindow; + } + + get movingWindow() { + return this._movingWindow; + } +};