diff --git a/src/Gestures/GesturePropertyTransition.vala b/src/Gestures/GesturePropertyTransition.vala index e2471c0dd..673bfcf96 100644 --- a/src/Gestures/GesturePropertyTransition.vala +++ b/src/Gestures/GesturePropertyTransition.vala @@ -37,11 +37,19 @@ public class Gala.GesturePropertyTransition : Object { public Value to_value { get; construct set; } /** - * If not null this can be used to have an intermediate step before animating back to the origin. - * Therefore using this makes mostly sense if {@link to_value} equals {@link from_value}. - * This is mostly used for the nudge animations when trying to switch workspaces where there isn't one anymore. + * The lower max overshoot. The gesture percentage by which #this animates the property is bounded + * by this property on the lower end. If it is in the form X.YY with Y not 0 the animation will be linear + * until X and then take another 100% to animate until X.YY (instead of YY%). + * Default is 0. */ - public Value? intermediate_value { get; construct; } + public double overshoot_lower_clamp { get; set; default = 0; } + /** + * Same as {@link overshoot_lower_clamp} but for the upper limit. + * If this is less than 1 and the transition is started without a gesture it will animate to + * the {@link to_value} by this percent and then back to the {@link from_value}. + * Default is 1. + */ + public double overshoot_upper_clamp { get; set; default = 1; } /** * This is the from value that's actually used when calculating the animation movement. @@ -49,6 +57,8 @@ public class Gala.GesturePropertyTransition : Object { * value of the target property, when calling {@link start}. */ private Value actual_from_value; + private float from_value_float; // Only valid in the time between start () and finish () + private float to_value_float; // Only valid in the time between start () and finish () private DoneCallback? done_callback; @@ -57,16 +67,14 @@ public class Gala.GesturePropertyTransition : Object { GestureTracker gesture_tracker, string property, Value? from_value, - Value to_value, - Value? intermediate_value = null + Value to_value ) { Object ( actor: actor, gesture_tracker: gesture_tracker, property: property, from_value: from_value, - to_value: to_value, - intermediate_value: intermediate_value + to_value: to_value ); } @@ -100,20 +108,44 @@ public class Gala.GesturePropertyTransition : Object { return; } + // 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); }; GestureTracker.OnUpdate on_animation_update = (percentage) => { - var animation_value = GestureTracker.animation_value (value_to_float (actual_from_value), value_to_float (intermediate_value ?? to_value), percentage); + var lower_clamp_int = (int) overshoot_lower_clamp; + var upper_clamp_int = (int) overshoot_upper_clamp; + + double stretched_percentage = 0; + if (percentage < lower_clamp_int) { + stretched_percentage = (percentage - lower_clamp_int) * - (overshoot_lower_clamp - lower_clamp_int); + } else if (percentage > upper_clamp_int) { + stretched_percentage = (percentage - upper_clamp_int) * (overshoot_upper_clamp - upper_clamp_int); + } + + percentage = percentage.clamp (lower_clamp_int, upper_clamp_int); + + var animation_value = GestureTracker.animation_value (from_value_float, to_value_float, percentage, false); + + if (stretched_percentage != 0) { + animation_value += (float) stretched_percentage * (to_value_float - from_value_float); + } + actor.set_property (property, value_from_float (animation_value)); }; - GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, calculated_duration) => { + GestureTracker.OnEnd on_animation_end = (percentage, completions, calculated_duration) => { + completions = completions.clamp ((int) overshoot_lower_clamp, (int) overshoot_upper_clamp); + var target_value = from_value_float + completions * (to_value_float - from_value_float); + actor.save_easing_state (); actor.set_easing_mode (EASE_OUT_QUAD); actor.set_easing_duration (AnimationsSettings.get_animation_duration (calculated_duration)); - actor.set_property (property, cancel_action ? actual_from_value : to_value); + actor.set_property (property, value_from_float (target_value)); actor.restore_easing_state (); unowned var transition = actor.get_transition (property); @@ -128,21 +160,21 @@ public class Gala.GesturePropertyTransition : Object { gesture_tracker.connect_handlers (on_animation_begin, on_animation_update, on_animation_end); } else { on_animation_begin (0); - if (intermediate_value != null) { + if (overshoot_upper_clamp < 1) { actor.save_easing_state (); actor.set_easing_mode (EASE_OUT_QUAD); actor.set_easing_duration (AnimationsSettings.get_animation_duration (gesture_tracker.min_animation_duration)); - actor.set_property (property, intermediate_value); + actor.set_property (property, value_from_float ((float) overshoot_upper_clamp * (to_value_float - from_value_float) + from_value_float)); actor.restore_easing_state (); unowned var transition = actor.get_transition (property); if (transition == null) { - on_animation_end (1, false, gesture_tracker.min_animation_duration); + on_animation_end (1, 1, gesture_tracker.min_animation_duration); } else { - transition.stopped.connect (() => on_animation_end (1, false, gesture_tracker.min_animation_duration)); + transition.stopped.connect (() => on_animation_end (1, 1, gesture_tracker.min_animation_duration)); } } else { - on_animation_end (1, false, gesture_tracker.min_animation_duration); + on_animation_end (1, 1, gesture_tracker.min_animation_duration); } } } diff --git a/src/Gestures/GestureTracker.vala b/src/Gestures/GestureTracker.vala index aba66f648..e56e9ee66 100644 --- a/src/Gestures/GestureTracker.vala +++ b/src/Gestures/GestureTracker.vala @@ -88,12 +88,16 @@ public class Gala.GestureTracker : Object { /** * @param percentage Value between 0 and 1. + * @param completions The number of times a full cycle of the gesture was completed in this go. Can be + * negative if the gesture was started in one direction but ended in the other. This is used to update + * the UI to the according state. 0 for example means that the UI should go back to the same state + * it was in before the gesture started. */ - public signal void on_end (double percentage, bool cancel_action, int calculated_duration); + public signal void on_end (double percentage, int completions, int calculated_duration); public delegate void OnBegin (double percentage); public delegate void OnUpdate (double percentage); - public delegate void OnEnd (double percentage, bool cancel_action, int calculated_duration); + public delegate void OnEnd (double percentage, int completions, int calculated_duration); /** * Backend used if enable_touchpad is called. @@ -239,12 +243,17 @@ public class Gala.GestureTracker : Object { private void gesture_end (double percentage, uint64 elapsed_time) { double end_percentage = applied_percentage (percentage, percentage_delta); - bool cancel_action = (end_percentage < SUCCESS_PERCENTAGE_THRESHOLD) - && ((end_percentage <= previous_percentage) && (velocity < SUCCESS_VELOCITY_THRESHOLD)); + 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); + if (!cancel_action) { + completions += end_percentage < 0 ? -1 : 1; + } + if (enabled) { - on_end (end_percentage, cancel_action, calculated_duration); + on_end (end_percentage, completions, calculated_duration); } disconnect_all_handlers (); @@ -254,8 +263,8 @@ public class Gala.GestureTracker : Object { velocity = 0; } - private static double applied_percentage (double percentage, double percentage_delta) { - return (percentage - percentage_delta).clamp (0, 1); + private static inline double applied_percentage (double percentage, double percentage_delta) { + return percentage - percentage_delta; } /** diff --git a/src/Gestures/ScrollBackend.vala b/src/Gestures/ScrollBackend.vala index ed933a243..1d4f42546 100644 --- a/src/Gestures/ScrollBackend.vala +++ b/src/Gestures/ScrollBackend.vala @@ -136,10 +136,7 @@ public class Gala.ScrollBackend : Object { double finish_delta = is_horizontal ? FINISH_DELTA_HORIZONTAL : FINISH_DELTA_VERTICAL; bool is_positive = (direction == GestureDirection.RIGHT || direction == GestureDirection.DOWN); - double clamp_low = is_positive ? 0 : -1; - double clamp_high = is_positive ? 1 : 0; - double normalized_delta = (used_delta / finish_delta).clamp (clamp_low, clamp_high).abs (); - return normalized_delta; + return (used_delta / finish_delta) * (is_positive ? 1 : -1); } } diff --git a/src/Widgets/MultitaskingView.vala b/src/Widgets/MultitaskingView.vala index a6bf15a7f..d78db4478 100644 --- a/src/Widgets/MultitaskingView.vala +++ b/src/Widgets/MultitaskingView.vala @@ -340,11 +340,6 @@ namespace Gala { float initial_x = workspaces.x; float target_x = 0; bool is_nudge_animation = !target_workspace_exists; - var scale = display.get_monitor_scale (display.get_primary_monitor ()); - var nudge_gap = InternalUtils.scale_to_int (WindowManagerGala.NUDGE_GAP, scale); - - unowned IconGroup active_icon_group = null; - unowned IconGroup? target_icon_group = null; if (is_nudge_animation) { var workspaces_geometry = InternalUtils.get_workspaces_geometry (display); @@ -355,21 +350,11 @@ namespace Gala { var workspace = workspace_clone.workspace; if (workspace == target_workspace) { - target_icon_group = workspace_clone.icon_group; target_x = -workspace_clone.multitasking_view_x (); - } else if (workspace == active_workspace) { - active_icon_group = workspace_clone.icon_group; } } } - if (!is_nudge_animation && active_icon_group.get_transition ("backdrop-opacity") != null) { - active_icon_group.remove_transition ("backdrop-opacity"); - } - if (!is_nudge_animation && target_icon_group.get_transition ("backdrop-opacity") != null) { - target_icon_group.remove_transition ("backdrop-opacity"); - } - debug ("Starting MultitaskingView switch workspace animation:"); debug ("Active workspace index: %d", active_workspace.index ()); debug ("Target workspace index: %d", target_workspace_index); @@ -379,28 +364,24 @@ namespace Gala { debug ("Target X: %f", target_x); switching_workspace_with_gesture = true; - if (target_workspace != null) { - target_workspace.activate (display.get_current_time ()); - } - if (is_nudge_animation) { - new GesturePropertyTransition (workspaces, workspace_gesture_tracker, "x", null, initial_x, initial_x + nudge_gap * -relative_dir).start (true); - } else { - new GesturePropertyTransition (workspaces, workspace_gesture_tracker, "x", null, target_x).start (true); - new GesturePropertyTransition (active_icon_group, workspace_gesture_tracker, "backdrop-opacity", 1f, 0f).start (true); - new GesturePropertyTransition (target_icon_group, workspace_gesture_tracker, "backdrop-opacity", 0f, 1f).start (true); - } + 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); - GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, calculated_duration) => { + new GesturePropertyTransition (workspaces, workspace_gesture_tracker, "x", null, target_x) { + overshoot_lower_clamp = lower_clamp, + overshoot_upper_clamp = upper_clamp + }.start (true); + + GestureTracker.OnEnd on_animation_end = (percentage, completions, calculated_duration) => { switching_workspace_with_gesture = false; - if (is_nudge_animation || cancel_action) { - active_workspace.activate (display.get_current_time ()); - } + completions = completions.clamp ((int) lower_clamp, (int) upper_clamp); + manager.get_workspace_by_index (active_workspace.index () + completions * relative_dir).activate (display.get_current_time ()); }; if (!AnimationsSettings.get_enable_animations ()) { - on_animation_end (1, false, 0); + on_animation_end (1, 1, 0); } else { workspace_gesture_tracker.connect_handlers (null, null, (owned) on_animation_end); } @@ -429,9 +410,6 @@ namespace Gala { if (workspace == active_workspace) { active_x = dest_x; - workspace_clone.icon_group.backdrop_opacity = 1.0f; - } else { - workspace_clone.icon_group.backdrop_opacity = 0.0f; } workspace_clone.save_easing_state (); @@ -706,8 +684,8 @@ namespace Gala { hide_docks (with_gesture, is_cancel_animation); } - GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => { - var animation_duration = cancel_action ? 0 : ANIMATION_DURATION; + GestureTracker.OnEnd on_animation_end = (percentage, completions) => { + var animation_duration = completions == 0 ? 0 : ANIMATION_DURATION; Timeout.add (animation_duration, () => { if (!opening) { foreach (var container in window_containers_monitors) { @@ -727,7 +705,7 @@ namespace Gala { animating = false; - if (cancel_action) { + if (completions == 0) { toggle (false, true); } @@ -736,7 +714,7 @@ namespace Gala { }; if (!with_gesture) { - on_animation_end (1, false, 0); + on_animation_end (1, 1, 0); } else { multitasking_gesture_tracker.connect_handlers (null, null, (owned) on_animation_end); } @@ -792,8 +770,8 @@ namespace Gala { clone.y = y; }; - GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => { - if (cancel_action) { + GestureTracker.OnEnd on_animation_end = (percentage, completions) => { + if (completions == 0) { return; } @@ -805,7 +783,7 @@ namespace Gala { }; if (!with_gesture || !AnimationsSettings.get_enable_animations ()) { - on_animation_end (1, false, 0); + on_animation_end (1, 1, 0); } else { multitasking_gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); } @@ -823,8 +801,8 @@ namespace Gala { dock.y = y; }; - GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => { - if (cancel_action) { + GestureTracker.OnEnd on_animation_end = (percentage, completions) => { + if (completions == 0) { return; } @@ -836,7 +814,7 @@ namespace Gala { }; if (!with_gesture || !AnimationsSettings.get_enable_animations ()) { - on_animation_end (1, false, 0); + on_animation_end (1, 1, 0); } else { multitasking_gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); } diff --git a/src/Widgets/WorkspaceClone.vala b/src/Widgets/WorkspaceClone.vala index e40562c30..cd47fb55d 100644 --- a/src/Widgets/WorkspaceClone.vala +++ b/src/Widgets/WorkspaceClone.vala @@ -232,6 +232,14 @@ namespace Gala { var listener = WindowListener.get_default (); listener.window_no_longer_on_all_workspaces.connect (add_window); + + parent_set.connect ((old_parent) => { + if (old_parent != null) { + old_parent.notify["x"].disconnect (update_icon_group_opacity); + } + + get_parent ().notify["x"].connect (update_icon_group_opacity); + }); } ~WorkspaceClone () { @@ -250,6 +258,14 @@ namespace Gala { icon_group.destroy (); } + private void update_icon_group_opacity () { + var offset = (multitasking_view_x () + get_parent ().x).abs (); + + var adjusted_width = width - InternalUtils.scale_to_int (X_OFFSET, scale_factor); + + icon_group.backdrop_opacity = (1 - (offset / adjusted_width)).clamp (0, 1); + } + private void reallocate () { icon_group.scale_factor = scale_factor; window_container.monitor_scale = scale_factor; diff --git a/src/WindowManager.vala b/src/WindowManager.vala index 9819565d9..b00c77c6c 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -643,15 +643,21 @@ namespace Gala { prepare_workspace_switch (active_index, active_index, direction); var monitor_scale = display.get_monitor_scale (display.get_primary_monitor ()); + var monitor_geom = display.get_monitor_geometry (display.get_primary_monitor ()); - var nudge_gap = InternalUtils.scale_to_int (NUDGE_GAP, monitor_scale); + var to_value = monitor_geom.width + WORKSPACE_GAP * monitor_scale; if (direction == RIGHT) { - nudge_gap *= -1; + to_value *= -1; } - new GesturePropertyTransition (out_group, gesture_tracker, "x", 0f, 0f, nudge_gap).start (switch_workspace_with_gesture); - new GesturePropertyTransition (wallpaper, gesture_tracker, "x", 0f, 0f, nudge_gap).start (switch_workspace_with_gesture, () => { + new GesturePropertyTransition (out_group, gesture_tracker, "x", 0f, to_value) { + overshoot_upper_clamp = 0.1 + }.start (switch_workspace_with_gesture); + + new GesturePropertyTransition (wallpaper, gesture_tracker, "x", 0f, to_value) { + overshoot_upper_clamp = 0.1 + }.start (switch_workspace_with_gesture, () => { switch_workspace_animation_finished (direction, false, true); animating_switch_workspace = false; }); @@ -2128,9 +2134,9 @@ namespace Gala { wallpaper_clone.x = x_in; }; - GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, duration) => { + GestureTracker.OnEnd on_animation_end = (percentage, completions, duration) => { if (switch_workspace_with_gesture && (percentage == 1 || percentage == 0)) { - switch_workspace_animation_finished (direction, cancel_action); + switch_workspace_animation_finished (direction, completions == 0); return; } @@ -2150,30 +2156,30 @@ namespace Gala { wallpaper.set_easing_mode (animation_mode); wallpaper.set_easing_duration (duration); - out_group.x = cancel_action ? 0.0f : x2; + out_group.x = completions * x2; out_group.restore_easing_state (); - in_group.x = cancel_action ? -x2 : 0.0f; + in_group.x = completions == 0 ? -x2 : 0.0f; in_group.restore_easing_state (); - wallpaper.x = cancel_action ? 0.0f : x2; + wallpaper.x = completions == 0 ? 0.0f : x2; wallpaper.restore_easing_state (); - wallpaper_clone.x = cancel_action ? -x2 : 0.0f; + wallpaper_clone.x = completions == 0 ? -x2 : 0.0f; wallpaper_clone.restore_easing_state (); var transition = in_group.get_transition ("x"); if (transition != null) { transition.completed.connect (() => { - switch_workspace_animation_finished (direction, cancel_action); + switch_workspace_animation_finished (direction, completions == 0); }); } else { - switch_workspace_animation_finished (direction, cancel_action); + switch_workspace_animation_finished (direction, completions == 0); } }; if (!switch_workspace_with_gesture) { - on_animation_end (1, false, AnimationDuration.WORKSPACE_SWITCH_MIN); + on_animation_end (1, 1, AnimationDuration.WORKSPACE_SWITCH_MIN); } else { gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); }