2026-07-04 11:11:18 +02:00
|
|
|
import Toybox.Lang;
|
2026-07-04 09:58:02 +02:00
|
|
|
import Toybox.Graphics;
|
|
|
|
|
import Toybox.WatchUi;
|
2026-07-04 11:11:18 +02:00
|
|
|
import Toybox.System;
|
|
|
|
|
import Toybox.Timer;
|
2026-07-04 16:42:33 +02:00
|
|
|
import Toybox.Math;
|
2026-07-04 09:58:02 +02:00
|
|
|
|
|
|
|
|
class garmin_padelView extends WatchUi.View {
|
2026-07-04 11:11:18 +02:00
|
|
|
private const ACCENT = 0x00E0A0; // padel green (sets/games)
|
|
|
|
|
private const LABEL_COLOR = 0x808080; // muted grey (S/G/pts labels)
|
2026-07-04 16:42:33 +02:00
|
|
|
private const COURT_BLUE = 0x1560BD; // court surface fill (configurable later)
|
2026-07-04 09:58:02 +02:00
|
|
|
|
2026-07-04 11:11:18 +02:00
|
|
|
private var _court as CourtRenderer;
|
|
|
|
|
private var _timer as Timer.Timer;
|
|
|
|
|
|
|
|
|
|
public function initialize() {
|
2026-07-04 09:58:02 +02:00
|
|
|
View.initialize();
|
2026-07-04 11:11:18 +02:00
|
|
|
_court = new CourtRenderer();
|
|
|
|
|
_timer = new Timer.Timer();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function onShow() as Void {
|
|
|
|
|
_timer.start(method(:onTick), 1000, true); // refresh clock + HR every second
|
2026-07-04 09:58:02 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-04 11:11:18 +02:00
|
|
|
public function onHide() as Void {
|
|
|
|
|
_timer.stop();
|
2026-07-04 09:58:02 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-04 11:11:18 +02:00
|
|
|
public function onTick() as Void {
|
|
|
|
|
WatchUi.requestUpdate();
|
2026-07-04 09:58:02 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-04 11:11:18 +02:00
|
|
|
public function onUpdate(dc as Graphics.Dc) as Void {
|
|
|
|
|
var m = getApp().getMatch();
|
|
|
|
|
var w = dc.getWidth();
|
|
|
|
|
var h = dc.getHeight();
|
|
|
|
|
var cx = w / 2;
|
|
|
|
|
var cy = h / 2;
|
|
|
|
|
|
|
|
|
|
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_BLACK);
|
|
|
|
|
dc.clear();
|
|
|
|
|
|
2026-07-04 16:42:33 +02:00
|
|
|
_court.draw(dc, cx, cy, m.servingQuadrant(), !m.isMatchOver(), COURT_BLUE);
|
2026-07-04 11:11:18 +02:00
|
|
|
_drawLeftColumn(dc, m);
|
|
|
|
|
_drawRightColumn(dc, m, w);
|
2026-07-04 16:42:33 +02:00
|
|
|
_drawHeartRate(dc, cx, cy);
|
|
|
|
|
_drawTime(dc, cx, h - 78);
|
2026-07-04 11:11:18 +02:00
|
|
|
|
|
|
|
|
if (m.isMatchOver()) {
|
|
|
|
|
_drawWinner(dc, cx, cy, m);
|
|
|
|
|
}
|
2026-07-04 09:58:02 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-04 11:11:18 +02:00
|
|
|
// Left: S and G mini-columns, your team on top, opponents below.
|
|
|
|
|
private function _drawLeftColumn(dc as Graphics.Dc, m as PadelMatch) as Void {
|
2026-07-04 16:42:33 +02:00
|
|
|
var sX = 30;
|
|
|
|
|
var gX = 76;
|
|
|
|
|
var labelY = 148;
|
|
|
|
|
var usY = 184;
|
|
|
|
|
var themY = 240;
|
2026-07-04 11:11:18 +02:00
|
|
|
|
|
|
|
|
dc.setColor(LABEL_COLOR, Graphics.COLOR_TRANSPARENT);
|
|
|
|
|
dc.drawText(sX, labelY, Graphics.FONT_XTINY, "S", Graphics.TEXT_JUSTIFY_CENTER);
|
|
|
|
|
dc.drawText(gX, labelY, Graphics.FONT_XTINY, "G", Graphics.TEXT_JUSTIFY_CENTER);
|
|
|
|
|
|
|
|
|
|
dc.setColor(ACCENT, Graphics.COLOR_TRANSPARENT);
|
|
|
|
|
dc.drawText(sX, usY, Graphics.FONT_TINY, m.sets(US).toString(), Graphics.TEXT_JUSTIFY_CENTER);
|
|
|
|
|
dc.drawText(sX, themY, Graphics.FONT_TINY, m.sets(THEM).toString(), Graphics.TEXT_JUSTIFY_CENTER);
|
2026-07-04 16:42:33 +02:00
|
|
|
|
|
|
|
|
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
|
2026-07-04 11:11:18 +02:00
|
|
|
dc.drawText(gX, usY, Graphics.FONT_TINY, m.games(US).toString(), Graphics.TEXT_JUSTIFY_CENTER);
|
|
|
|
|
dc.drawText(gX, themY, Graphics.FONT_TINY, m.games(THEM).toString(), Graphics.TEXT_JUSTIFY_CENTER);
|
2026-07-04 09:58:02 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-04 16:42:33 +02:00
|
|
|
// Right: Pts column, your team on top, opponents below.
|
2026-07-04 11:11:18 +02:00
|
|
|
private function _drawRightColumn(dc as Graphics.Dc, m as PadelMatch, w as Number) as Void {
|
2026-07-04 16:42:33 +02:00
|
|
|
var pX = w - 46;
|
|
|
|
|
var labelY = 148;
|
|
|
|
|
var usY = 184;
|
|
|
|
|
var themY = 240;
|
2026-07-04 11:11:18 +02:00
|
|
|
|
|
|
|
|
dc.setColor(LABEL_COLOR, Graphics.COLOR_TRANSPARENT);
|
2026-07-04 16:42:33 +02:00
|
|
|
dc.drawText(pX, labelY, Graphics.FONT_XTINY, "Pts", Graphics.TEXT_JUSTIFY_CENTER);
|
2026-07-04 11:11:18 +02:00
|
|
|
|
|
|
|
|
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
|
|
|
|
|
dc.drawText(pX, usY, Graphics.FONT_TINY, m.pointLabel(US), Graphics.TEXT_JUSTIFY_CENTER);
|
|
|
|
|
dc.drawText(pX, themY, Graphics.FONT_TINY, m.pointLabel(THEM), Graphics.TEXT_JUSTIFY_CENTER);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-04 16:42:33 +02:00
|
|
|
// Heart rate as a top arc gauge: 5 zone-colored segments (grey/blue/green/
|
|
|
|
|
// orange/red), a white marker on the current zone, and the bpm number just
|
|
|
|
|
// below — mirroring the standard Garmin run-activity HR view.
|
|
|
|
|
private function _drawHeartRate(dc as Graphics.Dc, cx as Number, cy as Number) as Void {
|
2026-07-04 11:11:18 +02:00
|
|
|
var hr = getApp().getHeartRate();
|
|
|
|
|
var bpm = hr.getHeartRate();
|
2026-07-04 16:42:33 +02:00
|
|
|
var zone = hr.getZone();
|
|
|
|
|
var r = cx - 9; // arc radius, hugging the top bezel
|
|
|
|
|
|
|
|
|
|
// 5 zone segments across the top (150deg .. 30deg, clockwise)
|
|
|
|
|
dc.setPenWidth(14);
|
|
|
|
|
for (var k = 0; k < 5; k += 1) {
|
|
|
|
|
var segStart = 150 - k * 24;
|
|
|
|
|
dc.setColor(_zoneColor(k + 1), Graphics.COLOR_TRANSPARENT);
|
|
|
|
|
dc.drawArc(cx, cy, r, Graphics.ARC_CLOCKWISE, segStart - 2, segStart - 22);
|
|
|
|
|
}
|
|
|
|
|
dc.setPenWidth(1);
|
|
|
|
|
|
|
|
|
|
// marker on the current zone
|
|
|
|
|
if (zone >= 1 && zone <= 5) {
|
|
|
|
|
var mid = 150 - (zone - 1) * 24 - 12;
|
|
|
|
|
var rad = Math.toRadians(mid);
|
|
|
|
|
var mx = (cx + r * Math.cos(rad)).toNumber();
|
|
|
|
|
var my = (cy - r * Math.sin(rad)).toNumber();
|
|
|
|
|
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
|
|
|
|
|
dc.fillCircle(mx, my, 8);
|
|
|
|
|
dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);
|
|
|
|
|
dc.drawCircle(mx, my, 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// exact rate, tucked just under the arc (dim placeholder when no reading,
|
|
|
|
|
// so it never reads as a court line)
|
|
|
|
|
var text = (bpm == null) ? "--" : bpm.toString();
|
|
|
|
|
var col = (bpm == null) ? LABEL_COLOR : Graphics.COLOR_WHITE;
|
|
|
|
|
dc.setColor(col, Graphics.COLOR_TRANSPARENT);
|
|
|
|
|
dc.drawText(cx, cy - 178, Graphics.FONT_MEDIUM, text, Graphics.TEXT_JUSTIFY_CENTER);
|
2026-07-04 11:11:18 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-04 16:42:33 +02:00
|
|
|
// Garmin HR zone colors: 1 grey, 2 blue, 3 green, 4 orange, 5 red.
|
|
|
|
|
private function _zoneColor(z as Number) as Number {
|
|
|
|
|
if (z == 1) { return Graphics.COLOR_LT_GRAY; }
|
|
|
|
|
if (z == 2) { return Graphics.COLOR_BLUE; }
|
|
|
|
|
if (z == 3) { return Graphics.COLOR_GREEN; }
|
|
|
|
|
if (z == 4) { return Graphics.COLOR_ORANGE; }
|
|
|
|
|
return Graphics.COLOR_RED;
|
2026-07-04 11:11:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function _drawTime(dc as Graphics.Dc, cx as Number, y as Number) as Void {
|
|
|
|
|
var now = System.getClockTime();
|
|
|
|
|
var text = now.hour.format("%02d") + ":" + now.min.format("%02d");
|
|
|
|
|
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
|
2026-07-04 16:42:33 +02:00
|
|
|
dc.drawText(cx, y, Graphics.FONT_MEDIUM, text, Graphics.TEXT_JUSTIFY_CENTER);
|
2026-07-04 11:11:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function _drawWinner(dc as Graphics.Dc, cx as Number, cy as Number, m as PadelMatch) as Void {
|
|
|
|
|
var text = (m.winner() == US) ? "You win!" : "They win";
|
|
|
|
|
dc.setColor(ACCENT, Graphics.COLOR_BLACK);
|
|
|
|
|
dc.fillRectangle(cx - 70, cy - 18, 140, 36);
|
|
|
|
|
dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_TRANSPARENT);
|
2026-07-04 13:51:25 +02:00
|
|
|
dc.drawText(cx, cy, Graphics.FONT_SMALL, text, Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER);
|
2026-07-04 11:11:18 +02:00
|
|
|
}
|
2026-07-04 09:58:02 +02:00
|
|
|
}
|