From 9e40449c649c6b839ac59979a9970a640fd602ee Mon Sep 17 00:00:00 2001 From: Alvin Wong Date: Mon, 3 May 2021 18:23:30 +0800 Subject: [PATCH] Add menu exit transition animation Part of which was removed in commit 04d72cb. --- source/Core/Src/gui.cpp | 109 +++++++++++++++++++----------- source/Core/Threads/GUIThread.cpp | 15 +++- 2 files changed, 82 insertions(+), 42 deletions(-) diff --git a/source/Core/Src/gui.cpp b/source/Core/Src/gui.cpp index 63eb711f..a24187d3 100644 --- a/source/Core/Src/gui.cpp +++ b/source/Core/Src/gui.cpp @@ -1034,6 +1034,18 @@ static bool settings_enterAdvancedMenu(void) { void gui_Menu(const menuitem *menu) { // Draw the settings menu and provide iteration support etc + + // This is used to detect whether a menu-exit transition should be played. + static bool wasInGuiMenu; + wasInGuiMenu = true; + + enum class NavState { + Idle, + Entering, + ScrollingDown, + Exiting, + }; + uint8_t currentScreen = 0; TickType_t autoRepeatTimer = 0; TickType_t autoRepeatAcceleration = 0; @@ -1043,7 +1055,7 @@ void gui_Menu(const menuitem *menu) { uint8_t scrollContentSize = 0; bool scrollBlink = false; bool lastValue = false; - bool scrollingDown = false; + NavState navState = NavState::Entering; ScrollMessage scrollMessage; @@ -1051,42 +1063,58 @@ void gui_Menu(const menuitem *menu) { scrollContentSize += 1; } - // Animated menu opening. - if (menu[currentScreen].draw != nullptr) { - // This menu is drawn in a secondary framebuffer. - // Then we play a transition from the current primary - // framebuffer to the new buffer. - // The extra buffer is discarded at the end of the transition. - animOpenState = true; - OLED::useSecondaryFramebuffer(true); - OLED::setCursor(0, 0); - OLED::clearScreen(); - menu[currentScreen].draw(); - OLED::useSecondaryFramebuffer(false); - OLED::transitionSecondaryFramebuffer(true); - animOpenState = false; - } - while ((menu[currentScreen].draw != nullptr) && earlyExit == false) { - OLED::setCursor(0, 0); - if (scrollingDown) { + + // Handle menu transition: + if (navState != NavState::Idle) { + // Check if this menu item shall be skipped. If it shall be skipped, + // `draw()` returns true. Draw on the secondary framebuffer as we want + // to keep the primary framebuffer intact for the upcoming transition + // animation. + OLED::useSecondaryFramebuffer(true); + if (menu[currentScreen].draw()) { + currentScreen++; + OLED::useSecondaryFramebuffer(false); + continue; + } + animOpenState = true; + // The menu entering/exiting transition uses the secondary framebuffer, + // but the scroll down transition does not. + if (navState == NavState::ScrollingDown) { + OLED::useSecondaryFramebuffer(false); + } + OLED::setCursor(0, 0); + OLED::clearScreen(); + menu[currentScreen].draw(); + if (navState == NavState::ScrollingDown) { + // Play the scroll down animation. + OLED::maskScrollIndicatorOnOLED(); + OLED::transitionScrollDown(); + } else { + // The menu was drawn in a secondary framebuffer. + // Now we play a transition from the pre-drawn primary + // framebuffer to the new buffer. + // The extra buffer is discarded at the end of the transition. + OLED::useSecondaryFramebuffer(false); + OLED::transitionSecondaryFramebuffer(navState == NavState::Entering); + } + animOpenState = false; + navState = NavState::Idle; } // If the user has hesitated for >=3 seconds, show the long text // Otherwise "draw" the option if ((xTaskGetTickCount() - lastButtonTime < (TICKS_SECOND * 3)) || menu[currentScreen].description == 0) { lcdRefresh = true; + OLED::setCursor(0, 0); OLED::clearScreen(); - if (menu[currentScreen].draw()) { - currentScreen++; - lcdRefresh = false; - } + menu[currentScreen].draw(); uint8_t indicatorHeight = OLED_HEIGHT / scrollContentSize; uint8_t position = OLED_HEIGHT * currentScreen / scrollContentSize; if (lastValue) scrollBlink = !scrollBlink; - if ((!lastValue || !scrollBlink) && !scrollingDown) + if (!lastValue || !scrollBlink) OLED::drawScrollIndicator(position, indicatorHeight); } else { // Draw description @@ -1095,15 +1123,8 @@ void gui_Menu(const menuitem *menu) { } if (lcdRefresh) { - if (scrollingDown) { - OLED::maskScrollIndicatorOnOLED(); - OLED::transitionScrollDown(); - scrollingDown = false; - animOpenState = false; - } else { - OLED::refresh(); // update the LCD - osDelay(40); - } + OLED::refresh(); // update the LCD + osDelay(40); lcdRefresh = false; } @@ -1114,6 +1135,16 @@ void gui_Menu(const menuitem *menu) { lastButtonState = buttons; } + auto callIncrementHandler = [&]() { + wasInGuiMenu = false; + bool res = menu[currentScreen].incrementHandler(); + if (wasInGuiMenu) { + navState = NavState::Exiting; + } + wasInGuiMenu = true; + return res; + }; + switch (buttons) { case BUTTON_BOTH: earlyExit = true; // will make us exit next loop @@ -1123,7 +1154,7 @@ void gui_Menu(const menuitem *menu) { // increment if (scrollMessage.isReset()) { if (menu[currentScreen].incrementHandler != nullptr) { - lastValue = menu[currentScreen].incrementHandler(); + lastValue = callIncrementHandler(); } else { earlyExit = true; } @@ -1133,14 +1164,14 @@ void gui_Menu(const menuitem *menu) { case BUTTON_B_SHORT: if (scrollMessage.isReset()) { currentScreen++; - scrollingDown = true; - lastValue = false; + navState = NavState::ScrollingDown; + lastValue = false; } else scrollMessage.reset(); break; case BUTTON_F_LONG: if (xTaskGetTickCount() + autoRepeatAcceleration > autoRepeatTimer + PRESS_ACCEL_INTERVAL_MAX) { - if ((lastValue = menu[currentScreen].incrementHandler())) + if ((lastValue = callIncrementHandler())) autoRepeatTimer = 1000; else autoRepeatTimer = 0; @@ -1155,7 +1186,7 @@ void gui_Menu(const menuitem *menu) { case BUTTON_B_LONG: if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration > PRESS_ACCEL_INTERVAL_MAX) { currentScreen++; - scrollingDown = true; + navState = NavState::ScrollingDown; autoRepeatTimer = xTaskGetTickCount(); scrollMessage.reset(); @@ -1178,8 +1209,6 @@ void gui_Menu(const menuitem *menu) { scrollMessage.reset(); } } - - animOpenState = false; } void enterSettingsMenu() { diff --git a/source/Core/Threads/GUIThread.cpp b/source/Core/Threads/GUIThread.cpp index 8437c4dc..ee9c935a 100644 --- a/source/Core/Threads/GUIThread.cpp +++ b/source/Core/Threads/GUIThread.cpp @@ -754,6 +754,7 @@ void startGUITask(void const *argument __unused) { bool buttonLockout = false; bool tempOnDisplay = false; bool tipDisconnectedDisplay = false; + bool showExitMenuTransition = false; { // Generate the flipped screen into ram for later use // flipped is generated by flipping each row @@ -820,6 +821,10 @@ void startGUITask(void const *argument __unused) { break; case BUTTON_B_SHORT: enterSettingsMenu(); // enter the settings menu + { + OLED::useSecondaryFramebuffer(true); + showExitMenuTransition = true; + } buttonLockout = true; break; default: @@ -921,7 +926,13 @@ void startGUITask(void const *argument __unused) { } } - OLED::refresh(); - GUIDelay(); + if (showExitMenuTransition) { + OLED::useSecondaryFramebuffer(false); + OLED::transitionSecondaryFramebuffer(false); + showExitMenuTransition = false; + } else { + OLED::refresh(); + GUIDelay(); + } } }