Files
Maison/PacoMouseCYD/Platformio/Arduino.old/gui.ino
2026-02-13 08:49:53 +01:00

1117 lines
39 KiB
C++

/* PacoMouseCYD throttle -- F. Cañada 2025-2026 -- https://usuaris.tinet.cat/fmco/
Very basic Graphical User Interface (GUI) for PacoMouseCYD
All data in absolute coordinates
*/
////////////////////////////////////////////////////////////
// ***** GUI (Graphical User Interface) *****
////////////////////////////////////////////////////////////
void initGUI() {
clearTimers();
clearEventStack();
clearObjectStack();
createObject(OBJ_WIN, WIN_DEFAULT);
}
////////////////////////////////////////////////////////////
// ***** TIMER *****
////////////////////////////////////////////////////////////
void clearTimers() {
uint16_t n;
for (n = 0; n < MAX_SYS_TIMER; n++)
setTimer(n, 0, TMR_STOP);
timerSys = millis();
}
void setTimer(uint16_t id, uint16_t count, uint16_t type) {
wTimerStack[id].tmrDelay = count;
wTimerStack[id].tmrCount = count;
wTimerStack[id].type = type;
}
void stopTimer(uint16_t id) {
setTimer(id, 0, TMR_STOP);
}
void timerProcess() {
uint16_t n;
if (millis() - timerSys > TMR_RESOLUTION) { // Every 100ms
timerSys = millis();
for (n = 0; n < MAX_SYS_TIMER; n++) {
if (wTimerStack[n].type != TMR_STOP) { // only running timers
if (wTimerStack[n].tmrCount > 0) {
wTimerStack[n].tmrCount--;
}
else {
newEvent(OBJ_TIMER, n, EVNT_TIMER); // timer expired
if (wTimerStack[n].type == TMR_ONESHOT)
wTimerStack[n].type = TMR_STOP;
if (wTimerStack[n].type == TMR_PERIODIC)
wTimerStack[n].tmrCount = wTimerStack[n].tmrDelay;
}
}
}
}
}
////////////////////////////////////////////////////////////
// ***** EVENT STACK *****
////////////////////////////////////////////////////////////
void clearEventStack() { // clear pending events
eventIn = 0;
eventOut = 0;
eventsPending = 0;
}
wEvent getEvent() { // get event from stack
eventOut = (eventOut + 1) & 0x001F;
eventsPending--;
return eventStack[eventOut];
}
void putEvent(wEvent event) { // put event on stack
eventIn = (eventIn + 1) & 0x001F;
eventsPending++;
eventStack[eventIn] = event;
}
void newEvent (uint16_t objType, uint16_t objID, uint16_t eventID) { // create new event
wEvent event;
event.objID = objID;
event.objType = objType;
event.eventID = eventID;
putEvent(event);
}
void sendClickEvent(uint16_t x, uint16_t y) {
uint16_t pos, type, id, w, h;
uint16_t xm, xM, ym, yM;
pos = endObjStack;
while (pos > 0) {
id = objStack[pos].objID;
switch (objStack[pos].objType) {
case OBJ_WIN:
xm = winData[id].x;
ym = winData[id].y;
xM = xm + winData[id].w;
yM = ym + winData[id].h;
break;
case OBJ_ICON:
xm = iconData[id].x;
ym = iconData[id].y;
xM = xm + iconData[id].w;
yM = ym + iconData[id].h;
//DEBUG_MSG("Click Icon ID: %d Pos: %d",objStack[pos].objID,pos)
break;
case OBJ_BUTTON:
xm = buttonData[id].x;
ym = buttonData[id].y;
xM = xm + buttonData[id].w;
yM = ym + buttonData[id].h;
break;
case OBJ_BAR:
if (barData[id].w > barData[id].h) { // Horizontal bar
if (barData[id].r > (barData[id].h / 2)) { // big cursor
w = barData[id].r * 2;
h = barData[id].h / 2;
xm = barData[id].x - barData[id].r;
ym = barData[id].y + h - barData[id].r;
xM = xm + barData[id].w + w + 1;
yM = ym + w + 1;
}
else {
xm = barData[id].x;
ym = barData[id].y;
xM = xm + barData[id].w;
yM = ym + barData[id].h;
}
}
else { // Vertical bar
if (barData[id].r > (barData[id].w / 2)) { // big cursor
h = barData[id].r * 2;
w = barData[id].w / 2;
xm = barData[id].x + w - barData[id].r;
ym = barData[id].y - barData[id].r;
xM = xm + h + 1;
yM = ym + barData[id].h + h + 1;
}
else {
xm = barData[id].x;
ym = barData[id].y;
xM = xm + barData[id].w;
yM = ym + barData[id].h;
}
}
break;
case OBJ_TXT:
xm = txtData[id].x;
ym = txtData[id].y;
xM = xm + txtData[id].w;
yM = ym + txtData[id].h;
break;
case OBJ_SWITCH:
xm = switchData[id].x;
ym = switchData[id].y;
xM = xm + ((switchData[id].h) * 2);
yM = ym + switchData[id].h;
break;
case OBJ_RADIO:
xm = radioData[id].x;
ym = radioData[id].y;
xM = xm + radioData[id].h;
yM = ym + (radioData[id].h * radioData[id].num);
break;
case OBJ_LPIC:
xm = lpicData[id].x;
ym = lpicData[id].y;
xM = xm + LPIC_WIDTH;
yM = ym + LPIC_HEIGHT;
break;
case OBJ_FNC:
xm = fncData[id].x;
ym = fncData[id].y;
xM = xm + FNC_WIDTH;
yM = ym + FNC_HEIGHT;
break;
case OBJ_KEYBOARD:
xm = keybData[id].x;
ym = keybData[id].y;
switch (keybData[id].type) {
case KEYB_KEYPAD:
xM = xm + KEYPAD_WIDTH;
yM = ym + KEYPAD_HEIGHT;
break;
case KEYB_KEYPAD_BIG:
case KEYB_KEYPAD_OPT:
xM = xm + KEYPAD_BIG_WIDTH;
yM = ym + KEYPAD_BIG_HEIGHT;
break;
default:
xM = xm + KEYB_WIDTH;
yM = ym + KEYB_HEIGHT;
break;
}
break;
default:
xm = tft.width(); // other objects don't generate click event
ym = tft.height();
xM = xm;
yM = ym;
}
//if ((x >= xm) && (x < xM) && (y >= ym) && (y < yM)) { // check if click is inside object
if (isInside(x, y, xm, xM, ym, yM)) {
newEvent(objStack[pos].objType, id, EVNT_CLICK);
lastClickX = x;
lastClickY = y;
return;
}
if ((pos == lastWinStack) && isModalWindow(objStack[lastWinStack].objID)) // With Modal window only cliks inside last window are valid
return;
pos--;
}
}
////////////////////////////////////////////////////////////
// ***** GRAPHIC OBJECT STACK *****
////////////////////////////////////////////////////////////
void clearObjectStack() { // Clear list of objects
endObjStack = 0;
objStack[endObjStack].objType = OBJ_UNDEF;
lastWinStack = endObjStack;
}
uint16_t createObject (uint16_t obj, uint16_t id) { // Add a new object to the list of objects
objStack[endObjStack].objType = obj;
objStack[endObjStack].objID = id;
if (obj == OBJ_WIN)
lastWinStack = endObjStack;
endObjStack++;
objStack[endObjStack].objType = OBJ_UNDEF;
//DEBUG_MSG("Create Object. Pos: %d", endObjStack - 1)
return (endObjStack - 1);
}
uint16_t findWindow(uint16_t id) {
uint16_t pos, cnt;
pos = OBJ_NOT_FOUND;
cnt = endObjStack;
while (cnt > 0) {
if ((objStack[cnt].objType == OBJ_WIN) && (objStack[cnt].objID == id)) {
pos = cnt;
cnt = 1; // finded, don't search anymore
}
cnt--;
}
DEBUG_MSG("Window finded at pos: %d", pos)
return pos;
}
uint16_t findPreviousWindow(uint16_t cnt) { // find previous window from position
uint16_t pos;
pos = 0;
while (cnt > 0) {
if (objStack[cnt].objType == OBJ_WIN) {
pos = cnt;
cnt = 1; // finded, don't search anymore
}
cnt--;
}
return pos;
}
uint16_t findNextWindow (uint16_t cnt) { // find next window from position
//uint16_t pos;
DEBUG_MSG("Curr. window: %d, End stack: %d", cnt, endObjStack)
cnt++;
while (cnt < endObjStack) {
if (objStack[cnt].objType == OBJ_WIN) {
//pos = cnt;
return cnt;
}
cnt++;
}
return endObjStack;
}
void closeWindow(uint16_t id) { // Close window
uint16_t pos, nxt, cnt, posRedraw;
if (objStack[lastWinStack].objID == id) { // check if it is last window
endObjStack = lastWinStack;
objStack[endObjStack].objType = OBJ_UNDEF;
DEBUG_MSG("Closing last window... %d", id)
}
else {
DEBUG_MSG("Closing other window ID: %d", id)
pos = findWindow(id); // find window and next window
if (pos == OBJ_NOT_FOUND)
return;
nxt = findNextWindow(pos);
DEBUG_MSG("Pos: %d, Next window: %d", pos, nxt)
cnt = 0;
while ((cnt + nxt) < endObjStack + 1) { // move rest of objects
objStack[pos + cnt] = objStack[nxt + cnt];
cnt++;
}
endObjStack = pos + cnt - 1;
DEBUG_MSG("Closing window pos: %d, nxt: %d", pos, nxt)
}
printObjStack();
//lastWinStack = findPreviousWindow(endObjStack); // update last window pointer
for (pos = findContainerWindow(id); pos < endObjStack; pos++) {
if (objStack[pos].objType == OBJ_WIN) {
newEvent (OBJ_WIN, objStack[pos].objID, EVNT_DRAW); // re-draw other windows
}
}
}
uint16_t findContainerWindow (uint16_t id) { // Find window of stack that contains the closed window
uint16_t pos, x1, y1, xm, xM, ym, yM, winID;
lastWinStack = findPreviousWindow(endObjStack); // update last window pointer
pos = lastWinStack;
while (pos) {
winID = objStack[pos].objID;
xm = winData[winID].x;
xM = xm + winData[winID].w + 1;
ym = winData[winID].y;
yM = xm + winData[winID].h + 1;
x1 = winData[id].x;
y1 = winData[id].y;
if (isInside(x1, y1, xm, xM, ym, yM)) { // Left top corner
if (isInside(x1 + winData[id].w, y1, xm, xM, ym, yM)) { // Rigth top corner
if (isInside(x1, y1 + winData[id].h, xm, xM, ym, yM)) { // Left bottom corner
if (isInside(x1 + winData[id].w, y1 + winData[id].h, xm, xM, ym, yM)) { // Right bottom corner
DEBUG_MSG("Container window at %d", pos);
return pos;
}
}
}
}
pos = findPreviousWindow(pos - 1);
}
return pos;
}
void setModalWindow(uint16_t id, bool state) {
winData[id].modal = state;
}
bool isModalWindow (uint16_t id) {
return winData[id].modal;
}
bool isWindow(uint16_t win) {
return (objStack[lastWinStack].objID == win);
}
////////////////////////////////////////////////////////////
// ***** DRAW OBJECTS *****
////////////////////////////////////////////////////////////
void printLabelTxt(uint16_t id) {
char label[MAX_LABEL_LNG];
uint16_t cnt, yPos;
char chr;
const char *language;
language = translations[id][currLanguage];
if (language == NULL)
language = translations[id][LANG_ENGLISH];
tft.setFreeFont(labelData[id].font);
tft.setTextColor(labelData[id].color);
tft.setTextDatum(labelData[id].align);
yPos = labelData[id].y;
cnt = 0;
chr = *language++;
while (chr) {
if (chr == '\n') {
label[cnt] = 0;
tft.drawString(label, labelData[id].x, yPos, GFXFF);
yPos += tft.fontHeight();
cnt = 0;
chr = *language++;
}
else {
label[cnt++] = chr;
chr = *language++;
}
}
label[cnt] = 0;
tft.drawString(label, labelData[id].x, yPos, GFXFF);
}
const char *getParmNum(const char *str, uint16_t *parm) {
char chr;
uint16_t value;
value = 0;
chr = *str;
while (isDigit(chr)) {
value = (value * 10) + (chr - '0');
str++;
chr = *str;
}
*parm = value;
return str;
}
void drawDrawStr(uint16_t id) {
char chr, cmd;
uint16_t x, y, w, c, x2, y2, color, value, depth, band, colorRGB;
const char *str;
bool isSprite;
x = drawStrData[id].x;
y = drawStrData[id].y;
color = COLOR_TRANSPARENT;
depth = 8;
band = 15;
str = drawStrData[id].str;
isSprite = false;
chr = *str++;
while (chr != 0) {
if (chr != ',')
cmd = chr;
switch (chr) {
case 'K': // Kn: Color n
str = getParmNum(str, &color);
//DEBUG_MSG("Color: %d", color);
break;
case 'k': // kn: Sprite color depth
str = getParmNum(str, &depth);
sprite.setColorDepth(depth);
break;
case ',':
str = getParmNum(str, &value);
//DEBUG_MSG("Value: %d", value);
switch (cmd) {
case 'R':
if (isSprite)
sprite.fillRect(x, y, w, value, colorDraw[color]);
else
tft.fillRect(x, y, w, value, colorDraw[color]);
break;
case 'r':
if (isSprite)
sprite.drawRect(x, y, w, value, colorDraw[color]);
else
tft.drawRect(x, y, w, value, colorDraw[color]);
break;
case 'L':
if (isSprite)
sprite.drawLine(x, y, w, value, colorDraw[color]);
else
tft.drawLine(x, y, w, value, colorDraw[color]);
break;
case 'l':
if (isSprite)
sprite.drawLine(x, y, w, value, colorDraw[color]);
else
tft.drawLine(x, y, w, value, colorDraw[color]);
x = w;
y = value;
break;
case 'p':
y2 = value;
break;
case 'T':
if (isSprite)
sprite.fillTriangle(x, y, x2, y2, w, value, colorDraw[color]);
else
tft.fillTriangle(x, y, x2, y2, w, value, colorDraw[color]);
break;
case 't':
if (isSprite)
sprite.drawTriangle(x, y, x2, y2, w, value, colorDraw[color]);
else
tft.drawTriangle(x, y, x2, y2, w, value, colorDraw[color]);
break;
case 'S':
sprite.createSprite(w, value); // then create the sprite
sprite.fillSprite(colorDraw[color]);
isSprite = true;
x = 0;
y = 0;
break;
case 'D':
colorRGB = colorDraw[color];
for (c = 0; c < value; c += band) {
if (isSprite)
sprite.fillRect(x, y + c, w, band, colorRGB);
else
tft.fillRect(x, y + c, w, band, colorRGB);
if (colorRGB & 0x001F)
colorRGB -= 0x0001;
if (colorRGB & 0x07E0)
colorRGB -= 0x0040;
if (colorRGB & 0xF800)
colorRGB -= 0x0800;
}
break;
}
break;
case 'R': // Rw,h: fill Rectangle
case 'r': // rw,h: draw Rectangle
case 'D': // Dw,h: Degrade rectangle
str = getParmNum(str, &w);
//DEBUG_MSG("Rect W: %d", w);
break;
case 'C': // Cr: fill Circle radius r
str = getParmNum(str, &c);
if (isSprite)
sprite.fillCircle(x, y, c, colorDraw[color]);
else
tft.fillCircle(x, y, c, colorDraw[color]);
break;
case 'c': // cr : draw Circle radius r
str = getParmNum(str, &c);
if (isSprite)
sprite.drawCircle(x, y, c, colorDraw[color]);
else
tft.drawCircle(x, y, c, colorDraw[color]);
break;
case 'l': // lx,y: draw polyline to x,y (X,Y set to end position: x,y)
case 'L': // Lx,y: draw Line to x,y
str = getParmNum(str, &w);
break;
case 't': // tx,y: draw triangle (X,Y),(px,y),(tx,y)
case 'T': // Tx,y: fill triangle (X,Y),(px,y),(Tx,y)
str = getParmNum(str, &w);
break;
case 'S': // Sw,h: create sprite and draw string into it. x & y set to 0, background color defined with Kn -> KnkdSw,h
str = getParmNum(str, &w);
break;
case 's': // sn: draw sprite with transparent color n
str = getParmNum(str, &value);
//sprite.pushSprite(drawStrData[id].x, drawStrData[id].y, value);
sprite.pushSprite(drawStrData[id].x, drawStrData[id].y, colorDraw[value]);
sprite.deleteSprite();
isSprite = false;
break;
case 'X': // X: x position
str = getParmNum(str, &x);
//DEBUG_MSG("X: %d", x);
break;
case 'x': // x: increment x position
str = getParmNum(str, &value);
x += value;
break;
case 'Y': // Y: y position
str = getParmNum(str, &y);
//DEBUG_MSG("Y: %d", y);
break;
case 'y': // y: increment y position
str = getParmNum(str, &value);
y += value;
break;
case 'p':
str = getParmNum(str, &x2); // p: second point (for triangles) -> XxYypx,yTx,y
break;
case 'd': // dn: Degrade band height
str = getParmNum(str, &band);
break;
}
chr = *str++;
}
}
void drawObject (uint16_t type, uint16_t id) {
uint16_t value, state, w, h;
char buf[20];
delay(0);
switch (type) {
case OBJ_WIN:
tft.fillRect(winData[id].x, winData[id].y, winData[id].w, winData[id].h, winData[id].backgnd); // window box
tft.drawRect(winData[id].x, winData[id].y, winData[id].w, winData[id].h, winData[id].color);
break;
case OBJ_LABEL:
printLabelTxt(id);
break;
case OBJ_CHAR:
tft.setFreeFont(charData[id].font);
tft.setTextColor(charData[id].color);
tft.setTextDatum(MC_DATUM);
tft.drawChar(charData[id].chr, charData[id].x, charData[id].y);
break;
case OBJ_TXT:
tft.fillRoundRect(txtData[id].x + 1, txtData[id].y + 1, txtData[id].w - 2, txtData[id].h - 2, 3, txtData[id].backgnd);
tft.drawRoundRect(txtData[id].x, txtData[id].y, txtData[id].w, txtData[id].h, 3, txtData[id].border);
tft.setFreeFont(txtData[id].font);
tft.setTextColor(txtData[id].color);
if (txtData[id].alignCenter) {
tft.setTextDatum(MC_DATUM);
tft.drawString(txtData[id].buf, txtData[id].x + (txtData[id].w / 2), txtData[id].y + (txtData[id].h / 2), GFXFF);
}
else {
tft.setTextDatum(ML_DATUM);
tft.drawString(txtData[id].buf, txtData[id].x + 3, txtData[id].y + (txtData[id].h / 2), GFXFF);
}
break;
case OBJ_DRAWSTR:
drawDrawStr(id);
break;
case OBJ_ICON:
//tft.pushImage(260, 120, 24, 32, (uint16_t*)cara_paco24x32, false); // 16 bits RGB565
tft.drawBitmap(iconData[id].x, iconData[id].y, iconData[id].bitmap, iconData[id].w, iconData[id].h, iconData[id].color); // monochrome
//DEBUG_MSG("Print Icon ID: %d, X:%d, Y:%d, PTR:%d, W:%d, H:%d, Color:%d",id, iconData[id].x, iconData[id].y, iconData[id].bitmap, iconData[id].w, iconData[id].h, iconData[id].color);
break;
case OBJ_BUTTON:
tft.fillRoundRect(buttonData[id].x + 1, buttonData[id].y + 1, buttonData[id].w - 2, buttonData[id].h - 2, 3, buttonData[id].backgnd);
tft.drawRoundRect(buttonData[id].x, buttonData[id].y, buttonData[id].w, buttonData[id].h, 4, buttonData[id].border);
if (buttonData[id].objType != OBJ_UNDEF)
drawObject(buttonData[id].objType, buttonData[id].objID);
break;
case OBJ_BAR:
if (barData[id].w > barData[id].h) { // Horizontal bar
if (barData[id].r > (barData[id].h / 2)) {
w = barData[id].r * 2;
h = barData[id].h / 2;
tft.fillRect(barData[id].x - barData[id].r, barData[id].y + h - barData[id].r, barData[id].w + w + 1, w + 1, barData[id].backgnd);
}
value = map (barData[id].value, barData[id].min, barData[id].max, 0, barData[id].w);
tft.fillRect(barData[id].x + 1, barData[id].y + 1, value, barData[id].h - 2, barData[id].colorOn);
tft.fillRect(barData[id].x + value + 1, barData[id].y + 1, barData[id].w - value - 2, barData[id].h - 2, barData[id].colorOff);
tft.drawRect(barData[id].x, barData[id].y, barData[id].w, barData[id].h, barData[id].border);
if (barData[id].r > 0) {
tft.fillCircle(barData[id].x + value, barData[id].y + (barData[id].h / 2), barData[id].r, barData[id].border);
}
}
else { // Vertical bar
if (barData[id].r > (barData[id].w / 2)) {
h = barData[id].r * 2;
w = barData[id].w / 2;
tft.fillRect(barData[id].x + w - barData[id].r, barData[id].y - barData[id].r, h + 1, barData[id].h + h + 1, barData[id].backgnd);
}
value = map (barData[id].value, barData[id].min, barData[id].max, 0, barData[id].h);
tft.fillRect(barData[id].x + 1, barData[id].y + barData[id].h - value - 1, barData[id].w - 2, value, barData[id].colorOn);
tft.fillRect(barData[id].x + 1, barData[id].y + 1, barData[id].w - 2, barData[id].h - value, barData[id].colorOff);
tft.drawRect(barData[id].x, barData[id].y, barData[id].w, barData[id].h, barData[id].border);
if (barData[id].r > 0) {
tft.fillCircle(barData[id].x + w, barData[id].y + barData[id].h - value, barData[id].r, barData[id].border);
}
}
break;
case OBJ_SWITCH:
value = switchData[id].state ? switchData[id].colorOn : switchData[id].colorOff;
tft.fillRoundRect(switchData[id].x, switchData[id].y, switchData[id].h * 2, switchData[id].h, switchData[id].h / 2, value);
value = switchData[id].h / 2;
state = switchData[id].state ? value + switchData[id].h : value;
tft.fillCircle(switchData[id].x + state, switchData[id].y + value, value - 2, switchData[id].colorKnob);
break;
case OBJ_RADIO:
w = radioData[id].h / 2;
h = 0;
for (value = 0; value < radioData[id].num; value++) {
tft.fillCircle(radioData[id].x + w, radioData[id].y + w + h, radioData[id].r, radioData[id].backgnd);
tft.drawCircle(radioData[id].x + w, radioData[id].y + w + h, radioData[id].r, radioData[id].border);
if (value == radioData[id].value)
tft.fillCircle(radioData[id].x + w, radioData[id].y + w + h, radioData[id].r - 2, radioData[id].border);
h += radioData[id].h;
}
break;
case OBJ_FNC:
value = fncData[id].num;
tft.drawBitmap(fncData[id].x, fncData[id].y, full32, 32, 32, fncData[id].backgnd); // delete
tft.drawBitmap(fncData[id].x, fncData[id].y, funcIcon[fncData[id].idIcon], 32, 32, fncData[id].color); // black
if (fncData[id].state)
tft.drawBitmap(fncData[id].x, fncData[id].y, funcIcon[fncData[id].idIcon + 1], 32, 32, fncData[id].colorOn); // yellow
if (value <= FNC_MAX) {
snprintf(buf, 4, "%d", value);
tft.setFreeFont(FSSB6);
tft.setTextColor(COLOR_WHITE); // white
tft.setTextDatum(BR_DATUM);
tft.drawString(buf, fncData[id].x + 31, fncData[id].y + 31, GFXFF);
}
break;
case OBJ_LPIC:
if (lpicData[id].id > 999) {
sprintf (FileName, "/image/%d.bmp", lpicData[id].id );
if (! drawBmp (FileName, lpicData[id].x, lpicData[id].y)) {
value = sdDetected ? SYS_NO_LOK : SYS_ELOK;
tft.drawBitmap(lpicData[id].x, lpicData[id].y, sysLocoPic[value], LPIC_WIDTH, LPIC_HEIGHT, COLOR_BLACK); // monochrome
}
}
else {
tft.drawBitmap(lpicData[id].x, lpicData[id].y, sysLocoPic[lpicData[id].id], LPIC_WIDTH, LPIC_HEIGHT, COLOR_BLACK); // monochrome
}
break;
case OBJ_GAUGE:
if (gaugeData[id].r > 0) { // Normal gauge
tft.drawArc(gaugeData[id].x, gaugeData[id].y, gaugeData[id].r, gaugeData[id].r - 15, 52, 52 + gaugeData[id].value, gaugeData[id].color, gaugeData[id].backgnd, false);
tft.drawArc(gaugeData[id].x, gaugeData[id].y, gaugeData[id].r, gaugeData[id].r - 15, 52 + gaugeData[id].value, 308, gaugeData[id].backgnd, gaugeData[id].color, false);
}
else { // Speed gauge
tft.setPivot(gaugeData[id].x, gaugeData[id].y); // Set pivot to middle of TFT screen
sprite.setColorDepth(8); // Create an 8bpp Sprite
sprite.createSprite(11, 13); // 8bpp requires 11 * 13 = 143 bytes
sprite.setPivot(70, 7); // Set pivot relative to top left corner of Sprite
sprite.fillSprite(gaugeData[id].backgnd); // Fill the Sprite with background
sprite.drawBitmap(0, 0, arc10, 11, 13, gaugeData[id].color);
for (value = 330; value < 360; value += 10) { // draw dial
sprite.pushRotated(value);
}
for (value = 0; value < 230; value += 10) {
sprite.pushRotated(value);
}
sprite.deleteSprite();
delay(0);
tft.drawArc(gaugeData[id].x, gaugeData[id].y, 71, 69, 270, 315, COLOR_RED, gaugeData[id].backgnd, false);
tft.drawCircle(gaugeData[id].x, gaugeData[id].y, 75, COLOR_GOLD);
tft.fillCircle(gaugeData[id].x, gaugeData[id].y, 20, gaugeData[id].color);
delay(0);
updateSpeedDir(); // draw needle & dir
delay(0);
}
break;
case OBJ_KEYBOARD:
switch (keybData[id].type) {
case KEYB_ALPHA:
drawKeyboard(keybData[id].x, keybData[id].y, KeybAlpha);
break;
case KEYB_NUM:
drawKeyboard(keybData[id].x, keybData[id].y, KeybNum);
break;
case KEYB_SYM:
drawKeyboard(keybData[id].x, keybData[id].y, KeybSym);
break;
case KEYB_KEYPAD_BIG:
drawKeypadBig(keybData[id].x, keybData[id].y, KeybKeypad, true);
break;
case KEYB_KEYPAD_OPT:
drawKeypadBig(keybData[id].x, keybData[id].y, KeybKeypad, false);
break;
case KEYB_KEYPAD:
drawKeypad(keybData[id].x, keybData[id].y, KeybKeypad);
break;
default:
drawKeyboard(keybData[id].x, keybData[id].y, KeybAlphaCaps);
break;
}
break;
}
delay(0);
}
void drawWindow(uint16_t id) {
uint16_t pos;
//wEvent redraw;
DEBUG_MSG("Finding window for printing... %d", id)
pos = findWindow(id);
if (pos != OBJ_NOT_FOUND) {
DEBUG_MSG("Printing window box ID: %d", id)
drawObject(OBJ_WIN, id); // draw window box
pos++;
printObjStack();
while ((objStack[pos].objType != OBJ_UNDEF) && (objStack[pos].objType != OBJ_WIN)) { // draw objects in window
drawObject(objStack[pos].objType, objStack[pos].objID);
//DEBUG_MSG("Drawing object pos: %d", pos)
pos++;
}
}
}
bool drawBmp(const char *filename, int16_t x, int16_t y) { // Draw BMP file (24 bits)
File bmpFS;
if ((x >= tft.width()) || (y >= tft.height()))
return false;
bmpFS = SD.open(filename); // Open requested file on SD card
if (!bmpFS) {
DEBUG_MSG("File %s not found", filename );
return false;
}
uint32_t seekOffset;
uint16_t w, h, row, col;
uint8_t r, g, b;
uint32_t startTime = millis();
if (read16(bmpFS) == 0x4D42) { // BMP signature
read32(bmpFS); // File size
read32(bmpFS); // Read & ignore creator bytes
seekOffset = read32(bmpFS); // Start of image data
read32(bmpFS);
w = read32(bmpFS);
h = read32(bmpFS);
if ((read16(bmpFS) == 1) && (read16(bmpFS) == 24) && (read32(bmpFS) == 0)) { // # planes -- must be '1', 24 bits per pixel, 0 = uncompressed
y += h - 1;
tft.setSwapBytes(true);
bmpFS.seek(seekOffset);
uint16_t padding = (4 - ((w * 3) & 3)) & 3;
uint8_t lineBuffer[w * 3];
for (row = 0; row < h; row++) {
bmpFS.read(lineBuffer, sizeof(lineBuffer));
uint8_t* bptr = lineBuffer;
uint16_t* tptr = (uint16_t*)lineBuffer;
// Convert 24 to 16 bit colours
for (uint16_t col = 0; col < w; col++) { // Convert pixel from BMP to TFT format (RGB565)
b = *bptr++;
g = *bptr++;
r = *bptr++;
*tptr++ = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
}
if (padding) // Read any line padding
bmpFS.read((uint8_t*)tptr, padding);
tft.pushImage(x, y--, w, 1, (uint16_t*)lineBuffer); // Push the pixel row to screen, pushImage will crop the line if needed
delay(0);
}
DEBUG_MSG("%s Loaded in %lu ms", filename, (millis() - startTime))
}
else {
DEBUG_MSG("BMP format not recognized.")
}
}
bmpFS.close();
tft.setSwapBytes(false);
return true;
}
uint16_t read16(fs::File & f) { // helper functions for BMP load
uint16_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read(); // MSB
return result;
}
uint32_t read32(fs::File & f) {
uint32_t result;
((uint8_t *)&result)[0] = f.read(); // LSB
((uint8_t *)&result)[1] = f.read();
((uint8_t *)&result)[2] = f.read();
((uint8_t *)&result)[3] = f.read(); // MSB
return result;
}
void drawKey (uint16_t x, uint16_t y, uint16_t w, uint16_t h) {
tft.fillRoundRect(x + 1, y + 1, w - 2, h - 2, 3, COLOR_NAVY); //inner button color
tft.drawRoundRect(x, y, w, h, 3, COLOR_WHITE); // outter button color
}
void drawKeyboard (uint16_t posX, uint16_t posY, const char type[][12]) {
int x, y, shiftRight;
tft.fillRect(posX + 1, posY, KEYB_WIDTH - 2, KEYB_HEIGHT - 2, COLOR_BACKGROUND);
tft.setFreeFont(FSSB9);
tft.setTextColor(COLOR_WHITE);
//tft.setTextDatum(MC_DATUM);
for (y = 0; y < 3; y++) { // Keys
shiftRight = type[y][0];
for (x = 2; x < type[y][1]; x++) {
drawKey(posX + 8 + shiftRight + (23 * (x - 2)) , posY + (30 * y), 20, 25);
tft.setCursor(posX + 11 + shiftRight + (23 * (x - 2)) , posY + 5 + 11 + (30 * y));
tft.print(type[y][x]);
}
}
drawKey(posX + 5, posY + 60, 30, 25); // ShiftKey
tft.drawBitmap(posX + 11, posY + 65, shift, 16, 16, COLOR_WHITE); // delete
if (type == KeybAlphaCaps)
tft.drawFastHLine(posX + 11 + 6, posY + 65 + 12, 5, COLOR_WHITE);
drawKey(posX + 5, posY + 90, 40, 25); // Num
tft.setCursor(posX + 8, posY + 11 + 95);
tft.print("123");
drawKey(posX + 200, posY + 60, 30, 25); // BackSpace
tft.drawBitmap(posX + 206, posY + 64, del, 16, 16, COLOR_WHITE); // delete
drawKey(posX + 189, posY + 90, 45, 25); // Special Characters
tft.setCursor(posX + 195, posY + 11 + 95);
tft.print("#?+");
drawKey(posX + 52, posY + 90, 130, 25); // Spacebar
}
void drawKeypad (uint16_t posX, uint16_t posY, const char type[][12]) {
int x, y, shiftRight;
tft.fillRect(posX + 1, posY + 1, KEYPAD_WIDTH - 2, KEYPAD_HEIGHT - 2, COLOR_BACKGROUND);
tft.setFreeFont(FSSB9);
tft.setTextColor(COLOR_WHITE);
tft.setTextDatum(MC_DATUM);
for (y = 0; y < 3; y++) {
shiftRight = type[y][0];
for (x = 2; x < type[y][1]; x++) {
drawKey(posX + 8 + shiftRight + (23 * (x - 2)) , posY + (30 * y), 20, 25);
tft.setCursor(posX + 12 + shiftRight + (23 * (x - 2)) , posY + 5 + 11 + (30 * y));
tft.print(type[y][x]);
}
}
drawKey(posX + 8, posY + 90, 32, 25); // Num 0
tft.setCursor(posX + 18, posY + 11 + 95);
tft.print("0");
drawKey(posX + 43, posY + 90, 30, 25); // BackSpace
tft.drawBitmap(posX + 43 + 7, posY + 94, del, 16, 16, COLOR_WHITE); // delete
}
void drawKeypadBig (uint16_t posX, uint16_t posY, const char type[][12], bool enter) {
int x, y, shiftRight;
tft.fillRect(posX + 1, posY + 1, KEYPAD_BIG_WIDTH - 2, KEYPAD_BIG_HEIGHT - 2, COLOR_BACKGROUND);
tft.setFreeFont(FSSB12);
tft.setTextColor(COLOR_WHITE);
tft.setTextDatum(MC_DATUM);
for (y = 0; y < 3; y++) {
shiftRight = type[y][0];
for (x = 2; x < type[y][1]; x++) {
drawKey(posX + 8 + shiftRight + (46 * (x - 2)) , posY + (50 * y), 40, 40);
tft.setCursor(posX + 20 + shiftRight + (46 * (x - 2)) , posY + 5 + 25 + (50 * y));
tft.print(type[y][x]);
}
}
drawKey(posX + 8, posY + 150, 40, 40); // BackSpace
tft.drawBitmap(posX + 20, posY + 160, del, 16, 16, COLOR_WHITE); // delete
drawKey(posX + 8 + 46, posY + 150, 40, 40); // Num 0
tft.setCursor(posX + 20 + 46, posY + 25 + 150);
tft.print("0");
drawKey(posX + 16 + 84, posY + 150, 40, 40); // Enter
if (enter)
tft.drawBitmap(posX + 84 + 26, posY + 160, arrowL, 16, 16, COLOR_GREENYELLOW); // enter
}
////////////////////////////////////////////////////////////
// ***** SUPPORT *****
////////////////////////////////////////////////////////////
void getLabelTxt(uint16_t id, char *buf) {
uint16_t cnt;
char chr;
const char *language;
language = translations[id][currLanguage];
if (language == NULL)
language = translations[id][LANG_ENGLISH];
cnt = 0;
chr = *language++;
//while (chr != END_OF_TEXT) {
while (chr) {
buf[cnt++] = chr;
chr = *language++;
}
buf[cnt] = '\0';;
}
char getKeyTyped(uint16_t id, uint16_t x, uint16_t y) { // get key typed on keyboard object
uint16_t x0, y0, xm, ym, xM, yM;
uint16_t xKey, yKey, xShift, xNum;
x0 = keybData[id].x; // window position
y0 = keybData[id].y;
switch (keybData[id].type) {
case KEYB_KEYPAD: // keypad
ym = y0 + 90; // last row
yM = ym + 25;
xm = x0 + 8; // Num 0
xM = xm + 32;
if (isInside(x, y, xm, xM, ym, yM))
return '0';
xm = x0 + 43; // Backspace
xM = xm + 32;
if (isInside(x, y, xm, xM, ym, yM))
return CHR_BKSPC;
for (yKey = 0; yKey < 3; yKey++) { // check first three rows
xShift = KeybKeypad[yKey][0];
xNum = KeybKeypad[yKey][1];
for (xKey = 2; xKey < xNum; xKey++) {
xm = x0 + xShift + 8 + (23 * (xKey - 2));
xM = xm + 20;
ym = y0 + (30 * yKey);
yM = ym + 25;
if (isInside(x, y, xm, xM, ym, yM)) {
return KeybKeypad[yKey][xKey];
}
}
}
break;
case KEYB_KEYPAD_BIG: // keypad big
case KEYB_KEYPAD_OPT:
ym = y0 + 150; // last row
yM = ym + 40;
xm = x0 + 8; // Backspace
xM = xm + 40;
if (isInside(x, y, xm, xM, ym, yM))
return CHR_BKSPC;
xm = x0 + 8 + 46; // Num 0
xM = xm + 40;
if (isInside(x, y, xm, xM, ym, yM))
return '0';
xm = x0 + 16 + 84; // Enter
xM = xm + 40;
if (isInside(x, y, xm, xM, ym, yM))
return '\n';
for (yKey = 0; yKey < 3; yKey++) { // check first three rows
xShift = KeybKeypad[yKey][0];
xNum = KeybKeypad[yKey][1];
for (xKey = 2; xKey < xNum; xKey++) {
xm = x0 + xShift + 8 + (46 * (xKey - 2));
xM = xm + 40;
ym = y0 + (50 * yKey);
yM = ym + 40;
if (isInside(x, y, xm, xM, ym, yM)) {
return KeybKeypad[yKey][xKey];
}
}
}
break;
default: // keyboard
ym = y0 + 90; // last row
yM = ym + 25;
xm = x0 + 52; // Spacebar
xM = xm + 130;
if (isInside(x, y, xm, xM, ym, yM))
return ' ';
xm = x0 + 5; // Num
xM = xm + 40;
if (isInside(x, y, xm, xM, ym, yM)) {
keybData[id].type = KEYB_NUM;
return CHR_REDRAW;
}
xm = x0 + 189; // Sym
xM = xm + 45;
if (isInside(x, y, xm, xM, ym, yM)) {
keybData[id].type = KEYB_SYM;
return CHR_REDRAW;
}
ym = y0 + 60; // third row
yM = ym + 25;
xm = x0 + 200; // Backspace
xM = xm + 30;
if (isInside(x, y, xm, xM, ym, yM))
return CHR_BKSPC;
xm = x0 + 5; // ShiftKey
xM = xm + 30;
if (isInside(x, y, xm, xM, ym, yM)) {
keybData[id].type = (keybData[id].type == KEYB_CAPS) ? KEYB_ALPHA : KEYB_CAPS;
return CHR_REDRAW;
}
for (yKey = 0; yKey < 3; yKey++) { // check first three rows
switch (keybData[id].type) {
case KEYB_ALPHA:
xShift = KeybAlpha[yKey][0];
xNum = KeybAlpha[yKey][1];
break;
case KEYB_NUM:
xShift = KeybNum[yKey][0];
xNum = KeybNum[yKey][1];
break;
case KEYB_SYM:
xShift = KeybSym[yKey][0];
xNum = KeybSym[yKey][1];
break;
default:
xShift = KeybAlphaCaps[yKey][0];
xNum = KeybAlphaCaps[yKey][1];
break;
}
for (xKey = 2; xKey < xNum; xKey++) {
xm = x0 + xShift + 8 + (23 * (xKey - 2));
xM = xm + 20;
ym = y0 + (30 * yKey);
yM = ym + 25;
if (isInside(x, y, xm, xM, ym, yM)) {
switch (keybData[id].type) {
case KEYB_ALPHA:
return KeybAlpha[yKey][xKey];
break;
case KEYB_NUM:
return KeybNum[yKey][xKey];
break;
case KEYB_SYM:
return KeybSym[yKey][xKey];
break;
default:
return KeybAlphaCaps[yKey][xKey];
break;
}
}
}
}
break;
}
return 0; // no key
}
bool isInside (uint16_t x, uint16_t y, uint16_t xm, uint16_t xM, uint16_t ym, uint16_t yM) {
return ((x >= xm) && (x < xM) && (y >= ym) && (y < yM));
}
void printObjStack() {
#ifdef DEBUG
return;
for (uint16_t n = 0; n < (endObjStack + 1); n++) {
Serial.print(objStack[n].objType);
Serial.print("(");
Serial.print(objStack[n].objID);
Serial.print("), ");
}
Serial.println("<- Stack");
#endif
}