diff --git a/workspace/TS100/Core/Inc/OLED.hpp b/workspace/TS100/Core/Inc/OLED.hpp index 3d5dfda8..8fcacefe 100644 --- a/workspace/TS100/Core/Inc/OLED.hpp +++ b/workspace/TS100/Core/Inc/OLED.hpp @@ -99,8 +99,7 @@ public: static void drawFilledRect(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, bool clear); static void drawHeatSymbol(uint8_t state); - static void presentSecondScreenBufferAnimatedBack(); - static void presentSecondScreenBufferAnimated(); + static void transitionToContents(uint8_t *framebuffer, bool forwardNavigation); static void set_framebuffer(uint8_t *buffer); private: static void drawChar(char c); // Draw a character to a specific location diff --git a/workspace/TS100/Core/Src/OLED.cpp b/workspace/TS100/Core/Src/OLED.cpp index 405786b7..8d4e2f77 100644 --- a/workspace/TS100/Core/Src/OLED.cpp +++ b/workspace/TS100/Core/Src/OLED.cpp @@ -125,72 +125,49 @@ void OLED::drawChar(char c) { cursor_x += fontWidth; } -void OLED::presentSecondScreenBufferAnimatedBack() { - uint8_t *firstBackStripPtr = &firstStripPtr[0]; - uint8_t *secondBackStripPtr = &secondStripPtr[0]; - set_framebuffer(NULL); +/** + * Plays a transition animation between two framebuffers. + * @param framebuffer Second framebuffer to use for animation. + * @param forward Direction of the navigation animation. + * + * If forward is true, this displays a forward navigation to the second framebuffer contents. + * Otherwise a rewinding navigation animation is shown to the second framebuffer contents. + */ +void OLED::transitionToContents(uint8_t *framebuffer, bool forwardNavigation) { + uint8_t *firstBackStripPtr = &framebuffer[0]; + uint8_t *secondBackStripPtr = &framebuffer[OLED_WIDTH]; - uint32_t totalDuration = 50; + uint32_t totalDuration = 50; // 500ms uint32_t duration = 0; uint32_t start = xTaskGetTickCount(); uint8_t offset = 0; + while (duration <= totalDuration) { duration = xTaskGetTickCount() - start; - - uint8_t progress = (duration * 100) / totalDuration; + uint8_t progress = duration * 100 / totalDuration; progress = easeInOutTiming(progress); progress = lerp(0, OLED_WIDTH, progress); if (progress > OLED_WIDTH) { progress = OLED_WIDTH; } - memmove(&firstStripPtr[progress], &firstStripPtr[offset], OLED_WIDTH - progress); - memmove(&secondStripPtr[progress], &secondStripPtr[offset], OLED_WIDTH - progress); + // When forward, current contents move to the left out. + // Otherwise the contents move to the right out. + uint8_t oldStart = forwardNavigation ? 0 : progress; + uint8_t oldPrevious = forwardNavigation ? progress - offset : offset; + + // Content from the second framebuffer moves in from the right (forward) + // or from the left (not forward). + uint8_t newStart = forwardNavigation ? OLED_WIDTH - progress : 0; + uint8_t newEnd = forwardNavigation ? 0 : OLED_WIDTH - progress; + offset = progress; - - memmove( - &firstStripPtr[0], - &firstBackStripPtr[OLED_WIDTH - progress], - progress); - memmove(&secondStripPtr[0], - &secondBackStripPtr[OLED_WIDTH - progress], - progress); - refresh(); - osDelay(40); - } -} - -void OLED::presentSecondScreenBufferAnimated() { - uint8_t *firstBackStripPtr = &firstStripPtr[0]; - uint8_t *secondBackStripPtr = &secondStripPtr[0]; - set_framebuffer(NULL); - - uint32_t totalDuration = 50; - uint32_t duration = 0; - uint32_t start = xTaskGetTickCount(); - uint8_t offset = 0; - while (duration < totalDuration) { - duration = xTaskGetTickCount() - start; + memmove(&firstStripPtr[oldStart], &firstStripPtr[oldPrevious], OLED_WIDTH - progress); + memmove(&secondStripPtr[oldStart], &secondStripPtr[oldPrevious], OLED_WIDTH - progress); - uint8_t progress = (duration * 100) / totalDuration; - progress = easeInOutTiming(progress); - progress = lerp(0, OLED_WIDTH, progress); - if (progress > OLED_WIDTH) { - progress = OLED_WIDTH; - } - - memmove(&firstStripPtr[0], &firstStripPtr[progress - offset], OLED_WIDTH - progress); - memmove(&secondStripPtr[0], &secondStripPtr[progress - offset], OLED_WIDTH - progress); - offset = progress; - - memmove( - &firstStripPtr[OLED_WIDTH - progress], - &firstBackStripPtr[0], - progress); - memmove(&secondStripPtr[OLED_WIDTH - progress], - &secondBackStripPtr[0], - progress); + memmove(&firstStripPtr[newStart], &firstBackStripPtr[newEnd], progress); + memmove(&secondStripPtr[newStart], &secondBackStripPtr[newEnd], progress); refresh(); osDelay(40); diff --git a/workspace/TS100/Core/Src/gui.cpp b/workspace/TS100/Core/Src/gui.cpp index 0d57f3e4..34779be6 100644 --- a/workspace/TS100/Core/Src/gui.cpp +++ b/workspace/TS100/Core/Src/gui.cpp @@ -826,14 +826,20 @@ void gui_Menu(const menuitem *menu) { static bool enterGUIMenu = true; enterGUIMenu = true; + // Animated menu opening. if (menu[currentScreen].draw.func != NULL) { - uint8_t secondFrameBuffer[OLED_WIDTH * 2]; - OLED::set_framebuffer(secondFrameBuffer); + // 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. + uint8_t secondaryFrameBuffer[OLED_WIDTH * 2]; + OLED::set_framebuffer(secondaryFrameBuffer); OLED::setFont(0); OLED::setCursor(0, 0); OLED::clearScreen(); menu[currentScreen].draw.func(); - OLED::presentSecondScreenBufferAnimated(); + OLED::set_framebuffer(NULL); + OLED::transitionToContents(secondaryFrameBuffer, true); } while ((menu[currentScreen].draw.func != NULL) && earlyExit == false) { @@ -888,13 +894,14 @@ void gui_Menu(const menuitem *menu) { menu[currentScreen].incrementHandler.func(); if (enterGUIMenu) { - uint8_t secondFrameBuffer[OLED_WIDTH * 2]; - OLED::set_framebuffer(secondFrameBuffer); + uint8_t secondaryFrameBuffer[OLED_WIDTH * 2]; + OLED::set_framebuffer(secondaryFrameBuffer); OLED::setFont(0); OLED::setCursor(0, 0); OLED::clearScreen(); menu[currentScreen].draw.func(); - OLED::presentSecondScreenBufferAnimatedBack(); + OLED::set_framebuffer(NULL); + OLED::transitionToContents(secondaryFrameBuffer, false); } } else { earlyExit = true;