garmin-decimalface/source/DecimalWatchFaceView.mc
2025-11-04 18:26:12 +01:00

320 lines
12 KiB
MonkeyC

using Toybox.WatchUi;
using Toybox.Graphics;
using Toybox.System;
using Toybox.Lang;
using Toybox.Time;
using Toybox.Time.Gregorian;
using Toybox.Application;
class DecimalWatchFaceView extends WatchUi.WatchFace {
var isHighPowerMode = true; // Track if we're in high power mode
function initialize() {
WatchFace.initialize();
}
// Load your resources here
function onLayout(dc) {
// We draw everything programmatically, no layout needed
}
// Called when this View is brought to the foreground. Restore
// the state of this View and prepare it to be shown. This includes
// loading resources into memory.
function onShow() {
}
// Update the view
function onUpdate(dc) {
// Get the current time
var clockTime = System.getClockTime();
var decimalTime = computeDecimalTime(clockTime);
// Check settings
var useFiveHourMode = Application.Properties.getValue("UseFiveHourMode");
var show12HourShadow = Application.Properties.getValue("Show12HourShadow");
var showNumericTime = Application.Properties.getValue("ShowNumericTime");
var displayTime = decimalTime;
var isMorning = true;
if (useFiveHourMode) {
// Convert 0-10 to 0-5 twice per day
if (decimalTime >= 5.0) {
displayTime = decimalTime - 5.0;
isMorning = false; // Afternoon
} else {
displayTime = decimalTime;
isMorning = true; // Morning
}
}
// Clear the screen
dc.setColor(Graphics.COLOR_BLACK, Graphics.COLOR_BLACK);
dc.clear();
// Draw the watchface
drawWatchFace(dc, useFiveHourMode, isMorning);
// Draw the hands
drawHand(dc, displayTime, useFiveHourMode, show12HourShadow, clockTime);
// Draw digital time displays
if (showNumericTime) {
// Draw decimal time at bottom
drawDigitalTime(dc, decimalTime);
// If 12-hour shadow is also enabled, draw traditional time at top
if (show12HourShadow) {
drawTraditionalTime(dc, clockTime);
}
}
}
// Called when this View is removed from the screen. Save the
// state of this View here. This includes freeing resources from
// memory.
function onHide() {
}
// The user has just looked at their watch. Timers and animations may be started here.
function onExitSleep() {
isHighPowerMode = true;
}
// Terminate any active timers and prepare for slow updates.
function onEnterSleep() {
isHighPowerMode = false;
}
// Compute decimal time from current clock time
// Returns a value from 0.0 to 10.0 representing the day's progress
function computeDecimalTime(clockTime) {
var hour = clockTime.hour;
var min = clockTime.min;
var sec = clockTime.sec;
// Convert to decimal: (hours + minutes/60 + seconds/3600) / 24 * 10
var decimalTime = (hour + (min / 60.0) + (sec / 3600.0)) / 24.0 * 10.0;
return decimalTime;
}
// Draw the watchface circle, tick marks, and numbers
function drawWatchFace(dc, useFiveHourMode, isMorning) {
var width = dc.getWidth();
var height = dc.getHeight();
var centerX = width / 2;
var centerY = height / 2;
var radius = (width < height ? width : height) / 2 - 10;
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
// Determine scale based on mode (5 or 10 hour)
var maxValue = useFiveHourMode ? 5.0 : 10.0;
var tickCount = useFiveHourMode ? 50 : 100; // 0.1 intervals
// Draw tick marks at 0.1 intervals (major at whole numbers, minor at 0.1)
for (var i = 0; i < tickCount; i++) {
var decimalValue = i * 0.1;
var angle = (decimalValue / maxValue) * 360.0 - 90.0; // -90 to start at top
var angleRad = Math.toRadians(angle);
// Determine if this is a major tick (whole number) or minor tick
var isMajor = (i % 10 == 0); // Every 10th position is a whole number
var tickLength = isMajor ? 15 : 8; // Major ticks are longer
var penWidth = isMajor ? 3 : 1; // Major ticks are thicker
// Calculate tick mark positions
var tickOuterX = centerX + radius * Math.cos(angleRad);
var tickOuterY = centerY + radius * Math.sin(angleRad);
var tickInnerX = centerX + (radius - tickLength) * Math.cos(angleRad);
var tickInnerY = centerY + (radius - tickLength) * Math.sin(angleRad);
// Draw tick mark
dc.setPenWidth(penWidth);
dc.drawLine(tickOuterX, tickOuterY, tickInnerX, tickInnerY);
// Draw number only for major ticks
if (isMajor) {
var numberX = centerX + (radius - 38) * Math.cos(angleRad);
var numberY = centerY + (radius - 38) * Math.sin(angleRad);
var numberValue = (i / 10).toNumber(); // Position 0-4 or 0-9
if (useFiveHourMode) {
// 5-hour mode: show different numbers based on morning/afternoon
if (isMorning) {
// Morning: show 5,1,2,3,4 (5 at top, then 1,2,3,4)
if (numberValue == 0) {
numberValue = 5;
}
// else numberValue stays as 1,2,3,4
} else {
// Afternoon: show 10,6,7,8,9 (10 at top, then 6,7,8,9)
if (numberValue == 0) {
numberValue = 10;
} else {
numberValue = numberValue + 5; // 1->6, 2->7, 3->8, 4->9
}
}
} else {
// 10-hour mode: show 10 at top instead of 0
if (numberValue == 0) {
numberValue = 10;
}
}
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
dc.drawText(
numberX,
numberY,
Graphics.FONT_MEDIUM,
numberValue.toString(),
Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER
);
}
}
// Draw center dot
dc.fillCircle(centerX, centerY, 5);
}
// Draw hour, minute, and second hands for current decimal time
function drawHand(dc, decimalTime, useFiveHourMode, show12HourShadow, clockTime) {
var width = dc.getWidth();
var height = dc.getHeight();
var centerX = width / 2;
var centerY = height / 2;
var radius = (width < height ? width : height) / 2 - 10;
// Draw traditional 12-hour shadow hands first (if enabled), so they appear behind
if (show12HourShadow) {
var hour = clockTime.hour;
var minute = clockTime.min;
// Calculate traditional 12-hour hand angle
var traditional12HourAngle = ((hour % 12) + (minute / 60.0)) / 12.0 * 360.0 - 90.0;
var traditional12HourLength = radius - 70; // Same as decimal hour hand
// Calculate traditional 60-minute hand angle
var traditional60MinuteAngle = (minute / 60.0) * 360.0 - 90.0;
var traditional60MinuteLength = radius - 45; // Same as decimal minute hand
// Draw traditional minute hand (dark gray)
drawSingleHand(dc, centerX, centerY, traditional60MinuteAngle, traditional60MinuteLength, 4, Graphics.COLOR_DK_GRAY);
// Draw traditional hour hand (dark red)
drawSingleHand(dc, centerX, centerY, traditional12HourAngle, traditional12HourLength, 6, Graphics.COLOR_DK_RED);
}
// Adjust scale based on mode
var maxValue = useFiveHourMode ? 5.0 : 10.0;
// Calculate hour hand (shows which decimal unit)
// In 10-hour mode: one rotation per day
// In 5-hour mode: two rotations per day
var hourAngle = (decimalTime / maxValue) * 360.0 - 90.0;
var hourHandLength = radius - 70; // Shorter hand
// Calculate minute hand (shows fractional part)
var fractionalPart = decimalTime - decimalTime.toNumber(); // Get decimal part
var minuteAngle = fractionalPart * 360.0 - 90.0;
var minuteHandLength = radius - 45; // Longer hand
// Calculate second hand (shows finer detail)
// Only visible in high power mode
var multiplier = useFiveHourMode ? 5.0 : 10.0;
var secondFractionalPart = (decimalTime * multiplier) - (decimalTime * multiplier).toNumber();
var secondAngle = secondFractionalPart * 360.0 - 90.0;
var secondHandLength = radius - 35; // Longest hand
// Draw second hand first (if in high power mode), so it appears behind other hands
if (isHighPowerMode) {
drawSingleHand(dc, centerX, centerY, secondAngle, secondHandLength, 2, Graphics.COLOR_YELLOW);
}
// Draw minute hand (longer, thinner)
drawSingleHand(dc, centerX, centerY, minuteAngle, minuteHandLength, 4, Graphics.COLOR_WHITE);
// Draw hour hand (shorter, wider)
drawSingleHand(dc, centerX, centerY, hourAngle, hourHandLength, 6, Graphics.COLOR_RED);
// Redraw center dot on top of hands
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
dc.fillCircle(centerX, centerY, 5);
}
// Helper function to draw a single hand
function drawSingleHand(dc, centerX, centerY, angle, handLength, handWidth, color) {
var angleRad = Math.toRadians(angle);
// Calculate hand tip position
var handTipX = centerX + handLength * Math.cos(angleRad);
var handTipY = centerY + handLength * Math.sin(angleRad);
// Calculate hand base positions (for a triangle/arrow shape)
var perpAngleRad1 = angleRad + Math.toRadians(90);
var perpAngleRad2 = angleRad - Math.toRadians(90);
var baseX1 = centerX + handWidth * Math.cos(perpAngleRad1);
var baseY1 = centerY + handWidth * Math.sin(perpAngleRad1);
var baseX2 = centerX + handWidth * Math.cos(perpAngleRad2);
var baseY2 = centerY + handWidth * Math.sin(perpAngleRad2);
// Draw the hand as a filled polygon
dc.setColor(color, Graphics.COLOR_TRANSPARENT);
dc.fillPolygon([
[handTipX, handTipY],
[baseX1, baseY1],
[baseX2, baseY2]
]);
}
// Draw digital display of decimal time
function drawDigitalTime(dc, decimalTime) {
var width = dc.getWidth();
var height = dc.getHeight();
// Format decimal time to 2 decimal places
var timeString = decimalTime.format("%.2f");
// Draw at bottom center
dc.setColor(Graphics.COLOR_WHITE, Graphics.COLOR_TRANSPARENT);
dc.drawText(
width / 2,
height * 0.75,
Graphics.FONT_LARGE,
timeString,
Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER
);
}
// Draw traditional 12-hour time display
function drawTraditionalTime(dc, clockTime) {
var width = dc.getWidth();
var height = dc.getHeight();
var hour = clockTime.hour;
var minute = clockTime.min;
// Convert to 12-hour format
var hour12 = hour % 12;
if (hour12 == 0) {
hour12 = 12;
}
// Format as HH:MM
var timeString = hour12.format("%d") + ":" + minute.format("%02d");
// Draw at top center in dark gray
dc.setColor(Graphics.COLOR_DK_GRAY, Graphics.COLOR_TRANSPARENT);
dc.drawText(
width / 2,
height * 0.25,
Graphics.FONT_LARGE,
timeString,
Graphics.TEXT_JUSTIFY_CENTER | Graphics.TEXT_JUSTIFY_VCENTER
);
}
}