From a8dcfa4656e01764380d081d74365092f08c114e Mon Sep 17 00:00:00 2001 From: Alexander Mikhaylenko Date: Sun, 30 Jun 2019 17:15:44 +0500 Subject: [PATCH] windowManager: Use SwipeTracker Replace existing four-finger gestures with SwipeTracker. Since TouchpadWorkspaceSwitchAction and WorkspaceSwitchAction are now unused, remove them. Change programmatic workspace transition to use easeOutCubic interpolator to match the gesture. Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/756 https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/826 --- js/ui/windowManager.js | 388 +++++++++++++++++++---------------------- 1 file changed, 177 insertions(+), 211 deletions(-) diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js index 7716bab74..f85298cd5 100644 --- a/js/ui/windowManager.js +++ b/js/ui/windowManager.js @@ -2,7 +2,6 @@ /* exported WindowManager */ const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi; -const Signals = imports.signals; const AltTab = imports.ui.altTab; const AppFavorites = imports.ui.appFavorites; @@ -15,6 +14,7 @@ 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; @@ -30,7 +30,6 @@ var WINDOW_ANIMATION_TIME = 250; var DIM_BRIGHTNESS = -0.3; var DIM_TIME = 500; var UNDIM_TIME = 250; -var WS_MOTION_THRESHOLD = 100; var APP_MOTION_THRESHOLD = 30; var ONE_SECOND = 1000; // in ms @@ -468,147 +467,6 @@ class TilePreview extends St.Widget { } }); -var TouchpadWorkspaceSwitchAction = class { - constructor(actor, allowedModes) { - this._allowedModes = allowedModes; - this._dx = 0; - this._dy = 0; - this._enabled = true; - actor.connect('captured-event', this._handleEvent.bind(this)); - this._touchpadSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.peripherals.touchpad' }); - } - - get enabled() { - return this._enabled; - } - - set enabled(enabled) { - if (this._enabled == enabled) - return; - - this._enabled = enabled; - if (!enabled) - this.emit('cancel'); - } - - _checkActivated() { - let dir; - - if (this._dy < -WS_MOTION_THRESHOLD) - dir = Meta.MotionDirection.DOWN; - else if (this._dy > WS_MOTION_THRESHOLD) - dir = Meta.MotionDirection.UP; - else if (this._dx < -WS_MOTION_THRESHOLD) - dir = Meta.MotionDirection.RIGHT; - else if (this._dx > WS_MOTION_THRESHOLD) - dir = Meta.MotionDirection.LEFT; - else - return false; - - this.emit('activated', dir); - return true; - } - - _handleEvent(actor, event) { - if (event.type() != Clutter.EventType.TOUCHPAD_SWIPE) - return Clutter.EVENT_PROPAGATE; - - if (event.get_touchpad_gesture_finger_count() != 4) - return Clutter.EVENT_PROPAGATE; - - if ((this._allowedModes & Main.actionMode) == 0) - return Clutter.EVENT_PROPAGATE; - - if (!this._enabled) - return Clutter.EVENT_PROPAGATE; - - if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.UPDATE) { - let [dx, dy] = event.get_gesture_motion_delta(); - - // Scale deltas up a bit to make it feel snappier - this._dx += dx * 2; - if (!this._touchpadSettings.get_boolean('natural-scroll')) - this._dy -= dy * 2; - else - this._dy += dy * 2; - - this.emit('motion', this._dx, this._dy); - } else { - if ((event.get_gesture_phase() == Clutter.TouchpadGesturePhase.END && !this._checkActivated()) || - event.get_gesture_phase() == Clutter.TouchpadGesturePhase.CANCEL) - this.emit('cancel'); - - this._dx = 0; - this._dy = 0; - } - - return Clutter.EVENT_STOP; - } -}; -Signals.addSignalMethods(TouchpadWorkspaceSwitchAction.prototype); - -var WorkspaceSwitchAction = GObject.registerClass({ - Signals: { 'activated': { param_types: [Meta.MotionDirection.$gtype] }, - 'motion': { param_types: [GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] }, - 'cancel': { param_types: [] } }, -}, class WorkspaceSwitchAction extends Clutter.SwipeAction { - _init(allowedModes) { - super._init(); - this.set_n_touch_points(4); - this._swept = false; - this._allowedModes = allowedModes; - - global.display.connect('grab-op-begin', () => { - this.cancel(); - }); - } - - vfunc_gesture_prepare(actor) { - this._swept = false; - - if (!super.vfunc_gesture_prepare(actor)) - return false; - - return this._allowedModes & Main.actionMode; - } - - vfunc_gesture_progress(_actor) { - let [x, y] = this.get_motion_coords(0); - let [xPress, yPress] = this.get_press_coords(0); - this.emit('motion', x - xPress, y - yPress); - return true; - } - - vfunc_gesture_cancel(_actor) { - if (!this._swept) - this.emit('cancel'); - } - - vfunc_swipe(actor, direction) { - let [x, y] = this.get_motion_coords(0); - let [xPress, yPress] = this.get_press_coords(0); - if (Math.abs(x - xPress) < WS_MOTION_THRESHOLD && - Math.abs(y - yPress) < WS_MOTION_THRESHOLD) { - this.emit('cancel'); - return; - } - - let dir; - - if (direction & Clutter.SwipeDirection.UP) - dir = Meta.MotionDirection.DOWN; - else if (direction & Clutter.SwipeDirection.DOWN) - dir = Meta.MotionDirection.UP; - else if (direction & Clutter.SwipeDirection.LEFT) - dir = Meta.MotionDirection.RIGHT; - else if (direction & Clutter.SwipeDirection.RIGHT) - dir = Meta.MotionDirection.LEFT; - - this._swept = true; - this.emit('activated', dir); - } -}); - var AppSwitchAction = GObject.registerClass({ Signals: { 'activated': {} }, }, class AppSwitchAction extends Clutter.GestureAction { @@ -1054,10 +912,17 @@ 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(); @@ -1068,18 +933,12 @@ var WindowManager = class { global.workspace_manager.override_workspace_layout(Meta.DisplayCorner.TOPLEFT, false, -1, 1); - let allowedModes = Shell.ActionMode.NORMAL; - let workspaceSwitchAction = new WorkspaceSwitchAction(allowedModes); - workspaceSwitchAction.connect('motion', this._switchWorkspaceMotion.bind(this)); - workspaceSwitchAction.connect('activated', this._actionSwitchWorkspace.bind(this)); - workspaceSwitchAction.connect('cancel', this._switchWorkspaceCancel.bind(this)); - global.stage.add_action(workspaceSwitchAction); - - // This is not a normal Clutter.GestureAction, doesn't need add_action() - let touchpadSwitchAction = new TouchpadWorkspaceSwitchAction(global.stage, allowedModes); - touchpadSwitchAction.connect('motion', this._switchWorkspaceMotion.bind(this)); - touchpadSwitchAction.connect('activated', this._actionSwitchWorkspace.bind(this)); - touchpadSwitchAction.connect('cancel', this._switchWorkspaceCancel.bind(this)); + 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)); @@ -1121,52 +980,6 @@ var WindowManager = class { return this._currentPadOsd; } - _switchWorkspaceMotion(action, xRel, yRel) { - let workspaceManager = global.workspace_manager; - let activeWorkspace = workspaceManager.get_active_workspace(); - - if (!this._switchData) - this._prepareWorkspaceSwitch(activeWorkspace.index(), -1); - - if (yRel < 0 && !this._switchData.surroundings[Meta.MotionDirection.DOWN]) - yRel = 0; - if (yRel > 0 && !this._switchData.surroundings[Meta.MotionDirection.UP]) - yRel = 0; - if (xRel < 0 && !this._switchData.surroundings[Meta.MotionDirection.RIGHT]) - xRel = 0; - if (xRel > 0 && !this._switchData.surroundings[Meta.MotionDirection.LEFT]) - xRel = 0; - - this._switchData.container.set_position(xRel, yRel); - } - - _switchWorkspaceCancel() { - if (!this._switchData || this._switchData.inProgress) - return; - let switchData = this._switchData; - this._switchData = null; - switchData.container.ease({ - x: 0, - y: 0, - duration: WINDOW_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, - onComplete: () => this._finishWorkspaceSwitch(switchData), - }); - } - - _actionSwitchWorkspace(action, direction) { - let workspaceManager = global.workspace_manager; - let activeWorkspace = workspaceManager.get_active_workspace(); - let newWs = activeWorkspace.get_neighbor(direction); - - if (newWs == activeWorkspace) { - this._switchWorkspaceCancel(); - } else { - this._switchData.gestureActivated = true; - this.actionMoveWorkspace(newWs); - } - } - _lookupIndex(windows, metaWindow) { for (let i = 0; i < windows.length; i++) { if (windows[i].metaWindow == metaWindow) @@ -1280,7 +1093,8 @@ var WindowManager = class { } _shouldAnimate() { - return !Main.overview.visible; + return !(Main.overview.visible || + (this._switchData && this._switchData.gestureActivated)); } _shouldAnimateActor(actor, types) { @@ -1860,13 +1674,17 @@ var WindowManager = class { continue; } - let info = { index: ws.index(), - actor: new Clutter.Actor() }; + 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); - let [x, y] = this._getPositionForDirection(dir, curWs, ws); info.actor.set_position(x, y); } @@ -1948,11 +1766,7 @@ var WindowManager = class { return; } - // If we come from a gesture, switchData will already be set, - // and we don't want to overwrite it. - if (!this._switchData) - this._prepareWorkspaceSwitch(from, to, direction); - + this._prepareWorkspaceSwitch(from, to, direction); this._switchData.inProgress = true; let workspaceManager = global.workspace_manager; @@ -1972,7 +1786,7 @@ var WindowManager = class { x: xDest, y: yDest, duration: WINDOW_ANIMATION_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, + mode: Clutter.AnimationMode.EASE_OUT_CUBIC, onComplete: () => this._switchWorkspaceDone(shellwm), }); } @@ -1982,6 +1796,158 @@ var WindowManager = class { 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 !== activeWorkspace) + this.actionMoveWorkspace(newWs); + this._finishWorkspaceSwitch(switchData); + }, + }); + } + + _switchWorkspaceStop() { + this._switchData.container.x = 0; + this._switchData.container.y = 0; + this._finishWorkspaceSwitch(this._switchData); + } + _showTilePreview(shellwm, window, tileRect, monitorIndex) { if (!this._tilePreview) this._tilePreview = new TilePreview();