mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-10-23 13:33:03 +00:00
Reduce UI hot-path diagnostics and input churn
This commit is contained in:
1 parent
50c2c388ea
commit
63549516ec
9 files changed
+332
-76
No files matched your search
@@ -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
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user