From 67a599e0b9b367852e366030ce3c13f2012b9ad7 Mon Sep 17 00:00:00 2001 From: Bertrand Songis Date: Sat, 20 Feb 2016 16:55:05 +0100 Subject: [PATCH] [CLI] reboot command added --- radio/src/CMakeLists.txt | 2 + radio/src/audio_arm.cpp | 4 +- radio/src/bitmaps/horus/alpha_sleep.png | Bin 15801 -> 0 bytes radio/src/bitmaps/horus/bmp_asterisk.png | Bin 0 -> 9376 bytes radio/src/bitmaps/horus/bmp_sleep.png | Bin 0 -> 15868 bytes radio/src/gui/horus/bitmaps.cpp | 38 ++- radio/src/gui/horus/bitmaps.h | 3 +- radio/src/gui/horus/gui.h | 2 +- radio/src/gui/horus/layout.cpp | 32 +- radio/src/gui/horus/layout.h | 67 +---- radio/src/gui/horus/layouts/layout1x1.cpp | 34 +-- radio/src/gui/horus/layouts/layout2+1.cpp | 56 ++-- radio/src/gui/horus/layouts/layout2x1.cpp | 32 +- radio/src/gui/horus/layouts/layout2x2.cpp | 32 +- radio/src/gui/horus/layouts/layout2x4.cpp | 46 ++- radio/src/gui/horus/lcd.cpp | 51 +--- radio/src/gui/horus/lcd.h | 10 +- .../gui/horus/menu_model_custom_functions.cpp | 4 +- radio/src/gui/horus/menu_model_setup.cpp | 10 +- radio/src/gui/horus/menus.h | 11 +- radio/src/gui/horus/popups.cpp | 2 +- radio/src/gui/horus/screens_setup.cpp | 278 ++++++++++++------ radio/src/gui/horus/theme.cpp | 57 +++- radio/src/gui/horus/theme.h | 33 ++- radio/src/gui/horus/themes/darkblue.cpp | 16 +- radio/src/gui/horus/topbar.cpp | 100 +++++++ radio/src/gui/horus/topbar.h | 44 +++ radio/src/gui/horus/view_main.cpp | 69 +---- radio/src/gui/horus/widget.cpp | 1 + radio/src/gui/horus/widget.h | 26 +- radio/src/gui/horus/widgets.cpp | 2 +- radio/src/gui/horus/widgets.h | 2 +- radio/src/gui/horus/widgets/gauge.cpp | 6 +- .../widgets/{modelpanel.cpp => modelbmp.cpp} | 16 +- radio/src/gui/horus/widgets/timer.cpp | 34 ++- radio/src/gui/horus/widgets/value.cpp | 32 +- radio/src/gui/horus/widgets_container.h | 133 +++++++++ radio/src/lua/interface.cpp | 118 ++++---- radio/src/myeeprom.h | 49 +-- radio/src/opentx.cpp | 12 +- radio/src/simu.cpp | 1 - radio/src/targets/simu/simpgmspace.cpp | 7 +- radio/src/targets/taranis/board_taranis.h | 1 + radio/src/tasks_arm.h | 46 +-- radio/src/translations/en.h.txt | 6 +- 45 files changed, 920 insertions(+), 605 deletions(-) delete mode 100644 radio/src/bitmaps/horus/alpha_sleep.png create mode 100644 radio/src/bitmaps/horus/bmp_asterisk.png create mode 100644 radio/src/bitmaps/horus/bmp_sleep.png create mode 100644 radio/src/gui/horus/topbar.cpp create mode 100644 radio/src/gui/horus/topbar.h rename radio/src/gui/horus/widgets/{modelpanel.cpp => modelbmp.cpp} (72%) create mode 100644 radio/src/gui/horus/widgets_container.h diff --git a/radio/src/CMakeLists.txt b/radio/src/CMakeLists.txt index e55e354b5..ca2fd8d74 100644 --- a/radio/src/CMakeLists.txt +++ b/radio/src/CMakeLists.txt @@ -80,6 +80,7 @@ if(PCB STREQUAL HORUS) view_text.cpp screens_setup.cpp theme.cpp + topbar.cpp layout.cpp widget.cpp ${THEMES_SRC} @@ -256,6 +257,7 @@ if(CPU_TYPE STREQUAL STM32F4) include_directories(${STM32LIB_DIR}/STM32F4xx_StdPeriph_Driver/inc) include_directories(${STM32LIB_DIR}/CMSIS/Device/ST/STM32F4xx/Include) include_directories(${STM32LIB_DIR}/CMSIS/include) + # add_definitions(-DSTM32F4) set(STM32LIB_SRC ${STM32LIB_SRC} CMSIS/Device/ST/STM32F4xx/Source/Templates/gcc_ride7/startup_stm32f40_41xxx.s diff --git a/radio/src/audio_arm.cpp b/radio/src/audio_arm.cpp index e5246a63c..b96c60190 100644 --- a/radio/src/audio_arm.cpp +++ b/radio/src/audio_arm.cpp @@ -249,8 +249,6 @@ void referenceSystemAudioFiles() fn = *fno.lfname ? fno.lfname : fno.fname; uint8_t len = strlen(fn); - TRACE("%s", fn); - // Eliminates directories / non wav files if (len < 5 || strcasecmp(fn+len-4, SOUNDS_EXT) || (fno.fattrib & AM_DIR)) continue; @@ -509,7 +507,7 @@ void audioTask(void * pdata) AUDIO_TADA(); #elif defined(SDCARD) if (!unexpectedShutdown) { - sdInit(); + // sdInit(); AUDIO_TADA(); } #endif diff --git a/radio/src/bitmaps/horus/alpha_sleep.png b/radio/src/bitmaps/horus/alpha_sleep.png deleted file mode 100644 index ee8b302160178dc11a78c52b1d253960a9ed7cd3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15801 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 Js>BR}{s**3=BWSx literal 0 HcmV?d00001 diff --git a/radio/src/bitmaps/horus/bmp_sleep.png b/radio/src/bitmaps/horus/bmp_sleep.png new file mode 100644 index 0000000000000000000000000000000000000000..70e163272d0e325e9c29b836861fd2bb000008d4 GIT binary patch literal 15868 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

