kopia lustrzana https://github.com/meshtastic/firmware
				
				
				
			BaseUI Show/Hide Frame Functionality (#7382)
* Rename System Frame (from Memory) in code base * Create menu options to Show/Hide frames: Node Lists, Bearings, Position, LoRa, Clock and Favorites frames * Move Region Picker into submenu * Tweak wording for Send Position vs Node Info if the device has GPSpull/8033/head
							rodzic
							
								
									c73fe85ec8
								
							
						
					
					
						commit
						953fcca304
					
				|  | @ -901,75 +901,90 @@ void Screen::setFrames(FrameFocus focus) | |||
|     } | ||||
| 
 | ||||
| #if defined(DISPLAY_CLOCK_FRAME) | ||||
|     fsi.positions.clock = numframes; | ||||
|     if (!hiddenFrames.clock) { | ||||
|         fsi.positions.clock = numframes; | ||||
| #if defined(M5STACK_UNITC6L) | ||||
|     normalFrames[numframes++] = graphics::ClockRenderer::drawAnalogClockFrame; | ||||
|         normalFrames[numframes++] = graphics::ClockRenderer::drawAnalogClockFrame; | ||||
| #else | ||||
|     normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame | ||||
|                                                              : graphics::ClockRenderer::drawDigitalClockFrame; | ||||
|         normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame | ||||
|                                                                  : graphics::ClockRenderer::drawDigitalClockFrame; | ||||
| #endif | ||||
|     indicatorIcons.push_back(digital_icon_clock); | ||||
|         indicatorIcons.push_back(digital_icon_clock); | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
|     // Declare this early so it’s available in FOCUS_PRESERVE block
 | ||||
|     bool willInsertTextMessage = shouldDrawMessage(&devicestate.rx_text_message); | ||||
| 
 | ||||
|     fsi.positions.home = numframes; | ||||
|     normalFrames[numframes++] = graphics::UIRenderer::drawDeviceFocused; | ||||
|     indicatorIcons.push_back(icon_home); | ||||
|     if (!hiddenFrames.home) { | ||||
|         fsi.positions.home = numframes; | ||||
|         normalFrames[numframes++] = graphics::UIRenderer::drawDeviceFocused; | ||||
|         indicatorIcons.push_back(icon_home); | ||||
|     } | ||||
| 
 | ||||
|     fsi.positions.textMessage = numframes; | ||||
|     normalFrames[numframes++] = graphics::MessageRenderer::drawTextMessageFrame; | ||||
|     indicatorIcons.push_back(icon_mail); | ||||
| 
 | ||||
| #ifndef USE_EINK | ||||
|     fsi.positions.nodelist = numframes; | ||||
|     normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicNodeListScreen; | ||||
|     indicatorIcons.push_back(icon_nodes); | ||||
|     if (!hiddenFrames.nodelist) { | ||||
|         fsi.positions.nodelist = numframes; | ||||
|         normalFrames[numframes++] = graphics::NodeListRenderer::drawDynamicNodeListScreen; | ||||
|         indicatorIcons.push_back(icon_nodes); | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
| // Show detailed node views only on E-Ink builds
 | ||||
| #ifdef USE_EINK | ||||
|     fsi.positions.nodelist_lastheard = numframes; | ||||
|     normalFrames[numframes++] = graphics::NodeListRenderer::drawLastHeardScreen; | ||||
|     indicatorIcons.push_back(icon_nodes); | ||||
| 
 | ||||
|     fsi.positions.nodelist_hopsignal = numframes; | ||||
|     normalFrames[numframes++] = graphics::NodeListRenderer::drawHopSignalScreen; | ||||
|     indicatorIcons.push_back(icon_signal); | ||||
| 
 | ||||
|     fsi.positions.nodelist_distance = numframes; | ||||
|     normalFrames[numframes++] = graphics::NodeListRenderer::drawDistanceScreen; | ||||
|     indicatorIcons.push_back(icon_distance); | ||||
|     if (!hiddenFrames.nodelist_lastheard) { | ||||
|         fsi.positions.nodelist_lastheard = numframes; | ||||
|         normalFrames[numframes++] = graphics::NodeListRenderer::drawLastHeardScreen; | ||||
|         indicatorIcons.push_back(icon_nodes); | ||||
|     } | ||||
|     if (!hiddenFrames.nodelist_hopsignal) { | ||||
|         fsi.positions.nodelist_hopsignal = numframes; | ||||
|         normalFrames[numframes++] = graphics::NodeListRenderer::drawHopSignalScreen; | ||||
|         indicatorIcons.push_back(icon_signal); | ||||
|     } | ||||
|     if (!hiddenFrames.nodelist_distance) { | ||||
|         fsi.positions.nodelist_distance = numframes; | ||||
|         normalFrames[numframes++] = graphics::NodeListRenderer::drawDistanceScreen; | ||||
|         indicatorIcons.push_back(icon_distance); | ||||
|     } | ||||
| #endif | ||||
| #if HAS_GPS | ||||
|     fsi.positions.nodelist_bearings = numframes; | ||||
|     normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses; | ||||
|     indicatorIcons.push_back(icon_list); | ||||
| 
 | ||||
|     fsi.positions.gps = numframes; | ||||
|     normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen; | ||||
|     indicatorIcons.push_back(icon_compass); | ||||
|     if (!hiddenFrames.nodelist_bearings) { | ||||
|         fsi.positions.nodelist_bearings = numframes; | ||||
|         normalFrames[numframes++] = graphics::NodeListRenderer::drawNodeListWithCompasses; | ||||
|         indicatorIcons.push_back(icon_list); | ||||
|     } | ||||
|     if (!hiddenFrames.gps) { | ||||
|         fsi.positions.gps = numframes; | ||||
|         normalFrames[numframes++] = graphics::UIRenderer::drawCompassAndLocationScreen; | ||||
|         indicatorIcons.push_back(icon_compass); | ||||
|     } | ||||
| #endif | ||||
|     if (RadioLibInterface::instance) { | ||||
|     if (RadioLibInterface::instance && !hiddenFrames.lora) { | ||||
|         fsi.positions.lora = numframes; | ||||
|         normalFrames[numframes++] = graphics::DebugRenderer::drawLoRaFocused; | ||||
|         indicatorIcons.push_back(icon_radio); | ||||
|     } | ||||
|     if (!dismissedFrames.memory) { | ||||
|         fsi.positions.memory = numframes; | ||||
|         normalFrames[numframes++] = graphics::DebugRenderer::drawMemoryUsage; | ||||
|         indicatorIcons.push_back(icon_memory); | ||||
|     if (!hiddenFrames.system) { | ||||
|         fsi.positions.system = numframes; | ||||
|         normalFrames[numframes++] = graphics::DebugRenderer::drawSystemScreen; | ||||
|         indicatorIcons.push_back(icon_system); | ||||
|     } | ||||
| #if !defined(DISPLAY_CLOCK_FRAME) | ||||
|     fsi.positions.clock = numframes; | ||||
|     normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame | ||||
|                                                              : graphics::ClockRenderer::drawDigitalClockFrame; | ||||
|     indicatorIcons.push_back(digital_icon_clock); | ||||
|     if (!hiddenFrames.clock) { | ||||
|         fsi.positions.clock = numframes; | ||||
|         normalFrames[numframes++] = uiconfig.is_clockface_analog ? graphics::ClockRenderer::drawAnalogClockFrame | ||||
|                                                                  : graphics::ClockRenderer::drawDigitalClockFrame; | ||||
|         indicatorIcons.push_back(digital_icon_clock); | ||||
|     } | ||||
| #endif | ||||
| 
 | ||||
| #if HAS_WIFI && !defined(ARCH_PORTDUINO) | ||||
|     if (!dismissedFrames.wifi && isWifiAvailable()) { | ||||
|     if (!hiddenFrames.wifi && isWifiAvailable()) { | ||||
|         fsi.positions.wifi = numframes; | ||||
|         normalFrames[numframes++] = graphics::DebugRenderer::drawDebugInfoWiFiTrampoline; | ||||
|         indicatorIcons.push_back(icon_wifi); | ||||
|  | @ -1011,27 +1026,29 @@ void Screen::setFrames(FrameFocus focus) | |||
|     if (numMeshNodes > 0) | ||||
|         numMeshNodes--; | ||||
| 
 | ||||
|     // Temporary array to hold favorite node frames
 | ||||
|     std::vector<FrameCallback> favoriteFrames; | ||||
|     if (!hiddenFrames.show_favorites) { | ||||
|         // Temporary array to hold favorite node frames
 | ||||
|         std::vector<FrameCallback> favoriteFrames; | ||||
| 
 | ||||
|     for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { | ||||
|         const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i); | ||||
|         if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) { | ||||
|             favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo); | ||||
|         for (size_t i = 0; i < nodeDB->getNumMeshNodes(); i++) { | ||||
|             const meshtastic_NodeInfoLite *n = nodeDB->getMeshNodeByIndex(i); | ||||
|             if (n && n->num != nodeDB->getNodeNum() && n->is_favorite) { | ||||
|                 favoriteFrames.push_back(graphics::UIRenderer::drawNodeInfo); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // Insert favorite frames *after* collecting them all
 | ||||
|     if (!favoriteFrames.empty()) { | ||||
|         fsi.positions.firstFavorite = numframes; | ||||
|         for (const auto &f : favoriteFrames) { | ||||
|             normalFrames[numframes++] = f; | ||||
|             indicatorIcons.push_back(icon_node); | ||||
|         // Insert favorite frames *after* collecting them all
 | ||||
|         if (!favoriteFrames.empty()) { | ||||
|             fsi.positions.firstFavorite = numframes; | ||||
|             for (const auto &f : favoriteFrames) { | ||||
|                 normalFrames[numframes++] = f; | ||||
|                 indicatorIcons.push_back(icon_node); | ||||
|             } | ||||
|             fsi.positions.lastFavorite = numframes - 1; | ||||
|         } else { | ||||
|             fsi.positions.firstFavorite = 255; | ||||
|             fsi.positions.lastFavorite = 255; | ||||
|         } | ||||
|         fsi.positions.lastFavorite = numframes - 1; | ||||
|     } else { | ||||
|         fsi.positions.firstFavorite = 255; | ||||
|         fsi.positions.lastFavorite = 255; | ||||
|     } | ||||
| 
 | ||||
|     fsi.frameCount = numframes;   // Total framecount is used to apply FOCUS_PRESERVE
 | ||||
|  | @ -1070,7 +1087,7 @@ void Screen::setFrames(FrameFocus focus) | |||
|         ui->switchToFrame(fsi.positions.clock); | ||||
|         break; | ||||
|     case FOCUS_SYSTEM: | ||||
|         ui->switchToFrame(fsi.positions.memory); | ||||
|         ui->switchToFrame(fsi.positions.system); | ||||
|         break; | ||||
| 
 | ||||
|     case FOCUS_PRESERVE: | ||||
|  | @ -1098,30 +1115,96 @@ void Screen::setFrameImmediateDraw(FrameCallback *drawFrames) | |||
|     setFastFramerate(); | ||||
| } | ||||
| 
 | ||||
| void Screen::toggleFrameVisibility(const std::string &frameName) | ||||
| { | ||||
| #ifndef USE_EINK | ||||
|     if (frameName == "nodelist") { | ||||
|         hiddenFrames.nodelist = !hiddenFrames.nodelist; | ||||
|     } | ||||
| #endif | ||||
| #ifdef USE_EINK | ||||
|     if (frameName == "nodelist_lastheard") { | ||||
|         hiddenFrames.nodelist_lastheard = !hiddenFrames.nodelist_lastheard; | ||||
|     } | ||||
|     if (frameName == "nodelist_hopsignal") { | ||||
|         hiddenFrames.nodelist_hopsignal = !hiddenFrames.nodelist_hopsignal; | ||||
|     } | ||||
|     if (frameName == "nodelist_distance") { | ||||
|         hiddenFrames.nodelist_distance = !hiddenFrames.nodelist_distance; | ||||
|     } | ||||
| #endif | ||||
| #if HAS_GPS | ||||
|     if (frameName == "nodelist_bearings") { | ||||
|         hiddenFrames.nodelist_bearings = !hiddenFrames.nodelist_bearings; | ||||
|     } | ||||
|     if (frameName == "gps") { | ||||
|         hiddenFrames.gps = !hiddenFrames.gps; | ||||
|     } | ||||
| #endif | ||||
|     if (frameName == "lora") { | ||||
|         hiddenFrames.lora = !hiddenFrames.lora; | ||||
|     } | ||||
|     if (frameName == "clock") { | ||||
|         hiddenFrames.clock = !hiddenFrames.clock; | ||||
|     } | ||||
|     if (frameName == "show_favorites") { | ||||
|         hiddenFrames.show_favorites = !hiddenFrames.show_favorites; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool Screen::isFrameHidden(const std::string &frameName) const | ||||
| { | ||||
| #ifndef USE_EINK | ||||
|     if (frameName == "nodelist") | ||||
|         return hiddenFrames.nodelist; | ||||
| #endif | ||||
| #ifdef USE_EINK | ||||
|     if (frameName == "nodelist_lastheard") | ||||
|         return hiddenFrames.nodelist_lastheard; | ||||
|     if (frameName == "nodelist_hopsignal") | ||||
|         return hiddenFrames.nodelist_hopsignal; | ||||
|     if (frameName == "nodelist_distance") | ||||
|         return hiddenFrames.nodelist_distance; | ||||
| #endif | ||||
| #if HAS_GPS | ||||
|     if (frameName == "nodelist_bearings") | ||||
|         return hiddenFrames.nodelist_bearings; | ||||
|     if (frameName == "gps") | ||||
|         return hiddenFrames.gps; | ||||
| #endif | ||||
|     if (frameName == "lora") | ||||
|         return hiddenFrames.lora; | ||||
|     if (frameName == "clock") | ||||
|         return hiddenFrames.clock; | ||||
|     if (frameName == "show_favorites") | ||||
|         return hiddenFrames.show_favorites; | ||||
| 
 | ||||
|     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::dismissCurrentFrame() | ||||
| void Screen::hideCurrentFrame() | ||||
| { | ||||
|     uint8_t currentFrame = ui->getUiState()->currentFrame; | ||||
|     bool dismissed = false; | ||||
| 
 | ||||
|     if (currentFrame == framesetInfo.positions.textMessage && devicestate.has_rx_text_message) { | ||||
|         LOG_INFO("Dismiss 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("Dismiss Waypoint"); | ||||
|         LOG_DEBUG("Hide Waypoint"); | ||||
|         devicestate.has_rx_waypoint = false; | ||||
|         dismissedFrames.waypoint = true; | ||||
|         hiddenFrames.waypoint = true; | ||||
|         dismissed = true; | ||||
|     } else if (currentFrame == framesetInfo.positions.wifi) { | ||||
|         LOG_DEBUG("Dismiss WiFi Screen"); | ||||
|         dismissedFrames.wifi = true; | ||||
|         LOG_DEBUG("Hide WiFi Screen"); | ||||
|         hiddenFrames.wifi = true; | ||||
|         dismissed = true; | ||||
|     } else if (currentFrame == framesetInfo.positions.memory) { | ||||
|         LOG_INFO("Dismiss Memory"); | ||||
|         dismissedFrames.memory = true; | ||||
|     } else if (currentFrame == framesetInfo.positions.lora) { | ||||
|         LOG_INFO("Hide LoRa"); | ||||
|         hiddenFrames.lora = true; | ||||
|         dismissed = true; | ||||
|     } | ||||
| 
 | ||||
|  | @ -1277,7 +1360,7 @@ int Screen::handleTextMessage(const meshtastic_MeshPacket *packet) | |||
|             // Outgoing message (likely sent from phone)
 | ||||
|             devicestate.has_rx_text_message = false; | ||||
|             memset(&devicestate.rx_text_message, 0, sizeof(devicestate.rx_text_message)); | ||||
|             dismissedFrames.textMessage = true; | ||||
|             hiddenFrames.textMessage = true; | ||||
|             hasUnreadMessage = false; // Clear unread state when user replies
 | ||||
| 
 | ||||
|             setFrames(FOCUS_PRESERVE); // Stay on same frame, silently update frame list
 | ||||
|  | @ -1402,7 +1485,7 @@ int Screen::handleInputEvent(const InputEvent *event) | |||
|             } else if (event->inputEvent == INPUT_BROKER_SELECT) { | ||||
|                 if (this->ui->getUiState()->currentFrame == framesetInfo.positions.home) { | ||||
|                     menuHandler::homeBaseMenu(); | ||||
|                 } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.memory) { | ||||
|                 } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.system) { | ||||
|                     menuHandler::systemBaseMenu(); | ||||
| #if HAS_GPS | ||||
|                 } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.gps && gps) { | ||||
|  | @ -1411,7 +1494,7 @@ int Screen::handleInputEvent(const InputEvent *event) | |||
|                 } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.clock) { | ||||
|                     menuHandler::clockMenu(); | ||||
|                 } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.lora) { | ||||
|                     menuHandler::LoraRegionPicker(); | ||||
|                     menuHandler::loraMenu(); | ||||
|                 } else if (this->ui->getUiState()->currentFrame == framesetInfo.positions.textMessage) { | ||||
|                     if (devicestate.rx_text_message.from) { | ||||
|                         menuHandler::messageResponseMenu(); | ||||
|  |  | |||
|  | @ -593,7 +593,11 @@ class Screen : public concurrency::OSThread | |||
|     void setSSLFrames(); | ||||
| 
 | ||||
|     // Dismiss the currently focussed frame, if possible (e.g. text message, waypoint)
 | ||||
|     void dismissCurrentFrame(); | ||||
|     void hideCurrentFrame(); | ||||
| 
 | ||||
|     // Menu-driven Show / Hide Toggle
 | ||||
|     void toggleFrameVisibility(const std::string &frameName); | ||||
|     bool isFrameHidden(const std::string &frameName) const; | ||||
| 
 | ||||
| #ifdef USE_EINK | ||||
|     /// Draw an image to remain on E-Ink display after screen off
 | ||||
|  | @ -655,7 +659,7 @@ class Screen : public concurrency::OSThread | |||
|             uint8_t settings = 255; | ||||
|             uint8_t wifi = 255; | ||||
|             uint8_t deviceFocused = 255; | ||||
|             uint8_t memory = 255; | ||||
|             uint8_t system = 255; | ||||
|             uint8_t gps = 255; | ||||
|             uint8_t home = 255; | ||||
|             uint8_t textMessage = 255; | ||||
|  | @ -673,12 +677,28 @@ class Screen : public concurrency::OSThread | |||
|         uint8_t frameCount = 0; | ||||
|     } framesetInfo; | ||||
| 
 | ||||
|     struct DismissedFrames { | ||||
|     struct hiddenFrames { | ||||
|         bool textMessage = false; | ||||
|         bool waypoint = false; | ||||
|         bool wifi = false; | ||||
|         bool memory = false; | ||||
|     } dismissedFrames; | ||||
|         bool system = false; | ||||
|         bool home = false; | ||||
|         bool clock = false; | ||||
| #ifndef USE_EINK | ||||
|         bool nodelist = false; | ||||
| #endif | ||||
| #ifdef USE_EINK | ||||
|         bool nodelist_lastheard = false; | ||||
|         bool nodelist_hopsignal = false; | ||||
|         bool nodelist_distance = false; | ||||
| #endif | ||||
| #if HAS_GPS | ||||
|         bool nodelist_bearings = false; | ||||
|         bool gps = false; | ||||
| #endif | ||||
|         bool lora = false; | ||||
|         bool show_favorites = false; | ||||
|     } hiddenFrames; | ||||
| 
 | ||||
|     /// Try to start drawing ASAP
 | ||||
|     void setFastFramerate(); | ||||
|  |  | |||
|  | @ -499,7 +499,7 @@ void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, | |||
| // ****************************
 | ||||
| // *      System Screen       *
 | ||||
| // ****************************
 | ||||
| void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) | ||||
| void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y) | ||||
| { | ||||
|     display->clear(); | ||||
|     display->setFont(FONT_SMALL); | ||||
|  |  | |||
|  | @ -31,8 +31,8 @@ void drawDebugInfoWiFiTrampoline(OLEDDisplay *display, OLEDDisplayUiState *state | |||
| // LoRa information display
 | ||||
| void drawLoRaFocused(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); | ||||
| 
 | ||||
| // Memory screen display
 | ||||
| void drawMemoryUsage(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); | ||||
| // System screen display
 | ||||
| void drawSystemScreen(OLEDDisplay *display, OLEDDisplayUiState *state, int16_t x, int16_t y); | ||||
| } // namespace DebugRenderer
 | ||||
| 
 | ||||
| } // namespace graphics
 | ||||
|  |  | |||
|  | @ -26,6 +26,24 @@ menuHandler::screenMenus menuHandler::menuQueue = menu_none; | |||
| bool test_enabled = false; | ||||
| uint8_t test_count = 0; | ||||
| 
 | ||||
| void menuHandler::loraMenu() | ||||
| { | ||||
|     static const char *optionsArray[] = {"Back", "Region Picker"}; | ||||
|     enum optionsNumbers { Back = 0, lora_picker = 1 }; | ||||
|     BannerOverlayOptions bannerOptions; | ||||
|     bannerOptions.message = "LoRa Actions"; | ||||
|     bannerOptions.optionsArrayPtr = optionsArray; | ||||
|     bannerOptions.optionsCount = 2; | ||||
|     bannerOptions.bannerCallback = [](int selected) -> void { | ||||
|         if (selected == Back) { | ||||
|             // No action
 | ||||
|         } else if (selected == lora_picker) { | ||||
|             menuHandler::menuQueue = menuHandler::lora_picker; | ||||
|         } | ||||
|     }; | ||||
|     screen->showOverlayBanner(bannerOptions); | ||||
| } | ||||
| 
 | ||||
| void menuHandler::OnboardMessage() | ||||
| { | ||||
|     static const char *optionsArray[] = {"OK", "Got it!"}; | ||||
|  | @ -320,7 +338,7 @@ void menuHandler::messageResponseMenu() | |||
|     bannerOptions.optionsCount = options; | ||||
|     bannerOptions.bannerCallback = [](int selected) -> void { | ||||
|         if (selected == Dismiss) { | ||||
|             screen->dismissCurrentFrame(); | ||||
|             screen->hideCurrentFrame(); | ||||
|         } else if (selected == Preset) { | ||||
|             if (devicestate.rx_text_message.to == NODENUM_BROADCAST) { | ||||
|                 cannedMessageModule->LaunchWithDestination(NODENUM_BROADCAST, devicestate.rx_text_message.channel); | ||||
|  | @ -361,8 +379,11 @@ void menuHandler::homeBaseMenu() | |||
|     optionsArray[options] = "Sleep Screen"; | ||||
|     optionsEnumArray[options++] = Sleep; | ||||
| #endif | ||||
| 
 | ||||
|     optionsArray[options] = "Send Position"; | ||||
|     if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) { | ||||
|         optionsArray[options] = "Send Position"; | ||||
|     } else { | ||||
|         optionsArray[options] = "Send Node Info"; | ||||
|     } | ||||
|     optionsEnumArray[options++] = Position; | ||||
| #if defined(M5STACK_UNITC6L) | ||||
|     optionsArray[options] = "New Preset"; | ||||
|  | @ -455,7 +476,7 @@ void menuHandler::textMessageBaseMenu() | |||
| 
 | ||||
| void menuHandler::systemBaseMenu() | ||||
| { | ||||
|     enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, Test, enumEnd }; | ||||
|     enum optionsNumbers { Back, Notifications, ScreenOptions, Bluetooth, PowerMenu, FrameToggles, Test, enumEnd }; | ||||
|     static const char *optionsArray[enumEnd] = {"Back"}; | ||||
|     static int optionsEnumArray[enumEnd] = {Back}; | ||||
|     int options = 1; | ||||
|  | @ -467,6 +488,9 @@ void menuHandler::systemBaseMenu() | |||
|     optionsArray[options] = "Screen Options"; | ||||
|     optionsEnumArray[options++] = ScreenOptions; | ||||
| #endif | ||||
| 
 | ||||
|     optionsArray[options] = "Frame Visiblity Toggle"; | ||||
|     optionsEnumArray[options++] = FrameToggles; | ||||
| #if defined(M5STACK_UNITC6L) | ||||
|     optionsArray[options] = "Bluetooth"; | ||||
| #else | ||||
|  | @ -504,6 +528,9 @@ void menuHandler::systemBaseMenu() | |||
|         } else if (selected == PowerMenu) { | ||||
|             menuHandler::menuQueue = menuHandler::power_menu; | ||||
|             screen->runNow(); | ||||
|         } else if (selected == FrameToggles) { | ||||
|             menuHandler::menuQueue = menuHandler::FrameToggles; | ||||
|             screen->runNow(); | ||||
|         } else if (selected == Test) { | ||||
|             menuHandler::menuQueue = menuHandler::test_menu; | ||||
|             screen->runNow(); | ||||
|  | @ -580,6 +607,7 @@ void menuHandler::positionBaseMenu() | |||
|         optionsArray[options] = "Compass Calibrate"; | ||||
|         optionsEnumArray[options++] = CompassCalibrate; | ||||
|     } | ||||
| 
 | ||||
|     BannerOverlayOptions bannerOptions; | ||||
|     bannerOptions.message = "Position Action"; | ||||
|     bannerOptions.optionsArrayPtr = optionsArray; | ||||
|  | @ -1220,6 +1248,116 @@ void menuHandler::keyVerificationFinalPrompt() | |||
|     } | ||||
| } | ||||
| 
 | ||||
| void menuHandler::FrameToggles_menu() | ||||
| { | ||||
|     enum optionsNumbers { | ||||
|         Finish, | ||||
|         nodelist, | ||||
|         nodelist_lastheard, | ||||
|         nodelist_hopsignal, | ||||
|         nodelist_distance, | ||||
|         nodelist_bearings, | ||||
|         gps, | ||||
|         lora, | ||||
|         clock, | ||||
|         show_favorites, | ||||
|         enumEnd | ||||
|     }; | ||||
|     static const char *optionsArray[enumEnd] = {"Finish"}; | ||||
|     static int optionsEnumArray[enumEnd] = {Finish}; | ||||
|     int options = 1; | ||||
| 
 | ||||
|     // Track last selected index (not enum value!)
 | ||||
|     static int lastSelectedIndex = 0; | ||||
| 
 | ||||
| #ifndef USE_EINK | ||||
|     optionsArray[options] = screen->isFrameHidden("nodelist") ? "Show Node List" : "Hide Node List"; | ||||
|     optionsEnumArray[options++] = nodelist; | ||||
| #endif | ||||
| #ifdef USE_EINK | ||||
|     optionsArray[options] = screen->isFrameHidden("nodelist_lastheard") ? "Show NL - Last Heard" : "Hide NL - Last Heard"; | ||||
|     optionsEnumArray[options++] = nodelist_lastheard; | ||||
|     optionsArray[options] = screen->isFrameHidden("nodelist_hopsignal") ? "Show NL - Hops/Signal" : "Hide NL - Hops/Signal"; | ||||
|     optionsEnumArray[options++] = nodelist_hopsignal; | ||||
|     optionsArray[options] = screen->isFrameHidden("nodelist_distance") ? "Show NL - Distance" : "Hide NL - Distance"; | ||||
|     optionsEnumArray[options++] = nodelist_distance; | ||||
| #endif | ||||
| #if HAS_GPS | ||||
|     optionsArray[options] = screen->isFrameHidden("nodelist_bearings") ? "Show Bearings" : "Hide Bearings"; | ||||
|     optionsEnumArray[options++] = nodelist_bearings; | ||||
| 
 | ||||
|     optionsArray[options] = screen->isFrameHidden("gps") ? "Show Position" : "Hide Position"; | ||||
|     optionsEnumArray[options++] = gps; | ||||
| #endif | ||||
| 
 | ||||
|     optionsArray[options] = screen->isFrameHidden("lora") ? "Show LoRa" : "Hide LoRa"; | ||||
|     optionsEnumArray[options++] = lora; | ||||
| 
 | ||||
|     optionsArray[options] = screen->isFrameHidden("clock") ? "Show Clock" : "Hide Clock"; | ||||
|     optionsEnumArray[options++] = clock; | ||||
| 
 | ||||
|     optionsArray[options] = screen->isFrameHidden("show_favorites") ? "Show Favorites" : "Hide Favorites"; | ||||
|     optionsEnumArray[options++] = show_favorites; | ||||
| 
 | ||||
|     BannerOverlayOptions bannerOptions; | ||||
|     bannerOptions.message = "Show/Hide Frames"; | ||||
|     bannerOptions.optionsArrayPtr = optionsArray; | ||||
|     bannerOptions.optionsCount = options; | ||||
|     bannerOptions.optionsEnumPtr = optionsEnumArray; | ||||
|     bannerOptions.InitialSelected = lastSelectedIndex; // Use index, not enum value
 | ||||
| 
 | ||||
|     bannerOptions.bannerCallback = [optionsEnumArray, options](int selected) mutable -> void { | ||||
|         // Find the index of selected in optionsEnumArray
 | ||||
|         int idx = 0; | ||||
|         for (; idx < options; ++idx) { | ||||
|             if (optionsEnumArray[idx] == selected) | ||||
|                 break; | ||||
|         } | ||||
|         lastSelectedIndex = idx; | ||||
| 
 | ||||
|         if (selected == Finish) { | ||||
|             screen->setFrames(Screen::FOCUS_DEFAULT); | ||||
|         } else if (selected == nodelist) { | ||||
|             screen->toggleFrameVisibility("nodelist"); | ||||
|             menuHandler::menuQueue = menuHandler::FrameToggles; | ||||
|             screen->runNow(); | ||||
|         } else if (selected == nodelist_lastheard) { | ||||
|             screen->toggleFrameVisibility("nodelist_lastheard"); | ||||
|             menuHandler::menuQueue = menuHandler::FrameToggles; | ||||
|             screen->runNow(); | ||||
|         } else if (selected == nodelist_hopsignal) { | ||||
|             screen->toggleFrameVisibility("nodelist_hopsignal"); | ||||
|             menuHandler::menuQueue = menuHandler::FrameToggles; | ||||
|             screen->runNow(); | ||||
|         } else if (selected == nodelist_distance) { | ||||
|             screen->toggleFrameVisibility("nodelist_distance"); | ||||
|             menuHandler::menuQueue = menuHandler::FrameToggles; | ||||
|             screen->runNow(); | ||||
|         } else if (selected == nodelist_bearings) { | ||||
|             screen->toggleFrameVisibility("nodelist_bearings"); | ||||
|             menuHandler::menuQueue = menuHandler::FrameToggles; | ||||
|             screen->runNow(); | ||||
|         } else if (selected == gps) { | ||||
|             screen->toggleFrameVisibility("gps"); | ||||
|             menuHandler::menuQueue = menuHandler::FrameToggles; | ||||
|             screen->runNow(); | ||||
|         } else if (selected == lora) { | ||||
|             screen->toggleFrameVisibility("lora"); | ||||
|             menuHandler::menuQueue = menuHandler::FrameToggles; | ||||
|             screen->runNow(); | ||||
|         } else if (selected == clock) { | ||||
|             screen->toggleFrameVisibility("clock"); | ||||
|             menuHandler::menuQueue = menuHandler::FrameToggles; | ||||
|             screen->runNow(); | ||||
|         } else if (selected == show_favorites) { | ||||
|             screen->toggleFrameVisibility("show_favorites"); | ||||
|             menuHandler::menuQueue = menuHandler::FrameToggles; | ||||
|             screen->runNow(); | ||||
|         } | ||||
|     }; | ||||
|     screen->showOverlayBanner(bannerOptions); | ||||
| } | ||||
| 
 | ||||
| void menuHandler::handleMenuSwitch(OLEDDisplay *display) | ||||
| { | ||||
|     if (menuQueue != menu_none) | ||||
|  | @ -1316,6 +1454,9 @@ void menuHandler::handleMenuSwitch(OLEDDisplay *display) | |||
|     case power_menu: | ||||
|         powerMenu(); | ||||
|         break; | ||||
|     case FrameToggles: | ||||
|         FrameToggles_menu(); | ||||
|         break; | ||||
|     case throttle_message: | ||||
|         screen->showSimpleBanner("Too Many Attempts\nTry again in 60 seconds.", 5000); | ||||
|         break; | ||||
|  |  | |||
|  | @ -39,11 +39,13 @@ class menuHandler | |||
|         key_verification_final_prompt, | ||||
|         trace_route_menu, | ||||
|         throttle_message, | ||||
|         FrameToggles | ||||
|     }; | ||||
|     static screenMenus menuQueue; | ||||
| 
 | ||||
|     static void OnboardMessage(); | ||||
|     static void LoraRegionPicker(uint32_t duration = 30000); | ||||
|     static void loraMenu(); | ||||
|     static void handleMenuSwitch(OLEDDisplay *display); | ||||
|     static void showConfirmationBanner(const char *message, std::function<void()> onConfirm); | ||||
|     static void clockMenu(); | ||||
|  | @ -77,6 +79,7 @@ class menuHandler | |||
|     static void screenOptionsMenu(); | ||||
|     static void powerMenu(); | ||||
|     static void textMessageMenu(); | ||||
|     static void FrameToggles_menu(); | ||||
| 
 | ||||
|   private: | ||||
|     static void saveUIConfig(); | ||||
|  |  | |||
|  | @ -118,8 +118,8 @@ const uint8_t icon_radio[] PROGMEM = { | |||
|     0xA9  // Row 7: #..#.#.#
 | ||||
| }; | ||||
| 
 | ||||
| // 🪙 Memory Icon
 | ||||
| const uint8_t icon_memory[] PROGMEM = { | ||||
| // 🪙 System Icon
 | ||||
| const uint8_t icon_system[] PROGMEM = { | ||||
|     0x24, // Row 0: ..#..#..
 | ||||
|     0x3C, // Row 1: ..####..
 | ||||
|     0xC3, // Row 2: ##....##
 | ||||
|  |  | |||
		Ładowanie…
	
		Reference in New Issue
	
	 Jason P
						Jason P