From 566b4ce6faf3f7c9f092026a22a8c0e240799b17 Mon Sep 17 00:00:00 2001 From: Peter Date: Wed, 19 May 2021 11:19:37 +1000 Subject: [PATCH 001/122] Add mention of ICONS8 Expand on once specifics are known. --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3f88f97c4..289c5d3f4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,10 @@ -## EdgeTX +# EdgeTX [![Commit Tests](https://github.com/EdgeTX/edgetx/actions/workflows/actions.yml/badge.svg)](https://github.com/EdgeTX/edgetx/actions/workflows/actions.yml) EdgeTX is the edge version of OpenTX. This is where edge cutting new stuff will be released, with an emphasis on new developments rather than maintenance and legacy. Stay tuned for more! + +## Acknowedgements +Some icon assets provided by [ICONS8](https://icons8.com). From 343073cfedeefd39dd5056e14113d02e817c2cd5 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 19 May 2021 22:06:53 +0200 Subject: [PATCH 002/122] Set mixer scheduler period to 0 in stopPulsesXxxxModule() --- radio/src/pulses/pulses.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/radio/src/pulses/pulses.cpp b/radio/src/pulses/pulses.cpp index 8ba672aa0..0ec02e49e 100755 --- a/radio/src/pulses/pulses.cpp +++ b/radio/src/pulses/pulses.cpp @@ -580,6 +580,7 @@ bool setupPulsesInternalModule(uint8_t protocol) void stopPulsesInternalModule() { if (moduleState[INTERNAL_MODULE].protocol != PROTOCOL_CHANNELS_UNINITIALIZED) { + mixerSchedulerSetPeriod(INTERNAL_MODULE, 0); intmoduleStop(); moduleState[INTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; } @@ -607,6 +608,7 @@ bool setupPulsesInternalModule() void stopPulsesExternalModule() { if (moduleState[EXTERNAL_MODULE].protocol != PROTOCOL_CHANNELS_UNINITIALIZED) { + mixerSchedulerSetPeriod(EXTERNAL_MODULE, 0); extmoduleStop(); moduleState[EXTERNAL_MODULE].protocol = PROTOCOL_CHANNELS_NONE; } From 2f27c3c1a6b0f8181f492f3fec3c6ff73f5115c4 Mon Sep 17 00:00:00 2001 From: Hydra Date: Thu, 20 May 2021 20:22:52 +0200 Subject: [PATCH 003/122] Add OneBit mode to HARDWARE menu. OneBit mode can help if your radio has a slow inverter circuit by changing the UART sampling behaviour. * It was found that the X9D+ and X7 (maybe others) have a slow inveter that messes up reception of fast (400,000baud) UART signals. On the scope the rise time of the signal is very long. * See RM0033 24.3.3 Receiver / Selecting the proper oversampling method. Page 631. Also note that reducing the pull-up resistor from 10k to 1k at R82 (X9D+) / R406 (X7) will also help with the rise times. However on the X7 changing the resistor is not enough, OneBit is needed too. --- radio/src/dataconstants.h | 7 ++++ radio/src/datastructs.h | 15 ++++---- .../src/gui/common/stdlcd/radio_hardware.cpp | 37 ++++++++++++++----- .../src/targets/taranis/telemetry_driver.cpp | 3 ++ radio/src/translations.cpp | 2 + radio/src/translations.h | 2 + radio/src/translations/cz.h.txt | 4 ++ radio/src/translations/de.h.txt | 4 ++ radio/src/translations/en.h.txt | 5 +++ radio/src/translations/fi.h.txt | 4 ++ radio/src/translations/fr.h.txt | 5 +++ radio/src/translations/it.h.txt | 4 ++ radio/src/translations/nl.h.txt | 4 ++ radio/src/translations/pl.h.txt | 4 ++ radio/src/translations/pt.h.txt | 4 ++ radio/src/translations/se.h.txt | 4 ++ 16 files changed, 92 insertions(+), 16 deletions(-) diff --git a/radio/src/dataconstants.h b/radio/src/dataconstants.h index 1c5fdc1ea..cf35f2957 100644 --- a/radio/src/dataconstants.h +++ b/radio/src/dataconstants.h @@ -909,6 +909,13 @@ enum BluetoothModes { #endif }; +enum UartSampleModes { + UART_SAMPLE_MODE_NORMAL = 0, + UART_SAMPLE_MODE_ONEBIT, + + UART_SAMPLE_MODE_MAX = UART_SAMPLE_MODE_ONEBIT +}; + // PXX2 constants #define PXX2_LEN_REGISTRATION_ID 8 #define PXX2_LEN_RX_NAME 8 diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 765245b3e..12c119302 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -821,6 +821,7 @@ PACK(struct RadioData { NOBACKUP(int8_t varioPitch); NOBACKUP(int8_t varioRange); NOBACKUP(int8_t varioRepeat); + NOBACKUP(int8_t uartSampleMode:2); // See UartSampleModes CustomFunctionData customFn[MAX_SPECIAL_FUNCTIONS]; EXTRA_GENERAL_FIELDS @@ -946,25 +947,25 @@ static inline void check_struct() CHKSIZE(TrainerData, 16); #if defined(PCBXLITES) - CHKSIZE(RadioData, 860); + CHKSIZE(RadioData, 861); CHKSIZE(ModelData, 6157); #elif defined(PCBXLITE) - CHKSIZE(RadioData, 858); + CHKSIZE(RadioData, 859); CHKSIZE(ModelData, 6157); #elif defined(PCBX7) - CHKSIZE(RadioData, 864); + CHKSIZE(RadioData, 865); CHKSIZE(ModelData, 6157); #elif defined(PCBX9E) - CHKSIZE(RadioData, 960); + CHKSIZE(RadioData, 961); CHKSIZE(ModelData, 6614); #elif defined(PCBX9D) || defined(PCBX9DP) - CHKSIZE(RadioData, 898); + CHKSIZE(RadioData, 899); CHKSIZE(ModelData, 6604); #elif defined(PCBSKY9X) - CHKSIZE(RadioData, 735); + CHKSIZE(RadioData, 736); CHKSIZE(ModelData, 5301); #elif defined(PCBHORUS) - CHKSIZE(RadioData, 881); + CHKSIZE(RadioData, 882); CHKSIZE(ModelData, 9736); #endif diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index 7f38f692e..1b82b205c 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -168,6 +168,8 @@ enum { ITEM_RADIO_HARDWARE_SERIAL_BAUDRATE, #endif + ITEM_RADIO_HARDWARE_SERIAL_SAMPLE_MODE, + #if defined(BLUETOOTH) ITEM_RADIO_HARDWARE_BLUETOOTH_MODE, ITEM_RADIO_HARDWARE_BLUETOOTH_PAIRING_CODE, @@ -290,6 +292,8 @@ enum { #define MAX_BAUD_ROWS #endif +#define SERIAL_SAMPLE_MODE_ROWS 0, + #if defined(AUX_SERIAL) #define AUX_SERIAL_ROWS 0, #else @@ -321,6 +325,20 @@ void onFactoryResetConfirm(const char * result) } #endif +void restartExternalModule() +{ + if (!IS_EXTERNAL_MODULE_ON()) { + return; + } + pauseMixerCalculations(); + pausePulses(); + EXTERNAL_MODULE_OFF(); + RTOS_WAIT_MS(20); // 20ms so that the pulses interrupt will reinit the frame rate + telemetryProtocol = 255; // force telemetry port + module reinitialization + EXTERNAL_MODULE_ON(); + resumePulses(); + resumeMixerCalculations(); +} void menuRadioHardware(event_t event) { MENU(STR_HARDWARE, menuTabGeneral, MENU_RADIO_HARDWARE, HEADER_LINE + ITEM_RADIO_HARDWARE_MAX, { @@ -337,6 +355,7 @@ void menuRadioHardware(event_t event) RTC_ROW TX_CAPACITY_MEASUREMENT_ROWS MAX_BAUD_ROWS + SERIAL_SAMPLE_MODE_ROWS BLUETOOTH_ROWS EXTERNAL_ANTENNA_ROW AUX_SERIAL_ROWS @@ -549,20 +568,20 @@ void menuRadioHardware(event_t event) lcdDrawNumber(HW_SETTINGS_COLUMN2, y, CROSSFIRE_BAUDRATES[g_eeGeneral.telemetryBaudrate], attr|LEFT); if (attr) { g_eeGeneral.telemetryBaudrate = DIM(CROSSFIRE_BAUDRATES) - 1 - checkIncDecModel(event, DIM(CROSSFIRE_BAUDRATES) - 1 - g_eeGeneral.telemetryBaudrate, 0, DIM(CROSSFIRE_BAUDRATES) - 1); - if (checkIncDec_Ret && IS_EXTERNAL_MODULE_ON()) { - pauseMixerCalculations(); - pausePulses(); - EXTERNAL_MODULE_OFF(); - RTOS_WAIT_MS(20); // 20ms so that the pulses interrupt will reinit the frame rate - telemetryProtocol = 255; // force telemetry port + module reinitialization - EXTERNAL_MODULE_ON(); - resumePulses(); - resumeMixerCalculations(); + if (checkIncDec_Ret) { + restartExternalModule(); } } break; #endif + case ITEM_RADIO_HARDWARE_SERIAL_SAMPLE_MODE: + g_eeGeneral.uartSampleMode = editChoice(HW_SETTINGS_COLUMN2, y, STR_SAMPLE_MODE, STR_SAMPLE_MODES, g_eeGeneral.uartSampleMode, 0, UART_SAMPLE_MODE_MAX, attr, event); + if (attr && checkIncDec_Ret) { + restartExternalModule(); + } + break; + #if defined(BLUETOOTH) case ITEM_RADIO_HARDWARE_BLUETOOTH_MODE: lcdDrawTextAlignedLeft(y, STR_BLUETOOTH); diff --git a/radio/src/targets/taranis/telemetry_driver.cpp b/radio/src/targets/taranis/telemetry_driver.cpp index c16d6feca..08b79b0c7 100644 --- a/radio/src/targets/taranis/telemetry_driver.cpp +++ b/radio/src/targets/taranis/telemetry_driver.cpp @@ -77,6 +77,9 @@ void telemetryPortInit(uint32_t baudrate, uint8_t mode) } USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; + if (g_eeGeneral.uartSampleMode == UART_SAMPLE_MODE_ONEBIT) { + USART_OneBitMethodCmd(TELEMETRY_USART, ENABLE); + } USART_Init(TELEMETRY_USART, &USART_InitStructure); USART_Cmd(TELEMETRY_USART, ENABLE); diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 7cc5977d7..fea49bcda 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -80,6 +80,7 @@ ISTR(SWTYPES); ISTR(POTTYPES); ISTR(SLIDERTYPES); ISTR(ANTENNA_MODES); +ISTR(SAMPLE_MODES); ISTR(SPORT_UPDATE_POWER_MODES); ISTR(SPORT_MODES); @@ -419,6 +420,7 @@ const char STR_FORMATTING[] = TR_FORMATTING; const char STR_TEMP_CALIB[] = TR_TEMP_CALIB; const char STR_TIME[] = TR_TIME; const char STR_MAXBAUDRATE[] = TR_MAXBAUDRATE; +const char STR_SAMPLE_MODE[] = TR_SAMPLE_MODE; const char STR_BAUDRATE[] = TR_BAUDRATE; const char STR_SD_INFO_TITLE[] = TR_SD_INFO_TITLE; const char STR_SD_TYPE[] = TR_SD_TYPE; diff --git a/radio/src/translations.h b/radio/src/translations.h index 0ec58fe8e..ef9b079ee 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -647,6 +647,8 @@ extern const char STR_FORMATTING[]; extern const char STR_TEMP_CALIB[]; extern const char STR_TIME[]; extern const char STR_MAXBAUDRATE[]; +extern const char STR_SAMPLE_MODE[]; +extern const char STR_SAMPLE_MODES[]; extern const char STR_BAUDRATE[]; extern const char STR_SD_INFO_TITLE[]; extern const char STR_SD_TYPE[]; diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index f042c994e..0c2b19a4c 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -805,6 +805,10 @@ #define TR_TEMP_CALIB " +=\006Teplota" #define TR_TIME "Čas" #define TR_MAXBAUDRATE "Max baudů" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Hledat" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 05b377a40..e266bd4c0 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -807,6 +807,10 @@ #define TR_TEMP_CALIB "Temp. abgl." #define TR_TIME "Uhrzeit:" #define TR_MAXBAUDRATE "Max Baud" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 7abf4a509..5f0e941b3 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -811,6 +811,11 @@ #define TR_TEMP_CALIB "Temp. Calib" #define TR_TIME "Time" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" + #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" #define TR_BLUETOOTH_INIT "Init" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 1e31139b1..ce0f92304 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -824,6 +824,10 @@ #define TR_TEMP_CALIB "Temp. Calib" #define TR_TIME "Time" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 06f7159a0..5e7d97f9d 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -827,6 +827,11 @@ #define TR_TEMP_CALIB "Calib. temp" #define TR_TIME "Heure" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" + #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Découvrir" #define TR_BLUETOOTH_INIT "Init" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index cdd3dfc94..6073fa38a 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -826,6 +826,10 @@ #define TR_TEMP_CALIB "Temp. Calib." #define TR_TIME "Ora" #define TR_MAXBAUDRATE "Max Bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Cerca" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index 4aad255b2..b39c7eceb 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -812,6 +812,10 @@ #define TR_TEMP_CALIB "Temp. Calib." #define TR_TIME "Tijd:" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 8923a5bd0..5c7572652 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -825,6 +825,10 @@ #define TR_TEMP_CALIB "Temp. kalibracji" #define TR_TIME "Czas" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index 9b82bfdfa..7082492e2 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -826,6 +826,10 @@ #define TR_TEMP_CALIB "Temp. Calib" #define TR_TIME "Time" #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 1b13ffb8f..0d35de122 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -825,6 +825,10 @@ #define TR_TEMP_CALIB "Temp. kalib." #define TR_TIME "Tid " #define TR_MAXBAUDRATE "Max bauds" +#define TR_SAMPLE_MODE "Sample Mode" + +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "Bluetooth" #define TR_BLUETOOTH_DISC "Discover" From d5895f60257230bb18895e1b65f465a2178e7e71 Mon Sep 17 00:00:00 2001 From: Hydra Date: Thu, 20 May 2021 21:42:52 +0200 Subject: [PATCH 004/122] Move new field to the end to avoid eeprom corruption for existing users. --- radio/src/datastructs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 12c119302..e54c86e1e 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -821,7 +821,6 @@ PACK(struct RadioData { NOBACKUP(int8_t varioPitch); NOBACKUP(int8_t varioRange); NOBACKUP(int8_t varioRepeat); - NOBACKUP(int8_t uartSampleMode:2); // See UartSampleModes CustomFunctionData customFn[MAX_SPECIAL_FUNCTIONS]; EXTRA_GENERAL_FIELDS @@ -831,6 +830,8 @@ PACK(struct RadioData { char ownerRegistrationID[PXX2_LEN_REGISTRATION_ID]; GYRO_FIELDS + + NOBACKUP(int8_t uartSampleMode:2); // See UartSampleModes }); #undef SWITCHES_WARNING_DATA From fdb5cc379c3a903928567553a00c71510e88904d Mon Sep 17 00:00:00 2001 From: Hydra Date: Thu, 20 May 2021 23:11:00 +0200 Subject: [PATCH 005/122] Fix missing whitespace after function. --- radio/src/gui/common/stdlcd/radio_hardware.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/radio/src/gui/common/stdlcd/radio_hardware.cpp b/radio/src/gui/common/stdlcd/radio_hardware.cpp index 1b82b205c..35a114222 100644 --- a/radio/src/gui/common/stdlcd/radio_hardware.cpp +++ b/radio/src/gui/common/stdlcd/radio_hardware.cpp @@ -339,6 +339,7 @@ void restartExternalModule() resumePulses(); resumeMixerCalculations(); } + void menuRadioHardware(event_t event) { MENU(STR_HARDWARE, menuTabGeneral, MENU_RADIO_HARDWARE, HEADER_LINE + ITEM_RADIO_HARDWARE_MAX, { From a1a0af52c36dd76178bebea01d8cea001f27368f Mon Sep 17 00:00:00 2001 From: eshifri Date: Sat, 5 Jun 2021 16:36:47 -0700 Subject: [PATCH 006/122] Fole copy/rename implemented --- radio/src/gui/colorlcd/radio_sdmanager.cpp | 80 ++++++++++++++++++++-- radio/src/opentx.h | 8 +++ 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index 3e9227cb0..bfa53d200 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -26,11 +26,71 @@ #include "io/bootloader_flash.h" #include "standalone_lua.h" +class FileNameEditWindow : public Page { + public: + FileNameEditWindow(const std::string iName) : + Page(ICON_RADIO_SD_MANAGER), + name(std::move(iName)) + { + buildBody(&body); + buildHeader(&header); + }; + + protected: + const std::string name; + + void buildHeader(Window * window) + { + new StaticText(window, {70, 4, 180, 30}, STR_RENAME_FILE, MENU_BGCOLOR); + } + + void buildBody(Window * window) + { + GridLayout grid(window); + grid.spacer(8); + uint8_t nameLength; + uint8_t extLength; + char* ext = (char *)getFileExtension(name.data(), 0, 0, &nameLength, &extLength); + + reusableBuffer.sdManager.ext[0] = 0; + if(ext) + strncpy(reusableBuffer.sdManager.ext, ext, extLength); + + nameLength -= extLength; + if (nameLength > SD_SCREEN_FILE_LENGTH) nameLength = SD_SCREEN_FILE_LENGTH; + + strncpy(reusableBuffer.sdManager.originalName, name.c_str(), nameLength); + reusableBuffer.sdManager.originalName[nameLength] = 0; + + auto newFileName = new TextEdit(window, grid.getSlot(), reusableBuffer.sdManager.originalName, SD_SCREEN_FILE_LENGTH, LcdFlags(0)); + { + char* newValue = newFileName->getData(); + size_t totalSize = strlen(newValue);//sizeof(reusableBuffer.sdManager.changedName); + strncpy(reusableBuffer.sdManager.changedName, newValue, totalSize); + reusableBuffer.sdManager.changedName[totalSize] = 0; + if (extLength) { + strncpy(reusableBuffer.sdManager.changedName + totalSize, reusableBuffer.sdManager.ext, extLength);//totalSisze-strlen(newValue)); + } + reusableBuffer.sdManager.changedName[totalSize + extLength] = 0; + f_rename((const TCHAR*)name.c_str(), reusableBuffer.sdManager.changedName); + } + } +}; + + RadioSdManagerPage::RadioSdManagerPage() : PageTab(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, ICON_RADIO_SD_MANAGER) { } +void RadioSdManagerPage::rebuild(FormWindow * window) +{ + coord_t scrollPosition = window->getScrollPositionY(); + window->clear(); + build(window); + window->setScrollPositionY(scrollPosition); +} + // TODO elsewhere extern bool compare_nocase(const std::string &first, const std::string &second); @@ -38,7 +98,7 @@ extern bool compare_nocase(const std::string &first, const std::string &second); char * getFullPath(const std::string &filename) { static char full_path[FF_MAX_LFN + 1]; // TODO optimize that! - f_getcwd(full_path, FF_MAX_LFN); + f_getcwd((TCHAR*)full_path, FF_MAX_LFN); strcat(full_path, "/"); strcat(full_path, filename.c_str()); return full_path; @@ -271,15 +331,25 @@ void RadioSdManagerPage::build(FormWindow * window) strncpy(clipboard.data.sd.filename, name.c_str(), CLIPBOARD_PATH_LEN - 1); }); if (clipboard.type == CLIPBOARD_TYPE_SD_FILE) { - menu->addLine(STR_PASTE, [=]() { - // TODO + menu->addLine(STR_PASTE, [=]() { + TCHAR lfn[FF_MAX_LFN+1]; + f_getcwd(lfn, FF_MAX_LFN); + + if (strcmp(clipboard.data.sd.directory, lfn)) { // prevent copying to the same directory + POPUP_WARNING(sdCopyFile(clipboard.data.sd.filename, clipboard.data.sd.directory, clipboard.data.sd.filename, lfn)); + clipboard.type = CLIPBOARD_TYPE_NONE; + } + rebuild(window); }); } menu->addLine(STR_RENAME_FILE, [=]() { - // TODO + auto few = new FileNameEditWindow(name); + few->setCloseHandler([=]() { + rebuild(window); + }); }); menu->addLine(STR_DELETE_FILE, [=]() { - f_unlink(getFullPath(name)); + f_unlink((const TCHAR*)getFullPath(name)); // coord_t scrollPosition = window->getScrollPositionY(); window->clear(); build(window); diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 75057c6c8..800cd3207 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -37,6 +37,12 @@ #include "usbd_conf.h" #endif +#if defined(LIBOPENUI) + #include "libopenui.h" +#else + #include "libopenui/src/libopenui_file.h" +#endif + #if defined(SIMU) #define SWITCH_SIMU(a, b) (a) #else @@ -1081,6 +1087,8 @@ union ReusableBuffer uint16_t offset; uint16_t count; char originalName[SD_SCREEN_FILE_LENGTH+1]; + char changedName[SD_SCREEN_FILE_LENGTH+1]; + char ext[LEN_FILE_EXTENSION_MAX+1]; OtaUpdateInformation otaUpdateInformation; char otaReceiverVersion[sizeof(TR_CURRENT_VERSION) + 12]; } sdManager; From afb297b0f611a008d9428a36f48a1179e3d9c0f9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 7 Jun 2021 11:52:50 +0200 Subject: [PATCH 007/122] Test new EdgeTX splashes and remove vendor specific branding Resolves #154 --- radio/src/bitmaps/128x64/splash.png | Bin 353 -> 383 bytes radio/src/bitmaps/128x64/splash_frsky.png | Bin 495 -> 0 bytes radio/src/bitmaps/212x64/splash.png | Bin 2331 -> 842 bytes radio/src/bitmaps/212x64/splash_frsky.png | Bin 1400 -> 0 bytes radio/src/gui/128x64/gui.h | 1 - radio/src/gui/128x64/splash.cpp | 22 ++++------------------ radio/src/gui/212x64/gui.h | 1 - radio/src/gui/212x64/splash.cpp | 21 ++++----------------- radio/src/opentx.cpp | 8 -------- 9 files changed, 8 insertions(+), 45 deletions(-) delete mode 100644 radio/src/bitmaps/128x64/splash_frsky.png delete mode 100644 radio/src/bitmaps/212x64/splash_frsky.png diff --git a/radio/src/bitmaps/128x64/splash.png b/radio/src/bitmaps/128x64/splash.png index 64ebfb9d56561b8f7656ab17bf914d3b5ad4e47f..ebb8ecad722e6e62e7099dedc7c4dc9146a2c563 100644 GIT binary patch delta 338 zcmV-Y0j>Vw0{;S#By1~mMObu0a%Ew3X>V>IRB3Hx08MXhAU7aNb#5RsFfuV9Ffcka zF*-6eAT2O8FfiT4vUmUh00(qQO+^Rg1_uQaHLymTlK=n!32;bRa{vGe@Bjb`@Bu=s zG?)MY00d`2O+f$vv5yP5&36_4QP z&;*XgQ8+?J2Le8@Hqg<Usa{y;MxJlQC+$9bm1_q~)K)5h0 z)5rjY8E4FSrF3FQ4CT(20fK>YQYP|UDML0u46Z`d6=VTwKrnbyk}g_lggcgSK z;vG_g#x_R*v6tPTRCzVF0hQKU;7;Rom?hTn>$JK~`;hxSGA;~jw?c-{ExCfX;LX)T k4BiCOfwO_W{$YL}p4nA59Z^iSo&W#<07*qoM6N<$g6~^{X#fBK delta 311 zcmV-70m%OU0^tIXB$1O6e+h6%S#tmY3ljhU3ljkVnw%H_000McNliru-vI><9R$~% zSl0jm0PaadK~y-)?b0!B!!Qg5P^lU`@&@%q)bLyd3|K{njy*!J;+#NNV5en3K!f9!u0-Vd_^60+pC z;(+|oKq^+r`;ru-I_G(g1H-;dKu%bJVvG#mg%n1GKs$rq!Um);J;x~dP^yAWZZJrG zEwe&@0_*Zp7CS*JFkm~;?w}nKL*MTBZ|fMKbvX?-7<4v=Kh`#Sbdo!pG_2taf@KM7 zd;@Q3J6_KFc~XGQEeaL-&5|2nvQ@Ss1)Ma@C5)-3$BVtW{s3tlvfrAWX7K<3002ov JPDHLkV1oH$gJS>y diff --git a/radio/src/bitmaps/128x64/splash_frsky.png b/radio/src/bitmaps/128x64/splash_frsky.png deleted file mode 100644 index 84b3cf5e4a324b78ee941041ebdc6afef7ffd3f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 495 zcmVWFU8GbZ8()Nlj2>E@cM*00C1;L_t(I%hl5{OT$1E z2k>{MkO+mV=u(j0pl*V=h(qXEa43$&$+44ugAC$bQRt}PqDv>gfqsBUR%yRMImj2t z7%|@Yu8EMIyU&$8N6}m zw4~b(>`dx2jfBGqF_{=;LWU)@=hWolR*5*OV9K$=mqKP`7{MKvpecxVWp~PE5(M6a zAd(nJ0SudrBt-;#1{OGW8IyeHz%dxl6bJ+XbK`}DTu2LJ3ps#~0KdR+2+eGw2=og9 zlcQ=8u`o2n7R;6@viJy^B^AV;1%jaM>j95FT$p7#2K_Q#+%^^dE=b#QUM{xs}81iNn~bi3E>=J2@{oW7KBw12s|^*ZXDT~*M4 lek*t+L1UvnXKls)zaLqsn*T_vxc~qF002ovPDHLkV1kC+(UJfF diff --git a/radio/src/bitmaps/212x64/splash.png b/radio/src/bitmaps/212x64/splash.png index 43cd387dc7b790ab437796d6464fd402a305adfa..9eac084cd8658fe619bcd385467743f6094986e7 100644 GIT binary patch delta 831 zcmV-F1Hk;763PaU7=Hu<0001x2Sk1V001j=MObu0a%Ew3X>V>IRB3Hx08@2tAT}UM zb#5RsFfuV9GBY|eF*-CdAT2O8FfgL`^P>O&00(qQO+^Rg1_uQa8G@eoZ~y=R32;bR za{vGe@Bjb`@Bu=sG?)MY00d`2O+f$vv5yP_xJZkMn>=N?+6G8&d$!~=jXn@zSh>(2oMmTST~OV00L%7L_t(|+U=IV zOCv!P$7ePP8oYqlUUjhkAc_!YBf{CFxjv{HBqF9c1i3;ukSnhd;i|yF{Ri7jU_kX>ao&DikGqYj2YQF5|&2F;u$@{TyhA}56CnqN-rz+I^e<(hY=1Z7p zkKLo`!QFElbWBs!)N_5Np9gKllchsI?NF45W(0TFzz)t#sI5E-S+y`h)shhxV}fEA zVD=fC`v&&f(y0^$acHEd)IT1yDe9B*#D02ranX55H;UWZuF{n6K1?`r7g)ywbk#V&u zwO+-&B8TG8l#c(v^^qAhZ57_jt-?Z_QPfXdM1MX2Z}4blP1##?@_UO-Mp5|mNH(Wm z)@~K-m^zt%g1MSdR7*7_Z14LPg<&b)O@=AzNxi;xKn literal 2331 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 diff --git a/radio/src/bitmaps/212x64/splash_frsky.png b/radio/src/bitmaps/212x64/splash_frsky.png deleted file mode 100644 index 37c410f47d08e331a948edc53a19a4946bde7080..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1400 zcmV-;1&8{HP)7fFS{o6FW1LEbGNapo8}LKSyNZ>y3Z7 zgSvydgSvzISWt)WS(as4_Wkkr7lF#Ia_NVM#f53hFCnR6b@3y6{0frd=YO_jIG%K` ztv)}fSxU#l)A+Z`KR>9U2V)Fycsc-3H-8+Jt{Pz?Q+r9B1t8ZS9#?{@7wl=G)ZKzp zSC4De?`bfGzJE@zYIryRCVpo1JLzGPS&&MA>NBgKq!2=sBsBoth{}CtP@@smK~gk; zS5oNsi9to17de29k_sRw0+gRx{Uk|l)VLgw+R?l|IVj0NEeU9n0ybX&lsa&d2S%Uq zSI4R69B?DIu-+*AJys#fm6b3UuWq`vGldet5g8OGPz>t@~b3d(E zyz6+t3>5hd;?&lvcI&~CTJm7hn)J+Oek+Y$B@Lg}+c`0j7Ri3I-if=3`d!S508#{^ zEXl-HdDJ|rHC;dPWLl)8##7oq(YKd|ieH0jH+E(B;CB)b)s8IP#FVz#t%-R6GXq%U zS$;87-N;eQ#7{ix!XEx8`NBzZMZ4U1zF=@ndO2HJm?he5>PK~o^39FBU$bLI#(**4 zyyRj2j9N%MB?kD$O|h zjd-g#RlPFT*M-!s@7S^@8`pT->95~m2y}6h=xspF0%78HaNXLDw?V(y*Q;o7qFZdM z_vy`)Z%t5s0cxw07uU*BH%(JrET~aUH(Y9QcD2{dK!qJn$8!QY7Yin5cR}pSUo!Gm zp!TwX>H z{d^V8q5$aEP0}CBCAaG)?U|{4v-$d!>noQQd2%Q9wn@FCg~Rz0eM|oCbxm+_{8^= tgtime-200) { - secondSplash = true; - drawSecondSplash(); - } -#endif - #if defined(PCBSKY9X) if (curTime < get_tmr10ms()) { curTime += 10; From 128346cdfa722634c8a8440596ada60e20fd0caa Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 7 Jun 2021 14:57:17 +0200 Subject: [PATCH 008/122] Removed branding from logo image --- radio/src/bitmaps/212x64/logo.png | Bin 1238 -> 8079 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/radio/src/bitmaps/212x64/logo.png b/radio/src/bitmaps/212x64/logo.png index ae8b9e8e5f140644ce1298d1756df03025c7854f..0223e2b6d785165a03ce13681a157bf8782de6e3 100644 GIT binary patch literal 8079 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 r?_* Date: Tue, 8 Jun 2021 21:36:32 +0200 Subject: [PATCH 009/122] Forward touch event to child windows even for widgets This is the case when the menu is started whereby the widget takes the full screen. Resolves #106 --- radio/src/gui/colorlcd/widget.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/gui/colorlcd/widget.cpp b/radio/src/gui/colorlcd/widget.cpp index 78de21dbe..b6e3afb9c 100644 --- a/radio/src/gui/colorlcd/widget.cpp +++ b/radio/src/gui/colorlcd/widget.cpp @@ -75,6 +75,10 @@ bool Widget::onTouchEnd(coord_t x, coord_t y) //TODO: forward to widget (lua for instance) return true; } + + if (Window::onTouchEnd(x, y)) { + return true; + } if (hasFocus()) { onPress(); From b0fb80b264ab728c0c225b7ab908553d0ba28398 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Jun 2021 09:24:48 +0200 Subject: [PATCH 010/122] ZChar nightmare / take 2 --- radio/src/lua/api_general.cpp | 39 +++++++++++++++-------------------- radio/src/lua/api_model.cpp | 38 +++++++++++++++++----------------- radio/src/lua/lua_api.h | 13 ++++++++++++ 3 files changed, 49 insertions(+), 41 deletions(-) diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 9ef7577b2..47eced18b 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -362,22 +362,20 @@ bool luaFindFieldByName(const char * name, LuaField & field, unsigned int flags) // search in telemetry field.desc[0] = '\0'; - for (int i=0; i 0) { - str2zchar(zname, name, 4); - } - else { - zname[0] = hex2zchar((id & 0xf000) >> 12); - zname[1] = hex2zchar((id & 0x0f00) >> 8); - zname[2] = hex2zchar((id & 0x00f0) >> 4); - zname[3] = hex2zchar((id & 0x000f) >> 0); + if (name == NULL || strlen(name) > 0) { + name_buf[0] = hex2char((id & 0xf000) >> 12); + name_buf[1] = hex2char((id & 0x0f00) >> 8); + name_buf[2] = hex2char((id & 0x00f0) >> 4); + name_buf[3] = hex2char((id & 0x000f) >> 0); } if (id | subId | instance) { - int index = setTelemetryValue(PROTOCOL_TELEMETRY_LUA, id, subId, instance, value, unit, prec); + int index = setTelemetryValue(PROTOCOL_TELEMETRY_LUA, id, subId, instance, + value, unit, prec); if (index >= 0) { TelemetrySensor &telemetrySensor = g_model.telemetrySensors[index]; telemetrySensor.id = id; telemetrySensor.subId = subId; telemetrySensor.instance = instance; - telemetrySensor.init(zname, unit, prec); + telemetrySensor.init(name ? name: name_buf, unit, prec); lua_pushboolean(L, true); - } - else { + } else { lua_pushboolean(L, false); } return 1; diff --git a/radio/src/lua/api_model.cpp b/radio/src/lua/api_model.cpp index feff8e6dd..9737e6704 100644 --- a/radio/src/lua/api_model.cpp +++ b/radio/src/lua/api_model.cpp @@ -39,9 +39,9 @@ Get current Model information static int luaModelGetInfo(lua_State *L) { lua_newtable(L); - lua_pushtablestring(L, "name", g_model.header.name); + lua_pushtablenstring(L, "name", g_model.header.name); #if LCD_DEPTH > 1 - lua_pushtablestring(L, "bitmap", g_model.header.bitmap); + lua_pushtablenstring(L, "bitmap", g_model.header.bitmap); #endif return 1; } @@ -66,7 +66,7 @@ static int luaModelSetInfo(lua_State *L) const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(g_model.header.name, name, sizeof(g_model.header.name)); + strncpy(g_model.header.name, name, sizeof(g_model.header.name)); #if defined(EEPROM) memcpy(modelHeaders[g_eeGeneral.currModel].name, g_model.header.name, sizeof(g_model.header.name)); #endif @@ -258,7 +258,7 @@ static int luaModelGetTimer(lua_State *L) lua_pushtableinteger(L, "countdownBeep", timer.countdownBeep); lua_pushtableboolean(L, "minuteBeep", timer.minuteBeep); lua_pushtableinteger(L, "persistent", timer.persistent); - lua_pushtablestring(L, "name", timer.name); + lua_pushtablenstring(L, "name", timer.name); } else { lua_pushnil(L); @@ -310,7 +310,7 @@ static int luaModelSetTimer(lua_State *L) } if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(timer.name, name, sizeof(timer.name)); + strncpy(timer.name, name, sizeof(timer.name)); } } storageDirty(EE_MODEL); @@ -424,7 +424,7 @@ static int luaModelGetFlightMode(lua_State * L) if (idx < MAX_FLIGHT_MODES) { FlightModeData * fm = flightModeAddress(idx); lua_newtable(L); - lua_pushtablestring(L, "name", fm->name); + lua_pushtablenstring(L, "name", fm->name); lua_pushtableinteger(L, "switch", fm->swtch); lua_pushtableinteger(L, "fadeIn", fm->fadeIn); lua_pushtableinteger(L, "fadeOut", fm->fadeOut); @@ -477,7 +477,7 @@ static int luaModelSetFlightMode(lua_State * L) const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(fm->name, name, sizeof(fm->name)); + strncpy(fm->name, name, sizeof(fm->name)); } else if (!strcmp(key, "switch")) { fm->swtch = luaL_checkinteger(L, -1); @@ -546,8 +546,8 @@ static int luaModelGetInput(lua_State *L) if (idx < count) { ExpoData * expo = expoAddress(first+idx); lua_newtable(L); - lua_pushtablestring(L, "name", expo->name); - lua_pushtablestring(L, "inputName", g_model.inputNames[chn]); + lua_pushtablenstring(L, "name", expo->name); + lua_pushtablenstring(L, "inputName", g_model.inputNames[chn]); lua_pushtableinteger(L, "source", expo->srcRaw); lua_pushtableinteger(L, "weight", expo->weight); lua_pushtableinteger(L, "offset", expo->offset); @@ -599,11 +599,11 @@ static int luaModelInsertInput(lua_State *L) const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(expo->name, name, sizeof(expo->name)); + strncpy(expo->name, name, sizeof(expo->name)); } else if (!strcmp(key, "inputName")) { const char * name = luaL_checkstring(L, -1); - str2zchar(g_model.inputNames[chn], name, LEN_INPUT_NAME); + strncpy(g_model.inputNames[chn], name, LEN_INPUT_NAME); } else if (!strcmp(key, "source")) { expo->srcRaw = luaL_checkinteger(L, -1); @@ -773,7 +773,7 @@ static int luaModelGetMix(lua_State *L) if (idx < count) { MixData * mix = mixAddress(first+idx); lua_newtable(L); - lua_pushtablestring(L, "name", mix->name); + lua_pushtablenstring(L, "name", mix->name); lua_pushtableinteger(L, "source", mix->srcRaw); lua_pushtableinteger(L, "weight", mix->weight); lua_pushtableinteger(L, "offset", mix->offset); @@ -827,7 +827,7 @@ static int luaModelInsertMix(lua_State *L) const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(mix->name, name, sizeof(mix->name)); + strncpy(mix->name, name, sizeof(mix->name)); } else if (!strcmp(key, "source")) { mix->srcRaw = luaL_checkinteger(L, -1); @@ -1043,7 +1043,7 @@ static int luaModelGetCurve(lua_State *L) if (idx < MAX_CURVES) { CurveHeader & CurveHeader = g_model.curves[idx]; lua_newtable(L); - lua_pushtablestring(L, "name", CurveHeader.name); + lua_pushtablenstring(L, "name", CurveHeader.name); lua_pushtableinteger(L, "type", CurveHeader.type); lua_pushtableboolean(L, "smooth", CurveHeader.smooth); lua_pushtableinteger(L, "points", CurveHeader.points + 5); @@ -1144,7 +1144,7 @@ static int luaModelSetCurve(lua_State *L) const char *key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char *name = luaL_checkstring(L, -1); - str2zchar(newCurveHeader.name, name, sizeof(newCurveHeader.name)); + strncpy(newCurveHeader.name, name, sizeof(newCurveHeader.name)); } else if (!strcmp(key, "type")) { newCurveHeader.type = luaL_checkinteger(L, -1); @@ -1301,7 +1301,7 @@ static int luaModelGetCustomFunction(lua_State *L) lua_pushtableinteger(L, "switch", CFN_SWITCH(cfn)); lua_pushtableinteger(L, "func", CFN_FUNC(cfn)); if (CFN_FUNC(cfn) == FUNC_PLAY_TRACK || CFN_FUNC(cfn) == FUNC_BACKGND_MUSIC || CFN_FUNC(cfn) == FUNC_PLAY_SCRIPT) { - lua_pushtablestring(L, "name", cfn->play.name); + lua_pushtablenstring(L, "name", cfn->play.name); } else { lua_pushtableinteger(L, "value", cfn->all.val); @@ -1398,7 +1398,7 @@ static int luaModelGetOutput(lua_State *L) if (idx < MAX_OUTPUT_CHANNELS) { LimitData * limit = limitAddress(idx); lua_newtable(L); - lua_pushtablestring(L, "name", limit->name); + lua_pushtablenstring(L, "name", limit->name); lua_pushtableinteger(L, "min", limit->min-1000); lua_pushtableinteger(L, "max", limit->max+1000); lua_pushtableinteger(L, "offset", limit->offset); @@ -1440,7 +1440,7 @@ static int luaModelSetOutput(lua_State *L) const char * key = luaL_checkstring(L, -2); if (!strcmp(key, "name")) { const char * name = luaL_checkstring(L, -1); - str2zchar(limit->name, name, sizeof(limit->name)); + strncpy(limit->name, name, sizeof(limit->name)); } else if (!strcmp(key, "min")) { limit->min = luaL_checkinteger(L, -1)+1000; @@ -1558,7 +1558,7 @@ static int luaModelGetSensor(lua_State *L) TelemetrySensor & sensor = g_model.telemetrySensors[idx]; lua_newtable(L); lua_pushtableinteger(L, "type", sensor.type); - lua_pushtablestring(L, "name", sensor.label); + lua_pushtablenstring(L, "name", sensor.label); lua_pushtableinteger(L, "unit", sensor.unit); lua_pushtableinteger(L, "prec", sensor.prec); if (sensor.type == TELEM_TYPE_CUSTOM) { diff --git a/radio/src/lua/lua_api.h b/radio/src/lua/lua_api.h index f7957e269..89192fb59 100644 --- a/radio/src/lua/lua_api.h +++ b/radio/src/lua/lua_api.h @@ -62,7 +62,20 @@ void luaInitThemesAndWidgets(); #define lua_pushtableboolean(L, k, v) (lua_pushstring(L, (k)), lua_pushboolean(L, (v)), lua_settable(L, -3)) #define lua_pushtableinteger(L, k, v) (lua_pushstring(L, (k)), lua_pushinteger(L, (v)), lua_settable(L, -3)) #define lua_pushtablenumber(L, k, v) (lua_pushstring(L, (k)), lua_pushnumber(L, (v)), lua_settable(L, -3)) + +// size based string (possibly no null-termination) +#define __lua_strncpy(s) \ + char tmp[sizeof(s) + 1]; \ + strncpy(tmp, (s), sizeof(s)); \ + tmp[sizeof(s)] = '\0'; + +// size based string (possibly no null-termination) +#define lua_pushnstring(L, s) { __lua_strncpy(s); lua_pushstring(L, tmp); } +#define lua_pushtablenstring(L, k, v) { __lua_strncpy(s); lua_pushstring(L, (k)); lua_pushstring(L, tmp); lua_settable(L, -3); } + +// null-terminated string #define lua_pushtablestring(L, k, v) (lua_pushstring(L, (k)), lua_pushstring(L, (v)), lua_settable(L, -3)) + #define lua_registerlib(L, name, tab) (luaL_newmetatable(L, name), luaL_setfuncs(L, tab, 0), lua_setglobal(L, name)) #define RUN_MIX_SCRIPT (1 << 0) From 247ac2a5242efc8316b112bd9647f46bd0bd4035 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Jun 2021 09:30:18 +0200 Subject: [PATCH 011/122] Fixes --- radio/src/lua/lua_api.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/radio/src/lua/lua_api.h b/radio/src/lua/lua_api.h index 89192fb59..2860e470c 100644 --- a/radio/src/lua/lua_api.h +++ b/radio/src/lua/lua_api.h @@ -64,14 +64,14 @@ void luaInitThemesAndWidgets(); #define lua_pushtablenumber(L, k, v) (lua_pushstring(L, (k)), lua_pushnumber(L, (v)), lua_settable(L, -3)) // size based string (possibly no null-termination) -#define __lua_strncpy(s) \ - char tmp[sizeof(s) + 1]; \ - strncpy(tmp, (s), sizeof(s)); \ +#define __lua_strncpy(s) \ + char tmp[sizeof(s) + 1]; \ + strncpy(tmp, (s), sizeof(tmp) - 1); \ tmp[sizeof(s)] = '\0'; // size based string (possibly no null-termination) #define lua_pushnstring(L, s) { __lua_strncpy(s); lua_pushstring(L, tmp); } -#define lua_pushtablenstring(L, k, v) { __lua_strncpy(s); lua_pushstring(L, (k)); lua_pushstring(L, tmp); lua_settable(L, -3); } +#define lua_pushtablenstring(L, k, v) { __lua_strncpy(v); lua_pushstring(L, (k)); lua_pushstring(L, tmp); lua_settable(L, -3); } // null-terminated string #define lua_pushtablestring(L, k, v) (lua_pushstring(L, (k)), lua_pushstring(L, (v)), lua_settable(L, -3)) From d34d789dccbed85e842b546f0857ea832945c3dd Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Jun 2021 10:32:05 +0200 Subject: [PATCH 012/122] More fixes --- radio/src/telemetry/telemetry_sensors.cpp | 8 ++++---- radio/src/tests/conversions.cpp | 14 +++++++------- radio/src/tests/lua.cpp | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/radio/src/telemetry/telemetry_sensors.cpp b/radio/src/telemetry/telemetry_sensors.cpp index c8ddb98f3..842312bf4 100644 --- a/radio/src/telemetry/telemetry_sensors.cpp +++ b/radio/src/telemetry/telemetry_sensors.cpp @@ -621,10 +621,10 @@ void TelemetrySensor::init(const char * label, uint8_t unit, uint8_t prec) void TelemetrySensor::init(uint16_t id) { char label[TELEM_LABEL_LEN]; // 4 - label[0] = hex2char((id & 0x0f00) >> 8); - label[1] = hex2char((id & 0x00f0) >> 4); - label[2] = hex2char((id & 0x000f) >> 0); - label[3] = '\0'; + label[0] = hex2char((id & 0xf000) >> 12); + label[1] = hex2char((id & 0x0f00) >> 8); + label[2] = hex2char((id & 0x00f0) >> 4); + label[3] = hex2char((id & 0x000f) >> 0); init(label); } diff --git a/radio/src/tests/conversions.cpp b/radio/src/tests/conversions.cpp index 30e889523..6b5ff0dbc 100644 --- a/radio/src/tests/conversions.cpp +++ b/radio/src/tests/conversions.cpp @@ -50,18 +50,18 @@ TEST(Conversions, ConversionX9DPFrom23) EXPECT_EQ(FUNC_LOGS, g_eeGeneral.customFn[0].func); EXPECT_EQ(20, g_eeGeneral.customFn[0].all.val); - EXPECT_STREQ("Tes", g_eeGeneral.switchNames[0]); // ZSTREQ + EXPECT_STRNEQ("Tes", g_eeGeneral.switchNames[0]); // ZSTREQ EXPECT_EQ(SWITCH_3POS, SWITCH_CONFIG(0)); - EXPECT_STREQ("Test", g_model.header.name); // ZSTREQ + EXPECT_STRNEQ("Test", g_model.header.name); // ZSTREQ EXPECT_EQ(TMRMODE_START, g_model.timers[0].mode); // new! EXPECT_EQ(SWSRC_SA0, g_model.timers[0].swtch); // new! EXPECT_EQ(80, g_model.mixData[0].weight); EXPECT_EQ(-100, g_model.limitData[0].max); // 90.0 EXPECT_EQ(80, g_model.expoData[0].weight); EXPECT_EQ(10, g_model.flightModeData[0].gvars[0]); - EXPECT_STREQ("Tes", g_model.gvars[0].name); // ZSTREQ - EXPECT_STREQ("Test", g_model.flightModeData[0].name); // ZSTREQ + EXPECT_STRNEQ("Tes", g_model.gvars[0].name); // ZSTREQ + EXPECT_STRNEQ("Test", g_model.flightModeData[0].name); // ZSTREQ #if defined(INTERNAL_MODULE_PXX2) EXPECT_EQ(MODULE_TYPE_ISRM_PXX2, g_model.moduleData[INTERNAL_MODULE].type); @@ -73,13 +73,13 @@ TEST(Conversions, ConversionX9DPFrom23) EXPECT_EQ(MODULE_TYPE_R9M_PXX1, g_model.moduleData[EXTERNAL_MODULE].type); EXPECT_EQ(MODULE_SUBTYPE_R9M_FCC, g_model.moduleData[EXTERNAL_MODULE].subType); - EXPECT_STREQ("Rud", g_model.inputNames[0]); // ZSTREQ - EXPECT_STREQ("Tes", g_model.telemetrySensors[0].label); // ZSTREQ + EXPECT_STRNEQ("Rud", g_model.inputNames[0]); // ZSTREQ + EXPECT_STRNEQ("Tes", g_model.telemetrySensors[0].label); // ZSTREQ EXPECT_EQ(10, g_model.telemetrySensors[0].id); EXPECT_EQ(10, g_model.telemetrySensors[0].frskyInstance.physID); EXPECT_EQ(MIXSRC_FIRST_TELEM, g_model.logicalSw[0].v1); - EXPECT_STREQ("abc.wav", g_model.customFn[1].play.name); + EXPECT_STRNEQ("abc.wav", g_model.customFn[1].play.name); } #endif diff --git a/radio/src/tests/lua.cpp b/radio/src/tests/lua.cpp index 9425726a9..f3535bd70 100644 --- a/radio/src/tests/lua.cpp +++ b/radio/src/tests/lua.cpp @@ -47,12 +47,12 @@ TEST(Lua, testSetModelInfo) luaExecStr("info.name = 'modelA'"); luaExecStr("model.setInfo(info)"); // luaExecStr("print('model name: '..info.name..' id: '..info.id)"); - EXPECT_ZSTREQ("modelA", g_model.header.name); + EXPECT_STRNEQ("modelA", g_model.header.name); luaExecStr("info.name = 'Model 1'"); luaExecStr("model.setInfo(info)"); // luaExecStr("print('model name: '..info.name..' id: '..info.id)"); - EXPECT_ZSTREQ("Model 1", g_model.header.name); + EXPECT_STRNEQ("Model 1", g_model.header.name); } TEST(Lua, testPanicProtection) From f31e12448d043033c1b6322dcddea1c0121ba644 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Jun 2021 11:01:49 +0200 Subject: [PATCH 013/122] Fix LUA insertMix() --- radio/src/gui/colorlcd/model_mixes.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index 08dc1d107..eb409397b 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -448,10 +448,15 @@ void deleteMix(uint8_t idx) storageDirty(EE_MODEL); } +#if defined(LUA) +// This is necessary as the LUA API uses th old interface +// where insertMix() has only one param. The channel is +// passed through s_currCh void insertMix(uint8_t idx) { - insertMix(idx, s_currCh + 1); + insertMix(idx, s_currCh - 1); } +#endif void copyMix(uint8_t source, uint8_t dest, int8_t ch) { From 57efb948db3e59742811a45dcde8d67f0e673ca1 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Jun 2021 11:08:41 +0200 Subject: [PATCH 014/122] More unit test fixes --- radio/src/tests/lua.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/tests/lua.cpp b/radio/src/tests/lua.cpp index f3535bd70..fe49b9e59 100644 --- a/radio/src/tests/lua.cpp +++ b/radio/src/tests/lua.cpp @@ -112,7 +112,7 @@ TEST(Lua, testModelInputs) // add one line on Input4 luaExecStr("model.insertInput(3, 0, {name='test1', source=MIXSRC_Thr, weight=56, offset=3, switch=2})"); EXPECT_EQ(3, (int)g_model.expoData[0].chn); - EXPECT_ZSTREQ("test1", g_model.expoData[0].name); + EXPECT_STRNEQ("test1", g_model.expoData[0].name); EXPECT_EQ(MIXSRC_Thr, g_model.expoData[0].srcRaw); EXPECT_EQ(56, g_model.expoData[0].weight); EXPECT_EQ(3, g_model.expoData[0].offset); @@ -121,14 +121,14 @@ TEST(Lua, testModelInputs) // add another one before existing line on Input4 luaExecStr("model.insertInput(3, 0, {name='test2', source=MIXSRC_Rud, weight=-56})"); EXPECT_EQ(3, (int)g_model.expoData[0].chn); - EXPECT_ZSTREQ("test2", g_model.expoData[0].name); + EXPECT_STRNEQ("test2", g_model.expoData[0].name); EXPECT_EQ(MIXSRC_Rud, g_model.expoData[0].srcRaw); EXPECT_EQ(-56, g_model.expoData[0].weight); EXPECT_EQ(0, g_model.expoData[0].offset); EXPECT_EQ(0, g_model.expoData[0].swtch); EXPECT_EQ(3, (int)g_model.expoData[1].chn); - EXPECT_ZSTREQ("test1", g_model.expoData[1].name); + EXPECT_STRNEQ("test1", g_model.expoData[1].name); EXPECT_EQ(MIXSRC_Thr, g_model.expoData[1].srcRaw); EXPECT_EQ(56, g_model.expoData[1].weight); EXPECT_EQ(3, g_model.expoData[1].offset); @@ -137,21 +137,21 @@ TEST(Lua, testModelInputs) // add another line after existing lines on Input4 luaExecStr("model.insertInput(3, model.getInputsCount(3), {name='test3', source=MIXSRC_Ail, weight=100})"); EXPECT_EQ(3, (int)g_model.expoData[0].chn); - EXPECT_ZSTREQ("test2", g_model.expoData[0].name); + EXPECT_STRNEQ("test2", g_model.expoData[0].name); EXPECT_EQ(MIXSRC_Rud, g_model.expoData[0].srcRaw); EXPECT_EQ(-56, g_model.expoData[0].weight); EXPECT_EQ(0, g_model.expoData[0].offset); EXPECT_EQ(0, g_model.expoData[0].swtch); EXPECT_EQ(3, (int)g_model.expoData[1].chn); - EXPECT_ZSTREQ("test1", g_model.expoData[1].name); + EXPECT_STRNEQ("test1", g_model.expoData[1].name); EXPECT_EQ(MIXSRC_Thr, g_model.expoData[1].srcRaw); EXPECT_EQ(56, g_model.expoData[1].weight); EXPECT_EQ(3, g_model.expoData[1].offset); EXPECT_EQ(2, g_model.expoData[1].swtch); EXPECT_EQ(3, (int)g_model.expoData[2].chn); - EXPECT_ZSTREQ("test3", g_model.expoData[2].name); + EXPECT_STRNEQ("test3", g_model.expoData[2].name); EXPECT_EQ(MIXSRC_Ail, g_model.expoData[2].srcRaw); EXPECT_EQ(100, g_model.expoData[2].weight); EXPECT_EQ(0, g_model.expoData[2].offset); From f9dbf47c292992b1a213e2ebc7b2c351ab9f714f Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Jun 2021 11:15:11 +0200 Subject: [PATCH 015/122] Updated logo.png --- radio/src/bitmaps/212x64/logo.png | Bin 8079 -> 244 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/radio/src/bitmaps/212x64/logo.png b/radio/src/bitmaps/212x64/logo.png index 0223e2b6d785165a03ce13681a157bf8782de6e3..50ed47ed74352009b7ad4f43a1471b0d1383d2a3 100644 GIT binary patch delta 228 zcmeCT|H3#yqMnhNfq`L0@SEd6ioL|s*OmP#8z-Zx%3Ad!yMRKR1s;*b3=CW!K$y{K zjmZq4AWOQVuOkD)#(wTUiL5|ATYyi9E0F&G|NqjP^X~##g`O^sAr-e$FWlv9P!M2v zz`f8kW+9`}_QEzMPWQ`TE13(JJ5CpPgg&ebxsLQ05a-R^#A|> literal 8079 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 Date: Wed, 9 Jun 2021 14:26:46 +0200 Subject: [PATCH 016/122] Place adcRead() back to mixer task to avoid issues with heartbeat triggered modules Resolves #166 --- radio/src/opentx.cpp | 4 ++++ .../src/targets/common/arm/stm32/mixer_scheduler_driver.cpp | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 0453b2517..79ded3d83 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -1091,6 +1091,10 @@ void getADC() } #endif + DEBUG_TIMER_START(debugTimerAdcRead); + adcRead(); + DEBUG_TIMER_STOP(debugTimerAdcRead); + for (uint8_t x=0; xARR = 2 * getMixerSchedulerPeriod() - 1; - //read ADCs - DEBUG_TIMER_START(debugTimerAdcRead); - adcRead(); - DEBUG_TIMER_STOP(debugTimerAdcRead); - // trigger mixer start mixerSchedulerISRTrigger(); } From f50a91ac709cc9de59aebee9f98b3715f2e59860 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Jun 2021 08:05:36 +0200 Subject: [PATCH 017/122] Fix "wonky timer" --- .../conversions/conversions_219_220.cpp | 8 ++++++-- radio/src/tests/conversions.cpp | 18 ++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/radio/src/storage/conversions/conversions_219_220.cpp b/radio/src/storage/conversions/conversions_219_220.cpp index 8743b2889..a865c6a8b 100644 --- a/radio/src/storage/conversions/conversions_219_220.cpp +++ b/radio/src/storage/conversions/conversions_219_220.cpp @@ -116,12 +116,16 @@ void convertModelData_219_to_220(ModelData &model) if (timer_219.mode >= TMRMODE_START) { timer_219.mode += 1; } - if (timer_219.mode < TMRMODE_COUNT) { + if (timer_219.mode < TMRMODE_COUNT + && timer_219.mode >=0) { timer.mode = timer_219.mode; } else { timer.mode = TMRMODE_START; - timer.swtch = timer_219.mode - (TMRMODE_COUNT - 1); + if (timer_219.mode > 0) + timer.swtch = timer_219.mode - (TMRMODE_COUNT - 1); + else + timer.swtch = timer_219.mode; } timer.start = timer_219.start; diff --git a/radio/src/tests/conversions.cpp b/radio/src/tests/conversions.cpp index 6b5ff0dbc..87c8c5cc5 100644 --- a/radio/src/tests/conversions.cpp +++ b/radio/src/tests/conversions.cpp @@ -81,6 +81,24 @@ TEST(Conversions, ConversionX9DPFrom23) EXPECT_STRNEQ("abc.wav", g_model.customFn[1].play.name); } + +TEST(Conversions, ConversionX9DPFrom23_2) +{ + loadEEPROMFile(TESTS_PATH "/eeprom_23_x9d+2.bin"); + + eepromOpen(); + eeLoadGeneralSettingsData(); + convertRadioData_219_to_220(g_eeGeneral); + eeConvertModel(6, 219); + eeLoadModel(6); + + EXPECT_EQ(710, g_eeGeneral.calib[0].spanNeg); + EXPECT_EQ(944, g_eeGeneral.calib[0].mid); + EXPECT_EQ(770, g_eeGeneral.calib[0].spanPos); + + EXPECT_EQ(TMRMODE_START, g_model.timers[0].mode); // new! + EXPECT_EQ(-SWSRC_SA0, g_model.timers[0].swtch); // new! +} #endif // #if defined(PCBXLITE) && !defined(PCBXLITES) From 105e90b8d0c62c41809299f0ff9872af2e912293 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 9 Jun 2021 14:40:27 +0200 Subject: [PATCH 018/122] Added missing file for unit tests --- radio/src/tests/eeprom_23_x9d+2.bin | Bin 0 -> 32768 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 radio/src/tests/eeprom_23_x9d+2.bin diff --git a/radio/src/tests/eeprom_23_x9d+2.bin b/radio/src/tests/eeprom_23_x9d+2.bin new file mode 100644 index 0000000000000000000000000000000000000000..de18f24739f119f68c8156e8051594af84a87ad9 GIT binary patch literal 32768 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 Date: Wed, 9 Jun 2021 18:45:34 -0700 Subject: [PATCH 019/122] formatting applied --- radio/src/gui/colorlcd/radio_sdmanager.cpp | 98 +++++++++++----------- 1 file changed, 50 insertions(+), 48 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index 4f5f11f13..290ae73aa 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -26,64 +26,66 @@ #include "io/bootloader_flash.h" #include "standalone_lua.h" -class FileNameEditWindow : public Page { +class FileNameEditWindow : public Page +{ public: - FileNameEditWindow(const std::string iName) : - Page(ICON_RADIO_SD_MANAGER), - name(std::move(iName)) - { - buildBody(&body); - buildHeader(&header); - }; + FileNameEditWindow(const std::string iName) : + Page(ICON_RADIO_SD_MANAGER), name(std::move(iName)) + { + buildBody(&body); + buildHeader(&header); + }; #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "FileNameEditWindow"; - } + std::string getName() const override { return "FileNameEditWindow"; } #endif protected: - const std::string name; + const std::string name; - void buildHeader(Window * window) + void buildHeader(Window *window) + { + new StaticText(window, {70, 4, 180, 30}, STR_RENAME_FILE, MENU_BGCOLOR); + } + + void buildBody(Window *window) + { + GridLayout grid(window); + grid.spacer(8); + uint8_t nameLength; + uint8_t extLength; + char *ext = + (char *)getFileExtension(name.data(), 0, 0, &nameLength, &extLength); + + reusableBuffer.sdManager.ext[0] = 0; + if (ext) strncpy(reusableBuffer.sdManager.ext, ext, extLength); + + nameLength -= extLength; + if (nameLength > SD_SCREEN_FILE_LENGTH) nameLength = SD_SCREEN_FILE_LENGTH; + + strncpy(reusableBuffer.sdManager.originalName, name.c_str(), nameLength); + reusableBuffer.sdManager.originalName[nameLength] = 0; + + auto newFileName = new TextEdit(window, grid.getSlot(), + reusableBuffer.sdManager.originalName, + SD_SCREEN_FILE_LENGTH, LcdFlags(0)); { - new StaticText(window, {70, 4, 180, 30}, STR_RENAME_FILE, MENU_BGCOLOR); + char *newValue = newFileName->getData(); + size_t totalSize = + strlen(newValue); // sizeof(reusableBuffer.sdManager.changedName); + strncpy(reusableBuffer.sdManager.changedName, newValue, totalSize); + reusableBuffer.sdManager.changedName[totalSize] = 0; + if (extLength) { + strncpy(reusableBuffer.sdManager.changedName + totalSize, + reusableBuffer.sdManager.ext, + extLength); // totalSisze-strlen(newValue)); + } + reusableBuffer.sdManager.changedName[totalSize + extLength] = 0; + f_rename((const TCHAR *)name.c_str(), + reusableBuffer.sdManager.changedName); } - - void buildBody(Window * window) - { - GridLayout grid(window); - grid.spacer(8); - uint8_t nameLength; - uint8_t extLength; - char* ext = (char *)getFileExtension(name.data(), 0, 0, &nameLength, &extLength); - - reusableBuffer.sdManager.ext[0] = 0; - if(ext) - strncpy(reusableBuffer.sdManager.ext, ext, extLength); - - nameLength -= extLength; - if (nameLength > SD_SCREEN_FILE_LENGTH) nameLength = SD_SCREEN_FILE_LENGTH; - - strncpy(reusableBuffer.sdManager.originalName, name.c_str(), nameLength); - reusableBuffer.sdManager.originalName[nameLength] = 0; - - auto newFileName = new TextEdit(window, grid.getSlot(), reusableBuffer.sdManager.originalName, SD_SCREEN_FILE_LENGTH, LcdFlags(0)); - { - char* newValue = newFileName->getData(); - size_t totalSize = strlen(newValue);//sizeof(reusableBuffer.sdManager.changedName); - strncpy(reusableBuffer.sdManager.changedName, newValue, totalSize); - reusableBuffer.sdManager.changedName[totalSize] = 0; - if (extLength) { - strncpy(reusableBuffer.sdManager.changedName + totalSize, reusableBuffer.sdManager.ext, extLength);//totalSisze-strlen(newValue)); - } - reusableBuffer.sdManager.changedName[totalSize + extLength] = 0; - f_rename((const TCHAR*)name.c_str(), reusableBuffer.sdManager.changedName); - } - } + } }; - RadioSdManagerPage::RadioSdManagerPage() : PageTab(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, ICON_RADIO_SD_MANAGER) { From 0d344bedd8f462fbfdd2d01ab7abc1065cb05db6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Jun 2021 13:17:49 +0200 Subject: [PATCH 020/122] [color] Batch conversions --- radio/src/gui/colorlcd/layout.h | 1 + .../colorlcd/layouts/layout_factory_impl.h | 2 +- radio/src/gui/colorlcd/model_select.cpp | 12 +-- radio/src/gui/colorlcd/themes/480_default.cpp | 5 +- radio/src/gui/colorlcd/themes/flysky.cpp | 5 +- radio/src/sdcard.h | 3 +- radio/src/storage/conversions/conversions.cpp | 91 ++++++++++++++++ .../conversions/conversions_219_220.cpp | 20 ++-- radio/src/storage/modelslist.cpp | 101 +----------------- radio/src/storage/modelslist.h | 2 - radio/src/storage/sdcard_common.cpp | 14 +-- radio/src/storage/sdcard_common.h | 1 + radio/src/thirdparty/libopenui | 2 +- radio/src/translations.cpp | 2 + radio/src/translations.h | 2 + radio/src/translations/cn.h.txt | 2 + radio/src/translations/cz.h.txt | 2 + radio/src/translations/de.h.txt | 2 + radio/src/translations/en.h.txt | 2 + radio/src/translations/es.h.txt | 2 + radio/src/translations/fi.h.txt | 2 + radio/src/translations/fr.h.txt | 2 + radio/src/translations/it.h.txt | 2 + radio/src/translations/nl.h.txt | 2 + radio/src/translations/pl.h.txt | 2 + radio/src/translations/pt.h.txt | 2 + radio/src/translations/se.h.txt | 2 + radio/src/translations/tw.h.txt | 2 + 28 files changed, 162 insertions(+), 127 deletions(-) diff --git a/radio/src/gui/colorlcd/layout.h b/radio/src/gui/colorlcd/layout.h index f611dc034..a1c31a9da 100644 --- a/radio/src/gui/colorlcd/layout.h +++ b/radio/src/gui/colorlcd/layout.h @@ -44,6 +44,7 @@ class LayoutFactory virtual const ZoneOption * getOptions() const = 0; virtual WidgetsContainer * create(LayoutPersistentData * persistentData) const = 0; virtual WidgetsContainer * load(LayoutPersistentData * persistentData) const = 0; + virtual void initPersistentData(LayoutPersistentData * persistentData) const = 0; protected: const char * id; diff --git a/radio/src/gui/colorlcd/layouts/layout_factory_impl.h b/radio/src/gui/colorlcd/layouts/layout_factory_impl.h index 30738471e..1acb3f7c5 100644 --- a/radio/src/gui/colorlcd/layouts/layout_factory_impl.h +++ b/radio/src/gui/colorlcd/layouts/layout_factory_impl.h @@ -150,7 +150,7 @@ class BaseLayoutFactory: public LayoutFactory return layout; } - void initPersistentData(Layout::PersistentData * persistentData) const + void initPersistentData(Layout::PersistentData * persistentData) const override { memset(persistentData, 0, sizeof(Layout::PersistentData)); if (options) { diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 2dbc75c93..465a54af7 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -128,16 +128,14 @@ class ModelButton: public Button { } else { error = readModel(modelCell->modelFilename, (uint8_t *)&partialModel.header, sizeof(partialModel), &version); - // LEN_BITMAP_NAME has now 4 bytes more - if (version <= 218) { - memmove(partialModel.timers, &(partialModel.header.bitmap[10]), sizeof(TimerData)*MAX_TIMERS); - memclear(&(partialModel.header.bitmap[10]), 4); + if (!error) { + if (modelCell->modelName[0] == '\0' + && partialModel.header.name[0] != '\0') { + modelCell->setModelName(partialModel.header.name); + } } } -// if (modelCell->modelName[0] == '\0' && !error) -// setModelName(partialModel.header.name); // resets buffer!!! - buffer = new BitmapBuffer(BMP_RGB565, MODELCELL_WIDTH, MODELCELL_HEIGHT); if (buffer == nullptr) { return; diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index 67f8f3b97..a674f1243 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -402,11 +402,12 @@ class Theme480: public OpenTxTheme dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS)|FOCUS_COLOR|CENTERED); } - void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const override + void drawProgressBar(BitmapBuffer *dc, coord_t x, coord_t y, coord_t w, + coord_t h, int value, int total) const override { dc->drawSolidRect(x, y, w, h, 1, DEFAULT_COLOR); if (value > 0) { - int width = (w * value) / 100; + int width = (w * value) / total; dc->drawSolidFilledRect(x + 2, y + 2, width - 4, h - 4, CHECKBOX_COLOR); } } diff --git a/radio/src/gui/colorlcd/themes/flysky.cpp b/radio/src/gui/colorlcd/themes/flysky.cpp index b10f50fb6..4a0648e5e 100644 --- a/radio/src/gui/colorlcd/themes/flysky.cpp +++ b/radio/src/gui/colorlcd/themes/flysky.cpp @@ -420,11 +420,12 @@ class FlyskyTheme: public OpenTxTheme dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS)|FOCUS_COLOR|CENTERED); } - void drawProgressBar(BitmapBuffer * dc, coord_t x, coord_t y, coord_t w, coord_t h, int value) const override + void drawProgressBar(BitmapBuffer *dc, coord_t x, coord_t y, coord_t w, + coord_t h, int value, int total) const override { dc->drawSolidRect(x, y, w, h, 1, DEFAULT_COLOR); if (value > 0) { - int width = (w * value) / 100; + int width = (w * value) / total; dc->drawSolidFilledRect(x + 2, y + 2, width - 4, h - 4, CHECKBOX_COLOR); } } diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index c58261c44..9901c1696 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -54,8 +54,9 @@ #define LEN_FILE_PATH_MAX (sizeof(SCRIPTS_TELEM_PATH)+1) // longest + "/" #if defined(COLORLCD) +#define RADIO_FILENAME "radio.bin" const char RADIO_MODELSLIST_PATH[] = RADIO_PATH PATH_SEPARATOR "models.txt"; -const char RADIO_SETTINGS_PATH[] = RADIO_PATH PATH_SEPARATOR "radio.bin"; +const char RADIO_SETTINGS_PATH[] = RADIO_PATH PATH_SEPARATOR RADIO_FILENAME; #if defined(SDCARD_YAML) const char RADIO_MODELSLIST_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "models.yml"; const char RADIO_SETTINGS_YAML_PATH[] = RADIO_PATH PATH_SEPARATOR "radio.yml"; diff --git a/radio/src/storage/conversions/conversions.cpp b/radio/src/storage/conversions/conversions.cpp index 75fa45aaf..74f8991c1 100644 --- a/radio/src/storage/conversions/conversions.cpp +++ b/radio/src/storage/conversions/conversions.cpp @@ -21,16 +21,107 @@ #include "opentx.h" #include "conversions.h" +#if defined(SDCARD) +#include "storage/modelslist.h" +#include "storage/sdcard_common.h" + +#if defined(COLORLCD) +static void drawProgressScreen(const char* filename, int progress, int total) +{ + OpenTxTheme* l_theme = static_cast(theme); + + lcd->reset(); + l_theme->drawBackground(lcd); + lcd->drawText(LCD_W/2, LCD_H/2 - 30, STR_CONVERTING, FONT(XL) | CENTERED | ALARM_COLOR); + lcd->drawText(LCD_W/2, LCD_H/2, filename, FONT(STD) | CENTERED | DEFAULT_COLOR); + + l_theme->drawProgressBar(lcd, + LCD_W / 4, + LCD_H / 2 + 40, + LCD_W / 2, + 20, + progress, total); + + WDG_RESET(); + lcdRefresh(); +} +#endif + void convertRadioData(int version) { TRACE("convertRadioData(%d)", version); +#if defined(COLORLCD) + // the theme has not been loaded before + static_cast(theme)->load(); +#endif + + // Init backlight mode before entering alert screens + requiredBacklightBright = BACKLIGHT_FORCED_ON; + +#if defined(PCBHORUS) + g_eeGeneral.blOffBright = 20; +#endif + + RAISE_ALERT(STR_STORAGE_WARNING, STR_SDCARD_CONVERSION_REQUIRE, NULL, + AU_NONE); + + unsigned converted = 0; + auto to_convert = modelslist.getModelsCount() + 1; + +#if defined(COLORLCD) + drawProgressScreen(RADIO_FILENAME, converted, to_convert); +#endif + + TRACE("converting '%s' (%d/%d)", RADIO_FILENAME, converted, to_convert); + #if STORAGE_CONVERSIONS < 220 if (version == 219) { convertRadioData_219_to_220(g_eeGeneral); } #endif + converted++; + +#if defined(SIMU) + RTOS_WAIT_MS(200); +#endif + + const char* error = nullptr; + for (auto category_ptr : modelslist.getCategories()) { + for (auto model_ptr : *category_ptr) { + + uint8_t model_version; + const char* filename = model_ptr->modelFilename; + + TRACE("converting '%s' (%d/%d)", filename, converted, to_convert); +#if defined(COLORLCD) + drawProgressScreen(filename, converted, to_convert); +#endif + + error = readModel(filename, (uint8_t *)&g_model, sizeof(g_model), &model_version); + if (!error) { + + convertModelData(model_version); + + char path[256]; + getModelPath(path, filename); + error = writeFile(path, (uint8_t *)&g_model, sizeof(g_model)); + //TODO: what should be done with this error? + } + + converted++; + +#if defined(SIMU) + RTOS_WAIT_MS(200); +#endif + } + } + + // reload models list + modelslist.clear(); + modelslist.load(); } +#endif void convertModelData(int version) { diff --git a/radio/src/storage/conversions/conversions_219_220.cpp b/radio/src/storage/conversions/conversions_219_220.cpp index 8743b2889..7fbe3eb0f 100644 --- a/radio/src/storage/conversions/conversions_219_220.cpp +++ b/radio/src/storage/conversions/conversions_219_220.cpp @@ -94,6 +94,11 @@ static void convertToStr(char* str, size_t len) } } +#if defined(COLORLCD) +extern const LayoutFactory * defaultLayout; +extern OpenTxTheme * defaultTheme; +#endif + void convertModelData_219_to_220(ModelData &model) { ModelData* oldModelAllocated = (ModelData*)malloc(sizeof(ModelData)); @@ -101,7 +106,6 @@ void convertModelData_219_to_220(ModelData &model) memcpy(&oldModel, &model, sizeof(ModelData)); ModelData& newModel = (ModelData&)model; - convertToStr(model.header.name, LEN_MODEL_NAME_219); for (uint8_t i=0; igetId()); + defaultLayout->initPersistentData(&newModel.screenData[0].layoutData); + } #endif free(oldModelAllocated); } -#if defined(PCBHORUS) -extern OpenTxTheme * defaultTheme; -#endif - void convertRadioData_219_to_220(RadioData & settings) { TRACE("Radio conversion from v219 to v220"); @@ -217,7 +221,7 @@ void convertRadioData_219_to_220(RadioData & settings) RadioData& oldSettings = *oldSettingsAllocated; memcpy(&oldSettings, &settings, sizeof(RadioData)); -#if defined(PCBHORUS) +#if defined(COLORLCD) // Clear CustomScreenData + TopBarPersistentData // as they cannot be converted (missing option types) strcpy(g_eeGeneral.themeName, defaultTheme->getName()); diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index 0b5c20b81..b871c5732 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/storage/modelslist.cpp @@ -28,17 +28,14 @@ using std::list; ModelsList modelslist; -ModelCell::ModelCell(const char * name): - valid_rfData(false) +ModelCell::ModelCell(const char* name) : valid_rfData(false) { strncpy(modelFilename, name, sizeof(modelFilename)); } -ModelCell::ModelCell(const char * name, uint8_t len) - : valid_rfData(false) +ModelCell::ModelCell(const char* name, uint8_t len) : valid_rfData(false) { - if (len > sizeof(modelFilename)-1) - len = sizeof(modelFilename)-1; + if (len > sizeof(modelFilename) - 1) len = sizeof(modelFilename) - 1; memcpy(modelFilename, name, len); modelFilename[len] = '\0'; @@ -55,7 +52,7 @@ void ModelCell::setModelName(char * name) char * tmp; strncpy(modelName, modelFilename, LEN_MODEL_NAME); tmp = (char *) memchr(modelName, '.', LEN_MODEL_NAME); - if (tmp) + if (tmp != nullptr) *tmp = 0; } } @@ -72,11 +69,10 @@ void ModelCell::setModelName(char* name, uint8_t len) char * tmp; strncpy(modelName, modelFilename, LEN_MODEL_NAME); tmp = (char *) memchr(modelName, '.', LEN_MODEL_NAME); - if (tmp != NULL) + if (tmp != nullptr) *tmp = 0; } - //resetBuffer(); } void ModelCell::setModelId(uint8_t moduleIdx, uint8_t id) @@ -84,93 +80,6 @@ void ModelCell::setModelId(uint8_t moduleIdx, uint8_t id) modelId[moduleIdx] = id; } -// void ModelCell::resetBuffer() -// { -// if (buffer) { -// delete buffer; -// buffer = NULL; -// } -// } - -// const BitmapBuffer * ModelCell::getBuffer() -// { -// if (!buffer) { -// loadBitmap(); -// } -// return buffer; -// } - -// void ModelCell::loadBitmap() -// { -// uint8_t version; - -// PACK(struct { -// ModelHeader header; -// TimerData timers[MAX_TIMERS]; -// }) partialmodel; - -// const char * error = NULL; -// BitmapBuffer * tmp_buffer; - -// if (strncmp(modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) == 0) { -// memcpy(&partialmodel.header, &g_model.header, sizeof(partialmodel)); -// } -// else { -// #if !defined (SDCARD_YAML) -// error = readModel(modelFilename, (uint8_t *)&partialmodel.header, sizeof(partialmodel), &version); -// // LEN_BITMAP_NAME has now 4 bytes more -// if (version <= 218) { -// memmove(partialmodel.timers, &(partialmodel.header.bitmap[10]), sizeof(TimerData)*MAX_TIMERS); -// memclear(&(partialmodel.header.bitmap[10]), 4); -// } -// #else -// memset(&partialmodel.header,0,sizeof(partialmodel)); -// #endif -// } - -// if ((modelName[0] == 0) && ! error) -// setModelName(partialmodel.header.name); // resets buffer!!! - -// tmp_buffer = new BitmapBuffer(BMP_RGB565, MODELCELL_WIDTH, MODELCELL_HEIGHT); -// if (tmp_buffer == NULL) { -// return; -// } -// tmp_buffer->clear(TEXT_BGCOLOR); - -// if (error) { -// tmp_buffer->drawText(5, 2, "(Invalid Model)", TEXT_COLOR); -// tmp_buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, TEXT_COLOR); -// } -// else { -// char timer[LEN_TIMER_STRING]; -// tmp_buffer->drawSizedText(5, 2, modelName, /*LEN_MODEL_NAME*/strlen(modelName), -// SMLSIZE|TEXT_COLOR); - -// getTimerString(timer, 0); -// for (uint8_t i = 0; i < MAX_TIMERS; i++) { -// if (partialmodel.timers[i].mode != 0 && partialmodel.timers[i].persistent) { -// getTimerString(timer, partialmodel.timers[i].value, 1); -// break; -// } -// } -// tmp_buffer->drawText(101, 40, timer, TEXT_COLOR); -// for (int i=0; i<4; i++) { -// tmp_buffer->drawBitmapPattern(104+i*11, 25, LBM_SCORE0, TITLE_BGCOLOR); -// } -// GET_FILENAME(filename, BITMAPS_PATH, partialmodel.header.bitmap, ""); -// const BitmapBuffer * bitmap = BitmapBuffer::load(filename); -// if (bitmap) { -// tmp_buffer->drawScaledBitmap(bitmap, 5, 24, 56, 32); -// delete bitmap; -// } -// else { -// tmp_buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, TEXT_COLOR); -// } -// } -// tmp_buffer->drawSolidHorizontalLine(5, 19, 143, LINE_COLOR); - -// buffer = tmp_buffer; -// } void ModelCell::save(FIL* file) { diff --git a/radio/src/storage/modelslist.h b/radio/src/storage/modelslist.h index 85054c512..07d1f7af6 100644 --- a/radio/src/storage/modelslist.h +++ b/radio/src/storage/modelslist.h @@ -65,8 +65,6 @@ class ModelCell void setRfModuleData(uint8_t moduleIdx, ModuleData* modData); bool fetchRfData(); - // void loadBitmap(); - // void resetBuffer(); }; class ModelsCategory: public std::list diff --git a/radio/src/storage/sdcard_common.cpp b/radio/src/storage/sdcard_common.cpp index 59e1e8668..cdf755ee8 100644 --- a/radio/src/storage/sdcard_common.cpp +++ b/radio/src/storage/sdcard_common.cpp @@ -145,6 +145,13 @@ void storageReadAll() { TRACE("storageReadAll"); + // Wipe models list in case + // it's being reloaded after USB connection + modelslist.clear(); + + // and reload the list + modelslist.load(); + if (loadRadioSettings() != nullptr) { storageEraseAll(true); } @@ -172,12 +179,5 @@ void storageReadAll() if (loadModel(g_eeGeneral.currModelFilename, false) != nullptr) { TRACE("No current model or SD card error"); } - - // Wipe models list in case - // it's being reloaded after USB connection - modelslist.clear(); - - // and reload the list - modelslist.load(); } diff --git a/radio/src/storage/sdcard_common.h b/radio/src/storage/sdcard_common.h index 5533e004a..9d7683d12 100644 --- a/radio/src/storage/sdcard_common.h +++ b/radio/src/storage/sdcard_common.h @@ -35,6 +35,7 @@ // opens radio.bin or model file const char * openFile(const char * fullpath, FIL * file, uint16_t * size, uint8_t * version); +const char * writeFile(const char * fullpath, const uint8_t * data, uint16_t size); void getModelPath(char * path, const char * filename); diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 4b78e03cd..ace19326c 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 4b78e03cd00608d7870e6603eb1ff974fbd05da6 +Subproject commit ace19326c5bacd9b0b2561c2c9a0fc780beecbd6 diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index c7b8b26ea..87363800f 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -821,6 +821,8 @@ const char STR_EEPROMOVERFLOW[] = TR_EEPROMOVERFLOW; const char STR_BAD_RADIO_DATA[] = TR_BAD_RADIO_DATA; const char STR_STORAGE_WARNING[] = TR_STORAGE_WARNING; const char STR_STORAGE_FORMAT[] = TR_STORAGE_FORMAT; +const char STR_SDCARD_CONVERSION_REQUIRE[] = TR_SDCARD_CONVERSION_REQUIRE; +const char STR_CONVERTING[] = TR_CONVERTING; #endif #if defined(LUA) diff --git a/radio/src/translations.h b/radio/src/translations.h index fd5175c44..ad0a0f68a 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -600,6 +600,8 @@ extern const char STR_CAT_NOT_EMPTY[]; extern const char STR_WARNING[]; extern const char STR_STORAGE_WARNING[]; extern const char STR_EEPROM_CONVERTING[]; +extern const char STR_SDCARD_CONVERSION_REQUIRE[]; +extern const char STR_CONVERTING[]; extern const char STR_THROTTLE_UPPERCASE[]; extern const char STR_ALARMSWARN[]; extern const char STR_SWITCHWARN[]; diff --git a/radio/src/translations/cn.h.txt b/radio/src/translations/cn.h.txt index b1577ab32..3de51490b 100644 --- a/radio/src/translations/cn.h.txt +++ b/radio/src/translations/cn.h.txt @@ -690,6 +690,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "存储" #define TR_EEPROM_CONVERTING "转换数据" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "油门" #define TR_ALARMSWARN "警告" #define TR_SWITCHWARN TR("开关", "开关位置") diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index c0801defa..d80f62470 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -777,6 +777,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "ÚLOŽIŠTĚ" #define TR_EEPROM_CONVERTING "Aktualizuji EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "PLYNU" #define TR_ALARMSWARN "ALARMU" #define TR_SWITCHWARN "POZICE" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 384fbd23d..eccd3bee1 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -782,6 +782,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "SPEICHER" #define TR_EEPROM_CONVERTING "EEPROM Konvertierung" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "GAS" #define TR_ALARMSWARN "ALARM" #define TR_SWITCHWARN "SCHALTER" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index ae77352ea..914910216 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -786,6 +786,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "Converting EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "THROTTLE" #define TR_ALARMSWARN "ALARMS" #define TR_SWITCHWARN TR("SWITCH", "CONTROL") diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index d483265d4..b88667292 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -784,6 +784,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "ALMAC." #define TR_EEPROM_CONVERTING "Convirtiendo EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE TR("ACELERAD.", "ACELERADOR") #define TR_ALARMSWARN "ALARMAS" #define TR_SWITCHWARN TR("INTERRUP.", "INTERRUPTOR") diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 626128ffc..05eb538cd 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -798,6 +798,8 @@ #define TR_EEPROMWARN "FINSKA" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "EEPROM Converting" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "THROTTLE" #define TR_ALARMSWARN "ALARMS" #define TR_SWITCHWARN TR("SWITCH","CONTROL") diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index 119d95a4d..4f0ffc7d3 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -803,6 +803,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STOCKAGE" #define TR_EEPROM_CONVERTING "Conversion EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "GAZ" #define TR_ALARMSWARN "SON" #define TR_SWITCHWARN TR("INTERS","CONTROLES") diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index 435d785d4..d035dfd1d 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -804,6 +804,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "Conversione EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "MOTORE" #define TR_ALARMSWARN "ALLARMI" #define TR_SWITCHWARN "CONTROLLI" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index f8c132f8e..b9486ca6e 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -787,6 +787,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "EEPROM Converteren" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "GAS" #define TR_ALARMSWARN "ALARM" #define TR_SWITCHWARN "SCHAKELAAR" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index d0259b62a..593e019da 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -801,6 +801,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "Konwersja EEPROM" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "GAZ" #define TR_ALARMSWARN "ALARM" #define TR_SWITCHWARN TR("Przełą","Kontrola") diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index a33ebe0fe..b16e08f32 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -802,6 +802,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "STORAGE" #define TR_EEPROM_CONVERTING "EEPROM Converting" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "ACELERAD" #define TR_ALARMSWARN "ALARMES" #define TR_SWITCHWARN "CHAVES" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 7addcd639..22dee994a 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -803,6 +803,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "LAGRING" #define TR_EEPROM_CONVERTING "EEPROM Konvertering" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "GAS" #define TR_ALARMSWARN "ALARM" #define TR_SWITCHWARN "BRYTARE" diff --git a/radio/src/translations/tw.h.txt b/radio/src/translations/tw.h.txt index e492070ac..06a903269 100644 --- a/radio/src/translations/tw.h.txt +++ b/radio/src/translations/tw.h.txt @@ -690,6 +690,8 @@ #define TR_EEPROMWARN "EEPROM" #define TR_STORAGE_WARNING "存儲" #define TR_EEPROM_CONVERTING "轉換數據" +#define TR_SDCARD_CONVERSION_REQUIRE "SD card conversion required" +#define TR_CONVERTING "Converting: " #define TR_THROTTLE_UPPERCASE "油門" #define TR_ALARMSWARN "警告" #define TR_SWITCHWARN TR("開關", "開關位置") From 685acfecf4125735350291c0f39d22be19396ee7 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Jun 2021 13:34:25 +0200 Subject: [PATCH 021/122] Set default values on custom screen options --- radio/src/gui/colorlcd/layouts/layout_factory_impl.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/radio/src/gui/colorlcd/layouts/layout_factory_impl.h b/radio/src/gui/colorlcd/layouts/layout_factory_impl.h index 1acb3f7c5..3a717a100 100644 --- a/radio/src/gui/colorlcd/layouts/layout_factory_impl.h +++ b/radio/src/gui/colorlcd/layouts/layout_factory_impl.h @@ -28,12 +28,12 @@ #include "widgets/widgets_container_impl.h" #include "layout.h" -#define LAYOUT_COMMON_OPTIONS \ - { STR_TOP_BAR, ZoneOption::Bool }, \ - { STR_FLIGHT_MODE, ZoneOption::Bool }, \ - { STR_SLIDERS, ZoneOption::Bool }, \ - { STR_TRIMS, ZoneOption::Bool }, \ - { STR_MIRROR, ZoneOption::Bool } +#define LAYOUT_COMMON_OPTIONS \ + {STR_TOP_BAR, ZoneOption::Bool, OPTION_VALUE_BOOL(true)}, \ + {STR_FLIGHT_MODE, ZoneOption::Bool, OPTION_VALUE_BOOL(true)}, \ + {STR_SLIDERS, ZoneOption::Bool, OPTION_VALUE_BOOL(true)}, \ + {STR_TRIMS, ZoneOption::Bool, OPTION_VALUE_BOOL(true)}, \ + {STR_MIRROR, ZoneOption::Bool, OPTION_VALUE_BOOL(false)} #define LAYOUT_OPTIONS_END \ { nullptr, ZoneOption::Bool } From e5a34a583f6a6015a3435dbce7af0892be9cf8f5 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Jun 2021 14:47:38 +0200 Subject: [PATCH 022/122] Fixed defines --- radio/src/storage/conversions/conversions.cpp | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/radio/src/storage/conversions/conversions.cpp b/radio/src/storage/conversions/conversions.cpp index 74f8991c1..18e52d6a0 100644 --- a/radio/src/storage/conversions/conversions.cpp +++ b/radio/src/storage/conversions/conversions.cpp @@ -21,11 +21,10 @@ #include "opentx.h" #include "conversions.h" -#if defined(SDCARD) +#if defined(COLORLCD) #include "storage/modelslist.h" #include "storage/sdcard_common.h" -#if defined(COLORLCD) static void drawProgressScreen(const char* filename, int progress, int total) { OpenTxTheme* l_theme = static_cast(theme); @@ -45,23 +44,17 @@ static void drawProgressScreen(const char* filename, int progress, int total) WDG_RESET(); lcdRefresh(); } -#endif void convertRadioData(int version) { TRACE("convertRadioData(%d)", version); -#if defined(COLORLCD) // the theme has not been loaded before static_cast(theme)->load(); -#endif // Init backlight mode before entering alert screens requiredBacklightBright = BACKLIGHT_FORCED_ON; - -#if defined(PCBHORUS) g_eeGeneral.blOffBright = 20; -#endif RAISE_ALERT(STR_STORAGE_WARNING, STR_SDCARD_CONVERSION_REQUIRE, NULL, AU_NONE); @@ -69,10 +62,7 @@ void convertRadioData(int version) unsigned converted = 0; auto to_convert = modelslist.getModelsCount() + 1; -#if defined(COLORLCD) drawProgressScreen(RADIO_FILENAME, converted, to_convert); -#endif - TRACE("converting '%s' (%d/%d)", RADIO_FILENAME, converted, to_convert); #if STORAGE_CONVERSIONS < 220 @@ -94,9 +84,7 @@ void convertRadioData(int version) const char* filename = model_ptr->modelFilename; TRACE("converting '%s' (%d/%d)", filename, converted, to_convert); -#if defined(COLORLCD) drawProgressScreen(filename, converted, to_convert); -#endif error = readModel(filename, (uint8_t *)&g_model, sizeof(g_model), &model_version); if (!error) { From 7bbfa0e3d3d03657684bdb464ca9beb6443e328b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Jun 2021 15:02:34 +0200 Subject: [PATCH 023/122] Fixes !switch conditions in timers Resolves #175 --- radio/src/timers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/timers.cpp b/radio/src/timers.cpp index afda7f0ef..0ee289e78 100644 --- a/radio/src/timers.cpp +++ b/radio/src/timers.cpp @@ -74,7 +74,7 @@ void evalTimers(int16_t throttle, uint8_t tick10ms) for (uint8_t i=0; i Date: Thu, 10 Jun 2021 15:17:16 +0200 Subject: [PATCH 024/122] Move back to RADIO and MODELS instead of RADIO24 and MODELS24 --- radio/src/sdcard.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/sdcard.h b/radio/src/sdcard.h index 9901c1696..5683f1afc 100644 --- a/radio/src/sdcard.h +++ b/radio/src/sdcard.h @@ -29,8 +29,8 @@ #define PATH_SEPARATOR "/" #define ROOT_PATH PATH_SEPARATOR -#define MODELS_PATH ROOT_PATH "MODELS24" // no trailing slash = important //TODO Temporary while working on both 2.3 and 2.4 -#define RADIO_PATH ROOT_PATH "RADIO24" // no trailing slash = important +#define MODELS_PATH ROOT_PATH "MODELS" // no trailing slash = important +#define RADIO_PATH ROOT_PATH "RADIO" // no trailing slash = important #define LOGS_PATH ROOT_PATH "LOGS" #define SCREENSHOTS_PATH ROOT_PATH "SCREENSHOTS" #define SOUNDS_PATH ROOT_PATH "SOUNDS/en" From 933dcf931af3eb9c0eacf83d6ae7deb132102dd6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Jun 2021 15:37:54 +0200 Subject: [PATCH 025/122] Update libopenui --- radio/src/thirdparty/libopenui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index ace19326c..d68fd2c90 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit ace19326c5bacd9b0b2561c2c9a0fc780beecbd6 +Subproject commit d68fd2c902c20a347f784612d4ab1e5c8d130e4d From 2bd231b103320af393f5e8b5902d0f5dccb544d3 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Jun 2021 17:36:32 +0200 Subject: [PATCH 026/122] Fixed RadioData size check Added 1 byte for ADC sample mode (see Hydra's PR). --- radio/src/datastructs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index a51253bf3..557a9f3cd 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -965,7 +965,7 @@ static inline void check_struct() CHKSIZE(RadioData, 899); CHKSIZE(ModelData, 6604); #elif defined(PCBHORUS) - CHKSIZE(RadioData, 901); + CHKSIZE(RadioData, 902); CHKSIZE(ModelData, 11020); #endif From 2513d28d4557aa544e74cc0170f141bf71c525ed Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Jun 2021 19:44:21 +0200 Subject: [PATCH 027/122] Fix CN / TW translations --- radio/src/translations/cn.h.txt | 3 +++ radio/src/translations/tw.h.txt | 3 +++ 2 files changed, 6 insertions(+) diff --git a/radio/src/translations/cn.h.txt b/radio/src/translations/cn.h.txt index 3de51490b..8f1b6f2ed 100644 --- a/radio/src/translations/cn.h.txt +++ b/radio/src/translations/cn.h.txt @@ -750,6 +750,9 @@ #define TR_TEMP_CALIB "温度校准" #define TR_TIME "时间" #define TR_MAXBAUDRATE "最大带宽" +#define TR_SAMPLE_MODE "Sample Mode" +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "蓝牙" #define TR_BLUETOOTH_DISC "发现" #define TR_BLUETOOTH_INIT "初始化" diff --git a/radio/src/translations/tw.h.txt b/radio/src/translations/tw.h.txt index 06a903269..7dba22e07 100644 --- a/radio/src/translations/tw.h.txt +++ b/radio/src/translations/tw.h.txt @@ -750,6 +750,9 @@ #define TR_TEMP_CALIB "溫度校準" #define TR_TIME "時間" #define TR_MAXBAUDRATE "最大帶寬" +#define TR_SAMPLE_MODE "Sample Mode" +#define LEN_SAMPLE_MODES "\006" +#define TR_SAMPLE_MODES "Normal""OneBit" #define TR_BLUETOOTH "藍牙" #define TR_BLUETOOTH_DISC "發現" #define TR_BLUETOOTH_INIT "初始化" From 8336647ecc383c041edd37759f359cdf4c10d8e0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 13 May 2021 17:41:52 +0200 Subject: [PATCH 028/122] Removed radio calibration bitmap --- radio/sdcard/horus/THEMES/EdgeTX/X10.bmp | Bin 102538 -> 0 bytes radio/sdcard/horus/THEMES/EdgeTX/X10S.bmp | Bin 100578 -> 0 bytes radio/sdcard/horus/THEMES/EdgeTX/horus.bmp | Bin 164938 -> 0 bytes radio/src/gui/colorlcd/bitmaps.cpp | 1 - radio/src/gui/colorlcd/bitmaps.h | 1 - radio/src/gui/colorlcd/radio_calibration.cpp | 11 ++--------- radio/src/gui/colorlcd/themes/480_default.cpp | 11 ----------- radio/src/gui/colorlcd/themes/flysky.cpp | 3 --- 8 files changed, 2 insertions(+), 25 deletions(-) delete mode 100755 radio/sdcard/horus/THEMES/EdgeTX/X10.bmp delete mode 100755 radio/sdcard/horus/THEMES/EdgeTX/X10S.bmp delete mode 100644 radio/sdcard/horus/THEMES/EdgeTX/horus.bmp diff --git a/radio/sdcard/horus/THEMES/EdgeTX/X10.bmp b/radio/sdcard/horus/THEMES/EdgeTX/X10.bmp deleted file mode 100755 index 24893bad005f8f1041fd223fbcfda10317e9f865..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102538 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 aM~c~ZReq{*Rclp_!^p7?)>A&r<@|r~j=JLj diff --git a/radio/sdcard/horus/THEMES/EdgeTX/X10S.bmp b/radio/sdcard/horus/THEMES/EdgeTX/X10S.bmp deleted file mode 100755 index e27932eefa8d8af7b916e56fbfdbdbc0f5eb7282..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100578 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 diff --git a/radio/sdcard/horus/THEMES/EdgeTX/horus.bmp b/radio/sdcard/horus/THEMES/EdgeTX/horus.bmp deleted file mode 100644 index 98bc631cd288bc7c16a1dd6e287b6255d98dd6f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 164938 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

LyzvUZy3t3z_zYy*HIO`~MNoEEed^tD@IoAT> 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 iJ?7>s?#uk$H-K*yzv*snVNMgetFilePath("shutdown.bmp")); - } + static const BitmapBuffer * shutdown = OpenTxTheme::instance()->shutdown; lcd->reset(); - if (shutdown) { OpenTxTheme::instance()->drawBackground(lcd); - lcd->drawBitmap((LCD_W - shutdown->width()) / 2, - (LCD_H - shutdown->height()) / 2, shutdown); + lcd->drawMask((LCD_W - shutdown->width()) / 2, + (LCD_H - shutdown->height()) / 2, shutdown, DEFAULT_COLOR); + int quarter = duration / (totalDuration / 5); if (quarter >= 1) lcd->drawBitmapPattern(LCD_W / 2, (LCD_H - SHUTDOWN_CIRCLE_DIAMETER) / 2, diff --git a/radio/src/gui/colorlcd/fullscreen_dialog.cpp b/radio/src/gui/colorlcd/fullscreen_dialog.cpp index 73f139421..4efc67064 100644 --- a/radio/src/gui/colorlcd/fullscreen_dialog.cpp +++ b/radio/src/gui/colorlcd/fullscreen_dialog.cpp @@ -54,16 +54,14 @@ void FullScreenDialog::paint(BitmapBuffer * dc) FOCUS_COLOR, OPACITY(8)); if (type == WARNING_TYPE_ALERT || type == WARNING_TYPE_ASTERISK) { - dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, - OpenTxTheme::instance()->asterisk); - } - else if (type == WARNING_TYPE_INFO) { - dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, - OpenTxTheme::instance()->busy); - } - else { // confirmation - dc->drawBitmap(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, - OpenTxTheme::instance()->question); + dc->drawMask(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, + OpenTxTheme::instance()->asterisk, ALARM_COLOR); + } else if (type == WARNING_TYPE_INFO) { + dc->drawMask(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, + OpenTxTheme::instance()->busy, ALARM_COLOR); + } else { // confirmation + dc->drawMask(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, + OpenTxTheme::instance()->question, ALARM_COLOR); } if (type == WARNING_TYPE_ALERT) { diff --git a/radio/src/gui/colorlcd/theme.cpp b/radio/src/gui/colorlcd/theme.cpp index 5954ca760..f009b3367 100644 --- a/radio/src/gui/colorlcd/theme.cpp +++ b/radio/src/gui/colorlcd/theme.cpp @@ -28,6 +28,7 @@ extern OpenTxTheme * defaultTheme; const BitmapBuffer * OpenTxTheme::asterisk = nullptr; const BitmapBuffer * OpenTxTheme::question = nullptr; const BitmapBuffer * OpenTxTheme::busy = nullptr; +const BitmapBuffer * OpenTxTheme::shutdown = nullptr; const uint8_t LBM_FOLDER[] = { #include "mask_folder.lbm" @@ -44,6 +45,22 @@ const uint8_t LBM_USB_PLUGGED[] = { #include "mask_usb_symbol.lbm" }; +const uint8_t asterisk_bitmap[] = { +#include "mask_asterisk.lbm" +}; + +const uint8_t question_bitmap[] = { +#include "mask_question.lbm" +}; + +const uint8_t busy_bitmap[] = { +#include "mask_busy.lbm" +}; + +const uint8_t shutdown_bitmap[] = { +#include "mask_shutdown.lbm" +}; + std::list & getRegisteredThemes() { static std::list themes; @@ -72,11 +89,13 @@ void OpenTxTheme::init() const void OpenTxTheme::load() const { if (!asterisk) - asterisk = BitmapBuffer::loadBitmap(getFilePath("asterisk.bmp")); + asterisk = BitmapBuffer::load8bitMask(asterisk_bitmap); if (!question) - question = BitmapBuffer::loadBitmap(getFilePath("question.bmp")); + question = BitmapBuffer::load8bitMask(question_bitmap); if (!busy) - busy = BitmapBuffer::loadBitmap(getFilePath("busy.bmp")); + busy = BitmapBuffer::load8bitMask(busy_bitmap); + if (!shutdown) + shutdown = BitmapBuffer::load8bitMask(shutdown_bitmap); } ZoneOptionValue * OpenTxTheme::getOptionValue(unsigned int index) const diff --git a/radio/src/gui/colorlcd/theme.h b/radio/src/gui/colorlcd/theme.h index 90d2db9e8..2d5c61608 100644 --- a/radio/src/gui/colorlcd/theme.h +++ b/radio/src/gui/colorlcd/theme.h @@ -114,6 +114,7 @@ class OpenTxTheme: public Theme static const BitmapBuffer * asterisk; static const BitmapBuffer * question; static const BitmapBuffer * busy; + static const BitmapBuffer * shutdown; }; OpenTxTheme * getTheme(const char * name); diff --git a/radio/src/gui/colorlcd/themes/480_bitmaps.h b/radio/src/gui/colorlcd/themes/480_bitmaps.h index 4fc6e5494..2b1df160b 100644 --- a/radio/src/gui/colorlcd/themes/480_bitmaps.h +++ b/radio/src/gui/colorlcd/themes/480_bitmaps.h @@ -226,3 +226,19 @@ const uint8_t stick_background[] = { const uint8_t trackp_background[] = { #include "alpha_trackp_background.lbm" }; +const uint8_t mask_sdfree[] = { +#include "mask_sdfree.lbm" +}; +const uint8_t mask_modelqty[] = { +#include "mask_modelqty.lbm" +}; +const uint8_t mask_modelname[] = { +#include "mask_modelname.lbm" +}; +const uint8_t mask_moveback[] = { +#include "mask_moveback.lbm" +}; +const uint8_t mask_moveico[] = { +#include "mask_moveico.lbm" +}; + diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index 97b6ebe79..374b474a5 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -213,29 +213,33 @@ class Theme480: public OpenTxTheme trackp_background, sizeof(trackp_background)); // Model Selection screen - delete modelselIconBitmap; - modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); - if (modelselIconBitmap) { - BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(getFilePath("modelsel/icon_default.png")); - modelselIconBitmap->drawBitmap(20, 8, bitmap); - delete bitmap; - } + + // Unused: + // + // delete modelselIconBitmap; + // modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); + // if (modelselIconBitmap) { + // BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(getFilePath("modelsel/icon_default.png")); + // modelselIconBitmap->drawBitmap(20, 8, bitmap); + // delete bitmap; + // } delete modelselSdFreeBitmap; - modelselSdFreeBitmap = BitmapBuffer::loadMask(getFilePath("modelsel/mask_sdfree.png")); + modelselSdFreeBitmap = BitmapBuffer::load8bitMask(mask_sdfree); delete modelselModelQtyBitmap; - modelselModelQtyBitmap = BitmapBuffer::loadMask(getFilePath("modelsel/mask_modelqty.png")); + modelselModelQtyBitmap = BitmapBuffer::load8bitMask(mask_modelqty); delete modelselModelNameBitmap; - modelselModelNameBitmap = BitmapBuffer::loadMask(getFilePath("modelsel/mask_modelname.png")); + modelselModelNameBitmap = BitmapBuffer::load8bitMask(mask_modelname); delete modelselModelMoveBackground; - modelselModelMoveBackground = BitmapBuffer::loadMask(getFilePath("modelsel/mask_moveback.png")); + modelselModelMoveBackground = BitmapBuffer::load8bitMask(mask_moveback); delete modelselModelMoveIcon; - modelselModelMoveIcon = BitmapBuffer::loadMask(getFilePath("modelsel/mask_moveico.png")); + modelselModelMoveIcon = BitmapBuffer::load8bitMask(mask_moveico); + //TODO: should be loaded from LUA, not here!!! delete modelselWizardBackground; modelselWizardBackground = BitmapBuffer::loadBitmap(getFilePath("wizard/background.png")); diff --git a/radio/src/gui/colorlcd/themes/flysky.cpp b/radio/src/gui/colorlcd/themes/flysky.cpp index 6cdccecb4..a395287d3 100644 --- a/radio/src/gui/colorlcd/themes/flysky.cpp +++ b/radio/src/gui/colorlcd/themes/flysky.cpp @@ -216,13 +216,13 @@ class FlyskyTheme: public OpenTxTheme calibTrackpBackground = BitmapBuffer::loadBitmap(getFilePath("trackp_background.png")); // Model Selection screen - delete modelselIconBitmap; - modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); - if (modelselIconBitmap) { - BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(getFilePath("modelsel/icon_default.png")); - modelselIconBitmap->drawBitmap(20, 8, bitmap); - delete bitmap; - } + // delete modelselIconBitmap; + // modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); + // if (modelselIconBitmap) { + // BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(getFilePath("modelsel/icon_default.png")); + // modelselIconBitmap->drawBitmap(20, 8, bitmap); + // delete bitmap; + // } delete modelselSdFreeBitmap; modelselSdFreeBitmap = BitmapBuffer::loadMask(getFilePath("modelsel/mask_sdfree.png")); From 1bf6ada539ab1ecb1d99cea0d8e4afdadfe19178 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Jun 2021 19:24:33 +0200 Subject: [PATCH 036/122] Use a more generic error icon Thx to https://icons8.com --- .../480x272/default_theme/mask_error.png | Bin 0 -> 9396 bytes radio/src/gui/colorlcd/fullscreen_dialog.cpp | 4 ++-- radio/src/gui/colorlcd/theme.cpp | 16 +++++----------- radio/src/gui/colorlcd/theme.h | 3 +-- radio/src/gui/colorlcd/themes/480_default.cpp | 3 +-- 5 files changed, 9 insertions(+), 17 deletions(-) create mode 100755 radio/src/bitmaps/480x272/default_theme/mask_error.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_error.png b/radio/src/bitmaps/480x272/default_theme/mask_error.png new file mode 100755 index 0000000000000000000000000000000000000000..3bf350e55c65b69be7c640c59d4ccb7a44723b35 GIT binary patch literal 9396 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 Ln(5rpIv@348ANv0 literal 0 HcmV?d00001 diff --git a/radio/src/gui/colorlcd/fullscreen_dialog.cpp b/radio/src/gui/colorlcd/fullscreen_dialog.cpp index 4efc67064..97a2f001b 100644 --- a/radio/src/gui/colorlcd/fullscreen_dialog.cpp +++ b/radio/src/gui/colorlcd/fullscreen_dialog.cpp @@ -55,13 +55,13 @@ void FullScreenDialog::paint(BitmapBuffer * dc) if (type == WARNING_TYPE_ALERT || type == WARNING_TYPE_ASTERISK) { dc->drawMask(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, - OpenTxTheme::instance()->asterisk, ALARM_COLOR); + OpenTxTheme::instance()->error, ALARM_COLOR); } else if (type == WARNING_TYPE_INFO) { dc->drawMask(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, OpenTxTheme::instance()->busy, ALARM_COLOR); } else { // confirmation dc->drawMask(ALERT_BITMAP_LEFT, ALERT_BITMAP_TOP, - OpenTxTheme::instance()->question, ALARM_COLOR); + OpenTxTheme::instance()->error, ALARM_COLOR); } if (type == WARNING_TYPE_ALERT) { diff --git a/radio/src/gui/colorlcd/theme.cpp b/radio/src/gui/colorlcd/theme.cpp index f009b3367..b9561fab9 100644 --- a/radio/src/gui/colorlcd/theme.cpp +++ b/radio/src/gui/colorlcd/theme.cpp @@ -25,8 +25,7 @@ //#include "modal_window.h" extern OpenTxTheme * defaultTheme; -const BitmapBuffer * OpenTxTheme::asterisk = nullptr; -const BitmapBuffer * OpenTxTheme::question = nullptr; +const BitmapBuffer * OpenTxTheme::error = nullptr; const BitmapBuffer * OpenTxTheme::busy = nullptr; const BitmapBuffer * OpenTxTheme::shutdown = nullptr; @@ -45,12 +44,9 @@ const uint8_t LBM_USB_PLUGGED[] = { #include "mask_usb_symbol.lbm" }; -const uint8_t asterisk_bitmap[] = { -#include "mask_asterisk.lbm" -}; -const uint8_t question_bitmap[] = { -#include "mask_question.lbm" +const uint8_t error_bitmap[] = { +#include "mask_error.lbm" }; const uint8_t busy_bitmap[] = { @@ -88,10 +84,8 @@ void OpenTxTheme::init() const void OpenTxTheme::load() const { - if (!asterisk) - asterisk = BitmapBuffer::load8bitMask(asterisk_bitmap); - if (!question) - question = BitmapBuffer::load8bitMask(question_bitmap); + if (!error) + error = BitmapBuffer::load8bitMask(error_bitmap); if (!busy) busy = BitmapBuffer::load8bitMask(busy_bitmap); if (!shutdown) diff --git a/radio/src/gui/colorlcd/theme.h b/radio/src/gui/colorlcd/theme.h index 2d5c61608..fdd5149c9 100644 --- a/radio/src/gui/colorlcd/theme.h +++ b/radio/src/gui/colorlcd/theme.h @@ -111,8 +111,7 @@ class OpenTxTheme: public Theme BitmapBuffer * thumb; public: - static const BitmapBuffer * asterisk; - static const BitmapBuffer * question; + static const BitmapBuffer * error; static const BitmapBuffer * busy; static const BitmapBuffer * shutdown; }; diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index 374b474a5..7b18995d1 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -334,8 +334,7 @@ class Theme480: public OpenTxTheme dc->drawBitmap(0 - dc->getOffsetX(), 0 - dc->getOffsetY(), backgroundBitmap); } else { - lcdSetColor(g_eeGeneral.themeData.options[0].value.unsignedValue); - dc->drawSolidFilledRect(0, 0, LCD_W, LCD_H, CUSTOM_COLOR); + dc->drawSolidFilledRect(0, 0, LCD_W, LCD_H, DEFAULT_BGCOLOR); } } From 4627b3c3240844dd3e07f06d845565c7cb9debd4 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 10 Jun 2021 20:20:56 +0200 Subject: [PATCH 037/122] Compute model select window height properly Resolves #180 --- radio/src/gui/colorlcd/model_select.cpp | 52 +++++-------------------- 1 file changed, 9 insertions(+), 43 deletions(-) diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 465a54af7..b0478ce90 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -40,44 +40,6 @@ enum ModelDeleteMode { }; uint8_t selectMode, deleteMode; -//ModelsList modelslist; - -//ModelsCategory * currentCategory; -//int currentCategoryIndex; -//ModelCell * currentModel; - -/* -void drawCategory(BitmapBuffer * dc, coord_t y, const char * name, bool selected) -{ - if (selected) { - lcdDrawSolidFilledRect(1, y-INVERT_VERT_MARGIN, CATEGORIES_WIDTH-10, INVERT_LINE_HEIGHT+2, FOCUS_BGCOLOR); - lcdDrawText(6, y, name, DEFAULT_COLOR | INVERS); - } - else { - lcdDrawText(6, y, name, DEFAULT_COLOR); - } -} - -void drawModel(coord_t x, coord_t y, ModelCell * model, bool current, bool selected) -{ - lcd->drawBitmap(x+1, y+1, model->getBuffer()); - if (current) { - lcd->drawBitmapPattern(x+66, y+43, LBM_ACTIVE_MODEL, TITLE_BGCOLOR); - } - if (selected) { - lcdDrawSolidRect(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2, 1, TITLE_BGCOLOR); - drawShadow(x, y, MODELCELL_WIDTH+2, MODELCELL_HEIGHT+2); - if (selectMode == MODE_MOVE_MODEL) { - lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->width(), y, modelselModelMoveBackground, TITLE_BGCOLOR); - lcd->drawMask(x+MODELCELL_WIDTH+2-modelselModelMoveBackground->width()+12, y+5, modelselModelMoveIcon, TEXT_BGCOLOR); - } - } -} -*/ - -//uint16_t categoriesVerticalOffset = 0; -//uint16_t categoriesVerticalPosition = 0; -#define MODEL_INDEX() (menuVerticalPosition*2+menuHorizontalPosition) #if 0 void setCurrentModel(unsigned int index) @@ -235,7 +197,7 @@ class ModelSelectFooter: public Window { ModelCell * currentModel = nullptr; }; -#if defined(PCBX10) +#if LCD_W > LCD_H constexpr coord_t MODEL_SELECT_CELL_WIDTH = (LCD_W - 3 * PAGE_PADDING) / 2; #else constexpr coord_t MODEL_SELECT_CELL_WIDTH = LCD_W - 2 * PAGE_PADDING; @@ -259,6 +221,7 @@ class ModelCategoryPageBody: public FormWindow { int index = 0; coord_t y = PAGE_PADDING; coord_t x = PAGE_PADDING; + coord_t h = PAGE_PADDING; for (auto & model: * category) { auto button = new ModelButton(this, {x, y, MODEL_SELECT_CELL_WIDTH, MODEL_SELECT_CELL_HEIGHT}, model, nullptr); @@ -319,21 +282,24 @@ class ModelCategoryPageBody: public FormWindow { footer->setCurrentModel(model); } - index++; -#if LCD_W >= 480 - if (x == PAGE_PADDING) { +#if LCD_W > LCD_H + if (index % 2 == 0) { x = PAGE_PADDING + MODEL_SELECT_CELL_WIDTH + PAGE_PADDING; + h += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING; } else { x = PAGE_PADDING; y += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING; } #else + h += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING; y += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING; #endif + + index++; } - setInnerHeight(y); + setInnerHeight(h); if (category->empty()) { setFocus(); From 430902ae6fc2376b087d1248cd3c6386b21dd4df Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 11 Jun 2021 07:01:15 +0200 Subject: [PATCH 038/122] Prevent USB pop-up items to be added multiple times Resolves #184 --- radio/src/main.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/radio/src/main.cpp b/radio/src/main.cpp index 45dd923ed..24dd94066 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -98,13 +98,15 @@ void onUSBConnectMenu(const char *result) void openUsbMenu() { - POPUP_MENU_ADD_ITEM(STR_USB_JOYSTICK); - POPUP_MENU_ADD_ITEM(STR_USB_MASS_STORAGE); + if (popupMenuHandler != onUSBConnectMenu) { + POPUP_MENU_ADD_ITEM(STR_USB_JOYSTICK); + POPUP_MENU_ADD_ITEM(STR_USB_MASS_STORAGE); #if defined(DEBUG) - POPUP_MENU_ADD_ITEM(STR_USB_SERIAL); + POPUP_MENU_ADD_ITEM(STR_USB_SERIAL); #endif - POPUP_MENU_TITLE(STR_SELECT_MODE); - POPUP_MENU_START(onUSBConnectMenu); + POPUP_MENU_TITLE(STR_SELECT_MODE); + POPUP_MENU_START(onUSBConnectMenu); + } } void closeUsbMenu() From ff4776b40dd92fd102032c574c0cf863876a089c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 11 Jun 2021 12:06:04 +0200 Subject: [PATCH 039/122] Use only the logo and background color as Splash screen. --- radio/src/bitmaps/480x272/CMakeLists.txt | 2 +- radio/src/bitmaps/480x272/big_logo.png | Bin 0 -> 34612 bytes radio/src/gui/colorlcd/splash.cpp | 7 +++++-- 3 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 radio/src/bitmaps/480x272/big_logo.png diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/480x272/CMakeLists.txt index 100143cce..32e10bb5f 100644 --- a/radio/src/bitmaps/480x272/CMakeLists.txt +++ b/radio/src/bitmaps/480x272/CMakeLists.txt @@ -20,7 +20,7 @@ set(BITMAP_RLE_ARGS ${BITMAP_ARGS} --rle) set(MASK_RLE_ARGS ${MASK_ARGS} --rle) add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/bmp_*.png" "5/6/5" "${BITMAP_ARGS}") -add_png_target(${BITMAP_TARGET_PREFIX}_splash "${RADIO_SRC_DIR}/bitmaps/480x272/splash.png") +add_png_target(${BITMAP_TARGET_PREFIX}_splash "${RADIO_SRC_DIR}/bitmaps/480x272/big_logo.png") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_calibration_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/calibration/bmp_*.png" "5/6/5" "${BITMAP_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_button_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/button/alpha_*.png" "4/4/4/4" "${BITMAP_ARGS}") diff --git a/radio/src/bitmaps/480x272/big_logo.png b/radio/src/bitmaps/480x272/big_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9642b41d7b315b95f79627a5fcc3cf0c38121679 GIT binary patch literal 34612 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 AjsO4v literal 0 HcmV?d00001 diff --git a/radio/src/gui/colorlcd/splash.cpp b/radio/src/gui/colorlcd/splash.cpp index 6b15b8a9a..4c035b5d0 100644 --- a/radio/src/gui/colorlcd/splash.cpp +++ b/radio/src/gui/colorlcd/splash.cpp @@ -23,11 +23,14 @@ #if defined(SPLASH) const uint8_t __bmp_splash[] { -#include "splash.lbm" +#include "big_logo.lbm" }; void drawSplash() { + constexpr LcdFlags splash_background_color = + COLOR2FLAGS(((0xC >> 3) << 11) | ((0x3F >> 2) << 5) | (0x66 >> 3)); + static bool loadSplashImg = true; static BitmapBuffer * splashImg = nullptr; @@ -43,7 +46,7 @@ void drawSplash() } } - lcd->clear(); + lcd->clear(splash_background_color); if (splashImg) { lcd->drawBitmap((LCD_W - splashImg->width())/2, From 66eda14cc440845323dcb76cc21dd3de0bdcaa35 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 11 Jun 2021 13:34:51 +0200 Subject: [PATCH 040/122] More decent shutdown animation --- .../480x272/default_theme/mask_shutdown.png | Bin 12739 -> 14651 bytes radio/src/gui/colorlcd/draw_functions.cpp | 34 ++++++++++-------- 2 files changed, 19 insertions(+), 15 deletions(-) mode change 100644 => 100755 radio/src/bitmaps/480x272/default_theme/mask_shutdown.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_shutdown.png b/radio/src/bitmaps/480x272/default_theme/mask_shutdown.png old mode 100644 new mode 100755 index 493254d7735a3eafbfc6c071e4af67d2cb0d72c1..87704cc5fc559b781680a7842b6d0ae52354bc0f GIT binary patch literal 14651 zcmeHsWmH^Swr=6>?gV!Vhr-?6oxBA-* zcyL$mQIn`8)I$z$B&u}EVLh-{I^RDAuzBY#9&MItlefkD_*(!qnfQZlRqr1r!@jG3 zdnbGuP@9#hi_M89tzzm&09AjUA-m^%GP`Q}#84B4SlT+00e5B7CK+#;x1>QHMY7vT zg0s46@bQBPOr5llAP14V=t3GR&CnI@@hhS`y*O2y{C&xb2uwYNU=BqJ$pP3hf8lqd zxUI*RgB|P<`mj{$dQoF)N$!v0jv?9=OPwI}t-L5fL_Uwm z=B?jr{bk3G>>XXu^g1S1)6+Vujq)XAJ_~-C#y*P=$@cWwz7vCG+5QWAtLj|W(U(mf zA)@wIcswE0C48@s%TLEOzC;7R%4-bAW+ln=yrL452V^-6Ca!!EOp;Ff zI1HjIQ$asMTqjnajvn_A9yMXmEK~<(cw>(fZKFP=V2pY<&fRWmnjfBDShsF-ILJl) zh-ovw{(;%XE3xvi#_J6BGt`2y!h6j< zpe9?xva0H6?NIeF?5@BiQ~i+sUDrJYBfgnvX0}aruHs{yw{O$yRlX#d`stBkbM*dS zBD81vednWRG8ysH-tQfzwuiDa-coVskF>l4;>jSsoMw@=yw~Iz;gPWMxps8GEaT%A zh#W^6nP)wG^Gj1MQox#J3+jg@(nkEekM6e0AZ{)3sBs_hlZr`;sp%Z=T&ab~`O?sq zfM#)nmo|4rL|u3RDW` zZ)TZt8@@xNj#O0v&6n9@=zfa50iEkC*a4~R7BL^F)aAi+9a!EK{jvGwww(=1-+E)Q z7$hE*GG)__wwOP|ZC|R38?3M!*5>dATPvqVwX}Nv}I8%7UCTaZ$I2C{W9=!gCWMY zrG&p7UO?^$1>^F8$B^whq3+)>&cFjD#Z{r!KP8vZP2D%Hq3=#j25YGjNrg)d3PB$_ z&^*BG*?ft(tkC$*&VcH)PK_5}|1sNpclw7!I3{nTN<7XLzYY!+Rv4@a0AR7Se6x@t zdUq7;3!WOr=|WdSn;t)>lJH~YohqAg#kGXNX+z}A=xY}Ff~Rq&B@1_q5Dp8vneaD{ zbNxBjoVx*+NkGJK*ZC5o;|0gW$_tKJM0sA`?>aK%-Fwj>>UGh1MU&KGl%D8p!<1Cs z(IcDvX+e1p^VDSuWdi}1ontG(dZ{|x9oTH@O z)3-*Ut&x9yyr$<-u>EM~7om!BmN)mcvlMW%z#|2%mh{Ptk7DIjEWe=l;=>~`jr}@6 zm5X+(E43OFOim6G10`n(f65Z_oEfJq+0zWdP_34aAO*T;miW(?#c*6q`ljk3>k*)H zrcsoWIJs~2F8U>NuBpqx_Aw9+ST_RuPS$7)RE-oklN1(w^NYh{w273HGg){HhGtL2_I zkr(~IlXXf(M;Nx?vMETO9g&I1rZ{8#KCjm}SS+LUZ6+ji-1O|N8B4Ynj}t>g`U?3t ziaZYw)BY$8?K;j`y%3q`O~%LSEDzQQ>G z+Bir*mnhpiR&fs4YXK#QUE3D+UZ4ROVd35g?d-zo$3(=Uoz$Wmif=RQB~vknj8jx; zhtqH7vhbcTaADkCXWuZ~yrM^HlTy%r{gT)t_Cq&HJ`a#~0N}BcTh26ve`1;dCr+j7 z<_uGP2z~9_lQmVs^zx~>DBNF>eaU~D+C}&&!Tqs7Vwoayx1;aC?2znO05liHZCi!FoKb6RA==YYR^4gvO-V2T;fL0SiPn`P268Mb5mHX8D|h3zn_f$=si0zQW4mPrNj(} z0uX2AzgN5oUm==^rfHM9mg+#5@Z}t_i7;yyKs-pMByO*DUdtX~adWg}+8g!x zK$iO|IZ$G%VB-ZtHsNj#g(E4b^;f7#e>iPy74_7eWyCD3OJh~8w2K7CKB}2RjM&sb zF)i7rcDYhyCaNUujjyQ`H{tOdDdl23q5X1x=Gr{XR-0*JR|nUy61U!@j3$jEc*&v@ z;xwFl>iv@lEE5&T83H}VYmBsLGw{6vt%Y#$v=t*Q%DRi{W)5OHtg)!`aO&_PEYu7W*_%tv183`^2 zmFh(RFq(wA9)~XJbl}G{>Ie&`Kru_M30Fzapmb=aySpRTkZdS^NN1S7945^E%)H;P zPrzf}u6Gm@&9-;#D*)enab!pCHu4zBQn@}gOiUvE@M{<0;rt$nJA6x9I6IZ@s?5CJ z>D&6oW!58AyH$J}vToo)gWe3D7ql=1ChgetQ0y-RA>YBhbZCOb+l(1U zJQiFCO7GhokEd_P*E<&!?XN=#lIg^(zUh+#i^)kd*~2hkkyQt)3V+3!dSQAYrU&5b zp!m2m3;w{(ls20;F3l&7*_9~#7@A<46q*jTIqxmCt{J+iJ+8olcb6KG8C$wL0`=YA zMeD4GJD0#oxBw}syKUnFDZTBCD=$85@dO_Z-ghKyeV8(}D@p0yydHewN>e7P;9+kR zpe{iZHF5H`g?R?lOmt<5*iJbOW3&t~tW5fhK~k*&32fSVns6C2Jm9M&g7mgM(@YW( zlckMF{cdD0_Z{rM`9%#rlH?^1g&5*x=9tpi)tQMl`bX+p&YJNs3_`wC3B)bFdyRSE zT6zd&iC4=D8q4XQWOIBtPw~vuXQ0%#TL|Iq3W>}1<(;8TR^)mZjz7flHx0h0<)zy=yy%6xK(0 zBSWk?BmT$7#Ekp*XTsIy_wLP0D{2*F0;pwP_UKZ)CqN^@yZ0N@wnykbV|F`B_1~H; zmif5e&YI}-J@~zDPRkSo^NZIz+X^Doqu0~s^Ev=b#=GQhh`Zvu$3)iggDVo{2ZSR z|1rvR$D;4`eZD0--Zoz%X9(g5LPqAKP%67a^Ltd|qY2GIl98~RfG~})ETf@SsG^F z`QJ25g{`T>F^eN2*ryEZT(f_x?DtqqJGJV6Z)=cq{zmS zv81fBPeXHKgyxX-`sLc*nG?z?Mpp&*Y=?s8$}TGK>SXU%IgDKI<%70cJY`qZE3Y1I zoQ;(M>S|2wDquwTPu2wlqTyd_{0as!A+RJg8aF`a{>&=LOtfh__Zj+c7=vFY)vnu!kItd4D(qhs z8-eL!RYHjsU*z4pCoUmsg{gKkif_}lF)+EemNDbB_&d>5GT*Mv1>Q6m1!p8nP>WzQ z!TosL$2I<<-&yPG+{d;KZx$vHPm+l{so6mghBz`MAKE01}ZRPNE^;`ihYlT!j+-|)%N z^<$NRx9Hc#I~R4#`qpugFlb`0Spf>SE;f~SeNxs*ZBlvzGp4Y_y9<;LNT2Zil1?_a z1`lY9f?b(IGtLRMMkMiJlc3c?&tRlg+x1iMGcb%m^ZPzFO>uhpODpwOryuJVd6X-7 zXkZGy#ybv666rU-i4OMe_KViVESs4< zq?drIrIXdugPCn>U@9t5ww4dajZ{>`3!0jGo3dx}_F^CORedv+^h~gjR~z?+HOEtJ z6qL&ZFnb$RTTr8>y~j;28@M67Dc7WWX*gsq3es1{`25}uf6nqk+-{_^rI%Aa{>xH7 z0xqhZ%$?Wv&_V8$BH zwek#Quicd63Q;t+hKros>Ok`;lq|eXU&uJD#k@IFfwC2DW<+hLY#8Lm*ElQi!gZY_ zE2vS@l!TLhGZW%=>jnEcs}dzE*nYD4KXb{H@s;%kn-_`U9?#QLiua4>` zUdoc94^n#o8ka@P?&}XjaRohN|e)V10B_}3+N^1JOX@#%`3@L86 z-Ec1&knLug(OnHzS6>hbN3Y={hJG8{MDlyov0#~WF0es zXuV?xoEL)Fk16Pc(Vzkbp(Q*ogQ9qf04?Qe135qH!Q>)z z)Rjubwwtii<3Tawmc2p~b7B;dG8_-=?v~!Yl=RN&LsGaNL$z~6i{S~imywmpr*4(T z^t+T_k8e>t)78$4Ywsg!1b}cx0E~VLJP9eOF?BGgR^-z!`3BEaXZ5E$G+=!~|UKpIt}845^8Ph2f*=u~;};IOq^uZoD7HO5#hBtVYcHt~XS zI@(-t%*>+dE3Dg7b9sA#B%{XK*P^ScA-3x&L?akutCN?q5-E-RvAC2+Z8=h9RLpEf zBU6E&`vZ~^+mc0^4#XIkDJ1*r#kUM!*U!26rU8dgMU5nnbIKyzTIiyzN1L1?!;s*{ z*d>n!wLqU)nz>0wCx_RD{n!gEHS*e_lA^z-(H!$tDcXkl?K5I52fpcEokWL6(ZjdI z%EflNqt@>*It+f{AXHkxvtFVnQD#t-Nb3bI*e=`-MZb91k$H)0;>R~%aLMHN(%UNY z9U_VH;#5Z|=-8N4Pc|w(y~Fssf;8Wngu5yY3)f|*YA(p3DW_9$L&$T)X=P?}%c2tcWBe0Ls!Sg= zAQ(~62&LCGtO2xEXl|u5)?PFzV%``}H_oBQfBVXWO8slrfg#B|G0YUoo~?o23cL-^ zs}Z)YNxiOs#DNYkt)9M9a)4_?;UtN}hgAv$K~MUDL@}EEZ0`Z9+7fb%Zx%+jJ3+U8 zw66CZ&qMzEL-+i=!bwQTObz1 z?gGDdM58sZ=>)jERa~YEYv&E~a6E0_`*&<_#LQbv6_&uEUw3LHzt-;IzLKWb2EDIF z8PLz&rWdcWp3w-WBsRq!q%0os1QJOX?aJ{RCf?5UOxrgK+JSSiM7els0u z5}CG4ir}&|6$)s(7tXRE)^!C1$MnfWt>S<@t<>`_Px9?Pr*RKB5j`FiIRhIVG5yqD z`6?`~9gMTq5vjM%4wGl`DhJLh`H6i8k@h76OxI!O+v2}{?iE~xO&$um>Zpo#*gvXS z6?}g!_=1vl4we0DC5m>E(5vrXN=r>wH_00?NniTz4XAE2?cpO6+vI&;VWMbDdR+Ml zN`KIXiY=`eBB?_+b#viY!plBTzzB?}34MzWW$}gTmn6-Ai`Grp`kLRpyk`BajZTkG z%y%#9&hoUiayio}?Y)VB%Y!R3l3LzA1b7+~4mAsL_hh2Ix)^y5qw!FpM(uIgii4W4 zy~g1&;|L|c)kRKF!dl2qoEA_@k&@3ij%~;a#L0dxK$~DsH-6$wuhtpUSY9u8~{H zo|yVj6J;~hVytUJ`{cd4DevdLu<$IdEWZ9p-t2ksRAqQS+M@h=nUuDS7lo;Q&-~Ot zeSt}2KsX=PFAxJ?t)!q{iixyZ)8+hQWeCS)05vOrX?4WQN()ITj&O`Yg%vft2;-6K zD$Z|=x9$`XpJi^(No$LyP6a506H&L{1oh@H2&myd;S~%N7>2c+h?55&ozux<-M<`J znl|$F!i3k1L1L3tD&T9K6sH_$!MeK|se+C6w>=)e@~3JskzLdYOw4cpV8&%yHd!8- zOFu8vf^G}lpL!H8)5Osxmn2rN#?<}Pt-LI;>|_Cw9ut1f z{FW?bm9kQY*rFlst(rbarUsT}@JP)Ly5?Ueg zhlzZsmRJ^Lnl}AnlUFMNEj&E0mD@FYjjucXonDXJX~H2HKF=3+8D+XE9Uqjf2o71E zeoZ)T>=eUxW!iGnKu`D(e2yNUAuciFuhcm_ihCu|s2fpQj@`wGio&5{Q}IF9TlMAW zPy5!*YG_9P@XLT0|5cQL%QdL=A}RGm+2O;?2z*+f=bWBBbY!6S}MWT_A#bvC=bcEGgiESYOy z$5AKO3*>E;{`d6p*y=G9Z5U_8yRMy4Z))cn(nD`UhYs3e#s~BoxN8#O41j2SSeH-D z6)h_$hH6q1-#iqU5psy6(%hbkrre*b*&d%TiZ!RV9|=Uo(Y3!^uF(JhP*HZ0l4|mj zl7BlDgq#It2c-zhb&C;3uQu9CGov3;7A6;f^fB?=rkHRGB+Bs&ynxLY7;m|07}*lT z9ln0e>#dA<^|1!MCYKd`5as8+*l&93QR++2(1f*Bz!?7tUBRI0LQ4%CWPQx#w6@;WowY3t?9*8pW zz8hvwyJzqm51JBwvtu4ID&vWS#jl6Og0|FBmmtD7|Kw1dB{%N3)GfMuBkex8#T1re z0fkqdOe%Uqaeq|(CNJAHO~Nhzyth$QCUlK$tbG3Jy55OTCA5>vwWQ%b0|Y#m_pdII zP6~Z=TH9zT84T$Bfet(=R*c?~gEDvwB=;39a`^_mZkTT~a%gx?T81I^)<2^S3LQOc z9NMZjpslsF-Q{V9`|cWsDcb#I<~lp%+!iC>4suMZr>q3BaB*S-Te_HAvH3c=LXL3( z03lIdSFnYHl_#aSm5rUVFx7Eu2Nk8Ar7)E)k1|l%Rnp4VPR`%mO4DCO%fjEmLco$r zR0LVb7X$%tvhoB|`Z_r}dw_g}sea>vAm5*d*{LXhi+DN+Q|T$IQA)bFTTybeakBwg zrG4$ZIjKaDDTUlEtw9=6GJirqu7s&>L6D0_;Fec1})K zhy<&LpR*^}m(|&W`WfO63@IxQ3wJwLPdgW9%4bZlxr>*lFclSKp7Kv{$gG95FJ$mf z1JBpL;XOPp+2tV@+>rG^1lT!&Kz>#rCo87_`(O1Tv&zbU*LL>!lSK%h?7m=Eb`CZm zyOYyDX?S=_d;i1Uf2rZ21vxEe*Rb+%@p8AYlJ>T8_N4x+QCCMVkH6aV^00ay`t7%) zr8PUmso$3Wsv|3}toC=EXButnoLqlvJWKyo($eDZa;{$Pj=yCrE!eFbt(+i^ctDUj z{z=}`&iWq#`lt0g&-^DxAm;v#|4-6?HxLCO%uCba6K)m6z=HRyg19=6wEqH!IJ;wngt|l)`#mNTz$B3FE z*wfm@-AR~A8EiqRs`-x@EjuSGO;7MMYaG0grE&nd1UNW2c=$O4{vo7ea?a4a|>2$3osv_1*a7sA2;})(LG$OJ$=CLR^m30 z9Du|bQd_^{Ov&&^`Z4~qxR0&Xb8Z1SIaqjIvvU7uvi_Dl{};Kx=>M6h|C9P(#s1Ki zbaC~ARI;t7s*m%3YyQ6w`~yMB&ce#s!{xto{jVZ_(DIkf2;%b}V~|Y_vS+gYebf9i zRi3NqfAR0n6#QSb0D=Cuk$;Kbf7A7Ey8a~w{w3jmXV<^!`j;5^mxTYFUH{+cLjK2N zx0N&G=FbQ6P;KWvB@KBXhc{Q0l>$6H|L3=tr9noJT;=pV000!6=NA;9Jr0d6kUM}DIB z9D%HPZ!Z0E+b@p7Za4IIhKG;eaM^s%7C6VCqLN~;y~rEDlmf&8Fbfa?SO6+qC`xFt zNN80Y$!0f|GMtu{mi*RMk}pAb@P~(oX1wpd+Bbv`T86WmegOc)jEqQo`ue&q_9kD@ z(ZTnaa}4JQ5=2V87=FWrP*_+f(U|lUIU(cd>WXP#XeeGg+dY)YzTOpuV%D^%Kh21V zNGU2xVaZ9vV$w!PgN>w=O(?NS2LC2dgyO9d9SbX~bd*FGpwmHKMWqj-XrZZz^DFR+ znGZz-eCX^!FQ3cG+G|!rUy6%*1rrl;`v!vx|Mn+QfN#4=RZUG_e0+R9+Wl^7N=g_H z505w$RX%{5hi83z}I^Ayd1Gw=U^2o^s8Ct zr%#{ii0;=5)fi6BcShGyI@i`HUuR^Ta`G#bV0(ec{DsIbe1*zq58V0_$=U{(*xCEP ze*Jm^-MQzvvECiyM-rGhboovxb0}(le!gu$$bD;o!YY~u!21r(=WwC-o&DIJr^PpC zTiZ2dXK`_;1;@rQ`25;hj0^?Z+Lrmv(|E?!&C?Sm!wM~PQ&Ur0Xb5KQj@)jFT_So91H727#Bp+kp)Di$QB%VKg~PpnVKd+ zeuxRKpstyLUF%vnwfWladrH-^e6+exL`aygVd@AqFgW-%EG$ekSgK4Vr|>97p=6^6 zWR3=?u7Jczty+g`=5oCoVFl{u=9Id~qVB^7Y&dABhB#s^fG3DdTe3uhG5FU-oI=Ja zTIl2+&5Sz4q(nh-gLWS?0RI)7riMm2ylL&M=JcL-s~;7elp~>T;6{MZ?0$ZJ_V=Fp+tV zVWT6_tY7#lKucE_ha@d8PxT?g%hPi$8jS?{3znj)s%+7?+_X&QkhGV3*|_ZuT<5_h zHlGr77(fsND)*|=VnSO`(W6QMzAd4yXlTf=kJve0>0}42wE0Oo_CP$V*tfT}6-$xB z_0)}&qbcM}!NRIFYL=){eLsB$6FRy>ts)sEA#P>U^y!nCgoMPvO}G>lR%1hh`faOR z3W=Ye-#x&Ln>dcFqO!8kz;!-5`SeG;VMnm=beEC^UeN7XQ~GM|x&*@6-kc-p! zT~1(tw0SS%V2+%WF~`f!wq>GPo|Z3MeiuE$m)5OCMic9GdToM-DwZb1!swEtQdx z0rsHOR96>I|7bHxK0^$JmBJ+S@M9=5{(kzsF4vhbIR>eqH;bc0pK6{6nrO(Q<^9N8 z9tXl(;)LjTYeq5M;+}FTQ=_du$6ZHqsh{w(My&gu2#}++=mgy0J-xg*qGN4rY`P(x zgXqBb*M;4jaklT{y#Tc-18x^2I(=J^pTi_|bqVfBIG2}~;{%(Gz7P2Dle(U54c5V| z{ha@}=H5&g^3l8(S;s;auCvKymGD-hB0a^0^G!K1w*A!nJQAdVRsB*S7Gwg%%TT*9 zagLwx6uK5CCZfv(LmJt^myfwnLPAU&994GU30i7&BH3|IO-+pz z;VgYpRZ^lkg3()@y|@hog1aI)ZiR(~v)L1AX=&9oG`jAng!uUfT2Tv;4eHFGumE%h zEuMCB2I43LY@|)%001Th#V9!-_027PkWlLOXdXo)077oe?xs1Hw-#VHiz6aM6bS4b z$P*6mo`bHl9jDTvpcqstfi{D5!}!4W)87UNUiE@e02PPDmkHbvRDMtIn9u&~hLb7)`WXsDpY7Ruc?VdPNcm6g%l%G!eNuIgYY+1Zn_ zwzqp<5mXU?H)MwQJh3q>S@2OT$cPi@ezMum&dhLL?cH4;hY2Ldy?hy7ZP@g|-GEj9 z18izqTKD!r=FpGitgL|>k{p%(fzgqX5(QjoFS@qCTXz|ey$c2=CgiK*RSI4JEv`8RnxLM~Z5$(2JQO zo%fRDfVY_Ep$OLFk#yFUmY7lKXlUzUFbERfC)!MS$(<>i!ax*W*_Wa|p9Wq3g~Gc|=NsH3MRx19X?HNFyr@0Z`p)CId&Al-j> z&2)k!7moW4#=~II*y?H$0tPv1dBf!Ds_OC=#Y{H!ACzbVJlby)=3NV>tGi#d$rP>g z1k^|rh$YBu>h>)`x{Btsa_i{^i?+DBI##`UTwI)1=BkY9^>Z(2ad>7vV5llW^xbyzUx0ZW{?%m#u?C1mpwwZX|#9 z-~|Xo47tC61U6iY39eob6_R=zo11Yd4i@SVqN+?g$U%aFL?QH;h+^-ju}2g21vlr6 zOo{Dse|@BbeKlpza~a?2v4ig5mtD*v=)Q&6V9&tJyvc*L?>h+Dw{OpPD(>7acE`u$ z^rLTFIA4W_L+|$;FSinmSbGehei^sDU|!xnT5gr3w_BAHjT6n0lauR&P#f!_0^TRogL$Nk-+N(q}!=p>RH$f1s-RF`8XWVm+aGvO;c?ybfkgMRpk(8TB+YHm(KWOmW?l z{2H5@BEFgd5@cW?EL!E*NHE|^I6Jekn0_I}XE#ALujel&KSyN*$fOPZ_?5Tgm>@$9 z*+O=Yk7H_0J02BEu_!1o&bLP*c6V)K)x;G>#6RwXJ+-@J-@p5sy&@L&m3n z0OzhLF9oWdp*Z??6K<`iXrrnMV*O{Mf{@_wK?wg*{#_tAG7!@L*dUM+9QpsXHQ|{5 zmje$1McRW9|CeL%5B2`dvilbl@t=eLMT7#l|L3v*{{PaQ3K0IE{ckErX;Yo>AE3D? z=)3=0{mOqGPKr6(=bumglqpK`6iTXh=m89|Dc$P0g>ikOF^dL z6!`DO|2D9Py^W2?dpCP)Cl7NEduJyI518x!_w0Z8DO_)f02&%Fmz$N1h^CAj1Rc!f zBjV%e;%Mz*4nbELVA2PHxZD+GBtQ7Todu$OF&_3k`1SOs{^OG~1`6TE_+K(-Y< z9Cl1Urdrq5Y6_1#I7N}d*2RTlHj}R*p3i+IQDSP3BzBacHH>(Bm4uu-CQKqj6&@~m z4qBq)U|+ceRDI9(1Fi#sCE!>0UH5s=Mso0!hUZGv*WQ=Ms^iyl({Ep$x}RibIO%f!;Nef?lY4o3CrXV~6`GIG3kpJemb!nqe0Mp;7Eyi@;vC@|To?8Wr$$j2xcfzR`7bZXu1nv_E5 z`r#Z_7fG$$exU>TIUtbxO@^8F{G;T(z_UIaJ(r-MzRrh=ysM_SE8iN3w*AjHPsYbJ z?Y5lmvqy?QAH#SnBk~Om7_Otytu!QSzwZq&xFz$ViLvTDYvKF9l~;r7nX!uJ$Ba28 zCjrvJ0ukIQ%HJQ0c9xO`<(#8k0)#=cLX)}g{@kGp%^+Wdyjlr}jgOBf$!cp^oqb1t z(6jGhpN;qaj@FE$xV*Wc(N~432ZqOnhbGCtqm8nA~L&}KRTX{?% zK1Xz0STs;K*YjfIYcnEYAXd;{Jwl?r{c#}rNZU=-TFQxYB9)ZlYR(|EqToL5FfC0{ zP4V*{5s{Lb23kPU9uXmZ`0Q-Dm1#ca-5u>|s?~-4(3A1%BK>&Ci7O8e1E;AqWaBlmBN+77d8&^-o#3PndussYwR9fc6!GM-Dod%IJzHcPHzXR8&ndey_lUt)Sb!pz;s9H>-A&|X ztwJDIRk4kH`d=ao>?l9gWQT_F*FU$DyyU2w$Q|2MQ@&Apfhhg8C`n%AW_w=zLFz^> zFM^}zL`OTU%o!fRk(^^e!rI?R^f3wtD%TXnu7C<}mPiqQQ*`%p(om8zb`3{(rct8G-)RdyOKVK$puS@x~6Trbxy8<8)L_$ZL7uRv+A zE>+BDx<7H6IJvN?p#Man08TWc0N4;?Y{2Nzbo+|so?n@bb1c1A z--PWO!CQnmbij5=VTYc2IIDUNPL^9b_*R;p>z((w;h7$Lm6y>qtl!(gvGlvnCE1ml zhWvVrx^P-Li-%B2H8;aAxG1T-GB|Om0$DrIcu3Zl!Ze$s)tgv`&V~wlFmbOX zvWg0>T0_W4>;S-aqjNoR{(YTbW#=fw65~x?9#2?&fp3> zzgZlu76HGYo8JSf<&Y} zMXOkx2NJ|iODG}E-1qPIHrQ98xdwkN{!X38_Y@Hvx8uoOCDoO+Uk*DTOUeP7~G4$5^pc4z}$`?Sf?={;)_YgNQeTK`oAp@wNC`p z+MSD2%_kcA?QYLr-yq_N9;&Wgf};Ay1C&0^ z3)wZ-Pi|P!;r9-~bwp-fKf~3w>f_FvEM6V2N zKQLGghJLIJ)u;>w8El^lC%5geF4Z8!{*{l!;%B7*_4*WP{cv$6pT_HwjMc|rbL9`YV@ zeE4!1y;QjPB{uPK^bK+4;9PRt&$S=)#$g#F_WEfF{>*skC4OUSv+~x}zrY$>I$0BN z;vog!1pIqYRVUm02xa&CZ_}C6c}+^%%m95ZPsv<$K~f4&1>^Xi4fea9>;8MG_^$1C zd&ZN86ARA-cFE~%pWmI-V+@?(l{0HYM>HsOY$nVf6eHHKK+@cuE99btB-)TjOF!*A z9R0{TzJkbmiVC$WksK5n+5xZm;s8)u3#q4Fb_?Vt1gx`s?b2CmcLf81kHb660ct*> z<75%a^hq@>jifs|U)e7Vuzl<6y{5On!&y;u$p;n)t4drpizGr$(^<4?Bt(x1racyq zB<+tCJ2OYC7CiXwb(TJu6(boG^HxguX@BzbL6^S7Q*km-kMAKT60Izec+h@`ZVI}8 zT;=3XSN&sYITzG;m5@2r^j?0>1L(ie=XE)a!n}j=)Q-WgU%SnMzFe%{wL^!OGqoB{+>F2V!2|zmwsNT*IxR2uRBrL_GW;6P&@@QYwBMnQ5J?% zPU#)tduj6>yQ?dZy!(i;;4$aWZpOtla|+nx!A=i*fFgW&Qpd~|uqnXQviqnPLg5v) zLN0ytt%$+&SoE=^DIX&YXfSra3l4CZVHn>>k`A;6oh?Z-?T?Fb@Q&%LPPx`y`Pk-v zaz0JYRW<$?ky<}MH>20DjPVB#OVKjiD!m_M&-ifkk}9Xjp2nX#k@_l{dGb+!NUT>iDl;*wis4yO3h&B!C?L-xQHJW~ z8VeYk#Ux7K;AA#qY_xqc5fNbDD~(DrX{J+1y*1=9Ml%Inp6&W3PHuk4G=P`a^6leB zpN1HtsV6nm1e~yejO9_%iwr^OGI&32k~%+DZ1edXgFtr6r|az$UX{0+t;^fibQ>U=krU+li~9iM?B}g%%Y{Du+Vm4nPK9ev9(9S# z1mH~c#GABVLGwc=syxda^`(a37wKFLZ_;(Yxr!4W<5nWzj)s8)N7)eEqWN<8!6;Qk zrehp6Qo+Dz4MkU(3i{cuT$)nma3HNB7o7lp%k{#KNxAAItn>pTy452?6K**LtKH9e zY~de}pv%?Syb#(oxRVnZA@W9Hw13%3ER^W>FU)shP^j%)e3Dh$MKfm0yUw?QMs)<&hjBAEVVjIxFD-O6MBb{{3P4Xf;aUk9z3DM6qIXVTAMMy(@Q7r~mlxAj>G|ZgS@%@Gd)`>DAV+y7M>3n;qMtqDUPubk$r2}2I z#_3$uGWgAW9%32nj;ou@ajVIT@2F%O^VF&J@|8RLV5t$aWZgjuD?+>#(#O1E%8R}^ zw(P*xM~&dwMEFtiJ0Kg`WmTP(0?Q>18DrsLpRjhngx*m6OQh?4K^Vl;nkMEZ(Nwq- zz7uOLevhv1)Y%<91T*DbX_tE1ET?>_?YWTeT0O$nRgXNbMWFdFx;u}96f_7ITyFT2 zqKI`t&39{Pgs`WHs6o{z{CU;YfZ+1-m?rRPcP=_7v`^Cd$|MqbU#B1`>+tsv+Fxj` zx}v*!i=qe}6Y!>xbNBQm8)r}X1|3m&YUKQ@D$b!E77&;`{%#RVRBh}E)u;CTvLNwe zkR32*{mJ`C`28`eBl<~vM~NC=8~;Y6uUuz|Z{-W`)YPE^&UYO};X0~ri}ftkbP%8- zy(a5Uv~{}6?|s{l^cxKhd&DarTcj`VafX>IISV=PM+u$p?i|ZDk*+{3M$AzRTx(}S zk=ht@znl5eGs`M*-w!kgnHjR#p#~0rZr7}^w+g~mP3_04+A%(wi6LVWyXK_l{Vwsf z%=ym9Oss4Th(G2QKDXGEI2wI<6$=16QQ_gaSoSxiCh@UfkMEB|%OUqiutD_|6=A1j;H;476Ah2aO{UCil@9L}<`} zBa4KE1Y93>SlbIaJ}fdd_{{zZjnbWVdop-G8BMx>-lv5QA+{8^;Qm9sUd04d+?r$8 zNuNG5lMf&+sJ^vsEPi)3_RGtqN7dd{kjy0^Oqf?E(A}x!!?i0GXriA`1%%+BMaIOG zQeuW|(=4JQR6s%Fl!405h511U`yd!)r`(*i=hjR#4?3EiCp^9V>A^?^nR)T)GJ^N5 z2D5VgsPDs$Qsv0vjiqDvLL?Uu61(EcRA%df`pcgAT9y!bXiYtaZ*us_%V~Jg^%ND3 zxkaQ=KVKpnn&N~*?Q9uR-%0SF3MUQ?u2@&_i%gYb z-Ux)^rxNHp{S5fUze5gJJa`vv*1W+VlwkiDr9#5b#TDXMPq>?5ehvZFY0v5zn|xZV zOstM6Gbn6f4dTAEdoSta#1(JVtp1F9ZsK9~{%|_WK4E(z{r&j{F2LJ-^_$`oYSACA zQl_A!2$6VU3kmjxbp@h3QkPBqgPyBUQa4qWNJU$Fkq<>~oE>pC%ewqPrT}}}&YGS9 zQ@`A^Gs9k@=XFaY#A)GhYorHX0n#K{TZ>wxf?G9PO_wc%HKBI$BSlFWfX>nvI)Pm^7Hn zRQh@eVed++e7Nt6m|)C6O70Q=rr=eYpSX!oE4brGHokKn%eM(cWoC9pCl;ei7EDOo z(w>l??_#aiU3Iw`VHgXakBK*w>l?H&q6<@Dihv|u34@cxjdZPjli0gyp6vg&BAkKD zxJg)rZ$75nN(H-gM}eq$n{meSA*C`ef8SkOG3E)xj<)RJ%^wuE2{yos{Iac?4IJZ^ zqt(UXtz?_aJvRc@AC)O>UF`J|BQ18~$)c;*nNkTRKe7|GgwCZ_4JHZ@Q2$U^IWJ66 zRb#Ygwwe%L5{)W!9g;3KusP{ZX*?l;{?tQRVXyPzZMvrznsS{yCjul3gW;fwj;_F%JtSkDaAqb&_VQ=`g5#K&MzZ75i><7zHiEwwjcF|WI-5Zh! zjMupZQZBN^SkpqAK5aPo8+m>exmJf0Gx-Nw2e{;<;#{ zG2GvqDI*BzJlH|`^YF7KaI^`1H}gi&H-L}<&{XsuPb(*{q-a$7@^G@ zsB3a32>pZc(>1zNBOKXrV>7N=M)?m7sQdQ6c(?#zok&9zn;M$-m@vFi%TE&*F%`9o zRVsrFZ(P1bOuEhoa^N&~WQ8f|XIAMM?vahev+}U~9sMJmw|s@PpAmaKN9a}RbhMK1$UvASwykf!d=w@AjL^i>-$jUN zF%yS__(@@FZss{NBiv_FdlJK&XyWDa?V6#nDAAbcoh4(8+f4CVtQMnx9ql6I{7ArR zX4E(9D|^k%ndJ@yn>6srWYIbpn6ReShS0l6f)U9+SCezjwU>n^pbEHd5g~qpG={%# zsiDKlb&8~-V%k_vQx>Mr@1Yq@~=4=-m*JhUX$uf*TJ}6 ze~ckHVfCes0ZEt3Ph(?)%s(;I#6_xW9mq~r`9d^ds*%mzYQ&YTqqk*lfPQ6Na^W$! zd*HvIY2goRg{5E|1^XZL`7u6;RuLX~!z}%!<6kI_(@n*$!ZGE&ds-CA5|ryxF03>P z4$Z7hAkq*!V=?S1hfn88fj$#`f9yJ!b~0|Hq#G&29C~l}i(Emv-qm>DP(OLx3mBR< zn|v>xiHx_ASyoi_&vgLG4!tE2vxQtR2CLC1+*tPAQQUP<2J6jYd}EypEi{1cAL|wIdfIUG}{vkcr(443)Ik2 zD)E=6v5p$nE90>rUIu&nywW;nkzm~v9CZWT45sSJrC#=czMHBf_btZ`-U$3gHSqQ0;dsHo}d=VeLUNK-; zsJiyWF8#)RM}n47x7ef}rk1$Fb~g8ET~@q;ncYH=UP1@aYmJ&A>jgJQS}{d=fDQW2Lz~)OA-rHZ@8CND9bWa-u29@q*HR^A9*3v8;8(K*> z1{_7b#%8H}Wgf5G$lW8k2V~Y!8#iR}w=wbG9(5#`S&B8lCpmuBq-cpTMH{IwKV?OO zNntq-o&&z?xnNJFp4U0)Mr$@I&H?Wh8W&r9Yuxhf@7;vS-S=XtUKiK^l1K#?rwhYI5{~cJC%a5kfrbX|310zC zZR>gL>?w#*VedwphR^Wky?-xLlf%bL2ETyW0f4!R0#^5M`N`L2(fmy=kCCtWpP#y! zh?raUf|Oj%E1N#jCO%iP=G46H=!Q==6JX_LA(yU)twxX9NauL1nEJ`@ZUI7?CKFL5 z9UgoB2&Me*1SfF-*c*M{tKmkQt8&SH$)EMb)hbIV=hw>tc2G>SovU?a`N6kg_eK=+ zPe7t$s{YUFfIh-#lJ`+VyIArGlquywPlm$td(?&zu)%pZusNy`vVImZnLWHo49$B_ zXM18fMr7A5gZ>nFCw>f)2Fdv!U*+rDn2FUe2Vr^7w*>)KGt)k61~q#`4>?MgHe8+ifMw{Yj}Z;4U%%-lx}dsXZV zcqS7A0_2vNUl-toLyFxk+gJXyv;&PGtF~|2$v(pi>Z4A1-cm_6Eg?>x6r> z&>F|YvlD&cHe3^xnX+umj<69cc9-4)%Weo}Z{5f@dFa~fw_i~`z{pz@DbZGl>)I@B zk5C`Y@MU$SKw`&(4aBagJT*Qjg9>3HHpga?x`!! z4lh4`KVwh);Non><6KXs{C&UCgk)=<7QVSI4B*Xpf|9Vq0;-*CirwfE6vPhO|dS zntX)jc&vMJ|D2&*BS3v&cz&UIqWC8-7>RN?ya+o;eCO6D<8v`V_p}f7HA^sb0}Ek# zGJ4EL?4}26FbcTH!U#cG-aV|s&xkTT|Xu}9RmLg}z)z$+(Os-0UQZKT~bof!Fj zjg4MNb74GD3Dp)dKm3`mc3!$-m!VhU6 ztiKul;5wE`W;AOkbBxbjbvAeArx#t;h!D;9{I8gaR7llB{_i_#JG~uE@ z#`{=YTX~&9&p5>B;<}a5KszQIzPMm}MJDbsO&i%)TkruPBNFH|h#Q4shCA^5u@yCH zeCY(J*68JZ?x9=i*k8^i!d!4F;QI^t5DWCFuD|=6Ac@rTrrUN=D1~8!21;allZotc zy&Q1}yk#5h2@KlDWQzRzPXUW^4ObK7thIlia2cH0F;h!VUg z7;;RW0{>69UnaB1f;vguQueugzIuIh#Y_h_gl?ZJJ5AEOx^(EUz|2I>7VbpyY<$X^ zeq*pMB$42L=YWz(t0Bpg;lRIt`~{U~g?i*KBea_?rM}Num_D7~mWmV}Yd#iGfCzQt`yJfDBgXU7 z_&(~A+RtLl+fZ|bN1i=OamX4z9`jD7Ezt*GT8Ep0Ou>Nmqf8i~Oc|+sj~CFlke7h< z%p1JB;xf9MHw-g7TM?Yd&S54YX%>N(onv=0!_BF;C7r(RnzGdAh!miI1@+ju4GQL^ zPhVJd?Z|&}B885Mw|lheLY^)>`g$7GkT8~5g&^;52ZdhVnXV;&-NhsD~@bKWU_Q(gLEN_=n${Nj8gcC()k61po1O?Ws5Y zDt6HAT{#!UvLH{j0XGsIZ5-u_avB`|OHm3F|8-PH{$=3;?bbl*?*r#Y0N? zW605jcsH>VrX>#ByuCPj6g8)J{iwL%Wt&e|egCa2vLHw#SS%6atJ0;i#)fA4zISI0 z7TBc&@8^5*rwu%o;OhPR8KWL22T7iLZ(S})=^Pr?L>e|T8dfaYFT;o42|WQa5brxS z*iClan@7mKBMIF_8vrjY{-ySgH?+vr*%6)Y_|C(7`yO1{5XT;qFEtpmMm5A0IkZk& z^2#W;xTny^lcEW!Gq zC`PBIe@LD9AJoV_9LC))K%K+gx(?Y2@4K-#T(E=b^st#(0b3I3-AU2xeZ$@#Y^E>g zI%~lg1z~UU(b|T!l=t(Uy5}f=uZU29I{GOU$ula3`smtX{UK@Xz3w!N>38;y_HW(@ zq#sj__rJVtTp6Udd|%h+J?hi3)+1#UECdz*tHhHB9O*&>$5w3BvmdhzKQ2w!fr&31 z!5rUSe6pW3fh~oavE&(DLX-G%^k)MXB&ws!XC+}qOvDFJ&sQT9j9&>WqCA0RwIqN2 zVB*!u!}mlE_22ts3-3%r;T?1)7oV6xu)=IGp2B7rS{|D_#og-3H|mMzpehN&MW|vc zz`vF8>Uht^V{u;g(6oP1RZspNcc2f^73WK-mtrpfJ|yCgjt!W!GMWAJCpla|~kh#L3l#Mu6`>@B7U*ifq== z+9u)vfyYGzcE6>c^U4ARMV=J73)ylN@vRObJmifv%Sny0&IiH88@prP_%E9G!zy!l z*^gcD?Sv>Y_O<=p@pppz5>Zhjz8CZfn;^=%VtQ=hi6|GU9`|m7+0&O>2}2hj9og;# z$UOltXv>*4X{#-yOb=>i$4ORmB4n8R8!-;h&U5N@_u_Hy_RCH-dhZDDCutd9NSi4nQ%1tLH!8s$Yh_VK$Gys>{BA=y!IqAP;%@w8UKniIvR1sxW6mqa^A#e!T_ zX&kHcpW?fdLgIY5C|7cO?@yKV2Y!igt7G!x1Y-jrIty2b;})si$aQB3Mf+{r{! zpM2eRH-}sIGkV6e$U+mVn6Ihz+&#dWhmCeA`=}a@Bsx?YUS1Bv(skOMLSxbr?P!EbCc7$X}~Lg~44asqh4nRF3l zADmc#yBM~9#GQy?!SPC~*$n`+I{}A8y5B{Pt{%!=Jzq(<9s5uCXm z|DAGI_w(QKxv+Am3xw>HgqHVAWl3PbraSmC;bcwLQI>w#i5*3tSn?!%W|SL<7bkJ8>(L|M0i`*8Hjg`4c9IAQ_7P|G0Vx5#_8rK~MM~0} zpmXAct$uoQ@i5a6nH$UI2?Z(f+^}&(c0V~qsHG8tc!{Xl~Il!qBN`Y)Wzg?_6@PFTuNfH&fVU=>8Uk;h%`nSoNm0Yh;g(; zk9-3cFU|OEfXDLC@pb!FTAEnb!NQxo!k}5UxaNgoK3%y^k4o*wKogf4#}m>CZ@BRk_sx>&qmR6^8iz#$4@@ zeHX`X2rqown153-+4G!h*wZc4-ds_$zZn~A=E9w#lLXv9j8b1R=laIiKARlP7(DNi z!{)1Cc=L(&7aX%}zpg}rI&CvOS8^+Ros8aV~+x*4@4_YQqO1u@^#AB#>*Ugb5`qaasrlZ}x>N&87z? zY?jS~`XbNXc=FTn9a1~XH>!CQ&GD<@8APGw8~A7VdqS0rUFT2;l#h-&%ZPR{Br0^k ze46j~CGuM~q(_%u$`Xuq{|c@!Q~LrI2nfR5>~ZDf-YBMROljKS?PuuLN;l!5q*^(!Q#P!}`1;NQmt z8v&DdI)lv!sJ|hro3^FUfieK<#l-^yDg0U^nYp|2vGO6+UP{?Ur|Y zp)xeUe^Ty#i6uGaii-FZ&f6G$?(|dIXFKkg66<3`r2Q}|=Vny?uZPM!&- z%K~M}0I5)#q>)68aL#zd@8m;pX;&ob6aX6gOc$X1PfX7pLFwP!jdF;?#jeW})jA*U ze2w9e_UHrA%y6P%OCzx%*dZeNM&pRfAxukH4YSK1a$mXT*Y`mU%Lw^{afhUXNqzQc zE1CrmV(sruF4yP3)6Y_hoF6N_mXvNF=Fej;uF03@#1$@l?8DA^M$D78*~^oM0)Ak3 zKO%Yh6`e1?F`$mWt@2ojqfwy>%yd!G@=4z!{f7^N2vYY0Orkk~zPF4*_uYVbX|#8dI3z?xsckaGu> zk3#M0n1TO_FFpqe{*~5=IO$CSn1T|?P?hl{*sKeDI#|R&n%7C=0a_3#6xOIbA~>cY zTiCj~EiVlxXmNhvf%CjNR($C}!dtWFMJkD-ATUW_3WM0qeodHotA{M$wuX3_=P#8L+yz1s(aWN#K%cz zJT@8Gy26S*ZjvoV2{nylf6_waT)>HNcI*_$nu|Sb^tVyL^DI)x1t`%TNW+I!j^TaR z!SgnGBg;$Q;`t~0Z>YV10s{B-kq(lebx}G-vuc+Tr+95A@WcAgXVLpH`-ZmQm`k7E zbst&x8^LxcH4AHWa9Stms%oniw{q9Xa*fSCvQ$C_xpEJZ>Pw47B%_hom9cd3uN*@4 ze!9}+LC}tOoIH6wIN`nF)GM2oMq_$M-UvbtgW6G_8fD57cd;3m+52s3#*HT8(^1v(yN3yRh zQX}enX$S6-G|MZ&e> 3) << 11) | ((0x3F >> 2) << 5) | (0x66 >> 3)); + void drawSleepBitmap() { lcd->reset(); lcd->clear(); - const BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(OpenTxTheme::instance()->getFilePath("sleep.bmp")); + const BitmapBuffer* bitmap = OpenTxTheme::instance()->shutdown; if (bitmap) { - lcd->drawBitmap((LCD_W-bitmap->width())/2, (LCD_H-bitmap->height())/2, bitmap); - delete bitmap; + lcd->drawMask((LCD_W-bitmap->width())/2, (LCD_H-bitmap->height())/2, + bitmap, splash_background_color); } lcdRefresh(); } -// -// TODO: move shutdown animation to Theme -// #define SHUTDOWN_CIRCLE_DIAMETER 150 -void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char * message) + +void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, + const char* message) { if (totalDuration == 0) return; @@ -100,38 +102,40 @@ void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, const char static const BitmapBuffer * shutdown = OpenTxTheme::instance()->shutdown; lcd->reset(); + lcd->clear(splash_background_color); + if (shutdown) { - OpenTxTheme::instance()->drawBackground(lcd); lcd->drawMask((LCD_W - shutdown->width()) / 2, - (LCD_H - shutdown->height()) / 2, shutdown, DEFAULT_COLOR); + (LCD_H - shutdown->height()) / 2, shutdown, + COLOR2FLAGS(WHITE)); int quarter = duration / (totalDuration / 5); if (quarter >= 1) lcd->drawBitmapPattern(LCD_W / 2, (LCD_H - SHUTDOWN_CIRCLE_DIAMETER) / 2, - LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, 0, + LBM_SHUTDOWN_CIRCLE, COLOR2FLAGS(WHITE), 0, SHUTDOWN_CIRCLE_DIAMETER / 2); if (quarter >= 2) lcd->drawBitmapPattern(LCD_W / 2, LCD_H / 2, LBM_SHUTDOWN_CIRCLE, - DEFAULT_COLOR, SHUTDOWN_CIRCLE_DIAMETER / 2, + COLOR2FLAGS(WHITE), SHUTDOWN_CIRCLE_DIAMETER / 2, SHUTDOWN_CIRCLE_DIAMETER / 2); if (quarter >= 3) lcd->drawBitmapPattern((LCD_W - SHUTDOWN_CIRCLE_DIAMETER) / 2, LCD_H / 2, - LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, + LBM_SHUTDOWN_CIRCLE, COLOR2FLAGS(WHITE), SHUTDOWN_CIRCLE_DIAMETER, SHUTDOWN_CIRCLE_DIAMETER / 2); if (quarter >= 4) lcd->drawBitmapPattern((LCD_W - SHUTDOWN_CIRCLE_DIAMETER) / 2, (LCD_H - SHUTDOWN_CIRCLE_DIAMETER) / 2, - LBM_SHUTDOWN_CIRCLE, DEFAULT_COLOR, + LBM_SHUTDOWN_CIRCLE, COLOR2FLAGS(WHITE), SHUTDOWN_CIRCLE_DIAMETER * 3 / 2, SHUTDOWN_CIRCLE_DIAMETER / 2); } else { - lcd->clear(); + //lcd->clear(); int quarter = duration / (totalDuration / 5); for (int i = 1; i <= 4; i++) { if (quarter >= i) { lcd->drawSolidFilledRect(LCD_W / 2 - 70 + 24 * i, LCD_H / 2 - 10, 20, - 20, DEFAULT_BGCOLOR); + 20, COLOR2FLAGS(WHITE)); } } } From f665ce5a9dbb09eaa53a6853d7ebe802df192c00 Mon Sep 17 00:00:00 2001 From: Risto Date: Fri, 11 Jun 2021 15:56:30 +0200 Subject: [PATCH 041/122] DMA2D configuration error fix (-DDEBUG_WINDOWS) (#190) * Fixes DMA2D ConfError * Check configuration and transfer errors Check for DMA2D configuration and transfer errors and do not enter the otherwise endless while loop. Thx to @raphaelcoeffic for the tip. --- radio/src/gui/colorlcd/fab_button.h | 2 +- radio/src/targets/horus/lcd_driver.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/fab_button.h b/radio/src/gui/colorlcd/fab_button.h index 34ee06c19..98b51707c 100644 --- a/radio/src/gui/colorlcd/fab_button.h +++ b/radio/src/gui/colorlcd/fab_button.h @@ -31,7 +31,7 @@ class FabButton: public Button { #if defined(DEBUG_WINDOWS) std::string getName() const override { - return "FabButton(" + std::to_string(icon) + ")"; + return "FabButton"; } #endif diff --git a/radio/src/targets/horus/lcd_driver.cpp b/radio/src/targets/horus/lcd_driver.cpp index 15764e3da..9fe6efacf 100644 --- a/radio/src/targets/horus/lcd_driver.cpp +++ b/radio/src/targets/horus/lcd_driver.cpp @@ -369,6 +369,10 @@ void DMAFillRect(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, ui /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -409,6 +413,10 @@ void DMACopyBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t x, /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -458,6 +466,10 @@ void DMACopyAlphaBitmap(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_ /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -512,6 +524,10 @@ void DMACopyAlphaMask(uint16_t * dest, uint16_t destw, uint16_t desth, uint16_t /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -545,6 +561,10 @@ void DMABitmapConvert(uint16_t * dest, const uint8_t * src, uint16_t w, uint16_t /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } @@ -578,6 +598,10 @@ void lcdCopy(void * dest, void * src) /* Start Transfer */ DMA2D_StartTransfer(); + /* Check configuration error */ + if ((DMA2D_GetFlagStatus(DMA2D_FLAG_CE) == SET) || (DMA2D_GetFlagStatus(DMA2D_FLAG_TE) == SET)) + return; // Exit if configuration or transfer error + /* Wait for CTC Flag activation */ while (DMA2D_GetFlagStatus(DMA2D_FLAG_TC) == RESET); } From e050aca5a0b6e22a75db2e5b8f5c50e79deb6320 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 11 Jun 2021 17:05:13 +0200 Subject: [PATCH 042/122] SetupWidgetPage: fix forwarding touch events to children Resolves #189 --- radio/src/gui/colorlcd/screen_setup.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/colorlcd/screen_setup.cpp b/radio/src/gui/colorlcd/screen_setup.cpp index 70f316fbd..686d78fd4 100644 --- a/radio/src/gui/colorlcd/screen_setup.cpp +++ b/radio/src/gui/colorlcd/screen_setup.cpp @@ -209,14 +209,13 @@ class SetupWidgetsPage: public FormWindow bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override { - // prevent screen sliding when setting up widgets + FormWindow::onTouchSlide(x, y, startX, startY, slideX, slideY); return true; } bool onTouchEnd(coord_t x, coord_t y) override { - if (FormWindow::onTouchEnd(x, y)) return true; - + FormWindow::onTouchEnd(x, y); return true; } #endif From c37bc9147594049df23c16805c3feeb2f133769d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 11 Jun 2021 12:54:02 +0200 Subject: [PATCH 043/122] Unregister LUA widgets when the factory is destoyed Resolves #183 --- radio/src/gui/colorlcd/widget.cpp | 6 ++++++ radio/src/gui/colorlcd/widget.h | 1 + radio/src/lua/widgets.cpp | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/radio/src/gui/colorlcd/widget.cpp b/radio/src/gui/colorlcd/widget.cpp index b6e3afb9c..350762fd8 100644 --- a/radio/src/gui/colorlcd/widget.cpp +++ b/radio/src/gui/colorlcd/widget.cpp @@ -196,6 +196,12 @@ void registerWidget(const WidgetFactory * factory) getRegisteredWidgets().push_back(factory); } +void unregisterWidget(const WidgetFactory * factory) +{ + TRACE("unregister widget %s", factory->getName()); + getRegisteredWidgets().remove(factory); +} + const WidgetFactory * getWidgetFactory(const char * name) { auto it = getRegisteredWidgets().cbegin(); diff --git a/radio/src/gui/colorlcd/widget.h b/radio/src/gui/colorlcd/widget.h index 1656af236..1b2a56c9f 100644 --- a/radio/src/gui/colorlcd/widget.h +++ b/radio/src/gui/colorlcd/widget.h @@ -114,6 +114,7 @@ class Widget : public Button }; void registerWidget(const WidgetFactory * factory); +void unregisterWidget(const WidgetFactory * factory); class WidgetFactory { diff --git a/radio/src/lua/widgets.cpp b/radio/src/lua/widgets.cpp index 1ffe6d182..38cfb596c 100644 --- a/radio/src/lua/widgets.cpp +++ b/radio/src/lua/widgets.cpp @@ -225,6 +225,11 @@ class LuaWidgetFactory: public WidgetFactory { } + ~LuaWidgetFactory() + { + unregisterWidget(this); + } + Widget * create(FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData, bool init=true) const override { if (lsWidgets == 0) return 0; From 78915daf950c248b28626564814aa466dd0b3ab0 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Fri, 11 Jun 2021 16:35:57 +0200 Subject: [PATCH 044/122] Fixed memory leak: widget factories need to be deleted in opentxClose() --- radio/src/gui/colorlcd/widget.h | 2 ++ radio/src/lua/lua_api.h | 3 +++ radio/src/lua/widgets.cpp | 10 ++++++++++ radio/src/opentx.cpp | 2 ++ 4 files changed, 17 insertions(+) diff --git a/radio/src/gui/colorlcd/widget.h b/radio/src/gui/colorlcd/widget.h index 1b2a56c9f..7906e1a61 100644 --- a/radio/src/gui/colorlcd/widget.h +++ b/radio/src/gui/colorlcd/widget.h @@ -126,6 +126,8 @@ class WidgetFactory registerWidget(this); } + virtual ~WidgetFactory() {} + inline const char * getName() const { return name; diff --git a/radio/src/lua/lua_api.h b/radio/src/lua/lua_api.h index 2860e470c..36f658d1a 100644 --- a/radio/src/lua/lua_api.h +++ b/radio/src/lua/lua_api.h @@ -187,6 +187,9 @@ void registerBitmapClass(lua_State * L); void luaSetInstructionsLimit(lua_State* L, int count); int luaLoadScriptFileToState(lua_State * L, const char * filename, const char * mode); +// Unregister LUA widget factories +void luaUnregisterWidgets(); + #if LCD_W > 350 #define RADIO_TOOL_NAME_MAXLEN 40 #else diff --git a/radio/src/lua/widgets.cpp b/radio/src/lua/widgets.cpp index 38cfb596c..b1d0bc489 100644 --- a/radio/src/lua/widgets.cpp +++ b/radio/src/lua/widgets.cpp @@ -512,3 +512,13 @@ void luaInitThemesAndWidgets() luaDoGc(lsWidgets, true); } } + +void luaUnregisterWidgets() +{ + std::list regWidgets(getRegisteredWidgets()); + for (auto w : regWidgets) { + if (dynamic_cast(w)) { + delete w; + } + } +} diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 79ded3d83..4d8cf4d76 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -1518,6 +1518,8 @@ void opentxClose(uint8_t shutdown) //TODO: In fact we want only to empty the trash (private method) MainWindow::instance()->run(); + + luaUnregisterWidgets(); luaClose(&lsWidgets); #endif From c28f61c646215feca3b757520a5eaea4aba53c39 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 12 Jun 2021 07:52:20 +0200 Subject: [PATCH 045/122] Reload LUA widgets before resuming the UI --- radio/src/opentx.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 4d8cf4d76..923ebb6a0 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -1521,6 +1521,7 @@ void opentxClose(uint8_t shutdown) luaUnregisterWidgets(); luaClose(&lsWidgets); + lsWidgets = 0; #endif #if defined(SDCARD) @@ -1534,6 +1535,11 @@ void opentxResume() TRACE("opentxResume"); sdMount(); +#if defined(COLORLCD) + // reload widgets + luaInitThemesAndWidgets(); +#endif + storageReadAll(); #if defined(COLORLCD) From e5bb4d3fb7bb55c3e721f0cc0c105eb8521bd5a1 Mon Sep 17 00:00:00 2001 From: yaapu Date: Fri, 11 Jun 2021 14:44:39 +0000 Subject: [PATCH 046/122] colorlcd: fix for drawNumber INVERS and text vertical and horizontal offsets --- radio/src/lua/api_colorlcd.cpp | 52 ++++++++++++++++++++++++++++++++-- radio/src/lua/api_colorlcd.h | 3 ++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/radio/src/lua/api_colorlcd.cpp b/radio/src/lua/api_colorlcd.cpp index 356f0a43f..a39df5517 100644 --- a/radio/src/lua/api_colorlcd.cpp +++ b/radio/src/lua/api_colorlcd.cpp @@ -27,6 +27,28 @@ #include "api_colorlcd.h" BitmapBuffer* luaLcdBuffer = nullptr; + +static int8_t getTextHorizontalOffset(LcdFlags flags) +{ + // no need to adjust if not right aligned + if (!(flags & RIGHT)) { + return 0; + } + const uint8_t font_index = FONT_INDEX(flags); + if (font_index >= sizeof(text_horizontal_offset)) { + return 0; + } + return text_horizontal_offset[font_index]; +} + +static int8_t getTextVerticalOffset(LcdFlags flags) +{ + const uint8_t font_index = FONT_INDEX(flags); + if (font_index >= sizeof(text_vertical_offset)) { + return 0; + } + return text_vertical_offset[font_index]; +} /*luadoc @function lcd.refresh() @@ -202,6 +224,10 @@ static int luaLcdDrawText(lua_State *L) const char * s = luaL_checkstring(L, 3); unsigned int att = luaL_optunsigned(L, 4, 0); + // apply text offsets, needed to align 2.4.x to 2.3.x font baselines + x += getTextHorizontalOffset(att); + y += getTextVerticalOffset(att); + bool shadowed = att & SHADOWED; bool invers = (att & INVERS); if (att & BLINK) invers = invers && !(BLINK_ON_PHASE); @@ -256,6 +282,10 @@ static int luaLcdDrawTimer(lua_State *L) int seconds = luaL_checkinteger(L, 3); unsigned int att = luaL_optunsigned(L, 4, 0); + // apply text offsets, needed to align 2.4.x to 2.3.x font baselines + x += getTextHorizontalOffset(att); + y += getTextVerticalOffset(att); + bool shadowed = att & SHADOWED; att = (att & 0xFFFF) | COLOR(COLOR_VAL(att)); @@ -295,8 +325,26 @@ static int luaLcdDrawNumber(lua_State *L) int val = luaL_checkinteger(L, 3); unsigned int att = luaL_optunsigned(L, 4, 0); + bool invers = (att & INVERS); att = (att & 0xFFFF) | COLOR(COLOR_VAL(att)); + // apply text offsets, needed to align 2.4.x to 2.3.x font baselines + x += getTextHorizontalOffset(att); + y += getTextVerticalOffset(att); + + char s[49]; + BitmapBuffer::formatNumberAsString(s, 49, val, att & 0xFFFF); + + if (invers) { + int height = getFontHeight(att & 0xFFFF); + int width = getTextWidth(s, 255, att); + luaLcdBuffer->drawSolidFilledRect( + x - INVERT_BOX_MARGIN, y - INVERT_BOX_MARGIN, + width + 2 * INVERT_BOX_MARGIN, height + 2 * INVERT_BOX_MARGIN, + FOCUS_BGCOLOR); + att = (att & 0xFFFF) | FOCUS_COLOR; + } + if ((att & SHADOWED) && !(att & INVERS)) { luaLcdBuffer->drawNumber(x, y, val, att & 0xFFFF); } @@ -664,9 +712,9 @@ static int luaLcdDrawGauge(lua_State *L) int num = luaL_checkinteger(L, 5); int den = luaL_checkinteger(L, 6); unsigned int flags = luaL_optunsigned(L, 7, 0); - + flags = (flags & 0xFFFF) | COLOR(COLOR_VAL(flags)); - + luaLcdBuffer->drawRect(x, y, w, h, 1, 0xff, flags); uint8_t len = limit((uint8_t)1, uint8_t(w*num/den), uint8_t(w)); luaLcdBuffer->drawSolidFilledRect(x+1, y+1, len, h-2, flags); diff --git a/radio/src/lua/api_colorlcd.h b/radio/src/lua/api_colorlcd.h index ae5f17109..13d480a66 100644 --- a/radio/src/lua/api_colorlcd.h +++ b/radio/src/lua/api_colorlcd.h @@ -33,5 +33,8 @@ constexpr coord_t INVERT_BOX_MARGIN = 2; +constexpr int8_t text_horizontal_offset[7] {-2,-2,-2,-2,-2,-2,-2}; +constexpr int8_t text_vertical_offset[7] {2,0,0,2,3,3,7}; + extern bool luaLcdAllowed; extern BitmapBuffer * luaLcdBuffer; From a15c1985897120676e3e0063d6871290e634b462 Mon Sep 17 00:00:00 2001 From: yaapu Date: Fri, 11 Jun 2021 16:07:40 +0000 Subject: [PATCH 047/122] libopenui: updated submodule commit hash to latest master branch --- radio/src/thirdparty/libopenui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index d68fd2c90..36f6e7498 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit d68fd2c902c20a347f784612d4ab1e5c8d130e4d +Subproject commit 36f6e749882eca3b58376ef4a41fb83133ddb714 From 07a3ff5c3a011cc075bc1310dd49c307d271b261 Mon Sep 17 00:00:00 2001 From: eshifri Date: Sat, 12 Jun 2021 10:22:42 -0700 Subject: [PATCH 048/122] fixed rename and copy --- radio/src/gui/colorlcd/radio_sdmanager.cpp | 80 ++++++++++++---------- radio/src/opentx.h | 2 - 2 files changed, 44 insertions(+), 38 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index 290ae73aa..5079e5269 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -25,6 +25,7 @@ #include "io/multi_firmware_update.h" #include "io/bootloader_flash.h" #include "standalone_lua.h" +#include "sdcard.h" class FileNameEditWindow : public Page { @@ -53,37 +54,39 @@ class FileNameEditWindow : public Page grid.spacer(8); uint8_t nameLength; uint8_t extLength; - char *ext = - (char *)getFileExtension(name.data(), 0, 0, &nameLength, &extLength); + char extension[LEN_FILE_EXTENSION_MAX + 1]; + memset(extension, 0, sizeof(extension)); + const char *ext = + getFileExtension(name.data(), 0, 0, &nameLength, &extLength); - reusableBuffer.sdManager.ext[0] = 0; - if (ext) strncpy(reusableBuffer.sdManager.ext, ext, extLength); + if (extLength > LEN_FILE_EXTENSION_MAX) extLength = LEN_FILE_EXTENSION_MAX; + if (ext) strncpy(extension, ext, extLength); + const uint8_t maxNameLength = SD_SCREEN_FILE_LENGTH - extLength; nameLength -= extLength; - if (nameLength > SD_SCREEN_FILE_LENGTH) nameLength = SD_SCREEN_FILE_LENGTH; + if (nameLength > maxNameLength) nameLength = maxNameLength; + memset(reusableBuffer.sdManager.originalName, 0, SD_SCREEN_FILE_LENGTH); strncpy(reusableBuffer.sdManager.originalName, name.c_str(), nameLength); - reusableBuffer.sdManager.originalName[nameLength] = 0; + reusableBuffer.sdManager.originalName[nameLength] = '\0'; - auto newFileName = new TextEdit(window, grid.getSlot(), - reusableBuffer.sdManager.originalName, - SD_SCREEN_FILE_LENGTH, LcdFlags(0)); - { - char *newValue = newFileName->getData(); - size_t totalSize = - strlen(newValue); // sizeof(reusableBuffer.sdManager.changedName); - strncpy(reusableBuffer.sdManager.changedName, newValue, totalSize); - reusableBuffer.sdManager.changedName[totalSize] = 0; + auto newFileName = new TextEdit( + window, grid.getSlot(), reusableBuffer.sdManager.originalName, + SD_SCREEN_FILE_LENGTH - extLength, LcdFlags(0)); + newFileName->setChangeHandler([=]() { + char *newValue = reusableBuffer.sdManager.originalName; + size_t totalSize = strlen(newValue); + char changedName[SD_SCREEN_FILE_LENGTH + 1]; + memset(changedName, 0, sizeof(changedName)); + strncpy(changedName, newValue, totalSize); + changedName[totalSize] = '\0'; if (extLength) { - strncpy(reusableBuffer.sdManager.changedName + totalSize, - reusableBuffer.sdManager.ext, - extLength); // totalSisze-strlen(newValue)); + strncpy(changedName + totalSize, extension, extLength); } - reusableBuffer.sdManager.changedName[totalSize + extLength] = 0; - f_rename((const TCHAR *)name.c_str(), - reusableBuffer.sdManager.changedName); - } - } + changedName[totalSize + extLength] = '\0'; + f_rename((const TCHAR *)name.c_str(), (const TCHAR *)changedName); + }); + }; }; RadioSdManagerPage::RadioSdManagerPage() : @@ -334,27 +337,32 @@ void RadioSdManagerPage::build(FormWindow * window) } if (!READ_ONLY()) { menu->addLine(STR_COPY_FILE, [=]() { - clipboard.type = CLIPBOARD_TYPE_SD_FILE; - f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN); - strncpy(clipboard.data.sd.filename, name.c_str(), CLIPBOARD_PATH_LEN - 1); + clipboard.type = CLIPBOARD_TYPE_SD_FILE; + f_getcwd(clipboard.data.sd.directory, CLIPBOARD_PATH_LEN); + strncpy(clipboard.data.sd.filename, name.c_str(), + CLIPBOARD_PATH_LEN - 1); }); if (clipboard.type == CLIPBOARD_TYPE_SD_FILE) { - menu->addLine(STR_PASTE, [=]() { - TCHAR lfn[FF_MAX_LFN+1]; - f_getcwd(lfn, FF_MAX_LFN); - - if (strcmp(clipboard.data.sd.directory, lfn)) { // prevent copying to the same directory - POPUP_WARNING(sdCopyFile(clipboard.data.sd.filename, clipboard.data.sd.directory, clipboard.data.sd.filename, lfn)); - clipboard.type = CLIPBOARD_TYPE_NONE; - } - rebuild(window); + menu->addLine(STR_PASTE, [=]() { + static char lfn[FF_MAX_LFN + 1]; // TODO optimize that! + f_getcwd((TCHAR *)lfn, FF_MAX_LFN); + // prevent copying to the same directory + if (strcmp(clipboard.data.sd.directory, lfn)) { + sdCopyFile(clipboard.data.sd.filename, + clipboard.data.sd.directory, + clipboard.data.sd.filename, lfn); + clipboard.type = CLIPBOARD_TYPE_NONE; + } + rebuild(window); }); } menu->addLine(STR_RENAME_FILE, [=]() { auto few = new FileNameEditWindow(name); - few->setCloseHandler([=]() { + few->setCloseHandler([=]() { + //window->clear(); rebuild(window); }); + // rebuild(window); }); }); menu->addLine(STR_DELETE_FILE, [=]() { f_unlink((const TCHAR*)getFullPath(name)); diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 800cd3207..c95b181a2 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -1087,8 +1087,6 @@ union ReusableBuffer uint16_t offset; uint16_t count; char originalName[SD_SCREEN_FILE_LENGTH+1]; - char changedName[SD_SCREEN_FILE_LENGTH+1]; - char ext[LEN_FILE_EXTENSION_MAX+1]; OtaUpdateInformation otaUpdateInformation; char otaReceiverVersion[sizeof(TR_CURRENT_VERSION) + 12]; } sdManager; From e4ee685a168924869668c663837593d316e1e4b4 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 12 Jun 2021 21:31:21 +0200 Subject: [PATCH 049/122] update libopenui --- radio/src/thirdparty/libopenui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index d68fd2c90..36f6e7498 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit d68fd2c902c20a347f784612d4ab1e5c8d130e4d +Subproject commit 36f6e749882eca3b58376ef4a41fb83133ddb714 From 29966c53ffe8062e7da51011db36d38274929b93 Mon Sep 17 00:00:00 2001 From: yaapu Date: Sat, 12 Jun 2021 22:05:07 +0000 Subject: [PATCH 050/122] colorlcd: fix for BOLD font h/v offsets, fix for lcd.clear() --- radio/src/lua/api_colorlcd.cpp | 2 +- radio/src/lua/api_colorlcd.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/radio/src/lua/api_colorlcd.cpp b/radio/src/lua/api_colorlcd.cpp index a39df5517..a4483728e 100644 --- a/radio/src/lua/api_colorlcd.cpp +++ b/radio/src/lua/api_colorlcd.cpp @@ -82,7 +82,7 @@ Clear the LCD screen static int luaLcdClear(lua_State * L) { if (luaLcdAllowed && luaLcdBuffer) { - LcdFlags color = luaL_optunsigned(L, 1, DEFAULT_BGCOLOR); + LcdFlags color = luaL_optunsigned(L, 1, DEFAULT_BGCOLOR_INDEX << 16u); luaLcdBuffer->clear(COLOR(COLOR_VAL(color))); } return 0; diff --git a/radio/src/lua/api_colorlcd.h b/radio/src/lua/api_colorlcd.h index 13d480a66..cad42e242 100644 --- a/radio/src/lua/api_colorlcd.h +++ b/radio/src/lua/api_colorlcd.h @@ -33,8 +33,8 @@ constexpr coord_t INVERT_BOX_MARGIN = 2; -constexpr int8_t text_horizontal_offset[7] {-2,-2,-2,-2,-2,-2,-2}; -constexpr int8_t text_vertical_offset[7] {2,0,0,2,3,3,7}; +constexpr int8_t text_horizontal_offset[7] {-2,-1,-2,-2,-2,-2,-2}; +constexpr int8_t text_vertical_offset[7] {2,2,0,2,3,3,7}; extern bool luaLcdAllowed; extern BitmapBuffer * luaLcdBuffer; From 3d0fd18e52a0691ab0be7c5d07bfc4d879a6204e Mon Sep 17 00:00:00 2001 From: Risto Date: Sun, 13 Jun 2021 11:17:41 +0200 Subject: [PATCH 051/122] Match color radio for the RGB565 hookup Uses upper bits for the display of lower bits of RGB565, that are not 0 on physical radio. Matches better the color display of radio. --- radio/src/simu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index 2ba72bef6..fc663fc9f 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -527,7 +527,7 @@ void OpenTxSim::refreshDisplay() setPixel(x, y, FXRGB(255, 255, 255)); } else { - FXColor color = FXRGB(255*((z&0xF800)>>11)/0x1f, 255*((z&0x07E0)>>5)/0x3F, 255*(z&0x001F)/0x01F); + FXColor color = FXRGB(((z & 0xF800) >> 8) + ((z & 0xE000) >> 13), ((z & 0x07E0) >> 3) + ((z & 0x0600) >> 9), (((z & 0x001F) << 3) & 0x00F8) + ((z & 0x001C) >> 2)); setPixel(x, y, color); } } From 4965927d397f5870685adce3f53af8a256da40f7 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 12:51:55 +0200 Subject: [PATCH 052/122] B/W: make shutdown confirmation depending on the issue at hand Resolve #199 --- radio/src/opentx.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 923ebb6a0..e934bcdcb 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -2074,15 +2074,15 @@ uint32_t pwrCheck() POPUP_CONFIRMATION(STR_MODEL_SHUTDOWN, nullptr); -#if defined(SHUTDOWN_CONFIRMATION) - if (TELEMETRY_STREAMING() && !g_eeGeneral.disableRssiPoweroffAlarm) { - SET_WARNING_INFO(STR_MODEL_STILL_POWERED, sizeof(TR_MODEL_STILL_POWERED), 0); + const char* msg = STR_MODEL_STILL_POWERED; + uint8_t msg_len = sizeof(TR_MODEL_STILL_POWERED); + if (usbPlugged() && getSelectedUsbMode() != USB_UNSELECTED_MODE) { + msg = STR_USB_STILL_CONNECTED; + msg_len = sizeof(TR_USB_STILL_CONNECTED); } -#else - SET_WARNING_INFO(STR_MODEL_STILL_POWERED, sizeof(TR_MODEL_STILL_POWERED), 0); -#endif - + event_t evt = getEvent(false); + SET_WARNING_INFO(msg, msg_len, 0); DISPLAY_WARNING(evt); LED_ERROR_BEGIN(); From 26b15d4d43bc1d2bab4f844bcb71537069476e94 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 06:32:36 +0200 Subject: [PATCH 053/122] Fixed issue with background on slide between view If no background image has been set. --- radio/src/gui/colorlcd/themes/480_default.cpp | 3 ++- radio/src/thirdparty/libopenui | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index 7b18995d1..e0c5714b0 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -334,7 +334,8 @@ class Theme480: public OpenTxTheme dc->drawBitmap(0 - dc->getOffsetX(), 0 - dc->getOffsetY(), backgroundBitmap); } else { - dc->drawSolidFilledRect(0, 0, LCD_W, LCD_H, DEFAULT_BGCOLOR); + dc->drawSolidFilledRect(0 - dc->getOffsetX(), 0 - dc->getOffsetY(), + LCD_W, LCD_H, DEFAULT_BGCOLOR); } } diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 36f6e7498..09e855345 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 36f6e749882eca3b58376ef4a41fb83133ddb714 +Subproject commit 09e855345973329eb66e319b2921ad3235331d66 From 57aa76c424ddf610be5f35af371e4159f32f5412 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 12 Jun 2021 17:51:28 +0200 Subject: [PATCH 054/122] Default Theme facelift --- radio/src/gui/colorlcd/colors.h | 77 +++++++++------- .../src/gui/colorlcd/layouts/topbar_impl.cpp | 40 +++++---- radio/src/gui/colorlcd/layouts/trims.cpp | 41 ++++++--- radio/src/gui/colorlcd/page.cpp | 5 +- radio/src/gui/colorlcd/sourcechoice.cpp | 7 +- radio/src/gui/colorlcd/switchchoice.cpp | 9 +- radio/src/gui/colorlcd/tabsgroup.cpp | 13 +-- radio/src/gui/colorlcd/theme.cpp | 67 +++++++------- radio/src/gui/colorlcd/theme.h | 6 +- radio/src/gui/colorlcd/themes/480_default.cpp | 85 +++++++++++------- radio/src/gui/colorlcd/themes/flysky.cpp | 63 +++++++------ radio/src/gui/colorlcd/timeedit.cpp | 6 +- radio/src/tests/transparency_CN_480x272.png | Bin 9219 -> 9206 bytes radio/src/tests/transparency_EN_480x272.png | Bin 9009 -> 8989 bytes radio/src/thirdparty/libopenui | 2 +- 15 files changed, 245 insertions(+), 176 deletions(-) diff --git a/radio/src/gui/colorlcd/colors.h b/radio/src/gui/colorlcd/colors.h index 8d0991676..58cdbab1b 100644 --- a/radio/src/gui/colorlcd/colors.h +++ b/radio/src/gui/colorlcd/colors.h @@ -27,50 +27,56 @@ // TODO common code, not in target enum LcdColorIndex { - DEFAULT_COLOR_INDEX, - DEFAULT_BGCOLOR_INDEX, - FOCUS_COLOR_INDEX, - FOCUS_BGCOLOR_INDEX, - TEXT_STATUSBAR_COLOR_INDEX, - LINE_COLOR_INDEX, - MENU_LINE_COLOR_INDEX = LINE_COLOR_INDEX, - CHECKBOX_COLOR_INDEX, - SCROLLBAR_COLOR_INDEX, - MENU_BGCOLOR_INDEX, - MENU_HIGHLIGHT_BGCOLOR_INDEX /*= MENU_BGCOLOR_INDEX*/, - MENU_COLOR_INDEX, - MENU_TITLE_DISABLE_COLOR_INDEX, - HEADER_COLOR_INDEX, ALARM_COLOR_INDEX, - HIGHLIGHT_COLOR_INDEX, - MENU_HIGHLIGHT_COLOR_INDEX = HIGHLIGHT_COLOR_INDEX, - TEXT_DISABLE_COLOR_INDEX, - DISABLE_COLOR_INDEX, - CURVE_COLOR_INDEX, - CURVE_AXIS_COLOR_INDEX, - CURVE_CURSOR_COLOR_INDEX, - HEADER_BGCOLOR_INDEX, - HEADER_ICON_BGCOLOR_INDEX, - HEADER_CURRENT_BGCOLOR_INDEX, - TITLE_BGCOLOR_INDEX, - TRIM_BGCOLOR_INDEX, - TRIM_SHADOW_COLOR_INDEX, - MAINVIEW_PANES_COLOR_INDEX, - MAINVIEW_GRAPHICS_COLOR_INDEX, - OVERLAY_COLOR_INDEX, - CUSTOM_COLOR_INDEX, BARGRAPH1_COLOR_INDEX, BARGRAPH2_COLOR_INDEX, BARGRAPH_BGCOLOR_INDEX, - SCROLLBOX_COLOR_INDEX, - TABLE_HEADER_BGCOLOR_INDEX, - TABLE_BGCOLOR_INDEX, BATTERY_CHARGE_COLOR_INDEX, + CHECKBOX_COLOR_INDEX, + CURVE_AXIS_COLOR_INDEX, + CURVE_COLOR_INDEX, + CURVE_CURSOR_COLOR_INDEX, + CUSTOM_COLOR_INDEX, + DEFAULT_BGCOLOR_INDEX, + DEFAULT_COLOR_INDEX, + DISABLE_COLOR_INDEX, + EDIT_MARKER_COLOR_INDEX, + FIELD_BGCOLOR_INDEX, + FIELD_FRAME_COLOR_INDEX, + FOCUS_BGCOLOR_INDEX, + FOCUS_COLOR_INDEX, + HEADER_BGCOLOR_INDEX, + HEADER_COLOR_INDEX, + HEADER_CURRENT_BGCOLOR_INDEX, + HEADER_ICON_BGCOLOR_INDEX, + HIGHLIGHT_COLOR_INDEX, + LINE_COLOR_INDEX, + MENU_HIGHLIGHT_BGCOLOR_INDEX, + MAINVIEW_GRAPHICS_COLOR_INDEX, + MAINVIEW_PANES_COLOR_INDEX, + MENU_BGCOLOR_INDEX, + MENU_COLOR_INDEX, + MENU_TITLE_DISABLE_COLOR_INDEX, + OVERLAY_COLOR_INDEX, + SCROLLBAR_COLOR_INDEX, + SCROLLBOX_COLOR_INDEX, + TABLE_BGCOLOR_INDEX, + TABLE_HEADER_BGCOLOR_INDEX, + TEXT_DISABLE_COLOR_INDEX, + TEXT_STATUSBAR_COLOR_INDEX, + TITLE_BGCOLOR_INDEX, + TRIM_BGCOLOR_INDEX, + TRIM_SHADOW_COLOR_INDEX, // this one MUST be last LCD_COLOR_COUNT, }; + +#define MENU_HIGHLIGHT_COLOR_INDEX HIGHLIGHT_COLOR_INDEX +#define MENU_LINE_COLOR_INDEX LINE_COLOR_INDEX + + // // Basic color definitions // @@ -116,6 +122,9 @@ inline void lcdSetColor(uint16_t color) #define DEFAULT_BGCOLOR COLOR(DEFAULT_BGCOLOR_INDEX) #define DEFAULT_COLOR COLOR(DEFAULT_COLOR_INDEX) #define DISABLE_COLOR COLOR(DISABLE_COLOR_INDEX) +#define EDIT_MARKER_COLOR COLOR(EDIT_MARKER_COLOR_INDEX) +#define FIELD_BGCOLOR COLOR(FIELD_BGCOLOR_INDEX) +#define FIELD_FRAME_COLOR COLOR(FIELD_FRAME_COLOR_INDEX) #define FOCUS_BGCOLOR COLOR(FOCUS_BGCOLOR_INDEX) #define FOCUS_COLOR COLOR(FOCUS_COLOR_INDEX) #define HEADER_BGCOLOR COLOR(HEADER_BGCOLOR_INDEX) diff --git a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp index 68d9e7378..7ba5bfb02 100644 --- a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp +++ b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp @@ -85,25 +85,26 @@ void TopbarImpl::paint(BitmapBuffer * dc) char str[10]; sprintf(str, "%d %s", t.tm_mday, STR_MONTHS[t.tm_mon]); - dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, MENU_COLOR); - dc->drawText(DATETIME_MIDDLE, DATETIME_LINE1, str, FONT(XS) | CENTERED| MENU_COLOR); + //dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, MENU_COLOR); + dc->drawText(DATETIME_MIDDLE, DATETIME_LINE1, str, FONT(XS) | CENTERED| FOCUS_COLOR); getTimerString(str, getValue(MIXSRC_TX_TIME)); - dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS) | CENTERED | MENU_COLOR); + dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS) | CENTERED | FOCUS_COLOR); #if defined(INTERNAL_GPS) if (gpsData.fix) { char s[10]; sprintf(s, "%d", gpsData.numSat); - dc->drawText(LCD_W-148, 4, s, FONT(XS) | CENTERED | MENU_COLOR); + dc->drawText(LCD_W-148, 4, s, FONT(XS) | CENTERED | FOCUS_COLOR); } - dc->drawBitmapPattern(LCD_W-158, 22, LBM_TOPMENU_GPS, (gpsData.fix) ? MENU_COLOR : MENU_TITLE_DISABLE_COLOR); + dc->drawBitmapPattern(LCD_W - 158, 22, LBM_TOPMENU_GPS, + (gpsData.fix) ? FOCUS_COLOR : MENU_TITLE_DISABLE_COLOR); #endif // USB icon if (usbPlugged()) { - LcdFlags flags = MENU_COLOR; + LcdFlags flags = FOCUS_COLOR; if (getSelectedUsbMode() == USB_UNSELECTED_MODE) { flags = MENU_TITLE_DISABLE_COLOR; } @@ -112,7 +113,7 @@ void TopbarImpl::paint(BitmapBuffer * dc) } // Logs else if (isFunctionActive(FUNCTION_LOGS) && BLINK_ON_PHASE) { - dc->drawBitmapPattern(LCD_W - 98, 6, LBM_DOT, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 98, 6, LBM_DOT, FOCUS_COLOR); } // RSSI @@ -120,42 +121,45 @@ void TopbarImpl::paint(BitmapBuffer * dc) const uint8_t rssiBarsHeight[] = {5, 10, 15, 21, 31}; for (unsigned int i = 0; i < DIM(rssiBarsHeight); i++) { uint8_t height = rssiBarsHeight[i]; - dc->drawSolidFilledRect(LCD_W - 90 + i * 6, 38 - height, 4, height, TELEMETRY_RSSI() >= rssiBarsValue[i] ? MENU_COLOR : MENU_TITLE_DISABLE_COLOR); + dc->drawSolidFilledRect(LCD_W - 90 + i * 6, 38 - height, 4, height, + TELEMETRY_RSSI() >= rssiBarsValue[i] + ? FOCUS_COLOR + : MENU_TITLE_DISABLE_COLOR); } #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) if (isModuleXJT(INTERNAL_MODULE) && isExternalAntennaEnabled()) { - dc->drawBitmapPattern(LCD_W-94, 4, LBM_TOPMENU_ANTENNA, MENU_COLOR); + dc->drawBitmapPattern(LCD_W-94, 4, LBM_TOPMENU_ANTENNA, FOCUS_COLOR); } #endif /* Audio volume */ dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_SCALE, MENU_TITLE_DISABLE_COLOR); if (requiredSpeakerVolume == 0 || g_eeGeneral.beepMode == e_mode_quiet) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_0, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_0, FOCUS_COLOR); else if (requiredSpeakerVolume < 7) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_1, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_1, FOCUS_COLOR); else if (requiredSpeakerVolume < 13) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_2, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_2, FOCUS_COLOR); else if (requiredSpeakerVolume < 19) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_3, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_3, FOCUS_COLOR); else - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_4, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_4, FOCUS_COLOR); /* Tx battery */ uint8_t bars = GET_TXBATT_BARS(5); #if defined(USB_CHARGER) if (usbChargerLed()) { - dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT_CHARGING, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT_CHARGING, FOCUS_COLOR); } else { - dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, FOCUS_COLOR); } #else - dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_COLOR); + dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, FOCUS_COLOR); #endif for (unsigned int i = 0; i < 5; i++) { - dc->drawSolidFilledRect(LCD_W - 128 + 4 * i, 30, 2, 8, i >= bars ? MENU_TITLE_DISABLE_COLOR : MENU_COLOR); + dc->drawSolidFilledRect(LCD_W - 128 + 4 * i, 30, 2, 8, i >= bars ? MENU_TITLE_DISABLE_COLOR : FOCUS_COLOR); } #if 0 diff --git a/radio/src/gui/colorlcd/layouts/trims.cpp b/radio/src/gui/colorlcd/layouts/trims.cpp index 013ba9bc6..82dc4f9e3 100644 --- a/radio/src/gui/colorlcd/layouts/trims.cpp +++ b/radio/src/gui/colorlcd/layouts/trims.cpp @@ -44,19 +44,25 @@ void MainViewHorizontalTrim::paint(BitmapBuffer * dc) } // Trim line - lcdSetColor(GREY); // TODO add a color - dc->drawSolidFilledRect(TRIM_SQUARE_SIZE / 2, (height() - TRIM_LINE_WIDTH - 1) / 2, width() - TRIM_SQUARE_SIZE + 1, TRIM_LINE_WIDTH, CUSTOM_COLOR); + dc->drawSolidFilledRect( + TRIM_SQUARE_SIZE / 2, (height() - TRIM_LINE_WIDTH - 1) / 2, + width() - TRIM_SQUARE_SIZE + 1, TRIM_LINE_WIDTH, DEFAULT_COLOR); // Trim square - coord_t x = divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value - trimMin), trimMax - trimMin); - drawTrimSquare(dc, x, 0, (value < TRIM_MIN || value > TRIM_MAX) ? HIGHLIGHT_COLOR /* TODO add a color */ : TRIM_BGCOLOR); + coord_t x = divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value - trimMin), + trimMax - trimMin); + drawTrimSquare(dc, x, 0, + (value < TRIM_MIN || value > TRIM_MAX) + ? HIGHLIGHT_COLOR /* TODO add a color */ + : TRIM_BGCOLOR); // Trim value / small lines on the square if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS) { // TODO DISPLAY_TRIMS_CHANGE - dc->drawNumber(x + (TRIM_SQUARE_SIZE + 1) / 2, 3, divRoundClosest(value * 100, trimMax), FONT(XXS) | FOCUS_COLOR | CENTERED); - } - else { + dc->drawNumber(x + (TRIM_SQUARE_SIZE + 1) / 2, 3, + divRoundClosest(value * 100, trimMax), + FONT(XXS) | FOCUS_COLOR | CENTERED); + } else { if (value >= 0) { dc->drawSolidVerticalLine(x + 4, 3, 9, FOCUS_COLOR); } @@ -79,19 +85,26 @@ void MainViewVerticalTrim::paint(BitmapBuffer * dc) } // Trim line - lcdSetColor(GREY); // TODO add a color - dc->drawSolidFilledRect((width() - TRIM_LINE_WIDTH) / 2, TRIM_SQUARE_SIZE / 2, TRIM_LINE_WIDTH, height() - TRIM_SQUARE_SIZE + 1, CUSTOM_COLOR); + dc->drawSolidFilledRect((width() - TRIM_LINE_WIDTH) / 2, TRIM_SQUARE_SIZE / 2, + TRIM_LINE_WIDTH, height() - TRIM_SQUARE_SIZE + 1, + DEFAULT_COLOR); // Trim square - coord_t y = height() - TRIM_SQUARE_SIZE - divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value - trimMin), trimMax - trimMin); - drawTrimSquare(dc, 0, y, (value < TRIM_MIN || value > TRIM_MAX) ? HIGHLIGHT_COLOR /* TODO add a color */ : TRIM_BGCOLOR); + coord_t y = height() - TRIM_SQUARE_SIZE - + divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value - trimMin), + trimMax - trimMin); + drawTrimSquare(dc, 0, y, + (value < TRIM_MIN || value > TRIM_MAX) + ? HIGHLIGHT_COLOR /* TODO add a color */ + : TRIM_BGCOLOR); // Trim value / small lines on the square if (g_model.displayTrims == DISPLAY_TRIMS_ALWAYS) { // TODO DISPLAY_TRIMS_CHANGE - dc->drawNumber((TRIM_SQUARE_SIZE + 1) / 2, y + 3, divRoundClosest(value * 100, trimMax), FONT(XXS) | FOCUS_COLOR | CENTERED); - } - else { + dc->drawNumber((TRIM_SQUARE_SIZE + 1) / 2, y + 3, + divRoundClosest(value * 100, trimMax), + FONT(XXS) | FOCUS_COLOR | CENTERED); + } else { if (value >= 0) { dc->drawSolidHorizontalLine(3, y + 4, 9, FOCUS_COLOR); } diff --git a/radio/src/gui/colorlcd/page.cpp b/radio/src/gui/colorlcd/page.cpp index a042e9d80..fa4084b98 100644 --- a/radio/src/gui/colorlcd/page.cpp +++ b/radio/src/gui/colorlcd/page.cpp @@ -37,8 +37,9 @@ PageHeader::PageHeader(Page * parent, uint8_t icon): void PageHeader::paint(BitmapBuffer * dc) { - OpenTxTheme::instance()->drawMenuBackground(dc, getIcon(), ""); - dc->drawSolidFilledRect(MENU_HEADER_HEIGHT, 0, LCD_W - MENU_HEADER_HEIGHT, MENU_HEADER_HEIGHT, MENU_BGCOLOR); + OpenTxTheme::instance()->drawPageHeaderBackground(dc, getIcon(), ""); + dc->drawSolidFilledRect(MENU_HEADER_HEIGHT, 0, LCD_W - MENU_HEADER_HEIGHT, + MENU_HEADER_HEIGHT, HEADER_COLOR); } Page::Page(unsigned icon): diff --git a/radio/src/gui/colorlcd/sourcechoice.cpp b/radio/src/gui/colorlcd/sourcechoice.cpp index 53a4c23c1..f0d1f0e57 100644 --- a/radio/src/gui/colorlcd/sourcechoice.cpp +++ b/radio/src/gui/colorlcd/sourcechoice.cpp @@ -64,11 +64,12 @@ void SourceChoice::paint(BitmapBuffer * dc) if (editMode) textColor = FOCUS_COLOR; else if (hasFocus()) - textColor = FOCUS_BGCOLOR; - else if (value == 0) - textColor = DISABLE_COLOR; + textColor = FOCUS_COLOR; + // else if (value == 0) + // textColor = DISABLE_COLOR; else textColor = 0; + drawSource(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor); } diff --git a/radio/src/gui/colorlcd/switchchoice.cpp b/radio/src/gui/colorlcd/switchchoice.cpp index 6f90e8878..65292f43e 100644 --- a/radio/src/gui/colorlcd/switchchoice.cpp +++ b/radio/src/gui/colorlcd/switchchoice.cpp @@ -48,11 +48,12 @@ void SwitchChoice::paint(BitmapBuffer * dc) if (editMode) textColor = FOCUS_COLOR; else if (hasFocus()) - textColor = FOCUS_BGCOLOR; - else if (value == 0) - textColor = DISABLE_COLOR; + textColor = FOCUS_COLOR; + // else if (value == 0) + // textColor = DISABLE_COLOR; else - textColor = 0; + textColor = DEFAULT_COLOR; + drawSwitch(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor); } diff --git a/radio/src/gui/colorlcd/tabsgroup.cpp b/radio/src/gui/colorlcd/tabsgroup.cpp index 3484d1187..48ee09042 100644 --- a/radio/src/gui/colorlcd/tabsgroup.cpp +++ b/radio/src/gui/colorlcd/tabsgroup.cpp @@ -46,12 +46,15 @@ TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon): void TabsGroupHeader::paint(BitmapBuffer * dc) { - OpenTxTheme::instance()->drawMenuBackground(dc, icon, title); + OpenTxTheme::instance()->drawPageHeaderBackground(dc, icon, title); } -TabsCarousel::TabsCarousel(Window * parent, TabsGroup * menu): - Window(parent, { MENU_HEADER_BUTTONS_LEFT, 0, LCD_W - MENU_HEADER_BUTTONS_LEFT, MENU_HEADER_HEIGHT + 10 }, TRANSPARENT), - menu(menu) +TabsCarousel::TabsCarousel(Window* parent, TabsGroup* menu) : + Window(parent, + {MENU_HEADER_BUTTONS_LEFT, 0, LCD_W - MENU_HEADER_BUTTONS_LEFT, + MENU_HEADER_HEIGHT + 10}, + TRANSPARENT), + menu(menu) { } @@ -62,7 +65,7 @@ void TabsCarousel::updateInnerWidth() void TabsCarousel::paint(BitmapBuffer * dc) { - OpenTxTheme::instance()->drawMenuHeader(dc, menu->tabs, currentIndex); + OpenTxTheme::instance()->drawPageHeader(dc, menu->tabs, currentIndex); } #if defined(HARDWARE_TOUCH) diff --git a/radio/src/gui/colorlcd/theme.cpp b/radio/src/gui/colorlcd/theme.cpp index b9561fab9..e3626dd6b 100644 --- a/radio/src/gui/colorlcd/theme.cpp +++ b/radio/src/gui/colorlcd/theme.cpp @@ -166,23 +166,18 @@ void OpenTxTheme::drawBackground(BitmapBuffer * dc) const // } //} -void OpenTxTheme::drawCheckBox(BitmapBuffer * dc, bool checked, coord_t x, coord_t y, bool focus) const +void OpenTxTheme::drawCheckBox(BitmapBuffer *dc, bool checked, coord_t x, + coord_t y, bool focus) const { + dc->drawSolidFilledRect(x, y, 16, 16, FIELD_BGCOLOR); if (focus) { - dc->drawSolidFilledRect(x + 0, y + 2, 16, 16, FOCUS_BGCOLOR); - if (checked) { - dc->drawSolidFilledRect(x + 2, y + 4, 12, 12, DEFAULT_BGCOLOR); - dc->drawSolidFilledRect(x + 3, y + 5, 10, 10, CHECKBOX_COLOR); - } - else { - dc->drawSolidFilledRect(x + 2, y + 4, 12, 12, DEFAULT_BGCOLOR); - } + dc->drawSolidRect(x, y, 16, 16, 2, FOCUS_BGCOLOR); } else { - dc->drawSolidRect(x + 0, y + 2, 16, 16, 1, DISABLE_COLOR); - if (checked) { - dc->drawSolidFilledRect(x + 2, y + 4, 12, 12, CHECKBOX_COLOR); - } + dc->drawSolidRect(x, y, 16, 16, 1, FIELD_FRAME_COLOR); + } + if (checked) { + dc->drawSolidFilledRect(x + 3, y + 3, 10, 10, CHECKBOX_COLOR); } } @@ -192,37 +187,43 @@ void OpenTxTheme::drawChoice(BitmapBuffer * dc, ChoiceBase * choice, const char if (choice->isEditMode()) textColor = FOCUS_COLOR; else if (choice->hasFocus()) - textColor = FOCUS_BGCOLOR; - else if (!str || str[0] == '\0') - textColor = DISABLE_COLOR; + textColor = FOCUS_COLOR; + // else if (!str || str[0] == '\0') + // textColor = FOCUS_COLOR; else textColor = DEFAULT_COLOR; - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, str[0] == '\0' ? "---" : str, textColor); - dc->drawBitmapPattern(choice->getRect().w - 20, (choice->getRect().h - 11) / 2, choice->getType() == CHOICE_TYPE_FOLDER ? LBM_FOLDER : LBM_DROPDOWN, textColor); + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, + str[0] == '\0' ? "---" : str, textColor); + + dc->drawBitmapPattern( + choice->getRect().w - 20, (choice->getRect().h - 11) / 2, + choice->getType() == CHOICE_TYPE_FOLDER ? LBM_FOLDER : LBM_DROPDOWN, + textColor); } -void OpenTxTheme::drawSlider(BitmapBuffer * dc, int vmin, int vmax, int value, const rect_t & rect, bool edit, bool focus) const +void OpenTxTheme::drawSlider(BitmapBuffer *dc, int vmin, int vmax, int value, + const rect_t &rect, bool edit, bool focus) const { int val = limit(vmin, value, vmax); int w = divRoundClosest((rect.w - 16) * (val - vmin), vmax - vmin); - if (edit) { - dc->drawBitmapPattern(0, 11, LBM_SLIDER_BAR_LEFT, FOCUS_BGCOLOR); - dc->drawSolidFilledRect(4, 11, rect.w - 8, 4, FOCUS_BGCOLOR); - dc->drawBitmapPattern(rect.w - 4, 11, LBM_SLIDER_BAR_RIGHT, FOCUS_BGCOLOR); - } - else { - dc->drawBitmapPattern(0, 11, LBM_SLIDER_BAR_LEFT, LINE_COLOR); - dc->drawSolidFilledRect(4, 11, rect.w - 8, 4, LINE_COLOR); - dc->drawBitmapPattern(rect.w - 4, 11, LBM_SLIDER_BAR_RIGHT, LINE_COLOR); + LcdFlags color = DEFAULT_COLOR; + if (focus) { + color = FOCUS_BGCOLOR; } - dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_OUT, DEFAULT_COLOR); - dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_MID, DEFAULT_BGCOLOR); - // if ((options & INVERS) && (!(options & BLINK) || !BLINK_ON_PHASE)) - if (focus) { - dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_IN, FOCUS_BGCOLOR); + dc->drawBitmapPattern(0, 11, LBM_SLIDER_BAR_LEFT, color); + dc->drawSolidFilledRect(4, 11, rect.w - 8, 4, color); + dc->drawBitmapPattern(rect.w - 4, 11, LBM_SLIDER_BAR_RIGHT, color); + + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_OUT, color); + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_MID, FIELD_BGCOLOR); + + if (edit) { + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_IN, EDIT_MARKER_COLOR); + } else { + dc->drawBitmapPattern(w, 5, LBM_SLIDER_POINT_IN, FIELD_BGCOLOR); } } diff --git a/radio/src/gui/colorlcd/theme.h b/radio/src/gui/colorlcd/theme.h index fdd5149c9..34b41e7be 100644 --- a/radio/src/gui/colorlcd/theme.h +++ b/radio/src/gui/colorlcd/theme.h @@ -85,9 +85,11 @@ class OpenTxTheme: public Theme virtual void drawBackground(BitmapBuffer * dc) const; - virtual void drawMenuBackground(BitmapBuffer * dc, uint8_t icon, const char *title) const = 0; + virtual void drawPageHeaderBackground(BitmapBuffer *dc, uint8_t icon, + const char *title) const = 0; - virtual void drawMenuHeader(BitmapBuffer * dc, std::vector & tabs, uint8_t currentIndex) const = 0; + virtual void drawPageHeader(BitmapBuffer *dc, std::vector &tabs, + uint8_t currentIndex) const = 0; // virtual void drawMessageBox(const char * title, const char * text, const // char * action, uint32_t flags) const override; diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index e0c5714b0..9c640329e 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -28,6 +28,16 @@ const ZoneOption OPTIONS_THEME_DEFAULT[] = { { nullptr, ZoneOption::Bool } }; +constexpr uint16_t __TEXT_COLOR = RGB(0x0C, 0x3F, 0x66); +constexpr uint16_t __BACKGROUND_COLOR = RGB(0x12, 0x5E, 0x99); +constexpr uint16_t __FOCUS_COLOR = RGB(0x14, 0xA1, 0xE5); +constexpr uint16_t __DATAFIELD_FRAME_COLOR = RGB(0xB6, 0xE0, 0xF2); +constexpr uint16_t __TAB_BACKGROUND_COLOR = RGB(0xE4, 0xEE, 0xF2); +constexpr uint16_t __PARAM_BACKGROUND_COLOR = WHITE; + +constexpr uint16_t __EDIT_MARKER_COLOR = RGB(0x00, 0x99, 0x09); +constexpr uint16_t __ACTIVE_MARKER_COLOR = RGB(0xFF, 0x99, 0x00); + class Theme480: public OpenTxTheme { public: @@ -45,43 +55,45 @@ class Theme480: public OpenTxTheme lcdColorTable[BARGRAPH2_COLOR_INDEX] = RGB(167, 167, 167); lcdColorTable[BARGRAPH_BGCOLOR_INDEX] = RGB(222, 222, 222); lcdColorTable[BATTERY_CHARGE_COLOR_INDEX] = GREEN; - lcdColorTable[CHECKBOX_COLOR_INDEX] = RED; + lcdColorTable[CHECKBOX_COLOR_INDEX] = __FOCUS_COLOR; lcdColorTable[CURVE_COLOR_INDEX] = RED; lcdColorTable[CURVE_CURSOR_COLOR_INDEX] = RED; - lcdColorTable[DEFAULT_BGCOLOR_INDEX] = WHITE; - lcdColorTable[DEFAULT_COLOR_INDEX] = BLACK; + lcdColorTable[DEFAULT_BGCOLOR_INDEX] = __TAB_BACKGROUND_COLOR; + lcdColorTable[DEFAULT_COLOR_INDEX] = __TEXT_COLOR; lcdColorTable[DISABLE_COLOR_INDEX] = LIGHTGREY; - lcdColorTable[FOCUS_BGCOLOR_INDEX] = RED; + lcdColorTable[EDIT_MARKER_COLOR_INDEX] = __EDIT_MARKER_COLOR; + lcdColorTable[FIELD_BGCOLOR_INDEX] = WHITE; + lcdColorTable[FIELD_FRAME_COLOR_INDEX] = __DATAFIELD_FRAME_COLOR; + lcdColorTable[FOCUS_BGCOLOR_INDEX] = __FOCUS_COLOR; lcdColorTable[FOCUS_COLOR_INDEX] = WHITE; - lcdColorTable[HEADER_COLOR_INDEX] = DARKRED; - lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = RED; - lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = RED; + lcdColorTable[HEADER_COLOR_INDEX] = __BACKGROUND_COLOR; + lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = __FOCUS_COLOR; + lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = __BACKGROUND_COLOR; lcdColorTable[HIGHLIGHT_COLOR_INDEX] = YELLOW; lcdColorTable[LINE_COLOR_INDEX] = GREY; lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = RED; lcdColorTable[MAINVIEW_PANES_COLOR_INDEX] = WHITE; - // this is what the status bar uses... - //lcdColorTable[MENU_BGCOLOR_INDEX] = DARKRED; + lcdColorTable[MENU_BGCOLOR_INDEX] = WHITE; + lcdColorTable[MENU_COLOR_INDEX] = __BACKGROUND_COLOR; // Menu font color - // Menu font color - lcdColorTable[MENU_BGCOLOR_INDEX] = DARKRED;//DARKGREY; - lcdColorTable[MENU_COLOR_INDEX] = WHITE; // Selected item in menu - lcdColorTable[MENU_HIGHLIGHT_BGCOLOR_INDEX] = BLACK; + lcdColorTable[MENU_HIGHLIGHT_BGCOLOR_INDEX] = __FOCUS_COLOR; lcdColorTable[MENU_HIGHLIGHT_COLOR_INDEX] = WHITE; + lcdColorTable[MENU_LINE_COLOR_INDEX] = __DATAFIELD_FRAME_COLOR; - lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = RGB(GET_RED(RED)>>1, GET_GREEN(RED)>>1, GET_BLUE(RED)>>1); + lcdColorTable[MENU_TITLE_DISABLE_COLOR_INDEX] = __TEXT_COLOR; lcdColorTable[OVERLAY_COLOR_INDEX] = BLACK; - lcdColorTable[SCROLLBAR_COLOR_INDEX] = RED; + lcdColorTable[SCROLLBAR_COLOR_INDEX] = __TEXT_COLOR; lcdColorTable[TEXT_DISABLE_COLOR_INDEX] = GREY; lcdColorTable[TEXT_STATUSBAR_COLOR_INDEX] = WHITE; - lcdColorTable[TITLE_BGCOLOR_INDEX] = RED; - lcdColorTable[TRIM_BGCOLOR_INDEX] = RED; + lcdColorTable[TITLE_BGCOLOR_INDEX] = __BACKGROUND_COLOR; + + lcdColorTable[TRIM_BGCOLOR_INDEX] = __FOCUS_COLOR; lcdColorTable[TRIM_SHADOW_COLOR_INDEX] = BLACK; } - void loadMenuIcon(uint8_t index, const uint8_t * lbm, uint32_t color=MENU_COLOR) const + void loadMenuIcon(uint8_t index, const uint8_t * lbm) const { BitmapBuffer * mask = BitmapBuffer::load8bitMask(lbm); if (mask) { @@ -91,15 +103,15 @@ class Theme480: public OpenTxTheme delete menuIconNormal[index]; menuIconNormal[index] = new BitmapBuffer(BMP_RGB565, mask->width(), mask->height()); if (menuIconNormal[index]) { - menuIconNormal[index]->clear(MENU_BGCOLOR); - menuIconNormal[index]->drawMask(0, 0, mask, color); + menuIconNormal[index]->clear(HEADER_ICON_BGCOLOR); + menuIconNormal[index]->drawMask(0, 0, mask, FOCUS_COLOR); } delete menuIconSelected[index]; menuIconSelected[index] = new BitmapBuffer(BMP_RGB565, mask->width(), mask->height()); if (menuIconSelected[index]) { menuIconSelected[index]->clear(HEADER_CURRENT_BGCOLOR); - menuIconSelected[index]->drawMask(0, 0, mask, color); + menuIconSelected[index]->drawMask(0, 0, mask, FOCUS_COLOR); } } } @@ -107,7 +119,7 @@ class Theme480: public OpenTxTheme void loadIcons() const { #if defined(LOG_TELEMETRY) || !defined(WATCHDOG) - loadMenuIcon(ICON_OPENTX, mask_opentx_testmode, DEFAULT_COLOR); + loadMenuIcon(ICON_OPENTX, mask_opentx_testmode); #else loadMenuIcon(ICON_OPENTX, mask_edgetx); #endif @@ -185,12 +197,12 @@ class Theme480: public OpenTxTheme currentMenuBackground->drawMask(0, 0, shadow, TRIM_SHADOW_COLOR); - currentMenuBackground->drawMask(10, 39, dot, MENU_COLOR); + currentMenuBackground->drawMask(10, 39, dot, FOCUS_COLOR); } delete topleftBitmap; topleftBitmap = BitmapBuffer::load8bitMaskOnBackground( - mask_topleft, TITLE_BGCOLOR, HEADER_COLOR); + mask_topleft, HEADER_CURRENT_BGCOLOR, HEADER_COLOR); delete background; delete shadow; @@ -347,7 +359,8 @@ class Theme480: public OpenTxTheme } } - void drawMenuBackground(BitmapBuffer * dc, uint8_t icon, const char * title) const override + void drawPageHeaderBackground(BitmapBuffer *dc, uint8_t icon, + const char *title) const override { if (topleftBitmap) { dc->drawBitmap(0, 0, topleftBitmap); @@ -363,8 +376,12 @@ class Theme480: public OpenTxTheme else dc->drawBitmap(5, 7, menuIconSelected[icon]); - dc->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, MENU_TITLE_TOP - MENU_HEADER_HEIGHT, DEFAULT_BGCOLOR); // the white separation line - dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, TITLE_BGCOLOR); // the title line background + dc->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, + MENU_TITLE_TOP - MENU_HEADER_HEIGHT, + DEFAULT_BGCOLOR); // the white separation line + + dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, + TITLE_BGCOLOR); // the title line background if (title) { dc->drawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP + 3, title, MENU_COLOR); } @@ -382,20 +399,24 @@ class Theme480: public OpenTxTheme return state == STATE_DEFAULT ? menuIconNormal[index] : menuIconSelected[index]; } - void drawMenuHeader(BitmapBuffer * dc, std::vector & tabs, uint8_t currentIndex) const override + void drawPageHeader(BitmapBuffer *dc, std::vector &tabs, + uint8_t currentIndex) const override { for (unsigned index = 0; index < tabs.size(); index++) { if (index != currentIndex) { - dc->drawBitmap(index*MENU_HEADER_BUTTON_WIDTH + 2, 7, menuIconNormal[tabs[index]->getIcon()]); + dc->drawBitmap(index * MENU_HEADER_BUTTON_WIDTH + 2, 7, + menuIconNormal[tabs[index]->getIcon()]); } } - dc->drawBitmap(currentIndex*MENU_HEADER_BUTTON_WIDTH, 0, currentMenuBackground); - dc->drawBitmap(currentIndex*MENU_HEADER_BUTTON_WIDTH + 2, 7, menuIconSelected[tabs[currentIndex]->getIcon()]); + dc->drawBitmap(currentIndex * MENU_HEADER_BUTTON_WIDTH, 0, + currentMenuBackground); + dc->drawBitmap(currentIndex * MENU_HEADER_BUTTON_WIDTH + 2, 7, + menuIconSelected[tabs[currentIndex]->getIcon()]); } void drawMenuDatetime(BitmapBuffer * dc) const { - dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, FOCUS_COLOR); + //dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, FOCUS_COLOR); struct gtm t; gettime(&t); diff --git a/radio/src/gui/colorlcd/themes/flysky.cpp b/radio/src/gui/colorlcd/themes/flysky.cpp index a395287d3..a8c519d41 100644 --- a/radio/src/gui/colorlcd/themes/flysky.cpp +++ b/radio/src/gui/colorlcd/themes/flysky.cpp @@ -348,25 +348,30 @@ class FlyskyTheme: public OpenTxTheme } } - void drawMenuBackground(BitmapBuffer * dc, uint8_t icon, const char * title) const override + void drawPageHeaderBackground(BitmapBuffer *dc, uint8_t icon, + const char *title) const override { -// if (topleftBitmap) { -// dc->drawBitmap(0, 0, topleftBitmap); -// uint16_t width = topleftBitmap->width(); -// dc->drawSolidFilledRect(width, 0, LCD_W-width, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); -// } -// else { - dc->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); -// } -// -// if (icon == ICON_OPENTX) -// dc->drawBitmap(4, 10, menuIconSelected[ICON_OPENTX]); -// else -// dc->drawBitmap(5, 7, menuIconSelected[icon]); -// - dc->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, MENU_TITLE_TOP - MENU_HEADER_HEIGHT, DEFAULT_BGCOLOR); // the white separation line - dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, TITLE_BGCOLOR); // the title line background -// + // if (topleftBitmap) { + // dc->drawBitmap(0, 0, topleftBitmap); + // uint16_t width = topleftBitmap->width(); + // dc->drawSolidFilledRect(width, 0, LCD_W-width, + // MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + // } + // else { + dc->drawSolidFilledRect(0, 0, LCD_W, MENU_HEADER_HEIGHT, HEADER_BGCOLOR); + // } + // + // if (icon == ICON_OPENTX) + // dc->drawBitmap(4, 10, menuIconSelected[ICON_OPENTX]); + // else + // dc->drawBitmap(5, 7, menuIconSelected[icon]); + // + dc->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, LCD_W, + MENU_TITLE_TOP - MENU_HEADER_HEIGHT, + DEFAULT_BGCOLOR); // the white separation line + dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, + TITLE_BGCOLOR); // the title line background + // if (title) { dc->drawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP + 2, title, MENU_COLOR); } @@ -382,19 +387,25 @@ class FlyskyTheme: public OpenTxTheme return state == STATE_DEFAULT ? menuIconNormal[index] : menuIconSelected[index]; } - void drawMenuHeader(BitmapBuffer * dc, std::vector & tabs, uint8_t currentIndex) const override + void drawPageHeader(BitmapBuffer *dc, std::vector &tabs, + uint8_t currentIndex) const override { uint8_t padding_left = 4; - dc->drawSolidFilledRect(0, 0, 4, MENU_HEADER_BUTTON_WIDTH, HEADER_BGCOLOR); + dc->drawSolidFilledRect(0, 0, 4, MENU_HEADER_BUTTON_WIDTH, + HEADER_BGCOLOR); for (unsigned i = 0; i < tabs.size(); i++) { - dc->drawBitmap(padding_left + i*MENU_HEADER_BUTTON_WIDTH, 0, theme->getIcon(tabs[i]->getIcon(), currentIndex == i ? STATE_PRESSED : STATE_DEFAULT)); + dc->drawBitmap( + padding_left + i * MENU_HEADER_BUTTON_WIDTH, 0, + theme->getIcon(tabs[i]->getIcon(), + currentIndex == i ? STATE_PRESSED : STATE_DEFAULT)); } -// coord_t x = padding_left + MENU_HEADER_BUTTON_WIDTH * tabs.size(); -// coord_t w = width() - x; -// if (w > 0) { -// dc->drawSolidFilledRect(x, 0, w, MENU_HEADER_BUTTON_WIDTH, HEADER_BGCOLOR); -// } + // coord_t x = padding_left + MENU_HEADER_BUTTON_WIDTH * tabs.size(); + // coord_t w = width() - x; + // if (w > 0) { + // dc->drawSolidFilledRect(x, 0, w, MENU_HEADER_BUTTON_WIDTH, + // HEADER_BGCOLOR); + // } } void drawMenuDatetime(BitmapBuffer * dc) const diff --git a/radio/src/gui/colorlcd/timeedit.cpp b/radio/src/gui/colorlcd/timeedit.cpp index 72245d17d..804eb4b9c 100644 --- a/radio/src/gui/colorlcd/timeedit.cpp +++ b/radio/src/gui/colorlcd/timeedit.cpp @@ -36,11 +36,13 @@ void TimeEdit::paint(BitmapBuffer * dc) if (editMode) textColor = FOCUS_COLOR; else if (hasFocus()) - textColor = FOCUS_BGCOLOR; + textColor = FOCUS_COLOR; else textColor = DEFAULT_COLOR; - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getTimerString(getValue(), (textFlags & TIMEHOUR) != 0), textColor); + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, + getTimerString(getValue(), (textFlags & TIMEHOUR) != 0), + textColor); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/tests/transparency_CN_480x272.png b/radio/src/tests/transparency_CN_480x272.png index 3f025249122ea513c47e816e309e7e911dee56a4..92083a206f956f9e805ec8a2c652f29351f35f5a 100644 GIT binary patch delta 1047 zcmZqn_~t%Ag>mmj)#YM_ZzBqmOj3m;nM4J}7AKlYF)dbP;9-;!yYPd1&BY7yyp0zH zBtXOgd8Wn57Zz)#OkSu|rn5xxfRU7Ovpox6Ooid0MV)_Qr!U?u;9mTtfUEJM*q4kd zIhaPpzR72$vnStD;+5sOBp`9u)8fqJWsPSpd|tqCntzjQ?c;O2o4#hP$e3&_F-K9z zhFRD`hUdw9l|LVzG2iUlchF!LccFySzY^r3X4hK z8-?33e0-ng9y#dTn8x*`PGF+K?Tbv3tczC`n6PYUJMco_c*Y@#$q$6hCeISpoxDR> zS#NI3;+LrxS{n|?etOTgBt=|_nfvE%f%*>|6J-13CDbOYEiVy&^x@or#m+K|S-(ua zpcFazk2LROemN!e67`1}Rg!UvA0Dy({Fk`!iO~`Ml-s`_v;6dvyW489$M^%tI!4>c z?I_6HHh#q?pw00*b_yfB|7y7qFpuqR?KEmS>Y?Wv8pG=-9B}z#`$4*P;mv6P8Od zvsPKU1z*eH=xJkEs%T(!#Bf%d+vL@Xnv(-$<8&8WbGAEFEld%A)N5^i;@iV^^C$PG z9zW>(FU+a$Vr<9ar8hD-8k`eKI+9HkdnRuL#)`j^!DJr^dHolDhZ5(-nS_`Cb@8%& zDpxcL*f4oQV{6memMJfmKHxicF)1DBP(g4&yvzaygxTbm(*A~^BsI&Vu#G`=w}5?z z#{}6;_7yBA`bGIZy$=Ki^2yW=d$-_A85{w&EN;Lc?gZ)V1*IJpBd!x>-MX%&gX&^*!&LWX`tTfVeUh2!k>@Nk7soB tf>K5gh~PN{ap<(giUy$EZQ$X|z|dyvy?gG0Il>G;;OXk;vd$@?2>_Rdv(W$m delta 1026 zcmez7-s~|!h4Iuz)#YM_cQ>XU&p4%G#9~up<90a9gvHI7L4wIdXTeADh{X%-rI;2g z8i0rfdlt9j3*3WGOjb~coP184ck)jO0c{WGhE$U@wt9{`I}EFm6*q1)*~MLG;?BQZ zKqA+%f7$bk0usE9KtlGB#N=2B?aBWX1Sa2=w3Sr=iq360Y?CRs!}yC$#lyqwn`CPr zp61;2HOwVqvWdhT1@n76J`W21{BYcMRR75e^fM^ z>>#Z>*+NcL&HXaBuhWCW{9HYMo%cPHuRpQ>{09bCMMIEdv;%=IZ?|v@z6x}?`Q#7M ze3Qi`9Tj&?E-=aD+a<8zftOPoKhKl*PBkA=O2i*!lu5)W8r*7Qf17Ps+@dcm_AHtF zQQmCwZgKv}6%u@tAIJz#{;eo)sBz0v;^6`VsawDx&2f5gT>Mk{)7k|I>JOXkpWI(u z(ZIBKip!lBdM>|IeuMnTR0cK0-$Ef*{b3JN`^6bDpwK+P(31(_UwDn2*le72uZ->Y=E zSzqLh)1OaC4napJOUO=F`S9VR#DoR=91eyV_sQFzDE}<)^`&pwDNtN825|K774|G^ zp6sun1x*F|lQm_Pj9&O1Qk)Q{akI4o6pgh%9`XPDe(vuN=YOe{@)9TRw|bm>cKqU^ zWD^#)XowLfp+^^&@z79; zsaYEmk57=}Q3)K6k9ZmtcXEH^XTNFqtC4B1z1!uB85|cBz@cz*@(1~eN^CjekNU+^ zem_4b|LHq(-2;Vs_Qx+`I~LCr2!}YQ7uh+AKa<|d zq9hX;cjtyziUw9k3}>yFtSDLW$HJnt8rFX^a+@1;?OpTXz4lI(d`qTe5Lae}F3 zS>UDh30hPHXrcmS2o>Nj={pJyHDcVbF2{U1yEKvMr=ONgaNUMV!F3dqk@A`u(4n+} z2V!eZ;}rwH$$urCZSM#_Pe|K74&VO3THypA<_xMjk(n5hnVz;;no;&((=w;?* z{Bb6^7qRsiZAVssS-J=CggBIxZ@>CVitn$oI?m_CU31r}IWBzBJT{KwE2X_;59_fg z@k*&Ok5TNRw{1t_E{+45%3gOp#6V1T0Nla%r6tfGz)g{|a4i?FiI9Z|vKIclSgD>w z#rEl^z74XnbM;&@rt_ZpmYPI++TN0%HTP%FLN`sIkH}N;?xLO-UG1-7&nI zxx3o)`r*t=V$b=2^IoTXM@aw*WePAZCkpa^t;XU*I>`t{=-MsI+|5a*xT7x0y%c9I zHX31So>Q^Zkjzq;uQQD%LLel^_w(jZ<=MP=kkvz9##7zIc-{acoZ#3{ z<z&TEC&7oFe>Ha6=8AXT3y47$2Ke*DYUcr9%8IL+beufdT9h;>x8bM5C zhlOT>kAx~Mymid8UI{+Su zb-;-$w29h>^tuJ0=ybmFwK1H+iBqX1JzQHUJ%o^f) ziE*wwMPBW&nG=Mu?Y;?42#bMVsRa&PrQH5%(JhN(sFi4`i-NFo9qJqp?Aj(5OuYfK zNft~IsHF%7p9zy|2`*I|BOw>cqO3UeBXFyVL6ubXe^THs!4YOzhK7ev8t_VM=Rbt* zz3=sr8^Dg+zzr2SK<_YA9+3N^OIv7(Zy1u!3bKi2U0&5)qreX27SqqPF~w#nJU27cr+X delta 1071 zcmbR1w$W{Z3geHBs@Fsf-`&`hY?3M@$s}hdw>Z&MifOST0}rE=*o7b5Yc5`p=WVB3j z6mT_OG<$UO7>d|>Xv=Lfmv?Iyf^!5!~6VeNLMLrIep z#OEp==rv~+wve%}n~?jl+g@Lz;L8iWBNyKo>~Kmb=}0zl?VEg1PHQrkIL~Bb1?9;J zQaqDqh$w563-v7%pVe}pU0y`ucYDoQ`+pPa=RaiZa!M)@f0PlnsE(tDudrv?s>uZk z=0FGgE8q3Bm=(ts%MnnZs(6^4?Nhm;U4_vu?m`o5o^=8evX3NuCr&;f)~R=KiRBun z2glpRQ+~Hksuq#|^HAaTJ!bn8<;I8kKb6~usZ{hV`{yXfv{?7WMHMTcN81e-WT+>$ zG#|>y=x;l)fwNIQ{>1&M-y56ew(tWT)&Gc*>r0SOAuxnqPJZLwZsEp#1L`P03x!naTuRwgZZ z>m&p8qi)UQI3>5qF;eoA?@KFaGfulWW1&As#g7UekAV0E39BWK_sc)o{gYp{%02jL z21idDg9eLR^o7MQ6VMWXfe|n~SthBe?iSFmv2@(VAOA=3|Gk~^^(Xf0yk}cTkjRv9ru#%_6AX29sxtC@6y?evYDnO1a{{hYHqi_5lmNAJ4Fo;$@U- zIq*W@c*Z6vs72C1KicawUYy&q_+_h_qCvuLf%-iPx9x$kvS0oKGy6|#3HSF-GG|+$ zDJ{1LTT0UeSvcKMoJr&@&}EG||78{=s6T907rE2=Bhf^#uZ`gwB>6}JlaJfv^OADR zz<{1ysU)MJ0E|?i<(|b6tq!hD#~Xe;KhVvca{KoK?&BF+GQ5my6b)`Q$@xk^HM+_I z!`p;K<5sK3LBB)H@o_!J4rXv901Z*>yVzm~%~QI-Y@i{Q!4aUyqNSzfcDT@lWd$%b z0t2cu3oVlLtrGrxe11IRNe?J-fC-*M0us57{c5M78C+tCA_K!j?Zs< diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 09e855345..f098dd9e4 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 09e855345973329eb66e319b2921ad3235331d66 +Subproject commit f098dd9e4fb6b87d8b12f7895b6832fa8843a149 From 85bc612f4a3ff2c1182f7f456f496e4e981e90b9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 06:51:43 +0200 Subject: [PATCH 055/122] Facelift round 2 --- radio/src/gui/colorlcd/channel_bar.h | 73 ++++++---- radio/src/gui/colorlcd/colors.h | 6 +- radio/src/gui/colorlcd/model_curves.cpp | 27 +++- radio/src/gui/colorlcd/model_inputs.cpp | 59 +++++--- radio/src/gui/colorlcd/model_inputs.h | 2 +- radio/src/gui/colorlcd/model_mixes.cpp | 33 +++-- radio/src/gui/colorlcd/model_outputs.cpp | 56 ++++++-- radio/src/gui/colorlcd/themes/480_default.cpp | 6 +- radio/src/gui/colorlcd/view_channels.cpp | 4 +- .../gui/colorlcd/view_logical_switches.cpp | 70 +++++----- .../src/gui/colorlcd/view_logical_switches.h | 127 +++++++++--------- 11 files changed, 286 insertions(+), 177 deletions(-) diff --git a/radio/src/gui/colorlcd/channel_bar.h b/radio/src/gui/colorlcd/channel_bar.h index 5c1afb1ad..c371620f1 100644 --- a/radio/src/gui/colorlcd/channel_bar.h +++ b/radio/src/gui/colorlcd/channel_bar.h @@ -58,18 +58,30 @@ class MixerChannelBar : public ChannelBar chanVal = limit(-VIEW_CHANNELS_LIMIT_PCT, chanVal, VIEW_CHANNELS_LIMIT_PCT); // Draw Background - dc->drawSolidFilledRect(0, 0, width(), height(), BARGRAPH_BGCOLOR); + dc->drawSolidFilledRect(0, 0, width(), height(), FIELD_BGCOLOR); // Draw mixer bar if (chanVal > 0) { - dc->drawSolidFilledRect(0 + width() / 2, 0, divRoundClosest(chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2), height(), BARGRAPH2_COLOR); - dc->drawNumber(width() / 2 - 10, 0, displayVal, FONT(XS) | DEFAULT_COLOR | RIGHT, 0, nullptr, "%"); - } - else if (chanVal < 0) { + + dc->drawSolidFilledRect( + 0 + width() / 2, 0, + divRoundClosest(chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2), + height(), FOCUS_BGCOLOR); + + dc->drawNumber(width() / 2 - 10, 0, displayVal, + FONT(XS) | DEFAULT_COLOR | RIGHT, 0, nullptr, "%"); + + } else if (chanVal < 0) { + const unsigned endpoint = width() / 2; - const unsigned size = divRoundClosest(-chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2); - dc->drawSolidFilledRect(endpoint - size, 0, size, height(), BARGRAPH2_COLOR); - dc->drawNumber(10 + width() / 2, 0, displayVal, FONT(XS) | DEFAULT_COLOR, 0, nullptr, "%"); + const unsigned size = + divRoundClosest(-chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2); + + dc->drawSolidFilledRect(endpoint - size, 0, size, height(), + FOCUS_BGCOLOR); + + dc->drawNumber(10 + width() / 2, 0, displayVal, + FONT(XS) | DEFAULT_COLOR, 0, nullptr, "%"); } // Draw middle bar @@ -100,36 +112,47 @@ class OutputChannelBar : public ChannelBar int chanVal = calcRESXto100(channelOutputs[channel]); int displayVal = chanVal; - chanVal = limit(-VIEW_CHANNELS_LIMIT_PCT, chanVal, VIEW_CHANNELS_LIMIT_PCT); + chanVal = limit(-VIEW_CHANNELS_LIMIT_PCT, chanVal, + VIEW_CHANNELS_LIMIT_PCT); // Draw Background - dc->drawSolidFilledRect(0, 0, width(), height(), BARGRAPH_BGCOLOR); + dc->drawSolidFilledRect(0, 0, width(), height(), FIELD_BGCOLOR); // Draw output bar if (chanVal > 0) { - dc->drawSolidFilledRect(width() / 2, 0, divRoundClosest(chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2), height(), BARGRAPH1_COLOR); - dc->drawNumber(width() / 2 - 10, 0, displayVal, FONT(XS) | DEFAULT_COLOR | RIGHT, 0, nullptr, "%"); - } - else if (chanVal < 0) { + + dc->drawSolidFilledRect( + width() / 2, 0, + divRoundClosest(chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2), + height(), HIGHLIGHT_COLOR); + + dc->drawNumber(width() / 2 - 10, 0, displayVal, + FONT(XS) | DEFAULT_COLOR | RIGHT, 0, nullptr, "%"); + + } else if (chanVal < 0) { unsigned endpoint = width() / 2; - unsigned size = divRoundClosest(-chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2); - dc->drawSolidFilledRect(endpoint - size, 0, size, height(), BARGRAPH1_COLOR); - dc->drawNumber(width() / 2 + 10, 0, displayVal, FONT(XS) | DEFAULT_COLOR, 0, nullptr, "%"); + unsigned size = + divRoundClosest(-chanVal * width(), VIEW_CHANNELS_LIMIT_PCT * 2); + + dc->drawSolidFilledRect(endpoint - size, 0, size, height(), + HIGHLIGHT_COLOR); + + dc->drawNumber(width() / 2 + 10, 0, displayVal, + FONT(XS) | DEFAULT_COLOR, 0, nullptr, "%"); } // Draw middle bar dc->drawSolidVerticalLine(width() / 2, 0, height(), DEFAULT_COLOR); // Draw output limits bars - LimitData * ld = limitAddress(channel); + LimitData* ld = limitAddress(channel); if (ld && ld->revert) { - drawOutputBarLimits(dc, posOnBar(-100 - ld->max / 10), posOnBar(100 - ld->min / 10)); - //lcd->drawBitmap(x - X_OFFSET + 7, y + 25, chanMonInvertedBitmap); + drawOutputBarLimits(dc, posOnBar(-100 - ld->max / 10), + posOnBar(100 - ld->min / 10)); + } else if (ld) { + drawOutputBarLimits(dc, posOnBar(-100 + ld->min / 10), + posOnBar(100 + ld->max / 10)); } - else if (ld) { - drawOutputBarLimits(dc, posOnBar(-100 + ld->min / 10), posOnBar(100 + ld->max / 10)); - } - } inline unsigned posOnBar(int value_to100) @@ -215,4 +238,4 @@ class ComboChannelBar : public ChannelBar protected: int value = 0; -}; \ No newline at end of file +}; diff --git a/radio/src/gui/colorlcd/colors.h b/radio/src/gui/colorlcd/colors.h index 58cdbab1b..93c83152c 100644 --- a/radio/src/gui/colorlcd/colors.h +++ b/radio/src/gui/colorlcd/colors.h @@ -56,6 +56,8 @@ enum LcdColorIndex MAINVIEW_PANES_COLOR_INDEX, MENU_BGCOLOR_INDEX, MENU_COLOR_INDEX, + MENU_HIGHLIGHT_COLOR_INDEX, + MENU_LINE_COLOR_INDEX, MENU_TITLE_DISABLE_COLOR_INDEX, OVERLAY_COLOR_INDEX, SCROLLBAR_COLOR_INDEX, @@ -73,8 +75,8 @@ enum LcdColorIndex }; -#define MENU_HIGHLIGHT_COLOR_INDEX HIGHLIGHT_COLOR_INDEX -#define MENU_LINE_COLOR_INDEX LINE_COLOR_INDEX +//#define MENU_HIGHLIGHT_COLOR_INDEX HIGHLIGHT_COLOR_INDEX +//#define MENU_LINE_COLOR_INDEX LINE_COLOR_INDEX // diff --git a/radio/src/gui/colorlcd/model_curves.cpp b/radio/src/gui/colorlcd/model_curves.cpp index fdf2295c0..29289cbe2 100644 --- a/radio/src/gui/colorlcd/model_curves.cpp +++ b/radio/src/gui/colorlcd/model_curves.cpp @@ -42,10 +42,16 @@ class CurveEditWindow : public Page void buildHeader(Window * window) { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MENUCURVE, 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENUCURVE, 0, MENU_COLOR); char s[16]; strAppendStringWithIndex(s, STR_CV, index + 1); - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, s, 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + s, 0, MENU_COLOR); } #if LCD_W > LCD_H @@ -226,8 +232,14 @@ class CurveButton : public Button { void paint(BitmapBuffer * dc) override { + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); + // bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); + if (hasFocus()) { + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + } else { + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + } // curve characteristics if (isCurveUsed(index)) { @@ -297,7 +309,10 @@ void ModelCurvesPage::build(FormWindow * window, int8_t focusIndex) if (isCurveUsed(index)) { // Curve label - new StaticText(window, grid.getLabelSlot(), getCurveString(1 + index), BUTTON_BACKGROUND, CENTERED); + auto text = + new StaticText(window, grid.getLabelSlot(), getCurveString(1 + index), + BUTTON_BACKGROUND, CENTERED); + text->setBackgroundColor(FOCUS_COLOR); // Curve drawing Button * button = new CurveButton(window, grid.getFieldSlot(), index); @@ -325,10 +340,10 @@ void ModelCurvesPage::build(FormWindow * window, int8_t focusIndex) } grid.spacer(button->height() + 5); - } - else { + } else { auto button = new TextButton(window, grid.getLabelSlot(), getCurveString(1 + index)); + button->setPressHandler([=]() { Menu *menu = new Menu(window); menu->addLine(STR_EDIT, [=]() { diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp index 143b3cf30..8d43f32fd 100644 --- a/radio/src/gui/colorlcd/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -302,19 +302,23 @@ void CommonInputOrMixButton::checkEvents() Button::checkEvents(); } -void CommonInputOrMixButton::drawFlightModes(BitmapBuffer *dc, FlightModesType value) +void CommonInputOrMixButton::drawFlightModes(BitmapBuffer *dc, + FlightModesType value, + LcdFlags textColor) { - dc->drawMask(146, 2 + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupFlightmodeIcon, DEFAULT_COLOR); + dc->drawMask(146, 2 + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupFlightmodeIcon, textColor); coord_t x = 166; for (int i = 0; i < MAX_FLIGHT_MODES; i++) { char s[] = " "; s[0] = '0' + i; if (value & (1 << i)) { - dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, s, FONT(XS) | TEXT_DISABLE_COLOR); - } - else { - dc->drawSolidFilledRect(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, 8, 3, CHECKBOX_COLOR); - dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, s, FONT(XS)); + dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, s, + FONT(XS) | TEXT_DISABLE_COLOR); + } else { + dc->drawSolidFilledRect(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, 8, 3, + FOCUS_BGCOLOR); + dc->drawText(x, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP + 2, s, FONT(XS) | textColor); } x += 8; } @@ -322,10 +326,11 @@ void CommonInputOrMixButton::drawFlightModes(BitmapBuffer *dc, FlightModesType v void CommonInputOrMixButton::paint(BitmapBuffer * dc) { - if (active) - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, HIGHLIGHT_COLOR); + dc->drawSolidFilledRect(0, 0, width(), height(), + hasFocus() ? FOCUS_BGCOLOR : FIELD_BGCOLOR); paintBody(dc); - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); + if (!hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FIELD_FRAME_COLOR); } class InputLineButton : public CommonInputOrMixButton { @@ -348,28 +353,38 @@ class InputLineButton : public CommonInputOrMixButton { { const ExpoData & line = g_model.expoData[index]; + LcdFlags textColor = DEFAULT_COLOR; + if (hasFocus()) + textColor = FOCUS_COLOR; + // first line ... - drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, -100, 100); - drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw); + drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, + -100, 100, textColor); + drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); if (line.name[0]) { - dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); - dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name)); + dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); + dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), + textColor); } // second line ... if (line.swtch) { - dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupSwitchIcon, DEFAULT_COLOR); - drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch); + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupSwitchIcon, textColor); + drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + textColor); } if (line.curve.value != 0 ) { - dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupCurveIcon, DEFAULT_COLOR); - drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve); + dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupCurveIcon, textColor); + drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, + textColor); } if (line.flightModes) { - drawFlightModes(dc, line.flightModes); + drawFlightModes(dc, line.flightModes, textColor); } } }; @@ -406,7 +421,11 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) ExpoData * line = g_model.expoData; for (uint8_t input = 0; input < MAX_INPUTS; input++) { if (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) { - new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_FIRST_INPUT + input), BUTTON_BACKGROUND, CENTERED); + auto txt = new StaticText(window, grid.getLabelSlot(), + getSourceString(MIXSRC_FIRST_INPUT + input), + 0, CENTERED); + txt->setBackgroundColor(FIELD_BGCOLOR); + while (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) { Button * button = new InputLineButton(window, grid.getFieldSlot(), inputIndex); if (focusIndex == inputIndex) diff --git a/radio/src/gui/colorlcd/model_inputs.h b/radio/src/gui/colorlcd/model_inputs.h index 0f6afbd93..9677c4916 100644 --- a/radio/src/gui/colorlcd/model_inputs.h +++ b/radio/src/gui/colorlcd/model_inputs.h @@ -56,7 +56,7 @@ class CommonInputOrMixButton : public Button { void checkEvents() override; - void drawFlightModes(BitmapBuffer *dc, FlightModesType value); + void drawFlightModes(BitmapBuffer *dc, FlightModesType value, LcdFlags textColor); void paint(BitmapBuffer * dc) override; diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index eb409397b..fbe28766f 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -246,28 +246,38 @@ class MixLineButton : public CommonInputOrMixButton { { const MixData & line = g_model.mixData[index]; + LcdFlags textColor = DEFAULT_COLOR; + if (hasFocus()) + textColor = FOCUS_COLOR; + // first line ... - drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, MIX_WEIGHT_MIN, MIX_WEIGHT_MAX); - drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw); + drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, + MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, textColor); + drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); if (line.name[0]) { - dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); - dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name)); + dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); + dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), + textColor); } // second line ... if (line.swtch) { - dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupSwitchIcon, DEFAULT_COLOR); - drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch); + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupSwitchIcon, textColor); + drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + textColor); } if (line.curve.value) { - dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupCurveIcon, DEFAULT_COLOR); - drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, 0); + dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupCurveIcon, textColor); + drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, + textColor); } if (line.flightModes) { - drawFlightModes(dc, line.flightModes); + drawFlightModes(dc, line.flightModes, textColor); } } }; @@ -329,7 +339,10 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) MixData * mix = g_model.mixData; for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) { if (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { - new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_CH1 + ch), BUTTON_BACKGROUND, CENTERED); + auto txt = new StaticText(window, grid.getLabelSlot(), + getSourceString(MIXSRC_CH1 + ch), 0, + CENTERED); + txt->setBackgroundColor(FIELD_BGCOLOR); uint8_t count = 0; while (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { Button * button = new MixLineButton(window, grid.getFieldSlot(), mixIndex); diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index aa381ad59..66576fd3f 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -39,8 +39,14 @@ class OutputEditWindow : public Page { void buildHeader(Window * window) { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MENULIMITS, 0, MENU_COLOR); - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, getSourceString(MIXSRC_CH1 + channel), 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENULIMITS, 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSourceString(MIXSRC_CH1 + channel), 0, MENU_COLOR); } void buildBody(FormWindow * window) @@ -120,28 +126,47 @@ class OutputLineButton : public Button { void paint(BitmapBuffer * dc) override { + LcdFlags textColor = DEFAULT_COLOR; + LcdFlags bgColor = FIELD_BGCOLOR; + + if (hasFocus()) { + textColor = FOCUS_COLOR; + bgColor = FOCUS_BGCOLOR; + } + + dc->drawSolidFilledRect(0, 0, width(), height(), bgColor); + // first line - dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, output->min - 1000, PREC1); - dc->drawNumber(68, FIELD_PADDING_TOP, output->max + 1000, PREC1); - dc->drawNumber(132, FIELD_PADDING_TOP, output->offset, PREC1); - dc->drawNumber(226, FIELD_PADDING_TOP, PPM_CENTER + output->ppmCenter, RIGHT); - dc->drawText(228, FIELD_PADDING_TOP, output->symetrical ? "=" : "\210"); + dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, output->min - 1000, + PREC1 | textColor); + dc->drawNumber(68, FIELD_PADDING_TOP, output->max + 1000, PREC1 | textColor); + dc->drawNumber(132, FIELD_PADDING_TOP, output->offset, PREC1 | textColor); + dc->drawNumber(226, FIELD_PADDING_TOP, PPM_CENTER + output->ppmCenter, + RIGHT | textColor); + dc->drawText(228, FIELD_PADDING_TOP, output->symetrical ? "=" : "\210", + textColor); // second line if (output->revert) { - dc->drawTextAtIndex(4, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, STR_MMMINV, output->revert); + dc->drawTextAtIndex(4, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, STR_MMMINV, + output->revert, textColor); } if (output->curve) { - dc->drawMask(68, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupCurveIcon, DEFAULT_COLOR); - dc->drawText(88, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, getCurveString(output->curve)); + dc->drawMask(68, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupCurveIcon, textColor); + dc->drawText(88, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + getCurveString(output->curve), textColor); } if (output->name[0]) { - dc->drawMask(146, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, mixerSetupLabelIcon, DEFAULT_COLOR); - dc->drawSizedText(166, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, output->name, sizeof(output->name)); + dc->drawMask(146, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupLabelIcon, textColor); + dc->drawSizedText(166, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + output->name, sizeof(output->name), textColor); } // bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, + hasFocus() ? FOCUS_BGCOLOR : FIELD_FRAME_COLOR); } protected: @@ -171,7 +196,10 @@ void ModelOutputsPage::build(FormWindow * window, int8_t focusChannel) LimitData * output = limitAddress(ch); // Channel label - new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_CH1 + ch), BUTTON_BACKGROUND, CENTERED); + auto txt = new StaticText(window, grid.getLabelSlot(), + getSourceString(MIXSRC_CH1 + ch), 0, + CENTERED); + txt->setBackgroundColor(FIELD_BGCOLOR); // Channel settings Button * button = new OutputLineButton(window, grid.getFieldSlot(), output); diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index 9c640329e..d13aebb16 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -36,7 +36,7 @@ constexpr uint16_t __TAB_BACKGROUND_COLOR = RGB(0xE4, 0xEE, 0xF2); constexpr uint16_t __PARAM_BACKGROUND_COLOR = WHITE; constexpr uint16_t __EDIT_MARKER_COLOR = RGB(0x00, 0x99, 0x09); -constexpr uint16_t __ACTIVE_MARKER_COLOR = RGB(0xFF, 0x99, 0x00); +constexpr uint16_t __ACTIVE_MARKER_COLOR = RGB(0xFF, 0xDE, 0x00); class Theme480: public OpenTxTheme { @@ -69,7 +69,7 @@ class Theme480: public OpenTxTheme lcdColorTable[HEADER_COLOR_INDEX] = __BACKGROUND_COLOR; lcdColorTable[HEADER_CURRENT_BGCOLOR_INDEX] = __FOCUS_COLOR; lcdColorTable[HEADER_ICON_BGCOLOR_INDEX] = __BACKGROUND_COLOR; - lcdColorTable[HIGHLIGHT_COLOR_INDEX] = YELLOW; + lcdColorTable[HIGHLIGHT_COLOR_INDEX] = __ACTIVE_MARKER_COLOR; lcdColorTable[LINE_COLOR_INDEX] = GREY; lcdColorTable[MAINVIEW_GRAPHICS_COLOR_INDEX] = RED; lcdColorTable[MAINVIEW_PANES_COLOR_INDEX] = WHITE; @@ -383,7 +383,7 @@ class Theme480: public OpenTxTheme dc->drawSolidFilledRect(0, MENU_TITLE_TOP, LCD_W, MENU_TITLE_HEIGHT, TITLE_BGCOLOR); // the title line background if (title) { - dc->drawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP + 3, title, MENU_COLOR); + dc->drawText(MENUS_MARGIN_LEFT, MENU_TITLE_TOP + 3, title, FOCUS_COLOR); } drawMenuDatetime(dc); diff --git a/radio/src/gui/colorlcd/view_channels.cpp b/radio/src/gui/colorlcd/view_channels.cpp index 84d6238bd..ab55fc170 100644 --- a/radio/src/gui/colorlcd/view_channels.cpp +++ b/radio/src/gui/colorlcd/view_channels.cpp @@ -57,8 +57,8 @@ class ChannelsViewFooter: public Window { // Draw legend bar coord_t x = 10; dc->drawSolidFilledRect(0, 0, width(), height(), TITLE_BGCOLOR); - x = drawChannelsMonitorLegend(dc, MENUS_MARGIN_LEFT, STR_MONITOR_OUTPUT_DESC, BARGRAPH1_COLOR); - drawChannelsMonitorLegend(dc, x, STR_MONITOR_MIXER_DESC, BARGRAPH2_COLOR); + x = drawChannelsMonitorLegend(dc, MENUS_MARGIN_LEFT, STR_MONITOR_OUTPUT_DESC, HIGHLIGHT_COLOR); + drawChannelsMonitorLegend(dc, x, STR_MONITOR_MIXER_DESC, FOCUS_BGCOLOR); } }; diff --git a/radio/src/gui/colorlcd/view_logical_switches.cpp b/radio/src/gui/colorlcd/view_logical_switches.cpp index d8c5afc3e..3b4810338 100644 --- a/radio/src/gui/colorlcd/view_logical_switches.cpp +++ b/radio/src/gui/colorlcd/view_logical_switches.cpp @@ -27,27 +27,30 @@ class LogicalSwitchDisplayButton : public TextButton { public: - LogicalSwitchDisplayButton(FormGroup * parent, const rect_t & rect, std::string text, unsigned index): - TextButton(parent, rect, std::move(text)), - index(index) - { - } + LogicalSwitchDisplayButton(FormGroup* parent, const rect_t& rect, + std::string text, unsigned index) : + TextButton(parent, rect, std::move(text), nullptr, OPAQUE), index(index) + { + setBgColorHandler([=]() -> LcdFlags { + if (getSwitch(SWSRC_SW1 + index)) return HIGHLIGHT_COLOR; + return FIELD_BGCOLOR; + }); + } - void checkEvents() override - { - bool newvalue = getSwitch(SWSRC_SW1 + index); - if (value != newvalue) { - if (newvalue) { - setTextFlags(FONT(BOLD) | HIGHLIGHT_COLOR); - } - else { - setTextFlags(FONT(STD) | DEFAULT_COLOR); - } - value = newvalue; - invalidate(); - } - Button::checkEvents(); - } + void checkEvents() override + { + bool newvalue = getSwitch(SWSRC_SW1 + index); + if (value != newvalue) { + if (newvalue) { + setTextFlags(FONT(BOLD) | DEFAULT_COLOR); + } else { + setTextFlags(FONT(STD) | DEFAULT_COLOR); + } + value = newvalue; + invalidate(); + } + Button::checkEvents(); + } protected: unsigned index = 0; @@ -61,21 +64,28 @@ void LogicalSwitchesViewPage::build(FormWindow * window) grid.spacer(PAGE_PADDING); grid.setLabelWidth(8); + // Footer + footer = new LogicalSwitchDisplayFooter( + window, {0, window->height() - LSW_VIEW_FOOTER_HEIGHT, window->width(), + LSW_VIEW_FOOTER_HEIGHT}); + // LSW table - std::string lsString ("LS64"); + std::string lsString("LS64"); lcdColorTable[CUSTOM_COLOR_INDEX] = RGB(160, 160, 160); for (uint8_t i = 0; i < MAX_LOGICAL_SWITCHES; i++) { + strAppendSigned(&lsString[1], i + 1, 2); - auto button = new LogicalSwitchDisplayButton(window, grid.getFieldSlot(8, i % 8), lsString, i); - button->setPressHandler([=]() { - footer->setIndex(i); - footer->invalidate(); + + auto button = new LogicalSwitchDisplayButton( + window, grid.getFieldSlot(8, i % 8), lsString, i); + + button->setFocusHandler([=](bool focus) { + if (focus) { + footer->setIndex(i); + footer->invalidate(); + } return 0; }); - if ((i + 1) % 8 == 0) - grid.nextLine(); + if ((i + 1) % 8 == 0) grid.nextLine(); } - - // Footer - footer = new LogicalSwitchDisplayFooter(window, {0,window->height() - LSW_VIEW_FOOTER_HEIGHT, window->width(), LSW_VIEW_FOOTER_HEIGHT}); } diff --git a/radio/src/gui/colorlcd/view_logical_switches.h b/radio/src/gui/colorlcd/view_logical_switches.h index f7aab6ecf..eb7785ffb 100644 --- a/radio/src/gui/colorlcd/view_logical_switches.h +++ b/radio/src/gui/colorlcd/view_logical_switches.h @@ -30,77 +30,76 @@ constexpr coord_t CSW_4TH_COLUMN = 280; constexpr coord_t CSW_5TH_COLUMN = 340; constexpr coord_t CSW_6TH_COLUMN = 390; -void putsEdgeDelayParam(BitmapBuffer * dc, coord_t x, coord_t y, LogicalSwitchData * ls, LcdFlags flags); +void putsEdgeDelayParam(BitmapBuffer* dc, coord_t x, coord_t y, + LogicalSwitchData* ls, LcdFlags flags); -class LogicalSwitchDisplayFooter: public Window { - public: - explicit LogicalSwitchDisplayFooter(Window * parent, rect_t rect): - Window(parent, rect, OPAQUE), - rect(rect) - { +class LogicalSwitchDisplayFooter : public Window +{ + public: + explicit LogicalSwitchDisplayFooter(Window* parent, rect_t rect) : + Window(parent, rect, OPAQUE), rect(rect) + { + } + + void setIndex(unsigned value) { index = value; } + + void paint(BitmapBuffer* dc) override + { + dc->clear(TITLE_BGCOLOR); + LogicalSwitchData* cs = lswAddress(index); + + LcdFlags textColor = FOCUS_COLOR; + dc->drawTextAtIndex(10, 1, STR_VCSWFUNC, cs->func, textColor); + // CSW params + unsigned int cstate = lswFamily(cs->func); + + if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { + drawSwitch(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); + drawSwitch(dc, CSW_3RD_COLUMN, 1, cs->v2, textColor); + } else if (cstate == LS_FAMILY_EDGE) { + drawSwitch(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); + putsEdgeDelayParam(dc, CSW_3RD_COLUMN, 1, cs, textColor); + } else if (cstate == LS_FAMILY_COMP) { + drawSource(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); + drawSource(dc, CSW_3RD_COLUMN, 1, cs->v2, textColor); + } else if (cstate == LS_FAMILY_TIMER) { + dc->drawNumber(CSW_2ND_COLUMN, 1, lswTimerValue(cs->v1), + LEFT | PREC1 | textColor); + dc->drawNumber(CSW_3RD_COLUMN, 1, lswTimerValue(cs->v2), + LEFT | PREC1 | textColor); + } else { + drawSource(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); + drawSourceCustomValue( + dc, CSW_3RD_COLUMN, 1, cs->v1, + cs->v1 <= MIXSRC_LAST_CH ? calc100toRESX(cs->v2) : cs->v2, + LEFT | textColor); } - void setIndex(unsigned value) - { - index = value; + // CSW AND switch + drawSwitch(dc, CSW_4TH_COLUMN, 1, cs->andsw, textColor); + + // CSW duration + if (cs->duration > 0) + dc->drawNumber(CSW_5TH_COLUMN, 1, cs->duration, + PREC1 | LEFT | textColor); + else + dc->drawTextAtIndex(CSW_5TH_COLUMN, 1, STR_MMMINV, 0, textColor); + + // CSW delay + if (cstate == LS_FAMILY_EDGE) { + dc->drawText(CSW_6TH_COLUMN, 1, STR_NA, textColor); + } else if (cs->delay > 0) { + dc->drawNumber(CSW_6TH_COLUMN, 1, cs->delay, PREC1 | LEFT | textColor); + } else { + dc->drawTextAtIndex(CSW_6TH_COLUMN, 1, STR_MMMINV, 0, textColor); } + } - void paint(BitmapBuffer * dc) override - { - dc->clear(TITLE_BGCOLOR); - LogicalSwitchData * cs = lswAddress(index); - dc->drawTextAtIndex(10, 1, STR_VCSWFUNC, cs->func, MENU_COLOR); - // CSW params - unsigned int cstate = lswFamily(cs->func); - - if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { - drawSwitch(dc, CSW_2ND_COLUMN, 1, cs->v1, MENU_COLOR); - drawSwitch(dc, CSW_3RD_COLUMN, 1, cs->v2, MENU_COLOR); - } - else if (cstate == LS_FAMILY_EDGE) { - drawSwitch(dc, CSW_2ND_COLUMN, 1, cs->v1, MENU_COLOR); - putsEdgeDelayParam(dc, CSW_3RD_COLUMN, 1, cs, MENU_COLOR); - } - else if (cstate == LS_FAMILY_COMP) { - drawSource(dc, CSW_2ND_COLUMN, 1, cs->v1, MENU_COLOR); - drawSource(dc, CSW_3RD_COLUMN, 1, cs->v2, MENU_COLOR); - } - else if (cstate == LS_FAMILY_TIMER) { - dc->drawNumber(CSW_2ND_COLUMN, 1, lswTimerValue(cs->v1), LEFT | PREC1 | MENU_COLOR); - dc->drawNumber(CSW_3RD_COLUMN, 1, lswTimerValue(cs->v2), LEFT | PREC1 | MENU_COLOR); - } - else { - drawSource(dc, CSW_2ND_COLUMN, 1, cs->v1, MENU_COLOR); - drawSourceCustomValue(dc, CSW_3RD_COLUMN, 1, cs->v1, cs->v1 <= MIXSRC_LAST_CH ? calc100toRESX(cs->v2) : cs->v2, LEFT | MENU_COLOR); - } - - // CSW AND switch - drawSwitch(dc, CSW_4TH_COLUMN, 1, cs->andsw, MENU_COLOR); - - // CSW duration - if (cs->duration > 0) - dc->drawNumber(CSW_5TH_COLUMN, 1, cs->duration, PREC1 | LEFT | MENU_COLOR); - else - dc->drawTextAtIndex(CSW_5TH_COLUMN, 1, STR_MMMINV, 0, MENU_COLOR); - - // CSW delay - if (cstate == LS_FAMILY_EDGE) { - dc->drawText(CSW_6TH_COLUMN, 1, STR_NA, MENU_COLOR); - } - else if (cs->delay > 0) { - dc->drawNumber(CSW_6TH_COLUMN, 1, cs->delay, PREC1 | LEFT | MENU_COLOR); - } - else { - dc->drawTextAtIndex(CSW_6TH_COLUMN, 1, STR_MMMINV, 0, MENU_COLOR); - } - } - - protected: - rect_t rect; - unsigned index = 0; + protected: + rect_t rect; + unsigned index = 0; }; - class LogicalSwitchesViewPage : public PageTab { public: From cd815232ee7b30e0e374951d1adb9dc6478ebb3a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 11:42:12 +0200 Subject: [PATCH 056/122] Facelift round 3 --- radio/src/gui/colorlcd/model_select.cpp | 496 ++++++++++----------- radio/src/gui/colorlcd/page.cpp | 4 +- radio/src/gui/colorlcd/page.h | 2 +- radio/src/gui/colorlcd/radio_sdmanager.cpp | 7 +- radio/src/gui/colorlcd/radio_tools.cpp | 5 +- radio/src/gui/colorlcd/tabsgroup.cpp | 4 +- radio/src/gui/colorlcd/tabsgroup.h | 2 +- radio/src/gui/colorlcd/widgets/text.cpp | 2 +- radio/src/storage/modelslist.h | 4 +- 9 files changed, 243 insertions(+), 283 deletions(-) diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index b0478ce90..1f15647f1 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -24,9 +24,23 @@ #include "storage/modelslist.h" #include "libopenui.h" -#define CATEGORIES_WIDTH 120 -#define MODELS_LEFT 123 -#define MODELS_COLUMN_WIDTH 174 +#if LCD_W > LCD_H +constexpr int MODEL_CELLS_PER_LINE = 3; +#else +constexpr int MODEL_CELLS_PER_LINE = 2; +#endif + +constexpr coord_t MODEL_CELL_PADDING = 6; + +constexpr coord_t MODEL_SELECT_CELL_WIDTH = + (LCD_W - (MODEL_CELLS_PER_LINE + 1) * MODEL_CELL_PADDING) / + MODEL_CELLS_PER_LINE; + +constexpr coord_t MODEL_SELECT_CELL_HEIGHT = 92; + +constexpr coord_t MODEL_IMAGE_WIDTH = MODEL_SELECT_CELL_WIDTH; +constexpr coord_t MODEL_IMAGE_HEIGHT = 72; + enum ModelSelectMode { MODE_SELECT_MODEL, @@ -66,310 +80,254 @@ void setCurrentCategory(unsigned int index) } #endif -class ModelButton: public Button { - public: - ModelButton(FormGroup * parent, const rect_t & rect, ModelCell * modelCell, Window * footer) : - Button(parent, rect), - modelCell(modelCell) - { - load(); - } +class ModelButton : public Button +{ + public: + ModelButton(FormGroup *parent, const rect_t &rect, ModelCell *modelCell) : + Button(parent, rect), modelCell(modelCell) + { + load(); + } - void load() - { - uint8_t version; + void load() + { + uint8_t version; - PACK(struct { - ModelHeader header; - TimerData timers[MAX_TIMERS]; - }) partialModel; - const char * error = nullptr; + PACK(struct { + ModelHeader header; + TimerData timers[MAX_TIMERS]; + }) + partialModel; + const char *error = nullptr; - if (strncmp(modelCell->modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) == 0) { - memcpy(&partialModel.header, &g_model.header, sizeof(partialModel)); - } - else { - error = readModel(modelCell->modelFilename, (uint8_t *)&partialModel.header, sizeof(partialModel), &version); - if (!error) { - if (modelCell->modelName[0] == '\0' - && partialModel.header.name[0] != '\0') { - modelCell->setModelName(partialModel.header.name); - } + if (strncmp(modelCell->modelFilename, g_eeGeneral.currModelFilename, + LEN_MODEL_FILENAME) == 0) { + memcpy(&partialModel.header, &g_model.header, sizeof(partialModel)); + } else { + error = + readModel(modelCell->modelFilename, (uint8_t *)&partialModel.header, + sizeof(partialModel), &version); + if (!error) { + if (modelCell->modelName[0] == '\0' && + partialModel.header.name[0] != '\0') { + modelCell->setModelName(partialModel.header.name); } } - - buffer = new BitmapBuffer(BMP_RGB565, MODELCELL_WIDTH, MODELCELL_HEIGHT); - if (buffer == nullptr) { - return; - } - buffer->clear(DEFAULT_BGCOLOR); - - if (error) { - buffer->drawText(5, 2, "(Invalid Model)", DEFAULT_COLOR); - buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, DEFAULT_COLOR); - } - else { - char timerName[LEN_TIMER_STRING]; - buffer->drawSizedText(5, 2, modelCell->modelName, LEN_MODEL_NAME, FONT(XS) | DEFAULT_COLOR); - getTimerString(timerName, 0); - for (auto & timer : partialModel.timers) { - if (timer.mode > 0 && timer.persistent) { - getTimerString(timerName, timer.value); - break; - } - } - buffer->drawText(101, 40, timerName, DEFAULT_COLOR); - for (int i = 0; i < 4; i++) { - buffer->drawBitmapPattern(104+i*11, 25, LBM_SCORE0, TITLE_BGCOLOR); - } - GET_FILENAME(filename, BITMAPS_PATH, partialModel.header.bitmap, ""); - const BitmapBuffer * bitmap = BitmapBuffer::loadBitmap(filename); - if (bitmap) { - buffer->drawScaledBitmap(bitmap, 5, 24, 56, 32); - delete bitmap; - } - else { - buffer->drawBitmapPattern(5, 23, LBM_LIBRARY_SLOT, DEFAULT_COLOR); - } - } - buffer->drawSolidHorizontalLine(5, 19, 143, LINE_COLOR); - } - - void paint(BitmapBuffer * dc) override - { - FormField::paint(dc); - dc->drawBitmap(10, 2, buffer); - if (modelCell == modelslist.getCurrentModel()) { - dc->drawBitmapPattern(112, 71, LBM_ACTIVE_MODEL, TITLE_BGCOLOR); - } } - const char * modelFilename() - { - return modelCell->modelFilename; + buffer = new BitmapBuffer(BMP_RGB565, width(), height()); + if (buffer == nullptr) { + return; + } + buffer->clear(FIELD_BGCOLOR); + + if (error) { + buffer->drawText(width() / 2, 2, "(Invalid Model)", + DEFAULT_COLOR | CENTERED); + } else { + buffer->drawSizedText(width() / 2, 2, modelCell->modelName, + LEN_MODEL_NAME, + DEFAULT_COLOR | CENTERED); + + GET_FILENAME(filename, BITMAPS_PATH, partialModel.header.bitmap, ""); + const BitmapBuffer *bitmap = BitmapBuffer::loadBitmap(filename); + if (bitmap) { + buffer->drawScaledBitmap(bitmap, 0, 0, width(), height()); + delete bitmap; + } else { + buffer->drawText(width() / 2, 56, "(No Picture)", + FONT(XXS) | DEFAULT_COLOR | CENTERED); + } + } + } + + void paint(BitmapBuffer *dc) override + { + FormField::paint(dc); + dc->drawBitmap(0, 0, buffer); + if (modelCell == modelslist.getCurrentModel()) { + dc->drawSolidFilledRect(0, 0, width(), 20, HIGHLIGHT_COLOR); + dc->drawSizedText(width() / 2, 2, modelCell->modelName, + LEN_MODEL_NAME, + DEFAULT_COLOR | CENTERED); + } else if (hasFocus()) { + dc->drawFilledRect(0, 0, width(), 20, SOLID, FOCUS_BGCOLOR, 5); + dc->drawSizedText(width() / 2, 2, modelCell->modelName, + LEN_MODEL_NAME, + FOCUS_COLOR | CENTERED); } - protected: - ModelCell * modelCell; - BitmapBuffer * buffer = nullptr; + if (!hasFocus()) { + dc->drawSolidRect(0, 0, width(), height(), 1, FIELD_FRAME_COLOR); + } else { + dc->drawSolidRect(0, 0, width(), height(), 2, FOCUS_BGCOLOR); + } + } + + const char *modelFilename() { return modelCell->modelFilename; } + + protected: + ModelCell *modelCell; + BitmapBuffer *buffer = nullptr; }; -class ModelSelectFooter: public Window { - public: - explicit ModelSelectFooter(Window * parent): - Window(parent, {0, parent->height() - MODEL_SELECT_FOOTER_HEIGHT, LCD_W, MODEL_SELECT_FOOTER_HEIGHT}, OPAQUE) - { - } +class ModelCategoryPageBody : public FormWindow +{ + public: + ModelCategoryPageBody(FormWindow *parent, const rect_t &rect, + ModelsCategory *category) : + FormWindow(parent, rect, FORM_FORWARD_FOCUS), category(category) + { + update(); + } - void setCurrentModel(ModelCell * model) - { - currentModel = model; - invalidate(); - } + void update(int selected = 0) + { + clear(); - void paint(BitmapBuffer * dc) override - { - dc->drawSolidFilledRect(0, 0, width(), height(), DISABLE_COLOR); - uint32_t size = sdGetSize() / 100; - coord_t x = 7; - if (modelselSdFreeBitmap) { - dc->drawMask(7, 4, modelselSdFreeBitmap, DEFAULT_COLOR); - x += modelselSdFreeBitmap->width() + 3; - } - x = dc->drawNumber(x, 3, size, PREC1|FONT(XS), 0, nullptr, "GB"); - x += 20; - if (modelselModelQtyBitmap) { - dc->drawMask(x, 4, modelselModelQtyBitmap, DEFAULT_COLOR); - x += modelselModelQtyBitmap->width() + 3; - } - x = dc->drawNumber(x, 3, modelslist.getModelsCount(), FONT(XS)); - if (currentModel) { - x += 20; - if (modelselModelNameBitmap) { - dc->drawMask(x, 4, modelselModelNameBitmap, DEFAULT_COLOR); - x += modelselModelNameBitmap->width() + 3; - } - dc->drawText(x, 3, currentModel->modelFilename, FONT(XS) | DEFAULT_COLOR); - } - } + int index = 0; + coord_t y = MODEL_CELL_PADDING; + coord_t x = MODEL_CELL_PADDING; - protected: - ModelCell * currentModel = nullptr; -}; + for (auto &model : *category) { + auto button = new ModelButton( + this, {x, y, MODEL_SELECT_CELL_WIDTH, MODEL_SELECT_CELL_HEIGHT}, + model); + button->setPressHandler([=]() -> uint8_t { + if (button->hasFocus()) { + Menu *menu = new Menu(parent); + if (model != modelslist.getCurrentModel()) { + menu->addLine(STR_SELECT_MODEL, [=]() { + // we store the latest changes if any + storageFlushCurrentModel(); + storageCheck(true); + memcpy(g_eeGeneral.currModelFilename, model->modelFilename, + LEN_MODEL_FILENAME); + loadModel(g_eeGeneral.currModelFilename, false); + storageDirty(EE_GENERAL); + storageCheck(true); -#if LCD_W > LCD_H -constexpr coord_t MODEL_SELECT_CELL_WIDTH = (LCD_W - 3 * PAGE_PADDING) / 2; -#else -constexpr coord_t MODEL_SELECT_CELL_WIDTH = LCD_W - 2 * PAGE_PADDING; -#endif -constexpr coord_t MODEL_SELECT_CELL_HEIGHT = 94; - -class ModelCategoryPageBody: public FormWindow { - public: - ModelCategoryPageBody(FormWindow * parent, const rect_t & rect, ModelsCategory * category, ModelSelectFooter * footer): - FormWindow(parent, rect, FORM_FORWARD_FOCUS), - category(category), - footer(footer) - { - update(); - } - - void update(int selected = 0) - { - clear(); - - int index = 0; - coord_t y = PAGE_PADDING; - coord_t x = PAGE_PADDING; - coord_t h = PAGE_PADDING; - for (auto & model: * category) { - auto button = new ModelButton(this, {x, y, MODEL_SELECT_CELL_WIDTH, MODEL_SELECT_CELL_HEIGHT}, model, nullptr); - - button->setFocusHandler([=](bool active) { - if (active) { - footer->setCurrentModel(model); + modelslist.setCurrentModel(model); + update(); // modelslist.getModelIndex(modelCell)); + }); } - }); - - button->setPressHandler([=]() -> uint8_t { - if (button->hasFocus()) { - Menu * menu = new Menu(parent); - if (model != modelslist.getCurrentModel()) { - menu->addLine(STR_SELECT_MODEL, [=]() { - // we store the latest changes if any - storageFlushCurrentModel(); - storageCheck(true); - memcpy(g_eeGeneral.currModelFilename, model->modelFilename, LEN_MODEL_FILENAME); - loadModel(g_eeGeneral.currModelFilename, false); - storageDirty(EE_GENERAL); - storageCheck(true); - - modelslist.setCurrentModel(model); - update(); // modelslist.getModelIndex(modelCell)); - }); - } - menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); - menu->addLine(STR_DUPLICATE_MODEL, [=]() { - char duplicatedFilename[LEN_MODEL_FILENAME + 1]; - memcpy(duplicatedFilename, model->modelFilename, sizeof(duplicatedFilename)); - if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, MODELS_PATH)) { - sdCopyFile(model->modelFilename, MODELS_PATH, duplicatedFilename, MODELS_PATH); - modelslist.addModel(category, duplicatedFilename); - update(index); - } - else { - POPUP_WARNING("Invalid File"); - } - }); - // menu->addLine(STR_MOVE_MODEL); - if (model != modelslist.getCurrentModel()) { - menu->addLine(STR_DELETE_MODEL, [=]() { - new ConfirmDialog(parent, STR_DELETE_MODEL, std::string(model->modelName, sizeof(model->modelName)).c_str(), [=] { - modelslist.removeModel(category, model); - update(index > 0 ? index - 1 : 0); + menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); + menu->addLine(STR_DUPLICATE_MODEL, [=]() { + char duplicatedFilename[LEN_MODEL_FILENAME + 1]; + memcpy(duplicatedFilename, model->modelFilename, + sizeof(duplicatedFilename)); + if (findNextFileIndex(duplicatedFilename, LEN_MODEL_FILENAME, + MODELS_PATH)) { + sdCopyFile(model->modelFilename, MODELS_PATH, duplicatedFilename, + MODELS_PATH); + modelslist.addModel(category, duplicatedFilename); + update(index); + } else { + POPUP_WARNING("Invalid File"); + } + }); + // menu->addLine(STR_MOVE_MODEL); + if (model != modelslist.getCurrentModel()) { + menu->addLine(STR_DELETE_MODEL, [=]() { + new ConfirmDialog( + parent, STR_DELETE_MODEL, + std::string(model->modelName, sizeof(model->modelName)) + .c_str(), + [=] { + modelslist.removeModel(category, model); + update(index > 0 ? index - 1 : 0); }); - }); - } - } - else { - button->setFocus(SET_FOCUS_DEFAULT); - } - return 1; - }); - - if (selected == index) { + }); + } + } else { button->setFocus(SET_FOCUS_DEFAULT); - footer->setCurrentModel(model); } + return 1; + }); + + if (selected == index) { + button->setFocus(SET_FOCUS_DEFAULT); + } + + index++; #if LCD_W > LCD_H - if (index % 2 == 0) { - x = PAGE_PADDING + MODEL_SELECT_CELL_WIDTH + PAGE_PADDING; - h += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING; - } - else { - x = PAGE_PADDING; - y += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING; - } + if (index % MODEL_CELLS_PER_LINE == 0) { + x = MODEL_CELL_PADDING; + y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING; + } else { + x += MODEL_CELL_PADDING + MODEL_SELECT_CELL_WIDTH; + } #else - h += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING; - y += MODEL_SELECT_CELL_HEIGHT + PAGE_PADDING; + y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING; #endif - - index++; - } - - setInnerHeight(h); - - if (category->empty()) { - setFocus(); - } } + y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING; + setInnerHeight(y); + + if (category->empty()) { + setFocus(); + } + } + #if defined(HARDWARE_KEYS) - void onEvent(event_t event) override - { - if (event == EVT_KEY_BREAK(KEY_ENTER)) { - Menu * menu = new Menu(this); - menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); - //TODO: create category? - } - else { - FormWindow::onEvent(event); - } + void onEvent(event_t event) override + { + if (event == EVT_KEY_BREAK(KEY_ENTER)) { + Menu *menu = new Menu(this); + menu->addLine(STR_CREATE_MODEL, getCreateModelAction()); + // TODO: create category? + } else { + FormWindow::onEvent(event); } + } #endif - void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window * from = nullptr) override - { - if (category->empty()) { - // use Window::setFocus() to avoid forwarding focus to nowhere - // this crashes currently in libopenui - Window::setFocus(flag, from); - } - else { - FormWindow::setFocus(flag, from); - } + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, + Window *from = nullptr) override + { + if (category->empty()) { + // use Window::setFocus() to avoid forwarding focus to nowhere + // this crashes currently in libopenui + Window::setFocus(flag, from); + } else { + FormWindow::setFocus(flag, from); } + } - - protected: - ModelsCategory * category; - ModelSelectFooter * footer; - - std::function getCreateModelAction() - { - return [=]() { - storageCheck(true); - modelslist.setCurrentModel(modelslist.addModel(category, createModel())); -#if defined(LUA) - // chainMenu(menuModelWizard); -#endif - update(category->size() - 1); - }; - } + protected: + ModelsCategory *category; + std::function getCreateModelAction() + { + return [=]() { + storageCheck(true); + modelslist.setCurrentModel(modelslist.addModel(category, createModel())); + update(category->size() - 1); + }; + } }; -class ModelCategoryPage: public PageTab { - public: - explicit ModelCategoryPage(ModelsCategory * category) : - PageTab(category->name, ICON_MODEL), - category(category) - { - } +class ModelCategoryPage : public PageTab +{ + public: + explicit ModelCategoryPage(ModelsCategory *category) : + PageTab(category->name, ICON_MODEL), category(category) + { + } - protected: - ModelsCategory * category; + protected: + ModelsCategory *category; - void build(FormWindow * window) override - { - auto footer = new ModelSelectFooter(window); - new ModelCategoryPageBody(window, {0, 0, LCD_W, window->height() - MODEL_SELECT_FOOTER_HEIGHT}, category, footer); - } + void build(FormWindow *window) override + { + new ModelCategoryPageBody( + window, {0, 0, LCD_W, window->height()}, + category); + } }; - ModelSelectMenu::ModelSelectMenu(): TabsGroup(ICON_MODEL) { diff --git a/radio/src/gui/colorlcd/page.cpp b/radio/src/gui/colorlcd/page.cpp index fa4084b98..91d7e3704 100644 --- a/radio/src/gui/colorlcd/page.cpp +++ b/radio/src/gui/colorlcd/page.cpp @@ -26,11 +26,11 @@ PageHeader::PageHeader(Page * parent, uint8_t icon): FormGroup(parent, { 0, 0, LCD_W, MENU_HEADER_HEIGHT }, OPAQUE), icon(icon) #if defined(HARDWARE_TOUCH) - , back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK, + , back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, [=]() -> uint8_t { parent->deleteLater(); return 0; - }, NO_FOCUS) + }, NO_FOCUS | FORM_NO_BORDER) #endif { } diff --git a/radio/src/gui/colorlcd/page.h b/radio/src/gui/colorlcd/page.h index 212993575..4b2afc505 100644 --- a/radio/src/gui/colorlcd/page.h +++ b/radio/src/gui/colorlcd/page.h @@ -51,7 +51,7 @@ class PageHeader: public FormGroup protected: uint8_t icon; #if defined(HARDWARE_TOUCH) - IconButton back; + Button back; #endif }; diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index 3e9227cb0..2cdcccad1 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -164,14 +164,15 @@ void RadioSdManagerPage::build(FormWindow * window) // sort directories and files directories.sort(compare_nocase); files.sort(compare_nocase); - + for (auto name: directories) { - new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t { + auto b = new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t { f_chdir(name.data()); window->clear(); build(window); return 0; - }, 0); + }, OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); grid.nextLine(); } diff --git a/radio/src/gui/colorlcd/radio_tools.cpp b/radio/src/gui/colorlcd/radio_tools.cpp index d3be72812..7cfc8e619 100644 --- a/radio/src/gui/colorlcd/radio_tools.cpp +++ b/radio/src/gui/colorlcd/radio_tools.cpp @@ -115,7 +115,7 @@ void RadioToolsPage::rebuild(FormWindow * window) new StaticText(window, grid.getLabelSlot(), "lua", BUTTON_BACKGROUND, CENTERED); std::string path_str(path); - new TextButton( + auto b = new TextButton( window, grid.getFieldSlot(1), label, [window, path_str]() -> uint8_t { f_chdir("/SCRIPTS/TOOLS/"); @@ -124,7 +124,8 @@ void RadioToolsPage::rebuild(FormWindow * window) StandaloneLuaWindow::instance()->attach(window); return 0; }, - 0); + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); grid.nextLine(); } } diff --git a/radio/src/gui/colorlcd/tabsgroup.cpp b/radio/src/gui/colorlcd/tabsgroup.cpp index 48ee09042..a0b7c32a8 100644 --- a/radio/src/gui/colorlcd/tabsgroup.cpp +++ b/radio/src/gui/colorlcd/tabsgroup.cpp @@ -32,12 +32,12 @@ TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon): FormGroup(parent, { 0, 0, LCD_W, MENU_BODY_TOP }, OPAQUE), #if defined(HARDWARE_TOUCH) - back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, ICON_BACK, + back(this, { 0, 0, MENU_HEADER_BUTTON_WIDTH, MENU_HEADER_BUTTON_WIDTH }, [=]() -> uint8_t { parent->deleteLater(); ViewMain::instance()->setFocus((SET_FOCUS_DEFAULT)); return 1; - }, NO_FOCUS), + }, NO_FOCUS | FORM_NO_BORDER), #endif icon(icon), carousel(this, parent) diff --git a/radio/src/gui/colorlcd/tabsgroup.h b/radio/src/gui/colorlcd/tabsgroup.h index a985c111d..d3af8c15d 100644 --- a/radio/src/gui/colorlcd/tabsgroup.h +++ b/radio/src/gui/colorlcd/tabsgroup.h @@ -155,7 +155,7 @@ class TabsGroupHeader: public FormGroup { protected: #if defined(HARDWARE_TOUCH) - IconButton back; + Button back; #endif uint8_t icon; TabsCarousel carousel; diff --git a/radio/src/gui/colorlcd/widgets/text.cpp b/radio/src/gui/colorlcd/widgets/text.cpp index cbc1f3db3..857fede08 100644 --- a/radio/src/gui/colorlcd/widgets/text.cpp +++ b/radio/src/gui/colorlcd/widgets/text.cpp @@ -53,7 +53,7 @@ class TextWidget: public Widget const ZoneOption TextWidget::options[] = { { STR_TEXT, ZoneOption::String, OPTION_VALUE_STRING(TEXT_WIDGET_DEFAULT_LABEL) }, - { STR_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(RED) }, + { STR_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(DEFAULT_COLOR>>16) }, { STR_SIZE, ZoneOption::TextSize, OPTION_VALUE_UNSIGNED(0) }, { STR_SHADOW, ZoneOption::Bool, OPTION_VALUE_BOOL(false) }, { nullptr, ZoneOption::Bool } diff --git a/radio/src/storage/modelslist.h b/radio/src/storage/modelslist.h index 07d1f7af6..f9bde8302 100644 --- a/radio/src/storage/modelslist.h +++ b/radio/src/storage/modelslist.h @@ -29,8 +29,8 @@ #include "sdcard_raw.h" #endif -#define MODELCELL_WIDTH 172 -#define MODELCELL_HEIGHT 59 +// #define MODELCELL_WIDTH 172 +// #define MODELCELL_HEIGHT 59 // modelXXXXXXX.bin F,FF F,3F,FF\r\n #define LEN_MODELS_IDX_LINE (LEN_MODEL_FILENAME + sizeof(" F,FF F,3F,FF\r\n")-1) From a4a816720fe2cbadeb4e7142598c3653ed244239 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 12:28:11 +0200 Subject: [PATCH 057/122] Some more fixes / tweaking --- radio/src/gui/colorlcd/model_flightmodes.cpp | 3 ++ radio/src/gui/colorlcd/model_gvars.cpp | 33 ++++++++++++------- .../gui/colorlcd/model_logical_switches.cpp | 13 ++++++-- radio/src/gui/colorlcd/model_select.cpp | 19 +++++++---- 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/radio/src/gui/colorlcd/model_flightmodes.cpp b/radio/src/gui/colorlcd/model_flightmodes.cpp index 1298828d4..26bcfc789 100644 --- a/radio/src/gui/colorlcd/model_flightmodes.cpp +++ b/radio/src/gui/colorlcd/model_flightmodes.cpp @@ -58,6 +58,9 @@ class FlightModeGroup: public FormGroup if (index == getFlightMode()) { dc->drawSolidFilledRect(0, 0, width(), height(), HIGHLIGHT_COLOR); } + else { + dc->drawSolidFilledRect(0, 0, width(), height(), FIELD_BGCOLOR); + } FormGroup::paint(dc); } diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index e810d85ed..ba123f7c7 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -64,7 +64,7 @@ void GVarButton::paint(BitmapBuffer * dc) currentFlightMode = getFlightMode(); gvarSum = 0; - dc->drawSolidFilledRect(0, 0, nameRectW, rect.h, CURVE_AXIS_COLOR); + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getGVarString(gvarIdx), 0); dc->drawSizedText(x, y, gvar->name, LEN_GVAR_NAME, 0); @@ -91,7 +91,8 @@ void GVarButton::paint(BitmapBuffer * dc) } if (!bgFilled) { - dc->drawSolidFilledRect(startX, y, width() - startX, PAGE_LINE_HEIGHT, CURVE_AXIS_COLOR); + dc->drawSolidFilledRect(startX, y, width() - startX, PAGE_LINE_HEIGHT, + FIELD_FRAME_COLOR); bgFilled = true; } @@ -116,7 +117,10 @@ void GVarButton::paint(BitmapBuffer * dc) } } - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? FOCUS_BGCOLOR : DISABLE_COLOR); + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FIELD_FRAME_COLOR); } void GVarButton::drawFlightMode(BitmapBuffer * dc, coord_t x, coord_t y, int fm, LcdFlags attr) @@ -131,17 +135,17 @@ void GVarRenderer::paint(BitmapBuffer * dc) lastFlightMode = getFlightMode(); FlightModeData * fmData = &g_model.flightModeData[lastFlightMode]; lastGVar = fmData->gvars[index]; - coord_t x = drawStringWithIndex(dc, 0, FIELD_PADDING_TOP, STR_GV, index + 1, MENU_COLOR, nullptr, "="); + coord_t x = drawStringWithIndex(dc, 0, FIELD_PADDING_TOP, STR_GV, index + 1, + DEFAULT_COLOR, nullptr, "="); if (lastGVar > GVAR_MAX) { uint8_t fm = lastGVar - GVAR_MAX - 1; if (fm >= lastFlightMode) fm++; char label[16]; getFlightModeString(label, fm + 1); - dc->drawSizedText(x, FIELD_PADDING_TOP, label, strlen(label), MENU_COLOR); - } - else { - drawGVarValue(dc, x, FIELD_PADDING_TOP, index, lastGVar, MENU_COLOR); + dc->drawSizedText(x, FIELD_PADDING_TOP, label, strlen(label), DEFAULT_COLOR); + } else { + drawGVarValue(dc, x, FIELD_PADDING_TOP, index, lastGVar, DEFAULT_COLOR); } } @@ -167,8 +171,15 @@ bool GVarRenderer::isUpdated() void GVarEditWindow::buildHeader(Window * window) { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_GLOBAL_VAR, 0, MENU_COLOR); - gVarInHeader = new GVarRenderer(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, index); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_GLOBAL_VAR, 0, DEFAULT_COLOR); + gVarInHeader = + new GVarRenderer(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + index); } void GVarEditWindow::checkEvents() @@ -357,4 +368,4 @@ void ModelGVarsPage::build(FormWindow * window) grid.nextLine(button->height()); } window->setInnerHeight(grid.getWindowHeight()); -} \ No newline at end of file +} diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp index 4d3e6aa5e..bae954375 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -73,8 +73,15 @@ class LogicalSwitchEditPage: public Page void buildHeader(Window * window) { - new StaticText(window, { PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT }, STR_MENULOGICALSWITCHES, 0, MENU_COLOR); - headerSwitchName = new StaticText(window, { PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT }, getSwitchPositionName(SWSRC_SW1 + index), 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENULOGICALSWITCHES, 0, MENU_COLOR); + headerSwitchName = new StaticText( + window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSwitchPositionName(SWSRC_SW1 + index), 0, MENU_COLOR); } void updateLogicalSwitchOneWindow() @@ -311,6 +318,8 @@ class LogicalSwitchButton : public Button { { if (active) dc->drawSolidFilledRect(2, 2, rect.w-4, rect.h-4, HIGHLIGHT_COLOR); + else + dc->drawSolidFilledRect(2, 2, rect.w-4, rect.h-4, FIELD_BGCOLOR); paintLogicalSwitchLine(dc); diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 1f15647f1..38d38e19e 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -125,10 +125,6 @@ class ModelButton : public Button buffer->drawText(width() / 2, 2, "(Invalid Model)", DEFAULT_COLOR | CENTERED); } else { - buffer->drawSizedText(width() / 2, 2, modelCell->modelName, - LEN_MODEL_NAME, - DEFAULT_COLOR | CENTERED); - GET_FILENAME(filename, BITMAPS_PATH, partialModel.header.bitmap, ""); const BitmapBuffer *bitmap = BitmapBuffer::loadBitmap(filename); if (bitmap) { @@ -150,11 +146,20 @@ class ModelButton : public Button dc->drawSizedText(width() / 2, 2, modelCell->modelName, LEN_MODEL_NAME, DEFAULT_COLOR | CENTERED); - } else if (hasFocus()) { - dc->drawFilledRect(0, 0, width(), 20, SOLID, FOCUS_BGCOLOR, 5); + } else { + LcdFlags textColor; + if (hasFocus()) { + dc->drawFilledRect(0, 0, width(), 20, SOLID, FOCUS_BGCOLOR, 5); + textColor = FOCUS_COLOR; + } + else { + dc->drawFilledRect(0, 0, width(), 20, SOLID, COLOR2FLAGS(WHITE)/*FIELD_BGCOLOR*/, 5); + textColor = DEFAULT_COLOR; + } + dc->drawSizedText(width() / 2, 2, modelCell->modelName, LEN_MODEL_NAME, - FOCUS_COLOR | CENTERED); + textColor | CENTERED); } if (!hasFocus()) { From cf1d83351c04e4b5d487240b99a8fd8deb5aa8c8 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 12:29:55 +0200 Subject: [PATCH 058/122] Try without focus marker on model name --- radio/src/gui/colorlcd/model_select.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 38d38e19e..e26ed184a 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -148,14 +148,14 @@ class ModelButton : public Button DEFAULT_COLOR | CENTERED); } else { LcdFlags textColor; - if (hasFocus()) { - dc->drawFilledRect(0, 0, width(), 20, SOLID, FOCUS_BGCOLOR, 5); - textColor = FOCUS_COLOR; - } - else { - dc->drawFilledRect(0, 0, width(), 20, SOLID, COLOR2FLAGS(WHITE)/*FIELD_BGCOLOR*/, 5); + // if (hasFocus()) { + // dc->drawFilledRect(0, 0, width(), 20, SOLID, FOCUS_BGCOLOR, 5); + // textColor = FOCUS_COLOR; + // } + // else { + dc->drawFilledRect(0, 0, width(), 20, SOLID, FIELD_BGCOLOR, 5); textColor = DEFAULT_COLOR; - } + // } dc->drawSizedText(width() / 2, 2, modelCell->modelName, LEN_MODEL_NAME, From 490cf003a30a8470e5cb45851d38b9eee5f87b5d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 18:14:56 +0200 Subject: [PATCH 059/122] Facelift round 4 --- radio/src/gui/colorlcd/model_curves.cpp | 17 +- radio/src/gui/colorlcd/model_curves.h | 2 +- radio/src/gui/colorlcd/model_gvars.cpp | 15 +- radio/src/gui/colorlcd/model_inputs.cpp | 167 ++-- .../gui/colorlcd/model_logical_switches.cpp | 225 ++--- .../src/gui/colorlcd/model_logical_switches.h | 2 +- radio/src/gui/colorlcd/model_mixes.cpp | 452 ++++----- radio/src/gui/colorlcd/model_outputs.cpp | 30 +- radio/src/gui/colorlcd/model_outputs.h | 2 +- radio/src/gui/colorlcd/special_functions.cpp | 875 ++++++++++-------- radio/src/gui/colorlcd/special_functions.h | 4 - 11 files changed, 992 insertions(+), 799 deletions(-) diff --git a/radio/src/gui/colorlcd/model_curves.cpp b/radio/src/gui/colorlcd/model_curves.cpp index 29289cbe2..c8a39ef02 100644 --- a/radio/src/gui/colorlcd/model_curves.cpp +++ b/radio/src/gui/colorlcd/model_curves.cpp @@ -309,10 +309,9 @@ void ModelCurvesPage::build(FormWindow * window, int8_t focusIndex) if (isCurveUsed(index)) { // Curve label - auto text = + auto txt = new StaticText(window, grid.getLabelSlot(), getCurveString(1 + index), BUTTON_BACKGROUND, CENTERED); - text->setBackgroundColor(FOCUS_COLOR); // Curve drawing Button * button = new CurveButton(window, grid.getFieldSlot(), index); @@ -334,11 +333,25 @@ void ModelCurvesPage::build(FormWindow * window, int8_t focusIndex) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); if (focusIndex == index) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } else { auto button = new TextButton(window, grid.getLabelSlot(), diff --git a/radio/src/gui/colorlcd/model_curves.h b/radio/src/gui/colorlcd/model_curves.h index a747b50e6..52fe9b1f4 100644 --- a/radio/src/gui/colorlcd/model_curves.h +++ b/radio/src/gui/colorlcd/model_curves.h @@ -30,7 +30,7 @@ class ModelCurvesPage: public PageTab { virtual void build(FormWindow * window) override { - build(window, -1); + build(window, 0); } protected: diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index ba123f7c7..2a6aaaf74 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -64,9 +64,18 @@ void GVarButton::paint(BitmapBuffer * dc) currentFlightMode = getFlightMode(); gvarSum = 0; - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getGVarString(gvarIdx), 0); - dc->drawSizedText(x, y, gvar->name, LEN_GVAR_NAME, 0); + LcdFlags bgColor = FIELD_BGCOLOR; + LcdFlags textColor = 0; + if (hasFocus()) { + bgColor = FOCUS_BGCOLOR; + textColor = FOCUS_COLOR; + } + + dc->drawSolidFilledRect(0, 0, nameRectW, rect.h, bgColor); + dc->drawSolidFilledRect(nameRectW, 0, rect.w - nameRectW, rect.h, FIELD_BGCOLOR); + + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getGVarString(gvarIdx), textColor); + dc->drawSizedText(x, y, gvar->name, LEN_GVAR_NAME, textColor); // values are right aligned x += GVAR_NAME_SIZE; diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp index 8d43f32fd..da4f58ba1 100644 --- a/radio/src/gui/colorlcd/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -327,66 +327,67 @@ void CommonInputOrMixButton::drawFlightModes(BitmapBuffer *dc, void CommonInputOrMixButton::paint(BitmapBuffer * dc) { dc->drawSolidFilledRect(0, 0, width(), height(), - hasFocus() ? FOCUS_BGCOLOR : FIELD_BGCOLOR); + /*hasFocus() ? FOCUS_BGCOLOR :*/ FIELD_BGCOLOR); paintBody(dc); + if (!hasFocus()) - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FIELD_FRAME_COLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); } -class InputLineButton : public CommonInputOrMixButton { - public: - InputLineButton(FormGroup * parent, const rect_t & rect, uint8_t index): +class InputLineButton : public CommonInputOrMixButton +{ + public: + InputLineButton(FormGroup *parent, const rect_t &rect, uint8_t index) : CommonInputOrMixButton(parent, rect, index) - { - const ExpoData & line = g_model.expoData[index]; - if (line.swtch || line.curve.value != 0 || line.flightModes) { - setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); - } + { + const ExpoData &line = g_model.expoData[index]; + if (line.swtch || line.curve.value != 0 || line.flightModes) { + setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); + } + } + + bool isActive() const override { return isExpoActive(index); } + + void paintBody(BitmapBuffer *dc) override + { + const ExpoData &line = g_model.expoData[index]; + + LcdFlags textColor = DEFAULT_COLOR; + // if (hasFocus()) + // textColor = FOCUS_COLOR; + + // first line ... + drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, + -100, 100, textColor); + drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); + + if (line.name[0]) { + dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); + dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), + textColor); } - bool isActive() const override - { - return isExpoActive(index); + // second line ... + if (line.swtch) { + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupSwitchIcon, textColor); + drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + textColor); } - void paintBody(BitmapBuffer * dc) override - { - const ExpoData & line = g_model.expoData[index]; - - LcdFlags textColor = DEFAULT_COLOR; - if (hasFocus()) - textColor = FOCUS_COLOR; - - // first line ... - drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, - -100, 100, textColor); - drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); - - if (line.name[0]) { - dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); - dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), - textColor); - } - - // second line ... - if (line.swtch) { - dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, - mixerSetupSwitchIcon, textColor); - drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + if (line.curve.value != 0) { + dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupCurveIcon, textColor); + drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, textColor); - } - - if (line.curve.value != 0 ) { - dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, - mixerSetupCurveIcon, textColor); - drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, - textColor); - } - - if (line.flightModes) { - drawFlightModes(dc, line.flightModes, textColor); - } } + + if (line.flightModes) { + drawFlightModes(dc, line.flightModes, textColor); + } + } }; ModelInputsPage::ModelInputsPage(): @@ -411,31 +412,29 @@ void ModelInputsPage::editInput(FormWindow * window, uint8_t input, uint8_t inde }); } -void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) +void ModelInputsPage::build(FormWindow *window, int8_t focusIndex) { FormGridLayout grid; grid.spacer(PAGE_PADDING); grid.setLabelWidth(66); int inputIndex = 0; - ExpoData * line = g_model.expoData; + ExpoData *line = g_model.expoData; for (uint8_t input = 0; input < MAX_INPUTS; input++) { if (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) { + coord_t h = grid.getWindowHeight(); auto txt = new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_FIRST_INPUT + input), - 0, CENTERED); - txt->setBackgroundColor(FIELD_BGCOLOR); + BUTTON_BACKGROUND, CENTERED); while (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) { - Button * button = new InputLineButton(window, grid.getFieldSlot(), inputIndex); - if (focusIndex == inputIndex) - button->setFocus(SET_FOCUS_DEFAULT); + Button *button = + new InputLineButton(window, grid.getFieldSlot(), inputIndex); button->setPressHandler([=]() -> uint8_t { button->bringToTop(); - Menu * menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { - editInput(window, input, inputIndex); - }); + Menu *menu = new Menu(window); + menu->addLine(STR_EDIT, + [=]() { editInput(window, input, inputIndex); }); if (!reachExposLimit()) { menu->addLine(STR_INSERT_BEFORE, [=]() { insertExpo(inputIndex, input); @@ -453,7 +452,8 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) menu->addLine(STR_PASTE_BEFORE, [=]() { copyExpo(s_copySrcIdx, inputIndex, PASTE_BEFORE); if (s_copyMode == MOVE_MODE) { - deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx + 1 + : s_copySrcIdx); s_copyMode = 0; } rebuild(window, inputIndex); @@ -461,10 +461,11 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) menu->addLine(STR_PASTE_AFTER, [=]() { copyExpo(s_copySrcIdx, inputIndex, PASTE_AFTER); if (s_copyMode == MOVE_MODE) { - deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx + 1 + : s_copySrcIdx); s_copyMode = 0; } - rebuild(window, inputIndex+1); + rebuild(window, inputIndex + 1); }); } } @@ -478,22 +479,39 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); - grid.spacer(button->height() - 2); - + if (focusIndex == inputIndex) { + button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); + } + grid.spacer(button->height() - 1); ++inputIndex; ++line; } + h = grid.getWindowHeight() - h + 1; + txt->setHeight(h); + grid.spacer(7); - } - else { - auto button = new TextButton(window, grid.getLabelSlot(), getSourceString(MIXSRC_FIRST_INPUT + input)); - if (focusIndex == inputIndex) - button->setFocus(SET_FOCUS_DEFAULT); + } else { + auto button = new TextButton(window, grid.getLabelSlot(), + getSourceString(MIXSRC_FIRST_INPUT + input)); + if (focusIndex == inputIndex) button->setFocus(SET_FOCUS_DEFAULT); button->setPressHandler([=]() -> uint8_t { button->bringToTop(); - Menu * menu = new Menu(window); + Menu *menu = new Menu(window); menu->addLine(STR_EDIT, [=]() { insertExpo(inputIndex, input); editInput(window, input, inputIndex); @@ -503,8 +521,9 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) if (s_copyMode != 0) { menu->addLine(STR_PASTE, [=]() { copyExpo(s_copySrcIdx, inputIndex, input); - if(s_copyMode == MOVE_MODE) { - deleteExpo((s_copySrcIdx >= inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + if (s_copyMode == MOVE_MODE) { + deleteExpo((s_copySrcIdx >= inputIndex) ? s_copySrcIdx + 1 + : s_copySrcIdx); s_copyMode = 0; } rebuild(window, -1); @@ -519,7 +538,7 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) } } - Window * focus = Window::getFocus(); + Window *focus = Window::getFocus(); if (focus) { focus->bringToTop(); } diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp index bae954375..ceae29080 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -241,95 +241,97 @@ static constexpr coord_t col1 = 20; static constexpr coord_t col2 = (LCD_W - 100) / 3 + col1; static constexpr coord_t col3 = ((LCD_W - 100) / 3) * 2 + col1; -class LogicalSwitchButton : public Button { - public: - LogicalSwitchButton(FormGroup * parent, const rect_t & rect, int lsIndex): - Button(parent, rect), - lsIndex(lsIndex), - active(isActive()) - { - LogicalSwitchData * ls = lswAddress(lsIndex); - if (ls->andsw != SWSRC_NONE || ls->duration != 0 || ls->delay != 0) - setHeight(height() + 20); - if (lswFamily(ls->func) == LS_FAMILY_EDGE) - setHeight(height() + 20); +class LogicalSwitchButton : public Button +{ + public: + LogicalSwitchButton(FormGroup* parent, const rect_t& rect, int lsIndex) : + Button(parent, rect), lsIndex(lsIndex), active(isActive()) + { + LogicalSwitchData* ls = lswAddress(lsIndex); + if (ls->andsw != SWSRC_NONE || ls->duration != 0 || ls->delay != 0) + setHeight(height() + 20); + if (lswFamily(ls->func) == LS_FAMILY_EDGE) setHeight(height() + 20); + } + + bool isActive() const + { + return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + lsIndex); + } + + void checkEvents() override + { + if (active != isActive()) { + invalidate(); + active = !active; } - bool isActive() const - { - return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + lsIndex); + Button::checkEvents(); + } + + void paintLogicalSwitchLine(BitmapBuffer* dc) + { + LogicalSwitchData* ls = lswAddress(lsIndex); + uint8_t lsFamily = lswFamily(ls->func); + + // CSW func + dc->drawTextAtIndex(col1, line1, STR_VCSWFUNC, ls->func); + + // CSW params + if (lsFamily == LS_FAMILY_BOOL || lsFamily == LS_FAMILY_STICKY) { + drawSwitch(dc, col2, line1, ls->v1); + drawSwitch(dc, col3, line1, ls->v2); + } else if (lsFamily == LS_FAMILY_EDGE) { + drawSwitch(dc, col1, line2, ls->v1); + putsEdgeDelayParam(dc, col2, line2, ls); + } else if (lsFamily == LS_FAMILY_COMP) { + drawSource(dc, col2, line1, ls->v1, 0); + drawSource(dc, col3, line1, ls->v2, 0); + } else if (lsFamily == LS_FAMILY_TIMER) { + dc->drawNumber(col2, line1, lswTimerValue(ls->v1), LEFT | PREC1); + dc->drawNumber(col3, line1, lswTimerValue(ls->v2), LEFT | PREC1); + } else { + drawSource(dc, col2, line1, ls->v1, 0); + drawSourceCustomValue( + dc, col3, line1, ls->v1, + (ls->v1 <= MIXSRC_LAST_CH ? calc100toRESX(ls->v2) : ls->v2), 0); } - void checkEvents() override - { - if (active != isActive()) { - invalidate(); - active = !active; - } + // AND switch + drawSwitch(dc, col1, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, + ls->andsw, 0); - Button::checkEvents(); + // CSW duration + if (ls->duration > 0) { + dc->drawNumber(col2, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, + ls->duration, PREC1 | LEFT); } - void paintLogicalSwitchLine(BitmapBuffer * dc) - { - LogicalSwitchData * ls = lswAddress(lsIndex); - uint8_t lsFamily = lswFamily(ls->func); - - // CSW func - dc->drawTextAtIndex(col1, line1, STR_VCSWFUNC, ls->func); - - // CSW params - if (lsFamily == LS_FAMILY_BOOL || lsFamily == LS_FAMILY_STICKY) { - drawSwitch(dc, col2, line1, ls->v1); - drawSwitch(dc, col3, line1, ls->v2); - } - else if (lsFamily == LS_FAMILY_EDGE) { - drawSwitch(dc, col1, line2, ls->v1); - putsEdgeDelayParam(dc, col2, line2, ls); - } - else if (lsFamily == LS_FAMILY_COMP) { - drawSource(dc, col2, line1, ls->v1, 0); - drawSource(dc, col3, line1, ls->v2, 0); - } - else if (lsFamily == LS_FAMILY_TIMER) { - dc->drawNumber(col2, line1, lswTimerValue(ls->v1), LEFT|PREC1); - dc->drawNumber(col3, line1, lswTimerValue(ls->v2), LEFT|PREC1); - } - else { - drawSource(dc, col2, line1, ls->v1, 0); - drawSourceCustomValue(dc, col3, line1, ls->v1, (ls->v1 <= MIXSRC_LAST_CH ? calc100toRESX(ls->v2) : ls->v2), 0); - } - - // AND switch - drawSwitch(dc, col1, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->andsw, 0); - - // CSW duration - if (ls->duration > 0) { - dc->drawNumber(col2, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->duration, PREC1 | LEFT); - } - - // CSW delay - if (lsFamily != LS_FAMILY_EDGE && ls->delay > 0) { - dc->drawNumber(col3, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->delay, PREC1 | LEFT); - } + // CSW delay + if (lsFamily != LS_FAMILY_EDGE && ls->delay > 0) { + dc->drawNumber(col3, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, + ls->delay, PREC1 | LEFT); } + } - void paint(BitmapBuffer * dc) override - { - if (active) - dc->drawSolidFilledRect(2, 2, rect.w-4, rect.h-4, HIGHLIGHT_COLOR); - else - dc->drawSolidFilledRect(2, 2, rect.w-4, rect.h-4, FIELD_BGCOLOR); + void paint(BitmapBuffer* dc) override + { + if (active) + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, HIGHLIGHT_COLOR); + else + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); - paintLogicalSwitchLine(dc); + paintLogicalSwitchLine(dc); - // The bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); - } + // The bounding rect + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + } - protected: - uint8_t lsIndex; - bool active; + protected: + uint8_t lsIndex; + bool active; }; ModelLogicalSwitchesPage::ModelLogicalSwitchesPage(): @@ -353,17 +355,18 @@ void ModelLogicalSwitchesPage::editLogicalSwitch(FormWindow * window, uint8_t ls }); } -void ModelLogicalSwitchesPage::build(FormWindow * window, int8_t focusIndex) +void ModelLogicalSwitchesPage::build(FormWindow* window, int8_t focusIndex) { FormGridLayout grid; grid.spacer(PAGE_PADDING); grid.setLabelWidth(66); for (uint8_t i = 0; i < MAX_LOGICAL_SWITCHES; i++) { - LogicalSwitchData * ls = lswAddress(i); + LogicalSwitchData* ls = lswAddress(i); if (ls->func == LS_FUNC_NONE) { - auto button = new TextButton(window, grid.getLabelSlot(), getSwitchPositionName(SWSRC_SW1+i)); + auto button = new TextButton(window, grid.getLabelSlot(), + getSwitchPositionName(SWSRC_SW1 + i)); button->setPressHandler([=]() { if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) { Menu* menu = new Menu(window); @@ -379,40 +382,54 @@ void ModelLogicalSwitchesPage::build(FormWindow * window, int8_t focusIndex) return 0; }); grid.spacer(button->height() + 5); - } - else { - new StaticText(window, grid.getLabelSlot(), getSwitchPositionName(SWSRC_SW1 + i), BUTTON_BACKGROUND, CENTERED); + } else { + auto txt = new StaticText(window, grid.getLabelSlot(), + getSwitchPositionName(SWSRC_SW1 + i), + BUTTON_BACKGROUND, CENTERED); auto button = new LogicalSwitchButton(window, grid.getFieldSlot(), i); button->setPressHandler([=]() { - Menu * menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { - editLogicalSwitch(window, i); + Menu* menu = new Menu(window); + menu->addLine(STR_EDIT, [=]() { editLogicalSwitch(window, i); }); + if (ls->func) + menu->addLine(STR_COPY, [=]() { + clipboard.type = CLIPBOARD_TYPE_CUSTOM_SWITCH; + clipboard.data.csw = *ls; }); - if (ls->func) - menu->addLine(STR_COPY, [=]() { - clipboard.type = CLIPBOARD_TYPE_CUSTOM_SWITCH; - clipboard.data.csw = *ls; - }); - if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) - menu->addLine(STR_PASTE, [=]() { - *ls = clipboard.data.csw; - storageDirty(EE_MODEL); - rebuild(window, i); - }); - if (ls->func || ls->v1 || ls->v2 || ls->delay || ls->duration || ls->andsw) - menu->addLine(STR_CLEAR, [=]() { - memset(ls, 0, sizeof(LogicalSwitchData)); - storageDirty(EE_MODEL); - rebuild(window, i); - }); - return 0; + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) + menu->addLine(STR_PASTE, [=]() { + *ls = clipboard.data.csw; + storageDirty(EE_MODEL); + rebuild(window, i); + }); + if (ls->func || ls->v1 || ls->v2 || ls->delay || ls->duration || + ls->andsw) + menu->addLine(STR_CLEAR, [=]() { + memset(ls, 0, sizeof(LogicalSwitchData)); + storageDirty(EE_MODEL); + rebuild(window, i); + }); + return 0; + }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); }); if (focusIndex == i) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } } diff --git a/radio/src/gui/colorlcd/model_logical_switches.h b/radio/src/gui/colorlcd/model_logical_switches.h index fc3a505bf..d211ead04 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.h +++ b/radio/src/gui/colorlcd/model_logical_switches.h @@ -29,7 +29,7 @@ public: virtual void build(FormWindow * window) override { - build(window, -1); + build(window, 0); } protected: diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index fbe28766f..e103d1926 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -53,245 +53,253 @@ bool reachMixesLimit() return false; } -class MixEditWindow : public Page { - public: - MixEditWindow(int8_t channel, uint8_t mixIndex) : - Page(ICON_MODEL_MIXER), - channel(channel), - mixIndex(mixIndex) - { - buildBody(&body); - buildHeader(&header); +class MixEditWindow : public Page +{ + public: + MixEditWindow(int8_t channel, uint8_t mixIndex) : + Page(ICON_MODEL_MIXER), channel(channel), mixIndex(mixIndex) + { + buildBody(&body); + buildHeader(&header); + } + + protected: + uint8_t channel; + uint8_t mixIndex; + FormGroup *curveParamField = nullptr; + + void buildHeader(Window *window) + { + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MIXES, 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSourceString(MIXSRC_CH1 + channel), 0, MENU_COLOR); + } + + void buildBody(FormWindow *window) + { + FormGridLayout grid; + grid.spacer(8); + + MixData *mix = mixAddress(mixIndex); + + // Mix name + new StaticText(window, grid.getLabelSlot(), STR_MIXNAME); + new RadioTextEdit(window, grid.getFieldSlot(), mix->name, + sizeof(mix->name)); + grid.nextLine(); + + // Source + new StaticText(window, grid.getLabelSlot(), STR_SOURCE); + new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST, + GET_SET_DEFAULT(mix->srcRaw)); + grid.nextLine(); + + // Weight + new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); + auto gvar = + new GVarNumberEdit(window, grid.getFieldSlot(), MIX_WEIGHT_MIN, + MIX_WEIGHT_MAX, GET_SET_DEFAULT(mix->weight)); + gvar->setSuffix("%"); + grid.nextLine(); + + // Offset + new StaticText(window, grid.getLabelSlot(), STR_OFFSET); + gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_OFFSET_MIN, + MIX_OFFSET_MAX, GET_SET_DEFAULT(mix->offset)); + gvar->setSuffix("%"); + grid.nextLine(); + + // Trim + new StaticText(window, grid.getLabelSlot(), STR_TRIM); + new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(mix->carryTrim)); + grid.nextLine(); + + // Curve + new StaticText(&body, grid.getLabelSlot(), STR_CURVE); + new Choice(&body, grid.getFieldSlot(2, 0), "\004DiffExpoFuncCstm", 0, + CURVE_REF_CUSTOM, GET_DEFAULT(mix->curve.type), + [=](int32_t newValue) { + mix->curve.type = newValue; + mix->curve.value = 0; + SET_DIRTY(); + updateCurveParamField(mix); + }); + curveParamField = + new FormGroup(&body, grid.getFieldSlot(2, 1), FORM_FORWARD_FOCUS); + updateCurveParamField(mix); + grid.nextLine(); + + // Flight modes + new StaticText(window, grid.getLabelSlot(), STR_FLMODE); + for (uint8_t i = 0; i < MAX_FLIGHT_MODES; i++) { + char fm[2] = {char('0' + i), '\0'}; + if (i > 0 && (i % 4) == 0) grid.nextLine(); + new TextButton( + window, grid.getFieldSlot(4, i % 4), fm, + [=]() -> uint8_t { + BFBIT_FLIP(mix->flightModes, bfBit(i)); + SET_DIRTY(); + return !(bfSingleBitGet(mix->flightModes, i)); + }, + OPAQUE | (bfSingleBitGet(mix->flightModes, i) ? 0 : BUTTON_CHECKED)); } + grid.nextLine(); - protected: - uint8_t channel; - uint8_t mixIndex; - FormGroup * curveParamField = nullptr; + // Switch + new StaticText(window, grid.getLabelSlot(), STR_SWITCH); + new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, + SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(mix->swtch)); + grid.nextLine(); - void buildHeader(Window * window) - { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MIXES, 0, MENU_COLOR); - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, getSourceString(MIXSRC_CH1 + channel), 0, MENU_COLOR); - } + // Warning + new StaticText(window, grid.getLabelSlot(), STR_MIXWARNING); + auto edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, 3, + GET_SET_DEFAULT(mix->mixWarn)); + edit->setZeroText(STR_OFF); + grid.nextLine(); - void buildBody(FormWindow * window) - { - FormGridLayout grid; - grid.spacer(8); + // Multiplex + new StaticText(window, grid.getLabelSlot(), STR_MULTPX); + new Choice(window, grid.getFieldSlot(), STR_VMLTPX, 0, 2, + GET_SET_DEFAULT(mix->mltpx)); + grid.nextLine(); - MixData * mix = mixAddress(mixIndex); + // Delay up + new StaticText(window, grid.getLabelSlot(), STR_DELAYUP); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->delayUp), + SET_VALUE(mix->delayUp, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Mix name - new StaticText(window, grid.getLabelSlot(), STR_MIXNAME); - new RadioTextEdit(window, grid.getFieldSlot(), mix->name, sizeof(mix->name)); - grid.nextLine(); + // Delay down + new StaticText(window, grid.getLabelSlot(), STR_DELAYDOWN); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->delayDown), + SET_VALUE(mix->delayDown, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Source - new StaticText(window, grid.getLabelSlot(), STR_SOURCE); - new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST, GET_SET_DEFAULT(mix->srcRaw)); - grid.nextLine(); + // Slow up + new StaticText(window, grid.getLabelSlot(), STR_SLOWUP); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->speedUp), + SET_VALUE(mix->speedUp, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Weight - new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); - auto gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, GET_SET_DEFAULT(mix->weight)); - gvar->setSuffix("%"); - grid.nextLine(); + // Slow down + new StaticText(window, grid.getLabelSlot(), STR_SLOWDOWN); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->speedDown), + SET_VALUE(mix->speedDown, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Offset - new StaticText(window, grid.getLabelSlot(), STR_OFFSET); - gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_OFFSET_MIN, MIX_OFFSET_MAX, GET_SET_DEFAULT(mix->offset)); - gvar->setSuffix("%"); - grid.nextLine(); + window->setInnerHeight(grid.getWindowHeight()); + } - // Trim - new StaticText(window, grid.getLabelSlot(), STR_TRIM); - new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(mix->carryTrim)); - grid.nextLine(); + // TODO share this code with INPUT + void updateCurveParamField(MixData *line) + { + curveParamField->clear(); - // Curve - new StaticText(&body, grid.getLabelSlot(), STR_CURVE); - new Choice(&body, grid.getFieldSlot(2, 0), "\004DiffExpoFuncCstm", 0, CURVE_REF_CUSTOM, - GET_DEFAULT(mix->curve.type), - [=](int32_t newValue) { - mix->curve.type = newValue; - mix->curve.value = 0; - SET_DIRTY(); - updateCurveParamField(mix); - }); - curveParamField = new FormGroup(&body, grid.getFieldSlot(2, 1), FORM_FORWARD_FOCUS); - updateCurveParamField(mix); - grid.nextLine(); + const rect_t rect = {0, 0, curveParamField->width(), + curveParamField->height()}; - // Flight modes - new StaticText(window, grid.getLabelSlot(), STR_FLMODE); - for (uint8_t i = 0; i < MAX_FLIGHT_MODES; i++) { - char fm[2] = {char('0' + i), '\0'}; - if (i > 0 && (i % 4) == 0) - grid.nextLine(); - new TextButton(window, grid.getFieldSlot(4, i % 4), fm, - [=]() -> uint8_t { - BFBIT_FLIP(mix->flightModes, bfBit(i)); - SET_DIRTY(); - return !(bfSingleBitGet(mix->flightModes, i)); - }, - OPAQUE | (bfSingleBitGet(mix->flightModes, i) ? 0 : BUTTON_CHECKED)); + switch (line->curve.type) { + case CURVE_REF_DIFF: + case CURVE_REF_EXPO: { + GVarNumberEdit *edit = + new GVarNumberEdit(curveParamField, rect, -100, 100, + GET_SET_DEFAULT(line->curve.value)); + edit->setSuffix("%"); + break; } - grid.nextLine(); - // Switch - new StaticText(window, grid.getLabelSlot(), STR_SWITCH); - new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(mix->swtch)); - grid.nextLine(); + case CURVE_REF_FUNC: + new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1, + GET_SET_DEFAULT(line->curve.value)); + break; - // Warning - new StaticText(window, grid.getLabelSlot(), STR_MIXWARNING); - auto edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, 3, GET_SET_DEFAULT(mix->mixWarn)); - edit->setZeroText(STR_OFF); - grid.nextLine(); - - // Multiplex - new StaticText(window, grid.getLabelSlot(), STR_MULTPX); - new Choice(window, grid.getFieldSlot(), STR_VMLTPX, 0, 2, GET_SET_DEFAULT(mix->mltpx)); - grid.nextLine(); - - // Delay up - new StaticText(window, grid.getLabelSlot(), STR_DELAYUP); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->delayUp), - SET_VALUE(mix->delayUp, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - // Delay down - new StaticText(window, grid.getLabelSlot(), STR_DELAYDOWN); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->delayDown), - SET_VALUE(mix->delayDown, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - // Slow up - new StaticText(window, grid.getLabelSlot(), STR_SLOWUP); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->speedUp), - SET_VALUE(mix->speedUp, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - // Slow down - new StaticText(window, grid.getLabelSlot(), STR_SLOWDOWN); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->speedDown), - SET_VALUE(mix->speedDown, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - window->setInnerHeight(grid.getWindowHeight()); - } - - // TODO share this code with INPUT - void updateCurveParamField(MixData * line) - { - curveParamField->clear(); - - const rect_t rect = {0, 0, curveParamField->width(), curveParamField->height()}; - - switch (line->curve.type) { - case CURVE_REF_DIFF: - case CURVE_REF_EXPO: - { - GVarNumberEdit * edit = new GVarNumberEdit(curveParamField, rect, -100, 100, GET_SET_DEFAULT(line->curve.value)); - edit->setSuffix("%"); - break; - } - - case CURVE_REF_FUNC: - new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1, GET_SET_DEFAULT(line->curve.value)); - break; - - case CURVE_REF_CUSTOM: - { - auto choice = new Choice(curveParamField, rect, -MAX_CURVES, MAX_CURVES, GET_SET_DEFAULT(line->curve.value)); - choice->setTextHandler([](int value) { - return getCurveString(value); - }); - break; - } + case CURVE_REF_CUSTOM: { + auto choice = new Choice(curveParamField, rect, -MAX_CURVES, MAX_CURVES, + GET_SET_DEFAULT(line->curve.value)); + choice->setTextHandler([](int value) { return getCurveString(value); }); + break; } } + } }; -class MixLineButton : public CommonInputOrMixButton { - public: - MixLineButton(FormGroup * parent, const rect_t & rect, uint8_t index) : +class MixLineButton : public CommonInputOrMixButton +{ + public: + MixLineButton(FormGroup *parent, const rect_t &rect, uint8_t index) : CommonInputOrMixButton(parent, rect, index) - { - const MixData & mix = g_model.mixData[index]; - if (mix.swtch || mix.curve.value != 0 || mix.flightModes) { - setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); - } + { + const MixData &mix = g_model.mixData[index]; + if (mix.swtch || mix.curve.value != 0 || mix.flightModes) { + setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); + } + } + + bool isActive() const override { return isMixActive(index); } + + void paintBody(BitmapBuffer *dc) override + { + const MixData &line = g_model.mixData[index]; + + LcdFlags textColor = DEFAULT_COLOR; + + // first line ... + drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, + MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, textColor); + drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); + + if (line.name[0]) { + dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); + dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), + textColor); } - bool isActive() const override - { - return isMixActive(index); + // second line ... + if (line.swtch) { + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupSwitchIcon, textColor); + drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + textColor); } - void paintBody(BitmapBuffer * dc) override - { - const MixData & line = g_model.mixData[index]; - - LcdFlags textColor = DEFAULT_COLOR; - if (hasFocus()) - textColor = FOCUS_COLOR; - - // first line ... - drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, - MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, textColor); - drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); - - if (line.name[0]) { - dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); - dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), - textColor); - } - - // second line ... - if (line.swtch) { - dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, - mixerSetupSwitchIcon, textColor); - drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + if (line.curve.value) { + dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupCurveIcon, textColor); + drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, textColor); - } - - if (line.curve.value) { - dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, - mixerSetupCurveIcon, textColor); - drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, - textColor); - } - - if (line.flightModes) { - drawFlightModes(dc, line.flightModes, textColor); - } } + + if (line.flightModes) { + drawFlightModes(dc, line.flightModes, textColor); + } + } }; void insertMix(uint8_t idx, uint8_t channel) { pauseMixerCalculations(); - MixData * mix = mixAddress(idx); + MixData *mix = mixAddress(idx); memmove(mix + 1, mix, (MAX_MIXERS - (idx + 1)) * sizeof(MixData)); memclear(mix, sizeof(MixData)); mix->destCh = channel; mix->srcRaw = channel + 1; if (!isSourceAvailable(mix->srcRaw)) { - mix->srcRaw = (channel > 3 ? MIXSRC_Rud - 1 + channel : MIXSRC_Rud - 1 + channelOrder(channel)); + mix->srcRaw = (channel > 3 ? MIXSRC_Rud - 1 + channel + : MIXSRC_Rud - 1 + channelOrder(channel)); while (!isSourceAvailable(mix->srcRaw)) { mix->srcRaw += 1; } @@ -339,15 +347,15 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) MixData * mix = g_model.mixData; for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) { if (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { + + coord_t h = grid.getWindowHeight(); auto txt = new StaticText(window, grid.getLabelSlot(), - getSourceString(MIXSRC_CH1 + ch), 0, - CENTERED); - txt->setBackgroundColor(FIELD_BGCOLOR); + getSourceString(MIXSRC_CH1 + ch), + BUTTON_BACKGROUND, CENTERED); + uint8_t count = 0; while (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { Button * button = new MixLineButton(window, grid.getFieldSlot(), mixIndex); - if (focusMixIndex == mixIndex) - button->setFocus(SET_FOCUS_DEFAULT); button->setPressHandler([=]() -> uint8_t { button->bringToTop(); Menu * menu = new Menu(window); @@ -396,16 +404,38 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); - if (count++ > 0) { - new StaticBitmap(window, {35, button->top() + (button->height() - 18) / 2, 25, 17}, mixerMultiplexBitmap[mix->mltpx]); + if (focusMixIndex == mixIndex) { + button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } - grid.spacer(button->height() - 2); + if (count++ > 0) { + new StaticBitmap( + window, {35, button->top() + (button->height() - 18) / 2, 25, 17}, + mixerMultiplexBitmap[mix->mltpx]); + } + + grid.spacer(button->height() - 1); ++mixIndex; ++mix; } + h = grid.getWindowHeight() - h + 1; + txt->setHeight(h); + grid.spacer(7); } else { diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index 66576fd3f..4212dea38 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -129,11 +129,6 @@ class OutputLineButton : public Button { LcdFlags textColor = DEFAULT_COLOR; LcdFlags bgColor = FIELD_BGCOLOR; - if (hasFocus()) { - textColor = FOCUS_COLOR; - bgColor = FOCUS_BGCOLOR; - } - dc->drawSolidFilledRect(0, 0, width(), height(), bgColor); // first line @@ -165,8 +160,10 @@ class OutputLineButton : public Button { } // bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, - hasFocus() ? FOCUS_BGCOLOR : FIELD_FRAME_COLOR); + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); } protected: @@ -197,9 +194,8 @@ void ModelOutputsPage::build(FormWindow * window, int8_t focusChannel) // Channel label auto txt = new StaticText(window, grid.getLabelSlot(), - getSourceString(MIXSRC_CH1 + ch), 0, - CENTERED); - txt->setBackgroundColor(FIELD_BGCOLOR); + getSourceString(MIXSRC_CH1 + ch), + BUTTON_BACKGROUND, CENTERED); // Channel settings Button * button = new OutputLineButton(window, grid.getFieldSlot(), output); @@ -231,11 +227,25 @@ void ModelOutputsPage::build(FormWindow * window, int8_t focusChannel) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); if (focusChannel == ch) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } diff --git a/radio/src/gui/colorlcd/model_outputs.h b/radio/src/gui/colorlcd/model_outputs.h index 2a5c61734..25671a4d7 100644 --- a/radio/src/gui/colorlcd/model_outputs.h +++ b/radio/src/gui/colorlcd/model_outputs.h @@ -29,7 +29,7 @@ class ModelOutputsPage: public PageTab { virtual void build(FormWindow * window) override { - build(window, -1); + build(window, 0); } protected: diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index a7092095a..0651e9e83 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -24,235 +24,301 @@ #define SET_DIRTY() storageDirty(functions == g_model.customFn ? EE_MODEL : EE_GENERAL) -class SpecialFunctionEditPage : public Page { - public: - SpecialFunctionEditPage(CustomFunctionData * functions, uint8_t index) : - Page(functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS : ICON_RADIO_GLOBAL_FUNCTIONS), +class SpecialFunctionEditPage : public Page +{ + public: + SpecialFunctionEditPage(CustomFunctionData *functions, uint8_t index) : + Page(functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS + : ICON_RADIO_GLOBAL_FUNCTIONS), functions(functions), index(index) - { - buildHeader(&header); - buildBody(&body); + { + buildHeader(&header); + buildBody(&body); + } + + protected: + CustomFunctionData *functions; + uint8_t index; + FormGroup *specialFunctionOneWindow = nullptr; + StaticText *headerSF = nullptr; + bool active = false; + + bool isActive() const + { + return ((functions == g_model.customFn + ? modelFunctionsContext.activeSwitches + : globalFunctionsContext.activeSwitches) & + ((MASK_CFN_TYPE)1 << index) + ? 1 + : 0); + } + + void checkEvents() override + { + Page::checkEvents(); + if (active != isActive()) { + invalidate(); + headerSF->setTextFlags(isActive() ? FONT(BOLD) | HIGHLIGHT_COLOR + : MENU_COLOR); + active = !active; } + } - protected: - CustomFunctionData * functions; - uint8_t index; - FormGroup * specialFunctionOneWindow = nullptr; - StaticText * headerSF = nullptr; - bool active = false; + void buildHeader(Window *window) + { + new StaticText( + window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, 20}, + functions == g_model.customFn ? STR_MENUCUSTOMFUNC + : STR_MENUSPECIALFUNCS, + 0, MENU_COLOR); + headerSF = new StaticText( + window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, 20}, + (functions == g_model.customFn ? "SF" : "GF") + std::to_string(index), + 0, MENU_COLOR); + } - bool isActive() const - { - return ((functions == g_model.customFn ? modelFunctionsContext.activeSwitches : globalFunctionsContext.activeSwitches) & ((MASK_CFN_TYPE)1 << index) ? 1 : 0); - } + void updateSpecialFunctionOneWindow() + { + FormGridLayout grid; + specialFunctionOneWindow->clear(); - void checkEvents() override - { - Page::checkEvents(); - if (active != isActive()) { - invalidate(); - headerSF->setTextFlags(isActive() ? FONT(BOLD) |HIGHLIGHT_COLOR : MENU_COLOR); - active = !active; - } - } + CustomFunctionData *cfn = &functions[index]; + uint8_t func = CFN_FUNC(cfn); - void buildHeader(Window * window) - { - new StaticText(window, { PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, 20 }, functions == g_model.customFn ? STR_MENUCUSTOMFUNC : STR_MENUSPECIALFUNCS, 0, MENU_COLOR); - headerSF = new StaticText(window, { PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, 20 }, (functions == g_model.customFn ? "SF" : "GF" ) + std::to_string(index), 0, MENU_COLOR); - } - - void updateSpecialFunctionOneWindow() - { - FormGridLayout grid; - specialFunctionOneWindow->clear(); - - CustomFunctionData * cfn = &functions[index]; - uint8_t func = CFN_FUNC(cfn); - - // Func param - switch (func) { - case FUNC_OVERRIDE_CHANNEL: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_CH); - new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 1, MAX_OUTPUT_CHANNELS, GET_SET_VALUE_WITH_OFFSET(CFN_CH_INDEX(cfn), 1)); - grid.nextLine(); - - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_TRAINER: { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - auto choice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0,NUM_STICKS + 1,GET_SET_DEFAULT(CFN_PARAM(cfn))); - choice->setTextHandler([=](int32_t value) { - if (value == 0) - return std::string(STR_STICKS); - else if (value == NUM_STICKS + 1) - return std::string(STR_CHANS); - else - return TEXT_AT_INDEX(STR_VSRCRAW, value);; - }); - grid.nextLine(); - break; - } - - case FUNC_RESET: - if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_RESET); - auto choice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, - FUNC_RESET_PARAM_FIRST_TELEM + lastUsedTelemetryIndex(), - GET_SET_DEFAULT(CFN_PARAM(cfn))); - choice->setAvailableHandler(isSourceAvailableInResetSpecialFunction); - choice->setTextHandler([=](int32_t value) { - if (value < FUNC_RESET_PARAM_FIRST_TELEM) - return TEXT_AT_INDEX(STR_VFSWRESET, value); - else - return std::string(g_model.telemetrySensors[value - FUNC_RESET_PARAM_FIRST_TELEM].label, TELEM_LABEL_LEN); - }); - grid.nextLine(); - } - break; - - case FUNC_VOLUME: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VOLUME); - new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_BACKLIGHT: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_PLAY_SOUND: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new Choice(specialFunctionOneWindow, grid.getFieldSlot(), STR_FUNCSOUNDS, 0, - AU_SPECIAL_SOUND_LAST - AU_SPECIAL_SOUND_FIRST - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_PLAY_TRACK: - case FUNC_BACKGND_MUSIC: - case FUNC_PLAY_SCRIPT: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new FileChoice(specialFunctionOneWindow, grid.getFieldSlot(), - func == FUNC_PLAY_SCRIPT ? SCRIPTS_FUNCS_PATH : std::string(SOUNDS_PATH, SOUNDS_PATH_LNG_OFS) + - std::string(currentLanguagePack->id, 2), - func == FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, - sizeof(cfn->play.name), - [=]() { - return std::string(cfn->play.name, ZLEN(cfn->play.name)); - }, - [=](std::string newValue) { - strncpy(cfn->play.name, newValue.c_str(), sizeof(cfn->play.name)); - SET_DIRTY(); - }, true); // strip extension - grid.nextLine(); - break; - - case FUNC_SET_TIMER: { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_TIMER); - auto timerchoice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, TIMERS - 1, GET_SET_DEFAULT(CFN_TIMER_INDEX(cfn))); - timerchoice->setTextHandler([](int32_t value) { - return std::string(STR_TIMER) + std::to_string(value + 1); - }); - grid.nextLine(); - - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new TimeEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 9 * 60 * 60 - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - } - - case FUNC_SET_FAILSAFE: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_MODULE); - new Choice(specialFunctionOneWindow, grid.getFieldSlot(), "\004Int.Ext.", 0, NUM_MODULES - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_PLAY_VALUE: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_HAPTIC: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 3, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_LOGS: { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - auto edit = new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 255, GET_SET_DEFAULT(CFN_PARAM(cfn))); - edit->setDisplayHandler([=](BitmapBuffer * dc, LcdFlags flags, int32_t value) { - dc->drawNumber(2, 2, CFN_PARAM(cfn), PREC1, sizeof(CFN_PARAM(cfn)), nullptr, "s"); - }); - break; - } - } - - if (HAS_ENABLE_PARAM(func)) { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_ENABLE); - new CheckBox(specialFunctionOneWindow, grid.getFieldSlot(), GET_SET_DEFAULT(CFN_ACTIVE(cfn))); + // Func param + switch (func) { + case FUNC_OVERRIDE_CHANNEL: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_CH); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 1, + MAX_OUTPUT_CHANNELS, + GET_SET_VALUE_WITH_OFFSET(CFN_CH_INDEX(cfn), 1)); grid.nextLine(); - } - else if (HAS_REPEAT_PARAM(func)) { // !1x 1x 1s 2s 3s ... - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_REPEAT); - auto repeat = new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(2, 1), -1, 60/CFN_PLAY_REPEAT_MUL, GET_SET_DEFAULT(CFN_PLAY_REPEAT(cfn))); - repeat->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), -100, 100, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_TRAINER: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + auto choice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + NUM_STICKS + 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + choice->setTextHandler([=](int32_t value) { if (value == 0) - dc->drawText(3, 0, "1x", flags); - else if (value == CFN_PLAY_REPEAT_NOSTART) - dc->drawText(3, 0, "!1x", flags); + return std::string(STR_STICKS); + else if (value == NUM_STICKS + 1) + return std::string(STR_CHANS); else - dc->drawNumber(3, 0, value * CFN_PLAY_REPEAT_MUL, flags, 0, nullptr, "s"); + return TEXT_AT_INDEX(STR_VSRCRAW, value); + ; }); grid.nextLine(); + break; } - specialFunctionOneWindow->adjustHeight(); + case FUNC_RESET: + if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_RESET); + auto choice = new Choice( + specialFunctionOneWindow, grid.getFieldSlot(), 0, + FUNC_RESET_PARAM_FIRST_TELEM + lastUsedTelemetryIndex(), + GET_SET_DEFAULT(CFN_PARAM(cfn))); + choice->setAvailableHandler(isSourceAvailableInResetSpecialFunction); + choice->setTextHandler([=](int32_t value) { + if (value < FUNC_RESET_PARAM_FIRST_TELEM) + return TEXT_AT_INDEX(STR_VFSWRESET, value); + else + return std::string( + g_model.telemetrySensors[value - FUNC_RESET_PARAM_FIRST_TELEM] + .label, + TELEM_LABEL_LEN); + }); + grid.nextLine(); + } + break; + + case FUNC_VOLUME: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VOLUME); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_BACKLIGHT: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_SOUND: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), + STR_FUNCSOUNDS, 0, + AU_SPECIAL_SOUND_LAST - AU_SPECIAL_SOUND_FIRST - 1, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_TRACK: + case FUNC_BACKGND_MUSIC: + case FUNC_PLAY_SCRIPT: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new FileChoice( + specialFunctionOneWindow, grid.getFieldSlot(), + func == FUNC_PLAY_SCRIPT + ? SCRIPTS_FUNCS_PATH + : std::string(SOUNDS_PATH, SOUNDS_PATH_LNG_OFS) + + std::string(currentLanguagePack->id, 2), + func == FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, + sizeof(cfn->play.name), + [=]() { return std::string(cfn->play.name, ZLEN(cfn->play.name)); }, + [=](std::string newValue) { + strncpy(cfn->play.name, newValue.c_str(), sizeof(cfn->play.name)); + SET_DIRTY(); + }, + true); // strip extension + grid.nextLine(); + break; + + case FUNC_SET_TIMER: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_TIMER); + auto timerchoice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + TIMERS - 1, GET_SET_DEFAULT(CFN_TIMER_INDEX(cfn))); + timerchoice->setTextHandler([](int32_t value) { + return std::string(STR_TIMER) + std::to_string(value + 1); + }); + grid.nextLine(); + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new TimeEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, + 9 * 60 * 60 - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + } + + case FUNC_SET_FAILSAFE: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_MODULE); + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), + "\004Int.Ext.", 0, NUM_MODULES - 1, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_VALUE: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MIXSRC_LAST_TELEM, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_HAPTIC: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 3, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_LOGS: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + auto edit = + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, + 255, GET_SET_DEFAULT(CFN_PARAM(cfn))); + edit->setDisplayHandler( + [=](BitmapBuffer *dc, LcdFlags flags, int32_t value) { + dc->drawNumber(2, 2, CFN_PARAM(cfn), PREC1, + sizeof(CFN_PARAM(cfn)), nullptr, "s"); + }); + break; + } } - void buildBody(FormWindow * window) - { - // SF.one - FormGridLayout grid; - grid.spacer(PAGE_PADDING); - - CustomFunctionData * cfn = &functions[index]; - - // Switch - new StaticText(window, grid.getLabelSlot(), STR_SWITCH); - auto switchChoice = new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST, SWSRC_LAST, GET_SET_DEFAULT(CFN_SWITCH(cfn))); - switchChoice->setAvailableHandler([=](int value) { - return (functions == g_model.customFn ? isSwitchAvailable(value, ModelCustomFunctionsContext) - : isSwitchAvailable(value, - GeneralCustomFunctionsContext)); - }); + if (HAS_ENABLE_PARAM(func)) { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_ENABLE); + new CheckBox(specialFunctionOneWindow, grid.getFieldSlot(), + GET_SET_DEFAULT(CFN_ACTIVE(cfn))); grid.nextLine(); - - // Function - new StaticText(window, grid.getLabelSlot(), STR_FUNC); - auto functionChoice = new Choice(window, grid.getFieldSlot(), STR_VFSWFUNC, 0, FUNC_MAX - 1, GET_DEFAULT(CFN_FUNC(cfn))); - functionChoice->setSetValueHandler([=](int32_t newValue) { - CFN_FUNC(cfn) = newValue; - CFN_RESET(cfn); - SET_DIRTY(); - updateSpecialFunctionOneWindow(); - }); - functionChoice->setAvailableHandler([=](int value) { - return isAssignableFunctionAvailable(value, functions); - }); + } else if (HAS_REPEAT_PARAM(func)) { // !1x 1x 1s 2s 3s ... + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_REPEAT); + auto repeat = new NumberEdit( + specialFunctionOneWindow, grid.getFieldSlot(2, 1), -1, + 60 / CFN_PLAY_REPEAT_MUL, GET_SET_DEFAULT(CFN_PLAY_REPEAT(cfn))); + repeat->setDisplayHandler( + [](BitmapBuffer *dc, LcdFlags flags, int32_t value) { + if (value == 0) + dc->drawText(3, 0, "1x", flags); + else if (value == CFN_PLAY_REPEAT_NOSTART) + dc->drawText(3, 0, "!1x", flags); + else + dc->drawNumber(3, 0, value * CFN_PLAY_REPEAT_MUL, flags, 0, + nullptr, "s"); + }); grid.nextLine(); + } - specialFunctionOneWindow = new FormGroup(window, {0, grid.getWindowHeight(), LCD_W, 0}, FORM_FORWARD_FOCUS); + specialFunctionOneWindow->adjustHeight(); + } + + void buildBody(FormWindow *window) + { + // SF.one + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + + CustomFunctionData *cfn = &functions[index]; + + // Switch + new StaticText(window, grid.getLabelSlot(), STR_SWITCH); + auto switchChoice = + new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST, SWSRC_LAST, + GET_SET_DEFAULT(CFN_SWITCH(cfn))); + switchChoice->setAvailableHandler([=](int value) { + return (functions == g_model.customFn + ? isSwitchAvailable(value, ModelCustomFunctionsContext) + : isSwitchAvailable(value, GeneralCustomFunctionsContext)); + }); + grid.nextLine(); + + // Function + new StaticText(window, grid.getLabelSlot(), STR_FUNC); + auto functionChoice = + new Choice(window, grid.getFieldSlot(), STR_VFSWFUNC, 0, FUNC_MAX - 1, + GET_DEFAULT(CFN_FUNC(cfn))); + functionChoice->setSetValueHandler([=](int32_t newValue) { + CFN_FUNC(cfn) = newValue; + CFN_RESET(cfn); + SET_DIRTY(); updateSpecialFunctionOneWindow(); - grid.addWindow(specialFunctionOneWindow); - } + }); + functionChoice->setAvailableHandler([=](int value) { + return isAssignableFunctionAvailable(value, functions); + }); + grid.nextLine(); + + specialFunctionOneWindow = new FormGroup( + window, {0, grid.getWindowHeight(), LCD_W, 0}, FORM_FORWARD_FOCUS); + updateSpecialFunctionOneWindow(); + grid.addWindow(specialFunctionOneWindow); + } }; static constexpr coord_t line1 = FIELD_PADDING_TOP; @@ -261,161 +327,178 @@ static constexpr coord_t col1 = 20; static constexpr coord_t col2 = (LCD_W - 100) / 3 + col1; static constexpr coord_t col3 = ((LCD_W - 100) / 3) * 2 + col1 + 20; -class SpecialFunctionButton : public Button { - public: - SpecialFunctionButton(FormWindow * parent, const rect_t &rect, CustomFunctionData * functions, uint8_t index) : - Button(parent, rect), - functions(functions), - index(index) - { - const CustomFunctionData * cfn = &functions[index]; - uint8_t func = CFN_FUNC(cfn); - if (!cfn->isEmpty() && - (HAS_ENABLE_PARAM(func) || HAS_REPEAT_PARAM(func) || (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || func == FUNC_PLAY_SCRIPT))) { - setHeight(line2 + PAGE_LINE_HEIGHT); - } +class SpecialFunctionButton : public Button +{ + public: + SpecialFunctionButton(FormWindow *parent, const rect_t &rect, + CustomFunctionData *functions, uint8_t index) : + Button(parent, rect), functions(functions), index(index) + { + const CustomFunctionData *cfn = &functions[index]; + uint8_t func = CFN_FUNC(cfn); + if (!cfn->isEmpty() && + (HAS_ENABLE_PARAM(func) || HAS_REPEAT_PARAM(func) || + (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || + func == FUNC_PLAY_SCRIPT))) { + setHeight(line2 + PAGE_LINE_HEIGHT); } + } #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "SpecialFunctionButton"; - } + std::string getName() const override { return "SpecialFunctionButton"; } #endif - bool isActive() const - { - return ((functions == g_model.customFn ? modelFunctionsContext.activeSwitches : globalFunctionsContext.activeSwitches) & ((MASK_CFN_TYPE)1 << index) ? 1 : 0); + bool isActive() const + { + return ((functions == g_model.customFn + ? modelFunctionsContext.activeSwitches + : globalFunctionsContext.activeSwitches) & + ((MASK_CFN_TYPE)1 << index) + ? 1 + : 0); + } + + void checkEvents() override + { + Button::checkEvents(); + if (active != isActive()) { + invalidate(); + active = !active; } + } - void checkEvents() override - { - Button::checkEvents(); - if (active != isActive()) { - invalidate(); - active = !active; - } + void paintSpecialFunctionLine(BitmapBuffer *dc) + { + const CustomFunctionData *cfn = &functions[index]; + if (functions[index].func == FUNC_OVERRIDE_CHANNEL && + functions != g_model.customFn) { + functions[index].func = FUNC_OVERRIDE_CHANNEL + 1; } + uint8_t func = CFN_FUNC(cfn); - void paintSpecialFunctionLine(BitmapBuffer * dc) - { - const CustomFunctionData * cfn = &functions[index]; - if (functions[index].func == FUNC_OVERRIDE_CHANNEL && functions != g_model.customFn) { - functions[index].func = FUNC_OVERRIDE_CHANNEL + 1; - } - uint8_t func = CFN_FUNC(cfn); + drawSwitch(dc, col1, line1, CFN_SWITCH(cfn), 0); + if (cfn->isEmpty()) return; - drawSwitch(dc, col1, line1, CFN_SWITCH(cfn), 0); - if (cfn->isEmpty()) - return; + dc->drawTextAtIndex(col2, line1, STR_VFSWFUNC, func, 0); + int16_t val_min = 0; + int16_t val_max = 255; - dc->drawTextAtIndex(col2, line1, STR_VFSWFUNC, func, 0); - int16_t val_min = 0; - int16_t val_max = 255; + switch (func) { + case FUNC_OVERRIDE_CHANNEL: + drawChn(dc, col1, line2, CFN_CH_INDEX(cfn) + 1, 0); + getMixSrcRange(MIXSRC_FIRST_CH, val_min, val_max); + dc->drawNumber(col2, line2, CFN_PARAM(cfn)); + break; - switch (func) { - case FUNC_OVERRIDE_CHANNEL: - drawChn(dc, col1, line2, CFN_CH_INDEX(cfn) + 1, 0); - getMixSrcRange(MIXSRC_FIRST_CH, val_min, val_max); - dc->drawNumber(col2, line2, CFN_PARAM(cfn)); - break; + case FUNC_TRAINER: + drawSource( + dc, col1, line2, + CFN_CH_INDEX(cfn) == 0 ? 0 : MIXSRC_Rud + CFN_CH_INDEX(cfn) - 1); + break; - case FUNC_TRAINER: - drawSource(dc, col1, line2, CFN_CH_INDEX(cfn) == 0 ? 0 : MIXSRC_Rud + CFN_CH_INDEX(cfn) - 1); - break; - - case FUNC_RESET: - if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { - dc->drawTextAtIndex(col1, line2, STR_VFSWRESET, CFN_PARAM(cfn)); - } - else { - TelemetrySensor * sensor = &g_model.telemetrySensors[CFN_PARAM(cfn) - FUNC_RESET_PARAM_FIRST_TELEM]; - dc->drawSizedText(col1, line2, sensor->label, TELEM_LABEL_LEN); - } - break; - - case FUNC_VOLUME: - drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); - break; - - case FUNC_BACKLIGHT: - drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); - break; - - case FUNC_PLAY_SOUND: - dc->drawTextAtIndex(col1, line2, STR_FUNCSOUNDS, CFN_PARAM(cfn)); - break; - - case FUNC_PLAY_TRACK: - case FUNC_BACKGND_MUSIC: - case FUNC_PLAY_SCRIPT: - if (ZEXIST(cfn->play.name)) - dc->drawSizedText(col1, line2, cfn->play.name, sizeof(cfn->play.name), 0); - else - dc->drawTextAtIndex(col1, line2, STR_VCSWFUNC, 0, 0); - break; - - case FUNC_SET_TIMER: - drawStringWithIndex(dc, col1, line2, STR_TIMER, CFN_TIMER_INDEX(cfn) + 1, 0); - break; - - case FUNC_SET_FAILSAFE: - dc->drawTextAtIndex(col1, line2, "\004Int.Ext.", CFN_PARAM(cfn), 0); - break; - - case FUNC_PLAY_VALUE: - drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); - break; - - case FUNC_HAPTIC: - dc->drawNumber(col1, line2, CFN_PARAM(cfn), 0); - break; - - case FUNC_LOGS: - dc->drawNumber(col3, line1, CFN_PARAM(cfn), PREC1, sizeof(CFN_PARAM(cfn)), nullptr, "s"); - break; - } - if (HAS_ENABLE_PARAM(func)) { - theme->drawCheckBox(dc, CFN_ACTIVE(cfn), col3, line2); - } - else if (HAS_REPEAT_PARAM(func)) { - if (CFN_PLAY_REPEAT(cfn) == 0) { - dc->drawText(col3, line2, "1x", 0); - } - else if (CFN_PLAY_REPEAT(cfn) == CFN_PLAY_REPEAT_NOSTART) { - dc->drawText(col3, line2, "!1x", 0); - } - else { - dc->drawNumber(col3 + 12, line2, CFN_PLAY_REPEAT(cfn) * CFN_PLAY_REPEAT_MUL, 0 | RIGHT, 0, nullptr, "s"); + case FUNC_RESET: + if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { + dc->drawTextAtIndex(col1, line2, STR_VFSWRESET, CFN_PARAM(cfn)); + } else { + TelemetrySensor *sensor = + &g_model.telemetrySensors[CFN_PARAM(cfn) - + FUNC_RESET_PARAM_FIRST_TELEM]; + dc->drawSizedText(col1, line2, sensor->label, TELEM_LABEL_LEN); } + break; + + case FUNC_VOLUME: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_BACKLIGHT: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_PLAY_SOUND: + dc->drawTextAtIndex(col1, line2, STR_FUNCSOUNDS, CFN_PARAM(cfn)); + break; + + case FUNC_PLAY_TRACK: + case FUNC_BACKGND_MUSIC: + case FUNC_PLAY_SCRIPT: + if (ZEXIST(cfn->play.name)) + dc->drawSizedText(col1, line2, cfn->play.name, sizeof(cfn->play.name), + 0); + else + dc->drawTextAtIndex(col1, line2, STR_VCSWFUNC, 0, 0); + break; + + case FUNC_SET_TIMER: + drawStringWithIndex(dc, col1, line2, STR_TIMER, + CFN_TIMER_INDEX(cfn) + 1, 0); + break; + + case FUNC_SET_FAILSAFE: + dc->drawTextAtIndex(col1, line2, "\004Int.Ext.", CFN_PARAM(cfn), 0); + break; + + case FUNC_PLAY_VALUE: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_HAPTIC: + dc->drawNumber(col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_LOGS: + dc->drawNumber(col3, line1, CFN_PARAM(cfn), PREC1, + sizeof(CFN_PARAM(cfn)), nullptr, "s"); + break; + } + if (HAS_ENABLE_PARAM(func)) { + theme->drawCheckBox(dc, CFN_ACTIVE(cfn), col3, line2); + } else if (HAS_REPEAT_PARAM(func)) { + if (CFN_PLAY_REPEAT(cfn) == 0) { + dc->drawText(col3, line2, "1x", 0); + } else if (CFN_PLAY_REPEAT(cfn) == CFN_PLAY_REPEAT_NOSTART) { + dc->drawText(col3, line2, "!1x", 0); + } else { + dc->drawNumber(col3 + 12, line2, + CFN_PLAY_REPEAT(cfn) * CFN_PLAY_REPEAT_MUL, 0 | RIGHT, 0, + nullptr, "s"); } } + } - void paint(BitmapBuffer * dc) override - { - if (active) - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, HIGHLIGHT_COLOR); + void paint(BitmapBuffer *dc) override + { + if (active) + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, HIGHLIGHT_COLOR); + else + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); - paintSpecialFunctionLine(dc); + paintSpecialFunctionLine(dc); - // The bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); - } + // The bounding rect + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + } - protected: - CustomFunctionData * functions; - uint8_t index; - bool active = false; + protected: + CustomFunctionData *functions; + uint8_t index; + bool active = false; }; -SpecialFunctionsPage::SpecialFunctionsPage(CustomFunctionData * functions) : - PageTab(functions == g_model.customFn ? STR_MENUCUSTOMFUNC : STR_MENUSPECIALFUNCS, - functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS : ICON_RADIO_GLOBAL_FUNCTIONS), - functions(functions) +SpecialFunctionsPage::SpecialFunctionsPage(CustomFunctionData *functions) : + PageTab(functions == g_model.customFn ? STR_MENUCUSTOMFUNC + : STR_MENUSPECIALFUNCS, + functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS + : ICON_RADIO_GLOBAL_FUNCTIONS), + functions(functions) { } -void SpecialFunctionsPage::rebuild(FormWindow * window, int8_t focusSpecialFunctionIndex) +void SpecialFunctionsPage::rebuild(FormWindow *window, + int8_t focusSpecialFunctionIndex) { coord_t scrollPosition = window->getScrollPositionY(); window->clear(); @@ -423,15 +506,14 @@ void SpecialFunctionsPage::rebuild(FormWindow * window, int8_t focusSpecialFunct window->setScrollPositionY(scrollPosition); } -void SpecialFunctionsPage::editSpecialFunction(FormWindow * window, uint8_t index) +void SpecialFunctionsPage::editSpecialFunction(FormWindow *window, + uint8_t index) { auto editPage = new SpecialFunctionEditPage(functions, index); - editPage->setCloseHandler([=]() { - rebuild(window, index); - }); + editPage->setCloseHandler([=]() { rebuild(window, index); }); } -void SpecialFunctionsPage::build(FormWindow * window, int8_t focusIndex) +void SpecialFunctionsPage::build(FormWindow *window, int8_t focusIndex) { FormGridLayout grid; grid.spacer(PAGE_PADDING); @@ -440,11 +522,10 @@ void SpecialFunctionsPage::build(FormWindow * window, int8_t focusIndex) Window::clearFocus(); char s[] = "SFxx"; - if (functions == g_eeGeneral.customFn) - s[0] = 'G'; + if (functions == g_eeGeneral.customFn) s[0] = 'G'; for (uint8_t i = 0; i < MAX_SPECIAL_FUNCTIONS; i++) { - CustomFunctionData * cfn = &functions[i]; + CustomFunctionData *cfn = &functions[i]; strAppendUnsigned(&s[2], i); if (cfn->swtch == 0) { @@ -464,64 +545,82 @@ void SpecialFunctionsPage::build(FormWindow * window, int8_t focusIndex) return 0; }); grid.spacer(button->height() + 5); - } - else { - new StaticText(window, grid.getLabelSlot(), s, BUTTON_BACKGROUND, CENTERED); + } else { + auto txt = new StaticText(window, grid.getLabelSlot(), s, + BUTTON_BACKGROUND, CENTERED); - auto button = new SpecialFunctionButton(window, grid.getFieldSlot(), functions, i); + auto button = + new SpecialFunctionButton(window, grid.getFieldSlot(), functions, i); button->setPressHandler([=]() { - button->bringToTop(); - Menu * menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { - editSpecialFunction(window, i); + button->bringToTop(); + Menu *menu = new Menu(window); + menu->addLine(STR_EDIT, [=]() { editSpecialFunction(window, i); }); + menu->addLine(STR_COPY, [=]() { + clipboard.type = CLIPBOARD_TYPE_CUSTOM_FUNCTION; + clipboard.data.cfn = *cfn; + }); + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION) { + menu->addLine(STR_PASTE, [=]() { + *cfn = clipboard.data.cfn; + SET_DIRTY(); + rebuild(window, i); }); - menu->addLine(STR_COPY, [=]() { - clipboard.type = CLIPBOARD_TYPE_CUSTOM_FUNCTION; - clipboard.data.cfn = *cfn; + } + if (functions[MAX_SPECIAL_FUNCTIONS - 1].isEmpty()) { + menu->addLine(STR_INSERT, [=]() { + memmove( + cfn + 1, cfn, + (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); + memset(cfn, 0, sizeof(CustomFunctionData)); + SET_DIRTY(); + rebuild(window, i); }); - if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION) { - menu->addLine(STR_PASTE, [=]() { - *cfn = clipboard.data.cfn; - SET_DIRTY(); - rebuild(window, i); - }); - } - if (functions[MAX_SPECIAL_FUNCTIONS - 1].isEmpty()) { - menu->addLine(STR_INSERT, [=]() { - memmove(cfn + 1, cfn, (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); - memset(cfn, 0, sizeof(CustomFunctionData)); - SET_DIRTY(); - rebuild(window, i); - }); - } - menu->addLine(STR_CLEAR, [=]() { - memset(cfn, 0, sizeof(CustomFunctionData)); + } + menu->addLine(STR_CLEAR, [=]() { + memset(cfn, 0, sizeof(CustomFunctionData)); + SET_DIRTY(); + }); + for (int j = i; j < MAX_SPECIAL_FUNCTIONS; j++) { + if (!functions[j].isEmpty()) { + menu->addLine(STR_DELETE, [=]() { + memmove( + cfn, cfn + 1, + (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); + memset(&functions[MAX_SPECIAL_FUNCTIONS - 1], 0, + sizeof(CustomFunctionData)); SET_DIRTY(); - }); - for (int j = i; j < MAX_SPECIAL_FUNCTIONS; j++) { - if (!functions[j].isEmpty()) { - menu->addLine(STR_DELETE, [=]() { - memmove(cfn, cfn + 1, (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); - memset(&functions[MAX_SPECIAL_FUNCTIONS - 1], 0, sizeof(CustomFunctionData)); - SET_DIRTY(); - rebuild(window, i); - }); - break; - } + rebuild(window, i); + }); + break; } - return 0; + } + return 0; + }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); }); if (focusIndex == i) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } } grid.nextLine(); -// window->setLastField(); + // window->setLastField(); window->setInnerHeight(grid.getWindowHeight()); } diff --git a/radio/src/gui/colorlcd/special_functions.h b/radio/src/gui/colorlcd/special_functions.h index 0fa96341c..eaf85ec9b 100644 --- a/radio/src/gui/colorlcd/special_functions.h +++ b/radio/src/gui/colorlcd/special_functions.h @@ -31,11 +31,7 @@ class SpecialFunctionsPage: public PageTab { void build(FormWindow * window) override { -#if defined(HARDWARE_TOUCH) - build(window, -1); -#else build(window, 0); -#endif } protected: From 8a1687c294b98a54e8ee8490406bd0700004ce22 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 18:43:57 +0200 Subject: [PATCH 060/122] Fixed some masks/bitmaps --- radio/src/gui/colorlcd/channel_bar.h | 4 ++-- radio/src/gui/colorlcd/model_mixes.cpp | 20 ++++++++++++---- radio/src/gui/colorlcd/themes/480_default.cpp | 24 +++++++++---------- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/radio/src/gui/colorlcd/channel_bar.h b/radio/src/gui/colorlcd/channel_bar.h index c371620f1..c9df34879 100644 --- a/radio/src/gui/colorlcd/channel_bar.h +++ b/radio/src/gui/colorlcd/channel_bar.h @@ -216,13 +216,13 @@ class ComboChannelBar : public ChannelBar // Override icon #if defined(OVERRIDE_CHANNEL_FUNCTION) if (safetyCh[channel] != OVERRIDE_CHANNEL_UNDEFINED) - dc->drawBitmap(0, 1, chanMonLockedBitmap); + dc->drawMask(0, 1, chanMonLockedBitmap, DEFAULT_COLOR); #endif // Channel reverted icon LimitData * ld = limitAddress(channel); if (ld && ld->revert) { - lcd->drawBitmap(0, 20, chanMonInvertedBitmap); + lcd->drawMask(0, 20, chanMonInvertedBitmap, DEFAULT_COLOR); } } diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index e103d1926..98ea2d923 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -404,15 +404,26 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) }); return 0; }); + + StaticBitmap* bitmap = nullptr; + if (count++ > 0) { + bitmap = new StaticBitmap( + window, {35, button->top() + (button->height() - 18) / 2, 25, 17}, + mixerMultiplexBitmap[mix->mltpx], DEFAULT_COLOR); + } + button->setFocusHandler([=](bool focus) { if (focus) { txt->setBackgroundColor(FOCUS_BGCOLOR); txt->setTextFlags(FOCUS_COLOR | CENTERED); + if (bitmap) bitmap->setMaskColor(FOCUS_COLOR); } else { txt->setBackgroundColor(FIELD_FRAME_COLOR); txt->setTextFlags(CENTERED); + if (bitmap) bitmap->setMaskColor(DEFAULT_COLOR); } txt->invalidate(); + if (bitmap) bitmap->invalidate(); }); if (focusMixIndex == mixIndex) { @@ -420,13 +431,12 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) txt->setBackgroundColor(FOCUS_BGCOLOR); txt->setTextFlags(FOCUS_COLOR | CENTERED); txt->invalidate(); + if (bitmap) { + bitmap->setMaskColor(FOCUS_COLOR); + bitmap->invalidate(); + } } - if (count++ > 0) { - new StaticBitmap( - window, {35, button->top() + (button->height() - 18) / 2, 25, 17}, - mixerMultiplexBitmap[mix->mltpx]); - } grid.spacer(button->height() - 1); ++mixIndex; diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index d13aebb16..a045b6c0b 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -257,41 +257,41 @@ class Theme480: public OpenTxTheme // Channels monitor screen delete chanMonLockedBitmap; - chanMonLockedBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("mask_monitor_lockch.png"), DEFAULT_COLOR, DEFAULT_BGCOLOR); + chanMonLockedBitmap = BitmapBuffer::load8bitMask(mask_monitor_lockch); delete chanMonInvertedBitmap; - chanMonInvertedBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("mask_monitor_inver.png"), DEFAULT_COLOR, DEFAULT_BGCOLOR); + chanMonInvertedBitmap = BitmapBuffer::load8bitMask(mask_monitor_inver); // Mixer setup screen delete mixerSetupMixerBitmap; - mixerSetupMixerBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_mixer.png", MENU_COLOR, MENU_BGCOLOR); + mixerSetupMixerBitmap = BitmapBuffer::load8bitMask(mask_sbar_mixer); delete mixerSetupToBitmap; - mixerSetupToBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_to.png", MENU_COLOR, MENU_BGCOLOR); + mixerSetupToBitmap = BitmapBuffer::load8bitMask(mask_sbar_to); delete mixerSetupOutputBitmap; - mixerSetupOutputBitmap = BitmapBuffer::loadMaskOnBackground("mask_sbar_output.png", MENU_COLOR, MENU_BGCOLOR); + mixerSetupOutputBitmap = BitmapBuffer::load8bitMask(mask_sbar_output); delete mixerSetupAddBitmap; - mixerSetupAddBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("mask_mplex_add.png"), DEFAULT_COLOR, DEFAULT_BGCOLOR); + mixerSetupAddBitmap = BitmapBuffer::load8bitMask(mask_mplex_add); delete mixerSetupMultiBitmap; - mixerSetupMultiBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("mask_mplex_multi.png"), DEFAULT_COLOR, DEFAULT_BGCOLOR); + mixerSetupMultiBitmap = BitmapBuffer::load8bitMask(mask_mplex_multi); delete mixerSetupReplaceBitmap; - mixerSetupReplaceBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("mask_mplex_replace.png"), DEFAULT_COLOR, DEFAULT_BGCOLOR); + mixerSetupReplaceBitmap = BitmapBuffer::load8bitMask(mask_mplex_replace); delete mixerSetupLabelIcon; - mixerSetupLabelIcon = BitmapBuffer::loadMask(getFilePath("mask_textline_label.png")); + mixerSetupLabelIcon = BitmapBuffer::load8bitMask(mask_textline_label); delete mixerSetupCurveIcon; - mixerSetupCurveIcon = BitmapBuffer::loadMask(getFilePath("mask_textline_curve.png")); + mixerSetupCurveIcon = BitmapBuffer::load8bitMask(mask_textline_curve); delete mixerSetupSwitchIcon; - mixerSetupSwitchIcon = BitmapBuffer::loadMask(getFilePath("mask_textline_switch.png")); + mixerSetupSwitchIcon = BitmapBuffer::load8bitMask(mask_textline_switch); delete mixerSetupFlightmodeIcon; - mixerSetupFlightmodeIcon = BitmapBuffer::loadMask(getFilePath("mask_textline_fm.png")); + mixerSetupFlightmodeIcon = BitmapBuffer::load8bitMask(mask_textline_fm); // delete mixerSetupSlowIcon; // mixerSetupSlowIcon = BitmapBuffer::loadMask(getFilePath("mask_textline_slow.png")); From ddd5261ad795c4a72f88a747494e75a5f68f0488 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 21:09:49 +0200 Subject: [PATCH 061/122] Fix font color for radio calibration page header --- radio/src/gui/colorlcd/radio_calibration.cpp | 37 ++++++++++++++------ 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_calibration.cpp b/radio/src/gui/colorlcd/radio_calibration.cpp index f34f09ec8..fcf41f723 100644 --- a/radio/src/gui/colorlcd/radio_calibration.cpp +++ b/radio/src/gui/colorlcd/radio_calibration.cpp @@ -30,11 +30,14 @@ uint8_t menuCalibrationState; class StickCalibrationWindow: public Window { public: - StickCalibrationWindow(Window * parent, const rect_t & rect, uint8_t stickX, uint8_t stickY): - Window(parent, rect, REFRESH_ALWAYS), - stickX(stickX), - stickY(stickY) - { + StickCalibrationWindow(Window *parent, const rect_t &rect, uint8_t stickX, + uint8_t stickY) : + Window(parent, rect, REFRESH_ALWAYS), stickX(stickX), stickY(stickY) + { + setLeft(rect.x - calibStickBackground->width() / 2); + setTop(rect.y - calibStickBackground->height() / 2); + setWidth(calibStickBackground->width()); + setHeight(calibStickBackground->height()); } void paint(BitmapBuffer * dc) override @@ -42,7 +45,9 @@ class StickCalibrationWindow: public Window { dc->drawBitmap(0, 0, calibStickBackground); int16_t x = calibratedAnalogs[CONVERT_MODE(stickX)]; int16_t y = calibratedAnalogs[CONVERT_MODE(stickY)]; - dc->drawBitmap(width() / 2 - 9 + (bitmapSize / 2 * x) / RESX, height() / 2 - 9 - (bitmapSize / 2 * y) / RESX, calibStick); + dc->drawBitmap(width() / 2 - 9 + (bitmapSize / 2 * x) / RESX, + height() / 2 - 9 - (bitmapSize / 2 * y) / RESX, + calibStick); } protected: @@ -61,8 +66,15 @@ RadioCalibrationPage::RadioCalibrationPage(bool initial): void RadioCalibrationPage::buildHeader(Window * window) { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MENUCALIBRATION, 0, MENU_COLOR); - text = new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MENUTOSTART, 0,MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENUCALIBRATION, 0, FOCUS_COLOR); + + text = new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + STR_MENUTOSTART, 0, FOCUS_COLOR); } void RadioCalibrationPage::buildBody(FormWindow * window) @@ -72,8 +84,13 @@ void RadioCalibrationPage::buildBody(FormWindow * window) // The two sticks //TODO: dynamic placing - new StickCalibrationWindow(window, {40, 20, 90, 90}, STICK1, STICK2); - new StickCalibrationWindow(window, {LCD_W - 130, 20, 90, 90}, STICK4, STICK3); + new StickCalibrationWindow(window, + {window->width() / 3, window->height() / 2, 0, 0}, + STICK1, STICK2); + + new StickCalibrationWindow(window, + {(2 * window->width()) / 3, window->height() / 2, 0, 0}, + STICK4, STICK3); rect_t r = { 0, 0, window->width(), window->height() }; auto deco = new ViewMainDecoration(window, r); From ecf792f3e7136e6a372ebd8eae79c520f0084e58 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 21:23:31 +0200 Subject: [PATCH 062/122] Fixed page header text colors --- radio/src/gui/colorlcd/model_curves.cpp | 4 ++-- radio/src/gui/colorlcd/model_gvars.cpp | 8 ++++---- radio/src/gui/colorlcd/model_inputs.cpp | 11 +++++++++-- radio/src/gui/colorlcd/model_logical_switches.cpp | 4 ++-- radio/src/gui/colorlcd/model_mixes.cpp | 4 ++-- radio/src/gui/colorlcd/model_outputs.cpp | 4 ++-- radio/src/gui/colorlcd/special_functions.cpp | 4 ++-- 7 files changed, 23 insertions(+), 16 deletions(-) diff --git a/radio/src/gui/colorlcd/model_curves.cpp b/radio/src/gui/colorlcd/model_curves.cpp index c8a39ef02..8d463fe67 100644 --- a/radio/src/gui/colorlcd/model_curves.cpp +++ b/radio/src/gui/colorlcd/model_curves.cpp @@ -45,13 +45,13 @@ class CurveEditWindow : public Page new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - STR_MENUCURVE, 0, MENU_COLOR); + STR_MENUCURVE, 0, FOCUS_COLOR); char s[16]; strAppendStringWithIndex(s, STR_CV, index + 1); new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - s, 0, MENU_COLOR); + s, 0, FOCUS_COLOR); } #if LCD_W > LCD_H diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index 2a6aaaf74..9b3cf42fa 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -145,16 +145,16 @@ void GVarRenderer::paint(BitmapBuffer * dc) FlightModeData * fmData = &g_model.flightModeData[lastFlightMode]; lastGVar = fmData->gvars[index]; coord_t x = drawStringWithIndex(dc, 0, FIELD_PADDING_TOP, STR_GV, index + 1, - DEFAULT_COLOR, nullptr, "="); + FOCUS_COLOR, nullptr, "="); if (lastGVar > GVAR_MAX) { uint8_t fm = lastGVar - GVAR_MAX - 1; if (fm >= lastFlightMode) fm++; char label[16]; getFlightModeString(label, fm + 1); - dc->drawSizedText(x, FIELD_PADDING_TOP, label, strlen(label), DEFAULT_COLOR); + dc->drawSizedText(x, FIELD_PADDING_TOP, label, strlen(label), FOCUS_COLOR); } else { - drawGVarValue(dc, x, FIELD_PADDING_TOP, index, lastGVar, DEFAULT_COLOR); + drawGVarValue(dc, x, FIELD_PADDING_TOP, index, lastGVar, FOCUS_COLOR); } } @@ -183,7 +183,7 @@ void GVarEditWindow::buildHeader(Window * window) new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - STR_GLOBAL_VAR, 0, DEFAULT_COLOR); + STR_GLOBAL_VAR, 0, FOCUS_COLOR); gVarInHeader = new GVarRenderer(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp index da4f58ba1..14f8d1cbc 100644 --- a/radio/src/gui/colorlcd/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -137,8 +137,15 @@ class InputEditWindow: public Page void buildHeader(Window * window) { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MENUINPUTS, 0, MENU_COLOR); - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, getSourceString(MIXSRC_FIRST_INPUT + input), 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENUINPUTS, 0,FOCUS_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSourceString(MIXSRC_FIRST_INPUT + input), 0, + FOCUS_COLOR); } // TODO share this code with MIXER diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp index ceae29080..90b900562 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -76,12 +76,12 @@ class LogicalSwitchEditPage: public Page new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - STR_MENULOGICALSWITCHES, 0, MENU_COLOR); + STR_MENULOGICALSWITCHES, 0, FOCUS_COLOR); headerSwitchName = new StaticText( window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - getSwitchPositionName(SWSRC_SW1 + index), 0, MENU_COLOR); + getSwitchPositionName(SWSRC_SW1 + index), 0, FOCUS_COLOR); } void updateLogicalSwitchOneWindow() diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index 98ea2d923..5ac524630 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -73,11 +73,11 @@ class MixEditWindow : public Page new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - STR_MIXES, 0, MENU_COLOR); + STR_MIXES, 0, FOCUS_COLOR); new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - getSourceString(MIXSRC_CH1 + channel), 0, MENU_COLOR); + getSourceString(MIXSRC_CH1 + channel), 0, FOCUS_COLOR); } void buildBody(FormWindow *window) diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index 4212dea38..bbc42f58c 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -42,11 +42,11 @@ class OutputEditWindow : public Page { new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - STR_MENULIMITS, 0, MENU_COLOR); + STR_MENULIMITS, 0, FOCUS_COLOR); new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - getSourceString(MIXSRC_CH1 + channel), 0, MENU_COLOR); + getSourceString(MIXSRC_CH1 + channel), 0, FOCUS_COLOR); } void buildBody(FormWindow * window) diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index 0651e9e83..72814bcd7 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -71,13 +71,13 @@ class SpecialFunctionEditPage : public Page window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, 20}, functions == g_model.customFn ? STR_MENUCUSTOMFUNC : STR_MENUSPECIALFUNCS, - 0, MENU_COLOR); + 0, FOCUS_COLOR); headerSF = new StaticText( window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, 20}, (functions == g_model.customFn ? "SF" : "GF") + std::to_string(index), - 0, MENU_COLOR); + 0, FOCUS_COLOR); } void updateSpecialFunctionOneWindow() From 236cf3fd430d7055624cbcc00c924b7e7f6b4bc3 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 06:35:57 +0200 Subject: [PATCH 063/122] Updated libopenui --- radio/src/thirdparty/libopenui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index f098dd9e4..2ef946f14 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit f098dd9e4fb6b87d8b12f7895b6832fa8843a149 +Subproject commit 2ef946f14768a23143c59a1ba578e69349b4dacb From 7bb8254197ed1f4c7af7c14ed302d7a9b9e623aa Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 06:53:40 +0200 Subject: [PATCH 064/122] Fix header white pixel --- radio/src/gui/colorlcd/themes/480_default.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index a045b6c0b..5d5907536 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -182,7 +182,7 @@ class Theme480: public OpenTxTheme currentMenuBackground->drawSolidFilledRect( 0, 0, currentMenuBackground->width(), MENU_HEADER_HEIGHT, - MENU_BGCOLOR); + HEADER_COLOR); currentMenuBackground->drawSolidFilledRect( 0, MENU_HEADER_HEIGHT, currentMenuBackground->width(), From 1f62bab4f186b14d4054be94631d85da97c2c8c2 Mon Sep 17 00:00:00 2001 From: yaapu Date: Mon, 14 Jun 2021 08:07:31 +0000 Subject: [PATCH 065/122] colorlcd: lua fix INVERS+RIGHT in drawText, drawNumber --- radio/src/lua/api_colorlcd.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/radio/src/lua/api_colorlcd.cpp b/radio/src/lua/api_colorlcd.cpp index a4483728e..c83229c16 100644 --- a/radio/src/lua/api_colorlcd.cpp +++ b/radio/src/lua/api_colorlcd.cpp @@ -243,8 +243,12 @@ static int luaLcdDrawText(lua_State *L) if (invers) { int height = getFontHeight(att & 0xFFFF); int width = getTextWidth(s, 255, att); + int ix = x - INVERT_BOX_MARGIN; + if (att & RIGHT) { + ix = x - width - INVERT_BOX_MARGIN; + } luaLcdBuffer->drawSolidFilledRect( - x - INVERT_BOX_MARGIN, y - INVERT_BOX_MARGIN, + ix, y - INVERT_BOX_MARGIN, width + 2 * INVERT_BOX_MARGIN, height + 2 * INVERT_BOX_MARGIN, FOCUS_BGCOLOR); att = (att & 0xFFFF) | FOCUS_COLOR; @@ -338,8 +342,12 @@ static int luaLcdDrawNumber(lua_State *L) if (invers) { int height = getFontHeight(att & 0xFFFF); int width = getTextWidth(s, 255, att); + int ix = x - INVERT_BOX_MARGIN; + if (att & RIGHT) { + ix = x - width - INVERT_BOX_MARGIN; + } luaLcdBuffer->drawSolidFilledRect( - x - INVERT_BOX_MARGIN, y - INVERT_BOX_MARGIN, + ix, y - INVERT_BOX_MARGIN, width + 2 * INVERT_BOX_MARGIN, height + 2 * INVERT_BOX_MARGIN, FOCUS_BGCOLOR); att = (att & 0xFFFF) | FOCUS_COLOR; From a535bfea181075c0910b98658bed3422ba1ea020 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 11:15:25 +0200 Subject: [PATCH 066/122] Fixed TOOLS according to style guidelines --- radio/src/gui/colorlcd/radio_tools.cpp | 190 ++++++++++++++++++++----- 1 file changed, 153 insertions(+), 37 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_tools.cpp b/radio/src/gui/colorlcd/radio_tools.cpp index 7cfc8e619..3484f2495 100644 --- a/radio/src/gui/colorlcd/radio_tools.cpp +++ b/radio/src/gui/colorlcd/radio_tools.cpp @@ -112,7 +112,8 @@ void RadioToolsPage::rebuild(FormWindow * window) *ext = '\0'; label = getBasename(path); } - new StaticText(window, grid.getLabelSlot(), "lua", BUTTON_BACKGROUND, CENTERED); + auto txt = new StaticText(window, grid.getLabelSlot(), "lua", + BUTTON_BACKGROUND, CENTERED); std::string path_str(path); auto b = new TextButton( @@ -126,6 +127,16 @@ void RadioToolsPage::rebuild(FormWindow * window) }, OPAQUE); b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } } @@ -134,61 +145,166 @@ void RadioToolsPage::rebuild(FormWindow * window) #if defined(INTERNAL_MODULE_PXX2) // PXX2 modules tools - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER)) { - new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_INT, [=]() -> uint8_t { - new RadioSpectrumAnalyser(INTERNAL_MODULE); - return 0; - }, 0); + if (isPXX2ModuleOptionAvailable( + reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE] + .information.modelID, + MODULE_OPTION_SPECTRUM_ANALYSER)) { + auto txt = new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND, + CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_INT, + [=]() -> uint8_t { + new RadioSpectrumAnalyser(INTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER)) { - new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), STR_POWER_METER_INT, [=]() -> uint8_t { -// new RadioPowerMeter(INTERNAL_MODULE); - return 0; - }, 0); + if (isPXX2ModuleOptionAvailable( + reusableBuffer.hardwareAndSettings.modules[INTERNAL_MODULE] + .information.modelID, + MODULE_OPTION_POWER_METER)) { + auto txt = new StaticText(window, grid.getLabelSlot(), "access", + BUTTON_BACKGROUND, CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), STR_POWER_METER_INT, + [=]() -> uint8_t { + // new RadioPowerMeter(INTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } #endif #if defined(INTERNAL_MODULE_MULTI) - new StaticText(window, grid.getLabelSlot(), "multi", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_INT, [=]() -> uint8_t { - new RadioSpectrumAnalyser(INTERNAL_MODULE); - return 0; - }, 0); - grid.nextLine(); + { + auto txt = new StaticText(window, grid.getLabelSlot(), "multi", + BUTTON_BACKGROUND, CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_INT, + [=]() -> uint8_t { + new RadioSpectrumAnalyser(INTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); + grid.nextLine(); + } #endif #if defined(PXX2)|| defined(MULTIMODULE) - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_SPECTRUM_ANALYSER) || isModuleMultimodule(EXTERNAL_MODULE)) { - new StaticText(window, grid.getLabelSlot(), isModuleMultimodule(EXTERNAL_MODULE) ? "multi" : "access", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_EXT, [=]() -> uint8_t { - new RadioSpectrumAnalyser(EXTERNAL_MODULE); - return 0; - }, 0); + if (isPXX2ModuleOptionAvailable( + reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE] + .information.modelID, + MODULE_OPTION_SPECTRUM_ANALYSER) || + isModuleMultimodule(EXTERNAL_MODULE)) { + auto txt = new StaticText(window, grid.getLabelSlot(), + isModuleMultimodule(EXTERNAL_MODULE) ? "multi" : "access", + BUTTON_BACKGROUND, CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), STR_SPECTRUM_ANALYSER_EXT, + [=]() -> uint8_t { + new RadioSpectrumAnalyser(EXTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } #endif #if defined(PXX2) - if (isPXX2ModuleOptionAvailable(reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE].information.modelID, MODULE_OPTION_POWER_METER)) { - new StaticText(window, grid.getLabelSlot(), "access", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), STR_POWER_METER_EXT, [=]() -> uint8_t { -// new RadioPowerMeter(EXTERNAL_MODULE); - return 0; - }, 0); - + if (isPXX2ModuleOptionAvailable( + reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE] + .information.modelID, + MODULE_OPTION_POWER_METER)) { + auto txt = new StaticText(window, grid.getLabelSlot(), "access", + BUTTON_BACKGROUND, CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), STR_POWER_METER_EXT, + [=]() -> uint8_t { + // new RadioPowerMeter(EXTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } #endif #if defined(GHOST) if (isModuleGhost(EXTERNAL_MODULE)) { - new StaticText(window, grid.getLabelSlot(), "ghost", BUTTON_BACKGROUND, CENTERED); - new TextButton(window, grid.getFieldSlot(1), "Ghost module config", [=]() -> uint8_t { - new RadioGhostModuleConfig(EXTERNAL_MODULE); - return 0; - }, 0); + auto txt = new StaticText(window, grid.getLabelSlot(), "ghost", + BUTTON_BACKGROUND, CENTERED); + auto b = new TextButton( + window, grid.getFieldSlot(1), "Ghost module config", + [=]() -> uint8_t { + new RadioGhostModuleConfig(EXTERNAL_MODULE); + return 0; + }, + OPAQUE); + b->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); + b->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); grid.nextLine(); } #endif From 13de840ee025e064ad260c2e40e8f9e664bf81f9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 13:48:48 +0200 Subject: [PATCH 067/122] Last round fixes --- radio/src/gui/colorlcd/curve.cpp | 29 ++-- radio/src/gui/colorlcd/curveedit.cpp | 117 +++++++++------- radio/src/gui/colorlcd/curveedit.h | 9 +- radio/src/gui/colorlcd/model_inputs.cpp | 152 +++++++++++---------- radio/src/gui/colorlcd/model_mixes.cpp | 34 +++-- radio/src/gui/colorlcd/model_setup.cpp | 6 +- radio/src/gui/colorlcd/sourcechoice.cpp | 2 + radio/src/gui/colorlcd/switchchoice.cpp | 2 + radio/src/gui/colorlcd/theme.h | 2 + radio/src/gui/colorlcd/view_channels.cpp | 6 +- radio/src/targets/horus/libopenui_config.h | 6 +- radio/src/thirdparty/libopenui | 2 +- 12 files changed, 208 insertions(+), 159 deletions(-) diff --git a/radio/src/gui/colorlcd/curve.cpp b/radio/src/gui/colorlcd/curve.cpp index ee62a79d0..ce796d63d 100644 --- a/radio/src/gui/colorlcd/curve.cpp +++ b/radio/src/gui/colorlcd/curve.cpp @@ -27,21 +27,20 @@ void Curve::drawBackground(BitmapBuffer * dc) { - lcdSetColor(RGB(0xE0, 0xE0, 0xE0)); - dc->clear(CUSTOM_COLOR); + dc->clear(FIELD_BGCOLOR); // Axis - dc->drawSolidHorizontalLine(0, height()/2, width(), DISABLE_COLOR); - dc->drawSolidVerticalLine(width()/2, 0, height(), DISABLE_COLOR); + dc->drawSolidHorizontalLine(0, height()/2, width(), FIELD_FRAME_COLOR); + dc->drawSolidVerticalLine(width()/2, 0, height(), FIELD_FRAME_COLOR); // Extra lines - dc->drawVerticalLine(width()/4, 0, height(), STASHED, DISABLE_COLOR); - dc->drawVerticalLine(width()*3/4, 0, height(), STASHED, DISABLE_COLOR); - dc->drawHorizontalLine(0, height()/4, width(), STASHED, DISABLE_COLOR); - dc->drawHorizontalLine(0, height()*3/4, width(), STASHED, DISABLE_COLOR); + dc->drawVerticalLine(width()/4, 0, height(), STASHED, FIELD_FRAME_COLOR); + dc->drawVerticalLine(width()*3/4, 0, height(), STASHED, FIELD_FRAME_COLOR); + dc->drawHorizontalLine(0, height()/4, width(), STASHED, FIELD_FRAME_COLOR); + dc->drawHorizontalLine(0, height()*3/4, width(), STASHED, FIELD_FRAME_COLOR); // Outside border - // dc->drawSolidRect(0, 0, width(), height(), 1, DEFAULT_COLOR); + dc->drawSolidRect(0, 0, width(), height(), 1, FIELD_FRAME_COLOR); } coord_t Curve::getPointX(int x) const @@ -89,17 +88,17 @@ void Curve::drawPosition(BitmapBuffer * dc) coord_t y = getPointY(valueY); // the 2 lines - dc->drawSolidHorizontalLine(0, y, width(), CURVE_CURSOR_COLOR); - dc->drawSolidVerticalLine(x, 0, height(), CURVE_CURSOR_COLOR); + dc->drawSolidHorizontalLine(0, y, width(), HIGHLIGHT_COLOR); + dc->drawSolidVerticalLine(x, 0, height(), HIGHLIGHT_COLOR); // the point (white inside) - dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT, CURVE_CURSOR_COLOR); - dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT_CENTER, DEFAULT_BGCOLOR); + dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT, HIGHLIGHT_COLOR); + dc->drawBitmapPattern(x-4, y-4, LBM_CURVE_POINT_CENTER, FOCUS_COLOR); char coords[16]; strAppendSigned(strAppend(strAppendSigned(coords, calcRESXto100(valueX)), ","), calcRESXto100(valueY)); - dc->drawSolidFilledRect(10, 11, 1 + getTextWidth(coords, 0, FONT(XS)), 17, CURVE_CURSOR_COLOR); - dc->drawText(11, 10, coords, FONT(XS)|DEFAULT_BGCOLOR); + dc->drawSolidFilledRect(10, 11, 1 + getTextWidth(coords, 0, FONT(XS)), 17, HIGHLIGHT_COLOR); + dc->drawText(11, 10, coords, FONT(XS) | FOCUS_COLOR); } void Curve::drawPoint(BitmapBuffer * dc, const CurvePoint & point) diff --git a/radio/src/gui/colorlcd/curveedit.cpp b/radio/src/gui/colorlcd/curveedit.cpp index 9eb248083..9de50061f 100644 --- a/radio/src/gui/colorlcd/curveedit.cpp +++ b/radio/src/gui/colorlcd/curveedit.cpp @@ -42,48 +42,73 @@ void CurveDataEdit::update() grid.setMarginRight(parent->width() - rect.w + 5); coord_t boxWidth = rect.w / 5; - coord_t boxHeight = (rect.h - 6) / 3; + coord_t boxHeight = PAGE_LINE_HEIGHT; CurveHeader & curve = g_model.curves[index]; uint8_t curvePointsCount = 5 + curve.points; // Point number for (uint8_t i = 0; i < curvePointsCount; i++) { - new StaticText(this, {i * boxWidth, 10, boxWidth, boxHeight}, std::to_string(i + 1), 0, RIGHT | TEXT_DISABLE_COLOR); + auto txt = new StaticText( + this, + {i * boxWidth + PAGE_PADDING, 10, boxWidth - PAGE_PADDING, boxHeight}, + std::to_string(i + 1) + " ", 0, RIGHT | DEFAULT_COLOR); + txt->setBackgroundColor(FIELD_FRAME_COLOR); } - grid.spacer(rect.h / 3); + grid.spacer(boxHeight + 10 + PAGE_PADDING); // x value if (curve.type == CURVE_TYPE_CUSTOM) { // Adjustable points for custom curves for (uint8_t i = 0; i < curvePointsCount; i++) { - int8_t * points = curveAddress(index); - auto pointEdit = new NumberEdit(this, {coord_t(PAGE_LINE_SPACING + 1 + i * boxWidth), 10 + boxHeight, coord_t(boxWidth - PAGE_LINE_SPACING), boxHeight - 12}, - i <= 1 ? -100 : points[curvePointsCount + i - 2], - i >= curvePointsCount - 2 ? 100 : points[curvePointsCount + i], - GET_VALUE(i == 0 ? -100 : i == curvePointsCount - 1 ? 100 : points[curvePointsCount + i - 1]), - [=](int32_t newValue) { - points[curvePointsCount + i - 1] = newValue; - SET_DIRTY(); - curveEdit->updatePreview(); - }, 0, RIGHT); + int8_t* points = curveAddress(index); + auto pointEdit = new NumberEdit( + this, + {coord_t(PAGE_LINE_SPACING + 1 + i * boxWidth), + grid.getWindowHeight(), coord_t(boxWidth - PAGE_LINE_SPACING), + boxHeight}, + i <= 1 ? -100 : points[curvePointsCount + i - 2], + i >= curvePointsCount - 2 ? 100 : points[curvePointsCount + i], + GET_VALUE(i == 0 ? -100 + : i == curvePointsCount - 1 + ? 100 + : points[curvePointsCount + i - 1]), + [=](int32_t newValue) { + points[curvePointsCount + i - 1] = newValue; + SET_DIRTY(); + curveEdit->updatePreview(); + }, + 0, RIGHT); if (i == 0 || i == curvePointsCount - 1) { pointEdit->disable(); } } - } - else { + } else { for (uint8_t i = 0; i < curvePointsCount; i++) { - new StaticText(this, {i * boxWidth, 10 + boxHeight, boxWidth, boxHeight}, std::to_string(-100 + 200 * i / (5 + curve.points - 1)), 0, RIGHT | TEXT_DISABLE_COLOR); + new StaticText(this, {i * boxWidth, grid.getWindowHeight(), boxWidth, boxHeight}, + std::to_string(-100 + 200 * i / (5 + curve.points - 1)), 0, + RIGHT | DEFAULT_COLOR); } } + grid.spacer(boxHeight + PAGE_PADDING); // y value for (uint8_t i = 0; i < curvePointsCount; i++) { - int8_t * points = curveAddress(index); - new NumberEdit(this, {coord_t(PAGE_LINE_SPACING + 1 + i * boxWidth), 10 + 2 * boxHeight, coord_t(boxWidth - PAGE_LINE_SPACING), boxHeight - 12}, -100, 100, - GET_VALUE(points[i]), [=](int32_t newValue) { points[i] = newValue; SET_DIRTY(); curveEdit->updatePreview(); }, 0, RIGHT); + int8_t* points = curveAddress(index); + new NumberEdit( + this, + {coord_t(PAGE_LINE_SPACING + 1 + i * boxWidth), + grid.getWindowHeight(), + coord_t(boxWidth - PAGE_LINE_SPACING), + boxHeight}, + -100, 100, GET_VALUE(points[i]), + [=](int32_t newValue) { + points[i] = newValue; + SET_DIRTY(); + curveEdit->updatePreview(); + }, + 0, RIGHT); } setInnerWidth(curvePointsCount * boxWidth); @@ -92,8 +117,8 @@ void CurveDataEdit::update() void CurveDataEdit::paint(BitmapBuffer * dc) { dc->clear(DEFAULT_BGCOLOR); - dc->drawSolidHorizontalLine(0, rect.h / 3, getInnerWidth(), 0); - dc->drawSolidHorizontalLine(0, 2 * rect.h / 3, getInnerWidth(), 0); + // dc->drawSolidHorizontalLine(0, rect.h / 3, getInnerWidth(), 0); + // dc->drawSolidHorizontalLine(0, 2 * rect.h / 3, getInnerWidth(), 0); drawHorizontalScrollbar(dc); } @@ -125,35 +150,35 @@ void CurveEdit::updatePreview() invalidate(); } -#if defined(HARDWARE_TOUCH) -bool CurveEdit::onTouchEnd(coord_t x, coord_t y) -{ - if (!hasFocus()) { - setFocus(SET_FOCUS_DEFAULT); - } +// #if defined(HARDWARE_TOUCH) +// bool CurveEdit::onTouchEnd(coord_t x, coord_t y) +// { +// if (!hasFocus()) { +// setFocus(SET_FOCUS_DEFAULT); +// } - CurveKeyboard::show(this, isCustomCurve()); +// CurveKeyboard::show(this, isCustomCurve()); - CurveHeader & curve = g_model.curves[index]; - for (int i=0; i<5 + curve.points; i++) { - if (i != current) { - point_t point = getPoint(index, i); - if (abs(preview.getPointX(point.x) - x) <= 10 && abs(preview.getPointY(point.y) - y) <= 10) { - current = i; - updatePreview(); - break; - } - } - } +// CurveHeader & curve = g_model.curves[index]; +// for (int i=0; i<5 + curve.points; i++) { +// if (i != current) { +// point_t point = getPoint(index, i); +// if (abs(preview.getPointX(point.x) - x) <= 10 && abs(preview.getPointY(point.y) - y) <= 10) { +// current = i; +// updatePreview(); +// break; +// } +// } +// } - return true; -} +// return true; +// } -void CurveEdit::onFocusLost() -{ - CurveKeyboard::hide(); -} -#endif +// void CurveEdit::onFocusLost() +// { +// CurveKeyboard::hide(); +// } +// #endif void CurveEdit::next() { diff --git a/radio/src/gui/colorlcd/curveedit.h b/radio/src/gui/colorlcd/curveedit.h index 11efa8f8a..d76de9553 100644 --- a/radio/src/gui/colorlcd/curveedit.h +++ b/radio/src/gui/colorlcd/curveedit.h @@ -62,11 +62,10 @@ class CurveEdit: public FormField void onEvent(event_t event) override; -#if defined(HARDWARE_TOUCH) - bool onTouchEnd(coord_t x, coord_t y) override; - - void onFocusLost() override; -#endif +// #if defined(HARDWARE_TOUCH) +// bool onTouchEnd(coord_t x, coord_t y) override; +// void onFocusLost() override; +// #endif protected: Curve preview; diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp index 14f8d1cbc..815d709e6 100644 --- a/radio/src/gui/colorlcd/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -91,101 +91,104 @@ void deleteExpo(uint8_t idx) storageDirty(EE_MODEL); } -class InputEditWindow: public Page +class InputEditWindow : public Page { - public: - InputEditWindow(int8_t input, uint8_t index): + public: + InputEditWindow(int8_t input, uint8_t index) : Page(ICON_MODEL_INPUTS), input(input), index(index), - preview(this, {INPUT_EDIT_CURVE_LEFT, INPUT_EDIT_CURVE_TOP, INPUT_EDIT_CURVE_WIDTH, INPUT_EDIT_CURVE_HEIGHT}, - [=](int x) -> int { - ExpoData * line = expoAddress(index); - int16_t anas[MAX_INPUTS] = {0}; - applyExpos(anas, e_perout_mode_inactive_flight_mode, line->srcRaw, x); - return anas[line->chn]; - }, - [=]() -> int { - return getValue(expoAddress(index)->srcRaw); - }) - { + preview( + this, + {INPUT_EDIT_CURVE_LEFT, INPUT_EDIT_CURVE_TOP, INPUT_EDIT_CURVE_WIDTH, + INPUT_EDIT_CURVE_HEIGHT}, + [=](int x) -> int { + ExpoData *line = expoAddress(index); + int16_t anas[MAX_INPUTS] = {0}; + applyExpos(anas, e_perout_mode_inactive_flight_mode, line->srcRaw, + x); + return anas[line->chn]; + }, + [=]() -> int { return getValue(expoAddress(index)->srcRaw); }) + { #if LCD_W > LCD_H - body.setWidth(LCD_W - 170); + body.setWidth(LCD_W - preview.width() - PAGE_PADDING); + body.setLeft(preview.width() + PAGE_PADDING); #else - body.setRect({0, INPUT_EDIT_CURVE_TOP + INPUT_EDIT_CURVE_HEIGHT, LCD_W, LCD_H - INPUT_EDIT_CURVE_TOP - INPUT_EDIT_CURVE_HEIGHT}); + body.setRect({0, INPUT_EDIT_CURVE_TOP + INPUT_EDIT_CURVE_HEIGHT, LCD_W, + LCD_H - INPUT_EDIT_CURVE_TOP - INPUT_EDIT_CURVE_HEIGHT}); #endif - buildBody(&body); - buildHeader(&header); - } + buildBody(&body); + buildHeader(&header); + } - void deleteLater(bool detach = true, bool trash = true) override - { - if (_deleted) - return; + void deleteLater(bool detach = true, bool trash = true) override + { + if (_deleted) return; - preview.deleteLater(true, false); + preview.deleteLater(true, false); - Page::deleteLater(detach, trash); - } + Page::deleteLater(detach, trash); + } - protected: - uint8_t input; - uint8_t index; - Curve preview; - Choice * trimChoice = nullptr; - FormGroup * curveParamField = nullptr; + protected: + uint8_t input; + uint8_t index; + Curve preview; + Choice *trimChoice = nullptr; + FormGroup *curveParamField = nullptr; - void buildHeader(Window * window) - { - new StaticText(window, - {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, - PAGE_LINE_HEIGHT}, - STR_MENUINPUTS, 0,FOCUS_COLOR); - new StaticText(window, - {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, - LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - getSourceString(MIXSRC_FIRST_INPUT + input), 0, - FOCUS_COLOR); - } + void buildHeader(Window *window) + { + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MENUINPUTS, 0, FOCUS_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSourceString(MIXSRC_FIRST_INPUT + input), 0, FOCUS_COLOR); + } - // TODO share this code with MIXER - void updateCurveParamField(ExpoData * line) - { - curveParamField->clear(); + // TODO share this code with MIXER + void updateCurveParamField(ExpoData *line) + { + curveParamField->clear(); - const rect_t rect = {0, 0, curveParamField->width(), curveParamField->height()}; + const rect_t rect = {0, 0, curveParamField->width(), + curveParamField->height()}; - switch (line->curve.type) { - case CURVE_REF_DIFF: - case CURVE_REF_EXPO: - { - GVarNumberEdit * edit = new GVarNumberEdit(curveParamField, rect, -100, 100, GET_SET_DEFAULT(line->curve.value)); - edit->setSuffix("%"); - break; - } + switch (line->curve.type) { + case CURVE_REF_DIFF: + case CURVE_REF_EXPO: { + GVarNumberEdit *edit = + new GVarNumberEdit(curveParamField, rect, -100, 100, + GET_SET_DEFAULT(line->curve.value)); + edit->setSuffix("%"); + break; + } - case CURVE_REF_FUNC: - new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1, GET_SET_DEFAULT(line->curve.value)); - break; + case CURVE_REF_FUNC: + new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1, + GET_SET_DEFAULT(line->curve.value)); + break; - case CURVE_REF_CUSTOM: - { - auto choice = new Choice(curveParamField, rect, -MAX_CURVES, MAX_CURVES, GET_SET_DEFAULT(line->curve.value)); - choice->setTextHandler([](int value) { - return getCurveString(value); - }); - break; - } + case CURVE_REF_CUSTOM: { + auto choice = new Choice(curveParamField, rect, -MAX_CURVES, MAX_CURVES, + GET_SET_DEFAULT(line->curve.value)); + choice->setTextHandler([](int value) { return getCurveString(value); }); + break; } } + } - void buildBody(FormWindow * window) - { - FormGridLayout grid; - grid.setLabelWidth(INPUT_EDIT_LABELS_WIDTH); - grid.spacer(PAGE_PADDING); + void buildBody(FormWindow *window) + { + FormGridLayout grid; + grid.setLabelWidth(INPUT_EDIT_LABELS_WIDTH); + grid.spacer(PAGE_PADDING); - ExpoData * line = expoAddress(index) ; + ExpoData *line = expoAddress(index); #if LCD_W > LCD_H grid.setMarginRight(180); @@ -495,6 +498,7 @@ void ModelInputsPage::build(FormWindow *window, int8_t focusIndex) txt->setTextFlags(CENTERED); } txt->invalidate(); + if (focus) button->bringToTop(); }); if (focusIndex == inputIndex) { diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index 5ac524630..7088fb649 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -331,6 +331,21 @@ void ModelMixesPage::editMix(FormWindow * window, uint8_t channel, uint8_t mixIn }); } +class MixLineTitle : public StaticText +{ + public: + using StaticText::StaticText; + + void setTextFlags(LcdFlags flags) + { + textFlags = flags; + for (auto child : children) { + auto bitmap = dynamic_cast(child); + if (bitmap) bitmap->setMaskColor(flags & 0xFFFF0000); + } + } +}; + void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) { FormGridLayout grid; @@ -349,9 +364,9 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) if (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { coord_t h = grid.getWindowHeight(); - auto txt = new StaticText(window, grid.getLabelSlot(), - getSourceString(MIXSRC_CH1 + ch), - BUTTON_BACKGROUND, CENTERED); + auto txt = new MixLineTitle(window, grid.getLabelSlot(), + getSourceString(MIXSRC_CH1 + ch), + BUTTON_BACKGROUND, CENTERED); uint8_t count = 0; while (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { @@ -408,7 +423,10 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) StaticBitmap* bitmap = nullptr; if (count++ > 0) { bitmap = new StaticBitmap( - window, {35, button->top() + (button->height() - 18) / 2, 25, 17}, + txt, + {35 - txt->left(), + button->top() - txt->top() + (button->height() - 18) / 2, 25, + 17}, mixerMultiplexBitmap[mix->mltpx], DEFAULT_COLOR); } @@ -416,14 +434,12 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) if (focus) { txt->setBackgroundColor(FOCUS_BGCOLOR); txt->setTextFlags(FOCUS_COLOR | CENTERED); - if (bitmap) bitmap->setMaskColor(FOCUS_COLOR); } else { txt->setBackgroundColor(FIELD_FRAME_COLOR); txt->setTextFlags(CENTERED); - if (bitmap) bitmap->setMaskColor(DEFAULT_COLOR); } txt->invalidate(); - if (bitmap) bitmap->invalidate(); + if (focus) button->bringToTop(); }); if (focusMixIndex == mixIndex) { @@ -431,10 +447,6 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) txt->setBackgroundColor(FOCUS_BGCOLOR); txt->setTextFlags(FOCUS_COLOR | CENTERED); txt->invalidate(); - if (bitmap) { - bitmap->setMaskColor(FOCUS_COLOR); - bitmap->invalidate(); - } } diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 208633f2c..8fe79bede 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -34,7 +34,11 @@ std::string switchWarninglabel(swsrc_t index) CHAR_DOWN }; - return TEXT_AT_INDEX(STR_VSRCRAW, (index + MIXSRC_FIRST_SWITCH - MIXSRC_Rud + 1)) + std::string(&switchPositions[g_model.switchWarningState >> (3*index) & 0x07], 1); + return TEXT_AT_INDEX(STR_VSRCRAW, + (index + MIXSRC_FIRST_SWITCH - MIXSRC_Rud + 1)) + + std::string( + &switchPositions[g_model.switchWarningState >> (3 * index) & 0x07], + 1); } class ChannelFailsafeBargraph: public Window { diff --git a/radio/src/gui/colorlcd/sourcechoice.cpp b/radio/src/gui/colorlcd/sourcechoice.cpp index f0d1f0e57..68efe32d8 100644 --- a/radio/src/gui/colorlcd/sourcechoice.cpp +++ b/radio/src/gui/colorlcd/sourcechoice.cpp @@ -71,6 +71,8 @@ void SourceChoice::paint(BitmapBuffer * dc) textColor = 0; drawSource(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor); + dc->drawBitmapPattern(rect.w - 20, (rect.h - 11) / 2, LBM_DROPDOWN, + textColor); } // defined in gui/gui_common.cpp diff --git a/radio/src/gui/colorlcd/switchchoice.cpp b/radio/src/gui/colorlcd/switchchoice.cpp index 65292f43e..9db366569 100644 --- a/radio/src/gui/colorlcd/switchchoice.cpp +++ b/radio/src/gui/colorlcd/switchchoice.cpp @@ -55,6 +55,8 @@ void SwitchChoice::paint(BitmapBuffer * dc) textColor = DEFAULT_COLOR; drawSwitch(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, value, textColor); + dc->drawBitmapPattern(rect.w - 20, (rect.h - 11) / 2, LBM_DROPDOWN, + textColor); } void SwitchChoice::fillMenu(Menu * menu, std::function filter) diff --git a/radio/src/gui/colorlcd/theme.h b/radio/src/gui/colorlcd/theme.h index 34b41e7be..9645a3084 100644 --- a/radio/src/gui/colorlcd/theme.h +++ b/radio/src/gui/colorlcd/theme.h @@ -124,4 +124,6 @@ void loadTheme(); std::list & getRegisteredThemes(); +extern const uint8_t LBM_DROPDOWN[]; + #endif // _COLORLCD_THEME_H_ diff --git a/radio/src/gui/colorlcd/view_channels.cpp b/radio/src/gui/colorlcd/view_channels.cpp index ab55fc170..8bfd1905a 100644 --- a/radio/src/gui/colorlcd/view_channels.cpp +++ b/radio/src/gui/colorlcd/view_channels.cpp @@ -46,9 +46,9 @@ class ChannelsViewFooter: public Window { // Draw single legend coord_t drawChannelsMonitorLegend(BitmapBuffer * dc, coord_t x, const char * s, int color) { - dc->drawSolidFilledRect(x, 2, LEG_COLORBOX + 2, LEG_COLORBOX + 2, BARGRAPH_BGCOLOR); - dc->drawSolidFilledRect(x + 1, 3, LEG_COLORBOX, LEG_COLORBOX, color); - dc->drawText(x + 20, 2, s, TEXT_STATUSBAR_COLOR); + dc->drawSolidFilledRect(x, 4, LEG_COLORBOX + 2, LEG_COLORBOX + 2, BARGRAPH_BGCOLOR); + dc->drawSolidFilledRect(x + 1, 5, LEG_COLORBOX, LEG_COLORBOX, color); + dc->drawText(x + 20, 4, s, TEXT_STATUSBAR_COLOR); return x + 25 + getTextWidth(s); } diff --git a/radio/src/targets/horus/libopenui_config.h b/radio/src/targets/horus/libopenui_config.h index 87c7e8f23..2e89621f3 100644 --- a/radio/src/targets/horus/libopenui_config.h +++ b/radio/src/targets/horus/libopenui_config.h @@ -106,9 +106,9 @@ constexpr uint32_t PAGE_TITLE_LEFT = 50; constexpr uint32_t INPUT_EDIT_LABELS_WIDTH = 120; constexpr coord_t INPUT_EDIT_CURVE_WIDTH = 158; -constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = LCD_H - MENU_HEADER_HEIGHT; -constexpr coord_t INPUT_EDIT_CURVE_LEFT = LCD_W - 158; -constexpr coord_t INPUT_EDIT_CURVE_TOP = MENU_HEADER_HEIGHT; +constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = INPUT_EDIT_CURVE_WIDTH; +constexpr coord_t INPUT_EDIT_CURVE_LEFT = PAGE_PADDING; +constexpr coord_t INPUT_EDIT_CURVE_TOP = MENU_HEADER_HEIGHT + PAGE_PADDING; constexpr coord_t MENUS_LINE_HEIGHT = 30; constexpr coord_t MENUS_WIDTH = 200; constexpr coord_t MENUS_OFFSET_TOP = 20; diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 2ef946f14..6b661c490 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 2ef946f14768a23143c59a1ba578e69349b4dacb +Subproject commit 6b661c4907bcafc08436e5c96fc5a38e9eb6e499 From 2bb76f14d421d3e43d07e646982b39294203aafa Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 14:38:03 +0200 Subject: [PATCH 068/122] Fixed telemetry refresh --- radio/src/gui/colorlcd/model_telemetry.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index ca24c6c99..0803eb61b 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -61,6 +61,12 @@ class SensorButton : public Button { void checkEvents() override { + uint32_t now = RTOS_GET_MS(); + if (now - lastRefresh >= 200) { + // update at least every 200ms + invalidate(); + } + TelemetryItem & telemetryItem = telemetryItems[index]; if (telemetryItem.isFresh()) { invalidate(); @@ -73,9 +79,10 @@ class SensorButton : public Button { { TelemetryItem &telemetryItem = telemetryItems[index]; - if (telemetryItem.isFresh()) { + if (telemetryItem.isFresh()) dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, HIGHLIGHT_COLOR); - } + else + dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, FIELD_BGCOLOR); dc->drawNumber(2, 1, number, LEFT, 0, nullptr, ":"); @@ -86,7 +93,7 @@ class SensorButton : public Button { drawSensorCustomValue(dc, SENSOR_COL2, line1, index, getValue(MIXSRC_FIRST_TELEM + 3 * index), LEFT | color); } else { - dc->drawText(SENSOR_COL2, line1, "---", CURVE_COLOR); + dc->drawText(SENSOR_COL2, line1, "---", DEFAULT_COLOR); } TelemetrySensor * sensor = & g_model.telemetrySensors[index]; @@ -96,12 +103,17 @@ class SensorButton : public Button { else if (sensor->type == TELEM_TYPE_CUSTOM && !g_model.ignoreSensorIds) { dc->drawNumber(SENSOR_COL3, line1, sensor->instance, LEFT); } - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); + + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); } protected: uint8_t index; uint8_t number; + uint32_t lastRefresh = 0; }; class SensorEditWindow : public Page { From 3179dc6bf18a2b5737585023927dbc54d8a9f941 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 16:08:24 +0200 Subject: [PATCH 069/122] Fix scrolling issue on input edit page --- radio/src/gui/colorlcd/page.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/page.h b/radio/src/gui/colorlcd/page.h index 4b2afc505..6d1717a91 100644 --- a/radio/src/gui/colorlcd/page.h +++ b/radio/src/gui/colorlcd/page.h @@ -84,7 +84,7 @@ class Page: public Window bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override { - Window::onTouchSlide(x, y, startX, startY, slideX, slideY); + Window::onTouchSlide(x, y, startX, startY, 0/*slideX*/, slideY); return true; } #endif From b610b22328670a86bd0b9a3d5d3d1cb567f5951c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 16:59:37 +0200 Subject: [PATCH 070/122] Fixed custom failsafe screen --- radio/src/gui/colorlcd/model_setup.cpp | 32 ++++++++++---------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 8fe79bede..b7817d2c7 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -84,7 +84,7 @@ class ChannelFailsafeBargraph: public Window { class FailSafeBody : public FormGroup { public: FailSafeBody(FormGroup * parent, const rect_t & rect, uint8_t moduleIdx) : - FormGroup(parent, rect), + FormGroup(parent, rect, FORM_FORWARD_FOCUS), moduleIdx(moduleIdx) { build(); @@ -110,16 +110,17 @@ class FailSafeBody : public FormGroup { new StaticText(this, grid.getLabelSlot(), getSourceString(MIXSRC_CH1 + ch)); // Channel numeric value - new NumberEdit(this, grid.getFieldSlot(3, 0), -lim, lim, - GET_DEFAULT(calcRESXto1000(g_model.failsafeChannels[ch])), + new NumberEdit(this, grid.getFieldSlot(8, 0), -lim, lim, + GET_DEFAULT(g_model.failsafeChannels[ch]), SET_VALUE(g_model.failsafeChannels[ch], newValue), - 0, PREC1); + 0, PREC1 | RIGHT); // Channel bargraph - new ChannelFailsafeBargraph(this, {150, grid.getWindowHeight(), 150, PAGE_LINE_HEIGHT}, moduleIdx, ch); + new ChannelFailsafeBargraph(this, {180, grid.getWindowHeight(), 150, PAGE_LINE_HEIGHT}, moduleIdx, ch); grid.nextLine(); } + grid.spacer(); auto out2fail = new TextButton(this, grid.getLineSlot(), STR_CHANNELS2FAILSAFE); out2fail->setPressHandler([=]() { setCustomFailsafe(moduleIdx); @@ -128,33 +129,24 @@ class FailSafeBody : public FormGroup { }); grid.nextLine(); - setInnerHeight(grid.getWindowHeight()); + setInnerHeight(grid.getWindowHeight() + PAGE_PADDING); } protected: uint8_t moduleIdx; }; -class FailSafeFooter : public Window { - public: - FailSafeFooter(Window * parent, const rect_t & rect) : - Window(parent, rect) - { - } - - void paint(BitmapBuffer * dc) override - { - } -}; - class FailSafePage : public Page { public: explicit FailSafePage(uint8_t moduleIdx) : Page(ICON_STATS_ANALOGS), moduleIdx(moduleIdx) { - new FailSafeBody(&body, {0, 0, LCD_W, height() - footerHeight}, moduleIdx); - new FailSafeFooter(&body, {0, height() - footerHeight, LCD_W, footerHeight}); + new FailSafeBody(&body, {0, 0, LCD_W, body.height()}, moduleIdx); + new StaticText(&header, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_FAILSAFESET, 0, FOCUS_COLOR); } protected: From 058a44c9a75a0fabd685fab48e29de9a2f06d944 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 17:21:42 +0200 Subject: [PATCH 071/122] Fix compiler warning --- radio/src/gui/colorlcd/model_mixes.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index 7088fb649..09804edf1 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -420,9 +420,8 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) return 0; }); - StaticBitmap* bitmap = nullptr; if (count++ > 0) { - bitmap = new StaticBitmap( + new StaticBitmap( txt, {35 - txt->left(), button->top() - txt->top() + (button->height() - 18) / 2, 25, From dea8b3717b05939a8ec3a7e49d2f51eef28d137e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 17:22:17 +0200 Subject: [PATCH 072/122] Fixed memory leak with model select bitmap --- radio/src/gui/colorlcd/model_select.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index e26ed184a..da6209d0e 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -89,6 +89,11 @@ class ModelButton : public Button load(); } + ~ModelButton() + { + if (buffer) { delete buffer; } + } + void load() { uint8_t version; @@ -115,6 +120,7 @@ class ModelButton : public Button } } + delete buffer; buffer = new BitmapBuffer(BMP_RGB565, width(), height()); if (buffer == nullptr) { return; @@ -140,7 +146,10 @@ class ModelButton : public Button void paint(BitmapBuffer *dc) override { FormField::paint(dc); - dc->drawBitmap(0, 0, buffer); + + if (buffer) + dc->drawBitmap(0, 0, buffer); + if (modelCell == modelslist.getCurrentModel()) { dc->drawSolidFilledRect(0, 0, width(), 20, HIGHLIGHT_COLOR); dc->drawSizedText(width() / 2, 2, modelCell->modelName, From f3fbed5a1bad8f0907a1fb3ad026e9a8e98fc884 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Mon, 14 Jun 2021 17:45:31 +0200 Subject: [PATCH 073/122] Fixed AUX serial definition in auxSerialSetup() --- radio/src/targets/common/arm/stm32/aux_serial_driver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp index b1c6816c4..a04c16ed8 100644 --- a/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp +++ b/radio/src/targets/common/arm/stm32/aux_serial_driver.cpp @@ -75,7 +75,7 @@ void auxSerialSetup(unsigned int baudrate, bool dma, uint16_t length, uint16_t p GPIO_ResetBits(AUX_SERIAL_RX_INVERT_GPIO, AUX_SERIAL_RX_INVERT_GPIO_PIN); #endif -#if defined(SERIAL_DMA_Stream_RX) +#if defined(AUX_SERIAL_DMA_Stream_RX) if (dma) { DMA_InitTypeDef DMA_InitStructure; auxSerialRxFifo.clear(); From 7789f5547c470b53388fb0e5aa8fd997c000c417 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 2 Jun 2021 06:49:06 +0200 Subject: [PATCH 074/122] Added touch support for T18 Resolves #138 --- radio/src/targets/horus/CMakeLists.txt | 1 + radio/src/targets/horus/hal.h | 30 ++++++++++++++++++-------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/radio/src/targets/horus/CMakeLists.txt b/radio/src/targets/horus/CMakeLists.txt index 1ff27544a..bfbb3153e 100644 --- a/radio/src/targets/horus/CMakeLists.txt +++ b/radio/src/targets/horus/CMakeLists.txt @@ -106,6 +106,7 @@ if (PCB STREQUAL X10) add_definitions(-DRADIO_FAMILY_T16) option(INTERNAL_MODULE_MULTI "Support for MULTI internal module" ON) option(BLUETOOTH "Support for bluetooth module" OFF) + set(HARDWARE_TOUCH YES) add_definitions(-DMANUFACTURER_JUMPER) else() set(FLAVOUR x10) diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index 82d6d8b2a..752ed29d4 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -707,15 +707,27 @@ #endif // I2C Bus -#define I2C_RCC_AHB1Periph RCC_AHB1Periph_GPIOB -#define I2C_RCC_APB1Periph RCC_APB1Periph_I2C1 -#define I2C I2C1 -#define I2C_GPIO GPIOB -#define I2C_SCL_GPIO_PIN GPIO_Pin_8 // PB.08 -#define I2C_SDA_GPIO_PIN GPIO_Pin_9 // PB.09 -#define I2C_GPIO_AF GPIO_AF_I2C1 -#define I2C_SCL_GPIO_PinSource GPIO_PinSource8 -#define I2C_SDA_GPIO_PinSource GPIO_PinSource9 +#if defined(RADIO_T18) + #define I2C_RCC_AHB1Periph RCC_AHB1Periph_GPIOH + #define I2C_RCC_APB1Periph RCC_APB1Periph_I2C3 + #define I2C I2C3 + #define I2C_GPIO GPIOH + #define I2C_SCL_GPIO_PIN GPIO_Pin_7 // PH.07 + #define I2C_SDA_GPIO_PIN GPIO_Pin_8 // PH.08 + #define I2C_GPIO_AF GPIO_AF_I2C3 + #define I2C_SCL_GPIO_PinSource GPIO_PinSource7 + #define I2C_SDA_GPIO_PinSource GPIO_PinSource8 +#else + #define I2C_RCC_AHB1Periph RCC_AHB1Periph_GPIOB + #define I2C_RCC_APB1Periph RCC_APB1Periph_I2C1 + #define I2C I2C1 + #define I2C_GPIO GPIOB + #define I2C_SCL_GPIO_PIN GPIO_Pin_8 // PB.08 + #define I2C_SDA_GPIO_PIN GPIO_Pin_9 // PB.09 + #define I2C_GPIO_AF GPIO_AF_I2C1 + #define I2C_SCL_GPIO_PinSource GPIO_PinSource8 + #define I2C_SDA_GPIO_PinSource GPIO_PinSource9 +#endif #define I2C_SPEED 400000 // Haptic From 4dd8692191b0336617cb490fd8564a4870f15936 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 2 Jun 2021 07:07:33 +0200 Subject: [PATCH 075/122] Added GT911 config as provided by Jumper (not yet correct ATM) --- radio/src/targets/horus/tp_gt911.cpp | 29 ++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/radio/src/targets/horus/tp_gt911.cpp b/radio/src/targets/horus/tp_gt911.cpp index eb8753e6f..6a57764a9 100644 --- a/radio/src/targets/horus/tp_gt911.cpp +++ b/radio/src/targets/horus/tp_gt911.cpp @@ -22,6 +22,33 @@ #include "tp_gt911.h" #include "touch.h" +#if defined(RADIO_T18) + +// TODO: +// This config has been provided by Jumper, but seems to be wrong: +// - coordinates are inverted, and only parts of the screen is covered +// +const uint8_t TOUCH_GT911_Cfg[] = { + 0x42, 0xE0, 0x01, 0x20, 0x03, 0x05, 0x35, 0x10, 0x01, 0x08, 0x28, 0x0F, + 0x5A, 0x46, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x18, + 0x1A, 0x1E, 0x14, 0x87, 0x28, 0x09, 0x46, 0x48, 0xB2, 0x04, 0x00, 0x00, + 0x00, 0x3A, 0x02, 0x1C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x32, 0x64, 0x94, 0xC5, 0x02, 0x00, 0x00, 0x00, 0x04, + 0xA2, 0x35, 0x00, 0x8F, 0x3D, 0x00, 0x80, 0x46, 0x00, 0x71, 0x51, 0x00, + 0x66, 0x5D, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, + 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1E, 0x1F, + 0x20, 0x21, 0x22, 0x24, 0x26, 0x1D, 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0x01 +}; + +#else + //GT911 param table const uint8_t TOUCH_GT911_Cfg[] = { @@ -211,6 +238,8 @@ const uint8_t TOUCH_GT911_Cfg[] = 0x00 // 0x80FE Reserved }; +#endif + uint8_t touchGT911Flag = 0; uint8_t touchEventOccured = 0; struct TouchData touchData; From c2623aa00ee29fe5391a44a9ec26dc9aee2b5ebe Mon Sep 17 00:00:00 2001 From: Risto Date: Mon, 14 Jun 2021 20:07:15 +0200 Subject: [PATCH 076/122] Show special functions from 1 to 64, instead of 0 to 63 To be more uniform with other elements, such as curves, global variables that start from 0. --- radio/src/gui/colorlcd/special_functions.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index 72814bcd7..92de8cc49 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -76,7 +76,7 @@ class SpecialFunctionEditPage : public Page window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, 20}, - (functions == g_model.customFn ? "SF" : "GF") + std::to_string(index), + (functions == g_model.customFn ? "SF" : "GF") + std::to_string(index+1), 0, FOCUS_COLOR); } @@ -526,7 +526,7 @@ void SpecialFunctionsPage::build(FormWindow *window, int8_t focusIndex) for (uint8_t i = 0; i < MAX_SPECIAL_FUNCTIONS; i++) { CustomFunctionData *cfn = &functions[i]; - strAppendUnsigned(&s[2], i); + strAppendUnsigned(&s[2], i+1); if (cfn->swtch == 0) { auto button = new TextButton(window, grid.getLabelSlot(), s); From 9b670b0e4d0d4e7cf3152b9e9cd8cf9f0e151b32 Mon Sep 17 00:00:00 2001 From: rotorman Date: Mon, 14 Jun 2021 20:47:04 +0200 Subject: [PATCH 077/122] The UARTs of radios are using 3.3V logic level, not TTL / 5V. --- radio/src/translations/cn.h.txt | 2 +- radio/src/translations/cz.h.txt | 2 +- radio/src/translations/de.h.txt | 2 +- radio/src/translations/en.h.txt | 2 +- radio/src/translations/es.h.txt | 2 +- radio/src/translations/fi.h.txt | 2 +- radio/src/translations/fr.h.txt | 2 +- radio/src/translations/it.h.txt | 2 +- radio/src/translations/nl.h.txt | 2 +- radio/src/translations/pl.h.txt | 2 +- radio/src/translations/pt.h.txt | 2 +- radio/src/translations/se.h.txt | 2 +- radio/src/translations/tw.h.txt | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/radio/src/translations/cn.h.txt b/radio/src/translations/cn.h.txt index 8f1b6f2ed..1f0e07dd3 100644 --- a/radio/src/translations/cn.h.txt +++ b/radio/src/translations/cn.h.txt @@ -718,7 +718,7 @@ #define TR_COPROC_TEMP "主板温度" #define TR_CAPAWARNING INDENT "电流过低" #define TR_TEMPWARNING INDENT "过热" -#define TR_TTL_WARNING "注意: 串口仅能使用TTL电平或5V" +#define TR_TTL_WARNING "注意:使用逻辑电平3.3V" #define TR_FUNC "功能" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/cz.h.txt b/radio/src/translations/cz.h.txt index f369d2b88..67ba25ac1 100644 --- a/radio/src/translations/cz.h.txt +++ b/radio/src/translations/cz.h.txt @@ -805,7 +805,7 @@ #define TR_COPROC_TEMP "Tepl. MB \016>" #define TR_CAPAWARNING INDENT "Nízká kapacita" #define TR_TEMPWARNING INDENT "Přehřátí" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Fce." #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/de.h.txt b/radio/src/translations/de.h.txt index 071d63b46..7486afd00 100644 --- a/radio/src/translations/de.h.txt +++ b/radio/src/translations/de.h.txt @@ -810,7 +810,7 @@ #define TR_COPROC_TEMP "MB Temp. \016>" #define TR_CAPAWARNING INDENT "Kapaz. niedrig" // wg 9XR-Pro #define TR_TEMPWARNING INDENT "Temp. größer" //wg 9XR-Pro -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funktion" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 6ebdca2f9..b990e4484 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -814,7 +814,7 @@ #define TR_COPROC_TEMP "MB Temp." #define TR_CAPAWARNING INDENT "Capacity low" #define TR_TEMPWARNING INDENT "Overheat" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Func" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/es.h.txt b/radio/src/translations/es.h.txt index b88667292..cdfdc068f 100644 --- a/radio/src/translations/es.h.txt +++ b/radio/src/translations/es.h.txt @@ -812,7 +812,7 @@ #define TR_COPROC_TEMP "MB Temp. \016>" #define TR_CAPAWARNING INDENT "Capacidad baja" #define TR_TEMPWARNING INDENT "Sobrecalent" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Función" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/fi.h.txt b/radio/src/translations/fi.h.txt index 3d41e884a..17bd0b0c1 100644 --- a/radio/src/translations/fi.h.txt +++ b/radio/src/translations/fi.h.txt @@ -826,7 +826,7 @@ #define TR_COPROC_TEMP "MB Temp. \016>" #define TR_CAPAWARNING INDENT "Capacity Low" #define TR_TEMPWARNING INDENT "Overheat" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Func" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/fr.h.txt b/radio/src/translations/fr.h.txt index a730fda64..d2ac7ff04 100644 --- a/radio/src/translations/fr.h.txt +++ b/radio/src/translations/fr.h.txt @@ -831,7 +831,7 @@ #define TR_COPROC_TEMP "Temp. MB \016>" #define TR_CAPAWARNING INDENT "Capacité Basse" #define TR_TEMPWARNING INDENT "Surchauffe" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Fonction" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/it.h.txt b/radio/src/translations/it.h.txt index a1adc9151..db28fde80 100644 --- a/radio/src/translations/it.h.txt +++ b/radio/src/translations/it.h.txt @@ -832,7 +832,7 @@ #define TR_COPROC_TEMP "Temp. MB \016>" #define TR_CAPAWARNING INDENT "Capacità Bassa" #define TR_TEMPWARNING INDENT "Temp. Alta" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funz" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/nl.h.txt b/radio/src/translations/nl.h.txt index b0bc956d9..4b4783a38 100644 --- a/radio/src/translations/nl.h.txt +++ b/radio/src/translations/nl.h.txt @@ -815,7 +815,7 @@ #define TR_COPROC_TEMP "MB Temp. \016>" #define TR_CAPAWARNING INDENT "Capaciteit laag" // wg 9XR-Pro #define TR_TEMPWARNING INDENT "Overhitting" //wg 9XR-Pro -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funktie" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/pl.h.txt b/radio/src/translations/pl.h.txt index 82ab7cb74..4b14f6f25 100644 --- a/radio/src/translations/pl.h.txt +++ b/radio/src/translations/pl.h.txt @@ -829,7 +829,7 @@ #define TR_COPROC_TEMP "Temp. MB" #define TR_CAPAWARNING INDENT "Mała pojemność" #define TR_TEMPWARNING INDENT "Przegrzanie" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funkc" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/pt.h.txt b/radio/src/translations/pt.h.txt index fe29043b4..0669c02c4 100644 --- a/radio/src/translations/pt.h.txt +++ b/radio/src/translations/pt.h.txt @@ -830,7 +830,7 @@ #define TR_COPROC_TEMP "MB Temp. \016>" #define TR_CAPAWARNING INDENT "Aviso Capacidade" #define TR_TEMPWARNING INDENT "Temperat. ALTA" -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funcao" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/se.h.txt b/radio/src/translations/se.h.txt index 8a790710b..c39926ca7 100644 --- a/radio/src/translations/se.h.txt +++ b/radio/src/translations/se.h.txt @@ -832,7 +832,7 @@ #define TR_COPROC_TEMP "MB temp. \016>" #define TR_CAPAWARNING INDENT "Låg Kapacitet" #define TR_TEMPWARNING TR(INDENT "Hög Temp ", INDENT "Hög Temperatur") -#define TR_TTL_WARNING "Warning: use only TLL or 5v" +#define TR_TTL_WARNING "Warning: use 3.3V logic levels" #define TR_FUNC "Funk" #define TR_V1 "V1" #define TR_V2 "V2" diff --git a/radio/src/translations/tw.h.txt b/radio/src/translations/tw.h.txt index 7dba22e07..0913cd8f4 100644 --- a/radio/src/translations/tw.h.txt +++ b/radio/src/translations/tw.h.txt @@ -718,7 +718,7 @@ #define TR_COPROC_TEMP "主板溫度" #define TR_CAPAWARNING INDENT "電流過低" #define TR_TEMPWARNING INDENT "過熱" -#define TR_TTL_WARNING "注意: 端口僅能使用TTL電平或5V" +#define TR_TTL_WARNING "注意:使用逻辑电平3.3V" #define TR_FUNC "功能" #define TR_V1 "V1" #define TR_V2 "V2" From c55d915d56708997f2cb778cf861dcefe6b9e980 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 15 Jun 2021 06:15:53 +0200 Subject: [PATCH 078/122] Fixed MPM proto subtype reset on subtype change --- radio/src/gui/128x64/model_setup.cpp | 1 + radio/src/pulses/modules_helpers.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/128x64/model_setup.cpp b/radio/src/gui/128x64/model_setup.cpp index 12ddf963d..08f1072c1 100644 --- a/radio/src/gui/128x64/model_setup.cpp +++ b/radio/src/gui/128x64/model_setup.cpp @@ -1576,6 +1576,7 @@ void menuModelSetup(event_t event) if (MULTIMODULE_PROTOCOL_KNOWN(moduleIdx)) { const char * title = getMultiOptionTitle(moduleIdx); + if (!title) break; lcdDrawText(INDENT_WIDTH, y, title); if (title == STR_MULTI_RFTUNE) { lcdDrawText(MODEL_SETUP_2ND_COLUMN + 23, y, "RSSI(", LEFT); diff --git a/radio/src/pulses/modules_helpers.h b/radio/src/pulses/modules_helpers.h index 227aa974d..c33a433c4 100644 --- a/radio/src/pulses/modules_helpers.h +++ b/radio/src/pulses/modules_helpers.h @@ -678,7 +678,6 @@ inline void resetMultiProtocolsOptions(uint8_t moduleIdx) else { g_model.moduleData[moduleIdx].multi.autoBindMode = 0; } - g_model.moduleData[moduleIdx].subType = 0; g_model.moduleData[moduleIdx].multi.optionValue = 0; g_model.moduleData[moduleIdx].multi.disableTelemetry = 0; g_model.moduleData[moduleIdx].multi.disableMapping = 0; From f4b50cee9dc34879ca71252c7f5eb4b76d1d5616 Mon Sep 17 00:00:00 2001 From: norulers Date: Tue, 15 Jun 2021 00:01:53 +0800 Subject: [PATCH 079/122] Update T18 gt911 configuration --- radio/src/targets/horus/tp_gt911.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/radio/src/targets/horus/tp_gt911.cpp b/radio/src/targets/horus/tp_gt911.cpp index 6a57764a9..2f973d49c 100644 --- a/radio/src/targets/horus/tp_gt911.cpp +++ b/radio/src/targets/horus/tp_gt911.cpp @@ -29,22 +29,22 @@ // - coordinates are inverted, and only parts of the screen is covered // const uint8_t TOUCH_GT911_Cfg[] = { - 0x42, 0xE0, 0x01, 0x20, 0x03, 0x05, 0x35, 0x10, 0x01, 0x08, 0x28, 0x0F, - 0x5A, 0x46, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x18, - 0x1A, 0x1E, 0x14, 0x87, 0x28, 0x09, 0x46, 0x48, 0xB2, 0x04, 0x00, 0x00, - 0x00, 0x3A, 0x02, 0x1C, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x32, 0x64, 0x94, 0xC5, 0x02, 0x00, 0x00, 0x00, 0x04, - 0xA2, 0x35, 0x00, 0x8F, 0x3D, 0x00, 0x80, 0x46, 0x00, 0x71, 0x51, 0x00, - 0x66, 0x5D, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xE0, 0x01, 0x10, 0x01, 0x05, 0x0C, 0x00, 0x01, 0x18, 0x28, 0x0F, + 0x50, 0x3C, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x18, + 0x1A, 0x1E, 0x14, 0x87, 0x29, 0x0A, 0x4B, 0x4D, 0xD3, 0x07, 0x00, 0x00, + 0x00, 0x19, 0x02, 0x10, 0x32, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x32, 0x73, 0x94, 0xD5, 0x02, 0x07, 0x00, 0x00, 0x04, + 0xAE, 0x36, 0x00, 0x94, 0x40, 0x00, 0x7E, 0x4C, 0x00, 0x6D, 0x59, 0x00, + 0x5D, 0x6A, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, 0x10, - 0x12, 0x14, 0x16, 0x18, 0x1A, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1E, 0x1F, - 0x20, 0x21, 0x22, 0x24, 0x26, 0x1D, 0x0C, 0x0A, 0x08, 0x06, 0x04, 0x02, - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, + 0x1A, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x02, + 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, + 0x26, 0x28, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x6F, 0x01 + 0xFF, 0xFF, 0xFF, 0xFF, 0x78, 0x01 }; #else From 5e03b762ea84af73a49cd607fa7495e48a26320a Mon Sep 17 00:00:00 2001 From: norulers Date: Tue, 15 Jun 2021 14:42:59 +0800 Subject: [PATCH 080/122] Update gt911 config version --- radio/src/targets/horus/tp_gt911.cpp | 207 ++++++++++++++++++++++++--- radio/src/targets/horus/tp_gt911.h | 2 +- 2 files changed, 186 insertions(+), 23 deletions(-) diff --git a/radio/src/targets/horus/tp_gt911.cpp b/radio/src/targets/horus/tp_gt911.cpp index 2f973d49c..6915a4a90 100644 --- a/radio/src/targets/horus/tp_gt911.cpp +++ b/radio/src/targets/horus/tp_gt911.cpp @@ -22,29 +22,192 @@ #include "tp_gt911.h" #include "touch.h" -#if defined(RADIO_T18) - -// TODO: -// This config has been provided by Jumper, but seems to be wrong: -// - coordinates are inverted, and only parts of the screen is covered -// +#if defined (RADIO_T18) const uint8_t TOUCH_GT911_Cfg[] = { - 0xFF, 0xE0, 0x01, 0x10, 0x01, 0x05, 0x0C, 0x00, 0x01, 0x18, 0x28, 0x0F, - 0x50, 0x3C, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x18, - 0x1A, 0x1E, 0x14, 0x87, 0x29, 0x0A, 0x4B, 0x4D, 0xD3, 0x07, 0x00, 0x00, - 0x00, 0x19, 0x02, 0x10, 0x32, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x32, 0x73, 0x94, 0xD5, 0x02, 0x07, 0x00, 0x00, 0x04, - 0xAE, 0x36, 0x00, 0x94, 0x40, 0x00, 0x7E, 0x4C, 0x00, 0x6D, 0x59, 0x00, - 0x5D, 0x6A, 0x00, 0x5D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0C, 0x0E, 0x10, 0x12, 0x14, 0x16, 0x18, - 0x1A, 0x1C, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x02, - 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x24, - 0x26, 0x28, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0x78, 0x01 + GT911_CFG_NUMER, // 0x8047 Config version + 0xE0, // 0x8048 X output map : x 480 + 0x01, + 0x10, // 0x804A Y ouptut max : y 272 + 0x01, + GT911_MAX_TP, // 0x804C Touch number + 0x0C, // 0x804D Module switch 1 : bit4= xy change Int mode + 0x00, // 0x804E Module switch 2 + 0x01, // 0x804F Shake_Count + 0x18, // 0x8050 Filter + 0x28, // 0x8051 Larger touch + 0x0F, // 0x8052 Noise reduction + 0x50, // 0x8053 Screen touch level + 0x3C, // 0x8054 Screen touch leave + 0x03, // 0x8055 Low power control + 0x05, // 0x8056 Refresh rate + 0x00, // 0x8057 X threshold + 0x00, // 0x8058 Y threshold + 0x00, // 0x8059 Reserved + 0x00, // 0x805A Reserved + 0x00, // 0x805B Space (top, bottom) + 0x00, // 0x805C Space (left, right) + 0x04, // 0x805D Mini filter + 0x18, // 0x805E Strech R0 + 0x1A, // 0x805F Strech R1 + 0x1E, // 0x8060 Strech R2 + 0x14, // 0x8061 Strech RM + 0x87, // 0x8062 Drv groupA num + 0x29, // 0x8063 Drv groupB num + 0x0A, // 0x8064 Sensor num + 0x4B, // 0x8065 FreqA factor + 0x4D, // 0x8066 FreqB factor + 0xD3, // 0x8067 Panel bit freq + 0x07, + 0x00, // 0x8069 Reserved + 0x00, + 0x00, // 0x806B Panel tx gain + 0x19, // 0x806C Panel rx gain + 0x02, // 0x806D Panel dump shift + 0x10, // 0x806E Drv frame control + 0x32, // 0x806F Charging level up + 0x00, // 0x8070 Module switch 3 + 0x00, // 0x8071 Gesture distance + 0x00, // 0x8072 Gesture long press time + 0x00, // 0x8073 X/Y slope adjust + 0x03, // 0x8074 Gesture control + 0x00, // 0x8075 Gesture switch 1 + 0x00, // 0x8076 Gesture switch 2 + 0x00, // 0x8077 Gesture refresh rate + 0x00, // 0x8078 Gesture touch level + 0x00, // 0x8079 New green wake up level + 0x32, // 0x807A Freq hopping start + 0x73, // 0x807B Freq hopping end + 0x94, // 0x807C Noise detect time + 0xD5, // 0x807D Hopping flag + 0x02, // 0x807E Hopping flag + 0x07, // 0x807F Noise threshold + 0x00, // 0x8080 Nois min threshold old + 0x00, // 0x8081 Reserved + 0x04, // 0x8082 Hopping sensor group + 0xAE, // 0x8083 Hopping Seg1 normalize + 0x36, // 0x8084 Hopping Seg1 factor + 0x00, // 0x8085 Main clock adjust + 0x94, // 0x8086 Hopping Seg2 normalize + 0x40, // 0x8087 Hopping Seg2 factor + 0x00, // 0x8088 Reserved + 0x7E, // 0x8089 Hopping Seg3 normalize + 0x4C, // 0x808A Hopping Seg3 factor + 0x00, // 0x808B Reserved + 0x6D, // 0x808C Hopping Seg4 normalize + 0x59, // 0x808D Hopping Seg4 factor + 0x00, // 0x808E Reserved + 0x5D, // 0x808F Hopping Seg5 normalize + 0x6A, // 0x8090 Hopping Seg5 factor + 0x00, // 0x8091 Reserved + 0x5D, // 0x8092 Hopping Seg6 normalize + 0x00, // 0x8093 Key1 + 0x00, // 0x8094 Key2 + 0x00, // 0x8095 Key3 + 0x00, // 0x8096 Key4 + 0x00, // 0x8097 Key area + 0x00, // 0x8098 Key touch level + 0x00, // 0x8099 Key leave level + 0x00, // 0x809A Key sens + 0x00, // 0x809B Key sens + 0x00, // 0x809C Key restrain + 0x00, // 0x809D Key restrain time + 0x00, // 0x809E Large gesture touch + 0x00, // 0x809F Reserved + 0x00, // 0x80A0 Reserved + 0x00, // 0x80A1 Hotknot noise map + 0x00, // 0x80A2 Link threshold + 0x00, // 0x80A3 Pxy threshold + 0x00, // 0x80A4 GHot dump shift + 0x00, // 0x80A5 GHot rx gain + 0x00, // 0x80A6 Freg gain + 0x00, // 0x80A7 Freg gain 1 + 0x00, // 0x80A8 Freg gain 2 + 0x00, // 0x80A9 Freg gain 3 + 0x00, // 0x80AA Reserved + 0x00, // 0x80AB Reserved + 0x00, // 0x80AC Reserved + 0x00, // 0x80AD Reserved + 0x00, // 0x80AE Reserved + 0x00, // 0x80AF Reserved + 0x00, // 0x80B0 Reserved + 0x00, // 0x80B1 Reserved + 0x00, // 0x80B2 Reserved + 0x00, // 0x80B3 Combine dis + 0x00, // 0x80B4 Split set + 0x00, // 0x80B5 Reserved + 0x00, // 0x80B6 Reserved + 0x0A, // 0x80B7 Sensor CH0 + 0x0C, // 0x80B8 Sensor CH1 + 0x0E, // 0x80B9 Sensor CH2 + 0x10, // 0x80BA Sensor CH3 + 0x12, // 0x80BB Sensor CH4 + 0x14, // 0x80BC Sensor CH5 + 0x16, // 0x80BD Sensor CH6 + 0x18, // 0x80BE Sensor CH7 + 0x1A, // 0x80BF Sensor CH8 + 0x1C, // 0x80C0 Sensor CH9 + 0xFF, // 0x80C1 Sensor CH10 + 0xFF, // 0x80C2 Sensor CH11 + 0xFF, // 0x80C3 Sensor CH12 + 0xFF, // 0x80C4 Sensor CH13 + 0xFF, // 0x80C5 Reserved + 0xFF, // 0x80C6 Reserved + 0xFF, // 0x80C7 Reserved + 0xFF, // 0x80C8 Reserved + 0xFF, // 0x80C9 Reserved + 0xFF, // 0x80CA Reserved + 0xFF, // 0x80CB Reserved + 0xFF, // 0x80CC Reserved + 0xFF, // 0x80CD Reserved + 0xFF, // 0x80CE Reserved + 0xFF, // 0x80CF Reserved + 0xFF, // 0x80D0 Reserved + 0xFF, // 0x80D1 Reserved + 0xFF, // 0x80D2 Reserved + 0xFF, // 0x80D3 Reserved + 0xFF, // 0x80D4 Reserved + 0x00, // 0x80D5 Driver CH0 + 0x02, // 0x80D6 Driver CH1 + 0x04, // 0x80D7 Driver CH2 + 0x06, // 0x80D8 Driver CH3 + 0x08, // 0x80D9 Driver CH4 + 0x0A, // 0x80DA Driver CH5 + 0x0C, // 0x80DB Driver CH6 + 0x1D, // 0x80DC Driver CH7 + 0x1E, // 0x80DD Driver CH8 + 0x1F, // 0x80DE Driver CH9 + 0x20, // 0x80DF Driver CH10 + 0x21, // 0x80E0 Driver CH11 + 0x22, // 0x80E1 Driver CH12 + 0x24, // 0x80E2 Driver CH13 + 0x26, // 0x80E3 Driver CH14 + 0x28, // 0x80E4 Driver CH15 + 0xFF, // 0x80E5 Driver CH16 + 0xFF, // 0x80E6 Driver CH17 + 0xFF, // 0x80E7 Driver CH18 + 0xFF, // 0x80E8 Driver CH19 + 0xFF, // 0x80E9 Driver CH20 + 0xFF, // 0x80EA Driver CH21 + 0xFF, // 0x80EB Driver CH22 + 0xFF, // 0x80EC Driver CH23 + 0xFF, // 0x80ED Driver CH24 + 0xFF, // 0x80EE Driver CH25 + 0xFF, // 0x80EF Reserved + 0xFF, // 0x80F0 Reserved + 0xFF, // 0x80F1 Reserved + 0xFF, // 0x80F2 Reserved + 0xFF, // 0x80F3 Reserved + 0xFF, // 0x80F4 Reserved + 0xFF, // 0x80F5 Reserved + 0xFF, // 0x80F6 Reserved + 0xFF, // 0x80F7 Reserved + 0xFF, // 0x80F8 Reserved + 0xFF, // 0x80F9 Reserved + 0xFF, // 0x80FA Reserved + 0xFF, // 0x80FB Reserved + 0xFF, // 0x80FC Reserved + 0xFF, // 0x80FD Reserved + 0xFF // 0x80FE Reserved }; #else diff --git a/radio/src/targets/horus/tp_gt911.h b/radio/src/targets/horus/tp_gt911.h index 624db2c92..ce48a680f 100644 --- a/radio/src/targets/horus/tp_gt911.h +++ b/radio/src/targets/horus/tp_gt911.h @@ -30,7 +30,7 @@ void touchPanelRead(); bool touchPanelEventOccured(); #define GT911_MAX_TP 5 -#define GT911_CFG_NUMER 0x6A +#define GT911_CFG_NUMER 0x6C //I2C #define GT_CMD_WR 0X28 From 5471b233f2b95324e91aa7ddb67a8f158254b88b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 15 Jun 2021 11:59:52 +0200 Subject: [PATCH 081/122] Fix models list loading Resolves #218 --- radio/src/storage/conversions/conversions.cpp | 5 ++++- radio/src/storage/sdcard_common.cpp | 6 +++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/radio/src/storage/conversions/conversions.cpp b/radio/src/storage/conversions/conversions.cpp index 18e52d6a0..7071b7540 100644 --- a/radio/src/storage/conversions/conversions.cpp +++ b/radio/src/storage/conversions/conversions.cpp @@ -75,7 +75,10 @@ void convertRadioData(int version) #if defined(SIMU) RTOS_WAIT_MS(200); #endif - + + // Load models list before converting + modelslist.load(); + const char* error = nullptr; for (auto category_ptr : modelslist.getCategories()) { for (auto model_ptr : *category_ptr) { diff --git a/radio/src/storage/sdcard_common.cpp b/radio/src/storage/sdcard_common.cpp index cdf755ee8..4f3a7b910 100644 --- a/radio/src/storage/sdcard_common.cpp +++ b/radio/src/storage/sdcard_common.cpp @@ -149,9 +149,6 @@ void storageReadAll() // it's being reloaded after USB connection modelslist.clear(); - // and reload the list - modelslist.load(); - if (loadRadioSettings() != nullptr) { storageEraseAll(true); } @@ -163,6 +160,9 @@ void storageReadAll() } } + // and reload the list + modelslist.load(); + // Current model filename is empty... // Let's fix it! if (strlen(g_eeGeneral.currModelFilename) == 0) { From ba7ce9e911c5628f0a0fdceb9aeffdda33080139 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 15 Jun 2021 12:01:28 +0200 Subject: [PATCH 082/122] Fixed model select frame size calculation Resolves #219 --- radio/src/gui/colorlcd/model_select.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index da6209d0e..9a4a0115b 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -277,7 +277,9 @@ class ModelCategoryPageBody : public FormWindow #endif } - y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING; + if (index % MODEL_CELLS_PER_LINE != 0) { + y += MODEL_SELECT_CELL_HEIGHT + MODEL_CELL_PADDING; + } setInnerHeight(y); if (category->empty()) { From 95c65772e43cea3885e269c6833c192e71bbba55 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 15 Jun 2021 15:42:24 +0200 Subject: [PATCH 083/122] Added X9D (not "+") --- .github/workflows/actions.yml | 1 + .github/workflows/nightly.yml | 1 + tools/build-gh.sh | 3 +++ 3 files changed, 5 insertions(+) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index 76fb8130a..a4dff18f0 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -29,6 +29,7 @@ jobs: - X10 - X12S - X7 + - X9D - X9DP - X9DP2019 - X9LITE diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index e3c663190..b4a213c74 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -28,6 +28,7 @@ jobs: - X10 - X12S - X7 + - X9D - X9DP - X9DP2019 - X9LITE diff --git a/tools/build-gh.sh b/tools/build-gh.sh index 2ccc0f914..528024fa8 100755 --- a/tools/build-gh.sh +++ b/tools/build-gh.sh @@ -100,6 +100,9 @@ case $FLAVOR in XLITES) BUILD_OPTIONS+="-DPCB=XLITES" ;; + X9D) + BUILD_OPTIONS+="-DPCB=X9D" + ;; X9DP) BUILD_OPTIONS+="-DPCB=X9D+" ;; From b8511a2b2275dc4d777f861ced8557a41e3da9b7 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 15 Jun 2021 16:19:43 +0200 Subject: [PATCH 084/122] Fix lcd.RGB() It does not require access to lcd, so let's not check that. --- radio/src/lua/api_colorlcd.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/radio/src/lua/api_colorlcd.cpp b/radio/src/lua/api_colorlcd.cpp index c83229c16..c30d73bb4 100644 --- a/radio/src/lua/api_colorlcd.cpp +++ b/radio/src/lua/api_colorlcd.cpp @@ -834,9 +834,6 @@ Returns a 5/6/5 rgb color code, that can be used with lcd.setColor */ static int luaRGB(lua_State *L) { - if (!luaLcdAllowed) - return 0; - int r = luaL_checkinteger(L, 1); int g = luaL_checkinteger(L, 2); int b = luaL_checkinteger(L, 3); From 60517df3496253d7ce9b2ee0dde72ac8abd8da5d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 15 Jun 2021 16:24:54 +0200 Subject: [PATCH 085/122] Removed necessity for lcd allowance in lcd.setColor() and lcd.getColor() --- radio/src/lua/api_colorlcd.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/radio/src/lua/api_colorlcd.cpp b/radio/src/lua/api_colorlcd.cpp index c30d73bb4..cff137481 100644 --- a/radio/src/lua/api_colorlcd.cpp +++ b/radio/src/lua/api_colorlcd.cpp @@ -783,9 +783,6 @@ Set a color for specific area */ static int luaLcdSetColor(lua_State *L) { - if (!luaLcdAllowed) - return 0; - unsigned int index = luaL_checkunsigned(L, 1) >> 16; unsigned int color = luaL_checkunsigned(L, 2); @@ -806,9 +803,6 @@ Get the color for specific area : see lcd.setColor for area list static int luaLcdGetColor(lua_State *L) { - if (!luaLcdAllowed) - return 0; - unsigned int index = luaL_checkunsigned(L, 1) >> 16; lua_pushunsigned(L, lcdColorTable[index]); From 8b92b3be982c3e9556a81e404b5390890e7d35b6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 15 Jun 2021 17:37:13 +0200 Subject: [PATCH 086/122] [Color] Some SD manager fixes --- radio/src/gui/colorlcd/radio_sdmanager.cpp | 38 +++++++++++++++------- radio/src/gui/colorlcd/radio_tools.cpp | 10 ++++-- radio/src/gui/colorlcd/tabsgroup.cpp | 5 +-- radio/src/gui/colorlcd/tabsgroup.h | 14 ++------ 4 files changed, 40 insertions(+), 27 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index de720ed17..cd442c3c1 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -92,6 +92,10 @@ class FileNameEditWindow : public Page RadioSdManagerPage::RadioSdManagerPage() : PageTab(SD_IS_HC() ? STR_SDHC_CARD : STR_SD_CARD, ICON_RADIO_SD_MANAGER) { + setOnSetVisibleHandler([]() { + TRACE("f_chdir(ROOT_PATH)"); + f_chdir(ROOT_PATH); + }); } void RadioSdManagerPage::rebuild(FormWindow * window) @@ -115,6 +119,13 @@ char * getFullPath(const std::string &filename) return full_path; } +char * getCurrentPath() +{ + static char path[FF_MAX_LFN + 1]; // TODO optimize that! + f_getcwd((TCHAR*)path, FF_MAX_LFN); + return path; +} + class FilePreview : public Window { public: @@ -209,6 +220,7 @@ void RadioSdManagerPage::build(FormWindow * window) std::list files; std::list directories; + std::string currentPath(getCurrentPath()); auto preview = new FilePreview(window, {LCD_W / 2 + 6, 0, LCD_W / 2 - 16, window->height()}); FRESULT res = f_opendir(&dir, "."); // Open the directory @@ -238,7 +250,8 @@ void RadioSdManagerPage::build(FormWindow * window) for (auto name: directories) { auto b = new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t { - f_chdir(name.data()); + std::string fullpath = currentPath + "/" + name; + f_chdir(fullpath.c_str()); window->clear(); build(window); return 0; @@ -250,6 +263,7 @@ void RadioSdManagerPage::build(FormWindow * window) for (auto name: files) { auto button = new TextButton(window, grid.getLabelSlot(), name, [=]() -> uint8_t { auto menu = new Menu(window); + f_chdir(currentPath.c_str()); const char *ext = getFileExtension(name.data()); if (ext) { if (!strcasecmp(ext, SOUNDS_EXT)) { @@ -286,14 +300,14 @@ void RadioSdManagerPage::build(FormWindow * window) dialog->flash(getFullPath(name)); }); } - else if (isExtensionMatching(ext, BITMAPS_EXT)) { - // TODO - } - else if (!strcasecmp(ext, TEXT_EXT)) { - menu->addLine(STR_VIEW_TEXT, [=]() { - // TODO - }); - } + // else if (isExtensionMatching(ext, BITMAPS_EXT)) { + // // TODO + // } + // else if (!strcasecmp(ext, TEXT_EXT)) { + // menu->addLine(STR_VIEW_TEXT, [=]() { + // // TODO + // }); + // } if (!READ_ONLY() && !strcasecmp(ext, FIRMWARE_EXT)) { if (isBootloader(name.data())) { menu->addLine(STR_FLASH_BOOTLOADER, [=]() { @@ -328,8 +342,9 @@ void RadioSdManagerPage::build(FormWindow * window) } #if defined(LUA) else if (isExtensionMatching(ext, SCRIPTS_EXT)) { + std::string fullpath = currentPath + "/" + name; menu->addLine(STR_EXECUTE_FILE, [=]() { - luaExec(getFullPath(name)); + luaExec(fullpath.c_str()); //TODO: check 'luaState' StandaloneLuaWindow::instance()->attach(window); }); @@ -374,7 +389,8 @@ void RadioSdManagerPage::build(FormWindow * window) }); } return 0; - }, 0); + }, OPAQUE); + button->setBgColorHandler([=]() -> LcdFlags { return FIELD_BGCOLOR; }); button->setFocusHandler([=](bool active) { if (active) { preview->setFile(getFullPath(name)); diff --git a/radio/src/gui/colorlcd/radio_tools.cpp b/radio/src/gui/colorlcd/radio_tools.cpp index 3484f2495..88c71033e 100644 --- a/radio/src/gui/colorlcd/radio_tools.cpp +++ b/radio/src/gui/colorlcd/radio_tools.cpp @@ -119,10 +119,14 @@ void RadioToolsPage::rebuild(FormWindow * window) auto b = new TextButton( window, grid.getFieldSlot(1), label, [window, path_str]() -> uint8_t { - f_chdir("/SCRIPTS/TOOLS/"); - luaExec(path_str.c_str()); - // TODO: check 'luaState' + + char toolPath[FF_MAX_LFN + 1]; + strncpy(toolPath, path_str.c_str(), sizeof(toolPath)-1); + *((char *)getBasename(toolPath)-1) = '\0'; + f_chdir(toolPath); + StandaloneLuaWindow::instance()->attach(window); + luaExec(path_str.c_str()); return 0; }, OPAQUE); diff --git a/radio/src/gui/colorlcd/tabsgroup.cpp b/radio/src/gui/colorlcd/tabsgroup.cpp index a0b7c32a8..f3ea3163b 100644 --- a/radio/src/gui/colorlcd/tabsgroup.cpp +++ b/radio/src/gui/colorlcd/tabsgroup.cpp @@ -146,9 +146,10 @@ void TabsGroup::setVisibleTab(PageTab * tab) Keyboard::hide(); #endif currentTab = tab; + if (tab->onSetVisible) tab->onSetVisible(); tab->build(&body); - if (!focusWindow) - setFocus(SET_FOCUS_DEFAULT); + if (!focusWindow) setFocus(SET_FOCUS_DEFAULT); + header.setTitle(tab->title.c_str()); invalidate(); } diff --git a/radio/src/gui/colorlcd/tabsgroup.h b/radio/src/gui/colorlcd/tabsgroup.h index d3af8c15d..1618bf7a0 100644 --- a/radio/src/gui/colorlcd/tabsgroup.h +++ b/radio/src/gui/colorlcd/tabsgroup.h @@ -50,17 +50,9 @@ class PageTab { { } - void destroy() + void setOnSetVisibleHandler(std::function handler) { - if (onPageDestroyed) { - onPageDestroyed(); - } - delete this; - } - - void setOnPageDestroyedHandler(std::function handler) - { - onPageDestroyed = std::move(handler); + onSetVisible = std::move(handler); } void setTitle(std::string value) @@ -81,7 +73,7 @@ class PageTab { protected: std::string title; unsigned icon = 0; - std::function onPageDestroyed; + std::function onSetVisible; }; class TabsCarousel: public Window { From 8cc5e7777561cea16df227579a9ae6d5a34e6714 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 15 Jun 2021 17:37:46 +0200 Subject: [PATCH 087/122] [Color] disabled PowerMeter and Ghost tool for now --- radio/src/gui/colorlcd/radio_tools.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radio/src/gui/colorlcd/radio_tools.cpp b/radio/src/gui/colorlcd/radio_tools.cpp index 88c71033e..9f83dda45 100644 --- a/radio/src/gui/colorlcd/radio_tools.cpp +++ b/radio/src/gui/colorlcd/radio_tools.cpp @@ -259,6 +259,7 @@ void RadioToolsPage::rebuild(FormWindow * window) } #endif #if defined(PXX2) +#if 0 // disabled Power Meter: not yet implemented if (isPXX2ModuleOptionAvailable( reusableBuffer.hardwareAndSettings.modules[EXTERNAL_MODULE] .information.modelID, @@ -286,8 +287,10 @@ void RadioToolsPage::rebuild(FormWindow * window) grid.nextLine(); } #endif +#endif #if defined(GHOST) +#if 0 if (isModuleGhost(EXTERNAL_MODULE)) { auto txt = new StaticText(window, grid.getLabelSlot(), "ghost", BUTTON_BACKGROUND, CENTERED); @@ -311,6 +314,7 @@ void RadioToolsPage::rebuild(FormWindow * window) }); grid.nextLine(); } +#endif #endif window->setInnerHeight(grid.getWindowHeight()); From a0090f4e0076037ea1a6e5426810104161c672bc Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 16 Jun 2021 05:47:40 +0200 Subject: [PATCH 088/122] [Color] Prevent ModelBmp from reloading the bitmap when not needed Resolves #214 --- radio/src/gui/colorlcd/widgets/modelbmp.cpp | 43 +++++++++++---------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/radio/src/gui/colorlcd/widgets/modelbmp.cpp b/radio/src/gui/colorlcd/widgets/modelbmp.cpp index 0d5a8d3cd..4c2d96c12 100644 --- a/radio/src/gui/colorlcd/widgets/modelbmp.cpp +++ b/radio/src/gui/colorlcd/widgets/modelbmp.cpp @@ -29,48 +29,44 @@ class ModelBitmapWidget: public Widget ModelBitmapWidget(const WidgetFactory * factory, FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData): Widget(factory, parent, rect, persistentData) { + loadBitmap(); } void refresh(BitmapBuffer * dc) override { - if (buffer && ((buffer->width() != width()) || (buffer->height() != height()))) { + if (buffer && + ((buffer->width() != width()) || (buffer->height() != height()) || + (deps_hash != getHash()))) { loadBitmap(); } // big space to draw if (rect.h >= 96 && rect.w >= 120) { - dc->drawFilledRect(0, 0, rect.w, rect.h, SOLID, MAINVIEW_PANES_COLOR, OPACITY(5)); - + if (buffer) { + dc->drawBitmap(0, 0, buffer.get()); + } + auto iconMask = theme->getIconMask(ICON_MODEL); if (iconMask) { - dc->drawMask(6, 4, iconMask, MAINVIEW_GRAPHICS_COLOR); + dc->drawMask(6, 4, iconMask, HEADER_COLOR); } - dc->drawSizedText(45, 10, g_model.header.name, LEN_MODEL_NAME, FONT(XS)); - dc->drawSolidFilledRect(39, 27, rect.w - 48, 2, MAINVIEW_GRAPHICS_COLOR); - - if (buffer) { - dc->drawBitmap(0, 38, buffer.get()); - } + dc->drawSizedText(45, 10, g_model.header.name, LEN_MODEL_NAME, + FONT(XS) | DEFAULT_COLOR); + dc->drawSolidFilledRect(39, 27, rect.w - 48, 2, HEADER_COLOR); } // smaller space to draw else if (buffer) { - dc->drawBitmap(0, 38, buffer.get()); - } + dc->drawBitmap(0, 0, buffer.get()); + } } void checkEvents() override { Widget::checkEvents(); - - uint32_t new_hash = hash(g_model.header.bitmap, sizeof(g_model.header.bitmap)); - new_hash ^= hash(g_model.header.name, sizeof(g_model.header.name)); - new_hash ^= hash(g_eeGeneral.themeName, sizeof(g_eeGeneral.themeName)); - - if (new_hash != deps_hash) { - deps_hash = new_hash; - loadBitmap(); + if (getHash() != deps_hash) { + invalidate(); } } @@ -78,6 +74,11 @@ class ModelBitmapWidget: public Widget std::unique_ptr buffer; uint32_t deps_hash = 0; + uint32_t getHash() + { + return hash(g_model.header.bitmap, sizeof(g_model.header.bitmap)); + } + void loadBitmap() { std::string filename = std::string(g_model.header.bitmap); @@ -94,7 +95,7 @@ class ModelBitmapWidget: public Widget } buffer->clear(DEFAULT_BGCOLOR); - buffer->drawScaledBitmap(bitmap.get(), 0, 0, width(), height() - 38); + buffer->drawScaledBitmap(bitmap.get(), 0, 38, width(), height() - 38); } }; From 368709288f90753a97c84ff6e94384b60903dd38 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 16 Jun 2021 08:21:22 +0200 Subject: [PATCH 089/122] Added X9D & T18 to the targets in fw.json --- fw.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fw.json b/fw.json index c6290644b..22102d1f2 100644 --- a/fw.json +++ b/fw.json @@ -2,12 +2,14 @@ "targets": [ ["TX16s", "tx16s-"], ["QX7", "x7-"], + ["X9D", "x9d-"], ["X9D Plus", "x9dp-"], ["X9D Plus 2019", "x9dp2019-"], ["T-Lite", "tlite-"], ["X-Lite", "xlite-"], ["X9 Lite", "x9lite-"], ["T16", "t16-"], + ["T18", "t18-"], ["TX12", "tx12-"], ["Horus X12s", "x12s-"], ["Horus X10", "x10-"], @@ -15,4 +17,4 @@ ["T8", "t8-"] ], "changelog": "- Major initial bug fixes" -} \ No newline at end of file +} From da8e1022a014eab3a106a47adbb1a7170401b589 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 16 Jun 2021 18:00:23 +0200 Subject: [PATCH 090/122] ModelBmp hotfix --- radio/src/gui/colorlcd/widgets/modelbmp.cpp | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/radio/src/gui/colorlcd/widgets/modelbmp.cpp b/radio/src/gui/colorlcd/widgets/modelbmp.cpp index 4c2d96c12..daf5977f7 100644 --- a/radio/src/gui/colorlcd/widgets/modelbmp.cpp +++ b/radio/src/gui/colorlcd/widgets/modelbmp.cpp @@ -37,7 +37,9 @@ class ModelBitmapWidget: public Widget if (buffer && ((buffer->width() != width()) || (buffer->height() != height()) || (deps_hash != getHash()))) { + loadBitmap(); + deps_hash = getHash(); } // big space to draw @@ -84,18 +86,19 @@ class ModelBitmapWidget: public Widget std::string filename = std::string(g_model.header.bitmap); std::string fullpath = std::string(BITMAPS_PATH PATH_SEPARATOR) + filename; - std::unique_ptr bitmap(BitmapBuffer::loadBitmap(fullpath.c_str())); - if (!bitmap) { - TRACE("could not load bitmap '%s'", filename.c_str()); - return; - } - if (!buffer || (buffer->width() != width()) || (buffer->height() != height())) { buffer.reset(new BitmapBuffer(BMP_RGB565, width(), height())); } buffer->clear(DEFAULT_BGCOLOR); - buffer->drawScaledBitmap(bitmap.get(), 0, 38, width(), height() - 38); + if (!filename.empty()) { + std::unique_ptr bitmap(BitmapBuffer::loadBitmap(fullpath.c_str())); + if (!bitmap) { + TRACE("could not load bitmap '%s'", filename.c_str()); + return; + } + buffer->drawScaledBitmap(bitmap.get(), 0, 38, width(), height() - 38); + } } }; From d10563d08b2e7c9cbe3af35ebe298ed536daf5e2 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 16 Jun 2021 17:43:27 +0200 Subject: [PATCH 091/122] Fixed more zchar / string copy errors Resolves #224 --- radio/src/logs.cpp | 2 +- radio/src/lua/api_general.cpp | 3 ++- radio/src/storage/eeprom_common.cpp | 2 +- radio/src/strhelpers.cpp | 18 +++++++++--------- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/radio/src/logs.cpp b/radio/src/logs.cpp index bd8767d52..b1003b441 100644 --- a/radio/src/logs.cpp +++ b/radio/src/logs.cpp @@ -143,7 +143,7 @@ void writeHeader() TelemetrySensor & sensor = g_model.telemetrySensors[i]; if (sensor.logs) { memset(label, 0, sizeof(label)); - zchar2str(label, sensor.label, TELEM_LABEL_LEN); + strncpy(label, sensor.label, TELEM_LABEL_LEN); uint8_t unit = sensor.unit; if (unit == UNIT_CELLS ) unit = UNIT_VOLTS; if (UNIT_RAW < unit && unit < UNIT_FIRST_VIRTUAL) { diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 47eced18b..f3654af3d 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -919,7 +919,8 @@ static int luaGetFlightMode(lua_State * L) } lua_pushnumber(L, mode); char name[sizeof(g_model.flightModeData[0].name)+1]; - zchar2str(name, g_model.flightModeData[mode].name, sizeof(g_model.flightModeData[0].name)); + strncpy(name, g_model.flightModeData[mode].name, sizeof(g_model.flightModeData[0].name)); + name[sizeof(g_model.flightModeData[0].name)] = '\0'; lua_pushstring(L, name); return 2; } diff --git a/radio/src/storage/eeprom_common.cpp b/radio/src/storage/eeprom_common.cpp index dffab63fb..839feedb3 100644 --- a/radio/src/storage/eeprom_common.cpp +++ b/radio/src/storage/eeprom_common.cpp @@ -169,7 +169,7 @@ void checkModelIdUnique(uint8_t index, uint8_t module) name = strAppendUnsigned(name+strlen(name), i + 1, 2); } else { - name += zchar2str(name, modelHeaders[i].name, LEN_MODEL_NAME); + name = strAppend(name, modelHeaders[i].name, LEN_MODEL_NAME); } } else { diff --git a/radio/src/strhelpers.cpp b/radio/src/strhelpers.cpp index 7777b7335..48bb7a8af 100644 --- a/radio/src/strhelpers.cpp +++ b/radio/src/strhelpers.cpp @@ -312,9 +312,8 @@ char * getSwitchWarningString(char * dest, swsrc_t idx) char * getSwitchName(char * dest, swsrc_t idx) { div_t swinfo = switchInfo(idx); - if (ZEXIST(g_eeGeneral.switchNames[swinfo.quot])) { - dest += zchar2str(dest, g_eeGeneral.switchNames[swinfo.quot], LEN_SWITCH_NAME); - // TODO tous zchar2str + if (g_eeGeneral.switchNames[swinfo.quot][0] != '\0') { + dest = strAppend(dest, g_eeGeneral.switchNames[swinfo.quot], LEN_SWITCH_NAME); } else { *dest++ = 'S'; @@ -404,7 +403,8 @@ char * getSwitchPositionName(char * dest, swsrc_t idx) } #endif else { - zchar2str(s, g_model.telemetrySensors[idx-SWSRC_FIRST_SENSOR].label, TELEM_LABEL_LEN); + strncpy(s, g_model.telemetrySensors[idx-SWSRC_FIRST_SENSOR].label, TELEM_LABEL_LEN); + s[TELEM_LABEL_LEN] = '\0'; } return dest; @@ -419,7 +419,7 @@ char * getSourceString(char * dest, mixsrc_t idx) idx -= MIXSRC_FIRST_INPUT; *dest = CHAR_INPUT; if (strlen(g_model.inputNames[idx])) { - memset(dest + 1, 0, LEN_INPUT_NAME); + memset(dest + 1, 0, LEN_INPUT_NAME+1); strncpy(dest + 1, g_model.inputNames[idx], LEN_INPUT_NAME); } else { @@ -473,8 +473,8 @@ char * getSourceString(char * dest, mixsrc_t idx) } else if (idx <= MIXSRC_LAST_SWITCH) { idx -= MIXSRC_FIRST_SWITCH; - if (ZEXIST(g_eeGeneral.switchNames[idx])) { - zchar2str(dest, g_eeGeneral.switchNames[idx], LEN_SWITCH_NAME); + if (g_eeGeneral.switchNames[idx][0] != '\0') { + strncpy(dest, g_eeGeneral.switchNames[idx], LEN_SWITCH_NAME); dest[LEN_SWITCH_NAME] = '\0'; } else { @@ -497,8 +497,8 @@ char * getSourceString(char * dest, mixsrc_t idx) getStringAtIndex(dest, STR_VSRCRAW, idx-MIXSRC_Rud+1-MAX_LOGICAL_SWITCHES-MAX_TRAINER_CHANNELS-MAX_OUTPUT_CHANNELS-MAX_GVARS); } else if (idx <= MIXSRC_LAST_TIMER) { - if(ZEXIST(g_model.timers[idx-MIXSRC_FIRST_TIMER].name)) { - zchar2str(dest,g_model.timers[idx-MIXSRC_FIRST_TIMER].name, LEN_TIMER_NAME); + if(g_model.timers[idx-MIXSRC_FIRST_TIMER].name[0] != '\0') { + strncpy(dest,g_model.timers[idx-MIXSRC_FIRST_TIMER].name, LEN_TIMER_NAME); dest[LEN_TIMER_NAME] = '\0'; } else { From 6b596602251699d8fe468aa44ea28f87d4a7c4eb Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Wed, 16 Jun 2021 22:31:41 +0200 Subject: [PATCH 092/122] Fixed trainer special function parameter assignement Resolves #203 --- radio/src/gui/colorlcd/special_functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index 92de8cc49..5c79687e0 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -109,7 +109,7 @@ class SpecialFunctionEditPage : public Page STR_VALUE); auto choice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, - NUM_STICKS + 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + NUM_STICKS + 1, GET_SET_DEFAULT(CFN_CH_INDEX(cfn))); choice->setTextHandler([=](int32_t value) { if (value == 0) return std::string(STR_STICKS); From eacc6e04ffd9993a886acb8ce8def8710859325b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 17 Jun 2021 08:29:45 +0200 Subject: [PATCH 093/122] [Color] make it a slow disco on telemetry screen Resolves #234 --- radio/src/gui/colorlcd/model_telemetry.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index 0803eb61b..e7c02f977 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -79,7 +79,7 @@ class SensorButton : public Button { { TelemetryItem &telemetryItem = telemetryItems[index]; - if (telemetryItem.isFresh()) + if (telemetryItem.isAvailable()) dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, HIGHLIGHT_COLOR); else dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, FIELD_BGCOLOR); From 87610b8449e6741f0f824e96d0bd962dbec2f3c8 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 17 Jun 2021 08:37:13 +0200 Subject: [PATCH 094/122] Fixed logical switch V2 edit value range Resolves #231 --- radio/src/gui/colorlcd/model_logical_switches.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp index 90b900562..16b6a6ec1 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -168,7 +168,7 @@ class LogicalSwitchEditPage: public Page new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V2); int16_t v2_min = 0, v2_max = 0; getMixSrcRange(cs->v1, v2_min, v2_max); - v2Edit = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MAX_LS_DELAY, GET_SET_DEFAULT(cs->v2)); + v2Edit = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), v2_min, v2_max, GET_SET_DEFAULT(cs->v2)); v2Edit->setDisplayHandler([=](BitmapBuffer * dc, LcdFlags flags, int32_t value) { drawSourceCustomValue(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, cs->v1, (cs->v1 <= MIXSRC_LAST_CH ? calc100toRESX(value) : value), flags); }); From b3289ae5d4b68d5bd5970c05ef71c0989a2293f8 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 17 Jun 2021 08:41:12 +0200 Subject: [PATCH 095/122] Fixed T2 / T3 display Resolves #232 --- radio/src/gui/colorlcd/view_main_decoration.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/colorlcd/view_main_decoration.cpp b/radio/src/gui/colorlcd/view_main_decoration.cpp index 16bc8cdfd..19684d928 100644 --- a/radio/src/gui/colorlcd/view_main_decoration.cpp +++ b/radio/src/gui/colorlcd/view_main_decoration.cpp @@ -300,8 +300,8 @@ void ViewMainDecoration::createTrims() VERTICAL_SLIDERS_HEIGHT }; - trims[TRIMS_LV] = new MainViewVerticalTrim(this, r, 1); - trims[TRIMS_RV] = new MainViewVerticalTrim(this, r, 2); + trims[TRIMS_LV] = new MainViewVerticalTrim(this, r, 2); + trims[TRIMS_RV] = new MainViewVerticalTrim(this, r, 1); } void ViewMainDecoration::createFlightMode() From 64e2b25a1fe0a605fe933913d3b2b2150e5c8104 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 17 Jun 2021 08:50:19 +0200 Subject: [PATCH 096/122] Fixed layout update when "Mirror" option is changed Resolves #230 --- radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp b/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp index c3a93ca1b..6be71f9d1 100644 --- a/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp +++ b/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp @@ -89,7 +89,8 @@ void Layout::adjustLayout() (hasTopbar() ? 1 << 0 : 0) | (hasSliders() ? 1 << 1 : 0) | (hasTrims() ? 1 << 2 : 0) | - (hasFlightMode() ? 1 << 3 : 0); + (hasFlightMode() ? 1 << 3 : 0) | + (isMirrored() ? 1 << 4 : 0); if (checkSettings == decorationSettings) { // everything ok, exit! From d02298c61c92a84b5288e430206917348c6c17da Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Thu, 17 Jun 2021 09:03:00 +0200 Subject: [PATCH 097/122] Fixed menu body height Resolves #211 --- radio/src/thirdparty/libopenui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 6b661c490..3c280f84c 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 6b661c4907bcafc08436e5c96fc5a38e9eb6e499 +Subproject commit 3c280f84cef56f84044c76d8aacea0d5e1bc8278 From f2ecce322dca389a3d1d1c8f787a80e811fdf161 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 17 Jun 2021 18:57:30 +1000 Subject: [PATCH 098/122] Update github issue templates, prune some OTX files --- .github/FUNDING.yml | 12 - .github/ISSUE_TEMPLATE/bug.md | 12 +- .github/ISSUE_TEMPLATE/help.md | 15 +- .github/ISSUE_TEMPLATE/newfeature.md | 5 +- CREDITS.txt | 2269 -------------------------- 5 files changed, 13 insertions(+), 2300 deletions(-) delete mode 100644 .github/FUNDING.yml delete mode 100644 CREDITS.txt diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index fdb7dc9f4..000000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1,12 +0,0 @@ -# These are supported funding model platforms - -github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] -patreon: # Replace with a single Patreon username -open_collective: # Replace with a single Open Collective username -ko_fi: # Replace with a single Ko-fi username -tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel -community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry -liberapay: # Replace with a single Liberapay username -issuehunt: # Replace with a single IssueHunt username -otechie: # Replace with a single Otechie username -custom: ['PayPal.Me/opentx'] diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md index c3483d818..4940fc9a8 100644 --- a/.github/ISSUE_TEMPLATE/bug.md +++ b/.github/ISSUE_TEMPLATE/bug.md @@ -1,6 +1,6 @@ --- name: Bug report -about: Create a report to help the OpenTX team to improve OpenTX +about: Create a report to help the EdgeTX team to improve EdgeTX --- *Before submitting, search the open issue if there is already a report of the bug* @@ -14,10 +14,6 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error **Expected behavior** A clear and concise description of what you expected to happen. @@ -26,6 +22,6 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **General information** - - OpenTX version (e.g. 2.1.23 or N123) - - Hardware (e.g. Frsky X9D+ or 9X Pro) - - OpenTX Companion (e.g. 2.1.23 on Ubuntu 16.04) + - EdgeTX version (e.g. 2.4.0 or nightly and #commit number) + - Hardware (e.g. Frsky X9D+ or RM TX12S) + diff --git a/.github/ISSUE_TEMPLATE/help.md b/.github/ISSUE_TEMPLATE/help.md index d50dfab6f..462ca6849 100644 --- a/.github/ISSUE_TEMPLATE/help.md +++ b/.github/ISSUE_TEMPLATE/help.md @@ -1,16 +1,13 @@ --- name: Help -about: Problems or question of using OpenTX +about: Problems or question of using EdgeTX --- -The issue tracker here is for devlopment of OpenTX. This is not a +The issue tracker here is for devlopment of EdgeTX. This is not a support site, only a bug tracking system. Please use support groups -like [rcgroups](https://rcgroups.com/) or -[openrcforum](https://www.openrcforums.com/forum/). See also our -[manual](https://opentx.gitbooks.io/manual-for-opentx-2-2/). +like [rcgroups](https://rcgroups.com/), [Discord](https://discord.gg/wF9wUKnZ6H) +or [Facebook](https://www.facebook.com/groups/edgetx). -For quick questions you can also join our general chat. - -Issues that fall into the category of asking for help, will be -generally closed as invalid without an answer. +Issues that fall into the category of asking for help may be re-directed +to the other support platforms or answered directly if time permits. diff --git a/.github/ISSUE_TEMPLATE/newfeature.md b/.github/ISSUE_TEMPLATE/newfeature.md index d5bf365fc..811b0cb21 100644 --- a/.github/ISSUE_TEMPLATE/newfeature.md +++ b/.github/ISSUE_TEMPLATE/newfeature.md @@ -1,9 +1,10 @@ --- name: Feature request -about: Suggest an idea for this project to make OpenTX even better +about: Suggest an idea to make EdgeTX even better! --- -*Before suggesting a new feature here, please search the open issue if there is already a request for the feature* +*Before suggesting a new feature here, please search the open issue if there is already a request for the feature. +You can add your comment there, or react so that we know know much interest there is in the feature request.* **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] diff --git a/CREDITS.txt b/CREDITS.txt deleted file mode 100644 index dc9dc238d..000000000 --- a/CREDITS.txt +++ /dev/null @@ -1,2269 +0,0 @@ -[Main developers] -Bertrand Songis (bsongis) -André Bernet (kilrah) -JC D. (3djc) -Michael Blandford -Damjan Adamic (projectkk2glider) -Franck Aguerre (jivarofad) -Romolo Manfredini (romoloman) -Martin Hotař (mhotar) -Maxim (Max) Paperno (mpaperno) -Arne Schwabe (schwabe) -Daniel Gorbea (dgatf) -Neil Horne (elecpower) - -[Translators] -[fr] Sebastien Charpentier (LapinFou) -[es] Daniel Gorbea (dgatf) - -[Other contributors] -Rob Thomson -Bryan J. Rentoul (gruvin) -Cameron Weeks (th9xer) -Andreas Weitl -Kjell Kernen -Gabriel Birkus -Jean-Pierre Parisy -Karl Szmutny -Michal Hlavinka -Pat Mackenzie -Michael Byrne - -[Companies and projects who have donated to OpenTX] -FrSky (monthly) -FlySky (monthly) -JumperRC (monthly) -RadioMaster (monthly) -HappyModel (monthly) -Aloft Hobbies -Jonathan Modellismo -WsTech -Aviomotive -BoltRC -Aerial Tech Hobbies -LitePilots -David Ankers (OpenPilot) -3D Robotics -RC Studio (monthly) -Craft & Theory (monthly) -OpenTX Manuals by Lothar Thole -Team BlackSheep -3DXR - -[People who have donated to OpenTX] -Mario Sette -Felice Baratto -Alessandro Mari -Gianpiero Spontina -Ingo Kuzniak -Hans-Åke Rahm -Tamas Varga -Thomas Gregg -Didier Christien -Alain Pinard -Sergio Melas -Paul Montenot -Chris Guy -Adrien Gravouille -Thomas Black -Marc Lucht -Zdenek Trojanek -Martin Hotar -Stefan Grundevik -Fabio Eboli -Federico Zotti -Albin Esbjörnsson -Kilian Hüttenhofer -Stephen Stough -Adilson Oliveira -Robert Saint Marie -Tad Leake -Andreas Weitl -Chan Lofland -Dirk Kasten -Denisov Alexander -Timo Varppee -Ondrej Hatapka -Valérie Billon -R. Scott Page -Johan Cronje -Renato Aranghelovici -Jeffrey Irvin -Kenneth Crump -Damjan Adamic -Martin Wilson -John Holdsworth -Daniel Schwartz -Dean Church (monthly) -Michael Halse -David Mark -Ted Moss -Jim Wagner -Nicola Porzi -Michael Halse -Chewytm -Espen Solbu -Mike Frizell -Bruce Jones -Stefano Airaghi -Dan Eeckhout -Alex Calderhead -Leonard MacKey -Heung Jin Choi -Mike Retterer -Keith Wood -Viljo Liukku -Eric Beier -Burt Pickard-Richardson -Helmut Renz -Rick Harms -Vincenzo Vavala -Patricia Martin -Harald Klein -Carl Petersen -Douglas Burkett -Clifford Evans -Teodor Yantchev -Jarad Berkman -Christian Schulz -Gregory J Shepherd -Michael Shellim -Ming Wang -Bernard Lukaschek -Nigel Chippindale -Mike Perkins -Ted Hammond -David Beanland -Bernd Koehler -Frédéric Duflos -Dave Fasken -Heinrich Illig -Michael Collins -Michael Manning -Craig Lorentz -André Lasseret -Felipe Alexandre de Oliveira -Brian Harris -Philippe Herbots -Rainer Nelte -Franck Bernhard -Erhard Werner -Stig Jøran Moen -Keith Hertzog -Christophe Dauvergne -Tauno Rautakorpi -Sven Assmus -Paul Dittman -Mike Matheny -Cheryl Wynne -Emay Toha -Don Nolder -André Lasseret -Filip Lowette -Arnaldo Gabella -Fiorenzo Agnelli -Michele Villa -Christophe Rodriguez -Larisa Filocamo -Hartmut Sahm -Åke Brodin -Ted Moss -Filippos Tigarakis -Chewytm -Eric Burdis -Nigel Chippindale -Michael Deasy -Stephen Stough -Kenneth Lilja -Robert Jero -Gary Bancroft -Robert Cotsford -NorthSide -George Constandinides -Robert Schonk Jr -Kevin Berkefeld -Peter Webster -Mike Matheny -Martin Sniedze -Fernando Rimoldi -Sidney Fisher -John Rodgers -Ed Pare -Thomas Kilduff -Robert Ferguson -Bryan Green -Kevin Berkefeld -Julien Michel -Krittikun Chiraphanda -Douglas Law -Alberto Scotti -Oliver Kunkel -D. Joseph Frazier -Heiko Polig -Andrew Fernie -Marco Lange -Nick McMillan -Michel Baily (monthly) -Patrick Mullen -Scott Campbell -Erich Roeder -Wayne Davy -Michael Fontenot -Willie Swanepoel -Satoru Sasaki -Tyler Darby -Nicholas Crego -Johan Moelich -Timo Esser -Brian Paddock -Jean Pierre Parisy -Norbert Martsch -Jean-Marie Guitard -Tilman Baumann -Fred Larssen -Ari Aalto -Robert Sams -Kirk Robertson -John Koontz -Jeffery Golias -Paul Robinson -Dave Matison -Mike Matheny -Pino Agostino -Graham Boucher -Nelson Noura -Joel Allamy -Philip David Martin -Sean Stephen -Bruce Mackay -Blake Spilman -Timothy Pelletier -Glen Roe (ShowMaster) -Jan Van Ooteghem -Lawrence Christensen -Leo Duflou -Bruce Wood -Arnd Feige -Tauno Rautakorpi -Steve Coley -Stefan Kiendl -Gregory Nuspel -Mitchell Brenner -Ronnnie DePrez -Ferenc Koller -Robert Sessler -Didier Lanot -Rodney Hunt -Steve Bernard -Paul Nazaroff -Gordon Ernenputsch -Ksamak -Marc Gittelman -David Poole -Ralph Sauer -Karl-Heinz Gansel -Michels Bernhard -Thomas Demmelbauer -Felice Baratto -Michael Halse -Horst Kampen -Ian Gough -Matthew Brett -Lexington S Morley -Clemens Drews -Axel Stöckle -Roberto Orsello -Richard Ruck -Hugh Caldwell -Christian Marauli -Albin Esbjörnsson -Per Esbjörnsson -Bernd Hohmann -Sławomir Juszczak -Stephen Henderson -David Morgan -Aloft Hobbies -Netanel Hobara -Cameron Galipo -Doug Schafer -Pietro Rossin -Earl Killingsworth -Victor B Wright -Terry McKinley -Марков Алексей -Offer Shmuely -Ian Lobley -Stéphane Fortas -Peter Mitrik -Thomas Majewski -ANF King -Henrik Hammarström -Kenneth Lilja -R Wells -Uwe Jordt -Harald Wilczak -Bernhard Lohmann -Ferenc Koller -Robert Ferguson -Daniel Byers -Tyler Darby -Miguel Villalpando -Kim Dong Hoon -Terry McKinley -Walter Bell -Clinton Linck -Josep Clodas Sola -Thierry Martin -Peter Mauro -Mitchell Brenner -Yak -Lluis Bullich Rañe -Steffen Rohr -Eric Jacquin -Harvest Lane -Ville Kuvaja -DK-System -Heinz Vogel -Heung Jin Choi -MMJS van der Helm -Dan Neelands -Yashiro Masayuki -Isaac Sloan -Harold Baird -Roy Bender -Bradley Murchie -Maximilian Süß -Dean Jansa -Ronald Donker -Paul Oldenkamp -Dietmar Drees -Bradley Murchie -Richard Fahie -James Kaufman -Henry Deucker -Christian Fuchs -Arold Reinders -Robert Miller -Mark Gaskievicz -Roland Müller -Frank Gewalt -Remi Viard -Reginald H Millsom -Pierluigi Sommaruga -A Harris -Stefan Grunenberg -Francisco Gutierrez Muñoz -Steve Coley -Sean Cull -Roberto Orsello -David Finger -Jean-Marie Oddo -Mike Matheny -Arron Bates -Tim Spurr -Daniel Morgan -Trent Ready -Mark Maskiell -Robert Crockford -Mark Illum -David McGuire -Martyn Coles -Jose Sobral -Heung Jin Choi -Tomasz Janiak -Fabien Gagné -Micha Ronen -Albert Blondin -Klaus Schuebel -Evening Shade Pottery -Thomas Kilduff -Sven Assmus -Markus Eberl -Nicholas Field -Harald Wilczak -John Grepps -Anthony Burbidge -Sherba Baine -Gordon Evans -Fred Figge -Uwe Kuehn -David Beatty -Aaron Stringer -Alan J Wasserman -Wolfgang Rauser -Samuel Cook -Friedrich Ipolt -Barrington Cole -Charles Lakins -Thos. O'Meara Photography -Adam Chevalier -Mike Mulligan -Serge Michaux -Robert Young -Wilco Hijink -Roy Nixon -Lexington S Morley -Daryl Dacko -Beat Zurflueh -Piotr Kundu -Kevin Berkefeld -Tomasz Klys -Mirco Didone' -Christoph Schroeter -Zdenek Trojanek -Bryan Anderson -Kamco Enterprises -Robert Bates -Robert Brown -Dennis Marinus -Ronald Van Putte -Thomas Kvitle Jenssen -Patrick Tuxbury -Istvan Magi -Sasaki Satoru -David Laburthe -Vit Jelinek -Pablo Gomez Martinez -Alan Thoren -Jack Clodfelter -William Simon -Peter Fithern -Thomas Svedman -TechFixPros -Rafael Eric Suarez Nordlow -Colin Rycroft -Emanuel Stassar -Michael Gregory -Robert Russell -Felix Kaiser -Joshua Bardwell -Davd Goodall -Leonard MacKey -Miles Kelly -Sergio Robledo -Mikael Wallbäck -Daniel Malcman -Paul Dittman -Attilio Rausse -Sidney Gates -Andrea Montefusco -Paul Luby -Donald L Webster -Kenneth Frehafer -Matthias Hase -Markus Wolf -Andrew Fazio -Stefan Künst -Stephen Adams -Tauno Rautakorpi -Frank Houvenaghel -George Neusse -Andreja Rusjakovski -Axel Stöckle -Erwin Störrle -Rodger Hopper -Fernando Cesar -Jonathan Gillis -DK-System -Lexington S Morley -Bernd Koehler -Davis RC -Pete Johnson -Patricia Martin -Florian Tschacher -Brian Spearing -Craig Hall -Joseph Case -Harald Klein -Robert Crockford -Chris Boultinghouse -Kitsew -Georgios Papadopoulos -Fourmost Products -Dennis Miller -Dave Matison -Maximilian Radig -Juan Rodriguez -Robert Sessler -John Murray -Martin Sniedze -David Poole -Manfred Delakowitz -Martijn Kruissen -Jörgen Norman -Nathan Kinney -John Godden -Larry Roper -Mike Frizell -Daniel Pospolita -Peter Herz -Don Mondrik -Paul Fram -Charles Knight -Matthew Adams -Kenneth Reed -Gary Wild -Christof Ramm -Bruno Bedard -Salih Samim Aydin -Alexander Pils -Andrew Scerbina -Donovan Bodishbaugh -Ray Dunn -Burt Pickard-Richardson -Robert Ferguson -CY Enterprise -Michel Stathakis Neto -Beat Zurflueh -Portstate Pty Ltd -Jens Hoffmann -Ricardo Santos -Jean-Francois Le Breton -Michael Deasy -Anindya Mukerjee -Emay Toha -Kamco Enterprises -Peter Mueller -Giovanni Trentini -Cecil Porter -Robert Canavan -Greg Higginbottom -Tim Walker -Spis Ltd/TurboNote -Patricia Cox -Matthew Lukes -Sabine Witte -Phillip Perry -Beat Zurflueh -Russell Stutzman -Dietmar Drees -Kenneth Ward -Jens Lochmüller -Anthony Witham -Peter Hauke -Arizona Action Photos -Francis F Dale -Willie Swanepoel -Edward Hauser -Adilson Oliveira -Joseph Ordille -Michael Shellim -Teddie Holmes -Alexandre Rault -Eric Albin -Lukas Kliment -Bernard Philippe -Bernard Delpy -Didier Klein -Arnaldo Guedes -Cyberauto Systems -John Alsbrooks -John Kearney -Christopher Borden -Marc Virgilio Ricci -Frederick Davie -Jürgen Enders -Terrence Pitts -Thomas Sandner -Marco Coltorti -Harald Wilczak -Rodney Lorimor -Richard Davis -Stephen Palmer -Michael Napolitan -Joshua Bardwell -Fred Dawson -Jim Wagner -Peter Johnson -Fredrik Lewerth -Arvin Stenersen -Daniel Morgan -Roy Nixon -Joachim Blunck -Karl-Heinz Gansel -Monrovia Mountain Enterprises -Alessandro Villa -Jan-Åke Nilsson -Matthias Rei -DVR Solutions BV -Bertrand Condevaux Lanloy -Henri Mirra -Aurélien Planiol -Kevin Caldwell -Andrew Garello -Tomasz Maziarz -Russell Flack -Fernando Rimoldi -Greg Reid -Dietmar Drees -Holger Landwehr -Arno Loehdorf -Boon Sripattha -Patrik Åkerfeldt -Alonzo Fretwell -Renjith Chandrasekharan -Ruben Gomez Hernandez -Michael Gardner -Judith Green -Timo Ruppell -John Lipscomb -Norbert Wenke -Erik Frieberg -Raimo Ruotsalainen -Brian Harris -Sebastian Blüml -Efisio Maxia -Jeff Theobald -Anthony Rizzo -Rodney Huddleston -Dietmar Drees -Jürgen Monden -David Morgan -Andrew Fernie -Donald Burke -Ronald Donker -Fredie Singh -Patrizio Simoncini -Harald Gabler -Michael Brown -Julien Gérard -Alexandre d'Alton -Peter Mergaerts -Jan Houwers -Christian Grandjean -Roger Bergevin -NorthSide -Sigmund Martinsen -Lionel Lafraise -Gisli Ofeigsson -The Gruvin9x Project -Manuel McLure -Fernando Rimoldi -Hiltenkamp Stephan -Sandeep Kishore Jain -Greg Parker -Norman Maaß -Adriano Del Ben -Justin Poole -Kenneth Harrison -Eugene LaFaille -John O'Shaughnessy -Uwe Jordt -Stuart Olson -Frédéric Devillard -Cheung Leo -Peter Webster -Jean-Claude Bachetta -Delfin Perena -Sae-wook Yoon -Focus on Excellence -Jeffery Golias -Nick Milton -Paul Zasso -Reidar Johannes Lillebotn -Handley William -Luke Williams -Michael Shellim -Brian Spearing -Derek Foster -Pasi Pitkänen -Lexington S Morley -Jeff Roberson -Volkov Victor -Bryan Anderson -Daniel Garber -Pavel Stein -RCdiy -Patrick Tuxbury -Mark Deverell -John Capell -Neil McVean -Matthew Elyash -Kerry Cochrell -Willem Aandewiel -Reg Bell -Jean Chenoz -Philippe Grand -Tauno Rautakorpi -Jeff Dillingham -Brad Kelley -Shaun Currier -Danilo Schiara -John Hennig -RK Custom Homes -Piet Teekens -Tommy Widenflycht -Johan Petrus Theophilus -Maria van Melis -Ralph Gustafson -Paul Luby -Andrew Duncan -Stephen Crowther -Edward Glover -Kelly Byrd -Brad Curtis -Wolfgang Kuhl -John Beidl -Roy Nixon -Glen Roe -CY Enterprise -Evening Shade Pottery -Gilles Dando -Patrick Richard -Justin Altemeier -Constant Dupuis -Tony Kenward -Pierre Rondel -Ernest Luguera Sanchez -Adam Brackney -Joe Baker -单 雷平 -Ben Fay -Chris Monk -Stan von Miller -Christian Krebs -Michel Motte -Graziano Rampinelli -Joe Baker -Jean-Philippe Laurent -Romain Rouzaud -Michel Garbarino -Henri Mirra -Otto Fröhlich -Thomas Cramer -Anthony Trombetta -Fernando Rimoldi -Dietmar Drees -Michal Kowalski -Ivan Correa -Modellbau -Massimo Luigi Stara -Graham Baldwin -Tomasz Rejman -Wolfgang Lang -John Greenwood -Till von Reumont -Paul Pereira Brunner -Donald L Singer -Junsun Lee -Guy Lamaison -Erwin Dulken -Mike H Dailey -Jon Cammarata -Martin Krueger -Adolfo Perogio -Sylvain Techer -Wes Warner -Kevin Caldwell -Ali Ersan Gur -Hans-Rudolf Riniker -Sébastien Boutard -Jean Chessé -Mike H Dailey -Don Polk -Roberto Orsello -Borovikov Dmitriy -John Lipscomb -Peter Glassey -Joseph Kramer -F.Javier Martinez Velasco -Claudio Piccinini -John Jackson -John Beech -Rene TraaJohn Jackson -Sebastien Charpentier (monthly) -Nigel Hendriks -Frédéric Devillard -Albert Herzog -Anindya Mukerjee -Kenny Stanberry -Tauno Rautakorpi -Mathias Krapp -Fernando Rimoldi -Wolfgang Rauser -Richard Shagam -Stephen Bailey -DK-System -Casa Kuxtal -Christopher Potts -Douglas George -Michael Banks -Jon Wiltshire -David Green -Guy Willems -Xavier Mestelan Pinon -Maik Gottschalk -Scott Sparks -John Dunning -RAD51 -Ron Powell -Alain Labonne -Carol Sills -Franck Aßmann -Larry Anderson -Mikael Wallbäck -Wes Warner -Christoffer H Henni -Donna Bayer -Garry Ogilvie -Karlheinz Sirsch -Fred Larssen -Rob Coppock -Franz Josef Hahn -Serge Ribault -Tanguy Le Moign -Barrett Powell -Patrice Lafeuille -Martin Blume -Marek Kopyto -Marco Lange -Gary Nix -Jack Oliphant -John Lever -Adolphe Montefiore -Steven De Weerdt -Håkan Berggen -Ayelet Egosi -Jean-Frederic Gaudreault -Jim Wagner -Lee Scott -Roger Keatley -Sherman Lovell -Hugh Caldwell -JB Fisher Company -Michels Bernhard -Pietro Rossin -Carsten Wache -Brock White -Steven Poretz -David Huelster -Gordon Stahl -John Mathison -Richard Duczmal -Gregory Barron -Marcel Fischer -Klas Mälman -Viljo Liukku -Eric Smith -Sven Assmus -Luke Mole -NorthSide -Hannes Mössler -James Kaufman -Lexington S Morley -Ralph Segar -Frédéric Devillard -Cory Theurer -Valentinos Zachariou -Garrett Paquette -Heung Jin Choi -Kerry Cochrell -Doug Miller -Andrzej Weydmann -Chair-man.nl -Eric Beier -Jay Warren -Marian Matej -Alastair Smith -Timothy Senecal -Dietmar Drees -Bruce Jones -Adil Busaidy -Herman Hofman -Jean Paul Michel -Peter McHenry -Julien lecoquierre -Jean-Marie Clay -Robert Nicoll -Dan Nixon -Ewald Moehring -Giovanni Carnesecchi -Paul Dumsday -Tadeusz Adamowski -Andrew Newton -Neil Horne -Kerstin Appelqvist -John Curran -Gerald Thiel -Jürg Bingisser -George Emer -Jerzy Manarczyk -Gerard Braakhuis -Nicolas Leblond -Burt Pickard-Richardson -Jiri Petrzelka -RK Custom Homes -David Semeria -Philip Lewis -William Kim -Dean Berg -Tim Spurr -Clifton Hipsher -Mike Frizell -Oliver Jacob -Pawel Stepien -Thomas Kilduff -Barry Fox -Anatoly Bakalenko -Bruce Anderson -Jeremy Cowgar -Edward Hauser -Stuart Brown -Anton Glock -Roger Ritchie -George Carnie -Philippe Stofleth -Tim Dzugan -Illia Izotov -Richard Miles -Attila Kapolnai -Gilles Dando -Denis Roussille -Olivier Anken -Michel Motte -David McDonald -Tauno Rautakorpi -Carsten Schlassa -Angel de la Dehesa Calles -Michel Garbarin -Algirdas Tamosiunas -Petr Kysela -Pascal Roux -Robert Whitfield -InTheTubeDeep -Jaromir Malek -Joe Street -Regin Poulsen -Hervé Laveyssiere -Walter Fowler -Richard Wagner -Kevin Hyman -Herald Reichel -Frederic Rouyre -David Libby -Hans Timmermans -Adrian De Zee -John Hanagan -Didier van Steensel -Julius Vicze -Cristiano Merighi -James Holley -Chris Merry -Jiri Fojtasek -Thierry Laligand -Hassouna Cherif -Fred Thijssen -Andrew Gardiner -Christian Niß -Douglas Penney -Starck Associates -Johannes Wollenweber -Fred Page -Jean Trouche -Patrick Blanc -Jean-Louis Tonnellier -Masoud -Mustafa Güven -William Waters -Joël Allamy -William Mckinstry -Jean-Jacques Pointel -Frédéric Rouyre -Jonathan Pruett -Jörg Zieren -Christopher Rhodes -Marco Strack -Stefano Boggia -Dwayne Myers -Stavros Papathanasiou -Roland Nittby -Courtney Puskas -NorthSide -Martin Polák -Ulf Reinhardt -Wolfgang Renisch -Trevor Hinze -Mohammad Zargari -Jean Pierre Bernard -Patrick Castellan -Udo Tödter -Marc J Merlin -Paul Chitonu -Wolfgang Rauser -Emmanuel Dallery -James Lyons -Tauno Rautakorpi -Gerald Peklar -Fabien Gagné -David Cox -Philip Cork -Kevin Wright -John Scott -Yves Berger -Otto Fröhlich -Kenneth Lilja -Benjamin Johnson -Brad Kelley -Michel Biancki -Lynn Laycock -Claude Godefroy -Gérald Joubert -Wayne J Podaima -Philippe Peyron -Julien Kieffer -Gregor Walter -Carlos Viegas -Roberto Orsello -Steve Murray -Marcel Schäfer -Sean Cull -Holger Bothmer -Michael Manning -Martin Risik -ZEN Computer Services -Egbert Stohlmann -Martin Peryea -Nathan James -Luis Suarez -Artur Uzieblo -InTheTubeDeep -Jaap Greve -Peter Glassey -David Reynolds -David Frazier -Donald L Singer -George Carnie -Susan Holbert -Richard Garrett -Paco Galván -Matthew Sylvester -Robert Bates -Hans-Peter Endres -Axel Stöckle -Leonard MacKey -Andrew Fernie -Mark McCoy -Damir Pooshs -Jeff Dillingham -Brent Hampton -Arkadiusz Milewski -Garry Ogilvie -Donald Porter -William Simon -Thomas Sandner -Pascal Pochard -Helmut Renz -T S Pung -Jorge Trinidad -Yeo Jonathan -David Olivier -Thierry Martin -Udo Nowakowski -Philippe Stofleth -Alain Labonne -Atam Gangaram Panday -Sajjad Rahman -Giovanni Carnesecchi -Lorenzo Frassy -Reinaldo Perez -Christian Aussillou -Christopher Potts -Eric Tremblet -Bernard Hunt -Evening Shade Pottery -Brian Cole -George McGinnis -Egid Schabl -Harold Henn -Chris VanCaeseele -Denis Novomlintsev -Pascal Amidey -Hermann Klampferer -Philippe Faure -Dean Berg -Sven Assmus -Gary Potter -Moritz Suerdieck -Eric Olivi -Jean-Jacques Pointel -Vianney Levesque -Fred Carlisle -Pierre Duvezin-Caubet -Pascal Dugué -Burt Pickard-Richardson -Robert Wilhelm -Thomas Kilduff -Hans-Rudolf Riniker -Arkadiusz Milewski -Kenneth Frehafer -Jens Ziegler -Peter Arnold -Jason Cole -Richard Vollkommer -Sherwood Drumheller -Jean-Christophe Gardaz -Igor Pongrac -James Mare -Kerry Cochrell -Florian Derriennic -HLL Vogels -Gordon Evans -Gaines Smith -Daniel Vogel -RCdiy -Joseph S Robinson -Jack Dunkley -Hannes Niethammer -Thierry Laligand -Pierre Duvezin-Caubet -Hannes Mössler -Petr Kysela -Calvin McCarthy -Piet Bomberg -Arne Johan Helle -Cees de Greef -Marc Gittelman -Scott Pritchett -Konstantinos Katsaklis -William Tortorello -Mark Maskiell -Gerald Peklar -Sidney Gates -Jörg Zander -Bryan Anderson -Stephane Monfette -Paul Poschen -Marco Lange -Peter Foto -Antonio Munuera -Vasil Petrov -Robert Wilhelm -Richard Hodgson -Manny Tau -Leonard MacKey -Johannes Wollenweber -Rodney Huddleston -Glen Roe -Chad Cranford -Tom Figge -Joerg-Dieter Rothfuchs -David Lorenzo -Chris Conroy -Fernando Wascheck -Martin Schoitsch -André Curci -Lukas Fischer -Larry Truesdale -Bernd Koehler -David Newton -Stephen Lesage -Mona Sturgis -Russell G Shanks -CY Enterprise -William Tortorello -André Lasseret -Lothar Thole -Stuart Olson -Paweł Stróż -Tim Buss -Christian Schaefer -Willyp -David Owens (monthly) -Willem Reij -Arthur Costerus -Martin Oldershaw -Robin Wienke -Jeffrey Johnson -Franz Josef Hahn -Burt Gray -Daniel Dupont -Felix Kaiser -Nicola Claudio Rossi -Gary Clayton -John Bitzer -Scott Campbell -Martin Polák -Jonathan Pruett -Charles Nowell -Randy Melton -Jo Jung -Chris Monk -Alvin Burke -Georges d'Haussy -Philippe Lhomme -Carsten Ullerup -Wolfgang Schneider-Reif -Anselm Schopplick -Georgios Papadopoulos -Thorsten Weber -Alexander Röhlich -Karl-Heinz Behn -Stefan Köhler -Jochen Kaphengst -Hans-Jüergen Duwe -Christoph Dalloz -Alexander Wimmer -Horst Efftinge -Nektarios Ntikmpasanis -Derek Gates -John Alsbrooks -Jürgen Deininger -Bruce Jones -Meinhard Schöll -Rolf Lemb -Casey Layton -Robert Bates -Udo Nowakowski -William Hallman -Paul Stein -Nurflügel & Modelltechnik -Gary Wild -James A Davey -Marco Giuseppe Colturi -Martin van Estrik -Vakhrushev Andrey -Jörgen Norman -Chen Zhongzhong -Eddie Ehlin -Evening Shade Pottery -Michel Vanaken -Forgues Research -Hike For Happiness -Vegar Elde -Anindya Mukerjee -Fabrice Debonnet -Darko Perković -Leonard MacKey -James A Davey -Matthias Häusel -Thierry Laligand -Pascal Gelis -Christelle Boudet -Damir Pooshs -Bertrand Brunner -Alexander Mathis -Jean-Luc Le Goff -Allan Abbott -Paul Elstob -Mark Levine -K L Wolfbrandt -Jeffrey Horton -Erlendur Konradsson -Dennis Miller -Шарлаимов Константин -Philippe Roussely -Michel Gutierrez -Joel Meziere -Widco Us -Marcos Guimaraes -Olivier Petri -Alfredo Mac Donald -Erich Strebel -Robert Goillandeau -Joel Lopez -Morten Myklebust -Ferenc Kunkli -Josh Kraker -Roger Bergevin -Geoff Sim -Arthur Reynolds -David Dalley -Colin Macdonald -Charles Wilson -Walter Daser -CG Tech Inc -Alexander Röhlich -Peter Goodrich -Randy Melton -Paul Pridday -Jörgen Norman -Peter Glassey -Carlos Vicente -Nael Bishtawi -Paul Dr. Willutzki -Heart of NM LLC -Ulf Hedlund -Meinolph Kaufmann -Bill Keenan -Ralf Kosel -Lukas Hucik -Uwe Jordt -Tuan Le -Burt Pickard-Richardson -Robert Rubio -Merv Dale -Michel Garbarino -Geoff Graham -Georges Pezet -Robert Wilhelm -Francis Herzog -Bo Hjeresen -Danny Koch -John Eppling -Thierry Malosse -Johann Antretter -Jan Kaiser -Rachel Berkhahn -Luca Massimiliano Comolli -Mario Wesierski -Evening Shade Pottery -Jean Desmet -Alexis Pons -Roger Bergevin -Eddie Ehlin -Bernard Avisse -Brent Warman -Tim Johnson -Reinhard Herden -Mark Hanson -Sander Helling -Anthony Hrabusicky -David Frazier -Alexander Orlov -Wes Warner -Samuel Grob -Wolfgang Kuhl -Stuart Olson -Michael Shellim -Jay Watkins -Juan Rodriguez -John Bitzer -Bryan Anderson -Claudio Piccinini -Patrick Breidenbach -Harald Puff -Mike Frizell -James A Davey -Ronald Fahlbusch -Piet Teekens -Jean Jacques Bultez -Paul Dittman -Philippe Faure -Jean-Jacques Pointel -Didier Christien -Huguette Viale -Marc Rivière -Cyril Maquaire -Nick Webb -Kristjan Crnoja -Gaines Smith -Tomasz Łapucha -Karl-Heinz Behn -Geir Atle Storhaug -Marco Lange -2 Brains Design -Philip Michels -Peter Helbling -Guy Lamaison -Michael Oeschger -Alistair Sutherland -Chris Hawley -Jeffrey Albers -Thomas Hollatz -Patrice Lafeuille -Gary Jones -Benoit Gruson -Petr Kysela -Sebastien Jehan -Jean Mathieu -Barry Andersen -James Vincent -Baker Hart -Thomas Voth -Michael Kiefer -Ronald Dalton -Thierry Laligand -Joel Lefebvre -Murray Rogers -Renato Elias -Richard Husta -Ian Ferreira -Zhang Tianyu -Josef de Beer -Ewald Moehring -Kevin Karamitros -Beat Zurflueh -Frank Morris -Edwin Butin Bik -Geir Atle Storhaug -Ted Hoenderdos -Sebastian Stefanitsch -Leif Pernstig -Michael Garofalo -Chris Sunman -Adam Kalus -Christoffer H Henni -Michael Stauning -Stuart Smith -Giuseppe Esposito -Stefano Boggia -Manuel Peon -Mark Bennett -Nicholas Platts -Carol B Meravi -Norman Hunt -Raymond Henderson -Christopher Marshall -Christopher Prinos -AntiSocial Engineering -Pierre Duvezin-Caubet -Hermann Haberer -Guy Poignonec -Robin Lauff -Renato Müller -Hannes Mössler -Cédric Lagoutte -Alexander Mathis -Barry Andersen -Philippe Glur -Felix Tang -Heiko Polig -Pierre Marty -Dirk Weiler -Eddie Ehlin -John Clarke -Laurent Thibaudet -James A Davey -Stuart Olson -claudio Piccinini -Kenneth Kriegel -Evening Shade Pottery -Raymond Henderson -Clifton Hipsher -François Carton -Olivier Portron -Didier Hochedez -Julien Gérard -Christian Syrbe -Juan Luis Guerrero Portero -Jonathan Badley -Ms Place -Heart of NM LLC -Daniele Gnesutta -Erik Peterson -SkyRaccoon -James Lummel -Jürgen Husske -Marko Domagoj Zic -Fabrice Debonnet -Ms Place -Jörg Hammer -Rapid Informatics LLC (monthly) -Renaud Perriguey -Edmond Dufresne -Jaroslav Gazik -Philippe Desrichard -Vincent Heron -Marcel Schäfer -Stefano Pascucci -Frédéric Bourgois -Stefan Hufnagl -Jean Philippe Bonacci -Jack Mitchell -Santiago Iglesias Garcia -Steve Kurywchak -Tore Skjefstad -Sergio Digiandco di Molino -Xavier Delepine -Miles Shearman -Bernard Teyssier -Entel Consulting BVBA -Stuart Olson -Maximilian Wechselberger -Kenneth Crump -Ken Elder -Darko Perković -Martin Krueger -Jack Lopes -Huguette Viale -Didier Christien -Pascal Roublot -Arnaud Sourisse -Aldo Midali -Sidney Gates -Serhiy Semenenko -Miguel Esquer Ramiro -Stefan Grunenberg -Ferenc Kunkli -Stefano Boggia -Lee Bennett -Morgantech -Marek Přikryl -Peter Scott -Ernst Camenzind -Steve Robbins -Jean Noel Berlan -Walter Szempruch -Petr Kysela -Gerhard Zdzuj -Joseph S Robinson -Zdenek Trojanek -Fred Page -Burt Pickard-Richardson -Eric Destrebecq -Derek Quigley -Lionel Lunan -Paul Dittman -Justin Delaire -Damir Pooshs -Alvin Burke -Pete Rathbun -Christophe Molliex -Ulf Hedlund -Damian Niegowski -Michae St Cyr -Ivan Pik -Rothermann, Kurt -Antinoro Ferdinando -Sam Day -Roman Rychtarik -Pierre Malriq -Jean Gaillard -Michel Garbarino -For Our Better World -Lothar Herrl -James Rhodes -Edward Glover -James Snodgrass -Dwayne Carden -Gerald Migely -Mark Schmidt -Hendrik Goessaert -Arturas Giedraitis -Paul Vernon -Rick Smith -Marcin Galczynski -Tom Knudtson -Sascha Heinrich -James Snodgrass -Alain Labonne -Darko Perković -Pierre Malriq -Donald Manson -Johannes Ferschl -Piotr Wysocki -Thomas Halenbeck (monthly) -Sjoerd Dost -Ralph Gustafson -Herbert Feix -Robert Costford -Taavi Tuisk -Carlos Quian -Noah Reinhart -Timothy Fry -Marc Stelzner -NXGRAPHICS -AIR-RC -Nicolas Deshaies -Jean-Francois Delhove -Hugo Daniel Farneti -RC electronic products -Adrian Andrei -Henk van de Kerkhof -Daniel Dubovski -Hans-Georg Böhm -Tiberiu Atudorei -Alessandro Apostoli (Yaapu!) -Kenneth Bonerigo -Frits van Voorst -Steven Dodge -Christian Buchinger -Patrick Blanc -Darrell Carew -Philippe Faure -Marco Lange -Aaron Tygart -Frantisek Vajbar -Mike Perkins -Knut Einar Kristensen -Georgios Papadopoulos -He Liu -Andrias Thomsen -Alain Azais -Alan Stuart -Fernando Rimoldi -James Snodgrass -Diego Mendoza Gonzalez -Dale Wheeler -Nico Wagner -James A Davey -Peter Glassey -Jeff Dillingham -Don Crowe -Nicola Claudio Rossi -Fabien Leroy -Tyler Nelson -Stuart Olson -Reinhard Plietsch -Manuel Spoerri -Paul Pearson -Thierry Laligand -Bruno Mexique -Uwe Jordt -Markus Goelzner -Jean-Claude Etiemble -Wojciech Winiarski -Michael Fleming -Russell G Shanks -Mike Sedgwick -William Goodrich -Marcel Schäfer -Joseph Santa Cruz -Gerald Roedel -James D McKeown -鄭 淳中 -Matthias Wuethrich -Michael Tessler -John Beech -Stefan Möst -Thomas McGraw -Bruno N Russo Jr -Daniel Eikenkötter -Guy Poignonec -Gérard Falaise -Jacques Loubier -Kelly Warner -James Cox -Sidney B Harden -CG Tech Inc. -Richard Gopstein -Francesco Ziche -Jean Paul Duroch -Romane Oughlissi -Alexander Maciol -Udo Kastenholz -Jacek Wielogórski -Siegfried Preißler -Bernard Mohamedaly -John North -Thomas Wormsley -Michael Schreiber -DTg Privat -Frédéric Devillard -Lauland Bourg -Jurij Peternel -Murray King -Horia Adrian Bucovanu -Freddy Prousteau -Wojciech Winiarski -James A Davey -James Donelson -David Jolly -Domingos S. Fonseca M. -Kenneth Frehafer -Christian Epple -David Williams -Michael Bernard -Michael Wagner -Carl Pennington -LTR Consulting -Norman Brisson -Darko Perković -Andreas Fischer -Pablo Peinado Abad -NthWave Web Works -Simon Brenner -Joseph Litko -Bradley Murchie -William Cessna -Justin Farnsworth -James Snodgrass -Peter Schneider -Norman Schenck -Boris Hemmer -Gilles Arbona -Patrice Germain -Mart Rogers -Stefan Möst -John Beech -John Rodgers -Stanton Sweet -Peter Suphan -FGS -Outward Ideas -Sylvie Le Madec -James Houck (monthly) -Frank Steinmetz -Gary Garland -Stefano Boggia -Jean-Jacques Pointel -Ferenc Kunkli -Sven Müller -Taavi Tuisk -Thomas Floyd -Matthew LePard -Peter Schneider -Michel Garbarino -Achim Stolze -Fernando Rimoldi -Bernard Delpy -Daniel Dubovsky -Jonathan Starch -Coolwind -Benoit Pichereau -Darko Perković -Burt Pickard-Richardson -Roland Nittby -Frank Barbas -Scott McCoy -Fabrice Debonnet -James Laver -Kevin Bowens -David Frech -Udo Kastenholz -Julio Margarido -Uwe Muendelein -James Jewitt -Daniel Ferreira -Michael Charlston -Leopoldo Cicero -Tom Van Driel -Karl Vetter -Johannes Janssen -François Blondé -Precision Aero -Thomas Håkansson -Gordon Evans -Benny Simonsen -George Kennedy -Stefano Boggia -Maksym Arshynkin -Tomas Sodergren -Jörg Lemnitz -TMac FPV -Josef Schaeffler -Mike Frizell -MeCodeGoodSomeday -Timothy Fry -John Beech -Wojciech Winiarski -Armin Kuepfer -Alain Bregand -James Thompson -Dautremépuis Benoit -Adriano Emanuele Figliola -Stefano Bortolotti -Gerald Zierer -Alessio Noto -Walter Fowler -Robert Black -Klaus Kernstock -Michael Wittich -AG-Software -Dietmar Drees -Winfried Goers -Steve Schoonover -N. Haasnoot (monthly) -Wolfram Bahmann -Florian Reck -Gary Bancroft -Karl-Heinz Behn -Stuart Godsmark -Josko Povse -David Reese -Paul Pearson -Stuart Olson -Pierre Connan -Henk Eeltink -Robert Miller -George Walker -Александр Щитов -Heiko Polig -Gary Wild -Peter Sarcher -Steve Schoonover -Francisco Orcera Exposito -Alan Powell -Martin Eriksson -Wayne J Podaima -Tom Wilkinson -Fred Ceratt -Stefan Grunenberg -Aram Parsamyan -Wolfgang Rauser -John Wall -Martin Haggas -Denis Kelly -James Houck -Brian Stephanik -William McNeice -Sven De Boeck -SB Europe Ltd -Evening Shade Pottery -D van Oostveen -Michael Grosso -Carlos Marcal -d@taweb -Justin Digby -Hermann Vocat -Michael Francini -Michael Chambers -Harold H Youngren -Yann Orselli -Dieter Schaad -Ernst Camenzind -Ciobanu Rivelino Costel -David Jolly -Aram Parsamyan -Stewart Davies -Karsten Schwarz -Robert Mckay -Michael Oeschger -Randy Ling -Jonathan Wise -David Gardner -Christophe Judas -Lachlan Peters -Patrick C Kutschke -Thomas Halenbeck -Barry Wright -Nicolas Ducher -Giampiero Sintoni -Sidney B Harden -John Moody -Sylvain Cubizolles -Adam Kouse -FCG Kas -Dennis Marinus -James Donelson -Stefan Hassler -Reinhard Prix -Jeffrey Johnson -Michael Conway -Ryan Buffington -Thomas Horns -Michael Carnes -William Hallman -James Snodgrass -Michal Mesaros -James Lindner -Laurence Gerrard -Nico Wagner -Christoph Rössner -Ruben Flores -Jim Cukla -Lee Taylor -Stefan Kuster -Barry Wright -Steven Gilley -Lloyd Evans -Nolan O'Hearne -Jeffrey James N Buffie -James Houck -Andreas Arlt -William Walker -Salvador Munoz Vinas -Chris Geiger Photography -John F Smith -Robert Zecha -Fernando Martins -Martin Brungard -Paul Witko -Michael Campbell -Stuart Posnett -PK Doddema -Leonard MacKey -Andrew Peterson -Dominik Sedlacek -Ronald Hellstern -Vicente Suarez -Patrick Geerts -Rossi Giampaolo -Jose Luis Franco Torres -Bart Sillen -Antonio Moreno -Carolina Carcione -Lonnie Misner -John Eppling -Phillip Elliott -Robin Bayer -Jurij Peternel -George Aandahl -Guy Dangerfield -Thomas Limpert -Ismael G Zapater -Alan Hall -Bradley Goodwin -Paul Dittman -John Johnson -Liam Mahoney -Joseph Carr -Fiorenzo Agnelli -Brian Ramsey -Angelo Movio -Weldon Parks -Simon Brink -Weldon Parks -Ralf Herzenbruch -Ashley Pearce -SneakIT -Soaring Stuff -Jörg Lemnitz -Johann Gross -Mark Gibson -Rendall Assoc -Cozyn Holdings Pty Ltd -Tim Karsten -Nick Hesson -Laurent Lapassade -Peter Deacon -Gilles Dando -Philippe Desrichard -Jón Haase -Michael Hansen -Sylvain Vanston -Helmut Nemecek -Alexander Mathis -Jay Watkins -Patrice Oudjaoudi -Didier Hochedez -Robert Hunter -Christophe Gilsoul -Lincoln Roberts -Johan Jurriens -Michael Ledward -Dave Waldesbuehl -Dale Rhoads -Denis Kelly -SBEurope LTD -Jean-Louis Mouche -Grant Mills -Fernando Rimoldi -Jim Cukla -Tomas Kirnak -Raymond Henderson -James Jewitt -Martin Sauter -Philip Hawkey -Lonnie Misner -Michael Hirschauer -Brian Bracken -Stuart Olson -Terry McKinley -Miklos Turcsin -Edward Skerness -Stefano Perinetti -Toni Marchante -Stuart Olson -Peter Campbell -Keith Wood -Leo Duflou -Michael Florey -Gerhard Weixelbaumer -Peter K Lehotsky -Elisha Jones -Joerg Sattler -paul42 -Pascal Heg -Bjorn Sorenssen -Chris Payne -Pascal Afflard -Robert Boboryko -Chris Whitehead -David Bradley -Ian Lever -Phillip Barber -Jan Bronee -N Haasnoot -Rick Smith -Randall Grant -Patrick Degouy -Piotr Zdanowski -David Gardner -TMac FPV -Bernard Thompson -Mark Sutton -Darren J Crane -Michael Keyes -Magnus Borg -David Lorentzen -Antony Smith -Aleksandr Sarkisov -Johannes Kuhne -Kathleen Cilluffo -David Hoffmann -Alan Porter -Arlé Mooldijk -Tenbones -Kim Bitsch Lauridsen -Arthur Costerus -Peter Harvey -John Simpson -Landy de los Santos -Graham Ridley -Schenk Peter -Joseph Rex -Richard Honeywell -Roy Nixon -Andrew Shewan -David Bradley -Alan Micklethwaite -Susan Siegel -Stephen Evans -William Hallman -Franck Michel -László Gajcsi (monthly) -Danny Koch -Robert Martin -Gavin Meerwald -Frédéric Maix -Frank Steinberg -Kevin Berkefeld -Marccos Colon -Kerry Reynolds -Kim Jeppesen -Arthur Peters -Andrew Lowes -Anthony Hallo -Bill Hill -Gerard Falaise -Kevin Raynor -Lorenzo gh -Jan Landeweer -David Bland -Manuel Spoerri -Jeff Jacobs -David Thorne -Maddox Philip -James Jewitt -Justin Digby -John Magnuson (monthly) -Holger Lippert -Hubert Huber -David Blum -Noam Kfir -Mats gårdstad friberg -Jesper Frickmann -Alan Bunker -Emanuel Kircher -Barry Andersen -Gabriel Formanek -Timothy Justiniano -Kelvin Garcia Urbina -Jan Lau -Choon Wei Jonathan Tan -Daniel Navas -Ray Moss -Markus Frielingsdorf -Michael Hirschauer -Jesse Frey -Philipp Koch -Robert Rubio -Dennis G Miller -Leonardo Garcia Osuna -Antonio Carrasco Pastor -Musa Pinar -Jens Moder -Josef de Beer -Chad Mofford -Pim Pouw -Kenth Jonsson -Gordon Westfall -Jean-Michel Saves -Roberto Orsello -Luboš Konvalinka -Lawrence Dorn -Michael Dittmann -Bernd Schmiedel -Raoul Piccioli -Uwe Probst -Glendon Satchell -Trevor Collins -Horst Grashauser -Miguel José Cárdenas Herrera -Mike Jennings -José Bernardo -Carlos Dangle -Gordon Evans -Pablo Peinado Abad -Carl Payne -Adam Sosnowski -Гасак Сергей -SaltCtyRacingFPV -Georg Bernhardt -James Mildenhall -Dave Plummer -Ivan Mokris -Patrick Daniels -Stuart Olson -Cory Znebel -Andrey Yarovoy -Christian Pons -Burt Pickard-Richardson -Michal Mesaros -Conrad Young -Martin Mathes -Jens Rainer Hansen -Mark Trible -Marc Dubois -Alan Wilkins -Daniel Hryciuk -Thomas Fox -Sidney B Harden -Augustas Sereika -David Belletete -Karl-Heinz Behn -Lena Hjort -Jan Langhorst -Alberto Scotti -John Rock -Adam Kouse -Ricardo Martins -Noel Irwin -Christoph Rausch -Anthony Hrabusicky -Sergio Tondini -Aldo Visentin -Greg Wilson -Mark Sutton -Lukas Kana -Peter Newman -Kevin Cave -Arold Reinders -Jason Plant -John Beech -Ian Parris -Danny Music -Eric Bissonnette -Riso LTD -William Booth -Ferenc Kunkli -Offer Shmuely -Troy Atneosen -Long Technologies -John Jaugilas -Jan-Hinrich Klee -Hans-Reinhard Mette -Alejandro Casals Oliver -David Homewood -John Dunning -Konrad Helbach -Bernard Delpy -Raymond Ellsworth -Bogdan Musial -Гасак Сергей -Philip Garcia -Helder Simoes -Thomas Blanchin -Jean-Luc Mesnier -Michael Bajer -David Homewood -Marc Sanders -Буров Александр -Rudy T'Jolleyn -Jay Watkins -Blaz Vodopivec -David De Salvo -Willem Verschoren -Dennis Kathrens -Alan Aucote -Richard Jerome Danstrom -David Garcia-Mendia -Mark Jones -John Zseleczky -Xiao Sun -Glenn Harvey -Albrecht Friebel -Gunter Klemke -Travis McCarthy -Franz Zier -Ian Contessa -Sean Sadler -Richard F Janczak -Mateusz Dziadosz -Thomas Merchant -M L Perkins -Stefano Boggia -Новоселов Юрий -James Rhodes -Peter Gaskill -Jacques Temey -Anthony Challis -Gerard Falaise -George McGinnis -Antoine Soulie -Tjeerd Jager -Bertold Van den Bergh -Stefanie Zerbe -James Mandelbaum -Javier J. Cordovez -Jess Barkley -Schneider Modell -Michael Hasselgaard -Thomas Young -Непочатов Алексей -Thomas Goebel -Joseph Macias -Paw Melin-Nielsen -Liam O'Brien -Ronald B Backman -Corvus Inspection Services Inc -Stuart Posnett -Clark Emerick -Helmut Sippel -Jhon Lennon Silva do Nascimento -Peter McCarthy -Bob Brown -Claude Lawyer -Peter Fithern -Michael Hoffman -Tim Smith -OpenSki Institute -Simon Melov -Richard Podolsky -James Wilson -John Norrbin -James Goins -Phillip Nguyen -Jeffrey M Engel -Richard Marotto -Greg Bell -Scott Geer -Thomas Abshier -Ernst Camenzind -Radomir Sterba -Ignacio Barrio -Bradley Murchie -Emmanuel Balintec -Rowell Torres -Christopher Kokis -Ken Bryan -Eric Yoder -Olav Roth -Rowell Torres -Christopher Kokis -Ken Bryan -Glenn Noonan -Michael Jakob -Dean Berg -David Hankins -Gary Cripps -Devin Crumb -Daniel Naude -James Miller -Pascal Cayol -eDefine -Mario Sette -Hans-Georg Böhm -Robert Oats -Helmut Suhrbier -Johannes Vorster -Deon du Rand -Stephen Hill-Harriss -Regi Pradeeswaran -Gary Busler -Stefano Boggia -Vernon Martin -Alexander W Wilson Jr -Adam Bowersock -Gary Burrows -Mark Gaskievicz -James Miller -Donald Taylor -Paul Makepeace -Thomas Fowler -Walter Szempruch -Charles Starck -Fabian Mächler -David Van Dyne -Karim el Mawardi From 8e758e499406402a5a66717de8d11e8af7cc0008 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 17 Jun 2021 19:03:55 +1000 Subject: [PATCH 099/122] CI should ignore changes to issue templates --- .github/workflows/actions.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index a4dff18f0..c50d5fa85 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -6,11 +6,13 @@ on: - '2.4' paths-ignore: - '**.md' + - '.github/ISSUE_TEMPLATE/**' pull_request: branches: - '2.4' paths-ignore: - '**.md' + - '.github/ISSUE_TEMPLATE/**' jobs: test: From 7e28102dab0bc63b287f1111976769acf11fa0fc Mon Sep 17 00:00:00 2001 From: Risto Date: Thu, 17 Jun 2021 19:56:25 +0200 Subject: [PATCH 100/122] Is not for WSL/WSL2 Added comment that this script is for bare-metal or virtualized Ubuntu installations only. Does NOT run under WSL or WSL2. --- tools/setup_buildenv_ubuntu20.04.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/setup_buildenv_ubuntu20.04.sh b/tools/setup_buildenv_ubuntu20.04.sh index e7b38719b..cce914737 100644 --- a/tools/setup_buildenv_ubuntu20.04.sh +++ b/tools/setup_buildenv_ubuntu20.04.sh @@ -1,6 +1,7 @@ #! /usr/bin/env bash -## Bash script to setup EdgeTX v2.4 development environment on Ubuntu 20.04. +## Bash script to setup EdgeTX v2.4 development environment on Ubuntu 20.04 running on bare-metal or in a virtual machine. +## Does NOT work with WSL/WSL2 under Windows 10! ## Let it run as normal user and when asked, give sudo credentials PAUSEAFTEREACHLINE="false" From a49abcf19fd40d0e7b35965141720e595ad442d9 Mon Sep 17 00:00:00 2001 From: rotorman Date: Thu, 17 Jun 2021 22:50:54 +0200 Subject: [PATCH 101/122] Re-created blank credits to allow building Companion and Simulator binaries for now, until a better solution is available. See #242 --- CREDITS.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 CREDITS.txt diff --git a/CREDITS.txt b/CREDITS.txt new file mode 100644 index 000000000..e69de29bb From 384130583d19e871165f542b8b23df4014dc2a21 Mon Sep 17 00:00:00 2001 From: Peter Feerick Date: Fri, 18 Jun 2021 11:11:07 +1000 Subject: [PATCH 102/122] Finish ripping out `credits.txt` by removing from `companion24` --- CREDITS.txt | 0 companion/src/CMakeLists.txt | 2 - companion/src/companion.qrc | 1 - companion/src/creditsdialog.cpp | 135 -------------------------------- companion/src/creditsdialog.h | 62 --------------- companion/src/mainwindow.cpp | 18 +---- companion/src/mainwindow.h | 2 - radio/src/thirdparty/libopenui | 2 +- 8 files changed, 4 insertions(+), 218 deletions(-) delete mode 100644 CREDITS.txt delete mode 100644 companion/src/creditsdialog.cpp delete mode 100644 companion/src/creditsdialog.h diff --git a/CREDITS.txt b/CREDITS.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/companion/src/CMakeLists.txt b/companion/src/CMakeLists.txt index 4e8f11ca5..b022c3bcf 100644 --- a/companion/src/CMakeLists.txt +++ b/companion/src/CMakeLists.txt @@ -223,7 +223,6 @@ set(companion_SRCS burnconfigdialog.cpp multimodelprinter.cpp comparedialog.cpp - creditsdialog.cpp releasenotesdialog.cpp releasenotesfirmwaredialog.cpp customizesplashdialog.cpp @@ -257,7 +256,6 @@ set(companion_MOC_HDRS comparedialog.h printdialog.h logsdialog.h - creditsdialog.h releasenotesdialog.h releasenotesfirmwaredialog.h customizesplashdialog.h diff --git a/companion/src/companion.qrc b/companion/src/companion.qrc index c76418135..5c231c5a2 100644 --- a/companion/src/companion.qrc +++ b/companion/src/companion.qrc @@ -2,7 +2,6 @@ images/customize.png images/icon.png - ../../CREDITS.txt ../releasenotes.txt images/screenshot.png images/splash.png diff --git a/companion/src/creditsdialog.cpp b/companion/src/creditsdialog.cpp deleted file mode 100644 index 29b6b79bc..000000000 --- a/companion/src/creditsdialog.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#include "creditsdialog.h" -#include "ui_htmldialog.h" -#include "helpers.h" -#include - -CreditsDialog::CreditsDialog(QWidget * parent): - QDialog(parent), - ui(new Ui::HtmlDialog) -{ - ui->setupUi(this); - - setWindowTitle(tr("OpenTX Contributors")); - setWindowIcon(CompanionIcon("contributors.png")); - - QString str = "" \ - "" \ - " " \ - "" - ""; - - foreach(CreditsSection section, readCredits()) { - str.append(formatTable(sectionTitle(section.title), section.names, 3)); - } - - str.append("" \ - " " \ - "
 
" + tr("Honors go to Rafal Tomczak (RadioClone), Thomas Husterer (th9x) and Erez Raviv (er9x and eePe)") + "
"); - -#if 0 - QFile blacklist(":/BLACKLIST.txt"); - if (blacklist.open(QIODevice::ReadOnly | QIODevice::Text)) { - QStringList names; - names << blacklist.readAll(); - str.append(formatTable(tr("OpenTX Blacklist"), names, 1)); - } -#endif - - str.append(""); - ui->textEditor->setHtml(str); - ui->textEditor->scroll(0, 0); - ui->textEditor->setOpenExternalLinks(true); -} - -CreditsDialog::~CreditsDialog() -{ - delete ui; -} - -QList CreditsDialog::readCredits() -{ - QFile credits(":/CREDITS.txt"); - QList result; - if (credits.open(QIODevice::ReadOnly | QIODevice::Text)) { - while (!credits.atEnd()) { - QByteArray line = credits.readLine().trimmed(); - if (line.size() >= 2) { - if (line.startsWith("[") && line.endsWith("]")) { - result.push_back(CreditsSection(line.mid(1, line.size() - 2))); - } else { - result.back().addName(line); - } - } - } - } - return result; -} - -QString CreditsDialog::sectionTitle(const QString & title) -{ - if (title == "Main developers") - return tr("Main developers"); - else if (title == "Translators") - return tr("Translators"); - else if (title == "Companies and projects who have donated to OpenTX") - return tr("Companies and projects who have donated to OpenTX"); - else if (title == "People who have donated to OpenTX") - return tr("People who have donated to OpenTX"); - else - return tr("Other contributors"); -} - -QString CreditsDialog::formatTable(const QString & title, const QStringList & names, int columns) -{ - const float cwidth = 100.0 / columns; - QString str = "" \ - " " \ - " " \ - "
 
" + title + "
"; - - str.append(""); - - int column = 0; - foreach(QString name, names) { - if (column == 0) { - str.append(""); - } - QString trclass = name.contains("monthly") ? "bold" : "normal"; - str.append(QString("").arg(cwidth).arg(trclass).arg(name.trimmed().replace("monthly", tr("monthly")))); - if (++column == columns) { - str.append(""); - column = 0; - } - } - - if (column != 0) { - str.append(""); - } - - str.append("
%3
"); - return str; -} diff --git a/companion/src/creditsdialog.h b/companion/src/creditsdialog.h deleted file mode 100644 index c1327aeea..000000000 --- a/companion/src/creditsdialog.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _CREDITSDIALOG_H_ -#define _CREDITSDIALOG_H_ - -#include - -namespace Ui { - class HtmlDialog; -} - -class CreditsDialog : public QDialog -{ - Q_OBJECT - - class CreditsSection - { - public: - CreditsSection(const QString & title): - title(title) - { - } - - void addName(const QString & name) - { - names.push_back(name); - } - - QString title; - QStringList names; - }; - - public: - explicit CreditsDialog(QWidget * parent = 0); - ~CreditsDialog(); - - private: - Ui::HtmlDialog * ui; - QList readCredits(); - QString sectionTitle(const QString & title); - QString formatTable(const QString & title, const QStringList & names, int colums); -}; - -#endif // _CREDITSDIALOG_H_ diff --git a/companion/src/mainwindow.cpp b/companion/src/mainwindow.cpp index 23c5f71b7..deb9652da 100644 --- a/companion/src/mainwindow.cpp +++ b/companion/src/mainwindow.cpp @@ -29,7 +29,6 @@ #include "downloaddialog.h" #include "printdialog.h" #include "version.h" -#include "creditsdialog.h" #include "releasenotesdialog.h" #include "releasenotesfirmwaredialog.h" #include "customizesplashdialog.h" @@ -868,13 +867,6 @@ void MainWindow::fwPrefs() dialog->deleteLater(); } -void MainWindow::contributors() -{ - CreditsDialog * dialog = new CreditsDialog(this); - dialog->exec(); - dialog->deleteLater(); -} - void MainWindow::sdsync() { // remember user-selectable options for duration of session TODO: save to settings @@ -1230,8 +1222,7 @@ void MainWindow::retranslateUi(bool showMsg) trAct(burnConfigAct, tr("Configure Communications..."), tr("Configure software for communicating with the Radio")); trAct(writeBUToRadioAct, tr("Write Backup to Radio"), tr("Write Backup from file to Radio")); trAct(readBUToFileAct, tr("Backup Radio to File"), tr("Save a complete backup file of all settings and model data in the Radio")); - trAct(contributorsAct, tr("Contributors..."), tr("A tribute to those who have contributed to OpenTX and Companion")); - + trAct(createProfileAct, tr("Add Radio Profile"), tr("Create a new Radio Settings Profile")); trAct(copyProfileAct, tr("Copy Current Radio Profile"), tr("Duplicate current Radio Settings Profile")); trAct(deleteProfileAct, tr("Delete Current Radio Profile..."), tr("Delete the current Radio Settings Profile")); @@ -1306,8 +1297,7 @@ void MainWindow::createActions() aboutAct = addAct("information.png", SLOT(about())); openDocURLAct = addAct("changelog.png", SLOT(openDocURL())); changelogAct = addAct("changelog.png", SLOT(changelog())); - contributorsAct = addAct("contributors.png", SLOT(contributors())); - + // these two get assigned menus in createMenus() recentFilesAct = addAct("recentdocument.png"); profilesMenuAct = addAct("profiles.png"); @@ -1315,7 +1305,6 @@ void MainWindow::createActions() exitAct->setMenuRole(QAction::QuitRole); aboutAct->setMenuRole(QAction::AboutRole); appPrefsAct->setMenuRole(QAction::PreferencesRole); - contributorsAct->setMenuRole(QAction::ApplicationSpecificRole); openDocURLAct->setMenuRole(QAction::ApplicationSpecificRole); checkForUpdatesAct->setMenuRole(QAction::ApplicationSpecificRole); changelogAct->setMenuRole(QAction::ApplicationSpecificRole); @@ -1402,8 +1391,7 @@ void MainWindow::createMenus() helpMenu->addSeparator(); helpMenu->addAction(changelogAct); helpMenu->addSeparator(); - helpMenu->addAction(contributorsAct); - + recentFilesMenu = new QMenu(this); recentFilesMenu->setToolTipsVisible(true); for ( int i = 0; i < g.historySize(); ++i) { diff --git a/companion/src/mainwindow.h b/companion/src/mainwindow.h index 0be249f04..78178d7f8 100644 --- a/companion/src/mainwindow.h +++ b/companion/src/mainwindow.h @@ -111,7 +111,6 @@ class MainWindow : public QMainWindow void readBackup(); void burnConfig(); void burnList(); - void contributors(); void sdsync(); void changelog(); void customizeSplash(); @@ -200,7 +199,6 @@ class MainWindow : public QMainWindow QAction *appPrefsAct; QAction *fwPrefsAct; QAction *checkForUpdatesAct; - QAction *contributorsAct; QAction *sdsyncAct; QAction *changelogAct; QAction *compareAct; diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 3c280f84c..6b661c490 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 3c280f84cef56f84044c76d8aacea0d5e1bc8278 +Subproject commit 6b661c4907bcafc08436e5c96fc5a38e9eb6e499 From 84e0d6e5a6b52ba338f55830bc800e7b41f6532d Mon Sep 17 00:00:00 2001 From: eshifri Date: Fri, 18 Jun 2021 14:00:14 -0700 Subject: [PATCH 103/122] change sign for number edit --- radio/src/gui/colorlcd/widgets/value.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/radio/src/gui/colorlcd/widgets/value.cpp b/radio/src/gui/colorlcd/widgets/value.cpp index b79a19d89..22fd4d3b7 100644 --- a/radio/src/gui/colorlcd/widgets/value.cpp +++ b/radio/src/gui/colorlcd/widgets/value.cpp @@ -90,6 +90,11 @@ class ValueWidget: public Widget drawTimer(dc, xValue, yValue, abs(timerState.val), attrValue | FONT(XL) | CUSTOM_COLOR); } + if (field == MIXSRC_TX_TIME) { + drawTimer(dc, xValue, yValue, getValue(MIXSRC_TX_TIME), + attrValue | FONT(XL) | CUSTOM_COLOR); + } + if (field >= MIXSRC_FIRST_TELEM) { TelemetryItem & telemetryItem = telemetryItems[(field - MIXSRC_FIRST_TELEM) / 3]; // TODO macro to convert a source to a telemetry index if (!telemetryItem.isAvailable() || telemetryItem.isOld()) { From 0d5504686eba05ef961e20c79580e6dfa549a48a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 19 Jun 2021 12:07:55 +0200 Subject: [PATCH 104/122] Remove widgets from the layout as a FormGroup as well and fix the focus order pointers. Resolves #257 and potentially more. --- radio/src/gui/colorlcd/layout.cpp | 2 -- .../colorlcd/widgets/widgets_container_impl.h | 19 +++++++++++++------ radio/src/thirdparty/libopenui | 2 +- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/radio/src/gui/colorlcd/layout.cpp b/radio/src/gui/colorlcd/layout.cpp index 10cdf1a96..862168d1f 100644 --- a/radio/src/gui/colorlcd/layout.cpp +++ b/radio/src/gui/colorlcd/layout.cpp @@ -123,8 +123,6 @@ void loadCustomScreens() } auto topbar = viewMain->getTopbar(); - - // TODO: how to reload this guy? topbar->load(); viewMain->setCurrentMainView(0); diff --git a/radio/src/gui/colorlcd/widgets/widgets_container_impl.h b/radio/src/gui/colorlcd/widgets/widgets_container_impl.h index 0fb615c3a..9d2836781 100644 --- a/radio/src/gui/colorlcd/widgets/widgets_container_impl.h +++ b/radio/src/gui/colorlcd/widgets/widgets_container_impl.h @@ -43,10 +43,12 @@ class WidgetsContainerImpl: public WidgetsContainer // remove old one if existing removeWidget(index); - Widget * widget = nullptr; + Widget* widget = nullptr; if (factory) { - strncpy(persistentData->zones[index].widgetName, factory->getName(), sizeof(ZonePersistentData::widgetName)); - widget = factory->create(this, getZone(index), &persistentData->zones[index].widgetData); + strncpy(persistentData->zones[index].widgetName, factory->getName(), + sizeof(ZonePersistentData::widgetName)); + widget = factory->create(this, getZone(index), + &persistentData->zones[index].widgetData); } widgets[index] = widget; @@ -61,12 +63,16 @@ class WidgetsContainerImpl: public WidgetsContainer if (index >= N) return; - if (widgets[index]) + if (widgets[index]) { + removeField(widgets[index]); widgets[index]->deleteLater(); + } widgets[index] = nullptr; - memset(persistentData->zones[index].widgetName, 0, sizeof(ZonePersistentData::widgetName)); - memset(&persistentData->zones[index].widgetData, 0, sizeof(Widget::PersistentData)); + memset(persistentData->zones[index].widgetName, 0, + sizeof(ZonePersistentData::widgetName)); + memset(&persistentData->zones[index].widgetData, 0, + sizeof(Widget::PersistentData)); } Widget * getWidget(unsigned int index) override @@ -89,6 +95,7 @@ class WidgetsContainerImpl: public WidgetsContainer // remove old widget if (widgets[i]) { + removeField(widgets[i]); widgets[i]->deleteLater(); widgets[i] = nullptr; } diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index 6b661c490..d1c94e627 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit 6b661c4907bcafc08436e5c96fc5a38e9eb6e499 +Subproject commit d1c94e6279e7b829f4b0953bc4bea1100bae5543 From fd02387cf2a5493200ef26df978f0c4d65290c98 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 19 Jun 2021 14:00:53 +0200 Subject: [PATCH 105/122] Restored proper data format for splashMode Resolves #236 --- radio/src/datastructs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 557a9f3cd..a41f03b68 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -705,7 +705,7 @@ PACK(struct TrainerData { #if defined(COLORLCD) #define SPLASH_MODE uint8_t splashSpares:3 #else - #define SPLASH_MODE uint8_t splashMode:1; uint8_t splashSpare:2 + #define SPLASH_MODE int8_t splashMode:3 #endif #if defined(PCBXLITES) From 82f60cdc81f2d12259b577dfc907c00879cdcd91 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 19 Jun 2021 14:48:41 +0200 Subject: [PATCH 106/122] Fix model names on model select page with not yet converted models Resolves #248 --- radio/src/gui/colorlcd/model_select.cpp | 23 +++++++++++++++---- radio/src/storage/conversions/conversions.cpp | 6 ++--- radio/src/storage/modelslist.cpp | 4 +++- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 9a4a0115b..04e08f621 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -108,15 +108,30 @@ class ModelButton : public Button if (strncmp(modelCell->modelFilename, g_eeGeneral.currModelFilename, LEN_MODEL_FILENAME) == 0) { memcpy(&partialModel.header, &g_model.header, sizeof(partialModel)); + version = EEPROM_VER; } else { error = readModel(modelCell->modelFilename, (uint8_t *)&partialModel.header, sizeof(partialModel), &version); - if (!error) { - if (modelCell->modelName[0] == '\0' && - partialModel.header.name[0] != '\0') { - modelCell->setModelName(partialModel.header.name); + } + + if (!error) { + if (modelCell->modelName[0] == '\0' && + partialModel.header.name[0] != '\0') { + + if (version == 219) { + size_t len = sizeof(partialModel.header.name); + char* str = partialModel.header.name; + for (int i=0; i < len; i++) { + str[i] = zchar2char(str[i]); + } + // Trim string + while(len > 0 && str[len-1]) { + if (str[len - 1] != ' ' && str[len - 1] != '\0') break; + str[--len] = '\0'; + } } + modelCell->setModelName(partialModel.header.name); } } diff --git a/radio/src/storage/conversions/conversions.cpp b/radio/src/storage/conversions/conversions.cpp index 7071b7540..34430cce6 100644 --- a/radio/src/storage/conversions/conversions.cpp +++ b/radio/src/storage/conversions/conversions.cpp @@ -59,6 +59,9 @@ void convertRadioData(int version) RAISE_ALERT(STR_STORAGE_WARNING, STR_SDCARD_CONVERSION_REQUIRE, NULL, AU_NONE); + // Load models list before converting + modelslist.load(); + unsigned converted = 0; auto to_convert = modelslist.getModelsCount() + 1; @@ -76,9 +79,6 @@ void convertRadioData(int version) RTOS_WAIT_MS(200); #endif - // Load models list before converting - modelslist.load(); - const char* error = nullptr; for (auto category_ptr : modelslist.getCategories()) { for (auto model_ptr : *category_ptr) { diff --git a/radio/src/storage/modelslist.cpp b/radio/src/storage/modelslist.cpp index b871c5732..1ba806284 100644 --- a/radio/src/storage/modelslist.cpp +++ b/radio/src/storage/modelslist.cpp @@ -48,7 +48,9 @@ ModelCell::~ModelCell() void ModelCell::setModelName(char * name) { strncpy(modelName, name, LEN_MODEL_NAME); - if (modelName[0] == 0) { + modelName[LEN_MODEL_NAME] = '\0'; + + if (modelName[0] == '\0') { char * tmp; strncpy(modelName, modelFilename, LEN_MODEL_NAME); tmp = (char *) memchr(modelName, '.', LEN_MODEL_NAME); From afe7c13b8b81a2f26f4fa17f2502d5aa207356c7 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 19 Jun 2021 16:25:20 +0200 Subject: [PATCH 107/122] Fix conversion of input line name, curve names, and write radio.bin to disk early Resolves #264 Resolves #255 --- radio/src/storage/conversions/conversions.cpp | 2 ++ radio/src/storage/conversions/conversions_219_220.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/radio/src/storage/conversions/conversions.cpp b/radio/src/storage/conversions/conversions.cpp index 34430cce6..0e474d1c4 100644 --- a/radio/src/storage/conversions/conversions.cpp +++ b/radio/src/storage/conversions/conversions.cpp @@ -71,6 +71,8 @@ void convertRadioData(int version) #if STORAGE_CONVERSIONS < 220 if (version == 219) { convertRadioData_219_to_220(g_eeGeneral); + storageDirty(EE_GENERAL); + storageCheck(true); } #endif converted++; diff --git a/radio/src/storage/conversions/conversions_219_220.cpp b/radio/src/storage/conversions/conversions_219_220.cpp index 8c6d841f1..9fb21e390 100644 --- a/radio/src/storage/conversions/conversions_219_220.cpp +++ b/radio/src/storage/conversions/conversions_219_220.cpp @@ -109,7 +109,7 @@ void convertModelData_219_to_220(ModelData &model) convertToStr(model.header.name, LEN_MODEL_NAME_219); for (uint8_t i=0; i Date: Sat, 19 Jun 2021 16:55:35 +0200 Subject: [PATCH 108/122] Add special function parameter for "Adjust" function Resolves #253 --- radio/src/gui/colorlcd/special_functions.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index 5c79687e0..e781bcb56 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -250,6 +250,19 @@ class SpecialFunctionEditPage : public Page }); break; } + + case FUNC_ADJUST_GVAR: { + //TODO: GVAR choice: CFN_GVAR_INDEX(cfn) + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + auto choice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MAX_GVARS - 1, GET_SET_DEFAULT(CFN_GVAR_INDEX(cfn))); + choice->setTextHandler([](int32_t value) { + return std::string(STR_GV) + std::to_string(value); + }); + grid.nextLine(); + } } if (HAS_ENABLE_PARAM(func)) { From 869bcc09b70f18c31bbd3371c84b05b3dac36540 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 19 Jun 2021 17:05:55 +0200 Subject: [PATCH 109/122] Fix displaying ModelBmp on small surfaces Resolves #269 --- radio/src/gui/colorlcd/widgets/modelbmp.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/widgets/modelbmp.cpp b/radio/src/gui/colorlcd/widgets/modelbmp.cpp index daf5977f7..3eee954d0 100644 --- a/radio/src/gui/colorlcd/widgets/modelbmp.cpp +++ b/radio/src/gui/colorlcd/widgets/modelbmp.cpp @@ -97,7 +97,12 @@ class ModelBitmapWidget: public Widget TRACE("could not load bitmap '%s'", filename.c_str()); return; } - buffer->drawScaledBitmap(bitmap.get(), 0, 38, width(), height() - 38); + + if (rect.h >= 96 && rect.w >= 120) { + buffer->drawScaledBitmap(bitmap.get(), 0, 38, width(), height() - 38); + } else { + buffer->drawScaledBitmap(bitmap.get(), 0, 0, width(), height()); + } } } }; From 83375befc1115bcb827245acdca98308975cc372 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 19 Jun 2021 17:05:55 +0200 Subject: [PATCH 110/122] Fix displaying ModelBmp on small surfaces Resolves #269 --- radio/src/gui/colorlcd/layouts/layout2x4.cpp | 21 +++++++++++++++++++- radio/src/gui/colorlcd/widgets/modelbmp.cpp | 7 ++++++- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/colorlcd/layouts/layout2x4.cpp b/radio/src/gui/colorlcd/layouts/layout2x4.cpp index b88c97095..18fa09a5e 100644 --- a/radio/src/gui/colorlcd/layouts/layout2x4.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2x4.cpp @@ -56,7 +56,7 @@ class Layout2x4: public Layout getOptionValue(OPTION_PANEL1_BACKGROUND)->boolValue = true; getOptionValue(OPTION_PANEL1_COLOR)->unsignedValue = RGB(77,112,203); getOptionValue(OPTION_PANEL2_BACKGROUND)->boolValue = true; - getOptionValue(OPTION_PANEL2_COLOR)->unsignedValue = RGB(77,112,203); + getOptionValue(OPTION_PANEL2_COLOR)->unsignedValue = RGB(7,1,2); } unsigned int getZonesCount() const override @@ -79,6 +79,25 @@ class Layout2x4: public Layout return zone; } + + void paint(BitmapBuffer * dc) override; }; +void Layout2x4::paint(BitmapBuffer* dc) +{ + rect_t fullScreen = Layout::getMainZone(); + fullScreen.w /=2; + if (getOptionValue(OPTION_PANEL1_BACKGROUND)->boolValue) { + lcdSetColor(getOptionValue(OPTION_PANEL1_COLOR)->unsignedValue); + dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, fullScreen.h, CUSTOM_COLOR); + } + + if (getOptionValue(OPTION_PANEL2_BACKGROUND)->boolValue) { + fullScreen.x += fullScreen.w; + lcdSetColor(getOptionValue(OPTION_PANEL2_COLOR)->unsignedValue); + dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, fullScreen.h, CUSTOM_COLOR); + } + Layout::paint(dc); +} + BaseLayoutFactory layout2x4("Layout2x4", "2 x 4", LBM_LAYOUT_2x4, OPTIONS_LAYOUT_2x4); diff --git a/radio/src/gui/colorlcd/widgets/modelbmp.cpp b/radio/src/gui/colorlcd/widgets/modelbmp.cpp index daf5977f7..3eee954d0 100644 --- a/radio/src/gui/colorlcd/widgets/modelbmp.cpp +++ b/radio/src/gui/colorlcd/widgets/modelbmp.cpp @@ -97,7 +97,12 @@ class ModelBitmapWidget: public Widget TRACE("could not load bitmap '%s'", filename.c_str()); return; } - buffer->drawScaledBitmap(bitmap.get(), 0, 38, width(), height() - 38); + + if (rect.h >= 96 && rect.w >= 120) { + buffer->drawScaledBitmap(bitmap.get(), 0, 38, width(), height() - 38); + } else { + buffer->drawScaledBitmap(bitmap.get(), 0, 0, width(), height()); + } } } }; From 02c4e74a3f896fed4d24bdda3b13940caa58dc51 Mon Sep 17 00:00:00 2001 From: eshifri Date: Sat, 19 Jun 2021 11:16:40 -0700 Subject: [PATCH 111/122] Fix for #256: change background for 2x4 widgets. --- radio/src/gui/colorlcd/layouts/layout2x4.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/layouts/layout2x4.cpp b/radio/src/gui/colorlcd/layouts/layout2x4.cpp index 18fa09a5e..106b60d18 100644 --- a/radio/src/gui/colorlcd/layouts/layout2x4.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2x4.cpp @@ -56,7 +56,7 @@ class Layout2x4: public Layout getOptionValue(OPTION_PANEL1_BACKGROUND)->boolValue = true; getOptionValue(OPTION_PANEL1_COLOR)->unsignedValue = RGB(77,112,203); getOptionValue(OPTION_PANEL2_BACKGROUND)->boolValue = true; - getOptionValue(OPTION_PANEL2_COLOR)->unsignedValue = RGB(7,1,2); + getOptionValue(OPTION_PANEL2_COLOR)->unsignedValue = RGB(77,112,203); } unsigned int getZonesCount() const override From 2644fc6c9e8484eb63a339480fff821744dcd337 Mon Sep 17 00:00:00 2001 From: eshifri Date: Sat, 19 Jun 2021 11:24:18 -0700 Subject: [PATCH 112/122] Fixed formatting --- radio/src/gui/colorlcd/layouts/layout2x4.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/colorlcd/layouts/layout2x4.cpp b/radio/src/gui/colorlcd/layouts/layout2x4.cpp index 106b60d18..17c2bc0e7 100644 --- a/radio/src/gui/colorlcd/layouts/layout2x4.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2x4.cpp @@ -86,16 +86,18 @@ class Layout2x4: public Layout void Layout2x4::paint(BitmapBuffer* dc) { rect_t fullScreen = Layout::getMainZone(); - fullScreen.w /=2; + fullScreen.w /= 2; if (getOptionValue(OPTION_PANEL1_BACKGROUND)->boolValue) { lcdSetColor(getOptionValue(OPTION_PANEL1_COLOR)->unsignedValue); - dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, fullScreen.h, CUSTOM_COLOR); + dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, + fullScreen.h, CUSTOM_COLOR); } if (getOptionValue(OPTION_PANEL2_BACKGROUND)->boolValue) { fullScreen.x += fullScreen.w; lcdSetColor(getOptionValue(OPTION_PANEL2_COLOR)->unsignedValue); - dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, fullScreen.h, CUSTOM_COLOR); + dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, + fullScreen.h, CUSTOM_COLOR); } Layout::paint(dc); } From 487d3713149bd15f3fc75e6d1c17b48fcbcf665b Mon Sep 17 00:00:00 2001 From: eshifri Date: Sat, 19 Jun 2021 22:53:23 -0700 Subject: [PATCH 113/122] Base class paint should be called prior. --- radio/src/gui/colorlcd/layouts/layout2x4.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/layouts/layout2x4.cpp b/radio/src/gui/colorlcd/layouts/layout2x4.cpp index 17c2bc0e7..7214c8535 100644 --- a/radio/src/gui/colorlcd/layouts/layout2x4.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2x4.cpp @@ -85,6 +85,7 @@ class Layout2x4: public Layout void Layout2x4::paint(BitmapBuffer* dc) { + Layout::paint(dc); rect_t fullScreen = Layout::getMainZone(); fullScreen.w /= 2; if (getOptionValue(OPTION_PANEL1_BACKGROUND)->boolValue) { @@ -99,7 +100,6 @@ void Layout2x4::paint(BitmapBuffer* dc) dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, fullScreen.h, CUSTOM_COLOR); } - Layout::paint(dc); } BaseLayoutFactory layout2x4("Layout2x4", "2 x 4", LBM_LAYOUT_2x4, OPTIONS_LAYOUT_2x4); From 9737ce80ff79226a012cd49489d8103fb028325c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 20 Jun 2021 08:08:23 +0200 Subject: [PATCH 114/122] Fixed Adjust GVAR special function Resolves #277 --- radio/src/gui/colorlcd/special_functions.cpp | 119 +++++++++++++++++-- 1 file changed, 109 insertions(+), 10 deletions(-) diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index e781bcb56..3faf9d9ad 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -252,16 +252,89 @@ class SpecialFunctionEditPage : public Page } case FUNC_ADJUST_GVAR: { - //TODO: GVAR choice: CFN_GVAR_INDEX(cfn) + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), - STR_VALUE); - auto choice = + STR_GLOBALVAR); + auto gvarchoice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MAX_GVARS - 1, GET_SET_DEFAULT(CFN_GVAR_INDEX(cfn))); - choice->setTextHandler([](int32_t value) { - return std::string(STR_GV) + std::to_string(value); + gvarchoice->setTextHandler([](int32_t value) { + return std::string(STR_GV) + std::to_string(value + 1); }); grid.nextLine(); + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_MODE); + auto modechoice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), + FUNC_ADJUST_GVAR_CONSTANT, FUNC_ADJUST_GVAR_INCDEC, + GET_DEFAULT(CFN_GVAR_MODE(cfn)), nullptr); + grid.nextLine(); + + modechoice->setTextHandler([](int32_t value) { + switch (value) { + case FUNC_ADJUST_GVAR_CONSTANT: + return std::string(STR_CONSTANT); + case FUNC_ADJUST_GVAR_SOURCE: + return std::string(STR_MIXSOURCE); + case FUNC_ADJUST_GVAR_GVAR: + return std::string(STR_GLOBALVAR); + case FUNC_ADJUST_GVAR_INCDEC: + return std::string(STR_INCDEC); + } + return std::string("---"); + }); + + modechoice->setSetValueHandler([=](int32_t newValue) { + CFN_GVAR_MODE(cfn) = newValue; + CFN_PARAM(cfn) = 0; + SET_DIRTY(); + updateSpecialFunctionOneWindow(); + }); + + switch (CFN_GVAR_MODE(cfn)) { + case FUNC_ADJUST_GVAR_CONSTANT: { + //TODO: label + int16_t val_min, val_max; + getMixSrcRange(CFN_GVAR_INDEX(cfn) + MIXSRC_FIRST_GVAR, val_min, + val_max); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), + val_min, val_max, GET_SET_DEFAULT(CFN_PARAM(cfn))); + break; + } + case FUNC_ADJUST_GVAR_SOURCE: + //TODO: label + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), + 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); + break; + case FUNC_ADJUST_GVAR_GVAR: { + auto gvarchoice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MAX_GVARS - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + gvarchoice->setTextHandler([](int32_t value) { + return std::string(STR_GV) + std::to_string(value + 1); + }); + gvarchoice->setAvailableHandler([=](int value) { + return CFN_GVAR_INDEX(cfn) != value; + }); + break; + } + case FUNC_ADJUST_GVAR_INCDEC: { + int16_t val_min, val_max; + getMixSrcRange(CFN_GVAR_INDEX(cfn) + MIXSRC_FIRST_GVAR, val_min, val_max); + getGVarIncDecRange(val_min, val_max); + auto numedit = new NumberEdit(specialFunctionOneWindow, + grid.getFieldSlot(), val_min, val_max, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + numedit->setDisplayHandler( + [](BitmapBuffer *dc, LcdFlags flags, int value) { + dc->drawNumber(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, abs(value), + flags, 0, value >= 0 ? "+= " : "-= ", nullptr); + }); + break; + } + } + grid.nextLine(); } } @@ -403,12 +476,18 @@ class SpecialFunctionButton : public Button dc->drawNumber(col2, line2, CFN_PARAM(cfn)); break; - case FUNC_TRAINER: - drawSource( - dc, col1, line2, - CFN_CH_INDEX(cfn) == 0 ? 0 : MIXSRC_Rud + CFN_CH_INDEX(cfn) - 1); + case FUNC_TRAINER: { + std::string text; + int16_t value = CFN_CH_INDEX(cfn); + if (value == 0) + text = std::string(STR_STICKS); + else if (value == NUM_STICKS + 1) + text = std::string(STR_CHANS); + else + text = TEXT_AT_INDEX(STR_VSRCRAW, value); + dc->drawText(col1, line2, text.c_str(), 0); break; - + } case FUNC_RESET: if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { dc->drawTextAtIndex(col1, line2, STR_VFSWRESET, CFN_PARAM(cfn)); @@ -463,6 +542,26 @@ class SpecialFunctionButton : public Button dc->drawNumber(col3, line1, CFN_PARAM(cfn), PREC1, sizeof(CFN_PARAM(cfn)), nullptr, "s"); break; + + case FUNC_ADJUST_GVAR: + switch(CFN_GVAR_MODE(cfn)) { + case FUNC_ADJUST_GVAR_CONSTANT: + dc->drawNumber(col1, line2, CFN_PARAM(cfn), 0); + break; + case FUNC_ADJUST_GVAR_SOURCE: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; + case FUNC_ADJUST_GVAR_GVAR: + drawSource(dc, col1, line2, CFN_PARAM(cfn) + MIXSRC_FIRST_GVAR, 0); + break; + case FUNC_ADJUST_GVAR_INCDEC: { + int16_t value = CFN_PARAM(cfn); + std::string text(value >= 0 ? "+= " : "-= "); + text += std::to_string(abs(value)); + dc->drawText(col1, line2, text.c_str()); + break; + } + } } if (HAS_ENABLE_PARAM(func)) { theme->drawCheckBox(dc, CFN_ACTIVE(cfn), col3, line2); From 1d050efa6da7cc5d5b7fe8644f98a9fd2246718b Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 20 Jun 2021 08:29:20 +0200 Subject: [PATCH 115/122] Fix cluttered GPS position display Resolves #273 --- radio/src/gui/colorlcd/draw_functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/draw_functions.cpp b/radio/src/gui/colorlcd/draw_functions.cpp index 122c0ac91..70355d4e1 100644 --- a/radio/src/gui/colorlcd/draw_functions.cpp +++ b/radio/src/gui/colorlcd/draw_functions.cpp @@ -317,7 +317,7 @@ coord_t drawGPSCoord(BitmapBuffer * dc, coord_t x, coord_t y, int32_t value, con } *tmp++ = direction[value>=0 ? 0 : 1]; *tmp = '\0'; - dc->drawText(x, y, s, flags); + x += dc->drawText(x, y, s, flags); return x; } From f3b50c177ca78154b36aeb07e89828b8ba391c7a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 20 Jun 2021 09:32:10 +0200 Subject: [PATCH 116/122] Fixed cluttered GPS display if right-aligned. --- radio/src/gui/colorlcd/draw_functions.cpp | 11 +++++------ radio/src/thirdparty/libopenui | 2 +- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/radio/src/gui/colorlcd/draw_functions.cpp b/radio/src/gui/colorlcd/draw_functions.cpp index 70355d4e1..687aeab62 100644 --- a/radio/src/gui/colorlcd/draw_functions.cpp +++ b/radio/src/gui/colorlcd/draw_functions.cpp @@ -317,17 +317,16 @@ coord_t drawGPSCoord(BitmapBuffer * dc, coord_t x, coord_t y, int32_t value, con } *tmp++ = direction[value>=0 ? 0 : 1]; *tmp = '\0'; - x += dc->drawText(x, y, s, flags); + x = dc->drawText(x, y, s, flags); return x; } void drawGPSPosition(BitmapBuffer * dc, coord_t x, coord_t y, int32_t longitude, int32_t latitude, LcdFlags flags) { - if (flags & EXPANDED) { - drawGPSCoord(dc, x, y, latitude, "NS", flags, true); - drawGPSCoord(dc, x, y + FH, longitude, "EW", flags, true); - } - else { + if (flags & RIGHT) { + x = drawGPSCoord(dc, x, y, longitude, "EW", flags, false); + drawGPSCoord(dc, x - 5, y, latitude, "NS", flags, false); + } else { x = drawGPSCoord(dc, x, y, latitude, "NS", flags, false); drawGPSCoord(dc, x + 5, y, longitude, "EW", flags, false); } diff --git a/radio/src/thirdparty/libopenui b/radio/src/thirdparty/libopenui index d1c94e627..bca0e093b 160000 --- a/radio/src/thirdparty/libopenui +++ b/radio/src/thirdparty/libopenui @@ -1 +1 @@ -Subproject commit d1c94e6279e7b829f4b0953bc4bea1100bae5543 +Subproject commit bca0e093b001596d27cc8297a3ec142a3b42239b From 3b317848720ff83cd435d47a550249fe9d10deaf Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 20 Jun 2021 09:33:27 +0200 Subject: [PATCH 117/122] Fixed value widget colors --- radio/src/gui/colorlcd/widgets/value.cpp | 34 +++++++++++++----------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/radio/src/gui/colorlcd/widgets/value.cpp b/radio/src/gui/colorlcd/widgets/value.cpp index 22fd4d3b7..f6515f731 100644 --- a/radio/src/gui/colorlcd/widgets/value.cpp +++ b/radio/src/gui/colorlcd/widgets/value.cpp @@ -26,10 +26,11 @@ const coord_t NUMBERS_PADDING = 4; class ValueWidget: public Widget { public: - ValueWidget(const WidgetFactory * factory, FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData): - Widget(factory, parent, rect, persistentData) - { - } + ValueWidget(const WidgetFactory* factory, FormGroup* parent, + const rect_t& rect, Widget::PersistentData* persistentData) : + Widget(factory, parent, rect, persistentData) + { + } void refresh(BitmapBuffer * dc) override { @@ -37,11 +38,12 @@ class ValueWidget: public Widget mixsrc_t field = persistentData->options[0].value.unsignedValue; // get color from options[1] - lcdSetColor(persistentData->options[1].value.unsignedValue); + LcdFlags color = COLOR2FLAGS(persistentData->options[1].value.unsignedValue); coord_t xValue, yValue, xLabel, yLabel; LcdFlags attrValue, attrLabel = 0; + bool twolines = false; if (width() < 120 && height() < 50) { xValue = 0; yValue = 14; @@ -64,7 +66,8 @@ class ValueWidget: public Widget yLabel = 2; if (field >= MIXSRC_FIRST_TELEM) { if (isGPSSensor(1 + (field - MIXSRC_FIRST_TELEM) / 3)) { - attrValue = LEFT | FONT(L) | EXPANDED; + attrValue = LEFT | FONT(L);// | EXPANDED; // TODO + twolines = true; } else { attrValue = LEFT | FONT(XL); @@ -72,7 +75,8 @@ class ValueWidget: public Widget } #if defined(INTERNAL_GPS) else if (field == MIXSRC_TX_GPS) { - attrValue = LEFT | FONT(L) | EXPANDED; + attrValue = LEFT | FONT(L);// | EXPANDED; // TODO + twolines = true; } #endif else { @@ -87,28 +91,28 @@ class ValueWidget: public Widget } drawSource(dc, NUMBERS_PADDING, 2, field, CUSTOM_COLOR); drawSource(dc, NUMBERS_PADDING + 1, 3, field, BLACK); - drawTimer(dc, xValue, yValue, abs(timerState.val), attrValue | FONT(XL) | CUSTOM_COLOR); + drawTimer(dc, xValue, yValue, abs(timerState.val), attrValue | FONT(XL) | color); } if (field == MIXSRC_TX_TIME) { drawTimer(dc, xValue, yValue, getValue(MIXSRC_TX_TIME), - attrValue | FONT(XL) | CUSTOM_COLOR); + attrValue | FONT(XL) | color); } if (field >= MIXSRC_FIRST_TELEM) { - TelemetryItem & telemetryItem = telemetryItems[(field - MIXSRC_FIRST_TELEM) / 3]; // TODO macro to convert a source to a telemetry index + TelemetryItem & telemetryItem = telemetryItems[(field - MIXSRC_FIRST_TELEM) / 3]; if (!telemetryItem.isAvailable() || telemetryItem.isOld()) { - lcdSetColor(lcdColorTable[TEXT_DISABLE_COLOR_INDEX]); + color = TEXT_DISABLE_COLOR; } } if (persistentData->options[2].value.boolValue) { - drawSource(dc,xLabel + 1, yLabel + 1, field, attrLabel | BLACK); - drawSourceValue(dc, xValue + 1, yValue + 1, field, attrValue | BLACK); + drawSource(dc,xLabel + 1, yLabel + 1, field, attrLabel | COLOR2FLAGS(BLACK)); + drawSourceValue(dc, xValue + 1, yValue + 1, field, attrValue | COLOR2FLAGS(BLACK)); } - drawSource(dc, xLabel, yLabel, field, attrLabel | CUSTOM_COLOR); - drawSourceValue(dc, xValue, yValue, field, attrValue | CUSTOM_COLOR); + drawSource(dc, xLabel, yLabel, field, attrLabel | color); + drawSourceValue(dc, xValue, yValue, field, attrValue | color); } void checkEvents() override From 3472bbdc781a93ef87fc6273b255889da921860e Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 20 Jun 2021 10:35:35 +0200 Subject: [PATCH 118/122] Fixed 2 lines display of GPS values --- radio/src/gui/colorlcd/draw_functions.cpp | 15 ++++++++++----- radio/src/gui/colorlcd/widgets/value.cpp | 15 ++++++--------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/radio/src/gui/colorlcd/draw_functions.cpp b/radio/src/gui/colorlcd/draw_functions.cpp index 687aeab62..0b2ca84f3 100644 --- a/radio/src/gui/colorlcd/draw_functions.cpp +++ b/radio/src/gui/colorlcd/draw_functions.cpp @@ -323,12 +323,17 @@ coord_t drawGPSCoord(BitmapBuffer * dc, coord_t x, coord_t y, int32_t value, con void drawGPSPosition(BitmapBuffer * dc, coord_t x, coord_t y, int32_t longitude, int32_t latitude, LcdFlags flags) { - if (flags & RIGHT) { - x = drawGPSCoord(dc, x, y, longitude, "EW", flags, false); - drawGPSCoord(dc, x - 5, y, latitude, "NS", flags, false); + if (flags & PREC1) { + drawGPSCoord(dc, x, y, latitude, "NS", flags, false); + drawGPSCoord(dc, x, y + FH, longitude, "EW", flags, false); } else { - x = drawGPSCoord(dc, x, y, latitude, "NS", flags, false); - drawGPSCoord(dc, x + 5, y, longitude, "EW", flags, false); + if (flags & RIGHT) { + x = drawGPSCoord(dc, x, y, longitude, "EW", flags, false); + drawGPSCoord(dc, x - 5, y, latitude, "NS", flags, false); + } else { + x = drawGPSCoord(dc, x, y, latitude, "NS", flags, false); + drawGPSCoord(dc, x + 5, y, longitude, "EW", flags, false); + } } } diff --git a/radio/src/gui/colorlcd/widgets/value.cpp b/radio/src/gui/colorlcd/widgets/value.cpp index f6515f731..6c8821a2a 100644 --- a/radio/src/gui/colorlcd/widgets/value.cpp +++ b/radio/src/gui/colorlcd/widgets/value.cpp @@ -43,7 +43,6 @@ class ValueWidget: public Widget coord_t xValue, yValue, xLabel, yLabel; LcdFlags attrValue, attrLabel = 0; - bool twolines = false; if (width() < 120 && height() < 50) { xValue = 0; yValue = 14; @@ -57,7 +56,7 @@ class ValueWidget: public Widget yValue = -2; xLabel = NUMBERS_PADDING; yLabel = +2; - attrValue = RIGHT | NO_UNIT | FONT(XL); + attrValue = RIGHT | NO_UNIT | FONT(L); } else { xValue = NUMBERS_PADDING; @@ -66,8 +65,7 @@ class ValueWidget: public Widget yLabel = 2; if (field >= MIXSRC_FIRST_TELEM) { if (isGPSSensor(1 + (field - MIXSRC_FIRST_TELEM) / 3)) { - attrValue = LEFT | FONT(L);// | EXPANDED; // TODO - twolines = true; + attrValue = LEFT | FONT(L) | PREC1; } else { attrValue = LEFT | FONT(XL); @@ -75,8 +73,7 @@ class ValueWidget: public Widget } #if defined(INTERNAL_GPS) else if (field == MIXSRC_TX_GPS) { - attrValue = LEFT | FONT(L);// | EXPANDED; // TODO - twolines = true; + attrValue = LEFT | FONT(L) | PREC1; } #endif else { @@ -87,10 +84,10 @@ class ValueWidget: public Widget if (field >= MIXSRC_FIRST_TIMER && field <= MIXSRC_LAST_TIMER) { TimerState & timerState = timersStates[field - MIXSRC_FIRST_TIMER]; if (timerState.val < 0) { - lcdSetColor(lcdColorTable[ALARM_COLOR_INDEX]); + color = ALARM_COLOR; } - drawSource(dc, NUMBERS_PADDING, 2, field, CUSTOM_COLOR); - drawSource(dc, NUMBERS_PADDING + 1, 3, field, BLACK); + drawSource(dc, NUMBERS_PADDING, 2, field, color); + drawSource(dc, NUMBERS_PADDING + 1, 3, field, COLOR2FLAGS(BLACK)); drawTimer(dc, xValue, yValue, abs(timerState.val), attrValue | FONT(XL) | color); } From 15a5b6e4cb21236d38e958961c82f15fe3e5e37f Mon Sep 17 00:00:00 2001 From: eshifri Date: Sun, 20 Jun 2021 07:46:35 -0700 Subject: [PATCH 119/122] Removed SetLcd --- radio/src/gui/colorlcd/layouts/layout2x4.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/radio/src/gui/colorlcd/layouts/layout2x4.cpp b/radio/src/gui/colorlcd/layouts/layout2x4.cpp index 7214c8535..66bc3c186 100644 --- a/radio/src/gui/colorlcd/layouts/layout2x4.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2x4.cpp @@ -89,16 +89,14 @@ void Layout2x4::paint(BitmapBuffer* dc) rect_t fullScreen = Layout::getMainZone(); fullScreen.w /= 2; if (getOptionValue(OPTION_PANEL1_BACKGROUND)->boolValue) { - lcdSetColor(getOptionValue(OPTION_PANEL1_COLOR)->unsignedValue); dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, - fullScreen.h, CUSTOM_COLOR); + fullScreen.h, COLOR2FLAGS(getOptionValue(OPTION_PANEL1_COLOR)->unsignedValue)); } if (getOptionValue(OPTION_PANEL2_BACKGROUND)->boolValue) { fullScreen.x += fullScreen.w; - lcdSetColor(getOptionValue(OPTION_PANEL2_COLOR)->unsignedValue); dc->drawSolidFilledRect(fullScreen.x, fullScreen.y, fullScreen.w, - fullScreen.h, CUSTOM_COLOR); + fullScreen.h, COLOR2FLAGS(getOptionValue(OPTION_PANEL2_COLOR)->unsignedValue)); } } From dc49c662ca79d46853ec1d5d967adef289fdfe39 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 20 Jun 2021 18:59:23 +0200 Subject: [PATCH 120/122] [Color] delete first all widgets before loading the new ones --- radio/src/gui/colorlcd/widgets/widgets_container_impl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radio/src/gui/colorlcd/widgets/widgets_container_impl.h b/radio/src/gui/colorlcd/widgets/widgets_container_impl.h index 9d2836781..8d3614ff8 100644 --- a/radio/src/gui/colorlcd/widgets/widgets_container_impl.h +++ b/radio/src/gui/colorlcd/widgets/widgets_container_impl.h @@ -92,14 +92,15 @@ class WidgetsContainerImpl: public WidgetsContainer { unsigned int count = getZonesCount(); for (unsigned int i = 0; i < count; i++) { - // remove old widget if (widgets[i]) { removeField(widgets[i]); widgets[i]->deleteLater(); widgets[i] = nullptr; } + } + for (unsigned int i = 0; i < count; i++) { // and load new one if required if (persistentData->zones[i].widgetName[0]) { char name[WIDGET_NAME_LEN + 1]; From 0011eebffa4a2aee46754796b6354a3806dfb4f9 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 20 Jun 2021 10:44:58 +0200 Subject: [PATCH 121/122] Fix attempt for locked touch Thx Jakub! --- radio/src/targets/horus/tp_gt911.cpp | 37 +++++++++++++++++++++------- radio/src/targets/horus/tp_gt911.h | 1 - 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/radio/src/targets/horus/tp_gt911.cpp b/radio/src/targets/horus/tp_gt911.cpp index 6915a4a90..624cac3f8 100644 --- a/radio/src/targets/horus/tp_gt911.cpp +++ b/radio/src/targets/horus/tp_gt911.cpp @@ -686,6 +686,24 @@ bool touchPanelInit(void) } } +static const char* event2str(uint8_t ev) +{ + switch(ev){ + case TE_NONE: + return "NONE"; + case TE_UP: + return "UP"; + case TE_DOWN: + return "DOWN"; + case TE_SLIDE_END: + return "SLIDE_END"; + case TE_SLIDE: + return "SLIDE"; + default: + return "UNKNOWN"; + } +} + void touchPanelRead() { uint8_t state = 0; @@ -695,7 +713,7 @@ void touchPanelRead() touchEventOccured = false; I2C_GT911_ReadRegister(GT911_READ_XY_REG, &state, 1); - + TRACE("touch state = 0x%x", state); if ((state & 0x80u) == 0x00) { // not ready return; @@ -705,7 +723,7 @@ void touchPanelRead() if (pointsCount > 0 && pointsCount < GT911_MAX_TP) { I2C_GT911_ReadRegister(GT911_READ_XY_REG + 1, touchData.data, pointsCount * sizeof(TouchPoint)); - if (touchData.pointsCount == 0) { + if (touchState.event == TE_NONE || touchState.event == TE_UP || touchState.event == TE_SLIDE_END) { touchState.event = TE_DOWN; touchState.startX = touchState.x = touchData.points[0].x; touchState.startY = touchState.y = touchData.points[0].y; @@ -719,20 +737,21 @@ void touchPanelRead() touchState.y = touchData.points[0].y; } } - touchData.pointsCount = pointsCount; } else { - if (touchData.pointsCount > 0) { - touchData.pointsCount = 0; - if (touchState.event == TE_SLIDE) - touchState.event = TE_SLIDE_END; - else - touchState.event = TE_UP; + if (touchState.event == TE_SLIDE) { + touchState.event = TE_SLIDE_END; + } else if (touchState.event == TE_DOWN) { + touchState.event = TE_UP; + } else if (touchState.event != TE_SLIDE_END){ + touchState.event = TE_NONE; } } uint8_t zero = 0; I2C_GT911_WriteRegister(GT911_READ_XY_REG, &zero, 1); + + TRACE("touch event = %s", event2str(touchState.event)); } extern "C" void TOUCH_INT_EXTI_IRQHandler1(void) diff --git a/radio/src/targets/horus/tp_gt911.h b/radio/src/targets/horus/tp_gt911.h index ce48a680f..edbf3fd57 100644 --- a/radio/src/targets/horus/tp_gt911.h +++ b/radio/src/targets/horus/tp_gt911.h @@ -69,7 +69,6 @@ PACK(typedef struct { }) TouchPoint; PACK(struct TouchData { - uint8_t pointsCount; union { TouchPoint points[GT911_MAX_TP]; From 78a0a45716611fda391df054e2d2ae4a556cc8b6 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 20 Jun 2021 20:08:46 +0200 Subject: [PATCH 122/122] Trigger build on tags as well --- .github/workflows/actions.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index c50d5fa85..4bfc8a80c 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -13,6 +13,9 @@ on: paths-ignore: - '**.md' - '.github/ISSUE_TEMPLATE/**' + create: + tags: + - v* jobs: test:

N;_gChAda*qG&?Dy8tU15Bo8AF;QV;#Q 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 jNR5nnBqp>& LCD_H - new StaticBitmap(window, {LCD_W / 2 - calibRadioPict->width() / 2, 5, calibRadioPict->height(), calibRadioPict->width()}, calibRadioPict); -#else - new StaticBitmap(window, {LCD_W / 2 - calibRadioPict->width() / 2, LCD_H / 2 - calibRadioPict->height() / 2, calibRadioPict->height(), calibRadioPict->width()}, calibRadioPict); -#endif - } - // The two sticks + + //TODO: dynamic placing new StickCalibrationWindow(window, {40, 20, 90, 90}, STICK1, STICK2); new StickCalibrationWindow(window, {LCD_W - 130, 20, 90, 90}, STICK4, STICK3); diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index a674f1243..15bf75507 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -195,17 +195,6 @@ class Theme480: public OpenTxTheme delete calibTrackpBackground; calibTrackpBackground = BitmapBuffer::loadBitmap(getFilePath("trackp_background.png")); - delete calibRadioPict; -#if defined(PCBX10) - if(STICKS_PWM_ENABLED()) { - calibRadioPict = BitmapBuffer::loadBitmap(getFilePath("X10S.bmp")); - } - else { - calibRadioPict = BitmapBuffer::loadBitmap(getFilePath("X10.bmp")); - } -#else - calibRadioPict = BitmapBuffer::loadBitmap(getFilePath("horus.bmp")); -#endif // Model Selection screen delete modelselIconBitmap; diff --git a/radio/src/gui/colorlcd/themes/flysky.cpp b/radio/src/gui/colorlcd/themes/flysky.cpp index 4a0648e5e..6cdccecb4 100644 --- a/radio/src/gui/colorlcd/themes/flysky.cpp +++ b/radio/src/gui/colorlcd/themes/flysky.cpp @@ -215,9 +215,6 @@ class FlyskyTheme: public OpenTxTheme delete calibTrackpBackground; calibTrackpBackground = BitmapBuffer::loadBitmap(getFilePath("trackp_background.png")); - delete calibRadioPict; - calibRadioPict = BitmapBuffer::loadBitmap(getFilePath("NV14.bmp")); - // Model Selection screen delete modelselIconBitmap; modelselIconBitmap = BitmapBuffer::loadMaskOnBackground("modelsel/mask_iconback.png", TITLE_BGCOLOR, DEFAULT_BGCOLOR); From 751be2200706ecc46be1c28a251a9291dccb657c Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 15 May 2021 01:13:11 +0200 Subject: [PATCH 029/122] Moved default theme masks into firmware --- radio/src/bitmaps/480x272/CMakeLists.txt | 5 +- .../480x272/default_theme/mask_back.png | Bin 0 -> 1056 bytes .../default_theme}/mask_currentmenu_bg.png | Bin .../default_theme}/mask_currentmenu_dot.png | Bin .../mask_currentmenu_shadow.png | Bin .../default_theme}/mask_menu_model.png | Bin .../default_theme}/mask_menu_radio.png | Bin .../default_theme}/mask_menu_stats.png | Bin .../default_theme}/mask_menu_theme.png | Bin .../default_theme}/mask_model_curves.png | Bin .../mask_model_flight_modes.png | Bin .../default_theme}/mask_model_gvars.png | Bin .../default_theme}/mask_model_heli.png | Bin .../default_theme}/mask_model_inputs.png | Bin .../mask_model_logical_switches.png | Bin .../default_theme}/mask_model_lua_scripts.png | Bin .../default_theme}/mask_model_mixer.png | Bin .../default_theme}/mask_model_outputs.png | Bin .../default_theme}/mask_model_setup.png | Bin .../mask_model_special_functions.png | Bin .../default_theme}/mask_model_telemetry.png | Bin .../480x272/default_theme}/mask_monitor.png | Bin .../default_theme}/mask_monitor_channels1.png | Bin .../default_theme}/mask_monitor_channels2.png | Bin .../default_theme}/mask_monitor_channels3.png | Bin .../default_theme}/mask_monitor_channels4.png | Bin .../default_theme}/mask_monitor_gvars.png | Bin .../default_theme}/mask_monitor_inver.png | Bin .../default_theme}/mask_monitor_lockch.png | Bin .../default_theme}/mask_monitor_logsw.png | Bin .../default_theme}/mask_monitor_mixes1.png | Bin .../default_theme}/mask_monitor_mixes2.png | Bin .../480x272/default_theme}/mask_mplex_add.png | Bin .../default_theme}/mask_mplex_multi.png | Bin .../default_theme}/mask_mplex_replace.png | Bin .../480x272/default_theme/mask_next.png | Bin 0 -> 1053 bytes .../480x272/default_theme}/mask_opentx.png | Bin .../default_theme}/mask_opentx_testmode.png | Bin .../default_theme}/mask_radio_calibration.png | Bin .../mask_radio_global_functions.png | Bin .../default_theme}/mask_radio_hardware.png | Bin .../default_theme}/mask_radio_sd_browser.png | Bin .../default_theme}/mask_radio_setup.png | Bin .../default_theme}/mask_radio_tools.png | Bin .../default_theme}/mask_radio_trainer.png | Bin .../default_theme}/mask_radio_version.png | Bin .../default_theme}/mask_sbar_mixer.png | Bin .../default_theme}/mask_sbar_output.png | Bin .../480x272/default_theme}/mask_sbar_to.png | Bin .../default_theme}/mask_stats_analogs.png | Bin .../default_theme}/mask_stats_debug.png | Bin .../480x272/default_theme}/mask_stats_lua.png | Bin .../mask_stats_throttle_graph.png | Bin .../default_theme}/mask_stats_timers.png | Bin .../default_theme}/mask_stats_value.png | Bin .../480x272/default_theme}/mask_switches.png | Bin .../default_theme}/mask_textline_curve.png | Bin .../default_theme}/mask_textline_delay.png | Bin .../mask_textline_delayslow.png | Bin .../default_theme}/mask_textline_fm.png | Bin .../default_theme}/mask_textline_label.png | Bin .../default_theme}/mask_textline_slow.png | Bin .../default_theme}/mask_textline_switch.png | Bin .../default_theme}/mask_theme_add_view.png | Bin .../default_theme}/mask_theme_setup.png | Bin .../default_theme}/mask_theme_view1.png | Bin .../default_theme}/mask_theme_view2.png | Bin .../default_theme}/mask_theme_view3.png | Bin .../default_theme}/mask_theme_view4.png | Bin .../default_theme}/mask_theme_view5.png | Bin .../default_theme}/mask_theme_view6.png | Bin .../default_theme}/mask_theme_view7.png | Bin .../default_theme}/mask_theme_view8.png | Bin .../480x272/default_theme/mask_topleft.png | Bin 0 -> 526 bytes radio/src/gui/colorlcd/themes/480_bitmaps.h | 219 ++++++++++++++++++ radio/src/gui/colorlcd/themes/480_default.cpp | 130 ++++++----- 76 files changed, 295 insertions(+), 59 deletions(-) create mode 100755 radio/src/bitmaps/480x272/default_theme/mask_back.png rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_currentmenu_bg.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_currentmenu_dot.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_currentmenu_shadow.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_menu_model.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_menu_radio.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_menu_stats.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_menu_theme.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_curves.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_flight_modes.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_gvars.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_heli.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_inputs.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_logical_switches.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_lua_scripts.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_mixer.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_outputs.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_setup.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_special_functions.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_model_telemetry.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor_channels1.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor_channels2.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor_channels3.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor_channels4.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor_gvars.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor_inver.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor_lockch.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor_logsw.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor_mixes1.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_monitor_mixes2.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_mplex_add.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_mplex_multi.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_mplex_replace.png (100%) create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_next.png rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_opentx.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_opentx_testmode.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_radio_calibration.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_radio_global_functions.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_radio_hardware.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_radio_sd_browser.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_radio_setup.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_radio_tools.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_radio_trainer.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_radio_version.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_sbar_mixer.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_sbar_output.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_sbar_to.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_stats_analogs.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_stats_debug.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_stats_lua.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_stats_throttle_graph.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_stats_timers.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_stats_value.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_switches.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_textline_curve.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_textline_delay.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_textline_delayslow.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_textline_fm.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_textline_label.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_textline_slow.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_textline_switch.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_theme_add_view.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_theme_setup.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_theme_view1.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_theme_view2.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_theme_view3.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_theme_view4.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_theme_view5.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_theme_view6.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_theme_view7.png (100%) rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_theme_view8.png (100%) create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_topleft.png create mode 100644 radio/src/gui/colorlcd/themes/480_bitmaps.h diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/480x272/CMakeLists.txt index 2233da191..7231592d4 100644 --- a/radio/src/bitmaps/480x272/CMakeLists.txt +++ b/radio/src/bitmaps/480x272/CMakeLists.txt @@ -27,9 +27,12 @@ add_bitmaps_target(${BITMAP_TARGET_PREFIX}_alpha_calibration_bitmaps "${RADIO_SR add_bitmaps_target(${BITMAP_TARGET_PREFIX}_masks ${RADIO_SRC_DIR}/bitmaps/480x272/mask_*.png 8bits "${MASK_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_slider_masks "${RADIO_SRC_DIR}/bitmaps/480x272/slider/*.png" 8bits "${MASK_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_layouts_masks "${RADIO_SRC_DIR}/gui/colorlcd/layouts/*.png" 8bits "${MASK_ARGS}") -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_themes_bitmaps "${RADIO_SRC_DIR}/gui/colorlcd/themes/*.png" "5/6/5" "${BITMAPS_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_fonts ${RADIO_SRC_DIR}/fonts/truetype/*.png 8bits "${MASK_RLE_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_volume_masks ${RADIO_SRC_DIR}/bitmaps/480x272/volume/*.png 8bits "${MASK_ARGS}") + +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_themes_masks "${RADIO_SRC_DIR}/bitmaps/480x272/default_theme/mask_*.png" 8bits "${BITMAPS_ARGS}") +add_bitmaps_target(${BITMAP_TARGET_PREFIX}_themes_alpha "${RADIO_SRC_DIR}/bitmaps/480x272/default_theme/alpha_*.png" "4/4/4/4" "${BITMAPS_ARGS}") + add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_bitmaps ${RADIO_SRC_DIR}/bitmaps/480x272/bootloader/bmp_*.png "4/4/4/4" "${BITMAP_RLE_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_icons ${RADIO_SRC_DIR}/bitmaps/480x272/bootloader/icon_*.png 8bits "${MASK_ARGS}") diff --git a/radio/src/bitmaps/480x272/default_theme/mask_back.png b/radio/src/bitmaps/480x272/default_theme/mask_back.png new file mode 100755 index 0000000000000000000000000000000000000000..ffd31b820f51305f67edb8a59176ecb9e080d523 GIT binary patch literal 1056 zcmV+*1mF9KP)8V<+hC-p~>1n^;4*=+wquB~smhbQHH#ava ziV6mU48xqCpNpc{Kg$kGQ525jwzs!E9uEKj0N{4JcXxNczrXt@!d9!Nsw&U(nM~&L z^0Hp9>#rb5QngwYMX`S_WQn9{TBTB9SvH%^{`~ytBmltJ*x1a>jMwWOoNqqb?KaD@ zG)+U$uU`oO@Or(;Wb)zRK~a>Uy;-NzxxT($US5V_*dUFMkMHd4a2zMg^1v^Qn3vz( z-QDWys>|gvs)C|uJRavbZs46Pz;yX-Y;52-ZjfLYURhbm=ksRnWEQ3|Dp@7%C@>6L zUtc#bKT|M8QJ$Zl*Vfj$h3<4ZW3gDFP%tk)Q?R$Ux2>(M(b3Uvp@+laTrMX`l4du0 z!!9l^NRsR}CxReOPEJ~_mi9lQ_aOfK{4^%I9teW?{eELm*^AyVnx+YY=)PIGTs}QL z<#}FJRogH}I-UOe6h%>0RXL7pG#dK-6h-;^`a%$7VPOHoumSdO$zoJht<`FoOok*$ zqn#lLq9}@GSxY0zRG;aC>hSQ;=kpo&fMGZiiQL}a+Ef2X{}KH1@{&j-P!#R1uk0;g zold7%EH)Yq{jk|=etdjPOiWBoO<8C@lTQ7&f7%d>*&RgR4C3+lA7V))60fhX!`wmo zh{fmg9UL48f?&q7E$@wg7`LTPw+8!Hr$?jFLZL9QnAX;B0D#ZuOD2;H!x+9Q%W|<; zyt%oNBx#ttzFs$)ruX*t!r`#f>C_K9osJ*~^?H5CFun7ZmX`MS_ZJrz-EKFAVUv@S zp-{-}cK1)TX@&@bFbsp^_|?@_tyY_#pN~eP-K$aWSFrszS8u*ntJQ2aJsywC2h5)+CJzK|Fu zXkzT<;NT)J3yCfc#xT06!GFO;on< zZ7~c(lB7jFb!_x{z3c1i_4Rce#|eUnMx%Fkcl~}}wGrutk`V7sw;|O zp-{->a;;WtB3$NbrmAYc-_PZ8p->1#f1M0P(NHLKb8}<1c$38xMQJvhCnqOfulF~^ zHd`zfGg~~w$d6=M{`mMvCX-I5Q`2)go$+{_<2YHCrzxhYsw7E|kB_lfOe-D$!0mQt zG8vxd4eDXpY}OD+C^|nsKRY{HE|E4|!QkcPWxL(}NzVizf5X!4cHiFKdcEGL z_U(52<>f_D6q=?fiu%8R{N3q^L;^yn{Tl#ab#?Xr{@#k~kMBI1ruX*td_JH4=%1gT zwOY-J>yHmfk_15*3T4Xw^<$Q0*;1)A#z(9;h~`+D^I{kliA3`G{9rJc znuEx)EC|Bc*_q$(*Oauu-Rt$H=8cM?v|6pJtE*rzs2K+U!0-2Gv)S=RWm>G?@890u zuC1+M7-nsLnwZqZVv%7OJ?JwtGpSU{_-23@$1fg_>&FU&aC>|E>FLS%&Vv}qiQ~9~ zg9DGpqd{S_*`m?t!^6XHIGoycCCl>j^K&|#)`O0s=;GpHE|;6~-q`7M&d<+%KA&F6 zIDY0_|Lg0kQmJU`6ac{E@uX6zbUN+zdd*&(CWt`@DT;D98~}ikqwnnO93CFhG_AGT z)GmVrL9i@auh*;9Dui%jW8>)PXlZH5f)autVEj#FbVcJhj^}xbqQc=YP1E|(Y%2Z( XqZ5q}{x}}+00000NkvXXu0mjfq#OU5 literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_opentx.png b/radio/src/bitmaps/480x272/default_theme/mask_opentx.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_opentx.png rename to radio/src/bitmaps/480x272/default_theme/mask_opentx.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_opentx_testmode.png b/radio/src/bitmaps/480x272/default_theme/mask_opentx_testmode.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_opentx_testmode.png rename to radio/src/bitmaps/480x272/default_theme/mask_opentx_testmode.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_calibration.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_calibration.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_calibration.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_calibration.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_global_functions.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_global_functions.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_global_functions.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_global_functions.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_hardware.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_hardware.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_hardware.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_hardware.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_sd_browser.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_sd_browser.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_sd_browser.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_sd_browser.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_setup.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_setup.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_setup.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_setup.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_tools.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_tools.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_tools.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_tools.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_trainer.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_trainer.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_trainer.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_trainer.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_radio_version.png b/radio/src/bitmaps/480x272/default_theme/mask_radio_version.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_radio_version.png rename to radio/src/bitmaps/480x272/default_theme/mask_radio_version.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_mixer.png b/radio/src/bitmaps/480x272/default_theme/mask_sbar_mixer.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_mixer.png rename to radio/src/bitmaps/480x272/default_theme/mask_sbar_mixer.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_output.png b/radio/src/bitmaps/480x272/default_theme/mask_sbar_output.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_output.png rename to radio/src/bitmaps/480x272/default_theme/mask_sbar_output.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_to.png b/radio/src/bitmaps/480x272/default_theme/mask_sbar_to.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_sbar_to.png rename to radio/src/bitmaps/480x272/default_theme/mask_sbar_to.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_analogs.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_analogs.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_analogs.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_analogs.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_debug.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_debug.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_debug.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_debug.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_lua.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_lua.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_lua.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_lua.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_throttle_graph.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_throttle_graph.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_throttle_graph.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_throttle_graph.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_timers.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_timers.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_timers.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_timers.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_stats_value.png b/radio/src/bitmaps/480x272/default_theme/mask_stats_value.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_stats_value.png rename to radio/src/bitmaps/480x272/default_theme/mask_stats_value.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_switches.png b/radio/src/bitmaps/480x272/default_theme/mask_switches.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_switches.png rename to radio/src/bitmaps/480x272/default_theme/mask_switches.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_curve.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_curve.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_curve.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_curve.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_delay.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_delay.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_delay.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_delay.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_delayslow.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_delayslow.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_delayslow.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_delayslow.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_fm.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_fm.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_fm.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_fm.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_label.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_label.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_label.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_label.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_slow.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_slow.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_slow.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_slow.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_textline_switch.png b/radio/src/bitmaps/480x272/default_theme/mask_textline_switch.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_textline_switch.png rename to radio/src/bitmaps/480x272/default_theme/mask_textline_switch.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_add_view.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_add_view.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_add_view.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_add_view.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_setup.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_setup.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_setup.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_setup.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view1.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view1.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view1.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view1.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view2.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view2.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view2.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view2.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view3.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view3.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view3.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view3.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view4.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view4.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view4.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view4.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view5.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view5.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view5.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view5.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view6.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view6.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view6.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view6.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view7.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view7.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view7.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view7.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view8.png b/radio/src/bitmaps/480x272/default_theme/mask_theme_view8.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_theme_view8.png rename to radio/src/bitmaps/480x272/default_theme/mask_theme_view8.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_topleft.png b/radio/src/bitmaps/480x272/default_theme/mask_topleft.png new file mode 100644 index 0000000000000000000000000000000000000000..603bfe1cfc8988218a602a6277b323dca5656c56 GIT binary patch literal 526 zcmV+p0`dKcP)1QB~WvxLhv1es(v<*eB@{73J?J%FuUu||5O?cns zi4fi0Pm74h;c)2nu{)_ldrawSolidFilledRect(0, 0, currentMenuBackground->width(), MENU_HEADER_HEIGHT, MENU_BGCOLOR); - currentMenuBackground->drawSolidFilledRect(0, MENU_HEADER_HEIGHT, currentMenuBackground->width(), MENU_TITLE_TOP - MENU_HEADER_HEIGHT, DEFAULT_BGCOLOR); - currentMenuBackground->drawSolidFilledRect(0, MENU_TITLE_TOP, currentMenuBackground->width(), currentMenuBackground->height() - MENU_TITLE_TOP, TITLE_BGCOLOR); - currentMenuBackground->drawMask(0, 0, background, HEADER_CURRENT_BGCOLOR); + + currentMenuBackground->drawSolidFilledRect( + 0, 0, currentMenuBackground->width(), MENU_HEADER_HEIGHT, + MENU_BGCOLOR); + + currentMenuBackground->drawSolidFilledRect( + 0, MENU_HEADER_HEIGHT, currentMenuBackground->width(), + MENU_TITLE_TOP - MENU_HEADER_HEIGHT, DEFAULT_BGCOLOR); + + currentMenuBackground->drawSolidFilledRect( + 0, MENU_TITLE_TOP, currentMenuBackground->width(), + currentMenuBackground->height() - MENU_TITLE_TOP, TITLE_BGCOLOR); + + currentMenuBackground->drawMask(0, 0, background, + HEADER_CURRENT_BGCOLOR); + currentMenuBackground->drawMask(0, 0, shadow, TRIM_SHADOW_COLOR); + currentMenuBackground->drawMask(10, 39, dot, MENU_COLOR); } delete topleftBitmap; - topleftBitmap = BitmapBuffer::loadMaskOnBackground(getFilePath("topleft.png"), TITLE_BGCOLOR, HEADER_COLOR); + topleftBitmap = BitmapBuffer::load8bitMaskOnBackground( + mask_topleft, TITLE_BGCOLOR, HEADER_COLOR); delete background; delete shadow; From 28718d9420f1c8df8149a3af25a206bdccb8f073 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 15 May 2021 01:32:10 +0200 Subject: [PATCH 030/122] Added missing bitmap deps --- radio/src/bitmaps/480x272/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/480x272/CMakeLists.txt index 7231592d4..4b413dcdc 100644 --- a/radio/src/bitmaps/480x272/CMakeLists.txt +++ b/radio/src/bitmaps/480x272/CMakeLists.txt @@ -39,4 +39,4 @@ add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_icons ${RADIO_SRC_DIR}/bit add_dependencies(${BITMAP_TARGET_PREFIX}_bitmaps ${BITMAP_TARGET_PREFIX}_calibration_bitmaps ${BITMAP_TARGET_PREFIX}_button_bitmaps ${BITMAP_TARGET_PREFIX}_alpha_bitmaps ${BITMAP_TARGET_PREFIX}_alpha_calibration_bitmaps ${BITMAP_TARGET_PREFIX}_masks ${BITMAP_TARGET_PREFIX}_slider_masks ${BITMAP_TARGET_PREFIX}_layouts_masks ${BITMAP_TARGET_PREFIX}_themes_bitmaps ${BITMAP_TARGET_PREFIX}_fonts ${BITMAP_TARGET_PREFIX}_volume_masks ${BITMAP_TARGET_PREFIX}_bootloader_bitmaps - ${BITMAP_TARGET_PREFIX}_bootloader_icons) + ${BITMAP_TARGET_PREFIX}_bootloader_icons ${BITMAP_TARGET_PREFIX}_themes_masks ${BITMAP_TARGET_PREFIX}_themes_alpha) From 8e0ed1482f74bd11821bd2cda081e1977a6b06cc Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 15 May 2021 11:22:33 +0200 Subject: [PATCH 031/122] Load calibration bitmaps from flash memory --- radio/src/bitmaps/480x272/CMakeLists.txt | 2 +- .../default_theme/alpha_stick_background.png} | Bin .../480x272/default_theme/alpha_stick_pointer.png} | Bin .../default_theme/alpha_trackp_background.png} | Bin radio/src/gui/colorlcd/themes/480_bitmaps.h | 9 +++++++++ radio/src/gui/colorlcd/themes/480_default.cpp | 10 ++++++---- 6 files changed, 16 insertions(+), 5 deletions(-) rename radio/{sdcard/horus/THEMES/EdgeTX/stick_background.png => src/bitmaps/480x272/default_theme/alpha_stick_background.png} (100%) rename radio/{sdcard/horus/THEMES/EdgeTX/stick_pointer.png => src/bitmaps/480x272/default_theme/alpha_stick_pointer.png} (100%) rename radio/{sdcard/horus/THEMES/EdgeTX/trackp_background.png => src/bitmaps/480x272/default_theme/alpha_trackp_background.png} (100%) diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/480x272/CMakeLists.txt index 4b413dcdc..b24d5528e 100644 --- a/radio/src/bitmaps/480x272/CMakeLists.txt +++ b/radio/src/bitmaps/480x272/CMakeLists.txt @@ -31,7 +31,7 @@ add_bitmaps_target(${BITMAP_TARGET_PREFIX}_fonts ${RADIO_SRC_DIR}/fonts/truetype add_bitmaps_target(${BITMAP_TARGET_PREFIX}_volume_masks ${RADIO_SRC_DIR}/bitmaps/480x272/volume/*.png 8bits "${MASK_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_themes_masks "${RADIO_SRC_DIR}/bitmaps/480x272/default_theme/mask_*.png" 8bits "${BITMAPS_ARGS}") -add_bitmaps_target(${BITMAP_TARGET_PREFIX}_themes_alpha "${RADIO_SRC_DIR}/bitmaps/480x272/default_theme/alpha_*.png" "4/4/4/4" "${BITMAPS_ARGS}") +add_png_target(${BITMAP_TARGET_PREFIX}_themes_alpha "${RADIO_SRC_DIR}/bitmaps/480x272/default_theme/alpha_*.png") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_bitmaps ${RADIO_SRC_DIR}/bitmaps/480x272/bootloader/bmp_*.png "4/4/4/4" "${BITMAP_RLE_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_icons ${RADIO_SRC_DIR}/bitmaps/480x272/bootloader/icon_*.png 8bits "${MASK_ARGS}") diff --git a/radio/sdcard/horus/THEMES/EdgeTX/stick_background.png b/radio/src/bitmaps/480x272/default_theme/alpha_stick_background.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/stick_background.png rename to radio/src/bitmaps/480x272/default_theme/alpha_stick_background.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/stick_pointer.png b/radio/src/bitmaps/480x272/default_theme/alpha_stick_pointer.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/stick_pointer.png rename to radio/src/bitmaps/480x272/default_theme/alpha_stick_pointer.png diff --git a/radio/sdcard/horus/THEMES/EdgeTX/trackp_background.png b/radio/src/bitmaps/480x272/default_theme/alpha_trackp_background.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/trackp_background.png rename to radio/src/bitmaps/480x272/default_theme/alpha_trackp_background.png diff --git a/radio/src/gui/colorlcd/themes/480_bitmaps.h b/radio/src/gui/colorlcd/themes/480_bitmaps.h index 06cd21f5f..4fc6e5494 100644 --- a/radio/src/gui/colorlcd/themes/480_bitmaps.h +++ b/radio/src/gui/colorlcd/themes/480_bitmaps.h @@ -217,3 +217,12 @@ const uint8_t mask_theme_view7[] = { const uint8_t mask_theme_view8[] = { #include "mask_theme_view8.lbm" }; +const uint8_t stick_pointer[] = { +#include "alpha_stick_pointer.lbm" +}; +const uint8_t stick_background[] = { +#include "alpha_stick_background.lbm" +}; +const uint8_t trackp_background[] = { +#include "alpha_trackp_background.lbm" +}; diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index 2f7866cf3..97b6ebe79 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -201,14 +201,16 @@ class Theme480: public OpenTxTheme { // Calibration screen delete calibStick; - calibStick = BitmapBuffer::loadBitmap(getFilePath("stick_pointer.png")); + calibStick = + BitmapBuffer::loadRamBitmap(stick_pointer, sizeof(stick_pointer)); delete calibStickBackground; - calibStickBackground = BitmapBuffer::loadBitmap(getFilePath("stick_background.png")); + calibStickBackground = BitmapBuffer::loadRamBitmap( + stick_background, sizeof(stick_background)); delete calibTrackpBackground; - calibTrackpBackground = BitmapBuffer::loadBitmap(getFilePath("trackp_background.png")); - + calibTrackpBackground = BitmapBuffer::loadRamBitmap( + trackp_background, sizeof(trackp_background)); // Model Selection screen delete modelselIconBitmap; From fac069cd217fcb369e22462b2b52a1ac2db48026 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 15 May 2021 15:44:11 +0200 Subject: [PATCH 032/122] add_png_target --- cmake/Bitmaps.cmake | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/cmake/Bitmaps.cmake b/cmake/Bitmaps.cmake index 41a8589b6..f44e09517 100644 --- a/cmake/Bitmaps.cmake +++ b/cmake/Bitmaps.cmake @@ -14,6 +14,22 @@ macro(add_bitmaps_target targetname filter format args) add_custom_target(${targetname} DEPENDS ${bitmaps_files}) endmacro() +macro(add_png_target targetname filter) + set(bitmaps_files) + file(GLOB bitmaps ${filter}) + foreach(bitmap ${bitmaps}) + get_filename_component(target ${bitmap} NAME_WE) + set(target ${target}.lbm) + add_custom_command( + OUTPUT ${target} + COMMAND ${PYTHON_EXECUTABLE} ${UTILS_DIR}/bin2lbm.py ${bitmap} ${target} + DEPENDS ${bitmap} ${UTILS_DIR}/bin2lbm.py + ) + list(APPEND bitmaps_files ${target}) + endforeach() + add_custom_target(${targetname} DEPENDS ${bitmaps_files}) +endmacro() + macro(add_fonts_target targetname filter) set(fonts_files) file(GLOB fonts ${filter}) From 2e77f23da4493dc3dbe96c89099a6633e6e4528a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 16 May 2021 09:23:11 +0200 Subject: [PATCH 033/122] Use compressed splash image --- radio/src/bitmaps/480x272/CMakeLists.txt | 2 ++ .../480x272/{bmp_splash.png => splash.png} | Bin radio/src/gui/colorlcd/splash.cpp | 24 +++++++++--------- 3 files changed, 14 insertions(+), 12 deletions(-) rename radio/src/bitmaps/480x272/{bmp_splash.png => splash.png} (100%) diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/480x272/CMakeLists.txt index b24d5528e..326c6ad4a 100644 --- a/radio/src/bitmaps/480x272/CMakeLists.txt +++ b/radio/src/bitmaps/480x272/CMakeLists.txt @@ -20,6 +20,8 @@ set(BITMAP_RLE_ARGS ${BITMAP_ARGS} --rle) set(MASK_RLE_ARGS ${MASK_ARGS} --rle) add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/bmp_*.png" "5/6/5" "${BITMAP_ARGS}") +add_png_target(${BITMAP_TARGET_PREFIX}_splash "${RADIO_SRC_DIR}/bitmaps/480x272/splash.png") + add_bitmaps_target(${BITMAP_TARGET_PREFIX}_calibration_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/calibration/bmp_*.png" "5/6/5" "${BITMAP_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_button_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/button/alpha_*.png" "4/4/4/4" "${BITMAP_ARGS}") add_bitmaps_target(${BITMAP_TARGET_PREFIX}_alpha_bitmaps "${RADIO_SRC_DIR}/bitmaps/480x272/alpha_*.png" "4/4/4/4" "${BITMAP_ARGS}") diff --git a/radio/src/bitmaps/480x272/bmp_splash.png b/radio/src/bitmaps/480x272/splash.png similarity index 100% rename from radio/src/bitmaps/480x272/bmp_splash.png rename to radio/src/bitmaps/480x272/splash.png diff --git a/radio/src/gui/colorlcd/splash.cpp b/radio/src/gui/colorlcd/splash.cpp index 1ac28e8b9..6b15b8a9a 100644 --- a/radio/src/gui/colorlcd/splash.cpp +++ b/radio/src/gui/colorlcd/splash.cpp @@ -22,20 +22,25 @@ #if defined(SPLASH) -const uint8_t __bmp_splash[] __ALIGNED(4) { -#include "bmp_splash.lbm" +const uint8_t __bmp_splash[] { +#include "splash.lbm" }; -Bitmap BMP_SPLASH(BMP_RGB565, (const uint16_t *)__bmp_splash); void drawSplash() { - static bool loadImgFromSD = true; + static bool loadSplashImg = true; static BitmapBuffer * splashImg = nullptr; - if (loadImgFromSD && splashImg == nullptr) { + // try splash from SD card first + if (loadSplashImg && splashImg == nullptr) { if (!sdMounted()) sdInit(); splashImg = BitmapBuffer::loadBitmap(BITMAPS_PATH "/" SPLASH_FILE); - loadImgFromSD = false; + loadSplashImg = false; + + // otherwise load from FLASH + if (splashImg == nullptr) { + splashImg = BitmapBuffer::loadRamBitmap(__bmp_splash, sizeof(__bmp_splash)); + } } lcd->clear(); @@ -45,12 +50,7 @@ void drawSplash() (LCD_H - splashImg->height())/2, splashImg); } - else { - lcd->drawBitmap((LCD_W - BMP_SPLASH.width())/2, - (LCD_H - BMP_SPLASH.height())/2, - &BMP_SPLASH); - } - + lcdRefresh(); } #endif From b661568dbef1f6253056abdc051a030fceec50cb Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 16 May 2021 09:59:38 +0200 Subject: [PATCH 034/122] Fix bitmap deps --- radio/src/bitmaps/480x272/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/480x272/CMakeLists.txt index 326c6ad4a..55d3b6867 100644 --- a/radio/src/bitmaps/480x272/CMakeLists.txt +++ b/radio/src/bitmaps/480x272/CMakeLists.txt @@ -40,5 +40,5 @@ add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_icons ${RADIO_SRC_DIR}/bit add_dependencies(${BITMAP_TARGET_PREFIX}_bitmaps ${BITMAP_TARGET_PREFIX}_calibration_bitmaps ${BITMAP_TARGET_PREFIX}_button_bitmaps ${BITMAP_TARGET_PREFIX}_alpha_bitmaps ${BITMAP_TARGET_PREFIX}_alpha_calibration_bitmaps ${BITMAP_TARGET_PREFIX}_masks ${BITMAP_TARGET_PREFIX}_slider_masks ${BITMAP_TARGET_PREFIX}_layouts_masks - ${BITMAP_TARGET_PREFIX}_themes_bitmaps ${BITMAP_TARGET_PREFIX}_fonts ${BITMAP_TARGET_PREFIX}_volume_masks ${BITMAP_TARGET_PREFIX}_bootloader_bitmaps - ${BITMAP_TARGET_PREFIX}_bootloader_icons ${BITMAP_TARGET_PREFIX}_themes_masks ${BITMAP_TARGET_PREFIX}_themes_alpha) + ${BITMAP_TARGET_PREFIX}_fonts ${BITMAP_TARGET_PREFIX}_themes_masks ${BITMAP_TARGET_PREFIX}_themes_alpha + ${BITMAP_TARGET_PREFIX}_volume_masks ${BITMAP_TARGET_PREFIX}_bootloader_bitmaps ${BITMAP_TARGET_PREFIX}_bootloader_icons ) From 4bcb703345c00eebcb30ee377d22979f806b618a Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sat, 15 May 2021 13:35:40 +0200 Subject: [PATCH 035/122] Moved even more bitmaps to flash --- radio/src/bitmaps/480x272/CMakeLists.txt | 4 +-- .../480x272/default_theme/mask_asterisk.png | Bin 0 -> 12570 bytes .../480x272/default_theme/mask_busy.png | Bin 0 -> 14831 bytes .../480x272/default_theme}/mask_edgetx.png | Bin .../480x272/default_theme/mask_iconback.png | Bin 0 -> 1498 bytes .../480x272/default_theme/mask_modelname.png | Bin 0 -> 661 bytes .../480x272/default_theme/mask_modelqty.png | Bin 0 -> 773 bytes .../480x272/default_theme/mask_moveback.png | Bin 0 -> 653 bytes .../480x272/default_theme/mask_moveico.png | Bin 0 -> 756 bytes .../480x272/default_theme/mask_question.png | Bin 0 -> 13796 bytes .../480x272/default_theme/mask_sdfree.png | Bin 0 -> 534 bytes .../480x272/default_theme/mask_shutdown.png | Bin 0 -> 12739 bytes .../bitmaps/480x272/default_theme/thumb.png | Bin 0 -> 4051 bytes radio/src/gui/colorlcd/bitmaps.cpp | 2 +- radio/src/gui/colorlcd/bitmaps.h | 2 +- radio/src/gui/colorlcd/draw_functions.cpp | 12 +++----- radio/src/gui/colorlcd/fullscreen_dialog.cpp | 18 +++++------ radio/src/gui/colorlcd/theme.cpp | 25 ++++++++++++++-- radio/src/gui/colorlcd/theme.h | 1 + radio/src/gui/colorlcd/themes/480_bitmaps.h | 16 ++++++++++ radio/src/gui/colorlcd/themes/480_default.cpp | 28 ++++++++++-------- radio/src/gui/colorlcd/themes/flysky.cpp | 14 ++++----- 22 files changed, 78 insertions(+), 44 deletions(-) create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_asterisk.png create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_busy.png rename radio/{sdcard/horus/THEMES/EdgeTX => src/bitmaps/480x272/default_theme}/mask_edgetx.png (100%) create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_iconback.png create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_modelname.png create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_modelqty.png create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_moveback.png create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_moveico.png create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_question.png create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_sdfree.png create mode 100644 radio/src/bitmaps/480x272/default_theme/mask_shutdown.png create mode 100644 radio/src/bitmaps/480x272/default_theme/thumb.png diff --git a/radio/src/bitmaps/480x272/CMakeLists.txt b/radio/src/bitmaps/480x272/CMakeLists.txt index 55d3b6867..100143cce 100644 --- a/radio/src/bitmaps/480x272/CMakeLists.txt +++ b/radio/src/bitmaps/480x272/CMakeLists.txt @@ -40,5 +40,5 @@ add_bitmaps_target(${BITMAP_TARGET_PREFIX}_bootloader_icons ${RADIO_SRC_DIR}/bit add_dependencies(${BITMAP_TARGET_PREFIX}_bitmaps ${BITMAP_TARGET_PREFIX}_calibration_bitmaps ${BITMAP_TARGET_PREFIX}_button_bitmaps ${BITMAP_TARGET_PREFIX}_alpha_bitmaps ${BITMAP_TARGET_PREFIX}_alpha_calibration_bitmaps ${BITMAP_TARGET_PREFIX}_masks ${BITMAP_TARGET_PREFIX}_slider_masks ${BITMAP_TARGET_PREFIX}_layouts_masks - ${BITMAP_TARGET_PREFIX}_fonts ${BITMAP_TARGET_PREFIX}_themes_masks ${BITMAP_TARGET_PREFIX}_themes_alpha - ${BITMAP_TARGET_PREFIX}_volume_masks ${BITMAP_TARGET_PREFIX}_bootloader_bitmaps ${BITMAP_TARGET_PREFIX}_bootloader_icons ) + ${BITMAP_TARGET_PREFIX}_fonts ${BITMAP_TARGET_PREFIX}_themes_masks ${BITMAP_TARGET_PREFIX}_themes_alpha ${BITMAP_TARGET_PREFIX}_splash ${BITMAP_TARGET_PREFIX}_volume_masks + ${BITMAP_TARGET_PREFIX}_bootloader_bitmaps ${BITMAP_TARGET_PREFIX}_bootloader_icons ) diff --git a/radio/src/bitmaps/480x272/default_theme/mask_asterisk.png b/radio/src/bitmaps/480x272/default_theme/mask_asterisk.png new file mode 100644 index 0000000000000000000000000000000000000000..fd10af269206306059e54944a2744b130733fce4 GIT binary patch literal 12570 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 PKL{wus7hB$nuPp6T^pid literal 0 HcmV?d00001 diff --git a/radio/src/bitmaps/480x272/default_theme/mask_busy.png b/radio/src/bitmaps/480x272/default_theme/mask_busy.png new file mode 100644 index 0000000000000000000000000000000000000000..a2d87020526b2eb8bdd3d131fb4a27ed03fe7fd2 GIT binary patch literal 14831 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 B`A`4= literal 0 HcmV?d00001 diff --git a/radio/sdcard/horus/THEMES/EdgeTX/mask_edgetx.png b/radio/src/bitmaps/480x272/default_theme/mask_edgetx.png similarity index 100% rename from radio/sdcard/horus/THEMES/EdgeTX/mask_edgetx.png rename to radio/src/bitmaps/480x272/default_theme/mask_edgetx.png diff --git a/radio/src/bitmaps/480x272/default_theme/mask_iconback.png b/radio/src/bitmaps/480x272/default_theme/mask_iconback.png new file mode 100644 index 0000000000000000000000000000000000000000..966f6b7f23a160c0b17bf2f93134bd50d4f280ce GIT binary patch literal 1498 zcmV<01tt24P)9qR!*xue&0nj1vn4h0lzu$MBL&7&Ak;wOt2%EXSzP2UK5Anj`uq}a! z!dzWl*%If6cnu8=sHmv0B{-qa&CQK1aej#B@puplg=`5<=yQL6kLTxS%W-7A55wUw z5{ZQ6;6!28*Vl7Xlq}0AFE3}hPEiynit=%t-U-ySi0AO|5Y5famSX%Un*fRD1j%I5 zatvMZXwDOl9djNz@0SoJ92v}i&I71vaROoDk@LhOtZdRk zT|C0Xqp~R;D_cV(-Y;WiYly^i0lcNtP&}W{hnkuiuD}F4yd9^ZcmQ>rP9Vg20F_QB z5av7*Pdvh$M{7Otkn>8IR4T=_HALdM1RnQMx9Jji=DnigH8wUvmUaIUjIa?Q&XZ*s z!C;UpA%Yzt;sI1Toj^$01gL3o0urx;;n^Bu5|0+@;^EmEBImgXvrP+$=OWC`d(>^Z z2qDh<_wOI7tE;&ZBG?h)Jb;=OClDqcl}_;pDVqQ_Elyx|YlzCG%P`|Ua-Pc|@k$so zO$#+FE<=d(LZJ{OiTQMb6CutkFE2+R5a3FPU`L4a04kkMAjEk9H7!nHW^0JVb0Mtm zGbJ7^)SW?ycr@oZ18dVlWz&U_^GX;Y&a1Dlhu7=nN{C=bi1Pq)pA)1~DWucsqUVu# zE`_CZnih|m7H427ouFH^vb6;Lwu(h>i zDn9W6OWBmoZmn+Fr7O;KW{2Fk%!57z{lHz-`?JA88i}!pt7=7Ks;WX!6pW3HSsJLdwG~}mUC3lIn3q96MLNb}m&3S}% zik3|P$Y!$uJ{~&v^z@h-!0Yv5WMl*Yu(Y&C;%WHkK49ZB=Q)|dtW9v&3(~m z6n?+IXy8~ZhO)9U92^{Ae}CU%nuH!krSpq;TJs{yGA1V{4TE-ecA~AV4e#&oh{xmh zWJ>&i*?Bb^2}2B@g=ymS_Vywa3gsT3eSW0I0=?2k z42I4F^6q}NizgN z{1a?MEYhT~N*5Aq3k9(ntZkwQ7Pg8YCPmVSh2Usq1V56KV%c%Cg}VnXk9T=L4)>l1 zlH6{$R1}5DWRf0_hbEJWMx&8uXJ?dUnYCJto6UxQ3ghvZk|Ytp!SeZhe0zJ_gRxlb zpV03Lhr=4Ex7lp?U%6ba{bfF%GnGnFuh$=RlVzE+*-UdZRI61eih`Gy7u4%@^m;u^ zCKCXF!{LA=Nw~SWfh0*N7K_N|^S>M|77JxrW~bBPcDv=@TCdk^x7)nBx}w|d-Y=(8 zsqpCNhyZA{S{Vw3czu1%L?S^^6lpe_34lhUk&2@HjAJ+)GMmlP>-GMaRS*OQ0s-c7 zIljKWax$6h!OP1_jz%M`@$vD&_xE=$77MOcE3H2m3>XT9_RDd(T=4t-NF)-7$K$xT zxPZgqz;rr=s;U?a252^$Xfzs#Mx$C9I#pF!C=^gGm$6>20RWjy2BIixj|4%0+wF$e z>xIE!fZc8f?(XjR_4UQYV!?X7&d0|`K0Q4h%(UC>62V}Q(P)&dR*RpXpCrlq`}>37 z^Yb%x{eGXaEMq(#!)moc6h++L-eR}gfuwc{f&iP%hU4R7oSvQnB)MEJSt^y7PN#W( veomcE$CHy2I-O2NA`w13JWy5DA0Oiz#dxpUGm9As00000NkvXXu0mjfg{vY) literal 0 HcmV?d00001 diff --git a/radio/src/bitmaps/480x272/default_theme/mask_modelqty.png b/radio/src/bitmaps/480x272/default_theme/mask_modelqty.png new file mode 100644 index 0000000000000000000000000000000000000000..9a38e4f13dcaa706050cbc0e4cade094077257f7 GIT binary patch literal 773 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