diff --git a/src/Gestures/GesturePropertyTransition.vala b/src/Gestures/GesturePropertyTransition.vala new file mode 100644 index 000000000..e2471c0dd --- /dev/null +++ b/src/Gestures/GesturePropertyTransition.vala @@ -0,0 +1,180 @@ +/* + * Copyright 2024 elementary, Inc. (https://elementary.io) + * SPDX-License-Identifier: GPL-3.0-or-later + * + * Authored by: Leonhard Kargl + */ + +/** + * A class that will animate a property of a {@link Clutter.Actor} one to one with a gesture or + * with easing without a gesture. Respects the enable animation setting. + */ +public class Gala.GesturePropertyTransition : Object { + public delegate void DoneCallback (); + + /** + * The actor whose property will be animated. + */ + public Clutter.Actor actor { get; construct; } + + public GestureTracker gesture_tracker { get; construct; } + + /** + * The property that will be animated. To be properly animated it has to be marked as + * animatable in the Clutter documentation and should be numeric. + */ + public string property { get; construct; } + + /** + * The starting value of the animation or null to use the current value. The value + * has to be of the same type as the property. + */ + public Value? from_value { get; construct set; } + + /** + * The value to animate to. It has to be of the same type as the property. + */ + 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. + */ + public Value? intermediate_value { get; construct; } + + /** + * This is the from value that's actually used when calculating the animation movement. + * If {@link from_value} isn't null this will be the same, otherwise it will be set to the current + * value of the target property, when calling {@link start}. + */ + private Value actual_from_value; + + private DoneCallback? done_callback; + + public GesturePropertyTransition ( + Clutter.Actor actor, + GestureTracker gesture_tracker, + string property, + Value? from_value, + Value to_value, + Value? intermediate_value = null + ) { + Object ( + actor: actor, + gesture_tracker: gesture_tracker, + property: property, + from_value: from_value, + to_value: to_value, + intermediate_value: intermediate_value + ); + } + + /** + * Starts animating the property from {@link from_value} to {@link to_value}. If with_gesture is true + * it will connect to the gesture trackers signals and animate according to the input finishing with an easing + * to the final position. If with_gesture is false it will just ease to the {@link to_value}. + * #this will keep itself alive until the animation finishes so it is safe to immediatly unref it after creation and calling start. + * + * @param done_callback a callback for when the transition finishes. It is guaranteed to be called exactly once. + */ + public void 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; + + 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; + } + + 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; + } + + 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); + actor.set_property (property, value_from_float (animation_value)); + }; + + GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, calculated_duration) => { + 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.restore_easing_state (); + + unowned var transition = actor.get_transition (property); + if (transition == null) { + finish (); + } else { + transition.stopped.connect (finish); + } + }; + + if (with_gesture && AnimationsSettings.get_enable_animations ()) { + gesture_tracker.connect_handlers (on_animation_begin, on_animation_update, on_animation_end); + } else { + on_animation_begin (0); + if (intermediate_value != null) { + 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.restore_easing_state (); + + unowned var transition = actor.get_transition (property); + if (transition == null) { + on_animation_end (1, false, gesture_tracker.min_animation_duration); + } else { + transition.stopped.connect (() => on_animation_end (1, false, gesture_tracker.min_animation_duration)); + } + } else { + on_animation_end (1, false, gesture_tracker.min_animation_duration); + } + } + } + + private void finish () { + if (done_callback != null) { + done_callback (); + } + + unref (); + } + + private float value_to_float (Value val) { + Value float_val = Value (typeof (float)); + if (val.transform (ref float_val)) { + return float_val.get_float (); + } + + critical ("Non numeric property specified"); + return 0; + } + + private Value value_from_float (float f) { + var float_val = Value (typeof (float)); + float_val.set_float (f); + + var val = Value (actual_from_value.type ()); + + if (!float_val.transform (ref val)) { + warning ("Failed to transform float to give type"); + } + + return val; + } +} diff --git a/src/Widgets/MultitaskingView.vala b/src/Widgets/MultitaskingView.vala index 4f58707e0..a6bf15a7f 100644 --- a/src/Widgets/MultitaskingView.vala +++ b/src/Widgets/MultitaskingView.vala @@ -383,58 +383,17 @@ namespace Gala { target_workspace.activate (display.get_current_time ()); } - GestureTracker.OnUpdate on_animation_update = (percentage) => { - var x = GestureTracker.animation_value (initial_x, target_x, percentage, true); - var icon_group_opacity = GestureTracker.animation_value (0.0f, 1.0f, percentage, false); - - if (is_nudge_animation) { - x = x.clamp (initial_x - nudge_gap, initial_x + nudge_gap); - } - - workspaces.x = x; - - if (!is_nudge_animation) { - active_icon_group.backdrop_opacity = 1.0f - icon_group_opacity; - target_icon_group.backdrop_opacity = icon_group_opacity; - } - }; + 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); + } GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, calculated_duration) => { switching_workspace_with_gesture = false; - var duration = is_nudge_animation ? - (uint) (AnimationDuration.NUDGE / 2) : - (uint) calculated_duration; - - workspaces.save_easing_state (); - workspaces.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - workspaces.set_easing_duration (duration); - workspaces.x = (is_nudge_animation || cancel_action) ? initial_x : target_x; - workspaces.restore_easing_state (); - - if (!is_nudge_animation) { - if (AnimationsSettings.get_enable_animations ()) { - var active_transition = new Clutter.PropertyTransition ("backdrop-opacity") { - duration = duration, - remove_on_complete = true - }; - active_transition.set_from_value (active_icon_group.backdrop_opacity); - active_transition.set_to_value (cancel_action ? 1.0f : 0.0f); - active_icon_group.add_transition ("backdrop-opacity", active_transition); - - var target_transition = new Clutter.PropertyTransition ("backdrop-opacity") { - duration = duration, - remove_on_complete = true - }; - target_transition.set_from_value (target_icon_group.backdrop_opacity); - target_transition.set_to_value (cancel_action ? 0.0f : 1.0f); - target_icon_group.add_transition ("backdrop-opacity", target_transition); - } else { - active_icon_group.backdrop_opacity = cancel_action ? 1.0f : 0.0f; - target_icon_group.backdrop_opacity = cancel_action ? 0.0f : 1.0f; - } - } - if (is_nudge_animation || cancel_action) { active_workspace.activate (display.get_current_time ()); } @@ -443,7 +402,7 @@ namespace Gala { if (!AnimationsSettings.get_enable_animations ()) { on_animation_end (1, false, 0); } else { - workspace_gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); + workspace_gesture_tracker.connect_handlers (null, null, (owned) on_animation_end); } } diff --git a/src/Widgets/WindowClone.vala b/src/Widgets/WindowClone.vala index b984dad20..4aa1c5cb5 100644 --- a/src/Widgets/WindowClone.vala +++ b/src/Widgets/WindowClone.vala @@ -207,8 +207,6 @@ public class Gala.WindowClone : Clutter.Actor { set_child_above_sibling (window_icon, clone); set_child_above_sibling (window_title, clone); - transition_to_original_state (false); - check_shadow_requirements (); if (should_fade ()) { @@ -221,7 +219,7 @@ public class Gala.WindowClone : Clutter.Actor { // window was opened, so we stay at our old place. if (was_waiting && slot != null) { opacity = 0; - take_slot (slot); + take_slot (slot, true); opacity = 255; request_reposition (); @@ -268,7 +266,7 @@ public class Gala.WindowClone : Clutter.Actor { * * @param animate Animate the transformation of the placement */ - public void transition_to_original_state (bool animate, bool with_gesture = false, bool is_cancel_animation = false) { + public void transition_to_original_state (bool with_gesture = false, bool is_cancel_animation = false) { var outer_rect = window.get_frame_rect (); unowned var display = window.get_display (); @@ -278,8 +276,6 @@ public class Gala.WindowClone : Clutter.Actor { var offset_x = monitor_geom.x; var offset_y = monitor_geom.y; - var initial_x = x; - var initial_y = y; var initial_width = width; var initial_height = height; @@ -290,22 +286,23 @@ public class Gala.WindowClone : Clutter.Actor { in_slot_animation = true; place_widgets (outer_rect.width, outer_rect.height, initial_scale); + new GesturePropertyTransition (this, gesture_tracker, "x", null, (float) target_x).start (with_gesture); + new GesturePropertyTransition (this, gesture_tracker, "y", null, (float) target_y).start (with_gesture); + new GesturePropertyTransition (this, gesture_tracker, "width", null, (float) outer_rect.width).start (with_gesture); + new GesturePropertyTransition (this, gesture_tracker, "height", null, (float) outer_rect.height).start (with_gesture); + new GesturePropertyTransition (this, gesture_tracker, "shadow-opacity", (uint8) 255, (uint8) 0).start (with_gesture); + new GesturePropertyTransition (window_icon, gesture_tracker, "opacity", 255u, 0u).start (with_gesture, () => { + in_slot_animation = false; + place_widgets (outer_rect.width, outer_rect.height, target_scale); + }); + GestureTracker.OnUpdate on_animation_update = (percentage) => { - var x = GestureTracker.animation_value (initial_x, target_x, percentage); - var y = GestureTracker.animation_value (initial_y, target_y, percentage); var width = GestureTracker.animation_value (initial_width, outer_rect.width, percentage); var height = GestureTracker.animation_value (initial_height, outer_rect.height, percentage); var scale = GestureTracker.animation_value (initial_scale, target_scale, percentage); - var opacity = GestureTracker.animation_value (255f, 0f, percentage); - - set_size (width, height); - set_position (x, y); - window_icon.opacity = (uint) opacity; set_window_icon_position (width, height, scale, false); place_widgets ((int)width, (int)height, scale); - - shadow_opacity = (uint8) opacity; }; GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => { @@ -313,45 +310,16 @@ public class Gala.WindowClone : Clutter.Actor { return; } - var duration = (animate && AnimationsSettings.get_enable_animations ()) ? MultitaskingView.ANIMATION_DURATION : 0; - - save_easing_state (); - set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - set_easing_duration (duration); - - set_position (target_x, target_y); - set_size (outer_rect.width, outer_rect.height); - - if (should_fade ()) { - opacity = 0; - } - - restore_easing_state (); - - if (animate) { - toggle_shadow (false); - } + toggle_shadow (false); window_icon.save_easing_state (); window_icon.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - window_icon.set_easing_duration (duration); - window_icon.opacity = 0; + window_icon.set_easing_duration (AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION)); set_window_icon_position (outer_rect.width, outer_rect.height, target_scale); window_icon.restore_easing_state (); - - var transition = window_icon.get_transition ("opacity"); - if (transition != null) { - transition.completed.connect (() => { - in_slot_animation = false; - place_widgets (outer_rect.width, outer_rect.height, target_scale); - }); - } else { - in_slot_animation = false; - place_widgets (outer_rect.width, outer_rect.height, target_scale); - } }; - if (!animate || gesture_tracker == null || !with_gesture || !AnimationsSettings.get_enable_animations ()) { + if (gesture_tracker == null || !with_gesture || !AnimationsSettings.get_enable_animations ()) { on_animation_end (1, false, 0); } else { gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); @@ -362,36 +330,44 @@ public class Gala.WindowClone : Clutter.Actor { * Animate the window to the given slot */ #if HAS_MUTTER45 - public void take_slot (Mtk.Rectangle rect, bool with_gesture = false, bool is_cancel_animation = false) { + public void take_slot (Mtk.Rectangle rect, bool from_window_position, bool with_gesture = false, bool is_cancel_animation = false) { #else - public void take_slot (Meta.Rectangle rect, bool with_gesture = false, bool is_cancel_animation = false) { + public void take_slot (Meta.Rectangle rect, bool from_window_position, bool with_gesture = false, bool is_cancel_animation = false) { #endif slot = rect; - var initial_x = x; - var initial_y = y; - var initial_width = width; - var initial_height = height; + in_slot_animation = true; active = false; - var scale = display.get_monitor_scale (display.get_monitor_index_for_rect (rect)); - in_slot_animation = true; + var outer_rect = window.get_frame_rect (); + + float initial_width = from_window_position ? outer_rect.width : width; + float initial_height = from_window_position ? outer_rect.height : height; + + var monitor_geom = display.get_monitor_geometry (window.get_monitor ()); + float intial_x = from_window_position ? outer_rect.x - monitor_geom.x : x; + float intial_y = from_window_position ? outer_rect.y - monitor_geom.y : y; + + var scale = display.get_monitor_scale (display.get_monitor_index_for_rect (rect)); place_widgets (rect.width, rect.height, scale); + set_window_icon_position (initial_width, initial_height, scale); + + new GesturePropertyTransition (this, gesture_tracker, "x", intial_x, (float) rect.x).start (with_gesture); + new GesturePropertyTransition (this, gesture_tracker, "y", intial_y, (float) rect.y).start (with_gesture); + new GesturePropertyTransition (this, gesture_tracker, "width", (float) initial_width, (float) rect.width).start (with_gesture); + new GesturePropertyTransition (this, gesture_tracker, "height", (float) initial_height, (float) rect.height).start (with_gesture); + new GesturePropertyTransition (this, gesture_tracker, "shadow-opacity", (uint8) 0, (uint8) 255).start (with_gesture); + new GesturePropertyTransition (window_icon, gesture_tracker, "opacity", 0u, 255u).start (with_gesture, () => { + in_slot_animation = false; + place_widgets (rect.width, rect.height, scale); + }); + GestureTracker.OnUpdate on_animation_update = (percentage) => { - var x = GestureTracker.animation_value (initial_x, rect.x, percentage); - var y = GestureTracker.animation_value (initial_y, rect.y, percentage); var width = GestureTracker.animation_value (initial_width, rect.width, percentage); var height = GestureTracker.animation_value (initial_height, rect.height, percentage); - var opacity = GestureTracker.animation_value (0f, 255f, percentage); - - set_size (width, height); - set_position (x, y); - window_icon.opacity = (uint) opacity; set_window_icon_position (width, height, scale, false); - - shadow_opacity = (uint8) opacity; }; GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => { @@ -401,34 +377,13 @@ public class Gala.WindowClone : Clutter.Actor { var duration = AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION); - save_easing_state (); - set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - set_easing_duration (duration); - - set_size (rect.width, rect.height); - set_position (rect.x, rect.y); - opacity = 255; - restore_easing_state (); - window_icon.save_easing_state (); window_icon.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); window_icon.set_easing_duration (duration); - window_icon.opacity = 255; set_window_icon_position (rect.width, rect.height, scale); window_icon.restore_easing_state (); toggle_shadow (true); - - var transition = window_icon.get_transition ("opacity"); - if (transition != null) { - transition.completed.connect (() => { - in_slot_animation = false; - place_widgets (rect.width, rect.height, scale); - }); - } else { - in_slot_animation = false; - place_widgets (rect.width, rect.height, scale); - } }; if (gesture_tracker == null || !with_gesture || !AnimationsSettings.get_enable_animations ()) { diff --git a/src/Widgets/WindowCloneContainer.vala b/src/Widgets/WindowCloneContainer.vala index f39571a33..d6ce80455 100644 --- a/src/Widgets/WindowCloneContainer.vala +++ b/src/Widgets/WindowCloneContainer.vala @@ -167,7 +167,7 @@ namespace Gala { * Recalculate the tiling positions of the windows and animate them to * the resulting spots. */ - public void reflow (bool with_gesture = false, bool is_cancel_animation = false) { + public void reflow (bool with_gesture = false, bool is_cancel_animation = false, bool opening = false) { if (!opened) { return; } @@ -206,7 +206,7 @@ namespace Gala { foreach (var tilable in window_positions) { unowned var clone = (WindowClone) tilable.id; - clone.take_slot (tilable.rect, with_gesture, is_cancel_animation); + clone.take_slot (tilable.rect, opening, with_gesture, is_cancel_animation); } } @@ -393,15 +393,7 @@ namespace Gala { current_window = null; } - // make sure our windows are where they belong in case they were moved - // while were closed. - if (gesture_tracker == null || !is_cancel_animation) { - foreach (var window in get_children ()) { - ((WindowClone) window).transition_to_original_state (false, with_gesture, is_cancel_animation); - } - } - - reflow (with_gesture, is_cancel_animation); + reflow (with_gesture, is_cancel_animation, true); } /** @@ -416,7 +408,7 @@ namespace Gala { opened = false; foreach (var window in get_children ()) { - ((WindowClone) window).transition_to_original_state (true, with_gesture, is_cancel_animation); + ((WindowClone) window).transition_to_original_state (with_gesture, is_cancel_animation); } } } diff --git a/src/Widgets/WorkspaceClone.vala b/src/Widgets/WorkspaceClone.vala index e72e081ee..0c4721ab3 100644 --- a/src/Widgets/WorkspaceClone.vala +++ b/src/Widgets/WorkspaceClone.vala @@ -364,46 +364,13 @@ namespace Gala { var scale = (float)(monitor.height - InternalUtils.scale_to_int (TOP_OFFSET + BOTTOM_OFFSET, scale_factor)) / monitor.height; var pivot_y = InternalUtils.scale_to_int (TOP_OFFSET, scale_factor) / (monitor.height - monitor.height * scale); + background.set_pivot_point (0.5f, pivot_y); update_size (monitor); - GestureTracker.OnBegin on_animation_begin = () => { - x = initial_x; - background.set_pivot_point (0.5f, pivot_y); - }; - - GestureTracker.OnUpdate on_animation_update = (percentage) => { - var x = GestureTracker.animation_value (initial_x, target_x, percentage); - set_x (x); - - var update_scale = (double) GestureTracker.animation_value (1.0f, (float)scale, percentage); - background.set_scale (update_scale, update_scale); - }; - - GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => { - if (cancel_action) { - return; - } - - save_easing_state (); - set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - set_easing_duration (AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION)); - set_x (target_x); - restore_easing_state (); - - background.save_easing_state (); - background.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - background.set_easing_duration (AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION)); - background.set_scale (scale, scale); - background.restore_easing_state (); - }; - - if (!with_gesture || !AnimationsSettings.get_enable_animations ()) { - on_animation_begin (0); - on_animation_end (1, false, 0); - } else { - gesture_tracker.connect_handlers ((owned) on_animation_begin, (owned) on_animation_update, (owned)on_animation_end); - } + new GesturePropertyTransition (this, gesture_tracker, "x", initial_x, target_x).start (with_gesture); + new GesturePropertyTransition (background, gesture_tracker, "scale-x", 1.0d, (double) scale).start (with_gesture); + new GesturePropertyTransition (background, gesture_tracker, "scale-y", 1.0d, (double) scale).start (with_gesture); #if HAS_MUTTER45 Mtk.Rectangle area = { @@ -444,41 +411,9 @@ namespace Gala { var initial_x = is_cancel_animation ? x : multitasking_view_x (); var target_x = multitasking_view_x () + current_x_overlap (); - double initial_scale_x, initial_scale_y; - background.get_scale (out initial_scale_x, out initial_scale_y); - - GestureTracker.OnUpdate on_animation_update = (percentage) => { - var x = GestureTracker.animation_value (initial_x, target_x, percentage); - set_x (x); - - double scale_x = (double) GestureTracker.animation_value ((float) initial_scale_x, 1.0f, percentage); - double scale_y = (double) GestureTracker.animation_value ((float) initial_scale_y, 1.0f, percentage); - background.set_scale (scale_x, scale_y); - }; - - GestureTracker.OnEnd on_animation_end = (percentage, cancel_action) => { - if (cancel_action) { - return; - } - - save_easing_state (); - set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - set_easing_duration (AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION)); - set_x (target_x); - restore_easing_state (); - - background.save_easing_state (); - background.set_easing_mode (Clutter.AnimationMode.EASE_OUT_QUAD); - background.set_easing_duration (AnimationsSettings.get_animation_duration (MultitaskingView.ANIMATION_DURATION)); - background.set_scale (1, 1); - background.restore_easing_state (); - }; - - if (!with_gesture || !AnimationsSettings.get_enable_animations ()) { - on_animation_end (1, false, 0); - } else { - gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); - } + new GesturePropertyTransition (this, gesture_tracker, "x", initial_x, target_x).start (with_gesture); + new GesturePropertyTransition (background, gesture_tracker, "scale-x", null, 1.0d).start (with_gesture); + new GesturePropertyTransition (background, gesture_tracker, "scale-y", null, 1.0d).start (with_gesture); window_container.close (with_gesture, is_cancel_animation); } diff --git a/src/WindowManager.vala b/src/WindowManager.vala index 3b2d08ef3..37d69c639 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -662,81 +662,16 @@ namespace Gala { var monitor_scale = display.get_monitor_scale (display.get_primary_monitor ()); var nudge_gap = InternalUtils.scale_to_int (NUDGE_GAP, monitor_scale); - float dest = 0; - if (!switch_workspace_with_gesture) { - dest = nudge_gap; - } else { - var workspaces_geometry = InternalUtils.get_workspaces_geometry (display); - dest = workspaces_geometry.width; - } - if (direction == Meta.MotionDirection.RIGHT) { - dest *= -1; + if (direction == RIGHT) { + nudge_gap *= -1; } - var animation_mode = Clutter.AnimationMode.EASE_OUT_CUBIC; - - GestureTracker.OnUpdate on_animation_update = (percentage) => { - var x_out = GestureTracker.animation_value (0.0f, dest, percentage, true).clamp (-nudge_gap, nudge_gap); - out_group.x = x_out; - wallpaper.x = x_out; - }; - - GestureTracker.OnEnd on_animation_end = (percentage, cancel_action, duration) => { - out_group.save_easing_state (); - out_group.set_easing_mode (animation_mode); - out_group.set_easing_duration (AnimationDuration.NUDGE / 2); - - wallpaper.save_easing_state (); - wallpaper.set_easing_mode (animation_mode); - wallpaper.set_easing_duration (AnimationDuration.NUDGE / 2); - - out_group.x = 0.0f; - out_group.restore_easing_state (); - - wallpaper.x = 0.0f; - wallpaper.restore_easing_state (); - - unowned var transition = out_group.get_transition ("x"); - transition.completed.connect (() => { - switch_workspace_animation_finished (direction, false, true); - animating_switch_workspace = false; - }); - }; - - if (!switch_workspace_with_gesture) { - double[] keyframes = { 0.5 }; - GLib.Value[] x = { dest }; - - var out_group_nudge = new Clutter.KeyframeTransition ("translation-x") { - duration = AnimationDuration.NUDGE, - remove_on_complete = true, - progress_mode = Clutter.AnimationMode.EASE_IN_QUAD - }; - out_group_nudge.set_from_value (0.0f); - out_group_nudge.set_to_value (0.0f); - out_group_nudge.set_key_frames (keyframes); - out_group_nudge.set_values (x); - out_group.add_transition ("nudge", out_group_nudge); - - var wallpaper_nudge = new Clutter.KeyframeTransition ("translation-x") { - duration = AnimationDuration.NUDGE, - remove_on_complete = true, - progress_mode = Clutter.AnimationMode.EASE_IN_QUAD - }; - wallpaper_nudge.set_from_value (0.0f); - wallpaper_nudge.set_to_value (0.0f); - wallpaper_nudge.set_key_frames (keyframes); - wallpaper_nudge.set_values (x); - wallpaper.add_transition ("nudge", wallpaper_nudge); - - wallpaper_nudge.completed.connect (() => { - switch_workspace_animation_finished (direction, false, true); - animating_switch_workspace = false; - }); - } else { - gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); - } + 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, () => { + switch_workspace_animation_finished (direction, false, true); + animating_switch_workspace = false; + }); } private void update_input_area () { @@ -2261,6 +2196,11 @@ namespace Gala { private void switch_workspace_animation_finished (Meta.MotionDirection animation_direction, bool cancel_action, bool is_nudge_animation = false) { + if (!animating_switch_workspace) { + return; + } + animating_switch_workspace = cancel_action; + if (switch_workspace_window_created_id > 0) { disconnect (switch_workspace_window_created_id); switch_workspace_window_created_id = 0; @@ -2269,7 +2209,6 @@ namespace Gala { if (!is_nudge_animation) { switch_workspace_completed (); } - animating_switch_workspace = cancel_action; if (cancel_action) { var cancel_direction = (animation_direction == Meta.MotionDirection.LEFT) @@ -2352,7 +2291,8 @@ namespace Gala { } public override void kill_switch_workspace () { - end_switch_workspace (); + // We don't care about animation direction, we don't want to cancel it, make it nudge so that it doesn't call switch_workspace_completed () + switch_workspace_animation_finished (LEFT, false, true); } public override void locate_pointer () { diff --git a/src/meson.build b/src/meson.build index 7e6488fd1..70d9d7b97 100644 --- a/src/meson.build +++ b/src/meson.build @@ -34,6 +34,7 @@ gala_bin_sources = files( 'ColorFilters/FilterManager.vala', 'ColorFilters/MonochromeEffect.vala', 'Gestures/Gesture.vala', + 'Gestures/GesturePropertyTransition.vala', 'Gestures/GestureSettings.vala', 'Gestures/GestureTracker.vala', 'Gestures/ScrollBackend.vala',