Reduce UI hot-path diagnostics and input churn

This commit is contained in:
liu weikai committed 2026-06-21 22:56:49 +08:00
1 parent 50c2c388ea
commit 63549516ec
9 files changed
+332 -76

No files matched your search

+13
View File
@@ -4,6 +4,7 @@
#include "ui/menu/menu_runtime.h"
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#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() : "<none>");
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<unsigned long>(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() : "<none>",
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;
@@ -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<unsigned long>(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<unsigned long>(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<unsigned>(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<unsigned long>(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<void*>(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() ? "<none>" : s_ui_font_chain.desc.c_str(),
s_content_font_chain.desc.empty() ? "<none>" : 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<unsigned long>(pack.estimated_ram_bytes),
pack.source_path.empty() ? "<none>" : 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<unsigned long>(pack.coverage.size()),
static_cast<unsigned long>(pack.estimated_ram_bytes),
pack.source_path.empty() ? "<none>" : 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
+50 -4
View File
@@ -4,6 +4,7 @@
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <string>
#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<size_t>(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() ? "<none>" : 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);
@@ -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 <cstdio> // snprintf
#include <cstdint>
#include <cstring>
#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
@@ -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());
}
@@ -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;
@@ -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<unsigned long>(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<unsigned long>(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)
@@ -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,