From 490cf003a30a8470e5cb45851d38b9eee5f87b5d Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Sun, 13 Jun 2021 18:14:56 +0200 Subject: [PATCH] Facelift round 4 --- radio/src/gui/colorlcd/model_curves.cpp | 17 +- radio/src/gui/colorlcd/model_curves.h | 2 +- radio/src/gui/colorlcd/model_gvars.cpp | 15 +- radio/src/gui/colorlcd/model_inputs.cpp | 167 ++-- .../gui/colorlcd/model_logical_switches.cpp | 225 ++--- .../src/gui/colorlcd/model_logical_switches.h | 2 +- radio/src/gui/colorlcd/model_mixes.cpp | 452 ++++----- radio/src/gui/colorlcd/model_outputs.cpp | 30 +- radio/src/gui/colorlcd/model_outputs.h | 2 +- radio/src/gui/colorlcd/special_functions.cpp | 875 ++++++++++-------- radio/src/gui/colorlcd/special_functions.h | 4 - 11 files changed, 992 insertions(+), 799 deletions(-) diff --git a/radio/src/gui/colorlcd/model_curves.cpp b/radio/src/gui/colorlcd/model_curves.cpp index 29289cbe2..c8a39ef02 100644 --- a/radio/src/gui/colorlcd/model_curves.cpp +++ b/radio/src/gui/colorlcd/model_curves.cpp @@ -309,10 +309,9 @@ void ModelCurvesPage::build(FormWindow * window, int8_t focusIndex) if (isCurveUsed(index)) { // Curve label - auto text = + auto txt = new StaticText(window, grid.getLabelSlot(), getCurveString(1 + index), BUTTON_BACKGROUND, CENTERED); - text->setBackgroundColor(FOCUS_COLOR); // Curve drawing Button * button = new CurveButton(window, grid.getFieldSlot(), index); @@ -334,11 +333,25 @@ void ModelCurvesPage::build(FormWindow * window, int8_t focusIndex) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); if (focusIndex == index) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } else { auto button = new TextButton(window, grid.getLabelSlot(), diff --git a/radio/src/gui/colorlcd/model_curves.h b/radio/src/gui/colorlcd/model_curves.h index a747b50e6..52fe9b1f4 100644 --- a/radio/src/gui/colorlcd/model_curves.h +++ b/radio/src/gui/colorlcd/model_curves.h @@ -30,7 +30,7 @@ class ModelCurvesPage: public PageTab { virtual void build(FormWindow * window) override { - build(window, -1); + build(window, 0); } protected: diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index ba123f7c7..2a6aaaf74 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -64,9 +64,18 @@ void GVarButton::paint(BitmapBuffer * dc) currentFlightMode = getFlightMode(); gvarSum = 0; - dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); - dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getGVarString(gvarIdx), 0); - dc->drawSizedText(x, y, gvar->name, LEN_GVAR_NAME, 0); + LcdFlags bgColor = FIELD_BGCOLOR; + LcdFlags textColor = 0; + if (hasFocus()) { + bgColor = FOCUS_BGCOLOR; + textColor = FOCUS_COLOR; + } + + dc->drawSolidFilledRect(0, 0, nameRectW, rect.h, bgColor); + dc->drawSolidFilledRect(nameRectW, 0, rect.w - nameRectW, rect.h, FIELD_BGCOLOR); + + dc->drawText(FIELD_PADDING_LEFT, FIELD_PADDING_TOP, getGVarString(gvarIdx), textColor); + dc->drawSizedText(x, y, gvar->name, LEN_GVAR_NAME, textColor); // values are right aligned x += GVAR_NAME_SIZE; diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp index 8d43f32fd..da4f58ba1 100644 --- a/radio/src/gui/colorlcd/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -327,66 +327,67 @@ void CommonInputOrMixButton::drawFlightModes(BitmapBuffer *dc, void CommonInputOrMixButton::paint(BitmapBuffer * dc) { dc->drawSolidFilledRect(0, 0, width(), height(), - hasFocus() ? FOCUS_BGCOLOR : FIELD_BGCOLOR); + /*hasFocus() ? FOCUS_BGCOLOR :*/ FIELD_BGCOLOR); paintBody(dc); + if (!hasFocus()) - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FIELD_FRAME_COLOR); + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); } -class InputLineButton : public CommonInputOrMixButton { - public: - InputLineButton(FormGroup * parent, const rect_t & rect, uint8_t index): +class InputLineButton : public CommonInputOrMixButton +{ + public: + InputLineButton(FormGroup *parent, const rect_t &rect, uint8_t index) : CommonInputOrMixButton(parent, rect, index) - { - const ExpoData & line = g_model.expoData[index]; - if (line.swtch || line.curve.value != 0 || line.flightModes) { - setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); - } + { + const ExpoData &line = g_model.expoData[index]; + if (line.swtch || line.curve.value != 0 || line.flightModes) { + setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); + } + } + + bool isActive() const override { return isExpoActive(index); } + + void paintBody(BitmapBuffer *dc) override + { + const ExpoData &line = g_model.expoData[index]; + + LcdFlags textColor = DEFAULT_COLOR; + // if (hasFocus()) + // textColor = FOCUS_COLOR; + + // first line ... + drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, + -100, 100, textColor); + drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); + + if (line.name[0]) { + dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); + dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), + textColor); } - bool isActive() const override - { - return isExpoActive(index); + // second line ... + if (line.swtch) { + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupSwitchIcon, textColor); + drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + textColor); } - void paintBody(BitmapBuffer * dc) override - { - const ExpoData & line = g_model.expoData[index]; - - LcdFlags textColor = DEFAULT_COLOR; - if (hasFocus()) - textColor = FOCUS_COLOR; - - // first line ... - drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, - -100, 100, textColor); - drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); - - if (line.name[0]) { - dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); - dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), - textColor); - } - - // second line ... - if (line.swtch) { - dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, - mixerSetupSwitchIcon, textColor); - drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + if (line.curve.value != 0) { + dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupCurveIcon, textColor); + drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, textColor); - } - - if (line.curve.value != 0 ) { - dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, - mixerSetupCurveIcon, textColor); - drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, - textColor); - } - - if (line.flightModes) { - drawFlightModes(dc, line.flightModes, textColor); - } } + + if (line.flightModes) { + drawFlightModes(dc, line.flightModes, textColor); + } + } }; ModelInputsPage::ModelInputsPage(): @@ -411,31 +412,29 @@ void ModelInputsPage::editInput(FormWindow * window, uint8_t input, uint8_t inde }); } -void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) +void ModelInputsPage::build(FormWindow *window, int8_t focusIndex) { FormGridLayout grid; grid.spacer(PAGE_PADDING); grid.setLabelWidth(66); int inputIndex = 0; - ExpoData * line = g_model.expoData; + ExpoData *line = g_model.expoData; for (uint8_t input = 0; input < MAX_INPUTS; input++) { if (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) { + coord_t h = grid.getWindowHeight(); auto txt = new StaticText(window, grid.getLabelSlot(), getSourceString(MIXSRC_FIRST_INPUT + input), - 0, CENTERED); - txt->setBackgroundColor(FIELD_BGCOLOR); + BUTTON_BACKGROUND, CENTERED); while (inputIndex < MAX_EXPOS && line->chn == input && EXPO_VALID(line)) { - Button * button = new InputLineButton(window, grid.getFieldSlot(), inputIndex); - if (focusIndex == inputIndex) - button->setFocus(SET_FOCUS_DEFAULT); + Button *button = + new InputLineButton(window, grid.getFieldSlot(), inputIndex); button->setPressHandler([=]() -> uint8_t { button->bringToTop(); - Menu * menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { - editInput(window, input, inputIndex); - }); + Menu *menu = new Menu(window); + menu->addLine(STR_EDIT, + [=]() { editInput(window, input, inputIndex); }); if (!reachExposLimit()) { menu->addLine(STR_INSERT_BEFORE, [=]() { insertExpo(inputIndex, input); @@ -453,7 +452,8 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) menu->addLine(STR_PASTE_BEFORE, [=]() { copyExpo(s_copySrcIdx, inputIndex, PASTE_BEFORE); if (s_copyMode == MOVE_MODE) { - deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx + 1 + : s_copySrcIdx); s_copyMode = 0; } rebuild(window, inputIndex); @@ -461,10 +461,11 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) menu->addLine(STR_PASTE_AFTER, [=]() { copyExpo(s_copySrcIdx, inputIndex, PASTE_AFTER); if (s_copyMode == MOVE_MODE) { - deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + deleteExpo((s_copySrcIdx > inputIndex) ? s_copySrcIdx + 1 + : s_copySrcIdx); s_copyMode = 0; } - rebuild(window, inputIndex+1); + rebuild(window, inputIndex + 1); }); } } @@ -478,22 +479,39 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); - grid.spacer(button->height() - 2); - + if (focusIndex == inputIndex) { + button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); + } + grid.spacer(button->height() - 1); ++inputIndex; ++line; } + h = grid.getWindowHeight() - h + 1; + txt->setHeight(h); + grid.spacer(7); - } - else { - auto button = new TextButton(window, grid.getLabelSlot(), getSourceString(MIXSRC_FIRST_INPUT + input)); - if (focusIndex == inputIndex) - button->setFocus(SET_FOCUS_DEFAULT); + } else { + auto button = new TextButton(window, grid.getLabelSlot(), + getSourceString(MIXSRC_FIRST_INPUT + input)); + if (focusIndex == inputIndex) button->setFocus(SET_FOCUS_DEFAULT); button->setPressHandler([=]() -> uint8_t { button->bringToTop(); - Menu * menu = new Menu(window); + Menu *menu = new Menu(window); menu->addLine(STR_EDIT, [=]() { insertExpo(inputIndex, input); editInput(window, input, inputIndex); @@ -503,8 +521,9 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) if (s_copyMode != 0) { menu->addLine(STR_PASTE, [=]() { copyExpo(s_copySrcIdx, inputIndex, input); - if(s_copyMode == MOVE_MODE) { - deleteExpo((s_copySrcIdx >= inputIndex) ? s_copySrcIdx+1 : s_copySrcIdx); + if (s_copyMode == MOVE_MODE) { + deleteExpo((s_copySrcIdx >= inputIndex) ? s_copySrcIdx + 1 + : s_copySrcIdx); s_copyMode = 0; } rebuild(window, -1); @@ -519,7 +538,7 @@ void ModelInputsPage::build(FormWindow * window, int8_t focusIndex) } } - Window * focus = Window::getFocus(); + Window *focus = Window::getFocus(); if (focus) { focus->bringToTop(); } diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp index bae954375..ceae29080 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -241,95 +241,97 @@ 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; -class LogicalSwitchButton : public Button { - public: - LogicalSwitchButton(FormGroup * parent, const rect_t & rect, int lsIndex): - Button(parent, rect), - lsIndex(lsIndex), - active(isActive()) - { - LogicalSwitchData * ls = lswAddress(lsIndex); - if (ls->andsw != SWSRC_NONE || ls->duration != 0 || ls->delay != 0) - setHeight(height() + 20); - if (lswFamily(ls->func) == LS_FAMILY_EDGE) - setHeight(height() + 20); +class LogicalSwitchButton : public Button +{ + public: + LogicalSwitchButton(FormGroup* parent, const rect_t& rect, int lsIndex) : + Button(parent, rect), lsIndex(lsIndex), active(isActive()) + { + LogicalSwitchData* ls = lswAddress(lsIndex); + if (ls->andsw != SWSRC_NONE || ls->duration != 0 || ls->delay != 0) + setHeight(height() + 20); + if (lswFamily(ls->func) == LS_FAMILY_EDGE) setHeight(height() + 20); + } + + bool isActive() const + { + return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + lsIndex); + } + + void checkEvents() override + { + if (active != isActive()) { + invalidate(); + active = !active; } - bool isActive() const - { - return getSwitch(SWSRC_FIRST_LOGICAL_SWITCH + lsIndex); + Button::checkEvents(); + } + + void paintLogicalSwitchLine(BitmapBuffer* dc) + { + LogicalSwitchData* ls = lswAddress(lsIndex); + uint8_t lsFamily = lswFamily(ls->func); + + // CSW func + dc->drawTextAtIndex(col1, line1, STR_VCSWFUNC, ls->func); + + // CSW params + if (lsFamily == LS_FAMILY_BOOL || lsFamily == LS_FAMILY_STICKY) { + drawSwitch(dc, col2, line1, ls->v1); + drawSwitch(dc, col3, line1, ls->v2); + } else if (lsFamily == LS_FAMILY_EDGE) { + drawSwitch(dc, col1, line2, ls->v1); + putsEdgeDelayParam(dc, col2, line2, ls); + } else if (lsFamily == LS_FAMILY_COMP) { + drawSource(dc, col2, line1, ls->v1, 0); + drawSource(dc, col3, line1, ls->v2, 0); + } else if (lsFamily == LS_FAMILY_TIMER) { + dc->drawNumber(col2, line1, lswTimerValue(ls->v1), LEFT | PREC1); + dc->drawNumber(col3, line1, lswTimerValue(ls->v2), LEFT | PREC1); + } else { + drawSource(dc, col2, line1, ls->v1, 0); + drawSourceCustomValue( + dc, col3, line1, ls->v1, + (ls->v1 <= MIXSRC_LAST_CH ? calc100toRESX(ls->v2) : ls->v2), 0); } - void checkEvents() override - { - if (active != isActive()) { - invalidate(); - active = !active; - } + // AND switch + drawSwitch(dc, col1, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, + ls->andsw, 0); - Button::checkEvents(); + // CSW duration + if (ls->duration > 0) { + dc->drawNumber(col2, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, + ls->duration, PREC1 | LEFT); } - void paintLogicalSwitchLine(BitmapBuffer * dc) - { - LogicalSwitchData * ls = lswAddress(lsIndex); - uint8_t lsFamily = lswFamily(ls->func); - - // CSW func - dc->drawTextAtIndex(col1, line1, STR_VCSWFUNC, ls->func); - - // CSW params - if (lsFamily == LS_FAMILY_BOOL || lsFamily == LS_FAMILY_STICKY) { - drawSwitch(dc, col2, line1, ls->v1); - drawSwitch(dc, col3, line1, ls->v2); - } - else if (lsFamily == LS_FAMILY_EDGE) { - drawSwitch(dc, col1, line2, ls->v1); - putsEdgeDelayParam(dc, col2, line2, ls); - } - else if (lsFamily == LS_FAMILY_COMP) { - drawSource(dc, col2, line1, ls->v1, 0); - drawSource(dc, col3, line1, ls->v2, 0); - } - else if (lsFamily == LS_FAMILY_TIMER) { - dc->drawNumber(col2, line1, lswTimerValue(ls->v1), LEFT|PREC1); - dc->drawNumber(col3, line1, lswTimerValue(ls->v2), LEFT|PREC1); - } - else { - drawSource(dc, col2, line1, ls->v1, 0); - drawSourceCustomValue(dc, col3, line1, ls->v1, (ls->v1 <= MIXSRC_LAST_CH ? calc100toRESX(ls->v2) : ls->v2), 0); - } - - // AND switch - drawSwitch(dc, col1, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->andsw, 0); - - // CSW duration - if (ls->duration > 0) { - dc->drawNumber(col2, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->duration, PREC1 | LEFT); - } - - // CSW delay - if (lsFamily != LS_FAMILY_EDGE && ls->delay > 0) { - dc->drawNumber(col3, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, ls->delay, PREC1 | LEFT); - } + // CSW delay + if (lsFamily != LS_FAMILY_EDGE && ls->delay > 0) { + dc->drawNumber(col3, (lsFamily == LS_FAMILY_EDGE) ? line3 : line2, + ls->delay, PREC1 | LEFT); } + } - void paint(BitmapBuffer * dc) override - { - if (active) - dc->drawSolidFilledRect(2, 2, rect.w-4, rect.h-4, HIGHLIGHT_COLOR); - else - dc->drawSolidFilledRect(2, 2, rect.w-4, rect.h-4, FIELD_BGCOLOR); + void paint(BitmapBuffer* dc) override + { + if (active) + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, HIGHLIGHT_COLOR); + else + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); - paintLogicalSwitchLine(dc); + paintLogicalSwitchLine(dc); - // The bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); - } + // The bounding rect + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + } - protected: - uint8_t lsIndex; - bool active; + protected: + uint8_t lsIndex; + bool active; }; ModelLogicalSwitchesPage::ModelLogicalSwitchesPage(): @@ -353,17 +355,18 @@ void ModelLogicalSwitchesPage::editLogicalSwitch(FormWindow * window, uint8_t ls }); } -void ModelLogicalSwitchesPage::build(FormWindow * window, int8_t focusIndex) +void ModelLogicalSwitchesPage::build(FormWindow* window, int8_t focusIndex) { FormGridLayout grid; grid.spacer(PAGE_PADDING); grid.setLabelWidth(66); for (uint8_t i = 0; i < MAX_LOGICAL_SWITCHES; i++) { - LogicalSwitchData * ls = lswAddress(i); + LogicalSwitchData* ls = lswAddress(i); if (ls->func == LS_FUNC_NONE) { - auto button = new TextButton(window, grid.getLabelSlot(), getSwitchPositionName(SWSRC_SW1+i)); + 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); @@ -379,40 +382,54 @@ void ModelLogicalSwitchesPage::build(FormWindow * window, int8_t focusIndex) return 0; }); grid.spacer(button->height() + 5); - } - else { - new StaticText(window, grid.getLabelSlot(), getSwitchPositionName(SWSRC_SW1 + i), BUTTON_BACKGROUND, CENTERED); + } else { + auto txt = new StaticText(window, grid.getLabelSlot(), + getSwitchPositionName(SWSRC_SW1 + i), + BUTTON_BACKGROUND, CENTERED); auto button = new LogicalSwitchButton(window, grid.getFieldSlot(), i); button->setPressHandler([=]() { - Menu * menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { - editLogicalSwitch(window, i); + Menu* menu = new Menu(window); + menu->addLine(STR_EDIT, [=]() { editLogicalSwitch(window, i); }); + if (ls->func) + menu->addLine(STR_COPY, [=]() { + clipboard.type = CLIPBOARD_TYPE_CUSTOM_SWITCH; + clipboard.data.csw = *ls; }); - if (ls->func) - 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); - }); - if (ls->func || 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); - }); - return 0; + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_SWITCH) + menu->addLine(STR_PASTE, [=]() { + *ls = clipboard.data.csw; + storageDirty(EE_MODEL); + rebuild(window, i); + }); + if (ls->func || 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); + }); + return 0; + }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); }); if (focusIndex == i) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } } diff --git a/radio/src/gui/colorlcd/model_logical_switches.h b/radio/src/gui/colorlcd/model_logical_switches.h index fc3a505bf..d211ead04 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.h +++ b/radio/src/gui/colorlcd/model_logical_switches.h @@ -29,7 +29,7 @@ public: virtual void build(FormWindow * window) override { - build(window, -1); + build(window, 0); } protected: diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index fbe28766f..e103d1926 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -53,245 +53,253 @@ bool reachMixesLimit() return false; } -class MixEditWindow : public Page { - public: - MixEditWindow(int8_t channel, uint8_t mixIndex) : - Page(ICON_MODEL_MIXER), - channel(channel), - mixIndex(mixIndex) - { - buildBody(&body); - buildHeader(&header); +class MixEditWindow : public Page +{ + public: + MixEditWindow(int8_t channel, uint8_t mixIndex) : + Page(ICON_MODEL_MIXER), channel(channel), mixIndex(mixIndex) + { + buildBody(&body); + buildHeader(&header); + } + + protected: + uint8_t channel; + uint8_t mixIndex; + FormGroup *curveParamField = nullptr; + + void buildHeader(Window *window) + { + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, + PAGE_LINE_HEIGHT}, + STR_MIXES, 0, MENU_COLOR); + new StaticText(window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + getSourceString(MIXSRC_CH1 + channel), 0, MENU_COLOR); + } + + void buildBody(FormWindow *window) + { + FormGridLayout grid; + grid.spacer(8); + + MixData *mix = mixAddress(mixIndex); + + // Mix name + new StaticText(window, grid.getLabelSlot(), STR_MIXNAME); + new RadioTextEdit(window, grid.getFieldSlot(), mix->name, + sizeof(mix->name)); + grid.nextLine(); + + // Source + new StaticText(window, grid.getLabelSlot(), STR_SOURCE); + new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST, + GET_SET_DEFAULT(mix->srcRaw)); + grid.nextLine(); + + // Weight + new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); + auto gvar = + new GVarNumberEdit(window, grid.getFieldSlot(), MIX_WEIGHT_MIN, + MIX_WEIGHT_MAX, GET_SET_DEFAULT(mix->weight)); + gvar->setSuffix("%"); + grid.nextLine(); + + // Offset + new StaticText(window, grid.getLabelSlot(), STR_OFFSET); + gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_OFFSET_MIN, + MIX_OFFSET_MAX, GET_SET_DEFAULT(mix->offset)); + gvar->setSuffix("%"); + grid.nextLine(); + + // Trim + new StaticText(window, grid.getLabelSlot(), STR_TRIM); + new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(mix->carryTrim)); + grid.nextLine(); + + // Curve + new StaticText(&body, grid.getLabelSlot(), STR_CURVE); + new Choice(&body, grid.getFieldSlot(2, 0), "\004DiffExpoFuncCstm", 0, + CURVE_REF_CUSTOM, GET_DEFAULT(mix->curve.type), + [=](int32_t newValue) { + mix->curve.type = newValue; + mix->curve.value = 0; + SET_DIRTY(); + updateCurveParamField(mix); + }); + curveParamField = + new FormGroup(&body, grid.getFieldSlot(2, 1), FORM_FORWARD_FOCUS); + updateCurveParamField(mix); + grid.nextLine(); + + // Flight modes + new StaticText(window, grid.getLabelSlot(), STR_FLMODE); + for (uint8_t i = 0; i < MAX_FLIGHT_MODES; i++) { + char fm[2] = {char('0' + i), '\0'}; + if (i > 0 && (i % 4) == 0) grid.nextLine(); + new TextButton( + window, grid.getFieldSlot(4, i % 4), fm, + [=]() -> uint8_t { + BFBIT_FLIP(mix->flightModes, bfBit(i)); + SET_DIRTY(); + return !(bfSingleBitGet(mix->flightModes, i)); + }, + OPAQUE | (bfSingleBitGet(mix->flightModes, i) ? 0 : BUTTON_CHECKED)); } + grid.nextLine(); - protected: - uint8_t channel; - uint8_t mixIndex; - FormGroup * curveParamField = nullptr; + // Switch + new StaticText(window, grid.getLabelSlot(), STR_SWITCH); + new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, + SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(mix->swtch)); + grid.nextLine(); - void buildHeader(Window * window) - { - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, STR_MIXES, 0, MENU_COLOR); - new StaticText(window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, getSourceString(MIXSRC_CH1 + channel), 0, MENU_COLOR); - } + // Warning + new StaticText(window, grid.getLabelSlot(), STR_MIXWARNING); + auto edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, 3, + GET_SET_DEFAULT(mix->mixWarn)); + edit->setZeroText(STR_OFF); + grid.nextLine(); - void buildBody(FormWindow * window) - { - FormGridLayout grid; - grid.spacer(8); + // Multiplex + new StaticText(window, grid.getLabelSlot(), STR_MULTPX); + new Choice(window, grid.getFieldSlot(), STR_VMLTPX, 0, 2, + GET_SET_DEFAULT(mix->mltpx)); + grid.nextLine(); - MixData * mix = mixAddress(mixIndex); + // Delay up + new StaticText(window, grid.getLabelSlot(), STR_DELAYUP); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->delayUp), + SET_VALUE(mix->delayUp, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Mix name - new StaticText(window, grid.getLabelSlot(), STR_MIXNAME); - new RadioTextEdit(window, grid.getFieldSlot(), mix->name, sizeof(mix->name)); - grid.nextLine(); + // Delay down + new StaticText(window, grid.getLabelSlot(), STR_DELAYDOWN); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->delayDown), + SET_VALUE(mix->delayDown, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Source - new StaticText(window, grid.getLabelSlot(), STR_SOURCE); - new SourceChoice(window, grid.getFieldSlot(), 0, MIXSRC_LAST, GET_SET_DEFAULT(mix->srcRaw)); - grid.nextLine(); + // Slow up + new StaticText(window, grid.getLabelSlot(), STR_SLOWUP); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->speedUp), + SET_VALUE(mix->speedUp, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Weight - new StaticText(window, grid.getLabelSlot(), STR_WEIGHT); - auto gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, GET_SET_DEFAULT(mix->weight)); - gvar->setSuffix("%"); - grid.nextLine(); + // Slow down + new StaticText(window, grid.getLabelSlot(), STR_SLOWDOWN); + edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, + GET_DEFAULT(mix->speedDown), + SET_VALUE(mix->speedDown, newValue), 0, PREC1); + edit->setSuffix("s"); + grid.nextLine(); - // Offset - new StaticText(window, grid.getLabelSlot(), STR_OFFSET); - gvar = new GVarNumberEdit(window, grid.getFieldSlot(), MIX_OFFSET_MIN, MIX_OFFSET_MAX, GET_SET_DEFAULT(mix->offset)); - gvar->setSuffix("%"); - grid.nextLine(); + window->setInnerHeight(grid.getWindowHeight()); + } - // Trim - new StaticText(window, grid.getLabelSlot(), STR_TRIM); - new CheckBox(window, grid.getFieldSlot(), GET_SET_INVERTED(mix->carryTrim)); - grid.nextLine(); + // TODO share this code with INPUT + void updateCurveParamField(MixData *line) + { + curveParamField->clear(); - // Curve - new StaticText(&body, grid.getLabelSlot(), STR_CURVE); - new Choice(&body, grid.getFieldSlot(2, 0), "\004DiffExpoFuncCstm", 0, CURVE_REF_CUSTOM, - GET_DEFAULT(mix->curve.type), - [=](int32_t newValue) { - mix->curve.type = newValue; - mix->curve.value = 0; - SET_DIRTY(); - updateCurveParamField(mix); - }); - curveParamField = new FormGroup(&body, grid.getFieldSlot(2, 1), FORM_FORWARD_FOCUS); - updateCurveParamField(mix); - grid.nextLine(); + const rect_t rect = {0, 0, curveParamField->width(), + curveParamField->height()}; - // Flight modes - new StaticText(window, grid.getLabelSlot(), STR_FLMODE); - for (uint8_t i = 0; i < MAX_FLIGHT_MODES; i++) { - char fm[2] = {char('0' + i), '\0'}; - if (i > 0 && (i % 4) == 0) - grid.nextLine(); - new TextButton(window, grid.getFieldSlot(4, i % 4), fm, - [=]() -> uint8_t { - BFBIT_FLIP(mix->flightModes, bfBit(i)); - SET_DIRTY(); - return !(bfSingleBitGet(mix->flightModes, i)); - }, - OPAQUE | (bfSingleBitGet(mix->flightModes, i) ? 0 : BUTTON_CHECKED)); + switch (line->curve.type) { + case CURVE_REF_DIFF: + case CURVE_REF_EXPO: { + GVarNumberEdit *edit = + new GVarNumberEdit(curveParamField, rect, -100, 100, + GET_SET_DEFAULT(line->curve.value)); + edit->setSuffix("%"); + break; } - grid.nextLine(); - // Switch - new StaticText(window, grid.getLabelSlot(), STR_SWITCH); - new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(mix->swtch)); - grid.nextLine(); + case CURVE_REF_FUNC: + new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1, + GET_SET_DEFAULT(line->curve.value)); + break; - // Warning - new StaticText(window, grid.getLabelSlot(), STR_MIXWARNING); - auto edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, 3, GET_SET_DEFAULT(mix->mixWarn)); - edit->setZeroText(STR_OFF); - grid.nextLine(); - - // Multiplex - new StaticText(window, grid.getLabelSlot(), STR_MULTPX); - new Choice(window, grid.getFieldSlot(), STR_VMLTPX, 0, 2, GET_SET_DEFAULT(mix->mltpx)); - grid.nextLine(); - - // Delay up - new StaticText(window, grid.getLabelSlot(), STR_DELAYUP); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->delayUp), - SET_VALUE(mix->delayUp, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - // Delay down - new StaticText(window, grid.getLabelSlot(), STR_DELAYDOWN); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->delayDown), - SET_VALUE(mix->delayDown, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - // Slow up - new StaticText(window, grid.getLabelSlot(), STR_SLOWUP); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->speedUp), - SET_VALUE(mix->speedUp, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - // Slow down - new StaticText(window, grid.getLabelSlot(), STR_SLOWDOWN); - edit = new NumberEdit(window, grid.getFieldSlot(2, 0), 0, DELAY_MAX, - GET_DEFAULT(mix->speedDown), - SET_VALUE(mix->speedDown, newValue), - 0, PREC1); - edit->setSuffix("s"); - grid.nextLine(); - - window->setInnerHeight(grid.getWindowHeight()); - } - - // TODO share this code with INPUT - void updateCurveParamField(MixData * line) - { - curveParamField->clear(); - - const rect_t rect = {0, 0, curveParamField->width(), curveParamField->height()}; - - switch (line->curve.type) { - case CURVE_REF_DIFF: - case CURVE_REF_EXPO: - { - GVarNumberEdit * edit = new GVarNumberEdit(curveParamField, rect, -100, 100, GET_SET_DEFAULT(line->curve.value)); - edit->setSuffix("%"); - break; - } - - case CURVE_REF_FUNC: - new Choice(curveParamField, rect, STR_VCURVEFUNC, 0, CURVE_BASE - 1, GET_SET_DEFAULT(line->curve.value)); - break; - - case CURVE_REF_CUSTOM: - { - auto choice = new Choice(curveParamField, rect, -MAX_CURVES, MAX_CURVES, GET_SET_DEFAULT(line->curve.value)); - choice->setTextHandler([](int value) { - return getCurveString(value); - }); - break; - } + case CURVE_REF_CUSTOM: { + auto choice = new Choice(curveParamField, rect, -MAX_CURVES, MAX_CURVES, + GET_SET_DEFAULT(line->curve.value)); + choice->setTextHandler([](int value) { return getCurveString(value); }); + break; } } + } }; -class MixLineButton : public CommonInputOrMixButton { - public: - MixLineButton(FormGroup * parent, const rect_t & rect, uint8_t index) : +class MixLineButton : public CommonInputOrMixButton +{ + public: + MixLineButton(FormGroup *parent, const rect_t &rect, uint8_t index) : CommonInputOrMixButton(parent, rect, index) - { - const MixData & mix = g_model.mixData[index]; - if (mix.swtch || mix.curve.value != 0 || mix.flightModes) { - setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); - } + { + const MixData &mix = g_model.mixData[index]; + if (mix.swtch || mix.curve.value != 0 || mix.flightModes) { + setHeight(height() + PAGE_LINE_HEIGHT + FIELD_PADDING_TOP); + } + } + + bool isActive() const override { return isMixActive(index); } + + void paintBody(BitmapBuffer *dc) override + { + const MixData &line = g_model.mixData[index]; + + LcdFlags textColor = DEFAULT_COLOR; + + // first line ... + drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, + MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, textColor); + drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); + + if (line.name[0]) { + dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); + dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), + textColor); } - bool isActive() const override - { - return isMixActive(index); + // second line ... + if (line.swtch) { + dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupSwitchIcon, textColor); + drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + textColor); } - void paintBody(BitmapBuffer * dc) override - { - const MixData & line = g_model.mixData[index]; - - LcdFlags textColor = DEFAULT_COLOR; - if (hasFocus()) - textColor = FOCUS_COLOR; - - // first line ... - drawValueOrGVar(dc, FIELD_PADDING_LEFT, FIELD_PADDING_TOP, line.weight, - MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, textColor); - drawSource(dc, 60, FIELD_PADDING_TOP, line.srcRaw, textColor); - - if (line.name[0]) { - dc->drawMask(146, FIELD_PADDING_TOP, mixerSetupLabelIcon, textColor); - dc->drawSizedText(166, FIELD_PADDING_TOP, line.name, sizeof(line.name), - textColor); - } - - // second line ... - if (line.swtch) { - dc->drawMask(3, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, - mixerSetupSwitchIcon, textColor); - drawSwitch(dc, 21, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.swtch, + if (line.curve.value) { + dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, + mixerSetupCurveIcon, textColor); + drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, textColor); - } - - if (line.curve.value) { - dc->drawMask(60, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, - mixerSetupCurveIcon, textColor); - drawCurveRef(dc, 80, PAGE_LINE_HEIGHT + FIELD_PADDING_TOP, line.curve, - textColor); - } - - if (line.flightModes) { - drawFlightModes(dc, line.flightModes, textColor); - } } + + if (line.flightModes) { + drawFlightModes(dc, line.flightModes, textColor); + } + } }; void insertMix(uint8_t idx, uint8_t channel) { pauseMixerCalculations(); - MixData * mix = mixAddress(idx); + MixData *mix = mixAddress(idx); memmove(mix + 1, mix, (MAX_MIXERS - (idx + 1)) * sizeof(MixData)); memclear(mix, sizeof(MixData)); mix->destCh = channel; mix->srcRaw = channel + 1; if (!isSourceAvailable(mix->srcRaw)) { - mix->srcRaw = (channel > 3 ? MIXSRC_Rud - 1 + channel : MIXSRC_Rud - 1 + channelOrder(channel)); + mix->srcRaw = (channel > 3 ? MIXSRC_Rud - 1 + channel + : MIXSRC_Rud - 1 + channelOrder(channel)); while (!isSourceAvailable(mix->srcRaw)) { mix->srcRaw += 1; } @@ -339,15 +347,15 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) MixData * mix = g_model.mixData; for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) { if (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { + + coord_t h = grid.getWindowHeight(); auto txt = new StaticText(window, grid.getLabelSlot(), - getSourceString(MIXSRC_CH1 + ch), 0, - CENTERED); - txt->setBackgroundColor(FIELD_BGCOLOR); + getSourceString(MIXSRC_CH1 + ch), + BUTTON_BACKGROUND, CENTERED); + uint8_t count = 0; while (mixIndex < MAX_MIXERS && mix->srcRaw > 0 && mix->destCh == ch) { Button * button = new MixLineButton(window, grid.getFieldSlot(), mixIndex); - if (focusMixIndex == mixIndex) - button->setFocus(SET_FOCUS_DEFAULT); button->setPressHandler([=]() -> uint8_t { button->bringToTop(); Menu * menu = new Menu(window); @@ -396,16 +404,38 @@ void ModelMixesPage::build(FormWindow * window, int8_t focusMixIndex) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); - if (count++ > 0) { - new StaticBitmap(window, {35, button->top() + (button->height() - 18) / 2, 25, 17}, mixerMultiplexBitmap[mix->mltpx]); + if (focusMixIndex == mixIndex) { + button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } - grid.spacer(button->height() - 2); + if (count++ > 0) { + new StaticBitmap( + window, {35, button->top() + (button->height() - 18) / 2, 25, 17}, + mixerMultiplexBitmap[mix->mltpx]); + } + + grid.spacer(button->height() - 1); ++mixIndex; ++mix; } + h = grid.getWindowHeight() - h + 1; + txt->setHeight(h); + grid.spacer(7); } else { diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index 66576fd3f..4212dea38 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -129,11 +129,6 @@ class OutputLineButton : public Button { LcdFlags textColor = DEFAULT_COLOR; LcdFlags bgColor = FIELD_BGCOLOR; - if (hasFocus()) { - textColor = FOCUS_COLOR; - bgColor = FOCUS_BGCOLOR; - } - dc->drawSolidFilledRect(0, 0, width(), height(), bgColor); // first line @@ -165,8 +160,10 @@ class OutputLineButton : public Button { } // bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, - hasFocus() ? FOCUS_BGCOLOR : FIELD_FRAME_COLOR); + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); } protected: @@ -197,9 +194,8 @@ void ModelOutputsPage::build(FormWindow * window, int8_t focusChannel) // Channel label auto txt = new StaticText(window, grid.getLabelSlot(), - getSourceString(MIXSRC_CH1 + ch), 0, - CENTERED); - txt->setBackgroundColor(FIELD_BGCOLOR); + getSourceString(MIXSRC_CH1 + ch), + BUTTON_BACKGROUND, CENTERED); // Channel settings Button * button = new OutputLineButton(window, grid.getFieldSlot(), output); @@ -231,11 +227,25 @@ void ModelOutputsPage::build(FormWindow * window, int8_t focusChannel) }); return 0; }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); + }); if (focusChannel == ch) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } diff --git a/radio/src/gui/colorlcd/model_outputs.h b/radio/src/gui/colorlcd/model_outputs.h index 2a5c61734..25671a4d7 100644 --- a/radio/src/gui/colorlcd/model_outputs.h +++ b/radio/src/gui/colorlcd/model_outputs.h @@ -29,7 +29,7 @@ class ModelOutputsPage: public PageTab { virtual void build(FormWindow * window) override { - build(window, -1); + build(window, 0); } protected: diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index a7092095a..0651e9e83 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -24,235 +24,301 @@ #define SET_DIRTY() storageDirty(functions == g_model.customFn ? EE_MODEL : EE_GENERAL) -class SpecialFunctionEditPage : public Page { - public: - SpecialFunctionEditPage(CustomFunctionData * functions, uint8_t index) : - Page(functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS : ICON_RADIO_GLOBAL_FUNCTIONS), +class SpecialFunctionEditPage : public Page +{ + public: + SpecialFunctionEditPage(CustomFunctionData *functions, uint8_t index) : + Page(functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS + : ICON_RADIO_GLOBAL_FUNCTIONS), functions(functions), index(index) - { - buildHeader(&header); - buildBody(&body); + { + buildHeader(&header); + buildBody(&body); + } + + protected: + CustomFunctionData *functions; + uint8_t index; + FormGroup *specialFunctionOneWindow = nullptr; + StaticText *headerSF = nullptr; + bool active = false; + + bool isActive() const + { + return ((functions == g_model.customFn + ? modelFunctionsContext.activeSwitches + : globalFunctionsContext.activeSwitches) & + ((MASK_CFN_TYPE)1 << index) + ? 1 + : 0); + } + + void checkEvents() override + { + Page::checkEvents(); + if (active != isActive()) { + invalidate(); + headerSF->setTextFlags(isActive() ? FONT(BOLD) | HIGHLIGHT_COLOR + : MENU_COLOR); + active = !active; } + } - protected: - CustomFunctionData * functions; - uint8_t index; - FormGroup * specialFunctionOneWindow = nullptr; - StaticText * headerSF = nullptr; - bool active = false; + void buildHeader(Window *window) + { + new StaticText( + window, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, 20}, + functions == g_model.customFn ? STR_MENUCUSTOMFUNC + : STR_MENUSPECIALFUNCS, + 0, MENU_COLOR); + headerSF = new StaticText( + window, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, 20}, + (functions == g_model.customFn ? "SF" : "GF") + std::to_string(index), + 0, MENU_COLOR); + } - bool isActive() const - { - return ((functions == g_model.customFn ? modelFunctionsContext.activeSwitches : globalFunctionsContext.activeSwitches) & ((MASK_CFN_TYPE)1 << index) ? 1 : 0); - } + void updateSpecialFunctionOneWindow() + { + FormGridLayout grid; + specialFunctionOneWindow->clear(); - void checkEvents() override - { - Page::checkEvents(); - if (active != isActive()) { - invalidate(); - headerSF->setTextFlags(isActive() ? FONT(BOLD) |HIGHLIGHT_COLOR : MENU_COLOR); - active = !active; - } - } + CustomFunctionData *cfn = &functions[index]; + uint8_t func = CFN_FUNC(cfn); - void buildHeader(Window * window) - { - new StaticText(window, { PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT, 20 }, functions == g_model.customFn ? STR_MENUCUSTOMFUNC : STR_MENUSPECIALFUNCS, 0, MENU_COLOR); - headerSF = new StaticText(window, { PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, LCD_W - PAGE_TITLE_LEFT, 20 }, (functions == g_model.customFn ? "SF" : "GF" ) + std::to_string(index), 0, MENU_COLOR); - } - - void updateSpecialFunctionOneWindow() - { - FormGridLayout grid; - specialFunctionOneWindow->clear(); - - CustomFunctionData * cfn = &functions[index]; - uint8_t func = CFN_FUNC(cfn); - - // Func param - switch (func) { - case FUNC_OVERRIDE_CHANNEL: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_CH); - new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 1, MAX_OUTPUT_CHANNELS, GET_SET_VALUE_WITH_OFFSET(CFN_CH_INDEX(cfn), 1)); - grid.nextLine(); - - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), -100, 100, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_TRAINER: { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - auto choice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0,NUM_STICKS + 1,GET_SET_DEFAULT(CFN_PARAM(cfn))); - choice->setTextHandler([=](int32_t value) { - if (value == 0) - return std::string(STR_STICKS); - else if (value == NUM_STICKS + 1) - return std::string(STR_CHANS); - else - return TEXT_AT_INDEX(STR_VSRCRAW, value);; - }); - grid.nextLine(); - break; - } - - case FUNC_RESET: - if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_RESET); - auto choice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, - FUNC_RESET_PARAM_FIRST_TELEM + lastUsedTelemetryIndex(), - GET_SET_DEFAULT(CFN_PARAM(cfn))); - choice->setAvailableHandler(isSourceAvailableInResetSpecialFunction); - choice->setTextHandler([=](int32_t value) { - if (value < FUNC_RESET_PARAM_FIRST_TELEM) - return TEXT_AT_INDEX(STR_VFSWRESET, value); - else - return std::string(g_model.telemetrySensors[value - FUNC_RESET_PARAM_FIRST_TELEM].label, TELEM_LABEL_LEN); - }); - grid.nextLine(); - } - break; - - case FUNC_VOLUME: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VOLUME); - new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_BACKLIGHT: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_PLAY_SOUND: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new Choice(specialFunctionOneWindow, grid.getFieldSlot(), STR_FUNCSOUNDS, 0, - AU_SPECIAL_SOUND_LAST - AU_SPECIAL_SOUND_FIRST - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_PLAY_TRACK: - case FUNC_BACKGND_MUSIC: - case FUNC_PLAY_SCRIPT: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new FileChoice(specialFunctionOneWindow, grid.getFieldSlot(), - func == FUNC_PLAY_SCRIPT ? SCRIPTS_FUNCS_PATH : std::string(SOUNDS_PATH, SOUNDS_PATH_LNG_OFS) + - std::string(currentLanguagePack->id, 2), - func == FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, - sizeof(cfn->play.name), - [=]() { - return std::string(cfn->play.name, ZLEN(cfn->play.name)); - }, - [=](std::string newValue) { - strncpy(cfn->play.name, newValue.c_str(), sizeof(cfn->play.name)); - SET_DIRTY(); - }, true); // strip extension - grid.nextLine(); - break; - - case FUNC_SET_TIMER: { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_TIMER); - auto timerchoice = new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, TIMERS - 1, GET_SET_DEFAULT(CFN_TIMER_INDEX(cfn))); - timerchoice->setTextHandler([](int32_t value) { - return std::string(STR_TIMER) + std::to_string(value + 1); - }); - grid.nextLine(); - - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new TimeEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 9 * 60 * 60 - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - } - - case FUNC_SET_FAILSAFE: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_MODULE); - new Choice(specialFunctionOneWindow, grid.getFieldSlot(), "\004Int.Ext.", 0, NUM_MODULES - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_PLAY_VALUE: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, MIXSRC_LAST_TELEM, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_HAPTIC: - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 3, GET_SET_DEFAULT(CFN_PARAM(cfn))); - grid.nextLine(); - break; - - case FUNC_LOGS: { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_VALUE); - auto edit = new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 255, GET_SET_DEFAULT(CFN_PARAM(cfn))); - edit->setDisplayHandler([=](BitmapBuffer * dc, LcdFlags flags, int32_t value) { - dc->drawNumber(2, 2, CFN_PARAM(cfn), PREC1, sizeof(CFN_PARAM(cfn)), nullptr, "s"); - }); - break; - } - } - - if (HAS_ENABLE_PARAM(func)) { - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_ENABLE); - new CheckBox(specialFunctionOneWindow, grid.getFieldSlot(), GET_SET_DEFAULT(CFN_ACTIVE(cfn))); + // Func param + switch (func) { + case FUNC_OVERRIDE_CHANNEL: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_CH); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 1, + MAX_OUTPUT_CHANNELS, + GET_SET_VALUE_WITH_OFFSET(CFN_CH_INDEX(cfn), 1)); grid.nextLine(); - } - else if (HAS_REPEAT_PARAM(func)) { // !1x 1x 1s 2s 3s ... - new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_REPEAT); - auto repeat = new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(2, 1), -1, 60/CFN_PLAY_REPEAT_MUL, GET_SET_DEFAULT(CFN_PLAY_REPEAT(cfn))); - repeat->setDisplayHandler([](BitmapBuffer * dc, LcdFlags flags, int32_t value) { + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), -100, 100, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_TRAINER: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + auto choice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + NUM_STICKS + 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + choice->setTextHandler([=](int32_t value) { if (value == 0) - dc->drawText(3, 0, "1x", flags); - else if (value == CFN_PLAY_REPEAT_NOSTART) - dc->drawText(3, 0, "!1x", flags); + return std::string(STR_STICKS); + else if (value == NUM_STICKS + 1) + return std::string(STR_CHANS); else - dc->drawNumber(3, 0, value * CFN_PLAY_REPEAT_MUL, flags, 0, nullptr, "s"); + return TEXT_AT_INDEX(STR_VSRCRAW, value); + ; }); grid.nextLine(); + break; } - specialFunctionOneWindow->adjustHeight(); + case FUNC_RESET: + if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_RESET); + auto choice = new Choice( + specialFunctionOneWindow, grid.getFieldSlot(), 0, + FUNC_RESET_PARAM_FIRST_TELEM + lastUsedTelemetryIndex(), + GET_SET_DEFAULT(CFN_PARAM(cfn))); + choice->setAvailableHandler(isSourceAvailableInResetSpecialFunction); + choice->setTextHandler([=](int32_t value) { + if (value < FUNC_RESET_PARAM_FIRST_TELEM) + return TEXT_AT_INDEX(STR_VFSWRESET, value); + else + return std::string( + g_model.telemetrySensors[value - FUNC_RESET_PARAM_FIRST_TELEM] + .label, + TELEM_LABEL_LEN); + }); + grid.nextLine(); + } + break; + + case FUNC_VOLUME: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VOLUME); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_BACKLIGHT: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MIXSRC_LAST_CH, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_SOUND: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), + STR_FUNCSOUNDS, 0, + AU_SPECIAL_SOUND_LAST - AU_SPECIAL_SOUND_FIRST - 1, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_TRACK: + case FUNC_BACKGND_MUSIC: + case FUNC_PLAY_SCRIPT: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new FileChoice( + specialFunctionOneWindow, grid.getFieldSlot(), + func == FUNC_PLAY_SCRIPT + ? SCRIPTS_FUNCS_PATH + : std::string(SOUNDS_PATH, SOUNDS_PATH_LNG_OFS) + + std::string(currentLanguagePack->id, 2), + func == FUNC_PLAY_SCRIPT ? SCRIPTS_EXT : SOUNDS_EXT, + sizeof(cfn->play.name), + [=]() { return std::string(cfn->play.name, ZLEN(cfn->play.name)); }, + [=](std::string newValue) { + strncpy(cfn->play.name, newValue.c_str(), sizeof(cfn->play.name)); + SET_DIRTY(); + }, + true); // strip extension + grid.nextLine(); + break; + + case FUNC_SET_TIMER: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_TIMER); + auto timerchoice = + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + TIMERS - 1, GET_SET_DEFAULT(CFN_TIMER_INDEX(cfn))); + timerchoice->setTextHandler([](int32_t value) { + return std::string(STR_TIMER) + std::to_string(value + 1); + }); + grid.nextLine(); + + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new TimeEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, + 9 * 60 * 60 - 1, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + } + + case FUNC_SET_FAILSAFE: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_MODULE); + new Choice(specialFunctionOneWindow, grid.getFieldSlot(), + "\004Int.Ext.", 0, NUM_MODULES - 1, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_PLAY_VALUE: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new SourceChoice(specialFunctionOneWindow, grid.getFieldSlot(), 0, + MIXSRC_LAST_TELEM, GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_HAPTIC: + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, 3, + GET_SET_DEFAULT(CFN_PARAM(cfn))); + grid.nextLine(); + break; + + case FUNC_LOGS: { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), + STR_VALUE); + auto edit = + new NumberEdit(specialFunctionOneWindow, grid.getFieldSlot(), 0, + 255, GET_SET_DEFAULT(CFN_PARAM(cfn))); + edit->setDisplayHandler( + [=](BitmapBuffer *dc, LcdFlags flags, int32_t value) { + dc->drawNumber(2, 2, CFN_PARAM(cfn), PREC1, + sizeof(CFN_PARAM(cfn)), nullptr, "s"); + }); + break; + } } - void buildBody(FormWindow * window) - { - // SF.one - FormGridLayout grid; - grid.spacer(PAGE_PADDING); - - CustomFunctionData * cfn = &functions[index]; - - // Switch - new StaticText(window, grid.getLabelSlot(), STR_SWITCH); - auto switchChoice = new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST, SWSRC_LAST, GET_SET_DEFAULT(CFN_SWITCH(cfn))); - switchChoice->setAvailableHandler([=](int value) { - return (functions == g_model.customFn ? isSwitchAvailable(value, ModelCustomFunctionsContext) - : isSwitchAvailable(value, - GeneralCustomFunctionsContext)); - }); + if (HAS_ENABLE_PARAM(func)) { + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_ENABLE); + new CheckBox(specialFunctionOneWindow, grid.getFieldSlot(), + GET_SET_DEFAULT(CFN_ACTIVE(cfn))); grid.nextLine(); - - // Function - new StaticText(window, grid.getLabelSlot(), STR_FUNC); - auto functionChoice = new Choice(window, grid.getFieldSlot(), STR_VFSWFUNC, 0, FUNC_MAX - 1, GET_DEFAULT(CFN_FUNC(cfn))); - functionChoice->setSetValueHandler([=](int32_t newValue) { - CFN_FUNC(cfn) = newValue; - CFN_RESET(cfn); - SET_DIRTY(); - updateSpecialFunctionOneWindow(); - }); - functionChoice->setAvailableHandler([=](int value) { - return isAssignableFunctionAvailable(value, functions); - }); + } else if (HAS_REPEAT_PARAM(func)) { // !1x 1x 1s 2s 3s ... + new StaticText(specialFunctionOneWindow, grid.getLabelSlot(), STR_REPEAT); + auto repeat = new NumberEdit( + specialFunctionOneWindow, grid.getFieldSlot(2, 1), -1, + 60 / CFN_PLAY_REPEAT_MUL, GET_SET_DEFAULT(CFN_PLAY_REPEAT(cfn))); + repeat->setDisplayHandler( + [](BitmapBuffer *dc, LcdFlags flags, int32_t value) { + if (value == 0) + dc->drawText(3, 0, "1x", flags); + else if (value == CFN_PLAY_REPEAT_NOSTART) + dc->drawText(3, 0, "!1x", flags); + else + dc->drawNumber(3, 0, value * CFN_PLAY_REPEAT_MUL, flags, 0, + nullptr, "s"); + }); grid.nextLine(); + } - specialFunctionOneWindow = new FormGroup(window, {0, grid.getWindowHeight(), LCD_W, 0}, FORM_FORWARD_FOCUS); + specialFunctionOneWindow->adjustHeight(); + } + + void buildBody(FormWindow *window) + { + // SF.one + FormGridLayout grid; + grid.spacer(PAGE_PADDING); + + CustomFunctionData *cfn = &functions[index]; + + // Switch + new StaticText(window, grid.getLabelSlot(), STR_SWITCH); + auto switchChoice = + new SwitchChoice(window, grid.getFieldSlot(), SWSRC_FIRST, SWSRC_LAST, + GET_SET_DEFAULT(CFN_SWITCH(cfn))); + switchChoice->setAvailableHandler([=](int value) { + return (functions == g_model.customFn + ? isSwitchAvailable(value, ModelCustomFunctionsContext) + : isSwitchAvailable(value, GeneralCustomFunctionsContext)); + }); + grid.nextLine(); + + // Function + new StaticText(window, grid.getLabelSlot(), STR_FUNC); + auto functionChoice = + new Choice(window, grid.getFieldSlot(), STR_VFSWFUNC, 0, FUNC_MAX - 1, + GET_DEFAULT(CFN_FUNC(cfn))); + functionChoice->setSetValueHandler([=](int32_t newValue) { + CFN_FUNC(cfn) = newValue; + CFN_RESET(cfn); + SET_DIRTY(); updateSpecialFunctionOneWindow(); - grid.addWindow(specialFunctionOneWindow); - } + }); + functionChoice->setAvailableHandler([=](int value) { + return isAssignableFunctionAvailable(value, functions); + }); + grid.nextLine(); + + specialFunctionOneWindow = new FormGroup( + window, {0, grid.getWindowHeight(), LCD_W, 0}, FORM_FORWARD_FOCUS); + updateSpecialFunctionOneWindow(); + grid.addWindow(specialFunctionOneWindow); + } }; static constexpr coord_t line1 = FIELD_PADDING_TOP; @@ -261,161 +327,178 @@ 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 + 20; -class SpecialFunctionButton : public Button { - public: - SpecialFunctionButton(FormWindow * parent, const rect_t &rect, CustomFunctionData * functions, uint8_t index) : - Button(parent, rect), - functions(functions), - index(index) - { - const CustomFunctionData * cfn = &functions[index]; - uint8_t func = CFN_FUNC(cfn); - if (!cfn->isEmpty() && - (HAS_ENABLE_PARAM(func) || HAS_REPEAT_PARAM(func) || (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || func == FUNC_PLAY_SCRIPT))) { - setHeight(line2 + PAGE_LINE_HEIGHT); - } +class SpecialFunctionButton : public Button +{ + public: + SpecialFunctionButton(FormWindow *parent, const rect_t &rect, + CustomFunctionData *functions, uint8_t index) : + Button(parent, rect), functions(functions), index(index) + { + const CustomFunctionData *cfn = &functions[index]; + uint8_t func = CFN_FUNC(cfn); + if (!cfn->isEmpty() && + (HAS_ENABLE_PARAM(func) || HAS_REPEAT_PARAM(func) || + (func == FUNC_PLAY_TRACK || func == FUNC_BACKGND_MUSIC || + func == FUNC_PLAY_SCRIPT))) { + setHeight(line2 + PAGE_LINE_HEIGHT); } + } #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "SpecialFunctionButton"; - } + std::string getName() const override { return "SpecialFunctionButton"; } #endif - bool isActive() const - { - return ((functions == g_model.customFn ? modelFunctionsContext.activeSwitches : globalFunctionsContext.activeSwitches) & ((MASK_CFN_TYPE)1 << index) ? 1 : 0); + bool isActive() const + { + return ((functions == g_model.customFn + ? modelFunctionsContext.activeSwitches + : globalFunctionsContext.activeSwitches) & + ((MASK_CFN_TYPE)1 << index) + ? 1 + : 0); + } + + void checkEvents() override + { + Button::checkEvents(); + if (active != isActive()) { + invalidate(); + active = !active; } + } - void checkEvents() override - { - Button::checkEvents(); - if (active != isActive()) { - invalidate(); - active = !active; - } + void paintSpecialFunctionLine(BitmapBuffer *dc) + { + const CustomFunctionData *cfn = &functions[index]; + if (functions[index].func == FUNC_OVERRIDE_CHANNEL && + functions != g_model.customFn) { + functions[index].func = FUNC_OVERRIDE_CHANNEL + 1; } + uint8_t func = CFN_FUNC(cfn); - void paintSpecialFunctionLine(BitmapBuffer * dc) - { - const CustomFunctionData * cfn = &functions[index]; - if (functions[index].func == FUNC_OVERRIDE_CHANNEL && functions != g_model.customFn) { - functions[index].func = FUNC_OVERRIDE_CHANNEL + 1; - } - uint8_t func = CFN_FUNC(cfn); + drawSwitch(dc, col1, line1, CFN_SWITCH(cfn), 0); + if (cfn->isEmpty()) return; - drawSwitch(dc, col1, line1, CFN_SWITCH(cfn), 0); - if (cfn->isEmpty()) - return; + dc->drawTextAtIndex(col2, line1, STR_VFSWFUNC, func, 0); + int16_t val_min = 0; + int16_t val_max = 255; - dc->drawTextAtIndex(col2, line1, STR_VFSWFUNC, func, 0); - int16_t val_min = 0; - int16_t val_max = 255; + switch (func) { + case FUNC_OVERRIDE_CHANNEL: + drawChn(dc, col1, line2, CFN_CH_INDEX(cfn) + 1, 0); + getMixSrcRange(MIXSRC_FIRST_CH, val_min, val_max); + dc->drawNumber(col2, line2, CFN_PARAM(cfn)); + break; - switch (func) { - case FUNC_OVERRIDE_CHANNEL: - drawChn(dc, col1, line2, CFN_CH_INDEX(cfn) + 1, 0); - getMixSrcRange(MIXSRC_FIRST_CH, val_min, val_max); - dc->drawNumber(col2, line2, CFN_PARAM(cfn)); - break; + case FUNC_TRAINER: + drawSource( + dc, col1, line2, + CFN_CH_INDEX(cfn) == 0 ? 0 : MIXSRC_Rud + CFN_CH_INDEX(cfn) - 1); + break; - case FUNC_TRAINER: - drawSource(dc, col1, line2, CFN_CH_INDEX(cfn) == 0 ? 0 : MIXSRC_Rud + CFN_CH_INDEX(cfn) - 1); - break; - - case FUNC_RESET: - if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { - dc->drawTextAtIndex(col1, line2, STR_VFSWRESET, CFN_PARAM(cfn)); - } - else { - TelemetrySensor * sensor = &g_model.telemetrySensors[CFN_PARAM(cfn) - FUNC_RESET_PARAM_FIRST_TELEM]; - dc->drawSizedText(col1, line2, sensor->label, TELEM_LABEL_LEN); - } - break; - - case FUNC_VOLUME: - drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); - break; - - case FUNC_BACKLIGHT: - drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); - break; - - case FUNC_PLAY_SOUND: - dc->drawTextAtIndex(col1, line2, STR_FUNCSOUNDS, CFN_PARAM(cfn)); - break; - - case FUNC_PLAY_TRACK: - case FUNC_BACKGND_MUSIC: - case FUNC_PLAY_SCRIPT: - if (ZEXIST(cfn->play.name)) - dc->drawSizedText(col1, line2, cfn->play.name, sizeof(cfn->play.name), 0); - else - dc->drawTextAtIndex(col1, line2, STR_VCSWFUNC, 0, 0); - break; - - case FUNC_SET_TIMER: - drawStringWithIndex(dc, col1, line2, STR_TIMER, CFN_TIMER_INDEX(cfn) + 1, 0); - break; - - case FUNC_SET_FAILSAFE: - dc->drawTextAtIndex(col1, line2, "\004Int.Ext.", CFN_PARAM(cfn), 0); - break; - - case FUNC_PLAY_VALUE: - drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); - break; - - case FUNC_HAPTIC: - dc->drawNumber(col1, line2, CFN_PARAM(cfn), 0); - break; - - case FUNC_LOGS: - dc->drawNumber(col3, line1, CFN_PARAM(cfn), PREC1, sizeof(CFN_PARAM(cfn)), nullptr, "s"); - break; - } - if (HAS_ENABLE_PARAM(func)) { - theme->drawCheckBox(dc, CFN_ACTIVE(cfn), col3, line2); - } - else if (HAS_REPEAT_PARAM(func)) { - if (CFN_PLAY_REPEAT(cfn) == 0) { - dc->drawText(col3, line2, "1x", 0); - } - else if (CFN_PLAY_REPEAT(cfn) == CFN_PLAY_REPEAT_NOSTART) { - dc->drawText(col3, line2, "!1x", 0); - } - else { - dc->drawNumber(col3 + 12, line2, CFN_PLAY_REPEAT(cfn) * CFN_PLAY_REPEAT_MUL, 0 | RIGHT, 0, nullptr, "s"); + case FUNC_RESET: + if (CFN_PARAM(cfn) < FUNC_RESET_PARAM_FIRST_TELEM) { + dc->drawTextAtIndex(col1, line2, STR_VFSWRESET, CFN_PARAM(cfn)); + } else { + TelemetrySensor *sensor = + &g_model.telemetrySensors[CFN_PARAM(cfn) - + FUNC_RESET_PARAM_FIRST_TELEM]; + dc->drawSizedText(col1, line2, sensor->label, TELEM_LABEL_LEN); } + break; + + case FUNC_VOLUME: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_BACKLIGHT: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_PLAY_SOUND: + dc->drawTextAtIndex(col1, line2, STR_FUNCSOUNDS, CFN_PARAM(cfn)); + break; + + case FUNC_PLAY_TRACK: + case FUNC_BACKGND_MUSIC: + case FUNC_PLAY_SCRIPT: + if (ZEXIST(cfn->play.name)) + dc->drawSizedText(col1, line2, cfn->play.name, sizeof(cfn->play.name), + 0); + else + dc->drawTextAtIndex(col1, line2, STR_VCSWFUNC, 0, 0); + break; + + case FUNC_SET_TIMER: + drawStringWithIndex(dc, col1, line2, STR_TIMER, + CFN_TIMER_INDEX(cfn) + 1, 0); + break; + + case FUNC_SET_FAILSAFE: + dc->drawTextAtIndex(col1, line2, "\004Int.Ext.", CFN_PARAM(cfn), 0); + break; + + case FUNC_PLAY_VALUE: + drawSource(dc, col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_HAPTIC: + dc->drawNumber(col1, line2, CFN_PARAM(cfn), 0); + break; + + case FUNC_LOGS: + dc->drawNumber(col3, line1, CFN_PARAM(cfn), PREC1, + sizeof(CFN_PARAM(cfn)), nullptr, "s"); + break; + } + if (HAS_ENABLE_PARAM(func)) { + theme->drawCheckBox(dc, CFN_ACTIVE(cfn), col3, line2); + } else if (HAS_REPEAT_PARAM(func)) { + if (CFN_PLAY_REPEAT(cfn) == 0) { + dc->drawText(col3, line2, "1x", 0); + } else if (CFN_PLAY_REPEAT(cfn) == CFN_PLAY_REPEAT_NOSTART) { + dc->drawText(col3, line2, "!1x", 0); + } else { + dc->drawNumber(col3 + 12, line2, + CFN_PLAY_REPEAT(cfn) * CFN_PLAY_REPEAT_MUL, 0 | RIGHT, 0, + nullptr, "s"); } } + } - void paint(BitmapBuffer * dc) override - { - if (active) - dc->drawSolidFilledRect(2, 2, rect.w - 4, rect.h - 4, HIGHLIGHT_COLOR); + void paint(BitmapBuffer *dc) override + { + if (active) + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, HIGHLIGHT_COLOR); + else + dc->drawSolidFilledRect(0, 0, rect.w, rect.h, FIELD_BGCOLOR); - paintSpecialFunctionLine(dc); + paintSpecialFunctionLine(dc); - // The bounding rect - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, hasFocus() ? CHECKBOX_COLOR : DISABLE_COLOR); - } + // The bounding rect + if (hasFocus()) + dc->drawSolidRect(0, 0, rect.w, rect.h, 2, FOCUS_BGCOLOR); + else + dc->drawSolidRect(0, 0, rect.w, rect.h, 1, FIELD_FRAME_COLOR); + } - protected: - CustomFunctionData * functions; - uint8_t index; - bool active = false; + protected: + CustomFunctionData *functions; + uint8_t index; + bool active = false; }; -SpecialFunctionsPage::SpecialFunctionsPage(CustomFunctionData * functions) : - PageTab(functions == g_model.customFn ? STR_MENUCUSTOMFUNC : STR_MENUSPECIALFUNCS, - functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS : ICON_RADIO_GLOBAL_FUNCTIONS), - functions(functions) +SpecialFunctionsPage::SpecialFunctionsPage(CustomFunctionData *functions) : + PageTab(functions == g_model.customFn ? STR_MENUCUSTOMFUNC + : STR_MENUSPECIALFUNCS, + functions == g_model.customFn ? ICON_MODEL_SPECIAL_FUNCTIONS + : ICON_RADIO_GLOBAL_FUNCTIONS), + functions(functions) { } -void SpecialFunctionsPage::rebuild(FormWindow * window, int8_t focusSpecialFunctionIndex) +void SpecialFunctionsPage::rebuild(FormWindow *window, + int8_t focusSpecialFunctionIndex) { coord_t scrollPosition = window->getScrollPositionY(); window->clear(); @@ -423,15 +506,14 @@ void SpecialFunctionsPage::rebuild(FormWindow * window, int8_t focusSpecialFunct window->setScrollPositionY(scrollPosition); } -void SpecialFunctionsPage::editSpecialFunction(FormWindow * window, uint8_t index) +void SpecialFunctionsPage::editSpecialFunction(FormWindow *window, + uint8_t index) { auto editPage = new SpecialFunctionEditPage(functions, index); - editPage->setCloseHandler([=]() { - rebuild(window, index); - }); + editPage->setCloseHandler([=]() { rebuild(window, index); }); } -void SpecialFunctionsPage::build(FormWindow * window, int8_t focusIndex) +void SpecialFunctionsPage::build(FormWindow *window, int8_t focusIndex) { FormGridLayout grid; grid.spacer(PAGE_PADDING); @@ -440,11 +522,10 @@ void SpecialFunctionsPage::build(FormWindow * window, int8_t focusIndex) Window::clearFocus(); char s[] = "SFxx"; - if (functions == g_eeGeneral.customFn) - s[0] = 'G'; + if (functions == g_eeGeneral.customFn) s[0] = 'G'; for (uint8_t i = 0; i < MAX_SPECIAL_FUNCTIONS; i++) { - CustomFunctionData * cfn = &functions[i]; + CustomFunctionData *cfn = &functions[i]; strAppendUnsigned(&s[2], i); if (cfn->swtch == 0) { @@ -464,64 +545,82 @@ void SpecialFunctionsPage::build(FormWindow * window, int8_t focusIndex) return 0; }); grid.spacer(button->height() + 5); - } - else { - new StaticText(window, grid.getLabelSlot(), s, BUTTON_BACKGROUND, CENTERED); + } else { + auto txt = new StaticText(window, grid.getLabelSlot(), s, + BUTTON_BACKGROUND, CENTERED); - auto button = new SpecialFunctionButton(window, grid.getFieldSlot(), functions, i); + auto button = + new SpecialFunctionButton(window, grid.getFieldSlot(), functions, i); button->setPressHandler([=]() { - button->bringToTop(); - Menu * menu = new Menu(window); - menu->addLine(STR_EDIT, [=]() { - editSpecialFunction(window, i); + button->bringToTop(); + Menu *menu = new Menu(window); + menu->addLine(STR_EDIT, [=]() { editSpecialFunction(window, i); }); + menu->addLine(STR_COPY, [=]() { + clipboard.type = CLIPBOARD_TYPE_CUSTOM_FUNCTION; + clipboard.data.cfn = *cfn; + }); + if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION) { + menu->addLine(STR_PASTE, [=]() { + *cfn = clipboard.data.cfn; + SET_DIRTY(); + rebuild(window, i); }); - menu->addLine(STR_COPY, [=]() { - clipboard.type = CLIPBOARD_TYPE_CUSTOM_FUNCTION; - clipboard.data.cfn = *cfn; + } + if (functions[MAX_SPECIAL_FUNCTIONS - 1].isEmpty()) { + menu->addLine(STR_INSERT, [=]() { + memmove( + cfn + 1, cfn, + (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); + memset(cfn, 0, sizeof(CustomFunctionData)); + SET_DIRTY(); + rebuild(window, i); }); - if (clipboard.type == CLIPBOARD_TYPE_CUSTOM_FUNCTION) { - menu->addLine(STR_PASTE, [=]() { - *cfn = clipboard.data.cfn; - SET_DIRTY(); - rebuild(window, i); - }); - } - if (functions[MAX_SPECIAL_FUNCTIONS - 1].isEmpty()) { - menu->addLine(STR_INSERT, [=]() { - memmove(cfn + 1, cfn, (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); - memset(cfn, 0, sizeof(CustomFunctionData)); - SET_DIRTY(); - rebuild(window, i); - }); - } - menu->addLine(STR_CLEAR, [=]() { - memset(cfn, 0, sizeof(CustomFunctionData)); + } + menu->addLine(STR_CLEAR, [=]() { + memset(cfn, 0, sizeof(CustomFunctionData)); + SET_DIRTY(); + }); + for (int j = i; j < MAX_SPECIAL_FUNCTIONS; j++) { + if (!functions[j].isEmpty()) { + menu->addLine(STR_DELETE, [=]() { + memmove( + cfn, cfn + 1, + (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); + memset(&functions[MAX_SPECIAL_FUNCTIONS - 1], 0, + sizeof(CustomFunctionData)); SET_DIRTY(); - }); - for (int j = i; j < MAX_SPECIAL_FUNCTIONS; j++) { - if (!functions[j].isEmpty()) { - menu->addLine(STR_DELETE, [=]() { - memmove(cfn, cfn + 1, (MAX_SPECIAL_FUNCTIONS - i - 1) * sizeof(CustomFunctionData)); - memset(&functions[MAX_SPECIAL_FUNCTIONS - 1], 0, sizeof(CustomFunctionData)); - SET_DIRTY(); - rebuild(window, i); - }); - break; - } + rebuild(window, i); + }); + break; } - return 0; + } + return 0; + }); + button->setFocusHandler([=](bool focus) { + if (focus) { + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + } else { + txt->setBackgroundColor(FIELD_FRAME_COLOR); + txt->setTextFlags(CENTERED); + } + txt->invalidate(); }); if (focusIndex == i) { button->setFocus(SET_FOCUS_DEFAULT); + txt->setBackgroundColor(FOCUS_BGCOLOR); + txt->setTextFlags(FOCUS_COLOR | CENTERED); + txt->invalidate(); } + txt->setHeight(button->height()); grid.spacer(button->height() + 5); } } grid.nextLine(); -// window->setLastField(); + // window->setLastField(); window->setInnerHeight(grid.getWindowHeight()); } diff --git a/radio/src/gui/colorlcd/special_functions.h b/radio/src/gui/colorlcd/special_functions.h index 0fa96341c..eaf85ec9b 100644 --- a/radio/src/gui/colorlcd/special_functions.h +++ b/radio/src/gui/colorlcd/special_functions.h @@ -31,11 +31,7 @@ class SpecialFunctionsPage: public PageTab { void build(FormWindow * window) override { -#if defined(HARDWARE_TOUCH) - build(window, -1); -#else build(window, 0); -#endif } protected: