Add PSRAM-backed menu widgets and unify adaptive launchers

This commit is contained in:
liu weikai committed 2026-10-16 09:43:54 +08:00
1 parent 1bb42567a8
commit 83b0f93fb9
43 files changed
+1394 -493

No files matched your search

@@ -1,90 +1,31 @@
#pragma once
#include "ui/menu/dashboard/dashboard_style.h"
#include <string>
#include "ui/menu/dashboard/dashboard_widget.h"
#include <array>
namespace ui::menu::dashboard
{
struct MeshWidgetUi
{
DashboardCardChrome chrome{};
lv_obj_t* orbit_outer = nullptr;
lv_obj_t* orbit_mid = nullptr;
lv_obj_t* orbit_core = nullptr;
lv_obj_t* hero_value = nullptr;
lv_obj_t* hero_caption = nullptr;
lv_obj_t* stat_tiles[3]{};
lv_obj_t* stat_values[3]{};
lv_obj_t* stat_captions[3]{};
lv_obj_t* signal_bar_wrap = nullptr;
lv_obj_t* signal_bars[12]{};
lv_obj_t* footer_label = nullptr;
};
struct GpsWidgetUi
{
DashboardCardChrome chrome{};
lv_obj_t* radar = nullptr;
lv_obj_t* radar_rings[3]{};
lv_obj_t* radar_cross_h = nullptr;
lv_obj_t* radar_cross_v = nullptr;
lv_obj_t* sat_dots[8]{};
lv_obj_t* sat_labels[8]{};
lv_obj_t* sat_use_tags[8]{};
lv_obj_t* stat_tiles[3]{};
lv_obj_t* stat_values[3]{};
lv_obj_t* stat_captions[3]{};
lv_obj_t* footer_label = nullptr;
};
struct RecentWidgetUi
{
DashboardCardChrome chrome{};
lv_obj_t* rows[2]{};
lv_obj_t* row_dots[2]{};
lv_obj_t* row_names[2]{};
lv_obj_t* row_previews[2]{};
std::string row_name_text[2];
std::string row_preview_text[2];
lv_obj_t* row_badges[2]{};
lv_obj_t* row_badge_labels[2]{};
lv_obj_t* scan_track = nullptr;
lv_obj_t* scan_runner = nullptr;
lv_obj_t* footer_label = nullptr;
};
struct CompassWidgetUi
{
DashboardCardChrome chrome{};
lv_obj_t* dial = nullptr;
lv_obj_t* dial_ring = nullptr;
lv_obj_t* dial_inner_ring = nullptr;
lv_obj_t* north_marker = nullptr;
lv_obj_t* needle = nullptr;
lv_point_precise_t needle_points[2]{};
lv_obj_t* center_dot = nullptr;
lv_obj_t* cardinal_labels[4]{};
lv_obj_t* target_name = nullptr;
lv_obj_t* distance_value = nullptr;
lv_obj_t* bearing_value = nullptr;
lv_obj_t* footer_label = nullptr;
};
// Container state only. No child controls or service data belong here.
// This owner is allocated in PSRAM; the registry retains one pointer.
struct DashboardUi
{
lv_obj_t* panel = nullptr;
lv_obj_t* grid = nullptr;
lv_obj_t* menu_grid = nullptr;
lv_obj_t* pager = nullptr;
lv_timer_t* timer = nullptr;
std::array<WidgetHandle, 4> widgets{};
uint32_t tick = 0;
bool dock_right = false;
MeshWidgetUi mesh{};
GpsWidgetUi gps{};
RecentWidgetUi recent{};
CompassWidgetUi compass{};
bool feed_open = false;
bool active = true;
uint8_t page = 0;
lv_indev_t* pointer = nullptr;
lv_point_t start{};
bool pressed = false;
bool inside_dashboard = false;
};
DashboardUi& dashboard_state();
DashboardUi* dashboard_state();
bool dashboard_create();
void dashboard_reset();
} // namespace ui::menu::dashboard
@@ -0,0 +1,76 @@
#pragma once
#include "lvgl.h"
#include "platform/ui/psram_ui_lease.h"
#include <cstdlib>
#include <new>
#if defined(ESP_PLATFORM)
#include "esp_heap_caps.h"
#endif
namespace ui::menu::dashboard
{
enum class WidgetKind : uint8_t
{
Mesh,
Gps,
Recent,
Compass,
Agenda,
Count
};
struct WidgetHandle
{
lv_obj_t* root = nullptr;
void* state = nullptr;
void (*refresh)(void*) = nullptr;
uint32_t period_ms = 1000;
uint32_t next_refresh_ms = 0;
};
inline void* widget_allocate(std::size_t bytes)
{
#if defined(ESP_PLATFORM)
return heap_caps_malloc(bytes, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
#else
return std::malloc(bytes);
#endif
}
inline void widget_free(void* memory)
{
#if defined(ESP_PLATFORM)
heap_caps_free(memory);
#else
std::free(memory);
#endif
}
struct WidgetLifetime
{
platform::ui::PsramUiLease memory;
};
template <typename T>
T* create_widget_state()
{
void* memory = widget_allocate(sizeof(T));
return memory ? new (memory) T{} : nullptr;
}
template <typename T>
void delete_widget_state(T* state)
{
if (!state) return;
state->~T();
widget_free(state);
}
template <typename T>
WidgetHandle own_widget(lv_obj_t* root, T* state, void (*refresh)(void*), uint32_t period_ms = 1000)
{
lv_obj_set_user_data(root, state);
lv_obj_add_event_cb(
root, [](lv_event_t* event)
{
// Child controls can borrow arrays in this state (compass points).
// They must finish DELETE before the backing PSRAM is released.
lv_obj_clean(lv_event_get_target_obj(event));
delete_widget_state(static_cast<T*>(lv_event_get_user_data(event))); },
LV_EVENT_DELETE, state);
return {root, state, refresh, period_ms, 0};
}
} // namespace ui::menu::dashboard
@@ -1,20 +1,15 @@
#pragma once
#include "ui/menu/dashboard/dashboard_widget.h"
#include "lvgl.h"
namespace ui::agenda
{
class IAgendaSource;
}
namespace ui::menu::dashboard
{
void create_mesh_widget(lv_obj_t* parent, lv_coord_t card_w, lv_coord_t card_h);
void refresh_mesh_widget();
void create_gps_widget(lv_obj_t* parent, lv_coord_t card_w, lv_coord_t card_h);
void refresh_gps_widget();
void create_recent_widget(lv_obj_t* parent, lv_coord_t card_w, lv_coord_t card_h);
void refresh_recent_widget();
void create_compass_widget(lv_obj_t* parent, lv_coord_t card_w, lv_coord_t card_h);
void refresh_compass_widget();
WidgetHandle create_mesh_widget(lv_obj_t* parent, lv_coord_t width, lv_coord_t height);
WidgetHandle create_gps_widget(lv_obj_t* parent, lv_coord_t width, lv_coord_t height);
WidgetHandle create_recent_widget(lv_obj_t* parent, lv_coord_t width, lv_coord_t height);
WidgetHandle create_compass_widget(lv_obj_t* parent, lv_coord_t width, lv_coord_t height);
WidgetHandle create_agenda_widget(lv_obj_t* parent, lv_coord_t width, lv_coord_t height, ui::agenda::IAgendaSource* source);
} // namespace ui::menu::dashboard
@@ -4,14 +4,24 @@
#include "ui/menu/menu_layout.h"
namespace ui::agenda
{
class IAgendaSource;
}
namespace ui::menu::dashboard
{
#if defined(TRAIL_MATE_ESP_BOARD_TAB5) || defined(TRAIL_MATE_ESP_BOARD_T_DISPLAY_P4)
#if defined(TRAIL_MATE_ESP_BOARD_TAB5) || defined(TRAIL_MATE_ESP_BOARD_T_DISPLAY_P4) || defined(ARDUINO_T_DECK) || defined(ARDUINO_WIO_TRACKER_L2) || defined(ARDUINO_T_LORA_PAGER)
void init(lv_obj_t* menu_panel, lv_obj_t* grid_panel, const ui::menu_layout::InitOptions& options);
void bringToFront();
void refresh_localized_text();
void setActive(bool active);
// Target-owned read port; its lifetime must exceed the menu's lifetime.
void bindAgendaSource(ui::agenda::IAgendaSource* source);
bool isFeedOpen();
bool handleKey(uint32_t key, bool at_left_edge = false);
bool handleEncoder(int delta);
#else
inline void init(lv_obj_t* menu_panel, lv_obj_t* grid_panel, const ui::menu_layout::InitOptions& options)
{
@@ -32,6 +42,10 @@ inline void setActive(bool active)
{
(void)active;
}
inline void bindAgendaSource(ui::agenda::IAgendaSource*) {}
inline bool isFeedOpen() { return false; }
inline bool handleKey(uint32_t, bool = false) { return false; }
inline bool handleEncoder(int) { return false; }
#endif
} // namespace ui::menu::dashboard