mirror of
https://github.com/meshtastic/firmware.git
synced 2025-12-28 21:50:35 +00:00
Multi message storage (#8182)
* First try at multimessage storage and display * Nrf built issue fix * Message view mode * Add channel name instead of channel slot * trunk fix * Fix for DM threading * fix for message time * rename of view mode to Conversations * Reply in thread feature * rename Select View Mode to Select Conversation * dismiss all live fix * Messages from phone show on screen * Decoupled message packets from screen.cpp and cleaned up * Cannedmessage cleanup and emotes fixed * Ack on messages sent * Ack message cleanup * Dismiss feature fixed * removed legacy temporary messages * Emote picker fix * Memory size debug * Build error fix * Sanity checks are okay sometimes * Lengthen channel name and finalize cleanup removal of Broadcast * Change DM to @ in order to unify on a single method * Continue unifying display, also show message status on the "isMine" lines * Add context for incoming messages * Better to say "in" vs "on" * crash fix for confirmation nodes * Fix outbound labels based to avoid creating delays * Eink autoscroll dissabled * gating for message storage when not using a screen * revert * Build fail fix * Don't error out with unset MAC address in unit tests * Provide some extra spacing for low hanging characters in messages * Reorder menu options and reword Respond * Reword menus to better reflect actions * Go to thread from favorite screen * Reorder Favorite Action Menu with simple word modifications * Consolidate wording on "Chats" * Mute channel fix * trunk fix * Clean up how muting works along with when we wake the screen * Fix builds for HELTEC_MESH_SOLAR * Signal bars for message ack * fix for notification renderer * Remove duplicate code, fix more Chats, and fix C6L MessageRenderer * Fix to many warnings related to BaseUI * preset aware signal strength display * More C6L fixes and clean up header lines * Use text aligns for message layout where necessary * Attempt to fix memory usage of invalidLifetime * Update channel mute for adjusted protobuf * Missed a comma in merge conflicts * cleanup to get more space * Trunk fixes * Optimize Hi Rez Chirpy to save space * more fixes * More cleanup * Remove used getConversationWith * Remove unused dismissNewestMessage * Fix another build error on occassion * Dimiss key combo function deprecated * More cleanup * Fn symbol code removed * Waypoint cleanup * Trunk fix * Fixup Waypoint screen with BaseUI code * Implement Haruki's ClockRenderer and broadcast decomposeTime across various files. * Revert "Implement Haruki's ClockRenderer and broadcast decomposeTime across various files." This reverts commit2f65721774. * Implement Haruki's ClockRenderer and broadcast decomposeTime across various files. Attempt 2! * remove memory usage debug * Revert only RangeTestModule.cpp change * Switch from dynamic std::string storage to fixed-size char[] * Removing old left over code * More optimization * Free Heap when not on Message screen * build error fixes * Restore ellipsis to end of long names * Remove legacy function renderMessageContent * improved destination filtering * force PKI * cleanup * Shorten longNames to not exceed message popups * log messages sent from apps * Trunk fix * Improve layout of messages screen * Fix potential crash for undefined variable * Revert changes to RedirectablePrint.cpp * Apply shortening to longNames in Select Destination * Fix short name displays * Fix sprintfOverlappingData issue * Fix nullPointerRedundantCheck warning on ESP32 * Add "Delete All Chats" to all chat views * Improve getSafeNodeName / sanitizeString code. * Improve getSafeNodeName further * Restore auto favorite; but only if not CLIENT_BASE * Don't favorite if WE are CLIENT_BASE role * Don't run message persistent in MUI * Fix broken endifs * Unkwnown nodes no longer show as ??? on message thread * More delete options and cleanup of code * fix for delete this chat * Message menu cleanup * trunk fix * Clean up some menu options and remove some Unit C6L ifdefines * Rework Delete flow * Desperate times call for desperate measures * Create a background on the connected icon to reduce overlap impact * Optimize code for background image * Fix for Muzi_Base * Trunk Fixes * Remove the up/down shortcut to launch canned messages (#8370) * Remove the up/down shortcut to launch canned messages * Enabled MQTT and WEBSERVER by default (#8679) Signed-off-by: kur1k0 <zhuzirun@m5stack.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz> --------- Signed-off-by: kur1k0 <zhuzirun@m5stack.com> Co-authored-by: Riker <zhuzirun@m5stack.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> * Correct string length calculation for signal bars * Manual message scrolling * Fix * Restore CannedMessages on Home Frame * UpDown situational destination for textMessage * Correct up/down destinations on textMessage frame * Update Screen.h for handleTextMessage * Update Screen.cpp to repair a merge issue * Add nudge scroll on UpDownEncoder devices. * Set nodeName to maximum size * Revert "Set nodeName to maximum size" This reverts commite254f39925. * Reflow Node Lists and TLora Pager Views (#8942) * Add files via upload * Move files into the right place * Short or Long Names for everyone! * Add scrolling to Node list * Pagination fix for Latest to oldest per page * Page counters * Dynamic scaling of column counts based upon screen size, clean up box drawing * Reflow Node Lists and TLora Pager Views (#8942) * Add files via upload * Move files into the right place * Short or Long Names for everyone! * Add scrolling to Node list * Pagination fix for Latest to oldest per page * Page counters * Dynamic scaling of column counts based upon screen size, clean up box drawing * Update exempt labels for stale bot workflow Adds triaged and backlog to the list of exempt labels. * Update naming of Frame Visibility toggles * Fix to scrolling * Fix for content cutting off when from us * Fix for "delete this chat" now it does delete the current one * Rework isHighResolution to be an enum called ScreenResolution * Migrate Unit C6L macro guards into currentResolution UltraLow checks * Mistakes happen - restoring NodeList Renderer line --------- Signed-off-by: kur1k0 <zhuzirun@m5stack.com> Co-authored-by: Jason P <applewiz@mac.com> Co-authored-by: Jonathan Bennett <jbennett@incomsystems.biz> Co-authored-by: Riker <zhuzirun@m5stack.com> Co-authored-by: Ben Meadors <benmmeadors@gmail.com> Co-authored-by: whywilson <m.tools@qq.com> Co-authored-by: Tom Fifield <tom@tomfifield.net>
This commit is contained in:
@@ -46,6 +46,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#endif
|
||||
#include "FSCommon.h"
|
||||
#include "MeshService.h"
|
||||
#include "MessageStore.h"
|
||||
#include "RadioLibInterface.h"
|
||||
#include "error.h"
|
||||
#include "gps/GeoCoord.h"
|
||||
@@ -64,10 +65,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#include "modules/WaypointModule.h"
|
||||
#include "sleep.h"
|
||||
#include "target_specific.h"
|
||||
|
||||
using graphics::Emote;
|
||||
using graphics::emotes;
|
||||
using graphics::numEmotes;
|
||||
extern MessageStore messageStore;
|
||||
|
||||
#if USE_TFTDISPLAY
|
||||
extern uint16_t TFT_MESH;
|
||||
@@ -119,10 +117,6 @@ uint32_t dopThresholds[5] = {2000, 1000, 500, 200, 100};
|
||||
// we'll need to hold onto pointers for the modules that can draw a frame.
|
||||
std::vector<MeshModule *> moduleFrames;
|
||||
|
||||
// Global variables for screen function overlay symbols
|
||||
std::vector<std::string> functionSymbol;
|
||||
std::string functionSymbolString;
|
||||
|
||||
#if HAS_GPS
|
||||
// GeoCoord object for the screen
|
||||
GeoCoord geoCoord;
|
||||
@@ -263,19 +257,11 @@ static void drawModuleFrame(OLEDDisplay *display, OLEDDisplayUiState *state, int
|
||||
} else {
|
||||
// otherwise, just display the module frame that's aligned with the current frame
|
||||
module_frame = state->currentFrame;
|
||||
// LOG_DEBUG("Screen is not in transition. Frame: %d", module_frame);
|
||||
}
|
||||
// LOG_DEBUG("Draw Module Frame %d", module_frame);
|
||||
MeshModule &pi = *moduleFrames.at(module_frame);
|
||||
pi.drawFrame(display, state, x, y);
|
||||
}
|
||||
|
||||
// Ignore messages originating from phone (from the current node 0x0) unless range test or store and forward module are enabled
|
||||
static bool shouldDrawMessage(const meshtastic_MeshPacket *packet)
|
||||
{
|
||||
return packet->from != 0 && !moduleConfig.store_forward.enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a recent lat/lon return a guess of the heading the user is walking on.
|
||||
*
|
||||
@@ -322,16 +308,16 @@ Screen::Screen(ScanI2C::DeviceAddress address, meshtastic_Config_DisplayConfig_O
|
||||
{
|
||||
graphics::normalFrames = new FrameCallback[MAX_NUM_NODES + NUM_EXTRA_FRAMES];
|
||||
|
||||
LOG_INFO("Protobuf Value uiconfig.screen_rgb_color: %d", uiconfig.screen_rgb_color);
|
||||
int32_t rawRGB = uiconfig.screen_rgb_color;
|
||||
if (rawRGB > 0 && rawRGB <= 255255255) {
|
||||
uint8_t TFT_MESH_r = (rawRGB >> 16) & 0xFF;
|
||||
uint8_t TFT_MESH_g = (rawRGB >> 8) & 0xFF;
|
||||
uint8_t TFT_MESH_b = rawRGB & 0xFF;
|
||||
LOG_INFO("Values of r,g,b: %d, %d, %d", TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
|
||||
|
||||
if (TFT_MESH_r <= 255 && TFT_MESH_g <= 255 && TFT_MESH_b <= 255) {
|
||||
TFT_MESH = COLOR565(TFT_MESH_r, TFT_MESH_g, TFT_MESH_b);
|
||||
// Only validate the combined value once
|
||||
if (rawRGB > 0 && rawRGB <= 255255255) {
|
||||
// Extract each component as a normal int first
|
||||
int r = (rawRGB >> 16) & 0xFF;
|
||||
int g = (rawRGB >> 8) & 0xFF;
|
||||
int b = rawRGB & 0xFF;
|
||||
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255) {
|
||||
TFT_MESH = COLOR565(static_cast<uint8_t>(r), static_cast<uint8_t>(g), static_cast<uint8_t>(b));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,10 +536,10 @@ void Screen::handleSetOn(bool on, FrameCallback einkScreensaver)
|
||||
void Screen::setup()
|
||||
{
|
||||
|
||||
// === Enable display rendering ===
|
||||
// Enable display rendering
|
||||
useDisplay = true;
|
||||
|
||||
// === Load saved brightness from UI config ===
|
||||
// Load saved brightness from UI config
|
||||
// For OLED displays (SSD1306), default brightness is 255 if not set
|
||||
if (uiconfig.screen_brightness == 0) {
|
||||
#if defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107)
|
||||
@@ -565,7 +551,7 @@ void Screen::setup()
|
||||
brightness = uiconfig.screen_brightness;
|
||||
}
|
||||
|
||||
// === Detect OLED subtype (if supported by board variant) ===
|
||||
// Detect OLED subtype (if supported by board variant)
|
||||
#ifdef AutoOLEDWire_h
|
||||
if (isAUTOOled)
|
||||
static_cast<AutoOLEDWire *>(dispdev)->setDetected(model);
|
||||
@@ -587,7 +573,7 @@ void Screen::setup()
|
||||
static_cast<ST7796Spi *>(dispdev)->setRGB(TFT_MESH);
|
||||
#endif
|
||||
|
||||
// === Initialize display and UI system ===
|
||||
// Initialize display and UI system
|
||||
ui->init();
|
||||
displayWidth = dispdev->width();
|
||||
displayHeight = dispdev->height();
|
||||
@@ -599,7 +585,7 @@ void Screen::setup()
|
||||
ui->disableAllIndicators(); // Disable page indicator dots
|
||||
ui->getUiState()->userData = this; // Allow static callbacks to access Screen instance
|
||||
|
||||
// === Apply loaded brightness ===
|
||||
// Apply loaded brightness
|
||||
#if defined(ST7789_CS)
|
||||
static_cast<TFTDisplay *>(dispdev)->setDisplayBrightness(brightness);
|
||||
#elif defined(USE_OLED) || defined(USE_SSD1306) || defined(USE_SH1106) || defined(USE_SH1107) || defined(USE_SPISSD1306)
|
||||
@@ -607,20 +593,20 @@ void Screen::setup()
|
||||
#endif
|
||||
LOG_INFO("Applied screen brightness: %d", brightness);
|
||||
|
||||
// === Set custom overlay callbacks ===
|
||||
// Set custom overlay callbacks
|
||||
static OverlayCallback overlays[] = {
|
||||
graphics::UIRenderer::drawNavigationBar // Custom indicator icons for each frame
|
||||
};
|
||||
ui->setOverlays(overlays, sizeof(overlays) / sizeof(overlays[0]));
|
||||
|
||||
// === Enable UTF-8 to display mapping ===
|
||||
// Enable UTF-8 to display mapping
|
||||
dispdev->setFontTableLookupFunction(customFontTableLookup);
|
||||
|
||||
#ifdef USERPREFS_OEM_TEXT
|
||||
logo_timeout *= 2; // Give more time for branded boot logos
|
||||
#endif
|
||||
|
||||
// === Configure alert frames (e.g., "Resuming..." or region name) ===
|
||||
// Configure alert frames (e.g., "Resuming..." or region name)
|
||||
EINK_ADD_FRAMEFLAG(dispdev, DEMAND_FAST); // Skip slow refresh
|
||||
alertFrames[0] = [this](OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) {
|
||||
#ifdef ARCH_ESP32
|
||||
@@ -636,10 +622,10 @@ void Screen::setup()
|
||||
ui->setFrames(alertFrames, 1);
|
||||
ui->disableAutoTransition(); // Require manual navigation between frames
|
||||
|
||||
// === Log buffer for on-screen logs (3 lines max) ===
|
||||
// Log buffer for on-screen logs (3 lines max)
|
||||
dispdev->setLogBuffer(3, 32);
|
||||
|
||||
// === Optional screen mirroring or flipping (e.g. for T-Beam orientation) ===
|
||||
// Optional screen mirroring or flipping (e.g. for T-Beam orientation)
|
||||
#ifdef SCREEN_MIRROR
|
||||
dispdev->mirrorScreen();
|
||||
#else
|
||||
@@ -657,7 +643,7 @@ void Screen::setup()
|
||||
}
|
||||
#endif
|
||||
|
||||
// === Generate device ID from MAC address ===
|
||||
// Generate device ID from MAC address
|
||||
uint8_t dmac[6];
|
||||
getMacAddr(dmac);
|
||||
snprintf(screen->ourId, sizeof(screen->ourId), "%02x%02x", dmac[4], dmac[5]);
|
||||
@@ -666,9 +652,9 @@ void Screen::setup()
|
||||
handleSetOn(false); // Ensure proper init for Arduino targets
|
||||
#endif
|
||||
|
||||
// === Turn on display and trigger first draw ===
|
||||
// Turn on display and trigger first draw
|
||||
handleSetOn(true);
|
||||
determineResolution(dispdev->height(), dispdev->width());
|
||||
graphics::currentResolution = graphics::determineScreenResolution(dispdev->height(), dispdev->width());
|
||||
ui->update();
|
||||
#ifndef USE_EINK
|
||||
ui->update(); // Some SSD1306 clones drop the first draw, so run twice
|
||||
@@ -689,7 +675,7 @@ void Screen::setup()
|
||||
touchScreenImpl1->init();
|
||||
#endif
|
||||
|
||||
// === Subscribe to device status updates ===
|
||||
// Subscribe to device status updates
|
||||
powerStatusObserver.observe(&powerStatus->onNewStatus);
|
||||
gpsStatusObserver.observe(&gpsStatus->onNewStatus);
|
||||
nodeStatusObserver.observe(&nodeStatus->onNewStatus);
|
||||
@@ -697,12 +683,14 @@ void Screen::setup()
|
||||
#if !MESHTASTIC_EXCLUDE_ADMIN
|
||||
adminMessageObserver.observe(adminModule);
|
||||
#endif
|
||||
if (textMessageModule)
|
||||
textMessageObserver.observe(textMessageModule);
|
||||
if (inputBroker)
|
||||
inputObserver.observe(inputBroker);
|
||||
|
||||
// === Notify modules that support UI events ===
|
||||
// Load persisted messages into RAM
|
||||
messageStore.loadFromFlash();
|
||||
LOG_INFO("MessageStore loaded from flash");
|
||||
|
||||
// Notify modules that support UI events
|
||||
MeshModule::observeUIEvents(&uiFrameEventObserver);
|
||||
}
|
||||
|
||||
@@ -773,6 +761,23 @@ int32_t Screen::runOnce()
|
||||
if (displayHeight == 0) {
|
||||
displayHeight = dispdev->getHeight();
|
||||
}
|
||||
|
||||
// Detect frame transitions and clear message cache when leaving text message screen
|
||||
{
|
||||
static int8_t lastFrameIndex = -1;
|
||||
int8_t currentFrameIndex = ui->getUiState()->currentFrame;
|
||||
int8_t textMsgIndex = framesetInfo.positions.textMessage;
|
||||
|
||||
if (lastFrameIndex != -1 && currentFrameIndex != lastFrameIndex) {
|
||||
|
||||
if (lastFrameIndex == textMsgIndex && currentFrameIndex != textMsgIndex) {
|
||||
graphics::MessageRenderer::clearMessageCache();
|
||||
}
|
||||
}
|
||||
|
||||
lastFrameIndex = currentFrameIndex;
|
||||
}
|
||||
|
||||
menuHandler::handleMenuSwitch(dispdev);
|
||||
|
||||
// Show boot screen for first logo_timeout seconds, then switch to normal operation.
|
||||
@@ -828,17 +833,17 @@ int32_t Screen::runOnce()
|
||||
break;
|
||||
case Cmd::ON_PRESS:
|
||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||
handleOnPress();
|
||||
showFrame(FrameDirection::NEXT);
|
||||
}
|
||||
break;
|
||||
case Cmd::SHOW_PREV_FRAME:
|
||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||
handleShowPrevFrame();
|
||||
showFrame(FrameDirection::PREVIOUS);
|
||||
}
|
||||
break;
|
||||
case Cmd::SHOW_NEXT_FRAME:
|
||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||
handleShowNextFrame();
|
||||
showFrame(FrameDirection::NEXT);
|
||||
}
|
||||
break;
|
||||
case Cmd::START_ALERT_FRAME: {
|
||||
@@ -859,6 +864,7 @@ int32_t Screen::runOnce()
|
||||
break;
|
||||
case Cmd::STOP_ALERT_FRAME:
|
||||
NotificationRenderer::pauseBanner = false;
|
||||
break;
|
||||
case Cmd::STOP_BOOT_SCREEN:
|
||||
EINK_ADD_FRAMEFLAG(dispdev, COSMETIC); // E-Ink: Explicitly use full-refresh for next frame
|
||||
if (NotificationRenderer::current_notification_type != notificationTypeEnum::text_input) {
|
||||
@@ -1029,9 +1035,6 @@ void Screen::setFrames(FrameFocus focus)
|
||||
}
|
||||
#endif
|
||||
|
||||
// Declare this early so it’s available in FOCUS_PRESERVE block
|
||||
bool willInsertTextMessage = shouldDrawMessage(&devicestate.rx_text_message);
|
||||
|
||||
if (!hiddenFrames.home) {
|
||||
fsi.positions.home = numframes;
|
||||
normalFrames[numframes++] = graphics::UIRenderer::drawDeviceFocused;
|
||||
@@ -1043,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
|
||||
@@ -1069,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;
|
||||
@@ -1173,7 +1183,7 @@ void Screen::setFrames(FrameFocus focus)
|
||||
}
|
||||
|
||||
fsi.frameCount = numframes; // Total framecount is used to apply FOCUS_PRESERVE
|
||||
this->frameCount = numframes; // ✅ Save frame count for use in custom overlay
|
||||
this->frameCount = numframes; // Save frame count for use in custom overlay
|
||||
LOG_DEBUG("Finished build frames. numframes: %d", numframes);
|
||||
|
||||
ui->setFrames(normalFrames, numframes);
|
||||
@@ -1193,10 +1203,6 @@ void Screen::setFrames(FrameFocus focus)
|
||||
case FOCUS_FAULT:
|
||||
ui->switchToFrame(fsi.positions.fault);
|
||||
break;
|
||||
case FOCUS_TEXTMESSAGE:
|
||||
hasUnreadMessage = false; // ✅ Clear when message is *viewed*
|
||||
ui->switchToFrame(fsi.positions.textMessage);
|
||||
break;
|
||||
case FOCUS_MODULE:
|
||||
// Whichever frame was marked by MeshModule::requestFocus(), if any
|
||||
// If no module requested focus, will show the first frame instead
|
||||
@@ -1239,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
|
||||
@@ -1255,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;
|
||||
}
|
||||
@@ -1279,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")
|
||||
@@ -1291,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
|
||||
@@ -1308,37 +1323,6 @@ bool Screen::isFrameHidden(const std::string &frameName) const
|
||||
return false;
|
||||
}
|
||||
|
||||
// Dismisses the currently displayed screen frame, if possible
|
||||
// Relevant for text message, waypoint, others in future?
|
||||
// Triggered with a CardKB keycombo
|
||||
void Screen::hideCurrentFrame()
|
||||
{
|
||||
uint8_t currentFrame = ui->getUiState()->currentFrame;
|
||||
bool dismissed = false;
|
||||
if (currentFrame == framesetInfo.positions.textMessage && devicestate.has_rx_text_message) {
|
||||
LOG_INFO("Hide Text Message");
|
||||
devicestate.has_rx_text_message = false;
|
||||
memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message));
|
||||
} else if (currentFrame == framesetInfo.positions.waypoint && devicestate.has_rx_waypoint) {
|
||||
LOG_DEBUG("Hide Waypoint");
|
||||
devicestate.has_rx_waypoint = false;
|
||||
hiddenFrames.waypoint = true;
|
||||
dismissed = true;
|
||||
} else if (currentFrame == framesetInfo.positions.wifi) {
|
||||
LOG_DEBUG("Hide WiFi Screen");
|
||||
hiddenFrames.wifi = true;
|
||||
dismissed = true;
|
||||
} else if (currentFrame == framesetInfo.positions.lora) {
|
||||
LOG_INFO("Hide LoRa");
|
||||
hiddenFrames.lora = true;
|
||||
dismissed = true;
|
||||
}
|
||||
|
||||
if (dismissed) {
|
||||
setFrames(FOCUS_DEFAULT); // You could also use FOCUS_PRESERVE
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::handleStartFirmwareUpdateScreen()
|
||||
{
|
||||
LOG_DEBUG("Show firmware screen");
|
||||
@@ -1391,28 +1375,6 @@ void Screen::decreaseBrightness()
|
||||
/* TO DO: add little popup in center of screen saying what brightness level it is set to*/
|
||||
}
|
||||
|
||||
void Screen::setFunctionSymbol(std::string sym)
|
||||
{
|
||||
if (std::find(functionSymbol.begin(), functionSymbol.end(), sym) == functionSymbol.end()) {
|
||||
functionSymbol.push_back(sym);
|
||||
functionSymbolString = "";
|
||||
for (auto symbol : functionSymbol) {
|
||||
functionSymbolString = symbol + " " + functionSymbolString;
|
||||
}
|
||||
setFastFramerate();
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::removeFunctionSymbol(std::string sym)
|
||||
{
|
||||
functionSymbol.erase(std::remove(functionSymbol.begin(), functionSymbol.end(), sym), functionSymbol.end());
|
||||
functionSymbolString = "";
|
||||
for (auto symbol : functionSymbol) {
|
||||
functionSymbolString = symbol + " " + functionSymbolString;
|
||||
}
|
||||
setFastFramerate();
|
||||
}
|
||||
|
||||
void Screen::handleOnPress()
|
||||
{
|
||||
// If screen was off, just wake it, otherwise advance to next frame
|
||||
@@ -1424,23 +1386,17 @@ void Screen::handleOnPress()
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::handleShowPrevFrame()
|
||||
void Screen::showFrame(FrameDirection direction)
|
||||
{
|
||||
// If screen was off, just wake it, otherwise go back to previous frame
|
||||
// If we are in a transition, the press must have bounced, drop it.
|
||||
// Only advance frames when UI is stable
|
||||
if (ui->getUiState()->frameState == FIXED) {
|
||||
ui->previousFrame();
|
||||
lastScreenTransition = millis();
|
||||
setFastFramerate();
|
||||
}
|
||||
}
|
||||
|
||||
void Screen::handleShowNextFrame()
|
||||
{
|
||||
// If screen was off, just wake it, otherwise advance to next frame
|
||||
// If we are in a transition, the press must have bounced, drop it.
|
||||
if (ui->getUiState()->frameState == FIXED) {
|
||||
ui->nextFrame();
|
||||
if (direction == FrameDirection::NEXT) {
|
||||
ui->nextFrame();
|
||||
} else {
|
||||
ui->previousFrame();
|
||||
}
|
||||
|
||||
lastScreenTransition = millis();
|
||||
setFastFramerate();
|
||||
}
|
||||
@@ -1466,7 +1422,6 @@ void Screen::setFastFramerate()
|
||||
|
||||
int Screen::handleStatusUpdate(const meshtastic::Status *arg)
|
||||
{
|
||||
// LOG_DEBUG("Screen got status update %d", arg->getStatusType());
|
||||
switch (arg->getStatusType()) {
|
||||
case STATUS_TYPE_NODE:
|
||||
if (showingNormalScreen && nodeStatus->getLastNumTotal() != nodeStatus->getNumTotal()) {
|
||||
@@ -1584,11 +1539,11 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet)
|
||||
screen->showSimpleBanner(banner, 3000);
|
||||
} else if (!channel.settings.has_module_settings || !channel.settings.module_settings.is_muted) {
|
||||
if (longName && longName[0]) {
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
strcpy(banner, "New Message");
|
||||
#else
|
||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||
#endif
|
||||
if (currentResolution == ScreenResolution::UltraLow) {
|
||||
strcpy(banner, "New Message");
|
||||
} else {
|
||||
snprintf(banner, sizeof(banner), "New Message from\n%s", longName);
|
||||
}
|
||||
} else {
|
||||
strcpy(banner, "New Message");
|
||||
}
|
||||
@@ -1624,16 +1579,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;
|
||||
@@ -1671,7 +1636,48 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
menuHandler::handleMenuSwitch(dispdev);
|
||||
return 0;
|
||||
}
|
||||
// UP/DOWN in message screen scrolls through message threads
|
||||
if (ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) {
|
||||
|
||||
if (event->inputEvent == INPUT_BROKER_UP) {
|
||||
if (messageStore.getMessages().empty()) {
|
||||
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
||||
} else {
|
||||
graphics::MessageRenderer::scrollUp();
|
||||
setFastFramerate(); // match existing behavior
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (event->inputEvent == INPUT_BROKER_DOWN) {
|
||||
if (messageStore.getMessages().empty()) {
|
||||
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
||||
} else {
|
||||
graphics::MessageRenderer::scrollDown();
|
||||
setFastFramerate();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// UP/DOWN in node list screens scrolls through node pages
|
||||
if (ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_nodes ||
|
||||
ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_location ||
|
||||
ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_lastheard ||
|
||||
ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_hopsignal ||
|
||||
ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_distance ||
|
||||
ui->getUiState()->currentFrame == framesetInfo.positions.nodelist_bearings) {
|
||||
if (event->inputEvent == INPUT_BROKER_UP) {
|
||||
graphics::NodeListRenderer::scrollUp();
|
||||
setFastFramerate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (event->inputEvent == INPUT_BROKER_DOWN) {
|
||||
graphics::NodeListRenderer::scrollDown();
|
||||
setFastFramerate();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
// 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
|
||||
if (showingNormalScreen) {
|
||||
@@ -1685,16 +1691,39 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
|
||||
// If no modules are using the input, move between frames
|
||||
if (!inputIntercepted) {
|
||||
#if defined(INPUTDRIVER_ENCODER_TYPE) && INPUTDRIVER_ENCODER_TYPE == 2
|
||||
bool handledEncoderScroll = false;
|
||||
const bool isTextMessageFrame = (framesetInfo.positions.textMessage != 255 &&
|
||||
this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage &&
|
||||
!messageStore.getMessages().empty());
|
||||
if (isTextMessageFrame) {
|
||||
if (event->inputEvent == INPUT_BROKER_UP_LONG) {
|
||||
graphics::MessageRenderer::nudgeScroll(-1);
|
||||
handledEncoderScroll = true;
|
||||
} else if (event->inputEvent == INPUT_BROKER_DOWN_LONG) {
|
||||
graphics::MessageRenderer::nudgeScroll(1);
|
||||
handledEncoderScroll = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (handledEncoderScroll) {
|
||||
setFastFramerate();
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
if (event->inputEvent == INPUT_BROKER_LEFT || event->inputEvent == INPUT_BROKER_ALT_PRESS) {
|
||||
showPrevFrame();
|
||||
showFrame(FrameDirection::PREVIOUS);
|
||||
} else if (event->inputEvent == INPUT_BROKER_RIGHT || event->inputEvent == INPUT_BROKER_USER_PRESS) {
|
||||
showNextFrame();
|
||||
showFrame(FrameDirection::NEXT);
|
||||
} else if (event->inputEvent == INPUT_BROKER_UP_LONG) {
|
||||
// Long press up button for fast frame switching
|
||||
showPrevFrame();
|
||||
} else if (event->inputEvent == INPUT_BROKER_DOWN_LONG) {
|
||||
// Long press down button for fast frame switching
|
||||
showNextFrame();
|
||||
} else if ((event->inputEvent == INPUT_BROKER_UP || event->inputEvent == INPUT_BROKER_DOWN) &&
|
||||
this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
||||
cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST);
|
||||
} else if (event->inputEvent == INPUT_BROKER_SELECT) {
|
||||
if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) {
|
||||
menuHandler::homeBaseMenu();
|
||||
@@ -1709,20 +1738,21 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) {
|
||||
menuHandler::loraMenu();
|
||||
} else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) {
|
||||
if (devicestate.rx_text_message.from) {
|
||||
if (!messageStore.getMessages().empty()) {
|
||||
menuHandler::messageResponseMenu();
|
||||
} else {
|
||||
#if defined(M5STACK_UNITC6L)
|
||||
menuHandler::textMessageMenu();
|
||||
#else
|
||||
menuHandler::textMessageBaseMenu();
|
||||
#endif
|
||||
if (currentResolution == ScreenResolution::UltraLow) {
|
||||
menuHandler::textMessageMenu();
|
||||
} else {
|
||||
menuHandler::textMessageBaseMenu();
|
||||
}
|
||||
}
|
||||
} else if (framesetInfo.positions.firstFavorite != 255 &&
|
||||
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 ||
|
||||
@@ -1733,7 +1763,7 @@ int Screen::handleInputEvent(const InputEvent *event)
|
||||
menuHandler::wifiBaseMenu();
|
||||
}
|
||||
} else if (event->inputEvent == INPUT_BROKER_BACK) {
|
||||
showPrevFrame();
|
||||
showFrame(FrameDirection::PREVIOUS);
|
||||
} else if (event->inputEvent == INPUT_BROKER_CANCEL) {
|
||||
setOn(false);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user