Add menu exit transition animation

Part of which was removed in commit 04d72cb.
This commit is contained in:
Alvin Wong
2021-05-03 18:23:30 +08:00
parent 9cccac68fc
commit 9e40449c64
2 changed files with 82 additions and 42 deletions

View File

@@ -1034,6 +1034,18 @@ static bool settings_enterAdvancedMenu(void) {
void gui_Menu(const menuitem *menu) { void gui_Menu(const menuitem *menu) {
// Draw the settings menu and provide iteration support etc // 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; uint8_t currentScreen = 0;
TickType_t autoRepeatTimer = 0; TickType_t autoRepeatTimer = 0;
TickType_t autoRepeatAcceleration = 0; TickType_t autoRepeatAcceleration = 0;
@@ -1043,7 +1055,7 @@ void gui_Menu(const menuitem *menu) {
uint8_t scrollContentSize = 0; uint8_t scrollContentSize = 0;
bool scrollBlink = false; bool scrollBlink = false;
bool lastValue = false; bool lastValue = false;
bool scrollingDown = false; NavState navState = NavState::Entering;
ScrollMessage scrollMessage; ScrollMessage scrollMessage;
@@ -1051,42 +1063,58 @@ void gui_Menu(const menuitem *menu) {
scrollContentSize += 1; 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) { 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; 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 // If the user has hesitated for >=3 seconds, show the long text
// Otherwise "draw" the option // Otherwise "draw" the option
if ((xTaskGetTickCount() - lastButtonTime < (TICKS_SECOND * 3)) || menu[currentScreen].description == 0) { if ((xTaskGetTickCount() - lastButtonTime < (TICKS_SECOND * 3)) || menu[currentScreen].description == 0) {
lcdRefresh = true; lcdRefresh = true;
OLED::setCursor(0, 0);
OLED::clearScreen(); OLED::clearScreen();
if (menu[currentScreen].draw()) { menu[currentScreen].draw();
currentScreen++;
lcdRefresh = false;
}
uint8_t indicatorHeight = OLED_HEIGHT / scrollContentSize; uint8_t indicatorHeight = OLED_HEIGHT / scrollContentSize;
uint8_t position = OLED_HEIGHT * currentScreen / scrollContentSize; uint8_t position = OLED_HEIGHT * currentScreen / scrollContentSize;
if (lastValue) if (lastValue)
scrollBlink = !scrollBlink; scrollBlink = !scrollBlink;
if ((!lastValue || !scrollBlink) && !scrollingDown) if (!lastValue || !scrollBlink)
OLED::drawScrollIndicator(position, indicatorHeight); OLED::drawScrollIndicator(position, indicatorHeight);
} else { } else {
// Draw description // Draw description
@@ -1095,15 +1123,8 @@ void gui_Menu(const menuitem *menu) {
} }
if (lcdRefresh) { if (lcdRefresh) {
if (scrollingDown) { OLED::refresh(); // update the LCD
OLED::maskScrollIndicatorOnOLED(); osDelay(40);
OLED::transitionScrollDown();
scrollingDown = false;
animOpenState = false;
} else {
OLED::refresh(); // update the LCD
osDelay(40);
}
lcdRefresh = false; lcdRefresh = false;
} }
@@ -1114,6 +1135,16 @@ void gui_Menu(const menuitem *menu) {
lastButtonState = buttons; lastButtonState = buttons;
} }
auto callIncrementHandler = [&]() {
wasInGuiMenu = false;
bool res = menu[currentScreen].incrementHandler();
if (wasInGuiMenu) {
navState = NavState::Exiting;
}
wasInGuiMenu = true;
return res;
};
switch (buttons) { switch (buttons) {
case BUTTON_BOTH: case BUTTON_BOTH:
earlyExit = true; // will make us exit next loop earlyExit = true; // will make us exit next loop
@@ -1123,7 +1154,7 @@ void gui_Menu(const menuitem *menu) {
// increment // increment
if (scrollMessage.isReset()) { if (scrollMessage.isReset()) {
if (menu[currentScreen].incrementHandler != nullptr) { if (menu[currentScreen].incrementHandler != nullptr) {
lastValue = menu[currentScreen].incrementHandler(); lastValue = callIncrementHandler();
} else { } else {
earlyExit = true; earlyExit = true;
} }
@@ -1133,14 +1164,14 @@ void gui_Menu(const menuitem *menu) {
case BUTTON_B_SHORT: case BUTTON_B_SHORT:
if (scrollMessage.isReset()) { if (scrollMessage.isReset()) {
currentScreen++; currentScreen++;
scrollingDown = true; navState = NavState::ScrollingDown;
lastValue = false; lastValue = false;
} else } else
scrollMessage.reset(); scrollMessage.reset();
break; break;
case BUTTON_F_LONG: case BUTTON_F_LONG:
if (xTaskGetTickCount() + autoRepeatAcceleration > autoRepeatTimer + PRESS_ACCEL_INTERVAL_MAX) { if (xTaskGetTickCount() + autoRepeatAcceleration > autoRepeatTimer + PRESS_ACCEL_INTERVAL_MAX) {
if ((lastValue = menu[currentScreen].incrementHandler())) if ((lastValue = callIncrementHandler()))
autoRepeatTimer = 1000; autoRepeatTimer = 1000;
else else
autoRepeatTimer = 0; autoRepeatTimer = 0;
@@ -1155,7 +1186,7 @@ void gui_Menu(const menuitem *menu) {
case BUTTON_B_LONG: case BUTTON_B_LONG:
if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration > PRESS_ACCEL_INTERVAL_MAX) { if (xTaskGetTickCount() - autoRepeatTimer + autoRepeatAcceleration > PRESS_ACCEL_INTERVAL_MAX) {
currentScreen++; currentScreen++;
scrollingDown = true; navState = NavState::ScrollingDown;
autoRepeatTimer = xTaskGetTickCount(); autoRepeatTimer = xTaskGetTickCount();
scrollMessage.reset(); scrollMessage.reset();
@@ -1178,8 +1209,6 @@ void gui_Menu(const menuitem *menu) {
scrollMessage.reset(); scrollMessage.reset();
} }
} }
animOpenState = false;
} }
void enterSettingsMenu() { void enterSettingsMenu() {

View File

@@ -754,6 +754,7 @@ void startGUITask(void const *argument __unused) {
bool buttonLockout = false; bool buttonLockout = false;
bool tempOnDisplay = false; bool tempOnDisplay = false;
bool tipDisconnectedDisplay = false; bool tipDisconnectedDisplay = false;
bool showExitMenuTransition = false;
{ {
// Generate the flipped screen into ram for later use // Generate the flipped screen into ram for later use
// flipped is generated by flipping each row // flipped is generated by flipping each row
@@ -820,6 +821,10 @@ void startGUITask(void const *argument __unused) {
break; break;
case BUTTON_B_SHORT: case BUTTON_B_SHORT:
enterSettingsMenu(); // enter the settings menu enterSettingsMenu(); // enter the settings menu
{
OLED::useSecondaryFramebuffer(true);
showExitMenuTransition = true;
}
buttonLockout = true; buttonLockout = true;
break; break;
default: default:
@@ -921,7 +926,13 @@ void startGUITask(void const *argument __unused) {
} }
} }
OLED::refresh(); if (showExitMenuTransition) {
GUIDelay(); OLED::useSecondaryFramebuffer(false);
OLED::transitionSecondaryFramebuffer(false);
showExitMenuTransition = false;
} else {
OLED::refresh();
GUIDelay();
}
} }
} }