diff --git a/.travis.yml b/.travis.yml index 445a27cf8..e0dc48c68 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,9 @@ -sudo: required -dist: xenial language: cpp -compiler: gcc -addons: - apt: - sources: - - sourceline: 'ppa:ubuntu-sdk-team/ppa' - - sourceline: 'ppa:ubuntu-toolchain-r/test' - - sourceline: 'ppa:beineri/opt-qt571-xenial' - update: true - packages: - - curl - - libmpfr4 - - libmpc3 - - libfox-1.6-dev - - libgtest-dev - - lib32stdc++6 - - libclang-common-6.0-dev - - clang-6.0 - - python3-pip +services: + - docker env: - global: - - QT_BASE=57 matrix: - FLAVOR=COMPANION - FLAVOR=ARM9X @@ -34,20 +14,7 @@ env: - FLAVOR=COLORLCD before_install: - - wget --quiet https://developer.arm.com/-/media/Files/downloads/gnu-rm/6-2017q2/gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2 - - tar xjf gcc-arm-none-eabi-6-2017-q2-update-linux.tar.bz2 - - sudo mv gcc-arm-none-eabi-6-2017-q2-update /opt/gcc-arm-none-eabi - - sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-gcc /usr/bin/arm-none-eabi-gcc - - sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-g++ /usr/bin/arm-none-eabi-g++ - - sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-as /usr/bin/arm-none-eabi-as - - sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-objcopy /usr/bin/arm-none-eabi-objcopy - - sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-objdump /usr/bin/arm-none-eabi-objdump - - sudo ln -s /opt/gcc-arm-none-eabi/bin/arm-none-eabi-size /usr/bin/arm-none-eabi-size - - sudo apt-get install --yes --force-yes -qq qt${QT_BASE}base qt${QT_BASE}multimedia qt${QT_BASE}svg qt${QT_BASE}tools; source /opt/qt${QT_BASE}/bin/qt${QT_BASE}-env.sh - - sudo ln -sf /usr/bin/python3 /usr/bin/python - - python3 -m pip install pip==20.3.3 - - sudo apt-get install python3-setuptools - - python3 -m pip install pillow==7.2.0 clang + - docker pull ghcr.io/raphaelcoeffic/opentx-commit-tests script: - - ./tools/commit-tests.sh + - docker run --rm --env FLAVOR=$FLAVOR -v $(pwd):/src ghcr.io/raphaelcoeffic/opentx-commit-tests diff --git a/radio/src/audio.cpp b/radio/src/audio.cpp index 42a8acae1..02fd5acb4 100644 --- a/radio/src/audio.cpp +++ b/radio/src/audio.cpp @@ -21,6 +21,10 @@ #include "opentx.h" #include +#if defined(LIBOPENUI) + #include "libopenui.h" +#endif + extern RTOS_MUTEX_HANDLE audioMutex; const int16_t sineValues[] = diff --git a/radio/src/audio.h b/radio/src/audio.h index c6d688434..e47c3cc53 100644 --- a/radio/src/audio.h +++ b/radio/src/audio.h @@ -25,6 +25,7 @@ #include #include "ff.h" #include "opentx_types.h" +#include "dataconstants.h" /* Implements a bit field, number of bits is set by the template, diff --git a/radio/src/bluetooth.cpp b/radio/src/bluetooth.cpp index 8e50ca6fa..d5669af53 100644 --- a/radio/src/bluetooth.cpp +++ b/radio/src/bluetooth.cpp @@ -21,6 +21,12 @@ #include "opentx.h" #include "io/frsky_firmware_update.h" +#if defined(LIBOPENUI) + #include "libopenui.h" +#else + #include "libopenui/src/libopenui_file.h" +#endif + #if defined(LOG_BLUETOOTH) extern FIL g_bluetoothFile; #endif diff --git a/radio/src/curves.cpp b/radio/src/curves.cpp index fe1dc377b..0bac367b9 100644 --- a/radio/src/curves.cpp +++ b/radio/src/curves.cpp @@ -20,6 +20,10 @@ #include "opentx.h" +#if defined(LIBOPENUI) + #include "libopenui.h" +#endif + int8_t * curveEnd[MAX_CURVES]; void loadCurves() diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index ae07276b3..69bb23166 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -553,11 +553,11 @@ typedef uint8_t swarnenable_t; #define LAYOUT_ID_LEN 10 PACK(struct CustomScreenData { char LayoutId[LAYOUT_ID_LEN]; - Layout::PersistentData layoutData; + LayoutPersistentData layoutData; }); #define CUSTOM_SCREENS_DATA \ NOBACKUP(CustomScreenData screenData[MAX_CUSTOM_SCREENS]); \ - NOBACKUP(TopBar::PersistentData topbarData); \ + NOBACKUP(TopBarPersistentData topbarData); \ NOBACKUP(uint8_t view); #else #define CUSTOM_SCREENS_DATA \ @@ -921,7 +921,7 @@ static inline void check_struct() CHKSIZE(ModelHeader, 31); CHKSIZE(CurveHeader, 4); CHKSIZE(CustomScreenData, 850); - CHKSIZE(TopBar::PersistentData, 300); + CHKTYPE(TopBarPersistentData, 300); #elif defined(PCBNV14) // TODO #elif defined(PCBSKY9X) diff --git a/radio/src/gui/colorlcd/CMakeLists.txt b/radio/src/gui/colorlcd/CMakeLists.txt index 9a2c1db3a..16dd56461 100644 --- a/radio/src/gui/colorlcd/CMakeLists.txt +++ b/radio/src/gui/colorlcd/CMakeLists.txt @@ -53,6 +53,7 @@ set(GUI_SRC radio_diaganas.cpp radio_calibration.cpp view_main.cpp + view_main_decoration.cpp view_channels.cpp view_logical_switches.cpp view_statistics.cpp @@ -95,10 +96,18 @@ set(SRC storage/modelslist.cpp ) + +option(WINDOWS_INSPECT_BORDERS "Draw bounding boxes around windows" OFF) +if(WINDOWS_INSPECT_BORDERS) + add_definitions(-DWINDOWS_INSPECT_BORDERS) +endif() + # includes libopenui include(${RADIO_SRC_DIR}/thirdparty/libopenui/src/CMakeLists.txt) include_directories(gui/libopenui) + add_definitions(-DLIBOPENUI) + foreach(LIBOPENUI_FILE ${LIBOPENUI_SRC}) set(SRC ${SRC} thirdparty/libopenui/src/${LIBOPENUI_FILE}) endforeach() diff --git a/radio/src/gui/colorlcd/colors.h b/radio/src/gui/colorlcd/colors.h index 0e4156979..eaf1dd473 100644 --- a/radio/src/gui/colorlcd/colors.h +++ b/radio/src/gui/colorlcd/colors.h @@ -67,4 +67,8 @@ #define SCROLLBOX_COLOR COLOR(SCROLLBOX_COLOR_INDEX) #define BATTERY_CHARGE_COLOR COLOR(BATTERY_CHARGE_COLOR_INDEX) +#if defined(WINDOWS_INSPECT_BORDERS) + #define WINDOWS_INSPECT_BORDER_COLOR LINE_COLOR +#endif + #endif // _COLORS_H_ diff --git a/radio/src/gui/colorlcd/curveedit.cpp b/radio/src/gui/colorlcd/curveedit.cpp index b1760bd32..9eb248083 100644 --- a/radio/src/gui/colorlcd/curveedit.cpp +++ b/radio/src/gui/colorlcd/curveedit.cpp @@ -21,6 +21,7 @@ #include "curveedit.h" #include "keyboard_curve.h" #include "opentx.h" // TODO for applyCustomCurve +#include "libopenui.h" #define SET_DIRTY() storageDirty(EE_MODEL) diff --git a/radio/src/gui/colorlcd/draw_functions.cpp b/radio/src/gui/colorlcd/draw_functions.cpp index b5aa5438f..f79c9b0dd 100644 --- a/radio/src/gui/colorlcd/draw_functions.cpp +++ b/radio/src/gui/colorlcd/draw_functions.cpp @@ -374,7 +374,7 @@ void drawSourceCustomValue(BitmapBuffer * dc, coord_t x, coord_t y, source_t sou { if (source >= MIXSRC_FIRST_TELEM) { source = (source-MIXSRC_FIRST_TELEM) / 3; - drawSensorCustomValue(dc, y, source, value, flags); + drawSensorCustomValue(dc, x, y, source, value, flags); } else if (source >= MIXSRC_FIRST_TIMER || source == MIXSRC_TX_TIME) { // TODO if (value < 0) flags |= BLINK|INVERS; diff --git a/radio/src/gui/colorlcd/fullscreen_dialog.cpp b/radio/src/gui/colorlcd/fullscreen_dialog.cpp index 49ffaefa0..0a3fd3542 100644 --- a/radio/src/gui/colorlcd/fullscreen_dialog.cpp +++ b/radio/src/gui/colorlcd/fullscreen_dialog.cpp @@ -21,6 +21,7 @@ #include "fullscreen_dialog.h" #include "mainwindow.h" #include "opentx.h" +#include "libopenui.h" FullScreenDialog::FullScreenDialog(uint8_t type, std::string title, std::string message, std::string action, const std::function & confirmHandler): FormGroup(MainWindow::instance(), {0, 0, LCD_W, LCD_H}, OPAQUE), diff --git a/radio/src/gui/colorlcd/gui.h b/radio/src/gui/colorlcd/gui.h index 12f0e6682..f99867164 100644 --- a/radio/src/gui/colorlcd/gui.h +++ b/radio/src/gui/colorlcd/gui.h @@ -47,7 +47,7 @@ void drawCurveHorizontalScale(); void drawCurveCoord(int x, int y, const char * text, bool active=false); void drawCurvePoint(int x, int y, LcdFlags color); -extern Layout * customScreens[MAX_CUSTOM_SCREENS]; +extern WidgetsContainer * customScreens[MAX_CUSTOM_SCREENS]; void drawAlertBox(const char * title, const char * text, const char * action); void showAlertBox(const char * title, const char * text, const char * action, uint8_t sound); diff --git a/radio/src/gui/colorlcd/gvar_numberedit.h b/radio/src/gui/colorlcd/gvar_numberedit.h index ac3b3e833..9051b3607 100644 --- a/radio/src/gui/colorlcd/gvar_numberedit.h +++ b/radio/src/gui/colorlcd/gvar_numberedit.h @@ -21,6 +21,7 @@ #define _GVAR_NUMBEREDIT_H_ #include "form.h" +#include "choice.h" #include "numberedit.h" #include "gvars.h" diff --git a/radio/src/gui/colorlcd/layout.cpp b/radio/src/gui/colorlcd/layout.cpp index c5bd2f3f9..6e64c7222 100644 --- a/radio/src/gui/colorlcd/layout.cpp +++ b/radio/src/gui/colorlcd/layout.cpp @@ -20,8 +20,8 @@ #include "opentx.h" #include "view_main.h" -#include "layouts/trims.h" -#include "layouts/sliders.h" + +WidgetsContainer * customScreens[MAX_CUSTOM_SCREENS] = {}; std::list & getRegisteredLayouts() { @@ -46,7 +46,11 @@ const LayoutFactory * getLayoutFactory(const char * name) return nullptr; } -Layout * loadLayout(const char * name, Layout::PersistentData * persistentData) +// +// Loads a layout, but does not attach it to any window +// +WidgetsContainer * +loadLayout(const char * name, LayoutPersistentData * persistentData) { const LayoutFactory * factory = getLayoutFactory(name); if (factory) { @@ -55,13 +59,14 @@ Layout * loadLayout(const char * name, Layout::PersistentData * persistentData) return nullptr; } +// +// Detaches and deletes all custom screens +// void deleteCustomScreens() { - for (unsigned int i = 0; i < MAX_CUSTOM_SCREENS; i++) { - auto& screen = customScreens[i]; + for (auto& screen : customScreens) { if (screen) { - screen->detach(); - delete screen; + screen->deleteLater(); screen = nullptr; } } @@ -75,206 +80,109 @@ void loadDefaultLayout() auto& screenData = g_model.screenData[0]; if (screen == nullptr && defaultLayout != nullptr) { + strcpy(screenData.LayoutId, defaultLayout->getId()); screen = defaultLayout->create(&screenData.layoutData); + // + // TODO: + // -> attach a few default widgets + // - ModelBmp + // - Timer + // - ??? + // if (screen) { screen->attach(ViewMain::instance()); } } } +// +// Loads and attaches all configured custom screens +// void loadCustomScreens() { - for (unsigned int i = 0; i < MAX_CUSTOM_SCREENS; i++) { + unsigned i = 0; + auto viewMain = ViewMain::instance(); + + while (i < MAX_CUSTOM_SCREENS) { auto& screen = customScreens[i]; screen = loadLayout(g_model.screenData[i].LayoutId, &g_model.screenData[i].layoutData); - if (screen) { - screen->attach(ViewMain::instance()); + if (!screen) { + // no more layouts + break; } + + // layout is ok, let's add it + screen->attach(viewMain); + viewMain->setMainViewsCount(i + 1); + screen->setLeft(viewMain->getMainViewLeftPos(i)); + i++; } + + auto topbar = viewMain->getTopbar(); + + // TODO: how to reload this guy? + topbar->load(); + + viewMain->setCurrentMainView(0); + viewMain->updateTopbarVisibility(); } -void Layout::decorate() +// +// Creates a new customer screen from factory: +// - the old screen is detached & deleted (including children) +// - new screen is configured into g_model +// - the new screen is returned (not attached) +// +WidgetsContainer * +createCustomScreen(const LayoutFactory* factory, unsigned customScreenIndex) { - // check if deco setting are still up-to-date - uint8_t checkSettings = - (hasTopbar() ? 1 << 0 : 0) | - (hasSliders() ? 1 << 1 : 0) | - (hasTrims() ? 1 << 2 : 0) | - (hasFlightMode() ? 1 << 3 : 0); + if (!factory || (customScreenIndex >= MAX_CUSTOM_SCREENS)) + return nullptr; - if (checkSettings == decorationSettings) { - // everything ok, exit! + if (customScreens[customScreenIndex]) { + customScreens[customScreenIndex]->deleteLater(true, false); + delete customScreens[customScreenIndex]; + } + + auto screen = factory->create(&g_model.screenData[customScreenIndex].layoutData); + customScreens[customScreenIndex] = screen; + + if (screen) { + auto dst = g_model.screenData[customScreenIndex].LayoutId; + auto src = factory->getId(); + strncpy(dst, src, sizeof(CustomScreenData::LayoutId)); + + return screen; + } + + return nullptr; +} + +void disposeCustomScreen(unsigned idx) +{ + // move custom screen data + if (idx >= MAX_CUSTOM_SCREENS) { return; } - // kill all decorations & re-decorate - for (auto deco: decorations) { - deco->deleteLater(); - } - decorations.clear(); - topBar = nullptr; + auto dst = &g_model.screenData[idx]; + auto src = dst + 1; + auto len = sizeof(CustomScreenData) * (MAX_CUSTOM_SCREENS - idx - 1); + memmove(dst, src, len); - // effect a full redraw - invalidate(); - - // save settings - decorationSettings = checkSettings; - - if (hasTopbar()) { - topBar = new TopBar(this); - topBar->load(); - decorations.push_back(topBar); - } - - if (hasSliders()) { -#if defined(HARDWARE_EXT1) || defined(HARDWARE_EXT2) - coord_t yOffset = (hasTrims() ? - TRIM_SQUARE_SIZE : 0) + (hasTopbar() ? TOPBAR_HEIGHT / 2 : 0); -#endif - - decorations.push_back( - new MainViewHorizontalSlider(this, {HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, - [=] { return calibratedAnalogs[CALIBRATED_POT1]; }) - ); - - if (IS_POT_MULTIPOS(POT2)) { - decorations.push_back( - new MainView6POS(this, {LCD_W / 2 - MULTIPOS_W / 2, LCD_H - TRIM_SQUARE_SIZE, MULTIPOS_W + 1, MULTIPOS_H}, - [=] { return (1 + (potsPos[1] & 0x0f)); }) - ); - } - else if (IS_POT(POT2)) { - decorations.push_back( - new MainViewHorizontalSlider(this, {LCD_W - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, - [=] { return calibratedAnalogs[CALIBRATED_POT2]; }) - ); - } - -#if defined(HARDWARE_POT3) - decorations.push_back( - new MainViewHorizontalSlider(this, {LCD_W - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, - [=] { return calibratedAnalogs[CALIBRATED_POT3]; }) - ); -#endif - -#if defined(HARDWARE_EXT1) - if (IS_POT_SLIDER_AVAILABLE(EXT1)) { - decorations.push_back( - new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2}, - [=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_LEFT]; }) - ); - decorations.push_back( - new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2}, - [=] { return calibratedAnalogs[CALIBRATED_POT_EXT1]; }) - ); - } - else { - decorations.push_back( - new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(hasTopbar())}, - [=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_LEFT]; }) - ); - } -#endif - -#if defined(HARDWARE_EXT2) - if (IS_POT_SLIDER_AVAILABLE(EXT2)) { - decorations.push_back( - new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset, TRIM_SQUARE_SIZE, - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2}, - [=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_RIGHT]; }) - ); - decorations.push_back( - new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 + yOffset, TRIM_SQUARE_SIZE, - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2}, - [=] { return calibratedAnalogs[CALIBRATED_POT_EXT2]; }) - ); - } - else { - decorations.push_back( - new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset, TRIM_SQUARE_SIZE, - VERTICAL_SLIDERS_HEIGHT(hasTopbar())}, - [=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_RIGHT]; }) - ); - } -#endif - } - - if (hasTrims()) { -#if defined(HARDWARE_POT3) || defined(HARDWARE_EXT1) - coord_t xOffset = hasSliders() ? TRIM_SQUARE_SIZE : 0; -#else - coord_t xOffset = 0; -#endif - coord_t yOffset = (hasTrims() ? - TRIM_SQUARE_SIZE : 0); - - // Trim order TRIM_LH, TRIM_LV, TRIM_RV, TRIM_RH - - // Left - decorations.push_back( - new MainViewHorizontalTrim(this, {HMARGIN, LCD_H - TRIM_SQUARE_SIZE + yOffset, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, - [=] { return getTrimValue(mixerCurrentFlightMode, 0); }) - ); - - // Right - decorations.push_back( - new MainViewHorizontalTrim(this, {LCD_W - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, LCD_H - TRIM_SQUARE_SIZE + yOffset, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, - [=] { return getTrimValue(mixerCurrentFlightMode, 3); }) - ); - - // Left - decorations.push_back( - new MainViewVerticalTrim(this, {HMARGIN + xOffset, LCD_H /2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset + (hasTopbar() ? TOPBAR_HEIGHT / 2 : 0), TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(hasTopbar())}, - [=] { return getTrimValue(mixerCurrentFlightMode, 1); }) - ); - - // Right - decorations.push_back( - new MainViewVerticalTrim(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE - xOffset, LCD_H /2 - VERTICAL_SLIDERS_HEIGHT(hasTopbar()) / 2 + yOffset + (hasTopbar() ? TOPBAR_HEIGHT / 2 : 0), TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(hasTopbar())}, - [=] { return getTrimValue(mixerCurrentFlightMode, 2); }) - ); - } - - if (hasFlightMode()) { - decorations.push_back( - new DynamicText(this, {50, LCD_H - 4 - (hasSliders() ? 2 * TRIM_SQUARE_SIZE: TRIM_SQUARE_SIZE), LCD_W - 100, 20}, [=] { - return g_model.flightModeData[mixerCurrentFlightMode].name; - }, CENTERED) - ); - } + dst = &g_model.screenData[MAX_CUSTOM_SCREENS - 1]; + len = sizeof(CustomScreenData); + memset(dst, 0, len); } -rect_t Layout::getMainZone(rect_t zone) const +LayoutFactory::LayoutFactory(const char * id, const char * name): + id(id), + name(name) { - if (hasTopbar()) { - zone.y += MENU_HEADER_HEIGHT; - zone.h -= MENU_HEADER_HEIGHT; - } + registerLayout(this); +} - if (hasFlightMode() || hasTrims()) { - zone.h -= TRIM_SQUARE_SIZE; - } - - if (hasSliders()) { -#if NUM_SLIDERS + NUM_POTS > 2 - zone.h -= TRIM_SQUARE_SIZE; -#endif - zone.w -= 2 * TRIM_SQUARE_SIZE; - zone.x += TRIM_SQUARE_SIZE; - } - - if (hasTrims()) { - zone.w -= 2 * TRIM_SQUARE_SIZE; - zone.x += TRIM_SQUARE_SIZE; - } - - if (zone.w % 2 != 0) - zone.w -= 1; - - if (zone.h % 2 != 0) - zone.h -= 1; - - return zone; -} diff --git a/radio/src/gui/colorlcd/layout.h b/radio/src/gui/colorlcd/layout.h index bdaf18c30..f611dc034 100644 --- a/radio/src/gui/colorlcd/layout.h +++ b/radio/src/gui/colorlcd/layout.h @@ -22,174 +22,35 @@ #include #include "widgets_container.h" -#include "lcd.h" -#include "topbar.h" #define MAX_LAYOUT_ZONES 10 #define MAX_LAYOUT_OPTIONS 10 constexpr coord_t TRIM_LINE_WIDTH = 8; constexpr coord_t TRIM_SQUARE_SIZE = 17; +constexpr coord_t MAIN_ZONE_BORDER = 10; +constexpr uint32_t LAYOUT_REFRESH = 1000 / 2; // 2 Hz -class LayoutFactory; +class BitmapBuffer; -#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_OPTIONS_END \ - { nullptr, ZoneOption::Bool } - -typedef WidgetsContainer LayoutBase; - -class Layout: public LayoutBase -{ - friend class LayoutFactory; - - public: - - // Common 'ZoneOptionValue's among all layouts - enum { - OPTION_TOPBAR = 0, - OPTION_FM, - OPTION_SLIDERS, - OPTION_TRIMS, - OPTION_MIRRORED, - - OPTION_LAST_DEFAULT=OPTION_MIRRORED - }; - - Layout(const LayoutFactory * factory, PersistentData * persistentData): - LayoutBase({0, 0, LCD_W, LCD_H}, persistentData), - factory(factory) - { - decorate(); - } - - void create() override - { - setOptionValue(OPTION_TOPBAR, OPTION_VALUE_BOOL(true)); - setOptionValue(OPTION_FM, OPTION_VALUE_BOOL(true)); - setOptionValue(OPTION_SLIDERS, OPTION_VALUE_BOOL(true)); - setOptionValue(OPTION_TRIMS, OPTION_VALUE_BOOL(true)); - setOptionValue(OPTION_MIRRORED, OPTION_VALUE_BOOL(false)); - decorate(); - } - - inline const LayoutFactory * getFactory() const - { - return factory; - } - - void checkEvents() override - { - LayoutBase::checkEvents(); - decorate(); - } - - bool hasTopbar() const { - return getOptionValue(OPTION_TOPBAR)->boolValue; - } - - bool hasFlightMode() const { - return getOptionValue(OPTION_FM)->boolValue; - } - - bool hasSliders() const { - return getOptionValue(OPTION_SLIDERS)->boolValue; - } - - bool hasTrims() const { - return getOptionValue(OPTION_TRIMS)->boolValue; - } - - bool isMirrored() const { - return getOptionValue(OPTION_MIRRORED)->boolValue; - } - - protected: - const LayoutFactory * factory; - TopBar * topBar = nullptr; - uint8_t decorationSettings = 0; - std::list decorations; - - // adds trims, sliders, pots, etc... - void decorate(); - - // get the Zone rect without decoration - rect_t getMainZone(rect_t zone) const; -}; - -void registerLayout(const LayoutFactory * factory); +typedef WidgetsContainerPersistentData LayoutPersistentData; class LayoutFactory { public: - LayoutFactory(const char * id, const char * name): - id(id), - name(name) - { - registerLayout(this); - } + LayoutFactory(const char * id, const char * name); const char * getId() const { return id; } const char * getName() const { return name; } virtual void drawThumb(BitmapBuffer * dc, uint16_t x, uint16_t y, LcdFlags flags) const = 0; virtual const ZoneOption * getOptions() const = 0; - virtual Layout * create(Layout::PersistentData * persistentData) const = 0; - virtual Layout * load(Layout::PersistentData * persistentData) const = 0; + virtual WidgetsContainer * create(LayoutPersistentData * persistentData) const = 0; + virtual WidgetsContainer * load(LayoutPersistentData * persistentData) const = 0; protected: const char * id; const char * name; }; -template -class BaseLayoutFactory: public LayoutFactory -{ - public: - BaseLayoutFactory(const char * id, const char * name, const uint8_t * bitmap, const ZoneOption * options): - LayoutFactory(id, name), - bitmap(bitmap), - options(options) - { - } - - void drawThumb(BitmapBuffer * dc, uint16_t x, uint16_t y, uint32_t flags) const override - { - dc->drawBitmapPattern(x, y, bitmap, flags); - } - - const ZoneOption * getOptions() const override - { - return options; - } - - Layout * create(Layout::PersistentData * persistentData) const override - { - Layout * layout = new T(this, persistentData); - if (layout) { - layout->create(); - } - return layout; - } - - Layout * load(Layout::PersistentData * persistentData) const override - { - Layout * layout = new T(this, persistentData); - if (layout) { - layout->load(); - } - return layout; - } - - protected: - const uint8_t * bitmap; - const ZoneOption * options; -}; - -Layout * loadLayout(const char * name, Layout::PersistentData * persistentData); +WidgetsContainer * loadLayout(const char * name, LayoutPersistentData * persistentData); // intented for new models void loadDefaultLayout(); @@ -197,4 +58,17 @@ void loadDefaultLayout(); // intended for existing models void loadCustomScreens(); +// delete all custom screens from memory +void deleteCustomScreens(); + +WidgetsContainer * +createCustomScreen(const LayoutFactory* factory, unsigned customScreenIndex); + +// Remove custom screen from the model +void disposeCustomScreen(unsigned idx); + +// Layout must register to be found +void registerLayout(const LayoutFactory * factory); + +// List of registered layout factories std::list & getRegisteredLayouts(); diff --git a/radio/src/gui/colorlcd/layouts/layout1x1.cpp b/radio/src/gui/colorlcd/layouts/layout1x1.cpp index ee641b530..3d469ecc7 100644 --- a/radio/src/gui/colorlcd/layouts/layout1x1.cpp +++ b/radio/src/gui/colorlcd/layouts/layout1x1.cpp @@ -18,10 +18,8 @@ * GNU General Public License for more details. */ -#include "opentx.h" -#include "trims.h" - -constexpr coord_t border = 10; +#include "layout.h" +#include "layout_factory_impl.h" const uint8_t LBM_LAYOUT_1x1[] = { #include "mask_layout1x1.lbm" @@ -47,9 +45,7 @@ class Layout1x1: public Layout rect_t getZone(unsigned int index) const override { - rect_t zone = getMainZone({border, border, LCD_W - 2 * border, LCD_H - 2 * border}); - - return zone; + return getMainZone(); } }; diff --git a/radio/src/gui/colorlcd/layouts/layout2+1.cpp b/radio/src/gui/colorlcd/layouts/layout2+1.cpp index dddd3f5f8..6f8d5eb6e 100644 --- a/radio/src/gui/colorlcd/layouts/layout2+1.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2+1.cpp @@ -18,11 +18,8 @@ * GNU General Public License for more details. */ -#include "opentx.h" -#include "sliders.h" -#include "trims.h" - -constexpr coord_t border = 10; +#include "layout.h" +#include "layout_factory_impl.h" const uint8_t LBM_LAYOUT_2P1[] = { #include "mask_layout2+1.lbm" @@ -48,7 +45,7 @@ class Layout2P1: public Layout rect_t getZone(unsigned int index) const override { - rect_t zone = getMainZone({border, border, LCD_W - 2 * border, LCD_H - 2 * border}); + rect_t zone = getMainZone(); if (index == 0) { return {isMirrored() ? zone.x : zone.x + zone.w / 2, zone.y,zone.w / 2, zone.h}; diff --git a/radio/src/gui/colorlcd/layouts/layout2x1.cpp b/radio/src/gui/colorlcd/layouts/layout2x1.cpp index 7db4f8c02..b514662fc 100644 --- a/radio/src/gui/colorlcd/layouts/layout2x1.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2x1.cpp @@ -18,11 +18,8 @@ * GNU General Public License for more details. */ -#include "opentx.h" -#include "sliders.h" -#include "trims.h" - -constexpr coord_t border = 10; +#include "layout.h" +#include "layout_factory_impl.h" const uint8_t LBM_LAYOUT_2x1[] = { #include "mask_layout2x1.lbm" @@ -48,7 +45,7 @@ class Layout2x1: public Layout rect_t getZone(unsigned int index) const override { - rect_t zone = getMainZone({border, border, LCD_W - 2 * border, LCD_H - 2 * border}); + rect_t zone = getMainZone(); if (index == 0) { return {isMirrored() ? zone.x + zone.w / 2 : zone.x, zone.y, zone.w / 2, zone.h}; diff --git a/radio/src/gui/colorlcd/layouts/layout2x2.cpp b/radio/src/gui/colorlcd/layouts/layout2x2.cpp index 05d973b62..174190cbf 100644 --- a/radio/src/gui/colorlcd/layouts/layout2x2.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2x2.cpp @@ -18,11 +18,8 @@ * GNU General Public License for more details. */ -#include "opentx.h" -#include "sliders.h" -#include "trims.h" - -constexpr coord_t border = 10; +#include "layout.h" +#include "layout_factory_impl.h" const uint8_t LBM_LAYOUT_2x2[] = { #include "mask_layout2x2.lbm" @@ -48,7 +45,7 @@ class Layout2x2: public Layout rect_t getZone(unsigned int index) const override { - rect_t zone = getMainZone({border, border, LCD_W - 2 * border, LCD_H - 2 * border}); + rect_t zone = getMainZone(); zone.w /= 2; zone.h /= 2; diff --git a/radio/src/gui/colorlcd/layouts/layout2x4.cpp b/radio/src/gui/colorlcd/layouts/layout2x4.cpp index b08009f83..b88c97095 100644 --- a/radio/src/gui/colorlcd/layouts/layout2x4.cpp +++ b/radio/src/gui/colorlcd/layouts/layout2x4.cpp @@ -18,9 +18,8 @@ * GNU General Public License for more details. */ -#include "opentx.h" - -constexpr coord_t border = 10; +#include "layout.h" +#include "layout_factory_impl.h" const uint8_t LBM_LAYOUT_2x4[] = { #include "mask_layout2x4.lbm" @@ -40,7 +39,7 @@ class Layout2x4: public Layout public: enum { - OPTION_PANEL1_BACKGROUND = OPTION_LAST_DEFAULT + 1, + OPTION_PANEL1_BACKGROUND = LAYOUT_OPTION_LAST_DEFAULT + 1, OPTION_PANEL1_COLOR, OPTION_PANEL2_BACKGROUND, OPTION_PANEL2_COLOR @@ -54,10 +53,10 @@ class Layout2x4: public Layout void create() override { Layout::create(); - setOptionValue(OPTION_PANEL1_BACKGROUND, OPTION_VALUE_BOOL(true)); - setOptionValue(OPTION_PANEL1_COLOR, OPTION_VALUE_UNSIGNED( RGB(77,112,203))); - setOptionValue(OPTION_PANEL2_BACKGROUND, OPTION_VALUE_BOOL(true)); - setOptionValue(OPTION_PANEL2_COLOR, OPTION_VALUE_UNSIGNED( RGB(77,112,203))); + 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); } unsigned int getZonesCount() const override @@ -67,7 +66,7 @@ class Layout2x4: public Layout rect_t getZone(unsigned int index) const override { - rect_t zone = getMainZone({border, border, LCD_W - 2 * border, LCD_H - 2 * border}); + rect_t zone = getMainZone(); zone.w /= 2; zone.h /= 4; diff --git a/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp b/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp new file mode 100644 index 000000000..c3a93ca1b --- /dev/null +++ b/radio/src/gui/colorlcd/layouts/layout_factory_impl.cpp @@ -0,0 +1,122 @@ +/* + * 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 "layout_factory_impl.h" +#include "layouts/trims.h" +#include "layouts/sliders.h" +#include "view_main_decoration.h" +#include "view_main.h" + +Layout::Layout(const LayoutFactory * factory, PersistentData * persistentData): + LayoutBase({0, 0, LCD_W, LCD_H}, persistentData), + factory(factory), + decoration(new ViewMainDecoration(this, getRect())) +{ + adjustLayout(); +} + +void Layout::create() +{ + memset(persistentData, 0, sizeof(PersistentData)); + + getOptionValue(LAYOUT_OPTION_TOPBAR)->boolValue = true; + getOptionValue(LAYOUT_OPTION_FM)->boolValue = true; + getOptionValue(LAYOUT_OPTION_SLIDERS)->boolValue = true; + getOptionValue(LAYOUT_OPTION_TRIMS)->boolValue = true; + getOptionValue(LAYOUT_OPTION_MIRRORED)->boolValue = false; +} + +#if defined(DEBUG_WINDOWS) +void Layout::paint(BitmapBuffer * dc) +{ + TRACE_WINDOWS("# painting -> %s", getWindowDebugString().c_str()); + LayoutBase::paint(dc); +} +#endif + +void Layout::checkEvents() +{ + LayoutBase::checkEvents(); + adjustLayout(); + + uint32_t now = RTOS_GET_MS(); + if (now - lastRefresh >= LAYOUT_REFRESH) { + lastRefresh = now; + invalidate(); + +#if defined(DEBUG_WINDOWS) + TRACE_WINDOWS("# %s refresh: %s", factory->getId(), getWindowDebugString().c_str()); +#endif + } +} + +void Layout::setTrimsVisible(bool visible) +{ + decoration->setTrimsVisible(visible); +} + +void Layout::setSlidersVisible(bool visible) +{ + decoration->setSlidersVisible(visible); +} + +void Layout::setFlightModeVisible(bool visible) +{ + decoration->setFlightModeVisible(visible); +} + +void Layout::adjustLayout() +{ + // Check if deco setting are still up-to-date + uint8_t checkSettings = + (hasTopbar() ? 1 << 0 : 0) | + (hasSliders() ? 1 << 1 : 0) | + (hasTrims() ? 1 << 2 : 0) | + (hasFlightMode() ? 1 << 3 : 0); + + if (checkSettings == decorationSettings) { + // everything ok, exit! + return; + } + + // Save settings + decorationSettings = checkSettings; + + // Set visible decoration + setSlidersVisible(hasSliders()); + setTrimsVisible(hasTrims()); + setFlightModeVisible(hasFlightMode()); + + // Re-compute positions + decoration->adjustDecoration(); + + // and update relevant windows + updateZones(); + + // probably not needed + //invalidate(); +} + +rect_t Layout::getMainZone() const +{ + rect_t zone = decoration->getMainZone(); + return ViewMain::instance()->getMainZone(zone, hasTopbar()); +} + diff --git a/radio/src/gui/colorlcd/layouts/layout_factory_impl.h b/radio/src/gui/colorlcd/layouts/layout_factory_impl.h new file mode 100644 index 000000000..1272b772f --- /dev/null +++ b/radio/src/gui/colorlcd/layouts/layout_factory_impl.h @@ -0,0 +1,170 @@ +/* + * 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. + */ + +#pragma once + +#include + +#include "audio.h" +#include "translations.h" + +#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_OPTIONS_END \ + { nullptr, ZoneOption::Bool } + +class ViewMainDecoration; + +typedef WidgetsContainerImpl LayoutBase; + +class Layout: public LayoutBase +{ + friend class LayoutFactory; + + public: + + Layout(const LayoutFactory * factory, PersistentData * persistentData); + +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "Layout"; + } + + void paint(BitmapBuffer * dc) override; +#endif + + void create() override; + + const LayoutFactory * getFactory() const + { + return factory; + } + + void checkEvents() override; + + bool hasTopbar() const { + return getOptionValue(LAYOUT_OPTION_TOPBAR)->boolValue; + } + + bool hasFlightMode() const { + return getOptionValue(LAYOUT_OPTION_FM)->boolValue; + } + + bool hasSliders() const { + return getOptionValue(LAYOUT_OPTION_SLIDERS)->boolValue; + } + + bool hasTrims() const { + return getOptionValue(LAYOUT_OPTION_TRIMS)->boolValue; + } + + bool isMirrored() const { + return getOptionValue(LAYOUT_OPTION_MIRRORED)->boolValue; + } + + // Set decoration visibility + void setTrimsVisible(bool visible); + void setSlidersVisible(bool visible); + void setFlightModeVisible(bool visible); + + // Updates settings for trims, sliders, pots, etc... + void adjustLayout() override; + + protected: + const LayoutFactory * factory = nullptr; + ViewMainDecoration* decoration = nullptr; + + // Decoration settings bitmask to detect updates + uint8_t decorationSettings = 0; + + // Last time we refreshed the window + uint32_t lastRefresh = 0; + + // Get the available space for widgets + rect_t getMainZone() const; +}; + +template +class BaseLayoutFactory: public LayoutFactory +{ + public: + BaseLayoutFactory(const char * id, const char * name, const uint8_t * bitmap, const ZoneOption * options): + LayoutFactory(id, name), + bitmap(bitmap), + options(options) + { + } + + void drawThumb(BitmapBuffer * dc, uint16_t x, uint16_t y, uint32_t flags) const override + { + dc->drawBitmapPattern(x, y, bitmap, flags); + } + + const ZoneOption * getOptions() const override + { + return options; + } + + Layout * create(Layout::PersistentData * persistentData) const override + { + initPersistentData(persistentData); + Layout * layout = new T(this, persistentData); + if (layout) { + layout->create(); + } + return layout; + } + + Layout * load(Layout::PersistentData * persistentData) const override + { + Layout * layout = new T(this, persistentData); + if (layout) { + layout->load(); + } + return layout; + } + + void initPersistentData(Layout::PersistentData * persistentData) const + { + memset(persistentData, 0, sizeof(Layout::PersistentData)); + if (options) { + int i = 0; + for (const ZoneOption * option = options; option->name; option++, i++) { + TRACE("LayoutFactory::initPersistentData() setting option '%s'", option->name); + // TODO compiler bug? The CPU freezes ... persistentData->options[i++] = option->deflt; + memcpy(&persistentData->options[i].value, &option->deflt, sizeof(ZoneOptionValue)); + persistentData->options[i].type = zoneValueEnumFromType(option->type); + } + } + } + + protected: + const uint8_t * bitmap; + const ZoneOption * options; +}; diff --git a/radio/src/gui/colorlcd/layouts/sliders.cpp b/radio/src/gui/colorlcd/layouts/sliders.cpp new file mode 100644 index 000000000..08a9fcc8c --- /dev/null +++ b/radio/src/gui/colorlcd/layouts/sliders.cpp @@ -0,0 +1,76 @@ +/* + * 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 "sliders.h" +#include "opentx.h" + +void MainViewHorizontalSlider::paint(BitmapBuffer * dc) +{ + // The ticks + int delta = (width() - TRIM_SQUARE_SIZE) / SLIDER_TICKS_COUNT; + coord_t x = TRIM_SQUARE_SIZE / 2; + for (uint8_t i = 0; i <= SLIDER_TICKS_COUNT; i++) { + if (i == 0 || i == SLIDER_TICKS_COUNT / 2 || i == SLIDER_TICKS_COUNT) + dc->drawSolidVerticalLine(x, 2, 13, DEFAULT_COLOR); + else + dc->drawSolidVerticalLine(x, 4, 9, DEFAULT_COLOR); + x += delta; + } + + // The square + x = width() - TRIM_SQUARE_SIZE - divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX); + drawTrimSquare(dc, x, 0, TRIM_BGCOLOR); +} + +void MainView6POS::paint(BitmapBuffer * dc) +{ + // The ticks + int delta = (width() - TRIM_SQUARE_SIZE) / (XPOTS_MULTIPOS_COUNT - 1); + coord_t x = TRIM_SQUARE_SIZE / 2; + for (uint8_t i = 0; i <= XPOTS_MULTIPOS_COUNT; i++) { + dc->drawSolidVerticalLine(x, 4, 9, DEFAULT_COLOR); + x += delta; + } + + // The square + auto value = 1 + (potsPos[idx] & 0x0f); + x = TRIM_SQUARE_SIZE / 2 + divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value -1) , 6); + drawTrimSquare(dc, x, 0, TRIM_BGCOLOR); + dc->drawNumber(x + 1, 0, value, FOCUS_COLOR); +} + +void MainViewVerticalSlider::paint(BitmapBuffer * dc) +{ + uint8_t slidersTick = height() / 5; + // The ticks + int delta = (height() - TRIM_SQUARE_SIZE) / slidersTick; + coord_t y = TRIM_SQUARE_SIZE / 2; + for (uint8_t i = 0; i <= slidersTick; i++) { + if (i == 0 || i == slidersTick / 2 || i == slidersTick) + dc->drawSolidHorizontalLine(2, y, 13, DEFAULT_COLOR); + else + dc->drawSolidHorizontalLine(4, y, 9, DEFAULT_COLOR); + y += delta; + } + + // The square + y = height() - TRIM_SQUARE_SIZE - divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX); + drawTrimSquare(dc, 0, y, TRIM_BGCOLOR); +} diff --git a/radio/src/gui/colorlcd/layouts/sliders.h b/radio/src/gui/colorlcd/layouts/sliders.h index 72cc539b4..6043ef093 100644 --- a/radio/src/gui/colorlcd/layouts/sliders.h +++ b/radio/src/gui/colorlcd/layouts/sliders.h @@ -32,24 +32,21 @@ constexpr coord_t HMARGIN = 5; constexpr coord_t HORIZONTAL_SLIDERS_WIDTH = SLIDER_TICKS_COUNT * 4 + TRIM_SQUARE_SIZE; constexpr coord_t MULTIPOS_H = 20; constexpr coord_t MULTIPOS_W = 50; -inline coord_t VERTICAL_SLIDERS_HEIGHT(bool topbar) -{ - return topbar ? SLIDER_TICKS_COUNT * 4 + TRIM_SQUARE_SIZE : SLIDER_TICKS_COUNT * 5 + TRIM_SQUARE_SIZE; -} +constexpr coord_t VERTICAL_SLIDERS_HEIGHT = SLIDER_TICKS_COUNT * 4 + TRIM_SQUARE_SIZE; class MainViewSlider : public Window { public: - MainViewSlider(Window * parent, const rect_t & rect, std::function getValue): + MainViewSlider(Window * parent, const rect_t & rect, uint8_t idx): Window(parent, rect), - getValue(std::move(getValue)) + idx(idx) { } void checkEvents() override { Window::checkEvents(); - int16_t newValue = getValue(); + int16_t newValue = calibratedAnalogs[idx]; if (value != newValue) { value = newValue; invalidate(); @@ -57,80 +54,27 @@ class MainViewSlider : public Window } protected: - std::function getValue; - int8_t value = 0; + uint8_t idx; + int16_t value = 0; }; class MainViewHorizontalSlider : public MainViewSlider { public: using MainViewSlider::MainViewSlider; - - void paint(BitmapBuffer * dc) override - { - // The ticks - int delta = (width() - TRIM_SQUARE_SIZE) / SLIDER_TICKS_COUNT; - coord_t x = TRIM_SQUARE_SIZE / 2; - for (uint8_t i = 0; i <= SLIDER_TICKS_COUNT; i++) { - if (i == 0 || i == SLIDER_TICKS_COUNT / 2 || i == SLIDER_TICKS_COUNT) - dc->drawSolidVerticalLine(x, 2, 13, DEFAULT_COLOR); - else - dc->drawSolidVerticalLine(x, 4, 9, DEFAULT_COLOR); - x += delta; - } - - // The square - auto value = - getValue(); - x = width() - TRIM_SQUARE_SIZE - divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX); - drawTrimSquare(dc, x, 0, TRIM_BGCOLOR); - } + void paint(BitmapBuffer * dc) override; }; class MainView6POS : public MainViewSlider { public: using MainViewSlider::MainViewSlider; - - void paint(BitmapBuffer * dc) override - { - // The ticks - int delta = (width() - TRIM_SQUARE_SIZE) / (XPOTS_MULTIPOS_COUNT - 1); - coord_t x = TRIM_SQUARE_SIZE / 2; - for (uint8_t i = 0; i <= XPOTS_MULTIPOS_COUNT; i++) { - dc->drawSolidVerticalLine(x, 4, 9, DEFAULT_COLOR); - x += delta; - } - - // The square - auto value = getValue(); - x = TRIM_SQUARE_SIZE / 2 + divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value -1) , 6); - drawTrimSquare(dc, x, 0, TRIM_BGCOLOR); - dc->drawNumber(x + 1, 0, value, FOCUS_COLOR); - } + void paint(BitmapBuffer * dc) override; }; class MainViewVerticalSlider : public MainViewSlider { public: using MainViewSlider::MainViewSlider; - - void paint(BitmapBuffer * dc) override - { - uint8_t slidersTick = height() / 5; - // The ticks - int delta = (height() - TRIM_SQUARE_SIZE) / slidersTick; - coord_t y = TRIM_SQUARE_SIZE / 2; - for (uint8_t i = 0; i <= slidersTick; i++) { - if (i == 0 || i == slidersTick / 2 || i == slidersTick) - dc->drawSolidHorizontalLine(2, y, 13, DEFAULT_COLOR); - else - dc->drawSolidHorizontalLine(4, y, 9, DEFAULT_COLOR); - y += delta; - } - - // The square - auto value = getValue(); - y = height() - TRIM_SQUARE_SIZE - divRoundClosest((height() - TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX); - drawTrimSquare(dc, 0, y, TRIM_BGCOLOR); - } + void paint(BitmapBuffer * dc) override; }; diff --git a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp new file mode 100644 index 000000000..14fe2cdb6 --- /dev/null +++ b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp @@ -0,0 +1,195 @@ +/* + * 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 "topbar_impl.h" +#include "opentx.h" + +const char * const STR_MONTHS[] = TR_MONTHS; +constexpr uint32_t TOPBAR_REFRESH = 1000 / 2; // 2 Hz + +TopbarImpl::TopbarImpl(Window * parent) : + TopbarImplBase({0, 0, LCD_W, MENU_HEADER_HEIGHT}, &g_model.topbarData) +{ + attach(parent); +} + +unsigned int TopbarImpl::getZonesCount() const +{ + return MAX_TOPBAR_ZONES; +} + +rect_t TopbarImpl::getZone(unsigned int index) const +{ + return { + coord_t(49 + (TOPBAR_ZONE_WIDTH + 2 * TOPBAR_ZONE_MARGIN) * index), + TOPBAR_ZONE_MARGIN, + TOPBAR_ZONE_WIDTH, + TOPBAR_HEIGHT + }; +} + +void TopbarImpl::setVisible(float visible) // 0.0 -> 1.0 +{ + if (visible == 0.0) { + setTop(-(int)MENU_HEADER_HEIGHT); + } + else if (visible == 1.0) { + setTop(0); + } + else if (visible > 0.0 && visible < 1.0){ + float top = - (float)MENU_HEADER_HEIGHT * (1.0 - visible); + setTop((coord_t)top); + } +} + +coord_t TopbarImpl::getVisibleHeight(float visible) const // 0.0 -> 1.0 +{ + if (visible == 0.0) { + return 0; + } + else if (visible == 1.0) { + return MENU_HEADER_HEIGHT; + } + + float h = (float)MENU_HEADER_HEIGHT * visible; + return (coord_t)h; +} + +void TopbarImpl::paint(BitmapBuffer * dc) +{ + dc->drawSolidFilledRect(0, 0, width(), height(), MENU_BGCOLOR); + OpenTxTheme::instance()->drawTopLeftBitmap(dc); + + struct gtm t; + gettime(&t); + 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); + + getTimerString(str, getValue(MIXSRC_TX_TIME)); + dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS) | CENTERED | MENU_COLOR); + + // USB icon + if (usbPlugged()) { + dc->drawBitmapPattern(LCD_W - 98, 8, LBM_TOPMENU_USB, MENU_COLOR); + } + + // Logs + if (isFunctionActive(FUNCTION_LOGS) && !usbPlugged() && BLINK_ON_PHASE) { + dc->drawBitmapPattern(LCD_W - 98, 6, LBM_DOT, MENU_COLOR); + } + + // RSSI + const uint8_t rssiBarsValue[] = {30, 40, 50, 60, 80}; + 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); + } + +#if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) + if (isModuleXJT(INTERNAL_MODULE) && isExternalAntennaEnabled()) { + dc->drawBitmapPattern(LCD_W-94, 4, LBM_TOPMENU_ANTENNA, MENU_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); + else if (requiredSpeakerVolume < 7) + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_1, MENU_COLOR); + else if (requiredSpeakerVolume < 13) + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_2, MENU_COLOR); + else if (requiredSpeakerVolume < 19) + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_3, MENU_COLOR); + else + dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_4, MENU_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); + } + else { + dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_COLOR); + } +#else + dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_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); + } + +#if 0 + // Radio battery - TODO + // drawValueWithUnit(370, 8, g_vbat100mV, UNIT_VOLTS, PREC1|FONT(XS)|MENU_COLOR); + // lcdDrawSolidRect(300, 3, 20, 50, MENU_COLOR); + // lcdDrawRect(batt_icon_x+FW, BAR_Y+1, 13, 7); + // lcdDrawSolidVerticalLine(batt_icon_x+FW+13, BAR_Y+2, 5); + + // Rx battery + if (g_model.voltsSource) { + TelemetryItem & item = telemetryItems[g_model.voltsSource-1]; + if (item.isAvailable()) { + int32_t value = item.value; + TelemetrySensor & sensor = g_model.telemetrySensors[g_model.frsky.altitudeSource-1]; + LcdFlags att = 0; + if (sensor.prec == 2) { + att |= PREC1; + value /= 10; + } + else if (sensor.prec == 1) { + att |= PREC1; + } + att |= (item.isOld() ? ALARM_COLOR : DEFAULT_COLOR); + lcdDrawSolidFilledRect(ALTITUDE_X, VOLTS_Y, ALTITUDE_W, ALTITUDE_H, DEFAULT_BGCOLOR); + lcdDrawText(ALTITUDE_X+PADDING, VOLTS_Y+2, "Voltage", att); + drawValueWithUnit(ALTITUDE_X+PADDING, VOLTS_Y+12, value, UNIT_VOLTS, FONT(XL)|LEFT|att); + } + } + + // Model altitude + if (g_model.frsky.altitudeSource) { + TelemetryItem & item = telemetryItems[g_model.frsky.altitudeSource-1]; + if (item.isAvailable()) { + int32_t value = item.value; + TelemetrySensor & sensor = g_model.telemetrySensors[g_model.frsky.altitudeSource-1]; + if (sensor.prec) value /= sensor.prec == 2 ? 100 : 10; + LcdFlags att = (item.isOld() ? ALARM_COLOR : DEFAULT_COLOR); + lcdDrawSolidFilledRect(ALTITUDE_X, ALTITUDE_Y, ALTITUDE_W, ALTITUDE_H, DEFAULT_BGCOLOR); + lcdDrawText(ALTITUDE_X+PADDING, ALTITUDE_Y+2, "Alt", att); + drawValueWithUnit(ALTITUDE_X+PADDING, ALTITUDE_Y+12, value, UNIT_METERS, FONT(XL)|LEFT|att); + } + } +#endif +} + +void TopbarImpl::checkEvents() +{ + uint32_t now = RTOS_GET_MS(); + if (now - lastRefresh >= TOPBAR_REFRESH) { + lastRefresh = now; + invalidate(); + } +} diff --git a/radio/src/gui/colorlcd/layouts/topbar_impl.h b/radio/src/gui/colorlcd/layouts/topbar_impl.h new file mode 100644 index 000000000..3315e8b3f --- /dev/null +++ b/radio/src/gui/colorlcd/layouts/topbar_impl.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#pragma once + +#include "topbar.h" +#include "layouts/layout_factory_impl.h" + +typedef WidgetsContainerImpl TopbarImplBase; + +class TopbarImpl: + public TopbarImplBase, + public Topbar +{ + public: + explicit TopbarImpl(Window * parent); + +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "TopBar"; + } +#endif + + void load() override + { + TopbarImplBase::load(); + } + + unsigned int getZonesCount() const override; + + rect_t getZone(unsigned int index) const override; + + void setVisible(float visible); + coord_t getVisibleHeight(float visible) const; // 0.0 -> 1.0 + + void paint(BitmapBuffer * dc) override; + + void checkEvents() override; + + protected: + uint32_t lastRefresh = 0; +}; diff --git a/radio/src/gui/colorlcd/layouts/trims.cpp b/radio/src/gui/colorlcd/layouts/trims.cpp new file mode 100644 index 000000000..013ba9bc6 --- /dev/null +++ b/radio/src/gui/colorlcd/layouts/trims.cpp @@ -0,0 +1,102 @@ +/* + * 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 "trims.h" +#include "opentx.h" + +void MainViewTrim::checkEvents() +{ + Window::checkEvents(); + int8_t newValue = getTrimValue(mixerCurrentFlightMode, idx); + if (value != newValue) { + value = newValue; + invalidate(); + } +} + +void MainViewHorizontalTrim::paint(BitmapBuffer * dc) +{ + int32_t trimMin, trimMax; + if (g_model.extendedTrims) { + trimMin = TRIM_EXTENDED_MIN; + trimMax = TRIM_EXTENDED_MAX; + } + else { + trimMin = TRIM_MIN; + trimMax = TRIM_MAX; + } + + // 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); + + // 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); + + // 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 { + if (value >= 0) { + dc->drawSolidVerticalLine(x + 4, 3, 9, FOCUS_COLOR); + } + if (value <= 0) { + dc->drawSolidVerticalLine(x + 10, 3, 9, FOCUS_COLOR); + } + } +} + +void MainViewVerticalTrim::paint(BitmapBuffer * dc) +{ + int32_t trimMin, trimMax; + if (g_model.extendedTrims) { + trimMin = TRIM_EXTENDED_MIN; + trimMax = TRIM_EXTENDED_MAX; + } + else { + trimMin = TRIM_MIN; + trimMax = TRIM_MAX; + } + + // 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); + + // 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); + + // 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 { + if (value >= 0) { + dc->drawSolidHorizontalLine(3, y + 4, 9, FOCUS_COLOR); + } + if (value <= 0) { + dc->drawSolidHorizontalLine(3, y + 10, 9, FOCUS_COLOR); + } + } +} diff --git a/radio/src/gui/colorlcd/layouts/trims.h b/radio/src/gui/colorlcd/layouts/trims.h index 9d090c498..ba8326f71 100644 --- a/radio/src/gui/colorlcd/layouts/trims.h +++ b/radio/src/gui/colorlcd/layouts/trims.h @@ -25,25 +25,17 @@ class MainViewTrim : public Window { public: - MainViewTrim(Window * parent, const rect_t & rect, std::function getValue): + MainViewTrim(Window * parent, const rect_t & rect, uint8_t idx): Window(parent, rect), - getValue(std::move(getValue)) + idx(idx) { } - void checkEvents() override - { - Window::checkEvents(); - int8_t newValue = getValue(); - if (value != newValue) { - value = newValue; - invalidate(); - } - } + void checkEvents() override; protected: - std::function getValue; - int8_t value = 0; + uint8_t idx; + int value = 0; }; class MainViewHorizontalTrim : public MainViewTrim @@ -51,41 +43,7 @@ class MainViewHorizontalTrim : public MainViewTrim public: using MainViewTrim::MainViewTrim; - void paint(BitmapBuffer * dc) override - { - int32_t trimMin, trimMax; - if (g_model.extendedTrims) { - trimMin = TRIM_EXTENDED_MIN; - trimMax = TRIM_EXTENDED_MAX; - } - else { - trimMin = TRIM_MIN; - trimMax = TRIM_MAX; - } - - // 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); - - // Trim square - auto value = getValue(); - 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 { - if (value >= 0) { - dc->drawSolidVerticalLine(x + 4, 3, 9, FOCUS_COLOR); - } - if (value <= 0) { - dc->drawSolidVerticalLine(x + 10, 3, 9, FOCUS_COLOR); - } - } - } + void paint(BitmapBuffer * dc) override; }; class MainViewVerticalTrim : public MainViewTrim @@ -93,39 +51,5 @@ class MainViewVerticalTrim : public MainViewTrim public: using MainViewTrim::MainViewTrim; - void paint(BitmapBuffer * dc) override - { - int32_t trimMin, trimMax; - if (g_model.extendedTrims) { - trimMin = TRIM_EXTENDED_MIN; - trimMax = TRIM_EXTENDED_MAX; - } - else { - trimMin = TRIM_MIN; - trimMax = TRIM_MAX; - } - - // 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); - - // Trim square - auto value = getValue(); - 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 { - if (value >= 0) { - dc->drawSolidHorizontalLine(3, y + 4, 9, FOCUS_COLOR); - } - if (value <= 0) { - dc->drawSolidHorizontalLine(3, y + 10, 9, FOCUS_COLOR); - } - } - } -}; \ No newline at end of file + void paint(BitmapBuffer * dc) override; +}; diff --git a/radio/src/gui/colorlcd/libopenui.h b/radio/src/gui/colorlcd/libopenui.h index dff106be4..5cb0422bf 100644 --- a/radio/src/gui/colorlcd/libopenui.h +++ b/radio/src/gui/colorlcd/libopenui.h @@ -55,6 +55,7 @@ #include "curveedit.h" #include "coloredit.h" #include "draw_functions.h" +#include "textedits.h" inline MessageDialog * createPopupInformation(const char * message) { diff --git a/radio/src/gui/colorlcd/menu_screen.cpp b/radio/src/gui/colorlcd/menu_screen.cpp index 5dc29a2c8..ad2bd61cd 100644 --- a/radio/src/gui/colorlcd/menu_screen.cpp +++ b/radio/src/gui/colorlcd/menu_screen.cpp @@ -21,22 +21,29 @@ #include "opentx.h" #include "menu_screen.h" #include "screen_setup.h" +#include "view_main.h" +#include "storage/storage.h" ScreenMenu::ScreenMenu(): TabsGroup(ICON_THEME) { updateTabs(); + + setCloseHandler([]{ + ViewMain::instance()->updateTopbarVisibility(); + storageDirty(EE_MODEL); + }); } void ScreenMenu::updateTabs() { removeAllTabs(); - addTab(new ScreenUserInterfacePage()); + addTab(new ScreenUserInterfacePage(this)); for (int index = 0; index < MAX_CUSTOM_SCREENS; index++) { if (customScreens[index]) { - auto tab = new ScreenSetupPage(this, customScreens[index], g_model.screenData[index]); + auto tab = new ScreenSetupPage(this, getTabs(), index); std::string title(STR_MAIN_VIEW_X); title.back() = index + '1'; tab->setTitle(title); @@ -45,7 +52,7 @@ void ScreenMenu::updateTabs() addTab(tab); } else { - addTab(new ScreenAddPage(this, index)); + addTab(new ScreenAddPage(this, getTabs())); break; } } diff --git a/radio/src/gui/colorlcd/model_curves.cpp b/radio/src/gui/colorlcd/model_curves.cpp index 40ad7a6d2..fc9cff1d2 100644 --- a/radio/src/gui/colorlcd/model_curves.cpp +++ b/radio/src/gui/colorlcd/model_curves.cpp @@ -67,7 +67,7 @@ class CurveEditWindow : public Page // Name new StaticText(window, grid.getFieldSlot(), STR_NAME); grid.nextLine(); - new TextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name)); + new RadioTextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name)); grid.nextLine(); // Type @@ -149,7 +149,7 @@ class CurveEditWindow : public Page // Name new StaticText(window, grid.getLabelSlot(), STR_NAME); - new TextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name)); + new RadioTextEdit(window, grid.getFieldSlot(), curve.name, sizeof(curve.name)); grid.nextLine(); // Type diff --git a/radio/src/gui/colorlcd/model_flightmodes.cpp b/radio/src/gui/colorlcd/model_flightmodes.cpp index 841e98d91..1298828d4 100644 --- a/radio/src/gui/colorlcd/model_flightmodes.cpp +++ b/radio/src/gui/colorlcd/model_flightmodes.cpp @@ -20,6 +20,7 @@ #include "model_flightmodes.h" #include "opentx.h" +#include "libopenui.h" #define SET_DIRTY() storageDirty(EE_MODEL) @@ -88,7 +89,7 @@ void ModelFlightModesPage::build(FormWindow * window) // Flight mode name new StaticText(group, grid.getLabelSlot(true), STR_NAME); - new TextEdit(group, grid.getFieldSlot(), g_model.flightModeData[i].name, LEN_FLIGHT_MODE_NAME); + new ModelTextEdit(group, grid.getFieldSlot(), g_model.flightModeData[i].name, LEN_FLIGHT_MODE_NAME); grid.nextLine(); // Flight mode switch diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index 5ab1a2b41..e810d85ed 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -242,7 +242,7 @@ void GVarEditWindow::buildBody(FormWindow * window) grid.spacer(8); new StaticText(window, grid.getLabelSlot(), STR_NAME); - new TextEdit(window, grid.getFieldSlot(), gvar->name, LEN_GVAR_NAME); + new RadioTextEdit(window, grid.getFieldSlot(), gvar->name, LEN_GVAR_NAME); grid.nextLine(); diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp index 78596644d..73e7c8718 100644 --- a/radio/src/gui/colorlcd/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -21,6 +21,7 @@ #include "model_inputs.h" #include "opentx.h" #include "gvar_numberedit.h" +#include "libopenui.h" #define SET_DIRTY() storageDirty(EE_MODEL) @@ -185,7 +186,7 @@ class InputEditWindow: public Page // Input Name new StaticText(window, grid.getLabelSlot(), STR_INPUTNAME); - new TextEdit(window, grid.getFieldSlot(), g_model.inputNames[line->chn], sizeof(g_model.inputNames[line->chn])); + new ModelTextEdit(window, grid.getFieldSlot(), g_model.inputNames[line->chn], sizeof(g_model.inputNames[line->chn])); grid.nextLine(); // Switch @@ -207,7 +208,7 @@ class InputEditWindow: public Page // Name new StaticText(window, grid.getLabelSlot(), STR_EXPONAME); - new TextEdit(window, grid.getFieldSlot(), line->name, sizeof(line->name)); + new ModelTextEdit(window, grid.getFieldSlot(), line->name, sizeof(line->name)); grid.nextLine(); // Source @@ -527,4 +528,4 @@ void insertExpo(uint8_t idx, uint8_t input) expo->weight = 100; resumeMixerCalculations(); storageDirty(EE_MODEL); -} \ No newline at end of file +} diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index 4bdc53bba..7bbb7a650 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -84,7 +84,7 @@ class MixEditWindow : public Page { // Mix name new StaticText(window, grid.getLabelSlot(), STR_MIXNAME); - new TextEdit(window, grid.getFieldSlot(), mix->name, sizeof(mix->name)); + new RadioTextEdit(window, grid.getFieldSlot(), mix->name, sizeof(mix->name)); grid.nextLine(); // Source diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index 02ad75a20..47a735b17 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -54,7 +54,7 @@ class OutputEditWindow : public Page { // Name new StaticText(window, grid.getLabelSlot(), STR_NAME); - new TextEdit(window, grid.getFieldSlot(), output->name, sizeof(output->name)); + new RadioTextEdit(window, grid.getFieldSlot(), output->name, sizeof(output->name)); grid.nextLine(); // Offset diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 53017aef8..2dbc75c93 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -22,6 +22,7 @@ #include "model_select.h" #include "opentx.h" #include "storage/modelslist.h" +#include "libopenui.h" #define CATEGORIES_WIDTH 120 #define MODELS_LEFT 123 diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 58ebebf43..f4057e238 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -171,7 +171,7 @@ class RegisterDialog: public Dialog { // Register ID new StaticText(form, grid.getLabelSlot(), STR_REG_ID); - auto edit = new TextEdit(form, grid.getFieldSlot(), g_model.modelRegistrationID, sizeof(g_model.modelRegistrationID)); + auto edit = new RadioTextEdit(form, grid.getFieldSlot(), g_model.modelRegistrationID, sizeof(g_model.modelRegistrationID)); grid.nextLine(); // UID @@ -218,7 +218,7 @@ class RegisterDialog: public Dialog { rect_t rect = waiting->getRect(); waiting->deleteLater(); - rxName = new TextEdit(&content->form, rect, reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME); + rxName = new RadioTextEdit(&content->form, rect, reusableBuffer.moduleSetup.pxx2.registerRxName, PXX2_LEN_RX_NAME); rect = exitButton->getRect(); auto okButton = new TextButton(&content->form, rect, "OK", [=]() -> int8_t { @@ -998,7 +998,7 @@ void ModelSetupPage::build(FormWindow * window) // Model name new StaticText(window, grid.getLabelSlot(), STR_MODELNAME); - auto text = new TextEdit(window, grid.getFieldSlot(), g_model.header.name, sizeof(g_model.header.name)); + auto text = new RadioTextEdit(window, grid.getFieldSlot(), g_model.header.name, sizeof(g_model.header.name)); text->setChangeHandler([=] { modelslist.load(); auto model = modelslist.getCurrentModel(); @@ -1035,7 +1035,7 @@ void ModelSetupPage::build(FormWindow * window) // Timer name new StaticText(window, grid.getLabelSlot(true), STR_NAME); grid.nextLine(); - new TextEdit(group, timerGrid.getSlot(), timer->name, LEN_TIMER_NAME); + new RadioTextEdit(group, timerGrid.getSlot(), timer->name, LEN_TIMER_NAME); timerGrid.nextLine(); // Timer mode diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index 9c711c39d..45e34a752 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -324,7 +324,7 @@ class SensorEditWindow : public Page { // Sensor name new StaticText(window, grid.getLabelSlot(), STR_NAME); - new TextEdit(window, grid.getFieldSlot(), sensor->label, sizeof(sensor->label)); + new RadioTextEdit(window, grid.getFieldSlot(), sensor->label, sizeof(sensor->label)); grid.nextLine(); // Type diff --git a/radio/src/gui/colorlcd/radio_calibration.cpp b/radio/src/gui/colorlcd/radio_calibration.cpp index 527a040f2..2cd07317a 100644 --- a/radio/src/gui/colorlcd/radio_calibration.cpp +++ b/radio/src/gui/colorlcd/radio_calibration.cpp @@ -21,6 +21,7 @@ #include "opentx.h" #include "radio_calibration.h" #include "sliders.h" +#include "view_main_decoration.h" #define XPOT_DELTA 10 #define XPOT_DELAY 5 /* cycles */ @@ -81,55 +82,14 @@ void RadioCalibrationPage::buildBody(FormWindow * window) new StickCalibrationWindow(window, {40, 20, 90, 90}, STICK1, STICK2); new StickCalibrationWindow(window, {LCD_W - 130, 20, 90, 90}, STICK4, STICK3); -#if defined(PCBHORUS) - - new MainViewHorizontalSlider(this, {HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, - [=] { return calibratedAnalogs[CALIBRATED_POT1]; }); - - if (IS_POT_MULTIPOS(POT2)) { - new MainView6POS(this, {LCD_W / 2 - MULTIPOS_W / 2, LCD_H - TRIM_SQUARE_SIZE, MULTIPOS_W + 1, MULTIPOS_H}, - [=] { return (1 + (potsPos[1] & 0x0f)); }); - } - - new MainViewHorizontalSlider(this, {LCD_W - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, - [=] { return calibratedAnalogs[CALIBRATED_POT3]; }); -#if defined(HARDWARE_EXT1) - coord_t yOffset = TOPBAR_HEIGHT / 2; - - if (IS_POT_SLIDER_AVAILABLE(EXT1)) { - new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(true) / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(true) / 2}, - [=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_LEFT]; }); - new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(true) / 2}, - [=] { return calibratedAnalogs[CALIBRATED_POT_EXT1]; }); - } - else { - new MainViewVerticalSlider(this, {HMARGIN, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(true) / 2 + yOffset, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT(true)}, - [=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_LEFT]; }); - } -#endif - -#if defined(HARDWARE_EXT2) - if (IS_POT_SLIDER_AVAILABLE(EXT2)) { - new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(true) / 2 + yOffset, TRIM_SQUARE_SIZE, - VERTICAL_SLIDERS_HEIGHT(true) / 2}, - [=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_RIGHT]; }); - new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 + yOffset, TRIM_SQUARE_SIZE, - VERTICAL_SLIDERS_HEIGHT(true) / 2}, - [=] { return calibratedAnalogs[CALIBRATED_POT_EXT2]; }); - } - else { - new MainViewVerticalSlider(this, {LCD_W - HMARGIN - TRIM_SQUARE_SIZE, LCD_H / 2 - VERTICAL_SLIDERS_HEIGHT(true) / 2 + yOffset, TRIM_SQUARE_SIZE, - VERTICAL_SLIDERS_HEIGHT(true)}, - [=] { return calibratedAnalogs[CALIBRATED_SLIDER_REAR_RIGHT]; }); - } -#endif -#elif defined(PCBNV14) - new MainViewHorizontalSlider(this, {HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, - [=] { return calibratedAnalogs[CALIBRATED_POT1]; }); - - new MainViewHorizontalSlider(this, {LCD_W - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, LCD_H - TRIM_SQUARE_SIZE, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, - [=] { return calibratedAnalogs[CALIBRATED_POT2]; }); + rect_t r = { 0, 0, window->width(), window->height() }; + auto deco = new ViewMainDecoration(window, r); + deco->setTrimsVisible(false); + deco->setSlidersVisible(true); + deco->setFlightModeVisible(false); + deco->adjustDecoration(); +#if defined(PCBNV14) new TextButton(window, {LCD_W - 120, LCD_H - 140, 90, 40}, "Next", [=]() -> uint8_t { nextStep(); diff --git a/radio/src/gui/colorlcd/radio_diaganas.cpp b/radio/src/gui/colorlcd/radio_diaganas.cpp index 23be1e154..f08d848c6 100644 --- a/radio/src/gui/colorlcd/radio_diaganas.cpp +++ b/radio/src/gui/colorlcd/radio_diaganas.cpp @@ -20,6 +20,7 @@ #include "opentx.h" #include "radio_diaganas.h" +#include "libopenui.h" constexpr coord_t ANA_OFFSET = 150; diff --git a/radio/src/gui/colorlcd/radio_diagkeys.cpp b/radio/src/gui/colorlcd/radio_diagkeys.cpp index f4c4e54ff..6f0c5f033 100644 --- a/radio/src/gui/colorlcd/radio_diagkeys.cpp +++ b/radio/src/gui/colorlcd/radio_diagkeys.cpp @@ -20,6 +20,7 @@ #include "opentx.h" #include "radio_diagkeys.h" +#include "libopenui.h" #if defined(KEYS_GPIO_PIN_PGUP) constexpr uint8_t KEY_START = 0; diff --git a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp index e97a211c5..368c0c195 100644 --- a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp +++ b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp @@ -20,6 +20,7 @@ #include "opentx.h" #include "radio_ghost_module_config.h" +#include "libopenui.h" class GhostModuleConfigWindow: public Window { diff --git a/radio/src/gui/colorlcd/radio_hardware.cpp b/radio/src/gui/colorlcd/radio_hardware.cpp index c2839c4b3..f2932f9a3 100644 --- a/radio/src/gui/colorlcd/radio_hardware.cpp +++ b/radio/src/gui/colorlcd/radio_hardware.cpp @@ -23,6 +23,7 @@ #include "radio_diagkeys.h" #include "radio_diaganas.h" #include "opentx.h" +#include "libopenui.h" #define SET_DIRTY() storageDirty(EE_GENERAL) @@ -135,7 +136,7 @@ class BluetoothConfigWindow : public FormGroup // BT radio name new StaticText(this, grid.getLabelSlot(true), STR_NAME); - new TextEdit(this, grid.getFieldSlot(), g_eeGeneral.bluetoothName, LEN_BLUETOOTH_NAME); + new RadioTextEdit(this, grid.getFieldSlot(), g_eeGeneral.bluetoothName, LEN_BLUETOOTH_NAME); grid.nextLine(); } @@ -174,7 +175,7 @@ void RadioHardwarePage::build(FormWindow * window) grid.nextLine(); for (int i = 0; i < NUM_STICKS; i++) { new StaticText(window, grid.getLabelSlot(true), TEXT_AT_INDEX(STR_VSRCRAW, (i + 1))); - new TextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i], LEN_ANA_NAME); + new RadioTextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i], LEN_ANA_NAME); grid.nextLine(); } @@ -183,7 +184,7 @@ void RadioHardwarePage::build(FormWindow * window) grid.nextLine(); for (int i = 0; i < NUM_POTS; i++) { new StaticText(window, grid.getLabelSlot(true), TEXT_AT_INDEX(STR_VSRCRAW, (i + NUM_STICKS + 1))); - new TextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i + NUM_STICKS], LEN_ANA_NAME); + new RadioTextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i + NUM_STICKS], LEN_ANA_NAME); new Choice(window, grid.getFieldSlot(2,1), STR_POTTYPES, POT_NONE, POT_WITHOUT_DETENT, [=]() -> int { return bfGet(g_eeGeneral.potsConfig, 2*i, 2); @@ -200,7 +201,7 @@ void RadioHardwarePage::build(FormWindow * window) grid.nextLine(); for (int i = 0; i < NUM_SLIDERS; i++) { new StaticText(window, grid.getLabelSlot(true), TEXT_AT_INDEX(STR_VSRCRAW, (i + NUM_STICKS + NUM_POTS + 1))); - new TextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i + NUM_STICKS], LEN_ANA_NAME); + new RadioTextEdit(window, grid.getFieldSlot(2,0), g_eeGeneral.anaNames[i + NUM_STICKS], LEN_ANA_NAME); new Choice(window, grid.getFieldSlot(2,1), STR_SLIDERTYPES, SLIDER_NONE, SLIDER_WITH_DETENT, [=]() -> int { uint8_t mask = (0x01 << i); @@ -220,7 +221,7 @@ void RadioHardwarePage::build(FormWindow * window) grid.nextLine(); for (int i = 0; i < NUM_SWITCHES; i++) { new SwitchDynamicLabel(window, grid.getLabelSlot(true), i); - new TextEdit(window, grid.getFieldSlot(2, 0), g_eeGeneral.switchNames[i], LEN_SWITCH_NAME); + new RadioTextEdit(window, grid.getFieldSlot(2, 0), g_eeGeneral.switchNames[i], LEN_SWITCH_NAME); new Choice(window, grid.getFieldSlot(2, 1), STR_SWTYPES, SWITCH_NONE, SWITCH_TYPE_MAX(i), [=]() -> int { return SWITCH_CONFIG(i); diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index 3b13677e6..063805cc7 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -22,6 +22,7 @@ #include "radio_setup.h" #include "opentx.h" +#include "libopenui.h" #define SET_DIRTY() storageDirty(EE_GENERAL) @@ -207,16 +208,6 @@ void RadioSetupPage::build(FormWindow * window) new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.beepVolume)); grid.nextLine(); - // Wav volume - new StaticText(window, grid.getLabelSlot(true), STR_WAV_VOLUME); - new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.wavVolume)); - grid.nextLine(); - - // Background volume - new StaticText(window, grid.getLabelSlot(true), STR_BG_VOLUME); - new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.backgroundVolume)); - grid.nextLine(); - // Beeps length new StaticText(window, grid.getLabelSlot(true), STR_BEEP_LENGTH); new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.beepLength)); @@ -227,14 +218,24 @@ void RadioSetupPage::build(FormWindow * window) auto edit = new NumberEdit(window, grid.getFieldSlot(), 0, 300, GET_DEFAULT(15 * g_eeGeneral.speakerPitch), [=](int32_t newValue) { - g_eeGeneral.speakerPitch = newValue / 15; - SET_DIRTY(); + g_eeGeneral.speakerPitch = newValue / 15; + SET_DIRTY(); }); edit->setStep(15); edit->setPrefix("+"); edit->setSuffix("Hz"); grid.nextLine(); + // Wav volume + new StaticText(window, grid.getLabelSlot(true), STR_WAV_VOLUME); + new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.wavVolume)); + grid.nextLine(); + + // Background volume + new StaticText(window, grid.getLabelSlot(true), STR_BG_VOLUME); + new Slider(window, grid.getFieldSlot(), -2, +2, GET_SET_DEFAULT(g_eeGeneral.backgroundVolume)); + grid.nextLine(); + #if defined(VARIO) { new Subtitle(window, grid.getLabelSlot(), STR_VARIO); @@ -304,6 +305,12 @@ void RadioSetupPage::build(FormWindow * window) edit->setSuffix("v"); grid.nextLine(); + // Inactivity alarm + new StaticText(window, grid.getLabelSlot(true), STR_INACTIVITYALARM); + edit = new NumberEdit(window, grid.getFieldSlot(), 0, 250, GET_SET_DEFAULT(g_eeGeneral.inactivityTimer)); + edit->setSuffix("minutes"); + grid.nextLine(); + // Alarms warning new StaticText(window, grid.getLabelSlot(true), STR_ALARMWARNING); new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_eeGeneral.disableAlarmWarning)); @@ -313,12 +320,6 @@ void RadioSetupPage::build(FormWindow * window) new StaticText(window, grid.getLabelSlot(true), STR_RSSI_SHUTDOWN_ALARM); new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(g_eeGeneral.disableRssiPoweroffAlarm)); grid.nextLine(); - - // Inactivity alarm - new StaticText(window, grid.getLabelSlot(true), STR_INACTIVITYALARM); - edit = new NumberEdit(window, grid.getFieldSlot(), 0, 250, GET_SET_DEFAULT(g_eeGeneral.inactivityTimer)); - edit->setSuffix("minutes"); - grid.nextLine(); } // Backlight @@ -399,7 +400,7 @@ void RadioSetupPage::build(FormWindow * window) #if defined(PXX2) // Owner ID new StaticText(window, grid.getLabelSlot(), STR_OWNER_ID); - new TextEdit(window, grid.getFieldSlot(), g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID); + new RadioTextEdit(window, grid.getFieldSlot(), g_eeGeneral.ownerRegistrationID, PXX2_LEN_REGISTRATION_ID); grid.nextLine(); #endif diff --git a/radio/src/gui/colorlcd/radio_spectrum_analyser.cpp b/radio/src/gui/colorlcd/radio_spectrum_analyser.cpp index 5df010844..4ea32a864 100644 --- a/radio/src/gui/colorlcd/radio_spectrum_analyser.cpp +++ b/radio/src/gui/colorlcd/radio_spectrum_analyser.cpp @@ -20,6 +20,7 @@ #include "radio_spectrum_analyser.h" #include "opentx.h" +#include "libopenui.h" #define SET_DIRTY() storageDirty(EE_GENERAL) diff --git a/radio/src/gui/colorlcd/radio_trainer.cpp b/radio/src/gui/colorlcd/radio_trainer.cpp index 53edb620c..202a732f9 100644 --- a/radio/src/gui/colorlcd/radio_trainer.cpp +++ b/radio/src/gui/colorlcd/radio_trainer.cpp @@ -20,6 +20,7 @@ #include "radio_trainer.h" #include "opentx.h" +#include "libopenui.h" #define SET_DIRTY() storageDirty(EE_GENERAL) diff --git a/radio/src/gui/colorlcd/screen_setup.cpp b/radio/src/gui/colorlcd/screen_setup.cpp index 48c6e99a4..7288a662b 100644 --- a/radio/src/gui/colorlcd/screen_setup.cpp +++ b/radio/src/gui/colorlcd/screen_setup.cpp @@ -23,75 +23,20 @@ #include "opentx.h" #include "view_main.h" #include "widget_settings.h" +#include "topbar.h" +#include "libopenui.h" +#include "layouts/layout_factory_impl.h" #define SET_DIRTY() storageDirty(EE_MODEL) -ScreenUserInterfacePage::ScreenUserInterfacePage(): - PageTab(STR_USER_INTERFACE, ICON_THEME_SETUP) +class LayoutChoice: public FormField { -} - -void ScreenUserInterfacePage::build(FormWindow * window) -{ - FormGridLayout grid; - - // Theme choice - new StaticText(window, grid.getLabelSlot(), STR_THEME, 0, 0); - // TODO: Theme picklist - grid.nextLine(); - - // Theme options ? - - // Top Bar - new StaticText(window, grid.getLabelSlot(), STR_TOP_BAR, 0, 0); - // TODO: enable settings topbar widgets - grid.nextLine(); -} - -ScreenAddPage::ScreenAddPage(ScreenMenu * menu, uint8_t pageIndex): - PageTab(), - menu(menu), - pageIndex(pageIndex) -{ - setTitle(STR_ADD_MAIN_VIEW); - setIcon(ICON_THEME_ADD_VIEW); -} - -void ScreenAddPage::build(FormWindow * window) -{ - rect_t buttonRect = {LCD_W / 2 - 100, (window->height() - 24) / 2, 200, 24}; - auto button = new TextButton(window, buttonRect, STR_ADD_MAIN_VIEW); - button->setPressHandler([=]() -> uint8_t { - - auto& screen = customScreens[pageIndex]; - auto& screenData = g_model.screenData[pageIndex]; - - const LayoutFactory * factory = getRegisteredLayouts().front(); - - screen = factory->create(&screenData.layoutData); - strncpy(screenData.LayoutId, factory->getId(), sizeof(screenData.LayoutId)); - - auto tab = new ScreenSetupPage(menu, screen, screenData); - std::string title(STR_MAIN_VIEW_X); - title.back() = pageIndex + '0'; - tab->setTitle(title); - tab->setIcon(ICON_THEME_VIEW1 + pageIndex); - - menu->removeTab(pageIndex); - menu->addTab(tab); - menu->setCurrentTab(pageIndex); - - if (pageIndex < MAX_CUSTOM_SCREENS - 1) { - menu->addTab(new ScreenAddPage(menu, pageIndex + 1)); - } - storageDirty(EE_MODEL); - return 0; - }); -} - -class LayoutChoice: public FormField { public: - LayoutChoice(Window * parent, const rect_t & rect, std::function getValue, std::function setValue): + + typedef std::function LayoutFactoryGetter; + typedef std::function LayoutFactorySetter; + + LayoutChoice(Window * parent, const rect_t & rect, LayoutFactoryGetter getValue, LayoutFactorySetter setValue): FormField(parent, {rect.x, rect.y, 59, 33}), getValue(std::move(getValue)), setValue(std::move(setValue)) @@ -102,7 +47,9 @@ class LayoutChoice: public FormField { { FormField::paint(dc); auto layout = getValue(); - layout->drawThumb(dc, 4, 4, editMode ? FOCUS_COLOR : LINE_COLOR); + if (layout) { + layout->drawThumb(dc, 4, 4, editMode ? FOCUS_COLOR : LINE_COLOR); + } } #if defined(HARDWARE_KEYS) @@ -162,94 +109,83 @@ class LayoutChoice: public FormField { std::function setValue; }; -ScreenSetupPage::ScreenSetupPage(ScreenMenu * menu, Layout*& screen, CustomScreenData& screenData): - PageTab(), - menu(menu), - screen(screen), - screenData(screenData) +SetupWidgetsPageSlot::SetupWidgetsPageSlot(FormGroup * parent, const rect_t & rect, WidgetsContainer* container, uint8_t slotIndex): + Button(parent, rect) { + setPressHandler([parent, container, slotIndex]() -> uint8_t { + + Menu * menu = new Menu(parent); + menu->addLine(TR_SELECT_WIDGET, [=]() { + Menu * menu = new Menu(parent); + for (auto factory: getRegisteredWidgets()) { + menu->addLine(factory->getName(), [=]() { + container->createWidget(slotIndex, factory); + }); + } + }); + + if (container->getWidget(slotIndex)) { + menu->addLine(TR_WIDGET_SETTINGS, [=]() { + auto widget = container->getWidget(slotIndex); + new WidgetSettings(parent, widget); + }); + menu->addLine(STR_REMOVE_WIDGET, [=]() { + container->removeWidget(slotIndex); + }); + } + + return 0; + }); } -class SetupWidgetsPageSlot: public Button +void SetupWidgetsPageSlot::paint(BitmapBuffer * dc) { - public: - SetupWidgetsPageSlot(FormGroup * parent, const rect_t & rect, Layout* screen, uint8_t slotIndex): - Button(parent, rect), - screen(screen), - slotIndex(slotIndex) - { - widget = screen->getWidget(slotIndex); - setPressHandler([=]() -> uint8_t { - if (widget) { - Menu * menu = new Menu(this); - menu->addLine(TR_SELECT_WIDGET, [=]() { - Menu * menu = new Menu(this); - for (auto factory: getRegisteredWidgets()) { - menu->addLine(factory->getName(), [=]() { - if (widget) widget->deleteLater(); - widget = screen->createWidget(slotIndex, factory); - }); - } - }); - menu->addLine(TR_WIDGET_SETTINGS, [=]() { - new WidgetSettings(this, widget); - }); - - menu->addLine(STR_REMOVE_WIDGET, [=]() { - widget->deleteLater(); - widget = nullptr; - }); - return 0; - } - else { - Menu * menu = new Menu(this); - for (auto factory: getRegisteredWidgets()) { - menu->addLine(factory->getName(), [=]() { - if (widget) widget->deleteLater(); - widget = screen->createWidget(slotIndex, factory); - }); - } - } - return 0; - }); - } - - void paint(BitmapBuffer * dc) override - { - if (hasFocus()) { - dc->drawSolidRect(0, 0, width(), height(), 1, DEFAULT_COLOR); - } - else { - dc->drawRect(0, 0, width(), height(), 1, DOTTED, DEFAULT_COLOR); - } - } - - protected: - Layout * screen; - uint8_t slotIndex; - Widget * widget = nullptr; -}; + if (hasFocus()) { + dc->drawRect(0, 0, width()-1, height()-1, 2, STASHED, CHECKBOX_COLOR); + } + else { + dc->drawSolidRect(0, 0, width()-1, height()-1, 2, LINE_COLOR); + } +} class SetupWidgetsPage: public FormWindow { public: - SetupWidgetsPage(Layout * screen): - FormWindow(MainWindow::instance(), {0, 0, LCD_W, LCD_H}, OPAQUE | FORM_FORWARD_FOCUS), - screen(screen), - oldParent(screen->getParent()) + SetupWidgetsPage(ScreenMenu* menu, uint8_t customScreenIdx): + FormWindow(ViewMain::instance(), {0, 0, 0, 0}, FORM_FORWARD_FOCUS), + menu(menu), + customScreenIdx(customScreenIdx) { Layer::push(this); - clearFocus(); - // attach this custom screen here so we can display it - screen->attach(this); - + // attach this custom screen here so we can display it + auto screen = customScreens[customScreenIdx]; + if (screen) { + screen->attach(this); + setRect(screen->getRect()); + screen->setLeft(0); + + auto viewMain = ViewMain::instance(); + savedView = viewMain->getCurrentMainView(); + viewMain->setCurrentMainView(customScreenIdx); + viewMain->bringToTop(); + setLeft(viewMain->getMainViewLeftPos(customScreenIdx)); + } + for (unsigned i = 0; i < screen->getZonesCount(); i++) { auto rect = screen->getZone(i); - new SetupWidgetsPageSlot(this, {rect.x - 1, rect.y - 1, rect.w + 2, rect.h + 2}, screen, i); + auto widget = new SetupWidgetsPageSlot(this, rect, customScreens[customScreenIdx], i); + if (i == 0) widget->setFocus(); } } +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "SetupWidgetPage(idx=" + std::to_string(customScreenIdx) + ")"; + } +#endif + void deleteLater(bool detach = true, bool trash = true) override { Layer::pop(this); @@ -257,14 +193,27 @@ class SetupWidgetsPage: public FormWindow #if defined(HARDWARE_TOUCH) Keyboard::hide(); #endif - if (oldParent) { - // give it back to its old parent before it gets deleted - screen->attach(oldParent); + auto screen = customScreens[customScreenIdx]; + if (screen) { + auto viewMain = ViewMain::instance(); + screen->attach(viewMain); + screen->setRect(getRect()); + viewMain->setCurrentMainView(savedView); } - FormWindow::deleteLater(detach, trash); + + // restore screen setting tab on top + menu->bringToTop(); } +#if defined(HARDWARE_TOUCH) + 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 + return true; + } +#endif + #if defined(HARDWARE_KEYS) void onEvent(event_t event) override { @@ -273,22 +222,169 @@ class SetupWidgetsPage: public FormWindow if (event == EVT_KEY_LONG(KEY_EXIT) || event == EVT_KEY_BREAK(KEY_EXIT)) { killEvents(event); deleteLater(); - SET_DIRTY(); } } #endif protected: - Layout* screen; - Window* oldParent; - - void paint(BitmapBuffer * dc) override - { - dc->clear(DEFAULT_BGCOLOR); - } + ScreenMenu* menu; + uint8_t customScreenIdx; + unsigned savedView = 0; + // void paint(BitmapBuffer * dc) override + // { + // //ViewMain::instance()->fullPaint(dc); + // //dc->clear(DEFAULT_BGCOLOR); + // } }; +ScreenUserInterfacePage::ScreenUserInterfacePage(ScreenMenu* menu): + PageTab(STR_USER_INTERFACE, ICON_THEME_SETUP), + menu(menu) +{ +} + +void ScreenUserInterfacePage::build(FormWindow * window) +{ + FormGridLayout grid; + + // Theme choice + new StaticText(window, grid.getLabelSlot(), STR_THEME, 0, 0); + // TODO: Theme picklist + grid.nextLine(); + + // Theme options ? + + // Top Bar + new StaticText(window, grid.getLabelSlot(), STR_TOP_BAR, 0, 0); + + auto menu = this->menu; + auto setupTopbarWidgets = new TextButton(window, grid.getFieldSlot(), STR_SETUP_WIDGETS); + setupTopbarWidgets->setPressHandler([menu]() -> uint8_t { + new SetupTopBarWidgetsPage(menu); + return 0; + }); +} + +ScreenAddPage::ScreenAddPage(ScreenMenu * menu, uint8_t pageIndex): + PageTab(), + menu(menu), + pageIndex(pageIndex) +{ + setTitle(STR_ADD_MAIN_VIEW); + setIcon(ICON_THEME_ADD_VIEW); +} + +extern const LayoutFactory * defaultLayout; + +void ScreenAddPage::build(FormWindow * window) +{ + rect_t buttonRect = {LCD_W / 2 - 100, (window->height() - 24) / 2, 200, 24}; + auto button = new TextButton(window, buttonRect, STR_ADD_MAIN_VIEW); + + auto pageIndex = this->pageIndex; + auto menu = this->menu; + + button->setPressHandler([menu, pageIndex]() -> uint8_t { + + // First page is "User interface", subtract it + auto newIdx = pageIndex - 1; + TRACE("ScreenAddPage: add screen: newIdx = %d", newIdx); + + auto& screen = customScreens[newIdx]; + auto& screenData = g_model.screenData[newIdx]; + + TRACE("ScreenAddPage: add screen: screen = %p", screen); + + const LayoutFactory * factory = defaultLayout; + if (factory) { + TRACE("ScreenAddPage: add screen: factory = %p", factory); + screen = factory->create(&screenData.layoutData); + + strncpy(screenData.LayoutId, factory->getId(), sizeof(screenData.LayoutId)); + TRACE("ScreenAddPage: add screen: LayoutId = %s", screenData.LayoutId); + + auto tab = new ScreenSetupPage(menu, pageIndex, newIdx); + std::string title(STR_MAIN_VIEW_X); + title.back() = newIdx + '1'; + tab->setTitle(title); + tab->setIcon(ICON_THEME_VIEW1 + newIdx); + + auto viewMain = ViewMain::instance(); + viewMain->setMainViewsCount(viewMain->getMainViewsCount() + 1); + + // remove current tab first + menu->setCurrentTab(0); + menu->removeTab(pageIndex); + + // add the new one + menu->addTab(tab); + menu->setCurrentTab(pageIndex); + + if (menu->getTabs() < MAX_CUSTOM_SCREENS) { + menu->addTab(new ScreenAddPage(menu, menu->getTabs())); + } + } + else { + TRACE("Add main view: factory is NULL"); + } + return 0; + }); +} + +ScreenSetupPage::ScreenSetupPage(ScreenMenu * menu, unsigned pageIndex, unsigned customScreenIndex): + PageTab(), + menu(menu), + pageIndex(pageIndex), + customScreenIndex(customScreenIndex) +{ +} + + + +static void updateLayoutOptions(FormGroup* optionsWindow, unsigned customScreenIndex) // TODO +{ + FormGridLayout grid; + optionsWindow->clear(); + + // Layout options... + int index = 0; + auto layout = dynamic_cast(customScreens[customScreenIndex]); + if (!layout) { + return; + } + auto factory = layout->getFactory(); + if (!factory) { + return; + } + + for (auto * option = factory->getOptions(); option->name; option++, index++) { + + auto layoutData = &g_model.screenData[customScreenIndex].layoutData; + ZoneOptionValue * value = &layoutData->options[index].value; + + // Option label + new StaticText(optionsWindow, grid.getLabelSlot(false), option->name); + + // Option value + switch (option->type) { + case ZoneOption::Bool: + new CheckBox(optionsWindow, grid.getFieldSlot(), GET_SET_DEFAULT(value->boolValue)); + break; + + case ZoneOption::Color: + new ColorEdit(optionsWindow, grid.getFieldSlot(), GET_SET_DEFAULT(value->unsignedValue)); + break; + + default: + break; + } + grid.nextLine(); + } + + optionsWindow->adjustHeight(); +} + void ScreenSetupPage::build(FormWindow * window) { FormGridLayout grid; @@ -296,75 +392,82 @@ void ScreenSetupPage::build(FormWindow * window) // Layout choice... new StaticText(window, grid.getLabelSlot(false), STR_LAYOUT); + auto layoutSlot = grid.getFieldSlot(); layoutSlot.h = 2 * PAGE_LINE_HEIGHT - 1; - layoutChoice = new LayoutChoice(window, layoutSlot, GET_VALUE(screen->getFactory()), [=](const LayoutFactory *factory) { - delete screen; - screen = factory->create(&screenData.layoutData); - strncpy(screenData.LayoutId, factory->getId(), sizeof(CustomScreenData::LayoutId)); - SET_DIRTY(); - updateLayoutOptions(); - }); + + // Dynamic options window... + auto idx = customScreenIndex; + rect_t optRect = {0, grid.getWindowHeight(), window->width(), 0}; + auto optionsWindow = new FormGroup(window, optRect, FORWARD_SCROLL | FORM_FORWARD_FOCUS); + + LayoutChoice::LayoutFactoryGetter getFactory = [idx] () -> const LayoutFactory * { + auto layout = dynamic_cast(customScreens[idx]); + if (!layout) return nullptr; + return layout->getFactory(); + }; + LayoutChoice::LayoutFactorySetter setLayout = [idx, optionsWindow](const LayoutFactory *factory) { + // delete any options potentially accessing + // the old custom screen + optionsWindow->clear(); + createCustomScreen(factory, idx); + updateLayoutOptions(optionsWindow, idx); + }; + + auto layoutChoice = new LayoutChoice(window, layoutSlot, getFactory, setLayout); grid.nextLine(layoutChoice->height()); // Setup widgets button... - setupWidgetsButton = new TextButton(window, grid.getFieldSlot(), STR_SETUP_WIDGETS); - setupWidgetsButton->setPressHandler([=]() -> uint8_t { - new SetupWidgetsPage(screen); + auto setupWidgetsButton = new TextButton(window, grid.getFieldSlot(), STR_SETUP_WIDGETS); + + auto menu = this->menu; + setupWidgetsButton->setPressHandler([idx, menu]() -> uint8_t { + new SetupWidgetsPage(menu, idx); return 0; }); grid.nextLine(); - // Dynamic options window... - optionsWindow = new FormGroup(window, {0, grid.getWindowHeight(), LCD_W, 0}, FORWARD_SCROLL | FORM_FORWARD_FOCUS); + // place the options window at the right spot + optionsWindow->setTop(grid.getWindowHeight()); + updateLayoutOptions(optionsWindow, idx); grid.addWindow(optionsWindow); - updateLayoutOptions(); -} -void ScreenSetupPage::updateLayoutOptions() -{ - FormGridLayout grid; - optionsWindow->clear(); + // fix focus order due to early insertion + FormField::link(setupWidgetsButton, optionsWindow); + window->setLastField(optionsWindow); + + // Prevent removing the last page + if (customScreens[1] != nullptr) { + auto button = new TextButton(window, grid.getFieldSlot(), STR_REMOVE_SCREEN); + auto menu = this->menu; + button->setPressHandler([menu, idx]() -> uint8_t { - // Layout options... - int index = 0; - for (auto * option = screen->getFactory()->getOptions(); option->name; option++, index++) { - ZoneOptionValue * value = screen->getOptionValue(index); + // Set the current tab to "User interface" to trigger body clearing + menu->setCurrentTab(0); - // Option label - new StaticText(optionsWindow, grid.getLabelSlot(false), option->name); + // Remove this screen from the model + disposeCustomScreen(idx); - // Option value - switch (option->type) { - case ZoneOption::Bool: - new CheckBox(optionsWindow, grid.getFieldSlot(), GET_SET_DEFAULT(value->boolValue)); - break; + // Delete all custom screens + deleteCustomScreens(); - case ZoneOption::Color: - new ColorEdit(optionsWindow, grid.getFieldSlot(), GET_SET_DEFAULT(value->unsignedValue)); - break; + // ... and reload + loadCustomScreens(); + menu->updateTabs(); - default: - break; - } - grid.nextLine(); - } + // Let's try to stay on the same page + // (first tab is "User interface") + auto pageIdx = idx + 1; - //if (pageIndex > 0 || customScreens[1]) { - auto button = new TextButton(optionsWindow, grid.getFieldSlot(), STR_REMOVE_SCREEN); - button->setPressHandler([=]() -> uint8_t { - // if (pageIndex < MAX_CUSTOM_SCREENS - 1) { - // memmove(&g_model.screenData[pageIndex], &g_model.screenData[pageIndex + 1], sizeof(CustomScreenData) * (MAX_CUSTOM_SCREENS - pageIndex - 1)); - // } - // memset(&g_model.screenData[MAX_CUSTOM_SCREENS - 1], 0, sizeof(CustomScreenData)); - memset(&screenData, 0, sizeof(CustomScreenData)); - loadCustomScreens(); - menu->updateTabs(); - storageDirty(EE_MODEL); - return 0; + // Subtract one more as the last one is "New main screen" + if (pageIdx > menu->getTabs() - 2) { + pageIdx = menu->getTabs() - 2; + } + menu->setCurrentTab(pageIdx); + return 0; }); - //} - optionsWindow->adjustHeight(); - optionsWindow->getParent()->adjustInnerHeight(); + // fix focus order due to early insertion + FormField::link(button, layoutChoice); + } } diff --git a/radio/src/gui/colorlcd/screen_setup.h b/radio/src/gui/colorlcd/screen_setup.h index a17f714cc..051aa70de 100644 --- a/radio/src/gui/colorlcd/screen_setup.h +++ b/radio/src/gui/colorlcd/screen_setup.h @@ -24,11 +24,12 @@ class ScreenUserInterfacePage: public PageTab { public: - ScreenUserInterfacePage(); + ScreenUserInterfacePage(ScreenMenu* menu); void build(FormWindow * window) override; protected: + ScreenMenu * menu; }; class ScreenAddPage: public PageTab { @@ -44,17 +45,20 @@ class ScreenAddPage: public PageTab { class ScreenSetupPage: public PageTab { public: - ScreenSetupPage(ScreenMenu * menu, Layout*& screen, CustomScreenData& screenData); + ScreenSetupPage(ScreenMenu * menu, unsigned pageIndex, unsigned customScreenIndex); void build(FormWindow * window) override; protected: ScreenMenu * menu; - Layout *& screen; - CustomScreenData& screenData; - FormGroup * optionsWindow = nullptr; - TextButton * setupWidgetsButton = nullptr; - FormField * layoutChoice = nullptr; + unsigned pageIndex; + unsigned customScreenIndex; +}; - void updateLayoutOptions(); +class SetupWidgetsPageSlot: public Button +{ + public: + SetupWidgetsPageSlot(FormGroup * parent, const rect_t & rect, WidgetsContainer* container, uint8_t slotIndex); + + void paint(BitmapBuffer * dc) override; }; diff --git a/radio/src/gui/colorlcd/screens_setup_old.cpp b/radio/src/gui/colorlcd/screens_setup_old.cpp deleted file mode 100644 index cc12cc312..000000000 --- a/radio/src/gui/colorlcd/screens_setup_old.cpp +++ /dev/null @@ -1,690 +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 "opentx.h" - -uint8_t THEME_ICONS[] = { - ICON_THEME, - ICON_THEME_SETUP, - ICON_THEME_VIEW1, - ICON_THEME_VIEW2, - ICON_THEME_VIEW3, - ICON_THEME_VIEW4, - ICON_THEME_VIEW5, - ICON_THEME_ADD_VIEW, -}; - -Layout * currentScreen; -WidgetsContainerInterface * currentContainer; -Widget * currentWidget; -uint8_t currentZone; -bool widgetNeedsSettings; - -#define SCREENS_SETUP_2ND_COLUMN 200 - -char fileSelection[LEN_ZONE_OPTION_STRING]; -uint8_t fileSelectionDone; - -int updateMainviewsMenu(); -bool menuScreenAdd(event_t event); -void onScreenSetupMenu(const char * result); - -void onZoneOptionFileSelectionMenu(const char * result) -{ - if (result == STR_UPDATE_LIST) { - if (!sdListFiles(BITMAPS_PATH, BITMAPS_EXT, LEN_ZONE_OPTION_STRING, nullptr)) { - POPUP_WARNING(STR_NO_BITMAPS_ON_SD); - } - } - else if (result != STR_EXIT) { - fileSelectionDone = true; - memcpy(fileSelection, result, sizeof(fileSelection)); - } -} - -uint8_t getZoneOptionColumns(const ZoneOption * option) -{ - if (option->type == ZoneOption::Color) { - return uint8_t(2 | NAVIGATION_LINE_BY_LINE); - } - else { - return 0; - } -} - -uint8_t editColorPart(coord_t x, coord_t y, event_t event, uint8_t part, uint8_t value, LcdFlags attr, uint32_t i_flags) -{ - const char * STR_COLOR_PARTS = "\002" "R:" "G:" "B:"; - uint8_t PART_BITS[] = { 5, 6, 5 }; - lcdDrawTextAtIndex(x, y, STR_COLOR_PARTS, part, (attr && menuHorizontalPosition < 0) ? FOCUS_COLOR : DEFAULT_COLOR); - lcdDrawNumber(x + 20, y, value << (8-PART_BITS[part]), LEFT|DEFAULT_COLOR|((attr && (menuHorizontalPosition < 0 || menuHorizontalPosition == part)) ? attr : DEFAULT_COLOR)); - if (attr && menuHorizontalPosition == part) { - value = checkIncDec(event, value, 0, (1 << PART_BITS[part])-1, i_flags); - } - return value; -} - -bool editZoneOption(coord_t y, const ZoneOption * option, ZoneOptionValue * value, LcdFlags attr, uint32_t i_flags, event_t event) -{ - lcdDrawText(MENUS_MARGIN_LEFT, y, option->name); - - if (option->type == ZoneOption::Bool) { - value->boolValue = editCheckBox(value->boolValue, SCREENS_SETUP_2ND_COLUMN, y, attr, event); // TODO always does storageDirty(EE_MODEL) - } - else if (option->type == ZoneOption::Integer) { - lcdDrawNumber(SCREENS_SETUP_2ND_COLUMN, y, value->signedValue, attr | LEFT); - if (attr) { - CHECK_INCDEC_MODELVAR(event, value->signedValue, option->min.signedValue, option->max.signedValue); // TODO i_flags - } - } - else if (option->type == ZoneOption::String) { - editName(SCREENS_SETUP_2ND_COLUMN, y, value->stringValue, sizeof(value->stringValue), event, attr); // TODO i_flags? - } - else if (option->type == ZoneOption::File) { - if (ZEXIST(value->stringValue)) - lcdDrawSizedText(SCREENS_SETUP_2ND_COLUMN, y, value->stringValue, sizeof(value->stringValue), attr); - else - lcdDrawTextAtIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_VCSWFUNC, 0, attr); // TODO define - if (attr) { - if (event==EVT_KEY_FIRST(KEY_ENTER)) { - s_editMode = 0; - if (sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(value->stringValue), value->stringValue, LIST_NONE_SD_FILE)) { - fileSelectionDone = false; - POPUP_MENU_START(onZoneOptionFileSelectionMenu); - } - else { - POPUP_WARNING(STR_NO_BITMAPS_ON_SD); - } - } - else if (fileSelectionDone) { - memcpy(value->stringValue, fileSelection, sizeof(fileSelection)); - fileSelectionDone = false; - storageDirty(i_flags); - } - } - } - else if (option->type == ZoneOption::TextSize) { - lcdDrawTextAtIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_TEXT_SIZE, value->unsignedValue, attr); - if (attr) { - value->unsignedValue = checkIncDec(event, value->unsignedValue, 0, 4, i_flags); - } - } - else if (option->type == ZoneOption::Timer) { - drawStringWithIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_TIMER, value->unsignedValue + 1, attr); - if (attr) { - value->unsignedValue = checkIncDec(event, value->unsignedValue, 0, MAX_TIMERS - 1, i_flags); - } - } - else if (option->type == ZoneOption::Source) { - drawSource(SCREENS_SETUP_2ND_COLUMN, y, value->unsignedValue, attr); - if (attr) { - CHECK_INCDEC_MODELSOURCE(event, value->unsignedValue, 1, MIXSRC_LAST_TELEM); - } - } - else if (option->type == ZoneOption::Color) { - RGB_SPLIT(value->unsignedValue, r, g, b); - - if (attr && menuHorizontalPosition < 0) { - lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN-3, y-1, 230, FH+1, FOCUS_BGCOLOR); - } - - lcdSetColor(value->unsignedValue); - lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN-1, y+1, 42, 17, DEFAULT_COLOR); - lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN, y+2, 40, 15, CUSTOM_COLOR); - - r = editColorPart(SCREENS_SETUP_2ND_COLUMN + 50, y, event, 0, r, attr, i_flags); - g = editColorPart(SCREENS_SETUP_2ND_COLUMN + 110, y, event, 1, g, attr, i_flags); - b = editColorPart(SCREENS_SETUP_2ND_COLUMN + 170, y, event, 2, b, attr, i_flags); - - if (attr && checkIncDec_Ret) { - value->unsignedValue = RGB_JOIN(r, g, b); - } - } - - return (attr && checkIncDec_Ret); -} - -int getOptionsCount(const ZoneOption * options) -{ - if (options == nullptr) { - return 0; - } - else { - int count = 0; - for (const ZoneOption * option = options; option->name; option++) { - count++; - } - return count; - } -} - -template -bool menuSettings(const char * title, T * object, uint32_t i_flags, event_t event) -{ - - if (object->getErrorMessage()) { - // display error instead of widget settings - // TODO nicer display (proper word-wrap) - SIMPLE_SUBMENU("Widget Error", ICON_MODEL_LUA_SCRIPTS, 1); - int len = strlen(object->getErrorMessage()); - int y = 3*FH; - const char * p = object->getErrorMessage(); - while (len > 0) { - lcdDrawSizedText(MENUS_MARGIN_LEFT, y, p, 30); - p += 30; - y += FH; - len -= 30; - } - return true; - } - - const ZoneOption * options = object->getOptions(); - linesCount = getOptionsCount(options); - uint8_t mstate_tab[MAX_WIDGET_OPTIONS]; - for (int i=0; i0) ? BLINK|INVERS : INVERS); - LcdFlags attr = (menuVerticalPosition == k ? blink : 0); - if (k < linesCount) { - const ZoneOption * option = &options[k]; - ZoneOptionValue * value = object->getOptionValue(k); - if (editZoneOption(y, option, value, attr, i_flags, event)) { - object->update(); - } - } - } - - return true; -} - -bool menuWidgetSettings(event_t event) -{ - return menuSettings(STR_WIDGET_SETTINGS, currentWidget, EE_MODEL, event); -} - -bool menuWidgetChoice(event_t event) -{ - static Widget * previousWidget = nullptr; - static Widget * currentWidget = nullptr; - static std::list::const_iterator iterator; - static Widget::PersistentData tempData; - - switch (event) { - case EVT_ENTRY: - { - previousWidget = currentContainer->getWidget(currentZone); - currentContainer->setWidget(currentZone, nullptr); - iterator = getRegisteredWidgets().cbegin(); - if (previousWidget) { - const WidgetFactory * factory = previousWidget->getFactory(); - std::list::const_iterator it = getRegisteredWidgets().cbegin(); - for (; it != getRegisteredWidgets().cend(); ++it) { - if (factory->getName() == (*it)->getName()) { - iterator = it; - break; - } - } - } - if (iterator != getRegisteredWidgets().cend()) - currentWidget = (*iterator)->create(currentContainer->getZone(currentZone), &tempData); - break; - } - - case EVT_KEY_FIRST(KEY_EXIT): - if (previousWidget) { - if (currentWidget) - delete currentWidget; - currentContainer->setWidget(currentZone, previousWidget); - } - popMenu(); - return false; - - case EVT_KEY_FIRST(KEY_ENTER): - if (iterator != getRegisteredWidgets().cend()) { - if (previousWidget) - delete previousWidget; - currentContainer->createWidget(currentZone, *iterator); - widgetNeedsSettings = currentContainer->getWidget(currentZone)->getFactory()->getOptions(); - storageDirty(EE_MODEL); - } - popMenu(); - return false; - - case EVT_ROTARY_RIGHT: - if (iterator != getRegisteredWidgets().cend() && iterator != --getRegisteredWidgets().cend()) { - ++iterator; - if (currentWidget) - delete currentWidget; - currentWidget = (*iterator)->create(currentContainer->getZone(currentZone), &tempData); - } - break; - - case EVT_ROTARY_LEFT: - if (iterator != getRegisteredWidgets().cbegin()) { - --iterator; - if (currentWidget) - delete currentWidget; - currentWidget = (*iterator)->create(currentContainer->getZone(currentZone), &tempData); - } - break; - } - - currentScreen->refresh(); - - Zone zone = currentContainer->getZone(currentZone); - lcdDrawFilledRect(0, 0, zone.x-2, LCD_H, SOLID, OVERLAY_COLOR | OPACITY(8)); - lcdDrawFilledRect(zone.x+zone.w+2, 0, LCD_W-zone.x-zone.w-2, LCD_H, SOLID, OVERLAY_COLOR | OPACITY(8)); - lcdDrawFilledRect(zone.x-2, 0, zone.w+4, zone.y-2, SOLID, OVERLAY_COLOR | OPACITY(8)); - lcdDrawFilledRect(zone.x-2, zone.y+zone.h+2, zone.w+4, LCD_H-zone.y-zone.h-2, SOLID, OVERLAY_COLOR | OPACITY(8)); - - if (currentWidget) - currentWidget->refresh(); - - if (iterator != getRegisteredWidgets().cbegin()) { - lcdDrawBitmapPattern(zone.x-10, zone.y+zone.h/2-10, LBM_SWIPE_CIRCLE, FOCUS_BGCOLOR); - lcdDrawBitmapPattern(zone.x-10, zone.y+zone.h/2-10, LBM_SWIPE_LEFT, FOCUS_COLOR); - } - if (iterator != --getRegisteredWidgets().cend()) { - lcdDrawBitmapPattern(zone.x+zone.w-9, zone.y+zone.h/2-10, LBM_SWIPE_CIRCLE, FOCUS_BGCOLOR); - lcdDrawBitmapPattern(zone.x+zone.w-9, zone.y+zone.h/2-10, LBM_SWIPE_RIGHT, FOCUS_COLOR); - } - if (currentWidget) - lcdDrawText(zone.x + zone.w, zone.y-1, currentWidget->getFactory()->getName(), RIGHT | DEFAULT_COLOR | FONT(XS) | INVERS); - - return true; -} - -void onZoneMenu(const char * result) -{ - if (result == STR_SELECT_WIDGET) { - pushMenu(menuWidgetChoice); - } - else if (result == STR_WIDGET_SETTINGS) { - pushMenu(menuWidgetSettings); - } - else if (result == STR_REMOVE_WIDGET) { - currentContainer->createWidget(currentZone, nullptr); - storageDirty(EE_MODEL); - } -} - -bool menuWidgetsSetup(event_t event) -{ - switch (event) { - case EVT_ENTRY: - menuVerticalPosition = 0; - break; - case EVT_KEY_FIRST(KEY_EXIT): - killEvents(KEY_EXIT); - popMenu(); - return false; - } - - currentScreen->refresh(); - - for (int i = currentContainer->getZonesCount() - 1; i >= 0; i--) { - Zone zone = currentContainer->getZone(i); - LcdFlags color; - int padding, thickness; - if (currentContainer == topbar) { - color = MENU_COLOR; - padding = 2; - thickness = 1; - } - else { - color = FOCUS_BGCOLOR; - padding = 4; - thickness = 2; - } - if (menuVerticalPosition == i) { - lcdDrawSolidRect(zone.x-padding, zone.y-padding, zone.w+2*padding, zone.h+2*padding, thickness, color); - if (event == EVT_KEY_FIRST(KEY_ENTER)) { - killEvents(KEY_ENTER); - currentZone = menuVerticalPosition; - currentWidget = currentContainer->getWidget(menuVerticalPosition); - if (currentWidget) { - POPUP_MENU_ADD_ITEM(STR_SELECT_WIDGET); - if (currentWidget->getFactory()->getOptions()) - POPUP_MENU_ADD_ITEM(STR_WIDGET_SETTINGS); - POPUP_MENU_ADD_ITEM(STR_REMOVE_WIDGET); - POPUP_MENU_START(onZoneMenu); - } - else { - onZoneMenu(STR_SELECT_WIDGET); - } - } - } - else { - if (widgetNeedsSettings) { - currentWidget = currentContainer->getWidget(menuVerticalPosition); - if (currentWidget) { - widgetNeedsSettings = false; - onZoneMenu(STR_WIDGET_SETTINGS); - } - } - lcdDrawRect(zone.x-padding, zone.y-padding, zone.w+2*padding, zone.h+2*padding, thickness, 0x3F, color); - } - } - navigate(event, currentContainer->getZonesCount(), currentContainer->getZonesCount(), 1); - return true; -} - -template -T * editThemeChoice(coord_t x, coord_t y, std::list & elList, T * current, bool needsOffsetCheck, LcdFlags attr, event_t event) -{ - static uint8_t menuHorizontalOffset = 0; - uint8_t elCount = elList.size(), last, idx; - coord_t pos; - int currentIndex = 0; - typename std::list::const_iterator elItr = elList.cbegin(); - - if (!elCount) - return nullptr; - - for (; elItr != elList.cend(); ++elItr, ++currentIndex) { - if ((*elItr) == current) - break; - } - - if (event == EVT_ENTRY) { - menuHorizontalOffset = 0; - needsOffsetCheck = true; - } - - if (needsOffsetCheck) { - if (currentIndex < menuHorizontalOffset) { - menuHorizontalOffset = currentIndex; - } - else if (currentIndex > menuHorizontalOffset + 3) { - menuHorizontalOffset = currentIndex - 3; - } - } - if (attr) { - if (menuHorizontalPosition < 0) { - lcdDrawSolidFilledRect(x-3, y-1, min(4, elCount)*56+1, 2*FH-5, FOCUS_BGCOLOR); - } - else { - if (needsOffsetCheck) { - menuHorizontalPosition = currentIndex; - } - else if (menuHorizontalPosition < menuHorizontalOffset) { - menuHorizontalOffset = menuHorizontalPosition; - } - else if (menuHorizontalPosition > menuHorizontalOffset + 3) { - menuHorizontalOffset = menuHorizontalPosition - 3; - } - } - } - last = min(menuHorizontalOffset + 4, elCount); - idx = menuHorizontalOffset; - pos = x; - elItr = elList.cbegin(); - std::advance(elItr, min(menuHorizontalOffset, elCount - 1)); - for (; idx < last && elItr != elList.cend(); ++idx, ++elItr, pos += 56) { - (*elItr)->drawThumb(pos, y+1, current == (*elItr) ? ((attr && menuHorizontalPosition < 0) ? FOCUS_COLOR : FOCUS_BGCOLOR) : LINE_COLOR); - } - if (elCount > 4) { - lcdDrawBitmapPattern(x - 12, y+1, LBM_CARROUSSEL_LEFT, menuHorizontalOffset > 0 ? LINE_COLOR : DISABLE_COLOR); - lcdDrawBitmapPattern(x + 4 * 56, y+1, LBM_CARROUSSEL_RIGHT, last < getRegisteredLayouts().size() ? LINE_COLOR : DISABLE_COLOR); - } - if (attr && menuHorizontalPosition >= 0) { - lcdDrawSolidRect(x + (menuHorizontalPosition - menuHorizontalOffset) * 56 - 3, y - 1, 57, 35, 1, FOCUS_BGCOLOR); - if (event == EVT_KEY_FIRST(KEY_ENTER)) { - s_editMode = 0; - elItr = elList.cbegin(); - if (menuHorizontalPosition < (int)elList.size()) - std::advance(elItr, menuHorizontalPosition); - if (elItr != elList.cend()) - return (*elItr); - else - return nullptr; - } - } - return nullptr; -} - -enum menuScreensThemeItems { - ITEM_SCREEN_SETUP_THEME, - ITEM_SCREEN_SETUP_THEME_OPTION1 = ITEM_SCREEN_SETUP_THEME+2 -}; - -bool menuScreensTheme(event_t event) -{ - bool needsOffsetCheck = (menuVerticalPosition != 0 || menuHorizontalPosition < 0); - const ZoneOption * options = theme->getOptions(); - int optionsCount = getOptionsCount(options); - linesCount = ITEM_SCREEN_SETUP_THEME_OPTION1 + optionsCount + 1; - - menuPageCount = updateMainviewsMenu(); - uint8_t mstate_tab[2 + MAX_THEME_OPTIONS + 1] = { uint8_t(NAVIGATION_LINE_BY_LINE | uint8_t(getRegisteredThemes().size()-1)), ORPHAN_ROW }; - for (int i=0; i 0) ? BLINK | INVERS : INVERS); - LcdFlags attr = (menuVerticalPosition == k ? blink : 0); - switch (k) { - case ITEM_SCREEN_SETUP_THEME: { - lcdDrawText(MENUS_MARGIN_LEFT, y + FH / 2, STR_THEME); - Theme * new_theme = editThemeChoice(SCREENS_SETUP_2ND_COLUMN, y, getRegisteredThemes(), theme, needsOffsetCheck, attr, event); - if (new_theme) { - new_theme->init(); - loadTheme(new_theme); - strncpy(g_eeGeneral.themeName, new_theme->getName(), sizeof(g_eeGeneral.themeName)); - killEvents(KEY_ENTER); - storageDirty(EE_GENERAL); - } - break; - } - - case ITEM_SCREEN_SETUP_THEME+1: - break; - - default: - { - uint8_t index = k - ITEM_SCREEN_SETUP_THEME_OPTION1; - if (index < optionsCount) { - const ZoneOption * option = &options[index]; - ZoneOptionValue * value = theme->getOptionValue(index); - bool ret = editZoneOption(y, option, value, attr, EE_GENERAL, event); - if (option->type == ZoneOption::Color) { - if (attr && event == EVT_KEY_FIRST(KEY_EXIT)) { - theme->update(); - } - } - else if (ret) { - theme->update(); - } - } - else if (index == optionsCount) { - lcdDrawText(MENUS_MARGIN_LEFT, y, STR_TOP_BAR); - drawButton(SCREENS_SETUP_2ND_COLUMN, y, STR_SETUP, attr); - if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) { - currentScreen = customScreens[0]; - currentContainer = topbar; - pushMenu(menuWidgetsSetup); - } - } - break; - } - } - } - - return true; -} - -enum MenuScreenSetupItems { - ITEM_SCREEN_SETUP_LAYOUT, - ITEM_SCREEN_SETUP_WIDGETS_SETUP = ITEM_SCREEN_SETUP_LAYOUT+2, - ITEM_SCREEN_SETUP_LAYOUT_OPTION1, -}; - -bool menuScreenSetup(int index, event_t event) -{ - if (customScreens[index] == nullptr) { - return menuScreenAdd(event); - } - - currentScreen = customScreens[index]; - currentContainer = currentScreen; - bool needsOffsetCheck = (menuVerticalPosition != 0 || menuHorizontalPosition < 0); - - char title[] = TR_MAIN_VIEW_X; - title[sizeof(title)-2] = '1' + index; - menuPageCount = updateMainviewsMenu(); - - const ZoneOption * options = currentScreen->getFactory()->getOptions(); - int optionsCount = getOptionsCount(options); - linesCount = ITEM_SCREEN_SETUP_LAYOUT_OPTION1 + optionsCount; - if (menuPageCount > 3) - ++linesCount; - - uint8_t mstate_tab[2 + MAX_LAYOUT_OPTIONS + 1] = { uint8_t(NAVIGATION_LINE_BY_LINE | uint8_t(getRegisteredLayouts().size()-1)), ORPHAN_ROW }; - for (int i=0; i0) ? BLINK|INVERS : INVERS); - LcdFlags attr = (menuVerticalPosition == k ? blink : 0); - switch(k) { - case ITEM_SCREEN_SETUP_LAYOUT: - { - lcdDrawText(MENUS_MARGIN_LEFT, y + FH / 2, STR_LAYOUT); - const LayoutFactory * factory = editThemeChoice(SCREENS_SETUP_2ND_COLUMN, y, getRegisteredLayouts(), currentScreen->getFactory(), needsOffsetCheck, attr, event); - if (factory) { - delete customScreens[index]; - currentScreen = customScreens[index] = factory->create(&g_model.screenData[index].layoutData); - strncpy(g_model.screenData[index].layoutName, factory->getName(), sizeof(g_model.screenData[index].layoutName)); - killEvents(KEY_ENTER); - storageDirty(EE_MODEL); - } - break; - } - - case ITEM_SCREEN_SETUP_LAYOUT+1: - break; - - case ITEM_SCREEN_SETUP_WIDGETS_SETUP: - drawButton(SCREENS_SETUP_2ND_COLUMN, y, STR_SETUP_WIDGETS, attr); - if (attr && event == EVT_KEY_FIRST(KEY_ENTER)) { - pushMenu(menuWidgetsSetup); - } - break; - - default: - { - uint8_t o = k - ITEM_SCREEN_SETUP_LAYOUT_OPTION1; - if (o < optionsCount) { - const ZoneOption * option = &options[o]; - ZoneOptionValue * value = currentScreen->getOptionValue(o); - if (editZoneOption(y, option, value, attr, EE_MODEL, event)) { - currentScreen->update(); - loadCustomScreens(); - } - } - else if (menuPageCount > 3 && o == optionsCount) { - drawButton(SCREENS_SETUP_2ND_COLUMN, y, STR_REMOVE_SCREEN, attr); - if (attr && event == EVT_KEY_LONG(KEY_ENTER)) { - delete currentScreen; - if (index != MAX_CUSTOM_SCREENS-1) { - memmove(&g_model.screenData[index], &g_model.screenData[index + 1], sizeof(CustomScreenData) * (MAX_CUSTOM_SCREENS - index - 1)); - memmove(&customScreens[index], &customScreens[index + 1], sizeof(Layout *) * (MAX_CUSTOM_SCREENS - index - 1)); - } - memset(&g_model.screenData[MAX_CUSTOM_SCREENS-1], 0, sizeof(CustomScreenData)); - customScreens[MAX_CUSTOM_SCREENS-1] = nullptr; - loadCustomScreens(); - killEvents(KEY_ENTER); - chainMenu(menuTabScreensSetup[index > 0 ? index : 1]); - return false; - } - } - break; - } - } - } - - return true; -} - -template -bool menuCustomScreenSetup(event_t event) -{ - return menuScreenSetup(N, event); -} - -const MenuHandlerFunc menuTabScreensSetup[1+MAX_CUSTOM_SCREENS] = { - menuScreensTheme, - menuCustomScreenSetup<0>, - menuCustomScreenSetup<1>, - menuCustomScreenSetup<2>, - menuCustomScreenSetup<3>, - menuCustomScreenSetup<4> -}; - -int updateMainviewsMenu() -{ - for (int index=1; indexcreate(&g_model.screenData[menuPageCount-2].layoutData); - strncpy(g_model.screenData[menuPageCount-2].layoutName, lf->getName(), sizeof(g_model.screenData[menuPageCount-2].layoutName)); - s_editMode = 0; - menuHorizontalPosition = -1; - killEvents(KEY_ENTER); - storageDirty(EE_MODEL); - return false; - } - - SIMPLE_MENU_WITH_OPTIONS(STR_ADD_MAIN_VIEW, THEME_ICONS, menuTabScreensSetup, menuPageCount, menuPageCount-1, 0); - return true; -} diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index 2b076a32a..5d8f03bae 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -20,6 +20,7 @@ #include "special_functions.h" #include "opentx.h" +#include "libopenui.h" #define SET_DIRTY() storageDirty(functions == g_model.customFn ? EE_MODEL : EE_GENERAL) diff --git a/radio/src/gui/colorlcd/tabsgroup.cpp b/radio/src/gui/colorlcd/tabsgroup.cpp index e4e29e7ea..eab15ee3f 100644 --- a/radio/src/gui/colorlcd/tabsgroup.cpp +++ b/radio/src/gui/colorlcd/tabsgroup.cpp @@ -27,6 +27,8 @@ #include "opentx.h" // TODO for constants... +#include + TabsGroupHeader::TabsGroupHeader(TabsGroup * parent, uint8_t icon): FormGroup(parent, { 0, 0, LCD_W, MENU_BODY_TOP }, OPAQUE), #if defined(HARDWARE_TOUCH) @@ -94,6 +96,19 @@ void TabsGroup::addTab(PageTab * page) setCurrentTab(0); } header.carousel.updateInnerWidth(); + invalidate(); +} + +int TabsGroup::removeTab(PageTab * page) +{ + auto tabIter = std::find(tabs.begin(), tabs.end(), page); + if (tabIter != tabs.end()) { + auto idx = tabIter - tabs.begin(); + removeTab(idx); + return idx; + } + + return -1; } void TabsGroup::removeTab(unsigned index) @@ -103,6 +118,7 @@ void TabsGroup::removeTab(unsigned index) } tabs.erase(tabs.begin() + index); header.carousel.updateInnerWidth(); + invalidate(); } void TabsGroup::removeAllTabs() @@ -132,6 +148,11 @@ void TabsGroup::setVisibleTab(PageTab * tab) } } +// TODO: add a mechanism to trigger a rebuild of a PageTab +// -> rebuild(): +// - as in setVisibleTab() (clear & build) +// - but without changing tab + void TabsGroup::checkEvents() { Window::checkEvents(); diff --git a/radio/src/gui/colorlcd/tabsgroup.h b/radio/src/gui/colorlcd/tabsgroup.h index ba49076de..a985c111d 100644 --- a/radio/src/gui/colorlcd/tabsgroup.h +++ b/radio/src/gui/colorlcd/tabsgroup.h @@ -193,8 +193,17 @@ class TabsGroup: public Window } #endif + unsigned getTabs() const + { + return tabs.size(); + } + void addTab(PageTab * page); + // Return the index of the found tab + // or -1 if the tab could not be found + int removeTab(PageTab * page); + void removeTab(unsigned index); void removeAllTabs(); @@ -206,7 +215,7 @@ class TabsGroup: public Window setVisibleTab(tabs[index]); } } - + void checkEvents() override; #if defined(HARDWARE_KEYS) diff --git a/radio/src/gui/colorlcd/textedits.h b/radio/src/gui/colorlcd/textedits.h new file mode 100644 index 000000000..15e3e2e79 --- /dev/null +++ b/radio/src/gui/colorlcd/textedits.h @@ -0,0 +1,49 @@ +/* + * 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. + */ + +#pragma once + +#include "libopenui.h" +#include "textedit.h" +#include "storage/storage.h" + +class ModelTextEdit: public TextEdit +{ + public: + ModelTextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags = 0): + TextEdit(parent, rect, value, length, windowFlags) + { + setChangeHandler([](){ + storageDirty(EE_MODEL); + }); + } +}; + +class RadioTextEdit: public TextEdit +{ + public: + RadioTextEdit(Window * parent, const rect_t & rect, char * value, uint8_t length, LcdFlags windowFlags = 0): + TextEdit(parent, rect, value, length, windowFlags) + { + setChangeHandler([](){ + storageDirty(EE_GENERAL); + }); + } +}; \ No newline at end of file diff --git a/radio/src/gui/colorlcd/theme.cpp b/radio/src/gui/colorlcd/theme.cpp index 1a9ba857c..e0fcd422a 100644 --- a/radio/src/gui/colorlcd/theme.cpp +++ b/radio/src/gui/colorlcd/theme.cpp @@ -19,6 +19,8 @@ */ #include "opentx.h" +#include "libopenui.h" + //#include "checkbox.h" //#include "modal_window.h" diff --git a/radio/src/gui/colorlcd/themes/480_default.cpp b/radio/src/gui/colorlcd/themes/480_default.cpp index 8479e3fb5..3e0947282 100644 --- a/radio/src/gui/colorlcd/themes/480_default.cpp +++ b/radio/src/gui/colorlcd/themes/480_default.cpp @@ -316,7 +316,7 @@ class Theme480: public OpenTxTheme void drawBackground(BitmapBuffer * dc) const override { if (backgroundBitmap) { - dc->drawBitmap(0, 0, backgroundBitmap); + dc->drawBitmap(0 - dc->getOffsetX(), 0 - dc->getOffsetY(), backgroundBitmap); } else { lcdSetColor(g_eeGeneral.themeData.options[0].value.unsignedValue); diff --git a/radio/src/gui/colorlcd/topbar.cpp b/radio/src/gui/colorlcd/topbar.cpp index cbe773a1e..172f4f9cf 100644 --- a/radio/src/gui/colorlcd/topbar.cpp +++ b/radio/src/gui/colorlcd/topbar.cpp @@ -19,139 +19,67 @@ */ #include "opentx.h" +#include "layer.h" +#include "view_main.h" +#include "screen_setup.h" +#include "storage/storage.h" +#include "topbar.h" +#include "topbar_impl.h" -TopBar::TopBar(Window * parent) : - WidgetsContainer({0, 0, LCD_W, MENU_HEADER_HEIGHT}, &g_model.topbarData) + +Topbar * TopbarFactory::create(Window * parent) { - attach(parent); + return new TopbarImpl(parent); } -unsigned int TopBar::getZonesCount() const +SetupTopBarWidgetsPage::SetupTopBarWidgetsPage(ScreenMenu* menu): + FormWindow(ViewMain::instance(), {0, 0, 0, 0}, FORM_FORWARD_FOCUS), + menu(menu) { - return MAX_TOPBAR_ZONES; + // remember focus + Layer::push(this); + + auto viewMain = ViewMain::instance(); + + // save current view & switch to 1st one + savedView = viewMain->getCurrentMainView(); + viewMain->setCurrentMainView(0); + viewMain->bringToTop(); + + //TODO: force the topbar to be visible? + + // adopt the dimensions of the main view + setRect(viewMain->getRect()); + + auto topbar = dynamic_cast(viewMain->getTopbar()); + for (unsigned i = 0; i < topbar->getZonesCount(); i++) { + auto rect = topbar->getZone(i); + auto widget = new SetupWidgetsPageSlot(this, rect, topbar, i); + if (i == 0) widget->setFocus(); + } } -rect_t TopBar::getZone(unsigned int index) const +void SetupTopBarWidgetsPage::deleteLater(bool detach, bool trash) { - return { - coord_t(49 + (TOPBAR_ZONE_WIDTH + 2 * TOPBAR_ZONE_MARGIN) * index), - TOPBAR_ZONE_MARGIN, - TOPBAR_ZONE_WIDTH, - TOPBAR_HEIGHT - }; + // restore screen setting tab on top + menu->bringToTop(); + Layer::pop(this); + + // and continue async deletion... + FormWindow::deleteLater(detach, trash); + + storageDirty(EE_MODEL); } -const char * const STR_MONTHS[] = TR_MONTHS; - -void TopBar::paint(BitmapBuffer * dc) +#if defined(HARDWARE_KEYS) +void SetupTopBarWidgetsPage::onEvent(event_t event) { - dc->drawSolidFilledRect(0, 0, width(), height(), MENU_BGCOLOR); - OpenTxTheme::instance()->drawTopLeftBitmap(dc); - - struct gtm t; - gettime(&t); - 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); - - getTimerString(str, getValue(MIXSRC_TX_TIME)); - dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS) | CENTERED | MENU_COLOR); - - // USB icon - if (usbPlugged()) { - dc->drawBitmapPattern(LCD_W - 98, 8, LBM_TOPMENU_USB, MENU_COLOR); - } - - // Logs - if (isFunctionActive(FUNCTION_LOGS) && !usbPlugged() && BLINK_ON_PHASE) { - dc->drawBitmapPattern(LCD_W - 98, 6, LBM_DOT, MENU_COLOR); - } - - // RSSI - const uint8_t rssiBarsValue[] = {30, 40, 50, 60, 80}; - 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); - } - -#if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) - if (isModuleXJT(INTERNAL_MODULE) && isExternalAntennaEnabled()) { - dc->drawBitmapPattern(LCD_W-94, 4, LBM_TOPMENU_ANTENNA, MENU_COLOR); + switch (event) { + case EVT_KEY_BREAK(KEY_EXIT): + killEvents(event); + deleteLater(); + break; } +} #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); - else if (requiredSpeakerVolume < 7) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_1, MENU_COLOR); - else if (requiredSpeakerVolume < 13) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_2, MENU_COLOR); - else if (requiredSpeakerVolume < 19) - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_3, MENU_COLOR); - else - dc->drawBitmapPattern(LCD_W - 130, 4, LBM_TOPMENU_VOLUME_4, MENU_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); - } - else { - dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_COLOR); - } -#else - dc->drawBitmapPattern(LCD_W - 130, 25, LBM_TOPMENU_TXBATT, MENU_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); - } - -#if 0 - // Radio battery - TODO - // drawValueWithUnit(370, 8, g_vbat100mV, UNIT_VOLTS, PREC1|FONT(XS)|MENU_COLOR); - // lcdDrawSolidRect(300, 3, 20, 50, MENU_COLOR); - // lcdDrawRect(batt_icon_x+FW, BAR_Y+1, 13, 7); - // lcdDrawSolidVerticalLine(batt_icon_x+FW+13, BAR_Y+2, 5); - - // Rx battery - if (g_model.voltsSource) { - TelemetryItem & item = telemetryItems[g_model.voltsSource-1]; - if (item.isAvailable()) { - int32_t value = item.value; - TelemetrySensor & sensor = g_model.telemetrySensors[g_model.frsky.altitudeSource-1]; - LcdFlags att = 0; - if (sensor.prec == 2) { - att |= PREC1; - value /= 10; - } - else if (sensor.prec == 1) { - att |= PREC1; - } - att |= (item.isOld() ? ALARM_COLOR : DEFAULT_COLOR); - lcdDrawSolidFilledRect(ALTITUDE_X, VOLTS_Y, ALTITUDE_W, ALTITUDE_H, DEFAULT_BGCOLOR); - lcdDrawText(ALTITUDE_X+PADDING, VOLTS_Y+2, "Voltage", att); - drawValueWithUnit(ALTITUDE_X+PADDING, VOLTS_Y+12, value, UNIT_VOLTS, FONT(XL)|LEFT|att); - } - } - - // Model altitude - if (g_model.frsky.altitudeSource) { - TelemetryItem & item = telemetryItems[g_model.frsky.altitudeSource-1]; - if (item.isAvailable()) { - int32_t value = item.value; - TelemetrySensor & sensor = g_model.telemetrySensors[g_model.frsky.altitudeSource-1]; - if (sensor.prec) value /= sensor.prec == 2 ? 100 : 10; - LcdFlags att = (item.isOld() ? ALARM_COLOR : DEFAULT_COLOR); - lcdDrawSolidFilledRect(ALTITUDE_X, ALTITUDE_Y, ALTITUDE_W, ALTITUDE_H, DEFAULT_BGCOLOR); - lcdDrawText(ALTITUDE_X+PADDING, ALTITUDE_Y+2, "Alt", att); - drawValueWithUnit(ALTITUDE_X+PADDING, ALTITUDE_Y+12, value, UNIT_METERS, FONT(XL)|LEFT|att); - } - } -#endif -} diff --git a/radio/src/gui/colorlcd/topbar.h b/radio/src/gui/colorlcd/topbar.h index 7a566dd0f..d7b519b43 100644 --- a/radio/src/gui/colorlcd/topbar.h +++ b/radio/src/gui/colorlcd/topbar.h @@ -18,32 +18,48 @@ * GNU General Public License for more details. */ -#ifndef _TOPBAR_H_ -#define _TOPBAR_H_ +#pragma once -#include "widgets_container.h" +#include "layout.h" -#define MAX_TOPBAR_ZONES 4 -#define MAX_TOPBAR_OPTIONS 1 // just because of VC++ which doesn't like 0-size arrays :( -#define TOPBAR_ZONE_WIDTH 70 +constexpr coord_t TOPBAR_ZONE_WIDTH = 70; constexpr coord_t TOPBAR_ZONE_MARGIN = 3; constexpr coord_t TOPBAR_HEIGHT = MENU_HEADER_HEIGHT - 2 * TOPBAR_ZONE_MARGIN; -class TopBar: public WidgetsContainer +class ScreenMenu; +class TopbarImpl; + +class Topbar { public: - explicit TopBar(Window * parent); - - unsigned int getZonesCount() const override; - - rect_t getZone(unsigned int index) const override; - - void paint(BitmapBuffer * dc) override; - - void checkEvents() override - { - invalidate(); - } + virtual void load() = 0; }; -#endif // _TOPBAR_H_ +class TopbarFactory +{ +public: + static Topbar * create(Window * parent); +}; + +class SetupTopBarWidgetsPage: public FormWindow +{ + public: + explicit SetupTopBarWidgetsPage(ScreenMenu* menu); + +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "SetupTopBarWidgetsPage"; + } +#endif + + void deleteLater(bool detach = true, bool trash = true) override; + +#if defined(HARDWARE_KEYS) + void onEvent(event_t event) override; +#endif + + protected: + ScreenMenu* menu; + unsigned savedView = 0; +}; diff --git a/radio/src/gui/colorlcd/view_logical_switches.cpp b/radio/src/gui/colorlcd/view_logical_switches.cpp index 233e6439b..d8c5afc3e 100644 --- a/radio/src/gui/colorlcd/view_logical_switches.cpp +++ b/radio/src/gui/colorlcd/view_logical_switches.cpp @@ -20,6 +20,7 @@ #include "opentx.h" #include "view_logical_switches.h" +#include "gridlayout.h" #include diff --git a/radio/src/gui/colorlcd/view_logical_switches.h b/radio/src/gui/colorlcd/view_logical_switches.h index 1f2750288..f7aab6ecf 100644 --- a/radio/src/gui/colorlcd/view_logical_switches.h +++ b/radio/src/gui/colorlcd/view_logical_switches.h @@ -21,6 +21,7 @@ #pragma once #include "opentx.h" +#include "tabsgroup.h" constexpr coord_t CSW_1ST_COLUMN = 50; constexpr coord_t CSW_2ND_COLUMN = 120; diff --git a/radio/src/gui/colorlcd/view_main.cpp b/radio/src/gui/colorlcd/view_main.cpp index a8c817951..bbd6d2420 100644 --- a/radio/src/gui/colorlcd/view_main.cpp +++ b/radio/src/gui/colorlcd/view_main.cpp @@ -22,46 +22,20 @@ #include "menu_model.h" #include "menu_radio.h" #include "menu_screen.h" -// #include "menu_screens.h" #include "model_select.h" #include "view_channels.h" #include "view_statistics.h" +#include "topbar_impl.h" +#include "menu.h" + #include "opentx.h" -Layout * customScreens[MAX_CUSTOM_SCREENS] = {}; - -void onMainViewMenu(const char *result) -{ - /*if (result == STR_VIEW_NOTES) { - pushModelNotes(); - } - else if (result == STR_STATISTICS) { - pushMenu(menuTabStats[0]); - } - else if (result == STR_ABOUT_US) { - chainMenu(menuAboutView); - } - else if (result == STR_MONITOR_SCREENS) { - pushMenu(menuTabMonitors[lastMonitorPage]); - }*/ -} - -int getMainViewsCount() -{ - for (int index=1; indexgetRect()), + topbar(dynamic_cast(TopbarFactory::create(this))) { - #if defined(HARDWARE_TOUCH) && !defined(HARDWARE_KEYS) new FabButton(this, 50, 100, ICON_MODEL, [=]() -> uint8_t { @@ -88,6 +62,7 @@ ViewMain::ViewMain(): }, NO_FOCUS); #endif + setPageWidth(getParent()->width()); focusWindow = this; } @@ -95,6 +70,196 @@ ViewMain::~ViewMain() { } +void ViewMain::setTopbarVisible(float visible) +{ + topbar->setVisible(visible); +} + +unsigned ViewMain::getMainViewsCount() const +{ + return views; +} + +void ViewMain::setMainViewsCount(unsigned views) +{ + if (views > MAX_CUSTOM_SCREENS) + views = MAX_CUSTOM_SCREENS; + + // update number of views + this->views = views; + + // adjust current screen if needed + if (g_model.view >= views) { + setCurrentMainView(views - 1); + } + + setInnerWidth(getParent()->width() * views); +} + +coord_t ViewMain::getMainViewLeftPos(unsigned view) const +{ + return getParent()->width() * view; +} + +rect_t ViewMain::getMainZone(rect_t zone, bool hasTopbar) const +{ + auto visibleHeight = topbar->getVisibleHeight(hasTopbar ? 1.0 : 0.0); + zone.y += visibleHeight; + zone.h -= visibleHeight; + + return zone; +} + +unsigned ViewMain::getCurrentMainView() const +{ + return g_model.view; +} + +void ViewMain::setCurrentMainView(unsigned view) +{ + if (view < getMainViewsCount()) { + setScrollPositionX(view * pageWidth); + TRACE("### switched to view #%i", g_model.view); + } +} + +void ViewMain::nextMainView() +{ + auto view = getCurrentMainView(); + if (++view >= getMainViewsCount()) + view = 0; + + setCurrentMainView(view); +} + +void ViewMain::previousMainView() +{ + auto view = getCurrentMainView(); + if (view > 0) + view--; + else + view = getMainViewsCount() - 1; + + setCurrentMainView(view); +} + +Topbar* ViewMain::getTopbar() +{ + return topbar; +} + +static bool hasTopbar(unsigned view) +{ + if (view < sizeof(g_model.screenData)) { + const auto& layoutData = g_model.screenData[view].layoutData; + return layoutData.options[LAYOUT_OPTION_TOPBAR].value.boolValue; + } + + return false; +} + +void ViewMain::updateTopbarVisibility() +{ + // relative to left visible page + int leftScroll = getScrollPositionX() % pageWidth; + if (leftScroll == 0) { + setTopbarVisible(hasTopbar(g_model.view)); + customScreens[g_model.view]->adjustLayout(); + } + else { + int leftIdx = getScrollPositionX() / pageWidth; + bool leftTopbar = hasTopbar(leftIdx); + bool rightTopbar = hasTopbar(leftIdx+1); + + if (leftTopbar != rightTopbar) { + + float ratio = (float)leftScroll / (float)pageWidth; + + if (leftTopbar) { + // scrolling from a screen with Topbar + ratio = 1.0 - ratio; + } + else { + // scrolling to a screen with Topbar + // -> ratio is ok + } + + setTopbarVisible(ratio); + customScreens[leftIdx]->adjustLayout(); + customScreens[leftIdx+1]->adjustLayout(); + } + } +} + +void ViewMain::setScrollPositionX(coord_t value) +{ + Window::setScrollPositionX(value); + topbar->setLeft(getScrollPositionX()); + + // update page index + g_model.view = getPageIndex(); + + // update Topbar + updateTopbarVisibility(); +} + +void ViewMain::setScrollPositionY(coord_t value) +{ + // this one is not used yet, but could + // usefull to place the screens upwards + Window::setScrollPositionY(value); + topbar->setTop(getScrollPositionY()); +} + +#if defined(HARDWARE_TOUCH) + +//#define DEBUG_SLIDE + +bool ViewMain::onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) +{ + // prevent screen sliding over the next one (one screen at a time) + if (slidingWindow == this) { + if (prevSlideState != touchState.event) { + if (touchState.event == TE_SLIDE_END) { + startSlidePage = getCurrentMainView(); +#if defined(DEBUG_SLIDE) + TRACE("### startSlidePage = %d ###", startSlidePage); +#endif + } + prevSlideState = touchState.event; + } + else if (prevSlideState == TE_SLIDE_END){ + + // let's check what would be the next position + auto nextPos = (getScrollPositionX() - slideX); + + // if we would get over more than one page counting from slide-start + if (abs(nextPos - getMainViewLeftPos(startSlidePage)) > pageWidth) { + +#if defined(DEBUG_SLIDE) + TRACE("### kill the move ###"); +#endif + // kill the movement and let "snap-to-view" finish the job + prevSlideState = TE_NONE; + touchState.event = TE_NONE; + touchState.lastDeltaX = 0; + touchState.lastDeltaY = 0; + return true; + } + } + } + +#if defined(DEBUG_SLIDE) + TRACE("%sWindow[scrollX=%d, scrollY=%d]->onTouchSlide[x=%d, y=%d, startX=%d, startY=%d, slideX=%d, slideY=%d]", + touchState.event == TE_SLIDE_END && slidingWindow ? "###" : "", + getScrollPositionX(), getScrollPositionY(), + x, y, startX, startY, slideX, slideY); +#endif + + return Window::onTouchSlide(x, y, startX, startY, slideX, slideY); +} +#endif + #if defined(HARDWARE_KEYS) void ViewMain::onEvent(event_t event) { @@ -118,6 +283,22 @@ void ViewMain::onEvent(event_t event) killEvents(event); openMenu(); break; + + case EVT_KEY_BREAK(KEY_PGDN): + killEvents(event); + nextMainView(); + break; + +//TODO: these need to go away! +// -> board code should map the keys as required +#if defined(KEYS_GPIO_REG_UP) + case EVT_KEY_BREAK(KEY_PGUP): +#else + case EVT_KEY_LONG(KEY_PGDN): +#endif + killEvents(event); + previousMainView(); + break; } } #endif @@ -164,32 +345,12 @@ void ViewMain::openMenu() void ViewMain::paint(BitmapBuffer * dc) { + TRACE_WINDOWS("### ViewMain::paint(offset_x=%d;offset_y=%d) ###", + dc->getOffsetX(), dc->getOffsetY()); + OpenTxTheme::instance()->drawBackground(dc); if (g_model.view >= getMainViewsCount()) { g_model.view = 0; } -// -// for (uint8_t i=0; irefresh(); } -// else -// customScreens[i]->background(); -// } -// } } - -#if 0 -bool menuMainViewChannelsMonitor(event_t event) -{ - switch (event) { - case EVT_KEY_BREAK(KEY_EXIT): - chainMenu(menuMainView); - event = 0; - return false; - } - - return menuChannelsView(event); -} -#endif diff --git a/radio/src/gui/colorlcd/view_main.h b/radio/src/gui/colorlcd/view_main.h index 69243fa48..3b57f611d 100644 --- a/radio/src/gui/colorlcd/view_main.h +++ b/radio/src/gui/colorlcd/view_main.h @@ -21,9 +21,15 @@ #ifndef _VIEW_MAIN_H_ #define _VIEW_MAIN_H_ +#include #include "form.h" +#include "topbar.h" -class ViewMain: public FormWindow + +class SetupWidgetsPage; +class SetupTopBarWidgetsPage; + +class ViewMain: public Window { // singleton explicit ViewMain(); @@ -46,13 +52,53 @@ class ViewMain: public FormWindow } #endif + void updateTopbarVisibility(); + + // Get the available space in the middle of the screen + // (without topbar) + rect_t getMainZone(rect_t zone, bool hasTopbar) const; + + unsigned getMainViewsCount() const; + void setMainViewsCount(unsigned views); + + coord_t getMainViewLeftPos(unsigned view) const; + + unsigned getCurrentMainView() const; + void setCurrentMainView(unsigned view); + + void nextMainView(); + void previousMainView(); + + Topbar* getTopbar(); + protected: static ViewMain * _instance; + unsigned views = 0; + TopbarImpl* topbar = nullptr; + + // Widget setup requires special permissions ;-) + friend class SetupWidgetsPage; + friend class SetupTopBarWidgetsPage; + + // Set topbar visibility [0.0 -> 1.0] + void setTopbarVisible(float visible); + + void setScrollPositionX(coord_t value) override; + void setScrollPositionY(coord_t value) override; + +#if defined(HARDWARE_TOUCH) + unsigned char prevSlideState = 0; + unsigned int startSlidePage = 0; + + bool onTouchSlide(coord_t x, coord_t y, coord_t startX, coord_t startY, coord_t slideX, coord_t slideY) override; +#endif + #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; #endif void paint(BitmapBuffer * dc) override; + void openMenu(); }; diff --git a/radio/src/gui/colorlcd/view_main_decoration.cpp b/radio/src/gui/colorlcd/view_main_decoration.cpp new file mode 100644 index 000000000..0041acb8c --- /dev/null +++ b/radio/src/gui/colorlcd/view_main_decoration.cpp @@ -0,0 +1,298 @@ +/* + * 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 "view_main_decoration.h" +#include "layout.h" + +// because of IS_POT_MULTIPOS() and pal +#include "opentx.h" + +// these 2 need things from opentx.h +#include "layouts/sliders.h" +#include "layouts/trims.h" + +#include "board.h" + +void ViewMainDecoration::setSlidersVisible(bool visible) +{ + // + // Horizontal Sliders + // + sliders[SLIDERS_POT1]->setHeight(visible ? TRIM_SQUARE_SIZE : 0); + + if (IS_POT_MULTIPOS(POT2)) { + sliders[SLIDERS_POT2]->setHeight(visible ? MULTIPOS_H : 0); + } + else if (IS_POT(POT2)) { + sliders[SLIDERS_POT2]->setHeight(visible ? TRIM_SQUARE_SIZE : 0); + } + +#if defined(HARDWARE_POT3) + sliders[SLIDERS_POT3]->setHeight(visible ? TRIM_SQUARE_SIZE : 0); +#endif + + // + // Vertical sliders + // + sliders[SLIDERS_REAR_LEFT]->setWidth(visible ? TRIM_SQUARE_SIZE : 0); + +#if defined(HARDWARE_EXT1) + if (IS_POT_SLIDER_AVAILABLE(EXT1)) { + sliders[SLIDERS_EXT1]->setWidth(visible ? TRIM_SQUARE_SIZE : 0); + } +#endif + + sliders[SLIDERS_REAR_RIGHT]->setWidth(visible ? TRIM_SQUARE_SIZE : 0); + +#if defined(HARDWARE_EXT2) + if (IS_POT_SLIDER_AVAILABLE(EXT2)) { + sliders[SLIDERS_EXT2]->setWidth(visible ? TRIM_SQUARE_SIZE : 0); + } +#endif +} + +void ViewMainDecoration::setTrimsVisible(bool visible) +{ + trims[TRIMS_LH]->setHeight(visible ? TRIM_SQUARE_SIZE : 0); + trims[TRIMS_RH]->setHeight(visible ? TRIM_SQUARE_SIZE : 0); + + trims[TRIMS_LV]->setWidth(visible ? TRIM_SQUARE_SIZE : 0); + trims[TRIMS_RV]->setWidth(visible ? TRIM_SQUARE_SIZE : 0); +} + +void ViewMainDecoration::setFlightModeVisible(bool visible) +{ + flightMode->setHeight(visible ? FM_LABEL_HEIGHT : 0); +} + +void ViewMainDecoration::adjustDecoration() +{ + // Sliders are closer to the edge and must be computed first + + // These are located on the bottom + auto pos = bottom() - sliders[SLIDERS_POT1]->height(); + sliders[SLIDERS_POT1]->setTop(pos); + + if (sliders[SLIDERS_POT2]) { + auto sl = sliders[SLIDERS_POT2]; + sl->setTop(pos); + if (IS_POT_MULTIPOS(POT2)) { + sl->setWidth(MULTIPOS_W); + } + else { // if !IS_POT(POT2) -> sliders[SLIDERS_POT2] == nullptr + sl->setWidth(HORIZONTAL_SLIDERS_WIDTH); + } + sl->setLeft((width() - sl->width()) / 2); + } + +#if defined(HARDWARE_POT3) + sliders[SLIDERS_POT3]->setTop(pos); +#endif + + // Horizontal trims are on top of horizontal sliders + pos -= trims[TRIMS_LH]->height(); + trims[TRIMS_LH]->setTop(pos); + trims[TRIMS_RH]->setTop(pos); + + // Vertical trims/slider are on top of horizontal sliders with a small margin + auto vertTop = pos - HMARGIN - VERTICAL_SLIDERS_HEIGHT; + + // Left side (vertical) + pos = left(); + sliders[SLIDERS_REAR_LEFT]->setLeft(pos); + sliders[SLIDERS_REAR_LEFT]->setTop(vertTop); + +#if defined(HARDWARE_EXT1) + sliders[SLIDERS_EXT1]->setLeft(pos); + if (IS_POT_SLIDER_AVAILABLE(EXT1)) { + auto rl = sliders[SLIDERS_REAR_LEFT]; + auto e1 = sliders[SLIDERS_EXT1]; + + // If EXT1 is configured as a slider, + // place it bellow rear-left slider (and make them smaller) + rl->setHeight(VERTICAL_SLIDERS_HEIGHT / 2); + e1->setTop(vertTop + rl->height() + HMARGIN/2); + e1->setHeight(rl->height()); + } + else { + auto rl = sliders[SLIDERS_REAR_LEFT]; + auto e1 = sliders[SLIDERS_EXT1]; + + // Otherwise hide the extra slider and make rear-left fullsize + rl->setHeight(VERTICAL_SLIDERS_HEIGHT); + e1->setHeight(0); + } +#endif + + // Right side (vertical) + pos = right() - sliders[SLIDERS_REAR_RIGHT]->width(); + sliders[SLIDERS_REAR_RIGHT]->setLeft(pos); + sliders[SLIDERS_REAR_RIGHT]->setTop(vertTop); + +#if defined(HARDWARE_EXT2) + sliders[SLIDERS_EXT2]->setLeft(pos); + if (IS_POT_SLIDER_AVAILABLE(EXT2)) { + auto rr = sliders[SLIDERS_REAR_RIGHT]; + auto e2 = sliders[SLIDERS_EXT2]; + + // If EXT2 is configured as a slider, + // place it bellow rear-left slider (and make them smaller) + rr->setHeight(VERTICAL_SLIDERS_HEIGHT / 2); + e2->setTop(vertTop + rr->height() + HMARGIN/2); + e2->setHeight(rr->height()); + } + else { + auto rr = sliders[SLIDERS_REAR_RIGHT]; + auto e2 = sliders[SLIDERS_EXT2]; + + // Otherwise hide the extra slider and make rear-left fullsize + rr->setHeight(VERTICAL_SLIDERS_HEIGHT); + e2->setHeight(0); + } +#endif + + // Finally place the vertical trims further from the edge + trims[TRIMS_LV]->setLeft(sliders[SLIDERS_REAR_LEFT]->right()); + trims[TRIMS_LV]->setTop(vertTop); + trims[TRIMS_RV]->setLeft(sliders[SLIDERS_REAR_RIGHT]->left() - trims[TRIMS_RV]->width()); + trims[TRIMS_RV]->setTop(vertTop); + + // Place the flight-mode text box + // -> between horiz trims (if existing) + // else on top of horiz sliders + // + pos = trims[TRIMS_LH]->right(); + flightMode->setLeft(pos); + + pos = trims[TRIMS_RH]->left() - pos; + flightMode->setWidth(pos); + + pos = sliders[SLIDERS_POT1]->top(); + pos -= flightMode->height(); + flightMode->setTop(pos); +} + +rect_t ViewMainDecoration::getMainZone() const +{ + rect_t zone = { + trims[TRIMS_LV]->right() + MAIN_ZONE_BORDER, + /*topbar->bottom() +*/ MAIN_ZONE_BORDER, + 0, 0 // let's compute them! + }; + + zone.w = trims[TRIMS_RV]->left() - MAIN_ZONE_BORDER - zone.x; + + // min(trims[TRIMS_LH]->top(), flightMode->top()) + if (trims[TRIMS_LH]->top() < flightMode->top()) + zone.h = trims[TRIMS_LH]->top(); + else + zone.h = flightMode->top(); + + zone.h -= MAIN_ZONE_BORDER + zone.y; + + return zone; +} + +void ViewMainDecoration::createSliders() +{ + // fixed size array, so that works + memset(sliders, 0, sizeof(sliders)); + + rect_t r = { + // left + HMARGIN, 0, + HORIZONTAL_SLIDERS_WIDTH, 0 + }; + + sliders[SLIDERS_POT1] = new MainViewHorizontalSlider(this, r, CALIBRATED_POT1); + + r = rect_t { 0, 0, 0, 0 }; + if (IS_POT_MULTIPOS(POT2)) { + sliders[SLIDERS_POT2] = new MainView6POS(this, r, 1); + } + else if (IS_POT(POT2)) { + sliders[SLIDERS_POT2] = new MainViewHorizontalSlider(this, r, CALIBRATED_POT2); + } + +#if defined(HARDWARE_POT3) + r = rect_t { + // right + width() - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, 0, + HORIZONTAL_SLIDERS_WIDTH, 0 + }; + + sliders[SLIDERS_POT3] = new MainViewHorizontalSlider(this, r, CALIBRATED_POT3); +#endif + + r = rect_t { 0, 0, 0, 0 }; + sliders[SLIDERS_REAR_LEFT] = new MainViewVerticalSlider(this, r, CALIBRATED_SLIDER_REAR_LEFT); + sliders[SLIDERS_REAR_RIGHT] = new MainViewVerticalSlider(this, r, CALIBRATED_SLIDER_REAR_RIGHT); + +#if defined(HARDWARE_EXT1) + sliders[SLIDERS_EXT1] = new MainViewVerticalSlider(this, r, CALIBRATED_POT_EXT1); +#endif + +#if defined(HARDWARE_EXT2) + sliders[SLIDERS_EXT2] = new MainViewVerticalSlider(this, r, CALIBRATED_POT_EXT2); +#endif +} + +void ViewMainDecoration::createTrims() +{ + // Trim order TRIM_LH, TRIM_LV, TRIM_RV, TRIM_RH + + rect_t r = { + left() + HMARGIN, 0, + HORIZONTAL_SLIDERS_WIDTH, 0 + }; + + trims[TRIMS_LH] = new MainViewHorizontalTrim(this, r, 0); + + r = rect_t { + right() - HORIZONTAL_SLIDERS_WIDTH - HMARGIN, 0, + HORIZONTAL_SLIDERS_WIDTH, 0 + }; + + trims[TRIMS_RH] = new MainViewHorizontalTrim(this, r, 3); + + r = rect_t { + 0, 0, 0, + VERTICAL_SLIDERS_HEIGHT + }; + + trims[TRIMS_LV] = new MainViewVerticalTrim(this, r, 2); + trims[TRIMS_RV] = new MainViewVerticalTrim(this, r, 1); +} + +void ViewMainDecoration::createFlightMode() +{ + rect_t r = { + // centered text box (50 pixels from either edge) + // -> re-size once the other components are set + 50, 0, width() - 100, 0 + }; + + std::function getFM = []() -> std::string { + return g_model.flightModeData[mixerCurrentFlightMode].name; + }; + + flightMode = new DynamicText(this, r, getFM, CENTERED); +} + diff --git a/radio/src/gui/colorlcd/view_main_decoration.h b/radio/src/gui/colorlcd/view_main_decoration.h new file mode 100644 index 000000000..d0e951d24 --- /dev/null +++ b/radio/src/gui/colorlcd/view_main_decoration.h @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#pragma once + +#include "window.h" + +constexpr coord_t FM_LABEL_HEIGHT = 20; + +class ViewMainDecoration: public Window +{ + public: + ViewMainDecoration(Window * parent, const rect_t & rect): + Window(parent, rect) + { + createSliders(); + createTrims(); + createFlightMode(); + } + + // Set decoration visibility + void setTrimsVisible(bool visible); + void setSlidersVisible(bool visible); + void setFlightModeVisible(bool visible); + + // Re-calculate positions + void adjustDecoration(); + + // Get the available space in the middle of the screen + // (without decoration) + rect_t getMainZone() const; + + protected: + +#if defined(DEBUG_WINDOWS) + virtual std::string getName() const + { + return "ViewMainDecoration"; + } +#endif + + enum { + SLIDERS_POT1 = 0, + SLIDERS_POT2, + SLIDERS_POT3, + SLIDERS_REAR_LEFT, + SLIDERS_EXT1, + SLIDERS_REAR_RIGHT, + SLIDERS_EXT2, + SLIDERS_MAX + }; + + enum { + TRIMS_LH = 0, + TRIMS_LV, + TRIMS_RV, + TRIMS_RH, + TRIMS_MAX + }; + + Window* sliders[SLIDERS_MAX]; + Window* trims[TRIMS_MAX]; + Window* flightMode; + + void createSliders(); + void createTrims(); + void createFlightMode(); +}; diff --git a/radio/src/gui/colorlcd/view_statistics.h b/radio/src/gui/colorlcd/view_statistics.h index c7568de1b..f9632e628 100644 --- a/radio/src/gui/colorlcd/view_statistics.h +++ b/radio/src/gui/colorlcd/view_statistics.h @@ -23,6 +23,8 @@ #include "opentx.h" #include "tabsgroup.h" #include "static.h" +#include "window.h" +#include "font.h" template class DebugInfoNumber : public Window @@ -34,7 +36,7 @@ class DebugInfoNumber : public Window prefix(prefix), suffix(suffix) { - coord_t prefixSize = getTextWidth(prefix, 0,HEADER_COLOR | FONT(XS)); + coord_t prefixSize = getTextWidth(prefix, 0, HEADER_COLOR | FONT(XS)); new StaticText(this, {0, 0, prefixSize, rect.h}, prefix, 0, HEADER_COLOR | FONT(XS)); new DynamicNumber(this, {prefixSize, 0, rect.w - prefixSize, rect.h}, [] { return menusStack.available(); diff --git a/radio/src/gui/colorlcd/widget.cpp b/radio/src/gui/colorlcd/widget.cpp index a99a0f477..a9c9a1647 100644 --- a/radio/src/gui/colorlcd/widget.cpp +++ b/radio/src/gui/colorlcd/widget.cpp @@ -19,6 +19,7 @@ */ #include "opentx.h" +#include "widget.h" std::list & getRegisteredWidgets() { @@ -34,7 +35,7 @@ void registerWidget(const WidgetFactory * factory) const WidgetFactory * getWidgetFactory(const char * name) { - std::list::const_iterator it = getRegisteredWidgets().cbegin(); + auto it = getRegisteredWidgets().cbegin(); for (; it != getRegisteredWidgets().cend();++it) { if (!strcmp(name, (*it)->getName())) { return (*it); @@ -43,7 +44,7 @@ const WidgetFactory * getWidgetFactory(const char * name) return nullptr; } -Widget * loadWidget(const char * name, FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData) +Widget * loadWidget(const char * name, FormGroup * parent, const rect_t & rect, WidgetPersistentData * persistentData) { const WidgetFactory * factory = getWidgetFactory(name); if (factory) { diff --git a/radio/src/gui/colorlcd/widget.h b/radio/src/gui/colorlcd/widget.h index e1bb59aae..279e662c4 100644 --- a/radio/src/gui/colorlcd/widget.h +++ b/radio/src/gui/colorlcd/widget.h @@ -23,26 +23,24 @@ #include #include #include "form.h" -#include "zone.h" +#include "widgets_container.h" #include "debug.h" -#define MAX_WIDGET_OPTIONS 5 - // YAML_GENERATOR defs #if !defined(USE_IDX) #define USE_IDX #endif class WidgetFactory; -class Widget : public FormField + +class Widget : public Window { public: - struct PersistentData { - ZoneOptionValueTyped options[MAX_WIDGET_OPTIONS] USE_IDX; - }; - Widget(const WidgetFactory * factory, FormGroup * parent, const rect_t & rect, PersistentData * persistentData): - FormField(parent, rect), + typedef WidgetPersistentData PersistentData; + + Widget(const WidgetFactory * factory, FormGroup * parent, const rect_t & rect, WidgetPersistentData * persistentData): + Window(parent, rect), factory(factory), persistentData(persistentData) { @@ -71,25 +69,30 @@ class Widget : public FormField return &persistentData->options[index].value; } - inline void setOptionValue(unsigned int index, const ZoneOptionValue& value) - { - persistentData->options[index].value = value; - } + // + // TODO: for some reason, this one crashes on the radio... + // + // inline void setOptionValue(unsigned int index, const ZoneOptionValue& value) + // { + // persistentData->options[index].value = value; + // } inline PersistentData * getPersistentData() { return persistentData; } - void paint(BitmapBuffer * dc) override - { - FormField::paint(dc); - } - virtual void background() { } +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "Widget"; + } +#endif + protected: const WidgetFactory * factory; PersistentData * persistentData; @@ -122,11 +125,11 @@ class WidgetFactory memset(persistentData, 0, sizeof(Widget::PersistentData)); if (options) { int i = 0; - for (const ZoneOption * option = options; option->name; option++) { + for (const ZoneOption * option = options; option->name; option++, i++) { TRACE("WidgetFactory::initPersistentData() setting option '%s'", option->name); // TODO compiler bug? The CPU freezes ... persistentData->options[i++] = option->deflt; - memcpy(&persistentData->options[i++].value, &option->deflt, sizeof(ZoneOptionValue)); - persistentData->options[i++].type = zoneValueEnumFromType(option->type); + memcpy(&persistentData->options[i].value, &option->deflt, sizeof(ZoneOptionValue)); + persistentData->options[i].type = zoneValueEnumFromType(option->type); } } } @@ -138,25 +141,6 @@ class WidgetFactory const ZoneOption * options; }; -template -class BaseWidgetFactory: public WidgetFactory -{ - public: - BaseWidgetFactory(const char * name, const ZoneOption * options): - WidgetFactory(name, options) - { - } - - Widget * create(FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData, bool init = true) const override - { - if (init) { - initPersistentData(persistentData); - } - - return new T(this, parent, rect, persistentData); - } -}; - inline const ZoneOption * Widget::getOptions() const { return getFactory()->getOptions(); diff --git a/radio/src/gui/colorlcd/widget_settings.cpp b/radio/src/gui/colorlcd/widget_settings.cpp index 510769b3d..39756062b 100644 --- a/radio/src/gui/colorlcd/widget_settings.cpp +++ b/radio/src/gui/colorlcd/widget_settings.cpp @@ -19,7 +19,11 @@ */ #include "opentx.h" + #include "widget_settings.h" +#include "libopenui.h" + +#define SET_DIRTY() storageDirty(EE_MODEL) static const rect_t widgetSettingsDialogRect = { LCD_W / 10, // x @@ -51,50 +55,58 @@ WidgetSettings::WidgetSettings(Window * parent, Widget * widget) : case ZoneOption::Integer: new NumberEdit(form, grid.getFieldSlot(), option.min.signedValue, option.max.signedValue, [=]() -> int { - return widget->getOptionValue(optIdx)->signedValue; + return widget->getOptionValue(optIdx)->signedValue; }, [=](int32_t newValue) { - widget->setOptionValue(optIdx, OPTION_VALUE_SIGNED(newValue)); + widget->getOptionValue(optIdx)->signedValue = newValue; }); break; case ZoneOption::Source: new SourceChoice(form, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, [=]() -> int16_t { - return (int16_t) widget->getOptionValue(optIdx)->unsignedValue; + return (int16_t) widget->getOptionValue(optIdx)->unsignedValue; }, [=](int16_t newValue) { - widget->setOptionValue(optIdx, OPTION_VALUE_UNSIGNED((uint32_t) newValue)); + widget->getOptionValue(optIdx)->unsignedValue = (uint32_t)newValue; }); break; case ZoneOption::Bool: new CheckBox(form, grid.getFieldSlot(), [=]() -> uint8_t { - return (uint8_t) widget->getOptionValue(optIdx)->boolValue; + return (uint8_t) widget->getOptionValue(optIdx)->boolValue; }, [=](int8_t newValue) { - widget->setOptionValue(optIdx, OPTION_VALUE_BOOL((uint32_t) newValue)); + widget->getOptionValue(optIdx)->boolValue = newValue; }); break; case ZoneOption::String: + new ModelTextEdit(form, grid.getFieldSlot(), widget->getOptionValue(optIdx)->stringValue, sizeof( widget->getOptionValue(optIdx)->stringValue)); break; case ZoneOption::File: break; - case ZoneOption::TextSize: - break; - - case ZoneOption::Timer: // Unsigned - { - auto tmChoice = new Choice(form, grid.getFieldSlot(), 0, TIMERS - 1, + case ZoneOption::TextSize: { + new Choice(form, grid.getFieldSlot(), STR_FONT_SIZES, 0, FONTS_COUNT - 1, [=]() -> int { // getValue return (int) widget->getOptionValue(optIdx)->unsignedValue; }, [=](int newValue) { // setValue - widget->setOptionValue(optIdx, OPTION_VALUE_UNSIGNED((uint32_t) newValue)); + widget->getOptionValue(optIdx)->unsignedValue = (uint32_t) newValue; + }); + break; + } + case ZoneOption::Timer: // Unsigned + { + auto tmChoice = new Choice(form, grid.getFieldSlot(), 0, TIMERS - 1, + [=]() -> int { // getValue + return (int) widget->getOptionValue(optIdx)->unsignedValue; + }, + [=](int newValue) { // setValue + widget->getOptionValue(optIdx)->unsignedValue = (uint32_t) newValue; }); tmChoice->setTextHandler([](int value) { @@ -108,19 +120,22 @@ WidgetSettings::WidgetSettings(Window * parent, Widget * widget) : option.min.unsignedValue, // min option.max.unsignedValue, // max [=]() -> int16_t { // getValue - return (uint8_t) widget->getOptionValue(optIdx)->unsignedValue; + return (uint8_t) widget->getOptionValue(optIdx)->unsignedValue; }, [=](int16_t newValue) { // setValue - widget->setOptionValue(optIdx, OPTION_VALUE_UNSIGNED((uint32_t) newValue)); + widget->getOptionValue(optIdx)->unsignedValue = (uint32_t) newValue; }); break; case ZoneOption::Color: - new ColorEdit(form, grid.getFieldSlot(), [=]() -> int { // getValue - return (int) widget->getOptionValue(optIdx)->unsignedValue; + for (uint8_t part = 0; part < 3; part++) + new ColorEdit(form, grid.getFieldSlot(), + [=]() -> int { // getValue + return (int) widget->getOptionValue(optIdx)->unsignedValue; }, [=](int newValue) { // setValue - widget->setOptionValue(optIdx, OPTION_VALUE_UNSIGNED((uint32_t) newValue)); + widget->getOptionValue(optIdx)->unsignedValue = (uint32_t) newValue; + SET_DIRTY(); }); break; } diff --git a/radio/src/gui/colorlcd/widgets/gauge.cpp b/radio/src/gui/colorlcd/widgets/gauge.cpp index f69978467..3f01aa6f3 100644 --- a/radio/src/gui/colorlcd/widgets/gauge.cpp +++ b/radio/src/gui/colorlcd/widgets/gauge.cpp @@ -19,6 +19,7 @@ */ #include "opentx.h" +#include "widgets_container_impl.h" class GaugeWidget: public Widget { diff --git a/radio/src/gui/colorlcd/widgets/modelbmp.cpp b/radio/src/gui/colorlcd/widgets/modelbmp.cpp index fbcffd95c..e6f67dbbd 100644 --- a/radio/src/gui/colorlcd/widgets/modelbmp.cpp +++ b/radio/src/gui/colorlcd/widgets/modelbmp.cpp @@ -19,6 +19,8 @@ */ #include "opentx.h" +#include "widgets_container_impl.h" + #include class ModelBitmapWidget: public Widget diff --git a/radio/src/gui/colorlcd/widgets/outputs.cpp b/radio/src/gui/colorlcd/widgets/outputs.cpp index 38c6d240a..8f3f11e42 100644 --- a/radio/src/gui/colorlcd/widgets/outputs.cpp +++ b/radio/src/gui/colorlcd/widgets/outputs.cpp @@ -19,12 +19,16 @@ */ #include "opentx.h" +#include "widgets_container_impl.h" #define RECT_BORDER 1 #define ROW_HEIGHT 17 #define VIEW_CHANNELS_LIMIT_PCT (g_model.extendedLimits ? LIMIT_EXT_PERCENT : 100) +constexpr uint32_t OUTPUTS_REFRESH = 1000 / 5; // 5 Hz + + class OutputsWidget: public Widget { public: @@ -100,11 +104,23 @@ class OutputsWidget: public Widget void checkEvents() override { Widget::checkEvents(); + + // Last time we refreshed the window + uint32_t now = RTOS_GET_MS(); + if (now - lastRefresh >= OUTPUTS_REFRESH) { + lastRefresh = now; + invalidate(); + } + invalidate(); } static const ZoneOption options[]; + + protected: + // Last time we refreshed the window + uint32_t lastRefresh = 0; }; const ZoneOption OutputsWidget::options[] = { diff --git a/radio/src/gui/colorlcd/widgets/text.cpp b/radio/src/gui/colorlcd/widgets/text.cpp index 7fecda88a..701dfef84 100644 --- a/radio/src/gui/colorlcd/widgets/text.cpp +++ b/radio/src/gui/colorlcd/widgets/text.cpp @@ -19,6 +19,7 @@ */ #include "opentx.h" +#include "widgets_container_impl.h" #define TEXT_WIDGET_DEFAULT_LABEL 'M', 'y', ' ', 'L', 'a', 'b', 'e', 'l' // "My Label" @@ -32,9 +33,6 @@ class TextWidget: public Widget void paint(BitmapBuffer * dc) override { - // clear the background - dc->clear(DEFAULT_BGCOLOR); - // get font color from options[1] lcdSetColor(persistentData->options[1].value.unsignedValue); diff --git a/radio/src/gui/colorlcd/widgets/timer.cpp b/radio/src/gui/colorlcd/widgets/timer.cpp index ffc0f850e..45336252d 100644 --- a/radio/src/gui/colorlcd/widgets/timer.cpp +++ b/radio/src/gui/colorlcd/widgets/timer.cpp @@ -19,6 +19,7 @@ */ #include "opentx.h" +#include "widgets_container_impl.h" class TimerWidget: public Widget { diff --git a/radio/src/gui/colorlcd/widgets/value.cpp b/radio/src/gui/colorlcd/widgets/value.cpp index 99312d125..2556a310f 100644 --- a/radio/src/gui/colorlcd/widgets/value.cpp +++ b/radio/src/gui/colorlcd/widgets/value.cpp @@ -19,6 +19,7 @@ */ #include "opentx.h" +#include "widgets_container_impl.h" const coord_t NUMBERS_PADDING = 4; @@ -32,10 +33,6 @@ class ValueWidget: public Widget void paint(BitmapBuffer * dc) override { - // draw the background and border - dc->clear(DEFAULT_BGCOLOR); - dc->drawFilledRect(0,0, width(), height(), SOLID, MAINVIEW_PANES_COLOR | OPACITY(5)); - // get source from options[0] mixsrc_t field = persistentData->options[0].value.unsignedValue; @@ -50,8 +47,8 @@ class ValueWidget: public Widget yValue = 14; xLabel = 0; yLabel = 0; - attrValue = LEFT | NO_UNIT | FONT(XS); - attrLabel = FONT(XS); + attrValue = LEFT | NO_UNIT | FONT(L); + attrLabel = LEFT; } else if (height() < 50) { xValue = width() - NUMBERS_PADDING; diff --git a/radio/src/gui/colorlcd/widgets/widgets_container_impl.h b/radio/src/gui/colorlcd/widgets/widgets_container_impl.h new file mode 100644 index 000000000..1966a0e0a --- /dev/null +++ b/radio/src/gui/colorlcd/widgets/widgets_container_impl.h @@ -0,0 +1,166 @@ +/* + * 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. + */ + +#pragma once + +#include "widget.h" +#include "widgets_container.h" + +template +class WidgetsContainerImpl: public WidgetsContainer +{ + public: + typedef WidgetsContainerPersistentData PersistentData; + + WidgetsContainerImpl(const rect_t & rect, PersistentData * persistentData): + WidgetsContainer(nullptr, rect, FORM_FORWARD_FOCUS), + persistentData(persistentData) + { + } + + Widget * createWidget(unsigned int index, const WidgetFactory * factory) override + { + if (index >= N) + return nullptr; + + // remove old one if existing + removeWidget(index); + + 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); + } + widgets[index] = widget; + + if (widget) + widget->attach(this); + + return widget; + } + + void removeWidget(unsigned int index) override + { + if (index >= N) + return; + + if (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)); + } + + Widget * getWidget(unsigned int index) override + { + if (index < N) + return widgets[index]; + + return nullptr; + } + + virtual void create() + { + memset(persistentData, 0, sizeof(PersistentData)); + } + + virtual void load() + { + unsigned int count = getZonesCount(); + for (unsigned int i = 0; i < count; i++) { + + // remove old widget + if (widgets[i]) { + widgets[i]->deleteLater(); + widgets[i] = nullptr; + } + + // and load new one if required + if (persistentData->zones[i].widgetName[0]) { + char name[WIDGET_NAME_LEN + 1]; + memset(name, 0, sizeof(name)); + strncpy(name, persistentData->zones[i].widgetName, WIDGET_NAME_LEN); + widgets[i] = loadWidget(name, this, getZone(i), &persistentData->zones[i].widgetData); + } + } + } + + inline ZoneOptionValue * getOptionValue(unsigned int index) const + { + return &persistentData->options[index].value; + } + + inline void setOptionValue(unsigned int index, const ZoneOptionValue& value) + { + persistentData->options[index].value = value; + } + + unsigned int getZonesCount() const override = 0; + + rect_t getZone(unsigned int index) const override = 0; + + virtual void background() + { + for (int i = 0; i < N; i++) { + if (widgets[i]) { + widgets[i]->background(); + } + } + } + + void updateZones() + { + for (int i = 0; i < N; i++) { + if (widgets[i]) { + auto zone = getZone(i); + widgets[i]->setRect(zone); + widgets[i]->setInnerHeight(zone.h); + } + } + } + + void adjustLayout() override + { + } + + protected: + PersistentData * persistentData; + Widget * widgets[N] = {}; +}; + +template +class BaseWidgetFactory: public WidgetFactory +{ + public: + BaseWidgetFactory(const char * name, const ZoneOption * options): + WidgetFactory(name, options) + { + } + + Widget * create(FormGroup * parent, const rect_t & rect, Widget::PersistentData * persistentData, bool init = true) const override + { + if (init) { + initPersistentData(persistentData); + } + + return new T(this, parent, rect, persistentData); + } +}; diff --git a/radio/src/gui/colorlcd/widgets_container.h b/radio/src/gui/colorlcd/widgets_container.h index 44302ba57..d02237b87 100644 --- a/radio/src/gui/colorlcd/widgets_container.h +++ b/radio/src/gui/colorlcd/widgets_container.h @@ -22,109 +22,60 @@ #define _WIDGETS_CONTAINER_H_ #include -#include -#include "widget.h" +#include "libopenui_types.h" +#include "form.h" +#include "zone.h" -class WidgetsContainerInterface +#define WIDGET_NAME_LEN 10 +#define MAX_WIDGET_OPTIONS 5 // Name? + +#define MAX_TOPBAR_ZONES 4 +#define MAX_TOPBAR_OPTIONS 1 // just because of VC++ which doesn't like 0-size arrays :( + +// Common 'ZoneOptionValue's among all layouts +enum { + LAYOUT_OPTION_TOPBAR = 0, + LAYOUT_OPTION_FM, + LAYOUT_OPTION_SLIDERS, + LAYOUT_OPTION_TRIMS, + LAYOUT_OPTION_MIRRORED, + + LAYOUT_OPTION_LAST_DEFAULT=LAYOUT_OPTION_MIRRORED +}; + +class Widget; +class WidgetFactory; +class LayoutFactory; + +struct WidgetPersistentData { + ZoneOptionValueTyped options[MAX_WIDGET_OPTIONS] USE_IDX; +}; + +struct ZonePersistentData { + char widgetName[WIDGET_NAME_LEN]; + WidgetPersistentData widgetData; +}; + +template +struct WidgetsContainerPersistentData { + ZonePersistentData zones[N]; + ZoneOptionValueTyped options[O]; +}; + +typedef WidgetsContainerPersistentData TopBarPersistentData; + +class WidgetsContainer: public FormGroup { public: + using FormGroup::FormGroup; + virtual unsigned int getZonesCount() const = 0; virtual rect_t getZone(unsigned int index) const = 0; virtual Widget * createWidget(unsigned int index, const WidgetFactory * factory) = 0; virtual Widget * getWidget(unsigned int index) = 0; + virtual void removeWidget(unsigned int index) = 0; + virtual void adjustLayout() = 0; }; -#define WIDGET_NAME_LEN 10 - -template -class WidgetsContainer: public FormGroup, public WidgetsContainerInterface -{ - public: - struct ZonePersistentData { - char widgetName[WIDGET_NAME_LEN]; - Widget::PersistentData widgetData; - }; - - struct PersistentData { - ZonePersistentData zones[N]; - ZoneOptionValueTyped options[O]; - }; - - WidgetsContainer(const rect_t & rect, PersistentData * persistentData): - FormGroup(nullptr, rect, FORM_FORWARD_FOCUS), - persistentData(persistentData) - { - } - - Widget * createWidget(unsigned int index, const WidgetFactory * factory) override - { - Widget * widget = nullptr; - memset(persistentData->zones[index].widgetName, 0, sizeof(persistentData->zones[index].widgetName)); - if (factory) { - strncpy(persistentData->zones[index].widgetName, factory->getName(), sizeof(persistentData->zones[index].widgetName)); - widget = factory->create(this, getZone(index), &persistentData->zones[index].widgetData); - } - widgets[index] = widget; - - return widget; - } - - Widget * getWidget(unsigned int index) override - { - if (index < N) - return widgets[index]; - - return nullptr; - } - - virtual void create() - { - memset(persistentData, 0, sizeof(PersistentData)); - } - - virtual void load() - { - unsigned int count = getZonesCount(); - for (unsigned int i = 0; i < count; i++) { - delete widgets[i]; - if (persistentData->zones[i].widgetName[0]) { - char name[WIDGET_NAME_LEN + 1]; - memset(name, 0, sizeof(name)); - strncpy(name, persistentData->zones[i].widgetName, WIDGET_NAME_LEN); - widgets[i] = loadWidget(name, this, getZone(i), &persistentData->zones[i].widgetData); - } - else { - widgets[i] = nullptr; - } - } - } - - inline ZoneOptionValue * getOptionValue(unsigned int index) const - { - return &persistentData->options[index].value; - } - - inline void setOptionValue(unsigned int index, const ZoneOptionValue& value) - { - persistentData->options[index].value = value; - } - - unsigned int getZonesCount() const override = 0; - - rect_t getZone(unsigned int index) const override = 0; - - virtual void background() - { - for (int i = 0; i < N; i++) { - if (widgets[i]) { - widgets[i]->background(); - } - } - } - - protected: - PersistentData * persistentData; - Widget * widgets[N] = {}; -}; #endif // _WIDGETS_CONTAINER_H_ diff --git a/radio/src/gui/colorlcd/zone.h b/radio/src/gui/colorlcd/zone.h index 26836b063..bd941433e 100644 --- a/radio/src/gui/colorlcd/zone.h +++ b/radio/src/gui/colorlcd/zone.h @@ -13,10 +13,10 @@ #define OPTION_VALUE_BOOL(x) { bool(x) } #define OPTION_VALUE_STRING(...) { *(ZoneOptionValue *)(const char *) #__VA_ARGS__ } #else - #define OPTION_VALUE_UNSIGNED(x) { .unsignedValue = (x) } - #define OPTION_VALUE_SIGNED(x) { .signedValue = (x) } - #define OPTION_VALUE_BOOL(x) { .boolValue = (x) } - #define OPTION_VALUE_STRING(...) { .stringValue = { __VA_ARGS__ } } + #define OPTION_VALUE_UNSIGNED(x) ZoneOptionValue{ .unsignedValue = (x) } + #define OPTION_VALUE_SIGNED(x) ZoneOptionValue{ .signedValue = (x) } + #define OPTION_VALUE_BOOL(x) ZoneOptionValue{ .boolValue = (x) } + #define OPTION_VALUE_STRING(...) ZoneOptionValue{ .stringValue = { __VA_ARGS__ } } #endif union ZoneOptionValue diff --git a/radio/src/io/bootloader_flash.cpp b/radio/src/io/bootloader_flash.cpp index 33a0ab28d..18ae9b272 100644 --- a/radio/src/io/bootloader_flash.cpp +++ b/radio/src/io/bootloader_flash.cpp @@ -21,6 +21,12 @@ #include "opentx.h" #include "bootloader_flash.h" +#if defined(LIBOPENUI) + #include "libopenui.h" +#else + #include "libopenui/src/libopenui_file.h" +#endif + bool isBootloader(const char * filename) { FIL file; diff --git a/radio/src/io/frsky_firmware_update.cpp b/radio/src/io/frsky_firmware_update.cpp index 5d91c0a1e..d46316c98 100644 --- a/radio/src/io/frsky_firmware_update.cpp +++ b/radio/src/io/frsky_firmware_update.cpp @@ -20,7 +20,12 @@ #include "opentx.h" #include "frsky_firmware_update.h" -#include "libopenui/src/libopenui_file.h" + +#if defined(LIBOPENUI) + #include "libopenui.h" +#else + #include "libopenui/src/libopenui_file.h" +#endif #define PRIM_REQ_POWERUP 0 #define PRIM_REQ_VERSION 1 diff --git a/radio/src/io/multi_firmware_update.cpp b/radio/src/io/multi_firmware_update.cpp index bb87a4142..0d8fda028 100644 --- a/radio/src/io/multi_firmware_update.cpp +++ b/radio/src/io/multi_firmware_update.cpp @@ -25,6 +25,12 @@ #include "stk500.h" #include "debug.h" +#if defined(LIBOPENUI) + #include "libopenui.h" +#else + #include "libopenui/src/libopenui_file.h" +#endif + #define UPDATE_MULTI_EXT_BIN ".bin" class MultiFirmwareUpdateDriver diff --git a/radio/src/logs.cpp b/radio/src/logs.cpp index 13f6f4a3a..bd8767d52 100644 --- a/radio/src/logs.cpp +++ b/radio/src/logs.cpp @@ -21,6 +21,10 @@ #include "opentx.h" #include "ff.h" +#if defined(LIBOPENUI) + #include "libopenui.h" +#endif + FIL g_oLogFile __DMA; const char * g_logError = nullptr; uint8_t logDelay; diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index 213421411..5ccde6e08 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -26,6 +26,10 @@ #include "telemetry/frsky.h" #include "telemetry/multi.h" +#if defined(LIBOPENUI) + #include "libopenui.h" +#endif + #if defined(PCBX12S) #include "lua/lua_exports_x12s.inc" // this line must be after lua headers #elif defined(RADIO_FAMILY_T16) diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index 0cc1d48cc..1b06b0168 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -27,7 +27,12 @@ #include "bin_allocator.h" #include "lua_api.h" #include "sdcard.h" -#include "libopenui/src/libopenui_file.h" + +#if defined(LIBOPENUI) + #include "libopenui.h" +#else + #include "libopenui/src/libopenui_file.h" +#endif extern "C" { #include diff --git a/radio/src/main.cpp b/radio/src/main.cpp index a43ddd254..c7b0fd8da 100644 --- a/radio/src/main.cpp +++ b/radio/src/main.cpp @@ -21,7 +21,7 @@ #include "opentx.h" #if defined(LIBOPENUI) - #include "mainwindow.h" + #include "libopenui.h" #endif uint8_t currentSpeakerVolume = 255; diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 4ad05a801..81f842582 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -22,9 +22,10 @@ #include #if defined(LIBOPENUI) -// #include "shutdown_animation.h" -#include "radio_calibration.h" -#include "view_main.h" + #include "libopenui.h" + // #include "shutdown_animation.h" + #include "radio_calibration.h" + #include "view_main.h" #endif #if defined(PCBSKY9X) diff --git a/radio/src/opentx.h b/radio/src/opentx.h index dbbd9eec8..f60c4ddeb 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -27,10 +27,6 @@ #include "opentx_helpers.h" #include "touch.h" -#if defined(LIBOPENUI) -#include "libopenui.h" -#endif - #if defined(SIMU) #include "targets/simu/simpgmspace.h" #endif diff --git a/radio/src/sdcard.cpp b/radio/src/sdcard.cpp index 1719eff2f..be6370a08 100644 --- a/radio/src/sdcard.cpp +++ b/radio/src/sdcard.cpp @@ -21,7 +21,12 @@ #include #include "opentx.h" #include "diskio.h" -#include "libopenui/src/libopenui_file.h" + +#if defined(LIBOPENUI) + #include "libopenui.h" +#else + #include "libopenui/src/libopenui_file.h" +#endif bool sdCardFormat() { diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index ee9c8ae45..adbe4788a 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -383,7 +383,7 @@ void OpenTxSim::updateKeysAndSwitches(bool start) static FXuint trimKeys[] = { KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12 }; #endif - for (unsigned i=0; igetKeyState(trimKeys[i])); } diff --git a/radio/src/storage/conversions/datastructs_218.h b/radio/src/storage/conversions/datastructs_218.h index e21816d40..6a7638feb 100644 --- a/radio/src/storage/conversions/datastructs_218.h +++ b/radio/src/storage/conversions/datastructs_218.h @@ -398,11 +398,11 @@ PACK(struct TelemetrySensor_218 { #if defined(PCBHORUS) PACK(struct CustomScreenData_v218 { char LayoutId[10]; - Layout::PersistentData layoutData; + LayoutPersistentData layoutData; }); #define VIEW_DATA \ CustomScreenData screenData[MAX_CUSTOM_SCREENS]; \ - TopBar::PersistentData topbarData; \ + TopBarPersistentData topbarData; \ uint8_t view; #elif defined(PCBTARANIS) #define VIEW_DATA uint8_t view; diff --git a/radio/src/storage/sdcard_common.cpp b/radio/src/storage/sdcard_common.cpp index 7abdfdded..b25b76082 100644 --- a/radio/src/storage/sdcard_common.cpp +++ b/radio/src/storage/sdcard_common.cpp @@ -60,6 +60,7 @@ void storageFormat() sdCheckAndCreateDirectory(RADIO_PATH); sdCheckAndCreateDirectory(MODELS_PATH); storageCreateModelsList(); + generalDefault(); } void storageCheck(bool immediately) @@ -148,6 +149,19 @@ void storageReadAll() } } + // Current model filename is empty... + // Let's fix it! + if (strlen(g_eeGeneral.currModelFilename) == 0) { + + // sizeof(currModelFilename) == LEN_MODEL_FILENAME + 1 + // make sure it is terminated (see doc for strncpy()) + strncpy(g_eeGeneral.currModelFilename, DEFAULT_MODEL_FILENAME, LEN_MODEL_FILENAME); + g_eeGeneral.currModelFilename[LEN_MODEL_FILENAME] = '\0'; + + storageDirty(EE_GENERAL); + storageCheck(true); + } + if (loadModel(g_eeGeneral.currModelFilename, false) != nullptr) { TRACE("No current model or SD card error"); } diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index b46ea4134..439032e74 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -61,6 +61,10 @@ void preModelLoad() #endif stopTrainer(); + +#if defined(COLORLCD) + deleteCustomScreens(); +#endif } void postRadioSettingsLoad() diff --git a/radio/src/targets/horus/libopenui_config.h b/radio/src/targets/horus/libopenui_config.h index b354635a5..77168ca71 100644 --- a/radio/src/targets/horus/libopenui_config.h +++ b/radio/src/targets/horus/libopenui_config.h @@ -29,7 +29,7 @@ typedef uint16_t pixel_t; constexpr bool WRAP_FORM_FIELDS_WITHIN_PAGE = true; -constexpr uint32_t SLIDE_SPEED_REDUCTION = 5; +constexpr short SLIDE_SPEED_REDUCTION = 5; constexpr uint32_t MENU_HEADER_BUTTON_WIDTH = 33; constexpr uint32_t MENU_HEADER_BUTTONS_LEFT = 47; diff --git a/radio/src/targets/simu/CMakeLists.txt b/radio/src/targets/simu/CMakeLists.txt index 523d38c81..beb25cbc6 100644 --- a/radio/src/targets/simu/CMakeLists.txt +++ b/radio/src/targets/simu/CMakeLists.txt @@ -83,8 +83,8 @@ else() # struct packing breaks on MinGW w/out -mno-ms-bitfields: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52991 & http://stackoverflow.com/questions/24015852/struct-packing-and-alignment-with-mingw set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mno-ms-bitfields") endif() - set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0") - set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") + set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -O0 -fsanitize=address") + set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -fsanitize=address") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${WARNING_FLAGS}") @@ -98,6 +98,7 @@ if(FOX_FOUND) endif() add_executable(simu WIN32 ${SIMU_SRC} ${RADIO_SRC_DIR}/simu.cpp) add_dependencies(simu ${RADIO_DEPENDENCIES}) + target_include_directories(simu PUBLIC ${FOX_INCLUDE_DIR} ) target_link_libraries(simu ${FOX_LIBRARY} pthread ${SDL_LIBRARY}) target_compile_definitions(simu PUBLIC -DSIMU) diff --git a/radio/src/telemetry/frsky_pxx2.cpp b/radio/src/telemetry/frsky_pxx2.cpp index 959d0ee43..51943e6f0 100644 --- a/radio/src/telemetry/frsky_pxx2.cpp +++ b/radio/src/telemetry/frsky_pxx2.cpp @@ -20,6 +20,10 @@ #include "opentx.h" +#if defined(LIBOPENUI) + #include "libopenui.h" +#endif + void processGetHardwareInfoFrame(uint8_t module, const uint8_t * frame) { if (moduleState[module].mode != MODULE_MODE_GET_HARDWARE_INFO) { diff --git a/radio/src/telemetry/telemetry.cpp b/radio/src/telemetry/telemetry.cpp index c32cd491c..9fdc6c749 100644 --- a/radio/src/telemetry/telemetry.cpp +++ b/radio/src/telemetry/telemetry.cpp @@ -23,6 +23,10 @@ #include "pulses/afhds3.h" #include "mixer_scheduler.h" +#if defined(LIBOPENUI) + #include "libopenui.h" +#endif + uint8_t telemetryStreaming = 0; uint8_t telemetryRxBuffer[TELEMETRY_RX_PACKET_SIZE]; // Receive buffer. 9 bytes (full packet), worst case 18 bytes with byte-stuffing (+1) uint8_t telemetryRxBufferCount = 0; diff --git a/radio/src/telemetry/telemetry_sensors.cpp b/radio/src/telemetry/telemetry_sensors.cpp index b439bd678..92f51e8c4 100644 --- a/radio/src/telemetry/telemetry_sensors.cpp +++ b/radio/src/telemetry/telemetry_sensors.cpp @@ -22,6 +22,10 @@ #define _USE_MATH_DEFINES #include +#if defined(LIBOPENUI) + #include "libopenui.h" +#endif + TelemetryItem telemetryItems[MAX_TELEMETRY_SENSORS]; uint8_t allowNewSensors; diff --git a/radio/src/translations.cpp b/radio/src/translations.cpp index 4d4075bb5..663e1c5bb 100644 --- a/radio/src/translations.cpp +++ b/radio/src/translations.cpp @@ -22,6 +22,9 @@ #define ISTR(x) const char STR_##x[] = LEN_##x TR_##x +#if defined(COLORLCD) +ISTR(FONT_SIZES); +#endif ISTR(OFFON); ISTR(MMMINV); ISTR(VBEEPMODE); diff --git a/radio/src/translations.h b/radio/src/translations.h index 755c6bca4..fa6f69e5c 100644 --- a/radio/src/translations.h +++ b/radio/src/translations.h @@ -152,6 +152,7 @@ extern const char STR_USBMODES[]; extern const char STR_JACK_MODES[]; extern const char STR_VFAILSAFE[]; +extern const char STR_FONT_SIZES[]; extern const char STR_VTRAINERMODES[]; extern const char STR_EXTERNAL_MODULE_PROTOCOLS[]; extern const char STR_INTERNAL_MODULE_PROTOCOLS[]; diff --git a/radio/src/translations/untranslated.h b/radio/src/translations/untranslated.h index bbd753897..b98d5b70e 100644 --- a/radio/src/translations/untranslated.h +++ b/radio/src/translations/untranslated.h @@ -100,6 +100,16 @@ #define TR_MODULE_R9M_LITE "R9MLite\0 " #endif +#if defined(COLORLCD) +#if defined(BOLD) +#define LEN_FONT_SIZES "\003" +#define TR_FONT_SIZES "STD" +#else +#define LEN_FONT_SIZES "\004" +#define TR_FONT_SIZES "STD\0""BOLD""XXS\0""XS\0 ""L\0 ""XL\0 ""XXL\0" +#endif +#endif + #define LEN_EXTERNAL_MODULE_PROTOCOLS "\014" #define TR_EXTERNAL_MODULE_PROTOCOLS "OFF\0 ""PPM\0 ""XJT\0 ""ISRM\0 ""DSM2\0 ""CRSF\0 ""MULTI\0 ""R9M\0 ""R9M ACCESS\0 " TR_MODULE_R9M_LITE "R9ML ACCESS\0""GHST\0 ""R9MLP ACCESS""SBUS\0 ""XJT Lite\0 ""AFHDS3\0 "