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