diff --git a/main/WidgetConfig.cpp b/main/WidgetConfig.cpp index 51d933b..cf8b214 100644 --- a/main/WidgetConfig.cpp +++ b/main/WidgetConfig.cpp @@ -434,7 +434,7 @@ WidgetConfig WidgetConfig::createButton(uint8_t id, int16_t x, int16_t y, strncpy(cfg.text, labelText, MAX_TEXT_LEN - 1); cfg.fontSize = 1; cfg.textAlign = static_cast(TextAlign::CENTER); - cfg.isContainer = true; + cfg.isContainer = false; cfg.textColor = {255, 255, 255}; cfg.bgColor = {33, 150, 243}; // Blue cfg.bgOpacity = 255; diff --git a/main/WidgetManager.cpp b/main/WidgetManager.cpp index f91908f..5381fa1 100644 --- a/main/WidgetManager.cpp +++ b/main/WidgetManager.cpp @@ -89,58 +89,73 @@ static void latin1_to_utf8(const char* src, size_t src_len, char* dst, size_t ds dst[di] = '\0'; } -static WidgetConfig makeButtonLabelChild(const WidgetConfig& button) { - WidgetConfig label = WidgetConfig::createLabel(0, 0, 0, button.text); - label.parentId = button.id; - if (button.width > 0) label.width = button.width; - if (button.height > 0) label.height = button.height; - label.fontSize = button.fontSize; - label.textAlign = button.textAlign; - label.textColor = button.textColor; - label.textSource = TextSource::STATIC; - label.bgOpacity = 0; - label.borderRadius = 0; - label.shadow.enabled = false; - // Preserve existing icon config if any - label.iconCodepoint = button.iconCodepoint; - label.iconPosition = button.iconPosition; - label.iconSize = button.iconSize; - label.iconGap = button.iconGap; - if (label.text[0] == '\0') { - strncpy(label.text, "Button", MAX_TEXT_LEN - 1); - label.text[MAX_TEXT_LEN - 1] = '\0'; - } - return label; +static bool color_equals(const Color& a, const Color& b) { + return a.r == b.r && a.g == b.g && a.b == b.b; } -static void ensureButtonLabels(ScreenConfig& screen) { - bool hasLabelChild[MAX_WIDGETS] = {}; +static bool is_auto_button_label(const WidgetConfig& button, const WidgetConfig& label) { + if (label.type != WidgetType::LABEL) return false; + if (label.parentId < 0 || label.parentId != static_cast(button.id)) return false; + if (label.textSource != TextSource::STATIC) return false; + bool textMatches = (strncmp(label.text, button.text, MAX_TEXT_LEN) == 0); + if (!textMatches && button.text[0] == '\0') { + if (strncmp(label.text, "Button", MAX_TEXT_LEN) == 0 || label.text[0] == '\0') { + textMatches = true; + } + } + if (!textMatches) return false; + if (label.fontSize != button.fontSize) return false; + if (label.textAlign != button.textAlign) return false; + if (!color_equals(label.textColor, button.textColor)) return false; + if (label.bgOpacity != 0) return false; + if (label.borderRadius != 0) return false; + if (label.shadow.enabled) return false; + if (label.iconCodepoint != button.iconCodepoint) return false; + if (label.iconPosition != button.iconPosition) return false; + if (label.iconSize != button.iconSize) return false; + if (label.iconGap != button.iconGap) return false; + if (label.x != 0 || label.y != 0) return false; + return true; +} + +static void normalizeButtons(ScreenConfig& screen) { + uint8_t removeIds[MAX_WIDGETS] = {}; + uint8_t removeCount = 0; + + // Remove legacy auto-generated label children for buttons for (uint8_t i = 0; i < screen.widgetCount; i++) { const WidgetConfig& w = screen.widgets[i]; - if (w.type == WidgetType::LABEL && w.parentId >= 0 && w.parentId < MAX_WIDGETS) { - hasLabelChild[w.parentId] = true; + if (w.type != WidgetType::LABEL || w.parentId < 0) continue; + + WidgetConfig* parent = screen.findWidget(static_cast(w.parentId)); + if (!parent || parent->type != WidgetType::BUTTON) continue; + + bool forceRemove = parent->text[0] != '\0'; + if (forceRemove || is_auto_button_label(*parent, w)) { + if (removeCount < MAX_WIDGETS) { + removeIds[removeCount++] = w.id; + } } } - const uint8_t initialCount = screen.widgetCount; - for (uint8_t i = 0; i < initialCount; i++) { + for (uint8_t i = 0; i < removeCount; i++) { + screen.removeWidget(removeIds[i]); + } + + // Update container flag based on remaining children + for (uint8_t i = 0; i < screen.widgetCount; i++) { WidgetConfig& w = screen.widgets[i]; if (w.type != WidgetType::BUTTON) continue; - w.isContainer = true; - - if (w.id < MAX_WIDGETS && hasLabelChild[w.id]) continue; - - WidgetConfig label = makeButtonLabelChild(w); - int newId = screen.addWidget(label); - if (newId < 0) { - ESP_LOGW(TAG, "No space to add label child for button %d", w.id); - w.isContainer = false; - continue; - } - if (w.id < MAX_WIDGETS) { - hasLabelChild[w.id] = true; + bool hasChild = false; + for (uint8_t j = 0; j < screen.widgetCount; j++) { + const WidgetConfig& child = screen.widgets[j]; + if (child.parentId >= 0 && child.parentId == static_cast(w.id)) { + hasChild = true; + break; + } } + w.isContainer = hasChild; } } @@ -188,7 +203,7 @@ void WidgetManager::createDefaultConfig() { progBtn.bgColor = {200, 50, 50}; // Red screen.addWidget(progBtn); - ensureButtonLabels(screen); + normalizeButtons(screen); config_->startScreenId = screen.id; config_->standbyEnabled = false; @@ -352,7 +367,7 @@ void WidgetManager::applyScreenLocked(uint8_t screenId) { return; } - ensureButtonLabels(*screen); + normalizeButtons(*screen); if (modalContainer_) { ESP_LOGI(TAG, "Closing modal first"); @@ -2286,7 +2301,7 @@ bool WidgetManager::updateConfigFromJson(const char* json) { if (!parseWidgets(widgets, screen)) { screen.widgetCount = 0; } - ensureButtonLabels(screen); + normalizeButtons(screen); newConfig->screenCount++; } @@ -2305,7 +2320,7 @@ bool WidgetManager::updateConfigFromJson(const char* json) { cJSON_Delete(root); return false; } - ensureButtonLabels(screen); + normalizeButtons(screen); } cJSON* startScreen = cJSON_GetObjectItem(root, "startScreen"); diff --git a/main/widgets/ButtonWidget.cpp b/main/widgets/ButtonWidget.cpp index 265d3a4..9519937 100644 --- a/main/widgets/ButtonWidget.cpp +++ b/main/widgets/ButtonWidget.cpp @@ -12,6 +12,12 @@ static lv_text_align_t toLvTextAlign(uint8_t align) { return LV_TEXT_ALIGN_CENTER; } +static lv_align_t toLvAlign(uint8_t align) { + if (align == static_cast(TextAlign::LEFT)) return LV_ALIGN_LEFT_MID; + if (align == static_cast(TextAlign::RIGHT)) return LV_ALIGN_RIGHT_MID; + return LV_ALIGN_CENTER; +} + static lv_flex_align_t toFlexAlign(uint8_t align) { if (align == static_cast(TextAlign::LEFT)) return LV_FLEX_ALIGN_START; if (align == static_cast(TextAlign::RIGHT)) return LV_FLEX_ALIGN_END; @@ -130,7 +136,7 @@ void ButtonWidget::setupFlexLayout() { void ButtonWidget::applyTextAlignment() { if (label_ == nullptr) return; lv_obj_set_style_text_align(label_, toLvTextAlign(config_.textAlign), 0); - lv_obj_center(label_); + lv_obj_align(label_, toLvAlign(config_.textAlign), 0, 0); } lv_obj_t* ButtonWidget::create(lv_obj_t* parent) { diff --git a/web-interface/src/components/widgets/settings/ButtonSettings.vue b/web-interface/src/components/widgets/settings/ButtonSettings.vue index d195223..dc7327b 100644 --- a/web-interface/src/components/widgets/settings/ButtonSettings.vue +++ b/web-interface/src/components/widgets/settings/ButtonSettings.vue @@ -13,8 +13,56 @@ + +

Typo

+
+ +
+
+ +
+ + +

Icon

+
+ + + +
+ +

Stil

+
@@ -63,13 +111,15 @@