From ce1084abc743405455b625f363c8b72932022b85 Mon Sep 17 00:00:00 2001 From: "E.T" Date: Sat, 20 Jan 2024 23:35:39 +0100 Subject: [PATCH] Introduce common app list for menu and serial (#1791) --- firmware/application/CMakeLists.txt | 1 + firmware/application/ui_navigation.cpp | 353 +++++++-------------- firmware/application/ui_navigation.hpp | 33 +- firmware/application/usb_serial_shell.cpp | 29 +- firmware/application/view_factory.hpp | 39 +++ firmware/application/view_factory_base.cpp | 28 ++ firmware/application/view_factory_base.hpp | 40 +++ firmware/common/external_app.hpp | 3 +- 8 files changed, 284 insertions(+), 242 deletions(-) create mode 100644 firmware/application/view_factory.hpp create mode 100644 firmware/application/view_factory_base.cpp create mode 100644 firmware/application/view_factory_base.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 2eae541e..c5bf6f50 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -346,6 +346,7 @@ set(CPPSRC ${CPLD_20170522_DATA_CPP} ${HACKRF_CPLD_DATA_CPP} ui_external_items_menu_loader.cpp + view_factory_base.cpp ${EXTCPPSRC} ) diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 58013d89..dc689a4c 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -111,166 +111,109 @@ namespace pmem = portapack::persistent_memory; namespace ui { -// When adding or removing apps from the Menu, please update it here: -std::vector NavigationView::fixedAppListFC = { - {"adsbrx", "ADS-B", RX}, - {"ais", "AIS Boats", RX}, - {"aprsrx", "APRS", RX}, - {"audio", "Audio", RX}, - {"blerx", "BLE Rx", RX}, - {"ert", "ERT Meter", RX}, - {"level", "Level", RX}, - {"pocsagrx", "POCSAG", RX}, - {"radiosnde", "Radiosnde", RX}, - {"recon", "Recon", RX}, - {"search", "Search", RX}, - {"tpms", "TPMS Cars", RX}, - {"weather", "Weather", RX}, - {"subghzd", "SubGhzD", RX}, - {"adsbtx", "ADS-B", TX}, - {"aprstx", "APRS TX", TX}, - {"bht", "BHT Xy/EP", TX}, - {"bletx", "BLE Tx", TX}, - {"morsetx", "Morse", TX}, - {"ooktx", "OOK", TX}, - {"pocsatx", "POCSAG TX", TX}, - {"rdstx", "RDS TX", TX}, - {"soundbrd", "Soundbrd", TX}, - {"sstvtx", "SSTV", TX}, - {"touchtune", "TouchTune", TX}, - {"capture", "Capture", RX}, - {"replay", "Replay", TX}, - {"remote", "Remote", TX}, - {"scanner", "Scanner", RX}, - {"microphone", "Microphone", TX}, - {"lookingglass", "Looking Glass", RX}}; +bool CstrCmp::operator()(const char* a, const char* b) const { + return strcmp(a, b) < 0; +} + +static NavigationView::AppMap generate_app_map(const NavigationView::AppList& appList) { + NavigationView::AppMap out; + + for (auto& app : appList) { + if (app.id == nullptr) { + // Skip items with no id + continue; + } + + auto res = out.emplace(app.id, app); + if (!res.second) { + chDbgPanic("Application cannot be added, ID not unique!"); + } + } + + return out; +} + +// TODO(u-foka): Check consistency of command names (where we add rx/tx postfix) +const NavigationView::AppList NavigationView::appList = { + /* HOME ******************************************************************/ + //{"playdead", "Play dead", HOME, Color::red(), &bitmap_icon_playdead, new ViewFactory()}, + {nullptr, "Receive", HOME, Color::cyan(), &bitmap_icon_receivers, new ViewFactory()}, + {nullptr, "Transmit", HOME, Color::cyan(), &bitmap_icon_transmit, new ViewFactory()}, + {"capture", "Capture", HOME, Color::red(), &bitmap_icon_capture, new ViewFactory()}, + {"replay", "Replay", HOME, Color::green(), &bitmap_icon_replay, new ViewFactory()}, + {"remote", "Remote", HOME, ui::Color::green(), &bitmap_icon_remote, new ViewFactory()}, + {"scanner", "Scanner", HOME, Color::green(), &bitmap_icon_scanner, new ViewFactory()}, + {"microphone", "Microphone", HOME, Color::green(), &bitmap_icon_microphone, new ViewFactory()}, + {"lookingglass", "Looking Glass", HOME, Color::green(), &bitmap_icon_looking, new ViewFactory()}, + {nullptr, "Utilities", HOME, Color::cyan(), &bitmap_icon_utilities, new ViewFactory()}, + {nullptr, "Settings", HOME, Color::cyan(), &bitmap_icon_setup, new ViewFactory()}, + {nullptr, "Debug", HOME, Color::light_grey(), &bitmap_icon_debug, new ViewFactory()}, + //{"about", "About", HOME, Color::cyan(), nullptr, new ViewFactory()}, + /* RX ********************************************************************/ + //{"acars", "ACARS", RX, Color::yellow(), &bitmap_icon_adsb, new ViewFactory()}, + {"adsbrx", "ADS-B", RX, Color::green(), &bitmap_icon_adsb, new ViewFactory()}, + {"ais", "AIS Boats", RX, Color::green(), &bitmap_icon_ais, new ViewFactory()}, + {"aprsrx", "APRS", RX, Color::green(), &bitmap_icon_aprs, new ViewFactory()}, + {"audio", "Audio", RX, Color::green(), &bitmap_icon_speaker, new ViewFactory()}, + //{"btle", "BTLE", RX, Color::yellow(), &bitmap_icon_btle, new ViewFactory()}, + //{"blecomm", "BLE Comm", RX, ui::Color::orange(), &bitmap_icon_btle, new ViewFactory()}, + {"blerx", "BLE Rx", RX, Color::green(), &bitmap_icon_btle, new ViewFactory()}, + {"ert", "ERT Meter", RX, Color::green(), &bitmap_icon_ert, new ViewFactory()}, + {"level", "Level", RX, Color::green(), &bitmap_icon_options_radio, new ViewFactory()}, + {"pocsag", "POCSAG", RX, Color::green(), &bitmap_icon_pocsag, new ViewFactory()}, + {"radiosonde", "Radiosnde", RX, Color::green(), &bitmap_icon_sonde, new ViewFactory()}, + {"recon", "Recon", RX, Color::green(), &bitmap_icon_scanner, new ViewFactory()}, + {"search", "Search", RX, Color::yellow(), &bitmap_icon_search, new ViewFactory()}, + {"tmps", "TPMS Cars", RX, Color::green(), &bitmap_icon_tpms, new ViewFactory()}, + {"weather", "Weather", RX, Color::green(), &bitmap_icon_thermometer, new ViewFactory()}, + {"subghzd", "SubGhzD", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory()}, + //{"fskrx", "FSK RX", RX, Color::yellow(), &bitmap_icon_remote, new ViewFactory()}, + //{"dmr", "DMR", RX, Color::dark_grey(), &bitmap_icon_dmr, new ViewFactory()}, + //{"sigfox", "SIGFOX", RX, Color::dark_grey(), &bitmap_icon_fox, new ViewFactory()}, + //{"lora", "LoRa", RX, Color::dark_grey(), &bitmap_icon_lora, new ViewFactory()}, + //{"sstv", "SSTV", RX, Color::dark_grey(), &bitmap_icon_sstv, new ViewFactory()}, + //{"tetra", "TETRA", RX, Color::dark_grey(), &bitmap_icon_tetra, new ViewFactory()}, + /* TX ********************************************************************/ + {"adsbtx", "ADS-B TX", TX, ui::Color::green(), &bitmap_icon_adsb, new ViewFactory()}, + {"aprstx", "APRS TX", TX, ui::Color::green(), &bitmap_icon_aprs, new ViewFactory()}, + {"bht", "BHT Xy/EP", TX, ui::Color::green(), &bitmap_icon_bht, new ViewFactory()}, + {"bletx", "BLE Tx", TX, ui::Color::green(), &bitmap_icon_btle, new ViewFactory()}, + //{"keyfob", "Key fob", TX, ui::Color::orange(), &bitmap_icon_keyfob, new ViewFactory()}, + {"morse", "Morse", TX, ui::Color::green(), &bitmap_icon_morse, new ViewFactory()}, + //{"nuoptixdtmf", "Nuoptix DTMF", TX, ui::Color::green(), &bitmap_icon_nuoptix, new ViewFactory()}, + {"ooktx", "OOK", TX, ui::Color::yellow(), &bitmap_icon_remote, new ViewFactory()}, + {"pocsagtx", "POCSAG TX", TX, ui::Color::green(), &bitmap_icon_pocsag, new ViewFactory()}, + {"rdstx", "RDS", TX, ui::Color::green(), &bitmap_icon_rds, new ViewFactory()}, + {"soundbrd", "Soundbrd", TX, ui::Color::green(), &bitmap_icon_soundboard, new ViewFactory()}, + {"sstvtx", "SSTV", TX, ui::Color::green(), &bitmap_icon_sstv, new ViewFactory()}, + {"touchtune", "TouchTune", TX, ui::Color::green(), &bitmap_icon_touchtunes, new ViewFactory()}, + /* UTILITIES *************************************************************/ + {"antennalength", "Antenna Length", UTILITIES, Color::green(), &bitmap_icon_tools_antenna, new ViewFactory()}, + {"filemanager", "File Manager", UTILITIES, Color::green(), &bitmap_icon_dir, new ViewFactory()}, + {"freqman", "Freq. Manager", UTILITIES, Color::green(), &bitmap_icon_freqman, new ViewFactory()}, + {"notepad", "Notepad", UTILITIES, Color::dark_cyan(), &bitmap_icon_notepad, new ViewFactory()}, + {"iqtrim", "IQ Trim", UTILITIES, Color::orange(), &bitmap_icon_trim, new ViewFactory()}, + {nullptr, "SD Over USB", UTILITIES, Color::yellow(), &bitmap_icon_hackrf, new ViewFactory()}, + {"signalgen", "Signal Gen", UTILITIES, Color::green(), &bitmap_icon_cwgen, new ViewFactory()}, + //{"testapp", "Test App", UTILITIES, Color::dark_grey(), nullptr, new ViewFactory()}, + //{"tonesearch", "Tone Search", UTILITIES, Color::dark_grey(), nullptr, new ViewFactory()}, + {"wavview", "Wav View", UTILITIES, Color::yellow(), &bitmap_icon_soundboard, new ViewFactory()}, + // Dangerous apps. + {nullptr, "Flash Utility", UTILITIES, Color::red(), &bitmap_icon_temperature, new ViewFactory()}, + {nullptr, "Wipe SD card", UTILITIES, Color::red(), &bitmap_icon_tools_wipesd, new ViewFactory()}, +}; + +const NavigationView::AppMap NavigationView::appMap = generate_app_map(NavigationView::appList); bool NavigationView::StartAppByName(const char* name) { home(false); - if (strcmp(name, "adsbrx") == 0) { - push(); - return true; - } - if (strcmp(name, "ais") == 0) { - push(); - return true; - } - if (strcmp(name, "aprsrx") == 0) { - push(); - return true; - } - if (strcmp(name, "audio") == 0) { - push(); - return true; - } - if (strcmp(name, "blerx") == 0) { - push(); - return true; - } - if (strcmp(name, "ert") == 0) { - push(); - return true; - } - if (strcmp(name, "level") == 0) { - push(); - return true; - } - if (strcmp(name, "pocsagrx") == 0) { - push(); - return true; - } - if (strcmp(name, "radiosnode") == 0) { - push(); - return true; - } - if (strcmp(name, "recon") == 0) { - push(); - return true; - } - if (strcmp(name, "search") == 0) { - push(); - return true; - } - if (strcmp(name, "tpms") == 0) { - push(); - return true; - } - if (strcmp(name, "weather") == 0) { - push(); - return true; - } - if (strcmp(name, "subghzd") == 0) { - push(); - return true; - } - if (strcmp(name, "adsbtx") == 0) { - push(); - return true; - } - if (strcmp(name, "aprstx") == 0) { - push(); - return true; - } - if (strcmp(name, "bht") == 0) { - push(); - return true; - } - if (strcmp(name, "bletx") == 0) { - push(); - return true; - } - if (strcmp(name, "morsetx") == 0) { - push(); - return true; - } - if (strcmp(name, "ooktx") == 0) { - push(); - return true; - } - if (strcmp(name, "pocsatx") == 0) { - push(); - return true; - } - if (strcmp(name, "rdstx") == 0) { - push(); - return true; - } - if (strcmp(name, "soundbrd") == 0) { - push(); - return true; - } - if (strcmp(name, "sstvtx") == 0) { - push(); - return true; - } - if (strcmp(name, "touchtune") == 0) { - push(); - return true; - } - if (strcmp(name, "capture") == 0) { - push(); - return true; - } - if (strcmp(name, "replay") == 0) { - push(); - return true; - } - if (strcmp(name, "remote") == 0) { - push(); - return true; - } - if (strcmp(name, "scanner") == 0) { - push(); - return true; - } - if (strcmp(name, "microphone") == 0) { - push(); - return true; - } - if (strcmp(name, "lookingglass") == 0) { - push(); + + auto it = appMap.find(name); + if (it != appMap.end()) { + push_view(std::unique_ptr(it->second.viewFactory->produce(*this))); return true; } + return false; } @@ -727,39 +670,25 @@ bool NavigationView::set_on_pop(std::function on_pop) { return true; } +/* Helpers **************************************************************/ + +static void add_apps(NavigationView& nav, BtnGridView& grid, app_location_t loc) { + for (auto& app : NavigationView::appList) { + if (app.menuLocation == loc) { + grid.add_item({app.displayName, app.iconColor, app.icon, + [&nav, &app]() { nav.push_view(std::unique_ptr(app.viewFactory->produce(nav))); }}); + } + }; +} + /* ReceiversMenuView *****************************************************/ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) { if (pmem::show_gui_return_icon()) { - add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); + add_item({"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}); } - add_items({ - // {"ACARS", Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push(); }}, - {"ADS-B", Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push(); }}, - {"AIS Boats", Color::green(), &bitmap_icon_ais, [&nav]() { nav.push(); }}, - //{"Analog TV", Color::yellow(), &bitmap_icon_sstv, [&nav]() { nav.push(); }}, //moved to ext - {"APRS", Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push(); }}, - {"Audio", Color::green(), &bitmap_icon_speaker, [&nav]() { nav.push(); }}, - //{"BTLE", Color::yellow(), &bitmap_icon_btle, [&nav]() { nav.push(); }}, - //{"BLE Comm", ui::Color::orange(), &bitmap_icon_btle, [&nav]() { nav.push(); }}, - {"BLE Rx", Color::green(), &bitmap_icon_btle, [&nav]() { nav.push(); }}, - {"ERT Meter", Color::green(), &bitmap_icon_ert, [&nav]() { nav.push(); }}, - {"Level", Color::green(), &bitmap_icon_options_radio, [&nav]() { nav.push(); }}, - //{"NRF", Color::yellow(), &bitmap_icon_nrf, [&nav]() { nav.push(); }}, //moved to ext - {"POCSAG", Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push(); }}, - {"Radiosnde", Color::green(), &bitmap_icon_sonde, [&nav]() { nav.push(); }}, - {"Recon", Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push(); }}, - {"Search", Color::yellow(), &bitmap_icon_search, [&nav]() { nav.push(); }}, - {"TPMS Cars", Color::green(), &bitmap_icon_tpms, [&nav]() { nav.push(); }}, - {"Weather", Color::green(), &bitmap_icon_thermometer, [&nav]() { nav.push(); }}, - {"SubGhzD", Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push(); }}, - // {"FSK RX", Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push(); }}, - // {"DMR", Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push(); }}, - // {"SIGFOX", Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push(); }}, - // {"LoRa", Color::dark_grey(), &bitmap_icon_lora, [&nav](){ nav.push(); }}, - // {"SSTV", Color::dark_grey(), &bitmap_icon_sstv, [&nav](){ nav.push(); }}, - // {"TETRA", Color::dark_grey(), &bitmap_icon_tetra, [&nav](){ nav.push(); }}, - }); + + add_apps(nav, *this, RX); for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::RX, nav)) { add_item(gridItem); @@ -772,27 +701,8 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { if (pmem::show_gui_return_icon()) { add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); } - add_items({ - {"ADS-B TX", ui::Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push(); }}, - {"APRS TX", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push(); }}, - {"BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav]() { nav.push(); }}, - {"BLE Tx", ui::Color::green(), &bitmap_icon_btle, [&nav]() { nav.push(); }}, - // {"BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav]() { nav.push(); }}, //moved to ext - //{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push(); }}, //moved to ext - //{"Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav]() { nav.push(); }}, //moved to ext - // { "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push(); }}, - // {"LGE", ui::Color::yellow(), &bitmap_icon_lge, [&nav]() { nav.push(); }}, //moved to ext - {"Morse", ui::Color::green(), &bitmap_icon_morse, [&nav]() { nav.push(); }}, - // { "Nuoptix DTMF", ui::Color::green(), &bitmap_icon_nuoptix, [&nav](){ nav.push(); }}, - {"OOK", ui::Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push(); }}, - {"POCSAG TX", ui::Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push(); }}, - {"RDS", ui::Color::green(), &bitmap_icon_rds, [&nav]() { nav.push(); }}, - {"Soundbrd", ui::Color::green(), &bitmap_icon_soundboard, [&nav]() { nav.push(); }}, - //{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push(); }}, - {"SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav]() { nav.push(); }}, - // {"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push(); }}, //moved to ext - {"TouchTune", ui::Color::green(), &bitmap_icon_touchtunes, [&nav]() { nav.push(); }}, - }); + + add_apps(nav, *this, TX); for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::TX, nav)) { add_item(gridItem); @@ -805,22 +715,8 @@ UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) { if (pmem::show_gui_return_icon()) { add_items({{"..", Color::light_grey(), &bitmap_icon_previous, [&nav]() { nav.pop(); }}}); } - add_items({ - {"Antenna Length", Color::green(), &bitmap_icon_tools_antenna, [&nav]() { nav.push(); }}, - {"File Manager", Color::green(), &bitmap_icon_dir, [&nav]() { nav.push(); }}, - {"Freq. Manager", Color::green(), &bitmap_icon_freqman, [&nav]() { nav.push(); }}, - {"Notepad", Color::dark_cyan(), &bitmap_icon_notepad, [&nav]() { nav.push(); }}, - {"IQ Trim", Color::orange(), &bitmap_icon_trim, [&nav]() { nav.push(); }}, - {"SD Over USB", Color::yellow(), &bitmap_icon_hackrf, [&nav]() { nav.push(); }}, - {"Signal Gen", Color::green(), &bitmap_icon_cwgen, [&nav]() { nav.push(); }}, - // {"Test App", Color::dark_grey(), nullptr, [&nav](){ nav.push(); }}, - // {"Tone Search", Color::dark_grey(), nullptr, [&nav](){ nav.push(); }}, - {"Wav View", Color::yellow(), &bitmap_icon_soundboard, [&nav]() { nav.push(); }}, - // Dangerous apps. - {"Flash Utility", Color::red(), &bitmap_icon_temperature, [&nav]() { nav.push(); }}, - {"Wipe SD card", Color::red(), &bitmap_icon_tools_wipesd, [&nav]() { nav.push(); }}, - }); + add_apps(nav, *this, UTILITIES); for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::UTILITIES, nav)) { add_item(gridItem); @@ -844,22 +740,9 @@ void SystemMenuView::hackrf_mode(NavigationView& nav) { } SystemMenuView::SystemMenuView(NavigationView& nav) { - add_items({ - // {"Play dead", Color::red(), &bitmap_icon_playdead, [&nav]() { nav.push(); }}, - {"Receive", Color::cyan(), &bitmap_icon_receivers, [&nav]() { nav.push(); }}, - {"Transmit", Color::cyan(), &bitmap_icon_transmit, [&nav]() { nav.push(); }}, - {"Capture", Color::red(), &bitmap_icon_capture, [&nav]() { nav.push(); }}, - {"Replay", Color::green(), &bitmap_icon_replay, [&nav]() { nav.push(); }}, - {"Remote", ui::Color::green(), &bitmap_icon_remote, [&nav]() { nav.push(); }}, - {"Scanner", Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push(); }}, - {"Microphone", Color::green(), &bitmap_icon_microphone, [&nav]() { nav.push(); }}, - {"Looking Glass", Color::green(), &bitmap_icon_looking, [&nav]() { nav.push(); }}, - {"Utilities", Color::cyan(), &bitmap_icon_utilities, [&nav]() { nav.push(); }}, - {"Settings", Color::cyan(), &bitmap_icon_setup, [&nav]() { nav.push(); }}, - {"Debug", Color::light_grey(), &bitmap_icon_debug, [&nav]() { nav.push(); }}, - {"HackRF", Color::cyan(), &bitmap_icon_hackrf, [this, &nav]() { hackrf_mode(nav); }}, - // {"About", Color::cyan(), nullptr, [&nav]() { nav.push(); }}, - }); + add_apps(nav, *this, HOME); + + add_item({"HackRF", Color::cyan(), &bitmap_icon_hackrf, [this, &nav]() { hackrf_mode(nav); }}); set_max_rows(2); // allow wider buttons set_arrow_enabled(false); diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index ae07924c..61333e15 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -23,6 +23,10 @@ #ifndef __UI_NAVIGATION_H__ #define __UI_NAVIGATION_H__ +#include +#include +#include + #include "ui.hpp" #include "ui_widget.hpp" #include "ui_focus.hpp" @@ -41,8 +45,7 @@ #include "lfsr_random.hpp" #include "sd_card.hpp" #include "external_app.hpp" -#include -#include +#include "view_factory.hpp" // for incrementing fake date when RTC battery is dead #define DATE_FILEFLAG u"/SETTINGS/DATE_FILEFLAG" @@ -57,6 +60,22 @@ enum modal_t { ABORT }; +class CstrCmp { + public: + bool operator()(const char* a, const char* b) const; +}; + +// Should only be used as part of the appList in NavigationView, the viewFactory will never be destroyed. +class AppInfo { + public: + const char* id; // MUST be unique! Used by serial command to start the app so it also has to make sense + const char* displayName; + app_location_t menuLocation; + Color iconColor; + const Bitmap* icon; + ViewFactoryBase* viewFactory; // Never destroyed, and I believe it's ok ;) Having a unique_ptr here breaks the initializer list of appList +}; + struct AppInfoConsole { const char* appCallName; const char* appFriendlyName; @@ -106,8 +125,14 @@ class NavigationView : public View { * Returns true if the handler was bound successfully. */ bool set_on_pop(std::function on_pop); - static std::vector fixedAppListFC; // fixed app list for console. vector, so can be incomplete and still iterateable - bool StartAppByName(const char* name); // Starts a View (app) by name stored in fixedAppListFC. This is to start apps from console + // App list is used to preserve order, so the menu items in the menu grid can stay in place + // App map is used to look up apps by id used by serial app start + using AppMap = std::map; + using AppList = std::vector; + static const AppMap appMap; + static const AppList appList; + + bool StartAppByName(const char* name); // Starts a View (app) by name stored in appListFC. This is to start apps from console private: struct ViewState { std::unique_ptr view; diff --git a/firmware/application/usb_serial_shell.cpp b/firmware/application/usb_serial_shell.cpp index 3b5c1fb1..a930e3df 100644 --- a/firmware/application/usb_serial_shell.cpp +++ b/firmware/application/usb_serial_shell.cpp @@ -710,6 +710,30 @@ static void printAppInfo(BaseSequentialStream* chp, ui::AppInfoConsole& element) } } +static void printAppInfo(BaseSequentialStream* chp, const ui::AppInfo& element) { + if (strlen(element.id) == 0) return; + chprintf(chp, element.id); + chprintf(chp, " "); + chprintf(chp, element.displayName); + chprintf(chp, " "); + switch (element.menuLocation) { + case RX: + chprintf(chp, "[RX]\r\n"); + break; + case TX: + chprintf(chp, "[TX]\r\n"); + break; + case UTILITIES: + chprintf(chp, "[UTIL]\r\n"); + break; + case DEBUG: + chprintf(chp, "[DEBUG]\r\n"); + break; + default: + break; + } +} + // returns the installed apps, those can be called by appstart APPNAME static void cmd_applist(BaseSequentialStream* chp, int argc, char* argv[]) { (void)argc; @@ -720,8 +744,9 @@ static void cmd_applist(BaseSequentialStream* chp, int argc, char* argv[]) { if (!top_widget) return; auto nav = static_cast(top_widget)->get_navigation_view(); if (!nav) return; - for (auto element : ui::NavigationView::fixedAppListFC) { - printAppInfo(chp, element); + // TODO(u-foka): Somehow order static and dynamic app lists together + for (auto& element : ui::NavigationView::appMap) { // Use the map as its ordered by id + printAppInfo(chp, element.second); } ui::ExternalItemsMenuLoader::load_all_external_items_callback([chp](ui::AppInfoConsole& info) { printAppInfo(chp, info); diff --git a/firmware/application/view_factory.hpp b/firmware/application/view_factory.hpp new file mode 100644 index 00000000..da6754ae --- /dev/null +++ b/firmware/application/view_factory.hpp @@ -0,0 +1,39 @@ +/* + * Copyright 2024 Tamas Eisenberger + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __VIEW_FACTORY_HPP__ +#define __VIEW_FACTORY_HPP__ + +#include "view_factory_base.hpp" + +namespace ui { + +template +class ViewFactory : public ViewFactoryBase { + public: + virtual std::unique_ptr produce(NavigationView& nav) const override { + return std::unique_ptr(new T(nav)); + } +}; + +} // namespace ui + +#endif //__VIEW_FACTORY_HPP__ diff --git a/firmware/application/view_factory_base.cpp b/firmware/application/view_factory_base.cpp new file mode 100644 index 00000000..4aa9bb30 --- /dev/null +++ b/firmware/application/view_factory_base.cpp @@ -0,0 +1,28 @@ +/* + * Copyright 2024 Tamas Eisenberger + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "view_factory_base.hpp" + +namespace ui { + +ViewFactoryBase::~ViewFactoryBase() {} + +} // namespace ui diff --git a/firmware/application/view_factory_base.hpp b/firmware/application/view_factory_base.hpp new file mode 100644 index 00000000..f84e0b09 --- /dev/null +++ b/firmware/application/view_factory_base.hpp @@ -0,0 +1,40 @@ +/* + * Copyright 2024 Tamas Eisenberger + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __VIEW_FACTORY_BASE_HPP__ +#define __VIEW_FACTORY_BASE_HPP__ + +#include +#include "ui_widget.hpp" + +namespace ui { + +class NavigationView; + +class ViewFactoryBase { + public: + virtual ~ViewFactoryBase(); + virtual std::unique_ptr produce(NavigationView& nav) const = 0; +}; + +} // namespace ui + +#endif //__VIEW_FACTORY_BASE_HPP__ diff --git a/firmware/common/external_app.hpp b/firmware/common/external_app.hpp index 38824fc2..0cae972c 100644 --- a/firmware/common/external_app.hpp +++ b/firmware/common/external_app.hpp @@ -34,7 +34,8 @@ enum app_location_t : uint32_t { UTILITIES = 0, RX, TX, - DEBUG + DEBUG, + HOME }; struct application_information_t {