Skip to content

Commit de8752f

Browse files
authored
MultitaskingView: Allow workspace switch animation to be interrupted (#2161)
1 parent 6b3e685 commit de8752f

File tree

5 files changed

+88
-39
lines changed

5 files changed

+88
-39
lines changed

src/Gestures/GesturePropertyTransition.vala

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,35 +88,55 @@ public class Gala.GesturePropertyTransition : Object {
8888
* be set immediately on {@link GestureTracker.OnEnd} not only once the animation ends to allow for interrupting the animation by starting a new gesture.
8989
* done_callback will only be called if the animation finishes, not if it is interrupted e.g. by starting a new animation for the same property,
9090
* destroying the actor or removing the transition.
91+
*
92+
* @return If a transition is currently in progress for the actor and the property the percentage how far the current value
93+
* is towards the to_value given the final value of the ongoing transition is returned. This is usally the case if a gesture ended but was
94+
* started again before the animation finished so this should be used to set {@link GestureTracker.initial_percentage}. If no transition
95+
* is in progress 0 is returned.
9196
*/
92-
public void start (bool with_gesture, owned DoneCallback? done_callback = null) {
97+
public double start (bool with_gesture, owned DoneCallback? done_callback = null) {
9398
ref ();
9499

95100
this.done_callback = (owned) done_callback;
96101

97102
Value current_value = {};
98103
actor.get_property (property, ref current_value);
99104

100-
actual_from_value = from_value ?? current_value;
105+
Value initial_value;
106+
107+
unowned var old_transition = actor.get_transition (property);
108+
if (old_transition != null) {
109+
initial_value = old_transition.interval.final;
110+
} else {
111+
initial_value = current_value;
112+
}
113+
114+
actual_from_value = from_value ?? initial_value;
101115

102116
if (actual_from_value.type () != current_value.type ()) {
103117
warning ("from_value of type %s is not of the same type as the property %s which is %s. Can't animate.", from_value.type_name (), property, current_value.type_name ());
104118
finish ();
105-
return;
119+
return 0;
106120
}
107121

108122
if (current_value.type () != to_value.type ()) {
109123
warning ("to_value of type %s is not of the same type as the property %s which is %s. Can't animate.", to_value.type_name (), property, current_value.type_name ());
110124
finish ();
111-
return;
125+
return 0;
112126
}
113127

114128
// Pre calculate some things, so we don't have to do it on every update
115129
from_value_float = value_to_float (actual_from_value);
116130
to_value_float = value_to_float (to_value);
117131

118-
GestureTracker.OnBegin on_animation_begin = () => {
119-
actor.set_property (property, actual_from_value);
132+
var current_value_double = (double) value_to_float (current_value);
133+
var initial_value_double = (double) value_to_float (initial_value);
134+
135+
var initial_percentage = ((to_value_float - initial_value_double) - (to_value_float - current_value_double)) / (to_value_float - initial_value_double);
136+
137+
GestureTracker.OnBegin on_animation_begin = (percentage) => {
138+
var animation_value = GestureTracker.animation_value (from_value_float, to_value_float, percentage, false);
139+
actor.set_property (property, value_from_float (animation_value));
120140
};
121141

122142
GestureTracker.OnUpdate on_animation_update = (percentage) => {
@@ -180,6 +200,8 @@ public class Gala.GesturePropertyTransition : Object {
180200
on_animation_end (1, 1, gesture_tracker.min_animation_duration);
181201
}
182202
}
203+
204+
return initial_percentage;
183205
}
184206

185207
private void finish (bool callback = true) {

src/Gestures/GestureTracker.vala

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,10 @@ public class Gala.GestureTracker : Object {
9595
* start receiving updates.
9696
* @param gesture the same gesture as in {@link on_gesture_detected}
9797
* @param timestamp the timestamp of the event that initiated the gesture or {@link Meta.CURRENT_TIME}.
98+
* @return the initial percentage that should already be preapplied. This is useful
99+
* if an animation was still ongoing when the gesture was started.
98100
*/
99-
public signal void on_gesture_handled (Gesture gesture, uint32 timestamp);
101+
public signal double on_gesture_handled (Gesture gesture, uint32 timestamp);
100102

101103
/**
102104
* Emitted right after on_gesture_detected with the initial gesture information.
@@ -135,19 +137,25 @@ public class Gala.GestureTracker : Object {
135137

136138
private Gee.ArrayList<ulong> handlers;
137139

140+
private double applied_percentage;
138141
private double previous_percentage;
139142
private uint64 previous_time;
140-
private double percentage_delta;
143+
private double previous_delta;
141144
private double velocity;
145+
// Used to check whether to cancel. Necessary because on_end is often called
146+
// with the same percentage as the last update so this is the one before the last update.
147+
private double old_previous;
142148

143149
construct {
144150
settings = new GestureSettings ();
145151

146152
handlers = new Gee.ArrayList<ulong> ();
153+
applied_percentage = 0;
147154
previous_percentage = 0;
148155
previous_time = 0;
149-
percentage_delta = 0;
156+
previous_delta = 0;
150157
velocity = 0;
158+
old_previous = 0;
151159
}
152160

153161
public GestureTracker (int min_animation_duration, int max_animation_duration) {
@@ -249,7 +257,7 @@ public class Gala.GestureTracker : Object {
249257
private bool gesture_detected (GestureBackend backend, Gesture gesture, uint32 timestamp) {
250258
if (enabled && on_gesture_detected (gesture)) {
251259
backend.prepare_gesture_handling ();
252-
on_gesture_handled (gesture, timestamp);
260+
applied_percentage = on_gesture_handled (gesture, timestamp);
253261
return true;
254262
}
255263

@@ -258,7 +266,7 @@ public class Gala.GestureTracker : Object {
258266

259267
private void gesture_begin (double percentage, uint64 elapsed_time) {
260268
if (enabled) {
261-
on_begin (percentage);
269+
on_begin (applied_percentage);
262270
}
263271

264272
recognizing = true;
@@ -267,6 +275,7 @@ public class Gala.GestureTracker : Object {
267275
}
268276

269277
private void gesture_update (double percentage, uint64 elapsed_time) {
278+
var updated_delta = previous_delta;
270279
if (elapsed_time != previous_time) {
271280
double distance = percentage - previous_percentage;
272281
double time = (double)(elapsed_time - previous_time);
@@ -275,43 +284,59 @@ public class Gala.GestureTracker : Object {
275284
if (velocity > MAX_VELOCITY) {
276285
velocity = MAX_VELOCITY;
277286
var used_percentage = MAX_VELOCITY * time + previous_percentage;
278-
percentage_delta += percentage - used_percentage;
287+
updated_delta += percentage - used_percentage;
279288
}
280289
}
281290

291+
applied_percentage += calculate_applied_delta (percentage, updated_delta);
292+
282293
if (enabled) {
283-
on_update (applied_percentage (percentage, percentage_delta));
294+
on_update (applied_percentage);
284295
}
285296

297+
old_previous = previous_percentage;
286298
previous_percentage = percentage;
287299
previous_time = elapsed_time;
300+
previous_delta = updated_delta;
288301
}
289302

290303
private void gesture_end (double percentage, uint64 elapsed_time) {
291-
double end_percentage = applied_percentage (percentage, percentage_delta);
292-
int completions = (int) end_percentage;
293-
bool cancel_action = (end_percentage.abs () < SUCCESS_PERCENTAGE_THRESHOLD)
294-
&& ((end_percentage.abs () <= previous_percentage.abs ()) && (velocity < SUCCESS_VELOCITY_THRESHOLD));
295-
int calculated_duration = calculate_end_animation_duration (end_percentage, cancel_action);
304+
applied_percentage += calculate_applied_delta (percentage, previous_delta);
305+
306+
int completions = (int) applied_percentage;
307+
308+
var remaining_percentage = applied_percentage - completions;
309+
310+
bool cancel_action = ((percentage - completions).abs () < SUCCESS_PERCENTAGE_THRESHOLD) && (velocity.abs () < SUCCESS_VELOCITY_THRESHOLD)
311+
|| ((percentage.abs () < old_previous.abs ()) && (velocity.abs () > SUCCESS_VELOCITY_THRESHOLD));
312+
313+
int calculated_duration = calculate_end_animation_duration (remaining_percentage, cancel_action);
296314

297315
if (!cancel_action) {
298-
completions += end_percentage < 0 ? -1 : 1;
316+
completions += applied_percentage < 0 ? -1 : 1;
299317
}
300318

301319
if (enabled) {
302-
on_end (end_percentage, completions, calculated_duration);
320+
on_end (applied_percentage, completions, calculated_duration);
303321
}
304322

305323
disconnect_all_handlers ();
306324
recognizing = false;
325+
applied_percentage = 0;
307326
previous_percentage = 0;
308327
previous_time = 0;
309-
percentage_delta = 0;
328+
previous_delta = 0;
310329
velocity = 0;
330+
old_previous = 0;
311331
}
312332

313-
private static inline double applied_percentage (double percentage, double percentage_delta) {
314-
return percentage - percentage_delta;
333+
/**
334+
* Calculate the delta between the new percentage and the previous one while taking into account
335+
* the velocity delta which makes sure we don't go over the MAX_VELOCITY. The velocity delta we use
336+
* for the calculation shouldn't be confused with this delta.
337+
*/
338+
private inline double calculate_applied_delta (double percentage, double percentage_delta) {
339+
return (percentage - percentage_delta) - (previous_percentage - previous_delta);
315340
}
316341

317342
/**

src/Widgets/MultitaskingView.vala

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,6 @@ namespace Gala {
4747
private Drawing.StyleManager style_manager;
4848

4949
private bool switching_workspace_with_gesture = false;
50-
private bool switching_workspace_in_progress {
51-
get {
52-
return switching_workspace_with_gesture || workspaces.get_transition ("x") != null;
53-
}
54-
}
5550

5651
public MultitaskingView (WindowManagerGala wm) {
5752
Object (wm: wm);
@@ -71,7 +66,7 @@ namespace Gala {
7166
multitasking_gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION);
7267
multitasking_gesture_tracker.enable_touchpad ();
7368
multitasking_gesture_tracker.on_gesture_detected.connect (on_multitasking_gesture_detected);
74-
multitasking_gesture_tracker.on_gesture_handled.connect (() => toggle (true, false));
69+
multitasking_gesture_tracker.on_gesture_handled.connect (on_multitasking_gesture_handled);
7570

7671
workspace_gesture_tracker = new GestureTracker (AnimationDuration.WORKSPACE_SWITCH_MIN, AnimationDuration.WORKSPACE_SWITCH);
7772
workspace_gesture_tracker.enable_touchpad ();
@@ -304,6 +299,11 @@ namespace Gala {
304299
return false;
305300
}
306301

302+
private double on_multitasking_gesture_handled (Gesture gesture, uint32 timestamp) {
303+
toggle (true, false);
304+
return 0;
305+
}
306+
307307
private bool on_workspace_gesture_detected (Gesture gesture) {
308308
if (!opened) {
309309
return false;
@@ -316,11 +316,7 @@ namespace Gala {
316316
return false;
317317
}
318318

319-
private void switch_workspace_with_gesture (Gesture gesture, uint32 timestamp) {
320-
if (switching_workspace_in_progress) {
321-
return;
322-
}
323-
319+
private double switch_workspace_with_gesture (Gesture gesture, uint32 timestamp) {
324320
var direction = workspace_gesture_tracker.settings.get_natural_scroll_direction (gesture);
325321

326322
unowned var manager = display.get_workspace_manager ();
@@ -364,7 +360,7 @@ namespace Gala {
364360
var upper_clamp = (direction == LEFT) ? (active_workspace.index () + 0.1) : (num_workspaces - active_workspace.index () - 0.9);
365361
var lower_clamp = (direction == RIGHT) ? - (active_workspace.index () + 0.1) : - (num_workspaces - active_workspace.index () - 0.9);
366362

367-
new GesturePropertyTransition (workspaces, workspace_gesture_tracker, "x", null, target_x) {
363+
var initial_percentage = new GesturePropertyTransition (workspaces, workspace_gesture_tracker, "x", null, target_x) {
368364
overshoot_lower_clamp = lower_clamp,
369365
overshoot_upper_clamp = upper_clamp
370366
}.start (true);
@@ -381,6 +377,8 @@ namespace Gala {
381377
} else {
382378
workspace_gesture_tracker.connect_handlers (null, null, (owned) on_animation_end);
383379
}
380+
381+
return initial_percentage;
384382
}
385383

386384
/**

src/WindowManager.vala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ namespace Gala {
596596
return switch_workspace_with_gesture || (action == SWITCH_WINDOWS && !window_switcher.opened);
597597
}
598598

599-
private void on_gesture_handled (Gesture gesture, uint32 timestamp) {
599+
private double on_gesture_handled (Gesture gesture, uint32 timestamp) {
600600
var direction = gesture_tracker.settings.get_natural_scroll_direction (gesture);
601601

602602
switch (GestureSettings.get_action (gesture)) {
@@ -620,6 +620,8 @@ namespace Gala {
620620
default:
621621
break;
622622
}
623+
624+
return 0;
623625
}
624626

625627
/**

src/Zoom.vala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public class Gala.Zoom : Object {
3333
gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION);
3434
gesture_tracker.enable_touchpad ();
3535
gesture_tracker.on_gesture_detected.connect (on_gesture_detected);
36-
gesture_tracker.on_gesture_handled.connect ((gesture) => zoom_with_gesture (gesture.direction));
36+
gesture_tracker.on_gesture_handled.connect (on_gesture_handled);
3737

3838
behavior_settings = new GLib.Settings ("io.elementary.desktop.wm.behavior");
3939

@@ -101,9 +101,9 @@ public class Gala.Zoom : Object {
101101
return Clutter.EVENT_STOP;
102102
}
103103

104-
private void zoom_with_gesture (GestureDirection direction) {
104+
private double on_gesture_handled (Gesture gesture, uint32 timestamp) {
105105
var initial_zoom = current_zoom;
106-
var target_zoom = (direction == GestureDirection.IN)
106+
var target_zoom = (gesture.direction == GestureDirection.IN)
107107
? initial_zoom - MAX_ZOOM
108108
: initial_zoom + MAX_ZOOM;
109109

@@ -123,6 +123,8 @@ public class Gala.Zoom : Object {
123123
};
124124

125125
gesture_tracker.connect_handlers (null, (owned) on_animation_update, null);
126+
127+
return 0;
126128
}
127129

128130
private inline Graphene.Point compute_new_pivot_point () {

0 commit comments

Comments
 (0)