Merge branch 'master' into MHP30
This commit is contained in:
@@ -308,7 +308,7 @@
|
|||||||
},
|
},
|
||||||
"LanguageSwitch": {
|
"LanguageSwitch": {
|
||||||
"text2": [
|
"text2": [
|
||||||
"Language:",
|
"Langue :",
|
||||||
" FR Français"
|
" FR Français"
|
||||||
],
|
],
|
||||||
"desc": ""
|
"desc": ""
|
||||||
|
|||||||
53
source/Core/Inc/ScrollMessage.hpp
Normal file
53
source/Core/Inc/ScrollMessage.hpp
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#ifndef SCROLL_MESSAGE_HPP_
|
||||||
|
#define SCROLL_MESSAGE_HPP_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class for showing a full-screen scrolling message.
|
||||||
|
*/
|
||||||
|
class ScrollMessage {
|
||||||
|
uint32_t messageStart = 0;
|
||||||
|
int16_t lastOffset = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calcualte the width in pixels of the message string, in the large
|
||||||
|
* font and taking into account multi-byte chars.
|
||||||
|
*
|
||||||
|
* @param message The null-terminated message string.
|
||||||
|
*/
|
||||||
|
static uint16_t messageWidth(const char *message);
|
||||||
|
|
||||||
|
public:
|
||||||
|
ScrollMessage() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets this `ScrollMessage` instance to its initial state.
|
||||||
|
*/
|
||||||
|
void reset() {
|
||||||
|
messageStart = 0;
|
||||||
|
lastOffset = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether this `ScrollMessage` instance is in its initial state.
|
||||||
|
*/
|
||||||
|
bool isReset() const { return messageStart == 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draw and update the scroll message if needed.
|
||||||
|
*
|
||||||
|
* This function does not call `OLED::refresh()`. If this function
|
||||||
|
* returns `true`, the caller shall call `OLED::refresh()` to draw the
|
||||||
|
* modified framebuffer to the OLED screen.
|
||||||
|
*
|
||||||
|
* @param message The null-terminated message string. This must be the
|
||||||
|
* same string as the previous call, unless this `ScrollMessage` instance
|
||||||
|
* is in its initial state or `reset()` has been called.
|
||||||
|
* @param currentTick The current tick as returned by `xTaskGetTickCount()`.
|
||||||
|
* @return Whether the OLED framebuffer has been modified.
|
||||||
|
*/
|
||||||
|
bool drawUpdate(const char *message, uint32_t currentTick);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* SCROLL_MESSAGE_HPP_ */
|
||||||
68
source/Core/Src/ScrollMessage.cpp
Normal file
68
source/Core/Src/ScrollMessage.cpp
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
#include "ScrollMessage.hpp"
|
||||||
|
|
||||||
|
#include "OLED.hpp"
|
||||||
|
#include "configuration.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Counts the number of chars in the string excluding the null terminator.
|
||||||
|
* This is a custom version of `strlen` which takes into account our custom
|
||||||
|
* double-byte char encoding.
|
||||||
|
* @param str The input string.
|
||||||
|
* @return The length of the string.
|
||||||
|
*/
|
||||||
|
static uint16_t str_display_len(const char *const str) {
|
||||||
|
const uint8_t *next = reinterpret_cast<const uint8_t *>(str);
|
||||||
|
uint16_t count = 0;
|
||||||
|
while (next[0]) {
|
||||||
|
if (next[0] <= 0xF0) {
|
||||||
|
count++;
|
||||||
|
next++;
|
||||||
|
} else {
|
||||||
|
if (!next[1]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
count++;
|
||||||
|
next += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ScrollMessage::messageWidth(const char *message) { return FONT_12_WIDTH * str_display_len(message); }
|
||||||
|
|
||||||
|
bool ScrollMessage::drawUpdate(const char *message, uint32_t currentTick) {
|
||||||
|
bool lcdRefresh = false;
|
||||||
|
|
||||||
|
if (messageStart == 0) {
|
||||||
|
messageStart = currentTick;
|
||||||
|
lcdRefresh = true;
|
||||||
|
}
|
||||||
|
int16_t messageOffset;
|
||||||
|
uint16_t msgWidth = messageWidth(message);
|
||||||
|
if (msgWidth > OLED_WIDTH) {
|
||||||
|
messageOffset = ((currentTick - messageStart) / (systemSettings.descriptionScrollSpeed == 1 ? TICKS_100MS / 10 : (TICKS_100MS / 5)));
|
||||||
|
messageOffset %= msgWidth + OLED_WIDTH; // Roll around at the end
|
||||||
|
if (messageOffset < OLED_WIDTH) {
|
||||||
|
// Snap the message to the left edge.
|
||||||
|
messageOffset = OLED_WIDTH;
|
||||||
|
} else if (messageOffset > msgWidth) {
|
||||||
|
// Snap the message to the right edge.
|
||||||
|
messageOffset = msgWidth;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Centre the message without scrolling.
|
||||||
|
messageOffset = (OLED_WIDTH - msgWidth) / 2 + msgWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastOffset != messageOffset) {
|
||||||
|
OLED::clearScreen();
|
||||||
|
|
||||||
|
//^ Rolling offset based on time
|
||||||
|
OLED::setCursor((OLED_WIDTH - messageOffset), 0);
|
||||||
|
OLED::print(message, FontStyle::LARGE);
|
||||||
|
lastOffset = messageOffset;
|
||||||
|
lcdRefresh = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lcdRefresh;
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "gui.hpp"
|
#include "gui.hpp"
|
||||||
#include "Buttons.hpp"
|
#include "Buttons.hpp"
|
||||||
|
#include "ScrollMessage.hpp"
|
||||||
#include "TipThermoModel.h"
|
#include "TipThermoModel.h"
|
||||||
#include "Translation.h"
|
#include "Translation.h"
|
||||||
#include "cmsis_os.h"
|
#include "cmsis_os.h"
|
||||||
@@ -270,52 +271,11 @@ static void printShortDescription(SettingsItemIndex settingsItemIndex, uint16_t
|
|||||||
OLED::setCursor(cursorCharPosition * FONT_12_WIDTH - 2, 0);
|
OLED::setCursor(cursorCharPosition * FONT_12_WIDTH - 2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Counts the number of chars in the string excluding the null terminator.
|
|
||||||
* This is a custom version of `strlen` which takes into account our custom
|
|
||||||
* double-byte char encoding.
|
|
||||||
* @param str The input string.
|
|
||||||
* @return The length of the string.
|
|
||||||
*/
|
|
||||||
static uint16_t str_display_len(const char *const str) {
|
|
||||||
const uint8_t *next = reinterpret_cast<const uint8_t *>(str);
|
|
||||||
uint16_t count = 0;
|
|
||||||
while (next[0]) {
|
|
||||||
if (next[0] <= 0xF0) {
|
|
||||||
count++;
|
|
||||||
next++;
|
|
||||||
} else {
|
|
||||||
if (!next[1]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
count++;
|
|
||||||
next += 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int userConfirmation(const char *message) {
|
static int userConfirmation(const char *message) {
|
||||||
uint16_t messageWidth = FONT_12_WIDTH * (str_display_len(message) + 7);
|
ScrollMessage scrollMessage;
|
||||||
uint32_t messageStart = xTaskGetTickCount();
|
|
||||||
|
|
||||||
OLED::setCursor(0, 0);
|
|
||||||
int16_t lastOffset = -1;
|
|
||||||
bool lcdRefresh = true;
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int16_t messageOffset = ((xTaskGetTickCount() - messageStart) / (systemSettings.descriptionScrollSpeed == 1 ? TICKS_100MS / 10 : (TICKS_100MS / 5)));
|
bool lcdRefresh = scrollMessage.drawUpdate(message, xTaskGetTickCount());
|
||||||
messageOffset %= messageWidth; // Roll around at the end
|
|
||||||
|
|
||||||
if (lastOffset != messageOffset) {
|
|
||||||
OLED::clearScreen();
|
|
||||||
|
|
||||||
//^ Rolling offset based on time
|
|
||||||
OLED::setCursor((OLED_WIDTH - messageOffset), 0);
|
|
||||||
OLED::print(message, FontStyle::LARGE);
|
|
||||||
lastOffset = messageOffset;
|
|
||||||
lcdRefresh = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ButtonState buttons = getButtonState();
|
ButtonState buttons = getButtonState();
|
||||||
switch (buttons) {
|
switch (buttons) {
|
||||||
@@ -336,7 +296,6 @@ static int userConfirmation(const char *message) {
|
|||||||
if (lcdRefresh) {
|
if (lcdRefresh) {
|
||||||
OLED::refresh();
|
OLED::refresh();
|
||||||
osDelay(40);
|
osDelay(40);
|
||||||
lcdRefresh = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1099,14 +1058,14 @@ void gui_Menu(const menuitem *menu) {
|
|||||||
TickType_t autoRepeatTimer = 0;
|
TickType_t autoRepeatTimer = 0;
|
||||||
TickType_t autoRepeatAcceleration = 0;
|
TickType_t autoRepeatAcceleration = 0;
|
||||||
bool earlyExit = false;
|
bool earlyExit = false;
|
||||||
TickType_t descriptionStart = 0;
|
|
||||||
int16_t lastOffset = -1;
|
|
||||||
bool lcdRefresh = true;
|
bool lcdRefresh = true;
|
||||||
ButtonState lastButtonState = BUTTON_NONE;
|
ButtonState lastButtonState = BUTTON_NONE;
|
||||||
uint8_t scrollContentSize = 0;
|
uint8_t scrollContentSize = 0;
|
||||||
bool scrollBlink = false;
|
bool scrollBlink = false;
|
||||||
bool lastValue = false;
|
bool lastValue = false;
|
||||||
|
|
||||||
|
ScrollMessage scrollMessage;
|
||||||
|
|
||||||
for (uint8_t i = 0; menu[i].draw != nullptr; i++) {
|
for (uint8_t i = 0; menu[i].draw != nullptr; i++) {
|
||||||
scrollContentSize += 1;
|
scrollContentSize += 1;
|
||||||
}
|
}
|
||||||
@@ -1144,23 +1103,10 @@ void gui_Menu(const menuitem *menu) {
|
|||||||
scrollBlink = !scrollBlink;
|
scrollBlink = !scrollBlink;
|
||||||
if (!lastValue || !scrollBlink)
|
if (!lastValue || !scrollBlink)
|
||||||
OLED::drawScrollIndicator(position, indicatorHeight);
|
OLED::drawScrollIndicator(position, indicatorHeight);
|
||||||
lastOffset = -1;
|
|
||||||
} else {
|
} else {
|
||||||
// Draw description
|
// Draw description
|
||||||
if (descriptionStart == 0)
|
|
||||||
descriptionStart = xTaskGetTickCount();
|
|
||||||
const char *description = translatedString(Tr->SettingsDescriptions[menu[currentScreen].description - 1]);
|
const char *description = translatedString(Tr->SettingsDescriptions[menu[currentScreen].description - 1]);
|
||||||
// lower the value - higher the speed
|
lcdRefresh |= scrollMessage.drawUpdate(description, xTaskGetTickCount());
|
||||||
int16_t descriptionWidth = FONT_12_WIDTH * (str_display_len(description) + 7);
|
|
||||||
int16_t descriptionOffset = ((xTaskGetTickCount() - descriptionStart) / (systemSettings.descriptionScrollSpeed == 1 ? (TICKS_100MS / 10) : (TICKS_100MS / 5)));
|
|
||||||
descriptionOffset %= descriptionWidth; // Roll around at the end
|
|
||||||
if (lastOffset != descriptionOffset) {
|
|
||||||
OLED::clearScreen();
|
|
||||||
OLED::setCursor((OLED_WIDTH - descriptionOffset), 0);
|
|
||||||
OLED::print(description, FontStyle::LARGE);
|
|
||||||
lastOffset = descriptionOffset;
|
|
||||||
lcdRefresh = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ButtonState buttons = getButtonState();
|
ButtonState buttons = getButtonState();
|
||||||
@@ -1172,26 +1118,26 @@ void gui_Menu(const menuitem *menu) {
|
|||||||
|
|
||||||
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
|
||||||
descriptionStart = 0;
|
scrollMessage.reset();
|
||||||
break;
|
break;
|
||||||
case BUTTON_F_SHORT:
|
case BUTTON_F_SHORT:
|
||||||
// increment
|
// increment
|
||||||
if (descriptionStart == 0) {
|
if (scrollMessage.isReset()) {
|
||||||
if (menu[currentScreen].incrementHandler != nullptr) {
|
if (menu[currentScreen].incrementHandler != nullptr) {
|
||||||
lastValue = menu[currentScreen].incrementHandler();
|
lastValue = menu[currentScreen].incrementHandler();
|
||||||
} else {
|
} else {
|
||||||
earlyExit = true;
|
earlyExit = true;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
descriptionStart = 0;
|
scrollMessage.reset();
|
||||||
break;
|
break;
|
||||||
case BUTTON_B_SHORT:
|
case BUTTON_B_SHORT:
|
||||||
if (descriptionStart == 0) {
|
if (scrollMessage.isReset()) {
|
||||||
currentScreen++;
|
currentScreen++;
|
||||||
lastValue = false;
|
lastValue = false;
|
||||||
} else
|
} else
|
||||||
descriptionStart = 0;
|
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) {
|
||||||
@@ -1202,7 +1148,7 @@ void gui_Menu(const menuitem *menu) {
|
|||||||
|
|
||||||
autoRepeatTimer += xTaskGetTickCount();
|
autoRepeatTimer += xTaskGetTickCount();
|
||||||
|
|
||||||
descriptionStart = 0;
|
scrollMessage.reset();
|
||||||
|
|
||||||
autoRepeatAcceleration += PRESS_ACCEL_STEP;
|
autoRepeatAcceleration += PRESS_ACCEL_STEP;
|
||||||
}
|
}
|
||||||
@@ -1210,8 +1156,8 @@ 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++;
|
||||||
autoRepeatTimer = xTaskGetTickCount();
|
autoRepeatTimer = xTaskGetTickCount();
|
||||||
descriptionStart = 0;
|
scrollMessage.reset();
|
||||||
|
|
||||||
autoRepeatAcceleration += PRESS_ACCEL_STEP;
|
autoRepeatAcceleration += PRESS_ACCEL_STEP;
|
||||||
}
|
}
|
||||||
@@ -1233,8 +1179,8 @@ void gui_Menu(const menuitem *menu) {
|
|||||||
if ((xTaskGetTickCount() - lastButtonTime) > (TICKS_SECOND * 30)) {
|
if ((xTaskGetTickCount() - lastButtonTime) > (TICKS_SECOND * 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;
|
||||||
descriptionStart = 0;
|
scrollMessage.reset();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user