Facelift round 4

This commit is contained in:
Raphael Coeffic
2021-06-14 06:34:57 +02:00
parent cf1d83351c
commit 490cf003a3
11 changed files with 992 additions and 799 deletions
+15 -2
View File
@@ -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(),
+1 -1
View File
@@ -30,7 +30,7 @@ class ModelCurvesPage: public PageTab {
virtual void build(FormWindow * window) override
{
build(window, -1);
build(window, 0);
}
protected:
+12 -3
View File
@@ -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;
+93 -74
View File
@@ -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();
}
+121 -104
View File
@@ -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);
}
}
@@ -29,7 +29,7 @@ public:
virtual void build(FormWindow * window) override
{
build(window, -1);
build(window, 0);
}
protected:
+241 -211
View File
@@ -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<uint8_t>(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<uint8_t>(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 {
+20 -10
View File
@@ -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);
}
+1 -1
View File
@@ -29,7 +29,7 @@ class ModelOutputsPage: public PageTab {
virtual void build(FormWindow * window) override
{
build(window, -1);
build(window, 0);
}
protected:
+487 -388
View File
@@ -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());
}
@@ -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: