diff --git a/radio/src/gui/horus/bitmapbuffer.cpp b/radio/src/gui/horus/bitmapbuffer.cpp index 3fc9f44fb..309c35c35 100644 --- a/radio/src/gui/horus/bitmapbuffer.cpp +++ b/radio/src/gui/horus/bitmapbuffer.cpp @@ -274,9 +274,9 @@ void BitmapBuffer::drawSizedText(coord_t x, coord_t y, const pm_char * s, uint8_ if (FONTSIZE(flags) == TINSIZE) drawSolidFilledRect(x-INVERT_HORZ_MARGIN+2, y-INVERT_VERT_MARGIN+2, width+2*INVERT_HORZ_MARGIN-5, INVERT_LINE_HEIGHT-7, TEXT_INVERTED_BGCOLOR); else if (FONTSIZE(flags) == SMLSIZE) - drawSolidFilledRect(x-INVERT_HORZ_MARGIN+1, y-INVERT_VERT_MARGIN, width+2*INVERT_HORZ_MARGIN-2, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); + drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y+1, width+2*INVERT_HORZ_MARGIN-2, INVERT_LINE_HEIGHT-5, TEXT_INVERTED_BGCOLOR); else - drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y/*-INVERT_VERT_MARGIN*/, width+2*INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); + drawSolidFilledRect(x-INVERT_HORZ_MARGIN, y, width+2*INVERT_HORZ_MARGIN, INVERT_LINE_HEIGHT, TEXT_INVERTED_BGCOLOR); } char str[256]; diff --git a/radio/src/gui/horus/bitmaps.h b/radio/src/gui/horus/bitmaps.h index a520571a3..a624f7ed4 100644 --- a/radio/src/gui/horus/bitmaps.h +++ b/radio/src/gui/horus/bitmaps.h @@ -51,6 +51,7 @@ extern const uint8_t LBM_MIXER_ICON[]; extern const uint8_t LBM_CURVES_ICON[]; extern const uint8_t LBM_LUA_SCRIPTS_ICON[]; extern const uint8_t LBM_TELEMETRY_ICON[]; +extern const uint8_t LBM_STATS_ANALOGS_ICON[]; // UI (theme / layout / widgets bitmaps extern const uint8_t LBM_MAINVIEWS_ICON[]; diff --git a/radio/src/gui/horus/menu_model_setup.cpp b/radio/src/gui/horus/menu_model_setup.cpp index a79a0e1bb..645a9d04d 100644 --- a/radio/src/gui/horus/menu_model_setup.cpp +++ b/radio/src/gui/horus/menu_model_setup.cpp @@ -708,88 +708,114 @@ bool menuModelSetup(evt_t event) bool menuModelFailsafe(evt_t event) { - static bool longNames = false; - bool newLongNames = false; uint8_t ch = 0; + uint8_t channelStart = g_model.moduleData[g_moduleIdx].channelsStart; - if (event == EVT_KEY_LONG(KEY_ENTER) && s_editMode) { - START_NO_HIGHLIGHT(); - g_model.moduleData[g_moduleIdx].failsafeChannels[menuVerticalPosition] = channelOutputs[menuVerticalPosition]; - storageDirty(EE_MODEL); - AUDIO_WARNING1(); - SEND_FAILSAFE_NOW(g_moduleIdx); + if (event == EVT_KEY_LONG(KEY_ENTER)) { + killEvents(event); + event = 0; + if (s_editMode) { + g_model.moduleData[g_moduleIdx].failsafeChannels[menuVerticalPosition] = channelOutputs[menuVerticalPosition+channelStart]; + storageDirty(EE_MODEL); + AUDIO_WARNING1(); + s_editMode = 0; + SEND_FAILSAFE_NOW(g_moduleIdx); + } + else { + int16_t & failsafe = g_model.moduleData[g_moduleIdx].failsafeChannels[menuVerticalPosition]; + if (failsafe < FAILSAFE_CHANNEL_HOLD) + failsafe = FAILSAFE_CHANNEL_HOLD; + else if (failsafe == FAILSAFE_CHANNEL_HOLD) + failsafe = FAILSAFE_CHANNEL_NOPULSE; + else + failsafe = 0; + storageDirty(EE_MODEL); + AUDIO_WARNING1(); + SEND_FAILSAFE_NOW(g_moduleIdx); + } } - SIMPLE_SUBMENU_NOTITLE(NUM_CHNOUT); + SIMPLE_SUBMENU_WITH_OPTIONS("FAILSAFE", LBM_STATS_ANALOGS_ICON, NUM_CHANNELS(g_moduleIdx), OPTION_MENU_NO_SCROLLBAR); + drawStringWithIndex(50, 3+FH, "Module", g_moduleIdx+1, MENU_TITLE_COLOR); #define COL_W (LCD_W/2) - const uint8_t SLIDER_W = 64; - // Column separator - // TODO lcdDrawSolidVerticalLine(LCD_W/2, FH, LCD_H-FH); + const uint8_t SLIDER_W = 128; - if (menuVerticalPosition >= 16) { - ch = 16; - } + unsigned int lim = g_model.extendedLimits ? 640*2 : 512*2; - // TODO lcd_putsCenter(0, FAILSAFESET); - // TODO lcdInvertLine(0); + for (uint8_t col=0; col<2; col++) { + for (uint8_t line=0; line<8; line++) { + coord_t x = col*(LCD_W/2); + coord_t y = MENU_CONTENT_TOP - FH + line*(FH+4); + int32_t channelValue = channelOutputs[ch+channelStart]; + int32_t failsafeValue = 0; + bool failsafeEditable = false; - for (int col=0; col<2; col++) { - coord_t x = col*COL_W+1; - - // Channels - for (int line=0; line<8; line++) { - coord_t y = 9+line*7; - int32_t val; - int ofs = (col ? 0 : 1); - - if (ch < g_model.moduleData[g_moduleIdx].channelsStart || ch >= NUM_CHANNELS(g_moduleIdx) + g_model.moduleData[g_moduleIdx].channelsStart) - val = 0; - else if (s_editMode && menuVerticalPosition == ch) - val = channelOutputs[ch]; - else - val = g_model.moduleData[g_moduleIdx].failsafeChannels[8*col+line]; - - // Channel name if present, number if not - uint8_t lenLabel = ZLEN(g_model.limitData[ch].name); - if (lenLabel > 4) { - newLongNames = longNames = true; + if (ch < NUM_CHANNELS(g_moduleIdx)) { + failsafeValue = g_model.moduleData[g_moduleIdx].failsafeChannels[8*col+line]; + failsafeEditable = true; } - if (lenLabel > 0) - lcdDrawSizedText(x+1-ofs, y, g_model.limitData[ch].name, sizeof(g_model.limitData[ch].name), ZCHAR | SMLSIZE); - else - putsChn(x+1-ofs, y, ch+1, SMLSIZE); + if (failsafeEditable) { + // Channel name if present, number if not + uint8_t lenLabel = ZLEN(g_model.limitData[ch+channelStart].name); + if (lenLabel > 0) { + putsChn(x+MENUS_MARGIN_LEFT, y-3, ch+1, TINSIZE); + lcdDrawSizedText(x+MENUS_MARGIN_LEFT, y+5, g_model.limitData[ch+channelStart].name, sizeof(g_model.limitData[ch+channelStart].name), ZCHAR|SMLSIZE); + } + else { + putsChn(x+MENUS_MARGIN_LEFT, y, ch+1, 0); + } - // Value - LcdFlags flags = TINSIZE; - if (menuVerticalPosition == ch && !NO_HIGHLIGHT()) { - flags |= INVERS; - if (s_editMode) - flags |= BLINK; - } + // Value + LcdFlags flags = RIGHT; + if (menuVerticalPosition == ch) { + flags |= INVERS; + if (s_editMode) { + if (failsafeValue == FAILSAFE_CHANNEL_HOLD || failsafeValue == FAILSAFE_CHANNEL_NOPULSE) { + s_editMode = 0; + } + else { + flags |= BLINK; + CHECK_INCDEC_MODELVAR(event, g_model.moduleData[g_moduleIdx].failsafeChannels[8*col+line], -lim, +lim); + } + } + } + + x += COL_W-4-MENUS_MARGIN_LEFT-SLIDER_W; + + if (failsafeValue == FAILSAFE_CHANNEL_HOLD) { + lcdDrawText(x, y+2, "HOLD", flags|SMLSIZE); + failsafeValue = 0; + } + else if (failsafeValue == FAILSAFE_CHANNEL_NOPULSE) { + lcdDrawText(x, y+2, "NONE", flags|SMLSIZE); + failsafeValue = 0; + } + else { #if defined(PPM_UNIT_US) - uint8_t wbar = (longNames ? SLIDER_W-10 : SLIDER_W); - lcdDrawNumber(x+COL_W-4-wbar-ofs, y, PPM_CH_CENTER(ch)+val/2, flags|RIGHT); + lcdDrawNumber(x, y, PPM_CH_CENTER(ch)+failsafeValue/2, flags); #elif defined(PPM_UNIT_PERCENT_PREC1) - uint8_t wbar = (longNames ? SLIDER_W-16 : SLIDER_W-6); - lcdDrawNumber(x+COL_W-4-wbar-ofs, y, calcRESXto1000(val), PREC1|flags|RIGHT); + lcdDrawNumber(x, y, calcRESXto1000(failsafeValue), PREC1|flags); #else - uint8_t wbar = (longNames ? SLIDER_W-10 : SLIDER_W); - lcdDrawNumber(x+COL_W-4-wbar-ofs, y, calcRESXto1000(val)/10, flags|RIGHT); + lcdDrawNumber(x, y, calcRESXto1000(failsafeValue)/10, flags); #endif + } - // Gauge - lcdDrawRect(x+COL_W-3-wbar-ofs, y, wbar+1, 6); - uint16_t lim = g_model.extendedLimits ? 640*2 : 512*2; - uint8_t len = limit((uint8_t)1, uint8_t((abs(val) * wbar/2 + lim/2) / lim), uint8_t(wbar/2)); - coord_t x0 = (val>0) ? x+COL_W-ofs-3-wbar/2 : x+COL_W-ofs-2-wbar/2-len; - lcdDrawSolidFilledRect(x0, y+1, len, 4, LINE_COLOR); + // Gauge + x += 4; + lcdDrawRect(x, y+3, SLIDER_W+1, 12); + unsigned int lenChannel = limit((uint8_t)1, uint8_t((abs(channelValue) * SLIDER_W/2 + lim/2) / lim), uint8_t(SLIDER_W/2)); + unsigned int lenFailsafe = limit((uint8_t)1, uint8_t((abs(failsafeValue) * SLIDER_W/2 + lim/2) / lim), uint8_t(SLIDER_W/2)); + x += SLIDER_W/2; + coord_t xChannel = (channelValue>0) ? x : x+1-lenChannel; + coord_t xFailsafe = (failsafeValue>0) ? x : x+1-lenFailsafe; + lcdDrawSolidFilledRect(xChannel, y+4, lenChannel, 5, TEXT_COLOR); + lcdDrawSolidFilledRect(xFailsafe, y+9, lenFailsafe, 5, ALARM_COLOR); + } ch++; } } - longNames = newLongNames; - return true; } diff --git a/radio/src/gui/horus/menus.h b/radio/src/gui/horus/menus.h index 396136c55..f37a621d9 100644 --- a/radio/src/gui/horus/menus.h +++ b/radio/src/gui/horus/menus.h @@ -319,13 +319,14 @@ bool check_submenu_simple(check_event_t event, uint8_t maxrow); if (!check(event, 0, NULL, 0, mstate_tab, DIM(mstate_tab)-1, lines_count)) return false; \ drawScreenTemplate(title, icon, options); -#define SIMPLE_SUBMENU_NOTITLE(lines_count) \ - if (!check_submenu_simple(event, lines_count)) return false - #define SIMPLE_SUBMENU(title, icon, lines_count) \ - SIMPLE_SUBMENU_NOTITLE(lines_count); \ + if (!check_submenu_simple(event, lines_count)) return false; \ drawScreenTemplate(title, icon) +#define SIMPLE_SUBMENU_WITH_OPTIONS(title, icon, lines_count, options) \ + if (!check_submenu_simple(event, lines_count)) return false; \ + drawScreenTemplate(title, icon, options) + typedef int select_menu_value_t; select_menu_value_t selectMenuItem(coord_t x, coord_t y, const pm_char * values, select_menu_value_t value, select_menu_value_t min, select_menu_value_t max, LcdFlags attr, evt_t event);