/* * Copyright (C) 2019-2025 Roger Clark, VK3KYY / G4KYF * Daniel Caujolle-Bert, F1RMB * * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * 4. Use of this source code or binary releases for commercial purposes is strictly forbidden. This includes, without limitation, * incorporation in a commercial product or incorporation into a product or project which allows commercial use. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #include "functions/settings.h" #include "user_interface/menuSystem.h" #include "user_interface/uiLocalisation.h" #include "user_interface/uiUtilities.h" #include "interfaces/wdog.h" #include "utils.h" #if defined(HAS_COLOURS) static const char *themeEntries[THEME_ITEM_MAX]; static uint16_t userThemeData[THEME_ITEM_MAX] = { 0xDEAD, 0xBEEF }; static int32_t currentThemeEntry = 0; static DayTime_t currentThemeDaytime = UNDEFINED; static void updateScreen(bool isFirstRun); static void handleEvent(uiEvent_t *ev); static void resetUserThemeData(void); // ------ #define COLOUR_CHANNEL_UNSET 0xFFFF #define COLOUR_RGB888_UNSET 0xDEADBEEF typedef enum { COLOUR_CHANNEL_RED = 0, COLOUR_CHANNEL_GREEN, COLOUR_CHANNEL_BLUE, COLOUR_CHANNEL_MAX } colourChannel_t; typedef struct { const char *name; int32_t value; // we need to handle < 0 and 0xFFFF } colourChannelData_t; static colourChannelData_t colourChannelsData[COLOUR_CHANNEL_MAX] = { { NULL, COLOUR_CHANNEL_UNSET }, { NULL, COLOUR_CHANNEL_UNSET }, { NULL, COLOUR_CHANNEL_UNSET } }; typedef struct { uint16_t maxValue; uint8_t step; } channelConstraint_t; static channelConstraint_t channelConstraints[COLOUR_CHANNEL_MAX] = { { 248, 8 }, // RED { 252, 4 }, // GREEN { 248, 8 }, // BLUE }; static uint32_t colourToEditRGB888 = COLOUR_RGB888_UNSET; static void updateBrowerScreen(bool isFirstRun); static void handleBrowserEvent(uiEvent_t *ev); static void exitBrowserCallback(void *data); static void updatePickerScreen(bool isFirstRun); static void handlePickerEvent(uiEvent_t *ev); static void exitPickerCallback(void *data); static void resetColourChannelsData(void); static menuStatus_t menuOptionsExitCode = MENU_STATUS_SUCCESS; menuStatus_t menuThemeOptions(uiEvent_t *ev, bool isFirstRun) { if (isFirstRun) { currentThemeDaytime = (DayTime_t)uiDataGlobal.currentSelectedChannelNumber; menuDataGlobal.numItems = (NIGHT + 1); //menuDataGlobal.currentItemIndex = 0; voicePromptsInit(); voicePromptsAppendPrompt(PROMPT_SILENCE); voicePromptsAppendLanguageString(currentLanguage->theme_chooser); voicePromptsAppendLanguageString(currentLanguage->menu); voicePromptsAppendPrompt(PROMPT_SILENCE); updateScreen(true); return (MENU_STATUS_LIST_TYPE | MENU_STATUS_SUCCESS); } else { menuOptionsExitCode = MENU_STATUS_SUCCESS; if (ev->hasEvent) { handleEvent(ev); } } return menuOptionsExitCode; } static void updateScreen(bool isFirstRun) { int mNum = 0; char buf[SCREEN_LINE_BUFFER_SIZE]; const char *leftSide = NULL;// initialize to please the compiler displayClearBuf(); menuDisplayTitle(currentLanguage->theme_chooser); for (int i = MENU_START_ITERATION_VALUE; i <= MENU_END_ITERATION_VALUE; i++) { if (menuDataGlobal.numItems <= (i + 1)) { break; } mNum = menuGetMenuOffset(menuDataGlobal.numItems, i); if (mNum == MENU_OFFSET_BEFORE_FIRST_ENTRY) { continue; } else if (mNum == MENU_OFFSET_AFTER_LAST_ENTRY) { break; } buf[0] = 0; buf[2] = 0; leftSide = NULL; switch(mNum) { default: leftSide = (const char *)((((DayTime_t)mNum) == DAY) ? currentLanguage->daytime_theme_day : currentLanguage->daytime_theme_night); break; } snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "%s", leftSide); if (i == 0) { bool wasPlaying = voicePromptsIsPlaying(); if (!isFirstRun) { voicePromptsInit(); } if (!wasPlaying || (menuDataGlobal.newOptionSelected || (menuDataGlobal.menuOptionsTimeout > 0))) { voicePromptsAppendLanguageString(leftSide); } promptsPlayNotAfterTx(); } menuDisplayEntry(i, mNum, buf, 0, THEME_ITEM_FG_MENU_ITEM, THEME_ITEM_COLOUR_NONE, THEME_ITEM_BG); } displayRender(); } static void handleEvent(uiEvent_t *ev) { bool isDirty = false; if (ev->events & BUTTON_EVENT) { if (repeatVoicePromptOnSK1(ev)) { return; } } if (ev->events & FUNCTION_EVENT) { if (ev->function == FUNC_REDRAW) { isDirty = true; //updateScreen(false); //return; } else if ((QUICKKEY_TYPE(ev->function) == QUICKKEY_MENU) && (QUICKKEY_ENTRYID(ev->function) < (NIGHT + 1))) { isDirty = true; menuDataGlobal.currentItemIndex = QUICKKEY_ENTRYID(ev->function); //updateScreen(false); } } if (ev->events & KEY_EVENT) { if (KEYCHECK_PRESS(ev->keys, KEY_DOWN) && (menuDataGlobal.numItems != 0)) { isDirty = true; menuSystemMenuIncrement(&menuDataGlobal.currentItemIndex, (NIGHT + 1)); menuDataGlobal.newOptionSelected = true; menuOptionsExitCode |= MENU_STATUS_LIST_TYPE; } else if (KEYCHECK_PRESS(ev->keys, KEY_UP)) { isDirty = true; menuSystemMenuDecrement(&menuDataGlobal.currentItemIndex, (NIGHT + 1)); menuDataGlobal.newOptionSelected = true; menuOptionsExitCode |= MENU_STATUS_LIST_TYPE; } else if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN)) { currentThemeDaytime = (DayTime_t)menuDataGlobal.currentItemIndex; menuSystemPushNewMenu(MENU_THEME_ITEMS_BROWSER); return; } else if (KEYCHECK_SHORTUP(ev->keys, KEY_RED)) { menuSystemPopPreviousMenu(); return; } else if (KEYCHECK_SHORTUP_NUMBER(ev->keys) && BUTTONCHECK_DOWN(ev, BUTTON_SK2)) { saveQuickkeyMenuIndex(ev->keys.key, menuSystemGetCurrentMenuNumber(), menuDataGlobal.currentItemIndex, 0); isDirty = true; } } if (isDirty) { updateScreen(false); } } menuStatus_t menuThemeItemsBrowser(uiEvent_t *ev, bool isFirstRun) { if (isFirstRun) { menuDataGlobal.menuOptionsSetQuickkey = 0; menuDataGlobal.menuOptionsTimeout = 0; menuDataGlobal.newOptionSelected = true; menuDataGlobal.numItems = THEME_ITEM_MAX; themeEntries[THEME_ITEM_FG_DEFAULT] = currentLanguage->theme_fg_default; themeEntries[THEME_ITEM_BG] = currentLanguage->theme_bg; themeEntries[THEME_ITEM_FG_DECORATION] = currentLanguage->theme_fg_decoration; themeEntries[THEME_ITEM_FG_TEXT_INPUT] = currentLanguage->theme_fg_text_input; themeEntries[THEME_ITEM_FG_SPLASHSCREEN] = currentLanguage->theme_fg_splashscreen; themeEntries[THEME_ITEM_BG_SPLASHSCREEN] = currentLanguage->theme_bg_splashscreen; themeEntries[THEME_ITEM_FG_NOTIFICATION] = currentLanguage->theme_fg_notification; themeEntries[THEME_ITEM_FG_WARNING_NOTIFICATION] = currentLanguage->theme_fg_warning_notification; themeEntries[THEME_ITEM_FG_ERROR_NOTIFICATION] = currentLanguage->theme_fg_error_notification; themeEntries[THEME_ITEM_BG_NOTIFICATION] = currentLanguage->theme_bg_notification; themeEntries[THEME_ITEM_FG_MENU_NAME] = currentLanguage->theme_fg_menu_name; themeEntries[THEME_ITEM_BG_MENU_NAME] = currentLanguage->theme_bg_menu_name; themeEntries[THEME_ITEM_FG_MENU_ITEM] = currentLanguage->theme_fg_menu_item; themeEntries[THEME_ITEM_BG_MENU_ITEM_SELECTED] = currentLanguage->theme_fg_menu_item_selected; themeEntries[THEME_ITEM_FG_OPTIONS_VALUE] = currentLanguage->theme_fg_options_value; themeEntries[THEME_ITEM_FG_HEADER_TEXT] = currentLanguage->theme_fg_header_text; themeEntries[THEME_ITEM_BG_HEADER_TEXT] = currentLanguage->theme_bg_header_text; themeEntries[THEME_ITEM_FG_RSSI_BAR] = currentLanguage->theme_fg_rssi_bar; themeEntries[THEME_ITEM_FG_RSSI_BAR_S9P] = currentLanguage->theme_fg_rssi_bar_s9p; themeEntries[THEME_ITEM_FG_CHANNEL_NAME] = currentLanguage->theme_fg_channel_name; themeEntries[THEME_ITEM_FG_CHANNEL_CONTACT] = currentLanguage->theme_fg_channel_contact; themeEntries[THEME_ITEM_FG_CHANNEL_CONTACT_INFO] = currentLanguage->theme_fg_channel_contact_info; themeEntries[THEME_ITEM_FG_ZONE_NAME] = currentLanguage->theme_fg_zone_name; themeEntries[THEME_ITEM_FG_RX_FREQ] = currentLanguage->theme_fg_rx_freq; themeEntries[THEME_ITEM_FG_TX_FREQ] = currentLanguage->theme_fg_tx_freq; themeEntries[THEME_ITEM_FG_CSS_SQL_VALUES] = currentLanguage->theme_fg_css_sql_values; themeEntries[THEME_ITEM_FG_TX_COUNTER] = currentLanguage->theme_fg_tx_counter; themeEntries[THEME_ITEM_FG_POLAR_DRAWING] = currentLanguage->theme_fg_polar_drawing; themeEntries[THEME_ITEM_FG_SATELLITE_COLOUR] = currentLanguage->theme_fg_satellite_colour; themeEntries[THEME_ITEM_FG_GPS_NUMBER] = currentLanguage->theme_fg_gps_number; themeEntries[THEME_ITEM_FG_GPS_COLOUR] = currentLanguage->theme_fg_gps_colour; themeEntries[THEME_ITEM_FG_BD_COLOUR] = currentLanguage->theme_fg_bd_colour; // Has to make a working copy of the global theme if ((userThemeData[0] == 0xDEAD) && (userThemeData[1] == 0xBEEF)) { memcpy(&userThemeData, &themeItems[currentThemeDaytime], (sizeof(uint16_t) * THEME_ITEM_MAX)); } else { // Apply the edited colour to the edited theme entry if (colourToEditRGB888 != COLOUR_RGB888_UNSET) { userThemeData[menuDataGlobal.currentItemIndex] = displayConvertRGB888ToNative(colourToEditRGB888); colourToEditRGB888 = COLOUR_RGB888_UNSET; } } voicePromptsInit(); voicePromptsAppendPrompt(PROMPT_SILENCE); voicePromptsAppendLanguageString(currentLanguage->theme_options); voicePromptsAppendLanguageString(currentLanguage->menu); voicePromptsAppendPrompt(PROMPT_SILENCE); menuSystemRegisterExitCallback(exitBrowserCallback, NULL); updateBrowerScreen(true); return (MENU_STATUS_LIST_TYPE | MENU_STATUS_SUCCESS); } else { menuOptionsExitCode = MENU_STATUS_SUCCESS; if (ev->hasEvent) { handleBrowserEvent(ev); } } return menuOptionsExitCode; } static void updateBrowerScreen(bool isFirstRun) { int mNum = 0; char buf[SCREEN_LINE_BUFFER_SIZE]; const char *leftSide = NULL;// initialize to please the compiler displayClearBuf(); menuDisplayTitle(currentLanguage->theme_options); for (int i = MENU_START_ITERATION_VALUE; i <= MENU_END_ITERATION_VALUE; i++) { mNum = menuGetMenuOffset(THEME_ITEM_MAX, i); if (mNum == MENU_OFFSET_BEFORE_FIRST_ENTRY) { continue; } else if (mNum == MENU_OFFSET_AFTER_LAST_ENTRY) { break; } buf[0] = 0; buf[2] = 0; leftSide = NULL; switch(mNum) { default: leftSide = themeEntries[mNum]; break; } snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "%s", leftSide); if (i == 0) { bool wasPlaying = voicePromptsIsPlaying(); if (!isFirstRun) { voicePromptsInit(); } if (!wasPlaying || (menuDataGlobal.newOptionSelected || (menuDataGlobal.menuOptionsTimeout > 0))) { voicePromptsAppendLanguageString(leftSide); } promptsPlayNotAfterTx(); } menuDisplayEntry(i, mNum, buf, 0, THEME_ITEM_FG_MENU_ITEM, THEME_ITEM_COLOUR_NONE, THEME_ITEM_BG); // Draw the color rectangle int16_t x = (DISPLAY_X_POS_MENU_TEXT_OFFSET + (16 * 8) + 2); int16_t y = DISPLAY_Y_POS_MENU_ENTRY_HIGHLIGHT + (i * MENU_ENTRY_HEIGHT) + 2; uint16_t fgColour, bgColour; displayGetForegroundAndBackgroundColours(&fgColour, &bgColour); displaySetForegroundAndBackgroundColours(userThemeData[mNum], themeItems[DAYTIME_CURRENT][THEME_ITEM_BG]); displayFillRect(x, y, 14, 12, false); // Outline the color rectangle when it's equal to background or menu focused colour if (((i != 0) && (bgColour == userThemeData[mNum])) || ((i == 0) && (userThemeData[mNum] == themeItems[DAYTIME_CURRENT][THEME_ITEM_BG_MENU_ITEM_SELECTED]))) { uint32_t invertedColour = (0xFFFFFF - __builtin_bswap16(PLATFORM_COLOUR_FORMAT_TO_RGB888(userThemeData[mNum]))); displaySetForegroundAndBackgroundColours(displayConvertRGB888ToNative(invertedColour), themeItems[DAYTIME_CURRENT][THEME_ITEM_BG_MENU_ITEM_SELECTED]); displayDrawRect(x, y, 14, 12, true); } displayThemeResetToDefault(); } displayRender(); } static void handleBrowserEvent(uiEvent_t *ev) { bool isDirty = false; if (ev->events & BUTTON_EVENT) { if (repeatVoicePromptOnSK1(ev)) { return; } } if ((ev->events & FUNCTION_EVENT) && (ev->function == FUNC_REDRAW)) { updateBrowerScreen(false); return; } if (ev->events & KEY_EVENT) { if (KEYCHECK_PRESS(ev->keys, KEY_DOWN) && (menuDataGlobal.numItems != 0)) { isDirty = true; menuSystemMenuIncrement(&menuDataGlobal.currentItemIndex, THEME_ITEM_MAX); menuDataGlobal.newOptionSelected = true; menuOptionsExitCode |= MENU_STATUS_LIST_TYPE; } else if (KEYCHECK_PRESS(ev->keys, KEY_UP)) { isDirty = true; menuSystemMenuDecrement(&menuDataGlobal.currentItemIndex, THEME_ITEM_MAX); menuDataGlobal.newOptionSelected = true; menuOptionsExitCode |= MENU_STATUS_LIST_TYPE; } else if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN)) { // User ask to go back to the default (hardcoded) theme if (BUTTONCHECK_DOWN(ev, BUTTON_SK1)) { themeInitToDefaultValues(currentThemeDaytime, ((currentThemeDaytime == DAY) ? false : true)); } else { // Apply edited theme memcpy(&themeItems[currentThemeDaytime], &userThemeData, (sizeof(uint16_t) * THEME_ITEM_MAX)); } // Save user's theme to flash, as a CustomData block if (BUTTONCHECK_DOWN(ev, BUTTON_SK2)) { displayThemeSaveToFlash(currentThemeDaytime); } displayThemeResetToDefault(); menuSystemPopAllAndDisplayRootMenu(); return; } else if (KEYCHECK_SHORTUP(ev->keys, KEY_RED)) { menuSystemPopPreviousMenu(); return; } else if (KEYCHECK_SHORTUP(ev->keys, KEY_HASH)) { colourToEditRGB888 = PLATFORM_COLOUR_FORMAT_TO_RGB888(__builtin_bswap16(userThemeData[menuDataGlobal.currentItemIndex])); currentThemeEntry = menuDataGlobal.currentItemIndex; menuSystemPushNewMenu(MENU_COLOUR_PICKER); return; } } if (isDirty) { updateBrowerScreen(false); } } static void exitBrowserCallback(void *data) { resetUserThemeData(); } // // Colour Picker // menuStatus_t menuColourPicker(uiEvent_t *ev, bool isFirstRun) { if (isFirstRun) { menuDataGlobal.menuOptionsSetQuickkey = 0; menuDataGlobal.menuOptionsTimeout = 0; menuDataGlobal.newOptionSelected = true; menuDataGlobal.numItems = COLOUR_CHANNEL_MAX; if ((colourChannelsData[COLOUR_CHANNEL_RED].value == COLOUR_CHANNEL_UNSET) && (colourChannelsData[COLOUR_CHANNEL_GREEN].value == COLOUR_CHANNEL_UNSET) && (colourChannelsData[COLOUR_CHANNEL_BLUE].value == COLOUR_CHANNEL_UNSET)) { colourChannelsData[COLOUR_CHANNEL_RED].name = currentLanguage->theme_colour_picker_red; colourChannelsData[COLOUR_CHANNEL_RED].value = (colourToEditRGB888 & 0xFF0000) >> 16; colourChannelsData[COLOUR_CHANNEL_GREEN].name = currentLanguage->theme_colour_picker_green; colourChannelsData[COLOUR_CHANNEL_GREEN].value = (colourToEditRGB888 & 0xFF00) >> 8; colourChannelsData[COLOUR_CHANNEL_BLUE].name = currentLanguage->theme_colour_picker_blue; colourChannelsData[COLOUR_CHANNEL_BLUE].value = (colourToEditRGB888 & 0xFF); } voicePromptsInit(); voicePromptsAppendPrompt(PROMPT_SILENCE); voicePromptsAppendLanguageString(currentLanguage->theme_options); voicePromptsAppendLanguageString(themeEntries[currentThemeEntry]); voicePromptsAppendPrompt(PROMPT_SILENCE); menuSystemRegisterExitCallback(exitPickerCallback, NULL); updatePickerScreen(true); return (MENU_STATUS_LIST_TYPE | MENU_STATUS_SUCCESS); } else { menuOptionsExitCode = MENU_STATUS_SUCCESS; if (ev->hasEvent) { handlePickerEvent(ev); } } return menuOptionsExitCode; } static uint32_t getColourInEditionRGB888(void) { return ((colourChannelsData[COLOUR_CHANNEL_RED].value << 16) | (colourChannelsData[COLOUR_CHANNEL_GREEN].value << 8) | colourChannelsData[COLOUR_CHANNEL_BLUE].value); } static void updatePickerScreen(bool isFirstRun) { int mNum = 0; char buf[SCREEN_LINE_BUFFER_SIZE]; const char *leftSide = NULL;// initialize to please the compiler char rightSideVar[SCREEN_LINE_BUFFER_SIZE]; displayClearBuf(); menuDisplayTitle(themeEntries[currentThemeEntry]); for (int i = MENU_START_ITERATION_VALUE; i <= MENU_END_ITERATION_VALUE; i++) { mNum = menuGetMenuOffset(COLOUR_CHANNEL_MAX, i); if (mNum == MENU_OFFSET_BEFORE_FIRST_ENTRY) { continue; } else if (mNum == MENU_OFFSET_AFTER_LAST_ENTRY) { break; } buf[0] = 0; buf[2] = 0; leftSide = NULL; rightSideVar[0] = 0; switch(mNum) { default: leftSide = colourChannelsData[mNum].name; snprintf(rightSideVar, SCREEN_LINE_BUFFER_SIZE, "%d", (settingsIsOptionBitSet(BIT_INVERSE_VIDEO) ? (channelConstraints[mNum].maxValue - colourChannelsData[mNum].value) : colourChannelsData[mNum].value)); break; } snprintf(buf, SCREEN_LINE_BUFFER_SIZE, "%s:%s", leftSide, rightSideVar); if (i == 0) { bool wasPlaying = voicePromptsIsPlaying(); if (!isFirstRun) { voicePromptsInit(); } if (!wasPlaying || (menuDataGlobal.newOptionSelected || (menuDataGlobal.menuOptionsTimeout > 0))) { voicePromptsAppendLanguageString(leftSide); } voicePromptsAppendString(rightSideVar); promptsPlayNotAfterTx(); } // We're cheating the menu vertical position, moving it to the bottom of the screen. menuDisplayEntry((i + 2), mNum, buf, (strlen(leftSide) + 1), THEME_ITEM_FG_MENU_ITEM, THEME_ITEM_FG_OPTIONS_VALUE, THEME_ITEM_BG); // Display color sample. uint32_t invertedColour = (0xFFFFFF - __builtin_bswap16(PLATFORM_COLOUR_FORMAT_TO_RGB888(userThemeData[THEME_ITEM_BG]))); displaySetForegroundAndBackgroundColours(displayConvertRGB888ToNative(getColourInEditionRGB888()), themeItems[DAYTIME_CURRENT][THEME_ITEM_BG]); displayFillRect(20, 25, (DISPLAY_SIZE_X - 40), 48, false); displaySetForegroundAndBackgroundColours(displayConvertRGB888ToNative(invertedColour), themeItems[DAYTIME_CURRENT][THEME_ITEM_BG]); displayDrawRect(20, 25, (DISPLAY_SIZE_X - 40), 48, true); displayDrawRect(21, 26, (DISPLAY_SIZE_X - 40) - 2, 46, true); displayThemeResetToDefault(); } displayRender(); } static void handlePickerEvent(uiEvent_t *ev) { bool isDirty = false; bool isInverted = settingsIsOptionBitSet(BIT_INVERSE_VIDEO); if (ev->events & BUTTON_EVENT) { if (repeatVoicePromptOnSK1(ev)) { return; } } if ((ev->events & FUNCTION_EVENT) && (ev->function == FUNC_REDRAW)) { isDirty = true; } if (ev->events & KEY_EVENT) { if (KEYCHECK_PRESS(ev->keys, KEY_DOWN) && (menuDataGlobal.numItems != 0)) { isDirty = true; menuSystemMenuIncrement(&menuDataGlobal.currentItemIndex, COLOUR_CHANNEL_MAX); menuDataGlobal.newOptionSelected = true; menuOptionsExitCode |= MENU_STATUS_LIST_TYPE; } else if (KEYCHECK_PRESS(ev->keys, KEY_UP)) { isDirty = true; menuSystemMenuDecrement(&menuDataGlobal.currentItemIndex, COLOUR_CHANNEL_MAX); menuDataGlobal.newOptionSelected = true; menuOptionsExitCode |= MENU_STATUS_LIST_TYPE; } else if (KEYCHECK_SHORTUP(ev->keys, KEY_GREEN)) { // Build New Colour colourToEditRGB888 = getColourInEditionRGB888(); resetColourChannelsData(); menuSystemPopPreviousMenu(); return; } else if (KEYCHECK_SHORTUP(ev->keys, KEY_RED)) { menuSystemPopPreviousMenu(); return; } else if (KEYCHECK_PRESS(ev->keys, (isInverted ? KEY_LEFT : KEY_RIGHT)) #if defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017) || KEYCHECK_SHORTUP(ev->keys, (isInverted ? KEY_ROTARY_DECREMENT : KEY_ROTARY_INCREMENT)) #endif ) { isDirty = true; colourChannelsData[menuDataGlobal.currentItemIndex].value += (channelConstraints[menuDataGlobal.currentItemIndex].step * (BUTTONCHECK_DOWN(ev, BUTTON_SK2) ? 3 : 1)); if (colourChannelsData[menuDataGlobal.currentItemIndex].value > channelConstraints[menuDataGlobal.currentItemIndex].maxValue) { colourChannelsData[menuDataGlobal.currentItemIndex].value = channelConstraints[menuDataGlobal.currentItemIndex].maxValue; } } else if (KEYCHECK_PRESS(ev->keys, (isInverted ? KEY_RIGHT : KEY_LEFT)) #if defined(PLATFORM_RT84_DM1701) || defined(PLATFORM_MD2017) || KEYCHECK_SHORTUP(ev->keys, (isInverted ? KEY_ROTARY_INCREMENT : KEY_ROTARY_DECREMENT)) #endif ) { isDirty = true; colourChannelsData[menuDataGlobal.currentItemIndex].value -= (channelConstraints[menuDataGlobal.currentItemIndex].step * (BUTTONCHECK_DOWN(ev, BUTTON_SK2) ? 3 : 1)); if (colourChannelsData[menuDataGlobal.currentItemIndex].value < 0) { colourChannelsData[menuDataGlobal.currentItemIndex].value = 0; } } } if (isDirty) { updatePickerScreen(false); } } static void exitPickerCallback(void *data) { if ((colourChannelsData[COLOUR_CHANNEL_RED].value != COLOUR_CHANNEL_UNSET) && (colourChannelsData[COLOUR_CHANNEL_GREEN].value != COLOUR_CHANNEL_UNSET) && (colourChannelsData[COLOUR_CHANNEL_BLUE].value != COLOUR_CHANNEL_UNSET)) { colourToEditRGB888 = COLOUR_RGB888_UNSET; resetColourChannelsData(); } } static void resetUserThemeData(void) { userThemeData[0] = 0xDEAD; userThemeData[1] = 0xBEEF; } static void resetColourChannelsData(void) { colourChannelsData[COLOUR_CHANNEL_RED].value = colourChannelsData[COLOUR_CHANNEL_GREEN].value = colourChannelsData[COLOUR_CHANNEL_BLUE].value = COLOUR_CHANNEL_UNSET; } #endif // HAS_COLOURS