diff --git a/src/Gestures/GesturePropertyTransition.vala b/src/Gestures/GesturePropertyTransition.vala index 0281a3964..29b9985fc 100644 --- a/src/Gestures/GesturePropertyTransition.vala +++ b/src/Gestures/GesturePropertyTransition.vala @@ -88,8 +88,13 @@ 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; @@ -97,26 +102,41 @@ public class Gala.GesturePropertyTransition : Object { 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) => { @@ -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) { diff --git a/src/Gestures/GestureTracker.vala b/src/Gestures/GestureTracker.vala index 89b684d0d..cf81ab272 100644 --- a/src/Gestures/GestureTracker.vala +++ b/src/Gestures/GestureTracker.vala @@ -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. @@ -135,19 +137,25 @@ public class Gala.GestureTracker : Object { private Gee.ArrayList 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 (); + 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) { @@ -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; } @@ -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; @@ -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); @@ -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); } /** diff --git a/src/Widgets/MultitaskingView.vala b/src/Widgets/MultitaskingView.vala index c98d83f00..923172552 100644 --- a/src/Widgets/MultitaskingView.vala +++ b/src/Widgets/MultitaskingView.vala @@ -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); @@ -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 (); @@ -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; @@ -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 (); @@ -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); @@ -381,6 +377,8 @@ namespace Gala { } else { workspace_gesture_tracker.connect_handlers (null, null, (owned) on_animation_end); } + + return initial_percentage; } /** diff --git a/src/WindowManager.vala b/src/WindowManager.vala index d74d3c308..7cc428aa9 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -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)) { @@ -620,6 +620,8 @@ namespace Gala { default: break; } + + return 0; } /** diff --git a/src/Zoom.vala b/src/Zoom.vala index fde9a5cd3..84a64de43 100644 --- a/src/Zoom.vala +++ b/src/Zoom.vala @@ -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"); @@ -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; @@ -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 () {