- Agrega cluster_screen.h con diseno estilo cockpit (Pantalla 4). - Tacometro horizontal deportivo (RPM) con 12 segmentos de color dinamico y lectura digital. - Velocimetro digital gigante en tipografia DSEG7 44pt con etiqueta de origen GPS/OBD y riel luminico. - Relojes inferiores dobles para Temperatura de Refrigerante (°C) y Voltaje de Bateria (V). - Expansion del sistema de navegacion y paginacion a 5 pantallas (0 a 4) con indicadores sincronizados. - Integracion completa con transiciones suaves y soporte para los 9 temas de color.
608 lines
21 KiB
C++
608 lines
21 KiB
C++
#pragma once
|
|
|
|
#include "DSEG7_Modern_BoldItalic_34.h"
|
|
#include "DSEG7_Modern_BoldItalic_80.h"
|
|
#include "Latin_Sans_Bold_26.h"
|
|
#include "rainbow_text.h"
|
|
#include "gps_module.h"
|
|
#include <Arduino.h>
|
|
#include <TFT_eSPI.h>
|
|
|
|
extern TFT_eSPI tft;
|
|
extern uint16_t getThemePrimaryColor();
|
|
extern uint16_t getThemeAccentColor();
|
|
extern uint16_t getThemeBgColor();
|
|
extern bool isRainbowTheme();
|
|
extern int8_t currentScreenPage;
|
|
extern int8_t fullScreenCell;
|
|
|
|
// Spanish 3-letter Month abbreviations
|
|
static const char *const SPANISH_MONTHS_SHORT[12] = {
|
|
"ENE", "FEB", "MAR", "ABR", "MAY", "JUN",
|
|
"JUL", "AGO", "SEP", "OCT", "NOV", "DIC"};
|
|
|
|
// Previous values for flicker-free redrawing
|
|
static uint8_t oldClockHour = 255;
|
|
static uint8_t oldClockMin = 255;
|
|
static uint8_t oldClockSec = 255;
|
|
static uint8_t oldClockDay = 255;
|
|
static uint8_t oldClockMonth = 255;
|
|
static uint16_t oldClockYear = 0;
|
|
|
|
// Smooth pre-rendered title sprites for Day of Week and Year (100% anti-aliased Latin_Sans_Bold_26 matching Page 1)
|
|
static TFT_eSprite sprDayTitle(&tft);
|
|
static uint8_t sprDayWday = 255;
|
|
|
|
inline void invalidateGpsClockTitles() {
|
|
sprDayWday = 255;
|
|
}
|
|
|
|
inline void updateDayTitleSprite(uint8_t wday) {
|
|
if (sprDayWday == wday && sprDayTitle.created()) return;
|
|
if (!sprDayTitle.created()) {
|
|
sprDayTitle.createSprite(160, 26);
|
|
}
|
|
if (sprDayTitle.created()) {
|
|
uint16_t cardBg = 0x10A2;
|
|
sprDayTitle.loadFont(Latin_Sans_Bold_26);
|
|
sprDayTitle.setTextPadding(0);
|
|
sprDayTitle.fillSprite(cardBg);
|
|
if (isYapaTheme()) {
|
|
int curX = 0;
|
|
char buf[2] = {0, 0};
|
|
String dName = SPANISH_DAYS[wday];
|
|
for (size_t i = 0; i < dName.length(); i++) {
|
|
buf[0] = dName[i];
|
|
if (buf[0] == ' ') { curX += sprDayTitle.textWidth(buf); continue; }
|
|
sprDayTitle.setTextColor(getYapaColor(i), cardBg);
|
|
sprDayTitle.drawString(buf, curX, 0);
|
|
curX += sprDayTitle.textWidth(buf);
|
|
}
|
|
} else {
|
|
sprDayTitle.setTextColor(TFT_WHITE, cardBg);
|
|
sprDayTitle.drawString(SPANISH_DAYS[wday], 0, 0, 1);
|
|
}
|
|
sprDayTitle.unloadFont();
|
|
if (isRainbowTheme()) {
|
|
applyRainbowToSprite(sprDayTitle, 4 + 116, 125 + 22, cardBg);
|
|
}
|
|
sprDayWday = wday;
|
|
}
|
|
}
|
|
|
|
static TFT_eSprite sprYearTitle(&tft);
|
|
static uint16_t sprYearVal = 0;
|
|
static uint16_t sprYearCol = 0;
|
|
|
|
inline void updateYearTitleSprite(uint16_t year, uint16_t priCol) {
|
|
if (sprYearVal == year && sprYearCol == priCol && sprYearTitle.created()) return;
|
|
if (!sprYearTitle.created()) {
|
|
sprYearTitle.createSprite(80, 26);
|
|
}
|
|
if (sprYearTitle.created()) {
|
|
uint16_t cardBg = 0x10A2;
|
|
sprYearTitle.loadFont(Latin_Sans_Bold_26);
|
|
sprYearTitle.setTextPadding(0);
|
|
sprYearTitle.fillSprite(cardBg);
|
|
if (isYapaTheme()) {
|
|
int curX = 0;
|
|
char buf[2] = {0, 0};
|
|
String yStr = String(year);
|
|
for (size_t i = 0; i < yStr.length(); i++) {
|
|
buf[0] = yStr[i];
|
|
sprYearTitle.setTextColor(getYapaColor(i + 2), cardBg);
|
|
sprYearTitle.drawString(buf, curX, 0);
|
|
curX += sprYearTitle.textWidth(buf);
|
|
}
|
|
} else {
|
|
sprYearTitle.setTextColor(priCol, cardBg);
|
|
sprYearTitle.drawString(String(year).c_str(), 0, 0, 1);
|
|
}
|
|
sprYearTitle.unloadFont();
|
|
if (isRainbowTheme()) {
|
|
applyRainbowToSprite(sprYearTitle, 4 + 116, 125 + 64, cardBg);
|
|
}
|
|
sprYearVal = year;
|
|
sprYearCol = priCol;
|
|
}
|
|
}
|
|
|
|
inline void initGpsClockTitles() {
|
|
updateDayTitleSprite(gpsDayOfWeek % 7);
|
|
updateYearTitleSprite(gpsYear, isRainbowTheme() ? (uint16_t)0xFFE0 : getThemeAccentColor());
|
|
}
|
|
|
|
// Draw Full-Width Date Card (Bottom) - Clean, prominent, automotive badge
|
|
// layout
|
|
inline void drawDateCardContent(int cardX, int cardY, int cardW, int cardH,
|
|
uint16_t cardBg, int x_offset = 0) {
|
|
if (cardX + cardW <= 0 || cardX >= 320)
|
|
return;
|
|
|
|
uint16_t accCol = isRainbowTheme() ? (uint16_t)0xFD20 : getThemeAccentColor();
|
|
uint16_t yearCol = isRainbowTheme() ? (uint16_t)0xFFE0 : accCol;
|
|
uint16_t cardBorder = 0x2945;
|
|
uint8_t wday = gpsDayOfWeek % 7;
|
|
uint8_t mIdx = (gpsMonth >= 1 && gpsMonth <= 12) ? (gpsMonth - 1) : 0;
|
|
String mShort = SPANISH_MONTHS_SHORT[mIdx];
|
|
String dayStr = (gpsDay < 10) ? "0" + String(gpsDay) : String(gpsDay);
|
|
|
|
// Left vertical accent bar
|
|
tft.fillRoundRect(cardX + 6, cardY + 8, 4, cardH - 16, 2, accCol);
|
|
|
|
// --- CALENDAR BADGE (Left side: 80x82 px) ---
|
|
int bx = cardX + 20;
|
|
int by = cardY + 14;
|
|
int bw = 80;
|
|
int bh = 82;
|
|
|
|
// Badge body
|
|
tft.fillRoundRect(bx, by, bw, bh, 6, 0x18C3);
|
|
tft.drawRoundRect(bx, by, bw, bh, 6, cardBorder);
|
|
|
|
// Badge top header (Month abbreviated in Accent Color)
|
|
if (isYapaTheme()) {
|
|
tft.fillRoundRect(bx, by, bw, 24, 6, 0x3A53);
|
|
tft.fillRect(bx, by + 18, bw, 6, 0x3A53);
|
|
int totalW = tft.textWidth(mShort.c_str(), 2);
|
|
int startX = bx + (bw - totalW) / 2;
|
|
char buf[2] = {0, 0};
|
|
for (size_t i = 0; i < mShort.length(); i++) {
|
|
buf[0] = mShort[i];
|
|
tft.setTextColor(getYapaColor(i), 0x3A53);
|
|
tft.drawString(buf, startX, by + 4, 2);
|
|
startX += tft.textWidth(buf, 2);
|
|
}
|
|
} else {
|
|
tft.fillRoundRect(bx, by, bw, 24, 6, accCol);
|
|
tft.fillRect(bx, by + 18, bw, 6, accCol); // flatten lower header corners
|
|
tft.setTextColor(TFT_BLACK, accCol);
|
|
tft.drawCentreString(mShort.c_str(), bx + (bw / 2), by + 4, 2);
|
|
}
|
|
|
|
// Badge body: Big Day Number in DSEG7 34pt
|
|
tft.loadFont(DSEG7_Modern_BoldItalic_34);
|
|
tft.setTextPadding(0);
|
|
if (isYapaTheme()) {
|
|
int totalW = tft.textWidth(dayStr.c_str());
|
|
int startX = bx + (bw - totalW) / 2;
|
|
char buf[2] = {0, 0};
|
|
for (size_t i = 0; i < dayStr.length(); i++) {
|
|
buf[0] = dayStr[i];
|
|
tft.setTextColor(getYapaColor(i), 0x18C3);
|
|
tft.drawString(buf, startX, by + 34);
|
|
startX += tft.textWidth(buf);
|
|
}
|
|
} else {
|
|
tft.setTextColor(TFT_WHITE, 0x18C3);
|
|
tft.drawCentreString(dayStr.c_str(), bx + (bw / 2), by + 34, 1);
|
|
}
|
|
tft.unloadFont();
|
|
|
|
// --- DATE DETAILS (Right side) ---
|
|
int tx = cardX + 116;
|
|
|
|
// Day of week in Latin Sans Bold 26 (Smooth antialiased, White, uppercase matching Page 1)
|
|
if (isYapaTheme()) {
|
|
tft.loadFont(Latin_Sans_Bold_26);
|
|
int curX = tx;
|
|
char buf[2] = {0, 0};
|
|
String dName = SPANISH_DAYS[wday];
|
|
for (size_t i = 0; i < dName.length(); i++) {
|
|
buf[0] = dName[i];
|
|
if (buf[0] == ' ') { curX += tft.textWidth(buf); continue; }
|
|
tft.setTextColor(getYapaColor(i), cardBg);
|
|
tft.drawString(buf, curX, cardY + 22);
|
|
curX += tft.textWidth(buf);
|
|
}
|
|
tft.unloadFont();
|
|
} else {
|
|
updateDayTitleSprite(wday);
|
|
if (sprDayTitle.created()) {
|
|
sprDayTitle.pushSprite(tx, cardY + 22);
|
|
} else {
|
|
tft.loadFont(Latin_Sans_Bold_26);
|
|
tft.setTextPadding(0);
|
|
tft.setTextColor(TFT_WHITE, cardBg);
|
|
tft.drawString(SPANISH_DAYS[wday], tx, cardY + 22, 1);
|
|
tft.unloadFont();
|
|
}
|
|
}
|
|
|
|
// Elegant two-tone horizontal dividing line
|
|
tft.drawFastHLine(tx, cardY + 54, 180, cardBorder);
|
|
tft.drawFastHLine(tx, cardY + 54, 50, isYapaTheme() ? getYapaColor(0) : accCol);
|
|
|
|
// Year in Latin Sans Bold 26 (Smooth antialiased, Accent color)
|
|
if (isYapaTheme()) {
|
|
tft.loadFont(Latin_Sans_Bold_26);
|
|
int curX = tx;
|
|
char buf[2] = {0, 0};
|
|
String yStr = String(gpsYear);
|
|
for (size_t i = 0; i < yStr.length(); i++) {
|
|
buf[0] = yStr[i];
|
|
tft.setTextColor(getYapaColor(i + 2), cardBg);
|
|
tft.drawString(buf, curX, cardY + 64);
|
|
curX += tft.textWidth(buf);
|
|
}
|
|
tft.unloadFont();
|
|
} else {
|
|
updateYearTitleSprite(gpsYear, yearCol);
|
|
if (sprYearTitle.created()) {
|
|
sprYearTitle.pushSprite(tx, cardY + 64);
|
|
} else {
|
|
tft.loadFont(Latin_Sans_Bold_26);
|
|
tft.setTextPadding(0);
|
|
tft.setTextColor(yearCol, cardBg);
|
|
tft.drawString(String(gpsYear).c_str(), tx, cardY + 64, 1);
|
|
tft.unloadFont();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Authentic DSEG7 antialiased dot alpha matrix (from
|
|
// DSEG7_Modern_BoldItalic_80)
|
|
static const uint8_t DSEG7_DOT_ALPHA[11][13] PROGMEM = {
|
|
{0x21, 0xEB, 0xEE, 0xED, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEC, 0xEE, 0xD7,
|
|
0x0A},
|
|
{0x35, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDC,
|
|
0x00},
|
|
{0x47, 0xFE, 0xFB, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFB, 0xFE, 0xC5,
|
|
0x00},
|
|
{0x5C, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0xFF, 0xB2,
|
|
0x00},
|
|
{0x71, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0x9E,
|
|
0x00},
|
|
{0x86, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0x88,
|
|
0x00},
|
|
{0x9C, 0xFF, 0xFB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0x73,
|
|
0x00},
|
|
{0xB1, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFB, 0xFF, 0x5E,
|
|
0x00},
|
|
{0xC5, 0xFF, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFD, 0xFF, 0x49,
|
|
0x00},
|
|
{0xD7, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0x35,
|
|
0x00},
|
|
{0xE6, 0xFF, 0xFD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFC, 0x24,
|
|
0x00},
|
|
};
|
|
|
|
// Helper: Draw authentic 7-segment italic colon dots with true anti-aliasing
|
|
// matching DSEG7 typography and optional rainbow gradient
|
|
template <typename T>
|
|
inline void draw7SegDot(T &dst, int x0, int y0, uint16_t color,
|
|
uint16_t bgCol, int clip_h = 240, bool rainbow = false) {
|
|
dst.startWrite();
|
|
for (int dy = 0; dy < 11; dy++) {
|
|
int curY = y0 + dy;
|
|
if (curY < 0 || curY >= clip_h)
|
|
continue;
|
|
|
|
int fl = 0;
|
|
int fxs = 0;
|
|
for (int dx = 0; dx < 13; dx++) {
|
|
uint8_t a = pgm_read_byte(&(DSEG7_DOT_ALPHA[dy][dx]));
|
|
int curX = x0 + dx;
|
|
if (a == 0) {
|
|
if (fl) {
|
|
dst.drawFastHLine(fxs, curY, fl, color);
|
|
fl = 0;
|
|
}
|
|
dst.drawPixel(curX, curY, bgCol);
|
|
} else if (a >= 235) {
|
|
if (fl == 0)
|
|
fxs = curX;
|
|
fl++;
|
|
} else {
|
|
if (fl) {
|
|
dst.drawFastHLine(fxs, curY, fl, color);
|
|
fl = 0;
|
|
}
|
|
uint16_t fg = rainbow ? getRainbowScreenColor(curX, curY) : color;
|
|
dst.drawPixel(curX, curY, dst.alphaBlend(a, fg, bgCol));
|
|
}
|
|
}
|
|
if (fl) {
|
|
if (rainbow) {
|
|
for (int i = 0; i < fl; i++) {
|
|
dst.drawPixel(fxs + i, curY, getRainbowScreenColor(fxs + i, curY));
|
|
}
|
|
} else {
|
|
dst.drawFastHLine(fxs, curY, fl, color);
|
|
}
|
|
}
|
|
}
|
|
dst.endWrite();
|
|
}
|
|
|
|
template <typename T>
|
|
inline void draw7SegColon(T &dst, int clkX, int clkY, uint16_t color,
|
|
uint16_t bgCol = 0x10A2, int clip_h = 240, bool rainbow = false) {
|
|
// Top dot: centered at clkX + 151, font row 19..29 (y = clkY + 30)
|
|
draw7SegDot(dst, clkX + 151, clkY + 30, color, bgCol, clip_h, rainbow);
|
|
// Bottom dot: centered at clkX + 148, font row 53..63 (y = clkY + 64)
|
|
draw7SegDot(dst, clkX + 148, clkY + 64, color, bgCol, clip_h, rainbow);
|
|
}
|
|
|
|
// Synchronize tracking variables without touching TFT display (zero flicker)
|
|
inline void syncGpsClockTrackers() {
|
|
oldClockHour = gpsHour;
|
|
oldClockMin = gpsMinute;
|
|
oldClockSec = gpsSecond;
|
|
oldClockDay = gpsDay;
|
|
oldClockMonth = gpsMonth;
|
|
oldClockYear = gpsYear;
|
|
}
|
|
|
|
// Draw full Page 2 (GPS Clock Screen)
|
|
inline void drawGpsClockScreen(int x_offset = 0) {
|
|
if (x_offset == 0) {
|
|
tft.fillScreen(TFT_BLACK);
|
|
} else {
|
|
tft.fillRect(0, 0, x_offset, 240, TFT_BLACK);
|
|
}
|
|
|
|
uint16_t priCol = getThemePrimaryColor();
|
|
uint16_t clkAccCol = isRainbowTheme() ? (uint16_t)0x07FF : priCol;
|
|
uint16_t cardBg = 0x10A2; // Dark graphite navy
|
|
uint16_t cardBorder = 0x2945; // Subtle edge border
|
|
bool synced = isGpsSynced();
|
|
|
|
// Center: 5 Pagination Indicator Dots [○ ○ ● ○ ○] (Page 2 of 5 active)
|
|
tft.fillCircle(132 + x_offset, 2, 2, 0x5AEB); // Page 0 inactive
|
|
tft.fillCircle(146 + x_offset, 2, 2, 0x5AEB); // Page 1 inactive
|
|
tft.fillCircle(160 + x_offset, 2, 2, priCol); // Page 2 active
|
|
tft.fillCircle(174 + x_offset, 2, 2, 0x5AEB); // Page 3 inactive
|
|
tft.fillCircle(188 + x_offset, 2, 2, 0x5AEB); // Page 4 inactive
|
|
|
|
// 2. MAIN CLOCK CARD (y = 19 to 121, height = 102, width = 312, x = 4)
|
|
int clkX = 4 + x_offset;
|
|
int clkY = 19;
|
|
int clkW = 312;
|
|
int clkH = 102;
|
|
|
|
tft.fillRoundRect(clkX, clkY, clkW, clkH, 6, cardBg);
|
|
tft.drawRoundRect(clkX, clkY, clkW, clkH, 6, cardBorder);
|
|
|
|
// Left vertical accent bar
|
|
tft.fillRoundRect(clkX + 6, clkY + 8, 4, clkH - 16, 2, clkAccCol);
|
|
|
|
// Digits: Hours HH & Minutes MM with smooth rainbow gradient
|
|
uint8_t h10 = (gpsHour / 10) % 10;
|
|
uint8_t h1 = gpsHour % 10;
|
|
uint8_t m10 = (gpsMinute / 10) % 10;
|
|
uint8_t m1 = gpsMinute % 10;
|
|
bool rbClk = isRainbowTheme();
|
|
uint16_t c_h10 = isYapaTheme() ? getYapaColor(0) : clkAccCol;
|
|
uint16_t c_h1 = isYapaTheme() ? getYapaColor(1) : clkAccCol;
|
|
uint16_t c_m10 = isYapaTheme() ? getYapaColor(2) : clkAccCol;
|
|
uint16_t c_m1 = isYapaTheme() ? getYapaColor(3) : clkAccCol;
|
|
|
|
drawDseg7Digit80(tft, clkX + 12, clkY + 11, h10, c_h10, cardBg, 240, rbClk);
|
|
drawDseg7Digit80(tft, clkX + 77, clkY + 11, h1, c_h1, cardBg, 240, rbClk);
|
|
drawDseg7Digit80(tft, clkX + 170, clkY + 11, m10, c_m10, cardBg, 240, rbClk);
|
|
drawDseg7Digit80(tft, clkX + 235, clkY + 11, m1, c_m1, cardBg, 240, rbClk);
|
|
|
|
// Blinking Colon Dots between HH and MM (italic 7-segment style with
|
|
// anti-aliasing)
|
|
bool colOn = (gpsSecond % 2 == 0);
|
|
uint16_t colonCol = colOn ? clkAccCol : 0x18C3;
|
|
draw7SegColon(tft, clkX, clkY, colonCol, cardBg, 240, colOn && rbClk);
|
|
|
|
// 3. FULL-WIDTH DATE CARD (y = 125 to 235, height = 110, width = 312, x = 4)
|
|
int dateX = 4 + x_offset;
|
|
int dateY = 125;
|
|
int dateW = 312;
|
|
int dateH = 110;
|
|
tft.fillRoundRect(dateX, dateY, dateW, dateH, 6, cardBg);
|
|
tft.drawRoundRect(dateX, dateY, dateW, dateH, 6, cardBorder);
|
|
drawDateCardContent(dateX, dateY, dateW, dateH, cardBg, x_offset);
|
|
|
|
// Sync state trackers
|
|
syncGpsClockTrackers();
|
|
}
|
|
|
|
// 100% Flicker-Free Live Refresh for resting Page 2
|
|
inline void updateGpsClockScreen(bool forceRedraw = false) {
|
|
if (currentScreenPage != 2 || fullScreenCell != -1)
|
|
return;
|
|
|
|
uint16_t priCol = getThemePrimaryColor();
|
|
uint16_t clkAccCol = isRainbowTheme() ? (uint16_t)0x07FF : priCol;
|
|
uint16_t cardBg = 0x10A2;
|
|
int clkX = 4;
|
|
int clkY = 19;
|
|
|
|
// 1. Seconds change: update colon dots only (no seconds text)
|
|
if (forceRedraw || gpsSecond != oldClockSec) {
|
|
oldClockSec = gpsSecond;
|
|
bool colOn = (gpsSecond % 2 == 0);
|
|
uint16_t colonCol = colOn ? clkAccCol : 0x18C3;
|
|
draw7SegColon(tft, clkX, clkY, colonCol, cardBg, 240, colOn && isRainbowTheme());
|
|
}
|
|
|
|
// 2. Minutes change: update MM digits
|
|
if (forceRedraw || gpsMinute != oldClockMin) {
|
|
oldClockMin = gpsMinute;
|
|
tft.fillRect(clkX + 166, clkY + 10, 142, 82, cardBg);
|
|
uint8_t m10 = (gpsMinute / 10) % 10;
|
|
uint8_t m1 = gpsMinute % 10;
|
|
uint16_t c_m10 = isYapaTheme() ? getYapaColor(2) : clkAccCol;
|
|
uint16_t c_m1 = isYapaTheme() ? getYapaColor(3) : clkAccCol;
|
|
drawDseg7Digit80(tft, clkX + 170, clkY + 11, m10, c_m10, cardBg, 240, isRainbowTheme());
|
|
drawDseg7Digit80(tft, clkX + 235, clkY + 11, m1, c_m1, cardBg, 240, isRainbowTheme());
|
|
}
|
|
|
|
// 3. Hours change: update HH digits
|
|
if (forceRedraw || gpsHour != oldClockHour) {
|
|
oldClockHour = gpsHour;
|
|
tft.fillRect(clkX + 12, clkY + 10, 136, 82, cardBg);
|
|
uint8_t h10 = (gpsHour / 10) % 10;
|
|
uint8_t h1 = gpsHour % 10;
|
|
uint16_t c_h10 = isYapaTheme() ? getYapaColor(0) : clkAccCol;
|
|
uint16_t c_h1 = isYapaTheme() ? getYapaColor(1) : clkAccCol;
|
|
drawDseg7Digit80(tft, clkX + 12, clkY + 11, h10, c_h10, cardBg, 240, isRainbowTheme());
|
|
drawDseg7Digit80(tft, clkX + 77, clkY + 11, h1, c_h1, cardBg, 240, isRainbowTheme());
|
|
}
|
|
|
|
// 4. Date changed (midnight rollover): redraw full date card
|
|
if (forceRedraw || gpsDay != oldClockDay || gpsMonth != oldClockMonth ||
|
|
gpsYear != oldClockYear) {
|
|
oldClockDay = gpsDay;
|
|
oldClockMonth = gpsMonth;
|
|
oldClockYear = gpsYear;
|
|
int dateX = 4;
|
|
int dateY = 125;
|
|
int dateW = 312;
|
|
int dateH = 110;
|
|
tft.fillRoundRect(dateX, dateY, dateW, dateH, 6, cardBg);
|
|
tft.drawRoundRect(dateX, dateY, dateW, dateH, 6, 0x2945);
|
|
drawDateCardContent(dateX, dateY, dateW, dateH, cardBg, 0);
|
|
}
|
|
}
|
|
|
|
// Render Page 2 into double-buffered sprite slice for slide / fade transitions
|
|
inline void renderPage2ToSprite(TFT_eSprite &s, int x_offset, int y_slice,
|
|
int slice_h) {
|
|
uint16_t priCol = getThemePrimaryColor();
|
|
uint16_t accCol = getThemeAccentColor();
|
|
uint16_t clkAccCol = isRainbowTheme() ? (uint16_t)0x07FF : priCol;
|
|
uint16_t cardBg = 0x10A2;
|
|
uint16_t cardBorder = 0x2945;
|
|
|
|
// 1. TOP BAR in top slice (pagination dots rendered by caller or top bar)
|
|
|
|
// 2. MAIN CLOCK CARD (y = 19 to 121)
|
|
int clkX = 4 + x_offset;
|
|
int clkY = 19 - y_slice;
|
|
int clkW = 312;
|
|
int clkH = 102;
|
|
|
|
if (clkX + clkW > 0 && clkX < 320 && clkY + clkH > 0 && clkY < slice_h) {
|
|
s.fillRoundRect(clkX, clkY, clkW, clkH, 6, cardBg);
|
|
s.drawRoundRect(clkX, clkY, clkW, clkH, 6, cardBorder);
|
|
s.fillRoundRect(clkX + 6, clkY + 8, 4, clkH - 16, 2, clkAccCol);
|
|
|
|
// Colon dots during transition (italic 7-segment style with anti-aliasing)
|
|
draw7SegColon(s, clkX, clkY, clkAccCol, cardBg, slice_h, isRainbowTheme());
|
|
|
|
// Digits: Hours HH (Right aligned to clkX + 142) & Minutes MM (Left aligned
|
|
// to clkX + 170)
|
|
int digitY = clkY + 11;
|
|
if (digitY + 80 > 0 && digitY < slice_h) {
|
|
uint8_t h10 = (gpsHour / 10) % 10;
|
|
uint8_t h1 = gpsHour % 10;
|
|
uint8_t m10 = (gpsMinute / 10) % 10;
|
|
uint8_t m1 = gpsMinute % 10;
|
|
|
|
uint16_t c_h10 = isYapaTheme() ? getYapaColor(0) : clkAccCol;
|
|
uint16_t c_h1 = isYapaTheme() ? getYapaColor(1) : clkAccCol;
|
|
uint16_t c_m10 = isYapaTheme() ? getYapaColor(2) : clkAccCol;
|
|
uint16_t c_m1 = isYapaTheme() ? getYapaColor(3) : clkAccCol;
|
|
|
|
drawDseg7Digit80(s, clkX + 12, digitY, h10, c_h10, cardBg, slice_h, isRainbowTheme());
|
|
drawDseg7Digit80(s, clkX + 77, digitY, h1, c_h1, cardBg, slice_h, isRainbowTheme());
|
|
drawDseg7Digit80(s, clkX + 170, digitY, m10, c_m10, cardBg, slice_h, isRainbowTheme());
|
|
drawDseg7Digit80(s, clkX + 235, digitY, m1, c_m1, cardBg, slice_h, isRainbowTheme());
|
|
}
|
|
}
|
|
|
|
// 3. FULL-WIDTH DATE CARD (y = 125 to 235) - Calendar Badge + Details
|
|
int dateX = 4 + x_offset;
|
|
int dateY = 125 - y_slice;
|
|
int dateW = 312;
|
|
int dateH = 110;
|
|
|
|
if (dateX + dateW > 0 && dateX < 320 && dateY + dateH > 0 &&
|
|
dateY < slice_h) {
|
|
uint16_t dateAccCol = isRainbowTheme() ? (uint16_t)0xFD20 : accCol;
|
|
uint16_t yearCol = isRainbowTheme() ? (uint16_t)0xFFE0 : accCol;
|
|
s.fillRoundRect(dateX, dateY, dateW, dateH, 6, cardBg);
|
|
s.drawRoundRect(dateX, dateY, dateW, dateH, 6, cardBorder);
|
|
s.fillRoundRect(dateX + 6, dateY + 8, 4, dateH - 16, 2, dateAccCol);
|
|
|
|
// Badge container
|
|
int bx = dateX + 20;
|
|
int by = dateY + 14;
|
|
int bw = 80;
|
|
int bh = 82;
|
|
if (by + bh > 0 && by < slice_h) {
|
|
s.fillRoundRect(bx, by, bw, bh, 6, 0x18C3);
|
|
s.drawRoundRect(bx, by, bw, bh, 6, cardBorder);
|
|
|
|
if (isYapaTheme()) {
|
|
s.fillRoundRect(bx, by, bw, 24, 6, 0x3A53);
|
|
s.fillRect(bx, by + 18, bw, 6, 0x3A53);
|
|
uint8_t mIdx = (gpsMonth >= 1 && gpsMonth <= 12) ? (gpsMonth - 1) : 0;
|
|
String mShort = SPANISH_MONTHS_SHORT[mIdx];
|
|
int totalW = s.textWidth(mShort.c_str(), 2);
|
|
int startX = bx + (bw - totalW) / 2;
|
|
char buf[2] = {0, 0};
|
|
for (size_t i = 0; i < mShort.length(); i++) {
|
|
buf[0] = mShort[i];
|
|
s.setTextColor(getYapaColor(i), 0x3A53);
|
|
s.drawString(buf, startX, by + 4, 2);
|
|
startX += s.textWidth(buf, 2);
|
|
}
|
|
|
|
// Badge day number in Yapa
|
|
String dayStr = (gpsDay < 10) ? "0" + String(gpsDay) : String(gpsDay);
|
|
s.loadFont(DSEG7_Modern_BoldItalic_34);
|
|
s.setTextPadding(0);
|
|
int dayW = s.textWidth(dayStr.c_str());
|
|
int dStartX = bx + (bw - dayW) / 2;
|
|
for (size_t i = 0; i < dayStr.length(); i++) {
|
|
buf[0] = dayStr[i];
|
|
s.setTextColor(getYapaColor(i), 0x18C3);
|
|
s.drawString(buf, dStartX, by + 34);
|
|
dStartX += s.textWidth(buf);
|
|
}
|
|
s.unloadFont();
|
|
} else {
|
|
s.fillRoundRect(bx, by, bw, 24, 6, dateAccCol);
|
|
s.fillRect(bx, by + 18, bw, 6, dateAccCol);
|
|
uint8_t mIdx = (gpsMonth >= 1 && gpsMonth <= 12) ? (gpsMonth - 1) : 0;
|
|
s.setTextColor(TFT_BLACK, dateAccCol);
|
|
s.drawCentreString(SPANISH_MONTHS_SHORT[mIdx], bx + (bw / 2), by + 4, 2);
|
|
|
|
// Badge day number
|
|
String dayStr = (gpsDay < 10) ? "0" + String(gpsDay) : String(gpsDay);
|
|
s.fontLoaded = true;
|
|
s.setTextColor(TFT_WHITE, 0x18C3);
|
|
s.setTextPadding(0);
|
|
s.drawCentreString(dayStr.c_str(), bx + (bw / 2), by + 34, 1);
|
|
s.fontLoaded = false;
|
|
}
|
|
}
|
|
|
|
int tx = dateX + 116;
|
|
uint8_t wday = gpsDayOfWeek % 7;
|
|
int dwY = dateY + 22;
|
|
if (dwY + 26 > 0 && dwY < slice_h) {
|
|
updateDayTitleSprite(wday);
|
|
if (sprDayTitle.created()) {
|
|
sprDayTitle.pushToSprite(&s, tx, dwY);
|
|
} else {
|
|
s.setTextColor(TFT_WHITE, cardBg);
|
|
s.drawString(SPANISH_DAYS[wday], tx, dwY, 4);
|
|
}
|
|
}
|
|
|
|
int divY = dateY + 54;
|
|
if (divY > 0 && divY < slice_h) {
|
|
s.drawFastHLine(tx, divY, 180, cardBorder);
|
|
s.drawFastHLine(tx, divY, 50, dateAccCol);
|
|
}
|
|
|
|
int yrY = dateY + 64;
|
|
if (yrY + 26 > 0 && yrY < slice_h) {
|
|
updateYearTitleSprite(gpsYear, yearCol);
|
|
if (sprYearTitle.created()) {
|
|
sprYearTitle.pushToSprite(&s, tx, yrY);
|
|
} else {
|
|
s.setTextColor(yearCol, cardBg);
|
|
s.drawString(String(gpsYear).c_str(), tx, yrY, 4);
|
|
}
|
|
}
|
|
}
|
|
}
|