feat(firmware): add a special function to trigger key lock (#7565)

This commit is contained in:
philmoz
2026-08-16 13:17:19 +10:00
committed by GitHub
parent a0c1aa7a2b
commit 6d793f9252
73 changed files with 829 additions and 608 deletions
@@ -203,11 +203,11 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF
case 0:
if (sub==k && menuHorizontalPosition < 1 && CFN_SWITCH(cfn) == SWSRC_NONE) {
CFN_ACTIVE(cfn) = 0; // Default is disabled
drawSwitch(MODEL_SPECIAL_FUNC_1ST_COLUMN, y, CFN_SWITCH(cfn), attr | INVERS | ((functionsContext->activeSwitches & ((MASK_CFN_TYPE)1 << k)) ? BOLD : 0));
drawSwitch(MODEL_SPECIAL_FUNC_1ST_COLUMN, y, CFN_SWITCH(cfn), attr | INVERS | (functionsContext->isFunctionSwitchActive(k) ? BOLD : 0));
if (active) CHECK_INCDEC_SWITCH(event, CFN_SWITCH(cfn), SWSRC_FIRST, SWSRC_LAST, eeFlags, isSwitchAvailableInCustomFunctions);
}
else {
drawSwitch(MODEL_SPECIAL_FUNC_1ST_COLUMN, y, CFN_SWITCH(cfn), attr | ((functionsContext->activeSwitches & ((MASK_CFN_TYPE)1 << k)) ? BOLD : 0));
drawSwitch(MODEL_SPECIAL_FUNC_1ST_COLUMN, y, CFN_SWITCH(cfn), attr | (functionsContext->isFunctionSwitchActive(k) ? BOLD : 0));
if (active || AUTOSWITCH_ENTER_LONG()) {
if (event == EVT_KEY_LONG(KEY_ENTER))
killEvents(event);
@@ -197,7 +197,7 @@ void menuSpecialFunctions(event_t event, CustomFunctionData * functions, CustomF
switch (j) {
case ITEM_CUSTOM_FUNCTIONS_SWITCH:
if(CFN_SWITCH(cfn) == SWSRC_NONE) CFN_ACTIVE(cfn) = 0; // Disable new function by default
drawSwitch(MODEL_SPECIAL_FUNC_1ST_COLUMN, y, CFN_SWITCH(cfn), attr | ((functionsContext->activeSwitches & ((MASK_CFN_TYPE)1 << k)) ? BOLD : 0));
drawSwitch(MODEL_SPECIAL_FUNC_1ST_COLUMN, y, CFN_SWITCH(cfn), attr | (functionsContext->isFunctionSwitchActive(k) ? BOLD : 0));
if (active || AUTOSWITCH_ENTER_LONG()) {
if (event == EVT_KEY_LONG(KEY_ENTER))
killEvents(event);
+3
View File
@@ -199,6 +199,9 @@ extern "C" void touchDriverRead(lv_indev_drv_t *drv, lv_indev_data_t *data)
// no touch input if special function is used
if (isFunctionActive(FUNCTION_DISABLE_TOUCH)) {
lv_indev_reset(touchDevice, nullptr);
if (st.event != TE_NONE) {
POPUP_BUBBLE(STR_TOUCH_DISABLED, 1500, DEFAULT_BUBBLE_WIDTH, DEFAULT_BUBBLE_Y-BUBBLE_HEIGHT);
}
return;
}
+4 -4
View File
@@ -116,8 +116,8 @@ static lv_obj_t* bubble_popup_create(lv_obj_t* parent)
class BubbleDialog : public Window
{
public:
BubbleDialog(const char* message, int timeout, coord_t width) :
Window(MainWindow::instance(), rect_t{(LCD_W - width) / 2, LCD_H - 100, width, 50},
BubbleDialog(const char* message, int timeout, coord_t width, coord_t y) :
Window(MainWindow::instance(), rect_t{(LCD_W - width) / 2, y, width, BUBBLE_HEIGHT},
bubble_popup_create),
startTime(lv_tick_get()), timeout(timeout)
{
@@ -147,7 +147,7 @@ class BubbleDialog : public Window
uint32_t timeout;
};
void POPUP_BUBBLE(const char* message, uint32_t timeout, coord_t width)
void POPUP_BUBBLE(const char* message, uint32_t timeout, coord_t width, coord_t y)
{
new BubbleDialog(message, timeout, width);
new BubbleDialog(message, timeout, width, y);
}
+6 -1
View File
@@ -23,6 +23,7 @@
#include <functional>
#include "edgetx_types.h"
#include "etx_lv_theme.h"
#include "hal.h"
enum WarningType
@@ -38,9 +39,13 @@ enum WarningType
typedef std::function<void(const char *, const char *, int, int)>
ProgressHandler;
constexpr coord_t DEFAULT_BUBBLE_WIDTH = LCD_W * 3 / 4;
constexpr coord_t DEFAULT_BUBBLE_Y = LCD_H * 7 / 10;
LAYOUT_VAL_SCALED(BUBBLE_HEIGHT, 50)
void POPUP_INFORMATION(const char *message);
void POPUP_WARNING(const char *message, const char *info = nullptr);
bool POPUP_WARNING_ON_UI_TASK(const char *message, const char *info = nullptr);
void POPUP_BUBBLE(const char *message, uint32_t timeout, coord_t width = LCD_W - 100);
void POPUP_BUBBLE(const char *message, uint32_t timeout, coord_t width = DEFAULT_BUBBLE_WIDTH, coord_t y = DEFAULT_BUBBLE_Y);
void show_ui_popup();
@@ -144,7 +144,14 @@ void ViewMain::setCurrentMainView(unsigned viewId)
lv_obj_set_tile_id(tile_view, viewId, 0, LV_ANIM_OFF);
}
void setRequestedMainView(uint8_t view) { g_model.view = view; }
void setRequestedMainView(uint8_t view)
{
auto viewMain = ViewMain::instance();
if (view < viewMain->getMainViewsCount()) {
viewMain->setCurrentMainView(view);
saveViewId(view);
}
}
void ViewMain::nextMainView()
{
+385 -372
View File
@@ -290,413 +290,426 @@ static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST};
#define PUSH_CS_DURATION_MIN 0 // 0 no duration : as long as switch is true
#define PUSH_CS_DURATION_MAX 255 // 25.5s longest duration
FunctionEditPage::FunctionEditPage(uint8_t index, EdgeTxIcon icon,
const char *title, const char *prefix) :
Page(icon), index(index)
class FunctionEditPage : public Page
{
buildHeader(header, title, prefix);
delayLoad();
}
void FunctionEditPage::delayedInit()
{
buildBody(body);
}
void FunctionEditPage::checkEvents()
{
Page::checkEvents();
if (active != isActive()) {
if (isActive()) {
lv_obj_add_state(headerSF->getLvObj(), ETX_STATE_SF_ACTIVE);
} else {
lv_obj_clear_state(headerSF->getLvObj(), ETX_STATE_SF_ACTIVE);
}
active = isActive();
public:
FunctionEditPage(uint8_t index, EdgeTxIcon icon, const char *title,
const char *prefix) : Page(icon), index(index)
{
buildHeader(header, title, prefix);
delayLoad();
}
}
void FunctionEditPage::buildHeader(Window *window, const char *title,
const char *prefix)
{
header->setTitle(title);
headerSF = header->setTitle2(prefix + std::to_string(index + 1));
void delayedInit() override
{
buildBody(body);
}
etx_txt_color(headerSF->getLvObj(), COLOR_THEME_ACTIVE_INDEX,
ETX_STATE_SF_ACTIVE);
etx_font(headerSF->getLvObj(), FONT_BOLD_INDEX, ETX_STATE_SF_ACTIVE);
}
protected:
uint8_t index;
Window *specialFunctionOneWindow = nullptr;
StaticText *headerSF = nullptr;
bool active = false;
bool saveKeyLockState;
void FunctionEditPage::addSourceChoice(FormLine *line, const char *title,
CustomFunctionData *cfn, int16_t vmax)
{
new StaticText(line, rect_t{}, title);
new SourceChoice(line, rect_t{}, 0, vmax, GET_SET_DEFAULT(CFN_PARAM(cfn)), true);
}
virtual bool isActive() const = 0;
virtual bool isSwitchAvailable(int value) const = 0;
virtual CustomFunctionData *customFunctionData() const = 0;
virtual bool isAssignableFunctionAvailable(int function) const = 0;
virtual void setDirty() const = 0;
NumberEdit *FunctionEditPage::addNumberEdit(FormLine *line, const char *title,
CustomFunctionData *cfn,
int16_t vmin, int16_t vmax)
{
new StaticText(line, rect_t{}, title);
return new NumberEdit(line, rect_t{}, vmin, vmax,
GET_SET_DEFAULT(CFN_PARAM(cfn)));
}
void FunctionEditPage::updateSpecialFunctionOneWindow()
{
specialFunctionOneWindow->clear();
specialFunctionOneWindow->setFlexLayout();
FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY);
auto line = specialFunctionOneWindow->newLine(grid);
CustomFunctionData *cfn = customFunctionData();
uint8_t func = CFN_FUNC(cfn);
// Func param
switch (func) {
case FUNC_OVERRIDE_CHANNEL: {
new StaticText(line, rect_t{}, STR_CH);
new NumberEdit(line, rect_t{}, 1, MAX_OUTPUT_CHANNELS,
GET_SET_VALUE_WITH_OFFSET(CFN_CH_INDEX(cfn), 1));
line = specialFunctionOneWindow->newLine(grid);
int limit =
(g_model.extendedLimits ? LIMIT_EXT_PERCENT : LIMIT_STD_PERCENT);
addNumberEdit(line, STR_VALUE, cfn, -limit, limit);
break;
}
case FUNC_TRAINER: {
new StaticText(line, rect_t{}, STR_VALUE);
auto max_sticks = adcGetMaxInputs(ADC_INPUT_MAIN);
auto choice = new Choice(line, rect_t{}, 0, max_sticks + 1,
GET_SET_DEFAULT(CFN_CH_INDEX(cfn)));
choice->setTextHandler([=](int32_t value) {
if (value == 0)
return std::string(STR_STICKS);
else if (value == MAX_STICKS + 1)
return std::string(STR_CHANS);
return std::string(getMainControlLabel(value - 1));
});
break;
}
case FUNC_RESET:
if (CFN_PARAM(cfn) <= FUNC_RESET_PARAM_LAST) {
new StaticText(line, rect_t{}, STR_RESET);
auto choice =
new Choice(line, rect_t{}, 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 std::string(STR_VFSWRESET[value]);
else
return std::string(
g_model.telemetrySensors[value - FUNC_RESET_PARAM_FIRST_TELEM]
.label,
TELEM_LABEL_LEN);
});
}
break;
case FUNC_VOLUME:
addSourceChoice(line, STR_VOLUME, cfn, MIXSRC_LAST_CH);
break;
case FUNC_BACKLIGHT:
addSourceChoice(line, STR_VALUE, cfn, MIXSRC_LAST_CH);
break;
case FUNC_PLAY_SOUND:
new StaticText(line, rect_t{}, STR_VALUE);
new Choice(line, rect_t{}, STR_FUNCSOUNDS, 0,
AU_SPECIAL_SOUND_LAST - AU_SPECIAL_SOUND_FIRST - 1,
GET_SET_DEFAULT(CFN_PARAM(cfn)));
break;
case FUNC_PLAY_TRACK:
case FUNC_BACKGND_MUSIC:
case FUNC_PLAY_SCRIPT:
case FUNC_RGB_LED:
new StaticText(line, rect_t{}, STR_VALUE);
new FileChoice(
line, rect_t{},
func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED
? (func == FUNC_PLAY_SCRIPT ? SCRIPTS_FUNCS_PATH
: SCRIPTS_RGB_PATH)
: std::string(SOUNDS_PATH, SOUNDS_PATH_LNG_OFS) +
std::string(currentLanguagePack->id, 2),
(func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED) ? 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();
if (func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED)
LUA_LOAD_MODEL_SCRIPTS();
},
true, funcGetLabel(func));
break;
case FUNC_SET_TIMER: {
new StaticText(line, rect_t{}, STR_TIMER);
if (timersSetupCount() > 0) {
auto timerchoice = new Choice(line, rect_t{}, 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);
});
timerchoice->setAvailableHandler(
[=](int value) { return isTimerSourceAvailable(value); });
line = specialFunctionOneWindow->newLine(grid);
new StaticText(line, rect_t{}, STR_VALUE);
new TimeEdit(line, rect_t{}, 0, 9 * 60 * 60 - 1,
GET_SET_DEFAULT(CFN_PARAM(cfn)));
void checkEvents() override
{
Page::checkEvents();
if (active != isActive()) {
if (isActive()) {
lv_obj_add_state(headerSF->getLvObj(), ETX_STATE_SF_ACTIVE);
} else {
new StaticText(line, rect_t{}, STR_NO_TIMERS);
lv_obj_clear_state(headerSF->getLvObj(), ETX_STATE_SF_ACTIVE);
}
break;
active = isActive();
}
}
static const char* const strModules[] = { "Int.", "Ext." };
case FUNC_SET_FAILSAFE:
new StaticText(line, rect_t{}, STR_MODULE);
new Choice(line, rect_t{}, strModules, 0, NUM_MODULES - 1,
GET_SET_DEFAULT(CFN_PARAM(cfn)));
break;
void addSourceChoice(FormLine *line, const char *title,
CustomFunctionData *cfn, int16_t vmax)
{
new StaticText(line, rect_t{}, title);
new SourceChoice(line, rect_t{}, 0, vmax, GET_SET_DEFAULT(CFN_PARAM(cfn)), true);
}
case FUNC_PLAY_VALUE:
addSourceChoice(line, STR_VALUE, cfn, MIXSRC_LAST_TELEM);
break;
NumberEdit *addNumberEdit(FormLine *line, const char *title,
CustomFunctionData *cfn, int16_t vmin, int16_t vmax)
{
new StaticText(line, rect_t{}, title);
return new NumberEdit(line, rect_t{}, vmin, vmax,
GET_SET_DEFAULT(CFN_PARAM(cfn)));
}
case FUNC_HAPTIC:
addNumberEdit(line, STR_VALUE, cfn, 0, 3);
break;
void updateSpecialFunctionOneWindow()
{
specialFunctionOneWindow->clear();
specialFunctionOneWindow->setFlexLayout();
FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY);
auto line = specialFunctionOneWindow->newLine(grid);
#if defined(FUNCTION_SWITCHES)
case FUNC_PUSH_CUST_SWITCH: {
new StaticText(line, rect_t{}, STR_SWITCH);
auto choice = new Choice(line, rect_t{}, 0, switchGetMaxSwitches() - 1,
[=]() { return switchGetSwitchFromCustomIdx(CFN_CS_INDEX(cfn)); },
[=](int n) { CFN_CS_INDEX(cfn) = switchGetCustomSwitchIdx(n); },
STR_SWITCH);
choice->setTextHandler([=](int n) {
return std::string(switchGetDefaultName(n));
});
choice->setAvailableHandler(switchIsCustomSwitch);
CustomFunctionData *cfn = customFunctionData();
uint8_t func = CFN_FUNC(cfn);
// Func param
switch (func) {
case FUNC_OVERRIDE_CHANNEL: {
new StaticText(line, rect_t{}, STR_CH);
new NumberEdit(line, rect_t{}, 1, MAX_OUTPUT_CHANNELS,
GET_SET_VALUE_WITH_OFFSET(CFN_CH_INDEX(cfn), 1));
line = specialFunctionOneWindow->newLine(grid);
auto edit = addNumberEdit(line, STR_INTERVAL, cfn, PUSH_CS_DURATION_MIN,
PUSH_CS_DURATION_MAX);
int limit =
(g_model.extendedLimits ? LIMIT_EXT_PERCENT : LIMIT_STD_PERCENT);
addNumberEdit(line, STR_VALUE, cfn, -limit, limit);
break;
}
case FUNC_TRAINER: {
new StaticText(line, rect_t{}, STR_VALUE);
auto max_sticks = adcGetMaxInputs(ADC_INPUT_MAIN);
auto choice = new Choice(line, rect_t{}, 0, max_sticks + 1,
GET_SET_DEFAULT(CFN_CH_INDEX(cfn)));
choice->setTextHandler([=](int32_t value) {
if (value == 0)
return std::string(STR_STICKS);
else if (value == max_sticks + 1)
return std::string(STR_CHANS);
return std::string(getMainControlLabel(value - 1));
});
break;
}
case FUNC_RESET:
if (CFN_PARAM(cfn) <= FUNC_RESET_PARAM_LAST) {
new StaticText(line, rect_t{}, STR_RESET);
auto choice =
new Choice(line, rect_t{}, 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 std::string(STR_VFSWRESET[value]);
else
return std::string(
g_model.telemetrySensors[value - FUNC_RESET_PARAM_FIRST_TELEM]
.label,
TELEM_LABEL_LEN);
});
}
break;
case FUNC_VOLUME:
addSourceChoice(line, STR_VOLUME, cfn, MIXSRC_LAST_CH);
break;
case FUNC_BACKLIGHT:
addSourceChoice(line, STR_VALUE, cfn, MIXSRC_LAST_CH);
break;
case FUNC_PLAY_SOUND:
new StaticText(line, rect_t{}, STR_VALUE);
new Choice(line, rect_t{}, STR_FUNCSOUNDS, 0,
AU_SPECIAL_SOUND_LAST - AU_SPECIAL_SOUND_FIRST - 1,
GET_SET_DEFAULT(CFN_PARAM(cfn)));
break;
case FUNC_PLAY_TRACK:
case FUNC_BACKGND_MUSIC:
case FUNC_PLAY_SCRIPT:
case FUNC_RGB_LED:
new StaticText(line, rect_t{}, STR_VALUE);
new FileChoice(
line, rect_t{},
func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED
? (func == FUNC_PLAY_SCRIPT ? SCRIPTS_FUNCS_PATH
: SCRIPTS_RGB_PATH)
: std::string(SOUNDS_PATH, SOUNDS_PATH_LNG_OFS) +
std::string(currentLanguagePack->id, 2),
(func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED) ? 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();
if (func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED)
LUA_LOAD_MODEL_SCRIPTS();
},
true, funcGetLabel(func));
break;
case FUNC_SET_TIMER: {
new StaticText(line, rect_t{}, STR_TIMER);
if (timersSetupCount() > 0) {
auto timerchoice = new Choice(line, rect_t{}, 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);
});
timerchoice->setAvailableHandler(
[=](int value) { return isTimerSourceAvailable(value); });
line = specialFunctionOneWindow->newLine(grid);
new StaticText(line, rect_t{}, STR_VALUE);
new TimeEdit(line, rect_t{}, 0, 9 * 60 * 60 - 1,
GET_SET_DEFAULT(CFN_PARAM(cfn)));
} else {
new StaticText(line, rect_t{}, STR_NO_TIMERS);
}
break;
}
case FUNC_SET_FAILSAFE:
static const char* const strModules[] = { "Int.", "Ext." };
new StaticText(line, rect_t{}, STR_MODULE);
new Choice(line, rect_t{}, strModules, 0, NUM_MODULES - 1,
GET_SET_DEFAULT(CFN_PARAM(cfn)));
break;
case FUNC_PLAY_VALUE:
addSourceChoice(line, STR_VALUE, cfn, MIXSRC_LAST_TELEM);
break;
case FUNC_HAPTIC:
addNumberEdit(line, STR_VALUE, cfn, 0, 3);
break;
#if defined(FUNCTION_SWITCHES)
case FUNC_PUSH_CUST_SWITCH: {
new StaticText(line, rect_t{}, STR_SWITCH);
auto choice = new Choice(line, rect_t{}, 0, switchGetMaxSwitches() - 1,
[=]() { return switchGetSwitchFromCustomIdx(CFN_CS_INDEX(cfn)); },
[=](int n) { CFN_CS_INDEX(cfn) = switchGetCustomSwitchIdx(n); },
STR_SWITCH);
choice->setTextHandler([=](int n) {
return std::string(switchGetDefaultName(n));
});
choice->setAvailableHandler(switchIsCustomSwitch);
line = specialFunctionOneWindow->newLine(grid);
auto edit = addNumberEdit(line, STR_INTERVAL, cfn, PUSH_CS_DURATION_MIN,
PUSH_CS_DURATION_MAX);
edit->setDisplayHandler([=](int32_t value) {
return formatNumberAsString(CFN_PARAM(cfn), PREC1, 0, nullptr, "s");
});
}
break;
#endif
case FUNC_LOGS: {
if (CFN_PARAM(cfn) == 0) // use stored value if SF exists
CFN_PARAM(cfn) = SD_LOGS_PERIOD_DEFAULT; // otherwise initialize with
// default value
auto edit = addNumberEdit(line, STR_INTERVAL, cfn, SD_LOGS_PERIOD_MIN,
SD_LOGS_PERIOD_MAX);
edit->setDefault(
SD_LOGS_PERIOD_DEFAULT); // set default period for DEF button
edit->setDisplayHandler([=](int32_t value) {
return formatNumberAsString(CFN_PARAM(cfn), PREC1, 0, nullptr, "s");
});
break;
}
break;
#endif
case FUNC_LOGS: {
if (CFN_PARAM(cfn) == 0) // use stored value if SF exists
CFN_PARAM(cfn) = SD_LOGS_PERIOD_DEFAULT; // otherwise initialize with
// default value
case FUNC_SET_SCREEN:
CFN_PARAM(cfn) = (int16_t)max(CFN_PARAM(cfn), (int16_t)1);
CFN_PARAM(cfn) = (int16_t)min(
CFN_PARAM(cfn), (int16_t)ViewMain::instance()->getMainViewsCount());
addNumberEdit(line, STR_VALUE, cfn, 1,
ViewMain::instance()->getMainViewsCount());
break;
auto edit = addNumberEdit(line, STR_INTERVAL, cfn, SD_LOGS_PERIOD_MIN,
SD_LOGS_PERIOD_MAX);
edit->setDefault(
SD_LOGS_PERIOD_DEFAULT); // set default period for DEF button
edit->setDisplayHandler([=](int32_t value) {
return formatNumberAsString(CFN_PARAM(cfn), PREC1, 0, nullptr, "s");
});
break;
}
case FUNC_ADJUST_GVAR: {
if (validateSFGV(cfn)) SET_DIRTY();
new StaticText(line, rect_t{}, STR_GLOBALVAR);
auto gvarchoice = new Choice(line, rect_t{}, 0, MAX_GVARS - 1,
GET_DEFAULT(CFN_GVAR_INDEX(cfn)),
[=](int32_t newValue){
CFN_GVAR_INDEX(cfn) = newValue;
SET_DIRTY();
updateSpecialFunctionOneWindow();
});
gvarchoice->setTextHandler([](int32_t value) {
return std::string(getSourceString(value + MIXSRC_FIRST_GVAR));
});
line = specialFunctionOneWindow->newLine(grid);
case FUNC_SET_SCREEN:
CFN_PARAM(cfn) = (int16_t)max(CFN_PARAM(cfn), (int16_t)1);
CFN_PARAM(cfn) = (int16_t)min(
CFN_PARAM(cfn), (int16_t)ViewMain::instance()->getMainViewsCount());
addNumberEdit(line, STR_VALUE, cfn, 1,
ViewMain::instance()->getMainViewsCount());
break;
new StaticText(line, rect_t{}, STR_MODE);
auto modechoice = new Choice(line, rect_t{}, FUNC_ADJUST_GVAR_CONSTANT,
FUNC_ADJUST_GVAR_INCDEC,
GET_DEFAULT(CFN_GVAR_MODE(cfn)),
[=](int32_t newValue) {
CFN_GVAR_MODE(cfn) = newValue;
CFN_PARAM(cfn) = 0;
SET_DIRTY();
updateSpecialFunctionOneWindow();
});
line = specialFunctionOneWindow->newLine(grid);
case FUNC_ADJUST_GVAR: {
if (validateSFGV(cfn)) SET_DIRTY();
new StaticText(line, rect_t{}, STR_GLOBALVAR);
auto gvarchoice = new Choice(line, rect_t{}, 0, MAX_GVARS - 1,
GET_DEFAULT(CFN_GVAR_INDEX(cfn)),
[=](int32_t newValue){
CFN_GVAR_INDEX(cfn) = newValue;
SET_DIRTY();
updateSpecialFunctionOneWindow();
});
gvarchoice->setTextHandler([](int32_t value) {
return std::string(getSourceString(value + MIXSRC_FIRST_GVAR));
});
line = specialFunctionOneWindow->newLine(grid);
modechoice->setTextHandler([](int32_t value) {
switch (value) {
case FUNC_ADJUST_GVAR_CONSTANT:
return std::string(STR_CONSTANT);
case FUNC_ADJUST_GVAR_SOURCE:
return std::string(STR_MIXSOURCE);
case FUNC_ADJUST_GVAR_SOURCERAW:
return std::string(STR_MIXSOURCERAW);
case FUNC_ADJUST_GVAR_GVAR:
return std::string(STR_GLOBALVAR);
case FUNC_ADJUST_GVAR_INCDEC:
return std::string(STR_INCDEC);
}
return std::string("---");
});
new StaticText(line, rect_t{}, STR_MODE);
auto modechoice = new Choice(line, rect_t{}, FUNC_ADJUST_GVAR_CONSTANT,
FUNC_ADJUST_GVAR_INCDEC,
GET_DEFAULT(CFN_GVAR_MODE(cfn)),
[=](int32_t newValue) {
CFN_GVAR_MODE(cfn) = newValue;
CFN_PARAM(cfn) = 0;
SET_DIRTY();
updateSpecialFunctionOneWindow();
});
line = specialFunctionOneWindow->newLine(grid);
modechoice->setTextHandler([](int32_t value) {
switch (value) {
case FUNC_ADJUST_GVAR_CONSTANT:
return std::string(STR_CONSTANT);
switch (CFN_GVAR_MODE(cfn)) {
case FUNC_ADJUST_GVAR_CONSTANT: {
int16_t val_min, val_max;
getMixSrcRange(CFN_GVAR_INDEX(cfn) + MIXSRC_FIRST_GVAR, val_min,
val_max);
addNumberEdit(line, STR_CONSTANT, cfn, val_min, val_max);
break;
}
case FUNC_ADJUST_GVAR_SOURCE:
return std::string(STR_MIXSOURCE);
case FUNC_ADJUST_GVAR_SOURCERAW:
return std::string(STR_MIXSOURCERAW);
case FUNC_ADJUST_GVAR_GVAR:
return std::string(STR_GLOBALVAR);
case FUNC_ADJUST_GVAR_INCDEC:
return std::string(STR_INCDEC);
}
return std::string("---");
});
switch (CFN_GVAR_MODE(cfn)) {
case FUNC_ADJUST_GVAR_CONSTANT: {
int16_t val_min, val_max;
getMixSrcRange(CFN_GVAR_INDEX(cfn) + MIXSRC_FIRST_GVAR, val_min,
val_max);
addNumberEdit(line, STR_CONSTANT, cfn, val_min, val_max);
break;
}
case FUNC_ADJUST_GVAR_SOURCE:
case FUNC_ADJUST_GVAR_SOURCERAW:
addSourceChoice(line, STR_MIXSOURCE, cfn, MIXSRC_LAST_CH);
break;
case FUNC_ADJUST_GVAR_GVAR: {
new StaticText(line, rect_t{}, STR_GLOBALVAR);
auto gvarchoice = new Choice(line, rect_t{}, 0, MAX_GVARS - 1,
GET_SET_DEFAULT(CFN_PARAM(cfn)));
gvarchoice->setTextHandler([](int32_t value) {
return std::string(getSourceString(value + MIXSRC_FIRST_GVAR));
});
gvarchoice->setAvailableHandler(
[=](int value) { return CFN_GVAR_INDEX(cfn) != value; });
break;
}
case FUNC_ADJUST_GVAR_INCDEC: {
int16_t val_min, val_max;
getMixSrcRange(CFN_GVAR_INDEX(cfn) + MIXSRC_FIRST_GVAR, val_min,
val_max);
getGVarIncDecRange(val_min, val_max);
auto numedit = addNumberEdit(line, STR_INCDEC, cfn, val_min, val_max);
numedit->setDisplayHandler([](int value) {
return formatNumberAsString(abs(value), 0, 0,
value >= 0 ? "+=" : "-=", nullptr);
});
break;
addSourceChoice(line, STR_MIXSOURCE, cfn, MIXSRC_LAST_CH);
break;
case FUNC_ADJUST_GVAR_GVAR: {
new StaticText(line, rect_t{}, STR_GLOBALVAR);
auto gvarchoice = new Choice(line, rect_t{}, 0, MAX_GVARS - 1,
GET_SET_DEFAULT(CFN_PARAM(cfn)));
gvarchoice->setTextHandler([](int32_t value) {
return std::string(getSourceString(value + MIXSRC_FIRST_GVAR));
});
gvarchoice->setAvailableHandler(
[=](int value) { return CFN_GVAR_INDEX(cfn) != value; });
break;
}
case FUNC_ADJUST_GVAR_INCDEC: {
int16_t val_min, val_max;
getMixSrcRange(CFN_GVAR_INDEX(cfn) + MIXSRC_FIRST_GVAR, val_min,
val_max);
getGVarIncDecRange(val_min, val_max);
auto numedit = addNumberEdit(line, STR_INCDEC, cfn, val_min, val_max);
numedit->setDisplayHandler([](int value) {
return formatNumberAsString(abs(value), 0, 0,
value >= 0 ? "+=" : "-=", nullptr);
});
break;
}
}
}
}
}
if (HAS_REPEAT_PARAM(func)) { // !1x 1x 1s 2s 3s ...
if (HAS_REPEAT_PARAM(func)) { // !1x 1x 1s 2s 3s ...
line = specialFunctionOneWindow->newLine(grid);
new StaticText(line, rect_t{}, STR_REPEAT);
if (func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED) {
auto repeat = new Choice(line, rect_t{}, 0, 1,
GET_DEFAULT((int8_t)CFN_PLAY_REPEAT(cfn)),
SET_DEFAULT(CFN_PLAY_REPEAT(cfn)));
repeat->setTextHandler([](int32_t value) {
// 0 == repeat at 50ms interval for backward compatibility
return (value == 0) ? std::string("On") : std::string("1x");
});
} else {
auto repeat = new NumberEdit(line, rect_t{}, -1, 60 / CFN_PLAY_REPEAT_MUL,
GET_DEFAULT((int8_t)CFN_PLAY_REPEAT(cfn)),
SET_DEFAULT(CFN_PLAY_REPEAT(cfn)));
repeat->setDisplayHandler([](int32_t value) {
if (value == 0)
return std::string("1x");
else if (value == (int8_t)CFN_PLAY_REPEAT_NOSTART)
return std::string("!1x");
else {
return formatNumberAsString(value * CFN_PLAY_REPEAT_MUL, 0, 0,
nullptr, "s");
}
});
}
}
line = specialFunctionOneWindow->newLine(grid);
new StaticText(line, rect_t{}, STR_REPEAT);
if (func == FUNC_PLAY_SCRIPT || func == FUNC_RGB_LED) {
auto repeat = new Choice(line, rect_t{}, 0, 1,
GET_DEFAULT((int8_t)CFN_PLAY_REPEAT(cfn)),
SET_DEFAULT(CFN_PLAY_REPEAT(cfn)));
repeat->setTextHandler([](int32_t value) {
// 0 == repeat at 50ms interval for backward compatibility
return (value == 0) ? std::string("On") : std::string("1x");
});
} else {
auto repeat = new NumberEdit(line, rect_t{}, -1, 60 / CFN_PLAY_REPEAT_MUL,
GET_DEFAULT((int8_t)CFN_PLAY_REPEAT(cfn)),
SET_DEFAULT(CFN_PLAY_REPEAT(cfn)));
repeat->setDisplayHandler([](int32_t value) {
if (value == 0)
return std::string("1x");
else if (value == (int8_t)CFN_PLAY_REPEAT_NOSTART)
return std::string("!1x");
else {
return formatNumberAsString(value * CFN_PLAY_REPEAT_MUL, 0, 0,
nullptr, "s");
}
});
}
new StaticText(line, rect_t{}, STR_ENABLE);
new ToggleSwitch(line, rect_t{}, GET_DEFAULT(CFN_ACTIVE(cfn)),
[=](int newValue) {
CFN_ACTIVE(cfn) = newValue;
SET_DIRTY();
if (CFN_FUNC(cfn) == FUNC_PLAY_SCRIPT || CFN_FUNC(cfn) == FUNC_RGB_LED)
LUA_LOAD_MODEL_SCRIPTS();
});
}
line = specialFunctionOneWindow->newLine(grid);
new StaticText(line, rect_t{}, STR_ENABLE);
new ToggleSwitch(line, rect_t{}, GET_DEFAULT(CFN_ACTIVE(cfn)),
[=](int newValue) {
CFN_ACTIVE(cfn) = newValue;
SET_DIRTY();
if (CFN_FUNC(cfn) == FUNC_PLAY_SCRIPT || CFN_FUNC(cfn) == FUNC_RGB_LED)
LUA_LOAD_MODEL_SCRIPTS();
});
}
void buildHeader(Window *window, const char *title, const char *prefix)
{
header->setTitle(title);
headerSF = header->setTitle2(prefix + std::to_string(index + 1));
void FunctionEditPage::buildBody(Window *form)
{
form->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_LARGE);
FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY);
CustomFunctionData *cfn = customFunctionData();
// Switch
auto line = form->newLine(grid);
new StaticText(line, rect_t{}, STR_SF_SWITCH);
auto switchChoice = new SwitchChoice(line, rect_t{}, SWSRC_FIRST, SWSRC_LAST,
GET_SET_DEFAULT(CFN_SWITCH(cfn)));
switchChoice->setAvailableHandler(
[=](int value) { return isSwitchAvailable(value); });
// Patch function in case not available
if (!isAssignableFunctionAvailable(CFN_FUNC(cfn))) {
auto func = 0;
while (!isAssignableFunctionAvailable(func) && (func < FUNC_MAX - 1)) {
func++;
}
if (func < FUNC_MAX - 1) {
CFN_FUNC(cfn) = func;
}
etx_txt_color(headerSF->getLvObj(), COLOR_THEME_ACTIVE_INDEX,
ETX_STATE_SF_ACTIVE);
etx_font(headerSF->getLvObj(), FONT_BOLD_INDEX, ETX_STATE_SF_ACTIVE);
}
// Function
line = form->newLine(grid);
new StaticText(line, rect_t{}, STR_FUNC);
auto functionChoice =
new Choice(line, rect_t{}, 0, FUNC_MAX - 1, GET_DEFAULT(getFuncSortIdx(CFN_FUNC(cfn))),
[=](int32_t newValue) {
Functions newFunc = cfn_sorted[newValue];
// If changing from Lua script then reload to remove old reference
if ((CFN_FUNC(cfn) == FUNC_PLAY_SCRIPT || CFN_FUNC(cfn) == FUNC_RGB_LED) && newFunc != FUNC_PLAY_SCRIPT && newFunc != FUNC_RGB_LED)
LUA_LOAD_MODEL_SCRIPTS();
CFN_FUNC(cfn) = newFunc;
CFN_RESET(cfn);
SET_DIRTY();
updateSpecialFunctionOneWindow();
});
functionChoice->setTextHandler([=](int val) { return funcGetLabel(cfn_sorted[val]); });
functionChoice->setAvailableHandler(
[=](int value) { return isAssignableFunctionAvailable(cfn_sorted[value]); });
void buildBody(Window *form)
{
form->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_LARGE);
specialFunctionOneWindow = new Window(form, rect_t{});
updateSpecialFunctionOneWindow();
}
FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY);
CustomFunctionData *cfn = customFunctionData();
// Switch
auto line = form->newLine(grid);
new StaticText(line, rect_t{}, STR_SF_SWITCH);
auto switchChoice = new SwitchChoice(line, rect_t{}, SWSRC_FIRST, SWSRC_LAST,
GET_SET_DEFAULT(CFN_SWITCH(cfn)));
switchChoice->setAvailableHandler(
[=](int value) { return isSwitchAvailable(value); });
// Patch function in case not available
if (!isAssignableFunctionAvailable(CFN_FUNC(cfn))) {
auto func = 0;
while (!isAssignableFunctionAvailable(func) && (func < FUNC_MAX - 1)) {
func++;
}
if (func < FUNC_MAX - 1) {
CFN_FUNC(cfn) = func;
}
}
// Function
line = form->newLine(grid);
new StaticText(line, rect_t{}, STR_FUNC);
auto functionChoice =
new Choice(line, rect_t{}, 0, FUNC_MAX - 1, GET_DEFAULT(getFuncSortIdx(CFN_FUNC(cfn))),
[=](int32_t newValue) {
Functions newFunc = cfn_sorted[newValue];
// If changing from Lua script then reload to remove old reference
if ((CFN_FUNC(cfn) == FUNC_PLAY_SCRIPT || CFN_FUNC(cfn) == FUNC_RGB_LED) && newFunc != FUNC_PLAY_SCRIPT && newFunc != FUNC_RGB_LED)
LUA_LOAD_MODEL_SCRIPTS();
CFN_FUNC(cfn) = newFunc;
CFN_RESET(cfn);
SET_DIRTY();
updateSpecialFunctionOneWindow();
});
functionChoice->setTextHandler([=](int val) { return funcGetLabel(cfn_sorted[val]); });
functionChoice->setAvailableHandler(
[=](int value) { return isAssignableFunctionAvailable(cfn_sorted[value]); });
specialFunctionOneWindow = new Window(form, rect_t{});
updateSpecialFunctionOneWindow();
}
};
//-----------------------------------------------------------------------------
@@ -929,7 +942,7 @@ class SpecialFunctionLineButton : public FunctionLineButton
protected:
bool isActive() const override
{
return modelFunctionsContext.activeSwitches & ((MASK_CFN_TYPE)1 << index);
return modelFunctionsContext.isFunctionSwitchActive(index);
}
};
@@ -947,7 +960,7 @@ class SpecialFunctionEditPage : public FunctionEditPage
protected:
bool isActive() const override
{
return modelFunctionsContext.activeSwitches & ((MASK_CFN_TYPE)1 << index);
return modelFunctionsContext.isFunctionSwitchActive(index);
}
bool isSwitchAvailable(int value) const override
@@ -1009,7 +1022,7 @@ class GlobalFunctionLineButton : public FunctionLineButton
protected:
bool isActive() const override
{
return globalFunctionsContext.activeSwitches & ((MASK_CFN_TYPE)1 << index);
return globalFunctionsContext.isFunctionSwitchActive(index);
}
};
@@ -1027,7 +1040,7 @@ class GlobalFunctionEditPage : public FunctionEditPage
protected:
bool isActive() const override
{
return globalFunctionsContext.activeSwitches & ((MASK_CFN_TYPE)1 << index);
return globalFunctionsContext.isFunctionSwitchActive(index);
}
bool isSwitchAvailable(int value) const override
@@ -28,7 +28,6 @@
struct CustomFunctionData;
class FunctionEditPage;
class FunctionLineButton;
class NumberEdit;
//-----------------------------------------------------------------------------
@@ -79,43 +78,6 @@ class FunctionLineButton : public ListLineButton
//-----------------------------------------------------------------------------
class FunctionEditPage : public Page
{
public:
FunctionEditPage(uint8_t index, EdgeTxIcon icon, const char *title,
const char *prefix);
void delayedInit() override;
protected:
uint8_t index;
Window *specialFunctionOneWindow = nullptr;
StaticText *headerSF = nullptr;
bool active = false;
virtual bool isActive() const = 0;
virtual bool isSwitchAvailable(int value) const = 0;
virtual CustomFunctionData *customFunctionData() const = 0;
virtual bool isAssignableFunctionAvailable(int function) const = 0;
virtual void setDirty() const = 0;
void checkEvents() override;
void buildHeader(Window *window, const char *title, const char *prefix);
void addSourceChoice(FormLine *line, const char *title,
CustomFunctionData *cfn, int16_t vmax);
NumberEdit *addNumberEdit(FormLine *line, const char *title,
CustomFunctionData *cfn, int16_t vmin, int16_t vmax);
void updateSpecialFunctionOneWindow();
void buildBody(Window *form);
};
//-----------------------------------------------------------------------------
class FunctionsPage : public PageGroupItem
{
public:
@@ -29,8 +29,6 @@
#include "stamp.h"
#include "theme_manager.h"
extern void checkSpeakerVolume();
constexpr const char* strip_leading_hyphen(const char* str) {
return (str[0] == '-') ? str + 1 : str;
}
@@ -250,10 +250,12 @@ void CLEAR_POPUP()
popupMenuItemsCount = 0;
}
void POPUP_WAIT(const char * s)
void POPUP_WAIT(const char * s, const char * info)
{
warningText = s;
warningInfoText = nullptr;
warningInfoText = info;
warningInfoLength = info ? strlen(info) : 0;
warningInfoFlags = 0;
warningType = WARNING_TYPE_WAIT;
popupFunc = runPopupWarning;
}
+2 -2
View File
@@ -82,7 +82,7 @@ typedef void (* ProgressHandler)(const char *, const char *, int, int);
#if !defined(GUI)
#define DISPLAY_WARNING(...)
inline void POPUP_WAIT(const char * s) { }
inline void POPUP_WAIT(const char * s, const char * = nullptr) { }
inline void POPUP_WARNING(const char *, const char * = nullptr) { }
inline void POPUP_CONFIRMATION(const char * s, PopupMenuHandler handler) { }
inline void POPUP_INPUT(const char * s, PopupFunc func) { }
@@ -90,7 +90,7 @@ typedef void (* ProgressHandler)(const char *, const char *, int, int);
#else
#define DISPLAY_WARNING(evt) (*popupFunc)(evt)
extern void CLEAR_POPUP();
extern void POPUP_WAIT(const char * s);
extern void POPUP_WAIT(const char * s, const char * = nullptr);
extern void POPUP_INFORMATION(const char * s);
extern void POPUP_WARNING(const char * message, const char * info = nullptr);
extern void SET_WARNING_INFO(const char * info, uint8_t length, uint8_t flags);
+4
View File
@@ -662,6 +662,10 @@ bool isAssignableFunctionAvailable(int function, bool modelFunctions)
case FUNC_PUSH_CUST_SWITCH:
return modelFunctions;
#endif
#if defined(KEYS_LOCK_KEY1) && defined(KEYS_LOCK_KEY2)
case FUNC_DISABLE_KEYS:
return g_eeGeneral.keyLockEnabled;
#endif
default:
return true;