mirror of
https://github.com/78/xiaozhi-esp32.git
synced 2026-02-19 10:28:10 +00:00
Compare commits
4 Commits
main
...
fix_setupu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
14d85f22ba | ||
|
|
763a62c6c5 | ||
|
|
c6e34f42ef | ||
|
|
fc2ade8993 |
@@ -171,14 +171,14 @@ elseif(CONFIG_BOARD_TYPE_EDA_SUPER_BEAR)
|
||||
set(BOARD_TYPE "eda-super-bear")
|
||||
elseif(CONFIG_BOARD_TYPE_MAGICLICK_S3_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(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_MAGICLICK_S3_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(DEFAULT_EMOJI_COLLECTION twemoji_32)
|
||||
set(DEFAULT_EMOJI_COLLECTION noto-emoji_64)
|
||||
elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3)
|
||||
set(BOARD_TYPE "magiclick-c3")
|
||||
set(BUILTIN_TEXT_FONT font_puhui_basic_16_4)
|
||||
|
||||
@@ -635,6 +635,17 @@ choice DISPLAY_STYLE
|
||||
|| BOARD_TYPE_ESP_SENSAIRSHUTTLE
|
||||
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
|
||||
prompt "Wake Word Implementation Type"
|
||||
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,
|
||||
bool swap_xy)
|
||||
: SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {
|
||||
SetupChatLabel();
|
||||
}
|
||||
|
||||
void ElectronEmojiDisplay::SetupUI() {
|
||||
@@ -25,9 +24,12 @@ void ElectronEmojiDisplay::SetupUI() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call parent SetupUI() first to create all lvgl objects
|
||||
// Call parent SetupUI() first to create all lvgl objects (including container_)
|
||||
SpiLcdDisplay::SetupUI();
|
||||
|
||||
|
||||
// Setup chat label after parent UI is initialized so that container_ is valid
|
||||
SetupChatLabel();
|
||||
|
||||
// Set default emotion after UI is initialized
|
||||
SetEmotion("staticstate");
|
||||
}
|
||||
@@ -40,18 +42,22 @@ void ElectronEmojiDisplay::InitializeElectronEmojis() {
|
||||
}
|
||||
|
||||
void ElectronEmojiDisplay::SetupChatLabel() {
|
||||
DisplayLockGuard lock(this);
|
||||
// Create/recreate the chat label under the display lock
|
||||
{
|
||||
DisplayLockGuard lock(this);
|
||||
|
||||
if (chat_message_label_) {
|
||||
lv_obj_del(chat_message_label_);
|
||||
if (chat_message_label_) {
|
||||
lv_obj_del(chat_message_label_);
|
||||
}
|
||||
|
||||
chat_message_label_ = lv_label_create(container_);
|
||||
lv_label_set_text(chat_message_label_, "");
|
||||
lv_obj_set_width(chat_message_label_, width_ * 0.9);
|
||||
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_color(chat_message_label_, lv_color_white(), 0);
|
||||
}
|
||||
|
||||
chat_message_label_ = lv_label_create(container_);
|
||||
lv_label_set_text(chat_message_label_, "");
|
||||
lv_obj_set_width(chat_message_label_, width_ * 0.9); // 限制宽度为屏幕宽度的 90%
|
||||
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_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"));
|
||||
}
|
||||
|
||||
|
||||
@@ -23,16 +23,18 @@ public:
|
||||
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)
|
||||
: 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);
|
||||
// 只需要覆盖颜色相关的样式
|
||||
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_bg_color(container_, lv_color_black(), 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(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(battery_label_, 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_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,
|
||||
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) {
|
||||
}
|
||||
|
||||
void SetupUI() override {
|
||||
SpiLcdDisplay::SetupUI();
|
||||
|
||||
// Apply custom color styles after parent creates all LVGL objects
|
||||
DisplayLockGuard lock(this);
|
||||
// 只需要覆盖颜色相关的样式
|
||||
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_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_text_color(network_label_, lv_color_black(), 0);
|
||||
lv_obj_set_style_text_color(notification_label_, lv_color_black(), 0);
|
||||
@@ -40,12 +42,11 @@ public:
|
||||
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_bg_color(content_, lv_color_black(), 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(chat_message_label_, lv_color_white(), 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = {
|
||||
|
||||
@@ -21,16 +21,18 @@ public:
|
||||
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)
|
||||
: 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);
|
||||
// 只需要覆盖颜色相关的样式
|
||||
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_bg_color(container_, lv_color_black(), 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(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(battery_label_, 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_text_color(emoji_label_, lv_color_white(), 0);
|
||||
|
||||
@@ -27,10 +27,13 @@ void OttoEmojiDisplay::SetupUI() {
|
||||
// Call parent SetupUI() first to create all lvgl objects
|
||||
SpiLcdDisplay::SetupUI();
|
||||
|
||||
// Setup preview image after UI is initialized
|
||||
DisplayLockGuard lock(this);
|
||||
lv_obj_set_size(preview_image_, width_ , height_ );
|
||||
|
||||
// Setup preview image after UI is initialized - release lock before calling SetEmotion
|
||||
// to avoid deadlock (SetEmotion also acquires DisplayLockGuard internally)
|
||||
{
|
||||
DisplayLockGuard lock(this);
|
||||
lv_obj_set_size(preview_image_, width_ , height_ );
|
||||
}
|
||||
|
||||
// Set default emotion after UI is initialized
|
||||
SetEmotion("staticstate");
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
"name": "esp32-s3-epaper-1.54-v2",
|
||||
"sdkconfig_append": [
|
||||
"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": [
|
||||
"CONFIG_SPIRAM_MODE_QUAD=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_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 */
|
||||
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_bg_color(bottom_bar_, lvgl_theme->background_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_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_add_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN); // Hide until there is content
|
||||
#endif
|
||||
|
||||
low_battery_popup_ = lv_obj_create(screen);
|
||||
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;
|
||||
}
|
||||
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() {
|
||||
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) {
|
||||
lv_label_set_text(chat_message_label_, "");
|
||||
}
|
||||
if (bottom_bar_ != nullptr) {
|
||||
lv_obj_add_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1253,7 +1297,11 @@ void LcdDisplay::SetHideSubtitle(bool hide) {
|
||||
if (hide) {
|
||||
lv_obj_add_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN);
|
||||
} else {
|
||||
lv_obj_remove_flag(bottom_bar_, LV_OBJ_FLAG_HIDDEN);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,10 @@ Download GitHub Actions artifacts and rename them with version numbers.
|
||||
|
||||
Usage:
|
||||
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
|
||||
@@ -147,12 +151,17 @@ def rename_artifact(original_name: str, version: str) -> str:
|
||||
if name.startswith("xiaozhi_"):
|
||||
name = name[len("xiaozhi_"):]
|
||||
|
||||
# Remove extension
|
||||
name_without_ext = os.path.splitext(name)[0]
|
||||
# Remove known extensions only (not using splitext to avoid issues with
|
||||
# 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)
|
||||
# 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
|
||||
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
|
||||
|
||||
|
||||
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():
|
||||
"""Main function to download and rename GitHub Actions artifacts."""
|
||||
parser = argparse.ArgumentParser(
|
||||
@@ -175,8 +195,8 @@ def main():
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output-dir",
|
||||
default="../releases",
|
||||
help="Output directory for downloaded artifacts (default: ../releases)"
|
||||
default=None,
|
||||
help="Output directory for downloaded artifacts (default: releases/<version> relative to project root)"
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
@@ -211,8 +231,15 @@ def main():
|
||||
print(f" - {artifact['name']}")
|
||||
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
|
||||
output_dir = Path(args.output_dir)
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Download and rename each artifact
|
||||
|
||||
Reference in New Issue
Block a user