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 demo/GraniteDemo.vala
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class Granite.Demo : Gtk.Application {
var toast_view = new ToastView ();
var settings_uris_view = new SettingsUrisView ();
var style_manager_view = new StyleManagerView ();
var multitouch_view = new SwipeView ();
var utils_view = new UtilsView ();
var video_view = new VideoView ();
var placeholder = new WelcomeView ();
Expand All @@ -55,6 +56,7 @@ public class Granite.Demo : Gtk.Application {
main_stack.add_titled (settings_uris_view, "settings_uris", "Settings URIs");
main_stack.add_titled (toast_view, "toasts", "Toast");
main_stack.add_titled (utils_view, "utils", "Utils");
main_stack.add_titled (multitouch_view, "multitouch", multitouch_view.title);
main_stack.add_titled (dialogs_view, "dialogs", "Dialogs");
main_stack.add_titled (application_view, "application", "Application");

Expand Down
59 changes: 59 additions & 0 deletions demo/Views/SwipeView.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright 2025 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: LGPL-3.0-or-later
*/

public class SwipeView : DemoPage {
// prevent Vala unrefing
private Granite.SwipeTracker swipe_tracker;

private Gtk.ProgressBar progressbar;
private uint spring_timeout = -1;

construct {
title = "Multitouch";

progressbar = new Gtk.ProgressBar () {
fraction = 0.5
};

child = progressbar;

swipe_tracker = new Granite.SwipeTracker (this);
swipe_tracker.begin_swipe.connect (on_swipe_begin);
swipe_tracker.update_swipe.connect (on_swipe_update);
swipe_tracker.end_swipe.connect (on_swipe_end);
}

private void on_swipe_begin () {
if (spring_timeout != -1) {
Source.remove (spring_timeout);
spring_timeout = -1;
}
}

private void on_swipe_update (double progress) {
progressbar.fraction = progress / 2 + 0.5;
}

private void on_swipe_end () {
double epsilon = 0.05;

// 60 FPS → 16.67 ms per frame
spring_timeout = Timeout.add (16, () => {
if (progressbar.fraction < 0.5 + epsilon && progressbar.fraction > 0.5 - epsilon) {
progressbar.fraction = 0.5;
spring_timeout = -1;
return Source.REMOVE;
}

if (progressbar.fraction < 0.5) {
progressbar.fraction *= 1.1;
} else {
progressbar.fraction *= 0.9;
}

return Source.CONTINUE;
});
}
}
1 change: 1 addition & 0 deletions demo/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ executable(
'Views/OverlayBarView.vala',
'Views/SettingsUrisView.vala',
'Views/StyleManagerView.vala',
'Views/SwipeView.vala',
'Views/ToastView.vala',
'Views/UtilsView.vala',
'Views/VideoView.vala',
Expand Down
67 changes: 67 additions & 0 deletions lib/SwipeTracker.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2024 elementary, Inc. (https://elementary.io)
* SPDX-License-Identifier: GPL-2.0-or-later
*/

public class Granite.SwipeTracker : Object {
// This signal is emitted right before a swipe will be started
public signal void begin_swipe ();

// This signal is emitted every time the progress value changes.
public signal void update_swipe (double progress);

// This signal is emitted as soon as the gesture has stopped.
public signal void end_swipe ();

private const int TOUCHPAD_BASE_DISTANCE_H = 400;
// private const int TOUCHPAD_BASE_DISTANCE_V = 300;

// The widget that accepts the swipe event
public unowned Gtk.Widget swipeable { get; construct; }

// How completed the swipe is
private double progress;
private double prev_offset = 0;

public Gtk.GestureDrag drag_gesture;

public SwipeTracker (Gtk.Widget swipeable) {
Object (swipeable: swipeable);
}

construct {
drag_gesture = new Gtk.GestureDrag ();
drag_gesture.drag_begin.connect (on_drag_begin);
drag_gesture.drag_update.connect (on_drag_update);
drag_gesture.drag_end.connect (on_drag_end);

swipeable.add_controller (drag_gesture);
}

~SwipeTracker () {
swipeable.remove_controller (drag_gesture);
}

private void on_drag_begin () {
prev_offset = 0;
progress = 0;
begin_swipe ();
}

private void on_drag_update (Gtk.Gesture gesture, double offset_x, double offset_y) {
double delta, offset;

offset = offset_x;
delta = offset - prev_offset;
prev_offset = offset;

progress += delta / TOUCHPAD_BASE_DISTANCE_H;
progress = progress.clamp (-1, 1);

update_swipe (progress);
}

private void on_drag_end () {
end_swipe ();
}
}
1 change: 1 addition & 0 deletions lib/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ libgranite_sources = files(
'Constants.vala',
'Init.vala',
'StyleManager.vala',
'SwipeTracker.vala',

'Services/Application.vala',
'Services/AsyncMutex.vala',
Expand Down