New flight modes page

This commit is contained in:
Raphael Coeffic
2022-06-11 15:38:08 +02:00
committed by Raphael Coeffic
parent 847ae4aeb1
commit 93919da945
+262 -182
View File
@@ -27,206 +27,286 @@
#define LABEL_RIGHT_MARGIN 5
ModelFlightModesPage::ModelFlightModesPage():
PageTab(STR_MENUFLIGHTMODES, ICON_MODEL_FLIGHT_MODES)
{
}
bool isTrimModeAvailable(int mode)
{
return (mode < 0 || (mode%2) == 0 || (mode/2) != 0); //ToDo menuVerticalPosition
}
class FlightModeGroup: public FormGroup
{
public:
FlightModeGroup(Window * parent, uint8_t index, const rect_t & rect) :
FormGroup(parent, rect),
index(index)
{
setFocusHandler([=] (bool focus) {
setSubWindowColors(focus);
});
}
void checkEvents() override
{
FormGroup::checkEvents();
bool newActive = (getFlightMode() == index);
if (newActive != active) {
active = newActive;
invalidate();
}
}
void paint(BitmapBuffer * dc) override
{
uint32_t fillColor = getFocus() != this ? COLOR_THEME_SECONDARY2 : COLOR_THEME_FOCUS;
dc->drawSolidFilledRect(0, 0, width(), height(), fillColor);
FormGroup::paint(dc);
}
void setSubWindowColors(bool focus)
{
int32_t color = focus ? COLOR_THEME_PRIMARY2 : COLOR_THEME_PRIMARY1;
for (auto window: getChildren()) {
if (dynamic_cast<StaticText *>(window) != nullptr) {
LcdFlags flags = dynamic_cast<Subtitle *>(window) == nullptr ? RIGHT : 0;
window->setTextFlags(flags | color);
}
}
}
protected:
uint8_t index;
bool active = false;
struct FlightModeEdit : public Page {
FlightModeEdit(uint8_t index);
};
rect_t getRightAlignedLabelSlot(FormGridLayout &grid)
static const lv_coord_t line_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1),
LV_GRID_TEMPLATE_LAST};
static const lv_coord_t line_row_dsc[] = {LV_GRID_CONTENT,
LV_GRID_TEMPLATE_LAST};
static const lv_coord_t trims_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(2), LV_GRID_FR(2),
LV_GRID_TEMPLATE_LAST};
static const lv_coord_t trims_row_dsc[] = {LV_GRID_CONTENT,
LV_GRID_CONTENT,
LV_GRID_CONTENT,
LV_GRID_TEMPLATE_LAST};
static void btn_changed(lv_event_t* e)
{
auto rect = grid.getLabelSlot();
rect.w -= LABEL_RIGHT_MARGIN;
return rect;
auto target = lv_event_get_target(e);
auto btn = (Button*)lv_obj_get_user_data(target);
auto obj = (lv_obj_t*)lv_event_get_user_data(e);
if (!btn->checked()) {
lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN);
} else {
lv_obj_clear_flag(obj, LV_OBJ_FLAG_HIDDEN);
lv_event_send(obj, LV_EVENT_VALUE_CHANGED, nullptr);
}
}
rect_t getRightAlignedFieldSlot(FormGridLayout &grid, uint8_t count, uint8_t index)
static void make_conditional(Window* w, Button* btn)
{
auto rect = grid.getFieldSlot(count, index);
rect.w -= LABEL_RIGHT_MARGIN;
return rect;
lv_obj_t* w_obj = w->getLvObj();
if (!btn->checked()) { lv_obj_add_flag(w_obj, LV_OBJ_FLAG_HIDDEN); }
lv_obj_t* btn_obj = btn->getLvObj();
lv_obj_add_event_cb(btn_obj, btn_changed, LV_EVENT_CLICKED, w_obj);
}
static std::string getFMTrimStr(uint8_t mode)
{
char str[4];
mode &= 0x1F;
if (mode & 1) str[0] = '+';
else str[0] = '=';
str[1] = ' ';
mode >>= 1;
if (mode > MAX_FLIGHT_MODES - 1) mode = MAX_FLIGHT_MODES - 1;
str[2] = '0' + mode;
str[3] = '\0';
return str;
}
FlightModeEdit::FlightModeEdit(uint8_t index) :
Page(ICON_MODEL_FLIGHT_MODES)
{
std::string title =
std::string(TR_MENUFLIGHTMODE) + " " + std::to_string(index);
new StaticText(&header,
{PAGE_TITLE_LEFT, PAGE_TITLE_TOP, LCD_W - PAGE_TITLE_LEFT,
PAGE_LINE_HEIGHT},
title, 0, COLOR_THEME_PRIMARY2);
auto form = new FormWindow(&body, rect_t{0, 0, body.width(), body.height()},
NO_FOCUS | FORM_FORWARD_FOCUS);
form->setFlexLayout();
FlexGridLayout grid(line_col_dsc, line_row_dsc, 2);
FlightModeData* p_fm = &g_model.flightModeData[index];
// Flight mode name
auto line = form->newLine(&grid);
new StaticText(line, rect_t{}, STR_NAME, 0, COLOR_THEME_PRIMARY1);
new ModelTextEdit(line, rect_t{}, p_fm->name, LEN_FLIGHT_MODE_NAME);
if (index > 0) {
// Switch
line = form->newLine(&grid);
new StaticText(line, rect_t{}, STR_SWITCH, 0, COLOR_THEME_PRIMARY1);
new SwitchChoice(line, rect_t{}, SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES,
GET_SET_DEFAULT(p_fm->swtch));
}
FlexGridLayout trim_grid(trims_col_dsc, trims_row_dsc);
line = form->newLine(&trim_grid);
new StaticText(line, rect_t{}, STR_TRIMS, 0, COLOR_THEME_PRIMARY1);
for (int t = 0; t < NUM_TRIMS; t++) {
auto trim_obj = window_create(line->getLvObj());
auto trim = new FormGroup::Line(line, trim_obj, nullptr, form);
lv_obj_set_layout(trim_obj, LV_LAYOUT_FLEX);
lv_obj_set_style_pad_column(trim_obj, 4, 0);
lv_obj_set_style_pad_bottom(trim_obj, 8, 0);
lv_obj_set_style_flex_cross_place(trim_obj, LV_FLEX_ALIGN_CENTER, 0);
trim_t* tr = &p_fm->trim[t];
auto btn = new TextButton(
trim, rect_t{}, getSourceString(MIXSRC_FIRST_TRIM + t),
[=]() {
if (tr->mode == TRIM_MODE_NONE) {
tr->mode = 0;
SET_DIRTY();
return 1;
} else {
tr->mode = TRIM_MODE_NONE;
SET_DIRTY();
return 0;
}
},
0, COLOR_THEME_PRIMARY1);
if (tr->mode != TRIM_MODE_NONE) btn->check();
btn->setWidth(LV_DPI_DEF / 2);
btn->setHeight(LV_DPI_DEF / 4);
auto tr_mode = new Choice(trim, rect_t{}, 0, 2*MAX_FLIGHT_MODES - 1,
GET_SET_DEFAULT(tr->mode));
tr_mode->setTextHandler(getFMTrimStr);
// show trim value choice iff btn->checked()
make_conditional(tr_mode, btn);
if (t % 2 == 1) {
trim_grid.nextCell();
}
}
// Fade in
line = form->newLine(&grid);
new StaticText(line, rect_t{}, STR_FADEIN, 0, COLOR_THEME_PRIMARY1);
new NumberEdit(line, rect_t{}, 0, DELAY_MAX, GET_DEFAULT(p_fm->fadeIn),
SET_VALUE(p_fm->fadeIn, newValue), 0, PREC1);
// Fade out
line = form->newLine(&grid);
new StaticText(line, rect_t{}, STR_FADEOUT, 0, COLOR_THEME_PRIMARY1);
new NumberEdit(line, rect_t{}, 0, DELAY_MAX, GET_DEFAULT(p_fm->fadeOut),
SET_VALUE(p_fm->fadeOut, newValue), 0, PREC1);
}
class FlightModeBtn: public Button
{
uint8_t index;
bool active = false;
public:
FlightModeBtn(Window * parent, uint8_t index);
void checkEvents() override;
void paint(BitmapBuffer* dc) override;
bool isActive() { return active; }
void onPress() override;
};
static void fm_draw_begin(lv_event_t *e)
{
auto fmg = (FlightModeBtn*)lv_event_get_user_data(e);
if (!fmg) return;
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
if (fmg->isActive()) {
dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_ACTIVE);
} else {
dsc->rect_dsc->bg_color = makeLvColor(COLOR_THEME_PRIMARY2);
}
}
FlightModeBtn::FlightModeBtn(Window * parent, uint8_t index) :
Button(parent, rect_t{}, nullptr, 0, 0, lv_btn_create),
index(index)
{
lv_obj_add_flag(lvobj, LV_OBJ_FLAG_CLICKABLE);
lv_obj_set_style_bg_opa(lvobj, LV_OPA_100, 0);
lv_obj_set_style_grid_cell_x_align(lvobj, LV_GRID_ALIGN_STRETCH, 0);
lv_obj_set_width(lvobj, lv_pct(100));
lv_obj_set_height(lvobj, LV_DPI_DEF / 3);
lv_obj_add_event_cb(lvobj, fm_draw_begin, LV_EVENT_DRAW_PART_BEGIN, this);
}
void FlightModeBtn::checkEvents()
{
bool newActive = (getFlightMode() == index);
if (newActive != active) {
active = newActive;
invalidate();
}
}
void FlightModeBtn::paint(BitmapBuffer* dc)
{
coord_t x=8, y=0;
LcdFlags txt_flags;
if (active) {
txt_flags = FONT(BOLD) | COLOR_THEME_PRIMARY2;
} else {
txt_flags = FONT(STD) | COLOR_THEME_SECONDARY1;
}
auto h = lv_obj_get_height(lvobj);
y = (h - getFontHeight(txt_flags)) / 2;
const auto& fm = g_model.flightModeData[index];
if (fm.name[0] != '\0') {
dc->drawSizedText(x, y, fm.name, sizeof(fm.name), txt_flags);
} else {
char label[8];
getFlightModeString(label, index + 1);
dc->drawSizedText(x, y, label, sizeof(label), txt_flags);
}
if ((index > 0) && (fm.swtch != SWSRC_NONE)) {
char sw[8];
getSwitchPositionName(sw, fm.swtch);
x = lv_obj_get_width(lvobj) - 8;
dc->drawSizedText(x, y, sw, sizeof(sw), txt_flags | RIGHT);
}
}
void FlightModeBtn::onPress()
{
new FlightModeEdit(index);
}
ModelFlightModesPage::ModelFlightModesPage():
PageTab(STR_MENUFLIGHTMODES, ICON_MODEL_FLIGHT_MODES)
{
}
// FM table: 3 x 3
static const lv_coord_t fmt_col_dsc[] = {LV_GRID_FR(1),
LV_GRID_FR(1),
LV_GRID_FR(1),
LV_GRID_TEMPLATE_LAST};
static const lv_coord_t fmt_row_dsc[] = {LV_GRID_CONTENT,
LV_GRID_CONTENT,
LV_GRID_CONTENT,
LV_GRID_TEMPLATE_LAST};
void ModelFlightModesPage::build(FormWindow * window)
{
coord_t y = 2;
window->setFlexLayout();
lv_obj_t* obj = window->getLvObj();
lv_obj_set_style_flex_cross_place(obj, LV_FLEX_ALIGN_CENTER, 0);
FlexGridLayout grid(fmt_col_dsc, fmt_row_dsc, 0);
auto fm_box = window->newLine(&grid);
obj = fm_box->getLvObj();
lv_obj_set_style_pad_all(obj, 8, 0);
lv_obj_set_style_pad_row(obj, 2, 0);
lv_obj_set_style_pad_column(obj, 2, 0);
for (int i = 0; i < MAX_FLIGHT_MODES; i++) {
auto group = new FlightModeGroup(window, i, { 2, y, LCD_W - 10, 0 });
FormGridLayout grid;
grid.setMarginRight(15);
#if LCD_W > LCD_H
grid.setLabelWidth(140);
#else
grid.setLabelWidth(110);
#endif
grid.spacer();
char label[16];
getFlightModeString(label, i + 1);
new Subtitle(group, grid.getLabelSlot(), label, 0, COLOR_THEME_PRIMARY1);
FormField *a = nullptr;
std::function<uint32_t (FormField *)> backgroundHandler = [=](FormField *field) {
if (! field->isEditMode() && !field->hasFocus())
return i == getFlightMode() ? COLOR_THEME_ACTIVE : COLOR_THEME_PRIMARY2;
else if (field->hasFocus() && ! field->isEditMode())
return COLOR_THEME_FOCUS;
else
return COLOR_THEME_EDIT;
};
bool bNarrowScreen = LCD_W < LCD_H;
// Flight mode name
new StaticText(group, getRightAlignedLabelSlot(grid), STR_NAME, 0, RIGHT | COLOR_THEME_PRIMARY1);
rect_t r = grid.getFieldSlot();;
if (! bNarrowScreen) {
r = grid.getFieldSlot(3,0);
r.w += 53;
}
a = new ModelTextEdit(group, r, g_model.flightModeData[i].name, LEN_FLIGHT_MODE_NAME);
a->setBackgroundHandler(backgroundHandler);
// Flight mode switch
if (i > 0) {
if (! bNarrowScreen) {
new StaticText(group, getRightAlignedFieldSlot(grid, 3, 1), STR_SWITCH, 0, RIGHT | COLOR_THEME_PRIMARY1);
a = new SwitchChoice(group, grid.getFieldSlot(3, 2), SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(g_model.flightModeData[i].swtch));
}
else {
grid.nextLine();
new StaticText(group, getRightAlignedLabelSlot(grid), STR_SWITCH, 0, RIGHT | COLOR_THEME_PRIMARY1);
a = new SwitchChoice(group, grid.getFieldSlot(), SWSRC_FIRST_IN_MIXES, SWSRC_LAST_IN_MIXES, GET_SET_DEFAULT(g_model.flightModeData[i].swtch));
}
a->setBackgroundHandler(backgroundHandler);
grid.nextLine();
}
else {
grid.nextLine();
}
// Flight mode trims
new StaticText(group, getRightAlignedLabelSlot(grid), STR_TRIMS, 0, RIGHT | COLOR_THEME_PRIMARY1);
const char * STR_VTRIMS_MODES = "\002"
"--"
"=0"
"+0"
"=1"
"+1"
"=2"
"+2"
"=3"
"+3"
"=4"
"+4"
"=5"
"+5"
"=6"
"+6"
"=7"
"+7"
"=8"
"+8";
for (int t = 0; t < NUM_TRIMS; t++) {
// TODO isTrimModeAvailable to avoid +{{CURRENT}}
a = new Choice(group, grid.getFieldSlot(NUM_TRIMS, t), STR_VTRIMS_MODES, -1, 2*MAX_FLIGHT_MODES-1,
GET_DEFAULT(g_model.flightModeData[i].trim[t].mode==TRIM_MODE_NONE ? -1 : g_model.flightModeData[i].trim[t].mode),
SET_DEFAULT(g_model.flightModeData[i].trim[t].mode));
a->setBackgroundHandler(backgroundHandler);
}
grid.nextLine();
// Flight mode fade in / out
new StaticText(group, getRightAlignedLabelSlot(grid), "Fade in/out", 0, RIGHT | COLOR_THEME_PRIMARY1);
a = new NumberEdit(group, grid.getFieldSlot(2, 0), 0, DELAY_MAX,
GET_DEFAULT(g_model.flightModeData[i].fadeIn),
SET_VALUE(g_model.flightModeData[i].fadeIn, newValue),
0, PREC1);
a->setBackgroundHandler(backgroundHandler);
a = new NumberEdit(group, grid.getFieldSlot(2, 1), 0, DELAY_MAX,
GET_DEFAULT(g_model.flightModeData[i].fadeOut),
SET_VALUE(g_model.flightModeData[i].fadeOut, newValue),
0, PREC1);
a->setBackgroundHandler(backgroundHandler);
grid.nextLine();
grid.spacer();
coord_t height = grid.getWindowHeight();
group->setHeight(height);
y += height + 2;
if (i == 0)
group->setSubWindowColors(true);
new FlightModeBtn(fm_box, i);
}
char label[32];
sprintf(label, "Check FM Trims");
// TODO rather use a centered slot?
// TODO dynamic text with FM index for the button text
new TextButton(window, { 60, y + 5, LCD_W - 120, PAGE_LINE_HEIGHT }, label,
[&]() -> uint8_t {
if (trimsCheckTimer)
trimsCheckTimer = 0;
else
trimsCheckTimer = 200; // 2 seconds trims cancelled
return trimsCheckTimer;
});
new TextButton(window, rect_t{}, "Check FM Trims", [&]() -> uint8_t {
if (trimsCheckTimer)
trimsCheckTimer = 0;
else
trimsCheckTimer = 200; // 2 seconds trims cancelled
return trimsCheckTimer;
});
}