mirror of
https://github.com/meshtastic/firmware.git
synced 2025-10-27 23:12:39 +00:00
Cannedmessage cleanup and emotes fixed
This commit is contained in:
parent
ebbb8a6f9f
commit
07d3726cde
@ -1437,16 +1437,26 @@ int Screen::handleUIFrameEvent(const UIFrameEvent *event)
|
||||
|
||||
if (showingNormalScreen) {
|
||||
// Regenerate the frameset, potentially honoring a module's internal requestFocus() call
|
||||
if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET)
|
||||
if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET) {
|
||||
setFrames(FOCUS_MODULE);
|
||||
}
|
||||
|
||||
// Regenerate the frameset, while Attempt to maintain focus on the current frame
|
||||
else if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND)
|
||||
// Regenerate the frameset, while attempting to maintain focus on the current frame
|
||||
else if (event->action == UIFrameEvent::Action::REGENERATE_FRAMESET_BACKGROUND) {
|
||||
setFrames(FOCUS_PRESERVE);
|
||||
}
|
||||
|
||||
// Don't regenerate the frameset, just re-draw whatever is on screen ASAP
|
||||
else if (event->action == UIFrameEvent::Action::REDRAW_ONLY)
|
||||
else if (event->action == UIFrameEvent::Action::REDRAW_ONLY) {
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
// Jump directly to the Text Message screen
|
||||
else if (event->action == UIFrameEvent::Action::SWITCH_TO_TEXTMESSAGE) {
|
||||
setFrames(FOCUS_PRESERVE); // preserve current frame ordering
|
||||
ui->switchToFrame(framesetInfo.positions.textMessage);
|
||||
setFastFramerate(); // force redraw ASAP
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@ -44,6 +44,7 @@ struct UIFrameEvent {
|
||||
REDRAW_ONLY, // Don't change which frames are show, just redraw, asap
|
||||
REGENERATE_FRAMESET, // Regenerate (change? add? remove?) screen frames, honoring requestFocus()
|
||||
REGENERATE_FRAMESET_BACKGROUND, // Regenerate screen frames, Attempt to remain on the same frame throughout
|
||||
SWITCH_TO_TEXTMESSAGE // Jump directly to the Text Message screen
|
||||
} action = REDRAW_ONLY;
|
||||
|
||||
// We might want to pass additional data inside this struct at some point
|
||||
|
||||
@ -75,7 +75,7 @@ CannedMessageModule::CannedMessageModule()
|
||||
|
||||
void CannedMessageModule::LaunchWithDestination(NodeNum newDest, uint8_t newChannel)
|
||||
{
|
||||
// 🚫 Do NOT override explicit broadcast replies
|
||||
// Do NOT override explicit broadcast replies
|
||||
// Only reuse lastDest in LaunchRepeatDestination()
|
||||
|
||||
dest = newDest;
|
||||
@ -116,7 +116,7 @@ void CannedMessageModule::LaunchRepeatDestination()
|
||||
|
||||
void CannedMessageModule::LaunchFreetextWithDestination(NodeNum newDest, uint8_t newChannel)
|
||||
{
|
||||
// 🚫 Do NOT override explicit broadcast replies
|
||||
// Do NOT override explicit broadcast replies
|
||||
// Only reuse lastDest in LaunchRepeatDestination()
|
||||
|
||||
dest = newDest;
|
||||
@ -578,7 +578,7 @@ bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bo
|
||||
if (runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION)
|
||||
return false;
|
||||
|
||||
// === Handle Cancel key: go inactive, clear UI state ===
|
||||
// Handle Cancel key: go inactive, clear UI state
|
||||
if (runState != CANNED_MESSAGE_RUN_STATE_INACTIVE &&
|
||||
(event->inputEvent == INPUT_BROKER_CANCEL || event->inputEvent == INPUT_BROKER_ALT_LONG)) {
|
||||
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
@ -607,7 +607,7 @@ bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bo
|
||||
} else if (isSelect) {
|
||||
const char *current = messages[currentMessageIndex];
|
||||
|
||||
// === [Select Destination] triggers destination selection UI ===
|
||||
// [Select Destination] triggers destination selection UI
|
||||
if (strcmp(current, "[Select Destination]") == 0) {
|
||||
returnToCannedList = true;
|
||||
runState = CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION;
|
||||
@ -618,7 +618,7 @@ bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bo
|
||||
return true;
|
||||
}
|
||||
|
||||
// === [Exit] returns to the main/inactive screen ===
|
||||
// [Exit] returns to the main/inactive screen
|
||||
if (strcmp(current, "[Exit]") == 0) {
|
||||
// Set runState to inactive so we return to main UI
|
||||
runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
@ -632,7 +632,7 @@ bool CannedMessageModule::handleMessageSelectorInput(const InputEvent *event, bo
|
||||
return true;
|
||||
}
|
||||
|
||||
// === [Free Text] triggers the free text input (virtual keyboard) ===
|
||||
// [Free Text] triggers the free text input (virtual keyboard)
|
||||
#if defined(USE_VIRTUAL_KEYBOARD)
|
||||
if (strcmp(current, "[-- Free Text --]") == 0) {
|
||||
runState = CANNED_MESSAGE_RUN_STATE_FREETEXT;
|
||||
@ -972,8 +972,17 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
|
||||
|
||||
this->waitingForAck = true;
|
||||
|
||||
// Send to mesh
|
||||
service->sendToMesh(p, RX_SRC_LOCAL, true);
|
||||
|
||||
// Show banner immediately
|
||||
if (screen) {
|
||||
graphics::BannerOverlayOptions opts;
|
||||
opts.message = "Sending...";
|
||||
opts.durationMs = 2000;
|
||||
screen->showOverlayBanner(opts);
|
||||
}
|
||||
|
||||
// Save outgoing message
|
||||
StoredMessage sm;
|
||||
|
||||
@ -1010,6 +1019,22 @@ void CannedMessageModule::sendText(NodeNum dest, ChannelIndex channel, const cha
|
||||
}
|
||||
|
||||
playComboTune();
|
||||
|
||||
// Important
|
||||
// Instead of INACTIVE here, use SENDING_ACTIVE so runOnce() does the cleanup.
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE;
|
||||
this->payload = wantReplies ? 1 : 0;
|
||||
requestFocus();
|
||||
|
||||
// Tell Screen to switch to TextMessage frame via UIFrameEvent
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::SWITCH_TO_TEXTMESSAGE;
|
||||
notifyObservers(&e);
|
||||
|
||||
// Now mark ourselves as sending
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE;
|
||||
this->payload = wantReplies ? 1 : 0;
|
||||
requestFocus(); // focus only after event is dispatched
|
||||
}
|
||||
|
||||
int32_t CannedMessageModule::runOnce()
|
||||
@ -1086,15 +1111,11 @@ int32_t CannedMessageModule::runOnce()
|
||||
}
|
||||
// Handle SENDING_ACTIVE state transition after virtual keyboard message
|
||||
else if (this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE && this->payload == 0) {
|
||||
// This happens after virtual keyboard message sending is complete
|
||||
LOG_INFO("Virtual keyboard message sending completed, returning to inactive state");
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
temporaryMessage = "";
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
this->currentMessageIndex = -1;
|
||||
this->freetext = "";
|
||||
this->cursor = 0;
|
||||
this->notifyObservers(&e);
|
||||
return INT32_MAX;
|
||||
} else if (((this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) || (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT)) &&
|
||||
!Throttle::isWithinTimespanMs(this->lastTouchMillis, INACTIVATE_AFTER_MS)) {
|
||||
// Reset module on inactivity
|
||||
@ -1122,7 +1143,21 @@ int32_t CannedMessageModule::runOnce()
|
||||
} else if (this->payload == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
||||
if (this->freetext.length() > 0) {
|
||||
sendText(this->dest, this->channel, this->freetext.c_str(), true);
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE;
|
||||
|
||||
// Clean up state but *don’t* deactivate yet
|
||||
this->currentMessageIndex = -1;
|
||||
this->freetext = "";
|
||||
this->cursor = 0;
|
||||
|
||||
// Tell Screen to jump straight to the TextMessage frame
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::SWITCH_TO_TEXTMESSAGE;
|
||||
this->notifyObservers(&e);
|
||||
|
||||
// Now deactivate this module
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
|
||||
return INT32_MAX; // don’t fall back into canned list
|
||||
} else {
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
}
|
||||
@ -1136,30 +1171,53 @@ int32_t CannedMessageModule::runOnce()
|
||||
return INT32_MAX;
|
||||
} else {
|
||||
sendText(this->dest, this->channel, this->messages[this->currentMessageIndex], true);
|
||||
|
||||
// Clean up state
|
||||
this->currentMessageIndex = -1;
|
||||
this->freetext = "";
|
||||
this->cursor = 0;
|
||||
|
||||
// Tell Screen to jump straight to the TextMessage frame
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::SWITCH_TO_TEXTMESSAGE;
|
||||
this->notifyObservers(&e);
|
||||
|
||||
// Now deactivate this module
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
|
||||
return INT32_MAX; // don’t fall back into canned list
|
||||
}
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE;
|
||||
} else {
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_INACTIVE;
|
||||
}
|
||||
}
|
||||
// fallback clean-up if nothing above returned
|
||||
this->currentMessageIndex = -1;
|
||||
this->freetext = "";
|
||||
this->cursor = 0;
|
||||
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
this->notifyObservers(&e);
|
||||
return 2000;
|
||||
|
||||
// Immediately stop, don’t linger on canned screen
|
||||
return INT32_MAX;
|
||||
}
|
||||
// Highlight [Select Destination] initially when entering the message list
|
||||
else if ((this->runState != CANNED_MESSAGE_RUN_STATE_FREETEXT) && (this->currentMessageIndex == -1)) {
|
||||
int selectDestination = 0;
|
||||
for (int i = 0; i < this->messagesCount; ++i) {
|
||||
if (strcmp(this->messages[i], "[Select Destination]") == 0) {
|
||||
selectDestination = i;
|
||||
break;
|
||||
// Only auto-highlight [Select Destination] if we’re ACTIVELY browsing,
|
||||
// not when coming back from a sent message.
|
||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE) {
|
||||
int selectDestination = 0;
|
||||
for (int i = 0; i < this->messagesCount; ++i) {
|
||||
if (strcmp(this->messages[i], "[Select Destination]") == 0) {
|
||||
selectDestination = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this->currentMessageIndex = selectDestination;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
}
|
||||
this->currentMessageIndex = selectDestination;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_ACTIVE;
|
||||
} else if (this->runState == CANNED_MESSAGE_RUN_STATE_ACTION_UP) {
|
||||
if (this->messagesCount > 0) {
|
||||
this->currentMessageIndex = getPrevIndex();
|
||||
@ -1280,7 +1338,10 @@ const char *CannedMessageModule::getNodeName(NodeNum node)
|
||||
|
||||
bool CannedMessageModule::shouldDraw()
|
||||
{
|
||||
return (currentMessageIndex != -1) || (this->runState != CANNED_MESSAGE_RUN_STATE_INACTIVE);
|
||||
// Only allow drawing when we're in an interactive UI state.
|
||||
return (this->runState == CANNED_MESSAGE_RUN_STATE_ACTIVE || this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT ||
|
||||
this->runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION ||
|
||||
this->runState == CANNED_MESSAGE_RUN_STATE_EMOTE_PICKER);
|
||||
}
|
||||
|
||||
// Has the user defined any canned messages?
|
||||
@ -1539,7 +1600,7 @@ void CannedMessageModule::drawDestinationSelectionScreen(OLEDDisplay *display, O
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
|
||||
// === Header ===
|
||||
// Header
|
||||
int titleY = 2;
|
||||
String titleText = "Select Destination";
|
||||
titleText += searchQuery.length() > 0 ? " [" + searchQuery + "]" : " [ ]";
|
||||
@ -1547,7 +1608,7 @@ void CannedMessageModule::drawDestinationSelectionScreen(OLEDDisplay *display, O
|
||||
display->drawString(display->getWidth() / 2, titleY, titleText);
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
|
||||
// === List Items ===
|
||||
// List Items
|
||||
int rowYOffset = titleY + (FONT_HEIGHT_SMALL - 4);
|
||||
int numActiveChannels = this->activeChannelIndices.size();
|
||||
int totalEntries = numActiveChannels + this->filteredNodes.size();
|
||||
@ -1556,7 +1617,7 @@ void CannedMessageModule::drawDestinationSelectionScreen(OLEDDisplay *display, O
|
||||
if (this->visibleRows < 1)
|
||||
this->visibleRows = 1;
|
||||
|
||||
// === Clamp scrolling ===
|
||||
// Clamp scrolling
|
||||
if (scrollIndex > totalEntries / columns)
|
||||
scrollIndex = totalEntries / columns;
|
||||
if (scrollIndex < 0)
|
||||
@ -1601,18 +1662,18 @@ void CannedMessageModule::drawDestinationSelectionScreen(OLEDDisplay *display, O
|
||||
if (strlen(entryText) == 0 || strcmp(entryText, "Unknown") == 0)
|
||||
strcpy(entryText, "?");
|
||||
|
||||
// === Highlight background (if selected) ===
|
||||
// Highlight background (if selected)
|
||||
if (itemIndex == destIndex) {
|
||||
int scrollPadding = 8; // Reserve space for scrollbar
|
||||
display->fillRect(0, yOffset + 2, display->getWidth() - scrollPadding, FONT_HEIGHT_SMALL - 5);
|
||||
display->setColor(BLACK);
|
||||
}
|
||||
|
||||
// === Draw entry text ===
|
||||
// Draw entry text
|
||||
display->drawString(xOffset + 2, yOffset, entryText);
|
||||
display->setColor(WHITE);
|
||||
|
||||
// === Draw key icon (after highlight) ===
|
||||
// Draw key icon (after highlight)
|
||||
if (itemIndex >= numActiveChannels) {
|
||||
int nodeIndex = itemIndex - numActiveChannels;
|
||||
if (nodeIndex >= 0 && nodeIndex < static_cast<int>(this->filteredNodes.size())) {
|
||||
@ -1731,7 +1792,13 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
|
||||
// === Draw temporary message if available ===
|
||||
// Never draw if state is outside our UI modes
|
||||
if (!(runState == CANNED_MESSAGE_RUN_STATE_ACTIVE || runState == CANNED_MESSAGE_RUN_STATE_FREETEXT ||
|
||||
runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION || runState == CANNED_MESSAGE_RUN_STATE_EMOTE_PICKER)) {
|
||||
return; // bail if not in a UI state that should render
|
||||
}
|
||||
|
||||
// Draw temporary message if available
|
||||
if (temporaryMessage.length() != 0) {
|
||||
requestFocus(); // Tell Screen::setFrames to move to our module's frame
|
||||
LOG_DEBUG("Draw temporary message: %s", temporaryMessage.c_str());
|
||||
@ -1741,94 +1808,19 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
return;
|
||||
}
|
||||
|
||||
// === Emote Picker Screen ===
|
||||
// Emote Picker Screen
|
||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_EMOTE_PICKER) {
|
||||
drawEmotePickerScreen(display, state, x, y); // <-- Call your emote picker drawer here
|
||||
return;
|
||||
}
|
||||
|
||||
// === Destination Selection ===
|
||||
// Destination Selection
|
||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_DESTINATION_SELECTION) {
|
||||
drawDestinationSelectionScreen(display, state, x, y);
|
||||
return;
|
||||
}
|
||||
|
||||
// === ACK/NACK Screen ===
|
||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED) {
|
||||
requestFocus();
|
||||
EINK_ADD_FRAMEFLAG(display, COSMETIC);
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
|
||||
#ifdef USE_EINK
|
||||
display->setFont(FONT_SMALL);
|
||||
int yOffset = y + 10;
|
||||
#else
|
||||
display->setFont(FONT_MEDIUM);
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
int yOffset = y;
|
||||
#else
|
||||
int yOffset = y + 10;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Delivery Status Message
|
||||
if (this->ack) {
|
||||
if (this->lastSentNode == NODENUM_BROADCAST) {
|
||||
snprintf(buffer, sizeof(buffer), "Broadcast Sent to\n%s", channels.getName(this->channel));
|
||||
} else if (this->lastAckHopLimit > this->lastAckHopStart) {
|
||||
snprintf(buffer, sizeof(buffer), "Delivered (%d hops)\nto %s", this->lastAckHopLimit - this->lastAckHopStart,
|
||||
getNodeName(this->incoming));
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "Delivered\nto %s", getNodeName(this->incoming));
|
||||
}
|
||||
} else {
|
||||
snprintf(buffer, sizeof(buffer), "Delivery failed\nto %s", getNodeName(this->incoming));
|
||||
}
|
||||
|
||||
// Draw delivery message and compute y-offset after text height
|
||||
int lineCount = 1;
|
||||
for (const char *ptr = buffer; *ptr; ptr++) {
|
||||
if (*ptr == '\n')
|
||||
lineCount++;
|
||||
}
|
||||
|
||||
display->drawString(display->getWidth() / 2 + x, yOffset, buffer);
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
yOffset += lineCount * FONT_HEIGHT_MEDIUM - 5; // only 1 line gap, no extra padding
|
||||
#else
|
||||
yOffset += lineCount * FONT_HEIGHT_MEDIUM; // only 1 line gap, no extra padding
|
||||
#endif
|
||||
#ifndef USE_EINK
|
||||
// SNR + RSSI Compact Line
|
||||
if (this->ack) {
|
||||
display->setFont(FONT_SMALL);
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
snprintf(buffer, sizeof(buffer), "SNR: %.1f dB \nRSSI: %d", this->lastRxSnr, this->lastRxRssi);
|
||||
#else
|
||||
snprintf(buffer, sizeof(buffer), "SNR: %.1f dB RSSI: %d", this->lastRxSnr, this->lastRxRssi);
|
||||
#endif
|
||||
display->drawString(display->getWidth() / 2 + x, yOffset, buffer);
|
||||
}
|
||||
#endif
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// === Sending Screen ===
|
||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_SENDING_ACTIVE) {
|
||||
EINK_ADD_FRAMEFLAG(display, COSMETIC);
|
||||
requestFocus();
|
||||
#ifdef USE_EINK
|
||||
display->setFont(FONT_SMALL);
|
||||
#else
|
||||
display->setFont(FONT_MEDIUM);
|
||||
#endif
|
||||
display->setTextAlignment(TEXT_ALIGN_CENTER);
|
||||
display->drawString(display->getWidth() / 2 + x, 0 + y + 12, "Sending...");
|
||||
return;
|
||||
}
|
||||
|
||||
// === Disabled Screen ===
|
||||
// Disabled Screen
|
||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_DISABLED) {
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
@ -1836,7 +1828,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
return;
|
||||
}
|
||||
|
||||
// === Free Text Input Screen ===
|
||||
// Free Text Input Screen
|
||||
if (this->runState == CANNED_MESSAGE_RUN_STATE_FREETEXT) {
|
||||
requestFocus();
|
||||
#if defined(USE_EINK) && defined(USE_EINK_DYNAMICDISPLAY)
|
||||
@ -1911,7 +1903,7 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
}
|
||||
}
|
||||
|
||||
// ===== Advanced word-wrapping (emotes + text, split by word, wrap by char if needed) =====
|
||||
// Advanced word-wrapping (emotes + text, split by word, wrap by char if needed)
|
||||
std::vector<std::vector<std::pair<bool, String>>> lines;
|
||||
std::vector<std::pair<bool, String>> currentLine;
|
||||
int lineWidth = 0;
|
||||
@ -2006,12 +1998,12 @@ void CannedMessageModule::drawFrame(OLEDDisplay *display, OLEDDisplayUiState *st
|
||||
return;
|
||||
}
|
||||
|
||||
// === Canned Messages List ===
|
||||
// Canned Messages List
|
||||
if (this->messagesCount > 0) {
|
||||
display->setTextAlignment(TEXT_ALIGN_LEFT);
|
||||
display->setFont(FONT_SMALL);
|
||||
|
||||
// ====== Precompute per-row heights based on emotes (centered if present) ======
|
||||
// Precompute per-row heights based on emotes (centered if present)
|
||||
const int baseRowSpacing = FONT_HEIGHT_SMALL - 4;
|
||||
|
||||
int topMsg;
|
||||
@ -2167,12 +2159,6 @@ ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &
|
||||
{
|
||||
if (mp.decoded.portnum == meshtastic_PortNum_ROUTING_APP && waitingForAck) {
|
||||
if (mp.decoded.request_id != 0) {
|
||||
// Trigger screen refresh for ACK/NACK feedback
|
||||
UIFrameEvent e;
|
||||
e.action = UIFrameEvent::Action::REGENERATE_FRAMESET;
|
||||
requestFocus();
|
||||
this->runState = CANNED_MESSAGE_RUN_STATE_ACK_NACK_RECEIVED;
|
||||
|
||||
// Decode the routing response
|
||||
meshtastic_Routing decoded = meshtastic_Routing_init_default;
|
||||
pb_decode_from_bytes(mp.decoded.payload.bytes, mp.decoded.payload.size, meshtastic_Routing_fields, &decoded);
|
||||
@ -2198,8 +2184,37 @@ ProcessMessage CannedMessageModule::handleReceived(const meshtastic_MeshPacket &
|
||||
this->ack = isAck && (wasBroadcast || isFromDest);
|
||||
|
||||
waitingForAck = false;
|
||||
this->notifyObservers(&e);
|
||||
setIntervalFromNow(3000); // Time to show ACK/NACK screen
|
||||
|
||||
// Capture radio metrics from this ACK/NACK packet
|
||||
this->lastRxRssi = mp.rx_rssi;
|
||||
this->lastRxSnr = mp.rx_snr;
|
||||
|
||||
// Show ACK/NACK as overlay banner
|
||||
if (screen) {
|
||||
graphics::BannerOverlayOptions opts;
|
||||
static char buf[96];
|
||||
|
||||
if (this->ack) {
|
||||
if (this->lastSentNode == NODENUM_BROADCAST) {
|
||||
snprintf(buf, sizeof(buf), "Broadcast sent to \n%s\n\nSNR: %.1f dB RSSI: %d dBm",
|
||||
channels.getName(this->channel), this->lastRxSnr, this->lastRxRssi);
|
||||
} else if (this->lastAckHopLimit > this->lastAckHopStart) {
|
||||
snprintf(buf, sizeof(buf), "Delivered (%d hops) to \n%s\n\nSNR: %.1f dB RSSI: %d dBm",
|
||||
this->lastAckHopLimit - this->lastAckHopStart, getNodeName(this->incoming), this->lastRxSnr,
|
||||
this->lastRxRssi);
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "Delivered to %s\n\nSNR: \n%.1f dB RSSI: %d dBm", getNodeName(this->incoming),
|
||||
this->lastRxSnr, this->lastRxRssi);
|
||||
}
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf), "Delivery failed to \n%s", getNodeName(this->incoming), this->lastRxSnr,
|
||||
this->lastRxRssi);
|
||||
}
|
||||
|
||||
opts.message = buf;
|
||||
opts.durationMs = 3000;
|
||||
screen->showOverlayBanner(opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user