Tap-to-rotate: tap icon to select, tap selected to open
Tapping an unselected icon in the menu ring rotates to it with animation. Tapping the already-selected icon opens it. Icon positions are tracked from last render for hit detection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
bcac9cbaa4
commit
a45f1b5215
2 changed files with 61 additions and 3 deletions
|
|
@ -1,9 +1,9 @@
|
||||||
import Toybox.Lang;
|
import Toybox.Lang;
|
||||||
import Toybox.WatchUi;
|
import Toybox.WatchUi;
|
||||||
|
|
||||||
// Routes menu input. UP/DOWN rotate the ring. START/STOP dispatches:
|
// Routes menu input. UP/DOWN rotate the ring. START/STOP opens
|
||||||
// - event types (arrest, start, …) push the GPS LoadingView
|
// the selected item. Tap on an icon rotates to it; tap on the
|
||||||
// - history / delete are stubbed until later phases
|
// already-selected icon opens it.
|
||||||
class MenuDelegate extends WatchUi.BehaviorDelegate {
|
class MenuDelegate extends WatchUi.BehaviorDelegate {
|
||||||
|
|
||||||
private var _view as MenuView;
|
private var _view as MenuView;
|
||||||
|
|
@ -23,7 +23,27 @@ class MenuDelegate extends WatchUi.BehaviorDelegate {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onTap(evt as WatchUi.ClickEvent) as Boolean {
|
||||||
|
var coords = evt.getCoordinates();
|
||||||
|
var tapX = coords[0] as Number;
|
||||||
|
var tapY = coords[1] as Number;
|
||||||
|
var idx = _view.itemIndexAt(tapX, tapY);
|
||||||
|
if (idx < 0) { return false; }
|
||||||
|
|
||||||
|
if (idx == _view.selectedIndex()) {
|
||||||
|
// Tap on already-selected item → open it.
|
||||||
|
return _openSelected();
|
||||||
|
}
|
||||||
|
// Tap on another item → rotate to it.
|
||||||
|
_view.rotateTo(idx);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
function onSelect() as Boolean {
|
function onSelect() as Boolean {
|
||||||
|
return _openSelected();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _openSelected() as Boolean {
|
||||||
var item = _view.selectedItem();
|
var item = _view.selectedItem();
|
||||||
var key = item[:key] as String;
|
var key = item[:key] as String;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,41 @@ class MenuView extends WatchUi.View {
|
||||||
return _items[_selectedIndex];
|
return _items[_selectedIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectedIndex() as Number {
|
||||||
|
return _selectedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
private var _iconPositions as Array = [];
|
||||||
|
|
||||||
|
// Returns the index of the icon closest to (tapX, tapY), or -1.
|
||||||
|
function itemIndexAt(tapX as Number, tapY as Number) as Number {
|
||||||
|
var bestIdx = -1;
|
||||||
|
var bestDist = 999999;
|
||||||
|
for (var i = 0; i < _iconPositions.size(); i++) {
|
||||||
|
var pos = _iconPositions[i] as Array;
|
||||||
|
var ix = pos[0] as Number;
|
||||||
|
var iy = pos[1] as Number;
|
||||||
|
var size = pos[2] as Number;
|
||||||
|
var dx = tapX - ix;
|
||||||
|
var dy = tapY - iy;
|
||||||
|
var dist = dx * dx + dy * dy;
|
||||||
|
var maxDist = (size / 2) * (size / 2);
|
||||||
|
if (dist < maxDist && dist < bestDist) {
|
||||||
|
bestDist = dist;
|
||||||
|
bestIdx = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rotate to a specific index with animation.
|
||||||
|
function rotateTo(index as Number) as Void {
|
||||||
|
if (index == _selectedIndex) { return; }
|
||||||
|
_startAnim(_selectedIndex);
|
||||||
|
_selectedIndex = index;
|
||||||
|
Application.Storage.setValue(STORAGE_KEY, _selectedIndex);
|
||||||
|
}
|
||||||
|
|
||||||
function onUpdate(dc as Dc) as Void {
|
function onUpdate(dc as Dc) as Void {
|
||||||
dc.setColor(Config.COLOR_FG, Config.COLOR_BG);
|
dc.setColor(Config.COLOR_FG, Config.COLOR_BG);
|
||||||
dc.clear();
|
dc.clear();
|
||||||
|
|
@ -102,6 +137,7 @@ class MenuView extends WatchUi.View {
|
||||||
if (diff < -n / 2.0) { diff = diff + n; }
|
if (diff < -n / 2.0) { diff = diff + n; }
|
||||||
var currentOffset = fromOffset + diff * ease;
|
var currentOffset = fromOffset + diff * ease;
|
||||||
|
|
||||||
|
_iconPositions = new [n];
|
||||||
for (var i = 0; i < n; i++) {
|
for (var i = 0; i < n; i++) {
|
||||||
var angle = selectionAngle + (i.toFloat() - currentOffset) * step;
|
var angle = selectionAngle + (i.toFloat() - currentOffset) * step;
|
||||||
var x = (cx + radius * Math.cos(angle)).toNumber();
|
var x = (cx + radius * Math.cos(angle)).toNumber();
|
||||||
|
|
@ -119,6 +155,8 @@ class MenuView extends WatchUi.View {
|
||||||
targetSize = (selSize - (selSize - baseSize) * ease).toNumber();
|
targetSize = (selSize - (selSize - baseSize) * ease).toNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_iconPositions[i] = [x, y, targetSize];
|
||||||
|
|
||||||
// Colored circle behind icon.
|
// Colored circle behind icon.
|
||||||
var color = _items[i][:color] as Number;
|
var color = _items[i][:color] as Number;
|
||||||
dc.setColor(color, Config.COLOR_BG);
|
dc.setColor(color, Config.COLOR_BG);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue