From 4e09bd7b8c6a1382e9b85aaad2fca969ed4798df Mon Sep 17 00:00:00 2001 From: philmoz Date: Sun, 5 Feb 2023 12:31:32 +1100 Subject: [PATCH] chore(color): LVGLify custom mixer scripts page (#3071) * LVGL flex conversion for custom mixer scripts. * Adjust spacing for portrait layout and widest text value. --- .../src/gui/colorlcd/model_mixer_scripts.cpp | 225 +++++++++++------- 1 file changed, 136 insertions(+), 89 deletions(-) diff --git a/radio/src/gui/colorlcd/model_mixer_scripts.cpp b/radio/src/gui/colorlcd/model_mixer_scripts.cpp index d17e3d5db..4cb1d1d96 100644 --- a/radio/src/gui/colorlcd/model_mixer_scripts.cpp +++ b/radio/src/gui/colorlcd/model_mixer_scripts.cpp @@ -28,11 +28,31 @@ #include "translations.h" #include "menus.h" #include "libopenui.h" - -#include +#include "lvgl_widgets/input_mix_line.h" #define SET_DIRTY() storageDirty(EE_MODEL) +// Overview grid +static const lv_coord_t col_dsc[] = {LV_GRID_CONTENT, + LV_GRID_TEMPLATE_LAST}; + +// Edit grid +#if LCD_W > LCD_H +static const lv_coord_t e_col_dsc[] = {LV_GRID_FR(2), LV_GRID_FR(3), + LV_GRID_TEMPLATE_LAST}; +#else +static const lv_coord_t e_col_dsc[] = {LV_GRID_FR(5), LV_GRID_FR(4), + LV_GRID_TEMPLATE_LAST}; +#endif + +// Line button grid +static const lv_coord_t b_col_dsc[] = {40, 84, 84, LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST +}; + +static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, + LV_GRID_TEMPLATE_LAST}; + class ScriptEditWindow : public Page { public: explicit ScriptEditWindow(uint8_t idx) : @@ -69,17 +89,21 @@ class ScriptEditWindow : public Page { void buildBody(FormWindow * window, bool focusScript = false) { - FormGridLayout grid; - grid.spacer(PAGE_PADDING); + auto form = new FormWindow(window, rect_t{}); + form->setFlexLayout(); + form->padAll(4); + + FlexGridLayout grid(e_col_dsc, row_dsc, 2); // the general pattern seems to be using capture-by-value for the closures: so need to copy the pointers, not the objects ScriptData* const sd = &(g_model.scriptsData[idx]); ScriptInputsOutputs* const sio = &(scriptInputsOutputs[idx]); - bool* const updatePtr = &update; - new StaticText(window, grid.getLabelSlot(), STR_SCRIPT, 0, COLOR_THEME_PRIMARY1); + // File + auto line = form->newLine(&grid); + new StaticText(line, rect_t{}, STR_SCRIPT, 0, COLOR_THEME_PRIMARY1); auto fc = new FileChoice( - window, grid.getFieldSlot(), SCRIPTS_MIXES_PATH, SCRIPTS_EXT, + line, rect_t{}, SCRIPTS_MIXES_PATH, SCRIPTS_EXT, LEN_SCRIPT_FILENAME, [=]() { return stringFromNtString(sd->file); }, [=](std::string newValue) { @@ -90,61 +114,48 @@ class ScriptEditWindow : public Page { } storageDirty(EE_MODEL); LUA_LOAD_MODEL_SCRIPT(idx); // async reload ... - *updatePtr = true; // TODO: sets update==true and afterwards calls rebuildBody() via checkEvents() + update = true; }, true); - grid.nextLine(); // Custom name - new StaticText(window, grid.getLabelSlot(), STR_NAME, 0, COLOR_THEME_PRIMARY1); - new ModelTextEdit(window, grid.getFieldSlot(), sd->name, sizeof(sd->name)); - grid.nextLine(); + line = form->newLine(&grid); + new StaticText(line, rect_t{}, STR_NAME, 0, COLOR_THEME_PRIMARY1); + new ModelTextEdit(line, rect_t{}, sd->name, sizeof(sd->name)); if (sio->inputsCount > 0) { - new Subtitle(window, grid.getLineSlot(), STR_INPUTS, 0, COLOR_THEME_PRIMARY1); - grid.nextLine(); - - auto gInputs = new FormGroup(window, grid.getFieldSlot(), FORM_BORDER_FOCUS_ONLY | PAINT_CHILDREN_FIRST); - GridLayout inputsGrid(gInputs); + line = form->newLine(&grid); + new Subtitle(line, rect_t{}, STR_INPUTS, 0, COLOR_THEME_PRIMARY1); for (int i = 0; i < sio->inputsCount; i++) { + line = form->newLine(&grid); ScriptInput& si = sio->inputs[i]; - new StaticText(window, grid.getLabelSlot(true), si.name, 0, COLOR_THEME_PRIMARY1); - grid.nextLine(); + auto lbl = new StaticText(line, rect_t{}, si.name, 0, COLOR_THEME_PRIMARY1); + lbl->padLeft(10); if (si.type == INPUT_TYPE_VALUE) { - (new NumberEdit(gInputs, inputsGrid.getSlot(), si.min, si.max, + (new NumberEdit(line, rect_t{}, si.min, si.max, GET_SET_WITH_OFFSET(sd->inputs[i].value, si.def)))->setDefault(si.def); } else { - new SourceChoice(gInputs, inputsGrid.getSlot(), 0, - MIXSRC_LAST_TELEM, + new SourceChoice(line, rect_t{}, 0, MIXSRC_LAST_TELEM, GET_SET_DEFAULT(sd->inputs[i].source)); } - inputsGrid.nextLine(); } - gInputs->setHeight(inputsGrid.getWindowHeight()); } if (sio->outputsCount > 0) { - new Subtitle(window, grid.getLabelSlot(), STR_OUTPUTS, 0, COLOR_THEME_PRIMARY1); - grid.nextLine(); - - auto gOutputs = - new FormGroup(window, grid.getLineSlot(), - FORM_BORDER_FOCUS_ONLY | PAINT_CHILDREN_FIRST); - FormGridLayout outputsGrid(gOutputs->width()); + line = form->newLine(&grid); + new Subtitle(line, rect_t{}, STR_OUTPUTS, 0, COLOR_THEME_PRIMARY1); for (int i = 0; i < sio->outputsCount; i++) { + line = form->newLine(&grid); ScriptOutput* so = &(sio->outputs[i]); - new DynamicText(gOutputs, outputsGrid.getLabelSlot(), [=]() { + auto lbl = new DynamicText(line, rect_t{}, [=]() { char s[16]; getSourceString(s, MIXSRC_FIRST_LUA + (idx * MAX_SCRIPT_OUTPUTS) + i); return std::string(s, sizeof(s) - 1); }, COLOR_THEME_PRIMARY1); - new DynamicNumber(gOutputs, outputsGrid.getFieldSlot(), [=]() { return calcRESXto1000(so->value); }, COLOR_THEME_PRIMARY1); - - outputsGrid.nextLine(); + lbl->padLeft(10); + new DynamicNumber(line, rect_t{}, [=]() { return calcRESXto1000(so->value); }, COLOR_THEME_PRIMARY1); } - gOutputs->setHeight(outputsGrid.getWindowHeight()); - grid.addWindow(gOutputs); } } @@ -160,61 +171,112 @@ class ScriptEditWindow : public Page { class ScriptLineButton : public Button { public: - ScriptLineButton(FormGroup* parent, const rect_t& rect, + ScriptLineButton(Window* parent, const rect_t& rect, const ScriptData& scriptData, - const ScriptInternalData* runtimeData) : - Button(parent, rect), scriptData(scriptData), runtimeData(runtimeData) + const ScriptInternalData* runtimeData, + uint8_t index) : + Button(parent, rect, nullptr, 0, 0, input_mix_line_create), + scriptData(scriptData), + runtimeData(runtimeData), + index(index) { +#if LCD_H > LCD_W + padTop(5); +#endif + padLeft(3); + padRight(3); + lv_obj_set_layout(lvobj, LV_LAYOUT_GRID); + lv_obj_set_grid_dsc_array(lvobj, b_col_dsc, row_dsc); + lv_obj_set_style_pad_row(lvobj, 0, 0); + lv_obj_set_style_pad_column(lvobj, 4, 0); + + lv_obj_update_layout(parent->getLvObj()); + if(lv_obj_is_visible(lvobj)) delayed_init(nullptr); + + lv_obj_add_event_cb(lvobj, ScriptLineButton::on_draw, LV_EVENT_DRAW_MAIN_BEGIN, nullptr); } - void paint(BitmapBuffer* dc) override + static void on_draw(lv_event_t * e) { - LcdFlags textColor = COLOR_THEME_SECONDARY1; - LcdFlags bgColor = COLOR_THEME_PRIMARY2; + lv_obj_t* target = lv_event_get_target(e); + auto line = (ScriptLineButton*)lv_obj_get_user_data(target); + if (line) { + if (!line->init) + line->delayed_init(e); + else + line->refresh(); + } + } + + void delayed_init(lv_event_t* e) + { + auto lbl = lv_label_create(lvobj); + lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lbl, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_CENTER, 0, 1); - dc->drawSolidFilledRect(0, 0, width(), height(), bgColor); + lv_label_set_text(lbl, (std::string("LUA") + std::to_string(index + 1)).c_str()); if (runtimeData) { - coord_t x = 2*FIELD_PADDING_LEFT; - coord_t y = FIELD_PADDING_TOP; + char s[20]; - x = dc->drawSizedText(x, y, scriptData.name, sizeof(scriptData.name), textColor); - x += 4*FIELD_PADDING_LEFT; + lbl = lv_label_create(lvobj); + lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lbl, LV_GRID_ALIGN_START, 1, 1, LV_GRID_ALIGN_CENTER, 0, 1); - dc->drawSizedText(x, y, scriptData.file, sizeof(scriptData.file), textColor); + strAppend(s, scriptData.name, LEN_SCRIPT_NAME); + lv_label_set_text(lbl, s); + + lbl = lv_label_create(lvobj); + lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lbl, LV_GRID_ALIGN_START, 2, 1, LV_GRID_ALIGN_CENTER, 0, 1); + + strAppend(s, scriptData.file, LEN_SCRIPT_FILENAME); + lv_label_set_text(lbl, s); + + lbl = lv_label_create(lvobj); + lv_obj_set_style_text_align(lbl, LV_TEXT_ALIGN_LEFT, 0); + lv_obj_set_grid_cell(lbl, LV_GRID_ALIGN_START, 3, 1, LV_GRID_ALIGN_CENTER, 0, 1); - x = width() - 2*FIELD_PADDING_LEFT; - y = FIELD_PADDING_TOP; - textColor |= RIGHT; - // TODO: runtimeData->instructions has no value switch (runtimeData->state) { case SCRIPT_SYNTAX_ERROR: - dc->drawSizedText(x, y, STR_SCRIPT_ERROR, strlen(STR_SCRIPT_ERROR), textColor); + lv_label_set_text(lbl, STR_SCRIPT_ERROR); break; - case SCRIPT_NOFILE: - dc->drawSizedText(x, y, STR_NEEDS_FILE, strlen(STR_NEEDS_FILE), textColor); + case SCRIPT_NOFILE: + lv_label_set_text(lbl, STR_NEEDS_FILE); break; - case SCRIPT_OK: - dc->drawSizedText(x, y, "-", 1, textColor); + case SCRIPT_OK: + lv_label_set_text(lbl, "-"); break; - default: + default: + lv_label_set_text(lbl, ""); break; } } - // bounding rect - if (hasFocus()) - dc->drawSolidRect(0, 0, rect.w, rect.h, 2, COLOR_THEME_FOCUS); - else - dc->drawSolidRect(0, 0, rect.w, rect.h, 1, COLOR_THEME_SECONDARY2); + init = true; + refresh(); + lv_obj_update_layout(lvobj); + + if(e) { + auto param = lv_event_get_param(e); + lv_event_send(lvobj, LV_EVENT_DRAW_MAIN, param); + } + } + + void refresh() + { } protected: + bool init = false; + uint8_t index; const ScriptData& scriptData; const ScriptInternalData* runtimeData; }; +#define CM_BUTTON_H 34 + ModelMixerScriptsPage::ModelMixerScriptsPage() : PageTab(STR_MENUCUSTOMSCRIPTS, ICON_MODEL_LUA_SCRIPTS) { @@ -230,13 +292,19 @@ void ModelMixerScriptsPage::rebuild(FormWindow * window, int8_t focusIdx) void ModelMixerScriptsPage::build(FormWindow * window, int8_t focusIdx) { - FormGridLayout grid; - grid.spacer(PAGE_PADDING); - grid.setLabelWidth(66); window->padAll(0); + auto form = new FormWindow(window, rect_t{}); + form->setFlexLayout(); + form->padAll(4); + + FlexGridLayout grid(col_dsc, row_dsc, 2); + + FormWindow::Line* line; + int8_t scriptIdx = 0; for (int8_t idx = 0; idx < MAX_SCRIPTS; idx++) { + auto line = form->newLine(&grid); ScriptInternalData* runtimeData = nullptr; ScriptData* const sd = &(g_model.scriptsData[idx]); @@ -246,12 +314,7 @@ void ModelMixerScriptsPage::build(FormWindow * window, int8_t focusIdx) runtimeData = &(scriptInternalData[scriptIdx++]); } - // LUAx label - auto const txt = new StaticText(window, grid.getLabelSlot(), - std::string("LUA") + std::to_string(idx + 1), - BUTTON_BACKGROUND, COLOR_THEME_PRIMARY1 | CENTERED); - - Button* const button = new ScriptLineButton(window, grid.getFieldSlot(), *sd, runtimeData); + Button* const button = new ScriptLineButton(line, rect_t{0, 0, window->width() - 12, CM_BUTTON_H}, *sd, runtimeData, idx); button->setPressHandler([=]() -> uint8_t { Menu* const menu = new Menu(window); @@ -269,23 +332,7 @@ void ModelMixerScriptsPage::build(FormWindow * window, int8_t focusIdx) } return 0; }); - - button->setFocusHandler([=](bool focus) { - if (focus) { - txt->setBackgroundColor(COLOR_THEME_FOCUS); - txt->setTextFlags(COLOR_THEME_PRIMARY2 | CENTERED); - } else { - txt->setBackgroundColor(COLOR_THEME_SECONDARY2); - txt->setTextFlags(COLOR_THEME_PRIMARY1 | CENTERED); - } - txt->invalidate(); - }); - - txt->setHeight(button->height()); - grid.spacer(button->height() + 5); } - - grid.nextLine(); } void ModelMixerScriptsPage::editLine(FormWindow * window, uint8_t idx)