1
0
forked from me/IronOS

Merge branch 'master' into MHP30

This commit is contained in:
Ben V. Brown
2021-05-04 19:05:26 +10:00
4 changed files with 139 additions and 72 deletions

View File

@@ -308,7 +308,7 @@
}, },
"LanguageSwitch": { "LanguageSwitch": {
"text2": [ "text2": [
"Language:", "Langue :",
" FR Français" " FR Français"
], ],
"desc": "" "desc": ""

View 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_ */

View 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;
}

View File

@@ -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();
} }
} }
} }