knxdisplay/main/WidgetConfig.cpp
2026-01-29 19:33:12 +01:00

301 lines
9.3 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++] = textAlign;
buf[pos++] = isContainer ? 1 : 0;
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);
// Chart properties
buf[pos++] = chartPeriod;
buf[pos++] = chartSeriesCount;
for (size_t i = 0; i < CHART_MAX_SERIES; ++i) {
buf[pos++] = chartKnxAddress[i] & 0xFF;
buf[pos++] = (chartKnxAddress[i] >> 8) & 0xFF;
}
for (size_t i = 0; i < CHART_MAX_SERIES; ++i) {
buf[pos++] = static_cast<uint8_t>(chartTextSource[i]);
}
for (size_t i = 0; i < CHART_MAX_SERIES; ++i) {
buf[pos++] = chartSeriesColor[i].r;
buf[pos++] = chartSeriesColor[i].g;
buf[pos++] = chartSeriesColor[i].b;
}
}
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++];
textAlign = buf[pos++];
isContainer = buf[pos++] != 0;
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++]);
// Chart properties
chartPeriod = buf[pos++];
chartSeriesCount = buf[pos++];
for (size_t i = 0; i < CHART_MAX_SERIES; ++i) {
chartKnxAddress[i] = buf[pos] | (buf[pos + 1] << 8);
pos += 2;
}
for (size_t i = 0; i < CHART_MAX_SERIES; ++i) {
chartTextSource[i] = static_cast<TextSource>(buf[pos++]);
}
for (size_t i = 0; i < CHART_MAX_SERIES; ++i) {
chartSeriesColor[i].r = buf[pos++];
chartSeriesColor[i].g = buf[pos++];
chartSeriesColor[i].b = 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.textAlign = static_cast<uint8_t>(TextAlign::LEFT);
cfg.isContainer = false;
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.textAlign = static_cast<uint8_t>(TextAlign::CENTER);
cfg.isContainer = true;
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;
knxTimeAddress = 0;
knxDateAddress = 0;
knxDateTimeAddress = 0;
knxNightModeAddress = 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;
}