From 3a0c856d7a8cbf5e581328aa499f596398d8804f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Exp=C3=B3sito?= Date: Tue, 23 Feb 2021 01:34:43 +0100 Subject: [PATCH] Multitasking view 1:1 switch desktop animation with scroll events (#1051) --- lib/Constants.vala | 2 + src/GestureAnimationDirector.vala | 2 +- src/Gestures/Gesture.vala | 40 +++++ src/Gestures/GestureSettings.vala | 50 ++++++ src/Gestures/GestureTracker.vala | 268 ++++++++++++++++++++++++++++++ src/Gestures/ScrollBackend.vala | 126 ++++++++++++++ src/Widgets/MultitaskingView.vala | 125 ++++++++++---- src/WindowManager.vala | 13 +- src/meson.build | 4 + 9 files changed, 590 insertions(+), 40 deletions(-) create mode 100644 src/Gestures/Gesture.vala create mode 100644 src/Gestures/GestureSettings.vala create mode 100644 src/Gestures/GestureTracker.vala create mode 100644 src/Gestures/ScrollBackend.vala diff --git a/lib/Constants.vala b/lib/Constants.vala index 92de3a7a..37868e28 100644 --- a/lib/Constants.vala +++ b/lib/Constants.vala @@ -31,5 +31,7 @@ namespace Gala { // Duration of the workspace switch animation WORKSPACE_SWITCH_MIN = 300, WORKSPACE_SWITCH = 400, + // Duration of the nudge animation when trying to switch to at the end of the workspace list + NUDGE = 360, } } diff --git a/src/GestureAnimationDirector.vala b/src/GestureAnimationDirector.vala index 8c8d0bd7..8c392b6b 100644 --- a/src/GestureAnimationDirector.vala +++ b/src/GestureAnimationDirector.vala @@ -67,7 +67,7 @@ public class Gala.GestureAnimationDirector : Object { velocity = 0; } - public GestureAnimationDirector(int min_animation_duration, int max_animation_duration) { + public GestureAnimationDirector (int min_animation_duration, int max_animation_duration) { Object (min_animation_duration: min_animation_duration, max_animation_duration: max_animation_duration); } diff --git a/src/Gestures/Gesture.vala b/src/Gestures/Gesture.vala new file mode 100644 index 00000000..6cdd0c2b --- /dev/null +++ b/src/Gestures/Gesture.vala @@ -0,0 +1,40 @@ +/* + * Copyright 2021 elementary, Inc (https://elementary.io) + * 2021 José Expósito + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +namespace Gala { + public enum GestureDirection { + UNKNOWN = 0, + + // GestureType.SWIPE and GestureType.SCROLL + UP = 1, + DOWN = 2, + LEFT = 3, + RIGHT = 4, + + // GestureType.PINCH + IN = 5, + OUT = 6, + } + + public class Gesture { + public Gdk.EventType type; + public GestureDirection direction; + public int fingers; + public Gdk.InputSource performed_on_device_type; + } +} diff --git a/src/Gestures/GestureSettings.vala b/src/Gestures/GestureSettings.vala new file mode 100644 index 00000000..3ee5bc01 --- /dev/null +++ b/src/Gestures/GestureSettings.vala @@ -0,0 +1,50 @@ +/* + * Copyright 2021 elementary, Inc (https://elementary.io) + * 2021 José Expósito + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * Utility class to access the gesture settings. Easily accessible through GestureTracker.settings. + */ +public class Gala.GestureSettings : Object { + private static GLib.Settings gala_settings; + private static GLib.Settings touchpad_settings; + + public const string MULTITASKING_ENABLED = "multitasking-gesture-enabled"; + public const string MULTITASKING_FINGERS = "multitasking-gesture-fingers"; + + public const string WORKSPACE_ENABLED = "workspaces-gesture-enabled"; + public const string WORKSPACE_FINGERS = "workspaces-gesture-fingers"; + + static construct { + gala_settings = new GLib.Settings ("io.elementary.desktop.wm.gestures"); + touchpad_settings = new GLib.Settings ("org.gnome.desktop.peripherals.touchpad"); + } + + public bool is_natural_scroll_enabled (Gdk.InputSource device_type) { + return (device_type == Gdk.InputSource.TOUCHSCREEN) + ? true + : touchpad_settings.get_boolean ("natural-scroll"); + } + + public bool is_gesture_enabled (string setting_id) { + return gala_settings.get_boolean (setting_id); + } + + public int gesture_fingers (string setting_id) { + return gala_settings.get_int (setting_id); + } +} diff --git a/src/Gestures/GestureTracker.vala b/src/Gestures/GestureTracker.vala new file mode 100644 index 00000000..d227a5a0 --- /dev/null +++ b/src/Gestures/GestureTracker.vala @@ -0,0 +1,268 @@ +/* + * Copyright 2021 elementary, Inc (https://elementary.io) + * 2021 José Expósito + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * Allow to use multi-touch gestures from different sources (backends). + * Usage: + * - Create a new instance of the class + * - Use the enable_* methods to enable different backends + * - Connect the on_gesture_detected to your code + * - When on_gesture_detected is emitted, if you want to handle the gesture, call connect_handlers + * to start receiving events + * - on_begin will be emitted once right after on_gesture_detected + * - on_update will be emitted 0 or more times + * - on_end will be emitted once when the gesture end + * - When on_end is emitted, the handler connected with connect_handlers will be automatically + * disconnected and you will only receive on_gesture_detected signals + * - The enabled flag is usually disabled on_end and re-enabled once the end animation finish. In + * this way, new gestures are not received while animating + */ +public class Gala.GestureTracker : Object { + /** + * Percentage of the animation to be completed to apply the action. + */ + private const double SUCCESS_PERCENTAGE_THRESHOLD = 0.2; + + /** + * When a gesture ends with a velocity greater than this constant, the action is not cancelled, + * even if the animation threshold has not been reached. + */ + private const double SUCCESS_VELOCITY_THRESHOLD = 0.003; + + /** + * When a gesture ends with less velocity that this constant, this velocity is used instead. + */ + private const double ANIMATION_BASE_VELOCITY = 0.002; + + /** + * Maximum velocity allowed on gesture update. + */ + private const double MAX_VELOCITY = 0.01; + + /** + * Multiplier used to match libhandy's animation duration. + */ + private const int DURATION_MULTIPLIER = 3; + + public GestureSettings settings { get; construct; } + public int min_animation_duration { get; construct; } + public int max_animation_duration { get; construct; } + + /** + * Property to control when event signals are emitted or not. + */ + public bool enabled { get; set; default = true; } + + /** + * Emitted when a new gesture is detected. + * If the receiving code needs to handle this gesture, it should call to connect_handlers to + * start receiving updates. + * @param gesture Information about the gesture. + */ + public signal void on_gesture_detected (Gesture gesture); + + /** + * Emitted right after on_gesture_detected with the initial gesture information. + * @param percentage Value between 0 and 1. + */ + public signal void on_begin (double percentage); + + /** + * Called every time the percentage changes. + * @param percentage Value between 0 and 1. + */ + public signal void on_update (double percentage); + + /** + * @param percentage Value between 0 and 1. + * @param cancel_action + * @param calculated_duration + */ + public signal void on_end (double percentage, bool cancel_action, 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); + + /** + * Scroll backend used if enable_scroll is called. + */ + private ScrollBackend scroll_backend; + + private Gee.ArrayList handlers; + + private double previous_percentage; + private uint64 previous_time; + private double percentage_delta; + private double velocity; + + construct { + settings = new GestureSettings (); + + handlers = new Gee.ArrayList (); + previous_percentage = 0; + previous_time = 0; + percentage_delta = 0; + velocity = 0; + } + + public GestureTracker (int min_animation_duration, int max_animation_duration) { + Object (min_animation_duration: min_animation_duration, max_animation_duration: max_animation_duration); + } + + /** + * Allow to receive scroll gestures. + * @param actor Clutter actor that will receive the scroll events. + * @param orientation If we are interested in the horizontal or vertical axis. + */ + public void enable_scroll (Clutter.Actor actor, Clutter.Orientation orientation) { + scroll_backend = new ScrollBackend (actor, orientation); + scroll_backend.on_gesture_detected.connect (gesture_detected); + scroll_backend.on_begin.connect (gesture_begin); + scroll_backend.on_update.connect (gesture_update); + scroll_backend.on_end.connect (gesture_end); + } + + public void connect_handlers (owned OnBegin? on_begin_handler, owned OnUpdate? on_update_handler, owned OnEnd? on_end_handler) { + if (on_begin_handler != null) { + ulong handler_id = on_begin.connect ((percentage) => on_begin_handler (percentage)); + handlers.add (handler_id); + } + + if (on_update_handler != null) { + ulong handler_id = on_update.connect ((percentage) => on_update_handler (percentage)); + handlers.add (handler_id); + } + + if (on_end_handler != null) { + ulong handler_id = on_end.connect ((percentage, cancel_action, duration) => on_end_handler (percentage, cancel_action, duration)); + handlers.add (handler_id); + } + } + + private void disconnect_all_handlers () { + foreach (var handler in handlers) { + disconnect (handler); + } + + handlers.clear (); + } + + /** + * Utility method to calculate the current animation value based on the percentage of the + * gesture performed. + * Animations are always linear, as they are 1:1 to the user's movement. + * @param initial_value Animation start value. + * @param target_value Animation end value. + * @param percentage Current animation percentage. + * @param rounded If the returned value should be rounded to match physical pixels. + * Default to false because some animations, like for example scaling an actor, use intermediate + * values not divisible by physical pixels. + * @returns The linear animation value at the specified percentage. + */ + public static float animation_value (float initial_value, float target_value, double percentage, bool rounded = false) { + float value = ((target_value - initial_value) * (float) percentage) + initial_value; + + if (rounded) { + var scale_factor = InternalUtils.get_ui_scaling_factor (); + value = (float) Math.round (value * scale_factor) / scale_factor; + } + + return value; + } + + private void gesture_detected (Gesture gesture) { + if (!enabled) { + return; + } + + on_gesture_detected (gesture); + } + + private void gesture_begin (double percentage, uint64 elapsed_time) { + if (!enabled) { + return; + } + + on_begin (percentage); + + previous_percentage = percentage; + previous_time = elapsed_time; + } + + private void gesture_update (double percentage, uint64 elapsed_time) { + if (!enabled) { + return; + } + + if (elapsed_time != previous_time) { + double distance = percentage - previous_percentage; + double time = (double)(elapsed_time - previous_time); + velocity = (distance / time); + + if (velocity > MAX_VELOCITY) { + velocity = MAX_VELOCITY; + var used_percentage = MAX_VELOCITY * time + previous_percentage; + percentage_delta += percentage - used_percentage; + } + } + + on_update (applied_percentage (percentage, percentage_delta)); + + previous_percentage = percentage; + previous_time = elapsed_time; + } + + private void gesture_end (double percentage, uint64 elapsed_time) { + if (!enabled) { + return; + } + + 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 calculated_duration = calculate_end_animation_duration (end_percentage, cancel_action); + + on_end (end_percentage, cancel_action, calculated_duration); + disconnect_all_handlers (); + + previous_percentage = 0; + previous_time = 0; + percentage_delta = 0; + velocity = 0; + } + + private static double applied_percentage (double percentage, double percentage_delta) { + return (percentage - percentage_delta).clamp (0, 1); + } + + /** + * Calculates the end animation duration using the current gesture velocity. + */ + private int calculate_end_animation_duration (double end_percentage, bool cancel_action) { + double animation_velocity = (velocity > ANIMATION_BASE_VELOCITY) + ? velocity + : ANIMATION_BASE_VELOCITY; + + double pending_percentage = cancel_action ? end_percentage : 1 - end_percentage; + + int duration = ((int)(pending_percentage / animation_velocity).abs () * DURATION_MULTIPLIER) + .clamp (min_animation_duration, max_animation_duration); + return duration; + } + } diff --git a/src/Gestures/ScrollBackend.vala b/src/Gestures/ScrollBackend.vala new file mode 100644 index 00000000..399c39f1 --- /dev/null +++ b/src/Gestures/ScrollBackend.vala @@ -0,0 +1,126 @@ +/* + * Copyright 2021 elementary, Inc (https://elementary.io) + * 2021 José Expósito + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * This gesture backend transforms the touchpad scroll events received by an actor into gestures. + */ +public class Gala.ScrollBackend : Object { + // Mutter does not expose the size of the touchpad, so we use the same values as GTK apps. + // From GNOME Shell, TOUCHPAD_BASE_[WIDTH|HEIGHT] / SCROLL_MULTIPLIER + // https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/js/ui/swipeTracker.js + private const double FINISH_DELTA_HORIZONTAL = 40; + private const double FINISH_DELTA_VERTICAL = 30; + + public signal void on_gesture_detected (Gesture gesture); + public signal void on_begin (double delta, uint64 time); + public signal void on_update (double delta, uint64 time); + public signal void on_end (double delta, uint64 time); + + public Clutter.Actor actor { get; construct; } + public Clutter.Orientation orientation { get; construct; } + + private bool started; + private double delta_x; + private double delta_y; + private GestureDirection direction; + + construct { + started = false; + delta_x = 0; + delta_y = 0; + direction = GestureDirection.UNKNOWN; + } + + public ScrollBackend (Clutter.Actor actor, Clutter.Orientation orientation) { + Object (actor: actor, orientation: orientation); + + actor.scroll_event.connect (on_scroll_event); + } + + private bool on_scroll_event (Clutter.ScrollEvent event) { + if (!can_handle_event (event)) { + return false; + } + + uint64 time = event.get_time (); + double x, y; + event.get_scroll_delta (out x, out y); + delta_x += x; + delta_y += y; + + if (!started) { + if (delta_x != 0 || delta_y != 0) { + Gesture gesture = build_gesture (delta_x, delta_y, orientation); + started = true; + direction = gesture.direction; + on_gesture_detected (gesture); + + double delta = calculate_delta (delta_x, delta_y, direction); + on_begin (delta, time); + } + } else { + double delta = calculate_delta (delta_x, delta_y, direction); + if (x == 0 && y == 0) { + started = false; + delta_x = 0; + delta_y = 0; + direction = GestureDirection.UNKNOWN; + on_end (delta, time); + } else { + on_update (delta, time); + } + } + + return true; + } + + private static bool can_handle_event (Clutter.ScrollEvent event) { + return event.get_type () == Clutter.EventType.SCROLL + && event.get_source_device ().get_device_type () == Clutter.InputDeviceType.TOUCHPAD_DEVICE + && event.get_scroll_direction () == Clutter.ScrollDirection.SMOOTH; + } + + private static Gesture build_gesture (double delta_x, double delta_y, Clutter.Orientation orientation) { + GestureDirection direction; + if (orientation == Clutter.Orientation.HORIZONTAL) { + direction = delta_x > 0 ? GestureDirection.RIGHT : GestureDirection.LEFT; + } else { + direction = delta_y > 0 ? GestureDirection.DOWN : GestureDirection.UP; + } + + return new Gesture () { + type = Gdk.EventType.SCROLL, + direction = direction, + fingers = 2, + performed_on_device_type = Gdk.InputSource.TOUCHPAD + }; + } + + private static double calculate_delta (double delta_x, double delta_y, GestureDirection direction) { + bool is_horizontal = (direction == GestureDirection.LEFT || direction == GestureDirection.RIGHT); + double used_delta = is_horizontal ? delta_x : delta_y; + 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; + } +} diff --git a/src/Widgets/MultitaskingView.vala b/src/Widgets/MultitaskingView.vala index f3e14dfe..166fc7e0 100644 --- a/src/Widgets/MultitaskingView.vala +++ b/src/Widgets/MultitaskingView.vala @@ -29,6 +29,7 @@ namespace Gala { public const AnimationMode ANIMATION_MODE = AnimationMode.EASE_OUT_QUAD; private GestureAnimationDirector gesture_animation_director; + private GestureTracker gesture_tracker; const int SMOOTH_SCROLL_DELAY = 500; @@ -39,8 +40,6 @@ namespace Gala { bool opened = false; bool animating = false; - bool is_smooth_scrolling = false; - List window_containers_monitors; IconGroupContainer icon_groups; @@ -61,6 +60,10 @@ namespace Gala { gesture_animation_director = new GestureAnimationDirector (ANIMATION_DURATION, ANIMATION_DURATION); + gesture_tracker = new GestureTracker (AnimationDuration.WORKSPACE_SWITCH_MIN, AnimationDuration.WORKSPACE_SWITCH); + gesture_tracker.enable_scroll (this, Clutter.Orientation.HORIZONTAL); + gesture_tracker.on_gesture_detected.connect (on_gesture_detected); + workspaces = new Actor (); workspaces.set_easing_mode (AnimationMode.EASE_OUT_QUAD); @@ -164,14 +167,17 @@ namespace Gala { } /** - * Scroll through workspaces + * Scroll through workspaces with the mouse wheel. Smooth scrolling is handled by + * GestureTracker. */ public override bool scroll_event (ScrollEvent scroll_event) { - if (!opened) + if (!opened) { return true; + } - if (scroll_event.direction != ScrollDirection.SMOOTH) + if (scroll_event.direction != ScrollDirection.SMOOTH) { return false; + } double dx, dy; #if VALA_0_32 @@ -181,43 +187,92 @@ namespace Gala { event->get_scroll_delta (out dx, out dy); #endif - var direction = MotionDirection.LEFT; - - // concept from maya to detect mouse wheel and proper smooth scroll and prevent - // too much repetition on the events + // concept from maya to detect mouse wheel if (Math.fabs (dy) == 1.0) { - // mouse wheel scroll - direction = dy > 0 ? MotionDirection.RIGHT : MotionDirection.LEFT; - } else if (!is_smooth_scrolling) { - // actual smooth scroll - var choice = Math.fabs (dx) > Math.fabs (dy) ? dx : dy; + var direction = dy > 0 ? MotionDirection.RIGHT : MotionDirection.LEFT; - if (choice > 0.3) - direction = MotionDirection.RIGHT; - else if (choice < -0.3) - direction = MotionDirection.LEFT; - else - return false; + unowned Meta.WorkspaceManager manager = display.get_workspace_manager (); + var active_workspace = manager.get_active_workspace (); + var new_workspace = active_workspace.get_neighbor (direction); - is_smooth_scrolling = true; - Timeout.add (SMOOTH_SCROLL_DELAY, () => { - is_smooth_scrolling = false; - return false; - }); - } else - // smooth scroll delay still active - return false; - - unowned Meta.WorkspaceManager manager = display.get_workspace_manager (); - var active_workspace = manager.get_active_workspace (); - var new_workspace = active_workspace.get_neighbor (direction); - - if (active_workspace != new_workspace) - new_workspace.activate (display.get_current_time ()); + if (active_workspace != new_workspace) { + new_workspace.activate (display.get_current_time ()); + } + } return false; } + private void on_gesture_detected (Gesture gesture) { + if (gesture.type == Gdk.EventType.SCROLL) { + Meta.MotionDirection direction = (gesture.direction == GestureDirection.LEFT) + ? Meta.MotionDirection.LEFT + : Meta.MotionDirection.RIGHT; + switch_workspace_with_gesture (direction); + } + } + + private void switch_workspace_with_gesture (Meta.MotionDirection direction) { + unowned Meta.WorkspaceManager manager = display.get_workspace_manager (); + var num_workspaces = manager.get_n_workspaces (); + var active_workspace_index = manager.get_active_workspace ().index (); + var target_workspace_index = (direction == Meta.MotionDirection.LEFT) + ? active_workspace_index - 1 + : active_workspace_index + 1; + + float initial_x = workspaces.x; + float target_x = 0; + bool is_nudge_animation = (target_workspace_index < 0 || target_workspace_index >= num_workspaces); + + if (is_nudge_animation) { + var nudge_delta = (direction == Meta.MotionDirection.LEFT) + ? WindowManagerGala.NUDGE_GAP + : -WindowManagerGala.NUDGE_GAP; + target_x = initial_x + nudge_delta * InternalUtils.get_ui_scaling_factor (); + } else { + foreach (var child in workspaces.get_children ()) { + unowned WorkspaceClone workspace_clone = (WorkspaceClone) child; + var index = workspace_clone.workspace.index (); + if (index == target_workspace_index) { + target_x = -workspace_clone.multitasking_view_x (); + break; + } + } + } + + debug ("Starting MultitaskingView switch workspace animation:"); + debug ("Active workspace index: %d", active_workspace_index); + debug ("Target workspace index: %d", target_workspace_index); + debug ("Total number of workspaces: %d", num_workspaces); + debug ("Is nudge animation: %s", is_nudge_animation ? "Yes" : "No"); + debug ("Initial X: %f", initial_x); + debug ("Target X: %f", target_x); + + GestureAnimationDirector.OnUpdate on_animation_update = (percentage) => { + var x = GestureTracker.animation_value (initial_x, target_x, percentage, true); + workspaces.x = x; + }; + + GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action, calculated_duration) => { + gesture_tracker.enabled = false; + + var duration = is_nudge_animation ? (AnimationDuration.NUDGE / 2) : calculated_duration; + workspaces.set_easing_duration (duration); + workspaces.x = (is_nudge_animation || cancel_action) ? initial_x : target_x; + + workspaces.get_transition ("x").completed.connect (() => { + gesture_tracker.enabled = true; + + if (!is_nudge_animation && !cancel_action) { + manager.get_workspace_by_index (target_workspace_index).activate (display.get_current_time ()); + update_positions (false); + } + }); + }; + + gesture_tracker.connect_handlers (null, (owned) on_animation_update, (owned) on_animation_end); + } + /** * Places the WorkspaceClones, moves the view so that the active one is shown * and does the same for the IconGroups. diff --git a/src/WindowManager.vala b/src/WindowManager.vala index 824bbc25..7c4fb801 100644 --- a/src/WindowManager.vala +++ b/src/WindowManager.vala @@ -100,6 +100,11 @@ namespace Gala { private bool animating_switch_workspace = false; private GestureAnimationDirector gesture_animation_director; + /** + * Amount of pixels to move on the nudge animation. + */ + public const float NUDGE_GAP = 32; + /** * Gap to show between workspaces while switching between them. */ @@ -495,8 +500,8 @@ namespace Gala { } private void play_nudge_animation (Meta.MotionDirection direction) { - int duration = 360; - var dest = (direction == Meta.MotionDirection.LEFT ? 32.0f : -32.0f); + var dest = (direction == Meta.MotionDirection.LEFT ? NUDGE_GAP : -NUDGE_GAP); + dest *= InternalUtils.get_ui_scaling_factor (); GestureAnimationDirector.OnUpdate on_animation_update = (percentage) => { var x = GestureAnimationDirector.animation_value (0.0f, dest, percentage, true); @@ -505,7 +510,7 @@ namespace Gala { GestureAnimationDirector.OnEnd on_animation_end = (percentage, cancel_action) => { var nudge_gesture = new Clutter.PropertyTransition ("x") { - duration = (duration / 2), + duration = (AnimationDuration.NUDGE / 2), remove_on_complete = true, progress_mode = Clutter.AnimationMode.LINEAR }; @@ -523,7 +528,7 @@ namespace Gala { GLib.Value[] x = { dest }; var nudge = new Clutter.KeyframeTransition ("translation-x") { - duration = duration, + duration = AnimationDuration.NUDGE, remove_on_complete = true, progress_mode = Clutter.AnimationMode.EASE_IN_QUAD }; diff --git a/src/meson.build b/src/meson.build index 4b930963..c8819352 100644 --- a/src/meson.build +++ b/src/meson.build @@ -23,6 +23,10 @@ gala_bin_sources = files( 'Background/BackgroundManager.vala', 'Background/BackgroundSource.vala', 'Background/SystemBackground.vala', + 'Gestures/Gesture.vala', + 'Gestures/GestureSettings.vala', + 'Gestures/GestureTracker.vala', + 'Gestures/ScrollBackend.vala', 'Widgets/IconGroup.vala', 'Widgets/IconGroupContainer.vala', 'Widgets/MonitorClone.vala',