#include "ButtonWidget.hpp" #include "../WidgetManager.hpp" #include "../Fonts.hpp" #include "esp_log.h" static const char* TAG = "ButtonWidget"; ButtonWidget::ButtonWidget(const WidgetConfig& config) : Widget(config) , contentContainer_(nullptr) , label_(nullptr) , iconLabel_(nullptr) { } ButtonWidget::~ButtonWidget() { // Remove event callback BEFORE the base class destructor deletes the object if (obj_) { lv_obj_remove_event_cb(obj_, clickCallback); } contentContainer_ = nullptr; label_ = nullptr; iconLabel_ = nullptr; } int ButtonWidget::encodeUtf8(uint32_t codepoint, char* buf) { if (codepoint < 0x80) { buf[0] = static_cast(codepoint); buf[1] = '\0'; return 1; } else if (codepoint < 0x800) { buf[0] = static_cast(0xC0 | (codepoint >> 6)); buf[1] = static_cast(0x80 | (codepoint & 0x3F)); buf[2] = '\0'; return 2; } else if (codepoint < 0x10000) { buf[0] = static_cast(0xE0 | (codepoint >> 12)); buf[1] = static_cast(0x80 | ((codepoint >> 6) & 0x3F)); buf[2] = static_cast(0x80 | (codepoint & 0x3F)); buf[3] = '\0'; return 3; } else if (codepoint < 0x110000) { buf[0] = static_cast(0xF0 | (codepoint >> 18)); buf[1] = static_cast(0x80 | ((codepoint >> 12) & 0x3F)); buf[2] = static_cast(0x80 | ((codepoint >> 6) & 0x3F)); buf[3] = static_cast(0x80 | (codepoint & 0x3F)); buf[4] = '\0'; return 4; } buf[0] = '\0'; return 0; } void ButtonWidget::clickCallback(lv_event_t* e) { ESP_LOGI(TAG, "clickCallback called"); ButtonWidget* widget = static_cast(lv_event_get_user_data(e)); if (!widget) return; lv_obj_t* target = static_cast(lv_event_get_target(e)); WidgetManager::instance().handleButtonAction(widget->getConfig(), target); } void ButtonWidget::setupFlexLayout() { if (contentContainer_ == nullptr) return; // Determine flex direction based on icon position bool isVertical = (config_.iconPosition == static_cast(IconPosition::TOP) || config_.iconPosition == static_cast(IconPosition::BOTTOM)); lv_obj_set_flex_flow(contentContainer_, isVertical ? LV_FLEX_FLOW_COLUMN : LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(contentContainer_, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); // Set gap between icon and text int gap = config_.iconGap > 0 ? config_.iconGap : 8; lv_obj_set_style_pad_gap(contentContainer_, gap, 0); } lv_obj_t* ButtonWidget::create(lv_obj_t* parent) { obj_ = lv_btn_create(parent); lv_obj_set_pos(obj_, config_.x, config_.y); lv_obj_set_size(obj_, config_.width > 0 ? config_.width : 100, config_.height > 0 ? config_.height : 50); lv_obj_add_event_cb(obj_, clickCallback, LV_EVENT_CLICKED, this); bool hasIcon = config_.iconCodepoint > 0 && Fonts::hasIconFont(); if (hasIcon) { // Create container for flex layout contentContainer_ = lv_obj_create(obj_); lv_obj_remove_style_all(contentContainer_); lv_obj_set_size(contentContainer_, LV_SIZE_CONTENT, LV_SIZE_CONTENT); lv_obj_center(contentContainer_); // Create icon label bool iconFirst = (config_.iconPosition == static_cast(IconPosition::LEFT) || config_.iconPosition == static_cast(IconPosition::TOP)); if (iconFirst) { iconLabel_ = lv_label_create(contentContainer_); char iconText[5]; encodeUtf8(config_.iconCodepoint, iconText); lv_label_set_text(iconLabel_, iconText); } // Create text label label_ = lv_label_create(contentContainer_); lv_label_set_text(label_, config_.text); if (!iconFirst) { iconLabel_ = lv_label_create(contentContainer_); char iconText[5]; encodeUtf8(config_.iconCodepoint, iconText); lv_label_set_text(iconLabel_, iconText); } setupFlexLayout(); } else { // Simple button without icon label_ = lv_label_create(obj_); lv_label_set_text(label_, config_.text); lv_obj_center(label_); } ESP_LOGI(TAG, "Created button '%s' at %d,%d (icon: 0x%lx)", config_.text, config_.x, config_.y, (unsigned long)config_.iconCodepoint); return obj_; } void ButtonWidget::applyStyle() { if (obj_ == nullptr) return; // Apply common style to button applyCommonStyle(); // Apply text style to label if (label_ != nullptr) { lv_obj_set_style_text_color(label_, lv_color_make( config_.textColor.r, config_.textColor.g, config_.textColor.b), 0); lv_obj_set_style_text_font(label_, getFontBySize(config_.fontSize), 0); } // Apply icon style if (iconLabel_ != nullptr) { lv_obj_set_style_text_color(iconLabel_, lv_color_make( config_.textColor.r, config_.textColor.g, config_.textColor.b), 0); uint8_t sizeIdx = config_.iconSize < 6 ? config_.iconSize : config_.fontSize; lv_obj_set_style_text_font(iconLabel_, Fonts::iconFont(sizeIdx), 0); } } bool ButtonWidget::isChecked() const { if (obj_ == nullptr) return false; return (lv_obj_get_state(obj_) & LV_STATE_CHECKED) != 0; }