diff --git a/src/graphics/Screen.cpp b/src/graphics/Screen.cpp index 40455b9a7..57c72595f 100644 --- a/src/graphics/Screen.cpp +++ b/src/graphics/Screen.cpp @@ -136,13 +136,14 @@ extern bool hasUnreadMessage; // The banner appears in the center of the screen and disappears after the specified duration // Called to trigger a banner with custom message and duration -void Screen::showOverlayBanner(const char *message, uint32_t durationMs, uint8_t options, std::function bannerCallback, - int8_t InitialSelected) +void Screen::showOverlayBanner(const char *message, uint32_t durationMs, const char **optionsArrayPtr, uint8_t options, + std::function bannerCallback, int8_t InitialSelected) { // Store the message and set the expiration timestamp strncpy(NotificationRenderer::alertBannerMessage, message, 255); NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs; + NotificationRenderer::optionsArrayPtr = optionsArrayPtr; NotificationRenderer::alertBannerOptions = options; NotificationRenderer::alertBannerCallback = bannerCallback; NotificationRenderer::curSelected = InitialSelected; @@ -559,6 +560,7 @@ int32_t Screen::runOnce() if (displayHeight == 0) { displayHeight = dispdev->getHeight(); } + handleMenuSwitch(); // Show boot screen for first logo_timeout seconds, then switch to normal operation. // serialSinceMsec adjusts for additional serial wait time during nRF52 bootup @@ -1210,6 +1212,8 @@ int Screen::handleInputEvent(const InputEvent *event) ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0])); setFastFramerate(); // Draw ASAP ui->update(); + + handleMenuSwitch(); return 0; } /* @@ -1247,16 +1251,18 @@ int Screen::handleInputEvent(const InputEvent *event) showNextFrame(); } else if (event->inputEvent == INPUT_BROKER_SELECT) { if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) { - const char *banner_message; + static const char **optionsArrayPtr; int options; if (kb_found) { - banner_message = "Action?\nBack\nSleep Screen\nNew Preset Msg\nNew Freetext Msg"; + const char *optionsArray[] = {"Action?", "Back", "Sleep Screen", "New Preset Msg", "New Freetext Msg"}; + optionsArrayPtr = optionsArray; options = 4; } else { - banner_message = "Action?\nBack\nSleep Screen\nNew Preset Msg"; + const char *optionsArray[] = {"Action?", "Back", "Sleep Screen", "New Preset Msg"}; + optionsArrayPtr = optionsArray; options = 3; } - showOverlayBanner(banner_message, 30000, options, [](int selected) -> void { + showOverlayBanner("Action?", 30000, optionsArrayPtr, options, [](int selected) -> void { if (selected == 1) { screen->setOn(false); } else if (selected == 2) { @@ -1267,7 +1273,8 @@ int Screen::handleInputEvent(const InputEvent *event) }); #if HAS_TFT } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.memory) { - showOverlayBanner("Switch to MUI?\nYes\nNo", 30000, 2, [](int selected) -> void { + static const char *optionsArray[] = {"Yes", "No"}; + showOverlayBanner("Switch to MUI?", 30000, optionsArray, 2, [](int selected) -> void { if (selected == 0) { config.display.displaymode = meshtastic_Config_DisplayConfig_DisplayMode_COLOR; config.bluetooth.enabled = false; @@ -1277,8 +1284,9 @@ int Screen::handleInputEvent(const InputEvent *event) }); #else } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.memory) { + static const char *optionsArray[] = {"All Enabled", "Disabled", "Notifications", "System Only"}; showOverlayBanner( - "Beeps Mode\nAll Enabled\nDisabled\nNotifications\nSystem Only", 30000, 4, + "Beeps Mode", 30000, optionsArray, 4, [](int selected) -> void { config.device.buzzer_mode = (meshtastic_Config_DeviceConfig_BuzzerMode)selected; service->reloadConfig(SEGMENT_CONFIG); @@ -1287,8 +1295,9 @@ int Screen::handleInputEvent(const InputEvent *event) #endif #if HAS_GPS } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.gps && gps) { + static const char *optionsArray[] = {"Back", "Enabled", "Disabled"}; showOverlayBanner( - "Toggle GPS\nBack\nEnabled\nDisabled", 30000, 3, + "Toggle GPS", 30000, optionsArray, 3, [](int selected) -> void { if (selected == 1) { config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; @@ -1306,25 +1315,39 @@ int Screen::handleInputEvent(const InputEvent *event) : 2); // set inital selection #endif } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.clock) { - TZPicker(); + static const char *optionsArray[] = {"Back", "12-hour", "Timezone"}; + showOverlayBanner("Clock Menu", 30000, optionsArray, 3, [](int selected) -> void { + if (selected == 1) { + screen->menuQueue = screen->twelve_hour_picker; + screen->setInterval(0); + runASAP = true; + } else if (selected == 2) { + screen->menuQueue = screen->TZ_picker; + screen->setInterval(0); + runASAP = true; + } + }); } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) { LoraRegionPicker(); } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage && devicestate.rx_text_message.from) { - const char *banner_message; + static const char **optionsArrayPtr; int options; if (kb_found) { - banner_message = "Message Action?\nBack\nDismiss\nReply via Preset\nReply via Freetext"; + const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext"}; + optionsArrayPtr = optionsArray; options = 4; } else { - banner_message = "Message Action?\nBack\nDismiss\nReply via Preset"; + const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset"}; + optionsArrayPtr = optionsArray; options = 3; } #ifdef HAS_I2S - banner_message = "Message Action?\nBack\nDismiss\nReply via Preset\nReply via Freetext\nRead Aloud"; + const char *optionsArray[] = {"Back", "Dismiss", "Reply via Preset", "Reply via Freetext", "Read Aloud"}; + optionsArrayPtr = optionsArray; options = 5; #endif - showOverlayBanner(banner_message, 30000, options, [](int selected) -> void { + showOverlayBanner("Message Action?", 30000, optionsArrayPtr, options, [](int selected) -> void { if (selected == 1) { screen->dismissCurrentFrame(); } else if (selected == 2) { @@ -1354,16 +1377,19 @@ int Screen::handleInputEvent(const InputEvent *event) } else if (framesetInfo.positions.firstFavorite != 255 && this->ui->getUiState()->currentFrame >= framesetInfo.positions.firstFavorite && this->ui->getUiState()->currentFrame <= framesetInfo.positions.lastFavorite) { - const char *banner_message; int options; + static const char **optionsArrayPtr; + if (kb_found) { - banner_message = "Message Node?\nBack\nNew Preset Msg\nNew Freetext Msg"; + static const char *optionsArray[] = {"Back", "New Preset Msg", "New Freetext Msg"}; + optionsArrayPtr = optionsArray; options = 3; } else { - banner_message = "Message Node?\nBack\nNew Preset Msg"; + static const char *optionsArray[] = {"Back", "New Preset Msg"}; + optionsArrayPtr = optionsArray; options = 2; } - showOverlayBanner(banner_message, 30000, options, [](int selected) -> void { + showOverlayBanner("Message Node?", 30000, optionsArrayPtr, options, [](int selected) -> void { if (selected == 1) { cannedMessageModule->LaunchWithDestination(graphics::UIRenderer::currentFavoriteNodeNum); } else if (selected == 2) { @@ -1404,12 +1430,33 @@ bool Screen::isOverlayBannerShowing() void Screen::LoraRegionPicker(uint32_t duration) { + static const char *optionsArray[] = {"Back", + "US", + "EU_433", + "EU_868", + "CN", + "JP", + "ANZ", + "KR", + "TW", + "RU", + "IN", + "NZ_865", + "TH", + "LORA_24", + "UA_433", + "UA_868", + "MY_433", + "MY_" + "919", + "SG_" + "923", + "PH_433", + "PH_868", + "PH_915", + "ANZ_433"}; showOverlayBanner( - "Set the LoRa " - "region\nBack\nUS\nEU_433\nEU_868\nCN\nJP\nANZ\nKR\nTW\nRU\nIN\nNZ_865\nTH\nLORA_24\nUA_433\nUA_868\nMY_433\nMY_" - "919\nSG_" - "923\nPH_433\nPH_868\nPH_915\nANZ_433", - duration, 23, + "Set the LoRa region", duration, optionsArray, 23, [](int selected) -> void { if (selected != 0 && config.lora.region != _meshtastic_Config_LoRaConfig_RegionCode(selected)) { config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected); @@ -1445,51 +1492,98 @@ void Screen::LoraRegionPicker(uint32_t duration) 0); } +void Screen::TwelveHourPicker() +{ + static const char *optionsArray[] = {"Back", "12-hour", "24-hour"}; + showOverlayBanner("Pick Timezone", 30000, optionsArray, 3, [](int selected) -> void { + if (selected == 0) { + return; + } else if (selected == 1) { + config.display.use_12h_clock = true; + } else { + config.display.use_12h_clock = false; + } + service->reloadConfig(SEGMENT_CONFIG); + }); +} + void Screen::TZPicker() { - showOverlayBanner( - "Pick " - "Timezone\nBack\nUS/Hawaii\nUS/Alaska\nUS/Pacific\nUS/Mountain\nUS/Central\nUS/Eastern\nUTC\nEU/Western\nEU/" - "Central\nEU/Eastern\nAsia/Kolkata\nAsia/Hong_Kong\nAU/AWST\nAU/ACST\nAU/AEST\nPacific/NZ", - 30000, 17, [](int selected) -> void { - if (selected == 1) { // Hawaii - strncpy(config.device.tzdef, "HST10", sizeof(config.device.tzdef)); - } else if (selected == 2) { // Alaska - strncpy(config.device.tzdef, "AKST9AKDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); - } else if (selected == 3) { // Pacific - strncpy(config.device.tzdef, "PST8PDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); - } else if (selected == 4) { // Mountain - strncpy(config.device.tzdef, "MST7MDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); - } else if (selected == 5) { // Central - strncpy(config.device.tzdef, "CST6CDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); - } else if (selected == 6) { // Eastern - strncpy(config.device.tzdef, "EST5EDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); - } else if (selected == 7) { // UTC - strncpy(config.device.tzdef, "UTC", sizeof(config.device.tzdef)); - } else if (selected == 8) { // EU/Western - strncpy(config.device.tzdef, "GMT0BST,M3.5.0/1,M10.5.0", sizeof(config.device.tzdef)); - } else if (selected == 9) { // EU/Central - strncpy(config.device.tzdef, "CET-1CEST,M3.5.0,M10.5.0/3", sizeof(config.device.tzdef)); - } else if (selected == 10) { // EU/Eastern - strncpy(config.device.tzdef, "EET-2EEST,M3.5.0/3,M10.5.0/4", sizeof(config.device.tzdef)); - } else if (selected == 11) { // Asia/Kolkata - strncpy(config.device.tzdef, "IST-5:30", sizeof(config.device.tzdef)); - } else if (selected == 12) { // China - strncpy(config.device.tzdef, "HKT-8", sizeof(config.device.tzdef)); - } else if (selected == 13) { // AU/AWST - strncpy(config.device.tzdef, "AWST-8", sizeof(config.device.tzdef)); - } else if (selected == 14) { // AU/ACST - strncpy(config.device.tzdef, "ACST-9:30ACDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef)); - } else if (selected == 15) { // AU/AEST - strncpy(config.device.tzdef, "AEST-10AEDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef)); - } else if (selected == 16) { // NZ - strncpy(config.device.tzdef, "NZST-12NZDT,M9.5.0,M4.1.0/3", sizeof(config.device.tzdef)); - } - if (selected != 0) { - setenv("TZ", config.device.tzdef, 1); - service->reloadConfig(SEGMENT_CONFIG); - } - }); + static const char *optionsArray[] = {"Back", + "US/Hawaii", + "US/Alaska", + "US/Pacific", + "US/Mountain", + "US/Central", + "US/Eastern", + "UTC", + "EU/Western", + "EU/" + "Central", + "EU/Eastern", + "Asia/Kolkata", + "Asia/Hong_Kong", + "AU/AWST", + "AU/ACST", + "AU/AEST", + "Pacific/NZ"}; + showOverlayBanner("Pick Timezone", 30000, optionsArray, 17, [](int selected) -> void { + if (selected == 1) { // Hawaii + strncpy(config.device.tzdef, "HST10", sizeof(config.device.tzdef)); + } else if (selected == 2) { // Alaska + strncpy(config.device.tzdef, "AKST9AKDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); + } else if (selected == 3) { // Pacific + strncpy(config.device.tzdef, "PST8PDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); + } else if (selected == 4) { // Mountain + strncpy(config.device.tzdef, "MST7MDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); + } else if (selected == 5) { // Central + strncpy(config.device.tzdef, "CST6CDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); + } else if (selected == 6) { // Eastern + strncpy(config.device.tzdef, "EST5EDT,M3.2.0,M11.1.0", sizeof(config.device.tzdef)); + } else if (selected == 7) { // UTC + strncpy(config.device.tzdef, "UTC", sizeof(config.device.tzdef)); + } else if (selected == 8) { // EU/Western + strncpy(config.device.tzdef, "GMT0BST,M3.5.0/1,M10.5.0", sizeof(config.device.tzdef)); + } else if (selected == 9) { // EU/Central + strncpy(config.device.tzdef, "CET-1CEST,M3.5.0,M10.5.0/3", sizeof(config.device.tzdef)); + } else if (selected == 10) { // EU/Eastern + strncpy(config.device.tzdef, "EET-2EEST,M3.5.0/3,M10.5.0/4", sizeof(config.device.tzdef)); + } else if (selected == 11) { // Asia/Kolkata + strncpy(config.device.tzdef, "IST-5:30", sizeof(config.device.tzdef)); + } else if (selected == 12) { // China + strncpy(config.device.tzdef, "HKT-8", sizeof(config.device.tzdef)); + } else if (selected == 13) { // AU/AWST + strncpy(config.device.tzdef, "AWST-8", sizeof(config.device.tzdef)); + } else if (selected == 14) { // AU/ACST + strncpy(config.device.tzdef, "ACST-9:30ACDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef)); + } else if (selected == 15) { // AU/AEST + strncpy(config.device.tzdef, "AEST-10AEDT,M10.1.0,M4.1.0/3", sizeof(config.device.tzdef)); + } else if (selected == 16) { // NZ + strncpy(config.device.tzdef, "NZST-12NZDT,M9.5.0,M4.1.0/3", sizeof(config.device.tzdef)); + } + if (selected != 0) { + setenv("TZ", config.device.tzdef, 1); + service->reloadConfig(SEGMENT_CONFIG); + } + }); +} + +void Screen::handleMenuSwitch() +{ + switch (menuQueue) { + case menu_none: + break; + case lora_picker: + LoraRegionPicker(); + break; + case TZ_picker: + TZPicker(); + break; + case twelve_hour_picker: + TwelveHourPicker(); + break; + } + menuQueue = menu_none; } } // namespace graphics diff --git a/src/graphics/Screen.h b/src/graphics/Screen.h index c264f0f07..21dcc0640 100644 --- a/src/graphics/Screen.h +++ b/src/graphics/Screen.h @@ -201,6 +201,9 @@ class Screen : public concurrency::OSThread size_t frameCount = 0; // Total number of active frames ~Screen(); + enum screenMenus { menu_none, lora_picker, TZ_picker, twelve_hour_picker }; + screenMenus menuQueue = menu_none; + // Which frame we want to be displayed, after we regen the frameset by calling setFrames enum FrameFocus : uint8_t { FOCUS_DEFAULT, // No specific frame @@ -285,8 +288,8 @@ class Screen : public concurrency::OSThread enqueueCmd(cmd); } - void showOverlayBanner(const char *message, uint32_t durationMs = 3000, uint8_t options = 0, - std::function bannerCallback = NULL, int8_t InitialSelected = 0); + void showOverlayBanner(const char *message, uint32_t durationMs = 3000, const char **optionsArrayPtr = nullptr, + uint8_t options = 0, std::function bannerCallback = NULL, int8_t InitialSelected = 0); void startFirmwareUpdateScreen() { @@ -601,7 +604,9 @@ class Screen : public concurrency::OSThread void handleShowNextFrame(); void handleShowPrevFrame(); void handleStartFirmwareUpdateScreen(); + void handleMenuSwitch(); void TZPicker(); + void TwelveHourPicker(); void LoraRegionPicker(uint32_t duration = 30000); // Info collected by setFrames method. diff --git a/src/graphics/draw/NotificationRenderer.cpp b/src/graphics/draw/NotificationRenderer.cpp index cf9c3203c..d784930b3 100644 --- a/src/graphics/draw/NotificationRenderer.cpp +++ b/src/graphics/draw/NotificationRenderer.cpp @@ -31,6 +31,7 @@ int8_t NotificationRenderer::curSelected = 0; char NotificationRenderer::alertBannerMessage[256] = {0}; uint32_t NotificationRenderer::alertBannerUntil = 0; // 0 is a special case meaning forever uint8_t NotificationRenderer::alertBannerOptions = 0; // last x lines are seelctable options +const char **NotificationRenderer::optionsArrayPtr = nullptr; std::function NotificationRenderer::alertBannerCallback = NULL; bool NotificationRenderer::pauseBanner = false; @@ -71,20 +72,20 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp // Search the message to determine if we need the bell added bool needs_bell = (strstr(alertBannerMessage, "Alert Received") != nullptr); - uint8_t firstOption = 0; uint8_t firstOptionToShow = 0; // Setup font and alignment display->setFont(FONT_SMALL); display->setTextAlignment(TEXT_ALIGN_LEFT); // We will manually center per line - const int MAX_LINES = 24; - + const int MAX_LINES = 5; + uint16_t optionWidths[alertBannerOptions] = {0}; uint16_t maxWidth = 0; uint16_t arrowsWidth = display->getStringWidth("> <", 4, true); uint16_t lineWidths[MAX_LINES] = {0}; uint16_t lineLengths[MAX_LINES] = {0}; char *lineStarts[MAX_LINES + 1]; uint16_t lineCount = 0; + uint16_t totalCount = 0; char lineBuffer[40] = {0}; // pointer to the terminating null char *alertEnd = alertBannerMessage + strnlen(alertBannerMessage, sizeof(alertBannerMessage)); @@ -101,14 +102,22 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp if (lineWidths[lineCount] > maxWidth) { maxWidth = lineWidths[lineCount]; } - if (alertBannerOptions > 0 && lineCount > 0 && lineWidths[lineCount] + arrowsWidth > maxWidth) { - maxWidth = lineWidths[lineCount] + arrowsWidth; - } lineCount++; // if we are doing a selection, add extra width for arrows } if (alertBannerOptions > 0) { + for (int i = 0; i < alertBannerOptions; i++) { + optionWidths[i] = display->getStringWidth(optionsArrayPtr[i], strlen(optionsArrayPtr[i]), true); + if (optionWidths[i] > maxWidth) { + maxWidth = optionWidths[i]; + } + if (optionWidths[i] + arrowsWidth > maxWidth) { + maxWidth = optionWidths[i] + arrowsWidth; + } + } + totalCount = lineCount + alertBannerOptions; + // respond to input if (inEvent == INPUT_BROKER_UP || inEvent == INPUT_BROKER_ALT_PRESS) { curSelected--; @@ -124,15 +133,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp curSelected = alertBannerOptions - 1; if (curSelected == alertBannerOptions) curSelected = 0; - // compare number of options to number of lines - if (lineCount < alertBannerOptions) - return; - firstOption = lineCount - alertBannerOptions; if (curSelected > 1 && alertBannerOptions > 3) { - firstOptionToShow = curSelected + firstOption - 1; + firstOptionToShow = curSelected - 1; // put the selected option in the middle } else { - firstOptionToShow = firstOption; + firstOptionToShow = 0; } } else { // not in an alert with a callback // TODO: check that at least a second has passed since the alert started @@ -143,7 +148,6 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp inEvent = INPUT_BROKER_NONE; if (alertBannerMessage[0] == '\0') return; - // set width from longest line uint16_t boxWidth = padding * 2 + maxWidth; if (needs_bell) { @@ -157,8 +161,8 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp // calculate max lines on screen? for now it's 4 // set height from line count uint16_t boxHeight; - if (lineCount <= 4) { - boxHeight = vPadding * 2 + lineCount * FONT_HEIGHT_SMALL + (lineCount - 1) * lineSpacing; + if (totalCount <= 4) { + boxHeight = vPadding * 2 + totalCount * FONT_HEIGHT_SMALL + (totalCount - 1) * lineSpacing; } else { boxHeight = vPadding * 2 + 4 * FONT_HEIGHT_SMALL + 4 * lineSpacing; } @@ -185,37 +189,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp int16_t lineY = boxTop + vPadding; for (int i = 0; i < lineCount; i++) { - // is this line selected? - // if so, start the buffer with -> and strncpy to the 4th location - if (i < lineCount - alertBannerOptions || alertBannerOptions == 0) { - strncpy(lineBuffer, lineStarts[i], 40); - if (lineLengths[i] > 39) - lineBuffer[39] = '\0'; - else - lineBuffer[lineLengths[i]] = '\0'; - } else if (i >= firstOptionToShow && i < firstOptionToShow + 3) { - if (i == curSelected + firstOption) { - if (lineLengths[i] > 35) - lineLengths[i] = 35; - strncpy(lineBuffer, "> ", 3); - strncpy(lineBuffer + 2, lineStarts[i], 36); - strncpy(lineBuffer + lineLengths[i] + 2, " <", 3); - lineLengths[i] += 4; - lineWidths[i] += display->getStringWidth("> <", 4, true); - if (lineLengths[i] > 35) - lineBuffer[39] = '\0'; - else - lineBuffer[lineLengths[i]] = '\0'; - } else { - strncpy(lineBuffer, lineStarts[i], 40); - if (lineLengths[i] > 39) - lineBuffer[39] = '\0'; - else - lineBuffer[lineLengths[i]] = '\0'; - } - } else { // add break for the additional lines - continue; - } + strncpy(lineBuffer, lineStarts[i], 40); + if (lineLengths[i] > 39) + lineBuffer[39] = '\0'; + else + lineBuffer[lineLengths[i]] = '\0'; int16_t textX = boxLeft + (boxWidth - lineWidths[i]) / 2; @@ -228,6 +206,34 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp display->drawString(textX, lineY, lineBuffer); lineY += FONT_HEIGHT_SMALL + lineSpacing; } + for (int i = 0; i < alertBannerOptions; i++) { + // is this line selected? + // if so, start the buffer with -> and strncpy to the 4th location + if (i >= firstOptionToShow && i < firstOptionToShow + 3) { + if (i == curSelected) { + strncpy(lineBuffer, "> ", 3); + strncpy(lineBuffer + 2, optionsArrayPtr[i], 36); + strncpy(lineBuffer + strlen(optionsArrayPtr[i]) + 2, " <", 3); + optionWidths[i] += arrowsWidth; + lineBuffer[39] = '\0'; + } else { + strncpy(lineBuffer, optionsArrayPtr[i], 40); + lineBuffer[39] = '\0'; + } + int16_t textX = boxLeft + (boxWidth - optionWidths[i]) / 2; + + if (needs_bell && i == 0) { + int bellY = lineY + (FONT_HEIGHT_SMALL - 8) / 2; + display->drawXbm(textX - 10, bellY, 8, 8, bell_alert); + display->drawXbm(textX + optionWidths[i] + 2, bellY, 8, 8, bell_alert); + } + + display->drawString(textX, lineY, lineBuffer); + lineY += FONT_HEIGHT_SMALL + lineSpacing; + } else { // add break for the additional lines + continue; + } + } } /// Draw the last text message we received diff --git a/src/graphics/draw/NotificationRenderer.h b/src/graphics/draw/NotificationRenderer.h index 3ed931dc6..2ec5fd9ec 100644 --- a/src/graphics/draw/NotificationRenderer.h +++ b/src/graphics/draw/NotificationRenderer.h @@ -12,7 +12,8 @@ class NotificationRenderer static char inEvent; static int8_t curSelected; static char alertBannerMessage[256]; - static uint32_t alertBannerUntil; // 0 is a special case meaning forever + static uint32_t alertBannerUntil; // 0 is a special case meaning forever + static const char **optionsArrayPtr; static uint8_t alertBannerOptions; // last x lines are seelctable options static std::function alertBannerCallback; diff --git a/src/modules/KeyVerificationModule.cpp b/src/modules/KeyVerificationModule.cpp index f5a9f2359..c0972c155 100644 --- a/src/modules/KeyVerificationModule.cpp +++ b/src/modules/KeyVerificationModule.cpp @@ -79,10 +79,10 @@ bool KeyVerificationModule::handleReceivedProtobuf(const meshtastic_MeshPacket & memset(message, 0, sizeof(message)); sprintf(message, "Verification: \n"); generateVerificationCode(message + 15); - sprintf(message + 24, "\nACCEPT\nREJECT"); + static const char *optionsArray[] = {"ACCEPT", "REJECT"}; LOG_INFO("Hash1 matches!"); if (screen) { - screen->showOverlayBanner(message, 30000, 2, [=](int selected) { + screen->showOverlayBanner(message, 30000, optionsArray, 2, [=](int selected) { if (selected == 0) { auto remoteNodePtr = nodeDB->getMeshNode(currentRemoteNode); remoteNodePtr->bitfield |= NODEINFO_BITFIELD_IS_KEY_MANUALLY_VERIFIED_MASK;