From 63549516ec55272bd3228b962ef0458fc2bf2018 Mon Sep 17 00:00:00 2001 From: liu weikai Date: Sun, 21 Jun 2026 22:56:49 +0800 Subject: [PATCH] Reduce UI hot-path diagnostics and input churn --- .../include/ui/assets/fonts/font_utils.h | 11 ++ modules/ui_shared/src/ui/app_runtime.cpp | 13 ++ .../src/ui/i18n/resource_pack_registry.cpp | 33 +++- modules/ui_shared/src/ui/menu/menu_layout.cpp | 54 +++++- .../screens/chat/chat_compose_components.cpp | 58 ++++++ .../ui/screens/chat/chat_ui_controller.cpp | 28 +-- .../walkie_talkie_page_runtime.cpp | 171 +++++++++++++----- .../src/ui/widgets/map/map_viewport.cpp | 28 +++ .../src/ui/widgets/text_candidate_picker.cpp | 12 +- 9 files changed, 332 insertions(+), 76 deletions(-) diff --git a/modules/ui_shared/include/ui/assets/fonts/font_utils.h b/modules/ui_shared/include/ui/assets/fonts/font_utils.h index c85e3cd7..25d33089 100644 --- a/modules/ui_shared/include/ui/assets/fonts/font_utils.h +++ b/modules/ui_shared/include/ui/assets/fonts/font_utils.h @@ -16,6 +16,10 @@ #define lv_font_montserrat_18 lv_font_montserrat_14 #endif +#ifndef UI_I18N_ROUTE_LOG_ENABLE +#define UI_I18N_ROUTE_LOG_ENABLE 0 +#endif + namespace ui::fonts { @@ -179,6 +183,7 @@ inline void log_localized_font_route(FontScope scope, const lv_font_t* base_font, const lv_font_t* resolved_font) { +#if UI_I18N_ROUTE_LOG_ENABLE if (!utf8_has_non_ascii(text)) { return; @@ -203,6 +208,12 @@ inline void log_localized_font_route(FontScope scope, static_cast(fallback), static_cast(resolved_font), text ? text : ""); +#else + (void)scope; + (void)text; + (void)base_font; + (void)resolved_font; +#endif } inline const lv_font_t* localized_font(FontScope scope, diff --git a/modules/ui_shared/src/ui/app_runtime.cpp b/modules/ui_shared/src/ui/app_runtime.cpp index 32b65ac6..195d5394 100644 --- a/modules/ui_shared/src/ui/app_runtime.cpp +++ b/modules/ui_shared/src/ui/app_runtime.cpp @@ -4,6 +4,7 @@ #include "ui/menu/menu_runtime.h" #include +#include #include #if defined(ESP_PLATFORM) @@ -215,6 +216,8 @@ void show_menu_internal() #if defined(ESP_PLATFORM) ESP_LOGI(kTag, "show_menu_internal active=%s", s_active_app ? s_active_app->name() : "(null)"); #endif + std::printf("[UI][Lifecycle] menu visible=1 active=%s scene=menu\n", + s_active_app ? s_active_app->name() : ""); ui_clear_active_app(); set_default_group(menu_g); ui::menu_layout::setMenuVisible(true); @@ -273,6 +276,8 @@ void exit_to_menu_timer_cb(lv_timer_t* timer) parent, static_cast(child_count(parent))); #endif + std::printf("[UI][Lifecycle] app exit name=%s reason=to_menu\n", + app->name()); app->exit(parent); if (parent != nullptr) { @@ -398,6 +403,8 @@ void ui_switch_to_app(AppScreen* app, lv_obj_t* parent) #if defined(ESP_PLATFORM) ESP_LOGI(kTag, "exiting previous app=%s", s_active_app->name()); #endif + std::printf("[UI][Lifecycle] app exit name=%s reason=switch\n", + s_active_app->name()); s_active_app->exit(parent); } if (app) @@ -405,6 +412,10 @@ void ui_switch_to_app(AppScreen* app, lv_obj_t* parent) #if defined(ESP_PLATFORM) ESP_LOGI(kTag, "enter app=%s", app->name()); #endif + std::printf("[UI][Lifecycle] app enter from=%s to=%s parent=%d\n", + s_active_app ? s_active_app->name() : "", + app->name(), + parent ? 1 : 0); app->enter(parent); ui::menu_runtime::setScene(ui::menu_runtime::Scene::App); } @@ -415,6 +426,8 @@ void ui_exit_active_app(lv_obj_t* parent) { if (s_active_app) { + std::printf("[UI][Lifecycle] app exit name=%s reason=explicit\n", + s_active_app->name()); s_active_app->exit(parent); } s_active_app = nullptr; diff --git a/modules/ui_shared/src/ui/i18n/resource_pack_registry.cpp b/modules/ui_shared/src/ui/i18n/resource_pack_registry.cpp index 98e299d2..850d4051 100644 --- a/modules/ui_shared/src/ui/i18n/resource_pack_registry.cpp +++ b/modules/ui_shared/src/ui/i18n/resource_pack_registry.cpp @@ -312,6 +312,7 @@ void log_limited_route(unsigned& counter, const char* text, const char* extra) { +#if UI_I18N_ROUTE_LOG_ENABLE if (counter >= max_count) { return; @@ -323,6 +324,14 @@ void log_limited_route(unsigned& counter, safe_text(via), safe_text(extra), text ? text : ""); +#else + (void)counter; + (void)max_count; + (void)route; + (void)via; + (void)text; + (void)extra; +#endif } const char* usage_name(FontPackUsage usage) @@ -1328,11 +1337,13 @@ bool load_font_pack(FontPackRecord& pack) pack.id.c_str(), pack.source_path.c_str(), static_cast(pack.estimated_ram_bytes)); +#if UI_I18N_ROUTE_LOG_ENABLE std::printf("%s[route] route=pack_load state=begin id=%s builtin=0 source=%s ram=%lu\n", kLogTag, pack.id.c_str(), pack.source_path.c_str(), static_cast(pack.estimated_ram_bytes)); +#endif ScopedFontLoadOverlay overlay(pack); pack.owned_font = lv_binfont_create(pack.source_path.c_str()); if (pack.owned_font == nullptr) @@ -1342,11 +1353,13 @@ bool load_font_pack(FontPackRecord& pack) kLogTag, pack.id.c_str(), pack.source_path.c_str()); +#if UI_I18N_ROUTE_LOG_ENABLE std::printf("%s[route] route=pack_load state=failed id=%s source=%s failures=%u\n", kLogTag, pack.id.c_str(), pack.source_path.c_str(), static_cast(pack.load_failure_count)); +#endif return false; } @@ -1355,11 +1368,13 @@ bool load_font_pack(FontPackRecord& pack) pack.id.c_str(), pack.source_path.c_str(), static_cast(pack.estimated_ram_bytes)); +#if UI_I18N_ROUTE_LOG_ENABLE std::printf("%s[route] route=pack_load state=ready id=%s source=%s font=%p\n", kLogTag, pack.id.c_str(), pack.source_path.c_str(), static_cast(pack.owned_font)); +#endif clear_font_load_failure(pack); return true; #else @@ -1492,10 +1507,12 @@ void rebuild_runtime_font_chains() if (s_ui_font_chain.desc != last_ui_chain || s_content_font_chain.desc != last_content_chain) { +#if UI_I18N_ROUTE_LOG_ENABLE std::printf("%s[route] route=font_chain ui_chain=%s content_chain=%s\n", kLogTag, s_ui_font_chain.desc.empty() ? "" : s_ui_font_chain.desc.c_str(), s_content_font_chain.desc.empty() ? "" : s_content_font_chain.desc.c_str()); +#endif last_ui_chain = s_ui_font_chain.desc; last_content_chain = s_content_font_chain.desc; } @@ -2059,6 +2076,7 @@ bool catalog_external_font_pack(const std::string& pack_dir) usage_name(pack.usage), static_cast(pack.estimated_ram_bytes), pack.source_path.empty() ? "" : pack.source_path.c_str()); +#if UI_I18N_ROUTE_LOG_ENABLE std::printf("%s[route] route=pack_catalog id=%s builtin=%d usage=%s coverage=%lu ram=%lu source=%s\n", kLogTag, pack.id.c_str(), @@ -2067,6 +2085,7 @@ bool catalog_external_font_pack(const std::string& pack_dir) static_cast(pack.coverage.size()), static_cast(pack.estimated_ram_bytes), pack.source_path.empty() ? "" : pack.source_path.c_str()); +#endif s_font_packs.push_back(std::move(pack)); return true; } @@ -3057,7 +3076,6 @@ bool ensure_content_font_for_text(const char* text) return true; } - const bool interesting_text = route_text_is_interesting(text); ScopedForcedFontLoadOverlay forced_overlay; bool changed = false; changed |= ensure_active_content_pack_loaded(); @@ -3103,6 +3121,8 @@ bool ensure_content_font_for_text(const char* text) rebuild_runtime_font_chains(); } +#if UI_I18N_ROUTE_LOG_ENABLE + const bool interesting_text = route_text_is_interesting(text); if (interesting_text && s_content_route_diagnostics < 80) { char extra[192]; @@ -3122,6 +3142,7 @@ bool ensure_content_font_for_text(const char* text) text, extra); } +#endif if (!missing.empty() && s_missing_content_font_diagnostics < kMaxMissingFontDiagnostics) { @@ -3280,6 +3301,7 @@ void set_label_text_raw(lv_obj_t* label, const char* text) if (label) { const char* value = text ? text : ""; +#if UI_I18N_ROUTE_LOG_ENABLE if (route_text_is_interesting(value)) { char extra[160]; @@ -3296,6 +3318,7 @@ void set_label_text_raw(lv_obj_t* label, const char* text) value, extra); } +#endif lv_label_set_text(label, value); ::ui::fonts::apply_localized_font(label, value, lv_obj_get_style_text_font(label, LV_PART_MAIN)); } @@ -3316,6 +3339,7 @@ void set_content_label_text_raw(lv_obj_t* label, const char* text) if (label) { const char* value = text ? text : ""; +#if UI_I18N_ROUTE_LOG_ENABLE if (route_text_is_interesting(value)) { char extra[192]; @@ -3332,6 +3356,7 @@ void set_content_label_text_raw(lv_obj_t* label, const char* text) value, extra); } +#endif lv_label_set_text(label, value); ::ui::fonts::apply_content_font(label, value, lv_obj_get_style_text_font(label, LV_PART_MAIN)); } @@ -3353,6 +3378,7 @@ void set_label_text_fmt(lv_obj_t* label, const char* english_fmt, ...) void log_direct_text_route(const char* owner, const void* label, const char* text) { +#if UI_I18N_ROUTE_LOG_ENABLE if (!route_text_is_interesting(text)) { return; @@ -3373,6 +3399,11 @@ void log_direct_text_route(const char* owner, const void* label, const char* tex "lv_label_set_text", text, extra); +#else + (void)owner; + (void)label; + (void)text; +#endif } } // namespace ui::i18n diff --git a/modules/ui_shared/src/ui/menu/menu_layout.cpp b/modules/ui_shared/src/ui/menu/menu_layout.cpp index 7c9a44c0..81c5bc5f 100644 --- a/modules/ui_shared/src/ui/menu/menu_layout.cpp +++ b/modules/ui_shared/src/ui/menu/menu_layout.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #if defined(ESP_PLATFORM) #include "esp_log.h" @@ -49,6 +50,7 @@ struct MenuAppUi lv_obj_t* icon = nullptr; lv_obj_t* button = nullptr; lv_obj_t* label = nullptr; + std::string rendered_name; }; struct BottomBarChipUi @@ -74,6 +76,8 @@ InitOptions s_init_options{}; uint32_t s_name_change_id = 0; #endif AppScreen* s_pending_app_launch = nullptr; +std::string s_last_refresh_locale; +std::string s_desc_rendered_name; void syncFocusedDescLabel(); @@ -1148,6 +1152,11 @@ void bringContentToFront() void refresh_localized_text() { + const char* locale = ::ui::i18n::current_locale_id(); + const std::string locale_key = locale ? locale : ""; + const bool locale_changed = locale_key != s_last_refresh_locale; + bool any_changed = locale_changed; + for (auto& item : s_menu_apps) { if (item.app == nullptr) @@ -1155,10 +1164,22 @@ void refresh_localized_text() continue; } - item.name = item.app->name(); + const char* current_name = item.app->name(); + item.name = current_name; if (item.label != nullptr) { - ::ui::i18n::set_label_text(item.label, item.name); + const char* existing = lv_label_get_text(item.label); + const bool label_changed = + existing == nullptr || + current_name == nullptr || + std::strcmp(existing, current_name) != 0; + if (locale_changed || item.rendered_name != (current_name ? current_name : "") || + label_changed) + { + ::ui::i18n::set_label_text(item.label, current_name); + item.rendered_name = current_name ? current_name : ""; + any_changed = true; + } } } @@ -1168,11 +1189,29 @@ void refresh_localized_text() const int index = findMenuButtonIndex(focused); if (index >= 0 && static_cast(index) < kMaxMenuApps) { - ::ui::i18n::set_label_text(s_desc_label, s_menu_apps[index].name); + const char* desc = s_menu_apps[index].name ? s_menu_apps[index].name : ""; + const char* existing = lv_label_get_text(s_desc_label); + const bool label_changed = existing == nullptr || std::strcmp(existing, desc) != 0; + if (locale_changed || s_desc_rendered_name != desc || label_changed) + { + ::ui::i18n::set_label_text(s_desc_label, desc); + s_desc_rendered_name = desc; + any_changed = true; + } } } - ui::menu::dashboard::refresh_localized_text(); + if (locale_changed) + { + ui::menu::dashboard::refresh_localized_text(); + } + if (any_changed) + { + std::printf("[UI][Lifecycle] menu localized_refresh locale=%s locale_changed=%d\n", + locale_key.empty() ? "" : locale_key.c_str(), + locale_changed ? 1 : 0); + } + s_last_refresh_locale = locale_key; } void set_bottom_bar_node_text(const char* text) @@ -1199,6 +1238,13 @@ void setMenuVisible(bool visible) { if (s_menu_panel != nullptr) { + const bool was_visible = !lv_obj_has_flag(s_menu_panel, LV_OBJ_FLAG_HIDDEN); + if (was_visible != visible) + { + std::printf("[UI][Lifecycle] menu visible=%d previous=%d\n", + visible ? 1 : 0, + was_visible ? 1 : 0); + } if (visible) { lv_obj_clear_flag(s_menu_panel, LV_OBJ_FLAG_HIDDEN); diff --git a/modules/ui_shared/src/ui/screens/chat/chat_compose_components.cpp b/modules/ui_shared/src/ui/screens/chat/chat_compose_components.cpp index 299cb8bc..ae75c44e 100644 --- a/modules/ui_shared/src/ui/screens/chat/chat_compose_components.cpp +++ b/modules/ui_shared/src/ui/screens/chat/chat_compose_components.cpp @@ -9,8 +9,10 @@ #include "ui/localization.h" #include "ui/ui_common.h" #include "ui/widgets/ime/ime_widget.h" +#include "ui/widgets/text_candidate_picker.h" #include // snprintf +#include #include #ifndef CHAT_COMPOSE_LOG_ENABLE @@ -48,6 +50,8 @@ struct ChatComposeScreen::Impl ActionContext send_ctx; ActionContext position_ctx; ActionContext cancel_ctx; + lv_obj_t* sym_btn = nullptr; + lv_obj_t* emoji_btn = nullptr; }; static void set_btn_label_white(lv_obj_t* btn) @@ -328,6 +332,60 @@ void ChatComposeScreen::setBackCallback(void (*cb)(void*), void* user_data) void ChatComposeScreen::attachImeWidget(::ui::widgets::ImeWidget* widget) { ime_widget_ = widget; + if (!impl_ || !widget || !impl_->w.textarea) + { + return; + } + + lv_obj_t* ime_toggle = widget->toggle_btn(); + lv_obj_t* toolbar = ime_toggle && lv_obj_is_valid(ime_toggle) ? lv_obj_get_parent(ime_toggle) : nullptr; + if (!toolbar || !lv_obj_is_valid(toolbar)) + { + impl_->sym_btn = nullptr; + impl_->emoji_btn = nullptr; + return; + } + + lv_group_t* group = lv_group_get_default(); + const auto ensure_candidate_button = + [&](lv_obj_t*& button, ::ui::widgets::text_candidates::CandidateSet set, std::uint32_t index) + { + if (button && (!lv_obj_is_valid(button) || lv_obj_get_parent(button) != toolbar)) + { + button = nullptr; + } + if (!button) + { + button = ::ui::widgets::add_text_candidate_button( + toolbar, + impl_->w.textarea, + set, + group, + ime_toggle); + } + else + { + lv_obj_set_user_data(button, impl_->w.textarea); + if (group) + { + lv_group_remove_obj(button); + lv_group_add_obj(group, button); + } + } + if (button) + { + lv_obj_move_to_index(button, index); + } + }; + + ensure_candidate_button( + impl_->sym_btn, + ::ui::widgets::text_candidates::CandidateSet::Symbols, + 1); + ensure_candidate_button( + impl_->emoji_btn, + ::ui::widgets::text_candidates::CandidateSet::Emoji, + 2); } lv_obj_t* ChatComposeScreen::getTextarea() const diff --git a/modules/ui_shared/src/ui/screens/chat/chat_ui_controller.cpp b/modules/ui_shared/src/ui/screens/chat/chat_ui_controller.cpp index 2cf7f067..bc232a2b 100644 --- a/modules/ui_shared/src/ui/screens/chat/chat_ui_controller.cpp +++ b/modules/ui_shared/src/ui/screens/chat/chat_ui_controller.cpp @@ -19,7 +19,6 @@ #include "ui/screens/chat/chat_team_workflow.h" #include "ui/ui_common.h" #include "ui/widgets/ime/ime_widget.h" -#include "ui/widgets/text_candidate_picker.h" #include "ui/runtime/ui_feedback.h" #include "ui_chat_runtime/chat_delivery_action_port_adapter.h" #include "ui_lvgl_ux_packs/common/key_verification_modal_renderer.h" @@ -746,32 +745,7 @@ void UiController::switchToCompose(chat::ConversationId conv) } compose_ime_->init(compose_content, compose_textarea); compose_->attachImeWidget(compose_ime_.get()); - lv_group_t* group = lv_group_get_default(); - lv_obj_t* ime_toggle = compose_ime_->toggle_btn(); - if (lv_obj_t* toolbar = ime_toggle ? lv_obj_get_parent(ime_toggle) : nullptr) - { - lv_obj_t* sym_btn = ::ui::widgets::add_text_candidate_button( - toolbar, - compose_textarea, - ::ui::widgets::text_candidates::CandidateSet::Symbols, - group, - ime_toggle); - lv_obj_t* emoji_btn = ::ui::widgets::add_text_candidate_button( - toolbar, - compose_textarea, - ::ui::widgets::text_candidates::CandidateSet::Emoji, - group, - ime_toggle); - if (sym_btn) - { - lv_obj_move_to_index(sym_btn, 1); - } - if (emoji_btn) - { - lv_obj_move_to_index(emoji_btn, 2); - } - } - if (group) + if (lv_group_t* group = lv_group_get_default()) { lv_group_add_obj(group, compose_ime_->focus_obj()); } diff --git a/modules/ui_shared/src/ui/screens/walkie_talkie/walkie_talkie_page_runtime.cpp b/modules/ui_shared/src/ui/screens/walkie_talkie/walkie_talkie_page_runtime.cpp index 80670059..1dc38f39 100644 --- a/modules/ui_shared/src/ui/screens/walkie_talkie/walkie_talkie_page_runtime.cpp +++ b/modules/ui_shared/src/ui/screens/walkie_talkie/walkie_talkie_page_runtime.cpp @@ -33,6 +33,7 @@ lv_obj_t* s_mod_label = nullptr; lv_obj_t* s_mode_label = nullptr; lv_obj_t* s_left_fill = nullptr; lv_obj_t* s_right_fill = nullptr; +lv_obj_t* s_monitor_row = nullptr; lv_obj_t* s_monitor_switch = nullptr; lv_obj_t* s_monitor_label = nullptr; lv_obj_t* s_volume_bar = nullptr; @@ -45,6 +46,55 @@ constexpr lv_coord_t kVuWidth = 12; constexpr lv_coord_t kVuHeight = 120; constexpr lv_coord_t kVolumeBarWidth = 220; constexpr lv_coord_t kVolumeBarHeight = 12; +constexpr lv_coord_t kMonitorRowHeight = 36; +constexpr lv_coord_t kMonitorSwitchWidth = 46; +constexpr lv_coord_t kMonitorSwitchHeight = 24; + +void set_monitor_visual_state(bool enabled) +{ + if (s_monitor_row) + { + if (enabled) + { + lv_obj_add_state(s_monitor_row, LV_STATE_CHECKED); + } + else + { + lv_obj_clear_state(s_monitor_row, LV_STATE_CHECKED); + } + } + if (s_monitor_switch) + { + if (enabled) + { + lv_obj_add_state(s_monitor_switch, LV_STATE_CHECKED); + } + else + { + lv_obj_clear_state(s_monitor_switch, LV_STATE_CHECKED); + } + } + if (s_monitor_label) + { + ::ui::i18n::set_label_text(s_monitor_label, enabled ? "Monitor On" : "Monitor Off"); + } +} + +void apply_monitor_enabled_from_ui(bool enabled) +{ + if (!platform::ui::walkie::set_monitor_enabled(enabled)) + { + const bool actual = platform::ui::walkie::get_status().monitor_enabled; + set_monitor_visual_state(actual); + const char* detail = platform::ui::walkie::last_error(); + ::ui::feedback::show_notice((detail && detail[0] != '\0') ? detail : "Monitor unavailable", + 2500); + return; + } + + set_monitor_visual_state(platform::ui::walkie::get_status().monitor_enabled); + ::ui::status::force_update(); +} void request_exit() { @@ -79,14 +129,56 @@ void monitor_switch_event_cb(lv_event_t* e) } const bool enabled = lv_obj_has_state(s_monitor_switch, LV_STATE_CHECKED); - if (!platform::ui::walkie::set_monitor_enabled(enabled)) + apply_monitor_enabled_from_ui(enabled); +} + +void monitor_row_event_cb(lv_event_t* e) +{ + if (!s_monitor_row) { - lv_obj_clear_state(s_monitor_switch, LV_STATE_CHECKED); - const char* detail = platform::ui::walkie::last_error(); - ::ui::feedback::show_notice((detail && detail[0] != '\0') ? detail : "Monitor unavailable", - 2500); + return; } - ::ui::status::force_update(); + + const lv_event_code_t code = lv_event_get_code(e); + if (code == LV_EVENT_CLICKED) + { + apply_monitor_enabled_from_ui(!lv_obj_has_state(s_monitor_row, LV_STATE_CHECKED)); + return; + } + if (code != LV_EVENT_KEY) + { + return; + } + + const uint32_t key = lv_event_get_key(e); + if (key == LV_KEY_ENTER || key == ' ') + { + apply_monitor_enabled_from_ui(!lv_obj_has_state(s_monitor_row, LV_STATE_CHECKED)); + lv_event_stop_processing(e); + return; + } + if (key == LV_KEY_BACKSPACE) + { + on_back(nullptr); + lv_event_stop_processing(e); + } +} + +void style_monitor_row(lv_obj_t* row) +{ + if (!row) + { + return; + } + lv_obj_set_style_bg_color(row, lv_color_hex(0xFFF7E9), LV_PART_MAIN); + lv_obj_set_style_bg_color(row, lv_color_hex(0xFFE5B5), LV_PART_MAIN | LV_STATE_CHECKED); + lv_obj_set_style_bg_opa(row, LV_OPA_COVER, LV_PART_MAIN); + lv_obj_set_style_border_width(row, 1, LV_PART_MAIN); + lv_obj_set_style_border_color(row, lv_color_hex(0xD9B06A), LV_PART_MAIN); + lv_obj_set_style_radius(row, 8, LV_PART_MAIN); + lv_obj_set_style_outline_width(row, 2, LV_STATE_FOCUSED); + lv_obj_set_style_outline_color(row, lv_color_hex(0xC98118), LV_STATE_FOCUSED); + lv_obj_set_style_outline_pad(row, 2, LV_STATE_FOCUSED); } void update_vu(lv_obj_t* fill, uint8_t level) @@ -123,19 +215,7 @@ void refresh_cb(lv_timer_t*) } if (s_monitor_switch) { - const bool checked = lv_obj_has_state(s_monitor_switch, LV_STATE_CHECKED); - if (st.monitor_enabled && !checked) - { - lv_obj_add_state(s_monitor_switch, LV_STATE_CHECKED); - } - else if (!st.monitor_enabled && checked) - { - lv_obj_clear_state(s_monitor_switch, LV_STATE_CHECKED); - } - } - if (s_monitor_label) - { - ::ui::i18n::set_label_text(s_monitor_label, st.monitor_enabled ? "Monitor On" : "Monitor Off"); + set_monitor_visual_state(st.monitor_enabled); } update_vu(s_left_fill, st.tx ? st.tx_level : st.rx_level); update_vu(s_right_fill, st.tx ? st.tx_level : st.rx_level); @@ -286,27 +366,41 @@ void enter(const shell::Host* host, lv_obj_t* parent) lv_obj_set_style_text_font(s_mode_label, &lv_font_montserrat_18, 0); lv_obj_set_style_text_align(s_mode_label, LV_TEXT_ALIGN_CENTER, 0); - lv_obj_t* monitor_row = lv_obj_create(stack); - lv_obj_set_size(monitor_row, LV_SIZE_CONTENT, 30); - lv_obj_set_style_bg_opa(monitor_row, LV_OPA_TRANSP, 0); - lv_obj_set_style_border_width(monitor_row, 0, 0); - lv_obj_set_style_pad_all(monitor_row, 0, 0); - lv_obj_set_style_pad_column(monitor_row, 8, 0); - lv_obj_set_flex_flow(monitor_row, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(monitor_row, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); - lv_obj_clear_flag(monitor_row, LV_OBJ_FLAG_SCROLLABLE); + s_monitor_row = lv_btn_create(stack); + lv_obj_set_size(s_monitor_row, kVolumeBarWidth, kMonitorRowHeight); + lv_obj_set_style_pad_left(s_monitor_row, 10, LV_PART_MAIN); + lv_obj_set_style_pad_right(s_monitor_row, 8, LV_PART_MAIN); + lv_obj_set_style_pad_top(s_monitor_row, 0, LV_PART_MAIN); + lv_obj_set_style_pad_bottom(s_monitor_row, 0, LV_PART_MAIN); + lv_obj_set_style_pad_column(s_monitor_row, 8, LV_PART_MAIN); + lv_obj_set_flex_flow(s_monitor_row, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(s_monitor_row, + LV_FLEX_ALIGN_SPACE_BETWEEN, + LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER); + lv_obj_clear_flag(s_monitor_row, LV_OBJ_FLAG_SCROLLABLE); + lv_obj_add_event_cb(s_monitor_row, monitor_row_event_cb, LV_EVENT_CLICKED, nullptr); + lv_obj_add_event_cb(s_monitor_row, monitor_row_event_cb, LV_EVENT_KEY, nullptr); + style_monitor_row(s_monitor_row); - s_monitor_label = lv_label_create(monitor_row); + s_monitor_label = lv_label_create(s_monitor_row); ::ui::i18n::set_label_text(s_monitor_label, "Monitor Off"); lv_obj_set_style_text_font(s_monitor_label, &lv_font_montserrat_14, 0); + lv_obj_set_style_text_color(s_monitor_label, lv_color_hex(0x3A2A1A), 0); + lv_obj_set_flex_grow(s_monitor_label, 1); + lv_label_set_long_mode(s_monitor_label, LV_LABEL_LONG_CLIP); - s_monitor_switch = lv_switch_create(monitor_row); - lv_obj_set_size(s_monitor_switch, 44, 24); + s_monitor_switch = lv_switch_create(s_monitor_row); + lv_obj_set_size(s_monitor_switch, kMonitorSwitchWidth, kMonitorSwitchHeight); + lv_obj_set_style_outline_width(s_monitor_switch, 2, LV_STATE_FOCUSED); + lv_obj_set_style_outline_color(s_monitor_switch, lv_color_hex(0xC98118), LV_STATE_FOCUSED); + lv_obj_set_style_outline_pad(s_monitor_switch, 2, LV_STATE_FOCUSED); + lv_obj_add_event_cb(s_monitor_switch, root_key_event_cb, LV_EVENT_KEY, nullptr); lv_obj_add_event_cb(s_monitor_switch, monitor_switch_event_cb, LV_EVENT_VALUE_CHANGED, nullptr); - if (app_g && s_monitor_switch) + if (app_g && s_monitor_row) { - lv_group_add_obj(app_g, s_monitor_switch); + lv_group_add_obj(app_g, s_monitor_row); } lv_obj_t* vu_left = lv_obj_create(content); @@ -408,15 +502,7 @@ void enter(const shell::Host* host, lv_obj_t* parent) set_freq_text(st.freq_mhz); if (s_monitor_switch) { - const bool checked = lv_obj_has_state(s_monitor_switch, LV_STATE_CHECKED); - if (st.monitor_enabled && !checked) - { - lv_obj_add_state(s_monitor_switch, LV_STATE_CHECKED); - } - else if (!st.monitor_enabled && checked) - { - lv_obj_clear_state(s_monitor_switch, LV_STATE_CHECKED); - } + set_monitor_visual_state(st.monitor_enabled); } if (!s_timer) @@ -446,6 +532,7 @@ void exit(lv_obj_t* parent) s_mode_label = nullptr; s_left_fill = nullptr; s_right_fill = nullptr; + s_monitor_row = nullptr; s_monitor_switch = nullptr; s_monitor_label = nullptr; s_volume_bar = nullptr; diff --git a/modules/ui_shared/src/ui/widgets/map/map_viewport.cpp b/modules/ui_shared/src/ui/widgets/map/map_viewport.cpp index 6dcfec6a..2ab9eee8 100644 --- a/modules/ui_shared/src/ui/widgets/map/map_viewport.cpp +++ b/modules/ui_shared/src/ui/widgets/map/map_viewport.cpp @@ -31,6 +31,7 @@ struct RuntimeImpl TileContext tile_ctx{}; lv_timer_t* loader_timer = nullptr; uint32_t loader_interval_ms = 50; + uint32_t last_loader_active_log_ms = 0; bool alive = false; bool has_map_data = false; bool has_visible_map_data = false; @@ -634,6 +635,21 @@ void loader_timer_cb(lv_timer_t* timer) return; } + const uint32_t now_ms = lv_tick_get(); + if (impl->last_loader_active_log_ms == 0 || + now_ms - impl->last_loader_active_log_ms >= 5000U) + { + impl->last_loader_active_log_ms = now_ms; + std::printf("[UI][Lifecycle] map tile_loader active root=%p interval_ms=%lu focus=%.6f,%.6f zoom=%d visible=%d data=%d\n", + impl->widgets.root, + static_cast(impl->loader_interval_ms), + impl->model.focus_point.lat, + impl->model.focus_point.lon, + impl->model.zoom, + impl->has_visible_map_data ? 1 : 0, + impl->has_map_data ? 1 : 0); + } + tile_loader_step(impl->tile_ctx); uint8_t missing_source = 0; @@ -747,7 +763,13 @@ Widgets create(Runtime& runtime, lv_obj_t* parent, uint32_t loader_interval_ms) &impl->has_visible_map_data); impl->loader_timer = lv_timer_create(loader_timer_cb, loader_interval_ms, impl); + impl->loader_interval_ms = loader_interval_ms; impl->alive = true; + std::printf("[UI][Lifecycle] map runtime create root=%p tile=%p loader=%p interval_ms=%lu\n", + impl->widgets.root, + impl->widgets.tile_layer, + impl->loader_timer, + static_cast(loader_interval_ms)); MAP_VIEWPORT_LOG("create root=%p tile=%p overlay=%p loader_timer=%p interval=%u\n", impl->widgets.root, @@ -770,6 +792,12 @@ void destroy(Runtime& runtime) impl->widgets.root, impl->loader_timer, impl->alive ? 1 : 0); + std::printf("[UI][Lifecycle] map runtime destroy root=%p loader=%p alive=%d visible=%d data=%d\n", + impl->widgets.root, + impl->loader_timer, + impl->alive ? 1 : 0, + impl->has_visible_map_data ? 1 : 0, + impl->has_map_data ? 1 : 0); impl->alive = false; if (impl->loader_timer) diff --git a/modules/ui_shared/src/ui/widgets/text_candidate_picker.cpp b/modules/ui_shared/src/ui/widgets/text_candidate_picker.cpp index dc470e40..76dda4fc 100644 --- a/modules/ui_shared/src/ui/widgets/text_candidate_picker.cpp +++ b/modules/ui_shared/src/ui/widgets/text_candidate_picker.cpp @@ -17,7 +17,7 @@ namespace constexpr std::size_t kMaxCandidateButtons = text_candidates::kMaxBuiltinTextCandidates; constexpr lv_coord_t kHeaderHeightPx = 30; -constexpr lv_coord_t kHeaderCloseButtonHeightPx = 28; +constexpr lv_coord_t kHeaderCloseButtonHeightPx = 26; constexpr lv_coord_t kPickerOuterPaddingPx = 6; constexpr lv_coord_t kGridGapPx = 6; @@ -287,6 +287,8 @@ void set_button_label(lv_obj_t* button, label = lv_label_create(button); } ::ui::i18n::set_content_label_text_raw(label, text ? text : ""); + lv_obj_set_width(label, LV_PCT(100)); + lv_label_set_long_mode(label, LV_LABEL_LONG_CLIP); lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN); lv_obj_set_style_text_color(label, color, LV_PART_MAIN); lv_obj_set_style_text_font(label, @@ -620,14 +622,17 @@ void open_text_candidate_picker(lv_obj_t* textarea, s_picker.title_label = title_label; const char* title = text_candidates::title(set); ::ui::i18n::set_label_text_raw(title_label, title); + lv_obj_set_height(title_label, LV_PCT(100)); + lv_label_set_long_mode(title_label, LV_LABEL_LONG_CLIP); lv_obj_set_style_text_font(title_label, ::ui::page_profile::resolve_caption_font(), LV_PART_MAIN); lv_obj_set_style_text_color(title_label, lv_color_hex(0x3A2A1A), LV_PART_MAIN); lv_obj_t* hint_label = lv_label_create(header); - ::ui::i18n::set_label_text_raw(hint_label, "WASD Q close E pick"); + ::ui::i18n::set_label_text_raw(hint_label, "WASD move Q close E pick"); lv_obj_set_flex_grow(hint_label, 1); + lv_obj_set_height(hint_label, LV_PCT(100)); lv_label_set_long_mode(hint_label, LV_LABEL_LONG_CLIP); lv_obj_set_style_text_align(hint_label, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN); lv_obj_set_style_text_font(hint_label, @@ -641,12 +646,14 @@ void open_text_candidate_picker(lv_obj_t* textarea, kHeaderCloseButtonHeightPx); style_toolbar_button(close_btn); set_button_label(close_btn, "Close"); + lv_obj_clear_flag(close_btn, LV_OBJ_FLAG_SCROLLABLE); lv_obj_add_event_cb(close_btn, on_close_clicked, LV_EVENT_CLICKED, nullptr); lv_obj_add_event_cb(close_btn, on_picker_key, LV_EVENT_KEY, nullptr); lv_obj_t* grid = lv_obj_create(s_picker.root); lv_obj_set_width(grid, LV_PCT(100)); lv_obj_set_flex_grow(grid, 1); + lv_obj_set_style_min_height(grid, 0, LV_PART_MAIN); lv_obj_set_flex_flow(grid, LV_FLEX_FLOW_ROW_WRAP); lv_obj_set_flex_align(grid, LV_FLEX_ALIGN_START, @@ -745,6 +752,7 @@ lv_obj_t* add_text_candidate_button(lv_obj_t* toolbar, lv_obj_t* button = lv_btn_create(toolbar); lv_obj_set_size(button, width, height); + lv_obj_clear_flag(button, LV_OBJ_FLAG_SCROLLABLE); lv_obj_set_user_data(button, textarea); style_toolbar_button(button, reference_button); set_button_label(button,