diff --git a/src/graphics/draw/NodeListRenderer.cpp b/src/graphics/draw/NodeListRenderer.cpp index 654c272229d..7b644023cb5 100644 --- a/src/graphics/draw/NodeListRenderer.cpp +++ b/src/graphics/draw/NodeListRenderer.cpp @@ -275,9 +275,12 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int int nameMaxWidth = getNodeNameMaxWidth(columnWidth, columnWidth - 25); int barsOffset = (currentResolution == ScreenResolution::High) ? (isLeftCol ? 20 : 24) : (isLeftCol ? 15 : 19); - int hopOffset = (currentResolution == ScreenResolution::High) ? (isLeftCol ? 21 : 29) : (isLeftCol ? 13 : 17); + constexpr int kBarCount = 4; + constexpr int kBarWidth = 2; + constexpr int kBarGap = 1; int barsXOffset = columnWidth - barsOffset; + int barsRightEdge = x + barsXOffset + ((kBarCount - 1) * (kBarWidth + kBarGap)) + kBarWidth; const int nameX = x + ((currentResolution == ScreenResolution::High) ? 6 : 3); char nodeName[96]; @@ -304,28 +307,35 @@ void drawEntryHopSignal(OLEDDisplay *display, meshtastic_NodeInfoLite *node, int } } - // Draw signal strength bars - int bars = (node->snr > 5) ? 4 : (node->snr > 0) ? 3 : (node->snr > -5) ? 2 : (node->snr > -10) ? 1 : 0; - int barWidth = 2; - int barStartX = x + barsXOffset; - int barStartY = y + 1 + (FONT_HEIGHT_SMALL / 2) + 2; + const bool isZeroHop = node->has_hops_away && node->hops_away == 0; - for (int b = 0; b < 4; b++) { - if (b < bars) { - int height = (b * 2); - display->fillRect(barStartX + (b * (barWidth + 1)), barStartY - height, barWidth, height); + // Show signal only for direct neighbors (0 hops) + if (isZeroHop) { + int bars = (node->snr > 5) ? 4 : (node->snr > 0) ? 3 : (node->snr > -5) ? 2 : (node->snr > -10) ? 1 : 0; + int barStartX = x + barsXOffset; + int barStartY = y + 1 + (FONT_HEIGHT_SMALL / 2) + 2; + + for (int b = 0; b < kBarCount; b++) { + if (b < bars) { + int height = (b * 2); + display->fillRect(barStartX + (b * (kBarWidth + kBarGap)), barStartY - height, kBarWidth, height); + } } } - // Draw hop count - char hopStr[6] = ""; - if (node->has_hops_away && node->hops_away > 0) - snprintf(hopStr, sizeof(hopStr), "[%d]", node->hops_away); + // Draw hop count + hop icon + if (node->has_hops_away && node->hops_away > 0) { + char hopCount[6]; + snprintf(hopCount, sizeof(hopCount), "%d", node->hops_away); + + const int hopCountWidth = display->getStringWidth(hopCount); + const int gap = 1; + const int totalWidth = hopCountWidth + gap + hop_width; + const int hopX = barsRightEdge - totalWidth; + const int iconY = y + (FONT_HEIGHT_SMALL - hop_height) / 2; - if (hopStr[0] != '\0') { - int rightEdge = x + columnWidth - hopOffset; - int textWidth = display->getStringWidth(hopStr); - display->drawString(rightEdge - textWidth, y, hopStr); + display->drawString(hopX, y, hopCount); + display->drawXbm(hopX + hopCountWidth + gap, iconY, hop_width, hop_height, hop); } } diff --git a/src/graphics/draw/UIRenderer.cpp b/src/graphics/draw/UIRenderer.cpp index 78d10988157..a048cc4aceb 100644 --- a/src/graphics/draw/UIRenderer.cpp +++ b/src/graphics/draw/UIRenderer.cpp @@ -396,10 +396,10 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat } #endif - // === 2. Signal and Hops (combined on one line, if available) === - char signalHopsStr[32] = ""; + // === 2. Signal/Hops line (if available) === bool haveSignal = false; int bars = 0; + const char *qualityLabel = nullptr; // Helper to get SNR limit based on modem preset auto getSnrLimit = [](meshtastic_Config_LoRaConfig_ModemPreset preset) -> float { @@ -420,81 +420,52 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat } }; - // Calculate signal grade using modem preset and SNR only - float snrLimit = getSnrLimit(config.lora.modem_preset); - float snr = node->snr; - - // Determine signal quality label and bars using SNR-only grading - const char *qualityLabel = nullptr; - - if (snr > snrLimit + 10) { - qualityLabel = "Good"; - bars = 4; - } else if (snr > snrLimit + 6) { - qualityLabel = "Good"; - bars = 3; - } else if (snr > snrLimit + 2) { - qualityLabel = "Good"; - bars = 2; - } else if (snr > snrLimit - 4) { - qualityLabel = "Fair"; - bars = 1; - } else { - qualityLabel = "Bad"; - bars = 1; - } - // Add extra spacing on the left if we have an API connection to account for the common footer icons const char *leftSideSpacing = graphics::isAPIConnected(service->api_state) ? (currentResolution == ScreenResolution::High ? " " : " ") : " "; + const bool isZeroHop = node->has_hops_away && node->hops_away == 0; + + // Signal text/bars are only for direct (zero-hop) nodes with valid SNR. + if (isZeroHop) { + float snr = node->snr; + if (snr > -100 && snr != 0) { + float snrLimit = getSnrLimit(config.lora.modem_preset); + // Determine signal quality label and bars using SNR-only grading. + if (snr > snrLimit + 10) { + qualityLabel = "Good"; + bars = 4; + } else if (snr > snrLimit + 6) { + qualityLabel = "Good"; + bars = 3; + } else if (snr > snrLimit + 2) { + qualityLabel = "Good"; + bars = 2; + } else if (snr > snrLimit - 4) { + qualityLabel = "Fair"; + bars = 1; + } else { + qualityLabel = "Bad"; + bars = 1; + } - // --- Build the Signal/Hops line --- - // Only show signal if we have valid SNR - if (snr > -100 && snr != 0) { - snprintf(signalHopsStr, sizeof(signalHopsStr), "%sSig:%s", leftSideSpacing, qualityLabel); - haveSignal = true; - } - - if (node->hops_away > 0) { - size_t len = strlen(signalHopsStr); - if (haveSignal) { - snprintf(signalHopsStr + len, sizeof(signalHopsStr) - len, " [#]"); - } else { - snprintf(signalHopsStr, sizeof(signalHopsStr), "[#]"); + haveSignal = true; } } - if (signalHopsStr[0]) { - int yPos = getTextPositions(display)[line++]; - int curX = x; - - // Split combined string into signal text and hop suffix - char sigPart[20] = ""; - const char *hopPart = nullptr; - - char *bracket = strchr(signalHopsStr, '['); - if (bracket) { - size_t n = (size_t)(bracket - signalHopsStr); - if (n >= sizeof(sigPart)) - n = sizeof(sigPart) - 1; - memcpy(sigPart, signalHopsStr, n); - sigPart[n] = '\0'; - - // Trim trailing spaces - while (strlen(sigPart) && sigPart[strlen(sigPart) - 1] == ' ') { - sigPart[strlen(sigPart) - 1] = '\0'; - } + const bool showHops = node->has_hops_away && node->hops_away > 0; - hopPart = bracket; // "[n Hop(s)]" - } else { - strncpy(sigPart, signalHopsStr, sizeof(sigPart) - 1); - sigPart[sizeof(sigPart) - 1] = '\0'; + if (haveSignal || showHops) { + int yPos = getTextPositions(display)[line++]; + int curX = x + display->getStringWidth(leftSideSpacing); + + // Draw signal quality text for zero-hop nodes when present. + if (haveSignal && qualityLabel) { + char signalLabel[20]; + snprintf(signalLabel, sizeof(signalLabel), "Sig:%s", qualityLabel); + display->drawString(curX, yPos, signalLabel); + curX += display->getStringWidth(signalLabel) + 4; } - // Draw signal quality text - display->drawString(curX, yPos, sigPart); - curX += display->getStringWidth(sigPart) + 4; - // Draw signal bars (skip on UltraLow, text only) if (currentResolution != ScreenResolution::UltraLow && haveSignal && bars > 0) { const int kMaxBars = 4; @@ -532,12 +503,12 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat curX += (kMaxBars * barWidth) + ((kMaxBars - 1) * barGap) + 2; } - // Draw hops AFTER the bars as: [ number + hop icon ] - if (hopPart && node->hops_away > 0) { - - // open bracket - display->drawString(curX, yPos, "["); - curX += display->getStringWidth("[") + 1; + // Draw hops for non-zero-hop nodes as: number + hop icon. + // This path is mutually exclusive with the zero-hop signal-bars path above. + if (showHops) { + // hop label + display->drawString(curX, yPos, "Hop:"); + curX += display->getStringWidth("Hop:") + 2; // hop count char hopCount[6]; @@ -549,9 +520,6 @@ void UIRenderer::drawFavoriteNode(OLEDDisplay *display, OLEDDisplayUiState *stat const int iconY = yPos + (FONT_HEIGHT_SMALL - hop_height) / 2; display->drawXbm(curX, iconY, hop_width, hop_height, hop); curX += hop_width + 1; - - // closing bracket - display->drawString(curX, yPos, "]"); } }