Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/Gestures/GestureBackend.vala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
*/

private interface Gala.GestureBackend : Object {
public signal float request_travel_distance ();

public signal bool on_gesture_detected (Gesture gesture, uint32 timestamp);
public signal void on_begin (double percentage, uint64 time);
public signal void on_update (double percentage, uint64 time);
Expand Down
28 changes: 21 additions & 7 deletions lib/Gestures/GestureController.vala
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ public class Gala.GestureController : Object {

private ToucheggBackend? touchegg_backend;
private TouchpadBackend? touchpad_backend;
private PanBackend? pan_backend;
private ScrollBackend? scroll_backend;

private GestureBackend? recognizing_backend;
Expand Down Expand Up @@ -134,6 +135,15 @@ public class Gala.GestureController : Object {
touchegg_backend.on_end.connect (gesture_end);
}

public void enable_touchscreen (Clutter.Actor actor) {
pan_backend = new PanBackend (wm, actor);
pan_backend.request_travel_distance.connect (on_request_travel_distance);
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);
}

public void enable_scroll (Clutter.Actor actor, Clutter.Orientation orientation) {
scroll_backend = new ScrollBackend (actor, orientation, new GestureSettings ());
scroll_backend.on_gesture_detected.connect (gesture_detected);
Expand All @@ -142,6 +152,10 @@ public class Gala.GestureController : Object {
scroll_backend.on_end.connect (gesture_end);
}

private float on_request_travel_distance () {
return target.get_travel_distance (action);
}

private void prepare () {
if (timeline != null) {
timeline = null;
Expand Down Expand Up @@ -178,8 +192,8 @@ public class Gala.GestureController : Object {
return recognizing;
}

private void gesture_begin (double percentage, uint64 elapsed_time) {
if (!recognizing) {
private void gesture_begin (GestureBackend backend, double percentage, uint64 elapsed_time) {
if (!recognizing || backend != recognizing_backend) {
return;
}

Expand All @@ -190,8 +204,8 @@ public class Gala.GestureController : Object {
previous_time = elapsed_time;
}

private void gesture_update (double percentage, uint64 elapsed_time) {
if (!recognizing) {
private void gesture_update (GestureBackend backend, double percentage, uint64 elapsed_time) {
if (!recognizing || backend != recognizing_backend) {
return;
}

Expand All @@ -215,8 +229,8 @@ public class Gala.GestureController : Object {
previous_delta = updated_delta;
}

private void gesture_end (double percentage, uint64 elapsed_time) {
if (!recognizing) {
private void gesture_end (GestureBackend backend, double percentage, uint64 elapsed_time) {
if (!recognizing || backend != recognizing_backend) {
return;
}

Expand Down Expand Up @@ -311,8 +325,8 @@ public class Gala.GestureController : Object {

public void cancel_gesture () {
if (recognizing) {
gesture_end (recognizing_backend, previous_percentage, previous_time);
recognizing_backend.cancel_gesture ();
gesture_end (previous_percentage, previous_time);
}
}
}
152 changes: 152 additions & 0 deletions lib/Gestures/PanBackend.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Authored by: Leonhard Kargl <[email protected]>
*/

internal class Gala.PanBackend : Object, GestureBackend {
public WindowManager wm { get; construct; }
public Clutter.Actor actor { get; construct; }

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;

public PanBackend (WindowManager wm, Clutter.Actor actor) {
Object (wm: wm, actor: actor);
}

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;
}

modal_proxy = wm.push_modal (actor, true);

on_begin (0, time);

return true;
}

private void on_gesture_end () {
if (modal_proxy != null) {
// Only emit on end if we actually began the gesture
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 () / request_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
};
}
}
4 changes: 4 additions & 0 deletions lib/Gestures/RootTarget.vala
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ public interface Gala.RootTarget : Object, GestureTarget {
*/
public abstract Clutter.Actor? actor { get; }

public virtual float get_travel_distance (GestureAction for_action) {
return 0.0f;
}

public void add_gesture_controller (GestureController controller) requires (controller.target == null) {
controller.attached (this);
weak_ref (controller.detached);
Expand Down
2 changes: 1 addition & 1 deletion lib/Gestures/ToucheggBackend.vala
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ private class Gala.ToucheggBackend : Object, GestureBackend {
signal_params.get ("(uudiut)", out type, out direction, out percentage, out fingers,
out performed_on_device_type, out elapsed_time);

if (Meta.Util.is_wayland_compositor () && performed_on_device_type != DeviceType.TOUCHSCREEN && type != PINCH) {
if (Meta.Util.is_wayland_compositor () && type != PINCH) {
return;
}

Expand Down
1 change: 1 addition & 0 deletions lib/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ gala_lib_sources = files(
'Gestures/GestureController.vala',
'Gestures/GestureSettings.vala',
'Gestures/GestureTarget.vala',
'Gestures/PanBackend.vala',
'Gestures/PropertyTarget.vala',
'Gestures/RootTarget.vala',
'Gestures/ScrollBackend.vala',
Expand Down
10 changes: 10 additions & 0 deletions src/Widgets/MultitaskingView/MultitaskingView.vala
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public class Gala.MultitaskingView : ActorTarget, RootTarget, ActivatableCompone

multitasking_gesture_controller = new GestureController (MULTITASKING_VIEW, wm, MULTITASKING_VIEW);
multitasking_gesture_controller.enable_touchpad (wm.stage);
multitasking_gesture_controller.enable_touchscreen (wm.stage);
add_gesture_controller (multitasking_gesture_controller);

add_target (ShellClientsManager.get_instance ()); // For hiding the panels
Expand All @@ -74,6 +75,7 @@ public class Gala.MultitaskingView : ActorTarget, RootTarget, ActivatableCompone
follow_natural_scroll = true,
};
workspaces_gesture_controller.enable_touchpad (wm.stage);
workspaces_gesture_controller.enable_touchscreen (wm.stage);
workspaces_gesture_controller.enable_scroll (this, HORIZONTAL);
add_gesture_controller (workspaces_gesture_controller);

Expand Down Expand Up @@ -254,6 +256,14 @@ public class Gala.MultitaskingView : ActorTarget, RootTarget, ActivatableCompone
workspaces_gesture_controller.cancel_gesture ();
}

public override float get_travel_distance (GestureAction for_action) {
switch (for_action) {
case MULTITASKING_VIEW: return primary_monitor_container.get_height () * 0.5f;
case SWITCH_WORKSPACE: return workspaces.get_first_child ().get_width ();
default: return 0;
}
}

public override void start_progress (GestureAction action) {
if (!visible) {
opened = true;
Expand Down