mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-10-24 22:13:27 +00:00
Fix Pager navigation, widget controls and contextual help
This commit is contained in:
1 parent
ffc74d7130
commit
846e99afa6
37 files changed
+980
-56
No files matched your search
@@ -3,11 +3,14 @@
|
||||
#include "lvgl.h"
|
||||
#include "platform/ui/walkie_runtime.h"
|
||||
#include "ui/app_runtime.h"
|
||||
#include "ui/input/pager_navigation.h"
|
||||
#include "ui/localization.h"
|
||||
#include "ui/menu/menu_dashboard.h"
|
||||
#include "ui/menu/menu_runtime.h"
|
||||
#include "ui/page/page_profile.h"
|
||||
#include "ui/screens/calculator/calculator_page_runtime.h"
|
||||
#include "ui/widgets/ime/ime_widget.h"
|
||||
#include "ui/widgets/top_bar.h"
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
@@ -22,6 +25,10 @@ lv_group_t* menu_g = nullptr;
|
||||
lv_obj_t* main_screen = nullptr;
|
||||
static lv_display_t* display;
|
||||
static lv_indev_t* pointer_device;
|
||||
static lv_indev_t* encoder_device;
|
||||
static lv_indev_t* keyboard_device;
|
||||
static int encoder_delta = 0, hardware_key = 0;
|
||||
static bool encoder_pressed = false, hardware_key_pressed = false;
|
||||
static lv_group_t* bound_input_group;
|
||||
#if defined(TRAIL_MATE_ESP_BOARD_T_DISPLAY_P4)
|
||||
#if defined(CONFIG_TRAIL_MATE_T_DISPLAY_P4_PANEL_HI8561)
|
||||
@@ -61,13 +68,30 @@ void set_default_group(lv_group_t* group)
|
||||
// Native keys/encoder are dispatched directly, independently of touch.
|
||||
// A pointer indev cannot retain a keypad/encoder group in LVGL.
|
||||
bound_input_group = group;
|
||||
if (encoder_device) lv_indev_set_group(encoder_device, group);
|
||||
if (keyboard_device) lv_indev_set_group(keyboard_device, group);
|
||||
lv_group_set_default(group);
|
||||
}
|
||||
|
||||
static lv_group_t* input_group()
|
||||
{
|
||||
// Pages may clear LVGL's auto-add default after binding their input group.
|
||||
return bound_input_group ? bound_input_group : lv_group_get_default();
|
||||
// The indev also clears a deleted group, unlike a borrowed host pointer.
|
||||
return keyboard_device ? lv_indev_get_group(keyboard_device) : bound_input_group;
|
||||
}
|
||||
|
||||
static lv_obj_t* focused_scroll_target()
|
||||
{
|
||||
auto* group = input_group();
|
||||
auto* focused = group ? lv_group_get_focused(group) : nullptr;
|
||||
if (!focused) return nullptr;
|
||||
if (lv_obj_has_flag(focused, LV_OBJ_FLAG_SCROLLABLE)) return focused;
|
||||
for (uint32_t i = 0; i < lv_obj_get_child_count(focused); ++i)
|
||||
{
|
||||
auto* child = lv_obj_get_child(focused, i);
|
||||
if (lv_obj_has_flag(child, LV_OBJ_FLAG_SCROLLABLE)) return child;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void ui_request_exit_to_menu()
|
||||
{
|
||||
@@ -106,8 +130,8 @@ static void collect_labels(lv_obj_t* obj)
|
||||
lv_area_t area;
|
||||
lv_obj_get_coords(obj, &area);
|
||||
char data[220];
|
||||
std::snprintf(data, sizeof(data), "%s{\"slider\":true,\"x\":%d,\"y\":%d,\"w\":%d,\"h\":%d,\"value\":%d}",
|
||||
snapshot.size() > 1 ? "," : "", static_cast<int>(area.x1), static_cast<int>(area.y1),
|
||||
std::snprintf(data, sizeof(data), "%s{\"id\":%u,\"slider\":true,\"focused\":%s,\"x\":%d,\"y\":%d,\"w\":%d,\"h\":%d,\"value\":%d}",
|
||||
snapshot.size() > 1 ? "," : "", static_cast<unsigned>(reinterpret_cast<uintptr_t>(obj)), lv_obj_has_state(obj, LV_STATE_FOCUS_KEY) ? "true" : "false", static_cast<int>(area.x1), static_cast<int>(area.y1),
|
||||
static_cast<int>(lv_area_get_width(&area)), static_cast<int>(lv_area_get_height(&area)), static_cast<int>(lv_slider_get_value(obj)));
|
||||
snapshot += data;
|
||||
}
|
||||
@@ -129,9 +153,9 @@ static void collect_labels(lv_obj_t* obj)
|
||||
lv_area_t area;
|
||||
lv_obj_get_coords(button ? parent : obj, &area);
|
||||
char data[200];
|
||||
std::snprintf(data, sizeof(data), "%s{\"id\":%u,\"button\":%s,\"x\":%d,\"y\":%d,\"w\":%d,\"h\":%d,\"text\":",
|
||||
std::snprintf(data, sizeof(data), "%s{\"id\":%u,\"button\":%s,\"focused\":%s,\"x\":%d,\"y\":%d,\"w\":%d,\"h\":%d,\"text\":",
|
||||
snapshot.size() > 1 ? "," : "", static_cast<unsigned>(reinterpret_cast<uintptr_t>(parent)),
|
||||
button ? "true" : "false", static_cast<int>(area.x1), static_cast<int>(area.y1),
|
||||
button ? "true" : "false", button && lv_obj_has_state(parent, LV_STATE_FOCUS_KEY) ? "true" : "false", static_cast<int>(area.x1), static_cast<int>(area.y1),
|
||||
static_cast<int>(lv_area_get_width(&area)), static_cast<int>(lv_area_get_height(&area)));
|
||||
snapshot += data;
|
||||
append_json_string(text);
|
||||
@@ -163,6 +187,21 @@ extern "C"
|
||||
{
|
||||
data->point.x = pointer_x; data->point.y = pointer_y;
|
||||
data->state = pointer_down ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; });
|
||||
encoder_device = lv_indev_create();
|
||||
lv_indev_set_type(encoder_device, LV_INDEV_TYPE_ENCODER);
|
||||
lv_indev_set_mode(encoder_device, LV_INDEV_MODE_EVENT);
|
||||
lv_indev_set_read_cb(encoder_device, [](lv_indev_t*, lv_indev_data_t* data)
|
||||
{
|
||||
data->enc_diff = encoder_delta;
|
||||
encoder_delta = 0;
|
||||
data->state = encoder_pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; });
|
||||
keyboard_device = lv_indev_create();
|
||||
lv_indev_set_type(keyboard_device, LV_INDEV_TYPE_KEYPAD);
|
||||
lv_indev_set_mode(keyboard_device, LV_INDEV_MODE_EVENT);
|
||||
lv_indev_set_read_cb(keyboard_device, [](lv_indev_t*, lv_indev_data_t* data)
|
||||
{
|
||||
data->key = hardware_key;
|
||||
data->state = hardware_key_pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; });
|
||||
app_g = lv_group_create();
|
||||
set_default_group(app_g);
|
||||
ui::i18n::set_locale("en", false);
|
||||
@@ -185,6 +224,11 @@ extern "C"
|
||||
{
|
||||
lv_tick_inc(elapsed);
|
||||
lv_timer_handler();
|
||||
// Hardware input readers keep polling while idle. In event-mode
|
||||
// previews, drain group-reset/release state between deliberate events
|
||||
// too, so a group's first real detent is not spent clearing its reset.
|
||||
if (encoder_device && !encoder_pressed) lv_indev_read(encoder_device);
|
||||
if (keyboard_device && !hardware_key_pressed) lv_indev_read(keyboard_device);
|
||||
ui::i18n::on_lvgl_frame_completed();
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE void native_pointer(int x, int y, int down)
|
||||
@@ -202,6 +246,7 @@ extern "C"
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE void native_key(int key)
|
||||
{
|
||||
if (ui::input::pager::handle_key(key, true, input_group())) return;
|
||||
if (preview_current_page() == 10 && ui::menu::dashboard::handleKey(key)) return;
|
||||
if (key == LV_KEY_ENTER && preview_current_page() == 10)
|
||||
{
|
||||
@@ -241,6 +286,7 @@ extern "C"
|
||||
}
|
||||
}
|
||||
if (pressed) native_key(key);
|
||||
else ui::input::pager::handle_key(key, false, input_group());
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE void native_encoder(int delta)
|
||||
{
|
||||
@@ -251,6 +297,81 @@ extern "C"
|
||||
else lv_group_focus_next(group);
|
||||
}
|
||||
}
|
||||
// Exercise LVGL's actual encoder edit/navigation and keypad press/release
|
||||
// state machines. The older helpers above intentionally send events directly.
|
||||
EMSCRIPTEN_KEEPALIVE void native_encoder_input(int delta, int pressed)
|
||||
{
|
||||
ui::input::pager::prepare_group(input_group());
|
||||
encoder_delta = ui::menu::dashboard::handleEncoder(delta) ? 0 : delta;
|
||||
encoder_pressed = ui::menu::dashboard::handleEncoderButton(pressed != 0) ? false : pressed != 0;
|
||||
lv_indev_read(encoder_device);
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE void native_keyboard_input(int key, int pressed)
|
||||
{
|
||||
if (key == '\r') key = LV_KEY_ENTER;
|
||||
if (pressed) ui::input::pager::prepare_group(input_group());
|
||||
if (ui::input::pager::handle_key(key, pressed != 0, input_group())) return;
|
||||
if (key < 128 && (ui::menu_runtime::handleWalkieKey(static_cast<char>(key), pressed) ||
|
||||
ui::menu_runtime::handleShortcutKey(static_cast<char>(key), pressed))) return;
|
||||
hardware_key = key;
|
||||
hardware_key_pressed = pressed != 0;
|
||||
lv_indev_read(keyboard_device);
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE uint32_t native_input_focus()
|
||||
{
|
||||
auto* group = input_group();
|
||||
return group ? static_cast<uint32_t>(reinterpret_cast<uintptr_t>(lv_group_get_focused(group))) : 0;
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE int native_input_editing()
|
||||
{
|
||||
auto* group = input_group();
|
||||
return group && lv_group_get_editing(group);
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE int native_input_is_editor()
|
||||
{
|
||||
auto* group = input_group();
|
||||
for (auto* object = group ? lv_group_get_focused(group) : nullptr; object; object = lv_obj_get_parent(object))
|
||||
if (lv_obj_check_type(object, &lv_textarea_class)) return 1;
|
||||
return 0;
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE int native_ime_proxy_protected()
|
||||
{
|
||||
auto* previous = input_group();
|
||||
auto* group = lv_group_create();
|
||||
set_default_group(group);
|
||||
auto* root = lv_obj_create(lv_screen_active());
|
||||
ui::widgets::TopBar bar;
|
||||
ui::widgets::top_bar_init(bar, root);
|
||||
auto* editor = lv_textarea_create(root);
|
||||
ui::widgets::ImeWidget ime;
|
||||
ime.init(root, editor);
|
||||
lv_group_add_obj(group, ime.focus_obj());
|
||||
lv_group_focus_obj(ime.focus_obj());
|
||||
lv_group_set_editing(group, true);
|
||||
ui::input::pager::prepare_group(group);
|
||||
const bool protected_focus = lv_group_get_focused(group) == ime.focus_obj();
|
||||
const bool protected_key = !ui::input::pager::handle_key('h', true, group);
|
||||
ime.detach();
|
||||
lv_obj_delete(root);
|
||||
set_default_group(previous);
|
||||
lv_group_delete(group);
|
||||
return protected_focus && protected_key;
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE unsigned native_input_count()
|
||||
{
|
||||
auto* group = input_group();
|
||||
return group ? lv_group_get_obj_count(group) : 0;
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE int native_input_scroll_y()
|
||||
{
|
||||
auto* target = focused_scroll_target();
|
||||
return target ? lv_obj_get_scroll_y(target) : 0;
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE int native_input_scroll_bottom()
|
||||
{
|
||||
auto* target = focused_scroll_target();
|
||||
return target ? lv_obj_get_scroll_bottom(target) : 0;
|
||||
}
|
||||
EMSCRIPTEN_KEEPALIVE const char* native_snapshot()
|
||||
{
|
||||
snapshot = "[";
|
||||
|
||||
Reference in new issue
Block a user