222 lines
6.4 KiB
C++
222 lines
6.4 KiB
C++
#include "Widget.hpp"
|
|
#include "../Fonts.hpp"
|
|
#include "lvgl.h"
|
|
|
|
Widget::Widget(const WidgetConfig& config)
|
|
: config_(config)
|
|
, obj_(nullptr)
|
|
{
|
|
}
|
|
|
|
Widget::~Widget() {
|
|
destroy();
|
|
}
|
|
|
|
void Widget::destroy() {
|
|
if (obj_ != nullptr) {
|
|
if (lv_obj_is_valid(obj_)) {
|
|
lv_obj_delete(obj_);
|
|
}
|
|
obj_ = nullptr;
|
|
}
|
|
}
|
|
|
|
void Widget::applyStyle() {
|
|
if (obj_ == nullptr) return;
|
|
applyCommonStyle();
|
|
}
|
|
|
|
void Widget::onKnxValue(float value) {
|
|
cachedPrimaryValue_ = value;
|
|
hasCachedPrimary_ = true;
|
|
evaluateConditions(cachedPrimaryValue_, cachedSecondaryValue_, cachedTertiaryValue_);
|
|
}
|
|
|
|
void Widget::onKnxValue2(float value) {
|
|
cachedSecondaryValue_ = value;
|
|
hasCachedSecondary_ = true;
|
|
evaluateConditions(cachedPrimaryValue_, cachedSecondaryValue_, cachedTertiaryValue_);
|
|
}
|
|
|
|
void Widget::onKnxValue3(float value) {
|
|
cachedTertiaryValue_ = value;
|
|
hasCachedTertiary_ = true;
|
|
evaluateConditions(cachedPrimaryValue_, cachedSecondaryValue_, cachedTertiaryValue_);
|
|
}
|
|
|
|
void Widget::onKnxSwitch(bool /*value*/) {
|
|
// Default: do nothing
|
|
}
|
|
|
|
void Widget::onKnxText(const char* /*text*/) {
|
|
// Default: do nothing
|
|
}
|
|
|
|
void Widget::onKnxTime(const struct tm& /*value*/, TextSource /*source*/) {
|
|
// Default: do nothing
|
|
}
|
|
|
|
void Widget::onHistoryUpdate() {
|
|
// Default: do nothing
|
|
}
|
|
|
|
bool Widget::evaluateConditions(float primaryValue, float secondaryValue, float tertiaryValue) {
|
|
if (config_.conditionCount == 0) return false;
|
|
|
|
const StyleCondition* bestMatch = nullptr;
|
|
uint8_t bestPriority = 255;
|
|
|
|
for (uint8_t i = 0; i < config_.conditionCount && i < MAX_CONDITIONS; ++i) {
|
|
const StyleCondition& cond = config_.conditions[i];
|
|
if (!cond.enabled) continue;
|
|
|
|
// Get the value to check based on source
|
|
float checkValue = 0.0f;
|
|
bool hasValue = false;
|
|
switch (cond.source) {
|
|
case ConditionSource::PRIMARY:
|
|
checkValue = primaryValue;
|
|
hasValue = hasCachedPrimary_;
|
|
break;
|
|
case ConditionSource::SECONDARY:
|
|
checkValue = secondaryValue;
|
|
hasValue = hasCachedSecondary_;
|
|
break;
|
|
case ConditionSource::TERTIARY:
|
|
checkValue = tertiaryValue;
|
|
hasValue = hasCachedTertiary_;
|
|
break;
|
|
}
|
|
|
|
if (!hasValue) continue;
|
|
|
|
// Evaluate condition
|
|
bool matches = false;
|
|
switch (cond.op) {
|
|
case ConditionOp::LESS:
|
|
matches = checkValue < cond.threshold;
|
|
break;
|
|
case ConditionOp::LESS_EQUAL:
|
|
matches = checkValue <= cond.threshold;
|
|
break;
|
|
case ConditionOp::EQUAL:
|
|
matches = checkValue == cond.threshold;
|
|
break;
|
|
case ConditionOp::GREATER_EQUAL:
|
|
matches = checkValue >= cond.threshold;
|
|
break;
|
|
case ConditionOp::GREATER:
|
|
matches = checkValue > cond.threshold;
|
|
break;
|
|
case ConditionOp::NOT_EQUAL:
|
|
matches = checkValue != cond.threshold;
|
|
break;
|
|
}
|
|
|
|
if (matches && cond.priority < bestPriority) {
|
|
bestMatch = &cond;
|
|
bestPriority = cond.priority;
|
|
}
|
|
}
|
|
|
|
if (bestMatch) {
|
|
applyConditionStyle(bestMatch->style);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Widget::applyConditionStyle(const ConditionStyle& style) {
|
|
if (obj_ == nullptr) return;
|
|
|
|
// Handle hide flag
|
|
if (style.flags & ConditionStyle::FLAG_HIDE) {
|
|
lv_obj_add_flag(obj_, LV_OBJ_FLAG_HIDDEN);
|
|
return;
|
|
} else {
|
|
lv_obj_clear_flag(obj_, LV_OBJ_FLAG_HIDDEN);
|
|
}
|
|
|
|
// Apply text color if flag set
|
|
if (style.flags & ConditionStyle::FLAG_USE_TEXT_COLOR) {
|
|
lv_obj_set_style_text_color(obj_, lv_color_make(
|
|
style.textColor.r, style.textColor.g, style.textColor.b), 0);
|
|
}
|
|
|
|
// Apply background color if flag set
|
|
if (style.flags & ConditionStyle::FLAG_USE_BG_COLOR) {
|
|
lv_obj_set_style_bg_color(obj_, lv_color_make(
|
|
style.bgColor.r, style.bgColor.g, style.bgColor.b), 0);
|
|
}
|
|
|
|
// Apply background opacity if flag set
|
|
if (style.flags & ConditionStyle::FLAG_USE_BG_OPACITY) {
|
|
lv_obj_set_style_bg_opa(obj_, style.bgOpacity, 0);
|
|
}
|
|
|
|
// Icon changes are handled by subclasses that have icons
|
|
if (style.iconCodepoint != 0) {
|
|
currentConditionIcon_ = style.iconCodepoint;
|
|
}
|
|
}
|
|
|
|
void Widget::applyCommonStyle() {
|
|
if (obj_ == nullptr) return;
|
|
|
|
// Text color
|
|
lv_obj_set_style_text_color(obj_, lv_color_make(
|
|
config_.textColor.r, config_.textColor.g, config_.textColor.b), 0);
|
|
|
|
// Font
|
|
lv_obj_set_style_text_font(obj_, getFontBySize(config_.fontSize), 0);
|
|
|
|
// Background (for buttons and labels with bg)
|
|
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);
|
|
}
|
|
|
|
// Border radius
|
|
if (config_.borderRadius > 0) {
|
|
lv_obj_set_style_radius(obj_, config_.borderRadius, 0);
|
|
}
|
|
|
|
// Shadow
|
|
applyShadowStyle();
|
|
}
|
|
|
|
void Widget::applyShadowStyle() {
|
|
if (obj_ == nullptr || !config_.shadow.enabled || config_.type == WidgetType::BUTTON) return;
|
|
|
|
// Limit shadow values to prevent memory issues on ESP32
|
|
constexpr int16_t MAX_SHADOW_BLUR = 15;
|
|
constexpr int16_t MAX_SHADOW_SPREAD = 8;
|
|
|
|
int16_t blur = config_.shadow.blur;
|
|
int16_t spread = config_.shadow.spread;
|
|
|
|
if (blur > MAX_SHADOW_BLUR) blur = MAX_SHADOW_BLUR;
|
|
if (blur < 0) blur = 0;
|
|
if (spread > MAX_SHADOW_SPREAD) spread = MAX_SHADOW_SPREAD;
|
|
if (spread < 0) spread = 0;
|
|
if (blur == 0) return;
|
|
|
|
lv_color_t shadowColor = lv_color_make(
|
|
config_.shadow.color.r, config_.shadow.color.g, config_.shadow.color.b);
|
|
|
|
// Default state shadow
|
|
lv_obj_set_style_shadow_color(obj_, shadowColor, 0);
|
|
lv_obj_set_style_shadow_opa(obj_, 180, 0);
|
|
lv_obj_set_style_shadow_width(obj_, blur, 0);
|
|
lv_obj_set_style_shadow_spread(obj_, spread, 0);
|
|
lv_obj_set_style_shadow_offset_x(obj_, config_.shadow.offsetX, 0);
|
|
lv_obj_set_style_shadow_offset_y(obj_, config_.shadow.offsetY, 0);
|
|
|
|
}
|
|
|
|
const lv_font_t* Widget::getFontBySize(uint8_t sizeIndex) {
|
|
return Fonts::bySizeIndex(sizeIndex);
|
|
}
|