diff --git a/radio/src/datastructs.h b/radio/src/datastructs.h index 3ff6089b8..25861a1c3 100644 --- a/radio/src/datastructs.h +++ b/radio/src/datastructs.h @@ -86,7 +86,7 @@ static inline void check_struct() CHKSIZE(ModelHeader, 131); CHKSIZE(CurveHeader, 4); CHKSIZE(CustomScreenData, 852); - CHKTYPE(TopBarPersistentData, 300); + CHKTYPE(TopBarPersistentData, 444); #elif defined(PCBNV14) // TODO #else @@ -146,14 +146,14 @@ static inline void check_struct() #elif defined(PCBHORUS) #if defined(PCBX10) CHKSIZE(RadioData, 916); - CHKSIZE(ModelData, 15463); + CHKSIZE(ModelData, 15607); #else CHKSIZE(RadioData, 916); - CHKSIZE(ModelData, 15463); + CHKSIZE(ModelData, 15607); #endif #elif defined(PCBNV14) CHKSIZE(RadioData, 916); - CHKSIZE(ModelData, 15319); + CHKSIZE(ModelData, 15463); #endif #undef CHKSIZE diff --git a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp index a3be4f255..b8a907261 100644 --- a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp +++ b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp @@ -24,67 +24,6 @@ constexpr uint32_t TOPBAR_REFRESH = 1000 / 10; // 10 Hz -static const uint8_t _LBM_DOT[] = { -#include "mask_dot.lbm" -}; - -static const uint8_t _LBM_TOPMENU_GPS[] = { -#include "mask_topmenu_gps_18.lbm" -}; - -static const uint8_t _LBM_TOPMENU_USB[] = { -#include "mask_topmenu_usb.lbm" -}; - -static const uint8_t _LBM_TOPMENU_VOLUME_0[] = { -#include "mask_volume_0.lbm" -}; - -static const uint8_t _LBM_TOPMENU_VOLUME_1[] = { -#include "mask_volume_1.lbm" -}; - -static const uint8_t _LBM_TOPMENU_VOLUME_2[] = { -#include "mask_volume_2.lbm" -}; - -static const uint8_t _LBM_TOPMENU_VOLUME_3[] = { -#include "mask_volume_3.lbm" -}; - -static const uint8_t _LBM_TOPMENU_VOLUME_4[] = { -#include "mask_volume_4.lbm" -}; - -static const uint8_t _LBM_TOPMENU_VOLUME_SCALE[] = { -#include "mask_volume_scale.lbm" -}; - -static const uint8_t _LBM_TOPMENU_TXBATT[] = { -#include "mask_txbat.lbm" -}; - -static const uint8_t _LBM_TOPMENU_TXBATT_CHARGING[] = { -#include "mask_txbat_charging.lbm" -}; - -static const uint8_t _LBM_TOPMENU_ANTENNA[] = { -#include "mask_antenna.lbm" -}; - -STATIC_LZ4_BITMAP(LBM_DOT); -STATIC_LZ4_BITMAP(LBM_TOPMENU_GPS); -STATIC_LZ4_BITMAP(LBM_TOPMENU_USB); -STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_0); -STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_1); -STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_2); -STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_3); -STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_4); -STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_SCALE); -STATIC_LZ4_BITMAP(LBM_TOPMENU_TXBATT); -STATIC_LZ4_BITMAP(LBM_TOPMENU_TXBATT_CHARGING); -STATIC_LZ4_BITMAP(LBM_TOPMENU_ANTENNA); - TopbarImpl::TopbarImpl(Window * parent) : TopbarImplBase(parent, {0, 0, LCD_W, MENU_HEADER_HEIGHT}, &g_model.topbarData) { @@ -92,21 +31,21 @@ TopbarImpl::TopbarImpl(Window * parent) : unsigned int TopbarImpl::getZonesCount() const { -#if defined(INTERNAL_GPS) - if (hasSerialMode(UART_MODE_GPS) != -1) { - return MAX_TOPBAR_ZONES-1; - } -#endif return MAX_TOPBAR_ZONES; } rect_t TopbarImpl::getZone(unsigned int index) const { + if (index == MAX_TOPBAR_ZONES - 1) { + coord_t size = LCD_W - 48 - (MAX_TOPBAR_ZONES - 1) * (TOPBAR_ZONE_WIDTH + TOPBAR_ZONE_HMARGIN); + return {LCD_W - size, TOPBAR_ZONE_VMARGIN, size, TOPBAR_ZONE_HEIGHT}; + } + return { - coord_t(49 + (TOPBAR_ZONE_WIDTH + 2 * TOPBAR_ZONE_MARGIN) * index), - TOPBAR_ZONE_MARGIN, + coord_t(48 + (TOPBAR_ZONE_WIDTH + TOPBAR_ZONE_HMARGIN) * index), + TOPBAR_ZONE_VMARGIN, TOPBAR_ZONE_WIDTH, - TOPBAR_HEIGHT + TOPBAR_ZONE_HEIGHT }; } @@ -139,137 +78,8 @@ coord_t TopbarImpl::getVisibleHeight(float visible) const // 0.0 -> 1.0 void TopbarImpl::paint(BitmapBuffer * dc) { + dc->drawSolidFilledRect(0, 0, width(), height(), COLOR_THEME_SECONDARY1); EdgeTxTheme::instance()->drawHeaderIcon(dc, ICON_EDGETX); - const TimerOptions timerOptions = {.options = SHOW_TIME}; - struct gtm t; - gettime(&t); - char str[10]; -#if defined(TRANSLATIONS_CN) || defined(TRANSLATIONS_TW) - sprintf(str, "%02d-%02d", t.tm_mon + 1, t.tm_mday); -#else - sprintf(str, "%d %s", t.tm_mday, STR_MONTHS[t.tm_mon]); -#endif - //dc->drawSolidVerticalLine(DATETIME_SEPARATOR_X, 7, 31, COLOR_THEME_SECONDARY1); - dc->drawText(DATETIME_MIDDLE, DATETIME_LINE1, str, FONT(XS) | CENTERED | COLOR_THEME_PRIMARY2); - getTimerString(str, getValue(MIXSRC_TX_TIME), timerOptions); - dc->drawText(DATETIME_MIDDLE, DATETIME_LINE2, str, FONT(XS) | CENTERED | COLOR_THEME_PRIMARY2); - -#if defined(INTERNAL_GPS) - if (hasSerialMode(UART_MODE_GPS) != -1) { - if (gpsData.fix) { - char s[10]; - sprintf(s, "%d", gpsData.numSat); - dc->drawText(GPS_X, 4, s, FONT(XS) | CENTERED | COLOR_THEME_PRIMARY2); - } - dc->drawBitmapPattern( - GPS_X - 10, 22, LBM_TOPMENU_GPS, - (gpsData.fix) ? COLOR_THEME_PRIMARY2 : COLOR_THEME_PRIMARY3); - } -#endif - - // USB icon - if (usbPlugged()) { - - LcdFlags flags = COLOR_THEME_PRIMARY2; - if (getSelectedUsbMode() == USB_UNSELECTED_MODE) { - flags = COLOR_THEME_PRIMARY3; - } - - dc->drawBitmapPattern(USB_X, 8, LBM_TOPMENU_USB, flags); - } - // Logs - else if (isFunctionActive(FUNCTION_LOGS) && BLINK_ON_PHASE) { - dc->drawBitmapPattern(LOG_X, 6, LBM_DOT, COLOR_THEME_PRIMARY2); - } - - // 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(RSSI_X + i * 6, 38 - height, 4, height, - TELEMETRY_RSSI() >= rssiBarsValue[i] - ? COLOR_THEME_PRIMARY2 - : COLOR_THEME_PRIMARY3); - } - -#if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) - if (isModuleXJT(INTERNAL_MODULE) && isExternalAntennaEnabled()) { - dc->drawBitmapPattern(RSSI_X - 4, 4, LBM_TOPMENU_ANTENNA, COLOR_THEME_PRIMARY2); - } -#endif - - /* Audio volume */ - dc->drawBitmapPattern(AUDIO_X, 4, LBM_TOPMENU_VOLUME_SCALE, COLOR_THEME_PRIMARY3); - if (requiredSpeakerVolume == 0 || g_eeGeneral.beepMode == e_mode_quiet) - dc->drawBitmapPattern(AUDIO_X, 4, LBM_TOPMENU_VOLUME_0, COLOR_THEME_PRIMARY2); - else if (requiredSpeakerVolume < 7) - dc->drawBitmapPattern(AUDIO_X, 4, LBM_TOPMENU_VOLUME_1, COLOR_THEME_PRIMARY2); - else if (requiredSpeakerVolume < 13) - dc->drawBitmapPattern(AUDIO_X, 4, LBM_TOPMENU_VOLUME_2, COLOR_THEME_PRIMARY2); - else if (requiredSpeakerVolume < 19) - dc->drawBitmapPattern(AUDIO_X, 4, LBM_TOPMENU_VOLUME_3, COLOR_THEME_PRIMARY2); - else - dc->drawBitmapPattern(AUDIO_X, 4, LBM_TOPMENU_VOLUME_4, COLOR_THEME_PRIMARY2); - - /* Tx battery */ - uint8_t bars = GET_TXBATT_BARS(5); -#if defined(USB_CHARGER) - if (usbChargerLed()) { - dc->drawBitmapPattern(AUDIO_X, 25, LBM_TOPMENU_TXBATT_CHARGING, COLOR_THEME_PRIMARY2); - } - else { - dc->drawBitmapPattern(AUDIO_X, 25, LBM_TOPMENU_TXBATT, COLOR_THEME_PRIMARY2); - } -#else - dc->drawBitmapPattern(AUDIO_X, 25, LBM_TOPMENU_TXBATT, COLOR_THEME_PRIMARY2); -#endif - for (unsigned int i = 0; i < 5; i++) { - dc->drawSolidFilledRect(AUDIO_X + 2 + 4 * i, 30, 2, 8, i >= bars ? COLOR_THEME_PRIMARY3 : COLOR_THEME_PRIMARY2); - } - -#if 0 - // Radio battery - TODO - // drawValueWithUnit(370, 8, g_vbat100mV, UNIT_VOLTS, PREC1|FONT(XS)|COLOR_THEME_SECONDARY1); - // lcdDrawSolidRect(300, 3, 20, 50, COLOR_THEME_SECONDARY1); - // 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() ? COLOR_THEME_WARNING : COLOR_THEME_SECONDARY1); - lcdDrawSolidFilledRect(ALTITUDE_X, VOLTS_Y, ALTITUDE_W, ALTITUDE_H, COLOR_THEME_SECONDARY3); - 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() ? COLOR_THEME_WARNING : COLOR_THEME_SECONDARY1); - lcdDrawSolidFilledRect(ALTITUDE_X, ALTITUDE_Y, ALTITUDE_W, ALTITUDE_H, COLOR_THEME_SECONDARY3); - 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() @@ -281,3 +91,23 @@ void TopbarImpl::checkEvents() } } +void TopbarImpl::removeWidget(unsigned int index) +{ + bool mark = false; + + // If user manually removes 'system' widgets, mark name so widget does not get reloaded on restart + if ((index == MAX_TOPBAR_ZONES - 1) && (strcmp(persistentData->zones[index].widgetName, "Date Time") == 0)) + mark = true; + if ((index == MAX_TOPBAR_ZONES - 2) && (strcmp(persistentData->zones[index].widgetName, "Radio Info") == 0)) + mark = true; +#if defined(INTERNAL_GPS) + if ((index == MAX_TOPBAR_ZONES - 3) && (strcmp(persistentData->zones[index].widgetName, "Internal GPS") == 0)) + mark = true; +#endif + + WidgetsContainerImpl::removeWidget(index); + + // If user manually removes 'system' widgets, mark name so widget does not get reloaded on restart + if (mark) + strcpy(persistentData->zones[index].widgetName, "--"); +} diff --git a/radio/src/gui/colorlcd/layouts/topbar_impl.h b/radio/src/gui/colorlcd/layouts/topbar_impl.h index de868ac7f..385fbdbae 100644 --- a/radio/src/gui/colorlcd/layouts/topbar_impl.h +++ b/radio/src/gui/colorlcd/layouts/topbar_impl.h @@ -56,7 +56,8 @@ class TopbarImpl: void checkEvents() override; - + void removeWidget(unsigned int index) override; + protected: uint32_t lastRefresh = 0; }; diff --git a/radio/src/gui/colorlcd/preview_window.h b/radio/src/gui/colorlcd/preview_window.h index abd1f03a4..5c27907e2 100644 --- a/radio/src/gui/colorlcd/preview_window.h +++ b/radio/src/gui/colorlcd/preview_window.h @@ -302,7 +302,7 @@ class PreviewWindow : public FormWindow dc->clear(COLOR_THEME_SECONDARY3); // top bar background - dc->drawSolidFilledRect(0, 0, rect.w, TOPBAR_HEIGHT, COLOR_THEME_SECONDARY1); + dc->drawSolidFilledRect(0, 0, rect.w, TOPBAR_ZONE_HEIGHT, COLOR_THEME_SECONDARY1); int width; int x = 5; @@ -315,7 +315,7 @@ class PreviewWindow : public FormWindow delete bm; dc->drawSolidFilledRect(x - 2, 0, MENU_HEADER_BUTTON_WIDTH + 2, - TOPBAR_HEIGHT, COLOR_THEME_FOCUS); + TOPBAR_ZONE_HEIGHT, COLOR_THEME_FOCUS); auto mask_radio_tools = getBuiltinIcon(ICON_RADIO_TOOLS); bm = getBitmap(mask_radio_tools, COLOR_THEME_FOCUS, COLOR_THEME_PRIMARY2, &width); diff --git a/radio/src/gui/colorlcd/topbar.h b/radio/src/gui/colorlcd/topbar.h index 85c864643..53e50b446 100644 --- a/radio/src/gui/colorlcd/topbar.h +++ b/radio/src/gui/colorlcd/topbar.h @@ -24,8 +24,9 @@ #include "layout.h" 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; +constexpr coord_t TOPBAR_ZONE_VMARGIN = 3; +constexpr coord_t TOPBAR_ZONE_HMARGIN = 3; +constexpr coord_t TOPBAR_ZONE_HEIGHT = MENU_HEADER_HEIGHT - 2 * TOPBAR_ZONE_VMARGIN; class ScreenMenu; class TopbarImpl; diff --git a/radio/src/gui/colorlcd/widgets/radio_info.cpp b/radio/src/gui/colorlcd/widgets/radio_info.cpp new file mode 100644 index 000000000..2c0ca4b9c --- /dev/null +++ b/radio/src/gui/colorlcd/widgets/radio_info.cpp @@ -0,0 +1,270 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * 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" +#include "widgets_container_impl.h" + +#define W_AUDIO_X 0 +#define W_USB_X 32 +#define W_LOG_X 32 +#define W_RSSI_X 40 + +static const uint8_t _LBM_DOT[] = { +#include "mask_dot.lbm" +}; + +static const uint8_t _LBM_TOPMENU_USB[] = { +#include "mask_topmenu_usb.lbm" +}; + +static const uint8_t _LBM_TOPMENU_VOLUME_0[] = { +#include "mask_volume_0.lbm" +}; + +static const uint8_t _LBM_TOPMENU_VOLUME_1[] = { +#include "mask_volume_1.lbm" +}; + +static const uint8_t _LBM_TOPMENU_VOLUME_2[] = { +#include "mask_volume_2.lbm" +}; + +static const uint8_t _LBM_TOPMENU_VOLUME_3[] = { +#include "mask_volume_3.lbm" +}; + +static const uint8_t _LBM_TOPMENU_VOLUME_4[] = { +#include "mask_volume_4.lbm" +}; + +static const uint8_t _LBM_TOPMENU_VOLUME_SCALE[] = { +#include "mask_volume_scale.lbm" +}; + +static const uint8_t _LBM_TOPMENU_TXBATT[] = { +#include "mask_txbat.lbm" +}; + +static const uint8_t _LBM_TOPMENU_TXBATT_CHARGING[] = { +#include "mask_txbat_charging.lbm" +}; + +static const uint8_t _LBM_TOPMENU_ANTENNA[] = { +#include "mask_antenna.lbm" +}; + +STATIC_LZ4_BITMAP(LBM_DOT); +STATIC_LZ4_BITMAP(LBM_TOPMENU_USB); +STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_0); +STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_1); +STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_2); +STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_3); +STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_4); +STATIC_LZ4_BITMAP(LBM_TOPMENU_VOLUME_SCALE); +STATIC_LZ4_BITMAP(LBM_TOPMENU_TXBATT); +STATIC_LZ4_BITMAP(LBM_TOPMENU_TXBATT_CHARGING); +STATIC_LZ4_BITMAP(LBM_TOPMENU_ANTENNA); + +class RadioInfoWidget: public Widget +{ + protected: + + public: + RadioInfoWidget(const WidgetFactory* factory, Window* parent, + const rect_t& rect, Widget::PersistentData* persistentData) : + Widget(factory, parent, rect, persistentData) + { + } + + void refresh(BitmapBuffer * dc) override + { + // USB icon + if (usbPlugged()) { + + LcdFlags flags = COLOR_THEME_PRIMARY2; + if (getSelectedUsbMode() == USB_UNSELECTED_MODE) { + flags = COLOR_THEME_PRIMARY3; + } + + dc->drawBitmapPattern(W_USB_X, 5, LBM_TOPMENU_USB, flags); + } + + // Logs + else if (isFunctionActive(FUNCTION_LOGS) && BLINK_ON_PHASE) { + dc->drawBitmapPattern(W_LOG_X, 3, LBM_DOT, COLOR_THEME_PRIMARY2); + } + + // 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(W_RSSI_X + i * 6, 35 - height, 4, height, + TELEMETRY_RSSI() >= rssiBarsValue[i] + ? COLOR_THEME_PRIMARY2 + : COLOR_THEME_PRIMARY3); + } + +#if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) + if (isModuleXJT(INTERNAL_MODULE) && isExternalAntennaEnabled()) { + dc->drawBitmapPattern(W_RSSI_X - 4, 1, LBM_TOPMENU_ANTENNA, COLOR_THEME_PRIMARY2); + } +#endif + + /* Audio volume */ + dc->drawBitmapPattern(W_AUDIO_X, 1, LBM_TOPMENU_VOLUME_SCALE, COLOR_THEME_PRIMARY3); + if (requiredSpeakerVolume == 0 || g_eeGeneral.beepMode == e_mode_quiet) + dc->drawBitmapPattern(W_AUDIO_X, 1, LBM_TOPMENU_VOLUME_0, COLOR_THEME_PRIMARY2); + else if (requiredSpeakerVolume < 7) + dc->drawBitmapPattern(W_AUDIO_X, 1, LBM_TOPMENU_VOLUME_1, COLOR_THEME_PRIMARY2); + else if (requiredSpeakerVolume < 13) + dc->drawBitmapPattern(W_AUDIO_X, 1, LBM_TOPMENU_VOLUME_2, COLOR_THEME_PRIMARY2); + else if (requiredSpeakerVolume < 19) + dc->drawBitmapPattern(W_AUDIO_X, 1, LBM_TOPMENU_VOLUME_3, COLOR_THEME_PRIMARY2); + else + dc->drawBitmapPattern(W_AUDIO_X, 1, LBM_TOPMENU_VOLUME_4, COLOR_THEME_PRIMARY2); + + /* Tx battery */ + uint8_t bars = GET_TXBATT_BARS(5); +#if defined(USB_CHARGER) + if (usbChargerLed()) { + dc->drawBitmapPattern(W_AUDIO_X, 22, LBM_TOPMENU_TXBATT_CHARGING, COLOR_THEME_PRIMARY2); + } + else { + dc->drawBitmapPattern(W_AUDIO_X, 22, LBM_TOPMENU_TXBATT, COLOR_THEME_PRIMARY2); + } +#else + dc->drawBitmapPattern(W_AUDIO_X, 22, LBM_TOPMENU_TXBATT, COLOR_THEME_PRIMARY2); +#endif + for (unsigned int i = 0; i < 5; i++) { + dc->drawSolidFilledRect(W_AUDIO_X + 2 + 4 * i, 27, 2, 8, i >= bars ? COLOR_THEME_PRIMARY3 : COLOR_THEME_PRIMARY2); + } + } + + void checkEvents() override + { + Widget::checkEvents(); + invalidate(); + } + + static const ZoneOption options[]; +}; + +BaseWidgetFactory RadioInfoWidget("Radio Info", nullptr, "Radio Info"); + +// Adjustment to make main view date/time align with model/radio settings views +#if LCD_W > LCD_H +#define DT_OFFSET 8 +#else +#define DT_OFFSET 1 +#endif + +class DateTimeWidget: public Widget +{ + protected: + + public: + DateTimeWidget(const WidgetFactory* factory, Window* parent, + const rect_t& rect, Widget::PersistentData* persistentData) : + Widget(factory, parent, rect, persistentData) + { + } + + void refresh(BitmapBuffer * dc) override + { + // get color from options + LcdFlags color = COLOR2FLAGS(persistentData->options[0].value.unsignedValue); + + const TimerOptions timerOptions = {.options = SHOW_TIME}; + struct gtm t; + gettime(&t); + char str[10]; +#if defined(TRANSLATIONS_CN) || defined(TRANSLATIONS_TW) + sprintf(str, "%02d-%02d", t.tm_mon + 1, t.tm_mday); +#else + sprintf(str, "%d %s", t.tm_mday, STR_MONTHS[t.tm_mon]); +#endif + dc->drawText(width()/2+DT_OFFSET, 3, str, FONT(XS) | CENTERED | color); + getTimerString(str, getValue(MIXSRC_TX_TIME), timerOptions); + dc->drawText(width()/2+DT_OFFSET, 18, str, FONT(XS) | CENTERED | color); + } + + void checkEvents() override + { + Widget::checkEvents(); + invalidate(); + } + + static const ZoneOption options[]; +}; + +const ZoneOption DateTimeWidget::options[] = { + { STR_COLOR, ZoneOption::Color, OPTION_VALUE_UNSIGNED(COLOR_THEME_PRIMARY2) }, + { nullptr, ZoneOption::Bool } +}; + +BaseWidgetFactory DateTimeWidget("Date Time", DateTimeWidget::options, "Date Time"); + +#if defined(INTERNAL_GPS) + +static const uint8_t _LBM_TOPMENU_GPS[] = { +#include "mask_topmenu_gps_18.lbm" +}; + +STATIC_LZ4_BITMAP(LBM_TOPMENU_GPS); + +class InternalGPSWidget: public Widget +{ + protected: + + public: + InternalGPSWidget(const WidgetFactory* factory, Window* parent, + const rect_t& rect, Widget::PersistentData* persistentData) : + Widget(factory, parent, rect, persistentData) + { + } + + void refresh(BitmapBuffer * dc) override + { + if (hasSerialMode(UART_MODE_GPS) != -1) { + if (gpsData.fix) { + char s[10]; + sprintf(s, "%d", gpsData.numSat); + dc->drawText(width() / 2, 1, s, FONT(XS) | CENTERED | COLOR_THEME_PRIMARY2); + } + dc->drawBitmapPattern( + width() / 2 - 10, 19, LBM_TOPMENU_GPS, + (gpsData.fix) ? COLOR_THEME_PRIMARY2 : COLOR_THEME_PRIMARY3); + } + } + + void checkEvents() override + { + Widget::checkEvents(); + invalidate(); + } + + static const ZoneOption options[]; +}; + +BaseWidgetFactory InternalGPSWidget("Internal GPS", nullptr, "Internal GPS"); + +#endif diff --git a/radio/src/gui/colorlcd/widgets_container.h b/radio/src/gui/colorlcd/widgets_container.h index ed8046aea..b6c2fd5c0 100644 --- a/radio/src/gui/colorlcd/widgets_container.h +++ b/radio/src/gui/colorlcd/widgets_container.h @@ -31,9 +31,9 @@ #define MAX_WIDGET_OPTIONS 5 // Name? #if LCD_W > LCD_H -#define MAX_TOPBAR_ZONES 4 +#define MAX_TOPBAR_ZONES 6 #else -#define MAX_TOPBAR_ZONES 2 +#define MAX_TOPBAR_ZONES 4 #endif #define MAX_TOPBAR_OPTIONS 1 // just because of VC++ which doesn't like 0-size arrays :( diff --git a/radio/src/gui/colorlcd/widgets_setup.cpp b/radio/src/gui/colorlcd/widgets_setup.cpp index aa9a6388b..3028fc41c 100644 --- a/radio/src/gui/colorlcd/widgets_setup.cpp +++ b/radio/src/gui/colorlcd/widgets_setup.cpp @@ -66,12 +66,7 @@ SetupWidgetsPageSlot::SetupWidgetsPageSlot(FormWindow* parent, void SetupWidgetsPageSlot::paint(BitmapBuffer* dc) { - if (hasFocus()) { - dc->drawRect(0, 0, width() - 1, height() - 1, 2, STASHED, - COLOR_THEME_FOCUS); - } else { - dc->drawSolidRect(0, 0, width() - 1, height() - 1, 2, COLOR_THEME_PRIMARY3); - } + dc->drawRect(0, 0, width(), height(), 2, hasFocus() ? STASHED : SOLID, COLOR_THEME_FOCUS); } SetupWidgetsPage::SetupWidgetsPage(uint8_t customScreenIdx) : diff --git a/radio/src/storage/storage_common.cpp b/radio/src/storage/storage_common.cpp index 755e5c41e..4c83d37a5 100644 --- a/radio/src/storage/storage_common.cpp +++ b/radio/src/storage/storage_common.cpp @@ -139,6 +139,28 @@ static void sortMixerLines() void postModelLoad(bool alarms) { +#if defined(COLORLCD) + // Load 'date time' widget if slot is empty + if (g_model.topbarData.zones[MAX_TOPBAR_ZONES-1].widgetName[0] == 0) { + strcpy(g_model.topbarData.zones[MAX_TOPBAR_ZONES-1].widgetName, "Date Time"); + g_model.topbarData.zones[MAX_TOPBAR_ZONES-1].widgetData.options[0].type = ZOV_Color; + g_model.topbarData.zones[MAX_TOPBAR_ZONES-1].widgetData.options[0].value.unsignedValue = 0xFFFFFF; + storageDirty(EE_MODEL); + } + // Load 'radio info' widget if slot is empty + if (g_model.topbarData.zones[MAX_TOPBAR_ZONES-2].widgetName[0] == 0) { + strcpy(g_model.topbarData.zones[MAX_TOPBAR_ZONES-2].widgetName, "Radio Info"); + storageDirty(EE_MODEL); + } +#if defined(INTERNAL_GPS) + // Load 'internal gps' widget if slot is empty + if (g_model.topbarData.zones[MAX_TOPBAR_ZONES-3].widgetName[0] == 0) { + strcpy(g_model.topbarData.zones[MAX_TOPBAR_ZONES-3].widgetName, "Internal GPS"); + storageDirty(EE_MODEL); + } +#endif +#endif + // Convert 'noGlobalFunctions' to 'radioGFDisabled' // TODO: Remove sometime in the future (and remove 'noGlobalFunctions' property) if (g_model.noGlobalFunctions) { diff --git a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp index 53046e4c6..2417bfe15 100644 --- a/radio/src/storage/yaml/yaml_datastructs_nv14.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_nv14.cpp @@ -287,7 +287,7 @@ static const struct YamlNode struct_ZoneOptionValueTyped[] = { YAML_UNION("value", 64, union_ZoneOptionValue_elmts, select_zov), YAML_END }; -static const struct YamlNode struct_OpenTxTheme__PersistentData[] = { +static const struct YamlNode struct_EdgeTxTheme__PersistentData[] = { YAML_ARRAY("options", 96, 5, struct_ZoneOptionValueTyped, NULL), YAML_END }; @@ -373,7 +373,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "blOffBright", 7 ), YAML_STRING("bluetoothName", 10), YAML_STRING("themeName", 8), - YAML_STRUCT("themeData", 480, struct_OpenTxTheme__PersistentData, NULL), + YAML_STRUCT("themeData", 480, struct_EdgeTxTheme__PersistentData, NULL), YAML_STRING("ownerRegistrationID", 8), YAML_CUSTOM("rotEncDirection",r_rotEncDirection,nullptr), YAML_UNSIGNED( "rotEncMode", 2 ), @@ -785,7 +785,7 @@ static const struct YamlNode struct_CustomScreenData[] = { YAML_END }; static const struct YamlNode struct_TopBarPersistentData[] = { - YAML_ARRAY("zones", 576, 2, struct_ZonePersistentData, NULL), + YAML_ARRAY("zones", 576, 4, struct_ZonePersistentData, NULL), YAML_ARRAY("options", 96, 1, struct_ZoneOptionValueTyped, NULL), YAML_END }; @@ -849,7 +849,7 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("potsWarnPosition", 8, 16, struct_signed_8, NULL), YAML_ARRAY("telemetrySensors", 112, 60, struct_TelemetrySensor, NULL), YAML_ARRAY("screenData", 6816, 10, struct_CustomScreenData, NULL), - YAML_STRUCT("topbarData", 1248, struct_TopBarPersistentData, NULL), + YAML_STRUCT("topbarData", 2400, struct_TopBarPersistentData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), YAML_UNSIGNED( "usbJoystickExtMode", 1 ), diff --git a/radio/src/storage/yaml/yaml_datastructs_x10.cpp b/radio/src/storage/yaml/yaml_datastructs_x10.cpp index 9eb40b869..54ba13f5f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x10.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x10.cpp @@ -295,7 +295,7 @@ static const struct YamlNode struct_ZoneOptionValueTyped[] = { YAML_UNION("value", 64, union_ZoneOptionValue_elmts, select_zov), YAML_END }; -static const struct YamlNode struct_OpenTxTheme__PersistentData[] = { +static const struct YamlNode struct_EdgeTxTheme__PersistentData[] = { YAML_ARRAY("options", 96, 5, struct_ZoneOptionValueTyped, NULL), YAML_END }; @@ -379,7 +379,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "blOffBright", 7 ), YAML_STRING("bluetoothName", 10), YAML_STRING("themeName", 8), - YAML_STRUCT("themeData", 480, struct_OpenTxTheme__PersistentData, NULL), + YAML_STRUCT("themeData", 480, struct_EdgeTxTheme__PersistentData, NULL), YAML_STRING("ownerRegistrationID", 8), YAML_CUSTOM("rotEncDirection",r_rotEncDirection,nullptr), YAML_UNSIGNED( "rotEncMode", 2 ), @@ -793,7 +793,7 @@ static const struct YamlNode struct_CustomScreenData[] = { YAML_END }; static const struct YamlNode struct_TopBarPersistentData[] = { - YAML_ARRAY("zones", 576, 4, struct_ZonePersistentData, NULL), + YAML_ARRAY("zones", 576, 6, struct_ZonePersistentData, NULL), YAML_ARRAY("options", 96, 1, struct_ZoneOptionValueTyped, NULL), YAML_END }; @@ -857,7 +857,7 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("potsWarnPosition", 8, 16, struct_signed_8, NULL), YAML_ARRAY("telemetrySensors", 112, 60, struct_TelemetrySensor, NULL), YAML_ARRAY("screenData", 6816, 10, struct_CustomScreenData, NULL), - YAML_STRUCT("topbarData", 2400, struct_TopBarPersistentData, NULL), + YAML_STRUCT("topbarData", 3552, struct_TopBarPersistentData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), YAML_UNSIGNED( "usbJoystickExtMode", 1 ), diff --git a/radio/src/storage/yaml/yaml_datastructs_x12s.cpp b/radio/src/storage/yaml/yaml_datastructs_x12s.cpp index 9eb40b869..54ba13f5f 100644 --- a/radio/src/storage/yaml/yaml_datastructs_x12s.cpp +++ b/radio/src/storage/yaml/yaml_datastructs_x12s.cpp @@ -295,7 +295,7 @@ static const struct YamlNode struct_ZoneOptionValueTyped[] = { YAML_UNION("value", 64, union_ZoneOptionValue_elmts, select_zov), YAML_END }; -static const struct YamlNode struct_OpenTxTheme__PersistentData[] = { +static const struct YamlNode struct_EdgeTxTheme__PersistentData[] = { YAML_ARRAY("options", 96, 5, struct_ZoneOptionValueTyped, NULL), YAML_END }; @@ -379,7 +379,7 @@ static const struct YamlNode struct_RadioData[] = { YAML_UNSIGNED( "blOffBright", 7 ), YAML_STRING("bluetoothName", 10), YAML_STRING("themeName", 8), - YAML_STRUCT("themeData", 480, struct_OpenTxTheme__PersistentData, NULL), + YAML_STRUCT("themeData", 480, struct_EdgeTxTheme__PersistentData, NULL), YAML_STRING("ownerRegistrationID", 8), YAML_CUSTOM("rotEncDirection",r_rotEncDirection,nullptr), YAML_UNSIGNED( "rotEncMode", 2 ), @@ -793,7 +793,7 @@ static const struct YamlNode struct_CustomScreenData[] = { YAML_END }; static const struct YamlNode struct_TopBarPersistentData[] = { - YAML_ARRAY("zones", 576, 4, struct_ZonePersistentData, NULL), + YAML_ARRAY("zones", 576, 6, struct_ZonePersistentData, NULL), YAML_ARRAY("options", 96, 1, struct_ZoneOptionValueTyped, NULL), YAML_END }; @@ -857,7 +857,7 @@ static const struct YamlNode struct_ModelData[] = { YAML_ARRAY("potsWarnPosition", 8, 16, struct_signed_8, NULL), YAML_ARRAY("telemetrySensors", 112, 60, struct_TelemetrySensor, NULL), YAML_ARRAY("screenData", 6816, 10, struct_CustomScreenData, NULL), - YAML_STRUCT("topbarData", 2400, struct_TopBarPersistentData, NULL), + YAML_STRUCT("topbarData", 3552, struct_TopBarPersistentData, NULL), YAML_UNSIGNED( "view", 8 ), YAML_STRING("modelRegistrationID", 8), YAML_UNSIGNED( "usbJoystickExtMode", 1 ), diff --git a/radio/src/targets/horus/libopenui_config.h b/radio/src/targets/horus/libopenui_config.h index 3ad235252..26431102b 100644 --- a/radio/src/targets/horus/libopenui_config.h +++ b/radio/src/targets/horus/libopenui_config.h @@ -21,12 +21,6 @@ #pragma once -constexpr uint32_t RSSI_X = LCD_W - 90; -constexpr uint32_t AUDIO_X = LCD_W - 130; -constexpr uint32_t USB_X = LCD_W - 98; -constexpr uint32_t LOG_X = LCD_W - 98; -constexpr uint32_t GPS_X = LCD_W - 148; - constexpr uint32_t ALERT_FRAME_TOP = 70; constexpr uint32_t ALERT_FRAME_HEIGHT = (LCD_H - 2 * ALERT_FRAME_TOP); constexpr uint32_t ALERT_BITMAP_TOP = ALERT_FRAME_TOP + 15; diff --git a/radio/src/targets/nv14/libopenui_config.h b/radio/src/targets/nv14/libopenui_config.h index cc9d1cf44..8c4d233d7 100644 --- a/radio/src/targets/nv14/libopenui_config.h +++ b/radio/src/targets/nv14/libopenui_config.h @@ -21,12 +21,6 @@ #pragma once -constexpr uint32_t RSSI_X = LCD_W - 80; -constexpr uint32_t AUDIO_X = LCD_W - 115; -constexpr uint32_t USB_X = LCD_W - 82; -constexpr uint32_t LOG_X = LCD_W - 82; -constexpr uint32_t GPS_X = LCD_W - 130; - constexpr uint32_t ALERT_FRAME_TOP = 70; constexpr uint32_t ALERT_FRAME_HEIGHT = (LCD_H - 2 * ALERT_FRAME_TOP); constexpr uint32_t ALERT_BITMAP_TOP = ALERT_FRAME_TOP + 15;