Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f98a390
MultitaskingView: Allow workspace switch gesture to be interrupted
leolost2605 Dec 11, 2024
d1ced0d
Merge branch 'main' into leolost/interruptible-workspace-switch-mtv
lenemter Dec 12, 2024
e6af97a
Merge branch 'main' into leolost/interruptible-workspace-switch-mtv
lenemter Dec 19, 2024
da35598
Merge branch 'main' into leolost/interruptible-workspace-switch-mtv
leolost2605 Dec 19, 2024
0debb33
Fix merge
leolost2605 Dec 19, 2024
530fdb3
Fix it
leolost2605 Dec 21, 2024
8d19cc1
Merge branch 'main' into leolost/interruptible-workspace-switch-mtv
leolost2605 Jan 14, 2025
e82572d
Cleanup some logic
leolost2605 Jan 14, 2025
11b70fd
Fix something
leolost2605 Jan 14, 2025
6f682da
Take initial percentage as on_gesture_handled return
leolost2605 Jan 14, 2025
828d9f2
Merge branch 'main' into leolost/interruptible-workspace-switch-mtv
leolost2605 Jan 19, 2025
5e0fcae
Merge branch 'main' into leolost/interruptible-workspace-switch-mtv
leolost2605 Jan 24, 2025
009656f
Fix the math
leolost2605 Jan 25, 2025
ff0f94d
Merge branch 'main' into leolost/interruptible-workspace-switch-mtv
leolost2605 Jan 25, 2025
7aa10b8
Merge branch 'main' into leolost/interruptible-workspace-switch-mtv
lenemter Jan 25, 2025
5d0f066
Merge branch 'main' into leolost/interruptible-workspace-switch-mtv
lenemter Jan 25, 2025
f569de0
Fix 100 percent cancel
leolost2605 Jan 25, 2025
737951a
Merge branch 'leolost/interruptible-workspace-switch-mtv' of github.c…
leolost2605 Jan 25, 2025
af1c2e4
Merge branch 'main' into leolost/interruptible-workspace-switch-mtv
leolost2605 Jan 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 28 additions & 6 deletions src/Gestures/GesturePropertyTransition.vala
Original file line number Diff line number Diff line change
Expand Up @@ -88,35 +88,55 @@ public class Gala.GesturePropertyTransition : Object {
* be set immediately on {@link GestureTracker.OnEnd} not only once the animation ends to allow for interrupting the animation by starting a new gesture.
* 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,
* destroying the actor or removing the transition.
*
* @return If a transition is currently in progress for the actor and the property the percentage how far the current value
* 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
* started again before the animation finished so this should be used to set {@link GestureTracker.initial_percentage}. If no transition
* is in progress 0 is returned.
*/
public void start (bool with_gesture, owned DoneCallback? done_callback = null) {
public double start (bool with_gesture, owned DoneCallback? done_callback = null) {
ref ();

this.done_callback = (owned) done_callback;

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

actual_from_value = from_value ?? current_value;
Value initial_value;

unowned var old_transition = actor.get_transition (property);
if (old_transition != null) {
initial_value = old_transition.interval.final;
} else {
initial_value = current_value;
}

actual_from_value = from_value ?? initial_value;

if (actual_from_value.type () != current_value.type ()) {
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 ());
finish ();
return;
return 0;
}

if (current_value.type () != to_value.type ()) {
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 ());
finish ();
return;
return 0;
}

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

GestureTracker.OnBegin on_animation_begin = () => {
actor.set_property (property, actual_from_value);
var current_value_double = (double) value_to_float (current_value);
var initial_value_double = (double) value_to_float (initial_value);

var initial_percentage = ((to_value_float - initial_value_double) - (to_value_float - current_value_double)) / (to_value_float - initial_value_double);

GestureTracker.OnBegin on_animation_begin = (percentage) => {
var animation_value = GestureTracker.animation_value (from_value_float, to_value_float, percentage, false);
actor.set_property (property, value_from_float (animation_value));
};

GestureTracker.OnUpdate on_animation_update = (percentage) => {
Expand Down Expand Up @@ -180,6 +200,8 @@ public class Gala.GesturePropertyTransition : Object {
on_animation_end (1, 1, gesture_tracker.min_animation_duration);
}
}

return initial_percentage;
}

private void finish (bool callback = true) {
Expand Down
59 changes: 42 additions & 17 deletions src/Gestures/GestureTracker.vala
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,10 @@ public class Gala.GestureTracker : Object {
* start receiving updates.
* @param gesture the same gesture as in {@link on_gesture_detected}
* @param timestamp the timestamp of the event that initiated the gesture or {@link Meta.CURRENT_TIME}.
* @return the initial percentage that should already be preapplied. This is useful
* if an animation was still ongoing when the gesture was started.
*/
public signal void on_gesture_handled (Gesture gesture, uint32 timestamp);
public signal double on_gesture_handled (Gesture gesture, uint32 timestamp);

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

private Gee.ArrayList<ulong> handlers;

private double applied_percentage;
private double previous_percentage;
private uint64 previous_time;
private double percentage_delta;
private double previous_delta;
private double velocity;
// Used to check whether to cancel. Necessary because on_end is often called
// with the same percentage as the last update so this is the one before the last update.
private double old_previous;

construct {
settings = new GestureSettings ();

handlers = new Gee.ArrayList<ulong> ();
applied_percentage = 0;
previous_percentage = 0;
previous_time = 0;
percentage_delta = 0;
previous_delta = 0;
velocity = 0;
old_previous = 0;
}

public GestureTracker (int min_animation_duration, int max_animation_duration) {
Expand Down Expand Up @@ -249,7 +257,7 @@ public class Gala.GestureTracker : Object {
private bool gesture_detected (GestureBackend backend, Gesture gesture, uint32 timestamp) {
if (enabled && on_gesture_detected (gesture)) {
backend.prepare_gesture_handling ();
on_gesture_handled (gesture, timestamp);
applied_percentage = on_gesture_handled (gesture, timestamp);
return true;
}

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

private void gesture_begin (double percentage, uint64 elapsed_time) {
if (enabled) {
on_begin (percentage);
on_begin (applied_percentage);
}

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

private void gesture_update (double percentage, uint64 elapsed_time) {
var updated_delta = previous_delta;
if (elapsed_time != previous_time) {
double distance = percentage - previous_percentage;
double time = (double)(elapsed_time - previous_time);
Expand All @@ -275,43 +284,59 @@ public class Gala.GestureTracker : Object {
if (velocity > MAX_VELOCITY) {
velocity = MAX_VELOCITY;
var used_percentage = MAX_VELOCITY * time + previous_percentage;
percentage_delta += percentage - used_percentage;
updated_delta += percentage - used_percentage;
}
}

applied_percentage += calculate_applied_delta (percentage, updated_delta);

if (enabled) {
on_update (applied_percentage (percentage, percentage_delta));
on_update (applied_percentage);
}

old_previous = previous_percentage;
previous_percentage = percentage;
previous_time = elapsed_time;
previous_delta = updated_delta;
}

private void gesture_end (double percentage, uint64 elapsed_time) {
double end_percentage = applied_percentage (percentage, percentage_delta);
int completions = (int) end_percentage;
bool cancel_action = (end_percentage.abs () < SUCCESS_PERCENTAGE_THRESHOLD)
&& ((end_percentage.abs () <= previous_percentage.abs ()) && (velocity < SUCCESS_VELOCITY_THRESHOLD));
int calculated_duration = calculate_end_animation_duration (end_percentage, cancel_action);
applied_percentage += calculate_applied_delta (percentage, previous_delta);

int completions = (int) applied_percentage;

var remaining_percentage = applied_percentage - completions;

bool cancel_action = ((percentage - completions).abs () < SUCCESS_PERCENTAGE_THRESHOLD) && (velocity.abs () < SUCCESS_VELOCITY_THRESHOLD)
|| ((percentage.abs () < old_previous.abs ()) && (velocity.abs () > SUCCESS_VELOCITY_THRESHOLD));

int calculated_duration = calculate_end_animation_duration (remaining_percentage, cancel_action);

if (!cancel_action) {
completions += end_percentage < 0 ? -1 : 1;
completions += applied_percentage < 0 ? -1 : 1;
}

if (enabled) {
on_end (end_percentage, completions, calculated_duration);
on_end (applied_percentage, completions, calculated_duration);
}

disconnect_all_handlers ();
recognizing = false;
applied_percentage = 0;
previous_percentage = 0;
previous_time = 0;
percentage_delta = 0;
previous_delta = 0;
velocity = 0;
old_previous = 0;
}

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

/**
Expand Down
22 changes: 10 additions & 12 deletions src/Widgets/MultitaskingView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,6 @@ namespace Gala {
private Drawing.StyleManager style_manager;

private bool switching_workspace_with_gesture = false;
private bool switching_workspace_in_progress {
get {
return switching_workspace_with_gesture || workspaces.get_transition ("x") != null;
}
}

public MultitaskingView (WindowManagerGala wm) {
Object (wm: wm);
Expand All @@ -71,7 +66,7 @@ namespace Gala {
multitasking_gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION);
multitasking_gesture_tracker.enable_touchpad ();
multitasking_gesture_tracker.on_gesture_detected.connect (on_multitasking_gesture_detected);
multitasking_gesture_tracker.on_gesture_handled.connect (() => toggle (true, false));
multitasking_gesture_tracker.on_gesture_handled.connect (on_multitasking_gesture_handled);

workspace_gesture_tracker = new GestureTracker (AnimationDuration.WORKSPACE_SWITCH_MIN, AnimationDuration.WORKSPACE_SWITCH);
workspace_gesture_tracker.enable_touchpad ();
Expand Down Expand Up @@ -304,6 +299,11 @@ namespace Gala {
return false;
}

private double on_multitasking_gesture_handled (Gesture gesture, uint32 timestamp) {
toggle (true, false);
return 0;
}

private bool on_workspace_gesture_detected (Gesture gesture) {
if (!opened) {
return false;
Expand All @@ -316,11 +316,7 @@ namespace Gala {
return false;
}

private void switch_workspace_with_gesture (Gesture gesture, uint32 timestamp) {
if (switching_workspace_in_progress) {
return;
}

private double switch_workspace_with_gesture (Gesture gesture, uint32 timestamp) {
var direction = workspace_gesture_tracker.settings.get_natural_scroll_direction (gesture);

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

new GesturePropertyTransition (workspaces, workspace_gesture_tracker, "x", null, target_x) {
var initial_percentage = new GesturePropertyTransition (workspaces, workspace_gesture_tracker, "x", null, target_x) {
overshoot_lower_clamp = lower_clamp,
overshoot_upper_clamp = upper_clamp
}.start (true);
Expand All @@ -381,6 +377,8 @@ namespace Gala {
} else {
workspace_gesture_tracker.connect_handlers (null, null, (owned) on_animation_end);
}

return initial_percentage;
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/WindowManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ namespace Gala {
return switch_workspace_with_gesture || (action == SWITCH_WINDOWS && !window_switcher.opened);
}

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

switch (GestureSettings.get_action (gesture)) {
Expand All @@ -620,6 +620,8 @@ namespace Gala {
default:
break;
}

return 0;
}

/**
Expand Down
8 changes: 5 additions & 3 deletions src/Zoom.vala
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class Gala.Zoom : Object {
gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION);
gesture_tracker.enable_touchpad ();
gesture_tracker.on_gesture_detected.connect (on_gesture_detected);
gesture_tracker.on_gesture_handled.connect ((gesture) => zoom_with_gesture (gesture.direction));
gesture_tracker.on_gesture_handled.connect (on_gesture_handled);

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

Expand Down Expand Up @@ -101,9 +101,9 @@ public class Gala.Zoom : Object {
return Clutter.EVENT_STOP;
}

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

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

gesture_tracker.connect_handlers (null, (owned) on_animation_update, null);

return 0;
}

private inline Graphene.Point compute_new_pivot_point () {
Expand Down
Loading