diff --git a/src/Gestures/Gesture.vala b/src/Gestures/Gesture.vala
index 9bccedaf7..5ffd2831d 100644
--- a/src/Gestures/Gesture.vala
+++ b/src/Gestures/Gesture.vala
@@ -35,9 +35,25 @@ namespace Gala {
}
public class Gesture {
+ public const float INVALID_COORD = float.MAX;
+
public Clutter.EventType type;
public GestureDirection direction;
public int fingers;
public Clutter.InputDeviceType performed_on_device_type;
+
+ /**
+ * The x coordinate of the initial contact point for the gesture.
+ * Doesn't have to be set. In that case it is set to {@link INVALID_COORD}.
+ * Currently the only backend not setting this is {@link GestureTracker.enable_touchpad}.
+ */
+ public float origin_x = INVALID_COORD;
+
+ /**
+ * The y coordinate of the initial contact point for the gesture.
+ * Doesn't have to be set. In that case it is set to {@link INVALID_COORD}.
+ * Currently the only backend not setting this is {@link GestureTracker.enable_touchpad}.
+ */
+ public float origin_y = INVALID_COORD;
}
}
diff --git a/src/Gestures/GestureSettings.vala b/src/Gestures/GestureSettings.vala
index 894bc0f10..4dd6da0bb 100644
--- a/src/Gestures/GestureSettings.vala
+++ b/src/Gestures/GestureSettings.vala
@@ -20,6 +20,14 @@
* Utility class to access the gesture settings. Easily accessible through GestureTracker.settings.
*/
public class Gala.GestureSettings : Object {
+ public enum GestureAction {
+ NONE,
+ SWITCH_WORKSPACE,
+ MOVE_TO_WORKSPACE,
+ SWITCH_WINDOWS,
+ MULTITASKING_VIEW
+ }
+
private static GLib.Settings gala_settings;
private static GLib.Settings touchpad_settings;
@@ -69,4 +77,41 @@ public class Gala.GestureSettings : Object {
public static string get_string (string setting_id) {
return gala_settings.get_string (setting_id);
}
+
+ public static GestureAction get_action (Gesture gesture) {
+ if (gesture.type == TOUCHPAD_SWIPE) {
+ var fingers = gesture.fingers;
+
+ if (gesture.direction == LEFT || gesture.direction == RIGHT) {
+ var three_finger_swipe_horizontal = get_string ("three-finger-swipe-horizontal");
+ var four_finger_swipe_horizontal = get_string ("four-finger-swipe-horizontal");
+
+ if (fingers == 3 && three_finger_swipe_horizontal == "switch-to-workspace" ||
+ fingers == 4 && four_finger_swipe_horizontal == "switch-to-workspace") {
+ return SWITCH_WORKSPACE;
+ }
+
+ if (fingers == 3 && three_finger_swipe_horizontal == "move-to-workspace" ||
+ fingers == 4 && four_finger_swipe_horizontal == "move-to-workspace") {
+ return MOVE_TO_WORKSPACE;
+ }
+
+
+ if (fingers == 3 && three_finger_swipe_horizontal == "switch-windows" ||
+ fingers == 4 && four_finger_swipe_horizontal == "switch-windows") {
+ return SWITCH_WINDOWS;
+ }
+ } else if (gesture.direction == UP || gesture.direction == DOWN) {
+ var three_finger_swipe_up = get_string ("three-finger-swipe-up");
+ var four_finger_swipe_up = get_string ("four-finger-swipe-up");
+
+ if (fingers == 3 && three_finger_swipe_up == "multitasking-view" ||
+ fingers == 4 && four_finger_swipe_up == "multitasking-view") {
+ return MULTITASKING_VIEW;
+ }
+ }
+ }
+
+ return NONE;
+ }
}
diff --git a/src/Gestures/GestureTracker.vala b/src/Gestures/GestureTracker.vala
index aba66f648..2f0c92a4e 100644
--- a/src/Gestures/GestureTracker.vala
+++ b/src/Gestures/GestureTracker.vala
@@ -16,6 +16,15 @@
* along with this program. If not, see .
*/
+public interface Gala.GestureBackend : Object {
+ public signal bool on_gesture_detected (Gesture gesture, uint32 timestamp);
+ 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 virtual void prepare_gesture_handling () { }
+}
+
/**
* Allow to use multi-touch gestures from different sources (backends).
* Usage:
@@ -68,11 +77,24 @@ public class Gala.GestureTracker : Object {
/**
* 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.
+ * This should only be used to determine whether the gesture should be handled. This shouldn't
+ * do any preparations instead those should be done in {@link on_gesture_handled}. This is because
+ * the backend might have to do some preparations itself before you are allowed to do some to avoid
+ * conflicts.
* @param gesture Information about the gesture.
+ * @return true if the gesture will be handled false otherwise. If false is returned the other
+ * signals may still be emitted but aren't guaranteed to be.
+ */
+ public signal bool on_gesture_detected (Gesture gesture);
+
+ /**
+ * Emitted if true was returned form {@link on_gesture_detected}. This should
+ * be used to do any preparations for gesture handling and to call {@link connect_handlers} to
+ * 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}.
*/
- public signal void on_gesture_detected (Gesture gesture);
+ public signal void on_gesture_handled (Gesture gesture, uint32 timestamp);
/**
* Emitted right after on_gesture_detected with the initial gesture information.
@@ -98,7 +120,12 @@ public class Gala.GestureTracker : Object {
/**
* Backend used if enable_touchpad is called.
*/
- private ToucheggBackend touchpad_backend;
+ private ToucheggBackend? touchpad_backend;
+
+ /**
+ * Pan backend used if enable_pan is called.
+ */
+ private PanBackend pan_backend;
/**
* Scroll backend used if enable_scroll is called.
@@ -137,6 +164,22 @@ public class Gala.GestureTracker : Object {
touchpad_backend.on_end.connect (gesture_end);
}
+ /**
+ * Allow to receive pan gestures.
+ * @param actor Clutter actor that will receive the events.
+ * @param travel_distance_func this will be called if a gesture is detected and true is returned from {@link on_gesture_detected}.
+ * The returned distance wil be used to calculate the percentage. It should be set to the amount something will travel (e.g.
+ * when moving an actor) based on the gesture to allow exact finger tracking. It can also be used
+ * to calculate the raw pixels the finger travelled at a given time with percentage * distance.
+ */
+ public void enable_pan (WindowManager wm, Clutter.Actor actor, owned PanBackend.GetTravelDistance travel_distance_func) {
+ pan_backend = new PanBackend (wm, actor, (owned) travel_distance_func);
+ pan_backend.on_gesture_detected.connect (gesture_detected);
+ pan_backend.on_begin.connect (gesture_begin);
+ pan_backend.on_update.connect (gesture_update);
+ pan_backend.on_end.connect (gesture_end);
+ }
+
/**
* Allow to receive scroll gestures.
* @param actor Clutter actor that will receive the scroll events.
@@ -201,10 +244,14 @@ public class Gala.GestureTracker : Object {
return value;
}
- private void gesture_detected (Gesture gesture) {
- if (enabled) {
- on_gesture_detected (gesture);
+ 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);
+ return true;
}
+
+ return false;
}
private void gesture_begin (double percentage, uint64 elapsed_time) {
diff --git a/src/Gestures/PanBackend.vala b/src/Gestures/PanBackend.vala
new file mode 100644
index 000000000..60c3a3a3d
--- /dev/null
+++ b/src/Gestures/PanBackend.vala
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2024 elementary, Inc. (https://elementary.io)
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ *
+ * Authored by: Leonhard Kargl
+ */
+
+public class Gala.PanBackend : Object, GestureBackend {
+ public delegate float GetTravelDistance ();
+
+ public WindowManager wm { get; construct; }
+ public Clutter.Actor actor { get; construct; }
+
+ private GetTravelDistance get_travel_distance_func;
+
+ private ModalProxy? modal_proxy;
+
+ private Clutter.PanAxis pan_axis;
+ private Clutter.PanAction pan_action;
+
+ private GestureDirection direction;
+
+ private float origin_x;
+ private float origin_y;
+
+ private float current_x;
+ private float current_y;
+
+ private float last_x_coord;
+ private float last_y_coord;
+ private uint last_n_points;
+
+ private float travel_distance;
+
+ public PanBackend (WindowManager wm, Clutter.Actor actor, owned GetTravelDistance get_travel_distance_func) {
+ Object (wm: wm, actor: actor);
+
+ this.get_travel_distance_func = (owned) get_travel_distance_func;
+ }
+
+ construct {
+ pan_action = new Clutter.PanAction () {
+ n_touch_points = 1
+ };
+
+ actor.add_action_full ("pan-gesture", CAPTURE, pan_action);
+
+ pan_action.gesture_begin.connect (on_gesture_begin);
+ pan_action.pan.connect (on_pan);
+ pan_action.gesture_end.connect (on_gesture_end);
+ pan_action.gesture_cancel.connect (on_gesture_end);
+ }
+
+ ~PanBackend () {
+ actor.remove_action (pan_action);
+ }
+
+ private bool on_gesture_begin () {
+ if (pan_action.get_last_event (0).get_source_device ().get_device_type () != TOUCHSCREEN_DEVICE) {
+ return false;
+ }
+
+ float x_coord, y_coord;
+ pan_action.get_press_coords (0, out x_coord, out y_coord);
+
+ origin_x = current_x = x_coord;
+ origin_y = current_y = y_coord;
+
+ var time = pan_action.get_last_event (0).get_time ();
+
+ var handled = on_gesture_detected (build_gesture (), time);
+
+ if (!handled) {
+ reset ();
+ return false;
+ }
+
+ travel_distance = get_travel_distance_func ();
+
+ on_begin (0, time);
+
+ return true;
+ }
+
+ public override void prepare_gesture_handling () {
+ modal_proxy = wm.push_modal (actor);
+ }
+
+ private void on_gesture_end () {
+ on_end (calculate_percentage (), Meta.CURRENT_TIME);
+
+ reset ();
+ }
+
+ private void reset () {
+ if (modal_proxy != null) {
+ wm.pop_modal (modal_proxy);
+ modal_proxy = null;
+ }
+
+ direction = GestureDirection.UNKNOWN;
+ last_n_points = 0;
+ last_x_coord = 0;
+ last_y_coord = 0;
+ }
+
+ private bool on_pan (Clutter.PanAction pan_action, Clutter.Actor actor, bool interpolate) {
+ var time = pan_action.get_last_event (0).get_time ();
+
+ float x, y;
+ pan_action.get_motion_coords (0, out x, out y);
+
+ if (pan_action.get_n_current_points () == last_n_points) {
+ current_x += x - last_x_coord;
+ current_y += y - last_y_coord;
+ }
+
+ last_x_coord = x;
+ last_y_coord = y;
+ last_n_points = pan_action.get_n_current_points ();
+
+ on_update (calculate_percentage (), time);
+
+ return true;
+ }
+
+ private double calculate_percentage () {
+ float current, origin;
+ if (pan_axis == X_AXIS) {
+ current = direction == RIGHT ? float.max (current_x, origin_x) : float.min (current_x, origin_x);
+ origin = origin_x;
+ } else {
+ current = direction == DOWN ? float.max (current_y, origin_y) : float.min (current_y, origin_y);
+ origin = origin_y;
+ }
+
+ return (current - origin).abs () / travel_distance;
+ }
+
+ private Gesture build_gesture () {
+ float delta_x, delta_y;
+ ((Clutter.GestureAction) pan_action).get_motion_delta (0, out delta_x, out delta_y);
+
+ pan_axis = delta_x.abs () > delta_y.abs () ? Clutter.PanAxis.X_AXIS : Clutter.PanAxis.Y_AXIS;
+
+ if (pan_axis == X_AXIS) {
+ direction = delta_x > 0 ? GestureDirection.RIGHT : GestureDirection.LEFT;
+ } else {
+ direction = delta_y > 0 ? GestureDirection.DOWN : GestureDirection.UP;
+ }
+
+ return new Gesture () {
+ type = Clutter.EventType.TOUCHPAD_SWIPE,
+ direction = direction,
+ fingers = (int) pan_action.get_n_current_points (),
+ performed_on_device_type = Clutter.InputDeviceType.TOUCHSCREEN_DEVICE,
+ origin_x = origin_x,
+ origin_y = origin_y
+ };
+ }
+}
diff --git a/src/Gestures/ScrollBackend.vala b/src/Gestures/ScrollBackend.vala
index ed933a243..ae5d45d19 100644
--- a/src/Gestures/ScrollBackend.vala
+++ b/src/Gestures/ScrollBackend.vala
@@ -19,18 +19,13 @@
/**
* This gesture backend transforms the touchpad scroll events received by an actor into gestures.
*/
-public class Gala.ScrollBackend : Object {
+public class Gala.ScrollBackend : Object, GestureBackend {
// 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; }
public GestureSettings settings { get; construct; }
@@ -62,7 +57,7 @@ public class Gala.ScrollBackend : Object {
return false;
}
- uint64 time = event.get_time ();
+ var time = event.get_time ();
double x, y;
event.get_scroll_delta (out x, out y);
@@ -80,10 +75,12 @@ public class Gala.ScrollBackend : Object {
if (!started) {
if (delta_x != 0 || delta_y != 0) {
- Gesture gesture = build_gesture (delta_x, delta_y, orientation);
+ float origin_x, origin_y;
+ event.get_coords (out origin_x, out origin_y);
+ Gesture gesture = build_gesture (origin_x, origin_y, delta_x, delta_y, orientation, time);
started = true;
direction = gesture.direction;
- on_gesture_detected (gesture);
+ on_gesture_detected (gesture, time);
double delta = calculate_delta (delta_x, delta_y, direction);
on_begin (delta, time);
@@ -114,7 +111,7 @@ public class Gala.ScrollBackend : Object {
&& event.get_scroll_direction () == Clutter.ScrollDirection.SMOOTH;
}
- private static Gesture build_gesture (double delta_x, double delta_y, Clutter.Orientation orientation) {
+ private static Gesture build_gesture (float origin_x, float origin_y, double delta_x, double delta_y, Clutter.Orientation orientation, uint32 timestamp) {
GestureDirection direction;
if (orientation == Clutter.Orientation.HORIZONTAL) {
direction = delta_x > 0 ? GestureDirection.RIGHT : GestureDirection.LEFT;
@@ -126,7 +123,9 @@ public class Gala.ScrollBackend : Object {
type = Clutter.EventType.SCROLL,
direction = direction,
fingers = 2,
- performed_on_device_type = Clutter.InputDeviceType.TOUCHPAD_DEVICE
+ performed_on_device_type = Clutter.InputDeviceType.TOUCHPAD_DEVICE,
+ origin_x = origin_x,
+ origin_y = origin_y
};
}
diff --git a/src/Gestures/ToucheggBackend.vala b/src/Gestures/ToucheggBackend.vala
index d4eeb224b..03fd2597b 100644
--- a/src/Gestures/ToucheggBackend.vala
+++ b/src/Gestures/ToucheggBackend.vala
@@ -20,12 +20,7 @@
* Singleton class to manage the connection with Touchégg daemon and receive touch events.
* See: [[https://github.com/JoseExposito/touchegg]]
*/
-public class Gala.ToucheggBackend : Object {
- 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 class Gala.ToucheggBackend : Object, GestureBackend {
/**
* Gesture type as returned by the daemon.
*/
@@ -197,12 +192,16 @@ public class Gala.ToucheggBackend : Object {
signal_params.get ("(uudiut)", out type, out direction, out percentage, out fingers,
out performed_on_device_type, out elapsed_time);
+ if (performed_on_device_type == TOUCHSCREEN) {
+ return;
+ }
+
var delta = percentage * DELTA_MULTIPLIER;
switch (signal_name) {
case DBUS_ON_GESTURE_BEGIN:
Idle.add (() => {
- on_gesture_detected (make_gesture (type, direction, fingers, performed_on_device_type));
+ on_gesture_detected (make_gesture (type, direction, fingers, performed_on_device_type), Meta.CURRENT_TIME);
on_begin (delta, elapsed_time);
return false;
});
diff --git a/src/InternalUtils.vala b/src/InternalUtils.vala
index 3578ef3d2..cf376678a 100644
--- a/src/InternalUtils.vala
+++ b/src/InternalUtils.vala
@@ -336,5 +336,14 @@ namespace Gala {
return { 0, 0, (int) screen_width, (int) screen_height };
}
}
+
+ public static float travel_distance_from_primary (Meta.Display display, Clutter.Orientation orientation) {
+ var geom = display.get_monitor_geometry (display.get_primary_monitor ());
+ if (orientation == HORIZONTAL) {
+ return geom.width;
+ } else {
+ return geom.height;
+ }
+ }
}
}
diff --git a/src/Widgets/MultitaskingView.vala b/src/Widgets/MultitaskingView.vala
index 50deea0a9..8d6fc9284 100644
--- a/src/Widgets/MultitaskingView.vala
+++ b/src/Widgets/MultitaskingView.vala
@@ -53,6 +53,8 @@ namespace Gala {
}
}
+ private float workspaces_travel_distance;
+
public MultitaskingView (WindowManager wm) {
Object (wm: wm);
}
@@ -70,12 +72,19 @@ namespace Gala {
multitasking_gesture_tracker = new GestureTracker (ANIMATION_DURATION, ANIMATION_DURATION);
multitasking_gesture_tracker.enable_touchpad ();
+ multitasking_gesture_tracker.enable_pan (wm, display.get_stage (), () => InternalUtils.travel_distance_from_primary (wm.get_display (), VERTICAL));
multitasking_gesture_tracker.on_gesture_detected.connect (on_multitasking_gesture_detected);
+ multitasking_gesture_tracker.on_gesture_handled.connect (() => toggle (true, false));
workspace_gesture_tracker = new GestureTracker (AnimationDuration.WORKSPACE_SWITCH_MIN, AnimationDuration.WORKSPACE_SWITCH);
workspace_gesture_tracker.enable_touchpad ();
+ workspace_gesture_tracker.enable_pan (wm, this, () => workspaces_travel_distance);
workspace_gesture_tracker.enable_scroll (this, Clutter.Orientation.HORIZONTAL);
workspace_gesture_tracker.on_gesture_detected.connect (on_workspace_gesture_detected);
+ workspace_gesture_tracker.on_gesture_handled.connect ((gesture, timestamp) => {
+ var direction = workspace_gesture_tracker.settings.get_natural_scroll_direction (gesture);
+ switch_workspace_with_gesture (direction, timestamp);
+ });
workspaces = new Clutter.Actor ();
@@ -290,39 +299,32 @@ namespace Gala {
workspaces.add_transition ("nudge", nudge);
}
- private void on_multitasking_gesture_detected (Gesture gesture) {
- if (gesture.type != Clutter.EventType.TOUCHPAD_SWIPE ||
- (gesture.fingers == 3 && GestureSettings.get_string ("three-finger-swipe-up") != "multitasking-view") ||
- (gesture.fingers == 4 && GestureSettings.get_string ("four-finger-swipe-up") != "multitasking-view")
- ) {
- return;
+ private bool on_multitasking_gesture_detected (Gesture gesture) {
+ if (GestureSettings.get_action (gesture) != MULTITASKING_VIEW) {
+ return false;
}
- if (gesture.direction == GestureDirection.UP && !opened) {
- toggle (true, false);
- } else if (gesture.direction == GestureDirection.DOWN && opened) {
- toggle (true, false);
+ if (gesture.direction == UP && !opened || gesture.direction == DOWN && opened) {
+ return true;
}
+
+ return false;
}
- private void on_workspace_gesture_detected (Gesture gesture) {
+ private bool on_workspace_gesture_detected (Gesture gesture) {
if (!opened) {
- return;
+ return false;
}
- var can_handle_swipe = gesture.type == Clutter.EventType.TOUCHPAD_SWIPE &&
- (gesture.direction == GestureDirection.LEFT || gesture.direction == GestureDirection.RIGHT);
-
- var fingers = (gesture.fingers == 3 && Gala.GestureSettings.get_string ("three-finger-swipe-horizontal") == "switch-to-workspace") ||
- (gesture.fingers == 4 && Gala.GestureSettings.get_string ("four-finger-swipe-horizontal") == "switch-to-workspace");
-
- if (gesture.type == Clutter.EventType.SCROLL || (can_handle_swipe && fingers)) {
- var direction = workspace_gesture_tracker.settings.get_natural_scroll_direction (gesture);
- switch_workspace_with_gesture (direction);
+ if (gesture.type == SCROLL || GestureSettings.get_action (gesture) == SWITCH_WORKSPACE ||
+ gesture.type == TOUCHPAD_SWIPE && gesture.fingers == 1 && (gesture.direction == RIGHT || gesture.direction == LEFT)) {
+ return true;
}
+
+ return false;
}
- private void switch_workspace_with_gesture (Meta.MotionDirection direction) {
+ private void switch_workspace_with_gesture (Meta.MotionDirection direction, uint32 timestamp) {
if (switching_workspace_in_progress) {
return;
}
@@ -363,6 +365,8 @@ namespace Gala {
}
}
+ workspaces_travel_distance = (initial_x - target_x).abs ();
+
if (!is_nudge_animation && active_icon_group.get_transition ("backdrop-opacity") != null) {
active_icon_group.remove_transition ("backdrop-opacity");
}
@@ -380,7 +384,7 @@ namespace Gala {
switching_workspace_with_gesture = true;
if (target_workspace != null) {
- target_workspace.activate (display.get_current_time ());
+ target_workspace.activate (timestamp);
}
GestureTracker.OnUpdate on_animation_update = (percentage) => {
@@ -692,9 +696,6 @@ namespace Gala {
}
if (opening) {
- modal_proxy = wm.push_modal (this);
- modal_proxy.set_keybinding_filter (keybinding_filter);
-
wm.background_group.hide ();
wm.window_group.hide ();
wm.top_window_group.hide ();
@@ -764,6 +765,13 @@ namespace Gala {
dock_clones.destroy_all_children ();
wm.pop_modal (modal_proxy);
+
+ multitasking_gesture_tracker.enable_pan (wm, wm.get_display ().get_stage (), () => InternalUtils.travel_distance_from_primary (wm.get_display (), VERTICAL));
+ } else {
+ modal_proxy = wm.push_modal (this);
+ modal_proxy.set_keybinding_filter (keybinding_filter);
+ // We now have to listen to events on this because it's now modal i.e. the stage doesn't get the events anymore
+ multitasking_gesture_tracker.enable_pan (wm, this, () => InternalUtils.travel_distance_from_primary (wm.get_display (), VERTICAL));
}
animating = false;
diff --git a/src/Widgets/WorkspaceClone.vala b/src/Widgets/WorkspaceClone.vala
index 2dfe0c991..aeac825e6 100644
--- a/src/Widgets/WorkspaceClone.vala
+++ b/src/Widgets/WorkspaceClone.vala
@@ -180,6 +180,13 @@ namespace Gala {
});
background = new FramedBackground (wm);
background.add_action (background_click_action);
+ // For some reason the click action doesn't respond to button releases anymore
+ // once the background was touched. This should hopefully be fixed with the
+ // new Clutter.Gestures that are being introduced upstream
+ background.button_release_event.connect (() => {
+ selected (true);
+ return Clutter.EVENT_STOP;
+ });
window_container = new WindowCloneContainer (wm, gesture_tracker, scale_factor) {
width = monitor_geometry.width,
diff --git a/src/WindowManager.vala b/src/WindowManager.vala
index a7fc980a1..de9a1f2b5 100644
--- a/src/WindowManager.vala
+++ b/src/WindowManager.vala
@@ -128,6 +128,7 @@ namespace Gala {
gesture_tracker = new GestureTracker (AnimationDuration.WORKSPACE_SWITCH_MIN, AnimationDuration.WORKSPACE_SWITCH);
gesture_tracker.enable_touchpad ();
gesture_tracker.on_gesture_detected.connect (on_gesture_detected);
+ gesture_tracker.on_gesture_handled.connect (on_gesture_handled);
info = Meta.PluginInfo () {name = "Gala", version = Config.VERSION, author = "Gala Developers",
license = "GPLv3", description = "A nice elementary window manager"};
@@ -184,6 +185,9 @@ namespace Gala {
screensaver.active_changed.connect (update_input_area);
stage = display.get_stage () as Clutter.Stage;
+
+ gesture_tracker.enable_pan (this, stage, () => InternalUtils.travel_distance_from_primary (get_display (), HORIZONTAL));
+
var background_settings = new GLib.Settings ("org.gnome.desktop.background");
var color = background_settings.get_string ("primary-color");
stage.background_color = Clutter.Color.from_string (color);
@@ -539,57 +543,41 @@ namespace Gala {
}
}
- private void on_gesture_detected (Gesture gesture) {
+ private bool on_gesture_detected (Gesture gesture) {
if (workspace_view.is_opened ()) {
- return;
- }
-
- if (gesture.type != Clutter.EventType.TOUCHPAD_SWIPE ||
- (gesture.direction != GestureDirection.LEFT && gesture.direction != GestureDirection.RIGHT)) {
- return;
+ return false;
}
- unowned var display = get_display ();
-
- var fingers = gesture.fingers;
-
- var three_finger_swipe_horizontal = GestureSettings.get_string ("three-finger-swipe-horizontal");
- var four_finger_swipe_horizontal = GestureSettings.get_string ("four-finger-swipe-horizontal");
-
- var three_fingers_switch_to_workspace = fingers == 3 && three_finger_swipe_horizontal == "switch-to-workspace";
- var four_fingers_switch_to_workspace = fingers == 4 && four_finger_swipe_horizontal == "switch-to-workspace";
-
- var three_fingers_move_to_workspace = fingers == 3 && three_finger_swipe_horizontal == "move-to-workspace";
- var four_fingers_move_to_workspace = fingers == 4 && four_finger_swipe_horizontal == "move-to-workspace";
-
- var three_fingers_switch_windows = fingers == 3 && three_finger_swipe_horizontal == "switch-windows";
- var four_fingers_switch_windows = fingers == 4 && four_finger_swipe_horizontal == "switch-windows";
+ var action = GestureSettings.get_action (gesture);
+ switch_workspace_with_gesture = action == SWITCH_WORKSPACE || action == MOVE_TO_WORKSPACE;
+ return switch_workspace_with_gesture || (action == SWITCH_WINDOWS && !window_switcher.opened);
+ }
- switch_workspace_with_gesture = three_fingers_switch_to_workspace || four_fingers_switch_to_workspace;
- if (switch_workspace_with_gesture) {
- var direction = gesture_tracker.settings.get_natural_scroll_direction (gesture);
- switch_to_next_workspace (direction, display.get_current_time ());
- return;
- }
+ private void on_gesture_handled (Gesture gesture, uint32 timestamp) {
+ var direction = gesture_tracker.settings.get_natural_scroll_direction (gesture);
- switch_workspace_with_gesture = three_fingers_move_to_workspace || four_fingers_move_to_workspace;
- if (switch_workspace_with_gesture) {
- unowned var manager = display.get_workspace_manager ();
+ switch (GestureSettings.get_action (gesture)) {
+ case MOVE_TO_WORKSPACE:
+ unowned var display = get_display ();
+ unowned var manager = display.get_workspace_manager ();
- var direction = gesture_tracker.settings.get_natural_scroll_direction (gesture);
+ moving = display.focus_window;
+ if (moving != null) {
+ moving.change_workspace (manager.get_active_workspace ().get_neighbor (direction));
+ }
+ switch_to_next_workspace (direction, timestamp);
+ break;
- moving = display.focus_window;
- if (moving != null) {
- moving.change_workspace (manager.get_active_workspace ().get_neighbor (direction));
- }
+ case SWITCH_WORKSPACE:
+ switch_to_next_workspace (direction, timestamp);
+ break;
- switch_to_next_workspace (direction, display.get_current_time ());
- return;
- }
+ case SWITCH_WINDOWS:
+ window_switcher.handle_gesture (gesture.direction);
+ break;
- var switch_windows = three_fingers_switch_windows || four_fingers_switch_windows;
- if (switch_windows && !window_switcher.opened) {
- window_switcher.handle_gesture (gesture.direction);
+ default:
+ break;
}
}
@@ -855,16 +843,16 @@ namespace Gala {
modal_stack.offer_head (proxy);
+ proxy.grab = stage.grab (actor);
+
// modal already active
if (modal_stack.size >= 2)
return proxy;
- unowned Meta.Display display = get_display ();
-
update_input_area ();
- proxy.grab = stage.grab (actor);
if (modal_stack.size == 1) {
+ unowned Meta.Display display = get_display ();
display.disable_unredirect ();
}
diff --git a/src/Zoom.vala b/src/Zoom.vala
index c3c27f5be..ea95cc40c 100644
--- a/src/Zoom.vala
+++ b/src/Zoom.vala
@@ -32,6 +32,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));
}
~Zoom () {
@@ -61,18 +62,20 @@ public class Gala.Zoom : Object {
zoom (-SHORTCUT_DELTA, true, wm.enable_animations);
}
- private void on_gesture_detected (Gesture gesture) {
+ private bool on_gesture_detected (Gesture gesture) {
if (gesture.type != Clutter.EventType.TOUCHPAD_PINCH ||
(gesture.direction != GestureDirection.IN && gesture.direction != GestureDirection.OUT)
) {
- return;
+ return false;
}
if ((gesture.fingers == 3 && GestureSettings.get_string ("three-finger-pinch") == "zoom") ||
(gesture.fingers == 4 && GestureSettings.get_string ("four-finger-pinch") == "zoom")
) {
- zoom_with_gesture (gesture.direction);
+ return true;
}
+
+ return false;
}
private void zoom_with_gesture (GestureDirection direction) {
diff --git a/src/meson.build b/src/meson.build
index e2e61807b..5e942075a 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -36,6 +36,7 @@ gala_bin_sources = files(
'Gestures/Gesture.vala',
'Gestures/GestureSettings.vala',
'Gestures/GestureTracker.vala',
+ 'Gestures/PanBackend.vala',
'Gestures/ScrollBackend.vala',
'Gestures/ToucheggBackend.vala',
'HotCorners/Barrier.vala',