From b7d0aeb7b97ff5c772878ad2a374f34b7a7eea99 Mon Sep 17 00:00:00 2001 From: liu weikai Date: Thu, 23 Jul 2026 20:26:27 +0800 Subject: [PATCH] feat(uconsole): complete desktop workspace UI --- apps/linux_uconsole_gtk/CMakeLists.txt | 72 + .../platform/desktop/sdl_window_presenter.cpp | 120 +- .../platform/desktop/sdl_window_presenter.h | 5 + .../src/platform/gtk/gtk_uconsole_app_state.h | 38 + .../platform/gtk/gtk_uconsole_data_layout.cpp | 4 +- .../platform/gtk/gtk_uconsole_data_logic.cpp | 100 ++ .../platform/gtk/gtk_uconsole_field_pages.cpp | 647 +++++++ .../platform/gtk/gtk_uconsole_layout_spec.h | 1 + .../platform/gtk/gtk_uconsole_map_logic.cpp | 10 +- .../src/platform/gtk/gtk_uconsole_pages.cpp | 6 + .../src/platform/gtk/gtk_uconsole_pages.h | 9 + .../platform/gtk/gtk_uconsole_radio_tools.cpp | 700 ++++++++ .../src/platform/gtk/gtk_uconsole_shell.cpp | 180 +- .../src/platform/gtk/gtk_uconsole_style.cpp | 48 +- .../tools/uconsole_sdl_screenshot_capture.cpp | 108 ++ docs/specs/images/uconsole-sdl/chat.png | Bin 0 -> 26836 bytes docs/specs/images/uconsole-sdl/extensions.png | Bin 0 -> 27531 bytes .../images/uconsole-sdl/gps-sky-plot.png | Bin 0 -> 97118 bytes docs/specs/images/uconsole-sdl/map.png | Bin 0 -> 23330 bytes docs/specs/images/uconsole-sdl/overview.png | Bin 0 -> 18624 bytes .../specs/images/uconsole-sdl/radio-tools.png | Bin 0 -> 23624 bytes .../images/uconsole-sdl/shortcut-help.png | Bin 0 -> 29108 bytes .../images/uconsole-sdl/sidebar-collapsed.png | Bin 0 -> 18301 bytes docs/specs/uconsole-feature-parity.md | 65 + docs/specs/uconsole-sdl-visual-review.md | 198 +++ .../src/packs/uconsole_desktop_ux_pack.cpp | 13 +- .../tests/test_uconsole_desktop_ux_pack.cpp | 15 +- .../common/src/app/linux_app_services.cpp | 6 + .../common/src/platform/ui/lora_runtime.cpp | 13 + .../src/platform/ui/tracker_runtime.cpp | 16 +- .../uconsole/src/uconsole_desktop_shell.cpp | 1546 +++++++++++++++-- 31 files changed, 3698 insertions(+), 222 deletions(-) create mode 100644 apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_field_pages.cpp create mode 100644 apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_radio_tools.cpp create mode 100644 apps/linux_uconsole_gtk/tools/uconsole_sdl_screenshot_capture.cpp create mode 100644 docs/specs/images/uconsole-sdl/chat.png create mode 100644 docs/specs/images/uconsole-sdl/extensions.png create mode 100644 docs/specs/images/uconsole-sdl/gps-sky-plot.png create mode 100644 docs/specs/images/uconsole-sdl/map.png create mode 100644 docs/specs/images/uconsole-sdl/overview.png create mode 100644 docs/specs/images/uconsole-sdl/radio-tools.png create mode 100644 docs/specs/images/uconsole-sdl/shortcut-help.png create mode 100644 docs/specs/images/uconsole-sdl/sidebar-collapsed.png create mode 100644 docs/specs/uconsole-feature-parity.md create mode 100644 docs/specs/uconsole-sdl-visual-review.md diff --git a/apps/linux_uconsole_gtk/CMakeLists.txt b/apps/linux_uconsole_gtk/CMakeLists.txt index 7ddce2a9..004d7e6a 100644 --- a/apps/linux_uconsole_gtk/CMakeLists.txt +++ b/apps/linux_uconsole_gtk/CMakeLists.txt @@ -16,6 +16,9 @@ set(TRAIL_MATE_UCONSOLE_PACKAGE_VERSION "0.1.0" option(TRAIL_MATE_UCONSOLE_BUILD_REAL_GTK_UI "Build the restored uConsole GTK executable when Linux GTK4 is available." ON) +option(TRAIL_MATE_UCONSOLE_BUILD_SDL_SCREENSHOT_CAPTURE + "Build the SDL-rendered uConsole screenshot capture tool." + OFF) set(TRAIL_MATE_UCONSOLE_REAL_GTK_SOURCES src/platform/gtk/gtk_uconsole_app.cpp @@ -23,6 +26,7 @@ set(TRAIL_MATE_UCONSOLE_REAL_GTK_SOURCES src/platform/gtk/gtk_uconsole_chat_logic.cpp src/platform/gtk/gtk_uconsole_data_layout.cpp src/platform/gtk/gtk_uconsole_data_logic.cpp + src/platform/gtk/gtk_uconsole_field_pages.cpp src/platform/gtk/gtk_uconsole_hardware_layout.cpp src/platform/gtk/gtk_uconsole_hardware_logic.cpp src/platform/gtk/gtk_uconsole_logs_layout.cpp @@ -33,6 +37,7 @@ set(TRAIL_MATE_UCONSOLE_REAL_GTK_SOURCES src/platform/gtk/gtk_uconsole_overview_layout.cpp src/platform/gtk/gtk_uconsole_overview_logic.cpp src/platform/gtk/gtk_uconsole_pages.cpp + src/platform/gtk/gtk_uconsole_radio_tools.cpp src/platform/gtk/gtk_uconsole_settings_layout.cpp src/platform/gtk/gtk_uconsole_settings_logic.cpp src/platform/gtk/gtk_uconsole_shell.cpp @@ -196,6 +201,73 @@ else() message(STATUS "Restored uConsole GTK executable is enabled only on Linux GTK4 hosts.") endif() +if(TRAIL_MATE_UCONSOLE_BUILD_SDL_SCREENSHOT_CAPTURE) + find_package(Threads REQUIRED) + find_package(SDL3 CONFIG REQUIRED) + + if(NOT TARGET lvgl) + set(CONFIG_LV_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + set(CONFIG_LV_BUILD_DEMOS OFF CACHE BOOL "" FORCE) + set(LV_BUILD_CONF_DIR + "${TRAIL_MATE_REPO_ROOT}/platform/linux/common/include" + CACHE PATH "LVGL configuration directory for uConsole SDL capture" + FORCE) + set(TRAIL_MATE_LOCAL_LVGL_DIR + "${TRAIL_MATE_REPO_ROOT}/managed_components/lvgl__lvgl") + if(EXISTS "${TRAIL_MATE_LOCAL_LVGL_DIR}/CMakeLists.txt") + add_subdirectory( + "${TRAIL_MATE_LOCAL_LVGL_DIR}" + "${CMAKE_BINARY_DIR}/managed_components/lvgl__lvgl" + EXCLUDE_FROM_ALL) + else() + message(FATAL_ERROR + "The uConsole SDL capture requires managed_components/lvgl__lvgl") + endif() + endif() + + include("${TRAIL_MATE_REPO_ROOT}/cmake/TrailMateLinuxSources.cmake") + trailmate_add_linux_common(trailmate_uconsole_sdl_linux_common) + target_sources(trailmate_uconsole_sdl_linux_common + PRIVATE + "${TRAIL_MATE_REPO_ROOT}/modules/core_chat/src/delivery/chat_message_ledger.cpp" + "${TRAIL_MATE_REPO_ROOT}/modules/core_chat/src/delivery/chat_outbox_service.cpp" + "${TRAIL_MATE_REPO_ROOT}/modules/core_chat/src/infra/mesh_peer_directory_core.cpp" + "${TRAIL_MATE_REPO_ROOT}/modules/core_chat/src/read/chat_read_state_ledger.cpp" + "${TRAIL_MATE_REPO_ROOT}/modules/core_chat/src/runtime/meshtastic_position_core.cpp" + "${TRAIL_MATE_REPO_ROOT}/modules/core_chat/src/runtime/meshtastic_waypoint_core.cpp") + + add_library(trailmate_uconsole_sdl_shell + "${TRAIL_MATE_REPO_ROOT}/platform/linux/uconsole/src/uconsole_chat_workspace_model.cpp" + "${TRAIL_MATE_REPO_ROOT}/platform/linux/uconsole/src/uconsole_dashboard_model.cpp" + "${TRAIL_MATE_REPO_ROOT}/platform/linux/uconsole/src/uconsole_desktop_shell.cpp" + "${TRAIL_MATE_REPO_ROOT}/platform/linux/uconsole/src/uconsole_hardware_probe.cpp") + target_include_directories(trailmate_uconsole_sdl_shell + PUBLIC + "${TRAIL_MATE_REPO_ROOT}/platform/linux/uconsole/include" + "${TRAIL_MATE_LINUX_COMMON_INCLUDE_ROOT}" + "${TRAIL_MATE_LINUX_COMMON_SRC_ROOT}") + target_compile_features(trailmate_uconsole_sdl_shell PUBLIC cxx_std_20) + target_compile_definitions(trailmate_uconsole_sdl_shell + PRIVATE TRAIL_MATE_UCONSOLE_LINUX=1) + target_link_libraries(trailmate_uconsole_sdl_shell + PUBLIC + lvgl + trailmate_uconsole_sdl_linux_common) + trailmate_apply_linux_common_warnings(trailmate_uconsole_sdl_shell) + + add_executable(trailmate_uconsole_sdl_screenshot_capture + src/platform/desktop/sdl_window_presenter.cpp + tools/uconsole_sdl_screenshot_capture.cpp) + target_include_directories(trailmate_uconsole_sdl_screenshot_capture + PRIVATE "${CMAKE_CURRENT_LIST_DIR}/src") + target_compile_features(trailmate_uconsole_sdl_screenshot_capture + PRIVATE cxx_std_20) + target_link_libraries(trailmate_uconsole_sdl_screenshot_capture + PRIVATE + SDL3::SDL3 + trailmate_uconsole_sdl_shell) +endif() + if(BUILD_TESTING) add_executable(trailmate_linux_uconsole_gtk_app_shell_smoke tests/linux_uconsole_gtk_app_shell_smoke.cpp) diff --git a/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.cpp b/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.cpp index 524fe66a..ea2ed8c5 100644 --- a/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.cpp +++ b/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.cpp @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -14,6 +17,9 @@ #include "app/input_event.h" #include "core/canvas.h" +#define LODEPNG_NO_COMPILE_CPP +#include "src/libs/lodepng/lodepng.h" + namespace trailmate::uconsole::desktop { namespace @@ -142,6 +148,75 @@ void handleTextInput(std::vector& queue, } } +void saveRendererPng(SDL_Renderer* renderer, + const std::filesystem::path& path) +{ + SDL_Surface* captured = + requireSdl(SDL_RenderReadPixels(renderer, nullptr), + "SDL_RenderReadPixels"); + SDL_Surface* rgba = + requireSdl(SDL_ConvertSurface(captured, SDL_PIXELFORMAT_RGBA32), + "SDL_ConvertSurface"); + SDL_DestroySurface(captured); + + std::vector pixels; + pixels.resize(static_cast(rgba->w) * + static_cast(rgba->h) * 4U); + const auto* source = static_cast(rgba->pixels); + for (int y = 0; y < rgba->h; ++y) + { + const auto* source_row = + source + (static_cast(y) * + static_cast(rgba->pitch)); + auto* destination_row = + pixels.data() + (static_cast(y) * + static_cast(rgba->w) * 4U); + std::copy_n(source_row, + static_cast(rgba->w) * 4U, + destination_row); + } + + const int width = rgba->w; + const int height = rgba->h; + SDL_DestroySurface(rgba); + + if (!path.parent_path().empty()) + { + std::filesystem::create_directories(path.parent_path()); + } + + unsigned char* encoded = nullptr; + std::size_t encoded_size = 0; + const unsigned error = + lodepng_encode32(&encoded, + &encoded_size, + pixels.data(), + static_cast(width), + static_cast(height)); + if (error != 0U) + { + throw std::runtime_error("failed to encode SDL screenshot " + + path.string() + ": " + + lodepng_error_text(error)); + } + + std::ofstream output(path, std::ios::binary); + if (!output) + { + std::free(encoded); + throw std::runtime_error("failed to open SDL screenshot " + + path.string()); + } + output.write(reinterpret_cast(encoded), + static_cast(encoded_size)); + std::free(encoded); + if (!output) + { + throw std::runtime_error("failed to write SDL screenshot " + + path.string()); + } +} + } // namespace struct SdlWindowPresenter::Impl @@ -155,6 +230,9 @@ struct SdlWindowPresenter::Impl int texture_height = 0; int window_width = 0; int window_height = 0; + int presented_frames = 0; + bool startup_inputs_queued = false; + bool startup_shortcut_queued = false; std::vector staging{}; std::vector input_queue{}; }; @@ -171,8 +249,15 @@ SdlWindowPresenter::SdlWindowPresenter(SdlWindowOptions options) requireSdl(SDL_Init(SDL_INIT_VIDEO), "SDL_Init"); - const SDL_WindowFlags flags = - impl_->options.fullscreen ? SDL_WINDOW_FULLSCREEN : 0; + SDL_WindowFlags flags = 0; + if (impl_->options.fullscreen) + { + flags |= SDL_WINDOW_FULLSCREEN; + } + if (impl_->options.hidden) + { + flags |= SDL_WINDOW_HIDDEN; + } impl_->window = requireSdl(SDL_CreateWindow(impl_->options.title.c_str(), impl_->window_width, @@ -204,6 +289,28 @@ SdlWindowPresenter::~SdlWindowPresenter() bool SdlWindowPresenter::pump() { + if (!impl_->startup_inputs_queued) + { + for (int step = 0; step < impl_->options.initial_nav_steps; ++step) + { + enqueueSpecial(impl_->input_queue, app::InputKey::Tab, "TAB"); + } + if (impl_->options.initial_nav_steps > 0) + { + enqueueSpecial(impl_->input_queue, app::InputKey::Enter, "OK"); + } + impl_->startup_inputs_queued = true; + } + else if (!impl_->startup_shortcut_queued && impl_->presented_frames >= 5) + { + if (impl_->options.initial_shortcut != '\0') + { + impl_->input_queue.push_back( + app::makeCharacterInput(impl_->options.initial_shortcut)); + } + impl_->startup_shortcut_queued = true; + } + SDL_Event event{}; while (SDL_PollEvent(&event)) { @@ -306,6 +413,15 @@ void SdlWindowPresenter::present(const core::Canvas& canvas) &destination), "SDL_RenderTexture"); requireSdl(SDL_RenderPresent(impl_->renderer), "SDL_RenderPresent"); + + ++impl_->presented_frames; + if (!impl_->options.screenshot_path.empty() && + impl_->presented_frames >= + std::max(1, impl_->options.screenshot_after_frames)) + { + saveRendererPng(impl_->renderer, impl_->options.screenshot_path); + impl_->running = false; + } } } // namespace trailmate::uconsole::desktop diff --git a/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.h b/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.h index 3ac514cb..1c7e3d9b 100644 --- a/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.h +++ b/apps/linux_uconsole_gtk/src/platform/desktop/sdl_window_presenter.h @@ -15,6 +15,11 @@ struct SdlWindowOptions int scale = 1; bool fullscreen = false; std::string title = "Trail Mate uConsole"; + std::string screenshot_path{}; + int screenshot_after_frames = 45; + int initial_nav_steps = 0; + char initial_shortcut = '\0'; + bool hidden = false; }; class SdlWindowPresenter : public cardputer_zero::platform::SurfacePresenter diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_app_state.h b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_app_state.h index 2c55f199..ffdda376 100644 --- a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_app_state.h +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_app_state.h @@ -16,6 +16,7 @@ #include "app/linux_app_services.h" #include "gps/usecase/gnss_skyplot_presenter.h" #include "platform/linux/runtime_packet_log.h" +#include "platform/ui/pack_repository_runtime.h" #include "uconsole/uconsole_chat_workspace_model.h" #include "uconsole/uconsole_dashboard_model.h" #include "uconsole/uconsole_map_workspace_model.h" @@ -81,6 +82,12 @@ struct GtkUConsoleAppState GtkWidget* nav_chat = nullptr; GtkWidget* nav_chat_badge = nullptr; GtkWidget* nav_map = nullptr; + GtkWidget* nav_contacts = nullptr; + GtkWidget* nav_gps = nullptr; + GtkWidget* nav_team = nullptr; + GtkWidget* nav_tracker = nullptr; + GtkWidget* nav_radio_tools = nullptr; + GtkWidget* nav_extensions = nullptr; GtkWidget* nav_hardware = nullptr; GtkWidget* nav_data = nullptr; GtkWidget* nav_logs = nullptr; @@ -114,6 +121,14 @@ struct GtkUConsoleAppState GtkWidget* capability_box = nullptr; GtkWidget* hardware_page_box = nullptr; GtkWidget* data_page_box = nullptr; + GtkWidget* contacts_page_box = nullptr; + GtkWidget* gps_page_box = nullptr; + GtkWidget* gps_skyplot = nullptr; + GtkWidget* gps_satellite_list = nullptr; + GtkWidget* team_page_box = nullptr; + GtkWidget* tracker_page_box = nullptr; + GtkWidget* radio_tools_page_box = nullptr; + GtkWidget* extensions_page_box = nullptr; GtkWidget* logs_page_box = nullptr; GtkWidget* logs_source_gps = nullptr; GtkWidget* logs_source_lora = nullptr; @@ -280,6 +295,29 @@ struct GtkUConsoleAppState int overview_timeline_filter_index = 0; std::string settings_notice{}; int settings_notice_ticks = 0; + std::string team_action_status{}; + std::string tracker_action_status{}; + + struct RadioSweepPoint + { + float frequency_mhz = 0.0F; + float rssi_dbm = -140.0F; + }; + + struct RadioSweepResult + { + bool ok = false; + std::string message{}; + std::vector points{}; + }; + + std::future radio_sweep_future{}; + std::vector radio_sweep_points{}; + std::string radio_tools_status{}; + bool radio_sweep_running = false; + std::vector<::ui::runtime::packs::PackageRecord> extension_catalog{}; + std::string extensions_status{}; + bool extensions_loaded = false; std::vector page_lifecycle{}; std::string active_page{}; diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_data_layout.cpp b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_data_layout.cpp index a31874f7..6273dfae 100644 --- a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_data_layout.cpp +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_data_layout.cpp @@ -7,8 +7,8 @@ namespace trailmate::uconsole::gtk GtkWidget* launchDataLayout(GtkUConsoleAppState& state) { return buildDetailsWorkspace( - "Data", - "Local SQLite state, message/contact counts, and map cache.", + "Data & offline maps", + "Local SQLite state, automatic map downloads, cache health, and offline storage.", &state.data_page_box); } diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_data_logic.cpp b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_data_logic.cpp index 765ed767..40936cde 100644 --- a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_data_logic.cpp +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_data_logic.cpp @@ -1,11 +1,31 @@ #include "platform/gtk/gtk_uconsole_pages.h" +#include "platform/gtk/gtk_uconsole_shell.h" #include "platform/gtk/gtk_uconsole_widgets.h" +#include #include namespace trailmate::uconsole::gtk { +namespace +{ + +void onDataOpenMapClicked(GtkButton*, gpointer data) +{ + showPage(*static_cast(data), "map"); +} + +void onDataRetryMapDownloadsClicked(GtkButton*, gpointer data) +{ + auto& state = *static_cast(data); + state.map_failed_tiles.clear(); + state.map_fetch_status = "state: retry requested"; + refreshUi(state); +} + +} // namespace + static void refreshDataPage(GtkUConsoleAppState& state, const UConsoleDashboardSnapshot& dashboard, const MapWorkspaceSnapshot& map_snapshot) @@ -41,6 +61,86 @@ static void refreshDataPage(GtkUConsoleAppState& state, .string())); gtk_box_append(GTK_BOX(state.data_page_box), strip); + const std::size_t visible_cached = static_cast(std::count_if( + map_snapshot.tiles.begin(), + map_snapshot.tiles.end(), + [](const MapTileItem& tile) + { return tile.available; })); + const std::size_t visible_total = map_snapshot.tiles.size(); + const std::size_t visible_missing = visible_total - visible_cached; + + GtkWidget* downloads = makePanel(); + gtk_widget_add_css_class(downloads, "map-download-panel"); + GtkWidget* download_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); + GtkWidget* download_title = + makeLabel("Automatic offline map downloads", "pane-heading"); + gtk_widget_set_hexpand(download_title, TRUE); + gtk_box_append(GTK_BOX(download_header), download_title); + + GtkWidget* retry = gtk_button_new_with_label("Retry failed"); + gtk_widget_add_css_class(retry, "nav-button"); + gtk_widget_set_sensitive(retry, + state.map_failed_tiles.empty() ? FALSE : TRUE); + g_signal_connect(retry, + "clicked", + G_CALLBACK(onDataRetryMapDownloadsClicked), + &state); + gtk_box_append(GTK_BOX(download_header), retry); + + GtkWidget* open_map = gtk_button_new_with_label("Open map"); + gtk_widget_add_css_class(open_map, "nav-button"); + g_signal_connect(open_map, + "clicked", + G_CALLBACK(onDataOpenMapClicked), + &state); + gtk_box_append(GTK_BOX(download_header), open_map); + gtk_box_append(GTK_BOX(downloads), download_header); + + GtkWidget* download_metrics = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_add_css_class(download_metrics, "metric-strip"); + gtk_box_append(GTK_BOX(download_metrics), + makeMetricCard("Visible tiles", + std::to_string(visible_cached) + " / " + + std::to_string(visible_total), + std::to_string(visible_missing) + + " waiting", + visible_missing > 0)); + gtk_box_append(GTK_BOX(download_metrics), + makeMetricCard("Active downloads", + std::to_string(state.map_fetch_jobs.size()), + "Up to " + + std::to_string( + kMaxConcurrentMapFetches) + + " concurrent")); + gtk_box_append(GTK_BOX(download_metrics), + makeMetricCard("Retry queue", + std::to_string( + state.map_failed_tiles.size()), + state.map_failed_tiles.empty() + ? "Healthy" + : "Exponential backoff", + !state.map_failed_tiles.empty())); + gtk_box_append(GTK_BOX(download_metrics), + makeMetricCard("Base layer", + map_snapshot.source_label, + "Zoom " + + std::to_string(map_snapshot.zoom))); + gtk_box_append(GTK_BOX(downloads), download_metrics); + + const std::string download_state = + state.map_fetch_status.empty() + ? "Idle; missing tiles are fetched automatically around the current map viewport." + : state.map_fetch_status; + gtk_box_append(GTK_BOX(downloads), + buildDetailRow("Background worker", + download_state, + !state.map_failed_tiles.empty())); + gtk_box_append(GTK_BOX(downloads), + buildDetailRow( + "Cache policy", + "Fetch visible base tiles on demand, keep them on disk, and reuse them offline.")); + gtk_box_append(GTK_BOX(state.data_page_box), downloads); + GtkWidget* detail = makePanel(); gtk_widget_add_css_class(detail, "inspector-pane"); gtk_box_append(GTK_BOX(detail), diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_field_pages.cpp b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_field_pages.cpp new file mode 100644 index 00000000..f799816d --- /dev/null +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_field_pages.cpp @@ -0,0 +1,647 @@ +#include "platform/gtk/gtk_uconsole_layout_spec.h" +#include "platform/gtk/gtk_uconsole_pages.h" +#include "platform/gtk/gtk_uconsole_shell.h" +#include "platform/gtk/gtk_uconsole_widgets.h" + +#include +#include +#include +#include +#include + +#include "gps/usecase/gnss_skyplot_presenter.h" +#include "platform/ui/gps_runtime.h" +#include "platform/ui/tracker_runtime.h" + +namespace trailmate::uconsole::gtk +{ +namespace +{ + +constexpr std::size_t kFieldConversationLimit = 64; +constexpr std::size_t kFieldSatelliteLimit = 32; + +::chat::NodeId nodeIdFromButton(GtkButton* button) +{ + return static_cast<::chat::NodeId>(GPOINTER_TO_UINT( + g_object_get_data(G_OBJECT(button), "trailmate-node-id"))); +} + +GtkWidget* makeFieldAction(const char* label, + const ChatNodeInfoItem& item, + GCallback callback, + GtkUConsoleAppState& state, + bool enabled = true) +{ + GtkWidget* button = gtk_button_new_with_label(label); + gtk_widget_add_css_class(button, "chat-node-action"); + g_object_set_data(G_OBJECT(button), + "trailmate-node-id", + GUINT_TO_POINTER(static_cast(item.node_id))); + gtk_widget_set_sensitive(button, enabled ? TRUE : FALSE); + g_signal_connect(button, "clicked", callback, &state); + return button; +} + +void onFieldNodeChatClicked(GtkButton* button, gpointer data) +{ + auto& state = *static_cast(data); + state.chat_model.selectNodeConversation(nodeIdFromButton(button)); + showPage(state, "chat"); +} + +GtkWidget* makeContactNodeCard(GtkUConsoleAppState& state, + const ChatNodeInfoItem& item) +{ + GtkWidget* card = makePanel(); + gtk_widget_add_css_class(card, "field-contact-card"); + + GtkWidget* title_row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + GtkWidget* title = makeLabel(item.title.c_str(), "row-title", true); + gtk_widget_set_hexpand(title, TRUE); + gtk_box_append(GTK_BOX(title_row), title); + gtk_box_append(GTK_BOX(title_row), + makeLabel(item.via_mqtt ? "MQTT" : "LoRa", "mini-chip")); + gtk_box_append(GTK_BOX(card), title_row); + gtk_box_append(GTK_BOX(card), + makeLabel(item.subtitle.c_str(), "row-meta", true)); + gtk_box_append(GTK_BOX(card), + makeLabel(item.status.c_str(), "row-meta", true)); + gtk_box_append(GTK_BOX(card), + makeLabel(item.signal.c_str(), "row-meta", true)); + gtk_box_append(GTK_BOX(card), + makeLabel(item.position.c_str(), + item.has_position ? "chat-node-position" + : "row-meta", + true)); + + GtkWidget* actions = gtk_flow_box_new(); + gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(actions), GTK_SELECTION_NONE); + gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(actions), 3); + gtk_flow_box_set_row_spacing(GTK_FLOW_BOX(actions), 4); + gtk_flow_box_set_column_spacing(GTK_FLOW_BOX(actions), 4); + gtk_flow_box_append(GTK_FLOW_BOX(actions), + makeFieldAction("Chat", + item, + G_CALLBACK(onFieldNodeChatClicked), + state)); + gtk_flow_box_append(GTK_FLOW_BOX(actions), + makeFieldAction(item.is_contact ? "Added" : "Add", + item, + G_CALLBACK(onChatNodeAddClicked), + state, + !item.is_contact)); + gtk_flow_box_append(GTK_FLOW_BOX(actions), + makeFieldAction("Info", + item, + G_CALLBACK(onChatNodeInfoClicked), + state)); + gtk_flow_box_append( + GTK_FLOW_BOX(actions), + makeFieldAction(item.is_ignored ? "Unignore" : "Ignore", + item, + G_CALLBACK(onChatNodeIgnoreClicked), + state)); + gtk_flow_box_append(GTK_FLOW_BOX(actions), + makeFieldAction("Exchange", + item, + G_CALLBACK( + onChatNodeExchangeUserInfoClicked), + state)); + gtk_flow_box_append( + GTK_FLOW_BOX(actions), + makeFieldAction(item.key_verified ? "Trusted" : "Verify key", + item, + G_CALLBACK(onChatNodeVerifyKeyClicked), + state, + !item.key_verified)); + gtk_box_append(GTK_BOX(card), actions); + return card; +} + +void appendNodeGroup(GtkUConsoleAppState& state, + GtkWidget* panel, + const std::vector& nodes, + bool contacts) +{ + bool appended = false; + for (const auto& item : nodes) + { + if (item.is_contact != contacts) + { + continue; + } + gtk_box_append(GTK_BOX(panel), makeContactNodeCard(state, item)); + appended = true; + } + if (!appended) + { + gtk_box_append(GTK_BOX(panel), + makeLabel(contacts ? "No saved contacts yet." + : "No nearby nodes yet.", + "empty-state")); + } +} + +GtkWidget* launchContactsLayout(GtkUConsoleAppState& state) +{ + return buildDetailsWorkspace( + "Contacts & nodes", + "Desktop node directory with messaging, trust, ignore, and user-info actions.", + &state.contacts_page_box); +} + +void refreshContactsLogic(GtkUConsoleAppState& state, + const GtkUConsoleRefreshSnapshot& snapshot) +{ + clearBox(state.contacts_page_box); + const ChatWorkspaceSnapshot chat = state.chat_model.snapshot( + kFieldConversationLimit, 0, state.chat_sort_mode); + + GtkWidget* metrics = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_add_css_class(metrics, "metric-strip"); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Contacts", + std::to_string( + snapshot.dashboard.contact_count), + "Saved identities")); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Nearby", + std::to_string( + snapshot.dashboard.nearby_count), + "Recently discovered")); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Ignored", + std::to_string( + snapshot.dashboard.ignored_count), + "Hidden from normal lists", + snapshot.dashboard.ignored_count > 0)); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Protocol", + snapshot.dashboard.mesh_protocol, + std::to_string(chat.nodes.size()) + + " decoded nodes")); + gtk_box_append(GTK_BOX(state.contacts_page_box), metrics); + + GtkWidget* workbench = makeWorkbench(GTK_ORIENTATION_HORIZONTAL, 8); + GtkWidget* contacts = makePanel(); + GtkWidget* nearby = makePanel(); + gtk_widget_set_hexpand(contacts, TRUE); + gtk_widget_set_hexpand(nearby, TRUE); + gtk_box_append(GTK_BOX(contacts), + makeLabel("Saved contacts", "pane-heading")); + gtk_box_append(GTK_BOX(nearby), + makeLabel("Nearby and discovered", "pane-heading")); + appendNodeGroup(state, contacts, chat.nodes, true); + appendNodeGroup(state, nearby, chat.nodes, false); + gtk_box_append(GTK_BOX(workbench), contacts); + gtk_box_append(GTK_BOX(workbench), nearby); + gtk_box_append(GTK_BOX(state.contacts_page_box), workbench); +} + +GtkWidget* makeSatelliteRow(const ::gps::GnssSkyplotSatellite& sat) +{ + GtkWidget* row = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); + gtk_widget_add_css_class(row, "gnss-sat-row"); + gtk_widget_add_css_class(row, sat.used ? "gnss-sat-used" + : "gnss-sat-unused"); + char title[32] = {}; + std::snprintf(title, + sizeof(title), + "%s %u", + ::gps::gnss_system_label(sat.system), + static_cast(sat.id)); + char detail[96] = {}; + std::snprintf(detail, + sizeof(detail), + "%s / SNR %d / elevation %u / azimuth %u", + sat.used ? "used" : "in view", + static_cast(sat.snr), + static_cast(sat.elevation), + static_cast(sat.azimuth)); + gtk_box_append(GTK_BOX(row), makeLabel(title, "gnss-sat-title")); + gtk_box_append(GTK_BOX(row), + makeLabel(detail, "gnss-sat-meta", true)); + return row; +} + +void onFieldOpenMapClicked(GtkButton*, gpointer data) +{ + showPage(*static_cast(data), "map"); +} + +GtkWidget* launchGpsLayout(GtkUConsoleAppState& state) +{ + return buildDetailsWorkspace( + "GPS & sky plot", + "Live fix, satellite geometry, receiver state, and map handoff.", + &state.gps_page_box); +} + +void refreshGpsLogic(GtkUConsoleAppState& state, + const GtkUConsoleRefreshSnapshot& snapshot) +{ + clearBox(state.gps_page_box); + + std::array<::gps::GnssSatInfo, ::gps::kMaxGnssSats> sats{}; + std::size_t sat_count = 0; + ::gps::GnssStatus status{}; + const bool has_snapshot = ::platform::ui::gps::get_gnss_snapshot( + sats.data(), sats.size(), &sat_count, &status); + const auto gps_state = ::platform::ui::gps::get_data(); + state.overview_gnss_view = ::gps::build_gnss_skyplot_view( + sats.data(), + sat_count, + status, + gps_state, + has_snapshot, + kFieldSatelliteLimit); + + GtkWidget* metrics = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_add_css_class(metrics, "metric-strip"); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Fix", + snapshot.dashboard.location.state, + snapshot.dashboard.location.coordinates, + snapshot.dashboard.location.attention)); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Satellites used", + std::to_string( + state.overview_gnss_view.status + .sats_in_use), + std::to_string( + state.overview_gnss_view.status + .sats_in_view) + + " in view")); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Map zoom", + std::to_string(snapshot.map.zoom), + snapshot.map.source_label)); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Receiver", + has_snapshot ? "Live" : "Waiting", + snapshot.dashboard.location.detail, + !has_snapshot)); + gtk_box_append(GTK_BOX(state.gps_page_box), metrics); + + GtkWidget* workbench = makeWorkbench(GTK_ORIENTATION_HORIZONTAL, 8); + GtkWidget* sky_panel = makePanel(); + gtk_widget_set_hexpand(sky_panel, TRUE); + gtk_box_append(GTK_BOX(sky_panel), + makeLabel("Satellite sky plot", "pane-heading")); + state.gps_skyplot = gtk_drawing_area_new(); + gtk_widget_add_css_class(state.gps_skyplot, "gnss-skyplot"); + gtk_widget_set_size_request(state.gps_skyplot, 440, 360); + gtk_widget_set_hexpand(state.gps_skyplot, TRUE); + gtk_widget_set_vexpand(state.gps_skyplot, TRUE); + gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(state.gps_skyplot), + drawOverviewGnssSkyplot, + &state, + nullptr); + gtk_box_append(GTK_BOX(sky_panel), state.gps_skyplot); + + GtkWidget* satellite_panel = makePanel(); + gtk_widget_set_size_request(satellite_panel, 320, -1); + gtk_box_append(GTK_BOX(satellite_panel), + makeLabel("Satellites", "pane-heading")); + state.gps_satellite_list = gtk_box_new(GTK_ORIENTATION_VERTICAL, 4); + if (state.overview_gnss_view.satellites.empty()) + { + gtk_box_append(GTK_BOX(state.gps_satellite_list), + makeLabel("No satellite data.", "empty-state")); + } + for (const auto& sat : state.overview_gnss_view.satellites) + { + gtk_box_append(GTK_BOX(state.gps_satellite_list), + makeSatelliteRow(sat)); + } + GtkWidget* satellite_scroll = gtk_scrolled_window_new(); + gtk_widget_set_vexpand(satellite_scroll, TRUE); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(satellite_scroll), + GTK_POLICY_NEVER, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(satellite_scroll), + state.gps_satellite_list); + gtk_box_append(GTK_BOX(satellite_panel), satellite_scroll); + + GtkWidget* open_map = gtk_button_new_with_label("Open map workspace"); + gtk_widget_add_css_class(open_map, "nav-button"); + g_signal_connect(open_map, + "clicked", + G_CALLBACK(onFieldOpenMapClicked), + &state); + gtk_box_append(GTK_BOX(satellite_panel), open_map); + gtk_box_append(GTK_BOX(workbench), sky_panel); + gtk_box_append(GTK_BOX(workbench), satellite_panel); + gtk_box_append(GTK_BOX(state.gps_page_box), workbench); +} + +const ChatConversationItem* firstTeamConversation( + const ChatWorkspaceSnapshot& snapshot) +{ + const auto it = std::find_if( + snapshot.conversations.begin(), + snapshot.conversations.end(), + [](const ChatConversationItem& item) + { return item.team; }); + return it == snapshot.conversations.end() ? nullptr : &*it; +} + +void onTeamOpenChatClicked(GtkButton*, gpointer data) +{ + auto& state = *static_cast(data); + const auto snapshot = state.chat_model.snapshot(kFieldConversationLimit, 0); + const auto* conversation = firstTeamConversation(snapshot); + if (conversation == nullptr) + { + state.team_action_status = "No team conversation is available yet."; + refreshUi(state); + return; + } + state.chat_model.selectConversation(conversation->id); + showPage(state, "chat"); +} + +void onTeamSharePositionClicked(GtkButton*, gpointer data) +{ + auto& state = *static_cast(data); + const auto snapshot = state.chat_model.snapshot(kFieldConversationLimit, 0); + const auto* conversation = firstTeamConversation(snapshot); + if (conversation == nullptr) + { + state.team_action_status = "Join or create a team before sharing."; + } + else + { + state.chat_model.selectConversation(conversation->id); + state.team_action_status = state.chat_model.sendCurrentPosition() + ? "Current position queued for the team." + : "Position could not be queued."; + } + refreshUi(state); +} + +GtkWidget* launchTeamLayout(GtkUConsoleAppState& state) +{ + return buildDetailsWorkspace( + "Team operations", + "Team state, field activity, team chat, and position sharing.", + &state.team_page_box); +} + +void showTeamPage(GtkUConsoleAppState& state) +{ + state.services.setTeamModeActive(true); +} + +void hideTeamPage(GtkUConsoleAppState& state) +{ + state.services.setTeamModeActive(false); +} + +void refreshTeamLogic(GtkUConsoleAppState& state, + const GtkUConsoleRefreshSnapshot& snapshot) +{ + clearBox(state.team_page_box); + const auto chat = state.chat_model.snapshot(kFieldConversationLimit, 0); + const bool has_team_chat = firstTeamConversation(chat) != nullptr; + + GtkWidget* summary = makePanel(); + gtk_box_append(GTK_BOX(summary), + makeLabel("Team status", "pane-heading")); + gtk_box_append(GTK_BOX(summary), + makeLabel(snapshot.dashboard.team_summary.c_str(), + "summary-detail", + true)); + if (!state.team_action_status.empty()) + { + gtk_box_append(GTK_BOX(summary), + makeLabel(state.team_action_status.c_str(), + "settings-status", + true)); + } + + GtkWidget* actions = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + GtkWidget* open_chat = gtk_button_new_with_label("Open team chat"); + GtkWidget* share = gtk_button_new_with_label("Share current position"); + gtk_widget_add_css_class(open_chat, "nav-button"); + gtk_widget_add_css_class(share, "nav-button"); + gtk_widget_set_sensitive(open_chat, has_team_chat ? TRUE : FALSE); + gtk_widget_set_sensitive(share, has_team_chat ? TRUE : FALSE); + g_signal_connect(open_chat, + "clicked", + G_CALLBACK(onTeamOpenChatClicked), + &state); + g_signal_connect(share, + "clicked", + G_CALLBACK(onTeamSharePositionClicked), + &state); + gtk_box_append(GTK_BOX(actions), open_chat); + gtk_box_append(GTK_BOX(actions), share); + gtk_box_append(GTK_BOX(summary), actions); + gtk_box_append(GTK_BOX(state.team_page_box), summary); + + GtkWidget* timeline = makePanel(); + gtk_box_append(GTK_BOX(timeline), + makeLabel("Team activity", "pane-heading")); + if (snapshot.dashboard.team_timeline.empty()) + { + gtk_box_append(GTK_BOX(timeline), + makeLabel("No team activity has been recorded.", + "empty-state")); + } + for (const auto& item : snapshot.dashboard.team_timeline) + { + gtk_box_append(GTK_BOX(timeline), + buildDetailRow(item.title, + item.detail, + item.attention)); + } + gtk_box_append(GTK_BOX(state.team_page_box), timeline); +} + +const char* trackerFormatLabel(std::uint8_t value) +{ + switch (value) + { + case 1: + return "CSV"; + case 2: + return "Binary"; + case 0: + default: + return "GPX"; + } +} + +void onTrackerToggleClicked(GtkButton*, gpointer data) +{ + auto& state = *static_cast(data); + auto& config = state.services.config(); + if (::platform::ui::tracker::is_recording()) + { + ::platform::ui::tracker::stop_recording(); + config.map_track_enabled = false; + state.services.applyPositionConfig(); + state.tracker_action_status = "Track recording stopped."; + } + else + { + config.map_track_enabled = true; + state.services.applyPositionConfig(); + const bool started = ::platform::ui::tracker::start_recording(); + state.tracker_action_status = started + ? "Track recording started." + : "Track file could not be created."; + if (!started) + { + config.map_track_enabled = false; + state.services.applyPositionConfig(); + } + } + state.services.saveConfig(); + refreshUi(state); +} + +GtkWidget* launchTrackerLayout(GtkUConsoleAppState& state) +{ + return buildDetailsWorkspace( + "Tracker", + "Linux-native GPX, CSV, or binary track recording with persistent files.", + &state.tracker_page_box); +} + +void refreshTrackerLogic(GtkUConsoleAppState& state, + const GtkUConsoleRefreshSnapshot& snapshot) +{ + clearBox(state.tracker_page_box); + const auto& config = state.services.config(); + const bool supported = ::platform::ui::tracker::is_supported(); + const bool recording = ::platform::ui::tracker::is_recording(); + std::string current_path{}; + (void)::platform::ui::tracker::current_path(current_path); + + GtkWidget* metrics = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_add_css_class(metrics, "metric-strip"); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Recorder", + recording ? "Recording" : "Stopped", + supported ? "Linux track writer ready" + : "Track directory unavailable", + !supported)); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Format", + trackerFormatLabel( + config.map_track_format), + "Persistent setting")); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Interval", + std::to_string( + config.map_track_interval) + + " s", + "Sampling cadence")); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("GPS", + snapshot.dashboard.location.state, + snapshot.dashboard.location.coordinates, + snapshot.dashboard.location.attention)); + gtk_box_append(GTK_BOX(state.tracker_page_box), metrics); + + GtkWidget* controls = makePanel(); + GtkWidget* header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); + GtkWidget* title = makeLabel("Recording session", "pane-heading"); + gtk_widget_set_hexpand(title, TRUE); + gtk_box_append(GTK_BOX(header), title); + GtkWidget* toggle = gtk_button_new_with_label( + recording ? "Stop recording" : "Start recording"); + gtk_widget_add_css_class(toggle, "nav-button"); + gtk_widget_set_sensitive(toggle, supported ? TRUE : FALSE); + g_signal_connect(toggle, + "clicked", + G_CALLBACK(onTrackerToggleClicked), + &state); + gtk_box_append(GTK_BOX(header), toggle); + gtk_box_append(GTK_BOX(controls), header); + gtk_box_append(GTK_BOX(controls), + buildDetailRow("Current file", + current_path.empty() ? "No active file" + : current_path)); + gtk_box_append(GTK_BOX(controls), + buildDetailRow("Track directory", + ::platform::ui::tracker::track_dir())); + if (!state.tracker_action_status.empty()) + { + gtk_box_append(GTK_BOX(controls), + makeLabel(state.tracker_action_status.c_str(), + "settings-status", + true)); + } + gtk_box_append(GTK_BOX(state.tracker_page_box), controls); + + std::vector tracks{}; + (void)::platform::ui::tracker::list_tracks(tracks, 32); + GtkWidget* history = makePanel(); + gtk_box_append(GTK_BOX(history), + makeLabel("Recorded tracks", "pane-heading")); + if (tracks.empty()) + { + gtk_box_append(GTK_BOX(history), + makeLabel("No track files yet.", "empty-state")); + } + for (const auto& track : tracks) + { + gtk_box_append(GTK_BOX(history), + buildDetailRow(track, + std::string("Stored in ") + + ::platform::ui::tracker::track_dir())); + } + gtk_box_append(GTK_BOX(state.tracker_page_box), history); +} + +} // namespace + +GtkUConsolePageLifecycle makeContactsPageLifecycle() +{ + return {.name = "contacts", + .title = "Contacts & nodes", + .onLaunch = launchContactsLayout, + .onShow = nullptr, + .onHide = nullptr, + .onRefresh = refreshContactsLogic, + .onDestroy = nullptr}; +} + +GtkUConsolePageLifecycle makeGpsPageLifecycle() +{ + return {.name = "gps", + .title = "GPS & sky plot", + .onLaunch = launchGpsLayout, + .onShow = nullptr, + .onHide = nullptr, + .onRefresh = refreshGpsLogic, + .onDestroy = nullptr}; +} + +GtkUConsolePageLifecycle makeTeamPageLifecycle() +{ + return {.name = "team", + .title = "Team operations", + .onLaunch = launchTeamLayout, + .onShow = showTeamPage, + .onHide = hideTeamPage, + .onRefresh = refreshTeamLogic, + .onDestroy = nullptr}; +} + +GtkUConsolePageLifecycle makeTrackerPageLifecycle() +{ + return {.name = "tracker", + .title = "Tracker", + .onLaunch = launchTrackerLayout, + .onShow = nullptr, + .onHide = nullptr, + .onRefresh = refreshTrackerLogic, + .onDestroy = nullptr}; +} + +} // namespace trailmate::uconsole::gtk diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_layout_spec.h b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_layout_spec.h index 5a054485..0643304c 100644 --- a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_layout_spec.h +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_layout_spec.h @@ -7,6 +7,7 @@ namespace trailmate::uconsole::gtk::layout_spec // Update the specification before changing product geometry here. constexpr int kGlobalStatusBarHeight = 28; +constexpr int kNavigationRailWidth = 184; constexpr int kOverviewGpsRailWidth = 208; constexpr int kOverviewTimelineRailWidth = 252; diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_map_logic.cpp b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_map_logic.cpp index cb44e82f..71ac24a7 100644 --- a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_map_logic.cpp +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_map_logic.cpp @@ -1243,9 +1243,6 @@ GtkWidget* buildContourTileCell(const MapWorkspaceSnapshot& snapshot, void refreshMap(GtkUConsoleAppState& state, const MapWorkspaceSnapshot& snapshot) { - pollContourFill(state); - maybeStartMapFetch(state, snapshot); - setActiveMapSourceButton( state.map_source_osm, ::platform::linux_runtime::MapBaseSource::Osm, @@ -1439,6 +1436,13 @@ void refreshMap(GtkUConsoleAppState& state, } } +void serviceMapDownloads(GtkUConsoleAppState& state, + const MapWorkspaceSnapshot& snapshot) +{ + pollContourFill(state); + maybeStartMapFetch(state, snapshot); +} + void refreshMap(GtkUConsoleAppState& state) { refreshMap(state, state.map_model.snapshot()); diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_pages.cpp b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_pages.cpp index 9d690fe2..a48be0ef 100644 --- a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_pages.cpp +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_pages.cpp @@ -7,7 +7,13 @@ std::vector buildGtkUConsolePageRegistry() { return {makeOverviewPageLifecycle(), makeChatPageLifecycle(), + makeContactsPageLifecycle(), makeMapPageLifecycle(), + makeGpsPageLifecycle(), + makeTeamPageLifecycle(), + makeTrackerPageLifecycle(), + makeRadioToolsPageLifecycle(), + makeExtensionsPageLifecycle(), makeHardwarePageLifecycle(), makeDataPageLifecycle(), makeLogsPageLifecycle(), diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_pages.h b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_pages.h index 0c4da9ab..5fcd9be5 100644 --- a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_pages.h +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_pages.h @@ -50,6 +50,8 @@ GtkUConsolePageLifecycle makeMapPageLifecycle(); GtkWidget* launchMapLayout(GtkUConsoleAppState& state); void refreshMapLogic(GtkUConsoleAppState& state, const GtkUConsoleRefreshSnapshot& snapshot); +void serviceMapDownloads(GtkUConsoleAppState& state, + const MapWorkspaceSnapshot& snapshot); void refreshMap(GtkUConsoleAppState& state); void refreshMap(GtkUConsoleAppState& state, const MapWorkspaceSnapshot& snapshot); void onMapSourceClicked(GtkButton* button, gpointer data); @@ -96,6 +98,13 @@ GtkWidget* launchDataLayout(GtkUConsoleAppState& state); void refreshDataLogic(GtkUConsoleAppState& state, const GtkUConsoleRefreshSnapshot& snapshot); +GtkUConsolePageLifecycle makeContactsPageLifecycle(); +GtkUConsolePageLifecycle makeGpsPageLifecycle(); +GtkUConsolePageLifecycle makeTeamPageLifecycle(); +GtkUConsolePageLifecycle makeTrackerPageLifecycle(); +GtkUConsolePageLifecycle makeRadioToolsPageLifecycle(); +GtkUConsolePageLifecycle makeExtensionsPageLifecycle(); + GtkUConsolePageLifecycle makeLogsPageLifecycle(); GtkWidget* launchLogsLayout(GtkUConsoleAppState& state); void refreshLogsLogic(GtkUConsoleAppState& state, diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_radio_tools.cpp b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_radio_tools.cpp new file mode 100644 index 00000000..adc4dbe6 --- /dev/null +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_radio_tools.cpp @@ -0,0 +1,700 @@ +#include "platform/gtk/gtk_uconsole_pages.h" +#include "platform/gtk/gtk_uconsole_shell.h" +#include "platform/gtk/gtk_uconsole_widgets.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chat/infra/meshtastic/mt_region.h" +#include "platform/ui/capability_status.h" +#include "platform/ui/lora_runtime.h" +#include "platform/ui/pack_repository_runtime.h" +#include "platform/ui/sstv_runtime.h" +#include "platform/ui/walkie_runtime.h" + +namespace trailmate::uconsole::gtk +{ +namespace +{ + +constexpr std::size_t kSweepPointCount = 48; +constexpr auto kSweepSettleTime = std::chrono::milliseconds(12); + +struct SweepPlan +{ + float start_mhz = 433.0F; + float end_mhz = 434.8F; + ::platform::ui::lora::ReceiveConfig receive{}; +}; + +std::string capabilityLabel(const ::platform::ui::CapabilityStatus& status) +{ + std::string label = ::platform::ui::capability_state_label(status.state); + if (status.message != nullptr && status.message[0] != '\0') + { + label += " — "; + label += status.message; + } + return label; +} + +bool capabilityNeedsAttention(const ::platform::ui::CapabilityStatus& status) +{ + return status.state == ::platform::ui::CapabilityState::Unsupported || + status.state == ::platform::ui::CapabilityState::Degraded || + status.state == ::platform::ui::CapabilityState::Error; +} + +SweepPlan makeSweepPlan(const GtkUConsoleAppState& state) +{ + SweepPlan plan{}; + const auto& config = state.services.config(); + const auto& mesh = config.mesh_protocol == ::chat::MeshProtocol::Meshtastic + ? config.meshtastic_config + : config.mesh_protocol == ::chat::MeshProtocol::MeshCore + ? config.meshcore_config + : config.reticulumConfig(); + plan.receive.bw_khz = displayBandwidthKHz(mesh, config.mesh_protocol); + plan.receive.sf = displaySpreadFactor(mesh, config.mesh_protocol); + plan.receive.cr = displayCodingRate(mesh, config.mesh_protocol); + plan.receive.tx_power = static_cast(displayTxPowerDbm(mesh)); + + if (config.mesh_protocol == ::chat::MeshProtocol::Meshtastic) + { + auto region_code = static_cast( + mesh.region); + if (region_code == + meshtastic_Config_LoRaConfig_RegionCode_UNSET) + { + region_code = meshtastic_Config_LoRaConfig_RegionCode_CN; + } + if (const auto* region = ::chat::meshtastic::findRegion(region_code); + region != nullptr) + { + plan.start_mhz = region->freq_start_mhz; + plan.end_mhz = region->freq_end_mhz; + return plan; + } + } + + const float center = displayFrequencyMhz(mesh); + if (std::isfinite(center) && center > 0.0F) + { + plan.start_mhz = center - 1.0F; + plan.end_mhz = center + 1.0F; + } + return plan; +} + +GtkUConsoleAppState::RadioSweepResult runSweep(SweepPlan plan) +{ + GtkUConsoleAppState::RadioSweepResult result{}; + if (!::platform::ui::lora::acquire()) + { + result.message = "LoRa receiver could not be acquired."; + return result; + } + + struct RadioReleaseGuard + { + ~RadioReleaseGuard() + { + ::platform::ui::lora::release(); + } + } guard; + + result.points.reserve(kSweepPointCount); + for (std::size_t index = 0; index < kSweepPointCount; ++index) + { + const float ratio = static_cast(index) / + static_cast(kSweepPointCount - 1); + const float frequency = + plan.start_mhz + ((plan.end_mhz - plan.start_mhz) * ratio); + if (!::platform::ui::lora::configure_receive(frequency, + plan.receive)) + { + result.message = "LoRa receive configuration failed during scan."; + return result; + } + std::this_thread::sleep_for(kSweepSettleTime); + const float rssi = ::platform::ui::lora::read_instant_rssi(); + if (std::isfinite(rssi)) + { + result.points.push_back({frequency, rssi}); + } + } + + result.ok = !result.points.empty(); + result.message = result.ok ? "Sweep complete; mesh radio configuration restored." + : "No valid RSSI samples were returned."; + return result; +} + +void drawSpectrum(GtkDrawingArea*, + cairo_t* cr, + int width, + int height, + gpointer data) +{ + const auto& state = *static_cast(data); + cairo_set_source_rgb(cr, 0.035, 0.055, 0.075); + cairo_paint(cr); + + constexpr double margin = 24.0; + const double graph_width = std::max(1.0, width - (margin * 2.0)); + const double graph_height = std::max(1.0, height - (margin * 2.0)); + cairo_set_line_width(cr, 1.0); + cairo_set_source_rgba(cr, 0.25, 0.45, 0.55, 0.35); + for (int line = 0; line <= 4; ++line) + { + const double y = margin + (graph_height * line / 4.0); + cairo_move_to(cr, margin, y); + cairo_line_to(cr, margin + graph_width, y); + } + cairo_stroke(cr); + + if (state.radio_sweep_points.empty()) + { + return; + } + + cairo_set_line_width(cr, 2.2); + cairo_set_source_rgb(cr, 0.20, 0.86, 0.73); + for (std::size_t index = 0; index < state.radio_sweep_points.size(); ++index) + { + const auto& point = state.radio_sweep_points[index]; + const double x = margin + graph_width * static_cast(index) / + static_cast( + state.radio_sweep_points.size() - 1); + const double normalized = std::clamp( + (static_cast(point.rssi_dbm) + 140.0) / 112.0, + 0.0, + 1.0); + const double y = margin + graph_height * (1.0 - normalized); + if (index == 0) + { + cairo_move_to(cr, x, y); + } + else + { + cairo_line_to(cr, x, y); + } + } + cairo_stroke(cr); +} + +void drawSstvFrame(GtkDrawingArea*, + cairo_t* cr, + int width, + int height, + gpointer) +{ + cairo_set_source_rgb(cr, 0.025, 0.035, 0.05); + cairo_paint(cr); + const std::uint16_t* pixels = ::platform::ui::sstv::framebuffer(); + const auto frame_width = ::platform::ui::sstv::frame_width(); + const auto frame_height = ::platform::ui::sstv::frame_height(); + if (pixels == nullptr || frame_width == 0 || frame_height == 0) + { + return; + } + + const double scale = std::min(static_cast(width) / frame_width, + static_cast(height) / frame_height); + const double left = (width - (frame_width * scale)) / 2.0; + const double top = (height - (frame_height * scale)) / 2.0; + constexpr std::uint16_t sample_step = 3; + for (std::uint16_t y = 0; y < frame_height; y += sample_step) + { + for (std::uint16_t x = 0; x < frame_width; x += sample_step) + { + const std::uint16_t rgb565 = + pixels[static_cast(y) * frame_width + x]; + const double red = ((rgb565 >> 11U) & 0x1FU) / 31.0; + const double green = ((rgb565 >> 5U) & 0x3FU) / 63.0; + const double blue = (rgb565 & 0x1FU) / 31.0; + cairo_set_source_rgb(cr, red, green, blue); + cairo_rectangle(cr, + left + (x * scale), + top + (y * scale), + sample_step * scale + 0.5, + sample_step * scale + 0.5); + cairo_fill(cr); + } + } +} + +void onStartSweepClicked(GtkButton*, gpointer data) +{ + auto& state = *static_cast(data); + if (state.radio_sweep_running) + { + return; + } + state.radio_sweep_running = true; + state.radio_tools_status = "Scanning the configured radio band..."; + const SweepPlan plan = makeSweepPlan(state); + state.radio_sweep_future = std::async( + std::launch::async, + [plan]() + { return runSweep(plan); }); + refreshUi(state); +} + +void onSstvToggleClicked(GtkButton*, gpointer data) +{ + auto& state = *static_cast(data); + if (::platform::ui::sstv::is_active()) + { + ::platform::ui::sstv::stop(); + state.radio_tools_status = "SSTV receiver stopped."; + } + else + { + state.radio_tools_status = ::platform::ui::sstv::start() + ? "SSTV receiver started." + : ::platform::ui::sstv::last_error(); + } + refreshUi(state); +} + +void onWalkieToggleClicked(GtkButton*, gpointer data) +{ + auto& state = *static_cast(data); + if (::platform::ui::walkie::is_active()) + { + ::platform::ui::walkie::set_ptt(false); + ::platform::ui::walkie::stop(); + state.radio_tools_status = "Walkie session stopped."; + } + else + { + state.radio_tools_status = ::platform::ui::walkie::start() + ? "Walkie session started." + : ::platform::ui::walkie::last_error(); + } + refreshUi(state); +} + +void onWalkiePttClicked(GtkButton*, gpointer data) +{ + auto& state = *static_cast(data); + const auto status = ::platform::ui::walkie::get_status(); + ::platform::ui::walkie::set_ptt(!status.tx); + refreshUi(state); +} + +void onWalkieMonitorClicked(GtkButton*, gpointer data) +{ + auto& state = *static_cast(data); + (void)::platform::ui::walkie::set_monitor_enabled( + !::platform::ui::walkie::monitor_enabled()); + refreshUi(state); +} + +void pollSweep(GtkUConsoleAppState& state) +{ + if (!state.radio_sweep_running || !state.radio_sweep_future.valid() || + state.radio_sweep_future.wait_for(std::chrono::seconds(0)) != + std::future_status::ready) + { + return; + } + auto result = state.radio_sweep_future.get(); + state.radio_sweep_running = false; + state.radio_tools_status = std::move(result.message); + if (result.ok) + { + state.radio_sweep_points = std::move(result.points); + } +} + +GtkWidget* launchRadioToolsLayout(GtkUConsoleAppState& state) +{ + return buildDetailsWorkspace( + "Radio tools", + "Desktop workbench for spectrum inspection, SSTV, and walkie controls.", + &state.radio_tools_page_box); +} + +void refreshRadioToolsLogic(GtkUConsoleAppState& state, + const GtkUConsoleRefreshSnapshot&) +{ + pollSweep(state); + clearBox(state.radio_tools_page_box); + + const auto lora_capability = ::platform::ui::lora::capability_status(); + const auto sstv_capability = ::platform::ui::sstv::capability_status(); + const auto walkie_capability = ::platform::ui::walkie::capability_status(); + const auto sstv_status = ::platform::ui::sstv::get_status(); + const auto walkie_status = ::platform::ui::walkie::get_status(); + + GtkWidget* metrics = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_add_css_class(metrics, "metric-strip"); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("LoRa sweep", + state.radio_sweep_running ? "Scanning" + : "Ready", + capabilityLabel(lora_capability), + capabilityNeedsAttention(lora_capability))); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("SSTV", + ::platform::ui::sstv::is_active() + ? "Receiving" + : "Idle", + capabilityLabel(sstv_capability), + capabilityNeedsAttention(sstv_capability))); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Walkie", + walkie_status.tx + ? "Transmitting" + : (walkie_status.active ? "Listening" + : "Stopped"), + capabilityLabel(walkie_capability), + capabilityNeedsAttention( + walkie_capability))); + gtk_box_append(GTK_BOX(state.radio_tools_page_box), metrics); + + GtkWidget* spectrum = makePanel(); + GtkWidget* spectrum_header = + gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); + GtkWidget* spectrum_title = makeLabel("Energy sweep", "pane-heading"); + gtk_widget_set_hexpand(spectrum_title, TRUE); + gtk_box_append(GTK_BOX(spectrum_header), spectrum_title); + GtkWidget* sweep = gtk_button_new_with_label( + state.radio_sweep_running ? "Scanning..." : "Run sweep"); + gtk_widget_add_css_class(sweep, "nav-button"); + gtk_widget_set_sensitive( + sweep, + !state.radio_sweep_running && ::platform::ui::lora::is_supported()); + g_signal_connect(sweep, + "clicked", + G_CALLBACK(onStartSweepClicked), + &state); + gtk_box_append(GTK_BOX(spectrum_header), sweep); + gtk_box_append(GTK_BOX(spectrum), spectrum_header); + GtkWidget* graph = gtk_drawing_area_new(); + gtk_widget_add_css_class(graph, "radio-spectrum"); + gtk_widget_set_size_request(graph, -1, 210); + gtk_widget_set_hexpand(graph, TRUE); + gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(graph), + drawSpectrum, + &state, + nullptr); + gtk_box_append(GTK_BOX(spectrum), graph); + if (!state.radio_sweep_points.empty()) + { + const auto strongest = std::max_element( + state.radio_sweep_points.begin(), + state.radio_sweep_points.end(), + [](const auto& lhs, const auto& rhs) + { return lhs.rssi_dbm < rhs.rssi_dbm; }); + char summary[128] = {}; + std::snprintf(summary, + sizeof(summary), + "%.3f–%.3f MHz / strongest %.3f MHz at %.1f dBm", + static_cast( + state.radio_sweep_points.front().frequency_mhz), + static_cast( + state.radio_sweep_points.back().frequency_mhz), + static_cast(strongest->frequency_mhz), + static_cast(strongest->rssi_dbm)); + gtk_box_append(GTK_BOX(spectrum), + makeLabel(summary, "row-meta", true)); + } + gtk_box_append(GTK_BOX(state.radio_tools_page_box), spectrum); + + GtkWidget* lower = makeWorkbench(GTK_ORIENTATION_HORIZONTAL, 8); + GtkWidget* sstv = makePanel(); + GtkWidget* sstv_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); + GtkWidget* sstv_title = makeLabel("SSTV receiver", "pane-heading"); + gtk_widget_set_hexpand(sstv_title, TRUE); + gtk_box_append(GTK_BOX(sstv_header), sstv_title); + GtkWidget* sstv_toggle = gtk_button_new_with_label( + ::platform::ui::sstv::is_active() ? "Stop" : "Start"); + gtk_widget_add_css_class(sstv_toggle, "nav-button"); + gtk_widget_set_sensitive(sstv_toggle, + ::platform::ui::sstv::is_supported()); + g_signal_connect(sstv_toggle, + "clicked", + G_CALLBACK(onSstvToggleClicked), + &state); + gtk_box_append(GTK_BOX(sstv_header), sstv_toggle); + gtk_box_append(GTK_BOX(sstv), sstv_header); + GtkWidget* sstv_preview = gtk_drawing_area_new(); + gtk_widget_set_size_request(sstv_preview, 360, 230); + gtk_widget_set_hexpand(sstv_preview, TRUE); + gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(sstv_preview), + drawSstvFrame, + nullptr, + nullptr); + gtk_box_append(GTK_BOX(sstv), sstv_preview); + gtk_box_append(GTK_BOX(sstv), + buildDetailRow( + "Progress", + std::to_string(static_cast( + std::clamp(sstv_status.progress, 0.0F, 1.0F) * + 100.0F)) + + "% / " + ::platform::ui::sstv::mode_name())); + const char* saved_path = ::platform::ui::sstv::last_saved_path(); + gtk_box_append(GTK_BOX(sstv), + buildDetailRow("Last image", + saved_path != nullptr && saved_path[0] != '\0' + ? saved_path + : "No decoded image yet")); + + GtkWidget* walkie = makePanel(); + GtkWidget* walkie_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); + GtkWidget* walkie_title = makeLabel("Walkie", "pane-heading"); + gtk_widget_set_hexpand(walkie_title, TRUE); + gtk_box_append(GTK_BOX(walkie_header), walkie_title); + GtkWidget* walkie_toggle = gtk_button_new_with_label( + walkie_status.active ? "Stop session" : "Start session"); + gtk_widget_add_css_class(walkie_toggle, "nav-button"); + gtk_widget_set_sensitive(walkie_toggle, + ::platform::ui::walkie::is_supported()); + g_signal_connect(walkie_toggle, + "clicked", + G_CALLBACK(onWalkieToggleClicked), + &state); + gtk_box_append(GTK_BOX(walkie_header), walkie_toggle); + gtk_box_append(GTK_BOX(walkie), walkie_header); + gtk_box_append(GTK_BOX(walkie), + buildDetailRow("Frequency", + std::to_string(walkie_status.freq_mhz) + + " MHz")); + gtk_box_append(GTK_BOX(walkie), + buildDetailRow("Levels", + "RX " + + std::to_string(walkie_status.rx_level) + + " / TX " + + std::to_string(walkie_status.tx_level))); + gtk_box_append(GTK_BOX(walkie), + buildDetailRow("Volume", + std::to_string( + ::platform::ui::walkie::volume()))); + GtkWidget* walkie_actions = + gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + GtkWidget* ptt = gtk_button_new_with_label(walkie_status.tx + ? "Release PTT" + : "Press PTT"); + GtkWidget* monitor = gtk_button_new_with_label( + walkie_status.monitor_enabled ? "Monitor on" : "Monitor off"); + gtk_widget_add_css_class(ptt, "nav-button"); + gtk_widget_add_css_class(monitor, "nav-button"); + gtk_widget_set_sensitive(ptt, walkie_status.active); + gtk_widget_set_sensitive(monitor, walkie_status.active); + g_signal_connect(ptt, + "clicked", + G_CALLBACK(onWalkiePttClicked), + &state); + g_signal_connect(monitor, + "clicked", + G_CALLBACK(onWalkieMonitorClicked), + &state); + gtk_box_append(GTK_BOX(walkie_actions), ptt); + gtk_box_append(GTK_BOX(walkie_actions), monitor); + gtk_box_append(GTK_BOX(walkie), walkie_actions); + gtk_box_append(GTK_BOX(lower), sstv); + gtk_box_append(GTK_BOX(lower), walkie); + gtk_box_append(GTK_BOX(state.radio_tools_page_box), lower); + + if (!state.radio_tools_status.empty()) + { + gtk_box_append(GTK_BOX(state.radio_tools_page_box), + makeLabel(state.radio_tools_status.c_str(), + "settings-status", + true)); + } +} + +void hideRadioTools(GtkUConsoleAppState&) +{ + ::platform::ui::walkie::set_ptt(false); +} + +void destroyRadioTools(GtkUConsoleAppState& state) +{ + ::platform::ui::walkie::set_ptt(false); + ::platform::ui::walkie::stop(); + ::platform::ui::sstv::stop(); + if (state.radio_sweep_future.valid()) + { + state.radio_sweep_future.wait(); + (void)state.radio_sweep_future.get(); + } + state.radio_sweep_running = false; +} + +void loadExtensionCatalog(GtkUConsoleAppState& state) +{ + if (state.extensions_loaded) + { + return; + } + state.extension_catalog.clear(); + std::string error{}; + state.extensions_loaded = + ::ui::runtime::packs::fetch_catalog(state.extension_catalog, error); + state.extensions_status = state.extensions_loaded + ? std::to_string( + state.extension_catalog.size()) + + " packages available locally." + : error; +} + +const char* packageIdFromButton(GtkButton* button) +{ + return static_cast( + g_object_get_data(G_OBJECT(button), "trailmate-package-id")); +} + +void onExtensionActionClicked(GtkButton* button, gpointer data) +{ + auto& state = *static_cast(data); + const char* package_id = packageIdFromButton(button); + const auto it = std::find_if( + state.extension_catalog.begin(), + state.extension_catalog.end(), + [package_id](const auto& package) + { return package.id == (package_id == nullptr ? "" : package_id); }); + if (it == state.extension_catalog.end()) + { + return; + } + + std::string error{}; + const bool ok = it->installed + ? ::ui::runtime::packs::uninstall_package(*it, error) + : ::ui::runtime::packs::start_install_package(*it, + error); + state.extensions_status = ok + ? it->display_name + + (it->installed ? " uninstalled." + : " installed.") + : error; + state.extensions_loaded = false; + refreshUi(state); +} + +GtkWidget* launchExtensionsLayout(GtkUConsoleAppState& state) +{ + return buildDetailsWorkspace( + "Extensions", + "Desktop package workspace backed by the existing Trail Mate pack repository.", + &state.extensions_page_box); +} + +void refreshExtensionsLogic(GtkUConsoleAppState& state, + const GtkUConsoleRefreshSnapshot&) +{ + loadExtensionCatalog(state); + clearBox(state.extensions_page_box); + + std::size_t installed = 0; + for (const auto& package : state.extension_catalog) + { + installed += package.installed ? 1U : 0U; + } + GtkWidget* metrics = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 6); + gtk_widget_add_css_class(metrics, "metric-strip"); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Catalog", + std::to_string( + state.extension_catalog.size()), + ::ui::runtime::packs::is_supported() + ? "Local package source ready" + : "Package source unavailable", + !::ui::runtime::packs::is_supported())); + gtk_box_append(GTK_BOX(metrics), + makeMetricCard("Installed", + std::to_string(installed), + "Persisted desktop package state")); + gtk_box_append(GTK_BOX(state.extensions_page_box), metrics); + + GtkWidget* catalog = makePanel(); + gtk_box_append(GTK_BOX(catalog), + makeLabel("Available packages", "pane-heading")); + if (state.extension_catalog.empty()) + { + gtk_box_append(GTK_BOX(catalog), + makeLabel("No packages are available.", + "empty-state")); + } + for (const auto& package : state.extension_catalog) + { + GtkWidget* row = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8); + gtk_widget_add_css_class(row, "row"); + GtkWidget* details = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); + gtk_widget_set_hexpand(details, TRUE); + gtk_box_append(GTK_BOX(details), + makeLabel(package.display_name.c_str(), + "row-title")); + gtk_box_append(GTK_BOX(details), + makeLabel((package.version + " / " + + package.package_type + " / " + + formatBytes(package.archive_size_bytes)) + .c_str(), + "row-meta")); + gtk_box_append(GTK_BOX(details), + makeLabel(package.summary.c_str(), + "row-meta", + true)); + gtk_box_append(GTK_BOX(row), details); + GtkWidget* action = gtk_button_new_with_label( + package.installed ? "Uninstall" : "Install"); + gtk_widget_add_css_class(action, "nav-button"); + g_object_set_data_full(G_OBJECT(action), + "trailmate-package-id", + g_strdup(package.id.c_str()), + g_free); + g_signal_connect(action, + "clicked", + G_CALLBACK(onExtensionActionClicked), + &state); + gtk_box_append(GTK_BOX(row), action); + gtk_box_append(GTK_BOX(catalog), row); + } + gtk_box_append(GTK_BOX(state.extensions_page_box), catalog); + if (!state.extensions_status.empty()) + { + gtk_box_append(GTK_BOX(state.extensions_page_box), + makeLabel(state.extensions_status.c_str(), + "settings-status", + true)); + } +} + +} // namespace + +GtkUConsolePageLifecycle makeRadioToolsPageLifecycle() +{ + return {.name = "radio-tools", + .title = "Radio tools", + .onLaunch = launchRadioToolsLayout, + .onShow = nullptr, + .onHide = hideRadioTools, + .onRefresh = refreshRadioToolsLogic, + .onDestroy = destroyRadioTools}; +} + +GtkUConsolePageLifecycle makeExtensionsPageLifecycle() +{ + return {.name = "extensions", + .title = "Extensions", + .onLaunch = launchExtensionsLayout, + .onShow = nullptr, + .onHide = nullptr, + .onRefresh = refreshExtensionsLogic, + .onDestroy = nullptr}; +} + +} // namespace trailmate::uconsole::gtk diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_shell.cpp b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_shell.cpp index 1826833e..be1d9d1f 100644 --- a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_shell.cpp +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_shell.cpp @@ -59,7 +59,13 @@ void setActiveNav(GtkUConsoleAppState& state, const char* page) apply(state.nav_overview, "overview"); apply(state.nav_chat, "chat"); + apply(state.nav_contacts, "contacts"); apply(state.nav_map, "map"); + apply(state.nav_gps, "gps"); + apply(state.nav_team, "team"); + apply(state.nav_tracker, "tracker"); + apply(state.nav_radio_tools, "radio-tools"); + apply(state.nav_extensions, "extensions"); apply(state.nav_hardware, "hardware"); apply(state.nav_data, "data"); apply(state.nav_logs, "logs"); @@ -81,6 +87,36 @@ void onMapClicked(GtkButton*, gpointer data) showPage(*static_cast(data), "map"); } +void onContactsClicked(GtkButton*, gpointer data) +{ + showPage(*static_cast(data), "contacts"); +} + +void onGpsClicked(GtkButton*, gpointer data) +{ + showPage(*static_cast(data), "gps"); +} + +void onTeamClicked(GtkButton*, gpointer data) +{ + showPage(*static_cast(data), "team"); +} + +void onTrackerClicked(GtkButton*, gpointer data) +{ + showPage(*static_cast(data), "tracker"); +} + +void onRadioToolsClicked(GtkButton*, gpointer data) +{ + showPage(*static_cast(data), "radio-tools"); +} + +void onExtensionsClicked(GtkButton*, gpointer data) +{ + showPage(*static_cast(data), "extensions"); +} + void onHardwareClicked(GtkButton*, gpointer data) { showPage(*static_cast(data), "hardware"); @@ -101,24 +137,48 @@ void onSettingsClicked(GtkButton*, gpointer data) showPage(*static_cast(data), "settings"); } +void styleNavigationButton(GtkWidget* button) +{ + gtk_widget_add_css_class(button, "menu-button"); + gtk_widget_set_halign(button, GTK_ALIGN_FILL); + if (GtkWidget* child = gtk_button_get_child(GTK_BUTTON(button)); + child != nullptr && GTK_IS_LABEL(child)) + { + gtk_label_set_xalign(GTK_LABEL(child), 0.0F); + } +} + GtkWidget* buildMenuBar(GtkUConsoleAppState& state) { - GtkWidget* bar = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12); - gtk_widget_add_css_class(bar, "menu-bar"); - gtk_widget_set_hexpand(bar, TRUE); + GtkWidget* bar = gtk_box_new(GTK_ORIENTATION_VERTICAL, 3); + gtk_widget_add_css_class(bar, "navigation-rail"); + gtk_widget_set_size_request(bar, layout_spec::kNavigationRailWidth, -1); + gtk_widget_set_hexpand(bar, FALSE); + gtk_widget_set_vexpand(bar, TRUE); - gtk_box_append(GTK_BOX(bar), makeLabel("Trail Mate", "menu-title")); + GtkWidget* brand = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1); + gtk_widget_add_css_class(brand, "navigation-brand"); + gtk_box_append(GTK_BOX(brand), makeLabel("TRAIL MATE", "menu-title")); + gtk_box_append(GTK_BOX(brand), + makeLabel("uConsole field desk", "navigation-subtitle")); + gtk_box_append(GTK_BOX(bar), brand); + + gtk_box_append(GTK_BOX(bar), + makeLabel("WORKSPACES", "navigation-section-label")); state.nav_overview = gtk_button_new_with_label("Overview"); - gtk_widget_add_css_class(state.nav_overview, "menu-button"); + styleNavigationButton(state.nav_overview); g_signal_connect(state.nav_overview, "clicked", G_CALLBACK(onOverviewClicked), &state); gtk_box_append(GTK_BOX(bar), state.nav_overview); state.nav_chat = gtk_button_new(); - gtk_widget_add_css_class(state.nav_chat, "menu-button"); + styleNavigationButton(state.nav_chat); GtkWidget* chat_nav_child = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); - gtk_box_append(GTK_BOX(chat_nav_child), makeLabel("Chat")); + GtkWidget* chat_nav_label = makeLabel("Chat"); + gtk_widget_set_hexpand(chat_nav_label, TRUE); + gtk_label_set_xalign(GTK_LABEL(chat_nav_label), 0.0F); + gtk_box_append(GTK_BOX(chat_nav_child), chat_nav_label); state.nav_chat_badge = makeLabel("", "menu-badge"); gtk_label_set_xalign(GTK_LABEL(state.nav_chat_badge), 0.5F); gtk_widget_set_visible(state.nav_chat_badge, FALSE); @@ -129,41 +189,100 @@ GtkWidget* buildMenuBar(GtkUConsoleAppState& state) gtk_box_append(GTK_BOX(bar), state.nav_chat); state.nav_map = gtk_button_new_with_label("Map"); - gtk_widget_add_css_class(state.nav_map, "menu-button"); + styleNavigationButton(state.nav_map); g_signal_connect(state.nav_map, "clicked", G_CALLBACK(onMapClicked), &state); gtk_box_append(GTK_BOX(bar), state.nav_map); + gtk_box_append(GTK_BOX(bar), + makeLabel("FIELD", "navigation-section-label")); + + state.nav_contacts = gtk_button_new_with_label("Contacts & nodes"); + styleNavigationButton(state.nav_contacts); + g_signal_connect(state.nav_contacts, + "clicked", + G_CALLBACK(onContactsClicked), + &state); + gtk_box_append(GTK_BOX(bar), state.nav_contacts); + + state.nav_gps = gtk_button_new_with_label("GPS & sky plot"); + styleNavigationButton(state.nav_gps); + g_signal_connect(state.nav_gps, + "clicked", + G_CALLBACK(onGpsClicked), + &state); + gtk_box_append(GTK_BOX(bar), state.nav_gps); + + state.nav_team = gtk_button_new_with_label("Team operations"); + styleNavigationButton(state.nav_team); + g_signal_connect(state.nav_team, + "clicked", + G_CALLBACK(onTeamClicked), + &state); + gtk_box_append(GTK_BOX(bar), state.nav_team); + + state.nav_tracker = gtk_button_new_with_label("Tracker"); + styleNavigationButton(state.nav_tracker); + g_signal_connect(state.nav_tracker, + "clicked", + G_CALLBACK(onTrackerClicked), + &state); + gtk_box_append(GTK_BOX(bar), state.nav_tracker); + + state.nav_radio_tools = gtk_button_new_with_label("Radio tools"); + styleNavigationButton(state.nav_radio_tools); + g_signal_connect(state.nav_radio_tools, + "clicked", + G_CALLBACK(onRadioToolsClicked), + &state); + gtk_box_append(GTK_BOX(bar), state.nav_radio_tools); + + gtk_box_append(GTK_BOX(bar), + makeLabel("SYSTEM", "navigation-section-label")); + state.nav_hardware = gtk_button_new_with_label("Hardware"); - gtk_widget_add_css_class(state.nav_hardware, "menu-button"); + styleNavigationButton(state.nav_hardware); g_signal_connect(state.nav_hardware, "clicked", G_CALLBACK(onHardwareClicked), &state); gtk_box_append(GTK_BOX(bar), state.nav_hardware); - state.nav_data = gtk_button_new_with_label("Data"); - gtk_widget_add_css_class(state.nav_data, "menu-button"); + state.nav_data = gtk_button_new_with_label("Data & maps"); + styleNavigationButton(state.nav_data); g_signal_connect(state.nav_data, "clicked", G_CALLBACK(onDataClicked), &state); gtk_box_append(GTK_BOX(bar), state.nav_data); + state.nav_extensions = gtk_button_new_with_label("Extensions"); + styleNavigationButton(state.nav_extensions); + g_signal_connect(state.nav_extensions, + "clicked", + G_CALLBACK(onExtensionsClicked), + &state); + gtk_box_append(GTK_BOX(bar), state.nav_extensions); + state.nav_logs = gtk_button_new_with_label("Logs"); - gtk_widget_add_css_class(state.nav_logs, "menu-button"); + styleNavigationButton(state.nav_logs); g_signal_connect(state.nav_logs, "clicked", G_CALLBACK(onLogsClicked), &state); gtk_box_append(GTK_BOX(bar), state.nav_logs); state.nav_settings = gtk_button_new_with_label("Settings"); - gtk_widget_add_css_class(state.nav_settings, "menu-button"); + styleNavigationButton(state.nav_settings); g_signal_connect(state.nav_settings, "clicked", G_CALLBACK(onSettingsClicked), &state); gtk_box_append(GTK_BOX(bar), state.nav_settings); GtkWidget* spacer = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); - gtk_widget_set_hexpand(spacer, TRUE); + gtk_widget_set_vexpand(spacer, TRUE); gtk_box_append(GTK_BOX(bar), spacer); - GtkWidget* hint = makeLabel("uConsole Linux", "chip"); - gtk_box_append(GTK_BOX(bar), hint); + GtkWidget* hint_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); + gtk_widget_add_css_class(hint_box, "navigation-footer"); + gtk_box_append(GTK_BOX(hint_box), + makeLabel("DESKTOP MODE", "navigation-footer-title")); + gtk_box_append(GTK_BOX(hint_box), + makeLabel("Keyboard + pointer", "navigation-subtitle")); + gtk_box_append(GTK_BOX(bar), hint_box); return bar; } @@ -232,13 +351,13 @@ GtkWidget* buildRoot(GtkUConsoleAppState& state) GtkWidget* root = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); gtk_widget_set_hexpand(root, TRUE); gtk_widget_set_vexpand(root, TRUE); - gtk_box_append(GTK_BOX(root), buildMenuBar(state)); GtkWidget* body = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); gtk_widget_add_css_class(body, "body"); gtk_widget_set_hexpand(body, TRUE); gtk_widget_set_vexpand(body, TRUE); gtk_widget_set_overflow(body, GTK_OVERFLOW_HIDDEN); + gtk_box_append(GTK_BOX(body), buildMenuBar(state)); state.page_lifecycle = buildGtkUConsolePageRegistry(); state.stack = gtk_stack_new(); @@ -255,28 +374,7 @@ GtkWidget* buildRoot(GtkUConsoleAppState& state) } gtk_box_append(GTK_BOX(body), state.stack); - GtkWidget* body_viewport = gtk_scrolled_window_new(); - gtk_widget_set_hexpand(body_viewport, TRUE); - gtk_widget_set_vexpand(body_viewport, TRUE); - gtk_widget_set_size_request(body_viewport, 1, 1); - gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(body_viewport), - GTK_POLICY_EXTERNAL, - GTK_POLICY_EXTERNAL); - gtk_scrolled_window_set_min_content_height( - GTK_SCROLLED_WINDOW(body_viewport), - 1); - gtk_scrolled_window_set_min_content_width( - GTK_SCROLLED_WINDOW(body_viewport), - 1); - gtk_scrolled_window_set_propagate_natural_height( - GTK_SCROLLED_WINDOW(body_viewport), - FALSE); - gtk_scrolled_window_set_propagate_natural_width( - GTK_SCROLLED_WINDOW(body_viewport), - FALSE); - gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(body_viewport), body); - - gtk_box_append(GTK_BOX(root), body_viewport); + gtk_box_append(GTK_BOX(root), body); gtk_box_append(GTK_BOX(root), buildStatusBar(state)); showPage(state, "overview"); return root; @@ -284,9 +382,11 @@ GtkWidget* buildRoot(GtkUConsoleAppState& state) void refreshUi(GtkUConsoleAppState& state) { - const GtkUConsoleRefreshSnapshot snapshot{ + GtkUConsoleRefreshSnapshot snapshot{ .dashboard = state.dashboard_model.snapshot(), .map = state.map_model.snapshot()}; + serviceMapDownloads(state, snapshot.map); + snapshot.map = state.map_model.snapshot(); setStatusChip(state.status_aio2, findHardware(snapshot.dashboard, "AIO2")); setStatusChip(state.status_lora, findHardware(snapshot.dashboard, "LoRa")); setStatusChip(state.status_gps, findHardware(snapshot.dashboard, "GPS")); diff --git a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_style.cpp b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_style.cpp index 3c9b6e1c..b727c8ae 100644 --- a/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_style.cpp +++ b/apps/linux_uconsole_gtk/src/platform/gtk/gtk_uconsole_style.cpp @@ -10,24 +10,52 @@ window { background: #e8ebe5; color: #1b211e; } -.menu-bar { +.navigation-rail { background: #1d2221; color: #f6f8f4; - padding: 3px 8px; - min-height: 28px; - border-bottom: 1px solid #111514; + padding: 10px 9px 9px 9px; + border-right: 1px solid #111514; +} +.navigation-brand { + padding: 2px 7px 8px 7px; +} +.navigation-subtitle { + color: #9da9a3; + font-size: 11px; +} +.navigation-section-label { + color: #7f8e87; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.08em; + padding: 7px 7px 2px 7px; +} +.navigation-footer { + border-top: 1px solid #39413e; + padding: 7px 7px 2px 7px; +} +.navigation-footer-title { + color: #d5ddd8; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.06em; } .menu-title { color: #f8faf7; font-weight: 700; - padding: 0 6px; + font-size: 16px; } .menu-button { background: transparent; color: #dce3de; border: 1px solid transparent; - border-radius: 4px; - padding: 3px 8px; + border-radius: 5px; + padding: 5px 9px; + min-height: 18px; +} +.menu-button:hover { + background: #2a312f; + border-color: #3d4844; } .menu-badge { background: #dfeee7; @@ -63,9 +91,9 @@ window { padding: 4px 9px; } .nav-button-active { - background: #dfeee7; - color: #12241e; - border-color: #6f9f8d; + background: #d9e8df; + color: #10271f; + border-color: #7ca18f; font-weight: 700; } .workbench { diff --git a/apps/linux_uconsole_gtk/tools/uconsole_sdl_screenshot_capture.cpp b/apps/linux_uconsole_gtk/tools/uconsole_sdl_screenshot_capture.cpp new file mode 100644 index 00000000..7717b110 --- /dev/null +++ b/apps/linux_uconsole_gtk/tools/uconsole_sdl_screenshot_capture.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include + +#include "platform/desktop/sdl_window_presenter.h" +#include "uconsole/uconsole_desktop_shell.h" + +namespace +{ + +struct CaptureTarget +{ + const char* id = nullptr; + const char* filename = nullptr; + int nav_steps = 0; + char shortcut = '\0'; +}; + +constexpr CaptureTarget kTargets[] = { + {"overview", "overview.png", 0, '\0'}, + {"chat", "chat.png", 1, '\0'}, + {"map", "map.png", 2, '\0'}, + {"gps", "gps-sky-plot.png", 4, '\0'}, + {"radio-tools", "radio-tools.png", 7, '\0'}, + {"extensions", "extensions.png", 10, '\0'}, + {"sidebar-collapsed", "sidebar-collapsed.png", 2, '\\'}, + {"shortcut-help", "shortcut-help.png", 0, 'h'}, +}; + +void setEnvironment(const char* name, const std::string& value) +{ +#if defined(_WIN32) + if (_putenv_s(name, value.c_str()) != 0) + { + throw std::runtime_error(std::string("failed to set ") + name); + } +#else + if (setenv(name, value.c_str(), 1) != 0) + { + throw std::runtime_error(std::string("failed to set ") + name); + } +#endif +} + +void configureCaptureRuntime() +{ + const std::filesystem::path runtime_root = + std::filesystem::current_path() / ".codex-build" / + "uconsole-sdl-runtime"; + setEnvironment("TRAIL_MATE_RUNTIME_MODE", "demo"); + setEnvironment("TRAIL_MATE_SETTINGS_ROOT", + (runtime_root / "settings").string()); + setEnvironment("TRAIL_MATE_SD_ROOT", (runtime_root / "sd").string()); + setEnvironment("TRAIL_MATE_CACHE_ROOT", (runtime_root / "cache").string()); +} + +void captureTarget(const CaptureTarget& target, + const std::filesystem::path& output_directory) +{ + const std::filesystem::path screenshot = + output_directory / target.filename; + trailmate::uconsole::desktop::SdlWindowPresenter presenter{ + {.width = 1280, + .height = 720, + .scale = 1, + .fullscreen = false, + .title = std::string("Trail Mate uConsole SDL - ") + target.id, + .screenshot_path = screenshot.string(), + .screenshot_after_frames = 45, + .initial_nav_steps = target.nav_steps, + .initial_shortcut = target.shortcut, + .hidden = true}}; + trailmate::uconsole::runUConsoleShell( + presenter, {.width = 1280, .height = 720, .frame_time_ms = 16}); + if (!std::filesystem::exists(screenshot)) + { + throw std::runtime_error("SDL capture did not create " + + screenshot.string()); + } + std::cout << target.id << ": " << screenshot.string() << '\n'; +} + +} // namespace + +int main(int argc, char** argv) +{ + try + { + const std::filesystem::path output_directory = + argc > 1 ? std::filesystem::path(argv[1]) + : std::filesystem::path("docs/specs/images/uconsole-sdl"); + std::filesystem::create_directories(output_directory); + configureCaptureRuntime(); + for (const auto& target : kTargets) + { + captureTarget(target, output_directory); + } + return 0; + } + catch (const std::exception& error) + { + std::cerr << "uConsole SDL capture failed: " << error.what() << '\n'; + return 1; + } +} diff --git a/docs/specs/images/uconsole-sdl/chat.png b/docs/specs/images/uconsole-sdl/chat.png new file mode 100644 index 0000000000000000000000000000000000000000..931da95915a9258dde9a2bbfdac4b0b56d12ee64 GIT binary patch literal 26836 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 literal 0 HcmV?d00001 diff --git a/docs/specs/images/uconsole-sdl/extensions.png b/docs/specs/images/uconsole-sdl/extensions.png new file mode 100644 index 0000000000000000000000000000000000000000..ef18dc0870fdc45d3c3599b89904ea9f9468b3fa GIT binary patch literal 27531 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 literal 0 HcmV?d00001 diff --git a/docs/specs/images/uconsole-sdl/gps-sky-plot.png b/docs/specs/images/uconsole-sdl/gps-sky-plot.png new file mode 100644 index 0000000000000000000000000000000000000000..0788078ca9e604eaef2aa2370cb71076a32cbc89 GIT binary patch literal 97118 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 literal 0 HcmV?d00001 diff --git a/docs/specs/images/uconsole-sdl/overview.png b/docs/specs/images/uconsole-sdl/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..c5633e8952554be115dc1c8ef27b241643b595f4 GIT binary patch literal 18624 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

r$L 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 literal 0 HcmV?d00001 diff --git a/docs/specs/images/uconsole-sdl/sidebar-collapsed.png b/docs/specs/images/uconsole-sdl/sidebar-collapsed.png new file mode 100644 index 0000000000000000000000000000000000000000..237afff10ff46d80d4668cbf4e69be42dde2086a GIT binary patch literal 18301 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, config_.map_track_interval)); + platform::ui::tracker::set_format( + static_cast( + std::min(config_.map_track_format, 2U))); + platform::ui::tracker::set_auto_recording(config_.map_track_enabled); (void)platform::ui::gps::diagnostics(); } diff --git a/platform/linux/common/src/platform/ui/lora_runtime.cpp b/platform/linux/common/src/platform/ui/lora_runtime.cpp index 33f49d99..d8a215c4 100644 --- a/platform/linux/common/src/platform/ui/lora_runtime.cpp +++ b/platform/linux/common/src/platform/ui/lora_runtime.cpp @@ -46,6 +46,8 @@ Clock::time_point s_started_at = Clock::now(); float s_freq_mhz = 0.0f; ReceiveConfig s_config{}; bool s_real_driver = false; +bool s_restore_config_valid = false; +::platform::linux_runtime::Sx126xLoRaConfig s_restore_config{}; std::string s_capability_message{}; bool env_flag_or_default(const char* name, bool fallback) @@ -177,6 +179,11 @@ bool acquire() std::lock_guard lock(s_mutex); auto& radio = ::platform::linux_runtime::Sx126xRadio::instance(); s_real_driver = radio.acquire(); + s_restore_config_valid = s_real_driver; + if (s_restore_config_valid) + { + s_restore_config = radio.appliedLoRaConfig(); + } s_acquired = s_real_driver || simulated_lora_enabled(); s_started_at = Clock::now(); return s_acquired; @@ -234,9 +241,15 @@ void release() s_configured = false; if (s_real_driver) { + if (s_restore_config_valid) + { + (void)::platform::linux_runtime::Sx126xRadio::instance() + .configureLoRa(s_restore_config); + } ::platform::linux_runtime::Sx126xRadio::instance().release(); } s_real_driver = false; + s_restore_config_valid = false; s_freq_mhz = 0.0f; } diff --git a/platform/linux/common/src/platform/ui/tracker_runtime.cpp b/platform/linux/common/src/platform/ui/tracker_runtime.cpp index 939e0181..cba896d7 100644 --- a/platform/linux/common/src/platform/ui/tracker_runtime.cpp +++ b/platform/linux/common/src/platform/ui/tracker_runtime.cpp @@ -24,6 +24,7 @@ namespace bool s_recording = false; std::string s_current_path{}; bool s_auto_recording = false; +bool s_started_automatically = false; uint32_t s_interval_seconds = 60; bool s_distance_only = false; Format s_format = Format::GPX; @@ -273,6 +274,7 @@ void stop_recording() } } s_recording = false; + s_started_automatically = false; s_current_path.clear(); s_last_sample_ms = 0; s_has_last_point = false; @@ -280,6 +282,11 @@ void stop_recording() void poll() { + if (s_auto_recording && !s_recording) + { + s_started_automatically = start_recording(); + } + const uint32_t now_ms = sys::millis_now(); if (!should_sample_now(now_ms)) { @@ -347,7 +354,14 @@ const char* track_dir() return s_track_dir_cache.c_str(); } -void set_auto_recording(bool enabled) { s_auto_recording = enabled; } +void set_auto_recording(bool enabled) +{ + s_auto_recording = enabled; + if (!enabled && s_started_automatically) + { + stop_recording(); + } +} void set_interval_seconds(uint32_t seconds) { s_interval_seconds = seconds; } void set_distance_only(bool enabled) { s_distance_only = enabled; } void set_format(Format format) { s_format = format; } diff --git a/platform/linux/uconsole/src/uconsole_desktop_shell.cpp b/platform/linux/uconsole/src/uconsole_desktop_shell.cpp index c41aa664..06d65e4c 100644 --- a/platform/linux/uconsole/src/uconsole_desktop_shell.cpp +++ b/platform/linux/uconsole/src/uconsole_desktop_shell.cpp @@ -2,7 +2,9 @@ #include #include +#include #include +#include #include #include #include @@ -34,15 +36,59 @@ constexpr int kMetricCount = 4; constexpr int kCapabilityRows = 5; constexpr int kChatConversationRows = 7; constexpr int kChatMessageRows = 7; +constexpr std::uint32_t kLvglFunctionKeyF1 = 0x110001U; -constexpr std::array kNavLabels{ +namespace embedded_palette +{ + +constexpr std::uint32_t kPageBg = 0xFFF3DF; +constexpr std::uint32_t kSurface = 0xFFF7E9; +constexpr std::uint32_t kSurfaceAlt = 0xFFF0D3; +constexpr std::uint32_t kPanelBg = 0xFAF0D8; +constexpr std::uint32_t kBorder = 0xD9B06A; +constexpr std::uint32_t kSeparator = 0xE8D2AB; +constexpr std::uint32_t kAccent = 0xEBA341; +constexpr std::uint32_t kAccentDark = 0xC98118; +constexpr std::uint32_t kHeaderText = 0x2A1A05; +constexpr std::uint32_t kText = 0x3A2A1A; +constexpr std::uint32_t kTextWarm = 0x6B4A1E; +constexpr std::uint32_t kTextMuted = 0x6A5646; +constexpr std::uint32_t kTextDim = 0x8A6A3A; +constexpr std::uint32_t kWhite = 0xFFFFFF; +constexpr std::uint32_t kStatusGreen = 0x5BAF4A; +constexpr std::uint32_t kStatusBlue = 0x2F6FD6; +constexpr std::uint32_t kWarn = 0xB94A2C; +constexpr std::uint32_t kError = 0xCC0000; +constexpr std::uint32_t kSoftAmber = 0xF3D39C; +constexpr std::uint32_t kSoftBlue = 0xDCE8F7; +constexpr std::uint32_t kSoftGreen = 0xDCEFD8; +constexpr std::uint32_t kSoftWarn = 0xF5D9D1; +constexpr std::uint32_t kMapBg = 0xF6E7C8; +constexpr std::uint32_t kPlotBg = 0xF2E4C8; +constexpr std::uint32_t kMapTile1 = 0xEAD9B2; +constexpr std::uint32_t kMapTile2 = 0xF2E4C8; +constexpr std::uint32_t kMapTile3 = 0xE4D2AA; +constexpr std::uint32_t kMapTile4 = 0xE0C894; +constexpr std::uint32_t kMapTile5 = 0xEEDBB4; +constexpr std::uint32_t kMapTile6 = 0xE7D3A4; +constexpr std::uint32_t kMapTile7 = 0xF3E2BE; +constexpr std::uint32_t kMapTile8 = 0xDEC58F; + +} // namespace embedded_palette + +constexpr std::array kNavLabels{ "Overview", "Chat", - "Contacts", "Map", + "Contacts", + "GPS & sky plot", "Team", - "Data", - "Diagnostics", + "Tracker", + "Radio tools", + "Hardware", + "Data & maps", + "Extensions", + "Logs", "Settings", }; @@ -52,11 +98,16 @@ enum class Section : std::uint8_t { Overview = 0, Chat, - Contacts, Map, + Contacts, + Gps, Team, + Tracker, + RadioTools, + Hardware, Data, - Diagnostics, + Extensions, + Logs, Settings, }; @@ -100,6 +151,8 @@ struct QueuedKeyEvent return LV_KEY_UP; case InputKey::Down: return LV_KEY_DOWN; + case InputKey::F1: + return kLvglFunctionKeyF1; case InputKey::Unknown: case InputKey::Fn: case InputKey::Ctrl: @@ -143,7 +196,9 @@ void resetBox(lv_obj_t* obj) lv_obj_clear_flag(obj, LV_OBJ_FLAG_SCROLLABLE); } -void applyPanel(lv_obj_t* obj, std::uint32_t bg, std::uint32_t border = 0xD3D9D2) +void applyPanel(lv_obj_t* obj, + std::uint32_t bg, + std::uint32_t border = embedded_palette::kBorder) { resetBox(obj); lv_obj_set_style_bg_color(obj, color(bg), 0); @@ -201,15 +256,25 @@ void setLabel(lv_obj_t* label, const char* text) case Section::Chat: return "Chat workspace"; case Section::Contacts: - return "Contacts workspace"; + return "Contacts & nodes"; case Section::Map: - return "Map workspace"; + return "Offline map workspace"; + case Section::Gps: + return "GPS & sky plot"; case Section::Team: - return "Team workspace"; + return "Team operations"; + case Section::Tracker: + return "Track recorder"; + case Section::RadioTools: + return "Radio tools"; + case Section::Hardware: + return "Hardware status"; case Section::Data: - return "Data workspace"; - case Section::Diagnostics: - return "Diagnostics workspace"; + return "Data & offline maps"; + case Section::Extensions: + return "Extensions"; + case Section::Logs: + return "Packet and runtime logs"; case Section::Settings: return "Settings workspace"; } @@ -225,15 +290,25 @@ void setLabel(lv_obj_t* label, const char* text) case Section::Chat: return "Conversation list and message activity preview."; case Section::Contacts: - return "Known nodes, nearby peers and trust status preview."; + return "Saved identities, nearby peers, trust and node actions."; case Section::Map: - return "Map canvas slot reserved for Linux package workflows."; + return "Cached tiles, background downloads, contours and field overlays."; + case Section::Gps: + return "Live receiver health, satellite geometry and fix details."; case Section::Team: - return "Roster, pairing and activity slot for team operations."; + return "Shared field state, team chat and location coordination."; + case Section::Tracker: + return "Persistent GPX, CSV or binary recording on Linux storage."; + case Section::RadioTools: + return "Energy sweep, SSTV receiver and walkie controls in one workbench."; + case Section::Hardware: + return "uConsole modules and shared capability status."; case Section::Data: - return "Import/export and local storage jobs will surface here."; - case Section::Diagnostics: - return "Runtime logs, capability state and hardware checks."; + return "Map cache, downloads, tracks and local application storage."; + case Section::Extensions: + return "Install and remove packages from the Trail Mate catalog."; + case Section::Logs: + return "Inspect decoded LoRa, GPS and MQTT traffic."; case Section::Settings: return "Grouped configuration entrypoint for Linux targets."; } @@ -326,6 +401,10 @@ class UConsoleDesktopShell { for (const auto& event : events) { + if (handleGlobalShortcut(event)) + { + continue; + } const std::uint32_t mapped = mapInputEvent(event); if (mapped == 0U) continue; key_events_.push_back({mapped, LV_INDEV_STATE_PRESSED}); @@ -457,17 +536,178 @@ class UConsoleDesktopShell shell->refreshChatWorkspace(true); } + bool handleGlobalShortcut(const InputEvent& event) + { + if (event.key == InputKey::F1) + { + toggleShortcutOverlay(); + return true; + } + + if (event.key == InputKey::Power) + { + if (shortcuts_visible_) + { + toggleShortcutOverlay(); + return true; + } + if (active_section_ != Section::Overview) + { + selectSection(Section::Overview); + return true; + } + return false; + } + + if (event.key != InputKey::Character || event.text == '\0') + { + return false; + } + + const bool editing_chat = + active_section_ == Section::Chat && group_ != nullptr && + lv_group_get_focused(group_) == chat_input_; + if (editing_chat) + { + return false; + } + + const char key = static_cast( + std::tolower(static_cast(event.text))); + if (shortcuts_visible_) + { + if (key == 'h' || key == '?') + { + toggleShortcutOverlay(); + } + return true; + } + + switch (key) + { + case 'h': + case '?': + toggleShortcutOverlay(); + return true; + case '\\': + toggleSidebar(); + return true; + case '[': + cycleSection(-1); + return true; + case ']': + cycleSection(1); + return true; + case 'o': + selectSection(Section::Overview); + return true; + case 'c': + selectSection(Section::Chat); + return true; + case 'm': + selectSection(Section::Map); + return true; + case 'n': + selectSection(Section::Contacts); + return true; + case 'g': + selectSection(Section::Gps); + return true; + case 't': + selectSection(Section::Team); + return true; + case 'k': + selectSection(Section::Tracker); + return true; + case 'r': + selectSection(Section::RadioTools); + return true; + case 'w': + selectSection(Section::Hardware); + return true; + case 'd': + selectSection(Section::Data); + return true; + case 'e': + selectSection(Section::Extensions); + return true; + case 'l': + selectSection(Section::Logs); + return true; + case 's': + selectSection(Section::Settings); + return true; + default: + return false; + } + } + + void cycleSection(int delta) + { + const int section_count = static_cast(kNavLabels.size()); + const int current = static_cast(active_section_); + const int next = (current + delta + section_count) % section_count; + selectSection(static_cast

(next)); + } + + void toggleSidebar() + { + if (sidebar_ == nullptr) + { + return; + } + sidebar_collapsed_ = !sidebar_collapsed_; + if (sidebar_collapsed_) + { + lv_obj_add_flag(sidebar_, LV_OBJ_FLAG_HIDDEN); + } + else + { + lv_obj_clear_flag(sidebar_, LV_OBJ_FLAG_HIDDEN); + const auto index = static_cast(active_section_); + if (index < nav_buttons_.size() && nav_buttons_[index] != nullptr) + { + lv_group_focus_obj(nav_buttons_[index]); + } + } + refreshFooter(); + } + + void toggleShortcutOverlay() + { + if (shortcut_overlay_ == nullptr) + { + return; + } + shortcuts_visible_ = !shortcuts_visible_; + if (shortcuts_visible_) + { + lv_obj_clear_flag(shortcut_overlay_, LV_OBJ_FLAG_HIDDEN); + lv_obj_move_foreground(shortcut_overlay_); + } + else + { + lv_obj_add_flag(shortcut_overlay_, LV_OBJ_FLAG_HIDDEN); + } + } + void selectSection(Section section) { active_section_ = section; refreshNavStyles(); setLabel(workspace_title_, sectionTitle(active_section_)); setLabel(workspace_subtitle_, sectionSubtitle(active_section_)); + if (active_section_ != Section::Overview && + active_section_ != Section::Chat) + { + rebuildDesktopPage(); + } refreshSectionVisibility(); if (active_section_ == Section::Chat) { refreshChatWorkspace(true); } + refreshFooter(); } void buildUi() @@ -475,7 +715,7 @@ class UConsoleDesktopShell lv_obj_t* root = lv_scr_act(); lv_obj_clean(root); resetBox(root); - lv_obj_set_style_bg_color(root, color(0xECEFEA), 0); + lv_obj_set_style_bg_color(root, color(embedded_palette::kPageBg), 0); lv_obj_set_style_bg_opa(root, LV_OPA_COVER, 0); lv_obj_set_style_pad_all(root, 0, 0); lv_obj_set_flex_flow(root, LV_FLEX_FLOW_COLUMN); @@ -488,8 +728,8 @@ class UConsoleDesktopShell applyTransparent(body); lv_obj_set_width(body, LV_PCT(100)); lv_obj_set_flex_grow(body, 1); - lv_obj_set_style_pad_all(body, 14, 0); - lv_obj_set_style_pad_column(body, 14, 0); + lv_obj_set_style_pad_all(body, 10, 0); + lv_obj_set_style_pad_column(body, 10, 0); lv_obj_set_flex_flow(body, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(body, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); @@ -497,6 +737,8 @@ class UConsoleDesktopShell buildSidebar(body); buildWorkspace(body); buildStatusPanel(body); + buildBottomBar(root); + buildShortcutOverlay(root); selectSection(Section::Overview); } @@ -506,34 +748,183 @@ class UConsoleDesktopShell lv_obj_t* bar = lv_obj_create(root); resetBox(bar); lv_obj_set_width(bar, LV_PCT(100)); - lv_obj_set_height(bar, 58); - lv_obj_set_style_bg_color(bar, color(0x202426), 0); + lv_obj_set_height(bar, 44); + lv_obj_set_style_bg_color(bar, color(embedded_palette::kAccent), 0); lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, 0); - lv_obj_set_style_pad_left(bar, 18, 0); - lv_obj_set_style_pad_right(bar, 18, 0); - lv_obj_set_style_pad_top(bar, 8, 0); - lv_obj_set_style_pad_bottom(bar, 8, 0); - lv_obj_set_style_pad_column(bar, 12, 0); + lv_obj_set_style_pad_left(bar, 14, 0); + lv_obj_set_style_pad_right(bar, 14, 0); + lv_obj_set_style_pad_top(bar, 5, 0); + lv_obj_set_style_pad_bottom(bar, 5, 0); + lv_obj_set_style_pad_column(bar, 8, 0); lv_obj_set_flex_flow(bar, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(bar, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); lv_obj_t* title_wrap = lv_obj_create(bar); applyTransparent(title_wrap); - lv_obj_set_height(title_wrap, LV_SIZE_CONTENT); + lv_obj_set_height(title_wrap, LV_PCT(100)); lv_obj_set_flex_grow(title_wrap, 1); - lv_obj_set_flex_flow(title_wrap, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_flow(title_wrap, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(title_wrap, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + + createLabel(title_wrap, "Trail Mate uConsole", &lv_font_montserrat_16, + embedded_palette::kHeaderText); + } + + void buildBottomBar(lv_obj_t* root) + { + lv_obj_t* bar = lv_obj_create(root); + resetBox(bar); + lv_obj_set_width(bar, LV_PCT(100)); + lv_obj_set_height(bar, 30); + lv_obj_set_style_bg_color(bar, + color(embedded_palette::kSurfaceAlt), 0); + lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, 0); + lv_obj_set_style_border_side(bar, LV_BORDER_SIDE_TOP, 0); + lv_obj_set_style_border_width(bar, 1, 0); + lv_obj_set_style_border_color(bar, + color(embedded_palette::kBorder), 0); + lv_obj_set_style_pad_left(bar, 12, 0); + lv_obj_set_style_pad_right(bar, 12, 0); + lv_obj_set_style_pad_column(bar, 10, 0); + lv_obj_set_flex_flow(bar, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(bar, LV_FLEX_ALIGN_START, + LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + + footer_context_label_ = + createLabel(bar, "Overview", &lv_font_montserrat_12, + embedded_palette::kTextWarm); + lv_obj_set_width(footer_context_label_, 190); + + footer_status_label_ = + createLabel(bar, "Starting services...", &lv_font_montserrat_12, + embedded_palette::kTextMuted); + lv_obj_set_flex_grow(footer_status_label_, 1); + lv_obj_set_style_text_align(footer_status_label_, + LV_TEXT_ALIGN_CENTER, 0); + + footer_shortcuts_label_ = + createLabel(bar, "", &lv_font_montserrat_12, + embedded_palette::kText); + lv_obj_set_width(footer_shortcuts_label_, 470); + lv_obj_set_style_text_align(footer_shortcuts_label_, + LV_TEXT_ALIGN_RIGHT, 0); + } + + void buildShortcutOverlay(lv_obj_t* root) + { + shortcut_overlay_ = lv_obj_create(root); + resetBox(shortcut_overlay_); + lv_obj_add_flag(shortcut_overlay_, LV_OBJ_FLAG_FLOATING); + lv_obj_set_size(shortcut_overlay_, LV_PCT(100), LV_PCT(100)); + lv_obj_set_pos(shortcut_overlay_, 0, 0); + lv_obj_set_style_bg_color(shortcut_overlay_, + color(embedded_palette::kHeaderText), 0); + lv_obj_set_style_bg_opa(shortcut_overlay_, LV_OPA_60, 0); + + lv_obj_t* card = lv_obj_create(shortcut_overlay_); + applyPanel(card, embedded_palette::kSurface, + embedded_palette::kBorder); + lv_obj_set_size(card, 690, 430); + lv_obj_set_style_pad_all(card, 18, 0); + lv_obj_set_style_pad_row(card, 14, 0); + lv_obj_set_flex_flow(card, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(card, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); + lv_obj_center(card); - createLabel(title_wrap, "Trail Mate uConsole", &lv_font_montserrat_20, - 0xF7F8F4); - createLabel(title_wrap, "Linux desktop-class target", - &lv_font_montserrat_12, 0xAAB2AF); + createLabel(card, "uConsole keyboard shortcuts", + &lv_font_montserrat_20, embedded_palette::kText); + createLabel(card, + "Global shortcuts are disabled while typing in the chat " + "composer.", + &lv_font_montserrat_12, embedded_palette::kTextMuted); - top_mesh_label_ = createChip(bar, "Mesh: -", 0x2F5D62, 0xDDF8F0); - top_node_label_ = createChip(bar, "Node: -", 0x584A1F, 0xFFF1BF); - top_unread_label_ = createChip(bar, "Unread: 0", 0x4F3B4D, 0xF8DDF2); + lv_obj_t* columns = lv_obj_create(card); + applyTransparent(columns); + lv_obj_set_width(columns, LV_PCT(100)); + lv_obj_set_flex_grow(columns, 1); + lv_obj_set_style_pad_column(columns, 30, 0); + lv_obj_set_flex_flow(columns, LV_FLEX_FLOW_ROW); + + lv_obj_t* navigation = lv_obj_create(columns); + applyTransparent(navigation); + lv_obj_set_height(navigation, LV_PCT(100)); + lv_obj_set_flex_grow(navigation, 1); + lv_obj_set_style_pad_row(navigation, 10, 0); + lv_obj_set_flex_flow(navigation, LV_FLEX_FLOW_COLUMN); + createLabel(navigation, "NAVIGATION", &lv_font_montserrat_12, + embedded_palette::kTextDim); + lv_obj_t* navigation_help = createLabel( + navigation, + "Up / Down / Tab Move focus\n" + "Enter / Right Open or activate\n" + "Esc Return to overview\n" + "[ / ] Previous / next workspace\n" + "\\ Collapse navigation\n" + "F1 / H Toggle this help", + &lv_font_montserrat_14, embedded_palette::kText, + LV_LABEL_LONG_WRAP); + lv_obj_set_width(navigation_help, LV_PCT(100)); + lv_obj_set_style_text_line_space(navigation_help, 9, 0); + + lv_obj_t* workspaces = lv_obj_create(columns); + applyTransparent(workspaces); + lv_obj_set_height(workspaces, LV_PCT(100)); + lv_obj_set_flex_grow(workspaces, 1); + lv_obj_set_style_pad_row(workspaces, 10, 0); + lv_obj_set_flex_flow(workspaces, LV_FLEX_FLOW_COLUMN); + createLabel(workspaces, "DIRECT WORKSPACES", &lv_font_montserrat_12, + embedded_palette::kTextDim); + lv_obj_t* workspace_help = createLabel( + workspaces, + "O Overview C Chat\n" + "M Map N Contacts\n" + "G GPS T Team\n" + "K Tracker R Radio tools\n" + "W Hardware D Data & maps\n" + "E Extensions L Logs\n" + "S Settings", + &lv_font_montserrat_14, embedded_palette::kText, + LV_LABEL_LONG_WRAP); + lv_obj_set_width(workspace_help, LV_PCT(100)); + lv_obj_set_style_text_line_space(workspace_help, 9, 0); + + createLabel(card, + "Shortcut model follows the keyboard-first Pager/T-Deck " + "interaction style.", + &lv_font_montserrat_12, embedded_palette::kTextMuted); + + lv_obj_add_flag(shortcut_overlay_, LV_OBJ_FLAG_HIDDEN); + } + + [[nodiscard]] const char* shortcutHint() const noexcept + { + switch (active_section_) + { + case Section::Chat: + return "\\ Nav | Tab Focus | Enter Open/Send | F1 Help"; + case Section::Map: + return "\\ Nav | M Map | [ ] Workspace | F1 Help"; + case Section::Overview: + return "\\ Nav | C Chat | M Map | [ ] Page | F1 Help"; + default: + return "\\ Nav | Enter Open | [ ] Workspace | F1 Help"; + } + } + + void refreshFooter() + { + const auto index = static_cast(active_section_); + std::string context = + index < kNavLabels.size() ? kNavLabels[index] : "Workspace"; + if (sidebar_collapsed_) + { + context += " / navigation hidden"; + } + setLabel(footer_context_label_, context); + setLabel(footer_shortcuts_label_, shortcutHint()); } lv_obj_t* createChip(lv_obj_t* parent, @@ -543,12 +934,12 @@ class UConsoleDesktopShell { lv_obj_t* chip = lv_obj_create(parent); resetBox(chip); - lv_obj_set_size(chip, LV_SIZE_CONTENT, 32); + lv_obj_set_size(chip, LV_SIZE_CONTENT, 26); lv_obj_set_style_bg_color(chip, color(bg), 0); lv_obj_set_style_bg_opa(chip, LV_OPA_COVER, 0); lv_obj_set_style_radius(chip, 6, 0); - lv_obj_set_style_pad_left(chip, 10, 0); - lv_obj_set_style_pad_right(chip, 10, 0); + lv_obj_set_style_pad_left(chip, 8, 0); + lv_obj_set_style_pad_right(chip, 8, 0); lv_obj_t* label = createLabel(chip, text, &lv_font_montserrat_12, fg); lv_obj_center(label); @@ -558,19 +949,36 @@ class UConsoleDesktopShell void buildSidebar(lv_obj_t* parent) { sidebar_ = lv_obj_create(parent); - applyPanel(sidebar_, 0x252A29, 0x252A29); - lv_obj_set_width(sidebar_, 220); + applyPanel(sidebar_, embedded_palette::kSurfaceAlt, + embedded_palette::kBorder); + lv_obj_set_width(sidebar_, 168); lv_obj_set_height(sidebar_, LV_PCT(100)); - lv_obj_set_style_pad_all(sidebar_, 12, 0); - lv_obj_set_style_pad_row(sidebar_, 8, 0); + lv_obj_set_style_pad_all(sidebar_, 7, 0); + lv_obj_set_style_pad_row(sidebar_, 2, 0); lv_obj_set_flex_flow(sidebar_, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(sidebar_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); - createLabel(sidebar_, "Workspace", &lv_font_montserrat_14, 0xE8EDE8); + createLabel(sidebar_, "TRAIL MATE", &lv_font_montserrat_16, + embedded_palette::kText); for (std::size_t index = 0; index < kNavLabels.size(); ++index) { + if (index == 0) + { + createLabel(sidebar_, "WORKSPACES", &lv_font_montserrat_10, + embedded_palette::kTextDim); + } + else if (index == 3) + { + createLabel(sidebar_, "FIELD", &lv_font_montserrat_10, + embedded_palette::kTextDim); + } + else if (index == 8) + { + createLabel(sidebar_, "SYSTEM", &lv_font_montserrat_10, + embedded_palette::kTextDim); + } nav_bindings_[index] = {this, static_cast
(index)}; nav_buttons_[index] = createNavButton(sidebar_, kNavLabels[index], @@ -585,18 +993,19 @@ class UConsoleDesktopShell lv_obj_t* button = lv_btn_create(parent); resetBox(button); lv_obj_set_width(button, LV_PCT(100)); - lv_obj_set_height(button, 42); - lv_obj_set_style_radius(button, 6, 0); - lv_obj_set_style_pad_left(button, 10, 0); - lv_obj_set_style_pad_right(button, 10, 0); - lv_obj_set_style_bg_color(button, color(0x303635), 0); + lv_obj_set_height(button, 25); + lv_obj_set_style_radius(button, 5, 0); + lv_obj_set_style_pad_left(button, 8, 0); + lv_obj_set_style_pad_right(button, 8, 0); + lv_obj_set_style_bg_color(button, color(embedded_palette::kSurface), 0); lv_obj_set_style_bg_opa(button, LV_OPA_COVER, 0); lv_obj_add_event_cb(button, navEventCb, LV_EVENT_CLICKED, binding); lv_obj_add_event_cb(button, navEventCb, LV_EVENT_KEY, binding); lv_group_add_obj(group_, button); - lv_obj_t* label = - createLabel(button, label_text, &lv_font_montserrat_14, 0xD7DED9); + lv_obj_t* label = createLabel(button, label_text, + &lv_font_montserrat_12, + embedded_palette::kText); lv_obj_set_width(label, LV_PCT(100)); lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_LEFT, 0); lv_obj_center(label); @@ -609,7 +1018,7 @@ class UConsoleDesktopShell applyTransparent(workspace); lv_obj_set_height(workspace, LV_PCT(100)); lv_obj_set_flex_grow(workspace, 1); - lv_obj_set_style_pad_row(workspace, 12, 0); + lv_obj_set_style_pad_row(workspace, 8, 0); lv_obj_set_flex_flow(workspace, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(workspace, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); @@ -617,21 +1026,24 @@ class UConsoleDesktopShell lv_obj_t* header = lv_obj_create(workspace); applyTransparent(header); lv_obj_set_width(header, LV_PCT(100)); - lv_obj_set_height(header, 54); - lv_obj_set_flex_flow(header, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(header, LV_FLEX_ALIGN_CENTER, - LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); - workspace_title_ = createLabel(header, sectionTitle(active_section_), - &lv_font_montserrat_20, 0x1F2523); + lv_obj_set_height(header, 36); + lv_obj_set_style_pad_column(header, 10, 0); + lv_obj_set_flex_flow(header, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(header, LV_FLEX_ALIGN_START, + LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + workspace_title_ = + createLabel(header, sectionTitle(active_section_), + &lv_font_montserrat_20, embedded_palette::kText); workspace_subtitle_ = createLabel(header, sectionSubtitle(active_section_), - &lv_font_montserrat_12, 0x61706A); + &lv_font_montserrat_12, + embedded_palette::kTextMuted); metrics_panel_ = lv_obj_create(workspace); applyTransparent(metrics_panel_); lv_obj_set_width(metrics_panel_, LV_PCT(100)); - lv_obj_set_height(metrics_panel_, 96); - lv_obj_set_style_pad_column(metrics_panel_, 10, 0); + lv_obj_set_height(metrics_panel_, 58); + lv_obj_set_style_pad_column(metrics_panel_, 8, 0); lv_obj_set_flex_flow(metrics_panel_, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(metrics_panel_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); @@ -642,36 +1054,41 @@ class UConsoleDesktopShell createMetric(metrics_panel_, 3, "Nearby"); conversation_panel_ = lv_obj_create(workspace); - applyPanel(conversation_panel_, 0xFFFFFF); + applyPanel(conversation_panel_, embedded_palette::kSurface); lv_obj_set_width(conversation_panel_, LV_PCT(100)); - lv_obj_set_flex_grow(conversation_panel_, 1); - lv_obj_set_style_pad_row(conversation_panel_, 8, 0); + lv_obj_set_height(conversation_panel_, LV_SIZE_CONTENT); + lv_obj_set_style_pad_row(conversation_panel_, 2, 0); lv_obj_set_flex_flow(conversation_panel_, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(conversation_panel_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); createLabel(conversation_panel_, "Recent conversations", - &lv_font_montserrat_16, 0x24302C); + &lv_font_montserrat_16, embedded_palette::kText); for (int index = 0; index < kConversationRows; ++index) { buildConversationRow(index); } buildChatWorkspace(workspace); + buildDesktopPages(workspace); } void createMetric(lv_obj_t* parent, int index, const char* title) { lv_obj_t* box = lv_obj_create(parent); - applyPanel(box, 0xFFFFFF); + applyPanel(box, embedded_palette::kSurface); lv_obj_set_height(box, LV_PCT(100)); lv_obj_set_flex_grow(box, 1); - lv_obj_set_flex_flow(box, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(box, LV_FLEX_ALIGN_CENTER, - LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); - createLabel(box, title, &lv_font_montserrat_12, 0x66716E); + lv_obj_set_style_pad_left(box, 10, 0); + lv_obj_set_style_pad_right(box, 10, 0); + lv_obj_set_flex_flow(box, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(box, LV_FLEX_ALIGN_SPACE_BETWEEN, + LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + createLabel(box, title, &lv_font_montserrat_12, + embedded_palette::kTextMuted); metric_value_labels_[index] = - createLabel(box, "0", &lv_font_montserrat_24, 0x1E2B27); + createLabel(box, "0", &lv_font_montserrat_20, + embedded_palette::kText); } void buildConversationRow(int index) @@ -679,47 +1096,51 @@ class UConsoleDesktopShell lv_obj_t* row = lv_obj_create(conversation_panel_); applyTransparent(row); lv_obj_set_width(row, LV_PCT(100)); - lv_obj_set_height(row, 62); - lv_obj_set_style_pad_top(row, 4, 0); - lv_obj_set_style_pad_bottom(row, 4, 0); - lv_obj_set_flex_flow(row, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(row, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_START, - LV_FLEX_ALIGN_START); + lv_obj_set_height(row, 42); + lv_obj_set_style_pad_column(row, 10, 0); + lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(row, LV_FLEX_ALIGN_START, + LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); conversation_rows_[index] = row; conversation_title_labels_[index] = - createLabel(row, "-", &lv_font_montserrat_14, 0x22302B); - lv_obj_set_width(conversation_title_labels_[index], LV_PCT(100)); + createLabel(row, "-", &lv_font_montserrat_14, + embedded_palette::kText, LV_LABEL_LONG_DOT); + lv_obj_set_width(conversation_title_labels_[index], 150); conversation_preview_labels_[index] = - createLabel(row, "", &lv_font_montserrat_12, 0x596760); - lv_obj_set_width(conversation_preview_labels_[index], LV_PCT(100)); + createLabel(row, "", &lv_font_montserrat_12, + embedded_palette::kTextMuted, LV_LABEL_LONG_DOT); + lv_obj_set_flex_grow(conversation_preview_labels_[index], 1); conversation_meta_labels_[index] = - createLabel(row, "", &lv_font_montserrat_12, 0x8A6A20); - lv_obj_set_width(conversation_meta_labels_[index], LV_PCT(100)); + createLabel(row, "", &lv_font_montserrat_12, + embedded_palette::kTextDim, LV_LABEL_LONG_DOT); + lv_obj_set_width(conversation_meta_labels_[index], 86); + lv_obj_set_style_text_align(conversation_meta_labels_[index], + LV_TEXT_ALIGN_RIGHT, 0); } void buildChatWorkspace(lv_obj_t* parent) { chat_panel_ = lv_obj_create(parent); - applyPanel(chat_panel_, 0xFFFFFF); + applyPanel(chat_panel_, embedded_palette::kSurface); lv_obj_set_width(chat_panel_, LV_PCT(100)); lv_obj_set_flex_grow(chat_panel_, 1); - lv_obj_set_style_pad_column(chat_panel_, 12, 0); + lv_obj_set_style_pad_column(chat_panel_, 10, 0); lv_obj_set_flex_flow(chat_panel_, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(chat_panel_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); lv_obj_t* conversation_list = lv_obj_create(chat_panel_); applyTransparent(conversation_list); - lv_obj_set_width(conversation_list, 310); + lv_obj_set_width(conversation_list, 280); lv_obj_set_height(conversation_list, LV_PCT(100)); - lv_obj_set_style_pad_row(conversation_list, 8, 0); + lv_obj_set_style_pad_row(conversation_list, 5, 0); lv_obj_set_flex_flow(conversation_list, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(conversation_list, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); createLabel(conversation_list, "Conversations", &lv_font_montserrat_16, - 0x24302C); + embedded_palette::kText); for (int index = 0; index < kChatConversationRows; ++index) { buildChatConversationRow(conversation_list, index); @@ -729,7 +1150,7 @@ class UConsoleDesktopShell applyTransparent(thread); lv_obj_set_height(thread, LV_PCT(100)); lv_obj_set_flex_grow(thread, 1); - lv_obj_set_style_pad_row(thread, 10, 0); + lv_obj_set_style_pad_row(thread, 6, 0); lv_obj_set_flex_flow(thread, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(thread, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); @@ -737,29 +1158,32 @@ class UConsoleDesktopShell lv_obj_t* thread_header = lv_obj_create(thread); applyTransparent(thread_header); lv_obj_set_width(thread_header, LV_PCT(100)); - lv_obj_set_height(thread_header, 48); - lv_obj_set_flex_flow(thread_header, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(thread_header, LV_FLEX_ALIGN_CENTER, - LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); - chat_title_label_ = - createLabel(thread_header, "-", &lv_font_montserrat_16, 0x1F2523); - chat_meta_label_ = - createLabel(thread_header, "-", &lv_font_montserrat_12, 0x61706A); - lv_obj_set_width(chat_title_label_, LV_PCT(100)); - lv_obj_set_width(chat_meta_label_, LV_PCT(100)); + lv_obj_set_height(thread_header, 34); + lv_obj_set_style_pad_column(thread_header, 8, 0); + lv_obj_set_flex_flow(thread_header, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(thread_header, LV_FLEX_ALIGN_START, + LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + chat_title_label_ = createLabel(thread_header, "-", + &lv_font_montserrat_16, + embedded_palette::kText); + chat_meta_label_ = createLabel(thread_header, "-", + &lv_font_montserrat_12, + embedded_palette::kTextMuted); + lv_obj_set_width(chat_title_label_, 190); + lv_obj_set_flex_grow(chat_meta_label_, 1); chat_messages_panel_ = lv_obj_create(thread); - applyPanel(chat_messages_panel_, 0xF8FAF6); + applyPanel(chat_messages_panel_, embedded_palette::kPanelBg); lv_obj_set_width(chat_messages_panel_, LV_PCT(100)); lv_obj_set_flex_grow(chat_messages_panel_, 1); - lv_obj_set_style_pad_row(chat_messages_panel_, 8, 0); + lv_obj_set_style_pad_row(chat_messages_panel_, 5, 0); lv_obj_set_flex_flow(chat_messages_panel_, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(chat_messages_panel_, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); - chat_empty_label_ = - createLabel(chat_messages_panel_, "No messages yet.", - &lv_font_montserrat_14, 0x65716B); + chat_empty_label_ = createLabel( + chat_messages_panel_, "No messages yet.", &lv_font_montserrat_14, + embedded_palette::kTextMuted); lv_obj_set_width(chat_empty_label_, LV_PCT(100)); for (int index = 0; index < kChatMessageRows; ++index) { @@ -769,14 +1193,14 @@ class UConsoleDesktopShell lv_obj_t* input_row = lv_obj_create(thread); applyTransparent(input_row); lv_obj_set_width(input_row, LV_PCT(100)); - lv_obj_set_height(input_row, 72); - lv_obj_set_style_pad_column(input_row, 10, 0); + lv_obj_set_height(input_row, 50); + lv_obj_set_style_pad_column(input_row, 8, 0); lv_obj_set_flex_flow(input_row, LV_FLEX_FLOW_ROW); lv_obj_set_flex_align(input_row, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); chat_input_ = lv_textarea_create(input_row); - lv_obj_set_height(chat_input_, 54); + lv_obj_set_height(chat_input_, 40); lv_obj_set_flex_grow(chat_input_, 1); lv_textarea_set_one_line(chat_input_, true); lv_textarea_set_placeholder_text(chat_input_, "Type a message"); @@ -786,25 +1210,782 @@ class UConsoleDesktopShell chat_send_button_ = lv_btn_create(input_row); resetBox(chat_send_button_); - lv_obj_set_size(chat_send_button_, 92, 54); + lv_obj_set_size(chat_send_button_, 76, 40); lv_obj_set_style_radius(chat_send_button_, 6, 0); - lv_obj_set_style_bg_color(chat_send_button_, color(0x2F5D62), 0); + lv_obj_set_style_bg_color(chat_send_button_, + color(embedded_palette::kAccent), 0); lv_obj_set_style_bg_opa(chat_send_button_, LV_OPA_COVER, 0); lv_obj_add_event_cb(chat_send_button_, chatSendEventCb, LV_EVENT_CLICKED, this); lv_obj_add_event_cb(chat_send_button_, chatSendEventCb, LV_EVENT_KEY, this); lv_group_add_obj(group_, chat_send_button_); - lv_obj_t* send_label = - createLabel(chat_send_button_, "Send", &lv_font_montserrat_14, - 0xF7F8F4); + lv_obj_t* send_label = createLabel( + chat_send_button_, "Send", &lv_font_montserrat_14, + embedded_palette::kHeaderText); lv_obj_center(send_label); chat_status_label_ = - createLabel(thread, "Ready.", &lv_font_montserrat_12, 0x66716E); + createLabel(thread, "Ready.", &lv_font_montserrat_12, + embedded_palette::kTextMuted); lv_obj_set_width(chat_status_label_, LV_PCT(100)); } + lv_obj_t* createPreviewPanel(lv_obj_t* parent, + const char* title, + int width = 0, + std::uint32_t background = + embedded_palette::kSurface, + bool fill_height = false) + { + lv_obj_t* panel = lv_obj_create(parent); + applyPanel(panel, background); + if (width > 0) + { + lv_obj_set_width(panel, width); + } + else + { + lv_obj_set_flex_grow(panel, 1); + } + lv_obj_set_height(panel, + fill_height ? LV_PCT(100) : LV_SIZE_CONTENT); + lv_obj_set_style_pad_all(panel, 10, 0); + lv_obj_set_style_pad_row(panel, 7, 0); + lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(panel, LV_FLEX_ALIGN_START, + LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); + createLabel(panel, title, &lv_font_montserrat_16, + embedded_palette::kText); + return panel; + } + + void addPreviewRow(lv_obj_t* parent, + const char* title, + const char* detail, + std::uint32_t accent = + embedded_palette::kStatusGreen) + { + lv_obj_t* row = lv_obj_create(parent); + applyPanel(row, embedded_palette::kPanelBg, + embedded_palette::kBorder); + lv_obj_set_width(row, LV_PCT(100)); + lv_obj_set_height(row, 54); + lv_obj_set_style_border_side(row, LV_BORDER_SIDE_LEFT, 0); + lv_obj_set_style_border_width(row, 4, 0); + lv_obj_set_style_border_color(row, color(accent), 0); + lv_obj_set_style_pad_all(row, 7, 0); + lv_obj_set_flex_flow(row, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(row, LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); + createLabel(row, title, &lv_font_montserrat_14, + embedded_palette::kText); + lv_obj_t* meta = createLabel( + row, detail, &lv_font_montserrat_12, + embedded_palette::kTextMuted, LV_LABEL_LONG_DOT); + lv_obj_set_width(meta, LV_PCT(100)); + } + + lv_obj_t* addActionPill(lv_obj_t* parent, + const char* text, + std::uint32_t background = + embedded_palette::kSoftGreen, + std::uint32_t foreground = + embedded_palette::kText) + { + lv_obj_t* pill = lv_obj_create(parent); + resetBox(pill); + lv_obj_set_size(pill, LV_SIZE_CONTENT, 28); + lv_obj_set_style_bg_color(pill, color(background), 0); + lv_obj_set_style_bg_opa(pill, LV_OPA_COVER, 0); + lv_obj_set_style_radius(pill, 5, 0); + lv_obj_set_style_pad_left(pill, 9, 0); + lv_obj_set_style_pad_right(pill, 9, 0); + lv_obj_t* label = + createLabel(pill, text, &lv_font_montserrat_12, foreground); + lv_obj_center(label); + return pill; + } + + void buildDesktopPages(lv_obj_t* parent) + { + desktop_page_panel_ = lv_obj_create(parent); + applyTransparent(desktop_page_panel_); + lv_obj_set_width(desktop_page_panel_, LV_PCT(100)); + lv_obj_set_flex_grow(desktop_page_panel_, 1); + lv_obj_set_style_pad_column(desktop_page_panel_, 10, 0); + lv_obj_set_flex_flow(desktop_page_panel_, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(desktop_page_panel_, LV_FLEX_ALIGN_START, + LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); + } + + void buildMapPreview() + { + lv_obj_t* map = createPreviewPanel( + desktop_page_panel_, "Field map", 0, embedded_palette::kMapBg, + true); + + lv_obj_t* toolbar = lv_obj_create(map); + applyTransparent(toolbar); + lv_obj_set_width(toolbar, LV_PCT(100)); + lv_obj_set_height(toolbar, 30); + lv_obj_set_style_pad_column(toolbar, 5, 0); + lv_obj_set_flex_flow(toolbar, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(toolbar, LV_FLEX_ALIGN_START, + LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER); + addActionPill(toolbar, "Terrain", embedded_palette::kSoftBlue, + embedded_palette::kText); + addActionPill(toolbar, "Contours"); + addActionPill(toolbar, "Recenter"); + addActionPill(toolbar, "Measure"); + + lv_obj_t* grid = lv_obj_create(map); + resetBox(grid); + lv_obj_set_width(grid, LV_PCT(100)); + lv_obj_set_flex_grow(grid, 1); + lv_obj_set_style_pad_row(grid, 2, 0); + lv_obj_set_flex_flow(grid, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(grid, LV_FLEX_ALIGN_START, + LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); + constexpr std::array tile_colors{ + embedded_palette::kMapTile1, embedded_palette::kMapTile2, + embedded_palette::kMapTile3, embedded_palette::kMapBg, + embedded_palette::kMapTile4, embedded_palette::kMapTile5, + embedded_palette::kMapTile6, embedded_palette::kMapTile7, + embedded_palette::kMapTile8}; + for (int row_index = 0; row_index < 3; ++row_index) + { + lv_obj_t* row = lv_obj_create(grid); + resetBox(row); + lv_obj_set_width(row, LV_PCT(100)); + lv_obj_set_height(row, 0); + lv_obj_set_flex_grow(row, 1); + lv_obj_set_style_pad_column(row, 2, 0); + lv_obj_set_flex_flow(row, LV_FLEX_FLOW_ROW); + for (int column = 0; column < 3; ++column) + { + const int tile_index = (row_index * 3) + column; + lv_obj_t* tile = lv_obj_create(row); + resetBox(tile); + lv_obj_set_width(tile, 0); + lv_obj_set_height(tile, LV_PCT(100)); + lv_obj_set_flex_grow(tile, 1); + lv_obj_set_style_bg_color( + tile, color(tile_colors[tile_index]), 0); + lv_obj_set_style_bg_opa(tile, LV_OPA_COVER, 0); + lv_obj_set_style_border_color( + tile, color(embedded_palette::kBorder), 0); + lv_obj_set_style_border_width(tile, 1, 0); + if (tile_index == 4) + { + lv_obj_t* marker = + addActionPill(tile, "YOU", + embedded_palette::kStatusGreen, + embedded_palette::kWhite); + lv_obj_center(marker); + } + else if (tile_index == 2 || tile_index == 6) + { + lv_obj_t* marker = addActionPill( + tile, tile_index == 2 ? "SCOUT" : "BASE", + embedded_palette::kSoftAmber, + embedded_palette::kText); + lv_obj_center(marker); + } + } + } + createLabel(map, + "31.2304 N / 121.4737 E / zoom 14 / Terrain", + &lv_font_montserrat_12, embedded_palette::kTextMuted); + + lv_obj_t* download = createPreviewPanel( + desktop_page_panel_, "Map downloads", 220, + embedded_palette::kSurface); + addPreviewRow(download, "Cache", "2,418 tiles / 386 MB", + embedded_palette::kStatusGreen); + addPreviewRow(download, "Downloading", "4 workers / visible area", + embedded_palette::kAccentDark); + addPreviewRow(download, "Retries", "2 queued / backoff active", + embedded_palette::kWarn); + addActionPill(download, "Open cache directory"); + } + + void buildGpsPreview() + { + lv_obj_t* sky = createPreviewPanel( + desktop_page_panel_, "Satellite sky plot", 0, + embedded_palette::kSurface, true); + lv_obj_t* stage = lv_obj_create(sky); + resetBox(stage); + lv_obj_set_width(stage, LV_PCT(100)); + lv_obj_set_flex_grow(stage, 1); + lv_obj_set_style_bg_color(stage, color(embedded_palette::kMapBg), 0); + lv_obj_set_style_bg_opa(stage, LV_OPA_COVER, 0); + lv_obj_set_style_border_color(stage, + color(embedded_palette::kBorder), 0); + lv_obj_set_style_border_width(stage, 1, 0); + lv_obj_set_style_radius(stage, 6, 0); + + constexpr int sky_x = 28; + constexpr int sky_y = 38; + constexpr int sky_size = 430; + constexpr int sky_radius = sky_size / 2; + constexpr int center_x = sky_x + sky_radius; + constexpr int center_y = sky_y + sky_radius; + constexpr std::uint32_t grid_color = 0xC9943F; + + lv_obj_t* vertical_axis = lv_obj_create(stage); + resetBox(vertical_axis); + lv_obj_set_pos(vertical_axis, center_x, sky_y); + lv_obj_set_size(vertical_axis, 1, sky_size); + lv_obj_set_style_bg_color(vertical_axis, color(grid_color), 0); + lv_obj_set_style_bg_opa(vertical_axis, LV_OPA_COVER, 0); + + lv_obj_t* horizontal_axis = lv_obj_create(stage); + resetBox(horizontal_axis); + lv_obj_set_pos(horizontal_axis, sky_x, center_y); + lv_obj_set_size(horizontal_axis, sky_size, 1); + lv_obj_set_style_bg_color(horizontal_axis, color(grid_color), 0); + lv_obj_set_style_bg_opa(horizontal_axis, LV_OPA_COVER, 0); + + constexpr std::array ring_sizes{ + sky_size, (sky_size * 2) / 3, sky_size / 3}; + for (std::size_t index = 0; index < ring_sizes.size(); ++index) + { + const int ring_size = ring_sizes[index]; + lv_obj_t* ring = lv_obj_create(stage); + resetBox(ring); + lv_obj_set_size(ring, ring_size, ring_size); + lv_obj_set_pos(ring, center_x - ring_size / 2, + center_y - ring_size / 2); + lv_obj_set_style_bg_opa(ring, LV_OPA_TRANSP, 0); + lv_obj_set_style_border_color(ring, color(grid_color), 0); + lv_obj_set_style_border_width(ring, index == 0 ? 2 : 1, 0); + lv_obj_set_style_radius(ring, LV_RADIUS_CIRCLE, 0); + } + + lv_obj_t* center_dot = lv_obj_create(stage); + resetBox(center_dot); + lv_obj_set_size(center_dot, 7, 7); + lv_obj_set_pos(center_dot, center_x - 3, center_y - 3); + lv_obj_set_style_bg_color( + center_dot, color(embedded_palette::kTextWarm), 0); + lv_obj_set_style_bg_opa(center_dot, LV_OPA_COVER, 0); + lv_obj_set_style_radius(center_dot, LV_RADIUS_CIRCLE, 0); + + struct PlotLabel + { + int x; + int y; + const char* text; + const lv_font_t* font; + std::uint32_t text_color; + }; + constexpr std::array plot_labels{{ + {center_x - 6, sky_y - 26, "N", &lv_font_montserrat_16, + embedded_palette::kText}, + {sky_x + sky_size + 11, center_y - 8, "E", + &lv_font_montserrat_16, embedded_palette::kText}, + {center_x - 5, sky_y + sky_size + 8, "S", + &lv_font_montserrat_16, embedded_palette::kText}, + {sky_x - 22, center_y - 8, "W", &lv_font_montserrat_16, + embedded_palette::kText}, + {sky_x + 34, sky_y + 37, "90 deg", &lv_font_montserrat_10, + embedded_palette::kTextDim}, + {sky_x + 105, sky_y + 108, "60 deg", &lv_font_montserrat_10, + embedded_palette::kTextDim}, + {center_x + sky_radius / 3 + 8, center_y - 18, "30 deg", + &lv_font_montserrat_10, embedded_palette::kTextDim}, + {center_x + 9, center_y + 8, "Horizon", + &lv_font_montserrat_10, embedded_palette::kTextDim}, + }}; + for (const auto& item : plot_labels) + { + lv_obj_t* label = + createLabel(stage, item.text, item.font, item.text_color); + lv_obj_set_pos(label, item.x, item.y); + } + + struct SatelliteDot + { + int id; + const char* system; + float azimuth; + float elevation; + int snr; + bool used; + std::uint32_t fill_color; + std::uint32_t border_color; + std::uint32_t text_color; + }; + constexpr std::array satellites{{ + {12, "GPS", 332.0F, 67.0F, 42, true, 0xE3B11F, 0x3E7D3E, + embedded_palette::kHeaderText}, + {31, "GPS", 296.0F, 52.0F, 39, true, 0xE3B11F, 0x3E7D3E, + embedded_palette::kHeaderText}, + {7, "BDS", 8.0F, 61.0F, 38, true, 0xB94A2C, 0x3E7D3E, + embedded_palette::kWhite}, + {4, "GAL", 158.0F, 48.0F, 36, true, 0x3E7D3E, 0x3E7D3E, + embedded_palette::kWhite}, + {11, "GAL", 132.0F, 35.0F, 33, true, 0x3E7D3E, 0x8FBF4D, + embedded_palette::kWhite}, + {19, "BDS", 226.0F, 29.0F, 30, true, 0xB94A2C, 0x8FBF4D, + embedded_palette::kWhite}, + {22, "GPS", 82.0F, 23.0F, 28, false, 0xE3B11F, 0xB94A2C, + embedded_palette::kHeaderText}, + {5, "GPS", 278.0F, 16.0F, 18, false, 0xE3B11F, 0x6E6E6E, + embedded_palette::kHeaderText}, + }}; + for (const auto& sat : satellites) + { + constexpr int dot_size = 31; + const float radius = + static_cast(sky_radius) * + (1.0F - std::clamp(sat.elevation, 0.0F, 90.0F) / 90.0F); + const float radians = + sat.azimuth * 3.1415926535F / 180.0F; + const int satellite_x = static_cast(std::round( + static_cast(center_x) + radius * std::sin(radians))); + const int satellite_y = static_cast(std::round( + static_cast(center_y) - radius * std::cos(radians))); + + lv_obj_t* dot = lv_obj_create(stage); + resetBox(dot); + lv_obj_set_size(dot, dot_size, dot_size); + lv_obj_set_pos(dot, satellite_x - dot_size / 2, + satellite_y - dot_size / 2); + lv_obj_set_style_bg_color(dot, color(sat.fill_color), 0); + lv_obj_set_style_bg_opa(dot, LV_OPA_COVER, 0); + lv_obj_set_style_border_color(dot, color(sat.border_color), 0); + lv_obj_set_style_border_width(dot, 3, 0); + lv_obj_set_style_radius(dot, LV_RADIUS_CIRCLE, 0); + + char id[8]; + std::snprintf(id, sizeof(id), "%d", sat.id); + lv_obj_t* label = createLabel( + dot, id, &lv_font_montserrat_10, sat.text_color); + lv_obj_center(label); + + if (sat.used) + { + lv_obj_t* use_tag = lv_obj_create(stage); + resetBox(use_tag); + lv_obj_set_size(use_tag, 28, 16); + lv_obj_set_pos(use_tag, satellite_x + 8, satellite_y + 8); + lv_obj_set_style_bg_color( + use_tag, color(embedded_palette::kStatusGreen), 0); + lv_obj_set_style_bg_opa(use_tag, LV_OPA_COVER, 0); + lv_obj_set_style_radius(use_tag, 8, 0); + lv_obj_t* use_label = + createLabel(use_tag, "USE", &lv_font_montserrat_10, + embedded_palette::kWhite); + lv_obj_center(use_label); + } + } + + constexpr int legend_x = 494; + lv_obj_t* constellation_title = + createLabel(stage, "CONSTELLATION", &lv_font_montserrat_10, + embedded_palette::kTextDim); + lv_obj_set_pos(constellation_title, legend_x, 76); + + struct LegendItem + { + const char* label; + std::uint32_t color; + }; + constexpr std::array systems{{ + {"GPS", 0xE3B11F}, + {"GLONASS", 0x2D6FB6}, + {"Galileo", 0x3E7D3E}, + {"BeiDou", 0xB94A2C}, + }}; + constexpr std::array signals{{ + {"Good", 0x3E7D3E}, + {"Weak", 0xC18B2C}, + {"Not used", 0xB94A2C}, + {"In view", 0x6E6E6E}, + }}; + const auto build_legend = + [stage](const std::array& items, int x, int y) + { + for (std::size_t index = 0; index < items.size(); ++index) + { + lv_obj_t* swatch = lv_obj_create(stage); + resetBox(swatch); + lv_obj_set_size(swatch, 13, 13); + lv_obj_set_pos(swatch, x, y + static_cast(index) * 28); + lv_obj_set_style_bg_color( + swatch, color(items[index].color), 0); + lv_obj_set_style_bg_opa(swatch, LV_OPA_COVER, 0); + lv_obj_set_style_radius(swatch, 4, 0); + + lv_obj_t* label = + createLabel(stage, items[index].label, + &lv_font_montserrat_12, + embedded_palette::kTextMuted); + lv_obj_set_pos(label, x + 21, + y - 1 + static_cast(index) * 28); + } + }; + build_legend(systems, legend_x, 96); + + lv_obj_t* signal_title = + createLabel(stage, "SIGNAL BORDER", &lv_font_montserrat_10, + embedded_palette::kTextDim); + lv_obj_set_pos(signal_title, legend_x, 228); + build_legend(signals, legend_x, 250); + + lv_obj_t* legend_note = + createLabel(stage, "Fill = constellation\nRing = signal state", + &lv_font_montserrat_10, + embedded_palette::kTextDim, LV_LABEL_LONG_WRAP); + lv_obj_set_width(legend_note, 170); + lv_obj_set_style_text_line_space(legend_note, 3, 0); + lv_obj_set_pos(legend_note, legend_x, 376); + + createLabel(sky, "North up / live 1 Hz / altitude 18.4 m", + &lv_font_montserrat_12, embedded_palette::kTextWarm); + + lv_obj_t* receiver = createPreviewPanel( + desktop_page_panel_, "Satellite status", 330, + embedded_palette::kSurface, true); + + lv_obj_t* receiver_summary = lv_obj_create(receiver); + applyPanel(receiver_summary, embedded_palette::kAccent, + embedded_palette::kAccentDark); + lv_obj_set_width(receiver_summary, LV_PCT(100)); + lv_obj_set_height(receiver_summary, 42); + lv_obj_set_style_pad_all(receiver_summary, 8, 0); + lv_obj_t* summary_label = + createLabel(receiver_summary, "USE 6/8 HDOP 0.8 FIX 3D", + &lv_font_montserrat_12, + embedded_palette::kHeaderText); + lv_obj_center(summary_label); + + lv_obj_t* table_header = lv_obj_create(receiver); + resetBox(table_header); + lv_obj_set_width(table_header, LV_PCT(100)); + lv_obj_set_height(table_header, 28); + lv_obj_set_style_bg_color( + table_header, color(embedded_palette::kSurfaceAlt), 0); + lv_obj_set_style_bg_opa(table_header, LV_OPA_COVER, 0); + + constexpr std::array column_x{8, 45, 112, 176, 227}; + constexpr std::array column_names{ + "ID", "SYS", "ELEV", "SNR", "USE"}; + for (std::size_t column = 0; column < column_names.size(); ++column) + { + lv_obj_t* label = + createLabel(table_header, column_names[column], + &lv_font_montserrat_10, + embedded_palette::kTextDim); + lv_obj_set_pos(label, column_x[column], 7); + } + + for (std::size_t row_index = 0; row_index < satellites.size(); + ++row_index) + { + const auto& sat = satellites[row_index]; + lv_obj_t* row = lv_obj_create(receiver); + resetBox(row); + lv_obj_set_width(row, LV_PCT(100)); + lv_obj_set_height(row, 37); + lv_obj_set_style_bg_color( + row, + color(row_index % 2 == 0 ? embedded_palette::kPanelBg + : embedded_palette::kSurface), + 0); + lv_obj_set_style_bg_opa(row, LV_OPA_COVER, 0); + lv_obj_set_style_border_color( + row, color(embedded_palette::kSeparator), 0); + lv_obj_set_style_border_width(row, 1, 0); + lv_obj_set_style_border_side(row, LV_BORDER_SIDE_BOTTOM, 0); + + char id[8]; + char elevation[12]; + char snr[8]; + std::snprintf(id, sizeof(id), "%d", sat.id); + std::snprintf(elevation, sizeof(elevation), "%.0f deg", + static_cast(sat.elevation)); + std::snprintf(snr, sizeof(snr), "%d", sat.snr); + const std::array values{ + id, sat.system, elevation, snr, sat.used ? "YES" : "NO"}; + for (std::size_t column = 0; column < values.size(); ++column) + { + const std::uint32_t value_color = + column == 1 + ? sat.fill_color + : (column == 4 + ? (sat.used ? embedded_palette::kStatusGreen + : embedded_palette::kWarn) + : embedded_palette::kText); + lv_obj_t* label = + createLabel(row, values[column], + &lv_font_montserrat_12, value_color); + lv_obj_set_pos(label, column_x[column], 10); + } + } + + lv_obj_t* receiver_meta = + createLabel(receiver, "Live / 31.2304, 121.4737 / GPS UTC", + &lv_font_montserrat_10, + embedded_palette::kTextMuted); + lv_obj_set_width(receiver_meta, LV_PCT(100)); + addActionPill(receiver, "Open map"); + } + + void buildRadioToolsPreview() + { + lv_obj_t* spectrum = createPreviewPanel( + desktop_page_panel_, "Energy sweep", 0, + embedded_palette::kSurface); + lv_obj_t* chart = lv_obj_create(spectrum); + applyPanel(chart, embedded_palette::kPlotBg, + embedded_palette::kBorder); + lv_obj_set_width(chart, LV_PCT(100)); + lv_obj_set_height(chart, 280); + lv_obj_set_style_pad_all(chart, 12, 0); + lv_obj_set_style_pad_column(chart, 3, 0); + lv_obj_set_flex_flow(chart, LV_FLEX_FLOW_ROW); + lv_obj_set_flex_align(chart, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_END, + LV_FLEX_ALIGN_END); + constexpr std::array levels{ + 42, 48, 44, 55, 50, 62, 71, 95, 128, 176, + 112, 76, 59, 52, 48, 66, 88, 142, 196, 154, + 92, 68, 58, 49, 45, 54, 73, 98}; + for (std::size_t index = 0; index < levels.size(); ++index) + { + lv_obj_t* bar = lv_obj_create(chart); + resetBox(bar); + lv_obj_set_width(bar, 0); + lv_obj_set_height(bar, levels[index]); + lv_obj_set_flex_grow(bar, 1); + lv_obj_set_style_bg_color( + bar, + color(levels[index] > 150 ? embedded_palette::kAccent + : embedded_palette::kStatusBlue), + 0); + lv_obj_set_style_bg_opa(bar, LV_OPA_COVER, 0); + lv_obj_set_style_radius(bar, 2, 0); + } + createLabel(spectrum, + "433.050 - 434.790 MHz / strongest 434.225 MHz at -69.1 dBm", + &lv_font_montserrat_12, embedded_palette::kTextWarm); + addActionPill(spectrum, "Run sweep"); + + lv_obj_t* sessions = lv_obj_create(desktop_page_panel_); + applyTransparent(sessions); + lv_obj_set_width(sessions, 330); + lv_obj_set_height(sessions, LV_PCT(100)); + lv_obj_set_style_pad_row(sessions, 10, 0); + lv_obj_set_flex_flow(sessions, LV_FLEX_FLOW_COLUMN); + + lv_obj_t* sstv = createPreviewPanel( + sessions, "SSTV receiver", 330, embedded_palette::kSurface); + addPreviewRow(sstv, "Mode", "Martin M1 / waiting for sync", + embedded_palette::kStatusBlue); + addPreviewRow(sstv, "Last image", "No decoded image yet"); + addActionPill(sstv, "Start receiver"); + + lv_obj_t* walkie = createPreviewPanel( + sessions, "Walkie", 330, embedded_palette::kSurface); + addPreviewRow(walkie, "Session", "Stopped / capability: Simulated", + embedded_palette::kWarn); + addPreviewRow(walkie, "Channel", "433.175 MHz / monitor off"); + lv_obj_t* actions = lv_obj_create(walkie); + applyTransparent(actions); + lv_obj_set_width(actions, LV_PCT(100)); + lv_obj_set_height(actions, 30); + lv_obj_set_style_pad_column(actions, 6, 0); + lv_obj_set_flex_flow(actions, LV_FLEX_FLOW_ROW); + addActionPill(actions, "Start session"); + addActionPill(actions, "Press PTT", embedded_palette::kSoftBlue, + embedded_palette::kText); + } + + void buildExtensionsPreview() + { + lv_obj_t* catalog = createPreviewPanel( + desktop_page_panel_, "Available packages", 0, + embedded_palette::kSurface); + addPreviewRow(catalog, "English language pack", + "v1.4 / locale / 86 KB / installed", + embedded_palette::kStatusGreen); + addPreviewRow(catalog, "Noto Sans CJK field font", + "v2.1 / font / 4.8 MB / update available", + embedded_palette::kAccentDark); + addPreviewRow(catalog, "Pinyin input method", + "v1.2 / IME / 612 KB / available", + embedded_palette::kStatusBlue); + addPreviewRow(catalog, "Emergency symbol set", + "v1.0 / assets / 138 KB / available", + embedded_palette::kTextWarm); + addPreviewRow(catalog, "Topographic map style", + "v0.9 / map style / 244 KB / available", + embedded_palette::kStatusGreen); + + lv_obj_t* details = createPreviewPanel( + desktop_page_panel_, "Package details", 320, + embedded_palette::kSurface); + createLabel(details, "Noto Sans CJK field font", + &lv_font_montserrat_16, embedded_palette::kText); + createLabel(details, "Typography for multilingual field messages,", + &lv_font_montserrat_12, embedded_palette::kTextMuted); + createLabel(details, "maps, node names and team coordination.", + &lv_font_montserrat_12, embedded_palette::kTextMuted); + addPreviewRow(details, "Compatibility", "Desktop / full memory profile", + embedded_palette::kStatusGreen); + addPreviewRow(details, "Storage", "linux-local / 4.8 MB"); + addPreviewRow(details, "Status", "Update available", + embedded_palette::kAccentDark); + addActionPill(details, "Install update"); + } + + void buildGenericFieldPreview(Section section) + { + lv_obj_t* primary = createPreviewPanel( + desktop_page_panel_, sectionTitle(section), 0, + embedded_palette::kSurface); + lv_obj_t* secondary = createPreviewPanel( + desktop_page_panel_, "Desktop actions", 320, + embedded_palette::kSurface); + switch (section) + { + case Section::Contacts: + addPreviewRow(primary, "Base Camp", + "!435a1002 / Meshtastic / online", + embedded_palette::kStatusGreen); + addPreviewRow(primary, "Trail Scout", + "!435a1003 / Meshtastic / 2 min ago", + embedded_palette::kStatusBlue); + addPreviewRow(primary, "MQTT Relay", + "!435a1010 / MQTT / trusted", + embedded_palette::kTextWarm); + addPreviewRow(secondary, "Selected node", + "Base Camp / key verified"); + addActionPill(secondary, "Open chat"); + addActionPill(secondary, "Request NodeInfo"); + addActionPill(secondary, "Ignore node", + embedded_palette::kSoftWarn, + embedded_palette::kWarn); + break; + case Section::Team: + addPreviewRow(primary, "Alpha team", + "4 members / leader Base Camp", + embedded_palette::kTextWarm); + addPreviewRow(primary, "Trail Scout shared position", + "31.2308, 121.4741 / 2 min ago", + embedded_palette::kStatusBlue); + addPreviewRow(primary, "Waypoint updated", + "North checkpoint / synced", + embedded_palette::kStatusGreen); + addActionPill(secondary, "Open team chat"); + addActionPill(secondary, "Share current position"); + break; + case Section::Tracker: + addPreviewRow(primary, "Recorder", "Recording / GPX", + embedded_palette::kWarn); + addPreviewRow(primary, "Current file", + "track-20260723-142830.gpx", + embedded_palette::kStatusBlue); + addPreviewRow(primary, "Sampling", "10 s / GPS 3D fix"); + addActionPill(secondary, "Stop recording", + embedded_palette::kSoftWarn, + embedded_palette::kWarn); + addActionPill(secondary, "Open track directory"); + break; + case Section::Hardware: + addPreviewRow(primary, "AIO2", "Available / USB serial", + embedded_palette::kStatusGreen); + addPreviewRow(primary, "LoRa SX1262", + "Available / spidev1.0", + embedded_palette::kStatusGreen); + addPreviewRow(primary, "GPS", "Available / 3D fix", + embedded_palette::kStatusGreen); + addPreviewRow(primary, "Audio", "Simulated backend", + embedded_palette::kWarn); + addPreviewRow(secondary, "Runtime", "Linux desktop / uConsole"); + addPreviewRow(secondary, "Protocol", "Native Meshtastic"); + break; + case Section::Data: + addPreviewRow(primary, "Map cache", + "2,418 tiles / 386 MB / healthy", + embedded_palette::kStatusGreen); + addPreviewRow(primary, "Background downloads", + "4 active / 2 retrying", + embedded_palette::kAccentDark); + addPreviewRow(primary, "Track files", "18 files / 42 MB"); + addPreviewRow(primary, "Messages", "SQLite / 3,284 records"); + addActionPill(secondary, "Open map workspace"); + addActionPill(secondary, "Retry failed tiles"); + break; + case Section::Logs: + addPreviewRow(primary, "14:28:31 LoRa RX", + "Meshtastic text / !435a1002 / RSSI -81", + embedded_palette::kStatusGreen); + addPreviewRow(primary, "14:28:30 GPS", + "GGA / 3D fix / 8 satellites", + embedded_palette::kStatusBlue); + addPreviewRow(primary, "14:28:28 MQTT RX", + "msh/CN/2/e/LongFast / decoded", + embedded_palette::kTextWarm); + addPreviewRow(secondary, "Source filter", "LoRa"); + addActionPill(secondary, "GPS"); + addActionPill(secondary, "LoRa"); + addActionPill(secondary, "MQTT"); + break; + case Section::Settings: + addPreviewRow(primary, "Radio protocol", "Meshtastic"); + addPreviewRow(primary, "Region and preset", + "CN / LongFast / 433.175 MHz"); + addPreviewRow(primary, "GPS", "Enabled / 5 s interval"); + addPreviewRow(primary, "Map", "Terrain / zoom 14 / auto download"); + addPreviewRow(secondary, "Configuration", "Unsaved changes: 0"); + addActionPill(secondary, "Apply settings"); + addActionPill(secondary, "Reload"); + break; + default: + break; + } + } + + void rebuildDesktopPage() + { + if (desktop_page_panel_ == nullptr) + { + return; + } + lv_obj_clean(desktop_page_panel_); + switch (active_section_) + { + case Section::Map: + buildMapPreview(); + break; + case Section::Gps: + buildGpsPreview(); + break; + case Section::RadioTools: + buildRadioToolsPreview(); + break; + case Section::Extensions: + buildExtensionsPreview(); + break; + case Section::Contacts: + case Section::Team: + case Section::Tracker: + case Section::Hardware: + case Section::Data: + case Section::Logs: + case Section::Settings: + buildGenericFieldPreview(active_section_); + break; + case Section::Overview: + case Section::Chat: + break; + } + } + void buildChatConversationRow(lv_obj_t* parent, int index) { chat_conversation_bindings_[index] = {this, @@ -815,7 +1996,8 @@ class UConsoleDesktopShell lv_obj_set_height(button, 74); lv_obj_set_style_radius(button, 6, 0); lv_obj_set_style_pad_all(button, 8, 0); - lv_obj_set_style_bg_color(button, color(0xF3F6F2), 0); + lv_obj_set_style_bg_color(button, + color(embedded_palette::kPanelBg), 0); lv_obj_set_style_bg_opa(button, LV_OPA_COVER, 0); lv_obj_set_flex_flow(button, LV_FLEX_FLOW_COLUMN); lv_obj_set_flex_align(button, LV_FLEX_ALIGN_CENTER, @@ -828,11 +2010,14 @@ class UConsoleDesktopShell chat_conversation_buttons_[index] = button; chat_conversation_title_labels_[index] = - createLabel(button, "-", &lv_font_montserrat_14, 0x22302B); + createLabel(button, "-", &lv_font_montserrat_14, + embedded_palette::kText); chat_conversation_preview_labels_[index] = - createLabel(button, "", &lv_font_montserrat_12, 0x596760); + createLabel(button, "", &lv_font_montserrat_12, + embedded_palette::kTextMuted); chat_conversation_meta_labels_[index] = - createLabel(button, "", &lv_font_montserrat_12, 0x8A6A20); + createLabel(button, "", &lv_font_montserrat_12, + embedded_palette::kTextDim); lv_obj_set_width(chat_conversation_title_labels_[index], LV_PCT(100)); lv_obj_set_width(chat_conversation_preview_labels_[index], LV_PCT(100)); lv_obj_set_width(chat_conversation_meta_labels_[index], LV_PCT(100)); @@ -841,7 +2026,7 @@ class UConsoleDesktopShell void buildChatMessageRow(int index) { lv_obj_t* row = lv_obj_create(chat_messages_panel_); - applyPanel(row, 0xFFFFFF); + applyPanel(row, embedded_palette::kSurface); lv_obj_set_width(row, LV_PCT(100)); lv_obj_set_height(row, 72); lv_obj_set_style_pad_all(row, 8, 0); @@ -851,12 +2036,14 @@ class UConsoleDesktopShell chat_message_rows_[index] = row; chat_message_sender_labels_[index] = - createLabel(row, "-", &lv_font_montserrat_12, 0x34413D); + createLabel(row, "-", &lv_font_montserrat_12, + embedded_palette::kTextWarm); chat_message_text_labels_[index] = - createLabel(row, "", &lv_font_montserrat_14, 0x17211E, - LV_LABEL_LONG_WRAP); + createLabel(row, "", &lv_font_montserrat_14, + embedded_palette::kText, LV_LABEL_LONG_WRAP); chat_message_meta_labels_[index] = - createLabel(row, "", &lv_font_montserrat_12, 0x66716E); + createLabel(row, "", &lv_font_montserrat_12, + embedded_palette::kTextMuted); lv_obj_set_width(chat_message_sender_labels_[index], LV_PCT(100)); lv_obj_set_width(chat_message_text_labels_[index], LV_PCT(100)); lv_obj_set_width(chat_message_meta_labels_[index], LV_PCT(100)); @@ -864,37 +2051,39 @@ class UConsoleDesktopShell void buildStatusPanel(lv_obj_t* parent) { - lv_obj_t* panel = lv_obj_create(parent); - applyPanel(panel, 0xFFFFFF); - lv_obj_set_width(panel, 330); - lv_obj_set_height(panel, LV_PCT(100)); - lv_obj_set_style_pad_row(panel, 12, 0); - lv_obj_set_flex_flow(panel, LV_FLEX_FLOW_COLUMN); - lv_obj_set_flex_align(panel, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START, - LV_FLEX_ALIGN_START); + status_panel_ = lv_obj_create(parent); + applyPanel(status_panel_, embedded_palette::kSurface); + lv_obj_set_width(status_panel_, 330); + lv_obj_set_height(status_panel_, LV_PCT(100)); + lv_obj_set_style_pad_row(status_panel_, 12, 0); + lv_obj_set_flex_flow(status_panel_, LV_FLEX_FLOW_COLUMN); + lv_obj_set_flex_align(status_panel_, LV_FLEX_ALIGN_START, + LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_START); - createLabel(panel, "Runtime and capabilities", &lv_font_montserrat_16, - 0x24302C); + createLabel(status_panel_, "Runtime and capabilities", + &lv_font_montserrat_16, embedded_palette::kText); for (int index = 0; index < kCapabilityRows; ++index) { capability_labels_[index] = - createLabel(panel, "-", &lv_font_montserrat_12, 0x4F5C57, + createLabel(status_panel_, "-", &lv_font_montserrat_12, + embedded_palette::kTextMuted, LV_LABEL_LONG_WRAP); lv_obj_set_width(capability_labels_[index], LV_PCT(100)); } - lv_obj_t* divider = lv_obj_create(panel); + lv_obj_t* divider = lv_obj_create(status_panel_); resetBox(divider); lv_obj_set_width(divider, LV_PCT(100)); lv_obj_set_height(divider, 1); - lv_obj_set_style_bg_color(divider, color(0xD9DEDA), 0); + lv_obj_set_style_bg_color(divider, + color(embedded_palette::kSeparator), 0); lv_obj_set_style_bg_opa(divider, LV_OPA_COVER, 0); - createLabel(panel, "Contacts and nearby nodes", &lv_font_montserrat_16, - 0x24302C); + createLabel(status_panel_, "Contacts and nearby nodes", + &lv_font_montserrat_16, embedded_palette::kText); for (int index = 0; index < kContactRows; ++index) { - buildContactRow(panel, index); + buildContactRow(status_panel_, index); } } @@ -910,10 +2099,12 @@ class UConsoleDesktopShell contact_rows_[index] = row; contact_name_labels_[index] = - createLabel(row, "-", &lv_font_montserrat_14, 0x22302B); + createLabel(row, "-", &lv_font_montserrat_14, + embedded_palette::kText); lv_obj_set_width(contact_name_labels_[index], LV_PCT(100)); contact_meta_labels_[index] = - createLabel(row, "", &lv_font_montserrat_12, 0x66716E); + createLabel(row, "", &lv_font_montserrat_12, + embedded_palette::kTextMuted); lv_obj_set_width(contact_meta_labels_[index], LV_PCT(100)); } @@ -925,32 +2116,41 @@ class UConsoleDesktopShell lv_obj_t* button = nav_buttons_[index]; if (button == nullptr) continue; const bool active = index == active_index; - lv_obj_set_style_bg_color(button, - color(active ? 0xD7E8DF : 0x303635), 0); + lv_obj_set_style_bg_color( + button, + color(active ? embedded_palette::kAccent + : embedded_palette::kSurface), + 0); lv_obj_set_style_border_width(button, active ? 1 : 0, 0); - lv_obj_set_style_border_color(button, color(0x7EA48F), 0); + lv_obj_set_style_border_color( + button, color(embedded_palette::kBorder), 0); lv_obj_t* label = lv_obj_get_child(button, 0); if (label != nullptr) { lv_obj_set_style_text_color( - label, color(active ? 0x15251F : 0xD7DED9), 0); + label, + color(active ? embedded_palette::kHeaderText + : embedded_palette::kText), + 0); } } } void refreshSectionVisibility() { + const bool overview_active = active_section_ == Section::Overview; const bool chat_active = active_section_ == Section::Chat; + const bool desktop_page_active = !overview_active && !chat_active; if (metrics_panel_ != nullptr) { - if (chat_active) + if (!overview_active) lv_obj_add_flag(metrics_panel_, LV_OBJ_FLAG_HIDDEN); else lv_obj_clear_flag(metrics_panel_, LV_OBJ_FLAG_HIDDEN); } if (conversation_panel_ != nullptr) { - if (chat_active) + if (!overview_active) lv_obj_add_flag(conversation_panel_, LV_OBJ_FLAG_HIDDEN); else lv_obj_clear_flag(conversation_panel_, LV_OBJ_FLAG_HIDDEN); @@ -962,6 +2162,17 @@ class UConsoleDesktopShell else lv_obj_add_flag(chat_panel_, LV_OBJ_FLAG_HIDDEN); } + if (desktop_page_panel_ != nullptr) + { + if (desktop_page_active) + lv_obj_clear_flag(desktop_page_panel_, LV_OBJ_FLAG_HIDDEN); + else + lv_obj_add_flag(desktop_page_panel_, LV_OBJ_FLAG_HIDDEN); + } + if (status_panel_ != nullptr) + { + lv_obj_add_flag(status_panel_, LV_OBJ_FLAG_HIDDEN); + } } void refreshDashboard(bool force) @@ -978,6 +2189,10 @@ class UConsoleDesktopShell setLabel(top_mesh_label_, "Mesh: " + snapshot.mesh_protocol); setLabel(top_node_label_, "Node: " + snapshot.self_node); setLabel(top_unread_label_, "Unread: " + formatCount(snapshot.unread_count)); + setLabel(footer_status_label_, + snapshot.mesh_protocol + " / node " + snapshot.self_node + + " / " + formatCount(snapshot.unread_count) + + " unread"); setLabel(metric_value_labels_[0], formatCount(snapshot.conversation_count)); @@ -999,7 +2214,9 @@ class UConsoleDesktopShell setLabel(conversation_meta_labels_[index], item.meta); lv_obj_set_style_text_color( conversation_meta_labels_[index], - color(item.unread > 0 ? 0xA06316 : 0x78827D), 0); + color(item.unread > 0 ? embedded_palette::kAccentDark + : embedded_palette::kTextDim), + 0); } else { @@ -1075,14 +2292,19 @@ class UConsoleDesktopShell setLabel(chat_conversation_meta_labels_[index], item.meta); lv_obj_set_style_bg_color( chat_conversation_buttons_[index], - color(item.active ? 0xD7E8DF : 0xF3F6F2), 0); + color(item.active ? embedded_palette::kSoftAmber + : embedded_palette::kPanelBg), + 0); lv_obj_set_style_border_width(chat_conversation_buttons_[index], item.active ? 1 : 0, 0); - lv_obj_set_style_border_color(chat_conversation_buttons_[index], - color(0x7EA48F), 0); + lv_obj_set_style_border_color( + chat_conversation_buttons_[index], + color(embedded_palette::kBorder), 0); lv_obj_set_style_text_color( chat_conversation_meta_labels_[index], - color(item.unread > 0 ? 0xA06316 : 0x66716E), 0); + color(item.unread > 0 ? embedded_palette::kAccentDark + : embedded_palette::kTextMuted), + 0); } const bool has_messages = !snapshot.messages.empty(); @@ -1111,12 +2333,16 @@ class UConsoleDesktopShell setLabel(chat_message_meta_labels_[index], item.meta); lv_obj_set_style_bg_color( chat_message_rows_[index], - color(item.failed ? 0xFFF0EE - : (item.outgoing ? 0xEEF8F3 : 0xFFFFFF)), + color(item.failed + ? embedded_palette::kSoftWarn + : (item.outgoing ? embedded_palette::kSoftGreen + : embedded_palette::kSurface)), 0); lv_obj_set_style_text_color( chat_message_meta_labels_[index], - color(item.failed ? 0xA23B30 : 0x66716E), 0); + color(item.failed ? embedded_palette::kError + : embedded_palette::kTextMuted), + 0); } } @@ -1124,6 +2350,8 @@ class UConsoleDesktopShell UConsoleDashboardModel dashboard_model_; UConsoleChatWorkspaceModel chat_model_; bool initialized_ = false; + bool sidebar_collapsed_ = false; + bool shortcuts_visible_ = false; Section active_section_ = Section::Overview; lv_group_t* group_ = nullptr; std::deque key_events_{}; @@ -1135,9 +2363,15 @@ class UConsoleDesktopShell lv_obj_t* top_mesh_label_ = nullptr; lv_obj_t* top_node_label_ = nullptr; lv_obj_t* top_unread_label_ = nullptr; + lv_obj_t* footer_context_label_ = nullptr; + lv_obj_t* footer_status_label_ = nullptr; + lv_obj_t* footer_shortcuts_label_ = nullptr; + lv_obj_t* shortcut_overlay_ = nullptr; lv_obj_t* metrics_panel_ = nullptr; lv_obj_t* conversation_panel_ = nullptr; lv_obj_t* chat_panel_ = nullptr; + lv_obj_t* desktop_page_panel_ = nullptr; + lv_obj_t* status_panel_ = nullptr; lv_obj_t* chat_messages_panel_ = nullptr; lv_obj_t* chat_title_label_ = nullptr; lv_obj_t* chat_meta_label_ = nullptr;