diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 9a6b97c01..149dd9eac 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -1046,11 +1046,16 @@ void Screen::setFrames(FrameFocus focus) indicatorIcons.push_back(icon_mail); #ifndef USE_EINK - if (!hiddenFrames.nodelist) { - fsi.positions.nodelist = numframes; - normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicNodeListScreen; + if (!hiddenFrames.nodelist_nodes) { + fsi.positions.nodelist_nodes = numframes; + normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicListScreen_Nodes; indicatorIcons.push_back(icon_nodes); } + if (!hiddenFrames.nodelist_location) { + fsi.positions.nodelist_location = numframes; + normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicListScreen_Location; + indicatorIcons.push_back(icon_list); + } #endif // Show detailed node views only on E-Ink builds @@ -1072,11 +1077,13 @@ void Screen::setFrames(FrameFocus focus) } #endif #if HAS_GPS +#ifdef USE_EINK if (!hiddenFrames.nodelist_bearings) { fsi.positions.nodelist_bearings = numframes; normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses; indicatorIcons.push_back(icon_list); } +#endif if (!hiddenFrames.gps) { fsi.positions.gps = numframes; normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen; @@ -1238,8 +1245,11 @@ void Screen::setFrameImmediateDraw(FrameCallback *drawFrames) void Screen::toggleFrameVisibility(const std::string &frameName) { #ifndef USE_EINK - if (frameName == "nodelist") { - hiddenFrames.nodelist = !hiddenFrames.nodelist; + if (frameName == "nodelist_nodes") { + hiddenFrames.nodelist_nodes = !hiddenFrames.nodelist_nodes; + } + if (frameName == "nodelist_location") { + hiddenFrames.nodelist_location = !hiddenFrames.nodelist_location; } #endif #ifdef USE_EINK @@ -1254,9 +1264,11 @@ void Screen::toggleFrameVisibility(const std::string &frameName) } #endif #if HAS_GPS +#ifdef USE_EINK if (frameName == "nodelist_bearings") { hiddenFrames.nodelist_bearings = !hiddenFrames.nodelist_bearings; } +#endif if (frameName == "gps") { hiddenFrames.gps = !hiddenFrames.gps; } @@ -1278,8 +1290,10 @@ void Screen::toggleFrameVisibility(const std::string &frameName) bool Screen::isFrameHidden(const std::string &frameName) const { #ifndef USE_EINK - if (frameName == "nodelist") - return hiddenFrames.nodelist; + if (frameName == "nodelist_nodes") + return hiddenFrames.nodelist_nodes; + if (frameName == "nodelist_location") + return hiddenFrames.nodelist_location; #endif #ifdef USE_EINK if (frameName == "nodelist_lastheard") @@ -1290,8 +1304,10 @@ bool Screen::isFrameHidden(const std::string &frameName) const return hiddenFrames.nodelist_distance; #endif #if HAS_GPS +#ifdef USE_EINK if (frameName == "nodelist_bearings") return hiddenFrames.nodelist_bearings; +#endif if (frameName == "gps") return hiddenFrames.gps; #endif @@ -1716,7 +1732,8 @@ int Screen::handleInputEvent(const InputEvent *event) this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite && this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) { menuHandler::favoriteBaseMenu(); - } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist || + } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_nodes || + this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_location || this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_lastheard || this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal || this->ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_distance || diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index d6e71d356..4bb808970 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -654,7 +654,8 @@ class Screen : public concurrency::OSThread uint8_t gps = 255; uint8_t home = 255; uint8_t textMessage = 255; - uint8_t nodelist = 255; + uint8_t nodelist_nodes = 255; + uint8_t nodelist_location = 255; uint8_t nodelist_lastheard = 255; uint8_t nodelist_hopsignal = 255; uint8_t nodelist_distance = 255; @@ -677,7 +678,8 @@ class Screen : public concurrency::OSThread bool home = false; bool clock = false; #ifndef USE_EINK - bool nodelist = false; + bool nodelist_nodes = false; + bool nodelist_location = false; #endif #ifdef USE_EINK bool nodelist_lastheard = false; @@ -685,7 +687,9 @@ class Screen : public concurrency::OSThread bool nodelist_distance = false; #endif #if HAS_GPS +#ifdef USE_EINK bool nodelist_bearings = false; +#endif bool gps = false; #endif bool lora = false; diff --git a/src/graphics/draw/MenuHandler.cpp b/src/graphics/draw/MenuHandler.cpp index 99c4d2915..f07d29a66 100644 --- a/src/graphics/draw/MenuHandler.cpp +++ b/src/graphics/draw/MenuHandler.cpp @@ -1111,20 +1111,33 @@ void menuHandler::positionBaseMenu() void menuHandler::nodeListMenu() { - enum optionsNumbers { Back, Favorite, TraceRoute, Verify, Reset, enumEnd }; -#if defined(M5STACK_UNITC6L) - static const char *optionsArray[] = {"Back", "Add Favorite", "Reset Node"}; -#else - static const char *optionsArray[] = {"Back", "Add Favorite", "Trace Route", "Key Verification", "Reset NodeDB"}; + enum optionsNumbers { Back, Favorite, TraceRoute, Verify, Reset, NodeNameLength, enumEnd }; + static const char *optionsArray[enumEnd] = {"Back"}; + static int optionsEnumArray[enumEnd] = {Back}; + int options = 1; + + optionsArray[options] = "Add Favorite"; + optionsEnumArray[options++] = Favorite; + optionsArray[options] = "Trace Route"; + optionsEnumArray[options++] = TraceRoute; + +#if !defined(M5STACK_UNITC6L) + optionsArray[options] = "Key Verification"; + optionsEnumArray[options++] = Verify; #endif + +#if defined(T_DECK) || defined(T_LORA_PAGER) || defined(HACKADAY_COMMUNICATOR) + optionsArray[options] = "Show Long/Short Name"; + optionsEnumArray[options++] = NodeNameLength; +#endif + optionsArray[options] = "Reset NodeDB"; + optionsEnumArray[options++] = Reset; + BannerOverlayOptions bannerOptions; bannerOptions.message = "Node Action"; bannerOptions.optionsArrayPtr = optionsArray; -#if defined(M5STACK_UNITC6L) - bannerOptions.optionsCount = 3; -#else - bannerOptions.optionsCount = 5; -#endif + bannerOptions.optionsCount = options; + bannerOptions.optionsEnumPtr = optionsEnumArray; bannerOptions.bannerCallback = [](int selected) -> void { if (selected == Favorite) { menuQueue = add_favorite; @@ -1138,6 +1151,9 @@ void menuHandler::nodeListMenu() } else if (selected == TraceRoute) { menuQueue = trace_route_menu; screen->runNow(); + } else if (selected == NodeNameLength) { + menuHandler::menuQueue = menuHandler::node_name_length_menu; + screen->runNow(); } }; screen->showOverlayBanner(bannerOptions); @@ -1752,16 +1768,11 @@ void menuHandler::screenOptionsMenu() hasSupportBrightness = false; #endif - enum optionsNumbers { Back, NodeNameLength, Brightness, ScreenColor, FrameToggles, DisplayUnits }; + enum optionsNumbers { Back, Brightness, ScreenColor, FrameToggles, DisplayUnits }; static const char *optionsArray[5] = {"Back"}; static int optionsEnumArray[5] = {Back}; int options = 1; -#if defined(T_DECK) || defined(T_LORA_PAGER) || defined(HACKADAY_COMMUNICATOR) - optionsArray[options] = "Show Long/Short Name"; - optionsEnumArray[options++] = NodeNameLength; -#endif - // Only show brightness for B&W displays if (hasSupportBrightness) { optionsArray[options] = "Brightness"; @@ -1793,9 +1804,6 @@ void menuHandler::screenOptionsMenu() } else if (selected == ScreenColor) { menuHandler::menuQueue = menuHandler::tftcolormenupicker; screen->runNow(); - } else if (selected == NodeNameLength) { - menuHandler::menuQueue = menuHandler::node_name_length_menu; - screen->runNow(); } else if (selected == FrameToggles) { menuHandler::menuQueue = menuHandler::FrameToggles; screen->runNow(); @@ -1891,7 +1899,8 @@ void menuHandler::FrameToggles_menu() { enum optionsNumbers { Finish, - nodelist, + nodelist_nodes, + nodelist_location, nodelist_lastheard, nodelist_hopsignal, nodelist_distance, @@ -1912,20 +1921,25 @@ void menuHandler::FrameToggles_menu() static int lastSelectedIndex = 0; #ifndef USE_EINK - optionsArray[options] = screen->isFrameHidden("nodelist") ? "Show Node List" : "Hide Node List"; - optionsEnumArray[options++] = nodelist; -#endif -#ifdef USE_EINK + optionsArray[options] = screen->isFrameHidden("nodelist_nodes") ? "Show Node List" : "Hide Node List"; + optionsEnumArray[options++] = nodelist_nodes; +#else optionsArray[options] = screen->isFrameHidden("nodelist_lastheard") ? "Show NL - Last Heard" : "Hide NL - Last Heard"; optionsEnumArray[options++] = nodelist_lastheard; optionsArray[options] = screen->isFrameHidden("nodelist_hopsignal") ? "Show NL - Hops/Signal" : "Hide NL - Hops/Signal"; optionsEnumArray[options++] = nodelist_hopsignal; +#endif + +#if HAS_GPS +#ifndef USE_EINK + optionsArray[options] = screen->isFrameHidden("nodelist_location") ? "Show Node Location List" : "Hide Node Location List"; + optionsEnumArray[options++] = nodelist_location; +#else optionsArray[options] = screen->isFrameHidden("nodelist_distance") ? "Show NL - Distance" : "Hide NL - Distance"; optionsEnumArray[options++] = nodelist_distance; -#endif -#if HAS_GPS - optionsArray[options] = screen->isFrameHidden("nodelist_bearings") ? "Show Bearings" : "Hide Bearings"; + optionsArray[options] = screen->isFrameHidden("nodelist_bearings") ? "Show NL - Bearings" : "Hide NL - Bearings"; optionsEnumArray[options++] = nodelist_bearings; +#endif optionsArray[options] = screen->isFrameHidden("gps") ? "Show Position" : "Hide Position"; optionsEnumArray[options++] = gps; @@ -1964,8 +1978,12 @@ void menuHandler::FrameToggles_menu() if (selected == Finish) { screen->setFrames(Screen::FOCUS_DEFAULT); - } else if (selected == nodelist) { - screen->toggleFrameVisibility("nodelist"); + } else if (selected == nodelist_nodes) { + screen->toggleFrameVisibility("nodelist_nodes"); + menuHandler::menuQueue = menuHandler::FrameToggles; + screen->runNow(); + } else if (selected == nodelist_location) { + screen->toggleFrameVisibility("nodelist_location"); menuHandler::menuQueue = menuHandler::FrameToggles; screen->runNow(); } else if (selected == nodelist_lastheard) { @@ -2184,4 +2202,4 @@ void menuHandler::saveUIConfig() } // namespace graphics -#endif +#endif \ No newline at end of file diff --git a/src/graphics/draw/NodeListRenderer.cpp b/src/graphics/draw/NodeListRenderer.cpp index b073f17b3..a1020268e 100644 --- a/src/graphics/draw/NodeListRenderer.cpp +++ b/src/graphics/draw/NodeListRenderer.cpp @@ -46,7 +46,8 @@ void drawScaledXBitmap16x16(int x, int y, int width, int height, const uint8_t * } // Static variables for dynamic cycling -static NodeListMode currentMode = MODE_LAST_HEARD; +static ListMode_Node currentMode_Nodes = MODE_LAST_HEARD; +static ListMode_Location currentMode_Location = MODE_DISTANCE; static int scrollIndex = 0; // ============================= @@ -55,7 +56,7 @@ static int scrollIndex = 0; const char *getSafeNodeName(OLEDDisplay *display, meshtastic_NodeInfoLite *node) { - static char nodeName[16]; // single static buffer we return + static char nodeName[25]; // single static buffer we return nodeName[0] = '\0'; auto writeFallbackId = [&] { @@ -109,9 +110,9 @@ const char *getSafeNodeName(OLEDDisplay *display, meshtastic_NodeInfoLite *node) return nodeName; } -const char *getCurrentModeTitle(int screenWidth) +const char *getCurrentModeTitle_Nodes(int screenWidth) { - switch (currentMode) { + switch (currentMode_Nodes) { case MODE_LAST_HEARD: return "Last Heard"; case MODE_HOP_SIGNAL: @@ -120,8 +121,18 @@ const char *getCurrentModeTitle(int screenWidth) #else return (isHighResolution) ? "Hops/Signal" : "Hops/Sig"; #endif + default: + return "Nodes"; + } +} + +const char *getCurrentModeTitle_Location(int screenWidth) +{ + switch (currentMode_Location) { case MODE_DISTANCE: return "Distance"; + case MODE_BEARING: + return "Bearings"; default: return "Nodes"; } @@ -140,10 +151,8 @@ int calculateMaxScroll(int totalEntries, int visibleRows) void drawColumnSeparator(OLEDDisplay *display, int16_t x, int16_t yStart, int16_t yEnd) { - int columnWidth = display->getWidth() / 2; - int separatorX = x + columnWidth - 2; for (int y = yStart; y <= yEnd; y += 2) { - display->setPixel(separatorX, y); + display->setPixel(x, y); } } @@ -332,18 +341,15 @@ void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16 } } -void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth) +void drawEntryDynamic_Nodes(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth) { - switch (currentMode) { + switch (currentMode_Nodes) { case MODE_LAST_HEARD: drawEntryLastHeard(display, node, x, y, columnWidth); break; case MODE_HOP_SIGNAL: drawEntryHopSignal(display, node, x, y, columnWidth); break; - case MODE_DISTANCE: - drawNodeDistance(display, node, x, y, columnWidth); - break; default: break; } @@ -436,9 +442,15 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t locationScreen = true; #if defined(M5STACK_UNITC6L) int columnWidth = display->getWidth(); +#elif defined(T_LORA_PAGER) + int columnWidth = display->getWidth() / 3; + if (config.display.use_long_node_name) { + columnWidth = display->getWidth() / 2; + } #else int columnWidth = display->getWidth() / 2; #endif + display->clear(); // Draw the battery/time header @@ -453,6 +465,11 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t int visibleNodeRows = totalRowsAvailable; #if defined(M5STACK_UNITC6L) int totalColumns = 1; +#elif defined(T_LORA_PAGER) + int totalColumns = 3; + if (config.display.use_long_node_name) { + totalColumns = 2; + } #else int totalColumns = 2; #endif @@ -502,7 +519,9 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t // Draw column separator if (shownCount > 0) { const int firstNodeY = y + 3; - drawColumnSeparator(display, x, firstNodeY, lastNodeY); + for (int horizontal_offset = 1; horizontal_offset < totalColumns; horizontal_offset++) { + drawColumnSeparator(display, columnWidth * horizontal_offset, firstNodeY, lastNodeY); + } } #endif @@ -516,10 +535,11 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t // ============================= #ifndef USE_EINK -void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +// Node list for Last Heard and Hop Signal views +void drawDynamicListScreen_Nodes(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { // Static variables to track mode and duration - static NodeListMode lastRenderedMode = MODE_COUNT; + static ListMode_Node lastRenderedMode = MODE_COUNT_NODE; static unsigned long modeStartTime = 0; unsigned long now = millis(); @@ -532,23 +552,65 @@ void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, } #endif // On very first call (on boot or state enter) - if (lastRenderedMode == MODE_COUNT) { - currentMode = MODE_LAST_HEARD; + if (lastRenderedMode == MODE_COUNT_NODE) { + currentMode_Nodes = MODE_LAST_HEARD; modeStartTime = now; } // Time to switch to next mode? if (now - modeStartTime >= getModeCycleIntervalMs()) { - currentMode = static_cast((currentMode + 1) % MODE_COUNT); + currentMode_Nodes = static_cast((currentMode_Nodes + 1) % MODE_COUNT_NODE); modeStartTime = now; } // Render screen based on currentMode - const char *title = getCurrentModeTitle(display->getWidth()); - drawNodeListScreen(display, state, x, y, title, drawEntryDynamic); + const char *title = getCurrentModeTitle_Nodes(display->getWidth()); + drawNodeListScreen(display, state, x, y, title, drawEntryDynamic_Nodes); // Track the last mode to avoid reinitializing modeStartTime - lastRenderedMode = currentMode; + lastRenderedMode = currentMode_Nodes; +} + +// Node list for Distance and Bearings views +void drawDynamicListScreen_Location(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) +{ + // Static variables to track mode and duration + static ListMode_Location lastRenderedMode = MODE_COUNT_LOCATION; + static unsigned long modeStartTime = 0; + + unsigned long now = millis(); + +#if defined(M5STACK_UNITC6L) + display->clear(); + if (now - lastSwitchTime >= 3000) { + display->display(); + lastSwitchTime = now; + } +#endif + // On very first call (on boot or state enter) + if (lastRenderedMode == MODE_COUNT_LOCATION) { + currentMode_Location = MODE_DISTANCE; + modeStartTime = now; + } + + // Time to switch to next mode? + if (now - modeStartTime >= getModeCycleIntervalMs()) { + currentMode_Location = static_cast((currentMode_Location + 1) % MODE_COUNT_LOCATION); + modeStartTime = now; + } + + // Render screen based on currentMode + const char *title = getCurrentModeTitle_Location(display->getWidth()); + + // Render screen based on currentMode_Location + if (currentMode_Location == MODE_DISTANCE) { + drawNodeListScreen(display, state, x, y, title, drawNodeDistance); + } else if (currentMode_Location == MODE_BEARING) { + drawNodeListWithCompasses(display, state, x, y); + } + + // Track the last mode to avoid reinitializing modeStartTime + lastRenderedMode = currentMode_Location; } #endif @@ -569,14 +631,12 @@ void drawHopSignalScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_ #endif drawNodeListScreen(display, state, x, y, title, drawEntryHopSignal); } - void drawDistanceScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { const char *title = "Distance"; drawNodeListScreen(display, state, x, y, title, drawNodeDistance); } #endif - void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) { float heading = 0; diff --git a/src/graphics/draw/NodeListRenderer.h b/src/graphics/draw/NodeListRenderer.h index ea8df8bd9..8b82fb117 100644 --- a/src/graphics/draw/NodeListRenderer.h +++ b/src/graphics/draw/NodeListRenderer.h @@ -23,8 +23,11 @@ namespace NodeListRenderer typedef void (*EntryRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int); typedef void (*NodeExtrasRenderer)(OLEDDisplay *, meshtastic_NodeInfoLite *, int16_t, int16_t, int, float, double, double); -// Node list mode enumeration -enum NodeListMode { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_DISTANCE = 2, MODE_COUNT = 3 }; +// Node list mode enumeration for Last Heard and Hop Signal views +enum ListMode_Node { MODE_LAST_HEARD = 0, MODE_HOP_SIGNAL = 1, MODE_COUNT_NODE = 2 }; + +// Node list mode enumeration for Distance and Bearings views +enum ListMode_Location { MODE_DISTANCE = 0, MODE_BEARING = 1, MODE_COUNT_LOCATION = 2 }; // Main node list screen function void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y, const char *title, @@ -35,7 +38,7 @@ void drawNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t void drawEntryLastHeard(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); void drawNodeDistance(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); -void drawEntryDynamic(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); +void drawEntryDynamic_Nodes(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); void drawEntryCompass(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16_t x, int16_t y, int columnWidth); // Extras renderers @@ -46,11 +49,13 @@ void drawCompassArrow(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int16 void drawLastHeardScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); void drawHopSignalScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); void drawDistanceScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); -void drawDynamicNodeListScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); +void drawDynamicListScreen_Nodes(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); +void drawDynamicListScreen_Location(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); void drawNodeListWithCompasses(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); // Utility functions -const char *getCurrentModeTitle(int screenWidth); +const char *getCurrentModeTitle_Nodes(int screenWidth); +const char *getCurrentModeTitle_Location(int screenWidth); const char *getSafeNodeName(meshtastic_NodeInfoLite *node); void drawColumns(OLEDDisplay *display, int16_t x, int16_t y, const char **fields);