knxdisplay/main/widgets/ClockWidget.cpp
2026-01-30 17:14:50 +01:00

152 lines
4.7 KiB
C++

#include "ClockWidget.hpp"
#include <cmath>
#ifndef PI
#define PI 3.14159265358979323846f
#endif
ClockWidget::ClockWidget(const WidgetConfig& config)
: Widget(config)
{
}
lv_obj_t* ClockWidget::create(lv_obj_t* parent) {
obj_ = lv_obj_create(parent);
if (!obj_) return nullptr;
lv_obj_remove_style_all(obj_);
lv_obj_set_pos(obj_, config_.x, config_.y);
lv_obj_set_size(obj_,
config_.width > 0 ? config_.width : 200,
config_.height > 0 ? config_.height : 200);
lv_obj_clear_flag(obj_, LV_OBJ_FLAG_SCROLLABLE);
// Create hands
hourHand_ = lv_line_create(obj_);
minuteHand_ = lv_line_create(obj_);
secondHand_ = lv_line_create(obj_);
centerDot_ = lv_obj_create(obj_);
// Style center dot
lv_obj_remove_style_all(centerDot_);
lv_obj_set_size(centerDot_, 8, 8);
lv_obj_set_style_radius(centerDot_, LV_RADIUS_CIRCLE, 0);
lv_obj_center(centerDot_);
// Initial update to set positions (will be updated by loop)
time_t now;
time(&now);
struct tm t;
localtime_r(&now, &t);
updateHands(t);
return obj_;
}
void ClockWidget::applyStyle() {
if (!obj_) return;
// Face style (background)
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); // Usually 50% for circle
} else {
lv_obj_set_style_radius(obj_, LV_RADIUS_CIRCLE, 0);
}
lv_obj_set_style_border_width(obj_, 2, 0);
lv_obj_set_style_border_color(obj_, lv_color_make(
config_.textColor.r, config_.textColor.g, config_.textColor.b), 0);
// Hand colors
lv_color_t handColor = lv_color_make(
config_.textColor.r, config_.textColor.g, config_.textColor.b);
lv_color_t secColor = lv_color_make(200, 50, 50); // Red for second hand
if (hourHand_) {
lv_obj_set_style_line_width(hourHand_, 6, 0);
lv_obj_set_style_line_color(hourHand_, handColor, 0);
lv_obj_set_style_line_rounded(hourHand_, true, 0);
}
if (minuteHand_) {
lv_obj_set_style_line_width(minuteHand_, 4, 0);
lv_obj_set_style_line_color(minuteHand_, handColor, 0);
lv_obj_set_style_line_rounded(minuteHand_, true, 0);
}
if (secondHand_) {
lv_obj_set_style_line_width(secondHand_, 2, 0);
lv_obj_set_style_line_color(secondHand_, secColor, 0);
lv_obj_set_style_line_rounded(secondHand_, true, 0);
}
if (centerDot_) {
lv_obj_set_style_bg_color(centerDot_, handColor, 0);
}
// Force redraw of hands with new style/dimensions
lastSecond_ = -1;
time_t now;
time(&now);
struct tm t;
localtime_r(&now, &t);
updateHands(t);
}
void ClockWidget::updateHands(const struct tm& t) {
if (t.tm_sec == lastSecond_ && t.tm_min == lastMinute_ && t.tm_hour == lastHour_) return;
lastSecond_ = t.tm_sec;
lastMinute_ = t.tm_min;
lastHour_ = t.tm_hour;
int32_t w = lv_obj_get_width(obj_);
int32_t h = lv_obj_get_height(obj_);
int32_t cx = w / 2;
int32_t cy = h / 2;
int32_t radius = (w < h ? w : h) / 2;
// Lengths
int32_t lenSec = radius - 10;
int32_t lenMin = radius - 20;
int32_t lenHour = radius * 0.6f;
// Angles (0 is top, clockwise)
// Second: 6 deg per sec
float angleSec = t.tm_sec * 6.0f;
// Minute: 6 deg per min + 0.1 deg per sec
float angleMin = t.tm_min * 6.0f + t.tm_sec * 0.1f;
// Hour: 30 deg per hour + 0.5 deg per min
float angleHour = (t.tm_hour % 12) * 30.0f + t.tm_min * 0.5f;
auto calcPoints = [&](float angle, int32_t len, lv_point_precise_t* p) {
float rad = (angle - 90.0f) * PI / 180.0f;
p[0].x = cx;
p[0].y = cy;
p[1].x = cx + static_cast<int32_t>(cos(rad) * len);
p[1].y = cy + static_cast<int32_t>(sin(rad) * len);
};
if (secondHand_) {
calcPoints(angleSec, lenSec, secondPoints_);
lv_line_set_points(secondHand_, secondPoints_, 2);
}
if (minuteHand_) {
calcPoints(angleMin, lenMin, minutePoints_);
lv_line_set_points(minuteHand_, minutePoints_, 2);
}
if (hourHand_) {
calcPoints(angleHour, lenHour, hourPoints_);
lv_line_set_points(hourHand_, hourPoints_, 2);
}
}
void ClockWidget::onKnxTime(const struct tm& value, TextSource source) {
// Only accept system time updates or explicitly configured KNX time sources if we supported them
// For now, ClockWidget listens to SYSTEM_TIME via WidgetManager broadcast
updateHands(value);
}