224 lines
8.0 KiB
C++
224 lines
8.0 KiB
C++
#include "LabelWidget.hpp"
|
|
#include "../Fonts.hpp"
|
|
#include <cstdio>
|
|
|
|
LabelWidget::LabelWidget(const WidgetConfig& config)
|
|
: Widget(config)
|
|
, container_(nullptr)
|
|
, textLabel_(nullptr)
|
|
, iconLabel_(nullptr)
|
|
{
|
|
}
|
|
|
|
static lv_text_align_t toLvTextAlign(uint8_t align) {
|
|
if (align == static_cast<uint8_t>(TextAlign::LEFT)) return LV_TEXT_ALIGN_LEFT;
|
|
if (align == static_cast<uint8_t>(TextAlign::RIGHT)) return LV_TEXT_ALIGN_RIGHT;
|
|
return LV_TEXT_ALIGN_CENTER;
|
|
}
|
|
|
|
static lv_flex_align_t toFlexAlign(uint8_t align) {
|
|
if (align == static_cast<uint8_t>(TextAlign::LEFT)) return LV_FLEX_ALIGN_START;
|
|
if (align == static_cast<uint8_t>(TextAlign::RIGHT)) return LV_FLEX_ALIGN_END;
|
|
return LV_FLEX_ALIGN_CENTER;
|
|
}
|
|
|
|
int LabelWidget::encodeUtf8(uint32_t codepoint, char* buf) {
|
|
if (codepoint < 0x80) {
|
|
buf[0] = static_cast<char>(codepoint);
|
|
buf[1] = '\0';
|
|
return 1;
|
|
} else if (codepoint < 0x800) {
|
|
buf[0] = static_cast<char>(0xC0 | (codepoint >> 6));
|
|
buf[1] = static_cast<char>(0x80 | (codepoint & 0x3F));
|
|
buf[2] = '\0';
|
|
return 2;
|
|
} else if (codepoint < 0x10000) {
|
|
buf[0] = static_cast<char>(0xE0 | (codepoint >> 12));
|
|
buf[1] = static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F));
|
|
buf[2] = static_cast<char>(0x80 | (codepoint & 0x3F));
|
|
buf[3] = '\0';
|
|
return 3;
|
|
} else if (codepoint < 0x110000) {
|
|
buf[0] = static_cast<char>(0xF0 | (codepoint >> 18));
|
|
buf[1] = static_cast<char>(0x80 | ((codepoint >> 12) & 0x3F));
|
|
buf[2] = static_cast<char>(0x80 | ((codepoint >> 6) & 0x3F));
|
|
buf[3] = static_cast<char>(0x80 | (codepoint & 0x3F));
|
|
buf[4] = '\0';
|
|
return 4;
|
|
}
|
|
buf[0] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
void LabelWidget::setupFlexLayout() {
|
|
if (container_ == nullptr) return;
|
|
|
|
// Determine flex direction based on icon position
|
|
bool isVertical = (config_.iconPosition == static_cast<uint8_t>(IconPosition::TOP) ||
|
|
config_.iconPosition == static_cast<uint8_t>(IconPosition::BOTTOM));
|
|
|
|
lv_obj_set_flex_flow(container_,
|
|
isVertical ? LV_FLEX_FLOW_COLUMN : LV_FLEX_FLOW_ROW);
|
|
lv_flex_align_t mainAlign = LV_FLEX_ALIGN_CENTER;
|
|
lv_flex_align_t crossAlign = LV_FLEX_ALIGN_CENTER;
|
|
lv_flex_align_t contentAlign = toFlexAlign(config_.textAlign);
|
|
if (contentAlign != LV_FLEX_ALIGN_CENTER) {
|
|
if (isVertical) {
|
|
crossAlign = contentAlign;
|
|
} else {
|
|
mainAlign = contentAlign;
|
|
}
|
|
}
|
|
lv_obj_set_flex_align(container_, mainAlign, crossAlign, LV_FLEX_ALIGN_CENTER);
|
|
|
|
// Set gap between icon and text
|
|
int gap = config_.iconGap > 0 ? config_.iconGap : 8;
|
|
lv_obj_set_style_pad_gap(container_, gap, 0);
|
|
}
|
|
|
|
lv_obj_t* LabelWidget::create(lv_obj_t* parent) {
|
|
bool hasIcon = config_.iconCodepoint > 0 && Fonts::hasIconFont();
|
|
bool needsContainer = hasIcon || config_.bgOpacity > 0 || config_.borderRadius > 0;
|
|
|
|
if (needsContainer) {
|
|
// Create container object for background/radius/flex layout
|
|
obj_ = lv_obj_create(parent);
|
|
if (obj_ == nullptr) {
|
|
return nullptr;
|
|
}
|
|
lv_obj_remove_style_all(obj_);
|
|
lv_obj_set_pos(obj_, config_.x, config_.y);
|
|
if (config_.width > 0 && config_.height > 0) {
|
|
lv_obj_set_size(obj_, config_.width, config_.height);
|
|
} else {
|
|
lv_obj_set_size(obj_, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
|
}
|
|
|
|
container_ = obj_;
|
|
|
|
if (hasIcon) {
|
|
// Create elements in correct order based on icon position
|
|
bool iconFirst = (config_.iconPosition == static_cast<uint8_t>(IconPosition::LEFT) ||
|
|
config_.iconPosition == static_cast<uint8_t>(IconPosition::TOP));
|
|
|
|
if (iconFirst) {
|
|
iconLabel_ = lv_label_create(container_);
|
|
char iconText[5];
|
|
encodeUtf8(config_.iconCodepoint, iconText);
|
|
lv_label_set_text(iconLabel_, iconText);
|
|
lv_obj_clear_flag(iconLabel_, LV_OBJ_FLAG_CLICKABLE);
|
|
}
|
|
|
|
textLabel_ = lv_label_create(container_);
|
|
lv_label_set_text(textLabel_, config_.text);
|
|
lv_obj_clear_flag(textLabel_, LV_OBJ_FLAG_CLICKABLE);
|
|
|
|
if (!iconFirst) {
|
|
iconLabel_ = lv_label_create(container_);
|
|
char iconText[5];
|
|
encodeUtf8(config_.iconCodepoint, iconText);
|
|
lv_label_set_text(iconLabel_, iconText);
|
|
lv_obj_clear_flag(iconLabel_, LV_OBJ_FLAG_CLICKABLE);
|
|
}
|
|
|
|
setupFlexLayout();
|
|
} else {
|
|
// Container with just text label (for bg/radius)
|
|
textLabel_ = lv_label_create(container_);
|
|
lv_label_set_text(textLabel_, config_.text);
|
|
lv_obj_center(textLabel_);
|
|
lv_obj_clear_flag(textLabel_, LV_OBJ_FLAG_CLICKABLE);
|
|
}
|
|
} else {
|
|
// Simple label without container
|
|
obj_ = lv_label_create(parent);
|
|
textLabel_ = obj_;
|
|
lv_label_set_text(obj_, config_.text);
|
|
lv_obj_set_pos(obj_, config_.x, config_.y);
|
|
if (config_.width > 0 && config_.height > 0) {
|
|
lv_obj_set_size(obj_, config_.width, config_.height);
|
|
}
|
|
}
|
|
|
|
if (obj_ != nullptr) {
|
|
lv_obj_clear_flag(obj_, LV_OBJ_FLAG_CLICKABLE);
|
|
}
|
|
return obj_;
|
|
}
|
|
|
|
void LabelWidget::applyStyle() {
|
|
if (obj_ == nullptr) return;
|
|
|
|
// Apply background and border radius if container exists
|
|
if (container_ != nullptr) {
|
|
if (config_.bgOpacity > 0) {
|
|
lv_obj_set_style_bg_color(obj_, lv_color_make(
|
|
config_.bgColor.r, config_.bgColor.g, config_.bgColor.b), 0);
|
|
lv_obj_set_style_bg_opa(obj_, config_.bgOpacity, 0);
|
|
}
|
|
if (config_.borderRadius > 0) {
|
|
lv_obj_set_style_radius(obj_, config_.borderRadius, 0);
|
|
}
|
|
}
|
|
|
|
// Apply text style
|
|
if (textLabel_ != nullptr) {
|
|
lv_obj_set_style_text_color(textLabel_, lv_color_make(
|
|
config_.textColor.r, config_.textColor.g, config_.textColor.b), 0);
|
|
lv_obj_set_style_text_font(textLabel_, Fonts::bySizeIndex(config_.fontSize), 0);
|
|
lv_obj_set_style_text_align(textLabel_, toLvTextAlign(config_.textAlign), 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);
|
|
}
|
|
|
|
// For simple label (no container), apply common style
|
|
if (container_ == nullptr) {
|
|
applyCommonStyle();
|
|
}
|
|
}
|
|
|
|
void LabelWidget::onKnxValue(float value) {
|
|
lv_obj_t* label = textLabel_ ? textLabel_ : obj_;
|
|
if (label == nullptr) return;
|
|
if (config_.textSource != TextSource::KNX_DPT_TEMP &&
|
|
config_.textSource != TextSource::KNX_DPT_PERCENT &&
|
|
config_.textSource != TextSource::KNX_DPT_POWER &&
|
|
config_.textSource != TextSource::KNX_DPT_ENERGY &&
|
|
config_.textSource != TextSource::KNX_DPT_DECIMALFACTOR) {
|
|
return;
|
|
}
|
|
|
|
char buf[32];
|
|
if (config_.textSource == TextSource::KNX_DPT_PERCENT ||
|
|
config_.textSource == TextSource::KNX_DPT_DECIMALFACTOR) {
|
|
int intValue = static_cast<int>(value + 0.5f);
|
|
snprintf(buf, sizeof(buf), config_.text, intValue);
|
|
} else {
|
|
snprintf(buf, sizeof(buf), config_.text, value);
|
|
}
|
|
lv_label_set_text(label, buf);
|
|
}
|
|
|
|
void LabelWidget::onKnxSwitch(bool value) {
|
|
lv_obj_t* label = textLabel_ ? textLabel_ : obj_;
|
|
if (label == nullptr) return;
|
|
if (config_.textSource != TextSource::KNX_DPT_SWITCH) return;
|
|
|
|
lv_label_set_text(label, value ? "EIN" : "AUS");
|
|
}
|
|
|
|
void LabelWidget::onKnxText(const char* text) {
|
|
lv_obj_t* label = textLabel_ ? textLabel_ : obj_;
|
|
if (label == nullptr) return;
|
|
if (config_.textSource != TextSource::KNX_DPT_TEXT) return;
|
|
|
|
lv_label_set_text(label, text);
|
|
}
|