Skip to content

Commit 4048647

Browse files
78Xiaoxia
andauthored
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 <[email protected]>
1 parent bce662d commit 4048647

29 files changed

+883
-618
lines changed

main/CMakeLists.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,11 @@ set(SOURCES "audio/audio_codec.cc"
5050
"display/display.cc"
5151
"display/lcd_display.cc"
5252
"display/oled_display.cc"
53-
"display/emoji_collection.cc"
53+
"display/lvgl_display/lvgl_display.cc"
54+
"display/lvgl_display/emoji_collection.cc"
55+
"display/lvgl_display/lvgl_theme.cc"
56+
"display/lvgl_display/lvgl_font.cc"
57+
"display/lvgl_display/lvgl_image.cc"
5458
"protocols/protocol.cc"
5559
"protocols/mqtt_protocol.cc"
5660
"protocols/websocket_protocol.cc"
@@ -64,7 +68,7 @@ set(SOURCES "audio/audio_codec.cc"
6468
"main.cc"
6569
)
6670

67-
set(INCLUDE_DIRS "." "display" "audio" "protocols")
71+
set(INCLUDE_DIRS "." "display" "display/lvgl_display" "audio" "protocols")
6872

6973
# Add board common files
7074
file(GLOB BOARD_COMMON_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/common/*.cc)

main/assets.cc

Lines changed: 81 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "board.h"
33
#include "display.h"
44
#include "application.h"
5+
#include "lvgl_theme.h"
56

67
#include <esp_log.h>
78
#include <spi_flash_mmap.h>
@@ -32,12 +33,6 @@ Assets::Assets(std::string default_assets_url) {
3233
}
3334

3435
Assets::~Assets() {
35-
if (custom_emoji_collection_ != nullptr) {
36-
delete custom_emoji_collection_;
37-
}
38-
if (text_font_) {
39-
cbin_font_delete(text_font_);
40-
}
4136
if (mmap_handle_ != 0) {
4237
esp_partition_munmap(mmap_handle_);
4338
}
@@ -111,6 +106,17 @@ bool Assets::InitializePartition() {
111106
return checksum_valid_;
112107
}
113108

109+
lv_color_t Assets::ParseColor(const std::string& color) {
110+
if (color.find("#") == 0) {
111+
// Convert #112233 to lv_color_t
112+
uint8_t r = strtol(color.substr(1, 2).c_str(), nullptr, 16);
113+
uint8_t g = strtol(color.substr(3, 2).c_str(), nullptr, 16);
114+
uint8_t b = strtol(color.substr(5, 2).c_str(), nullptr, 16);
115+
return lv_color_make(r, g, b);
116+
}
117+
return lv_color_black();
118+
}
119+
114120
bool Assets::Apply() {
115121
void* ptr = nullptr;
116122
size_t size = 0;
@@ -123,6 +129,14 @@ bool Assets::Apply() {
123129
ESP_LOGE(TAG, "The index.json file is not valid");
124130
return false;
125131
}
132+
133+
cJSON* version = cJSON_GetObjectItem(root, "version");
134+
if (cJSON_IsNumber(version)) {
135+
if (version->valuedouble > 1) {
136+
ESP_LOGE(TAG, "The assets version %d is not supported, please upgrade the firmware", version->valueint);
137+
return false;
138+
}
139+
}
126140

127141
cJSON* srmodels = cJSON_GetObjectItem(root, "srmodels");
128142
if (cJSON_IsString(srmodels)) {
@@ -144,28 +158,29 @@ bool Assets::Apply() {
144158
}
145159
}
146160

161+
auto& theme_manager = LvglThemeManager::GetInstance();
162+
auto light_theme = theme_manager.GetTheme("light");
163+
auto dark_theme = theme_manager.GetTheme("dark");
164+
147165
cJSON* font = cJSON_GetObjectItem(root, "text_font");
148166
if (cJSON_IsString(font)) {
149167
std::string fonts_text_file = font->valuestring;
150168
if (GetAssetData(fonts_text_file, ptr, size)) {
151-
if (text_font_ != nullptr) {
152-
cbin_font_delete(text_font_);
153-
}
154-
text_font_ = cbin_font_create(static_cast<uint8_t*>(ptr));
155-
if (text_font_ == nullptr) {
169+
auto text_font = std::make_shared<LvglCBinFont>(ptr);
170+
if (text_font->font() == nullptr) {
156171
ESP_LOGE(TAG, "Failed to load fonts.bin");
172+
return false;
157173
}
174+
light_theme->set_text_font(text_font);
175+
dark_theme->set_text_font(text_font);
158176
} else {
159177
ESP_LOGE(TAG, "The font file %s is not found", fonts_text_file.c_str());
160178
}
161179
}
162180

163181
cJSON* emoji_collection = cJSON_GetObjectItem(root, "emoji_collection");
164182
if (cJSON_IsArray(emoji_collection)) {
165-
if (custom_emoji_collection_ != nullptr) {
166-
delete custom_emoji_collection_;
167-
}
168-
custom_emoji_collection_ = new CustomEmojiCollection();
183+
auto custom_emoji_collection = std::make_shared<CustomEmojiCollection>();
169184
int emoji_count = cJSON_GetArraySize(emoji_collection);
170185
for (int i = 0; i < emoji_count; i++) {
171186
cJSON* emoji = cJSON_GetArrayItem(emoji_collection, i);
@@ -177,28 +192,63 @@ bool Assets::Apply() {
177192
ESP_LOGE(TAG, "Emoji %s image file %s is not found", name->valuestring, file->valuestring);
178193
continue;
179194
}
180-
auto img = new lv_img_dsc_t {
181-
.header = {
182-
.magic = LV_IMAGE_HEADER_MAGIC,
183-
.cf = LV_COLOR_FORMAT_RAW_ALPHA,
184-
},
185-
.data_size = size,
186-
.data = static_cast<uint8_t*>(ptr),
187-
};
188-
custom_emoji_collection_->AddEmoji(name->valuestring, img);
195+
custom_emoji_collection->AddEmoji(name->valuestring, new LvglRawImage(ptr, size));
189196
}
190197
}
191198
}
199+
light_theme->set_emoji_collection(custom_emoji_collection);
200+
dark_theme->set_emoji_collection(custom_emoji_collection);
201+
}
202+
203+
cJSON* skin = cJSON_GetObjectItem(root, "skin");
204+
if (cJSON_IsObject(skin)) {
205+
cJSON* light_skin = cJSON_GetObjectItem(skin, "light");
206+
if (cJSON_IsObject(light_skin)) {
207+
cJSON* text_color = cJSON_GetObjectItem(light_skin, "text_color");
208+
cJSON* background_color = cJSON_GetObjectItem(light_skin, "background_color");
209+
cJSON* background_image = cJSON_GetObjectItem(light_skin, "background_image");
210+
if (cJSON_IsString(text_color)) {
211+
light_theme->set_text_color(ParseColor(text_color->valuestring));
212+
}
213+
if (cJSON_IsString(background_color)) {
214+
light_theme->set_background_color(ParseColor(background_color->valuestring));
215+
light_theme->set_chat_background_color(ParseColor(background_color->valuestring));
216+
}
217+
if (cJSON_IsString(background_image)) {
218+
if (!GetAssetData(background_image->valuestring, ptr, size)) {
219+
ESP_LOGE(TAG, "The background image file %s is not found", background_image->valuestring);
220+
return false;
221+
}
222+
auto background_image = std::make_shared<LvglCBinImage>(ptr);
223+
light_theme->set_background_image(background_image);
224+
}
225+
}
226+
cJSON* dark_skin = cJSON_GetObjectItem(skin, "dark");
227+
if (cJSON_IsObject(dark_skin)) {
228+
cJSON* text_color = cJSON_GetObjectItem(dark_skin, "text_color");
229+
cJSON* background_color = cJSON_GetObjectItem(dark_skin, "background_color");
230+
cJSON* background_image = cJSON_GetObjectItem(dark_skin, "background_image");
231+
if (cJSON_IsString(text_color)) {
232+
dark_theme->set_text_color(ParseColor(text_color->valuestring));
233+
}
234+
if (cJSON_IsString(background_color)) {
235+
dark_theme->set_background_color(ParseColor(background_color->valuestring));
236+
dark_theme->set_chat_background_color(ParseColor(background_color->valuestring));
237+
}
238+
if (cJSON_IsString(background_image)) {
239+
if (!GetAssetData(background_image->valuestring, ptr, size)) {
240+
ESP_LOGE(TAG, "The background image file %s is not found", background_image->valuestring);
241+
return false;
242+
}
243+
auto background_image = std::make_shared<LvglCBinImage>(ptr);
244+
dark_theme->set_background_image(background_image);
245+
}
246+
}
192247
}
193248

194249
auto display = Board::GetInstance().GetDisplay();
195-
ESP_LOGI(TAG, "Applying new assets to display");
196-
display->UpdateStyle({
197-
.text_font = text_font_,
198-
.icon_font = nullptr,
199-
.emoji_collection = custom_emoji_collection_,
200-
});
201-
250+
ESP_LOGI(TAG, "Refreshing display theme...");
251+
display->SetTheme(display->GetTheme());
202252
cJSON_Delete(root);
203253
return true;
204254
}

main/assets.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,15 @@ class Assets {
5454
bool InitializePartition();
5555
uint32_t CalculateChecksum(const char* data, uint32_t length);
5656
bool GetAssetData(const std::string& name, void*& ptr, size_t& size);
57+
lv_color_t ParseColor(const std::string& color);
5758

5859
const esp_partition_t* partition_ = nullptr;
5960
esp_partition_mmap_handle_t mmap_handle_ = 0;
6061
const char* mmap_root_ = nullptr;
6162
bool partition_valid_ = false;
6263
bool checksum_valid_ = false;
6364
std::string default_assets_url_;
64-
lv_font_t* text_font_ = nullptr;
6565
srmodel_list_t* models_list_ = nullptr;
66-
CustomEmojiCollection* custom_emoji_collection_ = nullptr;
6766
std::map<std::string, Asset> assets_;
6867
};
6968

main/boards/common/ml307_board.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,10 @@ std::string Ml307Board::GetDeviceStatusJson() {
153153
}
154154
auto display = board.GetDisplay();
155155
if (display && display->height() > 64) { // For LCD display only
156-
cJSON_AddStringToObject(screen, "theme", display->GetTheme().c_str());
156+
auto theme = display->GetTheme();
157+
if (theme != nullptr) {
158+
cJSON_AddStringToObject(screen, "theme", theme->name().c_str());
159+
}
157160
}
158161
cJSON_AddItemToObject(root, "screen", screen);
159162

main/boards/common/wifi_board.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,10 @@ std::string WifiBoard::GetDeviceStatusJson() {
216216
}
217217
auto display = board.GetDisplay();
218218
if (display && display->height() > 64) { // For LCD display only
219-
cJSON_AddStringToObject(screen, "theme", display->GetTheme().c_str());
219+
auto theme = display->GetTheme();
220+
if (theme != nullptr) {
221+
cJSON_AddStringToObject(screen, "theme", theme->name().c_str());
222+
}
220223
}
221224
cJSON_AddItemToObject(root, "screen", screen);
222225

main/boards/electron-bot/electron_emoji_display.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "electron_emoji_display.h"
2+
#include "lvgl_theme.h"
23

34
#include <esp_log.h>
45
#include <font_awesome.h>
@@ -105,7 +106,11 @@ void ElectronEmojiDisplay::SetupGifContainer() {
105106

106107
lv_obj_align(chat_message_label_, LV_ALIGN_BOTTOM_MID, 0, 0);
107108

108-
LcdDisplay::SetTheme("dark");
109+
auto& theme_manager = LvglThemeManager::GetInstance();
110+
auto theme = theme_manager.GetTheme("dark");
111+
if (theme != nullptr) {
112+
LcdDisplay::SetTheme(theme);
113+
}
109114
}
110115

111116
void ElectronEmojiDisplay::SetEmotion(const char* emotion) {

main/boards/otto-robot/otto_emoji_display.cc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "otto_emoji_display.h"
2+
#include "lvgl_theme.h"
23

34
#include <esp_log.h>
45
#include <font_awesome.h>
@@ -107,7 +108,11 @@ void OttoEmojiDisplay::SetupGifContainer() {
107108

108109
lv_obj_align(chat_message_label_, LV_ALIGN_BOTTOM_MID, 0, 0);
109110

110-
LcdDisplay::SetTheme("dark");
111+
auto& theme_manager = LvglThemeManager::GetInstance();
112+
auto theme = theme_manager.GetTheme("dark");
113+
if (theme != nullptr) {
114+
LcdDisplay::SetTheme(theme);
115+
}
111116
}
112117

113118
void OttoEmojiDisplay::SetEmotion(const char* emotion) {

main/boards/sensecap-watcher/sensecap_watcher.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,17 +46,21 @@ class CustomLcdDisplay : public SpiLcdDisplay {
4646
: SpiLcdDisplay(io_handle, panel_handle, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
4747

4848
DisplayLockGuard lock(this);
49-
lv_obj_set_size(status_bar_, LV_HOR_RES, style_.text_font->line_height * 2 + 10);
49+
auto lvgl_theme = static_cast<LvglTheme*>(current_theme_);
50+
auto text_font = lvgl_theme->text_font()->font();
51+
auto icon_font = lvgl_theme->icon_font()->font();
52+
53+
lv_obj_set_size(status_bar_, LV_HOR_RES, text_font->line_height * 2 + 10);
5054
lv_obj_set_style_layout(status_bar_, LV_LAYOUT_NONE, 0);
5155
lv_obj_set_style_pad_top(status_bar_, 10, 0);
5256
lv_obj_set_style_pad_bottom(status_bar_, 1, 0);
5357

5458
// 针对圆形屏幕调整位置
5559
// network battery mute //
5660
// status //
57-
lv_obj_align(battery_label_, LV_ALIGN_TOP_MID, -2.5 * style_.icon_font->line_height, 0);
58-
lv_obj_align(network_label_, LV_ALIGN_TOP_MID, -0.5 * style_.icon_font->line_height, 0);
59-
lv_obj_align(mute_label_, LV_ALIGN_TOP_MID, 1.5 * style_.icon_font->line_height, 0);
61+
lv_obj_align(battery_label_, LV_ALIGN_TOP_MID, -2.5 * icon_font->line_height, 0);
62+
lv_obj_align(network_label_, LV_ALIGN_TOP_MID, -0.5 * icon_font->line_height, 0);
63+
lv_obj_align(mute_label_, LV_ALIGN_TOP_MID, 1.5 * icon_font->line_height, 0);
6064

6165
lv_obj_align(status_label_, LV_ALIGN_BOTTOM_MID, 0, 0);
6266
lv_obj_set_flex_grow(status_label_, 0);

main/boards/zhengchen-1.54tft-wifi/zhengchen_lcd_display.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define ZHENGCHEN_LCD_DISPLAY_H
33

44
#include "display/lcd_display.h"
5+
#include "lvgl_theme.h"
56
#include <esp_lvgl_port.h>
67

78
class ZHENGCHEN_LcdDisplay : public SpiLcdDisplay {
@@ -14,10 +15,12 @@ class ZHENGCHEN_LcdDisplay : public SpiLcdDisplay {
1415
using SpiLcdDisplay::SpiLcdDisplay;
1516

1617
void SetupHighTempWarningPopup() {
18+
auto lvgl_theme = static_cast<LvglTheme*>(current_theme_);
19+
auto text_font = lvgl_theme->text_font()->font();
1720
// 创建高温警告弹窗
1821
high_temp_popup_ = lv_obj_create(lv_screen_active()); // 使用当前屏幕
1922
lv_obj_set_scrollbar_mode(high_temp_popup_, LV_SCROLLBAR_MODE_OFF);
20-
lv_obj_set_size(high_temp_popup_, LV_HOR_RES * 0.9, style_.text_font->line_height * 2);
23+
lv_obj_set_size(high_temp_popup_, LV_HOR_RES * 0.9, text_font->line_height * 2);
2124
lv_obj_align(high_temp_popup_, LV_ALIGN_BOTTOM_MID, 0, 0);
2225
lv_obj_set_style_bg_color(high_temp_popup_, lv_palette_main(LV_PALETTE_RED), 0);
2326
lv_obj_set_style_radius(high_temp_popup_, 10, 0);

0 commit comments

Comments
 (0)