New flight modes page
This commit is contained in:
committed by
Raphael Coeffic
parent
847ae4aeb1
commit
93919da945
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user