Fix setupui (#1777)
* Enhance GitHub Actions artifact download script - Updated the output directory structure to save downloaded files in a version-specific subdirectory (releases/<version>). - Added a new function to determine the default releases directory path relative to the script's location. - Improved artifact renaming logic to handle known extensions more robustly and ensure compatibility with filenames containing dots. * Refactor UI setup in ElectronEmojiDisplay and OttoEmojiDisplay classes - Moved SetupChatLabel call in ElectronEmojiDisplay to ensure it is executed after the parent UI is initialized, preventing potential issues with container validity. - Updated SetupUI in OttoEmojiDisplay to release the display lock before calling SetEmotion, avoiding deadlock scenarios during UI setup. * Add multiline chat message support in display configuration - Introduced a new Kconfig option to enable multiline chat message display in the default mode. - Updated the LCD display setup to accommodate a dynamic height bottom bar for multiline messages. - Modified the configuration files for the waveshare-esp32-s3-epaper-1.54 board to include the new chat message setting. * Update font and emoji settings for Magiclick boards; enhance bottom bar visibility logic in LCD display - Changed the default text and emoji fonts for Magiclick S3 2P4 and S3 2P5 boards to Noto fonts. - Improved bottom bar visibility logic in LcdDisplay to hide when there is no content, ensuring a cleaner UI experience.
This commit is contained in:
@ -171,14 +171,14 @@ elseif(CONFIG_BOARD_TYPE_EDA_SUPER_BEAR)
|
|||||||
set(BOARD_TYPE "eda-super-bear")
|
set(BOARD_TYPE "eda-super-bear")
|
||||||
elseif(CONFIG_BOARD_TYPE_MAGICLICK_S3_2P4)
|
elseif(CONFIG_BOARD_TYPE_MAGICLICK_S3_2P4)
|
||||||
set(BOARD_TYPE "magiclick-2p4")
|
set(BOARD_TYPE "magiclick-2p4")
|
||||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
set(BUILTIN_TEXT_FONT font_noto_basic_16_4)
|
||||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
set(DEFAULT_EMOJI_COLLECTION noto-emoji_64)
|
||||||
elseif(CONFIG_BOARD_TYPE_MAGICLICK_S3_2P5)
|
elseif(CONFIG_BOARD_TYPE_MAGICLICK_S3_2P5)
|
||||||
set(BOARD_TYPE "magiclick-2p5")
|
set(BOARD_TYPE "magiclick-2p5")
|
||||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
set(BUILTIN_TEXT_FONT font_noto_basic_16_4)
|
||||||
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
set(BUILTIN_ICON_FONT font_awesome_16_4)
|
||||||
set(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
set(DEFAULT_EMOJI_COLLECTION noto-emoji_64)
|
||||||
elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3)
|
elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3)
|
||||||
set(BOARD_TYPE "magiclick-c3")
|
set(BOARD_TYPE "magiclick-c3")
|
||||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||||
|
|||||||
@ -635,6 +635,17 @@ choice DISPLAY_STYLE
|
|||||||
|| BOARD_TYPE_ESP_SENSAIRSHUTTLE
|
|| BOARD_TYPE_ESP_SENSAIRSHUTTLE
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
|
config USE_MULTILINE_CHAT_MESSAGE
|
||||||
|
bool "Use multiline chat message display (default mode only)"
|
||||||
|
depends on USE_DEFAULT_MESSAGE_STYLE
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
When enabled, the chat message area in the default display mode shows
|
||||||
|
multiple wrapped lines that grow upward from the bottom of the screen,
|
||||||
|
with auto-adaptive height.
|
||||||
|
When disabled (default), a single-line horizontally scrolling label
|
||||||
|
is shown at the bottom of the screen.
|
||||||
|
|
||||||
choice WAKE_WORD_TYPE
|
choice WAKE_WORD_TYPE
|
||||||
prompt "Wake Word Implementation Type"
|
prompt "Wake Word Implementation Type"
|
||||||
default USE_AFE_WAKE_WORD if (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
|
default USE_AFE_WAKE_WORD if (IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32P4) && SPIRAM
|
||||||
|
|||||||
@ -15,7 +15,6 @@
|
|||||||
ElectronEmojiDisplay::ElectronEmojiDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y,
|
ElectronEmojiDisplay::ElectronEmojiDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y,
|
||||||
bool swap_xy)
|
bool swap_xy)
|
||||||
: SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
|
: SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
|
||||||
SetupChatLabel();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElectronEmojiDisplay::SetupUI() {
|
void ElectronEmojiDisplay::SetupUI() {
|
||||||
@ -25,9 +24,12 @@ void ElectronEmojiDisplay::SetupUI() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call parent SetupUI() first to create all lvgl objects
|
// Call parent SetupUI() first to create all lvgl objects (including container_)
|
||||||
SpiLcdDisplay::SetupUI();
|
SpiLcdDisplay::SetupUI();
|
||||||
|
|
||||||
|
// Setup chat label after parent UI is initialized so that container_ is valid
|
||||||
|
SetupChatLabel();
|
||||||
|
|
||||||
// Set default emotion after UI is initialized
|
// Set default emotion after UI is initialized
|
||||||
SetEmotion("staticstate");
|
SetEmotion("staticstate");
|
||||||
}
|
}
|
||||||
@ -40,6 +42,8 @@ void ElectronEmojiDisplay::InitializeElectronEmojis() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ElectronEmojiDisplay::SetupChatLabel() {
|
void ElectronEmojiDisplay::SetupChatLabel() {
|
||||||
|
// Create/recreate the chat label under the display lock
|
||||||
|
{
|
||||||
DisplayLockGuard lock(this);
|
DisplayLockGuard lock(this);
|
||||||
|
|
||||||
if (chat_message_label_) {
|
if (chat_message_label_) {
|
||||||
@ -48,10 +52,12 @@ void ElectronEmojiDisplay::SetupChatLabel() {
|
|||||||
|
|
||||||
chat_message_label_ = lv_label_create(container_);
|
chat_message_label_ = lv_label_create(container_);
|
||||||
lv_label_set_text(chat_message_label_, "");
|
lv_label_set_text(chat_message_label_, "");
|
||||||
lv_obj_set_width(chat_message_label_, width_ * 0.9); // 限制宽度为屏幕宽度的 90%
|
lv_obj_set_width(chat_message_label_, width_ * 0.9);
|
||||||
lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); // 设置为自动换行模式
|
lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR);
|
||||||
lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_CENTER, 0); // 设置文本居中对齐
|
lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_CENTER, 0);
|
||||||
lv_obj_set_style_text_color(chat_message_label_, lv_color_white(), 0);
|
lv_obj_set_style_text_color(chat_message_label_, lv_color_white(), 0);
|
||||||
|
}
|
||||||
|
// SetTheme acquires DisplayLockGuard internally, so call it after releasing the lock above
|
||||||
SetTheme(LvglThemeManager::GetInstance().GetTheme("dark"));
|
SetTheme(LvglThemeManager::GetInstance().GetTheme("dark"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -23,16 +23,18 @@ public:
|
|||||||
NV3023Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel,
|
NV3023Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel,
|
||||||
int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy)
|
int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy)
|
||||||
: SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
|
: SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupUI() override {
|
||||||
|
SpiLcdDisplay::SetupUI();
|
||||||
|
|
||||||
|
// Apply custom color styles after parent creates all LVGL objects
|
||||||
DisplayLockGuard lock(this);
|
DisplayLockGuard lock(this);
|
||||||
// 只需要覆盖颜色相关的样式
|
|
||||||
auto screen = lv_disp_get_scr_act(lv_disp_get_default());
|
auto screen = lv_disp_get_scr_act(lv_disp_get_default());
|
||||||
lv_obj_set_style_text_color(screen, lv_color_black(), 0);
|
lv_obj_set_style_text_color(screen, lv_color_black(), 0);
|
||||||
|
|
||||||
// 设置容器背景色
|
|
||||||
lv_obj_set_style_bg_color(container_, lv_color_black(), 0);
|
lv_obj_set_style_bg_color(container_, lv_color_black(), 0);
|
||||||
|
|
||||||
// 设置状态栏背景色和文本颜色
|
|
||||||
lv_obj_set_style_bg_color(status_bar_, lv_color_white(), 0);
|
lv_obj_set_style_bg_color(status_bar_, lv_color_white(), 0);
|
||||||
lv_obj_set_style_text_color(network_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(network_label_, lv_color_black(), 0);
|
||||||
lv_obj_set_style_text_color(notification_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(notification_label_, lv_color_black(), 0);
|
||||||
@ -40,7 +42,6 @@ public:
|
|||||||
lv_obj_set_style_text_color(mute_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(mute_label_, lv_color_black(), 0);
|
||||||
lv_obj_set_style_text_color(battery_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(battery_label_, lv_color_black(), 0);
|
||||||
|
|
||||||
// 设置内容区背景色和文本颜色
|
|
||||||
lv_obj_set_style_bg_color(content_, lv_color_black(), 0);
|
lv_obj_set_style_bg_color(content_, lv_color_black(), 0);
|
||||||
lv_obj_set_style_border_width(content_, 0, 0);
|
lv_obj_set_style_border_width(content_, 0, 0);
|
||||||
lv_obj_set_style_text_color(emoji_label_, lv_color_white(), 0);
|
lv_obj_set_style_text_color(emoji_label_, lv_color_white(), 0);
|
||||||
|
|||||||
@ -23,16 +23,18 @@ public:
|
|||||||
GC9107Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel,
|
GC9107Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel,
|
||||||
int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy)
|
int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy)
|
||||||
: SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
|
: SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupUI() override {
|
||||||
|
SpiLcdDisplay::SetupUI();
|
||||||
|
|
||||||
|
// Apply custom color styles after parent creates all LVGL objects
|
||||||
DisplayLockGuard lock(this);
|
DisplayLockGuard lock(this);
|
||||||
// 只需要覆盖颜色相关的样式
|
|
||||||
auto screen = lv_disp_get_scr_act(lv_disp_get_default());
|
auto screen = lv_disp_get_scr_act(lv_disp_get_default());
|
||||||
lv_obj_set_style_text_color(screen, lv_color_black(), 0);
|
lv_obj_set_style_text_color(screen, lv_color_black(), 0);
|
||||||
|
|
||||||
// 设置容器背景色
|
|
||||||
lv_obj_set_style_bg_color(container_, lv_color_black(), 0);
|
lv_obj_set_style_bg_color(container_, lv_color_black(), 0);
|
||||||
|
|
||||||
// 设置状态栏背景色和文本颜色
|
|
||||||
lv_obj_set_style_bg_color(status_bar_, lv_color_make(0x1e, 0x90, 0xff), 0);
|
lv_obj_set_style_bg_color(status_bar_, lv_color_make(0x1e, 0x90, 0xff), 0);
|
||||||
lv_obj_set_style_text_color(network_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(network_label_, lv_color_black(), 0);
|
||||||
lv_obj_set_style_text_color(notification_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(notification_label_, lv_color_black(), 0);
|
||||||
@ -40,7 +42,6 @@ public:
|
|||||||
lv_obj_set_style_text_color(mute_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(mute_label_, lv_color_black(), 0);
|
||||||
lv_obj_set_style_text_color(battery_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(battery_label_, lv_color_black(), 0);
|
||||||
|
|
||||||
// 设置内容区背景色和文本颜色
|
|
||||||
lv_obj_set_style_bg_color(content_, lv_color_black(), 0);
|
lv_obj_set_style_bg_color(content_, lv_color_black(), 0);
|
||||||
lv_obj_set_style_border_width(content_, 0, 0);
|
lv_obj_set_style_border_width(content_, 0, 0);
|
||||||
lv_obj_set_style_text_color(emoji_label_, lv_color_white(), 0);
|
lv_obj_set_style_text_color(emoji_label_, lv_color_white(), 0);
|
||||||
|
|||||||
@ -21,16 +21,18 @@ public:
|
|||||||
NV3023Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel,
|
NV3023Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel,
|
||||||
int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy)
|
int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy)
|
||||||
: SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
|
: SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetupUI() override {
|
||||||
|
SpiLcdDisplay::SetupUI();
|
||||||
|
|
||||||
|
// Apply custom color styles after parent creates all LVGL objects
|
||||||
DisplayLockGuard lock(this);
|
DisplayLockGuard lock(this);
|
||||||
// 只需要覆盖颜色相关的样式
|
|
||||||
auto screen = lv_disp_get_scr_act(lv_disp_get_default());
|
auto screen = lv_disp_get_scr_act(lv_disp_get_default());
|
||||||
lv_obj_set_style_text_color(screen, lv_color_black(), 0);
|
lv_obj_set_style_text_color(screen, lv_color_black(), 0);
|
||||||
|
|
||||||
// 设置容器背景色
|
|
||||||
lv_obj_set_style_bg_color(container_, lv_color_black(), 0);
|
lv_obj_set_style_bg_color(container_, lv_color_black(), 0);
|
||||||
|
|
||||||
// 设置状态栏背景色和文本颜色
|
|
||||||
lv_obj_set_style_bg_color(status_bar_, lv_color_white(), 0);
|
lv_obj_set_style_bg_color(status_bar_, lv_color_white(), 0);
|
||||||
lv_obj_set_style_text_color(network_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(network_label_, lv_color_black(), 0);
|
||||||
lv_obj_set_style_text_color(notification_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(notification_label_, lv_color_black(), 0);
|
||||||
@ -38,7 +40,6 @@ public:
|
|||||||
lv_obj_set_style_text_color(mute_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(mute_label_, lv_color_black(), 0);
|
||||||
lv_obj_set_style_text_color(battery_label_, lv_color_black(), 0);
|
lv_obj_set_style_text_color(battery_label_, lv_color_black(), 0);
|
||||||
|
|
||||||
// 设置内容区背景色和文本颜色
|
|
||||||
lv_obj_set_style_bg_color(content_, lv_color_black(), 0);
|
lv_obj_set_style_bg_color(content_, lv_color_black(), 0);
|
||||||
lv_obj_set_style_border_width(content_, 0, 0);
|
lv_obj_set_style_border_width(content_, 0, 0);
|
||||||
lv_obj_set_style_text_color(emoji_label_, lv_color_white(), 0);
|
lv_obj_set_style_text_color(emoji_label_, lv_color_white(), 0);
|
||||||
|
|||||||
@ -27,9 +27,12 @@ void OttoEmojiDisplay::SetupUI() {
|
|||||||
// Call parent SetupUI() first to create all lvgl objects
|
// Call parent SetupUI() first to create all lvgl objects
|
||||||
SpiLcdDisplay::SetupUI();
|
SpiLcdDisplay::SetupUI();
|
||||||
|
|
||||||
// Setup preview image after UI is initialized
|
// Setup preview image after UI is initialized - release lock before calling SetEmotion
|
||||||
|
// to avoid deadlock (SetEmotion also acquires DisplayLockGuard internally)
|
||||||
|
{
|
||||||
DisplayLockGuard lock(this);
|
DisplayLockGuard lock(this);
|
||||||
lv_obj_set_size(preview_image_, width_ , height_ );
|
lv_obj_set_size(preview_image_, width_ , height_ );
|
||||||
|
}
|
||||||
|
|
||||||
// Set default emotion after UI is initialized
|
// Set default emotion after UI is initialized
|
||||||
SetEmotion("staticstate");
|
SetEmotion("staticstate");
|
||||||
|
|||||||
@ -6,7 +6,8 @@
|
|||||||
"name": "esp32-s3-epaper-1.54-v2",
|
"name": "esp32-s3-epaper-1.54-v2",
|
||||||
"sdkconfig_append": [
|
"sdkconfig_append": [
|
||||||
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
|
"CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y",
|
||||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\""
|
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/8m.csv\"",
|
||||||
|
"CONFIG_USE_MULTILINE_CHAT_MESSAGE=y"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -14,7 +15,8 @@
|
|||||||
"sdkconfig_append": [
|
"sdkconfig_append": [
|
||||||
"CONFIG_SPIRAM_MODE_QUAD=y",
|
"CONFIG_SPIRAM_MODE_QUAD=y",
|
||||||
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
|
"CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y",
|
||||||
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/4m.csv\""
|
"CONFIG_PARTITION_TABLE_CUSTOM_FILENAME=\"partitions/v2/4m.csv\"",
|
||||||
|
"CONFIG_USE_MULTILINE_CHAT_MESSAGE=y"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -924,9 +924,33 @@ void LcdDisplay::SetupUI() {
|
|||||||
lv_label_set_text(status_label_, Lang::Strings::INITIALIZING);
|
lv_label_set_text(status_label_, Lang::Strings::INITIALIZING);
|
||||||
lv_obj_align(status_label_, LV_ALIGN_CENTER, 0, 0);
|
lv_obj_align(status_label_, LV_ALIGN_CENTER, 0, 0);
|
||||||
|
|
||||||
|
#if CONFIG_USE_MULTILINE_CHAT_MESSAGE
|
||||||
|
/* Bottom bar - auto height, grows upward with wrapped text */
|
||||||
|
bottom_bar_ = lv_obj_create(screen);
|
||||||
|
lv_obj_set_width(bottom_bar_, LV_HOR_RES);
|
||||||
|
lv_obj_set_height(bottom_bar_, LV_SIZE_CONTENT);
|
||||||
|
lv_obj_set_style_radius(bottom_bar_, 0, 0);
|
||||||
|
lv_obj_set_style_bg_color(bottom_bar_, lvgl_theme->background_color(), 0);
|
||||||
|
lv_obj_set_style_bg_opa(bottom_bar_, LV_OPA_50, 0);
|
||||||
|
lv_obj_set_style_text_color(bottom_bar_, lvgl_theme->text_color(), 0);
|
||||||
|
lv_obj_set_style_pad_all(bottom_bar_, lvgl_theme->spacing(4), 0);
|
||||||
|
lv_obj_set_style_border_width(bottom_bar_, 0, 0);
|
||||||
|
lv_obj_set_scrollbar_mode(bottom_bar_, LV_SCROLLBAR_MODE_OFF);
|
||||||
|
lv_obj_align(bottom_bar_, LV_ALIGN_BOTTOM_MID, 0, 0);
|
||||||
|
|
||||||
|
/* chat_message_label_ placed in bottom_bar_, multiline wrapped display */
|
||||||
|
chat_message_label_ = lv_label_create(bottom_bar_);
|
||||||
|
lv_label_set_text(chat_message_label_, "");
|
||||||
|
lv_obj_set_width(chat_message_label_, LV_HOR_RES - lvgl_theme->spacing(8));
|
||||||
|
lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_WRAP);
|
||||||
|
lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_CENTER, 0);
|
||||||
|
lv_obj_set_style_text_color(chat_message_label_, lvgl_theme->text_color(), 0);
|
||||||
|
lv_obj_align(chat_message_label_, LV_ALIGN_CENTER, 0, 0);
|
||||||
|
lv_obj_add_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN); // Hide until there is content
|
||||||
|
#else
|
||||||
/* Top layer: Bottom bar - fixed height at bottom */
|
/* Top layer: Bottom bar - fixed height at bottom */
|
||||||
bottom_bar_ = lv_obj_create(screen);
|
bottom_bar_ = lv_obj_create(screen);
|
||||||
lv_obj_set_size(bottom_bar_, LV_HOR_RES, text_font->line_height + lvgl_theme->spacing(12));
|
lv_obj_set_size(bottom_bar_, LV_HOR_RES, text_font->line_height + lvgl_theme->spacing(8));
|
||||||
lv_obj_set_style_radius(bottom_bar_, 0, 0);
|
lv_obj_set_style_radius(bottom_bar_, 0, 0);
|
||||||
lv_obj_set_style_bg_color(bottom_bar_, lvgl_theme->background_color(), 0);
|
lv_obj_set_style_bg_color(bottom_bar_, lvgl_theme->background_color(), 0);
|
||||||
lv_obj_set_style_text_color(bottom_bar_, lvgl_theme->text_color(), 0);
|
lv_obj_set_style_text_color(bottom_bar_, lvgl_theme->text_color(), 0);
|
||||||
@ -953,6 +977,8 @@ void LcdDisplay::SetupUI() {
|
|||||||
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
|
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
|
||||||
lv_obj_set_style_anim(chat_message_label_, &a, LV_PART_MAIN);
|
lv_obj_set_style_anim(chat_message_label_, &a, LV_PART_MAIN);
|
||||||
lv_obj_set_style_anim_duration(chat_message_label_, lv_anim_speed_clamped(60, 300, 60000), LV_PART_MAIN);
|
lv_obj_set_style_anim_duration(chat_message_label_, lv_anim_speed_clamped(60, 300, 60000), LV_PART_MAIN);
|
||||||
|
lv_obj_add_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN); // Hide until there is content
|
||||||
|
#endif
|
||||||
|
|
||||||
low_battery_popup_ = lv_obj_create(screen);
|
low_battery_popup_ = lv_obj_create(screen);
|
||||||
lv_obj_set_scrollbar_mode(low_battery_popup_, LV_SCROLLBAR_MODE_OFF);
|
lv_obj_set_scrollbar_mode(low_battery_popup_, LV_SCROLLBAR_MODE_OFF);
|
||||||
@ -1016,14 +1042,32 @@ void LcdDisplay::SetChatMessage(const char* role, const char* content) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
lv_label_set_text(chat_message_label_, content);
|
lv_label_set_text(chat_message_label_, content);
|
||||||
|
// Show bottom_bar_ only when there is content (and subtitle is not globally hidden)
|
||||||
|
if (bottom_bar_ != nullptr) {
|
||||||
|
if (content == nullptr || content[0] == '\0') {
|
||||||
|
lv_obj_add_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
} else if (!hide_subtitle_) {
|
||||||
|
lv_obj_remove_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if CONFIG_USE_MULTILINE_CHAT_MESSAGE
|
||||||
|
// Re-align bottom_bar_ after text change so it stays anchored to the bottom
|
||||||
|
// as its height adapts to the wrapped content.
|
||||||
|
if (bottom_bar_ != nullptr) {
|
||||||
|
lv_obj_align(bottom_bar_, LV_ALIGN_BOTTOM_MID, 0, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void LcdDisplay::ClearChatMessages() {
|
void LcdDisplay::ClearChatMessages() {
|
||||||
DisplayLockGuard lock(this);
|
DisplayLockGuard lock(this);
|
||||||
// In non-wechat mode, just clear the chat message label
|
// In non-wechat mode, just clear the chat message label and hide the bar
|
||||||
if (chat_message_label_ != nullptr) {
|
if (chat_message_label_ != nullptr) {
|
||||||
lv_label_set_text(chat_message_label_, "");
|
lv_label_set_text(chat_message_label_, "");
|
||||||
}
|
}
|
||||||
|
if (bottom_bar_ != nullptr) {
|
||||||
|
lv_obj_add_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1253,7 +1297,11 @@ void LcdDisplay::SetHideSubtitle(bool hide) {
|
|||||||
if (hide) {
|
if (hide) {
|
||||||
lv_obj_add_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN);
|
lv_obj_add_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN);
|
||||||
} else {
|
} else {
|
||||||
|
// Only show if there is actual content to display
|
||||||
|
const char* text = (chat_message_label_ != nullptr) ? lv_label_get_text(chat_message_label_) : nullptr;
|
||||||
|
if (text != nullptr && text[0] != '\0') {
|
||||||
lv_obj_remove_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN);
|
lv_obj_remove_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,10 @@ Download GitHub Actions artifacts and rename them with version numbers.
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python download_github_runs.py 2.0.4 https://github.com/78/xiaozhi-esp32/actions/runs/18866246016
|
python download_github_runs.py 2.0.4 https://github.com/78/xiaozhi-esp32/actions/runs/18866246016
|
||||||
|
|
||||||
|
Output:
|
||||||
|
Files are downloaded to releases/<version>/ directory relative to the project root.
|
||||||
|
Example: releases/2.0.4/v2.0.4_atk-dnesp32s3-box0.zip
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
@ -147,12 +151,17 @@ def rename_artifact(original_name: str, version: str) -> str:
|
|||||||
if name.startswith("xiaozhi_"):
|
if name.startswith("xiaozhi_"):
|
||||||
name = name[len("xiaozhi_"):]
|
name = name[len("xiaozhi_"):]
|
||||||
|
|
||||||
# Remove extension
|
# Remove known extensions only (not using splitext to avoid issues with
|
||||||
name_without_ext = os.path.splitext(name)[0]
|
# names containing dots like "esp32-s3-touch-amoled-2.06")
|
||||||
|
known_extensions = ('.bin', '.zip')
|
||||||
|
for ext in known_extensions:
|
||||||
|
if name.endswith(ext):
|
||||||
|
name = name[:-len(ext)]
|
||||||
|
break
|
||||||
|
|
||||||
# Remove hash suffix (pattern: underscore followed by 40+ hex characters)
|
# Remove hash suffix (pattern: underscore followed by 40+ hex characters)
|
||||||
# This matches Git commit hashes and similar identifiers
|
# This matches Git commit hashes and similar identifiers
|
||||||
name_without_hash = re.sub(r'_[a-f0-9]{40,}$', '', name_without_ext)
|
name_without_hash = re.sub(r'_[a-f0-9]{40,}$', '', name)
|
||||||
|
|
||||||
# Add version prefix and .zip extension
|
# Add version prefix and .zip extension
|
||||||
new_name = f"v{version}_{name_without_hash}.zip"
|
new_name = f"v{version}_{name_without_hash}.zip"
|
||||||
@ -160,6 +169,17 @@ def rename_artifact(original_name: str, version: str) -> str:
|
|||||||
return new_name
|
return new_name
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_releases_dir() -> Path:
|
||||||
|
"""
|
||||||
|
Get the default releases directory path relative to this script's location.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path to the releases directory (script_dir/../releases)
|
||||||
|
"""
|
||||||
|
script_dir = Path(__file__).resolve().parent
|
||||||
|
return script_dir.parent / "releases"
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main function to download and rename GitHub Actions artifacts."""
|
"""Main function to download and rename GitHub Actions artifacts."""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
@ -175,8 +195,8 @@ def main():
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--output-dir",
|
"--output-dir",
|
||||||
default="../releases",
|
default=None,
|
||||||
help="Output directory for downloaded artifacts (default: ../releases)"
|
help="Output directory for downloaded artifacts (default: releases/<version> relative to project root)"
|
||||||
)
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@ -211,8 +231,15 @@ def main():
|
|||||||
print(f" - {artifact['name']}")
|
print(f" - {artifact['name']}")
|
||||||
print()
|
print()
|
||||||
|
|
||||||
|
# Determine output directory
|
||||||
|
if args.output_dir:
|
||||||
|
# User specified custom output directory
|
||||||
|
output_dir = Path(args.output_dir) / args.version
|
||||||
|
else:
|
||||||
|
# Default: releases/<version> relative to script location
|
||||||
|
output_dir = get_default_releases_dir() / args.version
|
||||||
|
|
||||||
# Create output directory
|
# Create output directory
|
||||||
output_dir = Path(args.output_dir)
|
|
||||||
output_dir.mkdir(parents=True, exist_ok=True)
|
output_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Download and rename each artifact
|
# Download and rename each artifact
|
||||||
|
|||||||
Reference in New Issue
Block a user