feat: Add lvgl display theme control (#1180)
* feat: Add lvgl display theme control * fix: compiling errors * move light/dark themes to lcd display * fix compile errors --------- Co-authored-by: Xiaoxia <terrence.huang@tenclass.com>
This commit is contained in:
144
main/display/lvgl_display/emoji_collection.cc
Normal file
144
main/display/lvgl_display/emoji_collection.cc
Normal file
@ -0,0 +1,144 @@
|
||||
#include "emoji_collection.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
#define TAG "EmojiCollection"
|
||||
|
||||
// These are declared in xiaozhi-fonts/src/font_emoji_32.c
|
||||
extern const lv_image_dsc_t emoji_1f636_32; // neutral
|
||||
extern const lv_image_dsc_t emoji_1f642_32; // happy
|
||||
extern const lv_image_dsc_t emoji_1f606_32; // laughing
|
||||
extern const lv_image_dsc_t emoji_1f602_32; // funny
|
||||
extern const lv_image_dsc_t emoji_1f614_32; // sad
|
||||
extern const lv_image_dsc_t emoji_1f620_32; // angry
|
||||
extern const lv_image_dsc_t emoji_1f62d_32; // crying
|
||||
extern const lv_image_dsc_t emoji_1f60d_32; // loving
|
||||
extern const lv_image_dsc_t emoji_1f633_32; // embarrassed
|
||||
extern const lv_image_dsc_t emoji_1f62f_32; // surprised
|
||||
extern const lv_image_dsc_t emoji_1f631_32; // shocked
|
||||
extern const lv_image_dsc_t emoji_1f914_32; // thinking
|
||||
extern const lv_image_dsc_t emoji_1f609_32; // winking
|
||||
extern const lv_image_dsc_t emoji_1f60e_32; // cool
|
||||
extern const lv_image_dsc_t emoji_1f60c_32; // relaxed
|
||||
extern const lv_image_dsc_t emoji_1f924_32; // delicious
|
||||
extern const lv_image_dsc_t emoji_1f618_32; // kissy
|
||||
extern const lv_image_dsc_t emoji_1f60f_32; // confident
|
||||
extern const lv_image_dsc_t emoji_1f634_32; // sleepy
|
||||
extern const lv_image_dsc_t emoji_1f61c_32; // silly
|
||||
extern const lv_image_dsc_t emoji_1f644_32; // confused
|
||||
|
||||
const lv_img_dsc_t* Twemoji32::GetEmojiImage(const char* name) const {
|
||||
static const std::unordered_map<std::string, const lv_img_dsc_t*> emoji_map = {
|
||||
{"neutral", &emoji_1f636_32},
|
||||
{"happy", &emoji_1f642_32},
|
||||
{"laughing", &emoji_1f606_32},
|
||||
{"funny", &emoji_1f602_32},
|
||||
{"sad", &emoji_1f614_32},
|
||||
{"angry", &emoji_1f620_32},
|
||||
{"crying", &emoji_1f62d_32},
|
||||
{"loving", &emoji_1f60d_32},
|
||||
{"embarrassed", &emoji_1f633_32},
|
||||
{"surprised", &emoji_1f62f_32},
|
||||
{"shocked", &emoji_1f631_32},
|
||||
{"thinking", &emoji_1f914_32},
|
||||
{"winking", &emoji_1f609_32},
|
||||
{"cool", &emoji_1f60e_32},
|
||||
{"relaxed", &emoji_1f60c_32},
|
||||
{"delicious", &emoji_1f924_32},
|
||||
{"kissy", &emoji_1f618_32},
|
||||
{"confident", &emoji_1f60f_32},
|
||||
{"sleepy", &emoji_1f634_32},
|
||||
{"silly", &emoji_1f61c_32},
|
||||
{"confused", &emoji_1f644_32},
|
||||
};
|
||||
|
||||
auto it = emoji_map.find(name);
|
||||
if (it != emoji_map.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "Emoji not found: %s", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
// These are declared in xiaozhi-fonts/src/font_emoji_64.c
|
||||
extern const lv_image_dsc_t emoji_1f636_64; // neutral
|
||||
extern const lv_image_dsc_t emoji_1f642_64; // happy
|
||||
extern const lv_image_dsc_t emoji_1f606_64; // laughing
|
||||
extern const lv_image_dsc_t emoji_1f602_64; // funny
|
||||
extern const lv_image_dsc_t emoji_1f614_64; // sad
|
||||
extern const lv_image_dsc_t emoji_1f620_64; // angry
|
||||
extern const lv_image_dsc_t emoji_1f62d_64; // crying
|
||||
extern const lv_image_dsc_t emoji_1f60d_64; // loving
|
||||
extern const lv_image_dsc_t emoji_1f633_64; // embarrassed
|
||||
extern const lv_image_dsc_t emoji_1f62f_64; // surprised
|
||||
extern const lv_image_dsc_t emoji_1f631_64; // shocked
|
||||
extern const lv_image_dsc_t emoji_1f914_64; // thinking
|
||||
extern const lv_image_dsc_t emoji_1f609_64; // winking
|
||||
extern const lv_image_dsc_t emoji_1f60e_64; // cool
|
||||
extern const lv_image_dsc_t emoji_1f60c_64; // relaxed
|
||||
extern const lv_image_dsc_t emoji_1f924_64; // delicious
|
||||
extern const lv_image_dsc_t emoji_1f618_64; // kissy
|
||||
extern const lv_image_dsc_t emoji_1f60f_64; // confident
|
||||
extern const lv_image_dsc_t emoji_1f634_64; // sleepy
|
||||
extern const lv_image_dsc_t emoji_1f61c_64; // silly
|
||||
extern const lv_image_dsc_t emoji_1f644_64; // confused
|
||||
|
||||
const lv_img_dsc_t* Twemoji64::GetEmojiImage(const char* name) const {
|
||||
static const std::unordered_map<std::string, const lv_img_dsc_t*> emoji_map = {
|
||||
{"neutral", &emoji_1f636_64},
|
||||
{"happy", &emoji_1f642_64},
|
||||
{"laughing", &emoji_1f606_64},
|
||||
{"funny", &emoji_1f602_64},
|
||||
{"sad", &emoji_1f614_64},
|
||||
{"angry", &emoji_1f620_64},
|
||||
{"crying", &emoji_1f62d_64},
|
||||
{"loving", &emoji_1f60d_64},
|
||||
{"embarrassed", &emoji_1f633_64},
|
||||
{"surprised", &emoji_1f62f_64},
|
||||
{"shocked", &emoji_1f631_64},
|
||||
{"thinking", &emoji_1f914_64},
|
||||
{"winking", &emoji_1f609_64},
|
||||
{"cool", &emoji_1f60e_64},
|
||||
{"relaxed", &emoji_1f60c_64},
|
||||
{"delicious", &emoji_1f924_64},
|
||||
{"kissy", &emoji_1f618_64},
|
||||
{"confident", &emoji_1f60f_64},
|
||||
{"sleepy", &emoji_1f634_64},
|
||||
{"silly", &emoji_1f61c_64},
|
||||
{"confused", &emoji_1f644_64},
|
||||
};
|
||||
|
||||
auto it = emoji_map.find(name);
|
||||
if (it != emoji_map.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "Emoji not found: %s", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
void CustomEmojiCollection::AddEmoji(const std::string& name, LvglImage* image) {
|
||||
emoji_collection_[name] = image;
|
||||
}
|
||||
|
||||
const lv_img_dsc_t* CustomEmojiCollection::GetEmojiImage(const char* name) const {
|
||||
auto it = emoji_collection_.find(name);
|
||||
if (it != emoji_collection_.end()) {
|
||||
return it->second->image_dsc();
|
||||
}
|
||||
|
||||
ESP_LOGW(TAG, "Emoji not found: %s", name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CustomEmojiCollection::~CustomEmojiCollection() {
|
||||
for (auto it = emoji_collection_.begin(); it != emoji_collection_.end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
emoji_collection_.clear();
|
||||
}
|
||||
40
main/display/lvgl_display/emoji_collection.h
Normal file
40
main/display/lvgl_display/emoji_collection.h
Normal file
@ -0,0 +1,40 @@
|
||||
#ifndef EMOJI_COLLECTION_H
|
||||
#define EMOJI_COLLECTION_H
|
||||
|
||||
#include "lvgl_image.h"
|
||||
|
||||
#include <lvgl.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
|
||||
// Define interface for emoji collection
|
||||
class EmojiCollection {
|
||||
public:
|
||||
virtual const lv_img_dsc_t* GetEmojiImage(const char* name) const = 0;
|
||||
virtual ~EmojiCollection() = default;
|
||||
};
|
||||
|
||||
class Twemoji32 : public EmojiCollection {
|
||||
public:
|
||||
virtual const lv_img_dsc_t* GetEmojiImage(const char* name) const override;
|
||||
};
|
||||
|
||||
class Twemoji64 : public EmojiCollection {
|
||||
public:
|
||||
virtual const lv_img_dsc_t* GetEmojiImage(const char* name) const override;
|
||||
};
|
||||
|
||||
class CustomEmojiCollection : public EmojiCollection {
|
||||
private:
|
||||
std::map<std::string, LvglImage*> emoji_collection_;
|
||||
|
||||
public:
|
||||
void AddEmoji(const std::string& name, LvglImage* image);
|
||||
virtual const lv_img_dsc_t* GetEmojiImage(const char* name) const override;
|
||||
virtual ~CustomEmojiCollection();
|
||||
};
|
||||
|
||||
#endif
|
||||
251
main/display/lvgl_display/lvgl_display.cc
Normal file
251
main/display/lvgl_display/lvgl_display.cc
Normal file
@ -0,0 +1,251 @@
|
||||
#include <esp_log.h>
|
||||
#include <esp_err.h>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <font_awesome.h>
|
||||
|
||||
#include "lvgl_display.h"
|
||||
#include "board.h"
|
||||
#include "application.h"
|
||||
#include "audio_codec.h"
|
||||
#include "settings.h"
|
||||
#include "assets/lang_config.h"
|
||||
|
||||
#define TAG "Display"
|
||||
|
||||
LvglDisplay::LvglDisplay() {
|
||||
// Notification timer
|
||||
esp_timer_create_args_t notification_timer_args = {
|
||||
.callback = [](void *arg) {
|
||||
LvglDisplay *display = static_cast<LvglDisplay*>(arg);
|
||||
DisplayLockGuard lock(display);
|
||||
lv_obj_add_flag(display->notification_label_, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_remove_flag(display->status_label_, LV_OBJ_FLAG_HIDDEN);
|
||||
},
|
||||
.arg = this,
|
||||
.dispatch_method = ESP_TIMER_TASK,
|
||||
.name = "notification_timer",
|
||||
.skip_unhandled_events = false,
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_timer_create(¬ification_timer_args, ¬ification_timer_));
|
||||
|
||||
// Create a power management lock
|
||||
auto ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "display_update", &pm_lock_);
|
||||
if (ret == ESP_ERR_NOT_SUPPORTED) {
|
||||
ESP_LOGI(TAG, "Power management not supported");
|
||||
} else {
|
||||
ESP_ERROR_CHECK(ret);
|
||||
}
|
||||
}
|
||||
|
||||
LvglDisplay::~LvglDisplay() {
|
||||
if (notification_timer_ != nullptr) {
|
||||
esp_timer_stop(notification_timer_);
|
||||
esp_timer_delete(notification_timer_);
|
||||
}
|
||||
|
||||
if (network_label_ != nullptr) {
|
||||
lv_obj_del(network_label_);
|
||||
}
|
||||
if (notification_label_ != nullptr) {
|
||||
lv_obj_del(notification_label_);
|
||||
}
|
||||
if (status_label_ != nullptr) {
|
||||
lv_obj_del(status_label_);
|
||||
}
|
||||
if (mute_label_ != nullptr) {
|
||||
lv_obj_del(mute_label_);
|
||||
}
|
||||
if (battery_label_ != nullptr) {
|
||||
lv_obj_del(battery_label_);
|
||||
}
|
||||
if (emotion_label_ != nullptr) {
|
||||
lv_obj_del(emotion_label_);
|
||||
}
|
||||
if( low_battery_popup_ != nullptr ) {
|
||||
lv_obj_del(low_battery_popup_);
|
||||
}
|
||||
if (pm_lock_ != nullptr) {
|
||||
esp_pm_lock_delete(pm_lock_);
|
||||
}
|
||||
}
|
||||
|
||||
void LvglDisplay::SetStatus(const char* status) {
|
||||
DisplayLockGuard lock(this);
|
||||
if (status_label_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
lv_label_set_text(status_label_, status);
|
||||
lv_obj_remove_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
last_status_update_time_ = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
void LvglDisplay::ShowNotification(const std::string ¬ification, int duration_ms) {
|
||||
ShowNotification(notification.c_str(), duration_ms);
|
||||
}
|
||||
|
||||
void LvglDisplay::ShowNotification(const char* notification, int duration_ms) {
|
||||
DisplayLockGuard lock(this);
|
||||
if (notification_label_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
lv_label_set_text(notification_label_, notification);
|
||||
lv_obj_remove_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
|
||||
lv_obj_add_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
esp_timer_stop(notification_timer_);
|
||||
ESP_ERROR_CHECK(esp_timer_start_once(notification_timer_, duration_ms * 1000));
|
||||
}
|
||||
|
||||
void LvglDisplay::UpdateStatusBar(bool update_all) {
|
||||
auto& app = Application::GetInstance();
|
||||
auto& board = Board::GetInstance();
|
||||
auto codec = board.GetAudioCodec();
|
||||
|
||||
// Update mute icon
|
||||
{
|
||||
DisplayLockGuard lock(this);
|
||||
if (mute_label_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果静音状态改变,则更新图标
|
||||
if (codec->output_volume() == 0 && !muted_) {
|
||||
muted_ = true;
|
||||
lv_label_set_text(mute_label_, FONT_AWESOME_VOLUME_XMARK);
|
||||
} else if (codec->output_volume() > 0 && muted_) {
|
||||
muted_ = false;
|
||||
lv_label_set_text(mute_label_, "");
|
||||
}
|
||||
}
|
||||
|
||||
// Update time
|
||||
if (app.GetDeviceState() == kDeviceStateIdle) {
|
||||
if (last_status_update_time_ + std::chrono::seconds(10) < std::chrono::system_clock::now()) {
|
||||
// Set status to clock "HH:MM"
|
||||
time_t now = time(NULL);
|
||||
struct tm* tm = localtime(&now);
|
||||
// Check if the we have already set the time
|
||||
if (tm->tm_year >= 2025 - 1900) {
|
||||
char time_str[16];
|
||||
strftime(time_str, sizeof(time_str), "%H:%M ", tm);
|
||||
SetStatus(time_str);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "System time is not set, tm_year: %d", tm->tm_year);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_pm_lock_acquire(pm_lock_);
|
||||
// 更新电池图标
|
||||
int battery_level;
|
||||
bool charging, discharging;
|
||||
const char* icon = nullptr;
|
||||
if (board.GetBatteryLevel(battery_level, charging, discharging)) {
|
||||
if (charging) {
|
||||
icon = FONT_AWESOME_BATTERY_BOLT;
|
||||
} else {
|
||||
const char* levels[] = {
|
||||
FONT_AWESOME_BATTERY_EMPTY, // 0-19%
|
||||
FONT_AWESOME_BATTERY_QUARTER, // 20-39%
|
||||
FONT_AWESOME_BATTERY_HALF, // 40-59%
|
||||
FONT_AWESOME_BATTERY_THREE_QUARTERS, // 60-79%
|
||||
FONT_AWESOME_BATTERY_FULL, // 80-99%
|
||||
FONT_AWESOME_BATTERY_FULL, // 100%
|
||||
};
|
||||
icon = levels[battery_level / 20];
|
||||
}
|
||||
DisplayLockGuard lock(this);
|
||||
if (battery_label_ != nullptr && battery_icon_ != icon) {
|
||||
battery_icon_ = icon;
|
||||
lv_label_set_text(battery_label_, battery_icon_);
|
||||
}
|
||||
|
||||
if (low_battery_popup_ != nullptr) {
|
||||
if (strcmp(icon, FONT_AWESOME_BATTERY_EMPTY) == 0 && discharging) {
|
||||
if (lv_obj_has_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN)) { // 如果低电量提示框隐藏,则显示
|
||||
lv_obj_remove_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN);
|
||||
app.PlaySound(Lang::Sounds::OGG_LOW_BATTERY);
|
||||
}
|
||||
} else {
|
||||
// Hide the low battery popup when the battery is not empty
|
||||
if (!lv_obj_has_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN)) { // 如果低电量提示框显示,则隐藏
|
||||
lv_obj_add_flag(low_battery_popup_, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 每 10 秒更新一次网络图标
|
||||
static int seconds_counter = 0;
|
||||
if (update_all || seconds_counter++ % 10 == 0) {
|
||||
// 升级固件时,不读取 4G 网络状态,避免占用 UART 资源
|
||||
auto device_state = Application::GetInstance().GetDeviceState();
|
||||
static const std::vector<DeviceState> allowed_states = {
|
||||
kDeviceStateIdle,
|
||||
kDeviceStateStarting,
|
||||
kDeviceStateWifiConfiguring,
|
||||
kDeviceStateListening,
|
||||
kDeviceStateActivating,
|
||||
};
|
||||
if (std::find(allowed_states.begin(), allowed_states.end(), device_state) != allowed_states.end()) {
|
||||
icon = board.GetNetworkStateIcon();
|
||||
if (network_label_ != nullptr && icon != nullptr && network_icon_ != icon) {
|
||||
DisplayLockGuard lock(this);
|
||||
network_icon_ = icon;
|
||||
lv_label_set_text(network_label_, network_icon_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
esp_pm_lock_release(pm_lock_);
|
||||
}
|
||||
|
||||
|
||||
void LvglDisplay::SetEmotion(const char* emotion) {
|
||||
const char* utf8 = font_awesome_get_utf8(emotion);
|
||||
DisplayLockGuard lock(this);
|
||||
if (emotion_label_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
if (utf8 != nullptr) {
|
||||
lv_label_set_text(emotion_label_, utf8);
|
||||
} else {
|
||||
lv_label_set_text(emotion_label_, FONT_AWESOME_NEUTRAL);
|
||||
}
|
||||
}
|
||||
|
||||
void LvglDisplay::SetPreviewImage(const lv_img_dsc_t* image) {
|
||||
// Do nothing but free the image
|
||||
if (image != nullptr) {
|
||||
heap_caps_free((void*)image->data);
|
||||
heap_caps_free((void*)image);
|
||||
}
|
||||
}
|
||||
|
||||
void LvglDisplay::SetChatMessage(const char* role, const char* content) {
|
||||
DisplayLockGuard lock(this);
|
||||
if (chat_message_label_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
lv_label_set_text(chat_message_label_, content);
|
||||
}
|
||||
|
||||
void LvglDisplay::SetTheme(Theme* theme) {
|
||||
current_theme_ = theme;
|
||||
Settings settings("display", true);
|
||||
settings.SetString("theme", theme->name());
|
||||
}
|
||||
|
||||
void LvglDisplay::SetPowerSaveMode(bool on) {
|
||||
if (on) {
|
||||
SetChatMessage("system", "");
|
||||
SetEmotion("sleepy");
|
||||
} else {
|
||||
SetChatMessage("system", "");
|
||||
SetEmotion("neutral");
|
||||
}
|
||||
}
|
||||
57
main/display/lvgl_display/lvgl_display.h
Normal file
57
main/display/lvgl_display/lvgl_display.h
Normal file
@ -0,0 +1,57 @@
|
||||
#ifndef LVGL_DISPLAY_H
|
||||
#define LVGL_DISPLAY_H
|
||||
|
||||
#include "display.h"
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <esp_timer.h>
|
||||
#include <esp_log.h>
|
||||
#include <esp_pm.h>
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
|
||||
class LvglDisplay : public Display {
|
||||
public:
|
||||
LvglDisplay();
|
||||
virtual ~LvglDisplay();
|
||||
|
||||
virtual void SetStatus(const char* status);
|
||||
virtual void ShowNotification(const char* notification, int duration_ms = 3000);
|
||||
virtual void ShowNotification(const std::string ¬ification, int duration_ms = 3000);
|
||||
virtual void SetEmotion(const char* emotion);
|
||||
virtual void SetChatMessage(const char* role, const char* content);
|
||||
virtual void SetPreviewImage(const lv_img_dsc_t* image);
|
||||
virtual void SetTheme(Theme* theme);
|
||||
virtual Theme* GetTheme() { return current_theme_; }
|
||||
virtual void UpdateStatusBar(bool update_all = false);
|
||||
virtual void SetPowerSaveMode(bool on);
|
||||
|
||||
protected:
|
||||
esp_pm_lock_handle_t pm_lock_ = nullptr;
|
||||
lv_display_t *display_ = nullptr;
|
||||
|
||||
lv_obj_t *emotion_label_ = nullptr;
|
||||
lv_obj_t *network_label_ = nullptr;
|
||||
lv_obj_t *status_label_ = nullptr;
|
||||
lv_obj_t *notification_label_ = nullptr;
|
||||
lv_obj_t *mute_label_ = nullptr;
|
||||
lv_obj_t *battery_label_ = nullptr;
|
||||
lv_obj_t* chat_message_label_ = nullptr;
|
||||
lv_obj_t* low_battery_popup_ = nullptr;
|
||||
lv_obj_t* low_battery_label_ = nullptr;
|
||||
|
||||
const char* battery_icon_ = nullptr;
|
||||
const char* network_icon_ = nullptr;
|
||||
bool muted_ = false;
|
||||
|
||||
std::chrono::system_clock::time_point last_status_update_time_;
|
||||
esp_timer_handle_t notification_timer_ = nullptr;
|
||||
|
||||
friend class DisplayLockGuard;
|
||||
virtual bool Lock(int timeout_ms = 0) = 0;
|
||||
virtual void Unlock() = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
13
main/display/lvgl_display/lvgl_font.cc
Normal file
13
main/display/lvgl_display/lvgl_font.cc
Normal file
@ -0,0 +1,13 @@
|
||||
#include "lvgl_font.h"
|
||||
#include <cbin_font.h>
|
||||
|
||||
|
||||
LvglCBinFont::LvglCBinFont(void* data) {
|
||||
font_ = cbin_font_create(static_cast<uint8_t*>(data));
|
||||
}
|
||||
|
||||
LvglCBinFont::~LvglCBinFont() {
|
||||
if (font_ != nullptr) {
|
||||
cbin_font_delete(font_);
|
||||
}
|
||||
}
|
||||
31
main/display/lvgl_display/lvgl_font.h
Normal file
31
main/display/lvgl_display/lvgl_font.h
Normal file
@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <lvgl.h>
|
||||
|
||||
|
||||
class LvglFont {
|
||||
public:
|
||||
virtual const lv_font_t* font() const = 0;
|
||||
virtual ~LvglFont() = default;
|
||||
};
|
||||
|
||||
// Built-in font
|
||||
class LvglBuiltInFont : public LvglFont {
|
||||
public:
|
||||
LvglBuiltInFont(const lv_font_t* font) : font_(font) {}
|
||||
virtual const lv_font_t* font() const override { return font_; }
|
||||
|
||||
private:
|
||||
const lv_font_t* font_;
|
||||
};
|
||||
|
||||
|
||||
class LvglCBinFont : public LvglFont {
|
||||
public:
|
||||
LvglCBinFont(void* data);
|
||||
virtual ~LvglCBinFont();
|
||||
virtual const lv_font_t* font() const override { return font_; }
|
||||
|
||||
private:
|
||||
lv_font_t* font_;
|
||||
};
|
||||
28
main/display/lvgl_display/lvgl_image.cc
Normal file
28
main/display/lvgl_display/lvgl_image.cc
Normal file
@ -0,0 +1,28 @@
|
||||
#include "lvgl_image.h"
|
||||
#include <cbin_font.h>
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <cstring>
|
||||
|
||||
#define TAG "LvglImage"
|
||||
|
||||
|
||||
LvglRawImage::LvglRawImage(void* data, size_t size) {
|
||||
bzero(&image_dsc_, sizeof(image_dsc_));
|
||||
image_dsc_.header.magic = LV_IMAGE_HEADER_MAGIC;
|
||||
image_dsc_.header.cf = LV_COLOR_FORMAT_RAW_ALPHA;
|
||||
image_dsc_.header.w = 0;
|
||||
image_dsc_.header.h = 0;
|
||||
image_dsc_.data_size = size;
|
||||
image_dsc_.data = static_cast<uint8_t*>(data);
|
||||
}
|
||||
|
||||
LvglCBinImage::LvglCBinImage(void* data) {
|
||||
image_dsc_ = cbin_img_dsc_create(static_cast<uint8_t*>(data));
|
||||
}
|
||||
|
||||
LvglCBinImage::~LvglCBinImage() {
|
||||
if (image_dsc_ != nullptr) {
|
||||
cbin_img_dsc_delete(image_dsc_);
|
||||
}
|
||||
}
|
||||
32
main/display/lvgl_display/lvgl_image.h
Normal file
32
main/display/lvgl_display/lvgl_image.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <lvgl.h>
|
||||
|
||||
|
||||
// Wrap around lv_img_dsc_t
|
||||
class LvglImage {
|
||||
public:
|
||||
virtual const lv_img_dsc_t* image_dsc() const = 0;
|
||||
virtual ~LvglImage() = default;
|
||||
};
|
||||
|
||||
|
||||
class LvglRawImage : public LvglImage {
|
||||
public:
|
||||
LvglRawImage(void* data, size_t size);
|
||||
virtual const lv_img_dsc_t* image_dsc() const override { return &image_dsc_; }
|
||||
|
||||
private:
|
||||
lv_img_dsc_t image_dsc_;
|
||||
};
|
||||
|
||||
|
||||
class LvglCBinImage : public LvglImage {
|
||||
public:
|
||||
LvglCBinImage(void* data);
|
||||
virtual ~LvglCBinImage();
|
||||
virtual const lv_img_dsc_t* image_dsc() const override { return image_dsc_; }
|
||||
|
||||
private:
|
||||
lv_img_dsc_t* image_dsc_ = nullptr;
|
||||
};
|
||||
19
main/display/lvgl_display/lvgl_theme.cc
Normal file
19
main/display/lvgl_display/lvgl_theme.cc
Normal file
@ -0,0 +1,19 @@
|
||||
#include "lvgl_theme.h"
|
||||
|
||||
LvglTheme::LvglTheme(const std::string& name) : Theme(name) {
|
||||
}
|
||||
|
||||
LvglThemeManager::LvglThemeManager() {
|
||||
}
|
||||
|
||||
LvglTheme* LvglThemeManager::GetTheme(const std::string& theme_name) {
|
||||
auto it = themes_.find(theme_name);
|
||||
if (it != themes_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void LvglThemeManager::RegisterTheme(const std::string& theme_name, LvglTheme* theme) {
|
||||
themes_[theme_name] = theme;
|
||||
}
|
||||
92
main/display/lvgl_display/lvgl_theme.h
Normal file
92
main/display/lvgl_display/lvgl_theme.h
Normal file
@ -0,0 +1,92 @@
|
||||
#pragma once
|
||||
|
||||
#include "display.h"
|
||||
#include "lvgl_image.h"
|
||||
#include "lvgl_font.h"
|
||||
#include "emoji_collection.h"
|
||||
|
||||
#include <lvgl.h>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
||||
class LvglTheme : public Theme {
|
||||
public:
|
||||
LvglTheme(const std::string& name);
|
||||
|
||||
// Properties
|
||||
inline lv_color_t background_color() const { return background_color_; }
|
||||
inline lv_color_t text_color() const { return text_color_; }
|
||||
inline lv_color_t chat_background_color() const { return chat_background_color_; }
|
||||
inline lv_color_t user_bubble_color() const { return user_bubble_color_; }
|
||||
inline lv_color_t assistant_bubble_color() const { return assistant_bubble_color_; }
|
||||
inline lv_color_t system_bubble_color() const { return system_bubble_color_; }
|
||||
inline lv_color_t system_text_color() const { return system_text_color_; }
|
||||
inline lv_color_t border_color() const { return border_color_; }
|
||||
inline lv_color_t low_battery_color() const { return low_battery_color_; }
|
||||
inline std::shared_ptr<LvglImage> background_image() const { return background_image_; }
|
||||
inline std::shared_ptr<EmojiCollection> emoji_collection() const { return emoji_collection_; }
|
||||
inline std::shared_ptr<LvglFont> text_font() const { return text_font_; }
|
||||
inline std::shared_ptr<LvglFont> icon_font() const { return icon_font_; }
|
||||
inline std::shared_ptr<LvglFont> large_icon_font() const { return large_icon_font_; }
|
||||
inline int spacing(int scale) const { return spacing_ * scale; }
|
||||
|
||||
inline void set_background_color(lv_color_t background) { background_color_ = background; }
|
||||
inline void set_text_color(lv_color_t text) { text_color_ = text; }
|
||||
inline void set_chat_background_color(lv_color_t chat_background) { chat_background_color_ = chat_background; }
|
||||
inline void set_user_bubble_color(lv_color_t user_bubble) { user_bubble_color_ = user_bubble; }
|
||||
inline void set_assistant_bubble_color(lv_color_t assistant_bubble) { assistant_bubble_color_ = assistant_bubble; }
|
||||
inline void set_system_bubble_color(lv_color_t system_bubble) { system_bubble_color_ = system_bubble; }
|
||||
inline void set_system_text_color(lv_color_t system_text) { system_text_color_ = system_text; }
|
||||
inline void set_border_color(lv_color_t border) { border_color_ = border; }
|
||||
inline void set_low_battery_color(lv_color_t low_battery) { low_battery_color_ = low_battery; }
|
||||
inline void set_background_image(std::shared_ptr<LvglImage> background_image) { background_image_ = background_image; }
|
||||
inline void set_emoji_collection(std::shared_ptr<EmojiCollection> emoji_collection) { emoji_collection_ = emoji_collection; }
|
||||
inline void set_text_font(std::shared_ptr<LvglFont> text_font) { text_font_ = text_font; }
|
||||
inline void set_icon_font(std::shared_ptr<LvglFont> icon_font) { icon_font_ = icon_font; }
|
||||
inline void set_large_icon_font(std::shared_ptr<LvglFont> large_icon_font) { large_icon_font_ = large_icon_font; }
|
||||
|
||||
private:
|
||||
int spacing_ = 2;
|
||||
|
||||
// Colors
|
||||
lv_color_t background_color_;
|
||||
lv_color_t text_color_;
|
||||
lv_color_t chat_background_color_;
|
||||
lv_color_t user_bubble_color_;
|
||||
lv_color_t assistant_bubble_color_;
|
||||
lv_color_t system_bubble_color_;
|
||||
lv_color_t system_text_color_;
|
||||
lv_color_t border_color_;
|
||||
lv_color_t low_battery_color_;
|
||||
|
||||
// Background image
|
||||
std::shared_ptr<LvglImage> background_image_ = nullptr;
|
||||
|
||||
// fonts
|
||||
std::shared_ptr<LvglFont> text_font_ = nullptr;
|
||||
std::shared_ptr<LvglFont> icon_font_ = nullptr;
|
||||
std::shared_ptr<LvglFont> large_icon_font_ = nullptr;
|
||||
|
||||
// Emoji collection
|
||||
std::shared_ptr<EmojiCollection> emoji_collection_ = nullptr;
|
||||
};
|
||||
|
||||
|
||||
class LvglThemeManager {
|
||||
public:
|
||||
static LvglThemeManager& GetInstance() {
|
||||
static LvglThemeManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void RegisterTheme(const std::string& theme_name, LvglTheme* theme);
|
||||
LvglTheme* GetTheme(const std::string& theme_name);
|
||||
|
||||
private:
|
||||
LvglThemeManager();
|
||||
void InitializeDefaultThemes();
|
||||
|
||||
std::map<std::string, LvglTheme*> themes_;
|
||||
};
|
||||
Reference in New Issue
Block a user