aboutsummaryrefslogtreecommitdiff
path: root/source/ui/Gesture.mc
blob: 0824eb1fdc6f7e0864572a0393c252b3877ed2cd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Author:  cirakg & claude ai
// Summary: Vertical swipes and drags that scroll a custom view (the Forerunner 70 sends no next/previous-page behaviour for them), scrolling once per gesture.

import Toybox.Lang;
import Toybox.System;
import Toybox.WatchUi;

// A touch gesture can reach a delegate several ways at once: as drag events,
// as a swipe, and as the next/previous-page behaviour. Some watches send only
// the drag. The first one to scroll marks the time; the others arriving within
// DEDUPE_MS are ignored, so one gesture scrolls once. The UP/DOWN buttons
// never mark, so they are only held back right after a touch gesture.
module Gesture {

    const DEDUPE_MS = 300;
    // A drag shorter than this part of the screen height does not scroll.
    const MIN_DRAG_DIVISOR = 8;
    // A drag longer than this part still counts as a drag, not a tap.
    const SLOP_DIVISOR = 32;

    var _scrolledMs as Number? = null;
    var _draggedMs as Number? = null;
    var _startY as Number? = null;

    function _within(since as Number?) as Boolean {
        if (since == null) {
            return false;
        }
        var elapsed = System.getTimer() - since;
        return elapsed >= 0 && elapsed < DEDUPE_MS;
    }

    // True right after a touch gesture scrolled: next/previous-page and
    // swipe events in this window belong to that gesture.
    function recent() as Boolean {
        return _within(_scrolledMs);
    }

    // True right after a drag: a tap arriving now is its end, not a tap.
    function tapIgnored() as Boolean {
        return _within(_draggedMs);
    }

    function isVertical(event as WatchUi.SwipeEvent) as Boolean {
        var direction = event.getDirection();
        return direction == WatchUi.SWIPE_UP || direction == WatchUi.SWIPE_DOWN;
    }

    // The step for a vertical swipe: 1 (next, swipe up), -1 (previous), or 0
    // when this gesture already scrolled.
    function swipeStep(event as WatchUi.SwipeEvent) as Number {
        if (recent()) {
            return 0;
        }
        _scrolledMs = System.getTimer();
        return event.getDirection() == WatchUi.SWIPE_UP ? 1 : -1;
    }

    // Feed a drag event. At the end of a long enough vertical drag returns
    // the signed number of units to scroll (positive = next, finger moved
    // up), at least 1 and distance / unit otherwise; 0 for anything else.
    function dragStep(event as WatchUi.DragEvent, unit as Number) as Number {
        var y = event.getCoordinates()[1];
        var type = event.getType();
        if (type == WatchUi.DRAG_TYPE_START) {
            _startY = y;
            return 0;
        }
        var start = _startY;
        if (type != WatchUi.DRAG_TYPE_STOP || start == null) {
            return 0;
        }
        _startY = null;
        var height = System.getDeviceSettings().screenHeight;
        var distance = start - y;
        var length = distance < 0 ? -distance : distance;
        if (length > height / SLOP_DIVISOR) {
            _draggedMs = System.getTimer();
        }
        if (length < height / MIN_DRAG_DIVISOR || recent()) {
            return 0;
        }
        _scrolledMs = System.getTimer();
        var units = unit > 0 ? length / unit : 1;
        units = units < 1 ? 1 : units;
        return distance < 0 ? -units : units;
    }
}