From c140e716658a1aae1324e9009e152a0ad67d29e7 Mon Sep 17 00:00:00 2001 From: philmoz Date: Fri, 27 Jan 2023 20:22:48 +1100 Subject: [PATCH] feat(color): LVGLify and enhance Logical Switches page (#3019) * Update logical switch layout: - Use LVGL flex / grid layout - Same height for all buttons - Display logical switch name inside button so entire row acts as button - Consistent press handler for buttons (always show popup menu) - Group buttons for empty switch in lines to utilise space - Remember selected button and reselect after edit. * Fix incorrect switch being selected when changing tabs. * Fix selection of focussed button when using 'Clear', and switching tabs. * Reorganise logical switch edit code. * Change button style to match input/mixes/outputs pages. * Single line overview on landscape LCD layout. Convert to using grid and lgvl objects for overview buttons. * Remove redundant flag. * Remove logic that tries to center scroll around selected button (not working). * Remove empty buttons and add a single '+' button at the bottom to match inputs and mixes. * Remove unused code. * Correct string usage. * Add long press handler for quick access to the '+' button functions. * Display logical switch operation and parameters as an expression. Add 's' suffix to time values. * Adjust spacing. * Set V2 number edit control to automatically resize and fit content to prevent clipping of large values. * Revert to fixed column layout. * Adjust grid spacing. * Convert the logical switch view in the channel monitor to LVGL flex. * chore: Formatting Co-authored-by: Phil Mitchell Co-authored-by: Peter Feerick --- .../gui/colorlcd/model_logical_switches.cpp | 839 +++++++++++------- .../src/gui/colorlcd/model_logical_switches.h | 27 +- .../gui/colorlcd/view_logical_switches.cpp | 309 +++++-- .../src/gui/colorlcd/view_logical_switches.h | 87 +- 4 files changed, 822 insertions(+), 440 deletions(-) diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp index 8bf806e457e..95ebaad520a 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -23,101 +23,141 @@ #include "opentx.h" #include "libopenui.h" #include "switches.h" +#include "lvgl_widgets/input_mix_line.h" -#define SET_DIRTY() storageDirty(EE_MODEL) +#define SET_DIRTY() storageDirty(EE_MODEL) -void putsEdgeDelayParam(BitmapBuffer * dc, coord_t x, coord_t y, LogicalSwitchData * ls, LcdFlags flags = 0) -{ - coord_t lcdNextPos = 0; - lcdNextPos = dc->drawText(x, y, "[", flags); - lcdNextPos = dc->drawNumber(lcdNextPos+2, y, lswTimerValue(ls->v2), LEFT | PREC1 | flags); - lcdNextPos = dc->drawText(lcdNextPos, y, ":", flags); - if (ls->v3 < 0) - lcdNextPos = dc->drawText(lcdNextPos+3, y, "<<", flags); - else if (ls->v3 == 0) - lcdNextPos = dc->drawText(lcdNextPos+3, y, "--", flags); - else - lcdNextPos = dc->drawNumber(lcdNextPos+3, y, lswTimerValue(ls->v2+ls->v3), LEFT | PREC1 | flags); - dc->drawText(lcdNextPos, y, "]", flags); -} +static const lv_coord_t col_dsc[] = {LV_GRID_FR(2), LV_GRID_FR(3), + LV_GRID_TEMPLATE_LAST}; +static const lv_coord_t col_dsc2[] = {LV_GRID_FR(4), LV_GRID_FR(3), + LV_GRID_FR(3), LV_GRID_TEMPLATE_LAST}; +static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; -class LogicalSwitchEditPage: public Page +class LogicalSwitchEditPage : public Page { - public: - explicit LogicalSwitchEditPage(uint8_t index): - Page(ICON_MODEL_LOGICAL_SWITCHES), - index(index) - { - buildHeader(&header); - buildBody(&body); - } + public: + explicit LogicalSwitchEditPage(uint8_t index) : + Page(ICON_MODEL_LOGICAL_SWITCHES), index(index) + { + buildHeader(&header); + buildBody(&body); + } - protected: - uint8_t index; - bool active = false; - FormGroup * logicalSwitchOneWindow = nullptr; - StaticText * headerSwitchName = nullptr; - NumberEdit * v2Edit = nullptr; + protected: + uint8_t index; + bool active = false; + FormGroup* logicalSwitchOneWindow = nullptr; + StaticText* headerSwitchName = nullptr; + NumberEdit* v2Edit = nullptr; - bool isActive() const - { - return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + index); - } + bool isActive() const + { + return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + index); + } - void checkEvents() override - { - Page::checkEvents(); - if (active != isActive()) { - if(isActive()) { - lv_obj_add_state(headerSwitchName->getLvObj(), LV_STATE_USER_1); - } else { - lv_obj_clear_state(headerSwitchName->getLvObj(), LV_STATE_USER_1); - } - active = isActive(); - invalidate(); + void checkEvents() override + { + Page::checkEvents(); + if (active != isActive()) { + if (isActive()) { + lv_obj_add_state(headerSwitchName->getLvObj(), LV_STATE_USER_1); + } else { + lv_obj_clear_state(headerSwitchName->getLvObj(), LV_STATE_USER_1); } + active = isActive(); + invalidate(); } + } - void buildHeader(Window * window) - { - header.setTitle(STR_MENULOGICALSWITCHES); - headerSwitchName = new StaticText( - window, - {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, - LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, - getSwitchPositionName(SWSRC_SW1 + index), 0, COLOR_THEME_PRIMARY2); - - lv_obj_set_style_text_color(headerSwitchName->getLvObj(), makeLvColor(COLOR_THEME_ACTIVE), LV_STATE_USER_1); - lv_obj_set_style_text_font(headerSwitchName->getLvObj(), getFont(FONT(BOLD)), LV_STATE_USER_1); - } - - void updateLogicalSwitchOneWindow() - { - FormGridLayout grid; - logicalSwitchOneWindow->clear(); - - LogicalSwitchData * cs = lswAddress(index); - uint8_t cstate = lswFamily(cs->func); + void buildHeader(Window* window) + { + header.setTitle(STR_MENULOGICALSWITCHES); + headerSwitchName = new StaticText( + window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSwitchPositionName(SWSRC_SW1 + index), 0, COLOR_THEME_PRIMARY2); + + lv_obj_set_style_text_color(headerSwitchName->getLvObj(), + makeLvColor(COLOR_THEME_ACTIVE), + LV_STATE_USER_1); + lv_obj_set_style_text_font(headerSwitchName->getLvObj(), + getFont(FONT(BOLD)), LV_STATE_USER_1); + } - if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V1, 0, COLOR_THEME_PRIMARY1); - auto choice = new SwitchChoice(logicalSwitchOneWindow, grid.getFieldSlot(), SWSRC_FIRST_IN_LOGICAL_SWITCHES, SWSRC_LAST_IN_LOGICAL_SWITCHES, GET_SET_DEFAULT(cs->v1)); + void updateLogicalSwitchOneWindow() + { + SwitchChoice* choice; + NumberEdit* timer; + + logicalSwitchOneWindow->clear(); + logicalSwitchOneWindow->setFlexLayout(); + FlexGridLayout grid(col_dsc, row_dsc, 2); + FlexGridLayout grid2(col_dsc2, row_dsc, 2); + + LogicalSwitchData* cs = lswAddress(index); + uint8_t cstate = lswFamily(cs->func); + + // V1 + auto line = logicalSwitchOneWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_V1, 0, COLOR_THEME_PRIMARY1); + switch (cstate) { + case LS_FAMILY_BOOL: + case LS_FAMILY_STICKY: + case LS_FAMILY_EDGE: + choice = new SwitchChoice( + line, rect_t{}, SWSRC_FIRST_IN_LOGICAL_SWITCHES, + SWSRC_LAST_IN_LOGICAL_SWITCHES, GET_SET_DEFAULT(cs->v1)); choice->setAvailableHandler(isSwitchAvailableInLogicalSwitches); - grid.nextLine(); + break; + case LS_FAMILY_COMP: + new SourceChoice(line, rect_t{}, 0, MIXSRC_LAST_TELEM, + GET_SET_DEFAULT(cs->v1)); + break; + case LS_FAMILY_TIMER: + timer = + new NumberEdit(line, rect_t{}, -128, 122, GET_SET_DEFAULT(cs->v1)); + timer->setDisplayHandler([](int32_t value) { + return formatNumberAsString(lswTimerValue(value), PREC1, 0, nullptr, + "s"); + }); + break; + default: + new SourceChoice(line, rect_t{}, 0, MIXSRC_LAST_TELEM, + GET_DEFAULT(cs->v1), [=](int32_t newValue) { + cs->v1 = newValue; + if (v2Edit != nullptr) { + int16_t v2_min = 0, v2_max = 0; + getMixSrcRange(cs->v1, v2_min, v2_max); + v2Edit->setMin(v2_min); + v2Edit->setMax(v2_max); + v2Edit->setValue(cs->v2); + } + SET_DIRTY(); + }); + break; + } - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V2, 0, COLOR_THEME_PRIMARY1); - choice = new SwitchChoice(logicalSwitchOneWindow, grid.getFieldSlot(), SWSRC_FIRST_IN_LOGICAL_SWITCHES, SWSRC_LAST_IN_LOGICAL_SWITCHES, GET_SET_DEFAULT(cs->v2)); - choice->setAvailableHandler(isSwitchAvailableInLogicalSwitches); - grid.nextLine(); - } - else if (cstate == LS_FAMILY_EDGE) { - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V1, 0, COLOR_THEME_PRIMARY1); - auto choice = new SwitchChoice(logicalSwitchOneWindow, grid.getFieldSlot(), SWSRC_FIRST_IN_LOGICAL_SWITCHES, SWSRC_LAST_IN_LOGICAL_SWITCHES, GET_SET_DEFAULT(cs->v1)); + // V2 + if (cstate == LS_FAMILY_EDGE) { + line = logicalSwitchOneWindow->newLine(&grid2); + } else { + line = logicalSwitchOneWindow->newLine(&grid); + } + new StaticText(line, rect_t{}, STR_V2, 0, COLOR_THEME_PRIMARY1); + switch (cstate) { + case LS_FAMILY_BOOL: + case LS_FAMILY_STICKY: + choice = new SwitchChoice( + line, rect_t{}, SWSRC_FIRST_IN_LOGICAL_SWITCHES, + SWSRC_LAST_IN_LOGICAL_SWITCHES, GET_SET_DEFAULT(cs->v2)); choice->setAvailableHandler(isSwitchAvailableInLogicalSwitches); - grid.nextLine(); - - auto edit1 = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(2, 0), -129, 122, GET_DEFAULT(cs->v2)); - auto edit2 = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(2, 1), -1, 222 - cs->v2, GET_SET_DEFAULT(cs->v3)); + break; + case LS_FAMILY_EDGE: { + auto edit1 = + new NumberEdit(line, rect_t{}, -129, 122, GET_DEFAULT(cs->v2)); + auto edit2 = new NumberEdit(line, rect_t{}, -1, 222 - cs->v2, + GET_SET_DEFAULT(cs->v3)); edit1->setSetValueHandler([=](int32_t newValue) { cs->v2 = newValue; SET_DIRTY(); @@ -125,7 +165,8 @@ class LogicalSwitchEditPage: public Page edit2->setValue(cs->v3); }); edit1->setDisplayHandler([](int32_t value) { - return formatNumberAsString(lswTimerValue(value), PREC1); + return formatNumberAsString(lswTimerValue(value), PREC1, 0, nullptr, + "s"); }); edit2->setDisplayHandler([cs](int32_t value) { if (value < 0) @@ -133,141 +174,228 @@ class LogicalSwitchEditPage: public Page else if (value == 0) return std::string("--"); else { - return formatNumberAsString(lswTimerValue(cs->v2 + value), PREC1); + return formatNumberAsString(lswTimerValue(cs->v2 + value), PREC1, 0, + nullptr, "s"); } }); - grid.nextLine(); - } - else if (cstate == LS_FAMILY_COMP) { - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V1, 0, COLOR_THEME_PRIMARY1); - new SourceChoice(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, GET_SET_DEFAULT(cs->v1)); - grid.nextLine(); - - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V2, 0, COLOR_THEME_PRIMARY1); - new SourceChoice(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, GET_SET_DEFAULT(cs->v2)); - grid.nextLine(); - } - else if (cstate == LS_FAMILY_TIMER) { - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V1, 0, COLOR_THEME_PRIMARY1); - auto timer = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), -128, 122, GET_SET_DEFAULT(cs->v1)); - timer->setDisplayHandler([](int32_t value) { - return formatNumberAsString(lswTimerValue(value), PREC1); - }); - grid.nextLine(); - - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V2, 0, COLOR_THEME_PRIMARY1); - timer = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), -128, 122, GET_SET_DEFAULT(cs->v2)); + } break; + case LS_FAMILY_COMP: + new SourceChoice(line, rect_t{}, 0, MIXSRC_LAST_TELEM, + GET_SET_DEFAULT(cs->v2)); + break; + case LS_FAMILY_TIMER: + timer = + new NumberEdit(line, rect_t{}, -128, 122, GET_SET_DEFAULT(cs->v2)); timer->setDisplayHandler([](int32_t value) { - return formatNumberAsString(lswTimerValue(value), PREC1); + return formatNumberAsString(lswTimerValue(value), PREC1, 0, nullptr, + "s"); }); - grid.nextLine(); - } - else { - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V1, 0, COLOR_THEME_PRIMARY1); - new SourceChoice(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, GET_DEFAULT(cs->v1), - [=](int32_t newValue) { - cs->v1 = newValue; - if (v2Edit != nullptr) - { - int16_t v2_min = 0, v2_max = 0; - getMixSrcRange(cs->v1, v2_min, v2_max); - v2Edit->setMin(v2_min); - v2Edit->setMax(v2_max); - v2Edit->setValue(cs->v2); - } - SET_DIRTY(); - }); - grid.nextLine(); - - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_V2, 0, COLOR_THEME_PRIMARY1); + break; + default: int16_t v2_min = 0, v2_max = 0; getMixSrcRange(cs->v1, v2_min, v2_max); - v2Edit = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), - v2_min, v2_max, GET_SET_DEFAULT(cs->v2)); + v2Edit = new NumberEdit(line, rect_t{}, v2_min, v2_max, + GET_SET_DEFAULT(cs->v2)); + lv_obj_set_width(v2Edit->getLvObj(), LV_SIZE_CONTENT); v2Edit->setDisplayHandler([=](int value) -> std::string { if (cs->v1 <= MIXSRC_LAST_CH) value = calc100toRESX(value); std::string txt = getSourceCustomValueString(cs->v1, value, 0); return txt; }); - grid.nextLine(); - } - - // AND switch - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_AND_SWITCH, 0, COLOR_THEME_PRIMARY1); - auto choice = new SwitchChoice(logicalSwitchOneWindow, grid.getFieldSlot(), -MAX_LS_ANDSW, MAX_LS_ANDSW, GET_SET_DEFAULT(cs->andsw)); - choice->setAvailableHandler(isSwitchAvailableInLogicalSwitches); - grid.nextLine(); - - // Duration - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_DURATION, 0, COLOR_THEME_PRIMARY1); - auto edit = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MAX_LS_DURATION, GET_SET_DEFAULT(cs->duration), 0, PREC1); - edit->setZeroText("---"); - grid.nextLine(); - - // Delay - new StaticText(logicalSwitchOneWindow, grid.getLabelSlot(), STR_DELAY, 0, COLOR_THEME_PRIMARY1); - if (cstate == LS_FAMILY_EDGE) { - new StaticText(logicalSwitchOneWindow, grid.getFieldSlot(), STR_NA, 0, COLOR_THEME_PRIMARY1); - } - else { - auto edit = new NumberEdit(logicalSwitchOneWindow, grid.getFieldSlot(), 0, MAX_LS_DELAY, GET_SET_DEFAULT(cs->delay), 0, PREC1); - edit->setZeroText("---"); - } - grid.nextLine(); + break; } - void buildBody(FormWindow * window) - { - FormGridLayout grid; - grid.spacer(PAGE_PADDING); - - LogicalSwitchData * cs = lswAddress(index); - - // LS Func - new StaticText(window, grid.getLabelSlot(), STR_FUNC, 0, COLOR_THEME_PRIMARY1); - auto functionChoice = new Choice(window, grid.getFieldSlot(), STR_VCSWFUNC, 0, LS_FUNC_MAX, GET_DEFAULT(cs->func)); - functionChoice->setSetValueHandler([=](int32_t newValue) { - cs->func = newValue; - if (lswFamily(cs->func) == LS_FAMILY_TIMER) { - cs->v1 = cs->v2 = 0; - } - else if (lswFamily(cs->func) == LS_FAMILY_EDGE) { - cs->v1 = 0; - cs->v2 = -129; - cs->v3 = 0; - } - else { - cs->v1 = cs->v2 = 0; - } - SET_DIRTY(); - updateLogicalSwitchOneWindow(); + // AND switch + line = logicalSwitchOneWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_AND_SWITCH, 0, COLOR_THEME_PRIMARY1); + choice = new SwitchChoice(line, rect_t{}, -MAX_LS_ANDSW, MAX_LS_ANDSW, + GET_SET_DEFAULT(cs->andsw)); + choice->setAvailableHandler(isSwitchAvailableInLogicalSwitches); + + // Duration + line = logicalSwitchOneWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_DURATION, 0, COLOR_THEME_PRIMARY1); + auto edit = new NumberEdit(line, rect_t{}, 0, MAX_LS_DURATION, + GET_SET_DEFAULT(cs->duration), 0, PREC1); + edit->setZeroText("---"); + edit->setDisplayHandler([](int32_t value) { + return formatNumberAsString(value, PREC1, 0, nullptr, "s"); + }); + + // Delay + line = logicalSwitchOneWindow->newLine(&grid); + new StaticText(line, rect_t{}, STR_DELAY, 0, COLOR_THEME_PRIMARY1); + if (cstate == LS_FAMILY_EDGE) { + new StaticText(line, rect_t{}, STR_NA, 0, COLOR_THEME_PRIMARY1); + } else { + auto edit = new NumberEdit(line, rect_t{}, 0, MAX_LS_DELAY, + GET_SET_DEFAULT(cs->delay), 0, PREC1); + edit->setDisplayHandler([](int32_t value) { + if (value == 0) return std::string("---"); + return formatNumberAsString(value, PREC1, 0, nullptr, "s"); }); - functionChoice->setAvailableHandler(isLogicalSwitchFunctionAvailable); - grid.nextLine(); + } + } - logicalSwitchOneWindow = new FormGroup(window, { 0, grid.getWindowHeight(), LCD_W, 0 }// , FORM_FORWARD_FOCUS - ); + void buildBody(FormWindow* window) + { + window->setFlexLayout(); + FlexGridLayout grid(col_dsc, row_dsc, 2); + lv_obj_set_style_pad_all(window->getLvObj(), lv_dpx(8), 0); + + LogicalSwitchData* cs = lswAddress(index); + + // LS Func + auto line = window->newLine(&grid); + new StaticText(line, rect_t{}, STR_FUNC, 0, COLOR_THEME_PRIMARY1); + auto functionChoice = new Choice(line, rect_t{}, STR_VCSWFUNC, 0, + LS_FUNC_MAX, GET_DEFAULT(cs->func)); + functionChoice->setSetValueHandler([=](int32_t newValue) { + cs->func = newValue; + if (lswFamily(cs->func) == LS_FAMILY_TIMER) { + cs->v1 = cs->v2 = 0; + } else if (lswFamily(cs->func) == LS_FAMILY_EDGE) { + cs->v1 = 0; + cs->v2 = -129; + cs->v3 = 0; + } else { + cs->v1 = cs->v2 = 0; + } + SET_DIRTY(); updateLogicalSwitchOneWindow(); - grid.addWindow(logicalSwitchOneWindow); - } + }); + functionChoice->setAvailableHandler(isLogicalSwitchFunctionAvailable); + + logicalSwitchOneWindow = new FormWindow(window, rect_t{}); + updateLogicalSwitchOneWindow(); + } }; -static constexpr coord_t line1 = FIELD_PADDING_TOP; -static constexpr coord_t line2 = line1 + PAGE_LINE_HEIGHT; -static constexpr coord_t col1 = 20; -static constexpr coord_t col2 = (LCD_W - 100) / 3 + col1; -static constexpr coord_t col3 = ((LCD_W - 100) / 3) * 2 + col1; +void getsEdgeDelayParam(char* s, LogicalSwitchData* ls) +{ + sprintf(s, "[%s:%s]", + formatNumberAsString(lswTimerValue(ls->v2), PREC1, 0, nullptr, "s") + .c_str(), + (ls->v3 < 0) ? "<<" + : (ls->v3 == 0) ? "--" + : formatNumberAsString(lswTimerValue(ls->v2 + ls->v3), + PREC1, 0, nullptr, "s") + .c_str()); +} + +#if LCD_W > LCD_H // Landscape + +#define TXT_ALIGN LV_GRID_ALIGN_CENTER + +static const lv_coord_t b_col_dsc[] = {36, 50, 88, 92, + 88, 40, 40, LV_GRID_TEMPLATE_LAST}; + +static const lv_coord_t b_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; + +#define NM_ROW_CNT 1 +#define ANDSW_ROW 0 +#define ANDSW_COL 4 + +#else // Portrait + +#define TXT_ALIGN LV_GRID_ALIGN_START + +static const lv_coord_t b_col_dsc[] = {36, LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; + +static const lv_coord_t b_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, + LV_GRID_TEMPLATE_LAST}; + +#define NM_ROW_CNT 2 +#define ANDSW_ROW 1 +#define ANDSW_COL 1 + +#endif class LogicalSwitchButton : public Button { public: - LogicalSwitchButton(FormGroup* parent, const rect_t& rect, int lsIndex) : - Button(parent, rect, nullptr, 0, COLOR_THEME_PRIMARY1), lsIndex(lsIndex), active(isActive()) + LogicalSwitchButton(Window* parent, const rect_t& rect, int lsIndex) : + Button(parent, rect, nullptr, 0, 0, input_mix_line_create), + lsIndex(lsIndex) { - LogicalSwitchData* ls = lswAddress(lsIndex); - if (ls->andsw != SWSRC_NONE || ls->duration != 0 || ls->delay != 0) - setHeight(height() + 20); +#if LCD_H > LCD_W + padTop(0); +#endif + padLeft(3); + padRight(3); + lv_obj_set_layout(lvobj, LV_LAYOUT_GRID); + lv_obj_set_grid_dsc_array(lvobj, b_col_dsc, b_row_dsc); + lv_obj_set_style_pad_row(lvobj, 0, 0); + lv_obj_set_style_pad_column(lvobj, 4, 0); + + check(isActive()); + + lv_obj_update_layout(parent->getLvObj()); + if (lv_obj_is_visible(lvobj)) delayed_init(nullptr); + + lv_obj_add_event_cb(lvobj, LogicalSwitchButton::on_draw, + LV_EVENT_DRAW_MAIN_BEGIN, nullptr); + } + + static void on_draw(lv_event_t* e) + { + lv_obj_t* target = lv_event_get_target(e); + auto line = (LogicalSwitchButton*)lv_obj_get_user_data(target); + if (line) { + if (!line->init) + line->delayed_init(e); + else + line->refresh(); + } + } + + void delayed_init(lv_event_t* e) + { + lsName = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsName, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lsName, LV_GRID_ALIGN_STRETCH, 0, 1, + LV_GRID_ALIGN_CENTER, 0, NM_ROW_CNT); + + lsFunc = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsFunc, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lsFunc, LV_GRID_ALIGN_STRETCH, 1, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + + lsV1 = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsV1, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_grid_cell(lsV1, LV_GRID_ALIGN_STRETCH, 2, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + + lsV2 = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsV2, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_grid_cell(lsV2, LV_GRID_ALIGN_STRETCH, 3, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + + lsAnd = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsAnd, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_grid_cell(lsAnd, LV_GRID_ALIGN_STRETCH, ANDSW_COL, 1, + LV_GRID_ALIGN_CENTER, ANDSW_ROW, 1); + + lsDuration = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsDuration, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_grid_cell(lsDuration, LV_GRID_ALIGN_STRETCH, ANDSW_COL + 1, 1, + LV_GRID_ALIGN_CENTER, ANDSW_ROW, 1); + + lsDelay = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsDelay, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_grid_cell(lsDelay, LV_GRID_ALIGN_STRETCH, ANDSW_COL + 2, 1, + LV_GRID_ALIGN_CENTER, ANDSW_ROW, 1); + + init = true; + refresh(); + lv_obj_update_layout(lvobj); + + if (e) { + auto param = lv_event_get_param(e); + lv_event_send(lvobj, LV_EVENT_DRAW_MAIN, param); + } } bool isActive() const @@ -277,171 +405,288 @@ class LogicalSwitchButton : public Button void checkEvents() override { - if (active != isActive()) { - invalidate(); - active = !active; - } - Button::checkEvents(); + check(isActive()); } - void paintLogicalSwitchLine(BitmapBuffer* dc) + void refresh() { + if (!init) return; + + char s[20]; + LogicalSwitchData* ls = lswAddress(lsIndex); uint8_t lsFamily = lswFamily(ls->func); - // CSW func - dc->drawTextAtIndex(col1, line1, STR_VCSWFUNC, ls->func, COLOR_THEME_SECONDARY1); - - // CSW params - if (lsFamily == LS_FAMILY_BOOL || lsFamily == LS_FAMILY_STICKY) { - drawSwitch(dc, col2, line1, ls->v1, COLOR_THEME_SECONDARY1); - drawSwitch(dc, col3, line1, ls->v2, COLOR_THEME_SECONDARY1); - } else if (lsFamily == LS_FAMILY_EDGE) { - drawSwitch(dc, col2, line1, ls->v1, COLOR_THEME_SECONDARY1); - putsEdgeDelayParam(dc, col3, line1, ls, COLOR_THEME_SECONDARY1); - } else if (lsFamily == LS_FAMILY_COMP) { - drawSource(dc, col2, line1, ls->v1, COLOR_THEME_SECONDARY1); - drawSource(dc, col3, line1, ls->v2, COLOR_THEME_SECONDARY1); - } else if (lsFamily == LS_FAMILY_TIMER) { - dc->drawNumber(col2, line1, lswTimerValue(ls->v1), COLOR_THEME_SECONDARY1 | LEFT | PREC1); - dc->drawNumber(col3, line1, lswTimerValue(ls->v2), COLOR_THEME_SECONDARY1 | LEFT | PREC1); - } else { - drawSource(dc, col2, line1, ls->v1, COLOR_THEME_SECONDARY1); - drawSourceCustomValue(dc, col3, line1, ls->v1, - (ls->v1 <= MIXSRC_LAST_CH ? calc100toRESX(ls->v2) : ls->v2), COLOR_THEME_SECONDARY1); + lv_label_set_text(lsName, getSwitchPositionName(SWSRC_SW1 + lsIndex)); + lv_label_set_text(lsFunc, STR_VCSWFUNC[ls->func]); + + // CSW params - V1 + switch (lsFamily) { + case LS_FAMILY_BOOL: + case LS_FAMILY_STICKY: + case LS_FAMILY_EDGE: + lv_label_set_text(lsV1, getSwitchPositionName(ls->v1)); + break; + case LS_FAMILY_TIMER: + lv_label_set_text(lsV1, formatNumberAsString(lswTimerValue(ls->v1), + PREC1, 0, nullptr, "s") + .c_str()); + break; + default: + lv_label_set_text(lsV1, getSourceString(ls->v1)); + break; + } + + // CSW params - V2 + strcat(s, " "); + switch (lsFamily) { + case LS_FAMILY_BOOL: + case LS_FAMILY_STICKY: + lv_label_set_text(lsV2, getSwitchPositionName(ls->v2)); + break; + case LS_FAMILY_EDGE: + getsEdgeDelayParam(s, ls); + lv_label_set_text(lsV2, s); + break; + case LS_FAMILY_TIMER: + lv_label_set_text(lsV2, formatNumberAsString(lswTimerValue(ls->v2), + PREC1, 0, nullptr, "s") + .c_str()); + break; + case LS_FAMILY_COMP: + lv_label_set_text(lsV2, getSourceString(ls->v2)); + break; + default: + lv_label_set_text( + lsV2, + getSourceCustomValueString( + ls->v1, + (ls->v1 <= MIXSRC_LAST_CH ? calc100toRESX(ls->v2) : ls->v2), + 0)); + break; } // AND switch - drawSwitch(dc, col1, line2, ls->andsw, COLOR_THEME_SECONDARY1); + lv_label_set_text(lsAnd, getSwitchPositionName(ls->andsw)); // CSW duration if (ls->duration > 0) { - dc->drawNumber(col2, line2, ls->duration, COLOR_THEME_SECONDARY1 | PREC1 | LEFT); + lv_label_set_text( + lsDuration, + formatNumberAsString(ls->duration, PREC1, 0, nullptr, "s").c_str()); + } else { + lv_label_set_text(lsDuration, ""); } // CSW delay if (lsFamily != LS_FAMILY_EDGE && ls->delay > 0) { - dc->drawNumber(col3, line2, ls->delay, COLOR_THEME_SECONDARY1 | PREC1 | LEFT); + lv_label_set_text( + lsDelay, + formatNumberAsString(ls->delay, PREC1, 0, nullptr, "s").c_str()); + } else { + lv_label_set_text(lsDelay, ""); } } - void paint(BitmapBuffer* dc) override - { - if (active) - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, COLOR_THEME_ACTIVE); - else - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, COLOR_THEME_PRIMARY2); - - paintLogicalSwitchLine(dc); - - // The bounding rect - if (hasFocus()) - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); - else - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); - } - protected: + bool init = false; uint8_t lsIndex; - bool active; + + lv_obj_t* lsName = nullptr; + lv_obj_t* lsFunc = nullptr; + lv_obj_t* lsV1 = nullptr; + lv_obj_t* lsV2 = nullptr; + lv_obj_t* lsAnd = nullptr; + lv_obj_t* lsDuration = nullptr; + lv_obj_t* lsDelay = nullptr; }; -ModelLogicalSwitchesPage::ModelLogicalSwitchesPage(): - PageTab(STR_MENULOGICALSWITCHES, ICON_MODEL_LOGICAL_SWITCHES) -{ -} +#if LCD_W > LCD_H +#define LS_BUTTON_H 34 +#else +#define LS_BUTTON_H 45 +#endif -void ModelLogicalSwitchesPage::rebuild(FormWindow * window, int8_t focusIndex) +ModelLogicalSwitchesPage::ModelLogicalSwitchesPage() : + PageTab(STR_MENULOGICALSWITCHES, ICON_MODEL_LOGICAL_SWITCHES) { - auto scroll_y = lv_obj_get_scroll_y(window->getLvObj()); - window->clear(); - build(window, focusIndex); - lv_obj_scroll_to_y(window->getLvObj(), scroll_y, LV_ANIM_OFF); } -void ModelLogicalSwitchesPage::editLogicalSwitch(FormWindow * window, uint8_t lsIndex) +void ModelLogicalSwitchesPage::rebuild(FormWindow* window) { - Window * lsWindow = new LogicalSwitchEditPage(lsIndex); - lsWindow->setCloseHandler([=]() { - rebuild(window, lsIndex); - }); + // When window.clear() is called the last button on screen is given focus + // (???) This causes the page to jump to the end when rebuilt. Set flag to + // bypass the button focus handler and reset focusIndex when rebuilding + isRebuilding = true; + window->clear(); + build(window); + isRebuilding = false; } -void ModelLogicalSwitchesPage::build(FormWindow* window, int8_t focusIndex) +void ModelLogicalSwitchesPage::newLS(FormWindow* window, bool pasteLS) { - FormGridLayout grid; - grid.spacer(PAGE_PADDING); - grid.setLabelWidth(66); - window->padAll(0); + Menu* menu = new Menu(Layer::back()); + menu->setTitle(STR_MENU_LOGICAL_SWITCHES); + // search for unused switches for (uint8_t i = 0; i < MAX_LOGICAL_SWITCHES; i++) { LogicalSwitchData* ls = lswAddress(i); - if (ls->func == LS_FUNC_NONE) { - auto button = new TextButton(window, grid.getLabelSlot(), - getSwitchPositionName(SWSRC_SW1 + i)); - button->setPressHandler([=]() { - if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) { - Menu* menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { editLogicalSwitch(window, i); }); - menu->addLine(STR_PASTE, [=]() { - *ls = clipboard.data.csw; - storageDirty(EE_MODEL); - rebuild(window, i); - }); + std::string ch_name(getSwitchPositionName(SWSRC_SW1 + i)); + menu->addLineBuffered(ch_name.c_str(), [=]() { + if (pasteLS) { + *ls = clipboard.data.csw; + storageDirty(EE_MODEL); + focusIndex = i; + rebuild(window); } else { - editLogicalSwitch(window, i); + Window* lsWindow = new LogicalSwitchEditPage(i); + lsWindow->setCloseHandler([=]() { + if (ls->func != LS_FUNC_NONE) { + focusIndex = i; + rebuild(window); + } + }); } - return 0; }); - - grid.spacer(button->height() + 5); - } else { - auto txt = new StaticText(window, grid.getLabelSlot(), - getSwitchPositionName(SWSRC_SW1 + i), - BUTTON_BACKGROUND, COLOR_THEME_PRIMARY1 | CENTERED); + } + } + menu->updateLines(); +} + +void ModelLogicalSwitchesPage::plusPopup(FormWindow* window) +{ + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) { + Menu* menu = new Menu(window); + menu->addLine(STR_NEW, [=]() { newLS(window, false); }); + menu->addLine(STR_PASTE, [=]() { newLS(window, true); }); + } else { + newLS(window, false); + } +} + +void ModelLogicalSwitchesPage::build(FormWindow* window) +{ +#if LCD_W > LCD_H +#define PER_ROW 6 + static const lv_coord_t l_col_dsc[] = { + LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; +#else +#define PER_ROW 4 + static const lv_coord_t l_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST}; +#endif + + window->padAll(4); + + auto form = new FormWindow(window, rect_t{}); + form->setFlexLayout(); + form->padAll(0); + + FlexGridLayout grid(l_col_dsc, row_dsc, 2); + + FormWindow::Line* line; + bool hasEmptySwitch = false; + Button* button; + + // Reset focusIndex after switching tabs + if (!isRebuilding) focusIndex = prevFocusIndex; + + for (uint8_t i = 0; i < MAX_LOGICAL_SWITCHES; i++) { + LogicalSwitchData* ls = lswAddress(i); + + bool isActive = (ls->func != LS_FUNC_NONE); + + if (isActive) { + line = form->newLine(&grid); + + button = new LogicalSwitchButton( + line, rect_t{0, 0, window->width() - 12, LS_BUTTON_H}, i); + + lv_obj_set_grid_cell(button->getLvObj(), LV_GRID_ALIGN_CENTER, 0, PER_ROW, + LV_GRID_ALIGN_CENTER, 0, 1); - auto button = new LogicalSwitchButton(window, grid.getFieldSlot(), i); button->setPressHandler([=]() { Menu* menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { editLogicalSwitch(window, i); }); - if (ls->func) + menu->addLine(STR_EDIT, [=]() { + Window* lsWindow = new LogicalSwitchEditPage(i); + lsWindow->setCloseHandler([=]() { + if (isActive) + lv_event_send(button->getLvObj(), LV_EVENT_VALUE_CHANGED, + nullptr); + else + rebuild(window); + }); + }); + if (isActive) { menu->addLine(STR_COPY, [=]() { clipboard.type = CLIPBOARD_TYPE_CUSTOM_SWITCH; clipboard.data.csw = *ls; }); + } if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) menu->addLine(STR_PASTE, [=]() { *ls = clipboard.data.csw; storageDirty(EE_MODEL); - rebuild(window, i); + rebuild(window); }); - if (ls->func || ls->v1 || ls->v2 || ls->delay || ls->duration || - ls->andsw) + if (isActive || ls->v1 || ls->v2 || ls->delay || ls->duration || + ls->andsw) { menu->addLine(STR_CLEAR, [=]() { memset(ls, 0, sizeof(LogicalSwitchData)); storageDirty(EE_MODEL); - rebuild(window, i); + rebuild(window); }); + } return 0; }); - button->setFocusHandler([=](bool focus) { - if (focus) { - txt->setBackgroundColor(COLOR_THEME_FOCUS); - txt->setTextFlags(COLOR_THEME_PRIMARY2 | CENTERED); - } else { - txt->setBackgroundColor(COLOR_THEME_SECONDARY2); - txt->setTextFlags(COLOR_THEME_PRIMARY1 | CENTERED); + + if (focusIndex == i) { + lv_group_focus_obj(button->getLvObj()); + } + + button->setLongPressHandler([=]() -> uint8_t { + if (addButton) { + lv_group_focus_obj(addButton->getLvObj()); + plusPopup(window); } - txt->invalidate(); + return 0; }); - txt->setHeight(button->height()); - grid.spacer(button->height() + 5); + button->setFocusHandler([=](bool hasFocus) { + if (hasFocus && !isRebuilding) { + prevFocusIndex = focusIndex; + focusIndex = i; + } + }); + } else { + hasEmptySwitch = true; } } - grid.nextLine(); - + if (hasEmptySwitch) { + line = form->newLine(&grid); + addButton = + new TextButton(line, rect_t{0, 0, window->width() - 12, LS_BUTTON_H}, + LV_SYMBOL_PLUS, [=]() { + plusPopup(window); + return 0; + }); + + addButton->setLongPressHandler([=]() -> uint8_t { + plusPopup(window); + return 0; + }); + + addButton->setFocusHandler([=](bool hasFocus) { + if (hasFocus && !isRebuilding) { + prevFocusIndex = focusIndex; + } + }); + } else { + addButton = nullptr; + } } diff --git a/radio/src/gui/colorlcd/model_logical_switches.h b/radio/src/gui/colorlcd/model_logical_switches.h index 2f1ac44af5c..5bb260d0d4b 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.h +++ b/radio/src/gui/colorlcd/model_logical_switches.h @@ -24,19 +24,22 @@ #include "tabsgroup.h" -class ModelLogicalSwitchesPage: public PageTab { -public: - ModelLogicalSwitchesPage(); +class ModelLogicalSwitchesPage : public PageTab +{ + public: + ModelLogicalSwitchesPage(); - virtual void build(FormWindow * window) override - { - build(window, 0); - } + virtual void build(FormWindow* window) override; -protected: - void build(FormWindow * window, int8_t focusIndex); - void rebuild(FormWindow * window, int8_t focusIndex); - void editLogicalSwitch(FormWindow * window, uint8_t lsIndex); + protected: + int8_t focusIndex = -1; + int8_t prevFocusIndex = -1; + bool isRebuilding = false; + Button* addButton = nullptr; + + void rebuild(FormWindow* window); + void newLS(FormWindow* window, bool pasteLS); + void plusPopup(FormWindow* window); }; -#endif //_MODEL_LOGICAL_SWITCHES_H +#endif //_MODEL_LOGICAL_SWITCHES_H diff --git a/radio/src/gui/colorlcd/view_logical_switches.cpp b/radio/src/gui/colorlcd/view_logical_switches.cpp index 122fe7fe6ad..af65d166fff 100644 --- a/radio/src/gui/colorlcd/view_logical_switches.cpp +++ b/radio/src/gui/colorlcd/view_logical_switches.cpp @@ -21,75 +21,284 @@ #include "opentx.h" #include "view_logical_switches.h" -#include "gridlayout.h" -#include -#if LCD_W > LCD_H // Landscape - #define BTN_MATRIX_COL 8 -#else // Portrait - #define BTN_MATRIX_COL 4 +#if LCD_W > LCD_H // Landscape + +#define BTN_MATRIX_COL 8 +#define BTN_HEIGHT 20 +#define FOOTER_HEIGHT 20 +#define DURSW_ROW 0 +#define DURSW_COL 3 + +// Switch grid +static const lv_coord_t col_dsc[] = { + LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; + +// Footer grid +static const lv_coord_t f_col_dsc[] = { + 60, LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), 50, + 50, LV_GRID_TEMPLATE_LAST}; + +#else // Portrait + +#define BTN_MATRIX_COL 4 +#define BTN_HEIGHT 21 +#define FOOTER_HEIGHT 40 +#define DURSW_ROW 1 +#define DURSW_COL 2 + +static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST}; + +// Footer grid +static const lv_coord_t f_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST}; + #endif +static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, + LV_GRID_TEMPLATE_LAST}; + +void getsEdgeDelayParam(char* s, LogicalSwitchData* ls); + +class LogicalSwitchDisplayFooter : public Window +{ + public: + explicit LogicalSwitchDisplayFooter(Window* parent, rect_t rect) : + Window(parent, rect, OPAQUE) + { + padAll(0); + padLeft(4); + padRight(4); + lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), 0); + + lv_obj_set_layout(lvobj, LV_LAYOUT_GRID); + lv_obj_set_grid_dsc_array(lvobj, f_col_dsc, row_dsc); + lv_obj_set_style_pad_row(lvobj, 0, 0); + lv_obj_set_style_pad_column(lvobj, 2, 0); + + lsFunc = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsFunc, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lsFunc, LV_GRID_ALIGN_STRETCH, 0, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + lv_obj_set_style_text_color(lsFunc, makeLvColor(COLOR_THEME_PRIMARY2), 0); + + lsV1 = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsV1, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lsV1, LV_GRID_ALIGN_STRETCH, 1, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + lv_obj_set_style_text_color(lsV1, makeLvColor(COLOR_THEME_PRIMARY2), 0); + + lsV2 = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsV2, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lsV2, LV_GRID_ALIGN_STRETCH, 2, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + lv_obj_set_style_text_color(lsV2, makeLvColor(COLOR_THEME_PRIMARY2), 0); + + lsAnd = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsAnd, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lsAnd, LV_GRID_ALIGN_STRETCH, 3, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + lv_obj_set_style_text_color(lsAnd, makeLvColor(COLOR_THEME_PRIMARY2), 0); + + lsDuration = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsDuration, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lsDuration, LV_GRID_ALIGN_STRETCH, DURSW_COL, 1, + LV_GRID_ALIGN_CENTER, DURSW_ROW, 1); + lv_obj_set_style_text_color(lsDuration, makeLvColor(COLOR_THEME_PRIMARY2), + 0); + + lsDelay = lv_label_create(lvobj); + lv_obj_set_style_text_align(lsDelay, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lsDelay, LV_GRID_ALIGN_STRETCH, DURSW_COL + 1, 1, + LV_GRID_ALIGN_CENTER, DURSW_ROW, 1); + lv_obj_set_style_text_color(lsDelay, makeLvColor(COLOR_THEME_PRIMARY2), 0); + + lv_obj_update_layout(parent->getLvObj()); + + refresh(); + } + + void refresh() + { + LogicalSwitchData* ls = lswAddress(lsIndex); + uint8_t lsFamily = lswFamily(ls->func); + + char s[20]; + + lv_label_set_text(lsFunc, STR_VCSWFUNC[ls->func]); + + // CSW params - V1 + switch (lsFamily) { + case LS_FAMILY_BOOL: + case LS_FAMILY_STICKY: + case LS_FAMILY_EDGE: + lv_label_set_text(lsV1, getSwitchPositionName(ls->v1)); + break; + case LS_FAMILY_TIMER: + lv_label_set_text(lsV1, formatNumberAsString(lswTimerValue(ls->v1), + PREC1, 0, nullptr, "s") + .c_str()); + break; + default: + lv_label_set_text(lsV1, getSourceString(ls->v1)); + break; + } + + // CSW params - V2 + strcat(s, " "); + switch (lsFamily) { + case LS_FAMILY_BOOL: + case LS_FAMILY_STICKY: + lv_label_set_text(lsV2, getSwitchPositionName(ls->v2)); + break; + case LS_FAMILY_EDGE: + getsEdgeDelayParam(s, ls); + lv_label_set_text(lsV2, s); + break; + case LS_FAMILY_TIMER: + lv_label_set_text(lsV2, formatNumberAsString(lswTimerValue(ls->v2), + PREC1, 0, nullptr, "s") + .c_str()); + break; + case LS_FAMILY_COMP: + lv_label_set_text(lsV2, getSourceString(ls->v2)); + break; + default: + lv_label_set_text( + lsV2, + getSourceCustomValueString( + ls->v1, + (ls->v1 <= MIXSRC_LAST_CH ? calc100toRESX(ls->v2) : ls->v2), + 0)); + break; + } + + // AND switch + lv_label_set_text(lsAnd, getSwitchPositionName(ls->andsw)); + + // CSW duration + if (ls->duration > 0) { + lv_label_set_text( + lsDuration, + formatNumberAsString(ls->duration, PREC1, 0, nullptr, "s").c_str()); + } else { + lv_label_set_text(lsDuration, ""); + } + + // CSW delay + if (lsFamily != LS_FAMILY_EDGE && ls->delay > 0) { + lv_label_set_text( + lsDelay, + formatNumberAsString(ls->delay, PREC1, 0, nullptr, "s").c_str()); + } else { + lv_label_set_text(lsDelay, ""); + } + } + + void setIndex(unsigned value) + { + lsIndex = value; + refresh(); + } + + protected: + unsigned lsIndex = 0; + lv_obj_t* lsFunc = nullptr; + lv_obj_t* lsV1 = nullptr; + lv_obj_t* lsV2 = nullptr; + lv_obj_t* lsAnd = nullptr; + lv_obj_t* lsDuration = nullptr; + lv_obj_t* lsDelay = nullptr; +}; + class LogicalSwitchDisplayButton : public TextButton { - public: - LogicalSwitchDisplayButton(FormGroup* parent, const rect_t& rect, - std::string text, unsigned index) : - TextButton(parent, rect, std::move(text), nullptr, OPAQUE), index(index) - { - } - - void checkEvents() override - { - bool newvalue = getSwitch(SWSRC_SW1 + index); - if (value != newvalue) { - if (newvalue) { - lv_obj_add_state(lvobj, LV_STATE_CHECKED); - } else { - lv_obj_clear_state(lvobj, LV_STATE_CHECKED); - } - value = newvalue; - invalidate(); - } - Button::checkEvents(); - } - - protected: - unsigned index = 0; - bool value = false; + public: + LogicalSwitchDisplayButton(Window* parent, const rect_t& rect, + std::string text, unsigned index) : + TextButton(parent, rect, std::move(text), nullptr, OPAQUE), index(index) + { + } + + void checkEvents() override + { + bool newvalue = getSwitch(SWSRC_SW1 + index); + if (value != newvalue) { + if (newvalue) { + lv_obj_add_state(lvobj, LV_STATE_CHECKED); + } else { + lv_obj_clear_state(lvobj, LV_STATE_CHECKED); + } + value = newvalue; + invalidate(); + } + Button::checkEvents(); + } + + protected: + unsigned index = 0; + bool value = false; }; -void LogicalSwitchesViewPage::build(FormWindow * window) +void LogicalSwitchesViewPage::build(FormWindow* window) { - constexpr coord_t LSW_VIEW_FOOTER_HEIGHT = 20; - FormGridLayout grid; - grid.spacer(PAGE_PADDING); - grid.setLabelWidth(BTN_MATRIX_COL); window->padAll(0); + auto form = new FormWindow(window, rect_t{}); + form->setFlexLayout(); + form->padAll(0); + form->padTop(3); + + FlexGridLayout grid(col_dsc, row_dsc, 2); + FormWindow::Line* line; + // Footer footer = new LogicalSwitchDisplayFooter( - window, {0, window->height() - LSW_VIEW_FOOTER_HEIGHT, window->width(), - LSW_VIEW_FOOTER_HEIGHT}); + window, + {0, window->height() - FOOTER_HEIGHT, window->width(), FOOTER_HEIGHT}); // LSW table - std::string lsString("LS64"); - lcdColorTable[CUSTOM_COLOR_INDEX] = RGB(160, 160, 160); + std::string lsString("L64"); for (uint8_t i = 0; i < MAX_LOGICAL_SWITCHES; i++) { + if ((i % BTN_MATRIX_COL) == 0) { + line = form->newLine(&grid); + line->padAll(1); + line->padLeft(4); + line->padRight(4); + } + + LogicalSwitchData* ls = lswAddress(i); + bool isActive = (ls->func != LS_FUNC_NONE); strAppendSigned(&lsString[1], i + 1, 2); - auto button = new LogicalSwitchDisplayButton( - window, grid.getFieldSlot(BTN_MATRIX_COL, i % BTN_MATRIX_COL), lsString, i); + if (isActive) { + auto button = new LogicalSwitchDisplayButton( + line, rect_t{0, 0, 0, BTN_HEIGHT}, lsString, i); + lv_obj_set_grid_cell(button->getLvObj(), LV_GRID_ALIGN_STRETCH, + i % BTN_MATRIX_COL, 1, LV_GRID_ALIGN_CENTER, 0, 1); - button->setFocusHandler([=](bool focus) { - if (focus) { - footer->setIndex(i); - footer->invalidate(); - } - return 0; - }); - if ((i + 1) % BTN_MATRIX_COL == 0) grid.nextLine(); + button->setFocusHandler([=](bool focus) { + if (focus) { + footer->setIndex(i); + footer->invalidate(); + } + return 0; + }); + } else { + auto lbl = lv_label_create(line->getLvObj()); + lv_obj_set_height(lbl, BTN_HEIGHT); + lv_obj_set_style_text_color(lbl, makeLvColor(COLOR_THEME_DISABLED), 0); + lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_CENTER, 0); + lv_obj_set_grid_cell(lbl, LV_GRID_ALIGN_STRETCH, i % BTN_MATRIX_COL, 1, + LV_GRID_ALIGN_CENTER, 0, 1); + lv_label_set_text(lbl, lsString.c_str()); + } } } diff --git a/radio/src/gui/colorlcd/view_logical_switches.h b/radio/src/gui/colorlcd/view_logical_switches.h index 74836c8e14b..c03d7db5f8a 100644 --- a/radio/src/gui/colorlcd/view_logical_switches.h +++ b/radio/src/gui/colorlcd/view_logical_switches.h @@ -25,92 +25,17 @@ #include "tabsgroup.h" #include "switches.h" -constexpr coord_t CSW_1ST_COLUMN = 50; -constexpr coord_t CSW_2ND_COLUMN = 120; -constexpr coord_t CSW_3RD_COLUMN = 200; -constexpr coord_t CSW_4TH_COLUMN = 280; -constexpr coord_t CSW_5TH_COLUMN = 340; -constexpr coord_t CSW_6TH_COLUMN = 390; +class LogicalSwitchDisplayFooter; -void putsEdgeDelayParam(BitmapBuffer* dc, coord_t x, coord_t y, - LogicalSwitchData* ls, LcdFlags flags); - -class LogicalSwitchDisplayFooter : public Window +class LogicalSwitchesViewPage : public PageTab { public: - explicit LogicalSwitchDisplayFooter(Window* parent, rect_t rect) : - Window(parent, rect, OPAQUE), rect(rect) - { - } - - void setIndex(unsigned value) { index = value; } - - void paint(BitmapBuffer* dc) override + LogicalSwitchesViewPage() : + PageTab(STR_MONITOR_SWITCHES, ICON_MONITOR_LOGICAL_SWITCHES) { - dc->clear(COLOR_THEME_SECONDARY1); - LogicalSwitchData* cs = lswAddress(index); - - LcdFlags textColor = COLOR_THEME_PRIMARY2; - dc->drawTextAtIndex(10, 1, STR_VCSWFUNC, cs->func, textColor); - // CSW params - unsigned int cstate = lswFamily(cs->func); - - if (cstate == LS_FAMILY_BOOL || cstate == LS_FAMILY_STICKY) { - drawSwitch(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); - drawSwitch(dc, CSW_3RD_COLUMN, 1, cs->v2, textColor); - } else if (cstate == LS_FAMILY_EDGE) { - drawSwitch(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); - putsEdgeDelayParam(dc, CSW_3RD_COLUMN, 1, cs, textColor); - } else if (cstate == LS_FAMILY_COMP) { - drawSource(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); - drawSource(dc, CSW_3RD_COLUMN, 1, cs->v2, textColor); - } else if (cstate == LS_FAMILY_TIMER) { - dc->drawNumber(CSW_2ND_COLUMN, 1, lswTimerValue(cs->v1), - LEFT | PREC1 | textColor); - dc->drawNumber(CSW_3RD_COLUMN, 1, lswTimerValue(cs->v2), - LEFT | PREC1 | textColor); - } else { - drawSource(dc, CSW_2ND_COLUMN, 1, cs->v1, textColor); - drawSourceCustomValue( - dc, CSW_3RD_COLUMN, 1, cs->v1, - cs->v1 <= MIXSRC_LAST_CH ? calc100toRESX(cs->v2) : cs->v2, - LEFT | textColor); - } - - // CSW AND switch - drawSwitch(dc, CSW_4TH_COLUMN, 1, cs->andsw, textColor); - - // CSW duration - if (cs->duration > 0) - dc->drawNumber(CSW_5TH_COLUMN, 1, cs->duration, - PREC1 | LEFT | textColor); - else - dc->drawTextAtIndex(CSW_5TH_COLUMN, 1, STR_MMMINV, 0, textColor); - - // CSW delay - if (cstate == LS_FAMILY_EDGE) { - dc->drawText(CSW_6TH_COLUMN, 1, STR_NA, textColor); - } else if (cs->delay > 0) { - dc->drawNumber(CSW_6TH_COLUMN, 1, cs->delay, PREC1 | LEFT | textColor); - } else { - dc->drawTextAtIndex(CSW_6TH_COLUMN, 1, STR_MMMINV, 0, textColor); - } } protected: - rect_t rect; - unsigned index = 0; -}; - -class LogicalSwitchesViewPage : public PageTab -{ - public: - LogicalSwitchesViewPage() : - PageTab(STR_MONITOR_SWITCHES, ICON_MONITOR_LOGICAL_SWITCHES) - { - } - - protected: - void build(FormWindow * window) override; - LogicalSwitchDisplayFooter * footer = nullptr; + void build(FormWindow* window) override; + LogicalSwitchDisplayFooter* footer = nullptr; };