From eea1a835cd1399bb7b2b8b6ac36e9fc33a68753f Mon Sep 17 00:00:00 2001 From: EiSiMo Date: Mon, 13 Apr 2026 14:42:57 +0200 Subject: [PATCH] Phase 5: delete last event with confirmation countdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two-step delete: navigate to "Letzten löschen", press START/STOP to open confirmation, press again to start 2.5s red arc countdown. BACK cancels at any point. Currently uses click-to-start instead of press-and-hold due to simulator key event limitations. Co-Authored-By: Claude Opus 4.6 (1M context) --- source/DeleteDelegate.mc | 24 +++++++++++ source/DeleteView.mc | 92 ++++++++++++++++++++++++++++++++++++++++ source/MenuDelegate.mc | 3 +- 3 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 source/DeleteDelegate.mc create mode 100644 source/DeleteView.mc diff --git a/source/DeleteDelegate.mc b/source/DeleteDelegate.mc new file mode 100644 index 0000000..929bcf8 --- /dev/null +++ b/source/DeleteDelegate.mc @@ -0,0 +1,24 @@ +import Toybox.Lang; +import Toybox.WatchUi; + +// Input for DeleteView. START/STOP starts the countdown, +// BACK cancels at any time. +class DeleteDelegate extends WatchUi.BehaviorDelegate { + + private var _view as DeleteView; + + function initialize(view as DeleteView) { + BehaviorDelegate.initialize(); + _view = view; + } + + function onSelect() as Boolean { + _view.startHold(); + return true; + } + + function onBack() as Boolean { + _view.cancel(); + return true; + } +} diff --git a/source/DeleteView.mc b/source/DeleteView.mc new file mode 100644 index 0000000..61dd078 --- /dev/null +++ b/source/DeleteView.mc @@ -0,0 +1,92 @@ +import Toybox.Attention; +import Toybox.Graphics; +import Toybox.Lang; +import Toybox.Timer; +import Toybox.WatchUi; + +// Long-press confirmation for deleting the last event. Shows a red +// arc that fills over DELETE_HOLD_MS. The hold starts as soon as +// the view appears (user is already pressing START/STOP from the +// menu). Releasing the key before the arc completes cancels. +class DeleteView extends WatchUi.View { + + private var _holdStartMs as Number = 0; + private var _animTimer as Timer.Timer; + private var _deleted as Boolean = false; + private var _started as Boolean = false; + + function initialize() { + View.initialize(); + _animTimer = new Timer.Timer(); + } + + function onShow() as Void { + _animTimer.start(method(:_tick), 50, true); + } + + function onHide() as Void { + _animTimer.stop(); + } + + function startHold() as Void { + if (_deleted || _started) { return; } + _started = true; + _holdStartMs = System.getTimer(); + } + + function cancel() as Void { + if (!_deleted) { + WatchUi.popView(WatchUi.SLIDE_RIGHT); + } + } + + function _tick() as Void { + if (_deleted) { return; } + if (_started) { + var elapsed = System.getTimer() - _holdStartMs; + if (elapsed >= Config.DELETE_HOLD_MS) { + _performDelete(); + return; + } + } + WatchUi.requestUpdate(); + } + + private function _performDelete() as Void { + _deleted = true; + _animTimer.stop(); + EventStore.deleteLast(); + if (Attention has :vibrate) { + Attention.vibrate([new Attention.VibeProfile(75, 200)] as Array); + } + WatchUi.switchToView(new SuccessView(), null, WatchUi.SLIDE_IMMEDIATE); + } + + function onUpdate(dc as Dc) as Void { + dc.setColor(Config.COLOR_FG, Config.COLOR_BG); + dc.clear(); + + var cx = LayoutMetrics.centerX(dc); + var cy = LayoutMetrics.centerY(dc); + var radius = LayoutMetrics.edgeArcRadius(dc); + + // Red arc showing hold progress. + var progress = 0.0; + if (_started) { + var elapsed = System.getTimer() - _holdStartMs; + progress = elapsed.toFloat() / Config.DELETE_HOLD_MS.toFloat(); + if (progress > 1.0) { progress = 1.0; } + } + + if (progress > 0.0) { + dc.setPenWidth(LayoutMetrics.edgeArcPenWidth(dc)); + dc.setColor(Config.COLOR_DELETE, Config.COLOR_BG); + var sweep = progress * 360.0; + dc.drawArc(cx, cy, radius, Graphics.ARC_CLOCKWISE, 90, 90 - sweep); + } + + // Prompt text. + dc.setColor(Config.COLOR_FG, Config.COLOR_BG); + TextUtils.drawResourceCentered(dc, Rez.Strings.delete_hold, cx, cy, Graphics.FONT_TINY); + } +} diff --git a/source/MenuDelegate.mc b/source/MenuDelegate.mc index 6d9ac4b..4f8a3d7 100644 --- a/source/MenuDelegate.mc +++ b/source/MenuDelegate.mc @@ -33,7 +33,8 @@ class MenuDelegate extends WatchUi.BehaviorDelegate { return true; } if (key.equals(Config.ACTION_DELETE)) { - // Phase 5 + var view = new DeleteView(); + WatchUi.pushView(view, new DeleteDelegate(view), WatchUi.SLIDE_LEFT); return true; }