knxdisplay/main/WidgetManager.hpp
2026-01-24 18:31:31 +01:00

119 lines
3.5 KiB
C++

#pragma once
#include "WidgetConfig.hpp"
#include "widgets/Widget.hpp"
#include "lvgl.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include <array>
#include <memory>
class WidgetManager {
public:
static WidgetManager& instance();
// Initialize (load config from SD card)
void init();
// Build/rebuild all widgets from current config
// Call this after loading or when user clicks "Save" in web editor
void applyConfig();
// Get current config for web editor (JSON)
void getConfigJson(char* buf, size_t bufSize) const;
// Update config from web editor (JSON) - does NOT apply immediately
bool updateConfigFromJson(const char* json);
// Save current config to SD card and apply to display
void saveAndApply();
// Reset to factory defaults
void resetToDefaults();
// Periodic tasks (standby handling)
void loop();
// User activity (resets standby timer)
void onUserActivity();
// Thread-safe KNX updates (queued to UI thread)
void onKnxValue(uint16_t groupAddr, float value);
void onKnxSwitch(uint16_t groupAddr, bool value);
void onKnxText(uint16_t groupAddr, const char* text);
// Button action handler
void handleButtonAction(const WidgetConfig& cfg, lv_obj_t* target);
void goBack();
// Direct config access
GuiConfig& getConfig() { return config_; }
const GuiConfig& getConfig() const { return config_; }
private:
WidgetManager();
~WidgetManager() = default;
WidgetManager(const WidgetManager&) = delete;
WidgetManager& operator=(const WidgetManager&) = delete;
static constexpr size_t UI_EVENT_QUEUE_LEN = 16;
static constexpr size_t UI_EVENT_TEXT_LEN = MAX_TEXT_LEN;
enum class UiEventType : uint8_t {
KNX_VALUE = 0,
KNX_SWITCH = 1,
KNX_TEXT = 2,
};
struct UiEvent {
UiEventType type;
uint16_t groupAddr;
float value;
bool state;
char text[UI_EVENT_TEXT_LEN];
};
void loadFromSdCard();
void saveToSdCard();
void destroyAllWidgets();
void createAllWidgets(const ScreenConfig& screen, lv_obj_t* parent);
bool enqueueUiEvent(const UiEvent& event);
void processUiQueue();
void applyKnxValue(uint16_t groupAddr, float value);
void applyKnxSwitch(uint16_t groupAddr, bool value);
void applyKnxText(uint16_t groupAddr, const char* text);
void createDefaultConfig();
void applyScreen(uint8_t screenId);
void showScreen(uint8_t screenId);
void showModalScreen(const ScreenConfig& screen);
void closeModal();
void enterStandby();
ScreenConfig* activeScreen();
const ScreenConfig* activeScreen() const;
static constexpr const char* CONFIG_FILE = "/sdcard/lvgl.json";
static constexpr int64_t NAV_DELAY_US = 200 * 1000; // 200ms delay for touch release
GuiConfig config_;
uint8_t activeScreenId_ = 0;
uint8_t previousScreenId_ = 0xFF;
uint8_t standbyReturnScreenId_ = 0xFF;
uint8_t modalScreenId_ = 0xFF;
bool standbyActive_ = false;
bool standbyWakePending_ = false;
uint8_t standbyWakeTarget_ = 0xFF;
bool navPending_ = false;
ButtonAction navAction_ = ButtonAction::KNX;
uint8_t navTargetScreen_ = 0xFF;
int64_t navRequestUs_ = 0;
int64_t lastActivityUs_ = 0;
// Runtime widget instances (indexed by widget ID)
std::array<std::unique_ptr<Widget>, MAX_WIDGETS> widgets_;
lv_obj_t* screen_ = nullptr;
lv_obj_t* modalContainer_ = nullptr;
lv_obj_t* modalDimmer_ = nullptr;
QueueHandle_t uiQueue_ = nullptr;
};