mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-08 22:22:05 +00:00
Add selection menu to bannerOverlay
This commit is contained in:
parent
ae96221292
commit
2df032bb06
@ -136,12 +136,14 @@ extern bool hasUnreadMessage;
|
|||||||
// The banner appears in the center of the screen and disappears after the specified duration
|
// 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
|
// Called to trigger a banner with custom message and duration
|
||||||
void Screen::showOverlayBanner(const char *message, uint32_t durationMs)
|
void Screen::showOverlayBanner(const char *message, uint32_t durationMs, uint8_t options, std::function<void(int)> bannerCallback)
|
||||||
{
|
{
|
||||||
// Store the message and set the expiration timestamp
|
// Store the message and set the expiration timestamp
|
||||||
strncpy(alertBannerMessage, message, 255);
|
strncpy(NotificationRenderer::alertBannerMessage, message, 255);
|
||||||
alertBannerMessage[255] = '\0'; // Ensure null termination
|
NotificationRenderer::alertBannerMessage[255] = '\0'; // Ensure null termination
|
||||||
alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs;
|
NotificationRenderer::alertBannerUntil = (durationMs == 0) ? 0 : millis() + durationMs;
|
||||||
|
NotificationRenderer::alertBannerOptions = options;
|
||||||
|
NotificationRenderer::alertBannerCallback = bannerCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
@ -1116,10 +1118,45 @@ int32_t Screen::runOnce()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DISABLE_WELCOME_UNSET
|
#ifndef DISABLE_WELCOME_UNSET
|
||||||
if (showingNormalScreen && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
if (!NotificationRenderer::isOverlayBannerShowing() && config.lora.region == meshtastic_Config_LoRaConfig_RegionCode_UNSET) {
|
||||||
setWelcomeFrames();
|
showOverlayBanner(
|
||||||
|
"Set the LoRa "
|
||||||
|
"region\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",
|
||||||
|
0, 21, [](int selected) -> void {
|
||||||
|
LOG_WARN("Chose %d", selected);
|
||||||
|
config.lora.region = _meshtastic_Config_LoRaConfig_RegionCode(selected + 1);
|
||||||
|
if (!owner.is_licensed) {
|
||||||
|
bool keygenSuccess = false;
|
||||||
|
if (config.security.private_key.size == 32) {
|
||||||
|
if (crypto->regeneratePublicKey(config.security.public_key.bytes, config.security.private_key.bytes)) {
|
||||||
|
keygenSuccess = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_INFO("Generate new PKI keys");
|
||||||
|
crypto->generateKeyPair(config.security.public_key.bytes, config.security.private_key.bytes);
|
||||||
|
keygenSuccess = true;
|
||||||
|
}
|
||||||
|
if (keygenSuccess) {
|
||||||
|
config.security.public_key.size = 32;
|
||||||
|
config.security.private_key.size = 32;
|
||||||
|
owner.public_key.size = 32;
|
||||||
|
memcpy(owner.public_key.bytes, config.security.public_key.bytes, 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config.lora.tx_enabled = true;
|
||||||
|
initRegion();
|
||||||
|
if (myRegion->dutyCycle < 100) {
|
||||||
|
config.lora.ignore_mqtt = true; // Ignore MQTT by default if region has a duty cycle limit
|
||||||
|
}
|
||||||
|
service->reloadConfig(SEGMENT_CONFIG);
|
||||||
|
rebootAtMsec = (millis() + DEFAULT_REBOOT_SECONDS * 1000);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (!NotificationRenderer::isOverlayBannerShowing() && rebootAtMsec != 0) {
|
||||||
|
showOverlayBanner("Rebooting...", 0);
|
||||||
|
}
|
||||||
|
|
||||||
// Process incoming commands.
|
// Process incoming commands.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@ -1226,23 +1263,12 @@ void Screen::setSSLFrames()
|
|||||||
{
|
{
|
||||||
if (address_found.address) {
|
if (address_found.address) {
|
||||||
// LOG_DEBUG("Show SSL frames");
|
// LOG_DEBUG("Show SSL frames");
|
||||||
static FrameCallback sslFrames[] = {graphics::NotificationRenderer::NotificationRenderer::drawSSLScreen};
|
static FrameCallback sslFrames[] = {NotificationRenderer::drawSSLScreen};
|
||||||
ui->setFrames(sslFrames, 1);
|
ui->setFrames(sslFrames, 1);
|
||||||
ui->update();
|
ui->update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* show a message that the SSL cert is being built
|
|
||||||
* it is expected that this will be used during the boot phase */
|
|
||||||
void Screen::setWelcomeFrames()
|
|
||||||
{
|
|
||||||
if (address_found.address) {
|
|
||||||
// LOG_DEBUG("Show Welcome frames");
|
|
||||||
static FrameCallback frames[] = {graphics::NotificationRenderer::NotificationRenderer::drawWelcomeScreen};
|
|
||||||
setFrameImmediateDraw(frames);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef USE_EINK
|
#ifdef USE_EINK
|
||||||
/// Determine which screensaver frame to use, then set the FrameCallback
|
/// Determine which screensaver frame to use, then set the FrameCallback
|
||||||
void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
|
void Screen::setScreensaverFrames(FrameCallback einkScreensaver)
|
||||||
@ -1357,7 +1383,7 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
// If we have a critical fault, show it first
|
// If we have a critical fault, show it first
|
||||||
fsi.positions.fault = numframes;
|
fsi.positions.fault = numframes;
|
||||||
if (error_code) {
|
if (error_code) {
|
||||||
normalFrames[numframes++] = graphics::NotificationRenderer::NotificationRenderer::drawCriticalFaultFrame;
|
normalFrames[numframes++] = NotificationRenderer::drawCriticalFaultFrame;
|
||||||
indicatorIcons.push_back(icon_error);
|
indicatorIcons.push_back(icon_error);
|
||||||
focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame
|
focus = FOCUS_FAULT; // Change our "focus" parameter, to ensure we show the fault frame
|
||||||
}
|
}
|
||||||
@ -1445,8 +1471,7 @@ void Screen::setFrames(FrameFocus focus)
|
|||||||
ui->disableAllIndicators();
|
ui->disableAllIndicators();
|
||||||
|
|
||||||
// Add overlays: frame icons and alert banner)
|
// Add overlays: frame icons and alert banner)
|
||||||
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar,
|
static OverlayCallback overlays[] = {graphics::UIRenderer::drawNavigationBar, NotificationRenderer::drawAlertBannerOverlay};
|
||||||
graphics::NotificationRenderer::NotificationRenderer::drawAlertBannerOverlay};
|
|
||||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||||
|
|
||||||
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
|
prevFrame = -1; // Force drawNodeInfo to pick a new node (because our list
|
||||||
@ -1532,7 +1557,7 @@ void Screen::handleStartFirmwareUpdateScreen()
|
|||||||
showingNormalScreen = false;
|
showingNormalScreen = false;
|
||||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
|
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // E-Ink: Explicitly use fast-refresh for next frame
|
||||||
|
|
||||||
static FrameCallback frames[] = {graphics::NotificationRenderer::NotificationRenderer::drawFrameFirmware};
|
static FrameCallback frames[] = {graphics::NotificationRenderer::drawFrameFirmware};
|
||||||
setFrameImmediateDraw(frames);
|
setFrameImmediateDraw(frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1770,7 +1795,12 @@ int Screen::handleInputEvent(const InputEvent *event)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (NotificationRenderer::isOverlayBannerShowing()) {
|
||||||
|
NotificationRenderer::inEvent = event->inputEvent;
|
||||||
|
setFrames();
|
||||||
|
ui->update();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
// Use left or right input from a keyboard to move between frames,
|
// Use left or right input from a keyboard to move between frames,
|
||||||
// so long as a mesh module isn't using these events for some other purpose
|
// so long as a mesh module isn't using these events for some other purpose
|
||||||
if (showingNormalScreen) {
|
if (showingNormalScreen) {
|
||||||
@ -1809,6 +1839,11 @@ int Screen::handleAdminMessage(const meshtastic_AdminMessage *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Screen::isOverlayBannerShowing()
|
||||||
|
{
|
||||||
|
return NotificationRenderer::isOverlayBannerShowing();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
#else
|
#else
|
||||||
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
|
graphics::Screen::Screen(ScanI2C::DeviceAddress, meshtastic_Config_DisplayConfig_OledType, OLEDDISPLAY_GEOMETRY) {}
|
||||||
|
@ -220,8 +220,7 @@ class Screen : public concurrency::OSThread
|
|||||||
meshtastic_Config_DisplayConfig_OledType model;
|
meshtastic_Config_DisplayConfig_OledType model;
|
||||||
OLEDDISPLAY_GEOMETRY geometry;
|
OLEDDISPLAY_GEOMETRY geometry;
|
||||||
|
|
||||||
char alertBannerMessage[256] = {0};
|
bool isOverlayBannerShowing();
|
||||||
uint32_t alertBannerUntil = 0; // 0 is a special case meaning forever
|
|
||||||
|
|
||||||
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
|
// Stores the last 4 of our hardware ID, to make finding the device for pairing easier
|
||||||
// FIXME: Needs refactoring and getMacAddr needs to be moved to a utility class
|
// FIXME: Needs refactoring and getMacAddr needs to be moved to a utility class
|
||||||
@ -286,12 +285,8 @@ class Screen : public concurrency::OSThread
|
|||||||
enqueueCmd(cmd);
|
enqueueCmd(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
void showOverlayBanner(const char *message, uint32_t durationMs = 3000);
|
void showOverlayBanner(const char *message, uint32_t durationMs = 3000, uint8_t options = 0,
|
||||||
|
std::function<void(int)> bannerCallback = NULL);
|
||||||
bool isOverlayBannerShowing()
|
|
||||||
{
|
|
||||||
return strlen(alertBannerMessage) > 0 && (alertBannerUntil == 0 || millis() <= alertBannerUntil);
|
|
||||||
}
|
|
||||||
|
|
||||||
void startFirmwareUpdateScreen()
|
void startFirmwareUpdateScreen()
|
||||||
{
|
{
|
||||||
@ -571,8 +566,6 @@ class Screen : public concurrency::OSThread
|
|||||||
/// Draws our SSL cert screen during boot (called from WebServer)
|
/// Draws our SSL cert screen during boot (called from WebServer)
|
||||||
void setSSLFrames();
|
void setSSLFrames();
|
||||||
|
|
||||||
void setWelcomeFrames();
|
|
||||||
|
|
||||||
// Dismiss the currently focussed frame, if possible (e.g. text message, waypoint)
|
// Dismiss the currently focussed frame, if possible (e.g. text message, waypoint)
|
||||||
void dismissCurrentFrame();
|
void dismissCurrentFrame();
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
#include "DisplayFormatters.h"
|
#include "DisplayFormatters.h"
|
||||||
#include "NodeDB.h"
|
#include "NodeDB.h"
|
||||||
#include "NotificationRenderer.h"
|
#include "NotificationRenderer.h"
|
||||||
#include "graphics/Screen.h"
|
|
||||||
#include "graphics/ScreenFonts.h"
|
#include "graphics/ScreenFonts.h"
|
||||||
#include "graphics/SharedUIDisplay.h"
|
#include "graphics/SharedUIDisplay.h"
|
||||||
#include "graphics/images.h"
|
#include "graphics/images.h"
|
||||||
@ -27,8 +26,12 @@ extern bool hasUnreadMessage;
|
|||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace NotificationRenderer
|
char NotificationRenderer::inEvent = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE);
|
||||||
{
|
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
|
||||||
|
std::function<void(int)> NotificationRenderer::alertBannerCallback = NULL;
|
||||||
|
|
||||||
// Used on boot when a certificate is being created
|
// Used on boot when a certificate is being created
|
||||||
void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
||||||
@ -36,6 +39,7 @@ void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiStat
|
|||||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
display->drawString(64 + x, y, "Creating SSL certificate");
|
display->drawString(64 + x, y, "Creating SSL certificate");
|
||||||
|
uint32_t alertBannerUntil = 0; // 0 is a special case meaning forever
|
||||||
|
|
||||||
#ifdef ARCH_ESP32
|
#ifdef ARCH_ESP32
|
||||||
yield();
|
yield();
|
||||||
@ -50,48 +54,27 @@ void NotificationRenderer::drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiStat
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used when booting without a region set
|
|
||||||
void NotificationRenderer::drawWelcomeScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y)
|
|
||||||
{
|
|
||||||
display->setFont(FONT_SMALL);
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
|
||||||
display->drawString(64 + x, y, "//\\ E S H T /\\ S T / C");
|
|
||||||
display->drawString(64 + x, y + FONT_HEIGHT_SMALL, getDeviceName());
|
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
|
||||||
|
|
||||||
if ((millis() / 10000) % 2) {
|
|
||||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2 - 3, "Set the region using the");
|
|
||||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 3 - 3, "Meshtastic Android, iOS,");
|
|
||||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 4 - 3, "Web or CLI clients.");
|
|
||||||
} else {
|
|
||||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 2 - 3, "Visit meshtastic.org");
|
|
||||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 3 - 3, "for more information.");
|
|
||||||
display->drawString(x, y + FONT_HEIGHT_SMALL * 4 - 3, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef ARCH_ESP32
|
|
||||||
yield();
|
|
||||||
esp_task_wdt_reset();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
|
void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state)
|
||||||
{
|
{
|
||||||
// Exit if no message is active or duration has passed
|
// Exit if no message is active or duration has passed
|
||||||
if (!screen->isOverlayBannerShowing())
|
if (!isOverlayBannerShowing())
|
||||||
return;
|
return;
|
||||||
|
LOG_DEBUG("event: %u, curSelected: %d", inEvent, curSelected);
|
||||||
// === Layout Configuration ===
|
// === Layout Configuration ===
|
||||||
constexpr uint16_t padding = 5; // Padding around text inside the box
|
constexpr uint16_t padding = 5; // Padding around text inside the box
|
||||||
|
constexpr uint16_t vPadding = 2; // Padding around text inside the box
|
||||||
constexpr uint8_t lineSpacing = 1; // Extra space between lines
|
constexpr uint8_t lineSpacing = 1; // Extra space between lines
|
||||||
|
|
||||||
// Search the message to determine if we need the bell added
|
// Search the message to determine if we need the bell added
|
||||||
bool needs_bell = (strstr(screen->alertBannerMessage, "Alert Received") != nullptr);
|
bool needs_bell = (strstr(alertBannerMessage, "Alert Received") != nullptr);
|
||||||
|
|
||||||
|
uint8_t firstOption = 0;
|
||||||
|
uint8_t firstOptionToShow = 0;
|
||||||
|
|
||||||
// Setup font and alignment
|
// Setup font and alignment
|
||||||
display->setFont(FONT_SMALL);
|
display->setFont(FONT_SMALL);
|
||||||
display->setTextAlignment(TEXT_ALIGN_LEFT); // We will manually center per line
|
display->setTextAlignment(TEXT_ALIGN_LEFT); // We will manually center per line
|
||||||
const int MAX_LINES = 10;
|
const int MAX_LINES = 23;
|
||||||
|
|
||||||
uint16_t maxWidth = 0;
|
uint16_t maxWidth = 0;
|
||||||
uint16_t lineWidths[MAX_LINES] = {0};
|
uint16_t lineWidths[MAX_LINES] = {0};
|
||||||
@ -100,12 +83,11 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
uint16_t lineCount = 0;
|
uint16_t lineCount = 0;
|
||||||
char lineBuffer[40] = {0};
|
char lineBuffer[40] = {0};
|
||||||
// pointer to the terminating null
|
// pointer to the terminating null
|
||||||
char *alertEnd = screen->alertBannerMessage + strnlen(screen->alertBannerMessage, sizeof(screen->alertBannerMessage));
|
char *alertEnd = alertBannerMessage + strnlen(alertBannerMessage, sizeof(alertBannerMessage));
|
||||||
lineStarts[lineCount] = screen->alertBannerMessage;
|
lineStarts[lineCount] = alertBannerMessage;
|
||||||
LOG_WARN(lineStarts[lineCount]);
|
|
||||||
|
|
||||||
// loop through lines finding \n characters
|
// loop through lines finding \n characters
|
||||||
while ((lineCount < 10) && (lineStarts[lineCount] < alertEnd)) {
|
while ((lineCount < MAX_LINES) && (lineStarts[lineCount] < alertEnd)) {
|
||||||
lineStarts[lineCount + 1] = std::find(lineStarts[lineCount], alertEnd, '\n');
|
lineStarts[lineCount + 1] = std::find(lineStarts[lineCount], alertEnd, '\n');
|
||||||
lineLengths[lineCount] = lineStarts[lineCount + 1] - lineStarts[lineCount];
|
lineLengths[lineCount] = lineStarts[lineCount + 1] - lineStarts[lineCount];
|
||||||
if (lineStarts[lineCount + 1][0] == '\n') {
|
if (lineStarts[lineCount + 1][0] == '\n') {
|
||||||
@ -116,6 +98,38 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
maxWidth = lineWidths[lineCount];
|
maxWidth = lineWidths[lineCount];
|
||||||
}
|
}
|
||||||
lineCount++;
|
lineCount++;
|
||||||
|
// if we are doing a selection, add extra width for arrows
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alertBannerOptions > 0) {
|
||||||
|
// respond to input
|
||||||
|
if (inEvent == meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_UP) {
|
||||||
|
curSelected--;
|
||||||
|
} else if (inEvent == meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_DOWN) {
|
||||||
|
curSelected++;
|
||||||
|
} else if (inEvent == meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT) {
|
||||||
|
alertBannerCallback(curSelected);
|
||||||
|
alertBannerMessage[0] = '\0';
|
||||||
|
}
|
||||||
|
if (curSelected == -1)
|
||||||
|
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;
|
||||||
|
// put the selected option in the middle
|
||||||
|
} else {
|
||||||
|
firstOptionToShow = firstOption;
|
||||||
|
}
|
||||||
|
} else { // not in an alert with a callback
|
||||||
|
// TODO: check that at least a second has passed since the alert started
|
||||||
|
if (inEvent == meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_SELECT) {
|
||||||
|
alertBannerMessage[0] = '\0'; // end the alert early
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set width from longest line
|
// set width from longest line
|
||||||
@ -128,9 +142,14 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
boxWidth += 20;
|
boxWidth += 20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// calculate max lines on screen? for now it's 4
|
||||||
// set height from line count
|
// set height from line count
|
||||||
uint16_t boxHeight = padding * 2 + lineCount * FONT_HEIGHT_SMALL + (lineCount - 1) * lineSpacing;
|
uint16_t boxHeight;
|
||||||
|
if (lineCount <= 4) {
|
||||||
|
boxHeight = vPadding * 2 + lineCount * FONT_HEIGHT_SMALL + (lineCount - 1) * lineSpacing;
|
||||||
|
} else {
|
||||||
|
boxHeight = vPadding * 2 + 4 * FONT_HEIGHT_SMALL + 4 * lineSpacing;
|
||||||
|
}
|
||||||
|
|
||||||
int16_t boxLeft = (display->width() / 2) - (boxWidth / 2);
|
int16_t boxLeft = (display->width() / 2) - (boxWidth / 2);
|
||||||
int16_t boxTop = (display->height() / 2) - (boxHeight / 2);
|
int16_t boxTop = (display->height() / 2) - (boxHeight / 2);
|
||||||
@ -151,13 +170,42 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
display->setColor(WHITE);
|
display->setColor(WHITE);
|
||||||
|
|
||||||
// === Draw each line centered in the box ===
|
// === Draw each line centered in the box ===
|
||||||
int16_t lineY = boxTop + padding;
|
int16_t lineY = boxTop + vPadding;
|
||||||
|
|
||||||
|
LOG_DEBUG("firstOptionToShow: %u, firstOption: %u", firstOptionToShow, firstOption);
|
||||||
|
//
|
||||||
for (int i = 0; i < lineCount; i++) {
|
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 == 0 || alertBannerOptions == 0) {
|
||||||
strncpy(lineBuffer, lineStarts[i], 40);
|
strncpy(lineBuffer, lineStarts[i], 40);
|
||||||
if (lineLengths[i] > 39)
|
if (lineLengths[i] > 39)
|
||||||
lineBuffer[39] = '\0';
|
lineBuffer[39] = '\0';
|
||||||
else
|
else
|
||||||
lineBuffer[lineLengths[i]] = '\0';
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
int16_t textX = boxLeft + (boxWidth - lineWidths[i]) / 2;
|
int16_t textX = boxLeft + (boxWidth - lineWidths[i]) / 2;
|
||||||
|
|
||||||
@ -173,6 +221,7 @@ void NotificationRenderer::drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisp
|
|||||||
|
|
||||||
lineY += FONT_HEIGHT_SMALL + lineSpacing;
|
lineY += FONT_HEIGHT_SMALL + lineSpacing;
|
||||||
}
|
}
|
||||||
|
inEvent = static_cast<char>(meshtastic_ModuleConfig_CannedMessageConfig_InputEventChar_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the last text message we received
|
/// Draw the last text message we received
|
||||||
@ -201,7 +250,10 @@ void NotificationRenderer::drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUi
|
|||||||
"Please be patient and do not power off.");
|
"Please be patient and do not power off.");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace NotificationRenderer
|
bool NotificationRenderer::isOverlayBannerShowing()
|
||||||
|
{
|
||||||
|
return strlen(alertBannerMessage) > 0 && (alertBannerUntil == 0 || millis() <= alertBannerUntil);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
#endif
|
#endif
|
@ -6,19 +6,21 @@
|
|||||||
namespace graphics
|
namespace graphics
|
||||||
{
|
{
|
||||||
|
|
||||||
namespace NotificationRenderer
|
|
||||||
{
|
|
||||||
|
|
||||||
class NotificationRenderer
|
class NotificationRenderer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static char inEvent;
|
||||||
|
static int8_t curSelected;
|
||||||
|
static char alertBannerMessage[256];
|
||||||
|
static uint32_t alertBannerUntil; // 0 is a special case meaning forever
|
||||||
|
static uint8_t alertBannerOptions; // last x lines are seelctable options
|
||||||
|
static std::function<void(int)> alertBannerCallback;
|
||||||
|
|
||||||
static void drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state);
|
static void drawAlertBannerOverlay(OLEDDisplay *display, OLEDDisplayUiState *state);
|
||||||
static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
static void drawCriticalFaultFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
static void drawSSLScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
static void drawWelcomeScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
|
||||||
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
static void drawFrameFirmware(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y);
|
||||||
|
static bool isOverlayBannerShowing();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace NotificationRenderer
|
|
||||||
|
|
||||||
} // namespace graphics
|
} // namespace graphics
|
||||||
|
Loading…
Reference in New Issue
Block a user