windowManager: Make workspace switching gestures follow motion
When 4fg swipe motion happens, set up early the workspace switching animation with all surrounding workspaces. This allows us to move all content back and forth in any direction. This works on both touchcreens and touchpads. When the gesture is activated, the same data is reused to follow up with the tween animation. The threshold has been also doubled, it was fairly small to start with, and feels better now that workspaces stick to fingers. https://bugzilla.gnome.org/show_bug.cgi?id=788994
This commit is contained in:
parent
e5c95b910d
commit
ebe6f59d7e
@ -3,6 +3,7 @@
|
||||
const Clutter = imports.gi.Clutter;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GObject = imports.gi.GObject;
|
||||
const Lang = imports.lang;
|
||||
const Mainloop = imports.mainloop;
|
||||
const Meta = imports.gi.Meta;
|
||||
@ -34,6 +35,7 @@ var WINDOW_ANIMATION_TIME = 0.25;
|
||||
var DIM_BRIGHTNESS = -0.3;
|
||||
var DIM_TIME = 0.500;
|
||||
var UNDIM_TIME = 0.250;
|
||||
var MOTION_THRESHOLD = 100;
|
||||
|
||||
var ONE_SECOND = 1000; // in ms
|
||||
|
||||
@ -495,13 +497,8 @@ var TouchpadWorkspaceSwitchAction = new Lang.Class({
|
||||
},
|
||||
|
||||
_checkActivated() {
|
||||
const MOTION_THRESHOLD = 50;
|
||||
let allowedModes = Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW;
|
||||
let dir;
|
||||
|
||||
if ((allowedModes & Main.actionMode) == 0)
|
||||
return;
|
||||
|
||||
if (this._dy < -MOTION_THRESHOLD)
|
||||
dir = Meta.MotionDirection.DOWN;
|
||||
else if (this._dy > MOTION_THRESHOLD)
|
||||
@ -511,26 +508,35 @@ var TouchpadWorkspaceSwitchAction = new Lang.Class({
|
||||
else if (this._dx > MOTION_THRESHOLD)
|
||||
dir = Meta.MotionDirection.LEFT;
|
||||
else
|
||||
return;
|
||||
return false;
|
||||
|
||||
this.emit('activated', dir);
|
||||
return true;
|
||||
},
|
||||
|
||||
_handleEvent(actor, event) {
|
||||
let allowedModes = Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW;
|
||||
|
||||
if (event.type() != Clutter.EventType.TOUCHPAD_SWIPE)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (event.get_touchpad_gesture_finger_count() != 4)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if ((allowedModes & Main.actionMode) == 0)
|
||||
return Clutter.EVENT_PROPAGATE;
|
||||
|
||||
if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.UPDATE) {
|
||||
let [dx, dy] = event.get_gesture_motion_delta();
|
||||
|
||||
this._dx += dx;
|
||||
this._dy += dy;
|
||||
// Scale deltas up a bit to make it feel snappier
|
||||
this._dx += dx * 2;
|
||||
this._dy += dy * 2;
|
||||
this.emit('motion', this._dx, this._dy);
|
||||
} else {
|
||||
if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.END)
|
||||
this._checkActivated();
|
||||
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;
|
||||
@ -544,14 +550,14 @@ Signals.addSignalMethods(TouchpadWorkspaceSwitchAction.prototype);
|
||||
var WorkspaceSwitchAction = new Lang.Class({
|
||||
Name: 'WorkspaceSwitchAction',
|
||||
Extends: Clutter.SwipeAction,
|
||||
Signals: { 'activated': { param_types: [Meta.MotionDirection.$gtype] } },
|
||||
Signals: { 'activated': { param_types: [Meta.MotionDirection.$gtype] },
|
||||
'motion': { param_types: [GObject.TYPE_DOUBLE, GObject.TYPE_DOUBLE] },
|
||||
'cancel': { param_types: [] }},
|
||||
|
||||
_init() {
|
||||
const MOTION_THRESHOLD = 50;
|
||||
|
||||
this.parent();
|
||||
this.set_n_touch_points(4);
|
||||
this.set_threshold_trigger_distance(MOTION_THRESHOLD, MOTION_THRESHOLD);
|
||||
this._swept = false;
|
||||
|
||||
global.display.connect('grab-op-begin', () => {
|
||||
this.cancel();
|
||||
@ -561,13 +567,35 @@ var WorkspaceSwitchAction = new Lang.Class({
|
||||
vfunc_gesture_prepare(actor) {
|
||||
let allowedModes = Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW;
|
||||
|
||||
this._swept = false;
|
||||
|
||||
if (!this.parent(actor))
|
||||
return false;
|
||||
|
||||
return (allowedModes & Main.actionMode);
|
||||
},
|
||||
|
||||
vfunc_swept(actor, direction) {
|
||||
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) < MOTION_THRESHOLD &&
|
||||
Math.abs(y - yPress) < MOTION_THRESHOLD) {
|
||||
this.emit('cancel');
|
||||
return;
|
||||
}
|
||||
|
||||
let dir;
|
||||
|
||||
if (direction & Clutter.SwipeDirection.UP)
|
||||
@ -579,6 +607,7 @@ var WorkspaceSwitchAction = new Lang.Class({
|
||||
else if (direction & Clutter.SwipeDirection.RIGHT)
|
||||
dir = Meta.MotionDirection.LEFT;
|
||||
|
||||
this._swept = true;
|
||||
this.emit('activated', dir);
|
||||
}
|
||||
});
|
||||
@ -700,7 +729,14 @@ var WindowManager = new Lang.Class({
|
||||
this._isWorkspacePrepended = false;
|
||||
|
||||
this._switchData = null;
|
||||
this._shellwm.connect('kill-switch-workspace', this._switchWorkspaceDone.bind(this));
|
||||
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);
|
||||
@ -982,12 +1018,16 @@ var WindowManager = new Lang.Class({
|
||||
false, -1, 1);
|
||||
|
||||
let gesture = new WorkspaceSwitchAction();
|
||||
gesture.connect('motion', this._switchWorkspaceMotion.bind(this));
|
||||
gesture.connect('activated', this._actionSwitchWorkspace.bind(this));
|
||||
gesture.connect('cancel', this._switchWorkspaceCancel.bind(this));
|
||||
global.stage.add_action(gesture);
|
||||
|
||||
// This is not a normal Clutter.GestureAction, doesn't need add_action()
|
||||
gesture = new TouchpadWorkspaceSwitchAction(global.stage);
|
||||
gesture.connect('motion', this._switchWorkspaceMotion.bind(this));
|
||||
gesture.connect('activated', this._actionSwitchWorkspace.bind(this));
|
||||
gesture.connect('cancel', this._switchWorkspaceCancel.bind(this));
|
||||
|
||||
gesture = new AppSwitchAction();
|
||||
gesture.connect('activated', this._switchApp.bind(this));
|
||||
@ -1016,11 +1056,52 @@ var WindowManager = new Lang.Class({
|
||||
return this._currentPadOsd.actor;
|
||||
},
|
||||
|
||||
_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;
|
||||
Tweener.addTween(switchData.container,
|
||||
{ x: 0,
|
||||
y: 0,
|
||||
time: WINDOW_ANIMATION_TIME,
|
||||
transition: 'easeOutQuad',
|
||||
onComplete: this._finishWorkspaceSwitch,
|
||||
onCompleteScope: this,
|
||||
onCompleteParams: [switchData],
|
||||
});
|
||||
},
|
||||
|
||||
_actionSwitchWorkspace(action, direction) {
|
||||
let workspaceManager = global.workspace_manager;
|
||||
let activeWorkspace = workspaceManager.get_active_workspace();
|
||||
let newWs = activeWorkspace.get_neighbor(direction);
|
||||
this.actionMoveWorkspace(newWs);
|
||||
|
||||
if (newWs == activeWorkspace) {
|
||||
this._switchWorkspaceCancel();
|
||||
} else {
|
||||
this._switchData.gestureActivated = true;
|
||||
this.actionMoveWorkspace(newWs);
|
||||
}
|
||||
},
|
||||
|
||||
_lookupIndex(windows, metaWindow) {
|
||||
@ -1762,6 +1843,8 @@ var WindowManager = new Lang.Class({
|
||||
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);
|
||||
@ -1857,6 +1940,13 @@ var WindowManager = new Lang.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._switchData.inProgress = true;
|
||||
|
||||
let [xDest, yDest] = this._getPositionForDirection(direction);
|
||||
|
||||
/* @direction is the direction that the "camera" moves, so the
|
||||
@ -1866,8 +1956,6 @@ var WindowManager = new Lang.Class({
|
||||
xDest = -xDest;
|
||||
yDest = -yDest;
|
||||
|
||||
this._prepareWorkspaceSwitch(from, to, direction);
|
||||
|
||||
Tweener.addTween(this._switchData.container,
|
||||
{ x: xDest,
|
||||
y: yDest,
|
||||
|
Loading…
Reference in New Issue
Block a user