Q@JywLm0&M5||C7`2?YR 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 S`wQf>LDCX(;+3L$e*XnfCcAzones[index].widgetName, 0, sizeof(persistentData->zones[index].widgetName)); - strncpy(persistentData->zones[index].widgetName, factory->getName(), sizeof(persistentData->zones[index].widgetName)); - widgets[index] = factory->create(getZone(index), &persistentData->zones[index].widgetData); -} - -void Layout::load() -{ - unsigned int count = getZonesCount(); - for (unsigned int i=0; izones[i].widgetName[0]) { - char name[sizeof(persistentData->zones[i].widgetName)+1]; - memset(name, 0, sizeof(name)); - strncpy(name, persistentData->zones[i].widgetName, sizeof(persistentData->zones[i].widgetName)); - widgets[i] = loadWidget(name, getZone(i), &persistentData->zones[i].widgetData); - } - } -} - -void Layout::refresh(bool setup) -{ - for (int i = 0; i < MAX_LAYOUT_ZONES; i++) { - if (widgets[i]) { - widgets[i]->refresh(); - } - } -} - const LayoutFactory * registeredLayouts[MAX_REGISTERED_LAYOUTS]; // TODO dynamic unsigned int countRegisteredLayouts = 0; void registerLayout(const LayoutFactory * factory) { if (countRegisteredLayouts < MAX_REGISTERED_LAYOUTS) { + TRACE("register layout %s", factory->getName()); registeredLayouts[countRegisteredLayouts++] = factory; } } @@ -90,4 +62,6 @@ void loadCustomScreens() if (customScreens[0] == NULL) { customScreens[0] = registeredLayouts[0]->create(&g_model.screenData[0].layoutData); } + + topbar->load(); } diff --git a/radio/src/gui/horus/layout.h b/radio/src/gui/horus/layout.h index 147586c1e..eb2e3e675 100644 --- a/radio/src/gui/horus/layout.h +++ b/radio/src/gui/horus/layout.h @@ -21,86 +21,31 @@ #ifndef _LAYOUT_H_ #define _LAYOUT_H_ -#include "widget.h" +#include "widgets_container.h" #define MAX_LAYOUT_ZONES 10 #define MAX_LAYOUT_OPTIONS 10 class LayoutFactory; -class Layout + +class Layout: public WidgetsContainer { friend class LayoutFactory; - public: - struct ZonePersistentData { - char widgetName[10]; - Widget::PersistentData widgetData; - }; - - struct PersistentData { - ZonePersistentData zones[MAX_LAYOUT_ZONES]; - ZoneOptionValue options[MAX_LAYOUT_OPTIONS]; - }; - public: Layout(const LayoutFactory * factory, PersistentData * persistentData): - factory(factory), - persistentData(persistentData) + WidgetsContainer(persistentData), + factory(factory) { - memset(widgets, 0, sizeof(widgets)); } - virtual ~Layout() - { - for (uint8_t i=0; ioptions[index]; - } - - void setOptionValue(unsigned int index, ZoneOptionValue value) const - { - persistentData->options[index] = value; - } - - virtual unsigned int getZonesCount() const = 0; - virtual Zone getZone(unsigned int index) const = 0; - - virtual void refresh(bool setup=false); - protected: const LayoutFactory * factory; - Widget * widgets[MAX_LAYOUT_ZONES]; - PersistentData * persistentData; }; void registerLayout(const LayoutFactory * factory); diff --git a/radio/src/gui/horus/layouts/layout1x1.cpp b/radio/src/gui/horus/layouts/layout1x1.cpp index 525b0f946..a83abc269 100644 --- a/radio/src/gui/horus/layouts/layout1x1.cpp +++ b/radio/src/gui/horus/layouts/layout1x1.cpp @@ -20,6 +20,16 @@ #include "opentx.h" +const uint8_t LBM_LAYOUT_1x1[] = { +#include "mask_layout1x1.lbm" +}; + +const ZoneOption OPTIONS_LAYOUT_1x1[] = { + { "Top bar", ZoneOption::Bool }, + { "Sliders+Trims", ZoneOption::Bool }, + { NULL, ZoneOption::Bool } +}; + class Layout1x1: public Layout { public: @@ -55,25 +65,15 @@ class Layout1x1: public Layout return zone; } - virtual void refresh(bool setup=false); - - static const ZoneOption options[]; - + virtual void refresh(); }; -const ZoneOption Layout1x1::options[] = { - { "Top bar", ZoneOption::Bool }, - { "Sliders+Trims", ZoneOption::Bool }, - { NULL, ZoneOption::Bool } -}; - -void Layout1x1::refresh(bool setup) +void Layout1x1::refresh() { theme->drawBackground(); if (persistentData->options[0].boolValue) { - // Top Bar - drawMainViewTopBar(); + drawTopBar(); } if (persistentData->options[1].boolValue) { @@ -86,11 +86,7 @@ void Layout1x1::refresh(bool setup) drawTrims(mixerCurrentFlightMode); } - Layout::refresh(setup); + Layout::refresh(); } -const uint8_t LBM_LAYOUT_1x1[] __DMA = { -#include "mask_layout1x1.lbm" -}; - -BaseLayoutFactory layout1x1("Layout1x1", LBM_LAYOUT_1x1, Layout1x1::options); +BaseLayoutFactory layout1x1("Layout1x1", LBM_LAYOUT_1x1, OPTIONS_LAYOUT_1x1); diff --git a/radio/src/gui/horus/layouts/layout2+1.cpp b/radio/src/gui/horus/layouts/layout2+1.cpp index 625140894..039ee2a90 100644 --- a/radio/src/gui/horus/layouts/layout2+1.cpp +++ b/radio/src/gui/horus/layouts/layout2+1.cpp @@ -20,6 +20,24 @@ #include "opentx.h" +const uint8_t LBM_LAYOUT_2P1[] = { +#include "mask_layout2+1.lbm" +}; + +const ZoneOption OPTIONS_LAYOUT_2P1[] = { + { "Top bar", ZoneOption::Bool }, + { "Flight mode", ZoneOption::Bool }, + { "Sliders", ZoneOption::Bool }, + { "Trims", ZoneOption::Bool }, + { NULL, ZoneOption::Bool } +}; + +const Zone ZONES_LAYOUT_2P1[3] = { + { 240, 60, 192, 152 }, + { 48, 60, 180, 70 }, + { 48, 142, 180, 70 } +}; + class Layout2P1: public Layout { public: @@ -39,43 +57,23 @@ class Layout2P1: public Layout virtual unsigned int getZonesCount() const { - return 3; + return DIM(ZONES_LAYOUT_2P1); } virtual Zone getZone(unsigned int index) const { - return zones[index]; + return ZONES_LAYOUT_2P1[index]; } - virtual void refresh(bool setup=false); - - static const ZoneOption options[]; - - protected: - static const Zone zones[3]; + virtual void refresh(); }; -const Zone Layout2P1::zones[3] = { - { 240, 60, 192, 145 }, - { 46, 60, 182, 66 }, - { 46, 139, 182, 66 } -}; - -const ZoneOption Layout2P1::options[] = { - { "Top bar", ZoneOption::Bool }, - { "Flight mode", ZoneOption::Bool }, - { "Sliders", ZoneOption::Bool }, - { "Trims", ZoneOption::Bool }, - { NULL, ZoneOption::Bool } -}; - -void Layout2P1::refresh(bool setup) +void Layout2P1::refresh() { theme->drawBackground(); if (persistentData->options[0].boolValue) { - // Top Bar - drawMainViewTopBar(); + drawTopBar(); } if (persistentData->options[1].boolValue) { @@ -98,11 +96,7 @@ void Layout2P1::refresh(bool setup) drawTrims(mixerCurrentFlightMode); } - Layout::refresh(setup); + Layout::refresh(); } -const uint8_t LBM_LAYOUT_2P1[] __DMA = { -#include "mask_layout2+1.lbm" -}; - -BaseLayoutFactory layout2P1("Layout2P1", LBM_LAYOUT_2P1, Layout2P1::options); +BaseLayoutFactory layout2P1("Layout2P1", LBM_LAYOUT_2P1, OPTIONS_LAYOUT_2P1); diff --git a/radio/src/gui/horus/layouts/layout2x1.cpp b/radio/src/gui/horus/layouts/layout2x1.cpp index b7433f63e..a96fc7006 100644 --- a/radio/src/gui/horus/layouts/layout2x1.cpp +++ b/radio/src/gui/horus/layouts/layout2x1.cpp @@ -20,6 +20,15 @@ #include "opentx.h" +const uint8_t LBM_LAYOUT_2x1[] = { +#include "mask_layout2x1.lbm" +}; + +const ZoneOption OPTIONS_LAYOUT_2x1[] = { + { "Top bar", ZoneOption::Bool }, + { NULL, ZoneOption::Bool } +}; + class Layout2x1: public Layout { public: @@ -55,31 +64,18 @@ class Layout2x1: public Layout return zone; } - virtual void refresh(bool setup=false); - - static const ZoneOption options[]; - + virtual void refresh(); }; -const ZoneOption Layout2x1::options[] = { - { "Top bar", ZoneOption::Bool }, - { NULL, ZoneOption::Bool } -}; - -void Layout2x1::refresh(bool setup) +void Layout2x1::refresh() { theme->drawBackground(); if (persistentData->options[0].boolValue) { - // Top Bar - drawMainViewTopBar(); + drawTopBar(); } - Layout::refresh(setup); + Layout::refresh(); } -const uint8_t LBM_LAYOUT_2x1[] __DMA = { -#include "mask_layout2x1.lbm" -}; - -BaseLayoutFactory Layout2x1("Layout2x1", LBM_LAYOUT_2x1, Layout2x1::options); +BaseLayoutFactory Layout2x1("Layout2x1", LBM_LAYOUT_2x1, OPTIONS_LAYOUT_2x1); diff --git a/radio/src/gui/horus/layouts/layout2x2.cpp b/radio/src/gui/horus/layouts/layout2x2.cpp index b10c55e64..0efa2df62 100644 --- a/radio/src/gui/horus/layouts/layout2x2.cpp +++ b/radio/src/gui/horus/layouts/layout2x2.cpp @@ -20,6 +20,15 @@ #include "opentx.h" +const uint8_t LBM_LAYOUT_2x2[] = { +#include "mask_layout2x2.lbm" +}; + +const ZoneOption OPTIONS_LAYOUT_2x2[] = { + { "Top bar", ZoneOption::Bool }, + { NULL, ZoneOption::Bool } +}; + class Layout2x2: public Layout { public: @@ -58,31 +67,18 @@ class Layout2x2: public Layout return zone; } - virtual void refresh(bool setup=false); - - static const ZoneOption options[]; - + virtual void refresh(); }; -const ZoneOption Layout2x2::options[] = { - { "Top bar", ZoneOption::Bool }, - { NULL, ZoneOption::Bool } -}; - -void Layout2x2::refresh(bool setup) +void Layout2x2::refresh() { theme->drawBackground(); if (persistentData->options[0].boolValue) { - // Top Bar - drawMainViewTopBar(); + drawTopBar(); } - Layout::refresh(setup); + Layout::refresh(); } -const uint8_t LBM_LAYOUT_2x2[] __DMA = { -#include "mask_layout2x2.lbm" -}; - -BaseLayoutFactory layout2x2("Layout2x2", LBM_LAYOUT_2x2, Layout2x2::options); +BaseLayoutFactory layout2x2("Layout2x2", LBM_LAYOUT_2x2, OPTIONS_LAYOUT_2x2); diff --git a/radio/src/gui/horus/layouts/layout2x4.cpp b/radio/src/gui/horus/layouts/layout2x4.cpp index 1c6f441a2..b5bd79861 100644 --- a/radio/src/gui/horus/layouts/layout2x4.cpp +++ b/radio/src/gui/horus/layouts/layout2x4.cpp @@ -20,6 +20,22 @@ #include "opentx.h" +const uint8_t LBM_LAYOUT_2x4[] = { +#include "mask_layout2x4.lbm" +}; + +const ZoneOption OPTIONS_LAYOUT_2x4[] = { + { "Top bar", ZoneOption::Bool }, + { "Flight mode", ZoneOption::Bool }, + { "Sliders", ZoneOption::Bool }, + { "Trims", ZoneOption::Bool }, + { "Panel1 background", ZoneOption::Bool }, + { " Color", ZoneOption::Color }, + { "Panel2 background", ZoneOption::Bool }, + { " Color", ZoneOption::Color }, + { NULL, ZoneOption::Bool } +}; + class Layout2x4: public Layout { public: @@ -56,31 +72,15 @@ class Layout2x4: public Layout return zone; } - virtual void refresh(bool setup=false); - - static const ZoneOption options[]; - + virtual void refresh(); }; -const ZoneOption Layout2x4::options[] = { - { "Top bar", ZoneOption::Bool }, - { "Flight mode", ZoneOption::Bool }, - { "Sliders", ZoneOption::Bool }, - { "Trims", ZoneOption::Bool }, - { "Panel1 background", ZoneOption::Bool }, - { " Color", ZoneOption::Color }, - { "Panel2 background", ZoneOption::Bool }, - { " Color", ZoneOption::Color }, - { NULL, ZoneOption::Bool } -}; - -void Layout2x4::refresh(bool setup) +void Layout2x4::refresh() { theme->drawBackground(); if (persistentData->options[0].boolValue) { - // Top Bar - drawMainViewTopBar(); + drawTopBar(); } if (persistentData->options[1].boolValue) { @@ -113,11 +113,7 @@ void Layout2x4::refresh(bool setup) lcdDrawSolidFilledRect(250, 50, 180, 170, CUSTOM_COLOR); } - Layout::refresh(setup); + Layout::refresh(); } -const uint8_t LBM_LAYOUT_2x4[] __DMA = { -#include "mask_layout2x4.lbm" -}; - -BaseLayoutFactory layout2x4("Layout2x4", LBM_LAYOUT_2x4, Layout2x4::options); +BaseLayoutFactory layout2x4("Layout2x4", LBM_LAYOUT_2x4, OPTIONS_LAYOUT_2x4); diff --git a/radio/src/gui/horus/lcd.cpp b/radio/src/gui/horus/lcd.cpp index 69c39b261..46cb8382d 100644 --- a/radio/src/gui/horus/lcd.cpp +++ b/radio/src/gui/horus/lcd.cpp @@ -109,24 +109,17 @@ int getFontHeight(LcdFlags flags) return heightTable[FONTSIZE(flags) >> 8]; } -int getBitmapScale(const uint8_t * bmp, int dstWidth, int dstHeight) +float getBitmapScale(const uint8_t * bmp, int dstWidth, int dstHeight) { - int widthScale, heightScale; - int bmpWidth = getBitmapWidth(bmp); int bmpHeight = getBitmapHeight(bmp); if (bmpWidth == 0 || bmpHeight == 0) return 0; - if (bmpWidth > dstWidth) - widthScale = -((bmpWidth+dstWidth-1) / dstWidth); - else - widthScale = (dstWidth / bmpWidth); - if (bmpHeight > dstHeight) - heightScale = -((bmpHeight+dstHeight-1) / dstHeight); - else - heightScale = (dstHeight / bmpHeight); + float widthScale = float(dstWidth) / bmpWidth; + float heightScale = float(dstHeight) / bmpHeight; + return min(widthScale, heightScale); } @@ -882,7 +875,7 @@ void lcdDrawVerticalLine(coord_t x, coord_t y, coord_t h, uint8_t pat, LcdFlags #if defined(SIMU) inline void lcdDrawBitmapDMA(coord_t x, coord_t y, coord_t width, coord_t height, const uint8_t * img) { - lcdDrawBitmap(x, y, img-4, 0, 0, -1); + lcdDrawBitmap(x, y, img-4, 0, 0, 1.0); } #endif @@ -906,7 +899,7 @@ void lcdDrawAlphaBitmap(coord_t x, coord_t y, const uint8_t * bmp) } } -void lcdDrawBitmap(coord_t x, coord_t y, const uint8_t * bmp, coord_t offset, coord_t height, int scale) +void lcdDrawBitmap(coord_t x, coord_t y, const uint8_t * bmp, coord_t offset, coord_t height, float scale) { int width = getBitmapWidth(bmp); int h = getBitmapHeight(bmp); @@ -926,28 +919,16 @@ void lcdDrawBitmap(coord_t x, coord_t y, const uint8_t * bmp, coord_t offset, co if (scale == 0) { lcdDrawBitmapDMA(x, y, width, height, bmp + 4 + offset * width * 2); } - else if (scale < 0) { - for (coord_t i=0, row=0; row= -1 && scale <= 1) + if (scale == 0.0) return size; - else if (scale < 0) - return -(size / scale); else return size * scale; } -int getBitmapScale(const uint8_t * bmp, int dstWidth, int dstHeight); +float getBitmapScale(const uint8_t * bmp, int dstWidth, int dstHeight); int getTextWidth(const pm_char *s, int len=0, LcdFlags flags=0); -void lcdDrawBitmap(coord_t x, coord_t y, const uint8_t * img, coord_t offset=0, coord_t height=0, int scale=0); +void lcdDrawBitmap(coord_t x, coord_t y, const uint8_t * img, coord_t offset=0, coord_t height=0, float scale=0); void lcdDrawBitmapPattern(coord_t x, coord_t y, const uint8_t * img, LcdFlags flags=0, coord_t offset=0, coord_t width=0); void lcdDrawAlphaBitmap(coord_t x, coord_t y, const uint8_t * bmp); diff --git a/radio/src/gui/horus/menu_model_custom_functions.cpp b/radio/src/gui/horus/menu_model_custom_functions.cpp index 46b7997eb..ebda017f3 100644 --- a/radio/src/gui/horus/menu_model_custom_functions.cpp +++ b/radio/src/gui/horus/menu_model_custom_functions.cpp @@ -2,7 +2,7 @@ * Copyright (C) OpenTX * * Based on code named - * th9x - http://code.google.com/p/th9x + * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * @@ -27,7 +27,7 @@ #define MODEL_CUSTOM_FUNC_4TH_COLUMN 440 #define MODEL_CUSTOM_FUNC_4TH_COLUMN_ONOFF 450 -void onCustomFunctionsFileSelectionMenu(const char *result) +void onCustomFunctionsFileSelectionMenu(const char * result) { int sub = menuVerticalPosition; CustomFunctionData * cf = &g_model.customFn[sub]; diff --git a/radio/src/gui/horus/menu_model_setup.cpp b/radio/src/gui/horus/menu_model_setup.cpp index 06d726d86..5b895d1e5 100644 --- a/radio/src/gui/horus/menu_model_setup.cpp +++ b/radio/src/gui/horus/menu_model_setup.cpp @@ -544,9 +544,9 @@ bool menuModelSetup(evt_t event) lcdDrawText(MENUS_MARGIN_LEFT, y, STR_MODE); lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_TARANIS_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].type, (menuHorizontalPosition==0 ? attr : 0)); if (IS_MODULE_XJT(EXTERNAL_MODULE)) - lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+40, y, STR_XJT_PROTOCOLS, 1+g_model.moduleData[EXTERNAL_MODULE].rfProtocol, (menuHorizontalPosition==1 ? attr : 0)); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+70, y, STR_XJT_PROTOCOLS, 1+g_model.moduleData[EXTERNAL_MODULE].rfProtocol, (menuHorizontalPosition==1 ? attr : 0)); else if (IS_MODULE_DSM2(EXTERNAL_MODULE)) - lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+40, y, STR_DSM_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, (menuHorizontalPosition==1 ? attr : 0)); + lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN+70, y, STR_DSM_PROTOCOLS, g_model.moduleData[EXTERNAL_MODULE].rfProtocol, (menuHorizontalPosition==1 ? attr : 0)); if (attr && s_editMode>0) { switch (menuHorizontalPosition) { case 0: @@ -672,7 +672,9 @@ bool menuModelSetup(evt_t event) lcdDrawText(MENUS_MARGIN_LEFT, y, TR_FAILSAFE); if (IS_MODULE_XJT(moduleIdx)) { lcdDrawTextAtIndex(MODEL_SETUP_2ND_COLUMN, y, STR_VFAILSAFE, moduleData.failsafeMode, menuHorizontalPosition==0 ? attr : 0); - if (moduleData.failsafeMode == FAILSAFE_CUSTOM) lcdDrawText(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_SET, menuHorizontalPosition==1 ? attr : 0); + if (moduleData.failsafeMode == FAILSAFE_CUSTOM) { + drawButton(MODEL_SETUP_2ND_COLUMN + MODEL_SETUP_SET_FAILSAFE_OFS, y, STR_SET, menuHorizontalPosition==1 ? attr : 0); + } if (attr) { if (moduleData.failsafeMode != FAILSAFE_CUSTOM) menuHorizontalPosition = 0; @@ -700,7 +702,7 @@ bool menuModelSetup(evt_t event) } if (IS_RANGECHECK_ENABLE()) { - displayPopup("RSSI :"); + theme->drawMessageBox("RSSI :", NULL, NULL, 0); lcdDrawNumber(WARNING_LINE_X, WARNING_INFOLINE_Y, TELEMETRY_RSSI(), DBLSIZE|LEFT); } diff --git a/radio/src/gui/horus/menus.h b/radio/src/gui/horus/menus.h index 73c85bf94..cc6860b5e 100644 --- a/radio/src/gui/horus/menus.h +++ b/radio/src/gui/horus/menus.h @@ -144,7 +144,7 @@ static const MenuHandlerFunc menuTabGeneral[] PROGMEM = { menuGeneralVersion, }; -extern MenuHandlerFunc menuTabMainviews[1+MAX_CUSTOM_SCREENS]; +extern const MenuHandlerFunc menuTabScreensSetup[1+MAX_CUSTOM_SCREENS] PROGMEM; bool menuFirstCalib(evt_t event); bool menuMainView(evt_t event); @@ -274,10 +274,13 @@ bool check_submenu_simple(check_event_t event, uint8_t maxrow); #define MENU(title, icons, tab, menu, lines_count, ...) \ MENU_WITH_OPTIONS(title, icons, tab, DIM(tab), menu, lines_count, __VA_ARGS__) -#define SIMPLE_MENU(title, icons, tab, menu, lines_count) \ +#define SIMPLE_MENU_WITH_OPTIONS(title, icons, tab, tabCount, menu, lines_count) \ if (event == EVT_ENTRY || event == EVT_ENTRY_UP) TRACE("Menu %s displayed ...", title); \ - if (!check_simple(event, menu, tab, DIM(tab), lines_count)) return false; \ - drawMenuTemplate(title, icons); \ + if (!check_simple(event, menu, tab, tabCount, lines_count)) return false; \ + drawMenuTemplate(title, icons); + +#define SIMPLE_MENU(title, icons, tab, menu, lines_count) \ + SIMPLE_MENU_WITH_OPTIONS(title, icons, tab, DIM(tab), menu, lines_count) #define SUBMENU(title, icon, lines_count, ...) \ MENU_TAB(__VA_ARGS__); \ diff --git a/radio/src/gui/horus/popups.cpp b/radio/src/gui/horus/popups.cpp index 08bf862a8..ca7fd331b 100644 --- a/radio/src/gui/horus/popups.cpp +++ b/radio/src/gui/horus/popups.cpp @@ -53,7 +53,7 @@ void displayMessageBox() void drawAlertBox(const char * title, const char * text, const char * action) { - theme->drawAlertBox(title, text, action); + theme->drawMessageBox(title, text, action, MESSAGEBOX_TYPE_ALERT); } void message(const pm_char * title, const pm_char * text, const char * action, uint8_t sound) diff --git a/radio/src/gui/horus/screens_setup.cpp b/radio/src/gui/horus/screens_setup.cpp index 397ad0824..c0d8dab15 100644 --- a/radio/src/gui/horus/screens_setup.cpp +++ b/radio/src/gui/horus/screens_setup.cpp @@ -20,66 +20,120 @@ #include "opentx.h" +const uint8_t * LBM_SCREENS_SETUP_ICONS[] = { + LBM_MAINVIEWS_ICON, + LBM_MAINVIEWS_TOPBAR_ICON, + LBM_MAINVIEWS_ICONS[0], + LBM_MAINVIEWS_ICONS[1], + LBM_MAINVIEWS_ICONS[2], + LBM_MAINVIEWS_ICONS[3], + LBM_MAINVIEWS_ICONS[4] +}; + Layout * currentScreen; +WidgetsContainerInterface * currentContainer; Widget * currentWidget; uint8_t currentZone; #define SCREENS_SETUP_2ND_COLUMN 200 -ZoneOptionValue editZoneOption(coord_t y, const ZoneOption * option, ZoneOptionValue value, LcdFlags attr, evt_t event) +char fileSelection[sizeof(ZoneOptionValue::stringValue)]; +uint8_t fileSelectionDone; + +int updateMainviewsMenu(); +bool menuScreenAdd(evt_t event); +void onScreenSetupMenu(const char * result); + +void onZoneOptionFileSelectionMenu(const char * result) +{ + if (result == STR_UPDATE_LIST) { + if (!sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(ZoneOptionValue::stringValue), NULL)) { + POPUP_WARNING(STR_NO_BITMAPS_ON_SD); + } + } + else { + fileSelectionDone = true; + memcpy(fileSelection, result, sizeof(fileSelection)); + } +} + +void editZoneOption(coord_t y, const ZoneOption * option, ZoneOptionValue * value, LcdFlags attr, uint32_t i_flags, evt_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); + 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); + lcdDrawNumber(SCREENS_SETUP_2ND_COLUMN, y, value->signedValue, attr | LEFT); if (attr) { - CHECK_INCDEC_MODELVAR(event, value.signedValue, -30000, 30000); + CHECK_INCDEC_MODELVAR(event, value->signedValue, -30000, 30000); // TODO i_flags } } else if (option->type == ZoneOption::String) { - editName(SCREENS_SETUP_2ND_COLUMN, y, value.stringValue, sizeof(value.stringValue), event, attr); + 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_BREAK(KEY_ENTER)) { + s_editMode = 0; + if (sdListFiles(BITMAPS_PATH, BITMAPS_EXT, sizeof(value->stringValue), value->stringValue, LIST_NONE_SD_FILE)) { + fileSelectionDone = false; + popupMenuHandler = 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, "\010StandardTiny\0 Small\0 Mid\0 Double", value.unsignedValue, attr); + lcdDrawTextAtIndex(SCREENS_SETUP_2ND_COLUMN, y, "\010StandardTiny\0 Small\0 Mid\0 Double", value->unsignedValue, attr); if (attr) { - value.unsignedValue = checkIncDec(event, value.unsignedValue, 0, 4, EE_MODEL); + 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); + drawStringWithIndex(SCREENS_SETUP_2ND_COLUMN, y, STR_TIMER, value->unsignedValue + 1, attr); if (attr) { - value.unsignedValue = checkIncDec(event, value.unsignedValue, 0, MAX_TIMERS - 1, EE_MODEL); + value->unsignedValue = checkIncDec(event, value->unsignedValue, 0, MAX_TIMERS - 1, i_flags); } } else if (option->type == ZoneOption::Source) { - putsMixerSource(SCREENS_SETUP_2ND_COLUMN, y, value.unsignedValue, attr); + putsMixerSource(SCREENS_SETUP_2ND_COLUMN, y, value->unsignedValue, attr); if (attr) { - CHECK_INCDEC_MODELSOURCE(event, value.unsignedValue, 1, MIXSRC_LAST); + CHECK_INCDEC_MODELSOURCE(event, value->unsignedValue, 1, MIXSRC_LAST); } } else if (option->type == ZoneOption::Color) { - lcdSetColor(value.unsignedValue); + lcdSetColor(value->unsignedValue); lcdDrawSolidRect(SCREENS_SETUP_2ND_COLUMN, y, 40, 15, 1, attr ? TEXT_INVERTED_BGCOLOR : TEXT_COLOR); lcdDrawSolidFilledRect(SCREENS_SETUP_2ND_COLUMN + 1, y + 1, 38, 13, CUSTOM_COLOR); if (attr) { - CHECK_INCDEC_MODELVAR(event, value.unsignedValue, 0, 65535); + value->unsignedValue = checkIncDec(event, value->unsignedValue, i_flags, 65535, 0); } } - return value; } -bool menuWidgetSettings(evt_t event) +template +bool menuSettings(const char * title, const T * object, uint32_t i_flags, evt_t event) { linesCount = 0; - const ZoneOption * options = currentWidget->getFactory()->getOptions(); + const ZoneOption * options = object->getOptions(); for (const ZoneOption * option = options; option->name; option++) { linesCount++; } - SUBMENU_WITH_OPTIONS("Widget settings", LBM_MAINVIEWS_ICON, linesCount, OPTION_MENU_TITLE_BAR, { 0, 0, 0 }); + SUBMENU_WITH_OPTIONS(title, LBM_MAINVIEWS_ICON, linesCount, OPTION_MENU_TITLE_BAR, { 0, 0, 0 }); for (int i=0; igetOptionValue(k); - value = editZoneOption(y, option, value, attr, event); - if (attr) { - currentWidget->setOptionValue(k, value); - } + ZoneOptionValue * value = object->getOptionValue(k); + editZoneOption(y, option, value, attr, i_flags, event); } } return true; } +bool menuWidgetSettings(evt_t event) +{ + return menuSettings("Widget settings", currentWidget, EE_MODEL, event); +} + +bool menuThemeSettings(evt_t event) +{ + return menuSettings("Theme settings", theme, EE_GENERAL, event); +} + bool menuWidgetChoice(evt_t event) { static Widget * previousWidget; @@ -108,8 +169,8 @@ bool menuWidgetChoice(evt_t event) case EVT_ENTRY: { const WidgetFactory * factory; - previousWidget = currentScreen->getWidget(currentZone); - currentScreen->setWidget(currentZone, NULL); + previousWidget = currentContainer->getWidget(currentZone); + currentContainer->setWidget(currentZone, NULL); menuHorizontalPosition = 0; if (previousWidget) { factory = previousWidget->getFactory(); @@ -123,19 +184,19 @@ bool menuWidgetChoice(evt_t event) else { factory = registeredWidgets[0]; } - currentWidget = factory->create(currentScreen->getZone(currentZone), &tempData); + currentWidget = factory->create(currentContainer->getZone(currentZone), &tempData); break; } case EVT_KEY_BREAK(KEY_EXIT): delete currentWidget; - currentScreen->setWidget(currentZone, previousWidget); + currentContainer->setWidget(currentZone, previousWidget); popMenu(); return false; case EVT_KEY_BREAK(KEY_ENTER): delete previousWidget; - currentScreen->createWidget(currentZone, registeredWidgets[menuHorizontalPosition]); + currentContainer->createWidget(currentZone, registeredWidgets[menuHorizontalPosition]); storageDirty(EE_MODEL); popMenu(); return false; @@ -143,21 +204,21 @@ bool menuWidgetChoice(evt_t event) case EVT_ROTARY_RIGHT: if (menuHorizontalPosition < int(countRegisteredWidgets-1)) { delete currentWidget; - currentWidget = registeredWidgets[++menuHorizontalPosition]->create(currentScreen->getZone(currentZone), &tempData); + currentWidget = registeredWidgets[++menuHorizontalPosition]->create(currentContainer->getZone(currentZone), &tempData); } break; case EVT_ROTARY_LEFT: if (menuHorizontalPosition > 0) { delete currentWidget; - currentWidget = registeredWidgets[--menuHorizontalPosition]->create(currentScreen->getZone(currentZone), &tempData); + currentWidget = registeredWidgets[--menuHorizontalPosition]->create(currentContainer->getZone(currentZone), &tempData); } break; } currentScreen->refresh(); - Zone zone = currentScreen->getZone(currentZone); + Zone zone = currentContainer->getZone(currentZone); lcdDrawFilledRect(0, 0, zone.x-2, LCD_H, SOLID, OVERLAY_COLOR | (8<<24)); lcdDrawFilledRect(zone.x+zone.w+2, 0, LCD_W-zone.x-zone.w-2, LCD_H, SOLID, OVERLAY_COLOR | (8<<24)); lcdDrawFilledRect(zone.x-2, 0, zone.w+4, zone.y-2, SOLID, OVERLAY_COLOR | (8<<24)); @@ -176,20 +237,18 @@ bool menuWidgetChoice(evt_t event) void onZoneMenu(const char * result) { if (result == STR_SELECT_WIDGET) { - currentZone = menuVerticalPosition; pushMenu(menuWidgetChoice); } else if (result == STR_WIDGET_SETTINGS) { - currentWidget = currentScreen->getWidget(menuVerticalPosition); pushMenu(menuWidgetSettings); } else if (result == STR_REMOVE_WIDGET) { - currentScreen->setWidget(menuVerticalPosition, NULL); + currentContainer->setWidget(currentZone, NULL); storageDirty(EE_MODEL); } } -bool menuSetupWidgets(evt_t event) +bool menuWidgetsSetup(evt_t event) { switch (event) { case EVT_ENTRY: @@ -200,17 +259,30 @@ bool menuSetupWidgets(evt_t event) return false; } - currentScreen->refresh(true); + currentScreen->refresh(); - for (int i=currentScreen->getZonesCount()-1; i>=0; i--) { - Zone zone = currentScreen->getZone(i); + for (int i=currentContainer->getZonesCount()-1; i>=0; i--) { + Zone zone = currentContainer->getZone(i); + LcdFlags color; + int padding, thickness; + if (currentContainer == topbar) { + color = MENU_TITLE_COLOR; + padding = 2; + thickness = 1; + } + else { + color = TEXT_INVERTED_BGCOLOR; + padding = 4; + thickness = 2; + } if (menuVerticalPosition == i) { - lcdDrawSolidRect(zone.x-4, zone.y-4, zone.w+8, zone.h+8, 2, TEXT_INVERTED_BGCOLOR); + lcdDrawSolidRect(zone.x-padding, zone.y-padding, zone.w+2*padding, zone.h+2*padding, thickness, color); if (event == EVT_KEY_BREAK(KEY_ENTER)) { - Widget * widget = currentScreen->getWidget(i); - if (widget) { + currentZone = menuVerticalPosition; + currentWidget = currentContainer->getWidget(menuVerticalPosition); + if (currentWidget) { POPUP_MENU_ADD_ITEM(STR_SELECT_WIDGET); - if (widget->getFactory()->getOptions()) + if (currentWidget->getFactory()->getOptions()) POPUP_MENU_ADD_ITEM(STR_WIDGET_SETTINGS); POPUP_MENU_ADD_ITEM(STR_REMOVE_WIDGET); popupMenuHandler = onZoneMenu; @@ -221,15 +293,13 @@ bool menuSetupWidgets(evt_t event) } } else { - lcdDrawRect(zone.x-4, zone.y-4, zone.w+8, zone.h+8, 2, 0x3F, TEXT_INVERTED_BGCOLOR); + lcdDrawRect(zone.x-padding, zone.y-padding, zone.w+2*padding, zone.h+2*padding, thickness, 0x3F, color); } } - navigate(event, currentScreen->getZonesCount(), currentScreen->getZonesCount(), 1); + navigate(event, currentContainer->getZonesCount(), currentContainer->getZonesCount(), 1); return true; } -extern int updateMainviewsMenu(); - template T * editThemeChoice(coord_t x, coord_t y, T * array[], uint8_t count, T * current, bool needsOffsetCheck, LcdFlags attr, evt_t event) { @@ -291,12 +361,19 @@ T * editThemeChoice(coord_t x, coord_t y, T * array[], uint8_t count, T * curren return NULL; } +enum menuScreensThemeItems { + ITEM_SCREEN_SETUP_THEME, + ITEM_SCREEN_SETUP_THEME_SETTINGS = ITEM_SCREEN_SETUP_THEME+2, + ITEM_SCREEN_SETUP_TOPBAR, + ITEM_SCREEN_SETUP_MAX +}; + bool menuScreensTheme(evt_t event) { bool needsOffsetCheck = (menuVerticalPosition != 0 || menuHorizontalPosition < 0); menuPageCount = updateMainviewsMenu(); - MENU_WITH_OPTIONS("User interface", LBM_MAINVIEWS_ICONS, menuTabMainviews, menuPageCount, 0, 1, { uint8_t(NAVIGATION_LINE_BY_LINE|uint8_t(countRegisteredThemes-1)), ORPHAN_ROW, 0, 0, 0, 0 }); + MENU_WITH_OPTIONS("User interface", LBM_SCREENS_SETUP_ICONS, menuTabScreensSetup, menuPageCount, 0, ITEM_SCREEN_SETUP_MAX, { uint8_t(NAVIGATION_LINE_BY_LINE|uint8_t(countRegisteredThemes-1)), ORPHAN_ROW, 0, 0, 0, 0 }); for (int i=0; i 0) ? BLINK | INVERS : INVERS); LcdFlags attr = (menuVerticalPosition == k ? blink : 0); switch (k) { - case 0: { + case ITEM_SCREEN_SETUP_THEME: { lcdDrawText(MENUS_MARGIN_LEFT, y + FH / 2, "Theme"); const Theme * new_theme = editThemeChoice(SCREENS_SETUP_2ND_COLUMN, y, registeredThemes, countRegisteredThemes, theme, needsOffsetCheck, attr, event); if (new_theme) { + new_theme->init(); loadTheme(new_theme); strncpy(g_eeGeneral.themeName, new_theme->getName(), sizeof(g_eeGeneral.themeName)); storageDirty(EE_GENERAL); @@ -315,34 +393,56 @@ bool menuScreensTheme(evt_t event) break; } - case 1: + case ITEM_SCREEN_SETUP_THEME_SETTINGS: + drawButton(SCREENS_SETUP_2ND_COLUMN, y, "Theme settings", attr); + if (attr && event == EVT_KEY_BREAK(KEY_ENTER) && theme->getOptions()) { + s_editMode = 0; + pushMenu(menuThemeSettings); + } break; + + case ITEM_SCREEN_SETUP_TOPBAR: + lcdDrawText(MENUS_MARGIN_LEFT, y, "Top bar"); + drawButton(SCREENS_SETUP_2ND_COLUMN, y, "Setup", attr); + if (attr && event == EVT_KEY_BREAK(KEY_ENTER)) { + currentScreen = customScreens[0]; + currentContainer = topbar; + pushMenu(menuWidgetsSetup); + } + break; + } } return true; } -bool menuScreenAdd(evt_t event); -void onScreenSetupMenu(const char * result); +enum menuScreenSetup { + ITEM_SCREEN_SETUP_LAYOUT, + ITEM_SCREEN_SETUP_WIDGETS_SETUP = ITEM_SCREEN_SETUP_LAYOUT+2, + ITEM_SCREEN_SETUP_LAYOUT_OPTION1, +}; -template -bool menuScreenSetup(evt_t event) +bool menuScreenSetup(int index, evt_t event) { - currentScreen = customScreens[T]; + if (customScreens[index] == NULL) { + return menuScreenAdd(event); + } + currentScreen = customScreens[index]; + currentContainer = currentScreen; bool needsOffsetCheck = (menuVerticalPosition != 0 || menuHorizontalPosition < 0); - linesCount = 3; + linesCount = ITEM_SCREEN_SETUP_LAYOUT_OPTION1; const ZoneOption * options = currentScreen->getFactory()->getOptions(); for (const ZoneOption * option = options; option->name; option++) { linesCount++; } char title[] = "Main view X"; - title[sizeof(title)-2] = '1' + T; + title[sizeof(title)-2] = '1' + index; menuPageCount = updateMainviewsMenu(); - MENU_WITH_OPTIONS(title, LBM_MAINVIEWS_ICONS, menuTabMainviews, menuPageCount, T+1, linesCount, { uint8_t(NAVIGATION_LINE_BY_LINE|uint8_t(countRegisteredLayouts-1)), ORPHAN_ROW, 0, 0, 0, 0 }); + MENU_WITH_OPTIONS(title, LBM_SCREENS_SETUP_ICONS, menuTabScreensSetup, menuPageCount, index+1, linesCount, { uint8_t(NAVIGATION_LINE_BY_LINE|uint8_t(countRegisteredLayouts-1)), ORPHAN_ROW, 0, 0, 0, 0 }); for (int i=0; i0) ? BLINK|INVERS : INVERS); LcdFlags attr = (menuVerticalPosition == k ? blink : 0); switch(k) { - case 0: + case ITEM_SCREEN_SETUP_LAYOUT: { lcdDrawText(MENUS_MARGIN_LEFT, y + FH / 2, "Layout"); const LayoutFactory * factory = editThemeChoice(SCREENS_SETUP_2ND_COLUMN, y, registeredLayouts, countRegisteredLayouts, currentScreen->getFactory(), needsOffsetCheck, attr, event); if (factory) { - delete customScreens[T]; - customScreens[T] = factory->create(&g_model.screenData[T].layoutData); - strncpy(g_model.screenData[T].layoutName, factory->getName(), sizeof(g_model.screenData[T].layoutName)); + delete customScreens[index]; + customScreens[index] = factory->create(&g_model.screenData[index].layoutData); + strncpy(g_model.screenData[index].layoutName, factory->getName(), sizeof(g_model.screenData[index].layoutName)); storageDirty(EE_MODEL); } break; } - case 1: + case ITEM_SCREEN_SETUP_LAYOUT+1: break; - case 2: + case ITEM_SCREEN_SETUP_WIDGETS_SETUP: drawButton(SCREENS_SETUP_2ND_COLUMN, y, "Setup widgets", attr); if (attr && event == EVT_KEY_BREAK(KEY_ENTER)) { - pushMenu(menuSetupWidgets); + pushMenu(menuWidgetsSetup); } break; default: if (k < linesCount) { - uint8_t index = k - 3; + uint8_t index = k - ITEM_SCREEN_SETUP_LAYOUT_OPTION1; const ZoneOption * option = &options[index]; - ZoneOptionValue value = currentScreen->getOptionValue(index); - value = editZoneOption(y, option, value, attr, event); - if (attr) { - currentScreen->setOptionValue(index, value); - } + ZoneOptionValue * value = currentScreen->getOptionValue(index); + editZoneOption(y, option, value, attr, EE_MODEL, event); } break; } } - if (menuVerticalPosition == -1 && T > 0 && event == EVT_KEY_LONG(KEY_ENTER)) { + if (menuVerticalPosition == -1 && index > 0 && event == EVT_KEY_LONG(KEY_ENTER)) { killEvents(KEY_ENTER); - menuHorizontalPosition = T; + menuHorizontalPosition = index; POPUP_MENU_ADD_ITEM(STR_REMOVE_SCREEN); popupMenuHandler = onScreenSetupMenu; } @@ -397,33 +494,29 @@ bool menuScreenSetup(evt_t event) return true; } -MenuHandlerFunc menuTabMainviews[1+MAX_CUSTOM_SCREENS] = { - menuScreensTheme, - menuScreenSetup<0>, - menuScreenSetup<1>, - menuScreenSetup<2>, - menuScreenSetup<3>, - menuScreenSetup<4> -}; +template +bool menuCustomScreenSetup(evt_t event) +{ + return menuScreenSetup(N, event); +} -const MenuHandlerFunc menuMainviews[MAX_CUSTOM_SCREENS] = { - menuScreenSetup<0>, - menuScreenSetup<1>, - menuScreenSetup<2>, - menuScreenSetup<3>, - menuScreenSetup<4> +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); - chainMenu(menuMainviews[menuPageCount-2]); + s_editMode = 0; return false; } - MENU_WITH_OPTIONS("Add main view", LBM_MAINVIEWS_ICONS, menuTabMainviews, menuPageCount, menuPageCount-1, 0, { uint8_t(NAVIGATION_LINE_BY_LINE|uint8_t(countRegisteredLayouts-1)), ORPHAN_ROW, 0, 0, 0, 0 }); - + SIMPLE_MENU_WITH_OPTIONS("Add main view", LBM_SCREENS_SETUP_ICONS, menuTabScreensSetup, menuPageCount, menuPageCount-1, 0); return true; } @@ -455,6 +547,6 @@ void onScreenSetupMenu(const char * result) } memset(&g_model.screenData[MAX_CUSTOM_SCREENS-1], 0, sizeof(CustomScreenData)); customScreens[MAX_CUSTOM_SCREENS-1] = NULL; - chainMenu(menuMainviews[menuHorizontalPosition > 0 ? menuHorizontalPosition-1 : 0]); + // chainMenu(menuMainviews[menuHorizontalPosition > 0 ? menuHorizontalPosition-1 : 0]); } } diff --git a/radio/src/gui/horus/theme.cpp b/radio/src/gui/horus/theme.cpp index e23832796..b503c7226 100644 --- a/radio/src/gui/horus/theme.cpp +++ b/radio/src/gui/horus/theme.cpp @@ -20,23 +20,54 @@ #include "opentx.h" +void Theme::init() const +{ + memset(&g_eeGeneral.themeData, 0, sizeof(Theme::PersistentData)); + if (options) { + int i = 0; + for (const ZoneOption * option = options; option->name; option++, i++) { + // TODO compiler bug? The CPU freezes ... g_eeGeneral.themeData.options[i] = &option->deflt; + memcpy(&g_eeGeneral.themeData.options[i], &option->deflt, sizeof(ZoneOptionValue)); + } + } +} + +ZoneOptionValue * Theme::getOptionValue(unsigned int index) const +{ + return &g_eeGeneral.themeData.options[index]; +} + +void Theme::drawThumb(uint16_t x, uint16_t y, uint32_t flags) const +{ + lcdDrawBitmap(x, y, bitmap); +} + void Theme::drawBackground() const { lcdDrawSolidFilledRect(0, 0, LCD_W, LCD_H, TEXT_BGCOLOR); } -void Theme::drawAlertBox(const char * title, const char * text, const char * action) const +void Theme::drawMessageBox(const char * title, const char * text, const char * action, uint32_t flags) const { - drawBackground(); - lcdDrawFilledRect(0, POPUP_Y, LCD_W, POPUP_H, SOLID, TEXT_INVERTED_COLOR | OPACITY(8)); - lcdDrawAlphaBitmap(POPUP_X-80, POPUP_Y+12, LBM_ASTERISK); + //if (flags & MESSAGEBOX_TYPE_ALERT) { + drawBackground(); + lcdDrawFilledRect(0, POPUP_Y, LCD_W, POPUP_H, SOLID, TEXT_INVERTED_COLOR | OPACITY(8)); + //} + + if ((flags & MESSAGEBOX_TYPE_ALERT) || (flags & MESSAGEBOX_TYPE_WARNING)) { + lcdDrawAlphaBitmap(POPUP_X-80, POPUP_Y+12, LBM_ASTERISK); + } #if defined(TRANSLATIONS_FR) || defined(TRANSLATIONS_IT) || defined(TRANSLATIONS_CZ) - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, STR_WARNING, ALARM_COLOR|DBLSIZE); - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+28, title, ALARM_COLOR|DBLSIZE); + if ((flags & MESSAGEBOX_TYPE_ALERT) || (flags & MESSAGEBOX_TYPE_WARNING)) { + lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, STR_WARNING, ALARM_COLOR|DBLSIZE); + } + lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+28, title, ALARM_COLOR|DBLSIZE); #else lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y, title, ALARM_COLOR|DBLSIZE); - lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+28, STR_WARNING, ALARM_COLOR|DBLSIZE); + if ((flags & MESSAGEBOX_TYPE_ALERT) || (flags & MESSAGEBOX_TYPE_WARNING)) { + lcdDrawText(WARNING_LINE_X, WARNING_LINE_Y+28, STR_WARNING, ALARM_COLOR|DBLSIZE); + } #endif if (text) { @@ -53,6 +84,7 @@ unsigned int countRegisteredThemes = 0; void registerTheme(const Theme * theme) { if (countRegisteredThemes < MAX_REGISTERED_THEMES) { + TRACE("register theme %s", theme->getName()); registeredThemes[countRegisteredThemes++] = theme; } } @@ -70,6 +102,7 @@ const Theme * getTheme(const char * name) void loadTheme(const Theme * new_theme) { + TRACE("load theme %s", new_theme->getName()); theme = new_theme; theme->load(); } @@ -80,5 +113,11 @@ void loadTheme() memset(name, 0, sizeof(name)); strncpy(name, g_eeGeneral.themeName, sizeof(g_eeGeneral.themeName)); const Theme * new_theme = getTheme(name); - loadTheme(new_theme ? new_theme : theme); -} \ No newline at end of file + if (new_theme) { + loadTheme(new_theme); + } + else { + theme->init(); + theme->load(); + } +} diff --git a/radio/src/gui/horus/theme.h b/radio/src/gui/horus/theme.h index 3af0f2c5a..cc50a4ad1 100644 --- a/radio/src/gui/horus/theme.h +++ b/radio/src/gui/horus/theme.h @@ -21,42 +21,59 @@ #ifndef _THEME_H_ #define _THEME_H_ -class Theme; +#define MAX_THEME_OPTIONS 5 +class Theme; void registerTheme(const Theme * theme); +#define MESSAGEBOX_TYPE_INFO 0 +#define MESSAGEBOX_TYPE_QUESTION 1 +#define MESSAGEBOX_TYPE_WARNING 2 +#define MESSAGEBOX_TYPE_ALERT 4 + class Theme { public: - Theme(const char * name, const uint8_t * bitmap): + struct PersistentData { + ZoneOptionValue options[MAX_THEME_OPTIONS]; + }; + + Theme(const char * name, const uint8_t * bitmap, const ZoneOption * options=NULL): name(name), - bitmap(bitmap) + bitmap(bitmap), + options(options) { registerTheme(this); } - const char * getName() const + inline const char * getName() const { return name; } - virtual void drawThumb(uint16_t x, uint16_t y, uint32_t flags) const + inline const ZoneOption * getOptions() const { - extern void lcdDrawBitmap(int x, int y, const uint8_t * bitmap, int offset=0, int height=0, int scale=0); - lcdDrawBitmap(x, y, bitmap); + return options; } + void init() const; + + ZoneOptionValue * getOptionValue(unsigned int index) const; + + virtual void drawThumb(uint16_t x, uint16_t y, uint32_t flags) const; + virtual void load() const = 0; virtual void drawBackground() const; virtual void drawTopbarBackground(const uint8_t * icon) const = 0; - virtual void drawAlertBox(const char * title, const char * text, const char * action) const; + virtual void drawMessageBox(const char * title, const char * text, const char * action, uint32_t flags) const; protected: const char * name; const uint8_t * bitmap; + const ZoneOption * options; }; extern const Theme * theme; diff --git a/radio/src/gui/horus/themes/darkblue.cpp b/radio/src/gui/horus/themes/darkblue.cpp index 59a1ac679..44caf5ef1 100644 --- a/radio/src/gui/horus/themes/darkblue.cpp +++ b/radio/src/gui/horus/themes/darkblue.cpp @@ -20,15 +20,19 @@ #include "opentx.h" -const uint8_t LBM_TOPMENU_BMP_OPENTX[] = { +const uint8_t LBM_TOPMENU_BMP_OPENTX[] __DMA = { #include "bmp_topmenu_opentx.lbm" }; +const uint8_t LBM_THEME_DARKBLUE[] __DMA = { +#include "bmp_darkblue.lbm" +}; + class DarkblueTheme: public Theme { public: - DarkblueTheme(const char * name, const uint8_t * bitmap): - Theme(name, bitmap) + DarkblueTheme(): + Theme("Darkblue", LBM_THEME_DARKBLUE) { } @@ -77,8 +81,4 @@ class DarkblueTheme: public Theme } }; -const uint8_t LBM_THEME_DARKBLUE[] __DMA = { -#include "bmp_darkblue.lbm" -}; - -DarkblueTheme darkblueTheme("Darkblue", LBM_THEME_DARKBLUE); \ No newline at end of file +DarkblueTheme darkblueTheme; diff --git a/radio/src/gui/horus/topbar.cpp b/radio/src/gui/horus/topbar.cpp new file mode 100644 index 000000000..4a4519106 --- /dev/null +++ b/radio/src/gui/horus/topbar.cpp @@ -0,0 +1,100 @@ +/* + * 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" + +unsigned int Topbar::getZonesCount() const +{ + return MAX_TOPBAR_ZONES; +} + +Zone Topbar::getZone(unsigned int index) const +{ + Zone zone; + zone.x = 50 + (TOPBAR_ZONE_WIDTH + 2*TOPBAR_ZONE_MARGIN) * index; + zone.y = TOPBAR_ZONE_MARGIN; + zone.w = TOPBAR_ZONE_WIDTH; + zone.h = MENU_HEADER_HEIGHT - 2*TOPBAR_ZONE_MARGIN; + return zone; +} + +void drawTopBar() +{ + theme->drawTopbarBackground(NULL); + + // USB icon + if (usbPlugged()) { + lcdDrawBitmapPattern(378, 8, LBM_TOPMENU_USB, MENU_TITLE_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]; + lcdDrawSolidFilledRect(390 + i * 6, 38 - height, 4, height, TELEMETRY_RSSI() >= rssiBarsValue[i] ? MENU_TITLE_COLOR : MENU_TITLE_DISABLE_COLOR); + } + + topbar->refresh(); + +#if 0 + // Radio battery - TODO + // putsValueWithUnit(370, 8, g_vbat100mV, UNIT_VOLTS, PREC1|SMLSIZE|MENU_TITLE_COLOR); + // lcdDrawSolidRect(300, 3, 20, 50, MENU_TITLE_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.frsky.voltsSource) { // TODO should not be in frsky struct + TelemetryItem & item = telemetryItems[g_model.frsky.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 : TEXT_COLOR); + lcdDrawSolidFilledRect(ALTITUDE_X, VOLTS_Y, ALTITUDE_W, ALTITUDE_H, TEXT_BGCOLOR); + lcdDrawText(ALTITUDE_X+PADDING, VOLTS_Y+2, "Voltage", att); + putsValueWithUnit(ALTITUDE_X+PADDING, VOLTS_Y+12, value, UNIT_VOLTS, DBLSIZE|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 : TEXT_COLOR); + lcdDrawSolidFilledRect(ALTITUDE_X, ALTITUDE_Y, ALTITUDE_W, ALTITUDE_H, TEXT_BGCOLOR); + lcdDrawText(ALTITUDE_X+PADDING, ALTITUDE_Y+2, "Alt", att); + putsValueWithUnit(ALTITUDE_X+PADDING, ALTITUDE_Y+12, value, UNIT_METERS, DBLSIZE|LEFT|att); + } + } +#endif + +} diff --git a/radio/src/gui/horus/topbar.h b/radio/src/gui/horus/topbar.h new file mode 100644 index 000000000..93d8aaadf --- /dev/null +++ b/radio/src/gui/horus/topbar.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _TOPBAR_H_ +#define _TOPBAR_H_ + +#include "widgets_container.h" + +#define MAX_TOPBAR_ZONES 5 +#define MAX_TOPBAR_OPTIONS 0 +#define TOPBAR_ZONE_WIDTH 60 +#define TOPBAR_ZONE_MARGIN 3 + +class Topbar: public WidgetsContainer +{ + public: + explicit Topbar(PersistentData * persistentData): + WidgetsContainer(persistentData) + { + } + + virtual unsigned int getZonesCount() const; + + virtual Zone getZone(unsigned int index) const; +}; + +#endif // _TOPBAR_H_ diff --git a/radio/src/gui/horus/view_main.cpp b/radio/src/gui/horus/view_main.cpp index 52b16c717..6dcf2346d 100644 --- a/radio/src/gui/horus/view_main.cpp +++ b/radio/src/gui/horus/view_main.cpp @@ -30,6 +30,7 @@ #define POTS_LINE_Y 252 Layout * customScreens[MAX_CUSTOM_SCREENS]; +Topbar * topbar; void drawMainPots() { @@ -79,72 +80,6 @@ void drawTrims(uint8_t flightMode) } } -void drawMainViewTopBar() -{ - const int ALTITUDE_Y = 16; - const int VOLTS_Y = 16+16+30; - const int ALTITUDE_W = 56; - const int ALTITUDE_X = LCD_W-ALTITUDE_Y-ALTITUDE_W; - const int ALTITUDE_H = 30; - const int PADDING = 4; - - theme->drawTopbarBackground(NULL); - - // USB icon - if (usbPlugged()) { - lcdDrawBitmapPattern(378, 8, LBM_TOPMENU_USB, MENU_TITLE_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= rssiBarsValue[i] ? MENU_TITLE_COLOR : MENU_TITLE_DISABLE_COLOR); - } - - // Radio battery - TODO - // putsValueWithUnit(370, 8, g_vbat100mV, UNIT_VOLTS, PREC1|SMLSIZE|MENU_TITLE_COLOR); - // lcdDrawSolidRect(300, 3, 20, 50, MENU_TITLE_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.frsky.voltsSource) { // TODO should not be in frsky struct - TelemetryItem & item = telemetryItems[g_model.frsky.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 : TEXT_COLOR); - lcdDrawSolidFilledRect(ALTITUDE_X, VOLTS_Y, ALTITUDE_W, ALTITUDE_H, TEXT_BGCOLOR); - lcdDrawText(ALTITUDE_X+PADDING, VOLTS_Y+2, "Voltage", att); - putsValueWithUnit(ALTITUDE_X+PADDING, VOLTS_Y+12, value, UNIT_VOLTS, DBLSIZE|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 : TEXT_COLOR); - lcdDrawSolidFilledRect(ALTITUDE_X, ALTITUDE_Y, ALTITUDE_W, ALTITUDE_H, TEXT_BGCOLOR); - lcdDrawText(ALTITUDE_X+PADDING, ALTITUDE_Y+2, "Alt", att); - putsValueWithUnit(ALTITUDE_X+PADDING, ALTITUDE_Y+12, value, UNIT_METERS, DBLSIZE|LEFT|att); - } - } -} - bool isViewAvailable(int index) { if (index <= VIEW_CHANNELS) @@ -187,7 +122,7 @@ void onMainViewMenu(const char *result) chainMenu(menuStatisticsView); } else if (result == STR_SETUP_SCREENS) { - pushMenu(menuTabMainviews[1]); + pushMenu(menuTabScreensSetup[1]); } else if (result == STR_ABOUT_US) { chainMenu(menuAboutView); diff --git a/radio/src/gui/horus/widget.cpp b/radio/src/gui/horus/widget.cpp index 0b73b5bf5..441e85477 100644 --- a/radio/src/gui/horus/widget.cpp +++ b/radio/src/gui/horus/widget.cpp @@ -25,6 +25,7 @@ unsigned int countRegisteredWidgets = 0; void registerWidget(const WidgetFactory * factory) { if (countRegisteredWidgets < MAX_REGISTERED_WIDGETS) { + TRACE("register widget %s", factory->getName()); registeredWidgets[countRegisteredWidgets++] = factory; } } diff --git a/radio/src/gui/horus/widget.h b/radio/src/gui/horus/widget.h index 1f98d7b9c..da75647ce 100644 --- a/radio/src/gui/horus/widget.h +++ b/radio/src/gui/horus/widget.h @@ -23,7 +23,7 @@ #include -#define MAX_WIDGET_OPTIONS 4 +#define MAX_WIDGET_OPTIONS 5 struct Zone { @@ -45,6 +45,7 @@ struct ZoneOption Source, Bool, String, + File, TextSize, Timer, Switch, @@ -75,19 +76,16 @@ class Widget { } - const WidgetFactory * getFactory() const + inline const WidgetFactory * getFactory() const { return factory; } - virtual ZoneOptionValue getOptionValue(unsigned int index) const - { - return persistentData->options[index]; - } + inline const ZoneOption * getOptions() const; - virtual void setOptionValue(unsigned int index, ZoneOptionValue value) const + inline ZoneOptionValue * getOptionValue(unsigned int index) const { - persistentData->options[index] = value; + return &persistentData->options[index]; } virtual void refresh() = 0; @@ -103,7 +101,7 @@ void registerWidget(const WidgetFactory * factory); class WidgetFactory { public: - WidgetFactory(const char * name, const ZoneOption * options): + WidgetFactory(const char * name, const ZoneOption * options=NULL): name(name), options(options) { @@ -126,11 +124,12 @@ class WidgetFactory if (options) { int i = 0; for (const ZoneOption * option = options; option->name; option++) { - persistentData->options[i++] = option->deflt; + // TODO compiler bug? The CPU freezes ... persistentData->options[i++] = option->deflt; + memcpy(&persistentData->options[i++], &option->deflt, sizeof(ZoneOptionValue)); } } } - + virtual Widget * create(const Zone & zone, Widget::PersistentData * persistentData, bool init=true) const = 0; protected: @@ -157,6 +156,11 @@ class BaseWidgetFactory: public WidgetFactory } }; +inline const ZoneOption * Widget::getOptions() const +{ + return getFactory()->getOptions(); +} + #define MAX_REGISTERED_WIDGETS 10 extern unsigned int countRegisteredWidgets; extern const WidgetFactory * registeredWidgets[MAX_REGISTERED_WIDGETS]; diff --git a/radio/src/gui/horus/widgets.cpp b/radio/src/gui/horus/widgets.cpp index 131b4fb67..c83ac5635 100644 --- a/radio/src/gui/horus/widgets.cpp +++ b/radio/src/gui/horus/widgets.cpp @@ -426,7 +426,7 @@ int16_t editGVarFieldValue(coord_t x, coord_t y, int16_t value, int16_t min, int void drawSleepBitmap() { lcdClear(); - lcdDrawAlphaBitmap((LCD_W-SLEEP_BITMAP_WIDTH)/2, (LCD_H-SLEEP_BITMAP_HEIGHT)/2, LBM_SLEEP); + lcdDrawBitmap((LCD_W-SLEEP_BITMAP_WIDTH)/2, (LCD_H-SLEEP_BITMAP_HEIGHT)/2, LBM_SLEEP); lcdRefresh(); } diff --git a/radio/src/gui/horus/widgets.h b/radio/src/gui/horus/widgets.h index 123e011bb..ef1daf95b 100644 --- a/radio/src/gui/horus/widgets.h +++ b/radio/src/gui/horus/widgets.h @@ -78,7 +78,7 @@ void drawSleepBitmap(); void drawShutdownBitmap(uint32_t index); // Main view standard widgets -void drawMainViewTopBar(); +void drawTopBar(); void drawMainPots(); void drawTrims(uint8_t flightMode); diff --git a/radio/src/gui/horus/widgets/gauge.cpp b/radio/src/gui/horus/widgets/gauge.cpp index 58b3c845b..d33896c20 100644 --- a/radio/src/gui/horus/widgets/gauge.cpp +++ b/radio/src/gui/horus/widgets/gauge.cpp @@ -63,9 +63,9 @@ void GaugeWidget::refresh() // Gauge lcdSetColor(color); - lcdDrawSolidFilledRect(zone.x, zone.y + 15, zone.w, 16, TEXT_INVERTED_COLOR); - lcdDrawNumber((percent >= 100 ? 20 : (percent >= 10 ? 10 : 0)) + zone.x+zone.w/2, zone.y + 16, percent, SMLSIZE | CUSTOM_COLOR, 0, NULL, "%"); - lcdInvertRect(zone.x + w, zone.y + 15, zone.w - w, 16, CUSTOM_COLOR); + lcdDrawSolidFilledRect(zone.x, zone.y + 16, zone.w, 16, TEXT_INVERTED_COLOR); + lcdDrawNumber((percent >= 100 ? 20 : (percent >= 10 ? 10 : 0)) + zone.x+zone.w/2, zone.y + 17, percent, SMLSIZE | CUSTOM_COLOR, 0, NULL, "%"); + lcdInvertRect(zone.x + w, zone.y + 16, zone.w - w, 16, CUSTOM_COLOR); } BaseWidgetFactory gaugeWidget("Gauge", GaugeWidget::options); diff --git a/radio/src/gui/horus/widgets/modelpanel.cpp b/radio/src/gui/horus/widgets/modelbmp.cpp similarity index 72% rename from radio/src/gui/horus/widgets/modelpanel.cpp rename to radio/src/gui/horus/widgets/modelbmp.cpp index 2d247a4d8..18bb47084 100644 --- a/radio/src/gui/horus/widgets/modelpanel.cpp +++ b/radio/src/gui/horus/widgets/modelbmp.cpp @@ -20,10 +20,10 @@ #include "opentx.h" -class ModelPanelWidget: public Widget +class ModelBitmapWidget: public Widget { public: - ModelPanelWidget(const WidgetFactory * factory, const Zone & zone, Widget::PersistentData * persistentData): + ModelBitmapWidget(const WidgetFactory * factory, const Zone & zone, Widget::PersistentData * persistentData): Widget(factory, zone, persistentData) { } @@ -31,26 +31,24 @@ class ModelPanelWidget: public Widget virtual void refresh(); }; -void ModelPanelWidget::refresh() +void ModelBitmapWidget::refresh() { if (zone.h >= MODEL_BITMAP_HEIGHT) { lcdDrawFilledRect(zone.x, zone.y, zone.w, zone.h, SOLID, MAINVIEW_PANES_COLOR | OPACITY(5)); lcdDrawBitmapPattern(zone.x + 6, zone.y + 4, LBM_MODEL_ICON, MAINVIEW_GRAPHICS_COLOR); lcdDrawSizedText(zone.x + 45, zone.y + 10, g_model.header.name, LEN_MODEL_NAME, ZCHAR | SMLSIZE); lcdDrawSolidFilledRect(zone.x + 39, zone.y + 27, zone.w - 48, 2, MAINVIEW_GRAPHICS_COLOR); - int scale = getBitmapScale(modelBitmap, zone.w, zone.h - 25); + float scale = getBitmapScale(modelBitmap, zone.w, zone.h - 25); int width = getBitmapScaledSize(getBitmapWidth(modelBitmap), scale); int height = getBitmapScaledSize(getBitmapHeight(modelBitmap), scale); lcdDrawBitmap(zone.x + (zone.w - width) / 2, zone.y + zone.h - height / 2 - height / 2, modelBitmap, 0, 0, scale); } else { - int scale = getBitmapScale(modelBitmap, zone.w, zone.h); + float scale = getBitmapScale(modelBitmap, 1000, zone.h); int width = getBitmapScaledSize(getBitmapWidth(modelBitmap), scale); int height = getBitmapScaledSize(getBitmapHeight(modelBitmap), scale); - lcdDrawFilledRect(zone.x, zone.y, zone.w, height, SOLID, MAINVIEW_PANES_COLOR | OPACITY(5)); - lcdDrawSizedText(zone.x + 5, zone.y + 10, g_model.header.name, LEN_MODEL_NAME, ZCHAR | SMLSIZE); - lcdDrawBitmap(zone.x + zone.w - width, zone.y, modelBitmap, 0, 0, scale); + lcdDrawBitmap(zone.x + (zone.w - width) / 2, zone.y + (zone.h - height) / 2, modelBitmap, 0, 0, scale); } } -BaseWidgetFactory modelPanelWidget("ModelPanel", NULL); +BaseWidgetFactory modelBitmapWidget("ModelBmp", NULL); diff --git a/radio/src/gui/horus/widgets/timer.cpp b/radio/src/gui/horus/widgets/timer.cpp index 86d76d451..ad5602837 100644 --- a/radio/src/gui/horus/widgets/timer.cpp +++ b/radio/src/gui/horus/widgets/timer.cpp @@ -41,21 +41,35 @@ const ZoneOption TimerWidget::options[] = { void TimerWidget::refresh() { uint32_t index = persistentData->options[0].unsignedValue; - TimerData & timerData = g_model.timers[index]; TimerState & timerState = timersStates[index]; - lcdDrawBitmapPattern(zone.x, zone.y, LBM_TIMER_BACKGROUND, MAINVIEW_PANES_COLOR); - if (timerData.start) { - lcdDrawBitmapPatternPie(zone.x+2, zone.y+3, LBM_RSCALE, MAINVIEW_GRAPHICS_COLOR, 0, timerState.val <= 0 ? 360 : 360*(timerData.start-timerState.val)/timerData.start); + + if (zone.w >= 180 && zone.h >= 70) { + lcdDrawBitmapPattern(zone.x, zone.y, LBM_TIMER_BACKGROUND, MAINVIEW_PANES_COLOR); + if (timerData.start) { + lcdDrawBitmapPatternPie( + zone.x + 2, + zone.y + 3, LBM_RSCALE, MAINVIEW_GRAPHICS_COLOR, 0, + timerState.val <= 0 ? 360 : 360 * (timerData.start - timerState.val) / timerData.start); + } + else { + lcdDrawBitmapPattern(zone.x + 3, zone.y + 4, LBM_TIMER, MAINVIEW_GRAPHICS_COLOR); + } + putsTimer(zone.x + 76, zone.y + 31, abs(timerState.val), TEXT_COLOR | DBLSIZE | LEFT); + if (ZLEN(timerData.name) > 0) { + lcdDrawSizedText(zone.x + 78, zone.y + 20, timerData.name, LEN_TIMER_NAME, ZCHAR | SMLSIZE | TEXT_COLOR); + } + drawStringWithIndex(zone.x + 137, zone.y + 17, "TMR", index + 1, SMLSIZE | TEXT_COLOR); } else { - lcdDrawBitmapPattern(zone.x+3, zone.y+4, LBM_TIMER, MAINVIEW_GRAPHICS_COLOR); + drawStringWithIndex(zone.x, zone.y, "TMR", index + 1, SMLSIZE | TEXT_INVERTED_COLOR); + if (zone.w > 100) { + putsTimer(zone.x, zone.y + 16, abs(timerState.val), TEXT_INVERTED_COLOR | LEFT | MIDSIZE); + } + else { + putsTimer(zone.x, zone.y + 14, abs(timerState.val), TEXT_INVERTED_COLOR | LEFT); + } } - putsTimer(zone.x+76, zone.y+31, abs(timerState.val), TEXT_COLOR|DBLSIZE|LEFT); - if (ZLEN(timerData.name) > 0) { - lcdDrawSizedText(zone.x+78, zone.y+20, timerData.name, LEN_TIMER_NAME, ZCHAR|SMLSIZE|TEXT_COLOR); - } - drawStringWithIndex(zone.x+137, zone.y+17, "TMR", index+1, SMLSIZE|TEXT_COLOR); } BaseWidgetFactory timerWidget("Timer", TimerWidget::options); diff --git a/radio/src/gui/horus/widgets/value.cpp b/radio/src/gui/horus/widgets/value.cpp index 8c679484a..9ac868cc5 100644 --- a/radio/src/gui/horus/widgets/value.cpp +++ b/radio/src/gui/horus/widgets/value.cpp @@ -44,23 +44,35 @@ void ValueWidget::refresh() mixsrc_t field = persistentData->options[0].unsignedValue; - LcdFlags color = TEXT_COLOR; + LcdFlags color = TEXT_INVERTED_COLOR; int x = zone.x; int y = zone.y; - lcdDrawFilledRect(zone.x, zone.y, zone.w, zone.h, SOLID, MAINVIEW_PANES_COLOR | OPACITY(5)); + // lcdDrawFilledRect(zone.x, zone.y, zone.w, zone.h, SOLID, MAINVIEW_PANES_COLOR | OPACITY(5)); - int xValue, yValue; - LcdFlags attr; - if (zone.h < 50) { + int xValue, yValue, xLabel, yLabel; + LcdFlags attrValue, attrLabel=0; + if (zone.w < 120 && zone.h < 50) { + xValue = x; + yValue = y+14; + xLabel = x; + yLabel = y; + attrValue = LEFT | NO_UNIT | MIDSIZE; + attrLabel = SMLSIZE; + } + else if (zone.h < 50) { xValue = x+zone.w-NUMBERS_PADDING; yValue = y-2; - attr = NO_UNIT; + xLabel = x+NUMBERS_PADDING; + yLabel = y+2; + attrValue = NO_UNIT | DBLSIZE; } else { xValue = x+NUMBERS_PADDING; yValue = y+16; - attr = LEFT; + xLabel = x+NUMBERS_PADDING; + yLabel = y+2; + attrValue = LEFT | DBLSIZE; } if (field >= MIXSRC_FIRST_TIMER && field <= MIXSRC_LAST_TIMER) { @@ -69,7 +81,7 @@ void ValueWidget::refresh() color = ALARM_COLOR; } putsMixerSource(x+NUMBERS_PADDING, y+2, field, color); - putsTimer(xValue, yValue, abs(timerState.val), attr|DBLSIZE|color); + putsTimer(xValue, yValue, abs(timerState.val), attrValue|DBLSIZE|color); return; } @@ -83,8 +95,8 @@ void ValueWidget::refresh() } } - putsMixerSource(x+NUMBERS_PADDING, y+2, field, color); - putsChannel(xValue, yValue, field, attr|DBLSIZE|color); + putsMixerSource(xLabel, yLabel, field, attrLabel|color); + putsChannel(xValue, yValue, field, attrValue|color); } BaseWidgetFactory ValueWidget("Value", ValueWidget::options); diff --git a/radio/src/gui/horus/widgets_container.h b/radio/src/gui/horus/widgets_container.h new file mode 100644 index 000000000..d12b61b24 --- /dev/null +++ b/radio/src/gui/horus/widgets_container.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _WIDGETS_CONTAINER_H_ +#define _WIDGETS_CONTAINER_H_ + +#include "widget.h" + +class WidgetsContainerInterface +{ + public: + virtual unsigned int getZonesCount() const = 0; + + virtual Zone getZone(unsigned int index) const = 0; + + inline Widget * getWidget(unsigned int index) + { + return widgets[index]; + } + + inline void setWidget(unsigned int index, Widget * widget) + { + widgets[index] = widget; + } + + virtual void createWidget(unsigned int index, const WidgetFactory * factory) = 0; + + protected: + Widget ** widgets; +}; + +template +class WidgetsContainer: public WidgetsContainerInterface +{ + public: + struct ZonePersistentData { + char widgetName[10]; + Widget::PersistentData widgetData; + }; + + struct PersistentData { + ZonePersistentData zones[N]; + ZoneOptionValue options[O]; + }; + + public: + WidgetsContainer(PersistentData * persistentData): + persistentData(persistentData) + { + widgets = (Widget **)calloc(N, sizeof(Widget *)); + } + + virtual ~WidgetsContainer() + { + if (widgets) { + for (uint8_t i=0; izones[index].widgetName, 0, sizeof(persistentData->zones[index].widgetName)); + strncpy(persistentData->zones[index].widgetName, factory->getName(), sizeof(persistentData->zones[index].widgetName)); + widgets[index] = factory->create(getZone(index), &persistentData->zones[index].widgetData); + } + } + + virtual void create() + { + memset(persistentData, 0, sizeof(PersistentData)); + } + + virtual void load() + { + if (widgets) { + unsigned int count = getZonesCount(); + for (unsigned int i=0; izones[i].widgetName[0]) { + char name[sizeof(persistentData->zones[i].widgetName)+1]; + memset(name, 0, sizeof(name)); + strncpy(name, persistentData->zones[i].widgetName, sizeof(persistentData->zones[i].widgetName)); + widgets[i] = loadWidget(name, getZone(i), &persistentData->zones[i].widgetData); + } + } + } + } + + inline ZoneOptionValue * getOptionValue(unsigned int index) const + { + return &persistentData->options[index]; + } + + virtual unsigned int getZonesCount() const = 0; + + virtual Zone getZone(unsigned int index) const = 0; + + virtual void refresh() + { + if (widgets) { + for (int i=0; irefresh(); + } + } + } + } + + protected: + PersistentData * persistentData; +}; + +#endif // _WIDGETS_CONTAINER_H_ diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index c8045f727..15c3c9a1f 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -776,13 +776,61 @@ void exec(int function, int nresults=0) } } +ZoneOption * createOptionsArray(int reference) +{ + if (reference == 0) { + return NULL; + } + + int count = 0; + lua_rawgeti(L, LUA_REGISTRYINDEX, reference); + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + count++; + } + + ZoneOption * options = (ZoneOption *)malloc(sizeof(ZoneOption) * (count+1)); + + lua_rawgeti(L, LUA_REGISTRYINDEX, reference); + ZoneOption * option = options; + for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { + luaL_checktype(L, -2, LUA_TNUMBER); // key is number + luaL_checktype(L, -1, LUA_TTABLE); // value is table + uint8_t field = 0; + for (lua_pushnil(L); lua_next(L, -2) && field<3; lua_pop(L, 1), field++) { + switch (field) { + case 0: + luaL_checktype(L, -2, LUA_TNUMBER); // key is number + luaL_checktype(L, -1, LUA_TSTRING); // value is string + option->name = lua_tostring(L, -1); + break; + case 1: + luaL_checktype(L, -2, LUA_TNUMBER); // key is number + luaL_checktype(L, -1, LUA_TNUMBER); // value is number + option->type = (ZoneOption::Type)lua_tointeger(L, -1); + break; + case 2: + luaL_checktype(L, -2, LUA_TNUMBER); // key is number + luaL_checktype(L, -1, LUA_TNUMBER); // value is number + option->deflt.signedValue = lua_tointeger(L, -1); + break; + } + } + TRACE("option %s %d %d", option->name, option->type, option->deflt.signedValue); + option++; + } + + option->name = NULL; // sentinel + + return options; +} + class LuaTheme: public Theme { friend void luaLoadThemeCallback(); public: - LuaTheme(const char * name, const uint8_t * bitmap): - Theme(name, bitmap), + LuaTheme(const char * name, const uint8_t * bitmap, int options): + Theme(name, bitmap, createOptionsArray(options)), loadFunction(0), drawBackgroundFunction(0), drawTopbarBackgroundFunction(0), @@ -823,7 +871,7 @@ class LuaTheme: public Theme void luaLoadThemeCallback() { const char * name=NULL, * bitmap=NULL; - int loadFunction=0, drawBackgroundFunction=0, drawTopbarBackgroundFunction=0; + int themeOptions=0, loadFunction=0, drawBackgroundFunction=0, drawTopbarBackgroundFunction=0; luaL_checktype(L, -1, LUA_TTABLE); @@ -835,6 +883,10 @@ void luaLoadThemeCallback() else if (!strcmp(key, "bitmap")) { bitmap = luaL_checkstring(L, -1); } + else if (!strcmp(key, "options")) { + themeOptions = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushnil(L); + } else if (!strcmp(key, "load")) { loadFunction = luaL_ref(L, LUA_REGISTRYINDEX); lua_pushnil(L); @@ -854,8 +906,9 @@ void luaLoadThemeCallback() strcpy(path, THEMES_PATH "/"); strcpy(path+sizeof(THEMES_PATH), bitmap); uint8_t * bitmapData = (uint8_t *)malloc(BITMAP_BUFFER_SIZE(51, 31)); + TRACE("path=%s bitmapData=%p %d %p", path, bitmapData, BITMAP_BUFFER_SIZE(51, 31), path); bmpLoad(bitmapData, path, 51, 31); - LuaTheme * theme = new LuaTheme(name, bitmapData); + LuaTheme * theme = new LuaTheme(name, bitmapData, themeOptions); theme->loadFunction = loadFunction; theme->drawBackgroundFunction = drawBackgroundFunction; theme->drawTopbarBackgroundFunction = drawTopbarBackgroundFunction; @@ -882,55 +935,6 @@ class LuaWidget: public Widget int widgetData; }; -ZoneOption * createOptionsArray(int reference) -{ - int count = 0; - lua_rawgeti(L, LUA_REGISTRYINDEX, reference); - for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { - count++; - } - - ZoneOption * options = (ZoneOption *)malloc(sizeof(ZoneOption) * (count+1)); - - lua_rawgeti(L, LUA_REGISTRYINDEX, reference); - ZoneOption * option = options; - for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) { - luaL_checktype(L, -2, LUA_TNUMBER); // key is number - luaL_checktype(L, -1, LUA_TTABLE); // value is table - // const char * key = NULL; - // ZoneOption::Type type = ZoneOption::Integer; - // int val = 0; - uint8_t field = 0; - for (lua_pushnil(L); lua_next(L, -2) && field<3; lua_pop(L, 1), field++) { - switch (field) { - case 0: - luaL_checktype(L, -2, LUA_TNUMBER); // key is number - luaL_checktype(L, -1, LUA_TSTRING); // value is string - option->name = lua_tostring(L, -1); - break; - case 1: - luaL_checktype(L, -2, LUA_TNUMBER); // key is number - luaL_checktype(L, -1, LUA_TNUMBER); // value is number - option->type = (ZoneOption::Type)lua_tointeger(L, -1); - break; - case 2: - luaL_checktype(L, -2, LUA_TNUMBER); // key is number - luaL_checktype(L, -1, LUA_TNUMBER); // value is number - option->deflt.signedValue = lua_tointeger(L, -1); - break; - } - } - - // TRACE("option[%d] = %s %d %d", i, key, type, val); - option++; - // options[i++]. = (ZoneOption) { key, type, { .signedValue = val } }; - } - - option->name = NULL; // sentinel - - return options; -} - void l_pushtableint(const char * key, int value) { lua_pushstring(L, key); @@ -998,7 +1002,7 @@ void LuaWidget::refresh() void luaLoadWidgetCallback() { - const char * name=NULL, * options=NULL; + const char * name=NULL; int widgetOptions=0, createFunction=0, refreshFunction=0; luaL_checktype(L, -1, LUA_TTABLE); @@ -1071,6 +1075,7 @@ void luaLoadFiles(const char * directory, void (*callback)()) int pathlen = strlen(path); FRESULT res = f_opendir(&dir, path); /* Open the directory */ + if (res == FR_OK) { path[pathlen++] = '/'; for (;;) { @@ -1078,12 +1083,15 @@ void luaLoadFiles(const char * directory, void (*callback)()) if (res != FR_OK || fno.fname[0] == 0) break; /* Break on error or end of dir */ fn = * fno.lfname ? fno.lfname : fno.fname; uint8_t len = strlen(fn); - // Eliminates directories / non wav files + // Eliminates directories / non scripts files if (len < 5 || strcasecmp(fn+len-4, SCRIPTS_EXT) || (fno.fattrib & AM_DIR)) continue; strcpy(&path[pathlen], fn); luaLoadFile(path, callback); } } + else { + TRACE("f_opendir(%s) failed, code=%d", path, res); + } } #endif diff --git a/radio/src/myeeprom.h b/radio/src/myeeprom.h index 24bce8ee8..ec1cb8431 100644 --- a/radio/src/myeeprom.h +++ b/radio/src/myeeprom.h @@ -440,8 +440,7 @@ enum UartModes { char anaNames[NUM_STICKS+NUM_POTS][LEN_ANA_NAME]; \ char currModelFilename[LEN_MODEL_FILENAME+1]; \ uint8_t bluetoothEnable; \ - char bluetoothName[LEN_BLUETOOTH_NAME]; \ - char themeName[8]; + char bluetoothName[LEN_BLUETOOTH_NAME]; #elif defined(PCBFLAMENCO) #define LEN_SWITCH_NAME 3 #define LEN_ANA_NAME 3 @@ -839,7 +838,32 @@ PACK(typedef struct { #define SWITCH_EXISTS(x) true #endif -#define ALTERNATE_VIEW 0x10 +#define ALTERNATE_VIEW 0x10 + +#if defined(COLORLCD) + #include "layout.h" + #include "theme.h" + #include "topbar.h" + #define SWITCHES_WARNING_DATA \ + swarnstate_t switchWarningState; + PACK(typedef struct { + char layoutName[10]; + Layout::PersistentData layoutData; + }) CustomScreenData; + #define CUSTOM_SCREENS_DATA \ + CustomScreenData screenData[MAX_CUSTOM_SCREENS]; \ + Topbar::PersistentData topbarData; + #define THEME_DATA \ + char themeName[8]; \ + Theme::PersistentData themeData; +#else + #define SWITCHES_WARNING_DATA \ + swarnstate_t switchWarningState; \ + swarnenable_t switchWarningEnable; + #define CUSTOM_SCREENS_DATA + #define THEME_DATA +#endif + PACK(typedef struct { uint8_t version; uint16_t variant; @@ -885,6 +909,8 @@ PACK(typedef struct { EXTRA_GENERAL_FIELDS + THEME_DATA + }) EEGeneral; #define SWITCHES_DELAY() uint8_t(15+g_eeGeneral.switchesDelay) @@ -2288,23 +2314,6 @@ enum DisplayTrims DISPLAY_TRIMS_ALWAYS }; -#if defined(COLORLCD) - #include "layout.h" - #define SWITCHES_WARNING_DATA \ - swarnstate_t switchWarningState; - PACK(typedef struct { - char layoutName[10]; - Layout::PersistentData layoutData; - }) CustomScreenData; - #define CUSTOM_SCREENS_DATA \ - CustomScreenData screenData[MAX_CUSTOM_SCREENS]; -#else - #define SWITCHES_WARNING_DATA \ - swarnstate_t switchWarningState; \ - swarnenable_t switchWarningEnable; - #define CUSTOM_SCREENS_DATA -#endif - PACK(typedef struct { ModelHeader header; TimerData timers[MAX_TIMERS]; diff --git a/radio/src/opentx.cpp b/radio/src/opentx.cpp index 735a00e0f..e3296cb33 100644 --- a/radio/src/opentx.cpp +++ b/radio/src/opentx.cpp @@ -2469,15 +2469,25 @@ uint16_t stackAvailable() void opentxInit(OPENTX_INIT_ARGS) { +#if defined(DEBUG) && defined(USB_SERIAL) + CoTickDelay(5000); // 10s +#endif + TRACE("opentxInit()"); + sdInit(); + #if defined(COLORLCD) + topbar = new Topbar(&g_model.topbarData); luaInit(); - loadTheme(); #endif storageReadAll(); +#if defined(COLORLCD) + loadTheme(); +#endif + #if defined(CPUARM) if (UNEXPECTED_SHUTDOWN()) { unexpectedShutdown = 1; diff --git a/radio/src/simu.cpp b/radio/src/simu.cpp index d45c38de5..db2a904a6 100644 --- a/radio/src/simu.cpp +++ b/radio/src/simu.cpp @@ -352,7 +352,6 @@ long Open9xSim::onTimeout(FXObject*, FXSelector, void*) updateKeysAndSwitches(); #if defined(PCBHORUS) - extern rotenc_t rotencValue; #define ROTENC_VALUE rotencValue #else #define ROTENC_VALUE g_rotenc[0] diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index c865eff4d..1e1bde4e5 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -337,12 +337,17 @@ void * main_thread(void *) menuHandlers[1] = menuModelSelect; #if defined(COLORLCD) + topbar = new Topbar(&g_model.topbarData); luaInit(); - loadTheme(); + // TODO the theme is not initialized, in case of sdcard error, we should have something strange #endif storageReadAll(); // load general setup and selected model +#if defined(COLORLCD) + loadTheme(); +#endif + #if defined(SIMU_DISKIO) f_mount(&g_FATFS_Obj, "", 1); // call sdGetFreeSectors() now because f_getfree() takes a long time first time it's called diff --git a/radio/src/targets/taranis/board_taranis.h b/radio/src/targets/taranis/board_taranis.h index 0146e2ac4..3d9657fc2 100644 --- a/radio/src/targets/taranis/board_taranis.h +++ b/radio/src/targets/taranis/board_taranis.h @@ -222,6 +222,7 @@ uint32_t readTrims(void); #if defined(REV9E) // Rotary Encoder driver +extern int32_t rotencValue; void rotencInit(void); void rotencEnd(void); void checkRotaryEncoder(void); diff --git a/radio/src/tasks_arm.h b/radio/src/tasks_arm.h index b07b5f4d7..d01a516b7 100644 --- a/radio/src/tasks_arm.h +++ b/radio/src/tasks_arm.h @@ -1,25 +1,25 @@ -/* - * Copyright (C) OpenTX - * - * Based on code named - * th9x - http://code.google.com/p/th9x - * er9x - http://code.google.com/p/er9x - * gruvin9x - http://code.google.com/p/gruvin9x - * - * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ - -#ifndef _TASKS_ARM_H_ -#define _TASKS_ARM_H_ +/* + * Copyright (C) OpenTX + * + * Based on code named + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _TASKS_ARM_H_ +#define _TASKS_ARM_H_ #if !defined(SIMU) extern "C" { @@ -73,4 +73,4 @@ extern TaskStack audioStack; void tasksStart(); -#endif // _TASKS_ARM_H_ +#endif // _TASKS_ARM_H_ diff --git a/radio/src/translations/en.h.txt b/radio/src/translations/en.h.txt index 206019a9e..f17745855 100644 --- a/radio/src/translations/en.h.txt +++ b/radio/src/translations/en.h.txt @@ -942,7 +942,7 @@ #define TR_RESET_TELEMETRY "Reset Telemetry" #define TR_STATISTICS "Statistics" #define TR_ABOUT_US "About" -#define TR_SETUP_SCREENS "Setup screens" +#define TR_SETUP_SCREENS "Setup screens" #define TR_AND_SWITCH "AND Switch" #define TR_SF "SF" #define TR_GF "GF" @@ -951,8 +951,8 @@ #define TR_BYTES "bytes" #define TR_MODULE_BIND BUTTON(TR("Bnd", "Bind")) #define TR_MODULE_RANGE BUTTON(TR("Rng", "Range")) -#define TR_RESET_BTN "[Reset]" -#define TR_SET "[Set]" +#define TR_RESET_BTN BUTTON("Reset") +#define TR_SET BUTTON("Set") #define TR_TRAINER "Trainer Port" #define TR_ANTENNAPROBLEM CENTER "TX Antenna problem!" #define TR_MODELIDUSED TR("ID already used","Model ID already used")