Merge pull request #614 from PixelPirate/navigation-animations
Navigation animations
This commit is contained in:
@@ -79,7 +79,7 @@ public:
|
|||||||
// Draws a number at the current cursor location
|
// Draws a number at the current cursor location
|
||||||
// Clears the buffer
|
// Clears the buffer
|
||||||
static void clearScreen() {
|
static void clearScreen() {
|
||||||
memset(&screenBuffer[FRAMEBUFFER_START], 0, OLED_WIDTH * 2);
|
memset(firstStripPtr, 0, OLED_WIDTH * 2);
|
||||||
}
|
}
|
||||||
// Draws the battery level symbol
|
// Draws the battery level symbol
|
||||||
static void drawBattery(uint8_t state) {
|
static void drawBattery(uint8_t state) {
|
||||||
@@ -101,8 +101,11 @@ public:
|
|||||||
bool clear);
|
bool clear);
|
||||||
static void drawHeatSymbol(uint8_t state);
|
static void drawHeatSymbol(uint8_t state);
|
||||||
static void drawScrollIndicator(uint8_t p, uint8_t h); // Draws a scrolling position indicator
|
static void drawScrollIndicator(uint8_t p, uint8_t h); // Draws a scrolling position indicator
|
||||||
|
static void transitionSecondaryFramebuffer(bool forwardNavigation);
|
||||||
|
static void useSecondaryFramebuffer(bool useSecondary);
|
||||||
private:
|
private:
|
||||||
static void drawChar(char c); // Draw a character to a specific location
|
static void drawChar(char c); // Draw a character to a specific location
|
||||||
|
static void setFramebuffer(uint8_t *buffer);
|
||||||
static const uint8_t* currentFont;// Pointer to the current font used for rendering to the buffer
|
static const uint8_t* currentFont;// Pointer to the current font used for rendering to the buffer
|
||||||
static uint8_t* firstStripPtr; // Pointers to the strips to allow for buffer having extra content
|
static uint8_t* firstStripPtr; // Pointers to the strips to allow for buffer having extra content
|
||||||
static uint8_t* secondStripPtr; //Pointers to the strips
|
static uint8_t* secondStripPtr; //Pointers to the strips
|
||||||
@@ -112,6 +115,7 @@ private:
|
|||||||
static int16_t cursor_x, cursor_y;
|
static int16_t cursor_x, cursor_y;
|
||||||
static uint8_t displayOffset;
|
static uint8_t displayOffset;
|
||||||
static uint8_t screenBuffer[16 + (OLED_WIDTH * 2) + 10]; // The data buffer
|
static uint8_t screenBuffer[16 + (OLED_WIDTH * 2) + 10]; // The data buffer
|
||||||
|
static uint8_t secondFrameBuffer[OLED_WIDTH * 2];
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* OLED_HPP_ */
|
#endif /* OLED_HPP_ */
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ uint8_t OLED::fontWidth, OLED::fontHeight;
|
|||||||
int16_t OLED::cursor_x, OLED::cursor_y;
|
int16_t OLED::cursor_x, OLED::cursor_y;
|
||||||
uint8_t OLED::displayOffset;
|
uint8_t OLED::displayOffset;
|
||||||
uint8_t OLED::screenBuffer[16 + (OLED_WIDTH * 2) + 10]; // The data buffer
|
uint8_t OLED::screenBuffer[16 + (OLED_WIDTH * 2) + 10]; // The data buffer
|
||||||
|
uint8_t OLED::secondFrameBuffer[OLED_WIDTH * 2];
|
||||||
|
|
||||||
/*Setup params for the OLED screen*/
|
/*Setup params for the OLED screen*/
|
||||||
/*http://www.displayfuture.com/Display/datasheet/controller/SSD1307.pdf*/
|
/*http://www.displayfuture.com/Display/datasheet/controller/SSD1307.pdf*/
|
||||||
@@ -62,6 +63,27 @@ uint8_t OLED_Setup_Array[] = {
|
|||||||
const uint8_t REFRESH_COMMANDS[17] = { 0x80, 0xAF, 0x80, 0x21, 0x80, 0x20, 0x80,
|
const uint8_t REFRESH_COMMANDS[17] = { 0x80, 0xAF, 0x80, 0x21, 0x80, 0x20, 0x80,
|
||||||
0x7F, 0x80, 0xC0, 0x80, 0x22, 0x80, 0x00, 0x80, 0x01, 0x40 };
|
0x7F, 0x80, 0xC0, 0x80, 0x22, 0x80, 0x00, 0x80, 0x01, 0x40 };
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Animation timing function that follows a bezier curve.
|
||||||
|
* @param t A given percentage value [0..<100]
|
||||||
|
* Returns a new percentage value with ease in and ease out.
|
||||||
|
* Original floating point formula: t * t * (3.0f - 2.0f * t);
|
||||||
|
*/
|
||||||
|
static uint8_t easeInOutTiming(uint8_t t) {
|
||||||
|
return t * t * (300 - 2 * t) / 10000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns the value between a and b, using a percentage value t.
|
||||||
|
* @param a The value associated with 0%
|
||||||
|
* @param b The value associated with 100%
|
||||||
|
* @param t The percentage [0..<100]
|
||||||
|
*/
|
||||||
|
static uint8_t lerp(uint8_t a, uint8_t b, uint8_t t) {
|
||||||
|
return a + t * (b - a) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
void OLED::initialize() {
|
void OLED::initialize() {
|
||||||
cursor_x = cursor_y = 0;
|
cursor_x = cursor_y = 0;
|
||||||
currentFont = USER_FONT_12;
|
currentFont = USER_FONT_12;
|
||||||
@@ -85,6 +107,17 @@ void OLED::initialize() {
|
|||||||
sizeof(OLED_Setup_Array));
|
sizeof(OLED_Setup_Array));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OLED::setFramebuffer(uint8_t *buffer) {
|
||||||
|
if (buffer == NULL) {
|
||||||
|
firstStripPtr = &screenBuffer[FRAMEBUFFER_START];
|
||||||
|
secondStripPtr = &screenBuffer[FRAMEBUFFER_START + OLED_WIDTH];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstStripPtr = &buffer[0];
|
||||||
|
secondStripPtr = &buffer[OLED_WIDTH];
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prints a char to the screen.
|
* Prints a char to the screen.
|
||||||
* UTF font handling is done using the two input chars.
|
* UTF font handling is done using the two input chars.
|
||||||
@@ -125,6 +158,62 @@ void OLED::drawScrollIndicator(uint8_t y, uint8_t height) {
|
|||||||
fillArea(OLED_WIDTH - 1, 8, 1, 8, column.strips[1]);
|
fillArea(OLED_WIDTH - 1, 8, 1, 8, column.strips[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays a transition animation between two framebuffers.
|
||||||
|
* @param forwardNavigation 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::transitionSecondaryFramebuffer(bool forwardNavigation) {
|
||||||
|
uint8_t *firstBackStripPtr = &secondFrameBuffer[0];
|
||||||
|
uint8_t *secondBackStripPtr = &secondFrameBuffer[OLED_WIDTH];
|
||||||
|
|
||||||
|
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;
|
||||||
|
progress = easeInOutTiming(progress);
|
||||||
|
progress = lerp(0, OLED_WIDTH, progress);
|
||||||
|
if (progress > OLED_WIDTH) {
|
||||||
|
progress = OLED_WIDTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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[oldStart], &firstStripPtr[oldPrevious], OLED_WIDTH - progress);
|
||||||
|
memmove(&secondStripPtr[oldStart], &secondStripPtr[oldPrevious], OLED_WIDTH - progress);
|
||||||
|
|
||||||
|
memmove(&firstStripPtr[newStart], &firstBackStripPtr[newEnd], progress);
|
||||||
|
memmove(&secondStripPtr[newStart], &secondBackStripPtr[newEnd], progress);
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
osDelay(40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void OLED::useSecondaryFramebuffer(bool useSecondary) {
|
||||||
|
if (useSecondary) {
|
||||||
|
setFramebuffer(secondFrameBuffer);
|
||||||
|
} else {
|
||||||
|
setFramebuffer(NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void OLED::setRotation(bool leftHanded) {
|
void OLED::setRotation(bool leftHanded) {
|
||||||
#ifdef MODEL_TS80
|
#ifdef MODEL_TS80
|
||||||
leftHanded = !leftHanded;
|
leftHanded = !leftHanded;
|
||||||
|
|||||||
@@ -826,12 +826,29 @@ void gui_Menu(const menuitem *menu) {
|
|||||||
int16_t lastOffset = -1;
|
int16_t lastOffset = -1;
|
||||||
bool lcdRefresh = true;
|
bool lcdRefresh = true;
|
||||||
ButtonState lastButtonState = BUTTON_NONE;
|
ButtonState lastButtonState = BUTTON_NONE;
|
||||||
|
static bool enterGUIMenu = true;
|
||||||
|
enterGUIMenu = true;
|
||||||
uint8_t scrollContentSize = 0;
|
uint8_t scrollContentSize = 0;
|
||||||
|
|
||||||
for (uint8_t i = 0; menu[i].draw.func != NULL; i++) {
|
for (uint8_t i = 0; menu[i].draw.func != NULL; i++) {
|
||||||
scrollContentSize += 1;
|
scrollContentSize += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Animated menu opening.
|
||||||
|
if (menu[currentScreen].draw.func != NULL) {
|
||||||
|
// 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.
|
||||||
|
OLED::useSecondaryFramebuffer(true);
|
||||||
|
OLED::setFont(0);
|
||||||
|
OLED::setCursor(0, 0);
|
||||||
|
OLED::clearScreen();
|
||||||
|
menu[currentScreen].draw.func();
|
||||||
|
OLED::useSecondaryFramebuffer(false);
|
||||||
|
OLED::transitionSecondaryFramebuffer(true);
|
||||||
|
}
|
||||||
|
|
||||||
while ((menu[currentScreen].draw.func != NULL) && earlyExit == false) {
|
while ((menu[currentScreen].draw.func != NULL) && earlyExit == false) {
|
||||||
OLED::setFont(0);
|
OLED::setFont(0);
|
||||||
OLED::setCursor(0, 0);
|
OLED::setCursor(0, 0);
|
||||||
@@ -882,10 +899,23 @@ void gui_Menu(const menuitem *menu) {
|
|||||||
case BUTTON_F_SHORT:
|
case BUTTON_F_SHORT:
|
||||||
// increment
|
// increment
|
||||||
if (descriptionStart == 0) {
|
if (descriptionStart == 0) {
|
||||||
if (menu[currentScreen].incrementHandler.func != NULL)
|
if (menu[currentScreen].incrementHandler.func != NULL) {
|
||||||
|
enterGUIMenu = false;
|
||||||
menu[currentScreen].incrementHandler.func();
|
menu[currentScreen].incrementHandler.func();
|
||||||
else
|
|
||||||
|
if (enterGUIMenu) {
|
||||||
|
OLED::useSecondaryFramebuffer(true);
|
||||||
|
OLED::setFont(0);
|
||||||
|
OLED::setCursor(0, 0);
|
||||||
|
OLED::clearScreen();
|
||||||
|
menu[currentScreen].draw.func();
|
||||||
|
OLED::useSecondaryFramebuffer(false);
|
||||||
|
OLED::transitionSecondaryFramebuffer(false);
|
||||||
|
}
|
||||||
|
enterGUIMenu = true;
|
||||||
|
} else {
|
||||||
earlyExit = true;
|
earlyExit = true;
|
||||||
|
}
|
||||||
} else
|
} else
|
||||||
descriptionStart = 0;
|
descriptionStart = 0;
|
||||||
break;
|
break;
|
||||||
@@ -931,7 +961,7 @@ void gui_Menu(const menuitem *menu) {
|
|||||||
osDelay(40);
|
osDelay(40);
|
||||||
lcdRefresh = false;
|
lcdRefresh = false;
|
||||||
}
|
}
|
||||||
if ((xTaskGetTickCount() - lastButtonTime) > (1000 * 30)) {
|
if ((xTaskGetTickCount() - lastButtonTime) > (100 * 30)) {
|
||||||
// If user has not pressed any buttons in 30 seconds, exit back a menu layer
|
// If user has not pressed any buttons in 30 seconds, exit back a menu layer
|
||||||
// This will trickle the user back to the main screen eventually
|
// This will trickle the user back to the main screen eventually
|
||||||
earlyExit = true;
|
earlyExit = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user