diff --git a/js/ui/workspacesView.js b/js/ui/workspacesView.js index aa4dd65fc..975dd0760 100644 --- a/js/ui/workspacesView.js +++ b/js/ui/workspacesView.js @@ -4,7 +4,7 @@ const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi; const Main = imports.ui.main; -const WindowManager = imports.ui.windowManager; +const SwipeTracker = imports.ui.swipeTracker; const Workspace = imports.ui.workspace; var WORKSPACE_SWITCH_TIME = 250; @@ -79,7 +79,6 @@ class WorkspacesView extends WorkspacesViewBase { super._init(monitorIndex); this._animating = false; // tweening - this._scrolling = false; // swipe-scrolling this._gestureActive = false; // touch(pad) gestures this._scrollAdjustment = scrollAdjustment; @@ -183,7 +182,7 @@ class WorkspacesView extends WorkspacesViewBase { if (showAnimation) { let easeParams = Object.assign(params, { duration: WORKSPACE_SWITCH_TIME, - mode: Clutter.AnimationMode.EASE_OUT_QUAD, + mode: Clutter.AnimationMode.EASE_OUT_CUBIC, }); // we have to call _updateVisibility() once before the // animation and once afterwards - it does not really @@ -211,7 +210,7 @@ class WorkspacesView extends WorkspacesViewBase { for (let w = 0; w < this._workspaces.length; w++) { let workspace = this._workspaces[w]; - if (this._animating || this._scrolling || this._gestureActive) + if (this._animating || this._gestureActive) workspace.show(); else if (this._inDrag) workspace.visible = Math.abs(w - active) <= 1; @@ -268,18 +267,6 @@ class WorkspacesView extends WorkspacesViewBase { workspaceManager.disconnect(this._reorderWorkspacesId); } - startSwipeScroll() { - this._scrolling = true; - } - - endSwipeScroll() { - this._scrolling = false; - - // Make sure title captions etc are shown as necessary - this._updateWorkspaceActors(true); - this._updateVisibility(); - } - startTouchGesture() { this._gestureActive = true; } @@ -395,12 +382,6 @@ class ExtraWorkspaceView extends WorkspacesViewBase { this._workspace.syncStacking(stackIndices); } - startSwipeScroll() { - } - - endSwipeScroll() { - } - startTouchGesture() { } @@ -438,42 +419,14 @@ class WorkspacesDisplay extends St.Widget { }); Main.overview.addAction(clickAction); this.bind_property('mapped', clickAction, 'enabled', GObject.BindingFlags.SYNC_CREATE); + this._clickAction = clickAction; - let panAction = new Clutter.PanAction({ threshold_trigger_edge: Clutter.GestureTriggerEdge.AFTER }); - panAction.connect('pan', this._onPan.bind(this)); - panAction.connect('gesture-begin', () => { - if (this._workspacesOnlyOnPrimary) { - let event = Clutter.get_current_event(); - if (this._getMonitorIndexForEvent(event) != this._primaryIndex) - return false; - } - - this._startSwipeScroll(); - return true; - }); - panAction.connect('gesture-cancel', () => { - clickAction.release(); - this._endSwipeScroll(); - }); - panAction.connect('gesture-end', () => { - clickAction.release(); - this._endSwipeScroll(); - }); - Main.overview.addAction(panAction); - this.bind_property('mapped', panAction, 'enabled', GObject.BindingFlags.SYNC_CREATE); - - let allowedModes = Shell.ActionMode.OVERVIEW; - let switchGesture = new WindowManager.WorkspaceSwitchAction(allowedModes); - switchGesture.connect('motion', this._onSwitchWorkspaceMotion.bind(this)); - switchGesture.connect('activated', this._onSwitchWorkspaceActivated.bind(this)); - switchGesture.connect('cancel', this._endTouchGesture.bind(this)); - Main.overview.addAction(switchGesture); - this.bind_property('mapped', switchGesture, 'enabled', GObject.BindingFlags.SYNC_CREATE); - - switchGesture = new WindowManager.TouchpadWorkspaceSwitchAction(global.stage, allowedModes); - switchGesture.connect('motion', this._onSwitchWorkspaceMotion.bind(this)); - switchGesture.connect('activated', this._onSwitchWorkspaceActivated.bind(this)); - switchGesture.connect('cancel', this._endTouchGesture.bind(this)); + this._swipeTracker = new SwipeTracker.SwipeTracker( + Main.layoutManager.overviewGroup, Shell.ActionMode.OVERVIEW); + this._swipeTracker.connect('begin', this._switchWorkspaceBegin.bind(this)); + this._swipeTracker.connect('update', this._switchWorkspaceUpdate.bind(this)); + this._swipeTracker.connect('end', this._switchWorkspaceEnd.bind(this)); + this.bind_property('mapped', this._swipeTracker, 'enabled', GObject.BindingFlags.SYNC_CREATE); this._primaryIndex = Main.layoutManager.primaryIndex; this._workspacesViews = []; @@ -491,7 +444,6 @@ class WorkspacesDisplay extends St.Widget { this._fullGeometry = null; - this._scrolling = false; // swipe-scrolling this._gestureActive = false; // touch(pad) gestures this._canScroll = true; // limiting scrolling speed @@ -528,7 +480,7 @@ class WorkspacesDisplay extends St.Widget { } _activeWorkspaceChanged(_wm, _from, _to, _direction) { - if (this._scrolling) + if (this._gestureActive) return; this._scrollToActive(); @@ -542,77 +494,89 @@ class WorkspacesDisplay extends St.Widget { } _updateScrollAdjustment(index) { - if (this._scrolling || this._gestureActive) + if (this._gestureActive) return; this._scrollAdjustment.ease(index, { - mode: Clutter.AnimationMode.EASE_OUT_QUAD, + mode: Clutter.AnimationMode.EASE_OUT_CUBIC, duration: WORKSPACE_SWITCH_TIME, }); } - _onPan(action) { - let [dist_, dx, dy] = action.get_motion_delta(0); + _directionForProgress(progress) { + if (global.workspace_manager.layout_rows === -1) { + return progress > 0 + ? Meta.MotionDirection.DOWN + : Meta.MotionDirection.UP; + } else if (this.text_direction === Clutter.TextDirection.RTL) { + return progress > 0 + ? Meta.MotionDirection.LEFT + : Meta.MotionDirection.RIGHT; + } else { + return progress > 0 + ? Meta.MotionDirection.RIGHT + : Meta.MotionDirection.LEFT; + } + } + + _switchWorkspaceBegin(tracker, monitor) { + if (this._workspacesOnlyOnPrimary && monitor !== this._primaryIndex) + return; + + let workspaceManager = global.workspace_manager; let adjustment = this._scrollAdjustment; - if (global.workspace_manager.layout_rows == -1) - adjustment.value -= (dy / this.height) * adjustment.page_size; - else if (this.text_direction == Clutter.TextDirection.RTL) - adjustment.value += (dx / this.width) * adjustment.page_size; - else - adjustment.value -= (dx / this.width) * adjustment.page_size; - return false; - } + if (this._gestureActive) + adjustment.remove_transition('value'); - _startSwipeScroll() { - for (let i = 0; i < this._workspacesViews.length; i++) - this._workspacesViews[i].startSwipeScroll(); - this._scrolling = true; - } + tracker.orientation = workspaceManager.layout_rows !== -1 + ? Clutter.Orientation.HORIZONTAL + : Clutter.Orientation.VERTICAL; - _endSwipeScroll() { - for (let i = 0; i < this._workspacesViews.length; i++) - this._workspacesViews[i].endSwipeScroll(); - this._scrolling = false; - this._scrollToActive(); - } - - _startTouchGesture() { for (let i = 0; i < this._workspacesViews.length; i++) this._workspacesViews[i].startTouchGesture(); + + let monitors = Main.layoutManager.monitors; + let geometry = monitor === this._primaryIndex + ? this._fullGeometry : monitors[monitor]; + let distance = global.workspace_manager.layout_rows === -1 + ? geometry.height : geometry.width; + + let progress = adjustment.value / adjustment.page_size; + let points = Array.from( + { length: workspaceManager.n_workspaces }, (v, i) => i); + + tracker.confirmSwipe(distance, points, progress, Math.round(progress)); + this._gestureActive = true; } + _switchWorkspaceUpdate(tracker, progress) { + let adjustment = this._scrollAdjustment; + adjustment.value = progress * adjustment.page_size; + } + + _switchWorkspaceEnd(tracker, duration, endProgress) { + this._clickAction.release(); + + let workspaceManager = global.workspace_manager; + let activeWorkspace = workspaceManager.get_active_workspace(); + let newWs = workspaceManager.get_workspace_by_index(endProgress); + + this._scrollAdjustment.ease(endProgress, { + mode: Clutter.AnimationMode.EASE_OUT_CUBIC, + duration, + onComplete: () => { + if (newWs !== activeWorkspace) + newWs.activate(global.get_current_time()); + this._endTouchGesture(); + }, + }); + } + _endTouchGesture() { for (let i = 0; i < this._workspacesViews.length; i++) this._workspacesViews[i].endTouchGesture(); this._gestureActive = false; - this._scrollToActive(); - } - - _onSwitchWorkspaceMotion(action, xRel, yRel) { - // We don't have a way to hook into start of touchpad actions, - // luckily this is safe to call repeatedly. - this._startTouchGesture(); - - let workspaceManager = global.workspace_manager; - let active = workspaceManager.get_active_workspace_index(); - let adjustment = this._scrollAdjustment; - if (workspaceManager.layout_rows == -1) - adjustment.value = (active - yRel / this.height) * adjustment.page_size; - else if (this.text_direction == Clutter.TextDirection.RTL) - adjustment.value = (active + xRel / this.width) * adjustment.page_size; - else - adjustment.value = (active - xRel / this.width) * adjustment.page_size; - } - - _onSwitchWorkspaceActivated(action, direction) { - let workspaceManager = global.workspace_manager; - let activeWorkspace = workspaceManager.get_active_workspace(); - let newWs = activeWorkspace.get_neighbor(direction); - if (newWs != activeWorkspace) - newWs.activate(global.get_current_time()); - - this._endTouchGesture(); } vfunc_navigate_focus(from, direction) { @@ -692,8 +656,6 @@ class WorkspacesDisplay extends St.Widget { else view = new WorkspacesView(i, this._scrollAdjustment); - view.connect('scroll-event', this._onScrollEvent.bind(this)); - // HACK: Avoid spurious allocation changes while updating views view.hide(); @@ -793,6 +755,9 @@ class WorkspacesDisplay extends St.Widget { } _onScrollEvent(actor, event) { + if (this._swipeTracker.canHandleScrollEvent(event)) + return Clutter.EVENT_PROPAGATE; + if (!this.mapped) return Clutter.EVENT_PROPAGATE;