257 lines
7.8 KiB
C++
257 lines
7.8 KiB
C++
#include "WidgetConfig.hpp"
|
|
#include <cstring>
|
|
#include <cstdio>
|
|
|
|
// WidgetConfig implementation
|
|
void WidgetConfig::serialize(uint8_t* buf) const {
|
|
memset(buf, 0, SERIALIZED_SIZE);
|
|
size_t pos = 0;
|
|
|
|
buf[pos++] = id;
|
|
buf[pos++] = static_cast<uint8_t>(type);
|
|
buf[pos++] = x & 0xFF; buf[pos++] = (x >> 8) & 0xFF;
|
|
buf[pos++] = y & 0xFF; buf[pos++] = (y >> 8) & 0xFF;
|
|
buf[pos++] = width & 0xFF; buf[pos++] = (width >> 8) & 0xFF;
|
|
buf[pos++] = height & 0xFF; buf[pos++] = (height >> 8) & 0xFF;
|
|
buf[pos++] = visible ? 1 : 0;
|
|
|
|
buf[pos++] = static_cast<uint8_t>(textSource);
|
|
memcpy(&buf[pos], text, MAX_TEXT_LEN); pos += MAX_TEXT_LEN;
|
|
buf[pos++] = knxAddress & 0xFF; buf[pos++] = (knxAddress >> 8) & 0xFF;
|
|
buf[pos++] = fontSize;
|
|
|
|
buf[pos++] = textColor.r; buf[pos++] = textColor.g; buf[pos++] = textColor.b;
|
|
buf[pos++] = bgColor.r; buf[pos++] = bgColor.g; buf[pos++] = bgColor.b;
|
|
buf[pos++] = bgOpacity;
|
|
buf[pos++] = borderRadius;
|
|
|
|
buf[pos++] = shadow.offsetX;
|
|
buf[pos++] = shadow.offsetY;
|
|
buf[pos++] = shadow.blur;
|
|
buf[pos++] = shadow.spread;
|
|
buf[pos++] = shadow.color.r; buf[pos++] = shadow.color.g; buf[pos++] = shadow.color.b;
|
|
buf[pos++] = shadow.enabled ? 1 : 0;
|
|
|
|
buf[pos++] = isToggle ? 1 : 0;
|
|
buf[pos++] = knxAddressWrite & 0xFF; buf[pos++] = (knxAddressWrite >> 8) & 0xFF;
|
|
buf[pos++] = static_cast<uint8_t>(action);
|
|
buf[pos++] = targetScreen;
|
|
|
|
// Icon properties
|
|
buf[pos++] = iconCodepoint & 0xFF;
|
|
buf[pos++] = (iconCodepoint >> 8) & 0xFF;
|
|
buf[pos++] = (iconCodepoint >> 16) & 0xFF;
|
|
buf[pos++] = (iconCodepoint >> 24) & 0xFF;
|
|
buf[pos++] = iconPosition;
|
|
buf[pos++] = iconSize;
|
|
buf[pos++] = static_cast<uint8_t>(iconGap);
|
|
|
|
// Hierarchy
|
|
buf[pos++] = static_cast<uint8_t>(parentId);
|
|
}
|
|
|
|
void WidgetConfig::deserialize(const uint8_t* buf) {
|
|
size_t pos = 0;
|
|
|
|
id = buf[pos++];
|
|
type = static_cast<WidgetType>(buf[pos++]);
|
|
x = buf[pos] | (buf[pos+1] << 8); pos += 2;
|
|
y = buf[pos] | (buf[pos+1] << 8); pos += 2;
|
|
width = buf[pos] | (buf[pos+1] << 8); pos += 2;
|
|
height = buf[pos] | (buf[pos+1] << 8); pos += 2;
|
|
visible = buf[pos++] != 0;
|
|
|
|
textSource = static_cast<TextSource>(buf[pos++]);
|
|
memcpy(text, &buf[pos], MAX_TEXT_LEN); pos += MAX_TEXT_LEN;
|
|
text[MAX_TEXT_LEN - 1] = '\0';
|
|
knxAddress = buf[pos] | (buf[pos+1] << 8); pos += 2;
|
|
fontSize = buf[pos++];
|
|
|
|
textColor.r = buf[pos++]; textColor.g = buf[pos++]; textColor.b = buf[pos++];
|
|
bgColor.r = buf[pos++]; bgColor.g = buf[pos++]; bgColor.b = buf[pos++];
|
|
bgOpacity = buf[pos++];
|
|
borderRadius = buf[pos++];
|
|
|
|
shadow.offsetX = static_cast<int8_t>(buf[pos++]);
|
|
shadow.offsetY = static_cast<int8_t>(buf[pos++]);
|
|
shadow.blur = buf[pos++];
|
|
shadow.spread = buf[pos++];
|
|
shadow.color.r = buf[pos++]; shadow.color.g = buf[pos++]; shadow.color.b = buf[pos++];
|
|
shadow.enabled = buf[pos++] != 0;
|
|
|
|
isToggle = buf[pos++] != 0;
|
|
knxAddressWrite = buf[pos] | (buf[pos+1] << 8);
|
|
pos += 2;
|
|
action = static_cast<ButtonAction>(buf[pos++]);
|
|
targetScreen = buf[pos++];
|
|
|
|
// Icon properties
|
|
iconCodepoint = buf[pos] | (buf[pos+1] << 8) | (buf[pos+2] << 16) | (buf[pos+3] << 24);
|
|
pos += 4;
|
|
iconPosition = buf[pos++];
|
|
iconSize = buf[pos++];
|
|
iconGap = static_cast<int8_t>(buf[pos++]);
|
|
|
|
// Hierarchy
|
|
parentId = static_cast<int8_t>(buf[pos++]);
|
|
}
|
|
|
|
WidgetConfig WidgetConfig::createLabel(uint8_t id, int16_t x, int16_t y, const char* labelText) {
|
|
WidgetConfig cfg = {};
|
|
cfg.id = id;
|
|
cfg.parentId = -1; // Root
|
|
cfg.type = WidgetType::LABEL;
|
|
cfg.x = x;
|
|
cfg.y = y;
|
|
cfg.width = 150;
|
|
cfg.height = 40;
|
|
cfg.visible = true;
|
|
cfg.textSource = TextSource::STATIC;
|
|
strncpy(cfg.text, labelText, MAX_TEXT_LEN - 1);
|
|
cfg.fontSize = 1; // 18pt
|
|
cfg.textColor = {255, 255, 255};
|
|
cfg.bgColor = {0, 0, 0};
|
|
cfg.bgOpacity = 0;
|
|
cfg.borderRadius = 0;
|
|
cfg.shadow.enabled = false;
|
|
// Icon defaults
|
|
cfg.iconCodepoint = 0;
|
|
cfg.iconPosition = 0;
|
|
cfg.iconSize = 1;
|
|
cfg.iconGap = 8;
|
|
return cfg;
|
|
}
|
|
|
|
WidgetConfig WidgetConfig::createKnxLabel(uint8_t id, int16_t x, int16_t y,
|
|
TextSource source, uint16_t knxAddr, const char* format) {
|
|
WidgetConfig cfg = createLabel(id, x, y, format);
|
|
cfg.textSource = source;
|
|
cfg.knxAddress = knxAddr;
|
|
return cfg;
|
|
}
|
|
|
|
WidgetConfig WidgetConfig::createButton(uint8_t id, int16_t x, int16_t y,
|
|
const char* labelText, uint16_t knxAddrWrite, bool toggle) {
|
|
WidgetConfig cfg = {};
|
|
cfg.id = id;
|
|
cfg.parentId = -1; // Root
|
|
cfg.type = WidgetType::BUTTON;
|
|
cfg.x = x;
|
|
cfg.y = y;
|
|
cfg.width = 120;
|
|
cfg.height = 50;
|
|
cfg.visible = true;
|
|
cfg.textSource = TextSource::STATIC;
|
|
strncpy(cfg.text, labelText, MAX_TEXT_LEN - 1);
|
|
cfg.fontSize = 1;
|
|
cfg.textColor = {255, 255, 255};
|
|
cfg.bgColor = {33, 150, 243}; // Blue
|
|
cfg.bgOpacity = 255;
|
|
cfg.borderRadius = 8;
|
|
cfg.shadow.enabled = true;
|
|
cfg.shadow.offsetX = 2;
|
|
cfg.shadow.offsetY = 2;
|
|
cfg.shadow.blur = 8;
|
|
cfg.shadow.spread = 0;
|
|
cfg.shadow.color = {0, 0, 0};
|
|
cfg.isToggle = toggle;
|
|
cfg.knxAddressWrite = knxAddrWrite;
|
|
cfg.action = ButtonAction::KNX;
|
|
cfg.targetScreen = 0;
|
|
// Icon defaults
|
|
cfg.iconCodepoint = 0;
|
|
cfg.iconPosition = 0;
|
|
cfg.iconSize = 1;
|
|
cfg.iconGap = 8;
|
|
return cfg;
|
|
}
|
|
|
|
// ScreenConfig implementation
|
|
void ScreenConfig::clear(uint8_t newId, const char* newName) {
|
|
id = newId;
|
|
mode = ScreenMode::FULLSCREEN;
|
|
backgroundColor = {26, 26, 46}; // Dark blue background
|
|
widgetCount = 0;
|
|
memset(widgets, 0, sizeof(widgets));
|
|
memset(name, 0, sizeof(name));
|
|
if (newName && newName[0] != '\0') {
|
|
strncpy(name, newName, sizeof(name) - 1);
|
|
}
|
|
// Modal defaults
|
|
modalX = 0; // 0 = centered
|
|
modalY = 0; // 0 = centered
|
|
modalWidth = 0; // 0 = auto
|
|
modalHeight = 0; // 0 = auto
|
|
modalBorderRadius = 12;
|
|
modalDimBackground = true;
|
|
}
|
|
|
|
int ScreenConfig::addWidget(const WidgetConfig& widget) {
|
|
if (widgetCount >= MAX_WIDGETS) return -1;
|
|
|
|
// Find next free ID
|
|
uint8_t newId = 0;
|
|
for (uint8_t i = 0; i < widgetCount; i++) {
|
|
if (widgets[i].id >= newId) newId = widgets[i].id + 1;
|
|
}
|
|
|
|
widgets[widgetCount] = widget;
|
|
widgets[widgetCount].id = newId;
|
|
widgetCount++;
|
|
return newId;
|
|
}
|
|
|
|
bool ScreenConfig::removeWidget(uint8_t id) {
|
|
for (uint8_t i = 0; i < widgetCount; i++) {
|
|
if (widgets[i].id == id) {
|
|
// Shift remaining widgets
|
|
for (uint8_t j = i; j < widgetCount - 1; j++) {
|
|
widgets[j] = widgets[j + 1];
|
|
}
|
|
widgetCount--;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
WidgetConfig* ScreenConfig::findWidget(uint8_t id) {
|
|
for (uint8_t i = 0; i < widgetCount; i++) {
|
|
if (widgets[i].id == id) return &widgets[i];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const WidgetConfig* ScreenConfig::findWidget(uint8_t id) const {
|
|
for (uint8_t i = 0; i < widgetCount; i++) {
|
|
if (widgets[i].id == id) return &widgets[i];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// GuiConfig implementation
|
|
void GuiConfig::clear() {
|
|
screenCount = 0;
|
|
startScreenId = 0;
|
|
standbyEnabled = false;
|
|
standbyScreenId = 0xFF;
|
|
standbyMinutes = 0;
|
|
for (size_t i = 0; i < MAX_SCREENS; i++) {
|
|
screens[i].clear(static_cast<uint8_t>(i), nullptr);
|
|
}
|
|
}
|
|
|
|
ScreenConfig* GuiConfig::findScreen(uint8_t id) {
|
|
for (uint8_t i = 0; i < screenCount; i++) {
|
|
if (screens[i].id == id) return &screens[i];
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const ScreenConfig* GuiConfig::findScreen(uint8_t id) const {
|
|
for (uint8_t i = 0; i < screenCount; i++) {
|
|
if (screens[i].id == id) return &screens[i];
|
|
}
|
|
return nullptr;
|
|
}
|