* Starting GUI render refactor to be more immediate mode Update TemperatureAdjust.cpp . Cleanup Soldering Sleep SolderingProfiles Soldering Rework Rough pass GUI Temp Adjust Cleanup old OperatingMode Debug Menu * Update TemperatureAdjust.cpp * Roughing some transition work * Fixup! Hook in the init starter helper * Better home screen button handler * FIXUP! Fix typo's . * Update SettingsMenu.cpp * More settings rework * More settings rendering * Fixup * Transitions Update SolderingProfile.cpp Hook in transistions * Update TemperatureAdjust.cpp * Update push.yml * Add auto-repeat to settings menu * Miniware: Use IT for I2C writes * Update USBPDDebug_HUSB238.cpp * Force write screen on side animation cancel . * Refactor moving down the settings list * Update settingsGUI.cpp * Update I2C_Wrapper.cpp * Update OLED.cpp * Rework button handling * Fix PD debug at boot * Fixup not showing right menu options * silence some warnings * Style cleanup * Fkit use bit-bang I2C for Miniware * Update GUIRendering.md * Fixup transition on enter soldering mode * Save Settings * Fixes for some animations not running Dont bail on animations if keypress is still held * Fixup settings acceleration * OLED Up animation * Link up/down on debug meny * Make all accelerometers I2C bus aware Update accelerometers_common.h * Make I2C mag optional * Miniware -> Only Bit-Bang I2C * Fixup for scrollbar FIXUP! Debug menu returns to home screen FIXUP! Up oled animation Fix temp exit * Settings menu -> Both buttons return a menu layer * Merge fixup * Update BMA223.cpp * Re-Enable OLED sleep * Save Setting on temp adjust exit * WiP on startup mode * Some autostart working * Add hibernation mode & more autostart fixes * If cant CJC; go to startup * Hibernate in sleep * Cleanup scroll indicator * FIXUP! Ensure startup warnings are linked in * FIXUP! Ensure we render out temp change before timing out * Ensure 100ms delay between CJC samples * Fix not re-calculating menu length on entering menu * Implement NegotiationinProgress for USB-PD * Mask heating until PD finishes negotiation * Fixup staying in hibernate correctly * Warning timeout * Show reset settings warning * Correctly compensate help text start time * Update GUIThread.cpp * Update USBPD.cpp * . * Fixup sleep time * Update printSleepCountdown.cpp * replacing countdown with big plus while in boost mode * bringing back the + 1 since it was missing when not in boost mode * Bail on USB-PD check after 3 seconds incase of DC source * Fix hibernate * Update PIDThread.cpp * did center plus symbol (boost mode) * Big refactor to not make settings increment handler handle the "is last item" return * Fixup boot logo * Fix flashing * Fixup recalculate the menu length on long hold * Fixup missing menu entries * Fix junk left on screen after user confirmation * Re-order button handler to use custom, then default order to allow setting associated setting * Attach setting for settings using custom handler * Fix swap +/- keys * Fix boost temp * Implement last menu option for Language selector * Wait for init before CJC runs * Check last setting via increment value * Update BSP.cpp * removed = from >= Otherwise incrementing would stop and the scroll bar would already flash at the second to last value. * (Hacky) Fix for Settings reset --------- Co-authored-by: discip <53649486+discip@users.noreply.github.com>
275 lines
9.5 KiB
C++
275 lines
9.5 KiB
C++
#include "OperatingModes.h"
|
|
#include "ScrollMessage.hpp"
|
|
|
|
#define HELP_TEXT_TIMEOUT_TICKS (TICKS_SECOND * 3)
|
|
/*
|
|
* The settings menu is the most complex bit of GUI code we have
|
|
* The menu consists of a two tier menu
|
|
* Main menu -> Categories
|
|
* Secondary menu -> Settings
|
|
*
|
|
* For each entry in the menu
|
|
*/
|
|
|
|
/**
|
|
* Prints two small lines (or one line for CJK) of short description for
|
|
* setting items and prepares cursor after it.
|
|
* @param settingsItemIndex Index of the setting item.
|
|
* @param cursorCharPosition Custom cursor char position to set after printing
|
|
* description.
|
|
*/
|
|
static void printShortDescription(SettingsItemIndex settingsItemIndex, uint16_t cursorCharPosition) {
|
|
// print short description (default single line, explicit double line)
|
|
uint8_t shortDescIndex = static_cast<uint8_t>(settingsItemIndex);
|
|
OLED::printWholeScreen(translatedString(Tr->SettingsShortNames[shortDescIndex]));
|
|
|
|
// prepare cursor for value
|
|
// make room for scroll indicator
|
|
OLED::setCursor(cursorCharPosition * FONT_12_WIDTH - 2, 0);
|
|
}
|
|
|
|
// Render a menu, based on the position given
|
|
// This will either draw the menu item, or the help text depending on how long its been since button press
|
|
void render_menu(const menuitem *item, guiContext *cxt) {
|
|
// If recent interaction or not help text draw the entry
|
|
if ((xTaskGetTickCount() - lastButtonTime < HELP_TEXT_TIMEOUT_TICKS) || item->description == 0) {
|
|
|
|
if (item->shortDescriptionSize > 0) {
|
|
printShortDescription(item->shortDescriptionIndex, item->shortDescriptionSize);
|
|
}
|
|
item->draw();
|
|
} else {
|
|
|
|
uint16_t *isRenderingHelp = &(cxt->scratch_state.state6);
|
|
*isRenderingHelp = 1;
|
|
// Draw description
|
|
const char *description = translatedString(Tr->SettingsDescriptions[item->description - 1]);
|
|
drawScrollingText(description, (xTaskGetTickCount() - lastButtonTime) - HELP_TEXT_TIMEOUT_TICKS);
|
|
}
|
|
}
|
|
|
|
uint16_t getMenuLength(const menuitem *menu, const uint16_t stop) {
|
|
// walk this menu to find the length
|
|
uint16_t counter = 0;
|
|
for (uint16_t pos = 0; pos < stop; pos++) {
|
|
// End of list
|
|
if (menu[pos].draw == nullptr) {
|
|
return counter;
|
|
}
|
|
// Otherwise increment for each visible item (null == always, or if not check function)
|
|
if (menu[pos].isVisible == nullptr || menu[pos].isVisible()) {
|
|
counter++;
|
|
}
|
|
}
|
|
return counter;
|
|
}
|
|
|
|
OperatingMode moveToNextEntry(guiContext *cxt) {
|
|
uint16_t *mainEntry = &(cxt->scratch_state.state1);
|
|
uint16_t *subEntry = &(cxt->scratch_state.state2);
|
|
uint16_t *currentMenuLength = &(cxt->scratch_state.state5);
|
|
uint16_t *isRenderingHelp = &(cxt->scratch_state.state6);
|
|
|
|
if (*isRenderingHelp) {
|
|
*isRenderingHelp = 0;
|
|
} else {
|
|
*currentMenuLength = 0; // Reset menu length
|
|
// Scroll down
|
|
// We can increment freely _once_
|
|
cxt->transitionMode = TransitionAnimation::Down;
|
|
if (*subEntry == 0) {
|
|
(*mainEntry) += 1;
|
|
|
|
if (rootSettingsMenu[*mainEntry].draw == nullptr) {
|
|
// We are off the end of the menu now
|
|
saveSettings();
|
|
cxt->transitionMode = TransitionAnimation::Left;
|
|
return OperatingMode::HomeScreen;
|
|
}
|
|
// Check if visible
|
|
if (rootSettingsMenu[*mainEntry].isVisible != nullptr && !rootSettingsMenu[*mainEntry].isVisible()) {
|
|
// We need to move on as this one isn't visible
|
|
return moveToNextEntry(cxt);
|
|
}
|
|
} else {
|
|
(*subEntry) += 1;
|
|
|
|
// If the new entry is null, we need to exit
|
|
if (subSettingsMenus[*mainEntry][(*subEntry) - 1].draw == nullptr) {
|
|
(*subEntry) = 0; // Reset back to the main menu
|
|
cxt->transitionMode = TransitionAnimation::Left;
|
|
// Have to break early to avoid the below check underflowing
|
|
return OperatingMode::SettingsMenu;
|
|
}
|
|
// Check if visible
|
|
if (subSettingsMenus[*mainEntry][(*subEntry) - 1].isVisible != nullptr && !subSettingsMenus[*mainEntry][(*subEntry) - 1].isVisible()) {
|
|
// We need to move on as this one isn't visible
|
|
return moveToNextEntry(cxt);
|
|
}
|
|
}
|
|
}
|
|
return OperatingMode::SettingsMenu;
|
|
}
|
|
|
|
OperatingMode gui_SettingsMenu(const ButtonState buttons, guiContext *cxt) {
|
|
// Render out the current settings menu
|
|
// State 1 -> Root menu
|
|
// State 2 -> Sub entry
|
|
// Draw main entry if sub-entry is 0, otherwise draw sub-entry
|
|
|
|
uint16_t *mainEntry = &(cxt->scratch_state.state1);
|
|
uint16_t *subEntry = &(cxt->scratch_state.state2);
|
|
uint32_t *autoRepeatAcceleration = &(cxt->scratch_state.state3);
|
|
uint32_t *autoRepeatTimer = &(cxt->scratch_state.state4);
|
|
uint16_t *currentMenuLength = &(cxt->scratch_state.state5);
|
|
uint16_t *isRenderingHelp = &(cxt->scratch_state.state6);
|
|
|
|
const menuitem *currentMenu;
|
|
// Draw the currently on screen item
|
|
uint16_t currentScreen;
|
|
if (*subEntry == 0) {
|
|
// Drawing main menu
|
|
currentMenu = rootSettingsMenu;
|
|
currentScreen = *mainEntry;
|
|
} else {
|
|
// Drawing sub menu
|
|
currentMenu = subSettingsMenus[*mainEntry];
|
|
currentScreen = (*subEntry) - 1;
|
|
}
|
|
render_menu(&(currentMenu[currentScreen]), cxt);
|
|
|
|
// Update the cached menu length if unknown
|
|
if (*currentMenuLength == 0) {
|
|
// We walk the current menu to find the length
|
|
*currentMenuLength = getMenuLength(currentMenu, 128 /* Max length of any menu*/);
|
|
}
|
|
|
|
if (*isRenderingHelp == 0) {
|
|
// Draw scroll
|
|
|
|
// Get virtual pos by counting entries from start to _here_
|
|
uint16_t currentVirtualPosition = getMenuLength(currentMenu, currentScreen + 1);
|
|
if (currentVirtualPosition > 0) {
|
|
currentVirtualPosition--;
|
|
}
|
|
// The height of the indicator is screen res height / total menu entries
|
|
uint8_t indicatorHeight = OLED_HEIGHT / *currentMenuLength;
|
|
|
|
if (indicatorHeight == 0) {
|
|
indicatorHeight = 1; // always at least 1 pixel
|
|
}
|
|
|
|
uint16_t position = (OLED_HEIGHT * (uint16_t)currentVirtualPosition) / *currentMenuLength;
|
|
|
|
bool showScrollbar = true;
|
|
|
|
// Store if its the last option for this setting
|
|
bool isLastOptionForSetting = false;
|
|
if ((int)currentMenu[currentScreen].autoSettingOption < (int)SettingsOptions::SettingsOptionsLength) {
|
|
isLastOptionForSetting = isLastSettingValue(currentMenu[currentScreen].autoSettingOption);
|
|
}
|
|
|
|
// Last settings menu entry, reset scroll show back so it flashes
|
|
if (isLastOptionForSetting) {
|
|
showScrollbar = false;
|
|
}
|
|
|
|
// Or Flash it
|
|
showScrollbar |= (xTaskGetTickCount() % (TICKS_SECOND / 4) < (TICKS_SECOND / 8));
|
|
|
|
if (showScrollbar) {
|
|
OLED::drawScrollIndicator((uint8_t)position, indicatorHeight);
|
|
}
|
|
}
|
|
// Now handle user button input
|
|
|
|
auto callIncrementHandler = [&]() {
|
|
if (currentMenu[currentScreen].incrementHandler != nullptr) {
|
|
currentMenu[currentScreen].incrementHandler();
|
|
} else if ((int)currentMenu[currentScreen].autoSettingOption < (int)SettingsOptions::SettingsOptionsLength) {
|
|
nextSettingValue(currentMenu[currentScreen].autoSettingOption);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
//
|
|
OperatingMode newMode = OperatingMode::SettingsMenu;
|
|
switch (buttons) {
|
|
case BUTTON_NONE:
|
|
(*autoRepeatAcceleration) = 0; // reset acceleration
|
|
(*autoRepeatTimer) = 0; // reset acceleration
|
|
break;
|
|
case BUTTON_BOTH:
|
|
if (*subEntry == 0) {
|
|
saveSettings();
|
|
cxt->transitionMode = TransitionAnimation::Left;
|
|
return OperatingMode::HomeScreen;
|
|
} else {
|
|
cxt->transitionMode = TransitionAnimation::Left;
|
|
*subEntry = 0;
|
|
return OperatingMode::SettingsMenu;
|
|
}
|
|
break;
|
|
|
|
case BUTTON_F_LONG:
|
|
if (xTaskGetTickCount() + (*autoRepeatAcceleration) > (*autoRepeatTimer) + PRESS_ACCEL_INTERVAL_MAX) {
|
|
callIncrementHandler();
|
|
// Update the check for if its the last version
|
|
bool isLastOptionForSetting = false;
|
|
if ((int)currentMenu[currentScreen].autoSettingOption < (int)SettingsOptions::SettingsOptionsLength) {
|
|
isLastOptionForSetting = isLastSettingValue(currentMenu[currentScreen].autoSettingOption);
|
|
}
|
|
|
|
if (isLastOptionForSetting) {
|
|
(*autoRepeatTimer) = TICKS_SECOND * 2;
|
|
} else {
|
|
(*autoRepeatTimer) = 0;
|
|
}
|
|
(*autoRepeatTimer) += xTaskGetTickCount();
|
|
(*autoRepeatAcceleration) += PRESS_ACCEL_STEP;
|
|
*currentMenuLength = 0; // Reset incase menu visible changes
|
|
}
|
|
break;
|
|
case BUTTON_F_SHORT:
|
|
// Increment setting
|
|
if (*isRenderingHelp) {
|
|
*isRenderingHelp = 0;
|
|
} else {
|
|
*currentMenuLength = 0; // Reset incase menu visible changes
|
|
if (*subEntry == 0) {
|
|
// In a root menu, if its null handler we enter the menu
|
|
if (currentMenu[currentScreen].incrementHandler != nullptr) {
|
|
currentMenu[currentScreen].incrementHandler();
|
|
} else {
|
|
(*subEntry) += 1;
|
|
cxt->transitionMode = TransitionAnimation::Right;
|
|
}
|
|
} else {
|
|
callIncrementHandler();
|
|
}
|
|
}
|
|
break;
|
|
case BUTTON_B_LONG:
|
|
if (xTaskGetTickCount() + (*autoRepeatAcceleration) > (*autoRepeatTimer) + PRESS_ACCEL_INTERVAL_MAX) {
|
|
(*autoRepeatTimer) = xTaskGetTickCount();
|
|
(*autoRepeatAcceleration) += PRESS_ACCEL_STEP;
|
|
} else {
|
|
break;
|
|
}
|
|
/* Fall through*/
|
|
case BUTTON_B_SHORT:
|
|
// Increment menu item
|
|
|
|
newMode = moveToNextEntry(cxt);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if ((PRESS_ACCEL_INTERVAL_MAX - (*autoRepeatAcceleration)) < PRESS_ACCEL_INTERVAL_MIN) {
|
|
(*autoRepeatAcceleration) = PRESS_ACCEL_INTERVAL_MAX - PRESS_ACCEL_INTERVAL_MIN;
|
|
}
|
|
|
|
// Otherwise we stay put for next render iteration
|
|
return newMode;
|
|
} |