From ce2fcefd43373227d0396b63055604cf0b3e346d Mon Sep 17 00:00:00 2001 From: philmoz Date: Fri, 3 May 2024 10:41:24 +1000 Subject: [PATCH 01/21] Change UI layout constants to static class constant. Convert magic numbers to class constants. Add defines for portrait/landscape layout. --- radio/src/gui/colorlcd/channel_bar.cpp | 20 +- radio/src/gui/colorlcd/channel_bar.h | 17 +- radio/src/gui/colorlcd/color_editor.cpp | 25 +- radio/src/gui/colorlcd/color_editor.h | 9 + radio/src/gui/colorlcd/color_picker.cpp | 33 +- radio/src/gui/colorlcd/curveedit.cpp | 4 +- radio/src/gui/colorlcd/custom_failsafe.cpp | 6 +- radio/src/gui/colorlcd/file_carosell.cpp | 2 +- radio/src/gui/colorlcd/fm_matrix.cpp | 23 +- radio/src/gui/colorlcd/fm_matrix.h | 2 + radio/src/gui/colorlcd/fullscreen_dialog.cpp | 2 +- radio/src/gui/colorlcd/gvar_numberedit.cpp | 4 +- radio/src/gui/colorlcd/gvar_numberedit.h | 6 +- radio/src/gui/colorlcd/hw_inputs.cpp | 62 +--- radio/src/gui/colorlcd/hw_inputs.h | 19 ++ radio/src/gui/colorlcd/hw_serial.cpp | 4 +- radio/src/gui/colorlcd/hw_serial.h | 3 + radio/src/gui/colorlcd/input_edit.cpp | 18 +- radio/src/gui/colorlcd/input_edit.h | 3 + radio/src/gui/colorlcd/layout.h | 10 +- .../colorlcd/layouts/layout_factory_impl.h | 10 +- radio/src/gui/colorlcd/layouts/sliders.cpp | 52 ++- radio/src/gui/colorlcd/layouts/sliders.h | 27 +- .../src/gui/colorlcd/layouts/topbar_impl.cpp | 16 +- radio/src/gui/colorlcd/layouts/topbar_impl.h | 8 + radio/src/gui/colorlcd/layouts/trims.cpp | 42 +-- radio/src/gui/colorlcd/list_line_button.cpp | 59 +--- radio/src/gui/colorlcd/list_line_button.h | 29 +- radio/src/gui/colorlcd/listbox.cpp | 9 +- radio/src/gui/colorlcd/listbox.h | 5 +- radio/src/gui/colorlcd/menu_model.cpp | 13 - radio/src/gui/colorlcd/menu_model.h | 4 - radio/src/gui/colorlcd/mixer_edit.cpp | 16 +- radio/src/gui/colorlcd/mixer_edit.h | 3 + radio/src/gui/colorlcd/mixer_edit_adv.cpp | 16 +- radio/src/gui/colorlcd/mixer_edit_adv.h | 2 + radio/src/gui/colorlcd/model_curves.cpp | 29 +- radio/src/gui/colorlcd/model_flightmodes.cpp | 307 +++++++++--------- radio/src/gui/colorlcd/model_flightmodes.h | 2 + radio/src/gui/colorlcd/model_gvars.cpp | 118 ++++--- radio/src/gui/colorlcd/model_heli.cpp | 2 +- radio/src/gui/colorlcd/model_inputs.cpp | 4 +- .../gui/colorlcd/model_logical_switches.cpp | 83 ++--- .../src/gui/colorlcd/model_mixer_scripts.cpp | 4 +- radio/src/gui/colorlcd/model_mixes.cpp | 26 +- radio/src/gui/colorlcd/model_outputs.cpp | 115 ++----- radio/src/gui/colorlcd/model_outputs.h | 17 + radio/src/gui/colorlcd/model_select.cpp | 58 ++-- radio/src/gui/colorlcd/model_select.h | 11 + radio/src/gui/colorlcd/model_setup.cpp | 18 +- radio/src/gui/colorlcd/model_telemetry.cpp | 55 ++-- radio/src/gui/colorlcd/model_telemetry.h | 2 + radio/src/gui/colorlcd/model_templates.cpp | 6 +- radio/src/gui/colorlcd/model_usbjoystick.cpp | 16 +- radio/src/gui/colorlcd/output_edit.cpp | 19 +- radio/src/gui/colorlcd/output_edit.h | 3 + radio/src/gui/colorlcd/page.cpp | 12 +- radio/src/gui/colorlcd/page.h | 3 + radio/src/gui/colorlcd/popups.h | 2 + radio/src/gui/colorlcd/preflight_checks.cpp | 26 +- radio/src/gui/colorlcd/preview_window.cpp | 33 +- radio/src/gui/colorlcd/preview_window.h | 24 ++ radio/src/gui/colorlcd/radio_calibration.cpp | 8 +- radio/src/gui/colorlcd/radio_diaganas.cpp | 10 +- radio/src/gui/colorlcd/radio_diagkeys.cpp | 16 +- .../colorlcd/radio_ghost_module_config.cpp | 4 +- radio/src/gui/colorlcd/radio_hardware.cpp | 6 +- radio/src/gui/colorlcd/radio_hardware.h | 2 + radio/src/gui/colorlcd/radio_sdmanager.cpp | 4 +- radio/src/gui/colorlcd/radio_setup.cpp | 25 +- radio/src/gui/colorlcd/radio_theme.cpp | 92 +++--- radio/src/gui/colorlcd/radio_theme.h | 7 + radio/src/gui/colorlcd/radio_tools.cpp | 13 +- radio/src/gui/colorlcd/radio_trainer.cpp | 13 +- radio/src/gui/colorlcd/radio_trainer.h | 2 + radio/src/gui/colorlcd/screen_setup.cpp | 6 +- .../gui/colorlcd/screen_user_interface.cpp | 4 +- .../src/gui/colorlcd/select_fab_carousel.cpp | 24 +- radio/src/gui/colorlcd/select_fab_carousel.h | 11 +- radio/src/gui/colorlcd/sourcechoice.cpp | 2 +- radio/src/gui/colorlcd/special_functions.cpp | 48 +-- radio/src/gui/colorlcd/special_functions.h | 22 ++ radio/src/gui/colorlcd/standalone_lua.cpp | 2 +- radio/src/gui/colorlcd/startup_shutdown.cpp | 32 +- radio/src/gui/colorlcd/tabsgroup.cpp | 55 +++- radio/src/gui/colorlcd/tabsgroup.h | 9 +- radio/src/gui/colorlcd/theme.cpp | 9 +- radio/src/gui/colorlcd/theme.h | 6 + .../src/gui/colorlcd/themes/etx_lv_theme.cpp | 54 +-- radio/src/gui/colorlcd/themes/etx_lv_theme.h | 29 +- radio/src/gui/colorlcd/topbar.h | 5 - radio/src/gui/colorlcd/view_channels.cpp | 4 +- .../gui/colorlcd/view_logical_switches.cpp | 50 ++- .../src/gui/colorlcd/view_logical_switches.h | 5 + .../src/gui/colorlcd/view_main_decoration.cpp | 16 +- radio/src/gui/colorlcd/view_main_menu.cpp | 14 +- radio/src/gui/colorlcd/view_statistics.cpp | 16 +- radio/src/gui/colorlcd/widget_settings.cpp | 2 +- radio/src/gui/colorlcd/widgets/radio_info.cpp | 83 +++-- radio/src/gui/colorlcd/widgets/timer.cpp | 35 +- radio/src/gui/colorlcd/widgets/value.cpp | 7 +- radio/src/gui/colorlcd/widgets_container.h | 7 +- radio/src/lua/api_general.cpp | 2 +- radio/src/lua/interface.cpp | 4 +- radio/src/opentx.h | 2 + radio/src/storage/modelslist.h | 2 +- radio/src/targets/horus/hal.h | 2 + radio/src/targets/nv14/hal.h | 3 + radio/src/targets/pl18/hal.h | 3 + .../libopenui/src/bitmapbuffer_draw_extra.cpp | 4 +- radio/src/thirdparty/libopenui/src/button.cpp | 12 +- radio/src/thirdparty/libopenui/src/choice.cpp | 2 +- radio/src/thirdparty/libopenui/src/dialog.h | 2 +- .../libopenui/src/libopenui_defines.h | 25 -- .../thirdparty/libopenui/src/menutoolbar.cpp | 2 +- .../thirdparty/libopenui/src/menutoolbar.h | 9 + .../thirdparty/libopenui/src/numberedit.cpp | 3 +- .../src/thirdparty/libopenui/src/numberedit.h | 2 + .../src/thirdparty/libopenui/src/progress.cpp | 2 +- .../src/thirdparty/libopenui/src/textedit.cpp | 3 +- radio/src/thirdparty/libopenui/src/textedit.h | 2 + .../thirdparty/libopenui/src/toggleswitch.cpp | 4 +- .../thirdparty/libopenui/src/toggleswitch.h | 4 +- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- radio/src/thirdparty/libopenui/src/window.h | 11 +- radio/src/translations/cn.h | 4 +- radio/src/translations/cz.h | 4 +- radio/src/translations/da.h | 8 +- radio/src/translations/de.h | 4 +- radio/src/translations/en.h | 4 +- radio/src/translations/es.h | 4 +- radio/src/translations/fi.h | 4 +- radio/src/translations/fr.h | 4 +- radio/src/translations/he.h | 4 +- radio/src/translations/it.h | 4 +- radio/src/translations/jp.h | 4 +- radio/src/translations/nl.h | 4 +- radio/src/translations/pl.h | 4 +- radio/src/translations/pt.h | 4 +- radio/src/translations/ru.h | 4 +- radio/src/translations/se.h | 8 +- radio/src/translations/tw.h | 4 +- radio/src/translations/ua.h | 4 +- 143 files changed, 1279 insertions(+), 1245 deletions(-) diff --git a/radio/src/gui/colorlcd/channel_bar.cpp b/radio/src/gui/colorlcd/channel_bar.cpp index aa7c986a6a6..e9f00953690 100644 --- a/radio/src/gui/colorlcd/channel_bar.cpp +++ b/radio/src/gui/colorlcd/channel_bar.cpp @@ -41,11 +41,13 @@ ChannelBar::ChannelBar(Window* parent, const rect_t& rect, lv_obj_set_pos(bar, width() / 2, 0); lv_obj_set_size(bar, 0, height()); + coord_t yo = (height() < 10) ? -1 : VAL_YO; + valText = lv_label_create(lvobj); - lv_obj_set_pos(valText, width() / 2 + 5, -2); - lv_obj_set_size(valText, 45, 12); + lv_obj_set_pos(valText, width() / 2 + VAL_XO, yo); + lv_obj_set_size(valText, VAL_W, VAL_H); etx_obj_add_style(valText, styles->text_align_left, LV_PART_MAIN); - lv_obj_set_style_translate_x(valText, -54, LV_STATE_USER_1); + lv_obj_set_style_translate_x(valText, VAL_XT, LV_STATE_USER_1); etx_obj_add_style(valText, styles->text_align_right, LV_STATE_USER_1); etx_font(valText, FONT_XS_INDEX); etx_txt_color(valText, indexFromColor(txtColor)); @@ -216,31 +218,31 @@ ComboChannelBar::ComboChannelBar(Window* parent, const rect_t& rect, isInHeader ? COLOR_THEME_PRIMARY2 : COLOR_THEME_SECONDARY1; outputChannelBar = new OutputChannelBar( - this, {LMARGIN, BAR_HEIGHT + TMARGIN, width() - LMARGIN, BAR_HEIGHT}, + this, {ChannelBar::LMARGIN, ChannelBar::BAR_HEIGHT + ChannelBar::TMARGIN, width() - ChannelBar::LMARGIN, ChannelBar::BAR_HEIGHT}, channel, isInHeader); new MixerChannelBar( this, - {LMARGIN, (2 * BAR_HEIGHT) + TMARGIN + 1, width() - LMARGIN, BAR_HEIGHT}, + {ChannelBar::LMARGIN, (2 * ChannelBar::BAR_HEIGHT) + ChannelBar::TMARGIN + 1, width() - ChannelBar::LMARGIN, ChannelBar::BAR_HEIGHT}, channel); // Channel number char chanString[] = TR_CH "32 "; strAppendSigned(&chanString[2], channel + 1, 2); - new StaticText(this, {LMARGIN, 0, LV_SIZE_CONTENT, 12}, chanString, + new StaticText(this, {ChannelBar::LMARGIN, 0, LV_SIZE_CONTENT, 12}, chanString, textColor | FONT(XS) | LEFT); // Channel name if (g_model.limitData[channel].name[0]) { char nm[LEN_CHANNEL_NAME + 1]; strAppend(nm, g_model.limitData[channel].name, LEN_CHANNEL_NAME); - new StaticText(this, {LMARGIN + 45, 0, LV_SIZE_CONTENT, 12}, nm, + new StaticText(this, {ChannelBar::LMARGIN + ChannelBar::VAL_W, 0, LV_SIZE_CONTENT, ChannelBar::VAL_H}, nm, textColor | FONT(XS) | LEFT); } // Channel value in µS new DynamicNumber( - this, {width() - 45, 0, 45, 12}, + this, {width() - ChannelBar::VAL_W, 0, ChannelBar::VAL_W, ChannelBar::VAL_H}, [=] { return PPM_CH_CENTER(channel) + channelOutputs[channel] / 2; }, textColor | FONT(XS) | RIGHT, "", STR_US); @@ -254,7 +256,7 @@ ComboChannelBar::ComboChannelBar(Window* parent, const rect_t& rect, // Channel reverted icon LimitData* ld = limitAddress(channel); if (ld && ld->revert) { - new StaticIcon(this, 0, 25, ICON_CHAN_MONITOR_INVERTED, + new StaticIcon(this, 0, ICON_SZ, ICON_CHAN_MONITOR_INVERTED, textColor); } } diff --git a/radio/src/gui/colorlcd/channel_bar.h b/radio/src/gui/colorlcd/channel_bar.h index cf399c7c3f0..5713dcd4148 100644 --- a/radio/src/gui/colorlcd/channel_bar.h +++ b/radio/src/gui/colorlcd/channel_bar.h @@ -23,11 +23,6 @@ #include "opentx.h" -constexpr coord_t ROW_HEIGHT = 42; -constexpr coord_t BAR_HEIGHT = 13; -constexpr coord_t LMARGIN = 15; -constexpr coord_t TMARGIN = 2; - class ChannelBar : public Window { public: @@ -35,6 +30,16 @@ class ChannelBar : public Window std::function getValue, LcdFlags barColor, LcdFlags textColor = COLOR_THEME_SECONDARY1); + static LAYOUT_VAL(BAR_HEIGHT, 13, 13) + static LAYOUT_VAL(LMARGIN, 15, 15) + static LAYOUT_VAL(TMARGIN, 2, 2) + + static LAYOUT_VAL(VAL_W, 45, 45) + static LAYOUT_VAL(VAL_H, 12, 12) + static LAYOUT_VAL(VAL_XO, 5, 5) + static LAYOUT_VAL(VAL_YO, -2, -2) + static LAYOUT_VAL(VAL_XT, -54, -54) + protected: int16_t value = -10000; std::function getValue; @@ -78,6 +83,8 @@ class ComboChannelBar : public Window ComboChannelBar(Window* parent, const rect_t& rect, uint8_t channel, bool isInHeader = false); + static LAYOUT_VAL(ICON_SZ, 25, 25) + protected: uint8_t channel; OutputChannelBar* outputChannelBar = nullptr; diff --git a/radio/src/gui/colorlcd/color_editor.cpp b/radio/src/gui/colorlcd/color_editor.cpp index aef0a018538..0757abfe55a 100644 --- a/radio/src/gui/colorlcd/color_editor.cpp +++ b/radio/src/gui/colorlcd/color_editor.cpp @@ -23,11 +23,6 @@ #include "button.h" #include "themes/etx_lv_theme.h" -constexpr int BAR_MARGIN = 5; - -constexpr int BAR_TOP_MARGIN = 5; -constexpr int BAR_HEIGHT_OFFSET = BAR_TOP_MARGIN + 25; - static const char* const RGBChars[MAX_BARS] = {"R", "G", "B"}; static const char* const HSVChars[MAX_BARS] = {"H", "S", "V"}; @@ -170,12 +165,12 @@ class ColorBar : public Window // draw cursor lv_area_t cursor_area; - cursor_area.x1 = area->x1 + (lv_area_get_width(area) / 2) - 5; - cursor_area.x2 = cursor_area.x1 + 10 - 1; + cursor_area.x1 = area->x1 + (lv_area_get_width(area) / 2) - ColorEditor::CRSR_SZ / 2; + cursor_area.x2 = cursor_area.x1 + ColorEditor::CRSR_SZ - 1; auto pos = bar->valueToScreen(bar->value); - cursor_area.y1 = area->y1 + pos - 3; - cursor_area.y2 = cursor_area.y1 + 10 - 1; + cursor_area.y1 = area->y1 + pos - ColorEditor::CRSR_YO; + cursor_area.y2 = cursor_area.y1 + ColorEditor::CRSR_SZ - 1; lv_draw_rect_dsc_t cursor_dsc; lv_draw_rect_dsc_init(&cursor_dsc); @@ -274,12 +269,12 @@ BarColorType::BarColorType(Window* parent) int leftPos = 0; rect_t r; - r.y = BAR_TOP_MARGIN; - r.w = spacePerBar - BAR_MARGIN - 5; - r.h = parent->height() - BAR_HEIGHT_OFFSET; + r.y = ColorEditor::BAR_TOP_MARGIN; + r.w = spacePerBar - ColorEditor::BAR_MARGIN - 5; + r.h = parent->height() - (ColorEditor::BAR_TOP_MARGIN + ColorEditor::BAR_HEIGHT_OFFSET); for (int i = 0; i < MAX_BARS; i++) { - r.x = leftPos + BAR_MARGIN; + r.x = leftPos + ColorEditor::BAR_MARGIN; bars[i] = new ColorBar(parent, r); leftPos += spacePerBar; @@ -289,8 +284,8 @@ BarColorType::BarColorType(Window* parent) auto x = bar->left(); auto y = bar->bottom(); - barLabels[i] = create_bar_label(parent->getLvObj(), x, y + 9); - barValLabels[i] = create_bar_value_label(parent->getLvObj(), x + 10, y + 3); + barLabels[i] = create_bar_label(parent->getLvObj(), x, y + ColorEditor::LBL_YO); + barValLabels[i] = create_bar_value_label(parent->getLvObj(), x + ColorEditor::VAL_XO, y + ColorEditor::VAL_YO); } } diff --git a/radio/src/gui/colorlcd/color_editor.h b/radio/src/gui/colorlcd/color_editor.h index 2c3f0e7d866..854ec897a72 100644 --- a/radio/src/gui/colorlcd/color_editor.h +++ b/radio/src/gui/colorlcd/color_editor.h @@ -45,6 +45,15 @@ class ColorEditor : public Window void setColorEditorType(COLOR_EDITOR_TYPE colorType); + static LAYOUT_VAL(BAR_MARGIN, 5, 5) + static LAYOUT_VAL(BAR_TOP_MARGIN, 5, 5) + static LAYOUT_VAL(BAR_HEIGHT_OFFSET, 25, 25) + static LAYOUT_VAL(LBL_YO, 9, 9) + static LAYOUT_VAL(VAL_XO, 10, 10) + static LAYOUT_VAL(VAL_YO, 3, 3) + static LAYOUT_VAL(CRSR_SZ, 10, 10) + static LAYOUT_VAL(CRSR_YO, 3, 3) + protected: ColorType* _colorType = nullptr; std::function _setValue; diff --git a/radio/src/gui/colorlcd/color_picker.cpp b/radio/src/gui/colorlcd/color_picker.cpp index 7f2c9348f76..97e27c94608 100644 --- a/radio/src/gui/colorlcd/color_picker.cpp +++ b/radio/src/gui/colorlcd/color_picker.cpp @@ -25,26 +25,18 @@ #include "color_list.h" #include "themes/etx_lv_theme.h" -// based on LVGL default switch size -constexpr lv_coord_t COLOR_PAD_WIDTH = (4 * LV_DPI_DEF) / 10; -constexpr lv_coord_t COLOR_PAD_HEIGHT = 32; - -#if LCD_W > LCD_H +#if !PORTRAIT_LCD // Landscape static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; -#define COLOR_EDIT_WIDTH LCD_W * 0.8 - #else // Portrait static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; -#define COLOR_EDIT_WIDTH LCD_W * 0.7 - #endif class ColorEditorPopup : public BaseDialog @@ -74,7 +66,7 @@ class ColorEditorPopup : public BaseDialog FlexGridLayout grid(col_dsc, row_dsc); auto line = form->newLine(grid); - rect_t r{0, 0, 7 * LV_DPI_DEF / 5, 7 * LV_DPI_DEF / 5}; + rect_t r{0, 0, CE_SZ, CE_SZ}; auto cedit = new ColorEditor(line, r, color, [=](uint32_t c) { updateColor(c); }); lv_obj_set_style_grid_cell_x_align(cedit->getLvObj(), LV_GRID_ALIGN_CENTER, @@ -94,7 +86,7 @@ class ColorEditorPopup : public BaseDialog colorPad = new ColorSwatch(hbox, {0, 0, COLOR_PAD_WIDTH, COLOR_PAD_HEIGHT}, COLOR_THEME_PRIMARY1); - hexStr = new StaticText(hbox, {0, 0, 100, 0}, "", COLOR_THEME_PRIMARY1 | FONT(L)); + hexStr = new StaticText(hbox, {0, 0, CVAL_W, 0}, "", COLOR_THEME_PRIMARY1 | FONT(L)); updateColor(color); @@ -135,29 +127,38 @@ class ColorEditorPopup : public BaseDialog hsvBtn->check(true); hbox = new Window(vbox, rect_t{}); - hbox->padTop(60); - hbox->setFlexLayout(LV_FLEX_FLOW_ROW, 20); + hbox->padTop(BTN_PAD_TOP); + hbox->setFlexLayout(LV_FLEX_FLOW_ROW, BTN_PAD_ROW); lv_obj_set_flex_align(hbox->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_END, LV_FLEX_ALIGN_SPACE_BETWEEN); lv_obj_set_flex_grow(hbox->getLvObj(), 1); - new TextButton(hbox, rect_t{0, 0, 80, 0}, STR_CANCEL, [=]() -> int8_t { + new TextButton(hbox, rect_t{0, 0, BTN_W, 0}, STR_CANCEL, [=]() -> int8_t { this->deleteLater(); return 0; }); - new TextButton(hbox, rect_t{0, 0, 80, 0}, STR_SAVE, [=]() -> int8_t { + new TextButton(hbox, rect_t{0, 0, BTN_W, 0}, STR_SAVE, [=]() -> int8_t { if (_setValue) _setValue(m_color); this->deleteLater(); return 0; }); } + + static LAYOUT_VAL(CE_SZ, 182, 182) + static LAYOUT_VAL(COLOR_EDIT_WIDTH, LCD_W * 0.8, LCD_W * 0.7) + static LAYOUT_VAL(COLOR_PAD_WIDTH, 52, 52) + static LAYOUT_VAL(COLOR_PAD_HEIGHT, 32, 32) + static LAYOUT_VAL(CVAL_W, 100, 100) + static LAYOUT_VAL(BTN_W, 80, 80) + static LAYOUT_VAL(BTN_PAD_TOP, 60, 60) + static LAYOUT_VAL(BTN_PAD_ROW, 20, 20) }; ColorPicker::ColorPicker(Window* parent, const rect_t& rect, std::function getValue, std::function setValue) : - Button(parent, {0, 0, COLOR_PAD_WIDTH, COLOR_PAD_HEIGHT}), + Button(parent, {0, 0, ColorEditorPopup::COLOR_PAD_WIDTH, ColorEditorPopup::COLOR_PAD_HEIGHT}), setValue(std::move(setValue)) { updateColor(getValue()); diff --git a/radio/src/gui/colorlcd/curveedit.cpp b/radio/src/gui/colorlcd/curveedit.cpp index 939e957a09f..c3509274981 100644 --- a/radio/src/gui/colorlcd/curveedit.cpp +++ b/radio/src/gui/colorlcd/curveedit.cpp @@ -39,7 +39,7 @@ CurveDataEdit::CurveDataEdit(Window* parent, const rect_t& rect, etx_scrollbar(lvobj); } -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define NUM_BTN_WIDTH 44 #else #define NUM_BTN_WIDTH 48 @@ -277,7 +277,7 @@ void CurveEditWindow::buildBody(Window* window) lv_obj_set_grid_align(line->getLvObj(), LV_GRID_ALIGN_SPACE_BETWEEN, LV_GRID_ALIGN_SPACE_BETWEEN); -#if LCD_H > LCD_W // portrait +#if PORTRAIT_LCD lv_obj_set_flex_flow(line->getLvObj(), LV_FLEX_FLOW_COLUMN); coord_t curveWidth = window->width() - 88; coord_t boxWidth = window->width(); diff --git a/radio/src/gui/colorlcd/custom_failsafe.cpp b/radio/src/gui/colorlcd/custom_failsafe.cpp index 10531899621..cbf0db9b7ec 100644 --- a/radio/src/gui/colorlcd/custom_failsafe.cpp +++ b/radio/src/gui/colorlcd/custom_failsafe.cpp @@ -36,12 +36,12 @@ class ChannelFailsafeBargraph : public Window etx_obj_add_style(lvobj, styles->border_thin, LV_PART_MAIN); etx_obj_add_style(lvobj, styles->border_color_black, LV_PART_MAIN); - outputsBar = new OutputChannelBar(this, {0, 1, width() - 2, BAR_HEIGHT}, + outputsBar = new OutputChannelBar(this, {0, 1, width() - 2, ChannelBar::BAR_HEIGHT}, channel, false, false); outputsBar->hide(); failsafeBar = new ChannelBar( - this, {0, BAR_HEIGHT + 3, width() - 2, BAR_HEIGHT}, + this, {0, ChannelBar::BAR_HEIGHT + 3, width() - 2, ChannelBar::BAR_HEIGHT}, [=] { return calcRESXto100(g_model.failsafeChannels[channel]); }, COLOR_THEME_WARNING, COLOR_THEME_WARNING); failsafeBar->hide(); @@ -187,7 +187,7 @@ static void set_failsafe(lv_event_t* e) if (combo) combo->update(); } -#if LCD_H > LCD_W +#if PORTRAIT_LCD #define FS_BARGRAPH_WIDTH (LV_DPI_DEF / 2) #else #define FS_BARGRAPH_WIDTH (LV_DPI_DEF) diff --git a/radio/src/gui/colorlcd/file_carosell.cpp b/radio/src/gui/colorlcd/file_carosell.cpp index 595060a7841..c493c2c5e62 100644 --- a/radio/src/gui/colorlcd/file_carosell.cpp +++ b/radio/src/gui/colorlcd/file_carosell.cpp @@ -35,7 +35,7 @@ FileCarosell::FileCarosell(Window *parent, const rect_t &rect, { setWindowFlag(NO_FOCUS); - message = new StaticText(this, {0, rect.h/2, rect.w, PAGE_LINE_HEIGHT}, "", CENTERED | FONT(L) | COLOR_THEME_PRIMARY1); + message = new StaticText(this, {0, rect.h/2, rect.w, EdgeTxStyles::PAGE_LINE_HEIGHT}, "", CENTERED | FONT(L) | COLOR_THEME_PRIMARY1); setFileNames(fileNames); } diff --git a/radio/src/gui/colorlcd/fm_matrix.cpp b/radio/src/gui/colorlcd/fm_matrix.cpp index baf12173444..bcd712a50dc 100644 --- a/radio/src/gui/colorlcd/fm_matrix.cpp +++ b/radio/src/gui/colorlcd/fm_matrix.cpp @@ -24,15 +24,19 @@ #include "opentx.h" #include "themes/etx_lv_theme.h" +#if PORTRAIT_LCD +#define FM_COLS 3 +#define FM_ROWS 3 +#else +#define FM_COLS 5 +#define FM_ROWS 2 +#endif + template FMMatrix::FMMatrix(Window* parent, const rect_t& r, T* input) : ButtonMatrix(parent, r), input(input) { -#if LCD_W > LCD_H - initBtnMap(5, MAX_FLIGHT_MODES); -#else - initBtnMap(3, MAX_FLIGHT_MODES); -#endif + initBtnMap(FM_COLS, MAX_FLIGHT_MODES); for (int i = 0; i < MAX_FLIGHT_MODES; i++) { setTextAndState(i); @@ -40,13 +44,8 @@ FMMatrix::FMMatrix(Window* parent, const rect_t& r, T* input) : update(); -#if LCD_W > LCD_H - lv_obj_set_width(lvobj, 258); - lv_obj_set_height(lvobj, 73); -#else - lv_obj_set_width(lvobj, 156); - lv_obj_set_height(lvobj, 108); -#endif + lv_obj_set_width(lvobj, FM_COLS * (FM_BTN_W + 3) + 3); + lv_obj_set_height(lvobj, FM_ROWS * (EdgeTxStyles::UI_ELEMENT_HEIGHT + 3) +3); padAll(PAD_SMALL); } diff --git a/radio/src/gui/colorlcd/fm_matrix.h b/radio/src/gui/colorlcd/fm_matrix.h index 382b532c91d..df41727343c 100644 --- a/radio/src/gui/colorlcd/fm_matrix.h +++ b/radio/src/gui/colorlcd/fm_matrix.h @@ -33,6 +33,8 @@ struct FMMatrix : public ButtonMatrix { void onPress(uint8_t btn_id); bool isActive(uint8_t btn_id); void setTextAndState(uint8_t btn_id); + + static LAYOUT_VAL(FM_BTN_W, 48, 48) }; extern template struct FMMatrix; diff --git a/radio/src/gui/colorlcd/fullscreen_dialog.cpp b/radio/src/gui/colorlcd/fullscreen_dialog.cpp index 3421a0bf558..e501bad6fba 100644 --- a/radio/src/gui/colorlcd/fullscreen_dialog.cpp +++ b/radio/src/gui/colorlcd/fullscreen_dialog.cpp @@ -30,7 +30,7 @@ #include "view_main.h" #include "hal/watchdog_driver.h" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD constexpr uint32_t ALERT_FRAME_TOP = 50; constexpr uint32_t ALERT_FRAME_HEIGHT = (LCD_H - 120); constexpr uint32_t ALERT_BITMAP_TOP = ALERT_FRAME_TOP + 25; diff --git a/radio/src/gui/colorlcd/gvar_numberedit.cpp b/radio/src/gui/colorlcd/gvar_numberedit.cpp index 40332147be8..db4d49d1980 100644 --- a/radio/src/gui/colorlcd/gvar_numberedit.cpp +++ b/radio/src/gui/colorlcd/gvar_numberedit.cpp @@ -64,13 +64,13 @@ GVarNumberEdit::GVarNumberEdit(Window* parent, const rect_t& rect, int32_t vmin, }); gvar_field->setTextHandler( [=](int32_t value) { return getGVarString(value); }); - gvar_field->setWidth(70); + gvar_field->setWidth(NUM_EDIT_W); num_field = new NumberEdit( this, rect_t{}, vmin, vmax, [=]() { return getValue() + voffset; }, nullptr); num_field->setTextFlag(textFlags); - num_field->setWidth(70); + num_field->setWidth(NUM_EDIT_W); num_field->setDefault(vdefault); #if defined(GVARS) diff --git a/radio/src/gui/colorlcd/gvar_numberedit.h b/radio/src/gui/colorlcd/gvar_numberedit.h index 7253965d409..5cb490ec08a 100644 --- a/radio/src/gui/colorlcd/gvar_numberedit.h +++ b/radio/src/gui/colorlcd/gvar_numberedit.h @@ -26,8 +26,6 @@ #include "numberedit.h" #include "gvars.h" -constexpr coord_t GVAR_BUTTON_WIDTH = 30; - class TextButton; class GVarNumberEdit : public Window @@ -43,7 +41,9 @@ class GVarNumberEdit : public Window void setFastStep(int value) { num_field->setFastStep(value); } void setAccelFactor(int value) { num_field->setAccelFactor(value); } - + + static LAYOUT_VAL(NUM_EDIT_W, 70, 70) + protected: Choice* gvar_field = nullptr; NumberEdit* num_field = nullptr; diff --git a/radio/src/gui/colorlcd/hw_inputs.cpp b/radio/src/gui/colorlcd/hw_inputs.cpp index 18d6c9d71d7..fa875b8a30f 100644 --- a/radio/src/gui/colorlcd/hw_inputs.cpp +++ b/radio/src/gui/colorlcd/hw_inputs.cpp @@ -32,9 +32,12 @@ struct HWInputEdit : public RadioTextEdit { HWInputEdit(Window* parent, char* name, size_t len, coord_t x = 0, coord_t y = 0) : - RadioTextEdit(parent, rect_t{x, y, 64, 32}, name, len) + RadioTextEdit(parent, rect_t{x, y, HW_INP_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, name, + len) { } + + static LAYOUT_VAL(HW_INP_W, 64, 64) }; static const lv_coord_t col_two_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(2), @@ -42,7 +45,7 @@ static const lv_coord_t col_two_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(2), static const lv_coord_t col_three_dsc[] = { LV_GRID_FR(8), LV_GRID_FR(12), LV_GRID_FR(20), LV_GRID_TEMPLATE_LAST}; -#if LCD_W > LCD_H +#if !PORTRAIT_LCD static const lv_coord_t pots_col_dsc[] = {LV_GRID_FR(2), LV_GRID_FR(2), LV_GRID_FR(5), LV_GRID_FR(2), LV_GRID_TEMPLATE_LAST}; @@ -77,30 +80,6 @@ HWSticks::HWSticks(Window* parent) : Window(parent, rect_t{}) #endif } -// Absolute layout for Pots popup - due to performance issues with lv_textarea -// in a flex layout -#if LCD_W > LCD_H -#define P_LBL_X 0 -#define P_LBL_W ((coord_t)((DIALOG_DEFAULT_WIDTH - 30) * 2 / 11)) -#define P_NM_X (P_LBL_X + P_LBL_W + 6) -#define P_TYP_X (P_NM_X + 70) -#define P_TYP_W 160 -#define P_INV_X (P_TYP_X + P_TYP_W + 6) -#define P_INV_W 52 -#define P_Y(i) (i * 36 + 2) -#define P_OFST_Y 0 -#else -#define P_LBL_X 0 -#define P_LBL_W ((coord_t)((DIALOG_DEFAULT_WIDTH - 18) * 13 / 21)) -#define P_NM_X (P_LBL_X + P_LBL_W + 6) -#define P_TYP_X 0 -#define P_TYP_W P_LBL_W -#define P_INV_X (P_TYP_X + P_TYP_W + 6) -#define P_INV_W 52 -#define P_Y(i) (i * 72 + 2) -#define P_OFST_Y 36 -#endif - HWPots::HWPots(Window* parent) : Window(parent, rect_t{0, 0, DIALOG_DEFAULT_WIDTH - 12, LV_SIZE_CONTENT}) { @@ -122,15 +101,17 @@ HWPots::HWPots(Window* parent) : // #if !defined(SIMU) && defined(RADIO_FAMILY_T16) // if (!globalData.flyskygimbals && (i >= (NUM_POTS - 2))) continue; // #endif - new StaticText(this, rect_t{P_LBL_X, P_Y(i) + 6, P_LBL_W, 32}, + new StaticText(this, + rect_t{P_LBL_X, P_Y(i) + 6, P_LBL_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, adcGetInputLabel(ADC_INPUT_FLEX, i)); new HWInputEdit(this, (char*)analogGetCustomLabel(ADC_INPUT_FLEX, i), LEN_ANA_NAME, P_NM_X, P_Y(i)); auto pot = new Choice( - this, rect_t{P_TYP_X, P_Y(i) + P_OFST_Y, P_TYP_W, 32}, STR_POTTYPES, - FLEX_NONE, FLEX_SWITCH, [=]() -> int { return getPotType(i); }, + this, rect_t{P_TYP_X, P_Y(i) + P_OFST_Y, P_TYP_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, + STR_POTTYPES, FLEX_NONE, FLEX_SWITCH, + [=]() -> int { return getPotType(i); }, [=](int newValue) { setPotType(i, newValue); switchFixFlexConfig(); @@ -140,7 +121,7 @@ HWPots::HWPots(Window* parent) : pot->setAvailableHandler([=](int val) { return isPotTypeAvailable(val); }); new ToggleSwitch( - this, rect_t{P_INV_X, P_Y(i) + P_OFST_Y, P_INV_W, 32}, + this, rect_t{P_INV_X, P_Y(i) + P_OFST_Y, P_INV_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, [=]() -> uint8_t { return (uint8_t)getPotInversion(i); }, [=](int8_t newValue) { setPotInversion(i, newValue); @@ -149,19 +130,11 @@ HWPots::HWPots(Window* parent) : } } -// Absolute layout for Switches popup - due to performance issues with -// lv_textarea in a flex layout -#if LCD_W > LCD_H -#define SW_CTRL_W 86 -#else -#define SW_CTRL_W 75 -#endif - class SwitchDynamicLabel : public StaticText { public: SwitchDynamicLabel(Window* parent, uint8_t index, coord_t x, coord_t y) : - StaticText(parent, rect_t{x, y, SW_CTRL_W, 32}, ""), + StaticText(parent, rect_t{x, y, HWSwitches::SW_CTRL_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, ""), index(index) { checkEvents(); @@ -219,15 +192,15 @@ HWSwitches::HWSwitches(Window* parent) : { auto max_switches = switchGetMaxSwitches(); for (int i = 0; i < max_switches; i++) { - new SwitchDynamicLabel(this, i, 2, i * 36 + 2); + new SwitchDynamicLabel(this, i, 2, i * SW_CTRL_H + 2); new HWInputEdit(this, (char*)switchGetCustomName(i), LEN_SWITCH_NAME, - SW_CTRL_W + 8, i * 36 + 2); + SW_CTRL_W + 8, i * SW_CTRL_H + 2); coord_t x = SW_CTRL_W * 2 + 14; Choice* channel = nullptr; if (switchIsFlex(i)) { channel = new Choice( - this, rect_t{x, i * 36 + 2, SW_CTRL_W, 32}, -1, + this, rect_t{x, i * SW_CTRL_H + 2, SW_CTRL_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, -1, adcGetMaxInputs(ADC_INPUT_FLEX) - 1, [=]() -> int { return switchGetFlexConfig(i); }, [=](int newValue) { switchConfigFlex(i, newValue); }); @@ -242,8 +215,9 @@ HWSwitches::HWSwitches(Window* parent) : } auto sw_cfg = new Choice( - this, rect_t{x, i * 36 + 2, SW_CTRL_W, 32}, STR_SWTYPES, SWITCH_NONE, - switchGetMaxType(i), [=]() -> int { return SWITCH_CONFIG(i); }, + this, rect_t{x, i * SW_CTRL_H + 2, SW_CTRL_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, + STR_SWTYPES, SWITCH_NONE, switchGetMaxType(i), + [=]() -> int { return SWITCH_CONFIG(i); }, [=](int newValue) { swconfig_t mask = (swconfig_t)SWITCH_CONFIG_MASK(i); g_eeGeneral.switchConfig = diff --git a/radio/src/gui/colorlcd/hw_inputs.h b/radio/src/gui/colorlcd/hw_inputs.h index 7695bff2857..8bc151e886b 100644 --- a/radio/src/gui/colorlcd/hw_inputs.h +++ b/radio/src/gui/colorlcd/hw_inputs.h @@ -32,10 +32,29 @@ struct HWSticks : public Window { struct HWPots : public Window { HWPots(Window* parent); bool potsChanged; + + // Absolute layout for Pots popup - due to performance issues with lv_textarea + // in a flex layout + static LAYOUT_VAL(P_LBL_X, 0, 0) + static LAYOUT_VAL(P_LBL_W, (DIALOG_DEFAULT_WIDTH - 45) * 2 / 11, (DIALOG_DEFAULT_WIDTH - 18) * 13 / 21) + static constexpr coord_t P_NM_X = P_LBL_X + P_LBL_W + PAD_MEDIUM; + static LAYOUT_VAL(P_NM_W, 70, 70) + static LAYOUT_VAL(P_TYP_X, P_NM_X + P_NM_W, 0) + static LAYOUT_VAL(P_TYP_W, 160, P_LBL_W) + static constexpr coord_t P_INV_X = P_TYP_X + P_TYP_W + PAD_MEDIUM; + static LAYOUT_VAL(P_INV_W, 52, 52) + static LAYOUT_VAL(P_ROW_H, 36, 72) + static LAYOUT_VAL(P_OFST_Y, 0, 36) + #define P_Y(i) (i * P_ROW_H + 2) }; struct HWSwitches : public Window { HWSwitches(Window* parent); + + // Absolute layout for Switches popup - due to performance issues with + // lv_textarea in a flex layout + static LAYOUT_VAL(SW_CTRL_W, 86, 75) + static LAYOUT_VAL(SW_CTRL_H, 36, 36) }; template diff --git a/radio/src/gui/colorlcd/hw_serial.cpp b/radio/src/gui/colorlcd/hw_serial.cpp index a24c5570742..bcab1fe3214 100644 --- a/radio/src/gui/colorlcd/hw_serial.cpp +++ b/radio/src/gui/colorlcd/hw_serial.cpp @@ -63,8 +63,8 @@ SerialConfigWindow::SerialConfigWindow(Window *parent, FlexGridLayout& grid) if (port_nr != SP_VCP) { grid.setColSpan(2); auto line = parent->newLine(grid); - line->padLeft(20); - line->padBottom(6); + line->padLeft(WARN_PADL); + line->padBottom(WARN_PADB); new StaticText(line, rect_t{}, STR_TTL_WARNING, COLOR_THEME_WARNING); grid.setColSpan(1); } diff --git a/radio/src/gui/colorlcd/hw_serial.h b/radio/src/gui/colorlcd/hw_serial.h index e29dd772cdf..26d5d64d6a1 100644 --- a/radio/src/gui/colorlcd/hw_serial.h +++ b/radio/src/gui/colorlcd/hw_serial.h @@ -27,4 +27,7 @@ class SerialConfigWindow { public: SerialConfigWindow(Window *parent, FlexGridLayout& grid); + + static LAYOUT_VAL(WARN_PADL, 20, 20); + static LAYOUT_VAL(WARN_PADB, 6, 6); }; diff --git a/radio/src/gui/colorlcd/input_edit.cpp b/radio/src/gui/colorlcd/input_edit.cpp index c50fa302965..44969b22e73 100644 --- a/radio/src/gui/colorlcd/input_edit.cpp +++ b/radio/src/gui/colorlcd/input_edit.cpp @@ -32,14 +32,6 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -#if LCD_W > LCD_H -constexpr coord_t INPUT_EDIT_CURVE_WIDTH = 140; -constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = INPUT_EDIT_CURVE_WIDTH; -#else -constexpr coord_t INPUT_EDIT_CURVE_WIDTH = 176; -constexpr coord_t INPUT_EDIT_CURVE_HEIGHT = 132; -#endif - InputEditWindow::InputEditWindow(int8_t input, uint8_t index) : Page(ICON_MODEL_INPUTS), input(input), index(index) { @@ -48,7 +40,7 @@ InputEditWindow::InputEditWindow(int8_t input, uint8_t index) : header->setTitle2(title2); auto body_obj = body->getLvObj(); -#if LCD_H > LCD_W // portrait +#if PORTRAIT_LCD // portrait lv_obj_set_flex_flow(body_obj, LV_FLEX_FLOW_COLUMN); #else // landscape lv_obj_set_flex_flow(body_obj, LV_FLEX_FLOW_ROW); @@ -60,10 +52,10 @@ InputEditWindow::InputEditWindow(int8_t input, uint8_t index) : lv_obj_set_flex_grow(box_obj, 2); etx_scrollbar(box_obj); -#if LCD_H > LCD_W // portrait - box->setWidth(body->width() - 2 * lv_dpx(8)); +#if PORTRAIT_LCD // portrait + box->setWidth(body->width() - 2 * PAD_MEDIUM); #else // landscape - box->setHeight(body->height() - 2 * lv_dpx(8)); + box->setHeight(body->height() - 2 * PAD_MEDIUM); #endif auto form = new Window(box, rect_t{}); @@ -89,7 +81,7 @@ static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; void InputEditWindow::buildBody(Window* form) { FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY); - form->setFlexLayout(); + form->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO); ExpoData* input = expoAddress(index); diff --git a/radio/src/gui/colorlcd/input_edit.h b/radio/src/gui/colorlcd/input_edit.h index b6d4654dd38..8af4e588a19 100644 --- a/radio/src/gui/colorlcd/input_edit.h +++ b/radio/src/gui/colorlcd/input_edit.h @@ -32,6 +32,9 @@ class InputEditWindow : public Page public: InputEditWindow(int8_t input, uint8_t index); + static LAYOUT_VAL(INPUT_EDIT_CURVE_WIDTH, 140, 176) + static LAYOUT_VAL(INPUT_EDIT_CURVE_HEIGHT, INPUT_EDIT_CURVE_WIDTH, 132) + protected: uint8_t input; uint8_t index; diff --git a/radio/src/gui/colorlcd/layout.h b/radio/src/gui/colorlcd/layout.h index 9c72ff148c3..51e72a1086e 100644 --- a/radio/src/gui/colorlcd/layout.h +++ b/radio/src/gui/colorlcd/layout.h @@ -28,9 +28,7 @@ #define MAX_LAYOUT_ZONES 10 #define MAX_LAYOUT_OPTIONS 10 -constexpr coord_t TRIM_LINE_WIDTH = 8; -constexpr coord_t TRIM_SQUARE_SIZE = 17; -constexpr coord_t MAIN_ZONE_BORDER = 10; + constexpr uint32_t LAYOUT_REFRESH = 1000 / 2; // 2 Hz #if !defined(YAML_GENERATOR) @@ -81,6 +79,12 @@ class LayoutFactory WidgetsContainer* createCustomScreen(unsigned customScreenIndex) const; + static LAYOUT_VAL(TRIM_LINE_WIDTH, 8, 8) + static LAYOUT_VAL(TRIM_SQUARE_SIZE, 17, 17) + + static LAYOUT_VAL(BM_W, 51, 22) + static LAYOUT_VAL(BM_H, 25, 34) + protected: const char* id; const char* name; diff --git a/radio/src/gui/colorlcd/layouts/layout_factory_impl.h b/radio/src/gui/colorlcd/layouts/layout_factory_impl.h index 6a955c58dad..e7b03f7e0dc 100644 --- a/radio/src/gui/colorlcd/layouts/layout_factory_impl.h +++ b/radio/src/gui/colorlcd/layouts/layout_factory_impl.h @@ -111,6 +111,8 @@ class Layout: public LayoutBase bool isAppMode() { return decorationSettings == DECORATION_NONE && zoneCount == 1; } + static LAYOUT_VAL(MAIN_ZONE_BORDER, 10, 10) + protected: const LayoutFactory * factory = nullptr; std::unique_ptr decoration; @@ -140,14 +142,6 @@ class Layout: public LayoutBase rect_t getZone(unsigned int index) const override; }; -#if LCD_W > LCD_H -#define BM_W 51 -#define BM_H 25 -#else -#define BM_W 22 -#define BM_H 34 -#endif - template class BaseLayoutFactory: public LayoutFactory { diff --git a/radio/src/gui/colorlcd/layouts/sliders.cpp b/radio/src/gui/colorlcd/layouts/sliders.cpp index 0963f0593e6..d0534910449 100644 --- a/radio/src/gui/colorlcd/layouts/sliders.cpp +++ b/radio/src/gui/colorlcd/layouts/sliders.cpp @@ -49,19 +49,19 @@ static const lv_style_const_prop_t shadow2_props[] = { static LV_STYLE_CONST_MULTI_INIT(shadow2_style, shadow2_props); SliderIcon::SliderIcon(Window* parent) : - Window(parent, rect_t{0, 0, 17, 17}) + Window(parent, rect_t{0, 0, MainViewSlider::SL_SZ + 2, MainViewSlider::SL_SZ + 2}) { setWindowFlag(NO_FOCUS); auto shad = lv_obj_create(lvobj); etx_obj_add_style(shad, shadow1_style, LV_PART_MAIN); lv_obj_set_pos(shad, 1, 1); - lv_obj_set_size(shad, 15, 15); + lv_obj_set_size(shad, MainViewSlider::SL_SZ, MainViewSlider::SL_SZ); fill = lv_obj_create(lvobj); etx_obj_add_style(fill, shadow2_style, LV_PART_MAIN); lv_obj_set_pos(fill, 0, 0); - lv_obj_set_size(fill, 15, 15); + lv_obj_set_size(fill, MainViewSlider::SL_SZ, MainViewSlider::SL_SZ); etx_solid_bg(fill, COLOR_THEME_FOCUS_INDEX); } @@ -70,17 +70,17 @@ MainViewSlider::MainViewSlider(Window* parent, const rect_t& rect, uint8_t idx, Window(parent, rect), idx(idx), isVertical(isVertical) { if (isVertical) { - int sliderTicksCount = (height() - TRIM_SQUARE_SIZE) / SLIDER_TICK_SPACING; + int sliderTicksCount = (height() - LayoutFactory::TRIM_SQUARE_SIZE) / SLIDER_TICK_SPACING; tickPoints = new lv_point_t[(sliderTicksCount + 1) * 2]; - lv_coord_t y = TRIM_SQUARE_SIZE / 2; + lv_coord_t y = LayoutFactory::TRIM_SQUARE_SIZE / 2; for (uint8_t i = 0; i <= sliderTicksCount; i++) { if (i == 0 || i == sliderTicksCount / 2 || i == sliderTicksCount) { - tickPoints[i * 2] = {2, y}; - tickPoints[i * 2 + 1] = {15, y}; + tickPoints[i * 2] = {SL_TK, y}; + tickPoints[i * 2 + 1] = {SL_SZ, y}; } else { - tickPoints[i * 2] = {4, y}; - tickPoints[i * 2 + 1] = {13, y}; + tickPoints[i * 2] = {SL_TK + 2, y}; + tickPoints[i * 2 + 1] = {SL_SZ - 2, y}; } auto line = lv_line_create(lvobj); etx_obj_add_style(line, styles->div_line, LV_PART_MAIN); @@ -88,17 +88,17 @@ MainViewSlider::MainViewSlider(Window* parent, const rect_t& rect, uint8_t idx, y += SLIDER_TICK_SPACING; } } else { - int sliderTicksCount = (width() - TRIM_SQUARE_SIZE) / SLIDER_TICK_SPACING; + int sliderTicksCount = (width() - LayoutFactory::TRIM_SQUARE_SIZE) / SLIDER_TICK_SPACING; tickPoints = new lv_point_t[(sliderTicksCount + 1) * 2]; - lv_coord_t x = TRIM_SQUARE_SIZE / 2; + lv_coord_t x = LayoutFactory::TRIM_SQUARE_SIZE / 2; for (uint8_t i = 0; i <= sliderTicksCount; i++) { if (i == 0 || i == sliderTicksCount / 2 || i == SLIDER_TICKS_COUNT) { - tickPoints[i * 2] = {x, 2}; - tickPoints[i * 2 + 1] = {x, 15}; + tickPoints[i * 2] = {x, SL_TK}; + tickPoints[i * 2 + 1] = {x, SL_SZ}; } else { - tickPoints[i * 2] = {x, 4}; - tickPoints[i * 2 + 1] = {x, 13}; + tickPoints[i * 2] = {x, SL_TK + 2}; + tickPoints[i * 2 + 1] = {x, SL_SZ - 2}; } auto line = lv_line_create(lvobj); etx_obj_add_style(line, styles->div_line, LV_PART_MAIN); @@ -110,9 +110,9 @@ MainViewSlider::MainViewSlider(Window* parent, const rect_t& rect, uint8_t idx, sliderIcon = new SliderIcon(this); coord_t x = 0, y = 0; if (isVertical) - y = (height() - TRIM_SQUARE_SIZE) / 2; + y = (height() - LayoutFactory::TRIM_SQUARE_SIZE) / 2; else - y = (width() - TRIM_SQUARE_SIZE) / 2; + y = (width() - LayoutFactory::TRIM_SQUARE_SIZE) / 2; lv_obj_set_pos(sliderIcon->getLvObj(), x, y); checkEvents(); @@ -137,10 +137,10 @@ void MainViewSlider::checkEvents() coord_t x = 0, y = 0; if (isVertical) { - y = divRoundClosest((height() - TRIM_SQUARE_SIZE) * (-value + RESX), + y = divRoundClosest((height() - LayoutFactory::TRIM_SQUARE_SIZE) * (-value + RESX), 2 * RESX); } else { - x = divRoundClosest((width() - TRIM_SQUARE_SIZE) * (value + RESX), + x = divRoundClosest((width() - LayoutFactory::TRIM_SQUARE_SIZE) * (value + RESX), 2 * RESX); } lv_obj_set_pos(sliderIcon->getLvObj(), x, y); @@ -150,7 +150,7 @@ void MainViewSlider::checkEvents() MainViewHorizontalSlider::MainViewHorizontalSlider(Window* parent, uint8_t idx) : MainViewSlider(parent, - rect_t{0, 0, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, + rect_t{0, 0, HORIZONTAL_SLIDERS_WIDTH, LayoutFactory::TRIM_SQUARE_SIZE}, idx, false) { } @@ -162,20 +162,16 @@ MainViewVerticalSlider::MainViewVerticalSlider(Window* parent, { } -constexpr coord_t MULTIPOS_H = 18; -constexpr coord_t MULTIPOS_W_SPACING = 12; -constexpr coord_t MULTIPOS_W = (6 + 1) * MULTIPOS_W_SPACING; - MainView6POS::MainView6POS(Window* parent, uint8_t idx) : Window(parent, rect_t{0, 0, MULTIPOS_W, MULTIPOS_H}), idx(idx) { char num[] = " "; - coord_t x = MULTIPOS_W_SPACING / 4 + TRIM_SQUARE_SIZE / 4; + coord_t x = MULTIPOS_W_SPACING / 4 + LayoutFactory::TRIM_SQUARE_SIZE / 4; for (uint8_t value = 0; value < XPOTS_MULTIPOS_COUNT; value++) { num[0] = value + '1'; auto p = lv_label_create(lvobj); lv_label_set_text(p, num); - lv_obj_set_size(p, 12, 12); + lv_obj_set_size(p, MULTIPOS_SZ, MULTIPOS_SZ); lv_obj_set_pos(p, x, 0); etx_txt_color(p, COLOR_THEME_SECONDARY1_INDEX, LV_PART_MAIN); etx_font(p, FONT_XS_INDEX, LV_PART_MAIN); @@ -184,8 +180,8 @@ MainView6POS::MainView6POS(Window* parent, uint8_t idx) : posIcon = new SliderIcon(this); posVal = lv_label_create(posIcon->getLvObj()); - lv_obj_set_pos(posVal, 3, -2); - lv_obj_set_size(posVal, 12, 12); + lv_obj_set_pos(posVal, MULTIPOS_XO, -2); + lv_obj_set_size(posVal, MULTIPOS_SZ, MULTIPOS_SZ); etx_txt_color(posVal, COLOR_THEME_PRIMARY2_INDEX, LV_PART_MAIN); etx_font(posVal, FONT_BOLD_INDEX, LV_PART_MAIN); diff --git a/radio/src/gui/colorlcd/layouts/sliders.h b/radio/src/gui/colorlcd/layouts/sliders.h index b09054a596e..2a063832813 100644 --- a/radio/src/gui/colorlcd/layouts/sliders.h +++ b/radio/src/gui/colorlcd/layouts/sliders.h @@ -23,17 +23,6 @@ #include "libopenui.h" -#if LCD_H > LCD_W -constexpr uint8_t SLIDER_TICKS_COUNT = 30; -#else -constexpr uint8_t SLIDER_TICKS_COUNT = 40; -#endif -constexpr coord_t SLIDER_TICK_SPACING = 4; -constexpr coord_t HORIZONTAL_SLIDERS_WIDTH = - SLIDER_TICKS_COUNT * SLIDER_TICK_SPACING + TRIM_SQUARE_SIZE; -constexpr coord_t VERTICAL_SLIDERS_HEIGHT = - SLIDER_TICKS_COUNT * SLIDER_TICK_SPACING + TRIM_SQUARE_SIZE; - class SliderIcon : public Window { public: @@ -50,6 +39,16 @@ class MainViewSlider : public Window bool isVertical); void checkEvents() override; + static LAYOUT_VAL(SLIDER_TICKS_COUNT, 40, 30) + static LAYOUT_VAL(SLIDER_TICK_SPACING, 4, 4) + static constexpr coord_t HORIZONTAL_SLIDERS_WIDTH = + SLIDER_TICKS_COUNT * SLIDER_TICK_SPACING + LayoutFactory::TRIM_SQUARE_SIZE; + static constexpr coord_t VERTICAL_SLIDERS_HEIGHT = + SLIDER_TICKS_COUNT * SLIDER_TICK_SPACING + LayoutFactory::TRIM_SQUARE_SIZE; + + static LAYOUT_VAL(SL_SZ, 15, 15) + static LAYOUT_VAL(SL_TK, 2, 2) + protected: uint8_t idx; int16_t value = -10000; @@ -79,6 +78,12 @@ class MainView6POS : public Window void checkEvents() override; + static LAYOUT_VAL(MULTIPOS_H, 18, 18) + static LAYOUT_VAL(MULTIPOS_W_SPACING, 12, 12) + static LAYOUT_VAL(MULTIPOS_SZ, 12, 12) + static LAYOUT_VAL(MULTIPOS_XO, 3, 3) + static constexpr coord_t MULTIPOS_W = (6 + 1) * MULTIPOS_W_SPACING; + protected: uint8_t idx; int16_t value = -10000; diff --git a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp index eff77dc1a44..46afc2a5c48 100644 --- a/radio/src/gui/colorlcd/layouts/topbar_impl.cpp +++ b/radio/src/gui/colorlcd/layouts/topbar_impl.cpp @@ -41,7 +41,7 @@ class TopBarEdgeTx : public HeaderIcon }; TopBar::TopBar(Window * parent) : - TopBarBase(parent, {0, 0, LCD_W, MENU_HEADER_HEIGHT}, &g_model.topbarData) + TopBarBase(parent, {0, 0, LCD_W, EdgeTxStyles::MENU_HEADER_HEIGHT}, &g_model.topbarData) { etx_solid_bg(lvobj, COLOR_THEME_SECONDARY1_INDEX); @@ -55,15 +55,15 @@ unsigned int TopBar::getZonesCount() const rect_t TopBar::getZone(unsigned int index) const { -#if LCD_H > LCD_W +#if PORTRAIT_LCD if (index == MAX_TOPBAR_ZONES - 1) { - coord_t size = LCD_W - 48 - (MAX_TOPBAR_ZONES - 1) * (TOPBAR_ZONE_WIDTH + TOPBAR_ZONE_HMARGIN); + coord_t size = LCD_W - HDR_DATE_XO - (MAX_TOPBAR_ZONES - 1) * (TOPBAR_ZONE_WIDTH + TOPBAR_ZONE_HMARGIN); return {LCD_W - size, TOPBAR_ZONE_VMARGIN, size, TOPBAR_ZONE_HEIGHT}; } #endif return { - coord_t(48 + (TOPBAR_ZONE_WIDTH + TOPBAR_ZONE_HMARGIN) * index), + coord_t(MENU_HEADER_BUTTONS_LEFT + 1 + (TOPBAR_ZONE_WIDTH + TOPBAR_ZONE_HMARGIN) * index), TOPBAR_ZONE_VMARGIN, TOPBAR_ZONE_WIDTH, TOPBAR_ZONE_HEIGHT @@ -73,13 +73,13 @@ rect_t TopBar::getZone(unsigned int index) const void TopBar::setVisible(float visible) // 0.0 -> 1.0 { if (visible == 0.0) { - setTop(-(int)MENU_HEADER_HEIGHT); + setTop(-(int)EdgeTxStyles::MENU_HEADER_HEIGHT); } else if (visible == 1.0) { setTop(0); } else if (visible > 0.0 && visible < 1.0){ - float top = - (float)MENU_HEADER_HEIGHT * (1.0 - visible); + float top = - (float)EdgeTxStyles::MENU_HEADER_HEIGHT * (1.0 - visible); setTop((coord_t)top); } } @@ -90,10 +90,10 @@ coord_t TopBar::getVisibleHeight(float visible) const // 0.0 -> 1.0 return 0; } else if (visible == 1.0) { - return MENU_HEADER_HEIGHT; + return EdgeTxStyles::MENU_HEADER_HEIGHT; } - float h = (float)MENU_HEADER_HEIGHT * visible; + float h = (float)EdgeTxStyles::MENU_HEADER_HEIGHT * visible; return (coord_t)h; } diff --git a/radio/src/gui/colorlcd/layouts/topbar_impl.h b/radio/src/gui/colorlcd/layouts/topbar_impl.h index 8eafc2913c4..004f70b8fb6 100644 --- a/radio/src/gui/colorlcd/layouts/topbar_impl.h +++ b/radio/src/gui/colorlcd/layouts/topbar_impl.h @@ -60,6 +60,14 @@ class TopBar: public TopBarBase void removeWidget(unsigned int index) override; + static LAYOUT_VAL(TOPBAR_ZONE_WIDTH, 70, 70) + static LAYOUT_VAL(TOPBAR_ZONE_VMARGIN, 3, 3) + static LAYOUT_VAL(TOPBAR_ZONE_HMARGIN, 2, 2) + static constexpr coord_t TOPBAR_ZONE_HEIGHT = EdgeTxStyles::MENU_HEADER_HEIGHT - 2 * TOPBAR_ZONE_VMARGIN; + + static LAYOUT_VAL(HDR_DATE_XO, 48, 48) + static LAYOUT_VAL(MENU_HEADER_BUTTONS_LEFT, 47, 47) + protected: uint32_t lastRefresh = 0; HeaderIcon* headerIcon = nullptr; diff --git a/radio/src/gui/colorlcd/layouts/trims.cpp b/radio/src/gui/colorlcd/layouts/trims.cpp index 618b667c6d8..9a65158d7d6 100644 --- a/radio/src/gui/colorlcd/layouts/trims.cpp +++ b/radio/src/gui/colorlcd/layouts/trims.cpp @@ -87,27 +87,27 @@ MainViewTrim::MainViewTrim(Window* parent, const rect_t& rect, uint8_t idx, etx_solid_bg(trimBar, COLOR_THEME_SECONDARY1_INDEX); etx_obj_add_style(trimBar, styles->rounded, LV_PART_MAIN); if (isVertical) { - lv_obj_set_pos(trimBar, (TRIM_SQUARE_SIZE - TRIM_LINE_WIDTH) / 2, - TRIM_SQUARE_SIZE / 2); - lv_obj_set_size(trimBar, TRIM_LINE_WIDTH, - VERTICAL_SLIDERS_HEIGHT - TRIM_SQUARE_SIZE + 1); + lv_obj_set_pos(trimBar, (LayoutFactory::TRIM_SQUARE_SIZE - LayoutFactory::TRIM_LINE_WIDTH) / 2, + LayoutFactory::TRIM_SQUARE_SIZE / 2); + lv_obj_set_size(trimBar, LayoutFactory::TRIM_LINE_WIDTH, + MainViewSlider::VERTICAL_SLIDERS_HEIGHT - LayoutFactory::TRIM_SQUARE_SIZE + 1); } else { - lv_obj_set_pos(trimBar, TRIM_SQUARE_SIZE / 2, - (TRIM_SQUARE_SIZE - TRIM_LINE_WIDTH - 1) / 2); - lv_obj_set_size(trimBar, HORIZONTAL_SLIDERS_WIDTH - TRIM_SQUARE_SIZE + 1, - TRIM_LINE_WIDTH); + lv_obj_set_pos(trimBar, LayoutFactory::TRIM_SQUARE_SIZE / 2, + (LayoutFactory::TRIM_SQUARE_SIZE - LayoutFactory::TRIM_LINE_WIDTH - 1) / 2); + lv_obj_set_size(trimBar, MainViewSlider::HORIZONTAL_SLIDERS_WIDTH - LayoutFactory::TRIM_SQUARE_SIZE + 1, + LayoutFactory::TRIM_LINE_WIDTH); } trimIcon = new TrimIcon(this, isVertical); coord_t x = 0, y = 0; if (isVertical) - y = (height() - TRIM_SQUARE_SIZE) / 2; + y = (height() - LayoutFactory::TRIM_SQUARE_SIZE) / 2; else - y = (width() - TRIM_SQUARE_SIZE) / 2; + y = (width() - LayoutFactory::TRIM_SQUARE_SIZE) / 2; lv_obj_set_pos(trimIcon->getLvObj(), x, y); trimValue = new DynamicNumber( - this, {0, 0, TRIM_SQUARE_SIZE, 12}, + this, {0, 0, LayoutFactory::TRIM_SQUARE_SIZE, 12}, [=]() { return divRoundClosest(abs(value) * 100, trimMax); }, COLOR_THEME_PRIMARY2 | FONT(XXS) | CENTERED); etx_solid_bg(trimValue->getLvObj(), COLOR_THEME_SECONDARY1_INDEX); @@ -141,13 +141,13 @@ void MainViewTrim::setPos() if (value) { if (isVertical) { x = 0; - y = (value > 0) ? VERTICAL_SLIDERS_HEIGHT * 4 / 5 - : VERTICAL_SLIDERS_HEIGHT / 5 - 11; + y = (value > 0) ? MainViewSlider::VERTICAL_SLIDERS_HEIGHT * 4 / 5 + : MainViewSlider::VERTICAL_SLIDERS_HEIGHT / 5 - 11; } else { - x = ((value < 0) ? HORIZONTAL_SLIDERS_WIDTH * 4 / 5 - : HORIZONTAL_SLIDERS_WIDTH / 5) - - TRIM_SQUARE_SIZE / 2; - y = (TRIM_SQUARE_SIZE - 12) / 2; + x = ((value < 0) ? MainViewSlider::HORIZONTAL_SLIDERS_WIDTH * 4 / 5 + : MainViewSlider::HORIZONTAL_SLIDERS_WIDTH / 5) - + LayoutFactory::TRIM_SQUARE_SIZE / 2; + y = (LayoutFactory::TRIM_SQUARE_SIZE - 12) / 2; } lv_obj_set_pos(trimValue->getLvObj(), x, y); trimValue->show(); @@ -208,7 +208,7 @@ coord_t MainViewTrim::sx() if (isVertical) return 0; return divRoundClosest( - (HORIZONTAL_SLIDERS_WIDTH - TRIM_SQUARE_SIZE) * (value - trimMin), + (MainViewSlider::HORIZONTAL_SLIDERS_WIDTH - LayoutFactory::TRIM_SQUARE_SIZE) * (value - trimMin), trimMax - trimMin); } @@ -217,20 +217,20 @@ coord_t MainViewTrim::sy() if (!isVertical) return 0; return divRoundClosest( - (VERTICAL_SLIDERS_HEIGHT - TRIM_SQUARE_SIZE) * (trimMax - value), + (MainViewSlider::VERTICAL_SLIDERS_HEIGHT - LayoutFactory::TRIM_SQUARE_SIZE) * (trimMax - value), trimMax - trimMin); } MainViewHorizontalTrim::MainViewHorizontalTrim(Window* parent, uint8_t idx) : MainViewTrim(parent, - rect_t{0, 0, HORIZONTAL_SLIDERS_WIDTH, TRIM_SQUARE_SIZE}, idx, + rect_t{0, 0, MainViewSlider::HORIZONTAL_SLIDERS_WIDTH, LayoutFactory::TRIM_SQUARE_SIZE}, idx, false) { } MainViewVerticalTrim::MainViewVerticalTrim(Window* parent, uint8_t idx) : MainViewTrim(parent, - rect_t{0, 0, TRIM_SQUARE_SIZE, VERTICAL_SLIDERS_HEIGHT}, idx, + rect_t{0, 0, LayoutFactory::TRIM_SQUARE_SIZE, MainViewSlider::VERTICAL_SLIDERS_HEIGHT}, idx, true) { } diff --git a/radio/src/gui/colorlcd/list_line_button.cpp b/radio/src/gui/colorlcd/list_line_button.cpp index bbd2bf2d171..708480cd163 100644 --- a/radio/src/gui/colorlcd/list_line_button.cpp +++ b/radio/src/gui/colorlcd/list_line_button.cpp @@ -61,46 +61,11 @@ void ListLineButton::checkEvents() ButtonBase::checkEvents(); } -// total: 92 x 17 -#define FM_CANVAS_HEIGHT 17 -#define FM_CANVAS_WIDTH 92 - -#if LCD_W > LCD_H // Landscape - -#define BTN_W 389 - -#define SRC_W 70 -#define OPT_W 171 -#define FM_X (OPT_X + OPT_W + 2) -#define FM_Y (WGT_Y + 2) - -#else // Portrait - -#define BTN_W 229 - -#define SRC_W 69 -#define OPT_W 106 -#define FM_X 12 -#define FM_Y (WGT_Y + WGT_H + 2) - -#endif - -#define WGT_X 2 -#define WGT_Y 2 -#define WGT_W 42 -#define WGT_H 21 -#define SRC_X (WGT_X + WGT_W + 2) -#define SRC_Y WGT_Y -#define SRC_H WGT_H -#define OPT_X (SRC_X + SRC_W + 2) -#define OPT_Y WGT_Y -#define OPT_H WGT_H - InputMixButtonBase::InputMixButtonBase(Window* parent, uint8_t index) : ListLineButton(parent, index) { setWidth(BTN_W); - setHeight(BTN_H); + setHeight(ListLineButton::BTN_H); padAll(PAD_ZERO); weight = lv_label_create(lvobj); @@ -146,8 +111,8 @@ void InputMixButtonBase::setFlightModes(uint16_t modes) free(fm_buffer); fm_canvas = nullptr; fm_buffer = nullptr; -#if LCD_H > LCD_W - setHeight(BTN_H); +#if PORTRAIT_LCD + setHeight(ListLineButton::BTN_H); #endif return; } @@ -158,8 +123,8 @@ void InputMixButtonBase::setFlightModes(uint16_t modes) lv_canvas_set_buffer(fm_canvas, fm_buffer, FM_CANVAS_WIDTH, FM_CANVAS_HEIGHT, LV_IMG_CF_ALPHA_8BIT); lv_obj_set_pos(fm_canvas, FM_X, FM_Y); -#if LCD_H > LCD_W - setHeight(BTN_H + FM_CANVAS_HEIGHT + 2); +#if PORTRAIT_LCD + setHeight(ListLineButton::BTN_H + FM_CANVAS_HEIGHT + 2); #endif lv_obj_set_style_img_recolor(fm_canvas, makeLvColor(COLOR_THEME_SECONDARY1), @@ -175,7 +140,7 @@ void InputMixButtonBase::setFlightModes(uint16_t modes) coord_t x = 0; lv_canvas_copy_buf(fm_canvas, mask->data, x, 0, w, h); - x += 20; + x += (w + PAD_TINY); lv_draw_label_dsc_t label_dsc; lv_draw_label_dsc_init(&label_dsc); @@ -193,11 +158,11 @@ void InputMixButtonBase::setFlightModes(uint16_t modes) if (fm_modes & (1 << i)) { label_dsc.color = lv_color_make(0x7f, 0x7f, 0x7f); } else { - lv_canvas_draw_rect(fm_canvas, x, 0, 8, 3, &rect_dsc); + lv_canvas_draw_rect(fm_canvas, x, 0, FM_W, 3, &rect_dsc); label_dsc.color = lv_color_white(); } - lv_canvas_draw_text(fm_canvas, x, 0, 8, &label_dsc, s); - x += 8; + lv_canvas_draw_text(fm_canvas, x, 0, FM_W, &label_dsc, s); + x += FM_W; } } @@ -212,7 +177,7 @@ static const lv_obj_class_t group_class = { .destructor_cb = nullptr, .user_data = nullptr, .event_cb = nullptr, - .width_def = LCD_W - 12, + .width_def = ListLineButton::GRP_W, .height_def = LV_SIZE_CONTENT, .editable = LV_OBJ_CLASS_EDITABLE_FALSE, .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE, @@ -238,12 +203,12 @@ InputMixGroupBase::InputMixGroupBase(Window* parent, mixsrc_t idx) : void InputMixGroupBase::adjustHeight() { - if (getLineCount() == 0) setHeight(InputMixButtonBase::BTN_H + 8); + if (getLineCount() == 0) setHeight(ListLineButton::BTN_H + 8); coord_t y = 2; for (auto it = lines.cbegin(); it != lines.cend(); ++it) { auto line = *it; - line->updatePos(LN_X, y); + line->updatePos(InputMixButtonBase::LN_X, y); y += line->height() + 2; } setHeight(y + 4); diff --git a/radio/src/gui/colorlcd/list_line_button.h b/radio/src/gui/colorlcd/list_line_button.h index b649b856f39..e684d1bfca5 100644 --- a/radio/src/gui/colorlcd/list_line_button.h +++ b/radio/src/gui/colorlcd/list_line_button.h @@ -37,6 +37,9 @@ class ListLineButton : public ButtonBase virtual void refresh() = 0; + static LAYOUT_VAL(BTN_H, 29, 29) + static constexpr coord_t GRP_W = LCD_W - PAD_MEDIUM * 2; + protected: uint8_t index; @@ -53,11 +56,31 @@ class InputMixButtonBase : public ListLineButton void setSource(mixsrc_t idx); void setFlightModes(uint16_t modes); - static constexpr coord_t BTN_H = 29; - virtual void updatePos(coord_t x, coord_t y) = 0; virtual void swapLvglGroup(InputMixButtonBase* line2) = 0; + // total: 90 x 17 + static LAYOUT_VAL(FM_CANVAS_HEIGHT, 17, 17) + static LAYOUT_VAL(FM_CANVAS_WIDTH, 90, 90) + + static LAYOUT_VAL(BTN_W, 389, 229) + static constexpr coord_t WGT_X = PAD_TINY; + static constexpr coord_t WGT_Y = PAD_TINY; + static LAYOUT_VAL(WGT_W, 42, 42) + static LAYOUT_VAL(WGT_H, 21, 21) + static constexpr coord_t SRC_X = WGT_X + WGT_W + PAD_TINY; + static constexpr coord_t SRC_Y = WGT_Y; + static LAYOUT_VAL(SRC_W, 70, 69) + static constexpr coord_t SRC_H = WGT_H; + static constexpr coord_t OPT_X = SRC_X + SRC_W + PAD_TINY; + static constexpr coord_t OPT_Y = WGT_Y; + static LAYOUT_VAL(OPT_W, 171, 106) + static constexpr coord_t OPT_H = WGT_H; + static LAYOUT_VAL(LN_X, 73, 73) + static LAYOUT_VAL(FM_X, (OPT_X + OPT_W + PAD_TINY), 12) + static LAYOUT_VAL(FM_Y, (WGT_Y + PAD_TINY), (WGT_Y + WGT_H + PAD_TINY)) + static LAYOUT_VAL(FM_W, 8, 8) + protected: lv_obj_t* fm_canvas = nullptr; @@ -84,8 +107,6 @@ class InputMixGroupBase : public Window void refresh(); protected: - static constexpr coord_t LN_X = 73; - mixsrc_t idx; lv_obj_t* label; std::list lines; diff --git a/radio/src/gui/colorlcd/listbox.cpp b/radio/src/gui/colorlcd/listbox.cpp index a1dc5400eea..a1faf0526ca 100644 --- a/radio/src/gui/colorlcd/listbox.cpp +++ b/radio/src/gui/colorlcd/listbox.cpp @@ -109,10 +109,10 @@ void ListBox::setSelected(int selected, bool force) void ListBox::setSelected(std::set selected) { - if(!multiSelect) return; + if (!multiSelect) return; - for(int i = 0; i < getRowCount(); i++) { - if(selected.find(i) != selected.end()) + for (int i = 0; i < getRowCount(); i++) { + if (selected.find(i) != selected.end()) lv_table_add_cell_ctrl(lvobj, i, 0, LV_TABLE_CELL_CTRL_CUSTOM_1); else lv_table_clear_cell_ctrl(lvobj, i, 0, LV_TABLE_CELL_CTRL_CUSTOM_1); @@ -224,8 +224,7 @@ void ListBox::onDrawEnd(uint16_t row, uint16_t col, lv_obj_draw_part_dsc_t* dsc) label_dsc.align = LV_TEXT_ALIGN_RIGHT; const char* sym = LV_SYMBOL_OK; - if (getSelectedSymbol) - sym = getSelectedSymbol(row); + if (getSelectedSymbol) sym = getSelectedSymbol(row); lv_coord_t w = 30; lv_coord_t h = 12; diff --git a/radio/src/gui/colorlcd/listbox.h b/radio/src/gui/colorlcd/listbox.h index cdce9e76bdd..35d094c051d 100644 --- a/radio/src/gui/colorlcd/listbox.h +++ b/radio/src/gui/colorlcd/listbox.h @@ -31,7 +31,8 @@ class ListBox : public TableField { std::function longPressHandler = nullptr; std::function pressHandler = nullptr; - std::function, std::set)> _multiSelectHandler = nullptr; + std::function, std::set)> + _multiSelectHandler = nullptr; std::function getSelectedSymbol = nullptr; bool autoEdit = false; @@ -85,6 +86,8 @@ class ListBox : public TableField std::string getName() const override { return "ListBox"; } #endif + static LAYOUT_VAL(MENUS_LINE_HEIGHT, 35, 35) + protected: static void event_cb(lv_event_t* e); int activeItem = -1; diff --git a/radio/src/gui/colorlcd/menu_model.cpp b/radio/src/gui/colorlcd/menu_model.cpp index 753b72060e4..67f32fa22e6 100644 --- a/radio/src/gui/colorlcd/menu_model.cpp +++ b/radio/src/gui/colorlcd/menu_model.cpp @@ -70,19 +70,6 @@ void ModelMenu::build() #endif } -#if defined(PCBNV14) || defined(PCBPL18) -void ModelMenu::addGoToMonitorsButton() -{ - new TextButton( - getHeaderWindow(), - {LCD_W / 2 + 6, MENU_TITLE_TOP + 1, LCD_W / 2 - 8, MENU_TITLE_HEIGHT - 2}, - STR_OPEN_CHANNEL_MONITORS, [=]() { - pushEvent(EVT_KEY_BREAK(KEY_MODEL)); - return 0; - }); -} -#endif - #if defined(HARDWARE_KEYS) void ModelMenu::onPressSYS() { diff --git a/radio/src/gui/colorlcd/menu_model.h b/radio/src/gui/colorlcd/menu_model.h index 297c74f556d..827ebf3bc01 100644 --- a/radio/src/gui/colorlcd/menu_model.h +++ b/radio/src/gui/colorlcd/menu_model.h @@ -33,10 +33,6 @@ class ModelMenu : public TabsGroup #endif protected: -#if defined(PCBNV14) || defined(PCBPL18) - void addGoToMonitorsButton(void); -#endif - void build(); #if defined(HARDWARE_KEYS) diff --git a/radio/src/gui/colorlcd/mixer_edit.cpp b/radio/src/gui/colorlcd/mixer_edit.cpp index ae2dd043ff4..28236852d84 100644 --- a/radio/src/gui/colorlcd/mixer_edit.cpp +++ b/radio/src/gui/colorlcd/mixer_edit.cpp @@ -34,16 +34,6 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -#if (LCD_W > LCD_H) -#define MIX_STATUS_BAR_WIDTH 250 -#define MIX_STATUS_BAR_MARGIN 3 -#define MIX_RIGHT_MARGIN 0 -#else -#define MIX_STATUS_BAR_WIDTH 180 -#define MIX_STATUS_BAR_MARGIN 0 -#define MIX_RIGHT_MARGIN 3 -#endif - class MixerEditStatusBar : public Window { public: @@ -57,6 +47,8 @@ class MixerEditStatusBar : public Window channel, true); } + static LAYOUT_VAL(MIX_STATUS_BAR_MARGIN, 3, 0) + protected: ComboChannelBar *channelBar; int8_t _channel; @@ -78,11 +70,11 @@ void MixEditWindow::buildHeader(Window *window) new MixerEditStatusBar( window, {window->getRect().w - MIX_STATUS_BAR_WIDTH - MIX_RIGHT_MARGIN, 0, - MIX_STATUS_BAR_WIDTH, MENU_HEADER_HEIGHT}, + MIX_STATUS_BAR_WIDTH, EdgeTxStyles::MENU_HEADER_HEIGHT}, channel); } -#if LCD_W > LCD_H +#if !PORTRAIT_LCD static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(2), LV_GRID_FR(1), LV_GRID_FR(3), LV_GRID_TEMPLATE_LAST}; diff --git a/radio/src/gui/colorlcd/mixer_edit.h b/radio/src/gui/colorlcd/mixer_edit.h index 660baa304c0..47fbb287e45 100644 --- a/radio/src/gui/colorlcd/mixer_edit.h +++ b/radio/src/gui/colorlcd/mixer_edit.h @@ -30,6 +30,9 @@ class MixEditWindow : public Page public: MixEditWindow(int8_t channel, uint8_t index); + static LAYOUT_VAL(MIX_STATUS_BAR_WIDTH, 250, 180) + static LAYOUT_VAL(MIX_RIGHT_MARGIN, 0, 3) + protected: uint8_t channel; uint8_t index; diff --git a/radio/src/gui/colorlcd/mixer_edit_adv.cpp b/radio/src/gui/colorlcd/mixer_edit_adv.cpp index f14714145a3..2edf92434a6 100644 --- a/radio/src/gui/colorlcd/mixer_edit_adv.cpp +++ b/radio/src/gui/colorlcd/mixer_edit_adv.cpp @@ -40,7 +40,7 @@ MixEditAdvanced::MixEditAdvanced(int8_t channel, uint8_t index) : buildBody(body); } -#if LCD_W > LCD_H +#if !PORTRAIT_LCD static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; @@ -80,27 +80,27 @@ void MixEditAdvanced::buildBody(Window* form) // Warning new StaticText(line, rect_t{}, STR_MIXWARNING); - auto edit = new NumberEdit(line, rect_t{0, 0, 100, 0}, 0, 3, + auto edit = new NumberEdit(line, rect_t{0, 0, NUM_EDIT_W, 0}, 0, 3, GET_SET_DEFAULT(mix->mixWarn)); edit->setZeroText(STR_OFF); // Delay up line = form->newLine(grid); new StaticText(line, rect_t{}, STR_DELAYUP); - edit = new NumberEdit(line, rect_t{0, 0, 100, 0}, 0, DELAY_MAX, + edit = new NumberEdit(line, rect_t{0, 0, NUM_EDIT_W, 0}, 0, DELAY_MAX, GET_DEFAULT(mix->delayUp), SET_VALUE(mix->delayUp, newValue), PREC1); edit->setSuffix("s"); // Delay down new StaticText(line, rect_t{}, STR_DELAYDOWN); - edit = new NumberEdit(line, rect_t{0, 0, 100, 0}, 0, DELAY_MAX, + edit = new NumberEdit(line, rect_t{0, 0, NUM_EDIT_W, 0}, 0, DELAY_MAX, GET_DEFAULT(mix->delayDown), SET_VALUE(mix->delayDown, newValue), PREC1); edit->setSuffix("s"); // Slow up/down precision -#if LCD_W > LCD_H +#if !PORTRAIT_LCD grid.setColSpan(2); #endif line = form->newLine(grid); @@ -115,20 +115,20 @@ void MixEditAdvanced::buildBody(Window* form) slowDn->update(); SET_DIRTY(); }); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD grid.setColSpan(1); #endif // Slow up line = form->newLine(grid); new StaticText(line, rect_t{}, STR_SLOWUP); - slowUp = new NumberEdit(line, rect_t{0, 0, 100, 0}, 0, DELAY_MAX, GET_DEFAULT(mix->speedUp), + slowUp = new NumberEdit(line, rect_t{0, 0, NUM_EDIT_W, 0}, 0, DELAY_MAX, GET_DEFAULT(mix->speedUp), SET_VALUE(mix->speedUp, newValue), mix->speedPrec ? PREC2 : PREC1); slowUp->setSuffix("s"); // Slow down new StaticText(line, rect_t{}, STR_SLOWDOWN); - slowDn = new NumberEdit(line, rect_t{0, 0, 100, 0}, 0, DELAY_MAX, GET_DEFAULT(mix->speedDown), + slowDn = new NumberEdit(line, rect_t{0, 0, NUM_EDIT_W, 0}, 0, DELAY_MAX, GET_DEFAULT(mix->speedDown), SET_VALUE(mix->speedDown, newValue), mix->speedPrec ? PREC2 : PREC1); slowDn->setSuffix("s"); } diff --git a/radio/src/gui/colorlcd/mixer_edit_adv.h b/radio/src/gui/colorlcd/mixer_edit_adv.h index b1cafe8a9d1..e77231c24bb 100644 --- a/radio/src/gui/colorlcd/mixer_edit_adv.h +++ b/radio/src/gui/colorlcd/mixer_edit_adv.h @@ -33,6 +33,8 @@ class MixEditAdvanced : public Page public: MixEditAdvanced(int8_t channel, uint8_t index); + static LAYOUT_VAL(NUM_EDIT_W, 100, 100) + protected: uint8_t channel; uint8_t index; diff --git a/radio/src/gui/colorlcd/model_curves.cpp b/radio/src/gui/colorlcd/model_curves.cpp index e7672d70a16..c7b29962dac 100644 --- a/radio/src/gui/colorlcd/model_curves.cpp +++ b/radio/src/gui/colorlcd/model_curves.cpp @@ -27,16 +27,10 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -#define PREVIEW_PAD 6 -#define TITLE_H 20 -#define INFO_H 27 -#define CURVE_BTN_W 142 -#define CURVE_BTH_H CURVE_BTN_W + TITLE_H + INFO_H - PREVIEW_PAD - -#if LCD_W > LCD_H -#define PER_ROW 3 -#else +#if PORTRAIT_LCD #define PER_ROW 2 +#else +#define PER_ROW 3 #endif class CurveButton : public Button @@ -54,7 +48,7 @@ class CurveButton : public Button s = strAppend(s, ":"); strAppend(s, g_model.curves[index].name, LEN_CURVE_NAME); } - title = new StaticText(this, {4, -1, width() - 12, 21}, buf, + title = new StaticText(this, {4, -1, width() - 12, TITLE_H + 1}, buf, COLOR_THEME_SECONDARY1 | CENTERED | FONT(BOLD)); etx_txt_color(title->getLvObj(), COLOR_THEME_PRIMARY2_INDEX, LV_PART_MAIN | LV_STATE_USER_1); @@ -71,8 +65,8 @@ class CurveButton : public Button // Preview preview = new CurveRenderer( this, - {PREVIEW_PAD, PREVIEW_PAD + TITLE_H, width() - PREVIEW_PAD * 2 - 4, - width() - PREVIEW_PAD * 2 - 4}, + {PAD_MEDIUM, PAD_MEDIUM + TITLE_H, width() - PAD_MEDIUM * 2 - 4, + width() - PAD_MEDIUM * 2 - 4}, [=](int x) -> int { return applyCustomCurve(x, index); }); // Curve characteristics @@ -85,6 +79,11 @@ class CurveButton : public Button void update() { preview->update(); } + static LAYOUT_VAL(TITLE_H, 20, 20) + static LAYOUT_VAL(INFO_H, 27, 27) + static LAYOUT_VAL(CURVE_BTN_W, 142, 142) + static constexpr coord_t CURVE_BTH_H = CURVE_BTN_W + TITLE_H + INFO_H - PAD_MEDIUM; + protected: uint8_t index; StaticText *title; @@ -209,7 +208,7 @@ void ModelCurvesPage::plusPopup(Window *window) void ModelCurvesPage::build(Window *window) { -#if LCD_W > LCD_H +#if !PORTRAIT_LCD static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; #else @@ -239,7 +238,7 @@ void ModelCurvesPage::build(Window *window) // Curve drawing auto button = - new CurveButton(line, rect_t{0, 0, CURVE_BTN_W, CURVE_BTH_H}, index); + new CurveButton(line, rect_t{0, 0, CurveButton::CURVE_BTN_W, CurveButton::CURVE_BTH_H}, index); button->setPressHandler([=]() -> uint8_t { Menu *menu = new Menu(window); menu->setTitle(STR_CURVE); @@ -296,7 +295,7 @@ void ModelCurvesPage::build(Window *window) LV_GRID_ALIGN_SPACE_BETWEEN); } - addButton = new TextButton(line, rect_t{0, 0, CURVE_BTN_W, CURVE_BTH_H}, + addButton = new TextButton(line, rect_t{0, 0, CurveButton::CURVE_BTN_W, CurveButton::CURVE_BTH_H}, LV_SYMBOL_PLUS, [=]() { plusPopup(window); return 0; diff --git a/radio/src/gui/colorlcd/model_flightmodes.cpp b/radio/src/gui/colorlcd/model_flightmodes.cpp index 531c7ce02ca..4d17712f4ed 100644 --- a/radio/src/gui/colorlcd/model_flightmodes.cpp +++ b/radio/src/gui/colorlcd/model_flightmodes.cpp @@ -49,7 +49,7 @@ static const lv_coord_t line_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), static const lv_coord_t line_row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TRIMS_PER_LINE 2 static const lv_coord_t trims_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; @@ -75,7 +75,7 @@ class TrimEdit : public Window lastTrim = tr->value; auto tr_btn = new TextButton( - this, rect_t{0, 0, 65, 0}, getSourceString(MIXSRC_FIRST_TRIM + trimId), + this, rect_t{0, 0, TR_BTN_W, 0}, getSourceString(MIXSRC_FIRST_TRIM + trimId), [=]() { tr->mode = (tr->mode == TRIM_MODE_NONE) ? 0 : TRIM_MODE_NONE; tr_mode->setValue(tr->mode); @@ -86,7 +86,7 @@ class TrimEdit : public Window if (tr->mode != TRIM_MODE_NONE) tr_btn->check(); - tr_mode = new Choice(this, rect_t{0, 0, 70, 0}, 0, 2 * MAX_FLIGHT_MODES, + tr_mode = new Choice(this, rect_t{0, 0, TR_MODE_W, 0}, 0, 2 * MAX_FLIGHT_MODES, GET_DEFAULT(tr->mode), [=](int val) { tr->mode = val; showControls(); @@ -102,12 +102,15 @@ class TrimEdit : public Window }); tr_value = new NumberEdit( - this, rect_t{0, 0, 70, 0}, g_model.extendedTrims ? -512 : -128, + this, rect_t{0, 0, TR_MODE_W, 0}, g_model.extendedTrims ? -512 : -128, g_model.extendedTrims ? 512 : 128, GET_SET_DEFAULT(tr->value)); showControls(); } + static LAYOUT_VAL(TR_BTN_W, 65, 65) + static LAYOUT_VAL(TR_MODE_W, 70, 70) + protected: int trimId; int fmId; @@ -197,155 +200,6 @@ class FlightModeEdit : public Page uint8_t index; }; -#if LCD_W > LCD_H // Landscape - -#define BTN_H 36 -#define FMID_W 36 -#define NAME_W 95 -#define NAME_Y 8 -#define SWTCH_Y 6 -#define MAX_FMTRIMS 6 -#define TRIM_W 30 -#define TRIM_X (SWTCH_X + SWTCH_W + 2) -#define TRIM_Y 0 -#define FADE_Y 6 - -#else // Portrait - -#define BTN_H 56 -#define FMID_W 46 -#define NAME_W 160 -#define NAME_Y 0 -#define SWTCH_Y 0 -#define MAX_FMTRIMS 4 -#define TRIM_W 40 -#define TRIM_X (FMID_X + FMID_W + 2) -#define TRIM_Y 20 -#define FADE_Y 24 - -#endif - -#define FMID_X 2 -#define FMID_Y (BTN_H / 2 - 12) -#define NAME_X (FMID_X + FMID_W + 2) -#define SWTCH_W 50 -#define SWTCH_X (NAME_X + NAME_W + 2) -#define TRIMC_W (MAX_FMTRIMS * TRIM_W) -#define FADE_W 45 -#define FADE_X (TRIM_X + TRIMC_W + 2) - -static void fm_id_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) -{ - etx_obj_add_style(obj, styles->text_align_left, LV_PART_MAIN); -} - -static const lv_obj_class_t fm_id_class = { - .base_class = &lv_label_class, - .constructor_cb = fm_id_constructor, - .destructor_cb = nullptr, - .user_data = nullptr, - .event_cb = nullptr, - .width_def = FMID_W, - .height_def = PAGE_LINE_HEIGHT, - .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, - .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, - .instance_size = sizeof(lv_label_t), -}; - -static void fm_name_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) -{ - etx_obj_add_style(obj, styles->text_align_left, LV_PART_MAIN); - etx_font(obj, FONT_XS_INDEX); -} - -static const lv_obj_class_t fm_name_class = { - .base_class = &lv_label_class, - .constructor_cb = fm_name_constructor, - .destructor_cb = nullptr, - .user_data = nullptr, - .event_cb = nullptr, - .width_def = NAME_W, - .height_def = PAGE_LINE_HEIGHT, - .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, - .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, - .instance_size = sizeof(lv_label_t), -}; - -static void fm_switch_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) -{ - etx_obj_add_style(obj, styles->text_align_left, LV_PART_MAIN); -} - -static const lv_obj_class_t fm_switch_class = { - .base_class = &lv_label_class, - .constructor_cb = fm_switch_constructor, - .destructor_cb = nullptr, - .user_data = nullptr, - .event_cb = nullptr, - .width_def = SWTCH_W, - .height_def = PAGE_LINE_HEIGHT, - .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, - .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, - .instance_size = sizeof(lv_label_t), -}; - -static void fm_fade_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) -{ - etx_obj_add_style(obj, styles->text_align_right, LV_PART_MAIN); -} - -static const lv_obj_class_t fm_fade_class = { - .base_class = &lv_label_class, - .constructor_cb = fm_fade_constructor, - .destructor_cb = nullptr, - .user_data = nullptr, - .event_cb = nullptr, - .width_def = FADE_W, - .height_def = PAGE_LINE_HEIGHT, - .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, - .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, - .instance_size = sizeof(lv_label_t), -}; - -static void fm_trim_mode_constructor(const lv_obj_class_t* class_p, - lv_obj_t* obj) -{ - etx_obj_add_style(obj, styles->text_align_center, LV_PART_MAIN); -} - -static const lv_obj_class_t fm_trim_mode_class = { - .base_class = &lv_label_class, - .constructor_cb = fm_trim_mode_constructor, - .destructor_cb = nullptr, - .user_data = nullptr, - .event_cb = nullptr, - .width_def = TRIM_W, - .height_def = 16, - .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, - .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, - .instance_size = sizeof(lv_label_t), -}; - -static void fm_trim_value_constructor(const lv_obj_class_t* class_p, - lv_obj_t* obj) -{ - etx_obj_add_style(obj, styles->text_align_center, LV_PART_MAIN); - etx_font(obj, FONT_XS_INDEX); -} - -static const lv_obj_class_t fm_trim_value_class = { - .base_class = &lv_label_class, - .constructor_cb = fm_trim_value_constructor, - .destructor_cb = nullptr, - .user_data = nullptr, - .event_cb = nullptr, - .width_def = TRIM_W, - .height_def = 16, - .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, - .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, - .instance_size = sizeof(lv_label_t), -}; - class FlightModeBtn : public ListLineButton { public: @@ -391,7 +245,7 @@ class FlightModeBtn : public ListLineButton fmTrimMode[i] = etx_create(&fm_trim_mode_class, lvobj); lv_obj_set_pos(fmTrimMode[i], TRIM_X + i * TRIM_W, TRIM_Y); fmTrimValue[i] = etx_create(&fm_trim_value_class, lvobj); - lv_obj_set_pos(fmTrimValue[i], TRIM_X + i * TRIM_W, TRIM_Y + 16); + lv_obj_set_pos(fmTrimValue[i], TRIM_X + i * TRIM_W, TRIM_Y + TRIM_H); } fmFadeIn = etx_create(&fm_fade_class, lvobj); @@ -468,6 +322,26 @@ class FlightModeBtn : public ListLineButton formatNumberAsString(fm.fadeOut, PREC1, 0, nullptr, "s").c_str()); } + static LAYOUT_VAL(BTN_H, 36, 56) + static LAYOUT_VAL(MAX_FMTRIMS, 6, 4) + static constexpr coord_t FMID_X = PAD_TINY; + static LAYOUT_VAL(FMID_Y, 6, 16) + static LAYOUT_VAL(FMID_W, 36, 46) + static constexpr coord_t NAME_X = FMID_X + FMID_W + PAD_TINY; + static LAYOUT_VAL(NAME_Y, 8, 0) + static LAYOUT_VAL(NAME_W, 95, 160) + static constexpr coord_t SWTCH_X = NAME_X + NAME_W + PAD_TINY; + static LAYOUT_VAL(SWTCH_Y, 6, 0) + static LAYOUT_VAL(SWTCH_W, 50, 50) + static LAYOUT_VAL(TRIM_X, SWTCH_X + SWTCH_W + PAD_TINY, FMID_X + FMID_W + PAD_TINY) + static LAYOUT_VAL(TRIM_Y, 0, 20) + static LAYOUT_VAL(TRIM_W, 30, 40) + static LAYOUT_VAL(TRIM_H, 16, 16) + static constexpr coord_t TRIMC_W = MAX_FMTRIMS * TRIM_W; + static constexpr coord_t FADE_X = TRIM_X + TRIMC_W + PAD_TINY; + static LAYOUT_VAL(FADE_Y, 6, 24) + static LAYOUT_VAL(FADE_W, 45, 45) + protected: bool init = false; bool refreshing = false; @@ -480,6 +354,125 @@ class FlightModeBtn : public ListLineButton lv_obj_t* fmFadeIn = nullptr; lv_obj_t* fmFadeOut = nullptr; int lastTrim[MAX_FMTRIMS]; + + static const lv_obj_class_t fm_id_class; + static const lv_obj_class_t fm_name_class; + static const lv_obj_class_t fm_switch_class; + static const lv_obj_class_t fm_fade_class; + static const lv_obj_class_t fm_trim_mode_class; + static const lv_obj_class_t fm_trim_value_class; +}; + +static void fm_id_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) +{ + etx_obj_add_style(obj, styles->text_align_left, LV_PART_MAIN); +} + +const lv_obj_class_t FlightModeBtn::fm_id_class = { + .base_class = &lv_label_class, + .constructor_cb = fm_id_constructor, + .destructor_cb = nullptr, + .user_data = nullptr, + .event_cb = nullptr, + .width_def = FlightModeBtn::FMID_W, + .height_def = EdgeTxStyles::PAGE_LINE_HEIGHT, + .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, + .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, + .instance_size = sizeof(lv_label_t), +}; + +static void fm_name_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) +{ + etx_obj_add_style(obj, styles->text_align_left, LV_PART_MAIN); + etx_font(obj, FONT_XS_INDEX); +} + +const lv_obj_class_t FlightModeBtn::fm_name_class = { + .base_class = &lv_label_class, + .constructor_cb = fm_name_constructor, + .destructor_cb = nullptr, + .user_data = nullptr, + .event_cb = nullptr, + .width_def = FlightModeBtn::NAME_W, + .height_def = EdgeTxStyles::PAGE_LINE_HEIGHT, + .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, + .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, + .instance_size = sizeof(lv_label_t), +}; + +static void fm_switch_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) +{ + etx_obj_add_style(obj, styles->text_align_left, LV_PART_MAIN); +} + +const lv_obj_class_t FlightModeBtn::fm_switch_class = { + .base_class = &lv_label_class, + .constructor_cb = fm_switch_constructor, + .destructor_cb = nullptr, + .user_data = nullptr, + .event_cb = nullptr, + .width_def = FlightModeBtn::SWTCH_W, + .height_def = EdgeTxStyles::PAGE_LINE_HEIGHT, + .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, + .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, + .instance_size = sizeof(lv_label_t), +}; + +static void fm_fade_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) +{ + etx_obj_add_style(obj, styles->text_align_right, LV_PART_MAIN); +} + +const lv_obj_class_t FlightModeBtn::fm_fade_class = { + .base_class = &lv_label_class, + .constructor_cb = fm_fade_constructor, + .destructor_cb = nullptr, + .user_data = nullptr, + .event_cb = nullptr, + .width_def = FlightModeBtn::FADE_W, + .height_def = EdgeTxStyles::PAGE_LINE_HEIGHT, + .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, + .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, + .instance_size = sizeof(lv_label_t), +}; + +static void fm_trim_mode_constructor(const lv_obj_class_t* class_p, + lv_obj_t* obj) +{ + etx_obj_add_style(obj, styles->text_align_center, LV_PART_MAIN); +} + +const lv_obj_class_t FlightModeBtn::fm_trim_mode_class = { + .base_class = &lv_label_class, + .constructor_cb = fm_trim_mode_constructor, + .destructor_cb = nullptr, + .user_data = nullptr, + .event_cb = nullptr, + .width_def = FlightModeBtn::TRIM_W, + .height_def = FlightModeBtn::TRIM_H, + .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, + .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, + .instance_size = sizeof(lv_label_t), +}; + +static void fm_trim_value_constructor(const lv_obj_class_t* class_p, + lv_obj_t* obj) +{ + etx_obj_add_style(obj, styles->text_align_center, LV_PART_MAIN); + etx_font(obj, FONT_XS_INDEX); +} + +const lv_obj_class_t FlightModeBtn::fm_trim_value_class = { + .base_class = &lv_label_class, + .constructor_cb = fm_trim_value_constructor, + .destructor_cb = nullptr, + .user_data = nullptr, + .event_cb = nullptr, + .width_def = FlightModeBtn::TRIM_W, + .height_def = FlightModeBtn::TRIM_H, + .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, + .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, + .instance_size = sizeof(lv_label_t), }; ModelFlightModesPage::ModelFlightModesPage() : @@ -499,8 +492,8 @@ void ModelFlightModesPage::build(Window* form) for (int i = 0; i < MAX_FLIGHT_MODES; i++) { auto btn = new FlightModeBtn(form, i); - lv_obj_set_pos(btn->getLvObj(), 6, i * (BTN_H + 3) + 4); - btn->setWidth(LCD_W - 12); + lv_obj_set_pos(btn->getLvObj(), PAD_MEDIUM, i * (FlightModeBtn::BTN_H + 3) + 4); + btn->setWidth(ListLineButton::GRP_W); btn->setPressHandler([=]() { new FlightModeEdit(i); @@ -509,7 +502,7 @@ void ModelFlightModesPage::build(Window* form) } trimCheck = new TextButton( - form, rect_t{6, MAX_FLIGHT_MODES * (BTN_H + 3) + 8, LCD_W - 12, 40}, STR_CHECKTRIMS, [&]() -> uint8_t { + form, rect_t{6, MAX_FLIGHT_MODES * (FlightModeBtn::BTN_H + 3) + PAD_LARGE, ListLineButton::GRP_W, TRIM_CHK_H}, STR_CHECKTRIMS, [&]() -> uint8_t { if (trimsCheckTimer) trimsCheckTimer = 0; else diff --git a/radio/src/gui/colorlcd/model_flightmodes.h b/radio/src/gui/colorlcd/model_flightmodes.h index cea2f7db1f3..0c47feef51c 100644 --- a/radio/src/gui/colorlcd/model_flightmodes.h +++ b/radio/src/gui/colorlcd/model_flightmodes.h @@ -33,6 +33,8 @@ class ModelFlightModesPage : public PageTab void build(Window* window) override; + static LAYOUT_VAL(TRIM_CHK_H, 40, 40) + protected: TextButton* trimCheck = nullptr; diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index 24fd7186692..89113d66da4 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -30,18 +30,6 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -#define GVAR_NAME_SIZE 48 -#define GVAR_VAL_H (PAGE_LINE_HEIGHT * 2 - 6) -#if LCD_W > LCD_H -#define BTN_H 38 -#define GVAR_VAL_W 45 -#define GVAR_COLS MAX_FLIGHT_MODES -#else -#define BTN_H (modelFMEnabled() ? 72 : 38) -#define GVAR_VAL_W 50 -#define GVAR_COLS 5 -#endif - #define ETX_STATE_VALUE_SMALL_FONT LV_STATE_USER_1 void getFMExtName(char* dest, int8_t idx) @@ -57,46 +45,6 @@ void getFMExtName(char* dest, int8_t idx) } } -static void gv_label_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) -{ - etx_obj_add_style(obj, styles->text_align_center, LV_PART_MAIN); - etx_font(obj, FONT_XS_INDEX); - etx_solid_bg(obj, COLOR_THEME_ACTIVE_INDEX, LV_STATE_CHECKED); -} - -static const lv_obj_class_t gv_label_class = { - .base_class = &lv_label_class, - .constructor_cb = gv_label_constructor, - .destructor_cb = nullptr, - .user_data = nullptr, - .event_cb = nullptr, - .width_def = GVAR_VAL_W, - .height_def = PAGE_LINE_HEIGHT - 6, - .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, - .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, - .instance_size = sizeof(lv_label_t), -}; - -static void gv_value_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) -{ - etx_obj_add_style(obj, styles->text_align_center, LV_PART_MAIN); - etx_font(obj, FONT_XS_INDEX, LV_PART_MAIN | ETX_STATE_VALUE_SMALL_FONT); - etx_solid_bg(obj, COLOR_THEME_ACTIVE_INDEX, LV_STATE_CHECKED); -} - -static const lv_obj_class_t gv_value_class = { - .base_class = &lv_label_class, - .constructor_cb = gv_value_constructor, - .destructor_cb = nullptr, - .user_data = nullptr, - .event_cb = nullptr, - .width_def = GVAR_VAL_W, - .height_def = PAGE_LINE_HEIGHT, - .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, - .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, - .instance_size = sizeof(lv_label_t), -}; - class GVarButton : public ListLineButton { public: @@ -105,7 +53,7 @@ class GVarButton : public ListLineButton { padAll(PAD_ZERO); setHeight(BTN_H); - if (!modelFMEnabled()) padLeft(8); + if (!modelFMEnabled()) padLeft(PAD_LARGE); lv_obj_add_event_cb(lvobj, GVarButton::on_draw, LV_EVENT_DRAW_MAIN_BEGIN, nullptr); @@ -118,6 +66,13 @@ class GVarButton : public ListLineButton if (line) line->build(); } + static LAYOUT_VAL(GVAR_NAME_SIZE, 44, 44) + static constexpr coord_t GVAR_VAL_H = EdgeTxStyles::PAGE_LINE_HEIGHT * 2 - PAD_MEDIUM; + static LAYOUT_VAL(GVAR_VAL_W, 45, 50) + static LAYOUT_VAL(GVAR_COLS, MAX_FLIGHT_MODES, 5) + static LAYOUT_VAL(BTN_H, 38, 72) + static LAYOUT_VAL(GVAR_NM_Y, 7, 24) + protected: bool init = false; uint8_t currentFlightMode = 0; // used for checking updates @@ -162,8 +117,8 @@ class GVarButton : public ListLineButton auto nm = lv_label_create(lvobj); lv_label_set_text(nm, getGVarString(index)); - lv_obj_set_pos(nm, 2, (BTN_H - PAGE_LINE_HEIGHT - 4) / 2); - lv_obj_set_size(nm, GVAR_NAME_SIZE, PAGE_LINE_HEIGHT); + lv_obj_set_pos(nm, PAD_TINY, GVAR_NM_Y); + lv_obj_set_size(nm, GVAR_NAME_SIZE, EdgeTxStyles::PAGE_LINE_HEIGHT); if (modelFMEnabled()) { char label[16] = {}; @@ -173,12 +128,12 @@ class GVarButton : public ListLineButton labelTexts[flightMode] = etx_create(&gv_label_class, lvobj); lv_label_set_text(labelTexts[flightMode], label); - lv_obj_set_pos(labelTexts[flightMode], (flightMode % GVAR_COLS) * GVAR_VAL_W + GVAR_NAME_SIZE + 4, + lv_obj_set_pos(labelTexts[flightMode], (flightMode % GVAR_COLS) * GVAR_VAL_W + GVAR_NAME_SIZE + PAD_SMALL, (flightMode / GVAR_COLS) * GVAR_VAL_H); valueTexts[flightMode] = etx_create(&gv_value_class, lvobj); - lv_obj_set_pos(valueTexts[flightMode], (flightMode % GVAR_COLS) * GVAR_VAL_W + GVAR_NAME_SIZE + 4, - (flightMode / GVAR_COLS) * GVAR_VAL_H + PAGE_LINE_HEIGHT - 6); + lv_obj_set_pos(valueTexts[flightMode], (flightMode % GVAR_COLS) * GVAR_VAL_W + GVAR_NAME_SIZE + PAD_SMALL, + (flightMode / GVAR_COLS) * GVAR_VAL_H + EdgeTxStyles::PAGE_LINE_HEIGHT - PAD_MEDIUM); if (flightMode == currentFlightMode) { lv_obj_add_state(valueTexts[flightMode], LV_STATE_CHECKED); @@ -189,7 +144,7 @@ class GVarButton : public ListLineButton } } else { valueTexts[0] = lv_label_create(lvobj); - lv_obj_set_pos(valueTexts[0], GVAR_NAME_SIZE + 6, (BTN_H - PAGE_LINE_HEIGHT - 4) / 2); + lv_obj_set_pos(valueTexts[0], GVAR_NAME_SIZE + PAD_MEDIUM, (BTN_H - EdgeTxStyles::PAGE_LINE_HEIGHT - PAD_SMALL) / 2); updateValueText(0); } @@ -231,6 +186,49 @@ class GVarButton : public ListLineButton bool isActive() const override { return false; } void refresh() override {} + + static const lv_obj_class_t gv_label_class; + static const lv_obj_class_t gv_value_class; +}; + +static void gv_label_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) +{ + etx_obj_add_style(obj, styles->text_align_center, LV_PART_MAIN); + etx_font(obj, FONT_XS_INDEX); + etx_solid_bg(obj, COLOR_THEME_ACTIVE_INDEX, LV_STATE_CHECKED); +} + +const lv_obj_class_t GVarButton::gv_label_class = { + .base_class = &lv_label_class, + .constructor_cb = gv_label_constructor, + .destructor_cb = nullptr, + .user_data = nullptr, + .event_cb = nullptr, + .width_def = GVarButton::GVAR_VAL_W, + .height_def = EdgeTxStyles::PAGE_LINE_HEIGHT - PAD_MEDIUM, + .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, + .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, + .instance_size = sizeof(lv_label_t), +}; + +static void gv_value_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) +{ + etx_obj_add_style(obj, styles->text_align_center, LV_PART_MAIN); + etx_font(obj, FONT_XS_INDEX, LV_PART_MAIN | ETX_STATE_VALUE_SMALL_FONT); + etx_solid_bg(obj, COLOR_THEME_ACTIVE_INDEX, LV_STATE_CHECKED); +} + +const lv_obj_class_t GVarButton::gv_value_class = { + .base_class = &lv_label_class, + .constructor_cb = gv_value_constructor, + .destructor_cb = nullptr, + .user_data = nullptr, + .event_cb = nullptr, + .width_def = GVarButton::GVAR_VAL_W, + .height_def = EdgeTxStyles::PAGE_LINE_HEIGHT, + .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, + .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, + .instance_size = sizeof(lv_label_t), }; class GVarEditWindow : public Page @@ -482,7 +480,7 @@ void ModelGVarsPage::rebuild(Window* window) void ModelGVarsPage::build(Window* window) { - window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY); + window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY_GAP); for (uint8_t index = 0; index < MAX_GVARS; index++) { auto button = new GVarButton(window, rect_t{}, index); diff --git a/radio/src/gui/colorlcd/model_heli.cpp b/radio/src/gui/colorlcd/model_heli.cpp index a7b77c5c35e..8346d0034c5 100644 --- a/radio/src/gui/colorlcd/model_heli.cpp +++ b/radio/src/gui/colorlcd/model_heli.cpp @@ -26,7 +26,7 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -#if LCD_W > LCD_H // landscape +#if !PORTRAIT_LCD // landscape static const lv_coord_t col_dsc[] = {LV_GRID_FR(2), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(2), LV_GRID_TEMPLATE_LAST}; diff --git a/radio/src/gui/colorlcd/model_inputs.cpp b/radio/src/gui/colorlcd/model_inputs.cpp index 688262b4cf1..4c1db5628de 100644 --- a/radio/src/gui/colorlcd/model_inputs.cpp +++ b/radio/src/gui/colorlcd/model_inputs.cpp @@ -400,10 +400,10 @@ void ModelInputsPage::build(Window* window) // reset clipboard _copyMode = 0; - window->setFlexLayout(LV_FLEX_FLOW_COLUMN, 3); + window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY); form = new Window(window, rect_t{}); - form->setFlexLayout(LV_FLEX_FLOW_COLUMN, 3); + form->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY); auto btn = new TextButton(window, rect_t{}, LV_SYMBOL_PLUS, [=]() { newInput(); diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp index b3f5631d3d4..572b700f86c 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -294,54 +294,6 @@ void getsEdgeDelayParam(char* s, LogicalSwitchData* ls) .c_str()); } -#if LCD_W > LCD_H // Landscape - -#define LS_BUTTON_H 32 - -#define NM_Y 4 -#define NM_W 30 -#define FN_W 50 -#define V1_Y NM_Y -#define AND_X (V2_X + V2_W + 2) -#define AND_Y NM_Y -#define DUR_W 40 - -#else // Portrait - -#define LS_BUTTON_H 44 - -#define NM_Y 10 -#define NM_W 36 -#define FN_W 58 -#define V1_Y 0 -#define AND_X (FN_X + FN_W + 2) -#define AND_Y 20 -#define DUR_W 54 - -#endif - -#define NM_X 2 -#define NM_H 20 -#define FN_X (NM_X + NM_W + 2) -#define FN_Y NM_Y -#define FN_H NM_H -#define V1_X (FN_X + FN_W + 2) -#define V1_W 88 -#define V1_H NM_H -#define V2_X (V1_X + V1_W + 2) -#define V2_Y V1_Y -#define V2_W 110 -#define V2_H NM_H -#define AND_W 88 -#define AND_H NM_H -#define DUR_X (AND_X + AND_W + 2) -#define DUR_Y AND_Y -#define DUR_H NM_H -#define DEL_X (DUR_X + DUR_W + 2) -#define DEL_Y AND_Y -#define DEL_H NM_H -#define DEL_W DUR_W - class LogicalSwitchButton : public ListLineButton { public: @@ -502,6 +454,37 @@ class LogicalSwitchButton : public ListLineButton } } + static LAYOUT_VAL(LS_BUTTON_H, 32, 44) + + static constexpr coord_t NM_X = PAD_TINY; + static LAYOUT_VAL(NM_Y, 4, 10) + static LAYOUT_VAL(NM_W, 30, 36) + static LAYOUT_VAL(NM_H, 20, 20) + static constexpr coord_t FN_X = NM_X + NM_W + PAD_TINY; + static constexpr coord_t FN_Y = NM_Y; + static LAYOUT_VAL(FN_W, 50, 58) + static constexpr coord_t FN_H = NM_H; + static constexpr coord_t V1_X = FN_X + FN_W + PAD_TINY; + static LAYOUT_VAL(V1_Y, NM_Y, 0) + static LAYOUT_VAL(V1_W, 88, 88) + static constexpr coord_t V1_H = NM_H; + static constexpr coord_t V2_X = V1_X + V1_W + PAD_TINY; + static constexpr coord_t V2_Y = V1_Y; + static LAYOUT_VAL(V2_W, 110, 110) + static constexpr coord_t V2_H = NM_H; + static LAYOUT_VAL(AND_X, V2_X + V2_W + PAD_TINY, FN_X + FN_W + PAD_TINY) + static LAYOUT_VAL(AND_Y, NM_Y, 20) + static constexpr coord_t AND_W = V1_W; + static constexpr coord_t AND_H = NM_H; + static constexpr coord_t DUR_X = AND_X + AND_W + PAD_TINY; + static constexpr coord_t DUR_Y = AND_Y; + static LAYOUT_VAL(DUR_W, 40, 54) + static constexpr coord_t DUR_H = NM_H; + static constexpr coord_t DEL_X = DUR_X + DUR_W + PAD_TINY; + static constexpr coord_t DEL_Y = AND_Y; + static constexpr coord_t DEL_H = NM_H; + static constexpr coord_t DEL_W = DUR_W; + protected: bool init = false; @@ -596,7 +579,7 @@ void ModelLogicalSwitchesPage::build(Window* window) line = window->newLine(grid); auto button = new LogicalSwitchButton( - line, rect_t{0, 0, window->width() - 12, LS_BUTTON_H}, i); + line, rect_t{0, 0, window->width() - 12, LogicalSwitchButton::LS_BUTTON_H}, i); button->setPressHandler([=]() { Menu* menu = new Menu(window); @@ -659,7 +642,7 @@ void ModelLogicalSwitchesPage::build(Window* window) if (hasEmptySwitch) { line = window->newLine(grid); addButton = - new TextButton(line, rect_t{0, 0, window->width() - 12, LS_BUTTON_H}, + new TextButton(line, rect_t{0, 0, window->width() - 12, LogicalSwitchButton::LS_BUTTON_H}, LV_SYMBOL_PLUS, [=]() { plusPopup(window); return 0; diff --git a/radio/src/gui/colorlcd/model_mixer_scripts.cpp b/radio/src/gui/colorlcd/model_mixer_scripts.cpp index fc4873b2af3..c825aa67dc7 100644 --- a/radio/src/gui/colorlcd/model_mixer_scripts.cpp +++ b/radio/src/gui/colorlcd/model_mixer_scripts.cpp @@ -39,7 +39,7 @@ static const lv_coord_t col_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; // Edit grid -#if LCD_W > LCD_H +#if !PORTRAIT_LCD static const lv_coord_t e_col_dsc[] = {LV_GRID_FR(2), LV_GRID_FR(3), LV_GRID_TEMPLATE_LAST}; #else @@ -181,7 +181,7 @@ class ScriptLineButton : public ListLineButton scriptData(scriptData), runtimeData(runtimeData) { -#if LCD_H > LCD_W +#if PORTRAIT_LCD padTop(5); #endif padLeft(3); diff --git a/radio/src/gui/colorlcd/model_mixes.cpp b/radio/src/gui/colorlcd/model_mixes.cpp index 8cc086656e2..f7f46e7c249 100644 --- a/radio/src/gui/colorlcd/model_mixes.cpp +++ b/radio/src/gui/colorlcd/model_mixes.cpp @@ -33,7 +33,7 @@ class MPlexIcon : public Window { public: MPlexIcon(Window* parent, uint8_t index) : - Window(parent, {0, 0, 25, 29}), + Window(parent, {0, 0, MPLEX_ICON_W, MPLEX_ICON_H}), index(index) { MixData* mix = mixAddress(index); @@ -67,6 +67,9 @@ class MPlexIcon : public Window index = i; } + static LAYOUT_VAL(MPLEX_ICON_W, 25, 25) + static LAYOUT_VAL(MPLEX_ICON_H, 29, 29) + protected: uint8_t index; StaticIcon* icon = nullptr; @@ -136,8 +139,8 @@ class MixLineButton : public InputMixButtonBase void updatePos(coord_t x, coord_t y) override { setPos(x, y); - mplex->setPos(x - 28, y); - mplex->show(y > BTN_H); + mplex->setPos(x - MPLEX_XO, y); + mplex->show(y > ListLineButton::BTN_H); } lv_obj_t* mplexLvObj() const { return mplex->getLvObj(); } @@ -160,6 +163,8 @@ class MixLineButton : public InputMixButtonBase } } + static LAYOUT_VAL(MPLEX_XO, 28, 28) + protected: MPlexIcon* mplex = nullptr; @@ -174,7 +179,7 @@ class MixGroup : public InputMixGroupBase { adjustHeight(); - lv_obj_set_pos(label, 2, -1); + lv_obj_set_pos(label, PAD_TINY, -1); lv_obj_t* chText = nullptr; if (idx >= MIXSRC_FIRST_CH && idx <= MIXSRC_LAST_CH && @@ -183,7 +188,7 @@ class MixGroup : public InputMixGroupBase etx_font(chText, FONT_XS_INDEX); lv_label_set_text_fmt(chText, TR_CH "%" PRIu32, UINT32_C(idx - MIXSRC_FIRST_CH + 1)); - lv_obj_set_pos(chText, 2, 17); + lv_obj_set_pos(chText, PAD_TINY, CHNUM_Y); } refresh(); @@ -192,7 +197,7 @@ class MixGroup : public InputMixGroupBase void enableMixerMonitor() { if (!monitor) - monitor = new MixerChannelBar(this, {LCD_W - 118, 1, 100, 14}, idx - MIXSRC_FIRST_CH); + monitor = new MixerChannelBar(this, {LCD_W - CHBAR_XO, 1, CHBAR_W, CHBAR_H}, idx - MIXSRC_FIRST_CH); monitorVisible = true; monitor->show(); adjustHeight(); @@ -207,15 +212,20 @@ class MixGroup : public InputMixGroupBase void adjustHeight() override { - coord_t y = monitorVisible ? 17 : 2; + coord_t y = monitorVisible ? CHNUM_Y : PAD_TINY; for (auto it = lines.cbegin(); it != lines.cend(); ++it) { auto line = *it; - line->updatePos(LN_X, y); + line->updatePos(InputMixButtonBase::LN_X, y); y += line->height() + 2; } setHeight(y + 4); } + static LAYOUT_VAL(CHNUM_Y, 17, 17) + static LAYOUT_VAL(CHBAR_XO, 118, 118) + static LAYOUT_VAL(CHBAR_W, 100, 100) + static LAYOUT_VAL(CHBAR_H, 14, 14) + protected: MixerChannelBar* monitor = nullptr; bool monitorVisible = false; diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index 89a24785894..9a1aa52d4a3 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -32,55 +32,6 @@ #define ETX_STATE_MINMAX_BOLD LV_STATE_USER_1 #define ETX_STATE_NAME_FONT_SMALL LV_STATE_USER_1 -#define CH_BAR_WIDTH 100 -#define CH_BAR_HEIGHT 16 - -#if LCD_W > LCD_H // Landscape - -#define CH_LINE_H 32 - -#define MIN_Y 4 -#define MAX_W 52 -#define OFF_X (MAX_X + MAX_W + 2) -#define OFF_Y 4 -#define OFF_W 44 -#define BAR_Y 6 - -#else // Portrait - -#define CH_LINE_H 50 - -#define MIN_Y 2 -#define MAX_W 60 -#define OFF_X (SRC_X + SRC_W + 2) -#define OFF_Y 24 -#define OFF_W 52 -#define BAR_Y 4 - -#endif - -#define SRC_X 2 -#define SRC_Y 1 -#define SRC_W 80 -#define SRC_H (CH_LINE_H - 6) -#define MIN_X (SRC_X + SRC_W + 2) -#define MIN_W 52 -#define MIN_H 20 -#define MAX_X (MIN_X + MIN_W + 2) -#define MAX_Y MIN_Y -#define MAX_H 20 -#define OFF_H 20 -#define CTR_X (OFF_X + OFF_W + 2) -#define CTR_Y OFF_Y -#define CTR_W 60 -#define CTR_H 20 -#define REV_X (CTR_X + CTR_W + 2) -#define REV_Y CTR_Y -#define REV_W 16 -#define CRV_X (REV_X + REV_W + 2) -#define CRV_Y (REV_Y + 1) -#define BAR_X (LCD_W - CH_BAR_WIDTH - 17) - class OutputLineButton : public ListLineButton { bool init = false; @@ -136,7 +87,7 @@ class OutputLineButton : public ListLineButton curve = new StaticIcon(this, CRV_X, CRV_Y, ICON_TEXTLINE_CURVE, COLOR_THEME_SECONDARY1); - bar = new OutputChannelBar(this, rect_t{BAR_X, BAR_Y, CH_BAR_WIDTH, CH_BAR_HEIGHT}, + bar = new OutputChannelBar(this, rect_t{BAR_X, PAD_MEDIUM, CH_BAR_WIDTH, CH_BAR_HEIGHT}, index, false, false); refresh(); @@ -156,7 +107,7 @@ class OutputLineButton : public ListLineButton lv_obj_set_pos(source, SRC_X, SRC_Y); lv_obj_set_size(source, SRC_W, SRC_H); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD etx_font(source, FONT_XS_INDEX, ETX_STATE_NAME_FONT_SMALL); lv_obj_set_style_pad_top(source, -2, ETX_STATE_NAME_FONT_SMALL); lv_obj_set_style_text_line_space(source, -3, ETX_STATE_NAME_FONT_SMALL); @@ -205,6 +156,38 @@ class OutputLineButton : public ListLineButton curve->show(output->curve); } + static LAYOUT_VAL(CH_LINE_H, 32, 50) + static LAYOUT_VAL(CH_BAR_WIDTH, 100, 100) + static LAYOUT_VAL(CH_BAR_HEIGHT, 16, 16) + static LAYOUT_VAL(BAR_XO, 17, 17) + static constexpr coord_t BAR_X = LCD_W - CH_BAR_WIDTH - BAR_XO; + + static constexpr coord_t SRC_X = PAD_TINY; + static constexpr coord_t SRC_Y = 1; + static LAYOUT_VAL(SRC_W, 80, 80) + static constexpr coord_t SRC_H = CH_LINE_H - PAD_MEDIUM; + static constexpr coord_t MIN_X = SRC_X + SRC_W + PAD_TINY; + static LAYOUT_VAL(MIN_Y, 4, 2) + static LAYOUT_VAL(MIN_W, 52, 52) + static LAYOUT_VAL(MIN_H, 20, 20) + static constexpr coord_t MAX_X = MIN_X + MIN_W + PAD_TINY; + static constexpr coord_t MAX_Y = MIN_Y; + static LAYOUT_VAL(MAX_W, 52, 60) + static constexpr coord_t MAX_H = MIN_H; + static LAYOUT_VAL(OFF_X, MAX_X + MAX_W + PAD_TINY, SRC_X + SRC_W + PAD_TINY) + static LAYOUT_VAL(OFF_Y, MIN_Y, 24) + static LAYOUT_VAL(OFF_W, 44, 52) + static constexpr coord_t OFF_H = MIN_H; + static constexpr coord_t CTR_X = OFF_X + OFF_W + PAD_TINY; + static constexpr coord_t CTR_Y = OFF_Y; + static LAYOUT_VAL(CTR_W, 60, 60) + static constexpr coord_t CTR_H = MIN_H; + static constexpr coord_t REV_X = CTR_X + CTR_W + PAD_TINY; + static constexpr coord_t REV_Y = CTR_Y; + static LAYOUT_VAL(REV_W, 16, 16) + static constexpr coord_t CRV_X = REV_X + REV_W + PAD_TINY; + static constexpr coord_t CRV_Y = REV_Y + 1; + protected: int value = -10000; @@ -242,34 +225,6 @@ ModelOutputsPage::ModelOutputsPage() : { } -#if LCD_W > LCD_H - -#define ADD_TRIMS_W ((LCD_W / 2) - 10) -#define EXLIM_X (ADD_TRIMS_X + ADD_TRIMS_W + 4) -#define EXLIM_Y 10 -#define EXLIMCB_Y 4 - -#else - -#define ADD_TRIMS_W (LCD_W - 12) -#define EXLIM_X 6 -#define EXLIM_Y (ADD_TRIMS_X + ADD_TRIMS_H + 8) -#define EXLIMCB_Y (ADD_TRIMS_X + ADD_TRIMS_H + 2) - -#endif - -#define ADD_TRIMS_X 6 -#define ADD_TRIMS_Y 4 -#define ADD_TRIMS_H 32 -#define EXLIM_W (EXLIMCB_X - EXLIM_X - 4) -#define EXLIM_H 20 -#define EXLIMCB_X (LCD_W - 58) -#define EXLIMCB_W 52 -#define EXLIMCB_H 32 -#define TRIMB_X 6 -#define TRIMB_Y (EXLIMCB_Y + EXLIMCB_H + 3) -#define TRIMB_W (LCD_W - 12) - void ModelOutputsPage::build(Window* window) { window->padAll(PAD_ZERO); @@ -286,7 +241,7 @@ void ModelOutputsPage::build(Window* window) for (uint8_t ch = 0; ch < MAX_OUTPUT_CHANNELS; ch++) { // Channel settings auto btn = new OutputLineButton(window, ch); - lv_obj_set_pos(btn->getLvObj(), TRIMB_X, TRIMB_Y + (ch * (CH_LINE_H + 2))); + lv_obj_set_pos(btn->getLvObj(), TRIMB_X, TRIMB_Y + (ch * (OutputLineButton::CH_LINE_H + 2))); btn->setWidth(TRIMB_W); LimitData* output = limitAddress(ch); diff --git a/radio/src/gui/colorlcd/model_outputs.h b/radio/src/gui/colorlcd/model_outputs.h index 6c8738b19c1..0d571a08240 100644 --- a/radio/src/gui/colorlcd/model_outputs.h +++ b/radio/src/gui/colorlcd/model_outputs.h @@ -31,6 +31,23 @@ class ModelOutputsPage : public PageTab ModelOutputsPage(); void build(Window* window) override; + static constexpr coord_t ADD_TRIMS_X = PAD_MEDIUM; + static constexpr coord_t ADD_TRIMS_Y = PAD_SMALL; + static LAYOUT_VAL(ADD_TRIMS_W, (LCD_W / 2) - 10, LCD_W - 12) + static constexpr coord_t ADD_TRIMS_H = EdgeTxStyles::UI_ELEMENT_HEIGHT; + static LAYOUT_VAL(EXLIM_XO, 58, 58) + static constexpr coord_t EXLIMCB_X = LCD_W - EXLIM_XO; + static LAYOUT_VAL(EXLIMCB_Y, 4, ADD_TRIMS_X + ADD_TRIMS_H + 2) + static LAYOUT_VAL(EXLIMCB_W, 52, 52) + static constexpr coord_t EXLIMCB_H = EdgeTxStyles::UI_ELEMENT_HEIGHT; + static LAYOUT_VAL(EXLIM_X, ADD_TRIMS_X + ADD_TRIMS_W + PAD_SMALL, 6) + static LAYOUT_VAL(EXLIM_Y, 10, ADD_TRIMS_X + ADD_TRIMS_H + 8) + static constexpr coord_t EXLIM_W = EXLIMCB_X - EXLIM_X - PAD_SMALL; + static LAYOUT_VAL(EXLIM_H, 20, 20) + static constexpr coord_t TRIMB_X = PAD_MEDIUM; + static constexpr coord_t TRIMB_Y = EXLIMCB_Y + EXLIMCB_H + PAD_MEDIUM / 2; + static constexpr coord_t TRIMB_W = LCD_W - PAD_MEDIUM * 2; + protected: void editOutput(uint8_t channel, OutputLineButton* btn); }; diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index e0f8622eacf..31d72b029f1 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -33,8 +33,6 @@ inline tmr10ms_t getTicks() { return g_tmr10ms; } -constexpr int BUTTONS_HEIGHT = 36; - struct ModelButtonLayout { uint16_t width; uint16_t height; @@ -43,34 +41,20 @@ struct ModelButtonLayout { uint16_t font; }; +static LAYOUT_VAL(L0_W, 165, 147) +static LAYOUT_VAL(L0_H, 92, 92) +static LAYOUT_VAL(L1_W, 108, 96) +static LAYOUT_VAL(L1_H, 61, 61) +static LAYOUT_VAL(L3_W, 336, 300) +static LAYOUT_VAL(MODEL_CELL_PADDING, 4, 4) + ModelButtonLayout modelLayouts[] = { -#if LCD_W > LCD_H // Landscape - {165, 92, 6, true, FONT(STD)}, - {108, 61, 6, true, FONT(XS)}, - {165, 32, 4, false, FONT(STD)}, - {336, 32, 4, false, FONT(STD)}, -#else // Portrait - {147, 92, 6, true, FONT(STD)}, - {96, 61, 6, true, FONT(XS)}, - {147, 32, 4, false, FONT(STD)}, - {300, 32, 4, false, FONT(STD)}, -#endif + {L0_W, L0_H, MODEL_CELL_PADDING, true, FONT(STD)}, + {L1_W, L1_H, MODEL_CELL_PADDING, true, FONT(XS)}, + {L0_W, EdgeTxStyles::UI_ELEMENT_HEIGHT, MODEL_CELL_PADDING, false, FONT(STD)}, + {L3_W, EdgeTxStyles::UI_ELEMENT_HEIGHT, MODEL_CELL_PADDING, false, FONT(STD)}, }; -#if LCD_W > LCD_H // Landscape -constexpr int LABELS_ROW = 0; -constexpr int MODELS_COL = 1; -constexpr int MODELS_ROW = 0; -constexpr int MODELS_ROW_CNT = 2; -constexpr int BUTTONS_ROW = 1; -#else // Portrait -constexpr int LABELS_ROW = 1; -constexpr int MODELS_COL = 0; -constexpr int MODELS_ROW = 0; -constexpr int MODELS_ROW_CNT = 1; -constexpr int BUTTONS_ROW = 2; -#endif - class ModelButton : public Button { public: @@ -697,7 +681,7 @@ void ModelLabelsWindow::buildHead(Window *hdr) setTitle(); // new model button - auto btn = new TextButton(hdr, rect_t{0, 0, 60, 32}, STR_NEW, [=]() { + auto btn = new TextButton(hdr, rect_t{0, 0, NEW_BTN_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_NEW, [=]() { auto menu = new Menu(this); menu->setTitle(STR_CREATE_NEW); menu->addLine(STR_NEW_MODEL, [=]() { newModel(); }); @@ -720,20 +704,18 @@ void ModelLabelsWindow::buildHead(Window *hdr) mdlselector->update(); return 0; }); - lv_obj_set_pos(mdlLayout->getLvObj(), LCD_W - 105, 6); + lv_obj_set_pos(mdlLayout->getLvObj(), LCD_W - LAYOUT_BTN_XO, LAYOUT_BTN_YO); } -#if LCD_W > LCD_H -constexpr int LABELS_WIDTH = 132; -static const lv_coord_t col_dsc[] = {LABELS_WIDTH, LV_GRID_FR(1), +#if !PORTRAIT_LCD +static const lv_coord_t col_dsc[] = {ModelLabelsWindow::LABELS_WIDTH, LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; -static const lv_coord_t row_dsc[] = {LV_GRID_FR(1), BUTTONS_HEIGHT, +static const lv_coord_t row_dsc[] = {LV_GRID_FR(1), ModelLabelsWindow::BUTTONS_HEIGHT, LV_GRID_TEMPLATE_LAST}; #else -constexpr int LABELS_HEIGHT = 140; static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; -static const lv_coord_t row_dsc[] = {LV_GRID_FR(1), LABELS_HEIGHT, - BUTTONS_HEIGHT, LV_GRID_TEMPLATE_LAST}; +static const lv_coord_t row_dsc[] = {LV_GRID_FR(1), ModelLabelsWindow::LABELS_HEIGHT, + ModelLabelsWindow::BUTTONS_HEIGHT, LV_GRID_TEMPLATE_LAST}; #endif void ModelLabelsWindow::buildBody(Window *window) @@ -748,7 +730,7 @@ void ModelLabelsWindow::buildBody(Window *window) mdlselector->setLblRefreshFunc([=]() { labelRefreshRequest(); }); auto mdl_obj = mdlselector->getLvObj(); lv_obj_set_grid_cell(mdl_obj, LV_GRID_ALIGN_STRETCH, MODELS_COL, 1, - LV_GRID_ALIGN_STRETCH, MODELS_ROW, MODELS_ROW_CNT); + LV_GRID_ALIGN_STRETCH, 0, MODELS_ROW_CNT); if (mdlselector->getSortOrder() == NO_SORT) mdlselector->setSortOrder(NAME_ASC); @@ -757,7 +739,7 @@ void ModelLabelsWindow::buildBody(Window *window) auto box = new Window(window, rect_t{}); box->padAll(PAD_ZERO); box->padLeft(6); -#if LCD_H > LCD_W +#if PORTRAIT_LCD box->padRight(6); box->padTop(6); box->padBottom(6); diff --git a/radio/src/gui/colorlcd/model_select.h b/radio/src/gui/colorlcd/model_select.h index c08f8a15ef6..67602fafeff 100644 --- a/radio/src/gui/colorlcd/model_select.h +++ b/radio/src/gui/colorlcd/model_select.h @@ -33,6 +33,17 @@ class ModelLabelsWindow : public Page public: ModelLabelsWindow(); + static LAYOUT_VAL(BUTTONS_HEIGHT, 36, 36) + static LAYOUT_VAL(LABELS_WIDTH, 132, 0) + static LAYOUT_VAL(LABELS_HEIGHT, 0, 152) + static LAYOUT_VAL(LABELS_ROW, 0, 1) + static LAYOUT_VAL(MODELS_COL, 1, 0) + static LAYOUT_VAL(MODELS_ROW_CNT, 2, 1) + static LAYOUT_VAL(BUTTONS_ROW, 1, 2) + static LAYOUT_VAL(NEW_BTN_W, 60, 60) + static LAYOUT_VAL(LAYOUT_BTN_XO, 105, 105) + static LAYOUT_VAL(LAYOUT_BTN_YO, 6, 6) + protected: ModelsSortBy sort = DEFAULT_MODEL_SORT; char tmpLabel[LABEL_LENGTH + 1] = "\0"; diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 200cd8cc084..7a7ff21ac9f 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -111,7 +111,7 @@ class SubScreenButton : public TextButton m_isActive(std::move(checkActive)) { // Room for two lines of text - setHeight(62); + setHeight(SUBSCR_BTN_H); setWidth((LCD_W - 30) / 3); lv_obj_set_width(label, lv_pct(100)); @@ -122,6 +122,8 @@ class SubScreenButton : public TextButton check(isActive()); } + static LAYOUT_VAL(SUBSCR_BTN_H, 62, 62) + protected: std::function m_isActive = nullptr; @@ -272,14 +274,6 @@ class ModelViewOptions : public Page } }; -#if LCD_W > LCD_H -#define SW_BTNS 8 -#define SW_BTN_W 56 -#else -#define SW_BTNS 4 -#define SW_BTN_W 72 -#endif - struct CenterBeepsMatrix : public ButtonMatrix { CenterBeepsMatrix(Window* parent, const rect_t& rect) : ButtonMatrix(parent, rect) @@ -351,6 +345,10 @@ struct CenterBeepsMatrix : public ButtonMatrix { setChecked(btn_id); } + static LAYOUT_VAL(SW_BTNS, 8, 4) + static LAYOUT_VAL(SW_BTN_W, 56, 72) + static LAYOUT_VAL(SW_BTN_H, 36, 36) + private: uint8_t max_analogs; uint8_t ana_idx[MAX_ANALOG_INPUTS]; @@ -364,7 +362,7 @@ class ModelOtherOptions : public Page header->setTitle(STR_MENU_MODEL_SETUP); header->setTitle2(STR_MENU_OTHER); - body->padAll(PAD_SMALL); + body->padAll(PAD_TINY); body->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO); diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index 496de81c2a1..288d13345cf 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -36,7 +36,7 @@ std::string getSensorCustomValue(uint8_t sensor, int32_t value, LcdFlags flags); -#if (LCD_H > LCD_W) || defined(TRANSLATIONS_CZ) +#if (PORTRAIT_LCD) || defined(TRANSLATIONS_CZ) #define TWOCOLBUTTONS 1 #else #define TWOCOLBUTTONS 0 @@ -64,10 +64,6 @@ static const lv_coord_t e_col_dsc2[] = {LV_GRID_FR(4), LV_GRID_FR(3), static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; -#define BTN_H 32 -#define NUM_W 36 -#define NAME_W 56 - class TSStyle { public: @@ -94,6 +90,13 @@ class TSStyle lv_style_t tsFreshStyle; + static LAYOUT_VAL(NUM_W, 36, 36) + static LAYOUT_VAL(NUM_H, 20, 20) + static LAYOUT_VAL(NAME_W, 56, 56) + static LAYOUT_VAL(ID_Y, 17, 17) + static LAYOUT_VAL(ID_H, 11, 11) + static LAYOUT_VAL(FRSH_Y, 10, 10) + private: bool styleInitDone; }; @@ -112,8 +115,8 @@ static const lv_obj_class_t ts_num_class = { .destructor_cb = nullptr, .user_data = nullptr, .event_cb = nullptr, - .width_def = NUM_W, - .height_def = 20, + .width_def = TSStyle::NUM_W, + .height_def = TSStyle::NUM_H, .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, .instance_size = sizeof(lv_label_t), @@ -139,8 +142,8 @@ static const lv_obj_class_t ts_id_class = { .destructor_cb = nullptr, .user_data = nullptr, .event_cb = nullptr, - .width_def = NUM_W, - .height_def = 11, + .width_def = TSStyle::NUM_W, + .height_def = TSStyle::ID_H, .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, .instance_size = sizeof(lv_label_t), @@ -165,8 +168,8 @@ static const lv_obj_class_t ts_name_class = { .destructor_cb = nullptr, .user_data = nullptr, .event_cb = nullptr, - .width_def = NAME_W, - .height_def = 20, + .width_def = TSStyle::NAME_W, + .height_def = TSStyle::NUM_H, .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, .instance_size = sizeof(lv_label_t), @@ -193,7 +196,7 @@ static const lv_obj_class_t ts_value_class = { .user_data = nullptr, .event_cb = nullptr, .width_def = LV_SIZE_CONTENT, - .height_def = 20, + .height_def = TSStyle::NUM_H, .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, .instance_size = sizeof(lv_label_t), @@ -220,7 +223,7 @@ static void ts_fresh_icon_constructor(const lv_obj_class_t* class_p, }; etx_obj_add_style(obj, tsStyle.tsFreshStyle, LV_PART_MAIN); - lv_canvas_set_buffer(obj, (void*)freshBitmap, 8, 8, LV_IMG_CF_ALPHA_8BIT); + lv_canvas_set_buffer(obj, (void*)freshBitmap, PAD_LARGE, PAD_LARGE, LV_IMG_CF_ALPHA_8BIT); lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); } @@ -230,8 +233,8 @@ static const lv_obj_class_t ts_fresh_icon_class = { .destructor_cb = nullptr, .user_data = nullptr, .event_cb = nullptr, - .width_def = 8, - .height_def = 8, + .width_def = PAD_LARGE, + .height_def = PAD_LARGE, .editable = LV_OBJ_CLASS_EDITABLE_FALSE, .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, .instance_size = sizeof(lv_canvas_t), @@ -244,7 +247,7 @@ class SensorButton : public ListLineButton ListLineButton(parent, index) { padAll(PAD_ZERO); - setHeight(BTN_H); + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT); check(isActive()); @@ -301,27 +304,27 @@ class SensorButton : public ListLineButton init = true; numLabel = tsStyle.newNum(lvobj, index); - lv_obj_set_pos(numLabel, 2, 3); + lv_obj_set_pos(numLabel, PAD_TINY, PAD_MEDIUM/2); TelemetrySensor* sensor = &g_model.telemetrySensors[index]; if (sensor->type == TELEM_TYPE_CUSTOM) { sprintf(s, "ID: %d", sensor->instance); idLabel = tsStyle.newId(lvobj, s); - lv_obj_set_pos(idLabel, 2, 17); + lv_obj_set_pos(idLabel, PAD_TINY, TSStyle::ID_Y); } setNumIdState(); strAppend(s, g_model.telemetrySensors[index].label, TELEM_LABEL_LEN); lv_obj_t* nm = tsStyle.newName(lvobj, s); - lv_obj_set_pos(nm, NUM_W + 4, 3); + lv_obj_set_pos(nm, TSStyle::NUM_W + PAD_SMALL, PAD_MEDIUM/2); fresh = etx_create(&ts_fresh_icon_class, lvobj); - lv_obj_set_pos(fresh, NUM_W + NAME_W + 6, 10); + lv_obj_set_pos(fresh, TSStyle::NUM_W + TSStyle::NAME_W + PAD_MEDIUM, TSStyle::FRSH_Y); valLabel = tsStyle.newValue(lvobj); - lv_obj_set_pos(valLabel, NUM_W + NAME_W + 16, 3); + lv_obj_set_pos(valLabel, TSStyle::NUM_W + TSStyle::NAME_W + PAD_LARGE * 2, PAD_MEDIUM/2); lv_obj_update_layout(lvobj); } @@ -669,7 +672,7 @@ class SensorEditWindow : public Page new StaticText(paramLines[P_ID], rect_t{}, STR_ID); auto num = new NumberEdit(paramLines[P_ID], rect_t{}, 0, 0xFFFF, GET_SET_DEFAULT(sensor->id)); -#if LCD_H > LCD_W +#if PORTRAIT_LCD // Portrait layout - need to limit width of edit box num->setWidth((lv_pct(28))); #endif @@ -683,7 +686,7 @@ class SensorEditWindow : public Page }); num = new NumberEdit(paramLines[P_ID], rect_t{}, 0, 0xff, GET_SET_DEFAULT(sensor->instance)); -#if LCD_H > LCD_W +#if PORTRAIT_LCD // Portrait layout - need to limit width of edit box num->setWidth(lv_pct(28)); #endif @@ -928,12 +931,6 @@ void ModelTelemetryPage::buildSensorList(int8_t focusSensorIndex) deleteAll->show(sensorsCount > 0); } -#if LCD_W > LCD_H -#define NUM_EDIT_W 100 -#else -#define NUM_EDIT_W 65 -#endif - void ModelTelemetryPage::build(Window* window) { window->padAll(PAD_TINY); diff --git a/radio/src/gui/colorlcd/model_telemetry.h b/radio/src/gui/colorlcd/model_telemetry.h index eb9f4fa7410..15258304561 100644 --- a/radio/src/gui/colorlcd/model_telemetry.h +++ b/radio/src/gui/colorlcd/model_telemetry.h @@ -33,6 +33,8 @@ class ModelTelemetryPage : public PageTab void build(Window* window) override; + static LAYOUT_VAL(NUM_EDIT_W, 100, 65) + protected: int lastKnownIndex = 0; Window* window = nullptr; diff --git a/radio/src/gui/colorlcd/model_templates.cpp b/radio/src/gui/colorlcd/model_templates.cpp index 0a306b40b6c..ecf81e9112b 100644 --- a/radio/src/gui/colorlcd/model_templates.cpp +++ b/radio/src/gui/colorlcd/model_templates.cpp @@ -135,7 +135,7 @@ class SelectTemplate : public TemplatePage for (auto name : files) { auto tb = new TextButton( - listWindow, rect_t{0, 0, lv_pct(100), PAGE_LINE_HEIGHT * 2}, name, + listWindow, rect_t{0, 0, lv_pct(100), EdgeTxStyles::PAGE_LINE_HEIGHT * 2}, name, [=]() -> uint8_t { deleteLater(); templateFolderPage->doUpdate(folder, name); @@ -176,7 +176,7 @@ SelectTemplateFolder::SelectTemplateFolder( header->setTitle2(STR_NEW_MODEL); auto tfb = new TextButton(listWindow, - rect_t{0, 0, lv_pct(100), PAGE_LINE_HEIGHT * 2}, + rect_t{0, 0, lv_pct(100), EdgeTxStyles::PAGE_LINE_HEIGHT * 2}, STR_BLANK_MODEL, [=]() -> uint8_t { doUpdate("", ""); return 0; @@ -215,7 +215,7 @@ SelectTemplateFolder::SelectTemplateFolder( if (!strcasecmp(name.c_str(), "WIZARD") == 0) { #endif auto tfb = new TextButton( - listWindow, rect_t{0, 0, lv_pct(100), PAGE_LINE_HEIGHT * 2}, name, + listWindow, rect_t{0, 0, lv_pct(100), EdgeTxStyles::PAGE_LINE_HEIGHT * 2}, name, [=]() -> uint8_t { new SelectTemplate(this, name); return 0; diff --git a/radio/src/gui/colorlcd/model_usbjoystick.cpp b/radio/src/gui/colorlcd/model_usbjoystick.cpp index 1e020635157..1f000e09a77 100644 --- a/radio/src/gui/colorlcd/model_usbjoystick.cpp +++ b/radio/src/gui/colorlcd/model_usbjoystick.cpp @@ -32,7 +32,7 @@ #define ETX_STATE_COLLISION_WARN LV_STATE_USER_1 -#if LCD_W > LCD_H // Landscape +#if !PORTRAIT_LCD // Landscape static const lv_coord_t line_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), @@ -289,7 +289,7 @@ class USBChannelEditWindow : public Page window, {window->getRect().w - USBCH_EDIT_STATUS_BAR_WIDTH - USBCH_EDIT_RIGHT_MARGIN, - 0, USBCH_EDIT_STATUS_BAR_WIDTH, MENU_HEADER_HEIGHT}, + 0, USBCH_EDIT_STATUS_BAR_WIDTH, EdgeTxStyles::MENU_HEADER_HEIGHT}, channel); } @@ -306,7 +306,7 @@ class USBChannelEditWindow : public Page new Choice(line, rect_t{}, STR_VUSBJOYSTICK_CH_MODE, 0, USBJOYS_CH_LAST, GET_DEFAULT(cch->mode), SET_VALUE_WUPDATE(cch->mode)); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = form->newLine(grid); #endif @@ -331,7 +331,7 @@ class USBChannelEditWindow : public Page this->update(); }); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = m_btnModeFrame->newLine(grid); #endif @@ -342,7 +342,7 @@ class USBChannelEditWindow : public Page line = m_btnModeFrame->newLine(grid); new StaticText(line, rect_t{}, STR_USBJOYSTICK_CH_BTNNUM); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = m_btnModeFrame->newLine(grid); #endif _BtnNumSel = new USBChannelButtonSel(line, rect_t{}, channel, @@ -381,7 +381,7 @@ class USBChannelLineButton : public ListLineButton ListLineButton(parent, index) { setHeight(USBCH_LINE_HEIGHT); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD padTop(4); #endif @@ -536,7 +536,7 @@ ModelUSBJoystickPage::ModelUSBJoystickPage() : Page(ICON_MODEL_USB) GET_DEFAULT(g_model.usbJoystickExtMode), SET_VALUE_WUPDATE(g_model.usbJoystickExtMode)); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = body->newLine(grid); #endif @@ -554,7 +554,7 @@ ModelUSBJoystickPage::ModelUSBJoystickPage() : Page(ICON_MODEL_USB) GET_DEFAULT(g_model.usbJoystickCircularCut), SET_VALUE_WUPDATE(g_model.usbJoystickCircularCut)); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = body->newLine(grid); #endif diff --git a/radio/src/gui/colorlcd/output_edit.cpp b/radio/src/gui/colorlcd/output_edit.cpp index 6a70d45e92e..67a3fb2df8e 100644 --- a/radio/src/gui/colorlcd/output_edit.cpp +++ b/radio/src/gui/colorlcd/output_edit.cpp @@ -30,15 +30,10 @@ #define ETX_STATE_MINMAX_HIGHLIGHT LV_STATE_USER_1 -#if (LCD_W > LCD_H) -#define OUTPUT_EDIT_STATUS_BAR_WIDTH 250 -#define OUTPUT_EDIT_STATUS_BAR_MARGIN 3 -#define OUTPUT_EDIT_RIGHT_MARGIN 0 -#else -#define OUTPUT_EDIT_STATUS_BAR_WIDTH 180 -#define OUTPUT_EDIT_STATUS_BAR_MARGIN 0 -#define OUTPUT_EDIT_RIGHT_MARGIN 3 -#endif +// deadband in % for switching direction of Min/Max text and value field highlighting +// 0 = no deadband +// 1..100 = [-DEADBAND; DEADBAND] +#define DEADBAND 0 // deadband in % for switching direction of Min/Max text and value field highlighting // 0 = no deadband @@ -58,6 +53,8 @@ class OutputEditStatusBar : public Window channel, true); } + static LAYOUT_VAL(OUTPUT_EDIT_STATUS_BAR_MARGIN, 3, 0) + protected: ComboChannelBar *channelBar; int8_t _channel; @@ -108,11 +105,11 @@ void OutputEditWindow::buildHeader(Window *window) window, {window->getRect().w - OUTPUT_EDIT_STATUS_BAR_WIDTH - OUTPUT_EDIT_RIGHT_MARGIN, - 0, OUTPUT_EDIT_STATUS_BAR_WIDTH, MENU_HEADER_HEIGHT}, + 0, OUTPUT_EDIT_STATUS_BAR_WIDTH, EdgeTxStyles::MENU_HEADER_HEIGHT}, channel); } -#if LCD_W > LCD_H +#if !PORTRAIT_LCD static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(2), LV_GRID_FR(1), LV_GRID_FR(2), LV_GRID_TEMPLATE_LAST}; diff --git a/radio/src/gui/colorlcd/output_edit.h b/radio/src/gui/colorlcd/output_edit.h index eeb0aa0e5da..95556c9897a 100644 --- a/radio/src/gui/colorlcd/output_edit.h +++ b/radio/src/gui/colorlcd/output_edit.h @@ -31,6 +31,9 @@ class OutputEditWindow : public Page public: explicit OutputEditWindow(uint8_t channel); + static LAYOUT_VAL(OUTPUT_EDIT_STATUS_BAR_WIDTH, 250, 180) + static LAYOUT_VAL(OUTPUT_EDIT_RIGHT_MARGIN, 0, 3) + protected: uint8_t channel; int value = 0; diff --git a/radio/src/gui/colorlcd/page.cpp b/radio/src/gui/colorlcd/page.cpp index cb450628907..5e960c595b9 100644 --- a/radio/src/gui/colorlcd/page.cpp +++ b/radio/src/gui/colorlcd/page.cpp @@ -26,7 +26,7 @@ #include "view_main.h" PageHeader::PageHeader(Page* parent, EdgeTxIcon icon) : - Window(parent, {0, 0, LCD_W, MENU_HEADER_HEIGHT}), + Window(parent, {0, 0, LCD_W, EdgeTxStyles::MENU_HEADER_HEIGHT}), icon(icon) { setWindowFlag(NO_FOCUS | OPAQUE); @@ -37,7 +37,7 @@ PageHeader::PageHeader(Page* parent, EdgeTxIcon icon) : title = new StaticText(this, {PAGE_TITLE_LEFT, PAGE_TITLE_TOP, - LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + LCD_W - PAGE_TITLE_LEFT, EdgeTxStyles::PAGE_LINE_HEIGHT}, "", COLOR_THEME_PRIMARY2); } @@ -45,8 +45,8 @@ StaticText* PageHeader::setTitle2(std::string txt) { if (title2 == nullptr) { title2 = new StaticText(this, - {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + PAGE_LINE_HEIGHT, - LCD_W - PAGE_TITLE_LEFT, PAGE_LINE_HEIGHT}, + {PAGE_TITLE_LEFT, PAGE_TITLE_TOP + EdgeTxStyles::PAGE_LINE_HEIGHT, + LCD_W - PAGE_TITLE_LEFT, EdgeTxStyles::PAGE_LINE_HEIGHT}, "", COLOR_THEME_PRIMARY2); } title2->setText(std::move(txt)); @@ -58,11 +58,11 @@ Page::Page(EdgeTxIcon icon, PaddingSize padding) : { header = new PageHeader(this, icon); body = new Window(this, - {0, MENU_HEADER_HEIGHT, LCD_W, LCD_H - MENU_HEADER_HEIGHT}); + {0, EdgeTxStyles::MENU_HEADER_HEIGHT, LCD_W, LCD_H - EdgeTxStyles::MENU_HEADER_HEIGHT}); body->setWindowFlag(NO_FOCUS); etx_solid_bg(lvobj); - lv_obj_set_style_max_height(body->getLvObj(), LCD_H - MENU_HEADER_HEIGHT, + lv_obj_set_style_max_height(body->getLvObj(), LCD_H - EdgeTxStyles::MENU_HEADER_HEIGHT, LV_PART_MAIN); etx_scrollbar(body->getLvObj()); diff --git a/radio/src/gui/colorlcd/page.h b/radio/src/gui/colorlcd/page.h index cc23a16486a..6a2815e4e92 100644 --- a/radio/src/gui/colorlcd/page.h +++ b/radio/src/gui/colorlcd/page.h @@ -35,6 +35,9 @@ class PageHeader : public Window void setTitle(std::string txt) { title->setText(std::move(txt)); } StaticText* setTitle2(std::string txt); + static LAYOUT_VAL(PAGE_TITLE_LEFT, 50, 50) + static constexpr coord_t PAGE_TITLE_TOP = 2; + protected: EdgeTxIcon icon; StaticText* title; diff --git a/radio/src/gui/colorlcd/popups.h b/radio/src/gui/colorlcd/popups.h index fdcf1eecb73..f093e635f84 100644 --- a/radio/src/gui/colorlcd/popups.h +++ b/radio/src/gui/colorlcd/popups.h @@ -21,6 +21,8 @@ #pragma once +#include + #include #include "libopenui.h" diff --git a/radio/src/gui/colorlcd/preflight_checks.cpp b/radio/src/gui/colorlcd/preflight_checks.cpp index c82107489c2..d6173201ebe 100644 --- a/radio/src/gui/colorlcd/preflight_checks.cpp +++ b/radio/src/gui/colorlcd/preflight_checks.cpp @@ -89,6 +89,10 @@ struct SwitchWarnMatrix : public ButtonMatrix { bool isActive(uint8_t btn_id); void setTextAndState(uint8_t btn_id); + static LAYOUT_VAL(SW_BTNS, 8, 4) + static LAYOUT_VAL(SW_BTN_W, 56, 72) + static LAYOUT_VAL(SW_BTN_H, 36, 36) + private: uint8_t sw_idx[MAX_SWITCHES]; }; @@ -108,6 +112,8 @@ PreflightChecks::PreflightChecks() : Page(ICON_MODEL_SETUP) header->setTitle(STR_MENU_MODEL_SETUP); header->setTitle2(STR_PREFLIGHT); + body->padAll(PAD_TINY); + body->setFlexLayout(); FlexGridLayout grid(line_col_dsc, line_row_dsc, PAD_TINY); @@ -193,14 +199,6 @@ static std::string switchWarninglabel(swsrc_t index) std::string(getSwitchWarnSymbol(warn_pos)); } -#if LCD_W > LCD_H -#define SW_BTNS 8 -#define SW_BTN_W 56 -#else -#define SW_BTNS 4 -#define SW_BTN_W 72 -#endif - SwitchWarnMatrix::SwitchWarnMatrix(Window* parent, const rect_t& r) : ButtonMatrix(parent, r) { @@ -225,10 +223,10 @@ SwitchWarnMatrix::SwitchWarnMatrix(Window* parent, const rect_t& r) : update(); - lv_obj_set_width(lvobj, min((int)btn_cnt, SW_BTNS) * SW_BTN_W + 4); + lv_obj_set_width(lvobj, min((int)btn_cnt, SW_BTNS) * SW_BTN_W + PAD_SMALL); uint8_t rows = ((btn_cnt - 1) / SW_BTNS) + 1; - lv_obj_set_height(lvobj, (rows * 36) + 4); + lv_obj_set_height(lvobj, (rows * SW_BTN_H) + PAD_SMALL); padAll(PAD_SMALL); } @@ -275,7 +273,7 @@ PotWarnMatrix::PotWarnMatrix(Window* parent, const rect_t& r) : } } - initBtnMap(min((int)btn_cnt, SW_BTNS), btn_cnt); + initBtnMap(min((int)btn_cnt, SwitchWarnMatrix::SW_BTNS), btn_cnt); uint8_t btn_id = 0; for (uint16_t i = 0; i < MAX_POTS; i++) { @@ -287,10 +285,10 @@ PotWarnMatrix::PotWarnMatrix(Window* parent, const rect_t& r) : update(); - lv_obj_set_width(lvobj, min((int)btn_cnt, SW_BTNS) * SW_BTN_W + 4); + lv_obj_set_width(lvobj, min((int)btn_cnt, SwitchWarnMatrix::SW_BTNS) * SwitchWarnMatrix::SW_BTN_W + PAD_SMALL); - uint8_t rows = ((btn_cnt - 1) / SW_BTNS) + 1; - lv_obj_set_height(lvobj, (rows * 36) + 4); + uint8_t rows = ((btn_cnt - 1) / SwitchWarnMatrix::SW_BTNS) + 1; + lv_obj_set_height(lvobj, (rows * SwitchWarnMatrix::SW_BTN_H) + PAD_SMALL); padAll(PAD_SMALL); } diff --git a/radio/src/gui/colorlcd/preview_window.cpp b/radio/src/gui/colorlcd/preview_window.cpp index 4ac9f914ab9..b9648566cec 100644 --- a/radio/src/gui/colorlcd/preview_window.cpp +++ b/radio/src/gui/colorlcd/preview_window.cpp @@ -27,6 +27,7 @@ #include "theme.h" #include "themes/etx_lv_theme.h" #include "trims.h" +#include "topbar_impl.h" extern inline tmr10ms_t getTicks() { return g_tmr10ms; } @@ -151,34 +152,34 @@ PreviewWindow::PreviewWindow(Window *window, rect_t rect, etx_solid_bg(lvobj, COLOR_THEME_SECONDARY3_INDEX); - auto topbar = new Window(this, {0, 0, LV_PCT(100), TOPBAR_ZONE_HEIGHT}); + auto topbar = new Window(this, {0, 0, LV_PCT(100), TopBar::TOPBAR_ZONE_HEIGHT}); etx_solid_bg(topbar->getLvObj(), COLOR_THEME_SECONDARY1_INDEX); - new StaticIcon(topbar, 5, 5, ICON_RADIO, + new StaticIcon(topbar, ICON_X1, ICON_Y, ICON_RADIO, COLOR_THEME_PRIMARY2); - new StaticIcon(topbar, 38, 5, ICON_RADIO_TOOLS, + new StaticIcon(topbar, ICON_X2, ICON_Y, ICON_RADIO_TOOLS, COLOR_THEME_PRIMARY2); - new StaticIcon(topbar, 71, 5, ICON_RADIO_SETUP, + new StaticIcon(topbar, ICON_X3, ICON_Y, ICON_RADIO_SETUP, COLOR_THEME_PRIMARY2); - new StaticText(this, {5, 44, 100, PAGE_LINE_HEIGHT}, STR_THEME_CHECKBOX); - new ThemedCheckBox(this, {100, 40, 40, 28}, true); - new ThemedCheckBox(this, {150, 40, 40, 28}, false); - (new ThemedButton(this, {210, 40, 100, 32}, STR_THEME_ACTIVE, true)) + new StaticText(this, {ICON_X1, CBT_Y, CBT_W, EdgeTxStyles::PAGE_LINE_HEIGHT}, STR_THEME_CHECKBOX); + new ThemedCheckBox(this, {CB1_X, CB_Y, CB_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, true); + new ThemedCheckBox(this, {CB2_X, CB_Y, CB_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, false); + (new ThemedButton(this, {BTN_X, BTN1_Y, BTN_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_THEME_ACTIVE, true)) ->check(true); - new ThemedButton(this, {210, 75, 100, 32}, STR_THEME_REGULAR, false); - new MainViewTrim(this, {5, 75, HORIZONTAL_SLIDERS_WIDTH, 20}, 0, false); - new MainViewSlider(this, {5, 97, HORIZONTAL_SLIDERS_WIDTH, 20}, 0, false); - new StaticText(this, {5, 122, 100, PAGE_LINE_HEIGHT}, STR_THEME_WARNING, + new ThemedButton(this, {BTN_X, BTN2_Y, BTN_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_THEME_REGULAR, false); + new MainViewTrim(this, {ICON_X1, TRIM_Y, MainViewSlider::HORIZONTAL_SLIDERS_WIDTH, TRIM_H}, 0, false); + new MainViewSlider(this, {ICON_X1, SLIDER_Y, MainViewSlider::HORIZONTAL_SLIDERS_WIDTH, TRIM_H}, 0, false); + new StaticText(this, {ICON_X1, TXT1_Y, TXT_W, EdgeTxStyles::PAGE_LINE_HEIGHT}, STR_THEME_WARNING, COLOR_THEME_WARNING); - new StaticText(this, {5, 144, 100, PAGE_LINE_HEIGHT}, STR_THEME_DISABLED, + new StaticText(this, {ICON_X1, TXT2_Y, TXT_W, EdgeTxStyles::PAGE_LINE_HEIGHT}, STR_THEME_DISABLED, COLOR_THEME_DISABLED); - new ThemedTextEdit(this, {5, 170, 100, 0}, STR_THEME_EDIT, true); - new ThemedTextEdit(this, {110, 170, 100, 0}, STR_THEME_FOCUS, false); + new ThemedTextEdit(this, {ICON_X1, EDT_Y, TXT_W, 0}, STR_THEME_EDIT, true); + new ThemedTextEdit(this, {EDT2_X, EDT_Y, TXT_W, 0}, STR_THEME_FOCUS, false); ticks = 0; - dateTime = new HeaderDateTime(this->getLvObj(), width() - 42, 4); + dateTime = new HeaderDateTime(this->getLvObj(), width() - DATE_XO, PAD_SMALL); lv_group_set_default(def_group); diff --git a/radio/src/gui/colorlcd/preview_window.h b/radio/src/gui/colorlcd/preview_window.h index bdd462c5c14..2cdb39b243e 100644 --- a/radio/src/gui/colorlcd/preview_window.h +++ b/radio/src/gui/colorlcd/preview_window.h @@ -34,6 +34,30 @@ class PreviewWindow : public Window void setColorList(std::vector colorList); + static LAYOUT_VAL(ICON_X1, 5, 5) + static LAYOUT_VAL(ICON_X2, 38, 38) + static LAYOUT_VAL(ICON_X3, 71, 71) + static LAYOUT_VAL(ICON_Y, 5, 5) + static LAYOUT_VAL(DATE_XO, 44, 44) + static LAYOUT_VAL(CBT_Y, 44, 44) + static LAYOUT_VAL(CBT_W, 100, 100) + static LAYOUT_VAL(CB1_X, 100, 100) + static LAYOUT_VAL(CB2_X, 154, 154) + static LAYOUT_VAL(CB_Y, 40, 40) + static LAYOUT_VAL(CB_W, 50, 50) + static LAYOUT_VAL(BTN_X, 210, 210) + static LAYOUT_VAL(BTN1_Y, 40, 40) + static LAYOUT_VAL(BTN2_Y, 79, 79) + static LAYOUT_VAL(BTN_W, 100, 100) + static LAYOUT_VAL(TRIM_Y, 79, 79) + static LAYOUT_VAL(TRIM_H, 20, 20) + static LAYOUT_VAL(SLIDER_Y, 101, 101) + static LAYOUT_VAL(TXT1_Y, 122, 122) + static LAYOUT_VAL(TXT2_Y, 144, 144) + static LAYOUT_VAL(TXT_W, 100, 100) + static LAYOUT_VAL(EDT2_X, 114, 114) + static LAYOUT_VAL(EDT_Y, 170, 170) + protected: tmr10ms_t ticks; HeaderDateTime *dateTime; diff --git a/radio/src/gui/colorlcd/radio_calibration.cpp b/radio/src/gui/colorlcd/radio_calibration.cpp index 4b2f682154b..26ba379e60b 100644 --- a/radio/src/gui/colorlcd/radio_calibration.cpp +++ b/radio/src/gui/colorlcd/radio_calibration.cpp @@ -52,13 +52,15 @@ class StickCalibrationWindow : public Window { int32_t x = calibratedAnalogs[stickX]; int32_t y = calibratedAnalogs[stickY]; - coord_t dx = width() / 2 - 9 + (bitmapSize / 2 * x) / RESX; - coord_t dy = height() / 2 - 9 - (bitmapSize / 2 * y) / RESX; + coord_t dx = width() / 2 - CAL_CTR + (CAL_SIZ / 2 * x) / RESX; + coord_t dy = height() / 2 - CAL_CTR - (CAL_SIZ / 2 * y) / RESX; lv_obj_set_pos(calibStick->getLvObj(), dx, dy); } + static LAYOUT_VAL(CAL_CTR, 9, 9) + static LAYOUT_VAL(CAL_SIZ, 68, 68) + protected: - static constexpr coord_t bitmapSize = 68; uint8_t stickX, stickY; StaticLZ4Image *calibStick = nullptr; }; diff --git a/radio/src/gui/colorlcd/radio_diaganas.cpp b/radio/src/gui/colorlcd/radio_diaganas.cpp index c0271fbfa38..5cf63920c32 100644 --- a/radio/src/gui/colorlcd/radio_diaganas.cpp +++ b/radio/src/gui/colorlcd/radio_diaganas.cpp @@ -32,7 +32,7 @@ #define STATSDEPTH 8 // ideally a value of power of 2 -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define GRIDCOLS 10 #define TSI2CEventsCol 5 @@ -83,7 +83,7 @@ class AnaViewWindow : public Window if (i >= pot_offset && (POT_CONFIG(i - pot_offset) == FLEX_NONE)) continue; -#if LCD_W > LCD_H +#if !PORTRAIT_LCD if ((i & 1) == 0) line = newLine(grid); #else line = newLine(grid); @@ -116,7 +116,7 @@ class AnaViewWindow : public Window }, COLOR_THEME_PRIMARY1); etx_obj_add_style(lbl->getLvObj(), (column4size() == 2) ? styles->text_align_left : styles->text_align_right, LV_PART_MAIN); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD lv_obj_set_grid_cell(lbl->getLvObj(), LV_GRID_ALIGN_STRETCH, 3 + (i & 1) * 5, column4size(), LV_GRID_ALIGN_CENTER, 0, 1); @@ -172,7 +172,7 @@ class AnaCalibratedViewWindow : public AnaViewWindow lv_obj_add_flag(touchLines[1], LV_OBJ_FLAG_HIDDEN); line = newLine(grid); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line->padTop(20); #else line->padTop(2); @@ -202,7 +202,7 @@ class AnaCalibratedViewWindow : public AnaViewWindow lv_obj_set_grid_cell(lbl2->getLvObj(), LV_GRID_ALIGN_STRETCH, 0, 5, LV_GRID_ALIGN_CENTER, 0, 1); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = newLine(grid); #endif lbl2 = new StaticText(line, rect_t{}, diff --git a/radio/src/gui/colorlcd/radio_diagkeys.cpp b/radio/src/gui/colorlcd/radio_diagkeys.cpp index 514ebd7c3cb..f00a1c49cf2 100644 --- a/radio/src/gui/colorlcd/radio_diagkeys.cpp +++ b/radio/src/gui/colorlcd/radio_diagkeys.cpp @@ -98,22 +98,22 @@ class RadioKeyDiagsWindow : public Window auto lbl = lv_label_create(obj); lv_label_set_text(lbl, keysGetLabel(k)); - lv_obj_set_pos(lbl, 0, i * FH); + lv_obj_set_pos(lbl, 0, i * EdgeTxStyles::PAGE_LINE_HEIGHT); lbl = lv_label_create(obj); lv_label_set_text(lbl, ""); - lv_obj_set_pos(lbl, 70, i * FH); + lv_obj_set_pos(lbl, 70, i * EdgeTxStyles::PAGE_LINE_HEIGHT); keyValues[i] = lbl; } #if defined(ROTARY_ENCODER_NAVIGATION) && !defined(USE_HATS_AS_KEYS) auto lbl = lv_label_create(obj); lv_label_set_text(lbl, STR_ROTARY_ENCODER); - lv_obj_set_pos(lbl, 0, (i + 1) * FH); + lv_obj_set_pos(lbl, 0, (i + 1) * EdgeTxStyles::PAGE_LINE_HEIGHT); reValue = lv_label_create(obj); lv_label_set_text(reValue, ""); - lv_obj_set_pos(reValue, 70, (i + 1) * FH); + lv_obj_set_pos(reValue, 70, (i + 1) * EdgeTxStyles::PAGE_LINE_HEIGHT); #endif } @@ -129,7 +129,7 @@ class RadioKeyDiagsWindow : public Window if (SWITCH_EXISTS(i)) { auto lbl = lv_label_create(obj); lv_label_set_text(lbl, ""); - lv_obj_set_pos(lbl, 0, row * FH); + lv_obj_set_pos(lbl, 0, row * EdgeTxStyles::PAGE_LINE_HEIGHT); switchValues[i] = lbl; row += 1; } @@ -157,16 +157,16 @@ class RadioKeyDiagsWindow : public Window lbl = lv_label_create(obj); formatNumberAsString(s, 10, i + 1, 0, 10, "T"); lv_label_set_text(lbl, s); - lv_obj_set_pos(lbl, 4, i * FH + FH); + lv_obj_set_pos(lbl, 4, i * EdgeTxStyles::PAGE_LINE_HEIGHT + EdgeTxStyles::PAGE_LINE_HEIGHT); lbl = lv_label_create(obj); lv_label_set_text(lbl, ""); - lv_obj_set_pos(lbl, 60, i * FH + FH); + lv_obj_set_pos(lbl, 60, i * EdgeTxStyles::PAGE_LINE_HEIGHT + EdgeTxStyles::PAGE_LINE_HEIGHT); trimValues[i * 2] = lbl; lbl = lv_label_create(obj); lv_label_set_text(lbl, ""); - lv_obj_set_pos(lbl, 75, i * FH + FH); + lv_obj_set_pos(lbl, 75, i * EdgeTxStyles::PAGE_LINE_HEIGHT + EdgeTxStyles::PAGE_LINE_HEIGHT); trimValues[i * 2 + 1] = lbl; } } diff --git a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp index 8e6414f3572..4ceb3c1ea65 100644 --- a/radio/src/gui/colorlcd/radio_ghost_module_config.cpp +++ b/radio/src/gui/colorlcd/radio_ghost_module_config.cpp @@ -32,7 +32,7 @@ class GhostModuleConfigWindow : public Window GhostModuleConfigWindow(Window* parent, const rect_t& rect) : Window(parent, rect) { -#if LCD_H > LCD_W +#if PORTRAIT_LCD constexpr coord_t xOffset = 20; constexpr coord_t xOffset2 = 140; #else @@ -162,7 +162,7 @@ void RadioGhostModuleConfig::buildBody(Window* window) { window->padAll(PAD_ZERO); new GhostModuleConfigWindow(window, - {0, 0, LCD_W, LCD_H - MENU_HEADER_HEIGHT - 5}); + {0, 0, LCD_W, LCD_H - EdgeTxStyles::MENU_HEADER_HEIGHT - 5}); } #if defined(HARDWARE_KEYS) && !defined(PCBPL18) diff --git a/radio/src/gui/colorlcd/radio_hardware.cpp b/radio/src/gui/colorlcd/radio_hardware.cpp index 2105611d8f6..2e696e247aa 100644 --- a/radio/src/gui/colorlcd/radio_hardware.cpp +++ b/radio/src/gui/colorlcd/radio_hardware.cpp @@ -101,12 +101,12 @@ void RadioHardwarePage::build(Window* window) auto box = hbox(line); auto batMin = new NumberEdit( - box, rect_t{0, 0, 80, 0}, -60 + 90, g_eeGeneral.vBatMax + 29 + 90, + box, rect_t{0, 0, NUM_EDIT_W, 0}, -60 + 90, g_eeGeneral.vBatMax + 29 + 90, GET_SET_WITH_OFFSET(g_eeGeneral.vBatMin, 90), PREC1); batMin->setSuffix("V"); new StaticText(box, rect_t{}, "-"); auto batMax = new NumberEdit( - box, rect_t{0, 0, 80, 0}, g_eeGeneral.vBatMin - 29 + 120, 40 + 120, + box, rect_t{0, 0, NUM_EDIT_W, 0}, g_eeGeneral.vBatMin - 29 + 120, 40 + 120, GET_SET_WITH_OFFSET(g_eeGeneral.vBatMax, 120), PREC1); batMax->setSuffix("V"); @@ -126,7 +126,7 @@ void RadioHardwarePage::build(Window* window) line = window->newLine(grid); new StaticText(line, rect_t{}, STR_BATT_CALIB); box = hbox(line); - new BatCalEdit(box, rect_t{0, 0, 80, 0}); + new BatCalEdit(box, rect_t{0, 0, NUM_EDIT_W, 0}); // RTC Batt check enable line = window->newLine(grid); diff --git a/radio/src/gui/colorlcd/radio_hardware.h b/radio/src/gui/colorlcd/radio_hardware.h index 1f023de6c3c..34cff494a35 100644 --- a/radio/src/gui/colorlcd/radio_hardware.h +++ b/radio/src/gui/colorlcd/radio_hardware.h @@ -27,6 +27,8 @@ class RadioHardwarePage : public PageTab { void checkEvents() override; + static LAYOUT_VAL(NUM_EDIT_W, 80, 80) + public: RadioHardwarePage(); diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index 30c654814e7..a5d17d5d9b0 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -273,7 +273,7 @@ ModuleCallback onUpdateStateChangedCallbackFor(FrskyOtaFlashDialog* dialog) { #endif // PXX2 -#if LCD_W > LCD_H // landscape +#if !PORTRAIT_LCD // landscape static const lv_coord_t col_dsc[] = {LV_GRID_FR(3), LV_GRID_FR(2), LV_GRID_TEMPLATE_LAST}; static const lv_coord_t row_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; #else // portrait @@ -302,7 +302,7 @@ void RadioSdManagerPage::build(Window * window) // Adjust file browser width browser->adjustWidth(); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD preview = new FilePreview(form, rect_t{0, 0, LCD_W * 2 / 5 - 8, LCD_H - 68}); #else preview = new FilePreview(form, rect_t{0, 0, LCD_W - 12, (LCD_H - 68) / 3 }); diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index 0cf844554cd..6dde93faf21 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -38,18 +38,6 @@ static const lv_coord_t col_two_dsc[] = {LV_GRID_FR(19), LV_GRID_FR(21), LV_GRID_TEMPLATE_LAST}; static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; -// Absolute layout for date/time setion due to slow performance -// of lv_textarea in a flex layout. -#if LCD_W > LCD_H -#define DT_EDT_W 80 -#define DT_EDT_X 220 -#define DT_LBL_W 200 -#else -#define DT_EDT_W 52 -#define DT_EDT_X 144 -#define DT_LBL_W 140 -#endif - class DateTimeWindow : public Window { public: @@ -94,6 +82,12 @@ class DateTimeWindow : public Window } } + // Absolute layout for date/time setion due to slow performance + // of lv_textarea in a flex layout. + static LAYOUT_VAL(DT_EDT_W, 80, 52) + static LAYOUT_VAL(DT_EDT_X, 220, 144) + static LAYOUT_VAL(DT_LBL_W, 200, 140) + protected: struct gtm m_tm; struct gtm m_last_tm; @@ -774,10 +768,7 @@ class ManageModelsSetupPage : public SubPage Window* favSelectMatch = nullptr; }; -RadioSetupPage::RadioSetupPage(): - PageTab(STR_RADIO_SETUP, ICON_RADIO_SETUP) -{ -} +RadioSetupPage::RadioSetupPage() : PageTab(STR_RADIO_SETUP, ICON_RADIO_SETUP) {} void RadioSetupPage::build(Window* window) { @@ -882,7 +873,7 @@ void RadioSetupPage::build(Window* window) #if defined(FAI_CHOICE) /* case ITEM_SETUP_FAI: - lcdDrawText(MENUS_MARGIN_LEFT, y, "FAI Mode"); + lcdDrawText(PAD_MEDIUM, y, "FAI Mode"); if (g_eeGeneral.fai) { lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, "Locked in FAI Mode"); } diff --git a/radio/src/gui/colorlcd/radio_theme.cpp b/radio/src/gui/colorlcd/radio_theme.cpp index 06a2b6af93a..3868906319f 100644 --- a/radio/src/gui/colorlcd/radio_theme.cpp +++ b/radio/src/gui/colorlcd/radio_theme.cpp @@ -30,32 +30,6 @@ #include "preview_window.h" #include "themes/etx_lv_theme.h" -constexpr int COLOR_PREVIEW_SIZE = 18; - -#if LCD_W > LCD_H -constexpr int LIST_WIDTH = ((LCD_W - 12) / 2 - COLOR_PREVIEW_SIZE); -constexpr int COLOR_LIST_WIDTH = ((LCD_W * 3) / 10); -#else -constexpr int LIST_HEIGHT = (LCD_H / 2 - 38); -constexpr int COLOR_LIST_HEIGHT = (LCD_H / 2 - 24); -#endif - -#if LCD_W > LCD_H -constexpr int BUTTON_WIDTH = 75; - -constexpr int COLOR_BOX_WIDTH = 45; -#else -constexpr int BUTTON_WIDTH = 65; - -constexpr int COLOR_BOX_WIDTH = 55; -#endif - -constexpr int BUTTON_HEIGHT = 30; -constexpr int COLOR_BOX_HEIGHT = 30; - -constexpr int BOX_MARGIN = 2; -constexpr int MAX_BOX_WIDTH = 15; - class ThemeColorPreview : public Window { public: @@ -66,7 +40,7 @@ class ThemeColorPreview : public Window setWindowFlag(NO_FOCUS); padAll(PAD_ZERO); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD setFlexLayout(LV_FLEX_FLOW_COLUMN, BOX_MARGIN); #else setFlexLayout(LV_FLEX_FLOW_ROW, BOX_MARGIN); @@ -79,7 +53,7 @@ class ThemeColorPreview : public Window clear(); setBoxWidth(); int size = (boxWidth + BOX_MARGIN) * colorList.size() - BOX_MARGIN; -#if LCD_W > LCD_H +#if !PORTRAIT_LCD padTop((height() - size) / 2); #else padLeft((width() - size) / 2); @@ -95,13 +69,16 @@ class ThemeColorPreview : public Window build(); } + static LAYOUT_VAL(MAX_BOX_WIDTH, 15, 15) + static constexpr int BOX_MARGIN = 2; + protected: std::vector colorList; int boxWidth = MAX_BOX_WIDTH; void setBoxWidth() { -#if LCD_W > LCD_H +#if !PORTRAIT_LCD boxWidth = (height() - (colorList.size() - 1) * BOX_MARGIN) / colorList.size(); #else @@ -166,7 +143,7 @@ class ThemeDetailsDialog : public BaseDialog line->padTop(10); auto button = - new TextButton(line, rect_t{0, 0, lv_pct(30), 32}, STR_CANCEL, [=]() { + new TextButton(line, rect_t{0, 0, lv_pct(30), 0}, STR_CANCEL, [=]() { deleteLater(); return 0; }); @@ -174,7 +151,7 @@ class ThemeDetailsDialog : public BaseDialog LV_GRID_ALIGN_CENTER, 0, 1); button = - new TextButton(line, rect_t{0, 0, lv_pct(30), 32}, STR_SAVE, [=]() { + new TextButton(line, rect_t{0, 0, lv_pct(30), 0}, STR_SAVE, [=]() { if (saveHandler != nullptr) if (!saveHandler(this->theme)) return 0; @@ -215,6 +192,17 @@ class ColorEditPage : public Page } } + static LAYOUT_VAL(COLOR_BOX_WIDTH, 45, 55) + static LAYOUT_VAL(COLOR_BOX_HEIGHT, 30, 30) + static LAYOUT_VAL(HEX_STR_W, 95, 95) + static LAYOUT_VAL(BUTTON_WIDTH, 75, 65) + +#if PORTRAIT_LCD + static constexpr int COLOR_LIST_HEIGHT = (LCD_H / 2 - 24); +#else + static constexpr int COLOR_LIST_WIDTH = ((LCD_W * 3) / 10); +#endif + protected: std::function _updateHandler; LcdColorIndex _indexOfColor; @@ -247,22 +235,22 @@ class ColorEditPage : public Page void buildBody(Window *form) { form->padAll(PAD_SMALL); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD form->setFlexLayout(LV_FLEX_FLOW_ROW, PAD_SMALL); - rect_t r = {0, 0, COLOR_LIST_WIDTH, form->height() - 8}; + rect_t r = {0, 0, COLOR_LIST_WIDTH, form->height() - PAD_LARGE}; #else form->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_SMALL); - rect_t r = {0, 0, form->width() - 9, COLOR_LIST_HEIGHT}; + rect_t r = {0, 0, form->width() - PAD_LARGE, COLOR_LIST_HEIGHT}; #endif Window *colForm = new Window(form, r); colForm->padAll(PAD_ZERO); colForm->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_SMALL, r.w); -#if LCD_W > LCD_H - r.w = form->width() - COLOR_LIST_WIDTH - 12; +#if !PORTRAIT_LCD + r.w = form->width() - COLOR_LIST_WIDTH - PAD_MEDIUM * 2; #else - r.h = form->height() - COLOR_LIST_HEIGHT - 12; + r.h = form->height() - COLOR_LIST_HEIGHT - PAD_MEDIUM * 2; #endif _previewWindow = new PreviewWindow(form, r, _theme->getColorList()); @@ -296,7 +284,7 @@ class ColorEditPage : public Page colBoxForm, r, _theme->getColorEntryByIndex(_indexOfColor)->colorValue); // hexBox - r.w = 95; + r.w = HEX_STR_W; _hexBox = new StaticText(colBoxForm, r, "", COLOR_THEME_PRIMARY1 | FONT(L) | RIGHT); setHexStr(_theme->getColorEntryByIndex(_indexOfColor)->colorValue); } @@ -307,14 +295,14 @@ class ColorEditPage : public Page header->setTitle(STR_EDIT_COLOR); auto t2 = header->setTitle2(ThemePersistance::getColorNames()[(int)_indexOfColor]); -#if LCD_H > LCD_W +#if PORTRAIT_LCD etx_font(t2->getLvObj(), FONT_XS_INDEX); #else LV_UNUSED(t2); #endif // page tabs - rect_t r = {LCD_W - 2 * (BUTTON_WIDTH + 5), 6, BUTTON_WIDTH, BUTTON_HEIGHT}; + rect_t r = {LCD_W - 2 * (BUTTON_WIDTH + 5), PAD_MEDIUM, BUTTON_WIDTH, EdgeTxStyles::UI_ELEMENT_HEIGHT}; _tabs.emplace_back(new TextButton(window, r, "RGB", [=]() { setActiveColorBar(0); return 1; @@ -384,12 +372,12 @@ class ThemeEditPage : public Page // page title header->setTitle(STR_EDIT_THEME); _themeName = header->setTitle2(_theme.getName()); -#if LCD_H > LCD_W +#if PORTRAIT_LCD etx_font(_themeName->getLvObj(), FONT_XS_INDEX); #endif // save and cancel - rect_t r = {LCD_W - (BUTTON_WIDTH + 5), 6, BUTTON_WIDTH, BUTTON_HEIGHT}; + rect_t r = {LCD_W - (ColorEditPage::BUTTON_WIDTH + 5), PAD_MEDIUM, ColorEditPage::BUTTON_WIDTH, EdgeTxStyles::UI_ELEMENT_HEIGHT}; new TextButton(window, r, STR_DETAILS, [=]() { new ThemeDetailsDialog(page, _theme, [=](ThemeFile t) { _theme.setAuthor(t.getAuthor()); @@ -408,22 +396,22 @@ class ThemeEditPage : public Page void buildBody(Window *form) { form->padAll(PAD_SMALL); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD form->setFlexLayout(LV_FLEX_FLOW_ROW, PAD_SMALL); - rect_t r = {0, 0, COLOR_LIST_WIDTH, form->height() - 8}; + rect_t r = {0, 0, ColorEditPage::COLOR_LIST_WIDTH, form->height() - PAD_LARGE}; #else form->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_SMALL); - rect_t r = {0, 0, form->width() - 8, COLOR_LIST_HEIGHT}; + rect_t r = {0, 0, form->width() - 8, ColorEditPage::COLOR_LIST_HEIGHT}; #endif _cList = new ColorList(form, r, _theme.getColorList()); _cList->setLongPressHandler([=]() { editColorPage(); }); _cList->setPressHandler([=]() { editColorPage(); }); -#if LCD_W > LCD_H - r.w = form->width() - COLOR_LIST_WIDTH - 12; +#if !PORTRAIT_LCD + r.w = form->width() - ColorEditPage::COLOR_LIST_WIDTH - PAD_MEDIUM * 2; #else - r.h = form->height() - COLOR_LIST_HEIGHT - 12; + r.h = form->height() - ColorEditPage::COLOR_LIST_HEIGHT - PAD_MEDIUM * 2; #endif _previewWindow = new PreviewWindow(form, r, _theme.getColorList()); } @@ -617,7 +605,7 @@ void ThemeSetupPage::build(Window *window) window->padAll(PAD_SMALL); pageWindow = window; -#if LCD_W > LCD_H +#if !PORTRAIT_LCD window->setFlexLayout(LV_FLEX_FLOW_ROW, PAD_TINY); #else window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY); @@ -634,14 +622,14 @@ void ThemeSetupPage::build(Window *window) authorText = nullptr; // create listbox and setup menus -#if LCD_W > LCD_H +#if !PORTRAIT_LCD rect_t r = {0, 0, LIST_WIDTH, window->height() - 8}; #else rect_t r = {0, 0, window->width() - 8, LIST_HEIGHT}; #endif setupListbox(window, r, tp); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD r.w = COLOR_PREVIEW_SIZE; #else r.h = COLOR_PREVIEW_SIZE; @@ -653,7 +641,7 @@ void ThemeSetupPage::build(Window *window) themeColorPreview = new ThemeColorPreview(window, r, colorList); themeColorPreview->setWidth(r.w); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD r.w = window->width() - LIST_WIDTH - COLOR_PREVIEW_SIZE - 12; r.h = window->height() - 8; #else diff --git a/radio/src/gui/colorlcd/radio_theme.h b/radio/src/gui/colorlcd/radio_theme.h index c593861dca5..05178f489c7 100644 --- a/radio/src/gui/colorlcd/radio_theme.h +++ b/radio/src/gui/colorlcd/radio_theme.h @@ -41,6 +41,13 @@ class ThemeSetupPage : public PageTab bool isVisible() const override { return radioThemesEnabled(); } + static LAYOUT_VAL(COLOR_PREVIEW_SIZE, 18, 18) +#if PORTRAIT_LCD + static constexpr int LIST_HEIGHT = (LCD_H / 2 - 38); +#else + static constexpr int LIST_WIDTH = ((LCD_W - PAD_MEDIUM * 2) / 2 - COLOR_PREVIEW_SIZE); +#endif + protected: TabsGroup *tabsGroup = nullptr; Window *pageWindow = nullptr; diff --git a/radio/src/gui/colorlcd/radio_tools.cpp b/radio/src/gui/colorlcd/radio_tools.cpp index 02a6b5deafe..5d60c83fc67 100644 --- a/radio/src/gui/colorlcd/radio_tools.cpp +++ b/radio/src/gui/colorlcd/radio_tools.cpp @@ -180,16 +180,19 @@ struct ToolButton : public TextButton { return 0; }) { - if (LCD_W > LCD_H) - setWidth((LCD_W - 24) / 3); // 3 columns on landscape - else - setWidth((LCD_W - 18) / 2); // 2 columns on portrait - setHeight(48); +#if !PORTRAIT_LCD + setWidth((LCD_W - 24) / 3); // 3 columns on landscape +#else + setWidth((LCD_W - 18) / 2); // 2 columns on portrait +#endif + setHeight(TOOLS_BTN_H); lv_obj_set_width(label, lv_pct(100)); etx_obj_add_style(label, styles->text_align_center, LV_PART_MAIN); lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); } + + static LAYOUT_VAL(TOOLS_BTN_H, 48, 48) }; void RadioToolsPage::rebuild(Window* window) diff --git a/radio/src/gui/colorlcd/radio_trainer.cpp b/radio/src/gui/colorlcd/radio_trainer.cpp index 4be9c3fad21..c8add3e8c40 100644 --- a/radio/src/gui/colorlcd/radio_trainer.cpp +++ b/radio/src/gui/colorlcd/radio_trainer.cpp @@ -34,17 +34,14 @@ RadioTrainerPage::RadioTrainerPage() : { } -#if LCD_W > LCD_H +#if !PORTRAIT_LCD static const lv_coord_t col_dsc[] = {LV_GRID_FR(7), LV_GRID_FR(13), LV_GRID_FR(10), LV_GRID_FR(10), LV_GRID_FR(10), LV_GRID_TEMPLATE_LAST}; - -#define NUM_EDIT_W 80 #else static const lv_coord_t col_dsc[] = {LV_GRID_FR(7), LV_GRID_FR(15), LV_GRID_FR(9), LV_GRID_FR(9), LV_GRID_TEMPLATE_LAST}; -#define NUM_EDIT_W 65 #endif static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; @@ -75,7 +72,7 @@ void RadioTrainerPage::build(Window* form) GET_SET_DEFAULT(td->studWeight)); weight->setSuffix("%"); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = form->newLine(grid); line->padLeft(30); line->padBottom(8); @@ -93,7 +90,7 @@ void RadioTrainerPage::build(Window* form) } auto line = form->newLine(grid); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line->padTop(10); #else line->padTop(6); @@ -113,7 +110,7 @@ void RadioTrainerPage::build(Window* form) lv_obj_set_grid_cell(multiplier->getLvObj(), LV_GRID_ALIGN_START, 2, 1, LV_GRID_ALIGN_CENTER, 0, 1); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = form->newLine(grid); line->padTop(10); #endif @@ -126,7 +123,7 @@ void RadioTrainerPage::build(Window* form) SET_DIRTY(); return 0; }); -#if LCD_H > LCD_W +#if PORTRAIT_LCD lv_obj_set_grid_cell(btn->getLvObj(), LV_GRID_ALIGN_STRETCH, 1, 2, LV_GRID_ALIGN_CENTER, 0, 1); #else diff --git a/radio/src/gui/colorlcd/radio_trainer.h b/radio/src/gui/colorlcd/radio_trainer.h index 969a083a9c0..c54c1309163 100644 --- a/radio/src/gui/colorlcd/radio_trainer.h +++ b/radio/src/gui/colorlcd/radio_trainer.h @@ -32,4 +32,6 @@ class RadioTrainerPage : public PageTab bool isVisible() const override { return radioTrainerEnabled(); } void build(Window* window) override; + + static LAYOUT_VAL(NUM_EDIT_W, 80, 65) }; diff --git a/radio/src/gui/colorlcd/screen_setup.cpp b/radio/src/gui/colorlcd/screen_setup.cpp index 8bbff3160c9..9261ef93c04 100644 --- a/radio/src/gui/colorlcd/screen_setup.cpp +++ b/radio/src/gui/colorlcd/screen_setup.cpp @@ -44,7 +44,7 @@ class LayoutChoice : public Button LayoutChoice(Window* parent, LayoutFactoryGetter getValue, LayoutFactorySetter setValue) : - Button(parent, {0, 0, BM_W + 12, BM_H + 12}), + Button(parent, {0, 0, LayoutFactory::BM_W + 12, LayoutFactory::BM_H + 12}), getValue(std::move(getValue)), _setValue(std::move(setValue)) { @@ -171,7 +171,7 @@ void ScreenAddPage::build(Window* window) }); } -#if LCD_W > LCD_H +#if !PORTRAIT_LCD static const lv_coord_t line_col_dsc[] = {LV_GRID_FR(2), LV_GRID_FR(1), LV_GRID_FR(2), LV_GRID_TEMPLATE_LAST}; #else @@ -236,7 +236,7 @@ void ScreenSetupPage::build(Window* window) Window* btn = new LayoutChoice(line, getFactory, setLayout); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = window->newLine(grid); grid.nextCell(); #endif diff --git a/radio/src/gui/colorlcd/screen_user_interface.cpp b/radio/src/gui/colorlcd/screen_user_interface.cpp index be9a54164e1..a61136f2f14 100644 --- a/radio/src/gui/colorlcd/screen_user_interface.cpp +++ b/radio/src/gui/colorlcd/screen_user_interface.cpp @@ -25,7 +25,7 @@ #include "menu_screen.h" #include "theme_manager.h" -#if LCD_W > LCD_H // landscape +#if !PORTRAIT_LCD // landscape // form grid static const lv_coord_t line_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), @@ -93,7 +93,7 @@ class ThemeView : public Window lv_obj_set_width(description->getLvObj(), lv_pct(100)); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD preview = new FilePreview(line, rect_t{0, 0, LCD_W / 2, LCD_H / 2}); #else preview = new FilePreview(line, rect_t{0, 0, LCD_W - 12, LCD_H / 2}); diff --git a/radio/src/gui/colorlcd/select_fab_carousel.cpp b/radio/src/gui/colorlcd/select_fab_carousel.cpp index 3cd813f2c2d..7f58648fba8 100644 --- a/radio/src/gui/colorlcd/select_fab_carousel.cpp +++ b/radio/src/gui/colorlcd/select_fab_carousel.cpp @@ -25,17 +25,13 @@ #include "button.h" #include "static.h" -constexpr coord_t FAB_BUTTON_INNER_WIDTH = FAB_BUTTON_WIDTH - 4; -constexpr coord_t FAB_ICON_SIZE = 52; -constexpr coord_t FAB_ICON_INNER_SIZE = 52 - 4; - static void etx_quick_button_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) { etx_obj_add_style(obj, styles->border_transparent, LV_PART_MAIN); etx_obj_add_style(obj, styles->rounded, LV_PART_MAIN); etx_txt_color(obj, COLOR_WHITE_INDEX, LV_PART_MAIN); - etx_obj_add_style(obj, styles->pad_large, LV_PART_MAIN); + etx_obj_add_style(obj, styles->pad_medium, LV_PART_MAIN); etx_obj_add_style(obj, styles->border, LV_PART_MAIN | LV_STATE_FOCUSED); etx_obj_add_style(obj, styles->border_color_white, @@ -48,8 +44,8 @@ static const lv_obj_class_t etx_quick_button_class = { .destructor_cb = nullptr, .user_data = nullptr, .event_cb = nullptr, - .width_def = FAB_BUTTON_WIDTH, - .height_def = FAB_BUTTON_HEIGHT, + .width_def = SelectFabCarousel::FAB_BUTTON_WIDTH, + .height_def = SelectFabCarousel::FAB_BUTTON_HEIGHT, .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, .instance_size = sizeof(lv_btn_t), @@ -78,8 +74,8 @@ static const lv_obj_class_t etx_quick_icon_class = { .destructor_cb = nullptr, .user_data = nullptr, .event_cb = nullptr, - .width_def = FAB_ICON_SIZE, - .height_def = FAB_ICON_SIZE, + .width_def = SelectFabCarousel::FAB_ICON_SIZE, + .height_def = SelectFabCarousel::FAB_ICON_SIZE, .editable = LV_OBJ_CLASS_EDITABLE_FALSE, .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE, .instance_size = sizeof(lv_obj_t), @@ -101,16 +97,16 @@ class SelectFabButton : public ButtonBase auto iconLayout = new Window(this, - {(FAB_BUTTON_INNER_WIDTH - FAB_ICON_SIZE) / 2, - (FAB_BUTTON_INNER_WIDTH - FAB_ICON_SIZE) / 2 - 2, - FAB_ICON_SIZE, FAB_ICON_SIZE}, + {(SelectFabCarousel::FAB_BUTTON_INNER_WIDTH - SelectFabCarousel::FAB_ICON_SIZE) / 2, + (SelectFabCarousel::FAB_BUTTON_INNER_WIDTH - SelectFabCarousel::FAB_ICON_SIZE) / 2 - 2, + SelectFabCarousel::FAB_ICON_SIZE, SelectFabCarousel::FAB_ICON_SIZE}, etx_quick_icon_create); iconLayout->setWindowFlag(NO_FOCUS); (new StaticIcon(iconLayout, 0, 0, icon, COLOR_WHITE)) - ->center(FAB_ICON_INNER_SIZE, FAB_ICON_INNER_SIZE); + ->center(SelectFabCarousel::FAB_ICON_SIZE - 4, SelectFabCarousel::FAB_ICON_SIZE - 4); - new StaticText(this, {0, FAB_BUTTON_HEIGHT - 48, FAB_BUTTON_INNER_WIDTH, 0}, + new StaticText(this, {0, SelectFabCarousel::FAB_BUTTON_HEIGHT - SelectFabCarousel::FAB_TXT_YO, SelectFabCarousel::FAB_BUTTON_INNER_WIDTH, 0}, title, COLOR_WHITE | CENTERED); } diff --git a/radio/src/gui/colorlcd/select_fab_carousel.h b/radio/src/gui/colorlcd/select_fab_carousel.h index 56a0fcce27e..35cb5891000 100644 --- a/radio/src/gui/colorlcd/select_fab_carousel.h +++ b/radio/src/gui/colorlcd/select_fab_carousel.h @@ -24,9 +24,6 @@ #include "form.h" #include "bitmaps.h" -constexpr coord_t FAB_BUTTON_WIDTH = 80; -constexpr coord_t FAB_BUTTON_HEIGHT = 114; - class SelectFabCarousel : public Window { public: @@ -39,4 +36,12 @@ class SelectFabCarousel : public Window // Add a new button to the carousel void addButton(EdgeTxIcon icon, const char* title, std::function pressHandler); + + static LAYOUT_VAL(FAB_BUTTON_WIDTH, 80, 80) + static LAYOUT_VAL(FAB_BUTTON_HEIGHT, 114, 114) + + static LAYOUT_VAL(FAB_ICON_SIZE, 52, 52) + static LAYOUT_VAL(FAB_TXT_YO, 48, 48) + static LAYOUT_VAL(FAB_PAD, 4, 4) + static constexpr coord_t FAB_BUTTON_INNER_WIDTH = FAB_BUTTON_WIDTH - PAD_MEDIUM; }; diff --git a/radio/src/gui/colorlcd/sourcechoice.cpp b/radio/src/gui/colorlcd/sourcechoice.cpp index 190bf599cbb..b2ddab04202 100644 --- a/radio/src/gui/colorlcd/sourcechoice.cpp +++ b/radio/src/gui/colorlcd/sourcechoice.cpp @@ -29,7 +29,7 @@ #include "strhelpers.h" #include "switches.h" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define FILTER_COLUMNS 3 #else #define FILTER_COLUMNS 2 diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index d8e0f59a790..8f68c706ecb 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -32,46 +32,6 @@ #define SET_DIRTY() setDirty() -#if LCD_W > LCD_H - -#define SF_BUTTON_H 32 - -#define NM_X 2 -#define NM_Y 4 -#define NM_W 43 -#define SW_Y NM_Y -#define SW_W 70 -#define FN_X (SW_X + SW_W + 2) -#define FN_Y NM_Y -#define FN_W 277 -#define RP_W 40 - -#else - -#define SF_BUTTON_H 44 - -#define NM_X 2 -#define NM_Y 10 -#define NM_W 40 -#define SW_Y 0 -#define SW_W 198 -#define FN_X (NM_X + NM_W + 2) -#define FN_Y 20 -#define FN_W SW_W -#define RP_W 34 - -#endif - -#define NM_H 20 -#define SW_X (NM_X + NM_W + 2) -#define SW_H NM_H -#define FN_H NM_H -#define RP_X (FN_X + FN_W + 2) -#define RP_Y NM_Y -#define RP_H NM_H -#define EN_X (RP_X + RP_W + 5) -#define EN_Y NM_Y + 2 - //----------------------------------------------------------------------------- static const char *_failsafe_module[] = { @@ -104,8 +64,8 @@ static const lv_obj_class_t sf_enable_state = { .destructor_cb = nullptr, .user_data = nullptr, .event_cb = nullptr, - .width_def = 16, - .height_def = 16, + .width_def = FunctionLineButton::EN_SZ, + .height_def = FunctionLineButton::EN_SZ, .editable = LV_OBJ_CLASS_EDITABLE_FALSE, .group_def = LV_OBJ_CLASS_GROUP_DEF_FALSE, .instance_size = sizeof(lv_obj_t), @@ -121,7 +81,7 @@ FunctionLineButton::FunctionLineButton(Window *parent, const rect_t &rect, uint8_t index, const char *prefix) : ListLineButton(parent, index), cfn(cfn), prefix(prefix) { - setHeight(SF_BUTTON_H); + setHeight(FunctionsPage::SF_BUTTON_H); padAll(PAD_ZERO); lv_obj_add_event_cb(lvobj, FunctionLineButton::on_draw, @@ -777,7 +737,7 @@ void FunctionsPage::plusPopup(Window *window) void FunctionsPage::build(Window *window) { -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define PER_ROW 6 static const lv_coord_t l_col_dsc[] = { LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), diff --git a/radio/src/gui/colorlcd/special_functions.h b/radio/src/gui/colorlcd/special_functions.h index 81033e21f25..36b28925999 100644 --- a/radio/src/gui/colorlcd/special_functions.h +++ b/radio/src/gui/colorlcd/special_functions.h @@ -50,6 +50,26 @@ class FunctionLineButton : public ListLineButton void refresh() override; + static constexpr coord_t NM_X = PAD_TINY; + static LAYOUT_VAL(NM_Y, 4, 10) + static LAYOUT_VAL(NM_W, 43, 40) + static LAYOUT_VAL(NM_H, 20, 20) + static constexpr coord_t SW_X = NM_X + NM_W + PAD_TINY; + static LAYOUT_VAL(SW_Y, NM_Y, 0) + static LAYOUT_VAL(SW_W, 70, 198) + static constexpr coord_t SW_H = NM_H; + static LAYOUT_VAL(FN_X, SW_X + SW_W + PAD_TINY, NM_X + NM_W + PAD_TINY) + static LAYOUT_VAL(FN_Y, NM_Y, 20) + static LAYOUT_VAL(FN_W, 278, SW_W) + static LAYOUT_VAL(RP_W, 40, 34) + static constexpr coord_t FN_H = NM_H; + static constexpr coord_t RP_X = FN_X + FN_W + PAD_TINY; + static constexpr coord_t RP_Y = NM_Y; + static constexpr coord_t RP_H = NM_H; + static constexpr coord_t EN_X = RP_X + RP_W + PAD_TINY; + static constexpr coord_t EN_Y = NM_Y + PAD_TINY_GAP; + static LAYOUT_VAL(EN_SZ, 16, 16) + protected: bool init = false; const CustomFunctionData *cfn; @@ -114,6 +134,8 @@ class FunctionsPage : public PageTab void build(Window* window) override; + static LAYOUT_VAL(SF_BUTTON_H, 32, 44) + protected: int8_t focusIndex = -1; int8_t prevFocusIndex = -1; diff --git a/radio/src/gui/colorlcd/standalone_lua.cpp b/radio/src/gui/colorlcd/standalone_lua.cpp index c186789bdf5..5a594a3909c 100644 --- a/radio/src/gui/colorlcd/standalone_lua.cpp +++ b/radio/src/gui/colorlcd/standalone_lua.cpp @@ -45,7 +45,7 @@ void LuaPopup::paint(BitmapBuffer* dc, uint8_t type, const char* text, dc->drawSolidFilledRect(0, POPUP_HEADER_HEIGHT, rect.w, rect.h - POPUP_HEADER_HEIGHT, COLOR_THEME_SECONDARY3); - dc->drawText(FIELD_PADDING_LEFT, POPUP_HEADER_HEIGHT + PAGE_LINE_HEIGHT, info, + dc->drawText(FIELD_PADDING_LEFT, POPUP_HEADER_HEIGHT + EdgeTxStyles::PAGE_LINE_HEIGHT, info, COLOR_THEME_SECONDARY1); } diff --git a/radio/src/gui/colorlcd/startup_shutdown.cpp b/radio/src/gui/colorlcd/startup_shutdown.cpp index b2a56785ab1..47537bb5da8 100644 --- a/radio/src/gui/colorlcd/startup_shutdown.cpp +++ b/radio/src/gui/colorlcd/startup_shutdown.cpp @@ -31,23 +31,25 @@ extern void checkSpeakerVolume(); #if defined(VERSION_TAG) const std::string ver_str = "" VERSION_TAG; const std::string nam_str = "" CODENAME; -#if LCD_W > LCD_H -#define TXT_Y 204 -#else +#if PORTRAIT_LCD #define TXT_Y 404 +#else +#define TXT_Y (LCD_H * 3 / 4) #endif #else const std::string ver_str = "" VERSION; const std::string nam_str = "" VERSION_SUFFIX; const std::string git_str = "(" GIT_STR ")"; -#if LCD_W > LCD_H -#define TXT_Y 180 -#else +#if PORTRAIT_LCD #define TXT_Y 380 +#else +#define TXT_Y (LCD_H * 2 / 3) #endif #endif -#if LCD_W > LCD_H +static LAYOUT_VAL(TXT_H, 24, 24) + +#if !PORTRAIT_LCD #define TXT_X (LCD_W * 4 / 5) #define IMG_X (LCD_W / 3) #define IMG_Y (LCD_H / 2) @@ -102,12 +104,11 @@ void drawSplash() new StaticLZ4Image(splashScreen, IMG_X - logo->width / 2, IMG_Y - logo->height / 2, logo); - new StaticText(splashScreen, {TXT_X - 100, TXT_Y, 200, 24}, ver_str.c_str(), - COLOR_GREY | CENTERED); - new StaticText(splashScreen, {TXT_X - 100, TXT_Y + 24, 200, 24}, + new StaticText(splashScreen, {TXT_X - 100, TXT_Y, 200, 24}, ver_str.c_str(), COLOR_GREY | CENTERED); + new StaticText(splashScreen, {TXT_X - 100, TXT_Y + TXT_H, 200, TXT_H}, nam_str.c_str(), COLOR_GREY | CENTERED); #if !defined(VERSION_TAG) - new StaticText(splashScreen, {TXT_X - 100, TXT_Y + 48, 200, 24}, + new StaticText(splashScreen, {TXT_X - 100, TXT_Y + TXT_H * 2, 200, TXT_H}, git_str.c_str(), COLOR_GREY | CENTERED); #endif } @@ -192,7 +193,8 @@ void drawSleepBitmap() if (shutdownWindow) { shutdownWindow->clear(); } else { - shutdownWindow = new Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}); + shutdownWindow = + new Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}); shutdownWindow->setWindowFlag(OPAQUE); etx_solid_bg(shutdownWindow->getLvObj(), COLOR_THEME_PRIMARY1_INDEX); } @@ -219,7 +221,8 @@ void drawShutdownAnimation(uint32_t duration, uint32_t totalDuration, if (totalDuration == 0) return; if (shutdownWindow == nullptr) { - shutdownWindow = new Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}); + shutdownWindow = + new Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}); shutdownWindow->setWindowFlag(OPAQUE); etx_solid_bg(shutdownWindow->getLvObj(), COLOR_THEME_PRIMARY1_INDEX); @@ -257,7 +260,8 @@ void drawFatalErrorScreen(const char* message) static Window* fatalErrorWindow = nullptr; if (!fatalErrorWindow) { - fatalErrorWindow = new Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}); + fatalErrorWindow = + new Window(MainWindow::instance(), {0, 0, LCD_W, LCD_H}); fatalErrorWindow->setWindowFlag(OPAQUE); etx_solid_bg(fatalErrorWindow->getLvObj(), COLOR_BLACK_INDEX); diff --git a/radio/src/gui/colorlcd/tabsgroup.cpp b/radio/src/gui/colorlcd/tabsgroup.cpp index 994e73ced8d..83735c567d4 100644 --- a/radio/src/gui/colorlcd/tabsgroup.cpp +++ b/radio/src/gui/colorlcd/tabsgroup.cpp @@ -24,6 +24,7 @@ #include "theme.h" #include "themes/etx_lv_theme.h" #include "view_main.h" +#include "topbar_impl.h" #if defined(HARDWARE_TOUCH) #include "keyboard_base.h" @@ -36,12 +37,15 @@ class SelectedTabIcon : public StaticIcon StaticIcon(parent, 0, 0, ICON_CURRENTMENU_SHADOW, COLOR_THEME_PRIMARY1) { new StaticIcon(this, 0, 0, ICON_CURRENTMENU_BG, COLOR_THEME_FOCUS); - new StaticIcon(this, 10, 39, ICON_CURRENTMENU_DOT, COLOR_THEME_PRIMARY2); + new StaticIcon(this, SEL_DOT_X, SEL_DOT_Y, ICON_CURRENTMENU_DOT, COLOR_THEME_PRIMARY2); } #if defined(DEBUG_WINDOWS) std::string getName() const override { return "SelectedTabIcon"; } #endif + + static LAYOUT_VAL(SEL_DOT_X, 10, 10) + static LAYOUT_VAL(SEL_DOT_Y, 39, 39) }; class TabCarouselButton : public ButtonBase @@ -54,7 +58,7 @@ class TabCarouselButton : public ButtonBase selected->hide(); lastIcon = getIcon(); - icon = new StaticIcon(this, 2, 7, lastIcon, COLOR_THEME_PRIMARY2); + icon = new StaticIcon(this, 2, ICON_Y, lastIcon, COLOR_THEME_PRIMARY2); show(isVisible()); } @@ -71,6 +75,8 @@ class TabCarouselButton : public ButtonBase EdgeTxIcon getIcon() const { return pageTab->getIcon(); } PageTab* page() const { return pageTab; } + static LAYOUT_VAL(ICON_Y, 7, 7) + protected: PageTab* pageTab; EdgeTxIcon lastIcon; @@ -84,7 +90,7 @@ class TabCarouselButton : public ButtonBase if (lastIcon != getIcon()) { lastIcon = getIcon(); icon->deleteLater(); - icon = new StaticIcon(this, 2, 7, lastIcon, COLOR_THEME_PRIMARY2); + icon = new StaticIcon(this, 2, ICON_Y, lastIcon, COLOR_THEME_PRIMARY2); } ButtonBase::checkEvents(); } @@ -95,13 +101,13 @@ class TabsCarousel : public Window public: TabsCarousel(Window* parent, TabsGroup* menu) : Window(parent, - {MENU_HEADER_BUTTONS_LEFT, 0, - LCD_W - 51 - MENU_HEADER_BUTTONS_LEFT, MENU_HEADER_HEIGHT + 10}), + {TopBar::MENU_HEADER_BUTTONS_LEFT, 0, + LCD_W - HDR_DATE_FULL_WIDTH - TopBar::MENU_HEADER_BUTTONS_LEFT, EdgeTxStyles::MENU_HEADER_HEIGHT + 10}), menu(menu) { setWindowFlag(NO_FOCUS); - lv_obj_set_style_max_width(lvobj, LCD_W - 51 - MENU_HEADER_BUTTONS_LEFT, + lv_obj_set_style_max_width(lvobj, LCD_W - HDR_DATE_FULL_WIDTH - TopBar::MENU_HEADER_BUTTONS_LEFT, LV_PART_MAIN); padAll(PAD_ZERO); @@ -148,7 +154,7 @@ class TabsCarousel : public Window void addTab(PageTab* page) { TabCarouselButton* btn = new TabCarouselButton( - this, {0, 0, MENU_HEADER_BUTTON_WIDTH + 3, MENU_TITLE_TOP + 5}, page); + this, {0, 0, MENU_HEADER_BUTTON_WIDTH + 3, TabsGroup::MENU_TITLE_TOP + 5}, page); btn->setPressHandler([=]() { menu->setCurrentTab(getIndex(btn->getIcon())); return true; @@ -182,6 +188,9 @@ class TabsCarousel : public Window } return -1; } + + static LAYOUT_VAL(MENU_HEADER_BUTTON_WIDTH, 33, 33) + static LAYOUT_VAL(HDR_DATE_FULL_WIDTH, 51, 51) }; #if defined(DEBUG) @@ -209,7 +218,8 @@ class TabsGroupHeader : public Window public: TabsGroupHeader(TabsGroup* menu, EdgeTxIcon icon) : - Window(menu, {0, 0, LCD_W, MENU_BODY_TOP}), icon(icon) + Window(menu, {0, 0, LCD_W, TabsGroup::MENU_BODY_TOP}), + icon(icon) { setWindowFlag(NO_FOCUS | OPAQUE); @@ -219,20 +229,20 @@ class TabsGroupHeader : public Window auto sep = lv_obj_create(lvobj); etx_solid_bg(sep); - lv_obj_set_pos(sep, 0, MENU_HEADER_HEIGHT); - lv_obj_set_size(sep, LCD_W, MENU_TITLE_TOP - MENU_HEADER_HEIGHT); + lv_obj_set_pos(sep, 0, EdgeTxStyles::MENU_HEADER_HEIGHT); + lv_obj_set_size(sep, LCD_W, TabsGroup::MENU_TITLE_TOP - EdgeTxStyles::MENU_HEADER_HEIGHT); titleLabel = lv_label_create(lvobj); etx_txt_color(titleLabel, COLOR_THEME_PRIMARY2_INDEX); lv_obj_set_style_pad_left(titleLabel, PAD_MEDIUM, LV_PART_MAIN); lv_obj_set_style_pad_top(titleLabel, 1, LV_PART_MAIN); - lv_obj_set_pos(titleLabel, 0, MENU_TITLE_TOP); - lv_obj_set_size(titleLabel, LCD_W, MENU_TITLE_HEIGHT); + lv_obj_set_pos(titleLabel, 0, TabsGroup::MENU_TITLE_TOP); + lv_obj_set_size(titleLabel, LCD_W, TabsGroup::MENU_TITLE_HEIGHT); setTitle(""); carousel = new TabsCarousel(this, menu); - dateTime = new HeaderDateTime(lvobj, LCD_W - 48, 6); + dateTime = new HeaderDateTime(lvobj, LCD_W - DATE_XO, DATE_Y); } void setTitle(const char* title) { lv_label_set_text(titleLabel, title); } @@ -241,6 +251,9 @@ class TabsGroupHeader : public Window std::string getName() const override { return "TabsGroupHeader"; } #endif + static LAYOUT_VAL(DATE_XO, 48, 48) + static LAYOUT_VAL(DATE_Y, 6, 6) + protected: EdgeTxIcon icon; TabsCarousel* carousel = nullptr; @@ -261,7 +274,8 @@ TabsGroup::TabsGroup(EdgeTxIcon icon) : NavWindow(MainWindow::instance(), {0, 0, LCD_W, LCD_H}) { header = new TabsGroupHeader(this, icon); - body = new Window(this, {0, MENU_BODY_TOP, LCD_W, MENU_BODY_HEIGHT}); + body = + new Window(this, {0, MENU_BODY_TOP, LCD_W, MENU_BODY_HEIGHT}); body->setWindowFlag(NO_FOCUS); etx_solid_bg(lvobj); @@ -362,4 +376,15 @@ void TabsGroup::onClicked() { Keyboard::hide(false); } void TabsGroup::onCancel() { deleteLater(); } -Window* TabsGroup::getHeaderWindow() { return header; } +#if defined(PCBNV14) || defined(PCBPL18) +void TabsGroup::addGoToMonitorsButton() +{ + new TextButton( + header, + {LCD_W / 2 + 6, MENU_TITLE_TOP + 1, LCD_W / 2 - 8, MENU_TITLE_HEIGHT - 2}, + STR_OPEN_CHANNEL_MONITORS, [=]() { + pushEvent(EVT_KEY_FIRST(KEY_MODEL)); + return 0; + }); +} +#endif diff --git a/radio/src/gui/colorlcd/tabsgroup.h b/radio/src/gui/colorlcd/tabsgroup.h index 30b71bf4c40..96ffa60a4c4 100644 --- a/radio/src/gui/colorlcd/tabsgroup.h +++ b/radio/src/gui/colorlcd/tabsgroup.h @@ -87,7 +87,10 @@ class TabsGroup : public NavWindow void onClicked() override; void onCancel() override; - Window* getHeaderWindow(); + static LAYOUT_VAL(MENU_TITLE_TOP, 48, 48) + static LAYOUT_VAL(MENU_TITLE_HEIGHT, 21, 21) + static constexpr coord_t MENU_BODY_TOP = MENU_TITLE_TOP + MENU_TITLE_HEIGHT; + static constexpr coord_t MENU_BODY_HEIGHT = LCD_H - MENU_BODY_TOP; protected: TabsGroupHeader* header = nullptr; @@ -100,4 +103,8 @@ class TabsGroup : public NavWindow void onPressPGUP() override; void onPressPGDN() override; #endif + +#if defined(PCBNV14) || defined(PCBPL18) + void addGoToMonitorsButton(void); +#endif }; diff --git a/radio/src/gui/colorlcd/theme.cpp b/radio/src/gui/colorlcd/theme.cpp index 774a2751131..7ed5c74b7b5 100644 --- a/radio/src/gui/colorlcd/theme.cpp +++ b/radio/src/gui/colorlcd/theme.cpp @@ -23,6 +23,7 @@ #include "libopenui.h" #include "theme_manager.h" +#include "topbar_impl.h" EdgeTxTheme* EdgeTxTheme::_instance = nullptr; @@ -64,14 +65,14 @@ HeaderDateTime::HeaderDateTime(lv_obj_t* parent, coord_t x, coord_t y) { date = lv_label_create(parent); lv_obj_set_pos(date, x, y); - lv_obj_set_size(date, 45, 12); + lv_obj_set_size(date, HDR_DATE_WIDTH, HDR_DATE_HEIGHT); lv_obj_set_style_text_align(date, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN); etx_txt_color(date, COLOR_THEME_PRIMARY2_INDEX); etx_font(date, FONT_XS_INDEX); time = lv_label_create(parent); - lv_obj_set_pos(time, x, y + 15); - lv_obj_set_size(time, 45, 12); + lv_obj_set_pos(time, x, y + HDR_DATE_LINE2); + lv_obj_set_size(time, HDR_DATE_WIDTH, HDR_DATE_HEIGHT); lv_obj_set_style_text_align(time, LV_TEXT_ALIGN_CENTER, LV_PART_MAIN); etx_txt_color(time, COLOR_THEME_PRIMARY2_INDEX); etx_font(time, FONT_XS_INDEX); @@ -119,7 +120,7 @@ UsbSDConnected::UsbSDConnected() : setWindowFlag(OPAQUE); etx_solid_bg(lvobj, COLOR_THEME_PRIMARY1_INDEX); - dateTime = new HeaderDateTime(lvobj, LCD_W - 48, 6); + dateTime = new HeaderDateTime(lvobj, LCD_W - TopBar::HDR_DATE_XO, HDR_DATE_Y); auto icon = new StaticIcon(this, 0, 0, ICON_USB_PLUGGED, COLOR_THEME_PRIMARY2); lv_obj_center(icon->getLvObj()); diff --git a/radio/src/gui/colorlcd/theme.h b/radio/src/gui/colorlcd/theme.h index 826248f075b..415ba6d7fcb 100644 --- a/radio/src/gui/colorlcd/theme.h +++ b/radio/src/gui/colorlcd/theme.h @@ -54,6 +54,10 @@ class HeaderDateTime void update(); void setColor(uint32_t color); + static LAYOUT_VAL(HDR_DATE_WIDTH, 45, 45) + static LAYOUT_VAL(HDR_DATE_HEIGHT, 12, 12) + static LAYOUT_VAL(HDR_DATE_LINE2, 15, 15) + protected: lv_obj_t *date = nullptr; lv_obj_t *time = nullptr; @@ -73,6 +77,8 @@ class UsbSDConnected : public Window void checkEvents() override; + static LAYOUT_VAL(HDR_DATE_Y, 6, 6) + protected: HeaderDateTime* dateTime = nullptr; }; diff --git a/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp b/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp index d3075152b84..4790e17b47f 100644 --- a/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp +++ b/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp @@ -71,9 +71,12 @@ LV_STYLE_CONST_SINGLE_INIT(EdgeTxStyles::pad_left_2, LV_STYLE_PAD_LEFT, 2); // Scrollbar const lv_style_const_prop_t scrollbar_props[] = { LV_STYLE_CONST_BG_OPA(LV_OPA_50), - LV_STYLE_CONST_PAD_TOP(3), LV_STYLE_CONST_PAD_BOTTOM(3), - LV_STYLE_CONST_PAD_LEFT(3), LV_STYLE_CONST_PAD_RIGHT(3), - LV_STYLE_CONST_WIDTH(4), LV_STYLE_PROP_INV, + LV_STYLE_CONST_PAD_TOP(3), + LV_STYLE_CONST_PAD_BOTTOM(3), + LV_STYLE_CONST_PAD_LEFT(3), + LV_STYLE_CONST_PAD_RIGHT(3), + LV_STYLE_CONST_WIDTH(PAD_SMALL), + LV_STYLE_PROP_INV, }; LV_STYLE_CONST_MULTI_INIT(EdgeTxStyles::scrollbar, scrollbar_props); @@ -95,40 +98,54 @@ const lv_style_const_prop_t pad_tiny_props[] = { LV_STYLE_CONST_MULTI_INIT(EdgeTxStyles::pad_tiny, pad_tiny_props); const lv_style_const_prop_t pad_small_props[] = { - LV_STYLE_CONST_PAD_TOP(4), LV_STYLE_CONST_PAD_BOTTOM(4), - LV_STYLE_CONST_PAD_LEFT(4), LV_STYLE_CONST_PAD_RIGHT(4), - LV_STYLE_CONST_PAD_ROW(4), LV_STYLE_CONST_PAD_COLUMN(4), + LV_STYLE_CONST_PAD_TOP(PAD_SMALL), + LV_STYLE_CONST_PAD_BOTTOM(PAD_SMALL), + LV_STYLE_CONST_PAD_LEFT(PAD_SMALL), + LV_STYLE_CONST_PAD_RIGHT(PAD_SMALL), + LV_STYLE_CONST_PAD_ROW(PAD_SMALL), + LV_STYLE_CONST_PAD_COLUMN(PAD_SMALL), LV_STYLE_PROP_INV, }; LV_STYLE_CONST_MULTI_INIT(EdgeTxStyles::pad_small, pad_small_props); const lv_style_const_prop_t pad_medium_props[] = { - LV_STYLE_CONST_PAD_TOP(6), LV_STYLE_CONST_PAD_BOTTOM(6), - LV_STYLE_CONST_PAD_LEFT(6), LV_STYLE_CONST_PAD_RIGHT(6), - LV_STYLE_CONST_PAD_ROW(4), LV_STYLE_CONST_PAD_COLUMN(4), + LV_STYLE_CONST_PAD_TOP(PAD_MEDIUM), + LV_STYLE_CONST_PAD_BOTTOM(PAD_MEDIUM), + LV_STYLE_CONST_PAD_LEFT(PAD_MEDIUM), + LV_STYLE_CONST_PAD_RIGHT(PAD_MEDIUM), + LV_STYLE_CONST_PAD_ROW(PAD_SMALL), + LV_STYLE_CONST_PAD_COLUMN(PAD_SMALL), LV_STYLE_PROP_INV, }; LV_STYLE_CONST_MULTI_INIT(EdgeTxStyles::pad_medium, pad_medium_props); const lv_style_const_prop_t pad_large_props[] = { - LV_STYLE_CONST_PAD_TOP(8), LV_STYLE_CONST_PAD_BOTTOM(8), - LV_STYLE_CONST_PAD_LEFT(8), LV_STYLE_CONST_PAD_RIGHT(8), - LV_STYLE_CONST_PAD_ROW(4), LV_STYLE_CONST_PAD_COLUMN(4), + LV_STYLE_CONST_PAD_TOP(PAD_LARGE), + LV_STYLE_CONST_PAD_BOTTOM(PAD_LARGE), + LV_STYLE_CONST_PAD_LEFT(PAD_LARGE), + LV_STYLE_CONST_PAD_RIGHT(PAD_LARGE), + LV_STYLE_CONST_PAD_ROW(PAD_SMALL), + LV_STYLE_CONST_PAD_COLUMN(PAD_SMALL), LV_STYLE_PROP_INV, }; LV_STYLE_CONST_MULTI_INIT(EdgeTxStyles::pad_large, pad_large_props); const lv_style_const_prop_t pad_button_props[] = { - LV_STYLE_CONST_PAD_TOP(2), LV_STYLE_CONST_PAD_BOTTOM(2), - LV_STYLE_CONST_PAD_LEFT(6), LV_STYLE_CONST_PAD_RIGHT(6), - LV_STYLE_CONST_PAD_ROW(2), LV_STYLE_CONST_PAD_COLUMN(2), + LV_STYLE_CONST_PAD_TOP(2), + LV_STYLE_CONST_PAD_BOTTOM(2), + LV_STYLE_CONST_PAD_LEFT(PAD_MEDIUM), + LV_STYLE_CONST_PAD_RIGHT(PAD_MEDIUM), + LV_STYLE_CONST_PAD_ROW(2), + LV_STYLE_CONST_PAD_COLUMN(2), LV_STYLE_PROP_INV, }; LV_STYLE_CONST_MULTI_INIT(EdgeTxStyles::pad_button, pad_button_props); const lv_style_const_prop_t pad_textarea_props[] = { - LV_STYLE_CONST_PAD_TOP(4), LV_STYLE_CONST_PAD_BOTTOM(3), - LV_STYLE_CONST_PAD_LEFT(4), LV_STYLE_CONST_PAD_RIGHT(4), + LV_STYLE_CONST_PAD_TOP(PAD_SMALL), + LV_STYLE_CONST_PAD_BOTTOM(PAD_SMALL - 1), + LV_STYLE_CONST_PAD_LEFT(PAD_SMALL), + LV_STYLE_CONST_PAD_RIGHT(PAD_SMALL), LV_STYLE_PROP_INV, }; LV_STYLE_CONST_MULTI_INIT(EdgeTxStyles::pad_textarea, pad_textarea_props); @@ -304,8 +321,7 @@ void EdgeTxStyles::applyColors() } lv_style_set_line_color(&graph_border, makeLvColor(COLOR_THEME_SECONDARY2)); - lv_style_set_line_color(&graph_dashed, - makeLvColor(COLOR_THEME_SECONDARY2)); + lv_style_set_line_color(&graph_dashed, makeLvColor(COLOR_THEME_SECONDARY2)); lv_style_set_line_color(&graph_line, makeLvColor(COLOR_THEME_SECONDARY1)); lv_style_set_line_color(&graph_position_line, makeLvColor(COLOR_THEME_ACTIVE)); diff --git a/radio/src/gui/colorlcd/themes/etx_lv_theme.h b/radio/src/gui/colorlcd/themes/etx_lv_theme.h index 75b1fd736e1..cf93d2cdd01 100644 --- a/radio/src/gui/colorlcd/themes/etx_lv_theme.h +++ b/radio/src/gui/colorlcd/themes/etx_lv_theme.h @@ -28,7 +28,30 @@ #include "colors.h" #include "fonts.h" -#include "window.h" + +/********************* + * Layout + *********************/ + +enum PaddingSize { + PAD_ZERO = 0, + PAD_TINY = 2, + PAD_TINY_GAP = 2, + PAD_SMALL = 4, + PAD_MEDIUM = 6, + PAD_LARGE = 8 +}; + +// Macros for setting up layout values +// LAYOUT_VAL - 2 values - landscape, portrait + +#if LANDSCAPE_LCD +#define LAYOUT_VAL(name, landscape, portrait) \ + constexpr coord_t name = landscape; +#else +#define LAYOUT_VAL(name, landscape, portrait) \ + constexpr coord_t name = portrait; +#endif /********************** * GLOBAL PROTOTYPES @@ -169,6 +192,10 @@ class EdgeTxStyles void init(); void applyColors(); + static LAYOUT_VAL(PAGE_LINE_HEIGHT, 20, 20) + static LAYOUT_VAL(UI_ELEMENT_HEIGHT, 32, 32) + static LAYOUT_VAL(MENU_HEADER_HEIGHT, 45, 45) + protected: bool initDone = false; }; diff --git a/radio/src/gui/colorlcd/topbar.h b/radio/src/gui/colorlcd/topbar.h index c703ea34555..20ae95694ce 100644 --- a/radio/src/gui/colorlcd/topbar.h +++ b/radio/src/gui/colorlcd/topbar.h @@ -23,11 +23,6 @@ #include "layout.h" -constexpr coord_t TOPBAR_ZONE_WIDTH = 70; -constexpr coord_t TOPBAR_ZONE_VMARGIN = 3; -constexpr coord_t TOPBAR_ZONE_HMARGIN = 2; -constexpr coord_t TOPBAR_ZONE_HEIGHT = MENU_HEADER_HEIGHT - 2 * TOPBAR_ZONE_VMARGIN; - class ScreenMenu; class TopBar; diff --git a/radio/src/gui/colorlcd/view_channels.cpp b/radio/src/gui/colorlcd/view_channels.cpp index 47e27b2c955..6f8692b745c 100644 --- a/radio/src/gui/colorlcd/view_channels.cpp +++ b/radio/src/gui/colorlcd/view_channels.cpp @@ -89,7 +89,7 @@ class ChannelsViewPage : public PageTab // Channels bars for (uint8_t chan = pageIndex * 8; chan < 8 + pageIndex * 8; chan++) { -#if LCD_H > LCD_W +#if PORTRAIT_LCD coord_t width = window->width() - (hmargin * 2); coord_t xPos = hmargin; coord_t yPos = (chan % 8) * @@ -100,7 +100,7 @@ class ChannelsViewPage : public PageTab coord_t yPos = (chan % 4) * ((window->height() - 23) / 4); #endif - new ComboChannelBar(window, {xPos, yPos, width, 3 * BAR_HEIGHT + 3}, + new ComboChannelBar(window, {xPos, yPos, width, 3 * ChannelBar::BAR_HEIGHT + 3}, chan); } diff --git a/radio/src/gui/colorlcd/view_logical_switches.cpp b/radio/src/gui/colorlcd/view_logical_switches.cpp index a1b29597acf..a0e31138055 100644 --- a/radio/src/gui/colorlcd/view_logical_switches.cpp +++ b/radio/src/gui/colorlcd/view_logical_switches.cpp @@ -25,14 +25,18 @@ #include "switches.h" #include "themes/etx_lv_theme.h" -#if LCD_W > LCD_H // Landscape +#if PORTRAIT_LCD -#define BTN_MATRIX_COL 8 -#define BTN_HEIGHT 20 -#define FOOTER_HEIGHT 20 -#define V2_COL_CNT 1 -#define ANDSW_ROW 0 -#define ANDSW_COL 3 +static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST}; + +// Footer grid +static const lv_coord_t f_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_FR(1), LV_GRID_FR(1), + LV_GRID_TEMPLATE_LAST}; + +#else // Landscape // Switch grid static const lv_coord_t col_dsc[] = { @@ -44,24 +48,6 @@ static const lv_coord_t col_dsc[] = { static const lv_coord_t f_col_dsc[] = { 60, LV_GRID_FR(1), 112, LV_GRID_FR(1), 50, 50, LV_GRID_TEMPLATE_LAST}; -#else // Portrait - -#define BTN_MATRIX_COL 4 -#define BTN_HEIGHT 21 -#define FOOTER_HEIGHT 40 -#define V2_COL_CNT 2 -#define ANDSW_ROW 1 -#define ANDSW_COL 1 - -static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), - LV_GRID_FR(1), LV_GRID_FR(1), - LV_GRID_TEMPLATE_LAST}; - -// Footer grid -static const lv_coord_t f_col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), - LV_GRID_FR(1), LV_GRID_FR(1), - LV_GRID_TEMPLATE_LAST}; - #endif static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, @@ -78,8 +64,8 @@ class LogicalSwitchDisplayFooter : public Window setWindowFlag(OPAQUE); padAll(PAD_ZERO); - padLeft(4); - padRight(4); + padLeft(PAD_SMALL); + padRight(PAD_SMALL); etx_solid_bg(lvobj, COLOR_THEME_SECONDARY1_INDEX); lv_obj_set_layout(lvobj, LV_LAYOUT_GRID); @@ -211,6 +197,10 @@ class LogicalSwitchDisplayFooter : public Window refresh(); } + static LAYOUT_VAL(V2_COL_CNT, 1, 2) + static LAYOUT_VAL(ANDSW_ROW, 0, 1) + static LAYOUT_VAL(ANDSW_COL, 3, 1) + protected: unsigned lsIndex = 0; lv_obj_t* lsFunc = nullptr; @@ -276,9 +266,9 @@ void LogicalSwitchesViewPage::build(Window* window) for (uint8_t i = 0; i < MAX_LOGICAL_SWITCHES; i++) { if ((i % BTN_MATRIX_COL) == 0) { line = form->newLine(grid); - lv_obj_set_style_pad_all(line->getLvObj(), 1, 0); - line->padLeft(4); - line->padRight(4); + lv_obj_set_style_pad_all(line->getLvObj(), BTN_PAD, LV_PART_MAIN); + line->padLeft(PAD_SMALL); + line->padRight(PAD_SMALL); } LogicalSwitchData* ls = lswAddress(i); diff --git a/radio/src/gui/colorlcd/view_logical_switches.h b/radio/src/gui/colorlcd/view_logical_switches.h index 776aeca251f..f63c39a7b7c 100644 --- a/radio/src/gui/colorlcd/view_logical_switches.h +++ b/radio/src/gui/colorlcd/view_logical_switches.h @@ -30,6 +30,11 @@ class LogicalSwitchesViewPage : public PageTab public: LogicalSwitchesViewPage(); + static LAYOUT_VAL(FOOTER_HEIGHT, 20, 40) + static LAYOUT_VAL(BTN_HEIGHT, 20, 21) + static LAYOUT_VAL(BTN_PAD, 1, 2) + static LAYOUT_VAL(BTN_MATRIX_COL, 8, 4) + protected: void build(Window* window) override; LogicalSwitchDisplayFooter* footer = nullptr; diff --git a/radio/src/gui/colorlcd/view_main_decoration.cpp b/radio/src/gui/colorlcd/view_main_decoration.cpp index 6868f4af492..12f73bcf014 100644 --- a/radio/src/gui/colorlcd/view_main_decoration.cpp +++ b/radio/src/gui/colorlcd/view_main_decoration.cpp @@ -148,34 +148,34 @@ void ViewMainDecoration::createSliders(Window* ml, Window* mr, Window* bl, Windo // create containers for the sliders, so that they are at the borders of the display // on top of each other, when there are two sliders to display per side auto leftPots = create_layout_box(ml, LV_ALIGN_LEFT_MID, LV_FLEX_FLOW_COLUMN); - leftPots->setHeight(VERTICAL_SLIDERS_HEIGHT); + leftPots->setHeight(MainViewSlider::HORIZONTAL_SLIDERS_WIDTH); auto rightPots = create_layout_box(mr, LV_ALIGN_RIGHT_MID, LV_FLEX_FLOW_COLUMN); - rightPots->setHeight(VERTICAL_SLIDERS_HEIGHT); + rightPots->setHeight(MainViewSlider::VERTICAL_SLIDERS_HEIGHT); - coord_t lsh = (IS_POT_AVAILABLE(pot + 2)) ? VERTICAL_SLIDERS_HEIGHT / 2 : VERTICAL_SLIDERS_HEIGHT; - coord_t rsh = (IS_POT_AVAILABLE(pot + 3)) ? VERTICAL_SLIDERS_HEIGHT / 2 : VERTICAL_SLIDERS_HEIGHT; + coord_t lsh = (IS_POT_AVAILABLE(pot + 2)) ? MainViewSlider::HORIZONTAL_SLIDERS_WIDTH / 2 : MainViewSlider::HORIZONTAL_SLIDERS_WIDTH; + coord_t rsh = (IS_POT_AVAILABLE(pot + 3)) ? MainViewSlider::HORIZONTAL_SLIDERS_WIDTH / 2 : MainViewSlider::HORIZONTAL_SLIDERS_WIDTH; if (IS_POT_AVAILABLE(pot)) { - sliders[sl] = new MainViewVerticalSlider(leftPots, rect_t{0, 0, TRIM_SQUARE_SIZE, lsh}, pot); + sliders[sl] = new MainViewVerticalSlider(leftPots, rect_t{0, 0, LayoutFactory::TRIM_SQUARE_SIZE, lsh}, pot); sl += 1; } pot += 1; if (IS_POT_AVAILABLE(pot)) { - sliders[sl] = new MainViewVerticalSlider(rightPots, rect_t{0, 0, TRIM_SQUARE_SIZE, rsh}, pot); + sliders[sl] = new MainViewVerticalSlider(rightPots, rect_t{0, 0, LayoutFactory::TRIM_SQUARE_SIZE, rsh}, pot); sl += 1; } pot += 1; if (IS_POT_AVAILABLE(pot)) { - sliders[sl] = new MainViewVerticalSlider(leftPots, rect_t{0, 0, TRIM_SQUARE_SIZE, lsh}, pot); + sliders[sl] = new MainViewVerticalSlider(leftPots, rect_t{0, 0, LayoutFactory::TRIM_SQUARE_SIZE, lsh}, pot); sl += 1; } pot += 1; if (IS_POT_AVAILABLE(pot)) { - sliders[sl] = new MainViewVerticalSlider(rightPots, rect_t{0, 0, TRIM_SQUARE_SIZE, rsh}, pot); + sliders[sl] = new MainViewVerticalSlider(rightPots, rect_t{0, 0, LayoutFactory::TRIM_SQUARE_SIZE, rsh}, pot); } } } diff --git a/radio/src/gui/colorlcd/view_main_menu.cpp b/radio/src/gui/colorlcd/view_main_menu.cpp index deb63f13358..a17490c43ef 100644 --- a/radio/src/gui/colorlcd/view_main_menu.cpp +++ b/radio/src/gui/colorlcd/view_main_menu.cpp @@ -58,12 +58,12 @@ static lv_obj_t* etx_modal_dialog_create(lv_obj_t* parent) return etx_create(&etx_modal_dialog_class, parent); } -#if LCD_W > LCD_H -#define VM_W (FAB_BUTTON_WIDTH * 4 + 16) -#define VM_H (FAB_BUTTON_HEIGHT * 2 + 16) +#if !PORTRAIT_LCD +#define VM_W (SelectFabCarousel::FAB_BUTTON_WIDTH * 4 + PAD_LARGE * 2) +#define VM_H (SelectFabCarousel::FAB_BUTTON_HEIGHT * 2 + PAD_LARGE * 2) #else -#define VM_W (FAB_BUTTON_WIDTH * 3 + 16) -#define VM_H (FAB_BUTTON_HEIGHT * 3 + 16) +#define VM_W (SelectFabCarousel::FAB_BUTTON_WIDTH * 3 + PAD_LARGE * 2) +#define VM_H (SelectFabCarousel::FAB_BUTTON_HEIGHT * 3 + PAD_LARGE * 2) #endif ViewMainMenu::ViewMainMenu(Window* parent, std::function closeHandler) : @@ -76,9 +76,9 @@ ViewMainMenu::ViewMainMenu(Window* parent, std::function closeHandler) : coord_t width = VM_W; bool hasNotes = modelHasNotes(); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD if (hasNotes) - width += FAB_BUTTON_WIDTH; + width += SelectFabCarousel::FAB_BUTTON_WIDTH; #endif auto box = diff --git a/radio/src/gui/colorlcd/view_statistics.cpp b/radio/src/gui/colorlcd/view_statistics.cpp index 501e7aef7e4..03429395ecf 100644 --- a/radio/src/gui/colorlcd/view_statistics.cpp +++ b/radio/src/gui/colorlcd/view_statistics.cpp @@ -57,7 +57,7 @@ static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; -#if LCD_W > LCD_H +#if !PORTRAIT_LCD static const lv_coord_t dbg_4col_dsc[] = {LV_GRID_FR(2), LV_GRID_FR(3), LV_GRID_FR(3), LV_GRID_FR(3), LV_GRID_TEMPLATE_LAST}; @@ -72,7 +72,7 @@ static const lv_coord_t dbg_3col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define CV_SCALE 3 #define DBG_B_WIDTH (LCD_W - 20) / 4 #else @@ -252,7 +252,7 @@ void DebugViewPage::build(Window* window) { window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO); -#if LCD_W > LCD_H +#if !PORTRAIT_LCD FlexGridLayout grid(dbg_4col_dsc, row_dsc, PAD_ZERO); FlexGridLayout grid2(dbg_4col_dsc, row_dsc, PAD_ZERO); #else @@ -291,7 +291,7 @@ void DebugViewPage::build(Window* window) // LUA timing data new StaticText(line, rect_t{}, STR_LUA_SCRIPTS_LABEL); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = window->newLine(grid); line->padAll(PAD_ZERO); line->padLeft(10); @@ -305,7 +305,7 @@ void DebugViewPage::build(Window* window) line = window->newLine(grid); line->padAll(PAD_ZERO); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line->padLeft(10); #else grid.nextCell(); @@ -319,7 +319,7 @@ void DebugViewPage::build(Window* window) line, rect_t{0, 0, DBG_B_WIDTH, DBG_B_HEIGHT}, [] { return luaGetMemUsed(lsWidgets); }, STR_MEM_USED_WIDGET); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = window->newLine(grid); line->padAll(PAD_ZERO); line->padLeft(10); @@ -335,7 +335,7 @@ void DebugViewPage::build(Window* window) // Stacks data new StaticText(line, rect_t{}, STR_FREE_STACK); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = window->newLine(grid2); line->padAll(PAD_ZERO); line->padLeft(10); @@ -371,7 +371,7 @@ void DebugViewPage::build(Window* window) line->padAll(PAD_TINY); new StaticText(line, rect_t{}, STR_INT_GPS_LABEL); -#if LCD_H > LCD_W +#if PORTRAIT_LCD line = window->newLine(grid2); line->padAll(PAD_ZERO); line->padLeft(10); diff --git a/radio/src/gui/colorlcd/widget_settings.cpp b/radio/src/gui/colorlcd/widget_settings.cpp index 144ef05ee42..db2001ba6a3 100644 --- a/radio/src/gui/colorlcd/widget_settings.cpp +++ b/radio/src/gui/colorlcd/widget_settings.cpp @@ -188,4 +188,4 @@ void WidgetSettings::onCancel() { widget->update(); deleteLater(); -} \ No newline at end of file +} diff --git a/radio/src/gui/colorlcd/widgets/radio_info.cpp b/radio/src/gui/colorlcd/widgets/radio_info.cpp index f4263d206a1..2be88d1e412 100644 --- a/radio/src/gui/colorlcd/widgets/radio_info.cpp +++ b/radio/src/gui/colorlcd/widgets/radio_info.cpp @@ -25,11 +25,6 @@ #include "widget.h" #include "widgets_container_impl.h" -#define W_AUDIO_X 0 -#define W_USB_X 32 -#define W_LOG_X 32 -#define W_RSSI_X 40 - class TopBarWidget : public Widget { public: @@ -48,45 +43,50 @@ class RadioInfoWidget : public TopBarWidget TopBarWidget(factory, parent, rect, persistentData) { // Logs - logsIcon = new StaticIcon(this, W_LOG_X, 3, ICON_DOT, COLOR_THEME_PRIMARY2); + logsIcon = new StaticIcon(this, W_LOG_X, W_LOG_Y, ICON_DOT, + COLOR_THEME_PRIMARY2); logsIcon->hide(); - usbIcon = new StaticIcon(this, W_USB_X, 5, ICON_TOPMENU_USB, - COLOR_THEME_PRIMARY2); + usbIcon = + new StaticIcon(this, W_USB_X, W_USB_Y, ICON_TOPMENU_USB, + COLOR_THEME_PRIMARY2); usbIcon->hide(); #if defined(AUDIO) - audioScale = - new StaticIcon(this, W_AUDIO_X + 15, 2, ICON_TOPMENU_VOLUME_SCALE, - COLOR_THEME_PRIMARY3); + audioScale = new StaticIcon(this, W_AUDIO_SCALE_X, 2, + ICON_TOPMENU_VOLUME_SCALE, + COLOR_THEME_PRIMARY3); for (int i = 0; i < 5; i += 1) { - audioVol[i] = new StaticIcon(this, W_AUDIO_X, 2, - (EdgeTxIcon)(ICON_TOPMENU_VOLUME_0 + i), - COLOR_THEME_PRIMARY2); + audioVol[i] = new StaticIcon( + this, W_AUDIO_X, 2, + (EdgeTxIcon)(ICON_TOPMENU_VOLUME_0 + i), + COLOR_THEME_PRIMARY2); audioVol[i]->hide(); } audioVol[0]->show(); #endif - batteryIcon = new StaticIcon(this, W_AUDIO_X, 25, ICON_TOPMENU_TXBATT, + batteryIcon = new StaticIcon(this, W_AUDIO_X, W_BATT_Y, + ICON_TOPMENU_TXBATT, COLOR_THEME_PRIMARY2); #if defined(USB_CHARGER) - batteryChargeIcon = - new StaticIcon(this, W_AUDIO_X + 25, 23, ICON_TOPMENU_TXBATT_CHARGE, - COLOR_THEME_PRIMARY2); + batteryChargeIcon = new StaticIcon( + this, W_BATT_CHG_X, W_BATT_CHG_Y, + ICON_TOPMENU_TXBATT_CHARGE, COLOR_THEME_PRIMARY2); batteryChargeIcon->hide(); #endif #if defined(INTERNAL_MODULE_PXX1) && defined(EXTERNAL_ANTENNA) - extAntenna = new StaticIcon(this, W_RSSI_X - 4, 1, ICON_TOPMENU_ANTENNA, + extAntenna = new StaticIcon(this, W_RSSI_X - 4, 1, + ICON_TOPMENU_ANTENNA, COLOR_THEME_PRIMARY2); extAntenna->hide(); #endif batteryFill = lv_obj_create(lvobj); lv_obj_set_pos(batteryFill, W_AUDIO_X + 1, 26); - lv_obj_set_size(batteryFill, 20, 9); + lv_obj_set_size(batteryFill, W_BATT_FILL_W, W_BATT_FILL_H); lv_obj_set_style_bg_opa(batteryFill, LV_OPA_COVER, LV_PART_MAIN); update(); @@ -95,8 +95,9 @@ class RadioInfoWidget : public TopBarWidget for (unsigned int i = 0; i < DIM(rssiBarsHeight); i++) { uint8_t height = rssiBarsHeight[i]; rssiBars[i] = lv_obj_create(lvobj); - lv_obj_set_pos(rssiBars[i], W_RSSI_X + i * 6, 35 - height); - lv_obj_set_size(rssiBars[i], 4, height); + lv_obj_set_pos(rssiBars[i], W_RSSI_X + i * W_RSSI_BAR_SZ, + W_RSSI_BAR_H - height); + lv_obj_set_size(rssiBars[i], W_RSSI_BAR_W, height); etx_solid_bg(rssiBars[i], COLOR_THEME_PRIMARY3_INDEX); etx_bg_color(rssiBars[i], COLOR_THEME_PRIMARY2_INDEX, LV_STATE_USER_1); } @@ -160,10 +161,10 @@ class RadioInfoWidget : public TopBarWidget #endif // Battery level - uint8_t bars = GET_TXBATT_BARS(20); + uint8_t bars = GET_TXBATT_BARS(W_BATT_FILL_W); if (bars != lastBatt) { lastBatt = bars; - lv_obj_set_size(batteryFill, bars, 9); + lv_obj_set_size(batteryFill, bars, W_BATT_FILL_H); if (bars >= 12) { lv_obj_clear_state(batteryFill, LV_STATE_USER_1 | LV_STATE_USER_2); } else if (bars >= 5) { @@ -191,6 +192,22 @@ class RadioInfoWidget : public TopBarWidget static const ZoneOption options[]; + static constexpr coord_t W_AUDIO_X = 0; + static LAYOUT_VAL(W_AUDIO_SCALE_X, 15, 15) + static LAYOUT_VAL(W_USB_X, 32, 32) + static LAYOUT_VAL(W_USB_Y, 5, 5) + static LAYOUT_VAL(W_LOG_X, 32, 32) + static LAYOUT_VAL(W_LOG_Y, 3, 3) + static LAYOUT_VAL(W_RSSI_X, 40, 40) + static LAYOUT_VAL(W_RSSI_BAR_W, 4, 4) + static LAYOUT_VAL(W_RSSI_BAR_H, 35, 35) + static LAYOUT_VAL(W_RSSI_BAR_SZ, 6, 6) + static LAYOUT_VAL(W_BATT_Y, 25, 25) + static LAYOUT_VAL(W_BATT_FILL_W, 20, 20) + static LAYOUT_VAL(W_BATT_FILL_H, 9, 9) + static LAYOUT_VAL(W_BATT_CHG_X, 25, 25) + static LAYOUT_VAL(W_BATT_CHG_Y, 23, 23) + protected: uint8_t lastVol = 0; uint8_t lastBatt = 0; @@ -221,13 +238,6 @@ const ZoneOption RadioInfoWidget::options[] = { BaseWidgetFactory RadioInfoWidget("Radio Info", RadioInfoWidget::options, STR_RADIO_INFO_WIDGET); -// Adjustment to make main view date/time align with model/radio settings views -#if LCD_W > LCD_H -#define DT_OFFSET 24 -#else -#define DT_OFFSET 8 -#endif - class DateTimeWidget : public TopBarWidget { public: @@ -235,7 +245,7 @@ class DateTimeWidget : public TopBarWidget const rect_t& rect, Widget::PersistentData* persistentData) : TopBarWidget(factory, parent, rect, persistentData) { - dateTime = new HeaderDateTime(lvobj, DT_OFFSET, 3); + dateTime = new HeaderDateTime(lvobj, DT_X, DT_Y); update(); } @@ -264,6 +274,10 @@ class DateTimeWidget : public TopBarWidget int8_t lastMinute = -1; static const ZoneOption options[]; + + // Adjustment to make main view date/time align with model/radio settings views + static LAYOUT_VAL(DT_X, 24, 8) + static LAYOUT_VAL(DT_Y, 3, 3) }; const ZoneOption DateTimeWidget::options[] = { @@ -285,8 +299,9 @@ class InternalGPSWidget : public TopBarWidget Widget::PersistentData* persistentData) : TopBarWidget(factory, parent, rect, persistentData) { - icon = new StaticIcon(this, width() / 2 - 10, 19, ICON_TOPMENU_GPS, - COLOR_THEME_PRIMARY3); + icon = + new StaticIcon(this, width() / 2 - 10, 19, + ICON_TOPMENU_GPS, COLOR_THEME_PRIMARY3); numSats = new DynamicNumber( this, {0, 1, width(), 12}, [=] { return gpsData.numSat; }, diff --git a/radio/src/gui/colorlcd/widgets/timer.cpp b/radio/src/gui/colorlcd/widgets/timer.cpp index b0624c1e9a2..a6d5c88cfda 100644 --- a/radio/src/gui/colorlcd/widgets/timer.cpp +++ b/radio/src/gui/colorlcd/widgets/timer.cpp @@ -70,13 +70,13 @@ class TimerWidget : public Widget // Timer value - on large widgets unit0 = createUnitLabel(); - lv_obj_set_pos(unit0, 111, 33); + lv_obj_set_pos(unit0, U0_X, U0_Y); unit1 = createUnitLabel(); - lv_obj_set_pos(unit1, 161, 33); + lv_obj_set_pos(unit1, U1_X, U1_Y); digits0 = createDigitsLabel(); - lv_obj_set_pos(digits0, 76, 31); + lv_obj_set_pos(digits0, D0_X, D0_Y); digits1 = createDigitsLabel(); - lv_obj_set_pos(digits1, 126, 31); + lv_obj_set_pos(digits1, D1_X, D1_Y); timerArc = lv_arc_create(lvobj); lv_arc_set_rotation(timerArc, 270); @@ -87,11 +87,11 @@ class TimerWidget : public Widget lv_obj_remove_style(timerArc, NULL, LV_PART_KNOB); lv_obj_clear_flag(timerArc, LV_OBJ_FLAG_CLICKABLE); lv_obj_set_pos(timerArc, 2, 3); - lv_obj_set_size(timerArc, 64, 64); + lv_obj_set_size(timerArc, TMR_ARC_SZ, TMR_ARC_SZ); lv_obj_set_style_arc_opa(timerArc, LV_OPA_TRANSP, LV_PART_MAIN); - lv_obj_set_style_arc_width(timerArc, 10, LV_PART_MAIN); + lv_obj_set_style_arc_width(timerArc, TMR_ARC_W, LV_PART_MAIN); lv_obj_set_style_arc_opa(timerArc, LV_OPA_COVER, LV_PART_INDICATOR); - lv_obj_set_style_arc_width(timerArc, 10, LV_PART_INDICATOR); + lv_obj_set_style_arc_width(timerArc, TMR_ARC_W, LV_PART_INDICATOR); etx_obj_add_style(timerArc, styles->arc_color, LV_PART_INDICATOR); lv_obj_add_flag(timerArc, LV_OBJ_FLAG_HIDDEN); @@ -192,6 +192,21 @@ class TimerWidget : public Widget static const ZoneOption options[]; + static LAYOUT_VAL(TMR_LRG_W, 180, 180) + static LAYOUT_VAL(TMR_ARC_SZ, 64, 64) + static LAYOUT_VAL(TMR_ARC_W, 10, 10) + static LAYOUT_VAL(NM_LRG_X, 78, 78) + static LAYOUT_VAL(NM_LRG_Y, 19, 19) + static LAYOUT_VAL(NM_LRG_W, 93, 93) + static LAYOUT_VAL(U0_X, 111, 111) + static LAYOUT_VAL(U0_Y, 33, 33) + static LAYOUT_VAL(U1_X, 161, 161) + static LAYOUT_VAL(U1_Y, 33, 33) + static LAYOUT_VAL(D0_X, 76, 76) + static LAYOUT_VAL(D0_Y, 31, 31) + static LAYOUT_VAL(D1_X, 126, 126) + static LAYOUT_VAL(D1_Y, 31, 31) + protected: tmrval_t lastValue = 0; uint32_t lastStartValue = 0; @@ -217,14 +232,14 @@ class TimerWidget : public Widget bool hasName = ZLEN(timerData.name) > 0; - if (width() >= 180 && height() >= 70) { + if (width() >= TMR_LRG_W && height() >= 70) { isLarge = true; if (hasName) lv_obj_clear_state(nameLabel, EXT_NAME_ALIGN_RIGHT); else lv_obj_add_state(nameLabel, EXT_NAME_ALIGN_RIGHT); - lv_obj_set_pos(nameLabel, 78, 19); - lv_obj_set_width(nameLabel, 93); + lv_obj_set_pos(nameLabel, NM_LRG_X, NM_LRG_Y); + lv_obj_set_width(nameLabel, NM_LRG_W); lv_obj_clear_state(nameLabel, ETX_NAME_COLOR_WHITE); lv_obj_add_flag(valLabel, LV_OBJ_FLAG_HIDDEN); diff --git a/radio/src/gui/colorlcd/widgets/value.cpp b/radio/src/gui/colorlcd/widgets/value.cpp index 75ca6dbd9ee..b2d2b66b503 100644 --- a/radio/src/gui/colorlcd/widgets/value.cpp +++ b/radio/src/gui/colorlcd/widgets/value.cpp @@ -159,6 +159,9 @@ class ValueWidget : public Widget lv_obj_t* valueShadow; LcdFlags valueFlags = 0; + static LAYOUT_VAL(VAL_Y1, 14, 14) + static LAYOUT_VAL(VAL_Y2, 18, 18) + void update() override { // get source from options[0] @@ -179,7 +182,7 @@ class ValueWidget : public Widget lv_coord_t labelX = 0; lv_coord_t labelY = 0; lv_coord_t valueX = 0; - lv_coord_t valueY = 14; + lv_coord_t valueY = VAL_Y1; // Set font to L lv_obj_clear_state(value, ETX_STATE_LARGE_FONT); @@ -204,7 +207,7 @@ class ValueWidget : public Widget valueX = (valAlign == ALIGN_LEFT) ? 4 : (valAlign == ALIGN_CENTER) ? 1 : -4; - valueY = 18; + valueY = VAL_Y2; if (field >= MIXSRC_FIRST_TELEM) { if (!isGPSSensor(1 + (field - MIXSRC_FIRST_TELEM) / 3)) { // Set font to XL diff --git a/radio/src/gui/colorlcd/widgets_container.h b/radio/src/gui/colorlcd/widgets_container.h index 01450b325cc..80bc6ae38fb 100644 --- a/radio/src/gui/colorlcd/widgets_container.h +++ b/radio/src/gui/colorlcd/widgets_container.h @@ -27,11 +27,8 @@ #define WIDGET_NAME_LEN 12 #define MAX_WIDGET_OPTIONS 5 // Name? -#if LCD_W > LCD_H -#define MAX_TOPBAR_ZONES 6 -#else -#define MAX_TOPBAR_ZONES 4 -#endif +static LAYOUT_VAL(MAX_TOPBAR_ZONES, 6, 4) + #define MAX_TOPBAR_OPTIONS 1 // just because of VC++ which doesn't like 0-size arrays :( // Common 'ZoneOptionValue's among all layouts diff --git a/radio/src/lua/api_general.cpp b/radio/src/lua/api_general.cpp index a13c62a30e7..d0a2d184cf5 100644 --- a/radio/src/lua/api_general.cpp +++ b/radio/src/lua/api_general.cpp @@ -3063,7 +3063,7 @@ LROT_BEGIN(etxcst, NULL, 0) LROT_NUMENTRY( TEXT_SIZE, ZoneOption::TextSize ) LROT_NUMENTRY( ALIGNMENT, ZoneOption::Align ) LROT_NUMENTRY( SWITCH, ZoneOption::Switch ) - LROT_NUMENTRY( MENU_HEADER_HEIGHT, COLOR2FLAGS(MENU_HEADER_HEIGHT) ) + LROT_NUMENTRY( MENU_HEADER_HEIGHT, COLOR2FLAGS(EdgeTxStyles::MENU_HEADER_HEIGHT) ) // Colors gui/colorlcd/colors.h LROT_NUMENTRY( COLOR_THEME_PRIMARY1, COLOR2FLAGS(COLOR_THEME_PRIMARY1_INDEX) ) diff --git a/radio/src/lua/interface.cpp b/radio/src/lua/interface.cpp index 007792d8e6e..3bc53b1eadf 100644 --- a/radio/src/lua/interface.cpp +++ b/radio/src/lua/interface.cpp @@ -735,7 +735,7 @@ void displayLuaError(bool firstCall = false) coord_t w = 0.75 * LCD_W; coord_t left = (LCD_W - w) / 2; -#if (LCD_W > LCD_H) +#if (!PORTRAIT_LCD) coord_t hh = getFontHeight(FONT(XL)) + 4; #else coord_t hh = getFontHeight(FONT(L)) + 4; @@ -750,7 +750,7 @@ void displayLuaError(bool firstCall = false) luaLcdBuffer->drawSolidFilledRect(left, top - hh, w, hh, COLOR_THEME_SECONDARY1); luaLcdBuffer->drawSolidFilledRect(left, top, w, h, COLOR_THEME_SECONDARY3); -#if (LCD_W > LCD_H) +#if (!PORTRAIT_LCD) luaLcdBuffer->drawText(left + 10, top - hh + 2, title, FONT(XL) | COLOR_THEME_PRIMARY2); luaLcdBuffer->drawTextLines(left + 10, top + 5, w - 20, h - 10, lua_warning_info, FONT(L) | COLOR_THEME_PRIMARY1); #else diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 88c18a37467..75f9c7567d6 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -690,7 +690,9 @@ union ReusableBuffer #if defined(SDCARD) struct { +#if defined(NUM_BODY_LINES) char lines[NUM_BODY_LINES][SD_SCREEN_FILE_LENGTH+1+1]; // the last char is used to store the flags (directory) of the line +#endif uint32_t available; uint16_t offset; uint16_t count; diff --git a/radio/src/storage/modelslist.h b/radio/src/storage/modelslist.h index 98c5485020e..9866e49a5af 100644 --- a/radio/src/storage/modelslist.h +++ b/radio/src/storage/modelslist.h @@ -48,7 +48,7 @@ #define DEFAULT_MODEL_SORT NAME_ASC -#if LCD_W > LCD_H // Landscape +#if !PORTRAIT_LCD // Landscape #define LABEL_TRUNCATE_LENGTH 21 #else #define LABEL_TRUNCATE_LENGTH 16 diff --git a/radio/src/targets/horus/hal.h b/radio/src/targets/horus/hal.h index a5b7e8c4883..7fe8e4c9813 100644 --- a/radio/src/targets/horus/hal.h +++ b/radio/src/targets/horus/hal.h @@ -1059,6 +1059,8 @@ #define BT_EN_GPIO_PIN LL_GPIO_PIN_10 // PG.10 #endif +#define PORTRAIT_LCD false +#define LANDSCAPE_LCD true #define LCD_W 480 #define LCD_H 272 #define LCD_PHYS_H LCD_H diff --git a/radio/src/targets/nv14/hal.h b/radio/src/targets/nv14/hal.h index 355633ef14e..9ae06f05afc 100644 --- a/radio/src/targets/nv14/hal.h +++ b/radio/src/targets/nv14/hal.h @@ -571,6 +571,9 @@ #define MIXER_SCHEDULER_TIMER_IRQn TIM8_BRK_TIM12_IRQn #define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_BRK_TIM12_IRQHandler +#define PORTRAIT_LCD true +#define LANDSCAPE_LCD false + #define LCD_W 320 #define LCD_H 480 diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index c819089bb0a..47dd3747a0e 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -696,6 +696,9 @@ #define MIXER_SCHEDULER_TIMER_IRQn TIM8_BRK_TIM12_IRQn #define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_BRK_TIM12_IRQHandler +#define PORTRAIT_LCD true +#define LANDSCAPE_LCD false + #define LCD_W 480 #define LCD_H 320 diff --git a/radio/src/thirdparty/libopenui/src/bitmapbuffer_draw_extra.cpp b/radio/src/thirdparty/libopenui/src/bitmapbuffer_draw_extra.cpp index 383e6f85d4c..dd390cb9f58 100644 --- a/radio/src/thirdparty/libopenui/src/bitmapbuffer_draw_extra.cpp +++ b/radio/src/thirdparty/libopenui/src/bitmapbuffer_draw_extra.cpp @@ -154,7 +154,7 @@ void BitmapBuffer::drawGPSPosition(coord_t x, coord_t y, int32_t longitude, { if (flags & PREC1) { drawGPSCoord(x, y, latitude, "NS", flags, true); - drawGPSCoord(x, y + FH, longitude, "EW", flags, true); + drawGPSCoord(x, y + EdgeTxStyles::PAGE_LINE_HEIGHT, longitude, "EW", flags, true); } else { if (flags & RIGHT) { x = drawGPSCoord(x, y, longitude, "EW", flags, true); @@ -181,7 +181,7 @@ void BitmapBuffer::drawDate(coord_t x, coord_t y, TelemetryItem &telemetryItem, x = drawText(x, y, s.c_str(), att); if (doTwoLines) { - y += FH; + y += EdgeTxStyles::PAGE_LINE_HEIGHT; x = ox; } else { x += 11; diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 6d4ce298812..10ba2126375 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -34,7 +34,7 @@ const lv_obj_class_t button_class = { .user_data = nullptr, .event_cb = nullptr, .width_def = LV_SIZE_CONTENT, - .height_def = 32, + .height_def = EdgeTxStyles::UI_ELEMENT_HEIGHT, .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, .group_def = LV_OBJ_CLASS_GROUP_DEF_TRUE, .instance_size = sizeof(lv_btn_t), @@ -54,7 +54,8 @@ Button::Button(Window* parent, const rect_t& rect, ButtonBase::ButtonBase(Window* parent, const rect_t& rect, std::function pressHandler, LvglCreate objConstruct) : - FormField(parent, rect, 0, objConstruct ? objConstruct : lv_btn_create), + FormField(parent, rect, 0, + objConstruct ? objConstruct : lv_btn_create), pressHandler(std::move(pressHandler)) { lv_obj_add_event_cb(lvobj, ButtonBase::long_pressed, LV_EVENT_LONG_PRESSED, @@ -106,7 +107,8 @@ void ButtonBase::long_pressed(lv_event_t* e) TextButton::TextButton(Window* parent, const rect_t& rect, std::string text, std::function pressHandler) : - ButtonBase(parent, rect, pressHandler, button_create), text(std::move(text)) + ButtonBase(parent, rect, pressHandler, button_create), + text(std::move(text)) { label = lv_label_create(lvobj); lv_label_set_text(label, this->text.c_str()); @@ -115,11 +117,11 @@ TextButton::TextButton(Window* parent, const rect_t& rect, std::string text, IconButton::IconButton(Window* parent, EdgeTxIcon icon, std::function pressHandler) : - ButtonBase(parent, {0, 0, 32, 32}, pressHandler, button_create) + ButtonBase(parent, {0, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT, EdgeTxStyles::UI_ELEMENT_HEIGHT}, pressHandler, button_create) { padAll(PAD_ZERO); iconImage = new StaticIcon(this, 0, 0, icon, COLOR_THEME_SECONDARY1); - iconImage->center(28, 28); + iconImage->center(EdgeTxStyles::UI_ELEMENT_HEIGHT - 4, EdgeTxStyles::UI_ELEMENT_HEIGHT - 4); } void IconButton::setIcon(EdgeTxIcon icon) { iconImage->setIcon(icon); } diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 195b35b363d..cf9452500e6 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -36,7 +36,7 @@ static const lv_obj_class_t choice_class = { .user_data = nullptr, .event_cb = nullptr, .width_def = LV_SIZE_CONTENT, - .height_def = 32, + .height_def = EdgeTxStyles::UI_ELEMENT_HEIGHT, .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, .instance_size = sizeof(lv_obj_t), diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index c1c852000a2..604b267b2fd 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -97,7 +97,7 @@ class DynamicMessageDialog : public BaseDialog DynamicMessageDialog(Window* parent, const char* title, std::function textHandler, const char* message = "", - const int lineHeight = PAGE_LINE_HEIGHT, + const int lineHeight = EdgeTxStyles::PAGE_LINE_HEIGHT, const LcdFlags textFlags = CENTERED); // Attn.: FONT(XXL) is not supported by DynamicMessageDialog diff --git a/radio/src/thirdparty/libopenui/src/libopenui_defines.h b/radio/src/thirdparty/libopenui/src/libopenui_defines.h index 11ca5102f43..f1289d0ff4c 100644 --- a/radio/src/thirdparty/libopenui/src/libopenui_defines.h +++ b/radio/src/thirdparty/libopenui/src/libopenui_defines.h @@ -24,23 +24,6 @@ #include "keys.h" #include "opentx_types.h" -constexpr uint32_t MENU_HEADER_BUTTON_WIDTH = 33; -constexpr uint32_t MENU_HEADER_BUTTONS_LEFT = 47; - -constexpr uint32_t MENU_HEADER_HEIGHT = 45; -constexpr uint32_t MENU_TITLE_TOP = 48; -constexpr uint32_t MENU_TITLE_HEIGHT = 21; -constexpr uint32_t MENU_BODY_TOP = MENU_TITLE_TOP + MENU_TITLE_HEIGHT; -constexpr uint32_t MENU_BODY_HEIGHT = LCD_H - MENU_BODY_TOP; -constexpr uint32_t MENUS_MARGIN_LEFT = 6; - -constexpr uint32_t PAGE_LINE_HEIGHT = 20; -constexpr uint32_t FH = PAGE_LINE_HEIGHT; -constexpr uint32_t NUM_BODY_LINES = MENU_BODY_HEIGHT / PAGE_LINE_HEIGHT; - -constexpr uint32_t PAGE_TITLE_TOP = 2; -constexpr uint32_t PAGE_TITLE_LEFT = 50; - // Used by Lua API #define INVERS 0x01u #define BLINK 0x1000u @@ -65,11 +48,3 @@ constexpr uint32_t PAGE_TITLE_LEFT = 50; #define NO_UNIT 0x40u #define LV_OBJ_FLAG_ENCODER_ACCEL LV_OBJ_FLAG_USER_1 - -constexpr coord_t MENUS_LINE_HEIGHT = 35; - -#if LCD_W > LCD_H -constexpr coord_t MENUS_MAX_HEIGHT = (MENUS_LINE_HEIGHT * 7) + 8; -#else -constexpr coord_t MENUS_MAX_HEIGHT = (MENUS_LINE_HEIGHT * 10); -#endif diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp index e565177778f..0458b8a846e 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.cpp +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.cpp @@ -65,7 +65,7 @@ MenuToolbarButton::MenuToolbarButton(Window* parent, const rect_t& rect, } MenuToolbar::MenuToolbar(Choice* choice, Menu* menu, const int columns) : - Window(menu, {0, 0, 76, MENUS_MAX_HEIGHT}), + Window(menu, {0, 0, 0, MENUS_MAX_HEIGHT}), choice(choice), menu(menu), filterColumns(columns), diff --git a/radio/src/thirdparty/libopenui/src/menutoolbar.h b/radio/src/thirdparty/libopenui/src/menutoolbar.h index 70166cd7b3a..6d287e56fe0 100644 --- a/radio/src/thirdparty/libopenui/src/menutoolbar.h +++ b/radio/src/thirdparty/libopenui/src/menutoolbar.h @@ -20,6 +20,7 @@ #include "button.h" #include "choice.h" +#include "listbox.h" class Menu; @@ -44,6 +45,14 @@ class MenuToolbar : public Window virtual void longPress() {} + static LAYOUT_VAL(MENUS_TOOLBAR_BUTTON_WIDTH, 32, 32) + +#if PORTRAIT_LCD + static constexpr coord_t MENUS_MAX_HEIGHT = (ListBox::MENUS_LINE_HEIGHT * 10); +#else + static constexpr coord_t MENUS_MAX_HEIGHT = (ListBox::MENUS_LINE_HEIGHT * 7) + 8; +#endif + protected: Choice* choice; Choice::FilterFct filter; diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 25fa05751dd..0e6b3b17cbc 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -59,6 +59,7 @@ NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, update(); etx_textarea_style(lvobj); + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT); etx_obj_add_style(lvobj, styles->text_align_right, LV_PART_MAIN); @@ -70,7 +71,7 @@ NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, lv_obj_set_parent(lvobj, parent->getLvObj()); setupLVGL(); - if (rect.w == 0) setWidth(100); + if (rect.w == 0) setWidth(DEF_W); lv_obj_enable_style_refresh(true); lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index a17e05e34f2..873000f679b 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -146,6 +146,8 @@ class NumberEdit: public FormField virtual void update(); + static LAYOUT_VAL(DEF_W, 100, 100) + protected: int vdefault = 0; int vmin; diff --git a/radio/src/thirdparty/libopenui/src/progress.cpp b/radio/src/thirdparty/libopenui/src/progress.cpp index 9a287c1487e..3488dcc9506 100644 --- a/radio/src/thirdparty/libopenui/src/progress.cpp +++ b/radio/src/thirdparty/libopenui/src/progress.cpp @@ -36,7 +36,7 @@ static const lv_obj_class_t bar_class = { .user_data = nullptr, .event_cb = nullptr, .width_def = LV_PCT(100), - .height_def = 32, + .height_def = EdgeTxStyles::UI_ELEMENT_HEIGHT, .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, .instance_size = sizeof(lv_bar_t), diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index 302b3a63cf0..e71cb784080 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -42,6 +42,7 @@ TextEdit::TextEdit(Window* parent, const rect_t& rect, char* value, update(); etx_textarea_style(lvobj); + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT); lv_textarea_set_max_length(lvobj, length); lv_textarea_set_placeholder_text(lvobj, "---"); @@ -49,7 +50,7 @@ TextEdit::TextEdit(Window* parent, const rect_t& rect, char* value, lv_obj_set_parent(lvobj, parent->getLvObj()); setupLVGL(); - if (rect.w == 0) setWidth(100); + if (rect.w == 0) setWidth(DEF_W); lv_obj_enable_style_refresh(true); lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index f0ac4a2778e..6ce20d13543 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -34,6 +34,8 @@ class TextEdit : public FormField void update(); + static LAYOUT_VAL(DEF_W, 100, 100) + protected: static void event_cb(lv_event_t* e); diff --git a/radio/src/thirdparty/libopenui/src/toggleswitch.cpp b/radio/src/thirdparty/libopenui/src/toggleswitch.cpp index d459f2c24d1..3a046e942b6 100644 --- a/radio/src/thirdparty/libopenui/src/toggleswitch.cpp +++ b/radio/src/thirdparty/libopenui/src/toggleswitch.cpp @@ -55,8 +55,8 @@ static const lv_obj_class_t switch_class = { .destructor_cb = nullptr, .user_data = nullptr, .event_cb = nullptr, - .width_def = 0, - .height_def = 32, + .width_def = ToggleSwitch::TOGGLE_W, + .height_def = EdgeTxStyles::UI_ELEMENT_HEIGHT, .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, .instance_size = sizeof(lv_switch_t), diff --git a/radio/src/thirdparty/libopenui/src/toggleswitch.h b/radio/src/thirdparty/libopenui/src/toggleswitch.h index 70bde0391f7..6ada607a025 100644 --- a/radio/src/thirdparty/libopenui/src/toggleswitch.h +++ b/radio/src/thirdparty/libopenui/src/toggleswitch.h @@ -47,7 +47,9 @@ class ToggleSwitch : public FormField } void update() const; - + + static LAYOUT_VAL(TOGGLE_W, 52, 52) + protected: std::function _getValue; std::function _setValue; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 95ab645948c..c0bd815566c 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -382,7 +382,7 @@ void Window::enable(bool enabled) void Window::addBackButton() { new ButtonBase( - this, {0, 0, MENU_HEADER_HEIGHT, MENU_HEADER_HEIGHT}, + this, {0, 0, EdgeTxStyles::MENU_HEADER_HEIGHT, EdgeTxStyles::MENU_HEADER_HEIGHT}, [=]() -> uint8_t { onCancel(); return 0; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 3b84ff17bbd..17fffd63fda 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -26,6 +26,7 @@ #include "bitmapbuffer.h" #include "libopenui_defines.h" #include "opentx_helpers.h" +#include "themes/etx_lv_theme.h" typedef uint32_t WindowFlags; @@ -33,14 +34,6 @@ typedef uint32_t WindowFlags; #undef OPAQUE #endif -enum PaddingSize { - PAD_ZERO = 0, - PAD_TINY = 2, - PAD_SMALL = 4, - PAD_MEDIUM = 6, - PAD_LARGE = 8 -}; - constexpr WindowFlags OPAQUE = 1u << 0u; constexpr WindowFlags NO_FOCUS = 1u << 1u; @@ -181,7 +174,7 @@ class Window virtual bool isBubblePopup() { return false; } void setFlexLayout(lv_flex_flow_t flow = LV_FLEX_FLOW_COLUMN, - lv_coord_t padding = 2, coord_t width = LV_PCT(100), + lv_coord_t padding = PAD_TINY, coord_t width = LV_PCT(100), coord_t height = LV_SIZE_CONTENT); FormLine *newLine(FlexGridLayout &layout); diff --git a/radio/src/translations/cn.h b/radio/src/translations/cn.h index e24ac1a551b..354d82e5e90 100644 --- a/radio/src/translations/cn.h +++ b/radio/src/translations/cn.h @@ -296,7 +296,7 @@ #define TR_ELEVATOR TR("俯仰源", "俯仰混控源") #define TR_SWASHRING "斜盘行程" #define TR_MODE "模式" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "左摇杆" #else #define TR_LEFT_STICK "左摇杆" @@ -1151,7 +1151,7 @@ #define TR_USE_THEME_COLOR "使用主题颜色" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "将所有微调导入中点偏移值" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "打开通道监视器" #else #define TR_OPEN_CHANNEL_MONITORS "通道监视" diff --git a/radio/src/translations/cz.h b/radio/src/translations/cz.h index 6078562dd42..11046bd11d6 100644 --- a/radio/src/translations/cz.h +++ b/radio/src/translations/cz.h @@ -311,7 +311,7 @@ #define TR_ELEVATOR TR3("Podélná cykl.", "Podélná cykl.", "Podélná cyklika") #define TR_SWASHRING "Cyklika" #define TR_MODE "Mód" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Vlevo" #else #define TR_LEFT_STICK "Vlevo" @@ -1168,7 +1168,7 @@ #define TR_USE_THEME_COLOR "Použít barevný motiv" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Trimy do subtrimů" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Otevřít monitor kanálů" #else #define TR_OPEN_CHANNEL_MONITORS "Otevřít mon. kanálů" diff --git a/radio/src/translations/da.h b/radio/src/translations/da.h index 87a9d773b55..f5b1ad69d77 100644 --- a/radio/src/translations/da.h +++ b/radio/src/translations/da.h @@ -51,7 +51,7 @@ #define TR_TRNCHN "KA1","KA2","KA3","KA4" #define TR_AUX_SERIAL_MODES "FRA","Telem spejlet","Telemetri ind","SBUS træner","LUA","CLI","GPS","Debug","SpaceMouse","Eksternt modul" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_SWTYPES "Ingen", "2 pos skift","2 position","3 position" #else #define TR_SWTYPES "Ingen","Skift","2POS","3POS" @@ -304,7 +304,7 @@ #define TR_ELEVATOR TR("Long. cyc.", "Long. cyc. kilde") #define TR_SWASHRING "Swash ring" #define TR_MODE TR("Tils.","Tilstand") -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Venstre" #else #define TR_LEFT_STICK "Ven" @@ -1122,7 +1122,7 @@ #define TR_WIDGET_SETTINGS "Widget indstilinger" #define TR_REMOVE_SCREEN "Slet skærm" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_SETUP_WIDGETS "Opsæt widget" #else #define TR_SETUP_WIDGETS "Widget" @@ -1167,7 +1167,7 @@ #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Tilføj alle trim til subtrim" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Åbn kanal monitor" #else #define TR_OPEN_CHANNEL_MONITORS "Åbn kanal mon." diff --git a/radio/src/translations/de.h b/radio/src/translations/de.h index 50ed5d63620..b56a1458e3b 100644 --- a/radio/src/translations/de.h +++ b/radio/src/translations/de.h @@ -302,7 +302,7 @@ #define TR_ELEVATOR "Nick Quelle" #define TR_SWASHRING TR("Ring Begrenz", "Ring Begrenzung") #define TR_MODE "Modus" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Links" #else #define TR_LEFT_STICK "Li" @@ -1159,7 +1159,7 @@ #define TR_USE_THEME_COLOR "Farbe des Themes verwenden" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Alle Trimmungen übernehmen" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Öffne Kanalmonitor" #else #define TR_OPEN_CHANNEL_MONITORS "Öffne Kanalmon." diff --git a/radio/src/translations/en.h b/radio/src/translations/en.h index 71c1df42f81..ae192287ff8 100644 --- a/radio/src/translations/en.h +++ b/radio/src/translations/en.h @@ -301,7 +301,7 @@ #define TR_ELEVATOR TR("Long. cyc.", "Long. cyc. source") #define TR_SWASHRING "Swash Ring" #define TR_MODE "Mode" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Left" #else #define TR_LEFT_STICK "Left" @@ -1156,7 +1156,7 @@ #define TR_USE_THEME_COLOR "Use theme color" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Add all Trims to Subtrims" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Open Channel Monitor" #else #define TR_OPEN_CHANNEL_MONITORS "Open Channel Mon." diff --git a/radio/src/translations/es.h b/radio/src/translations/es.h index 84d11bc36df..869e8b70163 100644 --- a/radio/src/translations/es.h +++ b/radio/src/translations/es.h @@ -299,7 +299,7 @@ #define TR_ELEVATOR TR("Col. long. ", "Fuente col. longitudinal") #define TR_SWASHRING "Ciclico" #define TR_MODE "Modo" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Izquierdo" #else #define TR_LEFT_STICK "Izq" @@ -1156,7 +1156,7 @@ #define TR_USE_THEME_COLOR "Use theme color" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Add all Trims to Subtrims" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Open Channel Monitor" #else #define TR_OPEN_CHANNEL_MONITORS "Open Channel Mon." diff --git a/radio/src/translations/fi.h b/radio/src/translations/fi.h index ca437831c40..e5b98f49a4b 100644 --- a/radio/src/translations/fi.h +++ b/radio/src/translations/fi.h @@ -313,7 +313,7 @@ #define TR_ELEVATOR TR("Long. cyc.", "Long. cyc. source") #define TR_SWASHRING "Swash Ring" #define TR_MODE "Mode" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Vasemmalle" #else #define TR_LEFT_STICK "Va" @@ -1170,7 +1170,7 @@ #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Add all Trims to Subtrims" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Open Channel Monitor" #else #define TR_OPEN_CHANNEL_MONITORS "Open Channel Mon." diff --git a/radio/src/translations/fr.h b/radio/src/translations/fr.h index 326d07eff92..a611c494529 100644 --- a/radio/src/translations/fr.h +++ b/radio/src/translations/fr.h @@ -308,7 +308,7 @@ #define TR_ELEVATOR "Source cyc. lon." #define TR_SWASHRING TR("Limite Cycl.", "Limite du cyclique") #define TR_MODE "Mode" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Gauche" #else #define TR_LEFT_STICK "Gauche" @@ -1173,7 +1173,7 @@ #define TR_USE_THEME_COLOR "Utiliser couleur du thème" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Ajouter tous trims aux sub-trims" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Ouvrir Monit. de Canal" #else #define TR_OPEN_CHANNEL_MONITORS "Ouvrir Mon. de Canal" diff --git a/radio/src/translations/he.h b/radio/src/translations/he.h index bc9ef8cf349..5932d3d0197 100644 --- a/radio/src/translations/he.h +++ b/radio/src/translations/he.h @@ -305,7 +305,7 @@ #define TR_ELEVATOR TR("Long. cyc.", "Long. cyc. source") #define TR_SWASHRING "Swash Ring" #define TR_MODE "Mode" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Left" #else #define TR_LEFT_STICK "Left" @@ -1155,7 +1155,7 @@ #define TR_USE_THEME_COLOR "Use theme color" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "מיכרוז כל הקיזוזים" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "פתח מצג ערוצים" #else #define TR_OPEN_CHANNEL_MONITORS "פתח מצג ערוצים." diff --git a/radio/src/translations/it.h b/radio/src/translations/it.h index f566bffef64..c54b22d8211 100644 --- a/radio/src/translations/it.h +++ b/radio/src/translations/it.h @@ -301,7 +301,7 @@ #define TR_ELEVATOR TR("Cic. long.", "Sorg. cic. long.") #define TR_SWASHRING "Anello Ciclico" #define TR_MODE "Modo" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Sinistro" #else #define TR_LEFT_STICK "Sx" @@ -1150,7 +1150,7 @@ #define TR_USE_THEME_COLOR "Usa colore tema" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Agg. Trim a Subtrim" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Apri Monitor Canali" #else #define TR_OPEN_CHANNEL_MONITORS "Apri Mon. Canali" diff --git a/radio/src/translations/jp.h b/radio/src/translations/jp.h index 7ddebe9d180..2246ccaa7e9 100644 --- a/radio/src/translations/jp.h +++ b/radio/src/translations/jp.h @@ -300,7 +300,7 @@ #define TR_ELEVATOR TR("Long. cyc.", "Long. cyc. ソース") #define TR_SWASHRING "スワッシュリング" #define TR_MODE "モード" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "左スティック" #else #define TR_LEFT_STICK "左スティック" @@ -1155,7 +1155,7 @@ #define TR_USE_THEME_COLOR "テーマ色を使用" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "サブトリムにすべてのトリムを追加" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "チャンネルモニター起動" #else #define TR_OPEN_CHANNEL_MONITORS "Open Channel Mon." diff --git a/radio/src/translations/nl.h b/radio/src/translations/nl.h index cfb0991617b..80eed888830 100644 --- a/radio/src/translations/nl.h +++ b/radio/src/translations/nl.h @@ -298,7 +298,7 @@ #define TR_ELEVATOR TR("Long. cyc.", "Long. cyc. source") #define TR_SWASHRING "Swash Ring" #define TR_MODE "Mode" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Linkerkant" #else #define TR_LEFT_STICK "Li" @@ -1162,7 +1162,7 @@ #define TR_USE_THEME_COLOR "Use theme color" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Add all Trims to Subtrims" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Open Channel Monitor" #else #define TR_OPEN_CHANNEL_MONITORS "Open Channel Mon." diff --git a/radio/src/translations/pl.h b/radio/src/translations/pl.h index 3b6f9bc52f4..72f153f6d24 100644 --- a/radio/src/translations/pl.h +++ b/radio/src/translations/pl.h @@ -298,7 +298,7 @@ #define TR_ELEVATOR TR("Long. cyc.", "Long. cyc. source") #define TR_SWASHRING "Tarcza " #define TR_MODE "Tryb" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Lewy" #else #define TR_LEFT_STICK "Lewy" @@ -1155,7 +1155,7 @@ #define TR_USE_THEME_COLOR "Użyj koloru szablonu" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Dodaj trymy do podtrymów" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Otwórz Monitor kanałów" #else #define TR_OPEN_CHANNEL_MONITORS "Otwórz Mon. kanałów" diff --git a/radio/src/translations/pt.h b/radio/src/translations/pt.h index 0eb4042acc5..178243b1058 100644 --- a/radio/src/translations/pt.h +++ b/radio/src/translations/pt.h @@ -304,7 +304,7 @@ #define TR_ELEVATOR TR("Arfagem", "Org. cíclico Arfagem") #define TR_SWASHRING "Swash Ring" #define TR_MODE "Modo" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Esq" #else #define TR_LEFT_STICK "Esq" @@ -1159,7 +1159,7 @@ #define TR_USE_THEME_COLOR "Use theme color" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Add all Trims to Subtrims" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Open Channel Monitor" #else #define TR_OPEN_CHANNEL_MONITORS "Open Channel Mon." diff --git a/radio/src/translations/ru.h b/radio/src/translations/ru.h index 139ccf93a8a..08df4c71880 100644 --- a/radio/src/translations/ru.h +++ b/radio/src/translations/ru.h @@ -303,7 +303,7 @@ #define TR_ELEVATOR TR("Тангаж", "Тангаж") #define TR_SWASHRING "Поворот" #define TR_MODE "Режим" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Лев" #else #define TR_LEFT_STICK "Лев" @@ -1158,7 +1158,7 @@ #define TR_USE_THEME_COLOR "Используйте цвет темы" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Добав все трим в субтрим" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Монитор откр кан" #else #define TR_OPEN_CHANNEL_MONITORS "Мон открыт кан" diff --git a/radio/src/translations/se.h b/radio/src/translations/se.h index d307bcc0267..ad0ae0bb77d 100644 --- a/radio/src/translations/se.h +++ b/radio/src/translations/se.h @@ -55,7 +55,7 @@ #define TR_TRNCHN "KA1","KA2","KA3","KA4" #define TR_AUX_SERIAL_MODES "AV","Speglad telemetri","Telemetri in","SBUS Lärare","LUA","CLI","GPS","Debug","SpaceMouse","Extern modul" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_SWTYPES "Ingen", "2 pos flipp","2 pos","3 pos" #else #define TR_SWTYPES "Ingen","Flipp","2 pos","3 pos" @@ -314,7 +314,7 @@ #define TR_ELEVATOR "Höjdroderkälla" #define TR_SWASHRING "Swashring" #define TR_MODE "Typ" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Vänster" #else #define TR_LEFT_STICK "Vä" @@ -1150,7 +1150,7 @@ #define TR_WIDGET_FULLSCREEN "Hel skärm" #define TR_REMOVE_SCREEN "Ta bort huvudvy" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_SETUP_WIDGETS "Konfigurera widgets" #else #define TR_SETUP_WIDGETS "Konfig. widgets" @@ -1191,7 +1191,7 @@ #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Addera alla trimmar till subtrimmar" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Öppna kanalmonitorn" #else #define TR_OPEN_CHANNEL_MONITORS "Öppna kanalmon." diff --git a/radio/src/translations/tw.h b/radio/src/translations/tw.h index bb125e95a81..171b8bfc18e 100644 --- a/radio/src/translations/tw.h +++ b/radio/src/translations/tw.h @@ -301,7 +301,7 @@ #define TR_ELEVATOR TR("俯仰源", "俯仰混控源") #define TR_SWASHRING "斜盤行程" #define TR_MODE "模式" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "左搖桿" #else #define TR_LEFT_STICK "左搖桿" @@ -1156,7 +1156,7 @@ #define TR_USE_THEME_COLOR "使用主題顏色" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "將所有微調導入中點偏移值" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "打開通道監視器" #else #define TR_OPEN_CHANNEL_MONITORS "通道監視" diff --git a/radio/src/translations/ua.h b/radio/src/translations/ua.h index eb31dcfc78e..c7bb683a493 100644 --- a/radio/src/translations/ua.h +++ b/radio/src/translations/ua.h @@ -303,7 +303,7 @@ #define TR_ELEVATOR TR("Тангаж", "Тангаж") #define TR_SWASHRING "Поворот" #define TR_MODE "Режим" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_LEFT_STICK "Ліво" #else #define TR_LEFT_STICK "Ліво" @@ -1158,7 +1158,7 @@ #define TR_USE_THEME_COLOR "Використ. колір теми" #define TR_ADD_ALL_TRIMS_TO_SUBTRIMS "Додати всі трими до субтримів" -#if LCD_W > LCD_H +#if !PORTRAIT_LCD #define TR_OPEN_CHANNEL_MONITORS "Відкрити монітори каналів" #else #define TR_OPEN_CHANNEL_MONITORS "Відкр. мон.кан." From 2a5201b310c1d452823431851482e14901c33034 Mon Sep 17 00:00:00 2001 From: philmoz Date: Tue, 30 Apr 2024 11:34:50 +1000 Subject: [PATCH 02/21] UI performance improvements. --- radio/src/gui/colorlcd/access_settings.cpp | 1 - radio/src/gui/colorlcd/curveedit.cpp | 2 +- radio/src/gui/colorlcd/hw_inputs.h | 9 - radio/src/gui/colorlcd/model_flightmodes.cpp | 47 +- radio/src/gui/colorlcd/model_gvars.cpp | 119 ++++- radio/src/gui/colorlcd/model_gvars.h | 4 + .../src/gui/colorlcd/model_mixer_scripts.cpp | 19 +- radio/src/gui/colorlcd/model_outputs.cpp | 28 +- radio/src/gui/colorlcd/model_select.cpp | 1 - radio/src/gui/colorlcd/model_telemetry.cpp | 11 +- radio/src/gui/colorlcd/model_usbjoystick.cpp | 17 +- radio/src/gui/colorlcd/preview_window.cpp | 8 +- radio/src/gui/colorlcd/radio_hardware.cpp | 86 ++-- radio/src/gui/colorlcd/radio_sdmanager.cpp | 28 +- radio/src/gui/colorlcd/radio_setup.cpp | 401 +++++++++------- radio/src/gui/colorlcd/radio_setup.h | 10 +- radio/src/gui/colorlcd/special_functions.cpp | 4 +- radio/src/gui/colorlcd/special_functions.h | 2 +- radio/src/gui/colorlcd/tabsgroup.cpp | 2 + radio/src/gui/colorlcd/tabsgroup.h | 1 + .../src/gui/colorlcd/themes/etx_lv_theme.cpp | 36 +- radio/src/gui/colorlcd/themes/etx_lv_theme.h | 3 +- radio/src/lv_conf.h | 4 + radio/src/thirdparty/libopenui/src/form.cpp | 12 - radio/src/thirdparty/libopenui/src/form.h | 3 - .../libopenui/src/keyboard_number.cpp | 2 +- .../libopenui/src/keyboard_number.h | 4 +- .../thirdparty/libopenui/src/numberedit.cpp | 436 ++++++++++++------ .../src/thirdparty/libopenui/src/numberedit.h | 225 ++++----- .../src/thirdparty/libopenui/src/textedit.cpp | 184 ++++++-- radio/src/thirdparty/libopenui/src/textedit.h | 27 +- radio/src/thirdparty/libopenui/src/window.cpp | 6 - radio/src/thirdparty/libopenui/src/window.h | 2 - 33 files changed, 1036 insertions(+), 708 deletions(-) diff --git a/radio/src/gui/colorlcd/access_settings.cpp b/radio/src/gui/colorlcd/access_settings.cpp index 05356b3f01b..03fa10d4bc2 100644 --- a/radio/src/gui/colorlcd/access_settings.cpp +++ b/radio/src/gui/colorlcd/access_settings.cpp @@ -370,7 +370,6 @@ RegisterDialog::RegisterDialog(Window* parent, uint8_t moduleIdx) : start(); // clears registration data buffer rx_name = new ModelTextEdit(line, rect_t{}, modSetup->registerRxName, PXX2_LEN_RX_NAME); - // lv_textarea_set_text(rx_name->getLvObj(), STR_WAITING_FOR_RX); rx_name->disable(); // Status diff --git a/radio/src/gui/colorlcd/curveedit.cpp b/radio/src/gui/colorlcd/curveedit.cpp index c3509274981..4300d2b353a 100644 --- a/radio/src/gui/colorlcd/curveedit.cpp +++ b/radio/src/gui/colorlcd/curveedit.cpp @@ -310,7 +310,7 @@ void CurveEditWindow::buildBody(Window* window) // Name new StaticText(iLine, rect_t{}, STR_NAME); - new ModelTextEdit(iLine, rect_t{0, 0, 100, 0}, curve.name, + new ModelTextEdit(iLine, rect_t{}, curve.name, sizeof(curve.name)); // Smooth diff --git a/radio/src/gui/colorlcd/hw_inputs.h b/radio/src/gui/colorlcd/hw_inputs.h index 8bc151e886b..ed26f14f1fe 100644 --- a/radio/src/gui/colorlcd/hw_inputs.h +++ b/radio/src/gui/colorlcd/hw_inputs.h @@ -61,12 +61,3 @@ template struct HWInputDialog : public BaseDialog { HWInputDialog(const char* title = nullptr); }; - -template -TextButton* makeHWInputButton(Window* parent, const char* title) -{ - return new TextButton(parent, rect_t{0, 0, 100, 0}, title, [=]() { - new HWInputDialog(title); - return 0; - }); -} diff --git a/radio/src/gui/colorlcd/model_flightmodes.cpp b/radio/src/gui/colorlcd/model_flightmodes.cpp index 4d17712f4ed..d7e55c5642d 100644 --- a/radio/src/gui/colorlcd/model_flightmodes.cpp +++ b/radio/src/gui/colorlcd/model_flightmodes.cpp @@ -209,8 +209,6 @@ class FlightModeBtn : public ListLineButton padColumn(PAD_ZERO); setHeight(BTN_H); - check(isActive()); - lv_obj_add_event_cb(lvobj, FlightModeBtn::on_draw, LV_EVENT_DRAW_MAIN_BEGIN, nullptr); } @@ -230,6 +228,8 @@ class FlightModeBtn : public ListLineButton { init = true; + check(isActive()); + fmID = etx_create(&fm_id_class, lvobj); lv_obj_set_pos(fmID, FMID_X, FMID_Y); char label[8]; @@ -257,23 +257,29 @@ class FlightModeBtn : public ListLineButton bool isActive() const override { return (getFlightMode() == index); } + void setTrimValue(uint8_t t) + { + lastTrim[t] = g_model.flightModeData[index].trim[t].value; + + uint8_t mode = g_model.flightModeData[index].trim[t].mode; + bool checked = (mode != TRIM_MODE_NONE); + bool showValue = (index == 0) || ((mode & 1) || (mode >> 1 == index)); + + if (checked && showValue) + lv_label_set_text(fmTrimValue[t], + formatNumberAsString(lastTrim[t]).c_str()); + else + lv_label_set_text(fmTrimValue[t], ""); + } + void checkEvents() override { ListLineButton::checkEvents(); if (!refreshing && init) { refreshing = true; - const auto& fm = g_model.flightModeData[index]; for (int t = 0; t < keysGetMaxTrims(); t += 1) { - if (lastTrim[t] != fm.trim[t].value) { - lastTrim[t] = fm.trim[t].value; - - uint8_t mode = fm.trim[t].mode; - bool checked = (mode != TRIM_MODE_NONE); - bool showValue = (index == 0) || ((mode & 1) || (mode >> 1 == index)); - - if (checked && showValue) - lv_label_set_text(fmTrimValue[t], - formatNumberAsString(fm.trim[t].value).c_str()); + if (lastTrim[t] != g_model.flightModeData[index].trim[t].value) { + setTrimValue(t); } } refreshing = false; @@ -301,17 +307,8 @@ class FlightModeBtn : public ListLineButton } for (int i = 0; i < keysGetMaxTrims(); i += 1) { - uint8_t mode = fm.trim[i].mode; - bool checked = (mode != TRIM_MODE_NONE); - bool showValue = (index == 0) || ((mode & 1) || (mode >> 1 == index)); - - lv_label_set_text(fmTrimMode[i], getFMTrimStr(mode, false).c_str()); - - if (checked && showValue) - lv_label_set_text(fmTrimValue[i], - formatNumberAsString(fm.trim[i].value).c_str()); - else - lv_label_set_text(fmTrimValue[i], ""); + setTrimValue(i); + lv_label_set_text(fmTrimMode[i], getFMTrimStr(fm.trim[i].mode, false).c_str()); } lv_label_set_text( @@ -353,7 +350,7 @@ class FlightModeBtn : public ListLineButton lv_obj_t* fmTrimValue[MAX_FMTRIMS] = {nullptr}; lv_obj_t* fmFadeIn = nullptr; lv_obj_t* fmFadeOut = nullptr; - int lastTrim[MAX_FMTRIMS]; + int lastTrim[MAX_FMTRIMS] = {0}; static const lv_obj_class_t fm_id_class; static const lv_obj_class_t fm_name_class; diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index 89113d66da4..6be86a030f5 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -48,7 +48,7 @@ void getFMExtName(char* dest, int8_t idx) class GVarButton : public ListLineButton { public: - GVarButton(Window* parent, const rect_t& rect, uint8_t gvar) : + GVarButton(Window* parent, uint8_t gvar) : ListLineButton(parent, gvar) { padAll(PAD_ZERO); @@ -67,16 +67,20 @@ class GVarButton : public ListLineButton } static LAYOUT_VAL(GVAR_NAME_SIZE, 44, 44) - static constexpr coord_t GVAR_VAL_H = EdgeTxStyles::PAGE_LINE_HEIGHT * 2 - PAD_MEDIUM; + static constexpr coord_t GVAR_VAL_H = EdgeTxStyles::PAGE_LINE_HEIGHT + 2; static LAYOUT_VAL(GVAR_VAL_W, 45, 50) static LAYOUT_VAL(GVAR_COLS, MAX_FLIGHT_MODES, 5) - static LAYOUT_VAL(BTN_H, 38, 72) - static LAYOUT_VAL(GVAR_NM_Y, 7, 24) + static LAYOUT_VAL(BTN_H, EdgeTxStyles::UI_ELEMENT_HEIGHT, 50) + static LAYOUT_VAL(GVAR_NM_Y, 4, 13) + static LAYOUT_VAL(GVAR_YO, 4, 2) + static LAYOUT_VAL(HDR_H, EdgeTxStyles::PAGE_LINE_HEIGHT + 2, EdgeTxStyles::PAGE_LINE_HEIGHT * 2 + 2) + + static const lv_obj_class_t gv_label_class; + static const lv_obj_class_t gv_value_class; protected: bool init = false; uint8_t currentFlightMode = 0; // used for checking updates - lv_obj_t* labelTexts[MAX_FLIGHT_MODES]; lv_obj_t* valueTexts[MAX_FLIGHT_MODES]; gvar_t values[MAX_FLIGHT_MODES]; @@ -90,9 +94,7 @@ class GVarButton : public ListLineButton uint8_t newFM = getFlightMode(); if (currentFlightMode != newFM) { lv_obj_add_state(valueTexts[newFM], LV_STATE_CHECKED); - lv_obj_add_state(labelTexts[newFM], LV_STATE_CHECKED); lv_obj_clear_state(valueTexts[currentFlightMode], LV_STATE_CHECKED); - lv_obj_clear_state(labelTexts[currentFlightMode], LV_STATE_CHECKED); currentFlightMode = newFM; } @@ -121,23 +123,13 @@ class GVarButton : public ListLineButton lv_obj_set_size(nm, GVAR_NAME_SIZE, EdgeTxStyles::PAGE_LINE_HEIGHT); if (modelFMEnabled()) { - char label[16] = {}; - for (int flightMode = 0; flightMode < MAX_FLIGHT_MODES; flightMode++) { - getFlightModeString(label, flightMode + 1); - - labelTexts[flightMode] = etx_create(&gv_label_class, lvobj); - lv_label_set_text(labelTexts[flightMode], label); - lv_obj_set_pos(labelTexts[flightMode], (flightMode % GVAR_COLS) * GVAR_VAL_W + GVAR_NAME_SIZE + PAD_SMALL, - (flightMode / GVAR_COLS) * GVAR_VAL_H); - valueTexts[flightMode] = etx_create(&gv_value_class, lvobj); - lv_obj_set_pos(valueTexts[flightMode], (flightMode % GVAR_COLS) * GVAR_VAL_W + GVAR_NAME_SIZE + PAD_SMALL, - (flightMode / GVAR_COLS) * GVAR_VAL_H + EdgeTxStyles::PAGE_LINE_HEIGHT - PAD_MEDIUM); + lv_obj_set_pos(valueTexts[flightMode], (flightMode % GVAR_COLS) * GVAR_VAL_W + GVAR_NAME_SIZE + 4, + (flightMode / GVAR_COLS) * GVAR_VAL_H + GVAR_YO); if (flightMode == currentFlightMode) { lv_obj_add_state(valueTexts[flightMode], LV_STATE_CHECKED); - lv_obj_add_state(labelTexts[flightMode], LV_STATE_CHECKED); } updateValueText(flightMode); @@ -186,9 +178,6 @@ class GVarButton : public ListLineButton bool isActive() const override { return false; } void refresh() override {} - - static const lv_obj_class_t gv_label_class; - static const lv_obj_class_t gv_value_class; }; static void gv_label_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) @@ -231,6 +220,74 @@ const lv_obj_class_t GVarButton::gv_value_class = { .instance_size = sizeof(lv_label_t), }; +class GVarHeader : public Window +{ + public: + GVarHeader(Window* parent) : + Window(parent, {0, 0, LCD_W, GVarButton::HDR_H}) + { + padAll(PAD_ZERO); + etx_solid_bg(lvobj, COLOR_THEME_SECONDARY3_INDEX); + + lv_obj_add_event_cb(lvobj, GVarHeader::on_draw, LV_EVENT_DRAW_MAIN_BEGIN, + nullptr); + } + + static void on_draw(lv_event_t* e) + { + lv_obj_t* target = lv_event_get_target(e); + auto line = (GVarHeader*)lv_obj_get_user_data(target); + if (line) line->build(); + } + + protected: + bool init = false; + uint8_t currentFlightMode = 0; // used for checking updates + lv_obj_t* labelTexts[MAX_FLIGHT_MODES]; + + int numFlightModes() { return modelFMEnabled() ? MAX_FLIGHT_MODES : 1; } + + void checkEvents() override + { + Window::checkEvents(); + if (init) { + uint8_t newFM = getFlightMode(); + if (currentFlightMode != newFM) { + lv_obj_add_state(labelTexts[newFM], LV_STATE_CHECKED); + lv_obj_clear_state(labelTexts[currentFlightMode], LV_STATE_CHECKED); + + currentFlightMode = newFM; + } + } + } + + void build() + { + if (init) return; + + init =true; + + currentFlightMode = getFlightMode(); + + char label[16] = {}; + + for (int flightMode = 0; flightMode < MAX_FLIGHT_MODES; flightMode++) { + getFlightModeString(label, flightMode + 1); + + labelTexts[flightMode] = etx_create(&GVarButton::gv_value_class, lvobj); + lv_label_set_text(labelTexts[flightMode], label); + lv_obj_set_pos(labelTexts[flightMode], (flightMode % GVarButton::GVAR_COLS) * GVarButton::GVAR_VAL_W + GVarButton::GVAR_NAME_SIZE + 12, + (flightMode / GVarButton::GVAR_COLS) * EdgeTxStyles::PAGE_LINE_HEIGHT + 1); + + if (flightMode == currentFlightMode) { + lv_obj_add_state(labelTexts[flightMode], LV_STATE_CHECKED); + } + } + + lv_obj_update_layout(lvobj); + } +}; + class GVarEditWindow : public Page { public: @@ -470,20 +527,34 @@ ModelGVarsPage::ModelGVarsPage() : { } +void ModelGVarsPage::cleanup() +{ + if (hdr) + hdr->deleteLater(); + hdr = nullptr; +} + void ModelGVarsPage::rebuild(Window* window) { auto scroll_y = lv_obj_get_scroll_y(window->getLvObj()); window->clear(); + cleanup(); build(window); lv_obj_scroll_to_y(window->getLvObj(), scroll_y, LV_ANIM_OFF); } void ModelGVarsPage::build(Window* window) { - window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY_GAP); + coord_t yo = 0; + if (modelFMEnabled()) { + hdr = new GVarHeader(window->getParent()); + lv_obj_set_pos(hdr->getLvObj(), 0, TabsGroup::MENU_TITLE_TOP + TabsGroup::MENU_TITLE_HEIGHT); + yo = GVarButton::HDR_H - 4; + } for (uint8_t index = 0; index < MAX_GVARS; index++) { - auto button = new GVarButton(window, rect_t{}, index); + auto button = new GVarButton(window, index); + lv_obj_set_pos(button->getLvObj(), 0, yo + index * (GVarButton::BTN_H + PAD_TINY)); button->setPressHandler([=]() { Menu* menu = new Menu(window); menu->addLine(STR_EDIT, [=]() { diff --git a/radio/src/gui/colorlcd/model_gvars.h b/radio/src/gui/colorlcd/model_gvars.h index 5de3af004a3..c09da19f62b 100644 --- a/radio/src/gui/colorlcd/model_gvars.h +++ b/radio/src/gui/colorlcd/model_gvars.h @@ -31,7 +31,11 @@ class ModelGVarsPage : public PageTab bool isVisible() const override { return modelGVEnabled(); } + void cleanup() override; + protected: + Window* hdr = nullptr; + void build(Window* window) override; void rebuild(Window* window); }; diff --git a/radio/src/gui/colorlcd/model_mixer_scripts.cpp b/radio/src/gui/colorlcd/model_mixer_scripts.cpp index c825aa67dc7..ad641d25824 100644 --- a/radio/src/gui/colorlcd/model_mixer_scripts.cpp +++ b/radio/src/gui/colorlcd/model_mixer_scripts.cpp @@ -192,7 +192,7 @@ class ScriptLineButton : public ListLineButton lv_obj_set_style_pad_column(lvobj, 4, 0); lv_obj_update_layout(parent->getLvObj()); - if (lv_obj_is_visible(lvobj)) delayed_init(nullptr); + if (lv_obj_is_visible(lvobj)) delayed_init(); lv_obj_add_event_cb(lvobj, ScriptLineButton::on_draw, LV_EVENT_DRAW_MAIN_BEGIN, nullptr); @@ -204,14 +204,15 @@ class ScriptLineButton : public ListLineButton auto line = (ScriptLineButton*)lv_obj_get_user_data(target); if (line) { if (!line->init) - line->delayed_init(e); - else - line->refresh(); + line->delayed_init(); + line->refresh(); } } - void delayed_init(lv_event_t* e) + void delayed_init() { + init = true; + auto lbl = lv_label_create(lvobj); etx_obj_add_style(lbl, styles->text_align_left, LV_PART_MAIN); lv_obj_set_grid_cell(lbl, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_CENTER, @@ -261,15 +262,7 @@ class ScriptLineButton : public ListLineButton } } - 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); - } } bool isActive() const override { return false; } diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index 9a1aa52d4a3..4445a0b0fcc 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -43,14 +43,15 @@ class OutputLineButton : public ListLineButton lv_obj_t* offset = nullptr; lv_obj_t* center = nullptr; StaticIcon* curve = nullptr; - OutputChannelBar* bar = nullptr; static void on_draw(lv_event_t* e) { lv_obj_t* target = lv_event_get_target(e); auto line = (OutputLineButton*)lv_obj_get_user_data(target); if (line) { - if (!line->init) line->delayed_init(); + if (!line->init) + line->delayed_init(); + line->refresh(); } } @@ -58,6 +59,16 @@ class OutputLineButton : public ListLineButton { init = true; + source = lv_label_create(lvobj); + lv_obj_set_pos(source, SRC_X, SRC_Y); + lv_obj_set_size(source, SRC_W, SRC_H); + +#if LCD_W > LCD_H + etx_font(source, FONT_XS_INDEX, ETX_STATE_NAME_FONT_SMALL); + lv_obj_set_style_pad_top(source, -2, ETX_STATE_NAME_FONT_SMALL); + lv_obj_set_style_text_line_space(source, -3, ETX_STATE_NAME_FONT_SMALL); +#endif + min = lv_label_create(lvobj); etx_obj_add_style(min, styles->text_align_right, LV_PART_MAIN); etx_font(min, FONT_BOLD_INDEX, ETX_STATE_MINMAX_BOLD); @@ -87,10 +98,9 @@ class OutputLineButton : public ListLineButton curve = new StaticIcon(this, CRV_X, CRV_Y, ICON_TEXTLINE_CURVE, COLOR_THEME_SECONDARY1); - bar = new OutputChannelBar(this, rect_t{BAR_X, PAD_MEDIUM, CH_BAR_WIDTH, CH_BAR_HEIGHT}, + new OutputChannelBar(this, rect_t{BAR_X, PAD_MEDIUM, CH_BAR_WIDTH, CH_BAR_HEIGHT}, index, false, false); - refresh(); checkEvents(); lv_obj_update_layout(lvobj); @@ -103,16 +113,6 @@ class OutputLineButton : public ListLineButton setHeight(CH_LINE_H); padAll(PAD_ZERO); - source = lv_label_create(lvobj); - lv_obj_set_pos(source, SRC_X, SRC_Y); - lv_obj_set_size(source, SRC_W, SRC_H); - -#if !PORTRAIT_LCD - etx_font(source, FONT_XS_INDEX, ETX_STATE_NAME_FONT_SMALL); - lv_obj_set_style_pad_top(source, -2, ETX_STATE_NAME_FONT_SMALL); - lv_obj_set_style_text_line_space(source, -3, ETX_STATE_NAME_FONT_SMALL); -#endif - lv_obj_add_event_cb(lvobj, OutputLineButton::on_draw, LV_EVENT_DRAW_MAIN_BEGIN, nullptr); } diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 31d72b029f1..f81884ce1f4 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -461,7 +461,6 @@ class LabelDialog : public ModalWindow auto edit = new TextEdit(box, rect_t{0, 0, LV_PCT(100), 0}, this->label, LABEL_LENGTH); - edit->padAll(PAD_MEDIUM); box = new Window(form, rect_t{}); box->padAll(PAD_MEDIUM); diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index 288d13345cf..1fd19b94716 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -263,6 +263,7 @@ class SensorButton : public ListLineButton lv_obj_t* valLabel = nullptr; lv_obj_t* fresh = nullptr; uint32_t lastRefresh = 0; + std::string valString; static void on_draw(lv_event_t* e) { @@ -270,8 +271,7 @@ class SensorButton : public ListLineButton auto line = (SensorButton*)lv_obj_get_user_data(target); if (line) { if (!line->init) - line->delayed_init(e); - line->refresh(); + line->delayed_init(); } } @@ -297,7 +297,7 @@ class SensorButton : public ListLineButton refresh(); } - void delayed_init(lv_event_t* e) + void delayed_init() { char s[20]; @@ -365,7 +365,10 @@ class SensorButton : public ListLineButton else lv_obj_clear_state(valLabel, ETX_STATE_VALUE_STALE_WARN); - lv_label_set_text(valLabel, s.c_str()); + if (valString != s) { + valString = s; + lv_label_set_text(valLabel, s.c_str()); + } } } }; diff --git a/radio/src/gui/colorlcd/model_usbjoystick.cpp b/radio/src/gui/colorlcd/model_usbjoystick.cpp index 1f000e09a77..dbbb67821d8 100644 --- a/radio/src/gui/colorlcd/model_usbjoystick.cpp +++ b/radio/src/gui/colorlcd/model_usbjoystick.cpp @@ -400,14 +400,15 @@ class USBChannelLineButton : public ListLineButton auto line = (USBChannelLineButton*)lv_obj_get_user_data(target); if (line) { if (!line->init) - line->delayed_init(e); - else - line->refresh(); + line->delayed_init(); + line->refresh(); } } - void delayed_init(lv_event_t* e) + void delayed_init() { + init = true; + m_chn = lv_label_create(lvobj); lv_obj_set_grid_cell(m_chn, LV_GRID_ALIGN_START, 0, 1, LV_GRID_ALIGN_CENTER, 0, USBCH_CHN_ROWS); @@ -442,15 +443,7 @@ class USBChannelLineButton : public ListLineButton lv_label_set_text(m_btn_mode, ""); lv_label_set_text(m_btns, ""); - 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() override diff --git a/radio/src/gui/colorlcd/preview_window.cpp b/radio/src/gui/colorlcd/preview_window.cpp index b9648566cec..3566c97573f 100644 --- a/radio/src/gui/colorlcd/preview_window.cpp +++ b/radio/src/gui/colorlcd/preview_window.cpp @@ -119,14 +119,10 @@ class ThemedTextEdit : public TextEdit public: ThemedTextEdit(Window *parent, const rect_t &rect, const char *text, bool edited) : - TextEdit(parent, rect, editText, strlen(text)) + TextEdit(parent, rect, editText, 0) { - lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICKABLE); - lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); strcpy(editText, text); - lv_obj_add_state(lvobj, LV_STATE_FOCUSED); - if (edited) lv_obj_add_state(lvobj, LV_STATE_EDITED); - update(); + preview(edited, editText, strlen(editText)); } #if defined(HARDWARE_KEYS) diff --git a/radio/src/gui/colorlcd/radio_hardware.cpp b/radio/src/gui/colorlcd/radio_hardware.cpp index 2e696e247aa..fd25dc3a485 100644 --- a/radio/src/gui/colorlcd/radio_hardware.cpp +++ b/radio/src/gui/colorlcd/radio_hardware.cpp @@ -87,9 +87,49 @@ class BatCalEdit : public NumberEdit } }; +class HWButtonGroup : public Window +{ + public: + typedef std::function PageFct; + typedef std::pair PageDef; + typedef std::list PageDefs; + +#if LCD_W > LCD_H + static constexpr coord_t COLS = 4; +#else + static constexpr coord_t COLS = 3; +#endif + static constexpr coord_t BTN_W = (LCD_W - PAD_MEDIUM * (COLS + 1) - PAD_TINY * 2) / COLS; + + HWButtonGroup(Window* parent, const rect_t& rect, const char* title, PageDefs pages) : + Window(parent, rect), title(title) + { + padAll(PAD_ZERO); + int rows = (pages.size() + COLS - 1) / COLS; + setHeight(rows * EdgeTxStyles::UI_ELEMENT_HEIGHT + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2 + EdgeTxStyles::PAGE_LINE_HEIGHT); + + new Subtitle(this, title); + + int n = 0; + for (auto& entry : pages) { + coord_t x = (n % COLS) * (BTN_W + PAD_MEDIUM) + PAD_TINY; + coord_t y = (n / COLS) * (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM) + EdgeTxStyles::PAGE_LINE_HEIGHT + 2; + + new TextButton(this, rect_t{x, y, BTN_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, entry.first, [&, entry]() { + entry.second(); + return 0; + }); + n += 1; + } + } + + protected: + std::string title; +}; + void RadioHardwarePage::build(Window* window) { - window->setFlexLayout(LV_FLEX_FLOW_COLUMN, 0); + window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY); FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY); @@ -181,44 +221,16 @@ void RadioHardwarePage::build(Window* window) new SerialConfigWindow(window, grid); // Calibration - new Subtitle(window, STR_INPUTS); - - box = new Window(window, rect_t{}); - box->setFlexLayout(LV_FLEX_FLOW_ROW_WRAP, PAD_MEDIUM); - lv_obj_set_style_flex_main_place(box->getLvObj(), LV_FLEX_ALIGN_SPACE_EVENLY, - 0); - box->padAll(PAD_MEDIUM); - - new TextButton(box, rect_t{0, 0, 100, 0}, STR_CALIBRATION, [=]() -> uint8_t { - new RadioCalibrationPage(); - return 0; + new HWButtonGroup(window, {0, 0, LCD_W - padding * 2, 0}, STR_INPUTS, { + {STR_CALIBRATION, []() { new RadioCalibrationPage(); }}, + {STR_STICKS, []() { new HWInputDialog(STR_STICKS); }}, + {STR_POTS, []() { new HWInputDialog(STR_POTS); }}, + {STR_SWITCHES, []() { new HWInputDialog(STR_SWITCHES); }}, }); - // Sticks - makeHWInputButton(box, STR_STICKS); - - // Pots & Sliders - makeHWInputButton(box, STR_POTS); - - // Switches - makeHWInputButton(box, STR_SWITCHES); - // Debugs - new Subtitle(window, STR_DEBUG); - - box = new Window(window, rect_t{}); - box->setFlexLayout(LV_FLEX_FLOW_ROW_WRAP, PAD_MEDIUM); - lv_obj_set_style_flex_main_place(box->getLvObj(), LV_FLEX_ALIGN_SPACE_EVENLY, - 0); - box->padAll(PAD_MEDIUM); - - new TextButton(box, rect_t{0, 0, 100, 0}, STR_ANALOGS_BTN, [=]() -> uint8_t { - new RadioAnalogsDiagsViewPageGroup(); - return 0; - }); - - new TextButton(box, rect_t{0, 0, 100, 0}, STR_KEYS_BTN, [=]() -> uint8_t { - new RadioKeyDiagsPage(); - return 0; + new HWButtonGroup(window, {0, 0, LCD_W - padding * 2, 0}, STR_DEBUG, { + {STR_ANALOGS_BTN, []() { new RadioAnalogsDiagsViewPageGroup(); }}, + {STR_KEYS_BTN, []() { new RadioKeyDiagsPage(); }}, }); } diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index a5d17d5d9b0..d040f01cf43 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -86,20 +86,20 @@ class FileNameEditWindow : public Page auto newFileName = new TextEdit( window, rect_t{0, 0, LV_PCT(100), 0}, reusableBuffer.sdManager.originalName, - SD_SCREEN_FILE_LENGTH - extLength); - newFileName->setChangeHandler([=]() { - char *newValue = reusableBuffer.sdManager.originalName; - size_t totalSize = strlen(newValue); - char changedName[SD_SCREEN_FILE_LENGTH + 1]; - memset(changedName, 0, sizeof(changedName)); - strncpy(changedName, newValue, totalSize); - changedName[totalSize] = '\0'; - if (extLength) { - strncpy(changedName + totalSize, extension, extLength); - } - changedName[totalSize + extLength] = '\0'; - f_rename((const TCHAR *)name.c_str(), (const TCHAR *)changedName); - }); + SD_SCREEN_FILE_LENGTH - extLength, + [=]() { + char *newValue = reusableBuffer.sdManager.originalName; + size_t totalSize = strlen(newValue); + char changedName[SD_SCREEN_FILE_LENGTH + 1]; + memset(changedName, 0, sizeof(changedName)); + strncpy(changedName, newValue, totalSize); + changedName[totalSize] = '\0'; + if (extLength) { + strncpy(changedName + totalSize, extension, extLength); + } + changedName[totalSize + extLength] = '\0'; + f_rename((const TCHAR *)name.c_str(), (const TCHAR *)changedName); + }); }; }; diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index 6dde93faf21..76ffdc77e19 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -42,8 +42,9 @@ class DateTimeWindow : public Window { public: DateTimeWindow(Window* parent, const rect_t& rect) : - Window(parent, {0, 0, LCD_W - 12, 74}) + Window(parent, rect) { + padAll(PAD_ZERO); build(); } @@ -55,40 +56,30 @@ class DateTimeWindow : public Window lastRefresh = get_tmr10ms(); gettime(&m_tm); - if (m_tm.tm_year != m_last_tm.tm_year) { - m_last_tm.tm_year = m_tm.tm_year; + if (m_tm.tm_year != m_last_tm.tm_year) year->update(); - } - if (m_tm.tm_mon != m_last_tm.tm_mon) { - m_last_tm.tm_mon = m_tm.tm_mon; + if (m_tm.tm_mon != m_last_tm.tm_mon) month->update(); - } - if (m_tm.tm_mday != m_last_tm.tm_mday) { - m_last_tm.tm_mday = m_tm.tm_mday; + if (m_tm.tm_mday != m_last_tm.tm_mday) day->update(); - } - if (m_tm.tm_hour != m_last_tm.tm_hour) { - m_last_tm.tm_hour = m_tm.tm_hour; + if (m_tm.tm_hour != m_last_tm.tm_hour) hour->update(); - } - if (m_tm.tm_min != m_last_tm.tm_min) { - m_last_tm.tm_min = m_tm.tm_min; + if (m_tm.tm_min != m_last_tm.tm_min) minutes->update(); - } - if (m_tm.tm_sec != m_last_tm.tm_sec) { - m_last_tm.tm_sec = m_tm.tm_sec; + if (m_tm.tm_sec != m_last_tm.tm_sec) seconds->update(); - } + m_last_tm = m_tm; } } // Absolute layout for date/time setion due to slow performance // of lv_textarea in a flex layout. static LAYOUT_VAL(DT_EDT_W, 80, 52) - static LAYOUT_VAL(DT_EDT_X, 220, 144) - static LAYOUT_VAL(DT_LBL_W, 200, 140) + static LAYOUT_VAL(DT_Y1, PAD_TINY, PAD_TINY) + static LAYOUT_VAL(DT_Y2, DT_Y1 + EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM, DT_Y1 + EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM) protected: + bool init = false; struct gtm m_tm; struct gtm m_last_tm; tmr10ms_t lastRefresh = 0; @@ -130,9 +121,9 @@ class DateTimeWindow : public Window m_last_tm = m_tm; // Date - new StaticText(this, rect_t{2, 8, DT_LBL_W, 21}, STR_DATE); + new StaticText(this, rect_t{PAD_TINY, DT_Y1 + PAD_MEDIUM, RadioSetupPage::LBL_W, EdgeTxStyles::PAGE_LINE_HEIGHT}, STR_DATE); year = new NumberEdit( - this, rect_t{DT_EDT_X, 2, DT_EDT_W, 32}, 2023, 2037, + this, rect_t{RadioSetupPage::EDT_X, DT_Y1, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 2023, 2037, [=]() -> int32_t { return TM_YEAR_BASE + m_tm.tm_year; }, [=](int32_t newValue) { m_last_tm.tm_year = m_tm.tm_year = newValue - TM_YEAR_BASE; @@ -141,7 +132,7 @@ class DateTimeWindow : public Window }); month = new NumberEdit( - this, rect_t{DT_EDT_X + DT_EDT_W + 2, 2, DT_EDT_W, 32}, 1, 12, + this, rect_t{RadioSetupPage::EDT_X + DT_EDT_W + PAD_TINY, DT_Y1, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 1, 12, [=]() -> int32_t { return 1 + m_tm.tm_mon; }, [=](int32_t newValue) { m_last_tm.tm_mon = m_tm.tm_mon = newValue - 1; @@ -152,7 +143,7 @@ class DateTimeWindow : public Window [](int32_t value) { return formatNumberAsString(value, LEADING0); }); day = new NumberEdit( - this, rect_t{DT_EDT_X + 2 * DT_EDT_W + 4, 2, DT_EDT_W, 32}, 1, + this, rect_t{RadioSetupPage::EDT_X + 2 * DT_EDT_W + PAD_SMALL, DT_Y1, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 1, daysInMonth(), [=]() -> int32_t { return m_tm.tm_mday; }, [=](int32_t newValue) { m_last_tm.tm_mday = m_tm.tm_mday = newValue; @@ -162,9 +153,9 @@ class DateTimeWindow : public Window [](int32_t value) { return formatNumberAsString(value, LEADING0, 2); }); // Time - new StaticText(this, rect_t{2, 46, DT_LBL_W, 21}, STR_TIME); + new StaticText(this, rect_t{PAD_TINY, DT_Y2 + PAD_MEDIUM, RadioSetupPage::LBL_W, EdgeTxStyles::PAGE_LINE_HEIGHT}, STR_TIME); hour = new NumberEdit( - this, rect_t{DT_EDT_X, 40, DT_EDT_W, 32}, 0, 23, + this, rect_t{RadioSetupPage::EDT_X, DT_Y2, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 23, [=]() -> int32_t { return m_tm.tm_hour; }, [=](int32_t newValue) { m_last_tm.tm_hour = m_tm.tm_hour = newValue; @@ -174,7 +165,7 @@ class DateTimeWindow : public Window [](int32_t value) { return formatNumberAsString(value, LEADING0, 2); }); minutes = new NumberEdit( - this, rect_t{DT_EDT_X + DT_EDT_W + 2, 40, DT_EDT_W, 32}, 0, 59, + this, rect_t{RadioSetupPage::EDT_X + DT_EDT_W + PAD_TINY, DT_Y2, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 59, [=]() -> int32_t { return m_tm.tm_min; }, [=](int32_t newValue) { m_last_tm.tm_min = m_tm.tm_min = newValue; @@ -184,7 +175,7 @@ class DateTimeWindow : public Window [](int32_t value) { return formatNumberAsString(value, LEADING0, 2); }); seconds = new NumberEdit( - this, rect_t{DT_EDT_X + DT_EDT_W * 2 + 4, 40, DT_EDT_W, 32}, 0, 59, + this, rect_t{RadioSetupPage::EDT_X + DT_EDT_W * 2 + PAD_SMALL, DT_Y2, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 59, [=]() -> int32_t { return m_tm.tm_sec; }, [=](int32_t newValue) { m_last_tm.tm_sec = m_tm.tm_sec = newValue; @@ -202,27 +193,44 @@ class WindowButtonGroup : public Window typedef std::pair PageDef; typedef std::list PageDefs; +#if LCD_W > LCD_H + static constexpr coord_t COLS = 3; +#else + static constexpr coord_t COLS = 2; +#endif + static constexpr coord_t BTN_W = (LCD_W - PAD_MEDIUM * (COLS + 1) - PAD_TINY * 2) / COLS; + WindowButtonGroup(Window* parent, const rect_t& rect, PageDefs pages) : - Window(parent, rect), pages(pages) + Window(parent, rect) { - padTop(PAD_TINY); - padBottom(PAD_SMALL); - setFlexLayout(LV_FLEX_FLOW_ROW_WRAP, PAD_LARGE); - lv_obj_set_style_flex_main_place(lvobj, LV_FLEX_ALIGN_SPACE_EVENLY, 0); - padRow(PAD_MEDIUM); - padBottom(PAD_SMALL); + padAll(PAD_TINY); + + int rows = (pages.size() + COLS - 1) / COLS; + setHeight(rows * EdgeTxStyles::UI_ELEMENT_HEIGHT + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2); + int n = 0; + int remaining = pages.size(); + coord_t xo = 0; for (auto& entry : pages) { - auto btn = new TextButton(this, rect_t{}, entry.first, [&, entry]() { + if (remaining < COLS && (n % COLS == 0)) + xo = ((COLS - remaining) * (BTN_W + PAD_MEDIUM)) / 2; + coord_t x = xo + (n % COLS) * (BTN_W + PAD_MEDIUM); + coord_t y = (n / COLS) * (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM); + + // TODO: sort out all caps title strings VS quick menu strings + std::string title(entry.first); + std::replace(title.begin(), title.end(), '\n', ' '); + + new TextButton(this, rect_t{x, y, BTN_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, title, [&, entry]() { entry.second(); return 0; }); - lv_obj_set_style_min_width(btn->getLvObj(), LV_DPI_DEF, 0); + n += 1; + remaining -= 1; } } protected: - PageDefs pages; }; class SubPage : public Page @@ -768,108 +776,153 @@ class ManageModelsSetupPage : public SubPage Window* favSelectMatch = nullptr; }; +class SetupLine : public Window +{ + public: + SetupLine(Window* parent, const rect_t& rect, const char* title, std::function createEdit) : + Window(parent, rect) + { + padAll(PAD_ZERO); + coord_t titleY = PAD_MEDIUM + 1; + coord_t titleH = EdgeTxStyles::PAGE_LINE_HEIGHT; + coord_t editY = PAD_TINY; + if (getTextWidth(title) >= RadioSetupPage::LBL_W) { + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2 + PAD_MEDIUM); + titleY = 0; + titleH = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY; + editY = PAD_SMALL + 1; + } else { + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); + } + new StaticText(this, {PAD_TINY, titleY, RadioSetupPage::LBL_W, titleH}, title); + createEdit(this, RadioSetupPage::EDT_X, editY); + } + + protected: +}; + RadioSetupPage::RadioSetupPage() : PageTab(STR_RADIO_SETUP, ICON_RADIO_SETUP) {} void RadioSetupPage::build(Window* window) { - FlexGridLayout grid(col_two_dsc, row_dsc, PAD_TINY); - window->setFlexLayout(); + coord_t y = 0; + Window * w; // Date & time picker including labels - new DateTimeWindow(window, rect_t{}); + w = new DateTimeWindow(window, {0, y, LCD_W - padding * 2, EdgeTxStyles::UI_ELEMENT_HEIGHT * 2 + PAD_TINY * 2 + PAD_MEDIUM}); - // TODO: sort out all caps title strings VS quick menu strings - std::string manageModelsTitle(STR_MAIN_MENU_MANAGE_MODELS); - std::replace(manageModelsTitle.begin(), manageModelsTitle.end(), '\n', ' '); + y += w->height() + padding; // Sub-pages - new WindowButtonGroup(window, rect_t{}, { + w = new WindowButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, { {STR_SOUND_LABEL, []() { new SoundPage(); }}, #if defined(VARIO) - {STR_VARIO, []() { new VarioPage(); }}, + {STR_VARIO, []() { new VarioPage(); }}, #endif #if defined(HAPTIC) - {STR_HAPTIC_LABEL, []() { new HapticPage(); }}, + {STR_HAPTIC_LABEL, []() { new HapticPage(); }}, #endif - {STR_ALARMS_LABEL, []() { new AlarmsPage(); }}, - {STR_BACKLIGHT_LABEL, []() { new BacklightPage(); }}, - {STR_GPS, []() { new GpsPage(); }}, - {STR_ENABLED_FEATURES, []() { new ViewOptionsPage(); }}, - {manageModelsTitle.c_str(), []() { new ManageModelsSetupPage(); }}, + {STR_ALARMS_LABEL, []() { new AlarmsPage(); }}, + {STR_BACKLIGHT_LABEL, []() { new BacklightPage(); }}, + {STR_GPS, []() { new GpsPage(); }}, + {STR_ENABLED_FEATURES, []() { new ViewOptionsPage(); }}, + {STR_MAIN_MENU_MANAGE_MODELS, []() { new ManageModelsSetupPage(); }}, }); + y += w->height() + padding; + // Splash screen - auto line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_SPLASHSCREEN); - new Choice( - line, rect_t{}, STR_SPLASHSCREEN_DELAYS, 0, 7, - [=]() -> int32_t { return 3 - g_eeGeneral.splashMode; }, - [=](int32_t newValue) { - g_eeGeneral.splashMode = 3 - newValue; - SET_DIRTY(); + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_SPLASHSCREEN, + [=](Window* parent, coord_t x, coord_t y) { + new Choice( + parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_SPLASHSCREEN_DELAYS, 0, 7, + [=]() -> int32_t { return 3 - g_eeGeneral.splashMode; }, + [=](int32_t newValue) { + g_eeGeneral.splashMode = 3 - newValue; + SET_DIRTY(); + }); }); + y += w->height() + padding; + + // PPM units + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_UNITS_PPM, + [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_PPMUNIT, PPM_PERCENT_PREC0, PPM_PERCENT_PREC1, + GET_SET_DEFAULT(g_eeGeneral.ppmunit)); + }); + + y += w->height() + padding; + // Play startup sound - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_PLAY_HELLO); - new ToggleSwitch(line, rect_t{}, GET_SET_INVERTED(g_eeGeneral.dontPlayHello)); + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_PLAY_HELLO, + [=](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, GET_SET_INVERTED(g_eeGeneral.dontPlayHello)); + }); + + y += w->height() + padding; #if defined(PWR_BUTTON_PRESS) // Pwr Off Delay - { - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_PWR_OFF_DELAY); - new Choice( - line, rect_t{}, STR_PWR_OFF_DELAYS, 0, 3, - [=]() -> int32_t { return 2 - g_eeGeneral.pwrOffSpeed; }, - [=](int32_t newValue) { - g_eeGeneral.pwrOffSpeed = 2 - newValue; - SET_DIRTY(); - }); - } + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_PWR_OFF_DELAY, + [=](Window* parent, coord_t x, coord_t y) { + new Choice( + parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_PWR_OFF_DELAYS, 0, 3, + [=]() -> int32_t { return 2 - g_eeGeneral.pwrOffSpeed; }, + [=](int32_t newValue) { + g_eeGeneral.pwrOffSpeed = 2 - newValue; + SET_DIRTY(); + }); + }); + + y += w->height() + padding; #endif #if defined(PXX2) // Owner ID - { - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_OWNER_ID); - new RadioTextEdit(line, rect_t{}, g_eeGeneral.ownerRegistrationID, - PXX2_LEN_REGISTRATION_ID); - } + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_OWNER_ID, + [=](Window* parent, coord_t x, coord_t y) { + new RadioTextEdit(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, g_eeGeneral.ownerRegistrationID, + PXX2_LEN_REGISTRATION_ID); + }); + + y += w->height() + padding; #endif // Country code - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_COUNTRY_CODE); - new Choice(line, rect_t{}, STR_COUNTRY_CODES, 0, 2, - GET_SET_DEFAULT(g_eeGeneral.countryCode)); + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_COUNTRY_CODE, + [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_COUNTRY_CODES, 0, 2, + GET_SET_DEFAULT(g_eeGeneral.countryCode)); + }); + + y += w->height() + padding; // Audio language - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_VOICE_LANGUAGE); - auto choice = - new Choice(line, rect_t{}, 0, DIM(languagePacks) - 2, - GET_VALUE(currentLanguagePackIdx), [](uint8_t newValue) { - currentLanguagePackIdx = newValue; - currentLanguagePack = languagePacks[currentLanguagePackIdx]; - strncpy(g_eeGeneral.ttsLanguage, currentLanguagePack->id, 2); - SET_DIRTY(); - }); - choice->setTextHandler( - [](uint8_t value) { return languagePacks[value]->name; }); + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_VOICE_LANGUAGE, + [=](Window* parent, coord_t x, coord_t y) { + auto choice = + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, DIM(languagePacks) - 2, + GET_VALUE(currentLanguagePackIdx), [](uint8_t newValue) { + currentLanguagePackIdx = newValue; + currentLanguagePack = languagePacks[currentLanguagePackIdx]; + strncpy(g_eeGeneral.ttsLanguage, currentLanguagePack->id, 2); + SET_DIRTY(); + }); + choice->setTextHandler( + [](uint8_t value) { return languagePacks[value]->name; }); + }); + + y += w->height() + padding; // Imperial units - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_UNITS_SYSTEM); - new Choice(line, rect_t{}, STR_VUNITSSYSTEM, 0, 1, - GET_SET_DEFAULT(g_eeGeneral.imperial)); + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_UNITS_SYSTEM, + [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_VUNITSSYSTEM, 0, 1, + GET_SET_DEFAULT(g_eeGeneral.imperial)); + }); - // PPM units - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_UNITS_PPM); - new Choice(line, rect_t{}, STR_PPMUNIT, PPM_PERCENT_PREC0, PPM_PERCENT_PREC1, - GET_SET_DEFAULT(g_eeGeneral.ppmunit)); + y += w->height() + padding; #if defined(FAI_CHOICE) /* case ITEM_SETUP_FAI: @@ -879,7 +932,7 @@ void RadioSetupPage::build(Window* window) } else { g_eeGeneral.fai = editCheckBox(g_eeGeneral.fai, RADIO_SETUP_2ND_COLUMN, y, - attr, event); if (attr && checkIncDec_Ret) { g_eeGeneral.fai = false; + attr, event); if (attr && checkIncDec_Ret) { g_eeGeneral.fai = false; POPUP_CONFIRMATION("FAI mode?"); } } @@ -887,77 +940,89 @@ void RadioSetupPage::build(Window* window) #endif // Switches delay - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_SWITCHES_DELAY); - auto edit = - new NumberEdit(line, rect_t{0, 0, 80, 32}, 0, 100, - GET_SET_VALUE_WITH_OFFSET(g_eeGeneral.switchesDelay, 15)); - edit->setDisplayHandler([](int32_t value) { - return formatNumberAsString(value * 10, 0, 0, nullptr, STR_MS); - }); + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_SWITCHES_DELAY, + [=](Window* parent, coord_t x, coord_t y) { + auto edit = + new NumberEdit(parent, {x, y, NUM_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 100, + GET_SET_VALUE_WITH_OFFSET(g_eeGeneral.switchesDelay, 15)); + edit->setDisplayHandler([](int32_t value) { + return formatNumberAsString(value * 10, 0, 0, nullptr, STR_MS); + }); + }); + + y += w->height() + padding; // USB mode - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_USBMODE); - new Choice(line, rect_t{}, STR_USBMODES, USB_UNSELECTED_MODE, USB_MAX_MODE, - GET_SET_DEFAULT(g_eeGeneral.USBMode)); + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_USBMODE, + [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_USBMODES, USB_UNSELECTED_MODE, USB_MAX_MODE, + GET_SET_DEFAULT(g_eeGeneral.USBMode)); + }); + + y += w->height() + padding; #if defined(ROTARY_ENCODER_NAVIGATION) && !defined(USE_HATS_AS_KEYS) - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_ROTARY_ENC_MODE); - new Choice(line, rect_t{}, STR_ROTARY_ENC_OPT, ROTARY_ENCODER_MODE_NORMAL, - ROTARY_ENCODER_MODE_INVERT_BOTH, - GET_SET_DEFAULT(g_eeGeneral.rotEncMode)); + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_ROTARY_ENC_MODE, + [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_ROTARY_ENC_OPT, ROTARY_ENCODER_MODE_NORMAL, + ROTARY_ENCODER_MODE_INVERT_BOTH, + GET_SET_DEFAULT(g_eeGeneral.rotEncMode)); + }); + + y += w->height() + padding; #endif #if defined(USE_HATS_AS_KEYS) - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_HATSMODE); - auto box = new Window(line, rect_t{}); - box->padAll(PAD_TINY); - box->setFlexLayout(LV_FLEX_FLOW_ROW, PAD_TINY); - new Choice(box, rect_t{}, STR_HATSOPT, HATSMODE_TRIMS_ONLY, - HATSMODE_SWITCHABLE, GET_SET_DEFAULT(g_eeGeneral.hatsMode)); - new TextButton(box, rect_t{}, "?", [=]() { - new MessageDialog(window, STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", - LEFT); - return 0; - }); + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_HATSMODE, + [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 120, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_HATSOPT, HATSMODE_TRIMS_ONLY, + HATSMODE_SWITCHABLE, GET_SET_DEFAULT(g_eeGeneral.hatsMode)); + new TextButton(parent, {x + 120 + PAD_MEDIUM, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, "?", [=]() { + new MessageDialog(parent, STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", + LEFT); + return 0; + }); + }); + + y += w->height() + padding; #endif // RX channel order - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_DEF_CHAN_ORD); // RAET->AETR - - uint8_t mains = adcGetMaxInputs(ADC_INPUT_MAIN); - auto max_order = inputMappingGetMaxChannelOrder() - 1; - choice = new Choice(line, rect_t{}, 0, max_order, - GET_SET_DEFAULT(g_eeGeneral.templateSetup)); - - choice->setTextHandler([=](uint8_t value) { - std::string s; - for (uint8_t i = 0; i < mains; i++) { - s += getAnalogShortLabel(inputMappingChannelOrder(value, i)); - } - return s; - }); + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_DEF_CHAN_ORD, + [=](Window* parent, coord_t x, coord_t y) { + uint8_t mains = adcGetMaxInputs(ADC_INPUT_MAIN); + auto max_order = inputMappingGetMaxChannelOrder() - 1; + auto choice = new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, max_order, + GET_SET_DEFAULT(g_eeGeneral.templateSetup)); + + choice->setTextHandler([=](uint8_t value) { + std::string s; + for (uint8_t i = 0; i < mains; i++) { + s += getAnalogShortLabel(inputMappingChannelOrder(value, i)); + } + return s; + }); + }); + + y += w->height() + padding; // Stick mode - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_MODE); - choice = new Choice(line, rect_t{}, 0, 3, GET_DEFAULT(g_eeGeneral.stickMode), - [=](uint8_t newValue) { - mixerTaskStop(); - g_eeGeneral.stickMode = newValue; - SET_DIRTY(); - checkThrottleStick(); - mixerTaskStart(); - }); - choice->setTextHandler([](uint8_t value) { - auto stick0 = inputMappingConvertMode(value, 0); - auto stick1 = inputMappingConvertMode(value, 1); - return std::to_string(1 + value) + ": " + STR_LEFT_STICK + " = " + - std::string(getMainControlLabel(stick0)) + "+" + - std::string(getMainControlLabel(stick1)); - }); + new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_MODE, + [=](Window* parent, coord_t x, coord_t y) { + auto choice = new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 3, GET_DEFAULT(g_eeGeneral.stickMode), + [=](uint8_t newValue) { + mixerTaskStop(); + g_eeGeneral.stickMode = newValue; + SET_DIRTY(); + checkThrottleStick(); + mixerTaskStart(); + }); + choice->setTextHandler([](uint8_t value) { + auto stick0 = inputMappingConvertMode(value, 0); + auto stick1 = inputMappingConvertMode(value, 1); + return std::to_string(1 + value) + ": " + STR_LEFT_STICK + " = " + + std::string(getMainControlLabel(stick0)) + "+" + + std::string(getMainControlLabel(stick1)); + }); + }); } diff --git a/radio/src/gui/colorlcd/radio_setup.h b/radio/src/gui/colorlcd/radio_setup.h index 1c267e89c7b..13fa9e8dd52 100644 --- a/radio/src/gui/colorlcd/radio_setup.h +++ b/radio/src/gui/colorlcd/radio_setup.h @@ -24,8 +24,12 @@ #include "tabsgroup.h" class RadioSetupPage: public PageTab { - public: - RadioSetupPage(); + public: + RadioSetupPage(); - void build(Window * window) override; + void build(Window * window) override; + + static LAYOUT_VAL(NUM_W, 80, 80) + static LAYOUT_VAL(EDT_X, 220, 144) + static constexpr coord_t LBL_W = EDT_X - PAD_TINY - PAD_SMALL; }; diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index 8f68c706ecb..54c9e0cd856 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -294,10 +294,10 @@ void FunctionEditPage::on_draw(lv_event_t *e) { lv_obj_t *target = lv_event_get_target(e); auto page = (FunctionEditPage *)lv_obj_get_user_data(target); - if (page) page->delayed_init(e); + if (page) page->delayed_init(); } -void FunctionEditPage::delayed_init(lv_event_t *e) +void FunctionEditPage::delayed_init() { if (!init) { init = true; diff --git a/radio/src/gui/colorlcd/special_functions.h b/radio/src/gui/colorlcd/special_functions.h index 36b28925999..7bdc7a5c206 100644 --- a/radio/src/gui/colorlcd/special_functions.h +++ b/radio/src/gui/colorlcd/special_functions.h @@ -94,7 +94,7 @@ class FunctionEditPage : public Page static void on_draw(lv_event_t *e); - void delayed_init(lv_event_t *e); + void delayed_init(); protected: bool init = false; diff --git a/radio/src/gui/colorlcd/tabsgroup.cpp b/radio/src/gui/colorlcd/tabsgroup.cpp index 83735c567d4..bf5bf800f08 100644 --- a/radio/src/gui/colorlcd/tabsgroup.cpp +++ b/radio/src/gui/colorlcd/tabsgroup.cpp @@ -334,6 +334,8 @@ void TabsGroup::setVisibleTab(PageTab* tab) header->setTitle(tab->title.c_str()); body->clear(); + if (currentTab) + currentTab->cleanup(); currentTab = tab; #if defined(DEBUG) diff --git a/radio/src/gui/colorlcd/tabsgroup.h b/radio/src/gui/colorlcd/tabsgroup.h index 96ffa60a4c4..49543f48f27 100644 --- a/radio/src/gui/colorlcd/tabsgroup.h +++ b/radio/src/gui/colorlcd/tabsgroup.h @@ -54,6 +54,7 @@ class PageTab EdgeTxIcon getIcon() const { return icon; } virtual void update(uint8_t index) {} + virtual void cleanup() {} protected: std::string title; diff --git a/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp b/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp index 4790e17b47f..edf7720f5bb 100644 --- a/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp +++ b/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp @@ -511,9 +511,21 @@ void etx_scrollbar(lv_obj_t* obj) lv_obj_set_scrollbar_mode(obj, LV_SCROLLBAR_MODE_AUTO); } -void etx_textarea_style(lv_obj_t* obj) +// Object creators + +lv_obj_t* etx_create(const lv_obj_class_t* class_p, lv_obj_t* parent) { - etx_std_settings(obj, LV_PART_MAIN); + lv_obj_t* obj = lv_obj_class_create_obj(class_p, parent); + lv_obj_class_init_obj(obj); + + return obj; +} + +static void textarea_constructor(const lv_obj_class_t* class_p, lv_obj_t* obj) +{ + etx_obj_add_style(obj, styles->border, LV_PART_MAIN); + etx_obj_add_style(obj, styles->border_color_normal, LV_PART_MAIN); + etx_obj_add_style(obj, styles->rounded, LV_PART_MAIN); etx_std_ctrl_colors(obj, LV_PART_MAIN); etx_obj_add_style(obj, styles->pad_textarea, LV_PART_MAIN); @@ -532,12 +544,20 @@ void etx_textarea_style(lv_obj_t* obj) lv_obj_set_height(ta->label, 21); } -// Object creators +static const lv_obj_class_t textarea_class = { + .base_class = &lv_textarea_class, + .constructor_cb = textarea_constructor, + .destructor_cb = nullptr, + .user_data = nullptr, + .event_cb = nullptr, + .width_def = 0, + .height_def = EdgeTxStyles::UI_ELEMENT_HEIGHT, + .editable = LV_OBJ_CLASS_EDITABLE_INHERIT, + .group_def = LV_OBJ_CLASS_GROUP_DEF_INHERIT, + .instance_size = sizeof(lv_textarea_t), +}; -lv_obj_t* etx_create(const lv_obj_class_t* class_p, lv_obj_t* parent) +lv_obj_t* etx_textarea_create(lv_obj_t* parent) { - lv_obj_t* obj = lv_obj_class_create_obj(class_p, parent); - lv_obj_class_init_obj(obj); - - return obj; + return etx_create(&textarea_class, parent); } diff --git a/radio/src/gui/colorlcd/themes/etx_lv_theme.h b/radio/src/gui/colorlcd/themes/etx_lv_theme.h index cf93d2cdd01..519725c5daa 100644 --- a/radio/src/gui/colorlcd/themes/etx_lv_theme.h +++ b/radio/src/gui/colorlcd/themes/etx_lv_theme.h @@ -72,6 +72,7 @@ void usePreviewStyle(); void useMainStyle(); lv_obj_t* etx_create(const lv_obj_class_t* class_p, lv_obj_t* parent); +lv_obj_t* etx_textarea_create(lv_obj_t* parent); lv_obj_t* window_create(lv_obj_t* parent); void etx_std_style(lv_obj_t* obj, lv_style_selector_t selector = LV_PART_MAIN, @@ -103,8 +104,6 @@ void etx_txt_color(lv_obj_t* obj, LcdColorIndex colorIdx, void etx_img_color(lv_obj_t* obj, LcdColorIndex colorIdx, lv_style_selector_t selector = LV_PART_MAIN); -void etx_textarea_style(lv_obj_t* obj); - // Create a style with a single property #define LV_STYLE_CONST_SINGLE_INIT(var_name, prop, value) \ const lv_style_t var_name = {.v_p = {.value1 = {.num = value}}, \ diff --git a/radio/src/lv_conf.h b/radio/src/lv_conf.h index 4c793159492..56186a87c3d 100644 --- a/radio/src/lv_conf.h +++ b/radio/src/lv_conf.h @@ -452,7 +452,11 @@ #define LV_FONT_FMT_TXT_LARGE 0 /*Enables/disables support for compressed fonts.*/ +#if defined(BOOT) #define LV_USE_FONT_COMPRESSED 1 +#else +#define LV_USE_FONT_COMPRESSED 0 +#endif /*Enable subpixel rendering*/ #define LV_USE_FONT_SUBPX 0 diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 3bf3b971121..e6791bfea72 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -39,12 +39,6 @@ void FlexGridLayout::add(Window* w) } } -FormField::FormField(const rect_t& rect, LcdFlags textFlags) : - Window(rect) -{ - setTextFlag(textFlags); -} - FormField::FormField(Window* parent, const rect_t& rect, LcdFlags textFlags, LvglCreate objConstruct) : Window(parent, rect, objConstruct) @@ -53,12 +47,6 @@ FormField::FormField(Window* parent, const rect_t& rect, LcdFlags textFlags, lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS); } -void FormField::setupLVGL() -{ - Window::setupLVGL(); - lv_obj_add_flag(lvobj, LV_OBJ_FLAG_SCROLL_ON_FOCUS); -} - void FormField::setEditMode(bool newEditMode) { editMode = newEditMode; diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 6134265e9f6..850547e4aad 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -77,12 +77,9 @@ class FlexGridLayout class FormField : public Window { public: - FormField(const rect_t& rect, LcdFlags textFlags); FormField(Window* parent, const rect_t& rect, LcdFlags textFlags = 0, LvglCreate objConstruct = nullptr); - void setupLVGL() override; - virtual void changeEnd(bool forceChanged = false) { if (changeHandler) { diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp index 868d53a5925..f8fa1d9daf9 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.cpp +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.cpp @@ -155,7 +155,7 @@ NumberKeyboard::NumberKeyboard() : Keyboard(KEYBOARD_HEIGHT) NumberKeyboard::~NumberKeyboard() { _instance = nullptr; } -void NumberKeyboard::show(NumberEdit* field) +void NumberKeyboard::show(FormField* field) { if (!_instance) _instance = new NumberKeyboard(); diff --git a/radio/src/thirdparty/libopenui/src/keyboard_number.h b/radio/src/thirdparty/libopenui/src/keyboard_number.h index 7feeae575e2..c06ad23db5e 100644 --- a/radio/src/thirdparty/libopenui/src/keyboard_number.h +++ b/radio/src/thirdparty/libopenui/src/keyboard_number.h @@ -20,8 +20,6 @@ #include "keyboard_base.h" -class NumberEdit; - class NumberKeyboard : public Keyboard { public: @@ -32,7 +30,7 @@ class NumberKeyboard : public Keyboard std::string getName() const override { return "NumberKeyboard"; } #endif - static void show(NumberEdit* field); + static void show(FormField* field); void handleEvent(const char* btn); diff --git a/radio/src/thirdparty/libopenui/src/numberedit.cpp b/radio/src/thirdparty/libopenui/src/numberedit.cpp index 0e6b3b17cbc..bd779a47ab1 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.cpp +++ b/radio/src/thirdparty/libopenui/src/numberedit.cpp @@ -23,171 +23,350 @@ #include "strhelpers.h" #include "themes/etx_lv_theme.h" -static void numberedit_cb(lv_event_t* e) +class NumberArea : public FormField { - NumberEdit* numEdit = (NumberEdit*)lv_event_get_user_data(e); - if (!numEdit || numEdit->deleted()) return; - - uint32_t key = lv_event_get_key(e); - switch (key) { - case LV_KEY_LEFT: - numEdit->onEvent(EVT_ROTARY_LEFT); - break; - case LV_KEY_RIGHT: - numEdit->onEvent(EVT_ROTARY_RIGHT); - break; - } -} - -NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, - std::function getValue, - std::function setValue, LcdFlags textFlags) : - FormField(rect, textFlags), - vmin(vmin), - vmax(vmax), - _getValue(std::move(getValue)), - _setValue(std::move(setValue)) -{ - lv_obj_enable_style_refresh(false); - - // Workaround for performance issues with lv_textarea - create on top layer - // not this window then reparent to this window after setup finished - this->parent = parent; - lvobj = lv_textarea_create(lv_layer_top()); - - // Do this first - before any styles are applied, otherwise it is very slow - update(); - - etx_textarea_style(lvobj); - setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT); + public: + NumberArea(Window* parent, const rect_t& rect, int vmin, int vmax, + std::function getValue, + std::function setValue = nullptr, + LcdFlags textFlags = 0) : + FormField(parent, rect, textFlags, etx_textarea_create), + vmin(vmin), + vmax(vmax), + _getValue(std::move(getValue)), + _setValue(std::move(setValue)) + { + if (rect.w == 0) setWidth(DEF_W); - etx_obj_add_style(lvobj, styles->text_align_right, LV_PART_MAIN); + etx_obj_add_style(lvobj, styles->text_align_right, LV_PART_MAIN); - // Allow encoder acceleration - lv_obj_add_flag(lvobj, LV_OBJ_FLAG_ENCODER_ACCEL); + // Allow encoder acceleration + lv_obj_add_flag(lvobj, LV_OBJ_FLAG_ENCODER_ACCEL); - lv_obj_add_event_cb(lvobj, numberedit_cb, LV_EVENT_KEY, this); + lv_obj_add_event_cb(lvobj, NumberArea::numberedit_cb, LV_EVENT_KEY, this); - lv_obj_set_parent(lvobj, parent->getLvObj()); - setupLVGL(); - - if (rect.w == 0) setWidth(DEF_W); + update(); + } - lv_obj_enable_style_refresh(true); - lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); -} +#if defined(DEBUG_WINDOWS) + std::string getName() const override + { + return "NumberArea(" + std::to_string(getValue()) + ")"; + } +#endif -void NumberEdit::onEvent(event_t event) -{ - TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), - event); + void onEvent(event_t event) override + { + TRACE_WINDOWS("%s received event 0x%X", getWindowDebugString().c_str(), + event); - if (editMode) { - switch (event) { + if (editMode) { + switch (event) { #if defined(HARDWARE_KEYS) - case EVT_ROTARY_RIGHT: { - int value = getValue(); - auto step = getStep(); - step += (rotaryEncoderGetAccel() * getAccelFactor()) / 8; - do { + case EVT_ROTARY_RIGHT: { + int value = getValue(); + auto step = vstep; + step += (rotaryEncoderGetAccel() * accelFactor) / 8; + do { #if defined(USE_HATS_AS_KEYS) - value -= step; + value -= step; #else - value += step; + value += step; #endif - } while (isValueAvailable && !isValueAvailable(value) && value <= vmax); - if (value <= vmax) { - setValue(value); - } else { - setValue(vmax); - onKeyError(); + } while (isValueAvailable && !isValueAvailable(value) && + value <= vmax); + if (value <= vmax) { + setValue(value); + } else { + setValue(vmax); + onKeyError(); + } + return; } - return; - } - case EVT_ROTARY_LEFT: { - int value = getValue(); - auto step = getStep(); - step += (rotaryEncoderGetAccel() * getAccelFactor()) / 8; - do { + case EVT_ROTARY_LEFT: { + int value = getValue(); + auto step = vstep; + step += (rotaryEncoderGetAccel() * accelFactor) / 8; + do { #if defined(USE_HATS_AS_KEYS) - value += step; + value += step; #else - value -= step; + value -= step; #endif - } while (isValueAvailable && !isValueAvailable(value) && value >= vmin); - if (value >= vmin) { - setValue(value); - } else { - setValue(vmin); - onKeyError(); + } while (isValueAvailable && !isValueAvailable(value) && + value >= vmin); + if (value >= vmin) { + setValue(value); + } else { + setValue(vmin); + onKeyError(); + } + return; } - return; - } #endif - case EVT_VIRTUAL_KEY_PLUS: - setValue(getValue() + getStep()); - break; + case EVT_VIRTUAL_KEY_PLUS: + setValue(getValue() + vstep); + break; - case EVT_VIRTUAL_KEY_MINUS: - setValue(getValue() - getStep()); - break; + case EVT_VIRTUAL_KEY_MINUS: + setValue(getValue() - vstep); + break; - case EVT_VIRTUAL_KEY_FORWARD: - setValue(getValue() + getFastStep() * getStep()); - break; + case EVT_VIRTUAL_KEY_FORWARD: + setValue(getValue() + fastStep * vstep); + break; - case EVT_VIRTUAL_KEY_BACKWARD: - setValue(getValue() - getFastStep() * getStep()); - break; + case EVT_VIRTUAL_KEY_BACKWARD: + setValue(getValue() - fastStep * vstep); + break; - case EVT_VIRTUAL_KEY_DEFAULT: - setValue(getDefault()); - break; + case EVT_VIRTUAL_KEY_DEFAULT: + setValue(vdefault); + break; - case EVT_VIRTUAL_KEY_MAX: - setValue(getMax()); - break; + case EVT_VIRTUAL_KEY_MAX: + setValue(vmax); + break; - case EVT_VIRTUAL_KEY_MIN: - setValue(getMin()); - break; + case EVT_VIRTUAL_KEY_MIN: + setValue(vmin); + break; + + case EVT_VIRTUAL_KEY_SIGN: + setValue(-getValue()); + break; + } + } + + FormField::onEvent(event); + } + + void onClicked() override + { + lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + if (indev_type == LV_INDEV_TYPE_POINTER) { + setEditMode(true); + } else { + FormField::onClicked(); + } + } + + void setMin(int value) { vmin = value; } + void setMax(int value) { vmax = value; } + void setDefault(int value) { vdefault = value; } + void setStep(int value) { vstep = value; } + void setFastStep(int value) { fastStep = value; } + void setAccelFactor(int value) { accelFactor = value; } + void setValue(int value) + { + auto newValue = limit(vmin, value, vmax); + if (newValue != currentValue) { + currentValue = newValue; + if (_setValue != nullptr) { + _setValue(currentValue); + } + } + updateDisplay(); + } + + int32_t getValue() const { return _getValue != nullptr ? _getValue() : 0; } + + void setPrefix(std::string value) { prefix = std::move(value); } + + void setSuffix(std::string value) { suffix = std::move(value); } + + void setZeroText(std::string value) { zeroText = std::move(value); } + + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } + + void setSetValueHandler(std::function handler) + { + _setValue = std::move(handler); + } + + void setGetValueHandler(std::function handler) + { + _getValue = std::move(handler); + } + + void setDisplayHandler(std::function function) + { + displayFunction = std::move(function); + } + + void setCancelHandler(std::function handler) + { + cancelHandler = std::move(handler); + } + + void openKeyboard() { NumberKeyboard::show(this); } + void directEdit() { FormField::onClicked(); } + + void update() + { + if (_getValue == nullptr) return; + currentValue = _getValue(); + updateDisplay(); + } + + static LAYOUT_VAL(DEF_W, 100, 100) - case EVT_VIRTUAL_KEY_SIGN: - setValue(-getValue()); + protected: + int vdefault = 0; + int vmin; + int vmax; + int vstep = 1; + int fastStep = 10; + int accelFactor = 4; + int currentValue; + std::string prefix; + std::string suffix; + std::string zeroText; + std::function _getValue; + std::function _setValue; + std::function displayFunction; + std::function isValueAvailable; + std::function cancelHandler = nullptr; + + void updateDisplay() + { + if (lvobj != nullptr) { + std::string str; + if (displayFunction != nullptr) { + str = displayFunction(currentValue); + } else if (!zeroText.empty() && currentValue == 0) { + str = zeroText; + } else { + str = formatNumberAsString(currentValue, textFlags, 0, prefix.c_str(), + suffix.c_str()); + } + lv_textarea_set_text(lvobj, str.c_str()); + } + } + + void onCancel() override + { + if (cancelHandler) + cancelHandler(); + else + FormField::onCancel(); + } + + static void numberedit_cb(lv_event_t* e) + { + NumberArea* numEdit = (NumberArea*)lv_event_get_user_data(e); + if (!numEdit || numEdit->deleted()) return; + + uint32_t key = lv_event_get_key(e); + switch (key) { + case LV_KEY_LEFT: + numEdit->onEvent(EVT_ROTARY_LEFT); + break; + case LV_KEY_RIGHT: + numEdit->onEvent(EVT_ROTARY_RIGHT); break; } } +}; + +/* + The lv_textarea object is slow. To avoid too much overhead on views with multiple + edit fields, the text area is initially displayed as a button. When the button + is pressed, a text area object is created over the top of the button in order + to edit the value. +*/ +NumberEdit::NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, + std::function getValue, + std::function setValue, LcdFlags textFlags) : + TextButton(parent, rect, "", + [=]() { + openEdit(); + return 0; + }), + _getValue(std::move(getValue)), + _setValue(std::move(setValue)), + vmin(vmin), + vmax(vmax) +{ + if (rect.w == 0) setWidth(NumberArea::DEF_W); + + setTextFlag(textFlags); + + lv_obj_set_width(label, width() - PAD_MEDIUM * 2 - 2); + etx_obj_add_style(label, styles->text_align_right, LV_PART_MAIN); - FormField::onEvent(event); + update(); } -void NumberEdit::onClicked() +void NumberEdit::openEdit() { - lv_indev_type_t indev_type = lv_indev_get_type(lv_indev_get_act()); + if (edit == nullptr) { + edit = new NumberArea( + this, + {-(PAD_MEDIUM + 2), -(PAD_TINY + 2), + lv_obj_get_width(lvobj), lv_obj_get_height(lvobj)}, + this->vmin, this->vmax, _getValue, _setValue, textFlags); + edit->setChangeHandler([=]() { + update(); + lv_group_focus_obj(lvobj); + edit->hide(); + }); + edit->setCancelHandler([=]() { + lv_group_focus_obj(lvobj); + edit->hide(); + }); + } + edit->setTextFlag(textFlags); + edit->setSetValueHandler(_setValue); + edit->setGetValueHandler(_getValue); + edit->setAvailableHandler(isValueAvailable); + edit->setDisplayHandler(displayFunction); + edit->setDefault(vdefault); + edit->setMin(vmin); + edit->setMax(vmax); + edit->setStep(step); + edit->setFastStep(fastStep); + edit->setAccelFactor(accelFactor); + edit->setPrefix(prefix); + edit->setSuffix(suffix); + edit->setZeroText(zeroText); + edit->update(); + edit->show(); + lv_group_focus_obj(edit->getLvObj()); + lv_indev_type_t indev_type = + lv_indev_get_type(lv_indev_get_act()); if (indev_type == LV_INDEV_TYPE_POINTER) { - NumberKeyboard::show(this); - return; + edit->openKeyboard(); + } else { + edit->directEdit(); } + lv_obj_add_state(lvobj, LV_STATE_FOCUSED); +} - FormField::onClicked(); +void NumberEdit::update() +{ + if (_getValue == nullptr) return; + currentValue = _getValue(); + updateDisplay(); } void NumberEdit::updateDisplay() { - if (lvobj != nullptr) { - std::string str; - if (displayFunction != nullptr) { - str = displayFunction(currentValue); - } else if (!zeroText.empty() && currentValue == 0) { - str = zeroText; - } else { - str = formatNumberAsString(currentValue, textFlags, 0, prefix.c_str(), - suffix.c_str()); - } - lv_textarea_set_text(lvobj, str.c_str()); + std::string str; + if (displayFunction != nullptr) { + str = displayFunction(currentValue); + } else if (!zeroText.empty() && currentValue == 0) { + str = zeroText; + } else { + str = formatNumberAsString(currentValue, textFlags, 0, prefix.c_str(), + suffix.c_str()); } + setText(str); } void NumberEdit::setValue(int value) @@ -200,14 +379,5 @@ void NumberEdit::setValue(int value) } } updateDisplay(); -} - -void NumberEdit::update() -{ - if (_getValue == nullptr) return; - auto newValue = _getValue(); - if (newValue != currentValue) { - currentValue = newValue; - } - updateDisplay(); + if (edit) edit->setValue(value); } diff --git a/radio/src/thirdparty/libopenui/src/numberedit.h b/radio/src/thirdparty/libopenui/src/numberedit.h index 873000f679b..da7ca7da78a 100644 --- a/radio/src/thirdparty/libopenui/src/numberedit.h +++ b/radio/src/thirdparty/libopenui/src/numberedit.h @@ -19,150 +19,95 @@ #pragma once #include "form.h" +#include "button.h" -class NumberEdit: public FormField +class NumberArea; + +class NumberEdit : public TextButton { - public: - NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, - std::function getValue, - std::function setValue = nullptr, - LcdFlags textFlags = 0); + public: + NumberEdit(Window* parent, const rect_t& rect, int vmin, int vmax, + std::function getValue, + std::function setValue = nullptr, + LcdFlags textFlags = 0); #if defined(DEBUG_WINDOWS) - std::string getName() const override - { - return "NumberEdit(" + std::to_string(getValue()) + ")"; - } + std::string getName() const override + { + return "NumberEdit"; + } #endif - void setAvailableHandler(std::function handler) - { - isValueAvailable = std::move(handler); - } - - void onEvent(event_t event) override; - void onClicked() override; - - void setMin(int value) - { - vmin = value; - } - - void setMax(int value) - { - vmax = value; - } - - void setDefault(int value) - { - vdefault = value; - } - - int32_t getMin() const - { - return vmin; - } - - int32_t getMax() const - { - return vmax; - } - - int32_t getDefault() const - { - return vdefault; - } - - void setStep(int value) - { - step = value; - } - - int32_t getStep() const - { - return step; - } - - void setFastStep(int value) - { - fastStep = value; - } - - int32_t getFastStep() const - { - return fastStep; - } - - void setAccelFactor(int value) - { - accelFactor = value; - } - - int32_t getAccelFactor() const - { - return accelFactor; - } - - void setValue(int value); - - void setPrefix(std::string value) - { - prefix = std::move(value); - update(); - } - - void setSuffix(std::string value) - { - suffix = std::move(value); - update(); - } - - void setZeroText(std::string value) - { - zeroText = std::move(value); - update(); - } - - void setSetValueHandler(std::function handler) - { - _setValue = std::move(handler); - } - - void setGetValueHandler(std::function handler) - { - _getValue = std::move(handler); - } - - int32_t getValue() const - { - return _getValue != nullptr ? _getValue() : 0; - } - - void setDisplayHandler(std::function function) - { - displayFunction = std::move(function); - update(); - } - - virtual void update(); - - static LAYOUT_VAL(DEF_W, 100, 100) - - protected: - int vdefault = 0; - int vmin; - int vmax; - int step = 1; - int fastStep = 10; - int accelFactor = 4; - int currentValue; - std::string prefix; - std::string suffix; - std::string zeroText; - std::function _getValue; - std::function _setValue; - std::function displayFunction; - std::function isValueAvailable; - - void updateDisplay(); + virtual void update(); + + int32_t getMax() const { return vmax; } + + void setMin(int value) { vmin = value; } + void setMax(int value) { vmax = value; } + void setDefault(int value) { vdefault = value; } + void setStep(int value) { step = value; } + void setFastStep(int value) { fastStep = value; } + void setAccelFactor(int value) { accelFactor = value; } + void setValue(int value); + + void setPrefix(std::string value) + { + prefix = std::move(value); + update(); + } + + void setSuffix(std::string value) + { + suffix = std::move(value); + update(); + } + + void setZeroText(std::string value) + { + zeroText = std::move(value); + update(); + } + + void setAvailableHandler(std::function handler) + { + isValueAvailable = std::move(handler); + } + + void setDisplayHandler(std::function function) + { + displayFunction = std::move(function); + update(); + } + + void setSetValueHandler(std::function handler) + { + _setValue = std::move(handler); + } + + void setGetValueHandler(std::function handler) + { + _getValue = std::move(handler); + } + + int32_t getValue() const { return _getValue != nullptr ? _getValue() : 0; } + + protected: + NumberArea* edit = nullptr; + std::function _getValue; + std::function _setValue; + int vdefault = 0; + int vmin; + int vmax; + int step = 1; + int fastStep = 10; + int accelFactor = 4; + int currentValue; + std::string prefix; + std::string suffix; + std::string zeroText; + std::function displayFunction; + std::function isValueAvailable; + + void updateDisplay(); + void openEdit(); }; diff --git a/radio/src/thirdparty/libopenui/src/textedit.cpp b/radio/src/thirdparty/libopenui/src/textedit.cpp index e71cb784080..e1dee05c114 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.cpp +++ b/radio/src/thirdparty/libopenui/src/textedit.cpp @@ -27,81 +27,169 @@ #include "menu.h" #endif -TextEdit::TextEdit(Window* parent, const rect_t& rect, char* value, - uint8_t length) : - FormField(rect, 0), value(value), length(length) +class TextArea : public FormField { - lv_obj_enable_style_refresh(false); + public: + TextArea(Window* parent, const rect_t& rect, char* value, uint8_t length) : + FormField(parent, rect, 0, etx_textarea_create), value(value), length(length) + { + lv_textarea_set_max_length(lvobj, length); + lv_textarea_set_placeholder_text(lvobj, "---"); - // Workaround for performance issues with lv_textarea - create on top layer - // not this window then reparent to this window after setup finished - this->parent = parent; - lvobj = lv_textarea_create(lv_layer_top()); + if (rect.w == 0) setWidth(DEF_W); - // Do this first - before any styles are applied, otherwise it is very slow - update(); + update(); + } - etx_textarea_style(lvobj); - setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT); +#if defined(DEBUG_WINDOWS) + std::string getName() const override { return "TextArea"; } +#endif - lv_textarea_set_max_length(lvobj, length); - lv_textarea_set_placeholder_text(lvobj, "---"); + void update() + { + // value may not be null-terminated + std::string txt(value, length); + lv_textarea_set_text(lvobj, txt.c_str()); + } - lv_obj_set_parent(lvobj, parent->getLvObj()); - setupLVGL(); + void onClicked() override { + setEditMode(true); + } - if (rect.w == 0) setWidth(DEF_W); + void openKeyboard() { + TextKeyboard::show(this); + } - lv_obj_enable_style_refresh(true); - lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); -} + void setCancelHandler(std::function handler) + { + cancelHandler = std::move(handler); + } -void TextEdit::update() -{ - // value may not be null-terminated - std::string txt(value, length); - lv_textarea_set_text(lvobj, txt.c_str()); -} + static LAYOUT_VAL(DEF_W, 100, 100) + + protected: + char* value; + uint8_t length; + std::function cancelHandler = nullptr; + + void trim() + { + for (int i = length - 1; i >= 0; i--) { + if (value[i] == ' ' || value[i] == '\0') + value[i] = '\0'; + else + break; + } + } -void TextEdit::trim() -{ - for (int i = length - 1; i >= 0; i--) { - if (value[i] == ' ' || value[i] == '\0') - value[i] = '\0'; + void changeEnd(bool forceChanged = false) override + { + if (lvobj == nullptr) return; + + bool changed = false; + auto text = lv_textarea_get_text(lvobj); + if (strncmp(value, text, length) != 0) { + changed = true; + } + + if (changed || forceChanged) { + strncpy(value, text, length); + trim(); + FormField::changeEnd(); + } else if (cancelHandler) { + cancelHandler(); + } + } + + void onCancel() override + { + if (cancelHandler) + cancelHandler(); else - break; + FormField::onCancel(); } -} +}; -void TextEdit::changeEnd(bool forceChanged) +/* + The lv_textarea object is slow. To avoid too much overhead on views with multiple + edit fields, the text area is initially displayed as a button. When the button + is pressed, a text area object is created over the top of the button in order + to edit the value. +*/ +TextEdit::TextEdit(Window* parent, const rect_t& rect, char* text, + uint8_t length, + std::function updateHandler) : + TextButton(parent, rect, "", [=]() { + openEdit(); + return 0; + }), + updateHandler(updateHandler), text(text), length(length) { - if (lvobj == nullptr) return; + if (rect.w == 0) setWidth(TextArea::DEF_W); + + update(); + lv_obj_align(label, LV_ALIGN_OUT_LEFT_MID, 0, PAD_TINY); +} - bool changed = false; - auto text = lv_textarea_get_text(lvobj); - if (strncmp(value, text, length) != 0) { - changed = true; +void TextEdit::update() +{ + if (text[0]) { + std::string s(text, length); + setText(s); + } else { + setText("---"); } +} - if (changed || forceChanged) { - strncpy(value, text, length); - trim(); - FormField::changeEnd(); +void TextEdit::openEdit() +{ + if (edit == nullptr) { + edit = new TextArea(this, + {-(PAD_MEDIUM + 2), -(PAD_TINY + 2), + lv_obj_get_width(lvobj), lv_obj_get_height(lvobj)}, + text, length); + edit->setChangeHandler([=]() { + std::string s(text, length); + setText(s); + if (updateHandler) updateHandler(); + lv_group_focus_obj(lvobj); + edit->hide(); + }); + edit->setCancelHandler([=]() { + lv_group_focus_obj(lvobj); + edit->hide(); + }); } + edit->show(); + lv_group_focus_obj(edit->getLvObj()); + edit->openKeyboard(); + lv_obj_add_state(lvobj, LV_STATE_FOCUSED); } -void TextEdit::onClicked() { TextKeyboard::show(this); } +void TextEdit::preview(bool edited, char* text, uint8_t length) +{ + edit = new TextArea(this, + {-(PAD_MEDIUM + 2), -(PAD_TINY + 2), width(), height()}, + text, length); + lv_group_focus_obj(edit->getLvObj()); + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICKABLE); + lv_obj_clear_flag(edit->getLvObj(), LV_OBJ_FLAG_CLICKABLE); + lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); + lv_obj_clear_flag(edit->getLvObj(), LV_OBJ_FLAG_CLICK_FOCUSABLE); + lv_obj_add_state(edit->getLvObj(), LV_STATE_FOCUSED); + if (edited) lv_obj_add_state(edit->getLvObj(), LV_STATE_EDITED); +} ModelTextEdit::ModelTextEdit(Window* parent, const rect_t& rect, char* value, uint8_t length) : - TextEdit(parent, rect, value, length) + TextEdit(parent, rect, value, length, + []() { storageDirty(EE_MODEL); }) { - setChangeHandler([]() { storageDirty(EE_MODEL); }); } RadioTextEdit::RadioTextEdit(Window* parent, const rect_t& rect, char* value, uint8_t length) : - TextEdit(parent, rect, value, length) + TextEdit(parent, rect, value, length, + []() { storageDirty(EE_GENERAL); }) { - setChangeHandler([]() { storageDirty(EE_GENERAL); }); } diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 6ce20d13543..373be9a6ce2 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -19,33 +19,30 @@ #pragma once #include "form.h" +#include "button.h" -class TextEdit : public FormField +class TextArea; + +class TextEdit : public TextButton { public: - TextEdit(Window* parent, const rect_t& rect, char* value, uint8_t length); + TextEdit(Window* parent, const rect_t& rect, char* text, uint8_t length, + std::function updateHandler = nullptr); #if defined(DEBUG_WINDOWS) - std::string getName() const override { return "TextEdit"; } + std::string getName() const override { return "TextEdit \"" + text + "\""; } #endif - uint8_t getMaxLength() const { return length; } - char* getData() const { return value; } - + void preview(bool edited, char* text, uint8_t length); void update(); - static LAYOUT_VAL(DEF_W, 100, 100) - protected: - static void event_cb(lv_event_t* e); - - char* value; + std::function updateHandler = nullptr; + TextArea* edit = nullptr; + char* text; uint8_t length; - void trim(); - - void changeEnd(bool forceChanged = false) override; - void onClicked() override; + void openEdit(); }; class ModelTextEdit : public TextEdit diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index c0bd815566c..af7f9cae5ec 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -103,11 +103,6 @@ Window::Window(Window *parent, const rect_t &rect, LvglCreate objConstruct) : if (objConstruct == nullptr) objConstruct = window_create; lvobj = objConstruct(lv_parent); - Window::setupLVGL(); -} - -void Window::setupLVGL() -{ lv_obj_set_user_data(lvobj, this); lv_obj_add_event_cb(lvobj, Window::window_event_cb, LV_EVENT_ALL, nullptr); @@ -115,7 +110,6 @@ void Window::setupLVGL() if (rect.w) lv_obj_set_width(lvobj, rect.w); if (rect.h) lv_obj_set_height(lvobj, rect.h); - // lv_obj_set_scrollbar_mode(lvobj, LV_SCROLLBAR_MODE_OFF); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_SCROLL_ELASTIC); if (parent) { diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 17fffd63fda..babe68a63ac 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -48,8 +48,6 @@ class Window Window(const rect_t &rect); Window(Window *parent, const rect_t &rect, LvglCreate objConstruct = nullptr); - virtual void setupLVGL(); - virtual ~Window(); #if defined(DEBUG_WINDOWS) From bcaafc19f160983df64dc583c2d90df51203961d Mon Sep 17 00:00:00 2001 From: philmoz Date: Tue, 7 May 2024 09:57:28 +1000 Subject: [PATCH 03/21] Use dialog popup for file rename. --- radio/src/gui/colorlcd/model_select.cpp | 65 +-------------- radio/src/gui/colorlcd/radio_sdmanager.cpp | 82 ++++--------------- radio/src/thirdparty/libopenui/src/dialog.cpp | 50 +++++++++++ radio/src/thirdparty/libopenui/src/dialog.h | 15 ++++ 4 files changed, 84 insertions(+), 128 deletions(-) diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index f81884ce1f4..82cc6842e7a 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -431,67 +431,6 @@ class ModelsPageBody : public Window //----------------------------------------------------------------------------- -class LabelDialog : public ModalWindow -{ - public: - LabelDialog(Window *parent, char *label, - std::function _saveHandler = nullptr) : - ModalWindow(parent, false), saveHandler(std::move(_saveHandler)) - { - strncpy(this->label, label, LABEL_LENGTH); - this->label[LABEL_LENGTH] = '\0'; - - auto form = new Window(this, rect_t{}); - form->padAll(PAD_ZERO); - form->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO, LCD_W * 0.8, - LV_SIZE_CONTENT); - etx_solid_bg(form->getLvObj()); - lv_obj_center(form->getLvObj()); - - auto hdr = new StaticText(form, {0, 0, LV_PCT(100), 0}, STR_ENTER_LABEL, - COLOR_THEME_PRIMARY2); - etx_solid_bg(hdr->getLvObj(), COLOR_THEME_SECONDARY1_INDEX); - hdr->padAll(PAD_MEDIUM); - - auto box = new Window(form, rect_t{}); - box->padAll(PAD_MEDIUM); - box->setFlexLayout(LV_FLEX_FLOW_ROW, 40, LV_PCT(100), LV_SIZE_CONTENT); - lv_obj_set_flex_align(box->getLvObj(), LV_FLEX_ALIGN_CENTER, - LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_BETWEEN); - - auto edit = new TextEdit(box, rect_t{0, 0, LV_PCT(100), 0}, this->label, - LABEL_LENGTH); - - box = new Window(form, rect_t{}); - box->padAll(PAD_MEDIUM); - box->setFlexLayout(LV_FLEX_FLOW_ROW, 40, LV_PCT(100), LV_SIZE_CONTENT); - lv_obj_set_flex_align(box->getLvObj(), LV_FLEX_ALIGN_CENTER, - LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_BETWEEN); - - new TextButton(box, rect_t{0, 0, 96, 0}, STR_CANCEL, [=]() { - deleteLater(); - return 0; - }); - - new TextButton(box, rect_t{0, 0, 96, 0}, STR_SAVE, [=]() { - if (saveHandler != nullptr) saveHandler(this->label); - deleteLater(); - return 0; - }); - } - - void onCancel() override - { - deleteLater(); - } - - protected: - std::function saveHandler; - char label[LABEL_LENGTH + 1]; -}; - -//----------------------------------------------------------------------------- - class ModelLayoutButton : public IconButton { public: @@ -659,7 +598,7 @@ void ModelLabelsWindow::newModel() void ModelLabelsWindow::newLabel() { tmpLabel[0] = '\0'; - new LabelDialog(this, tmpLabel, [=](std::string label) { + new LabelDialog(parent, tmpLabel, LABEL_LENGTH, STR_ENTER_LABEL, [=](std::string label) { int newlabindex = modelslabels.addLabel(label); if (newlabindex >= 0) { std::set newset; @@ -849,7 +788,7 @@ void ModelLabelsWindow::buildBody(Window *window) auto oldLabel = labels[selected]; strncpy(tmpLabel, oldLabel.c_str(), LABEL_LENGTH); tmpLabel[LABEL_LENGTH] = '\0'; - new LabelDialog(this, tmpLabel, [=](std::string newLabel) { + new LabelDialog(this, tmpLabel, LABEL_LENGTH, STR_ENTER_LABEL, [=](std::string newLabel) { if (newLabel.size() > 0) { auto rndialog = new ProgressDialog(this, STR_RENAME_LABEL, [=]() {}); diff --git a/radio/src/gui/colorlcd/radio_sdmanager.cpp b/radio/src/gui/colorlcd/radio_sdmanager.cpp index d040f01cf43..719d1c2b2fb 100644 --- a/radio/src/gui/colorlcd/radio_sdmanager.cpp +++ b/radio/src/gui/colorlcd/radio_sdmanager.cpp @@ -40,69 +40,6 @@ constexpr int WARN_FILE_LENGTH = 40 * 1024; #define CELL_CTRL_DIR LV_TABLE_CELL_CTRL_CUSTOM_1 #define CELL_CTRL_FILE LV_TABLE_CELL_CTRL_CUSTOM_2 -class FileNameEditWindow : public Page -{ - public: - FileNameEditWindow(const std::string iName) : - Page(ICON_RADIO_SD_MANAGER), name(std::move(iName)) - { - buildHeader(header); - buildBody(body); - }; - -#if defined(DEBUG_WINDOWS) - std::string getName() const override { return "FileNameEditWindow"; } -#endif - protected: - const std::string name; - - void buildHeader(Window *window) - { - header->setTitle(STR_RENAME_FILE); - } - - void buildBody(Window *window) - { - window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_SMALL); - window->padTop(12); - - uint8_t nameLength; - uint8_t extLength; - char extension[LEN_FILE_EXTENSION_MAX + 1]; - memset(extension, 0, sizeof(extension)); - const char *ext = - getFileExtension(name.c_str(), 0, 0, &nameLength, &extLength); - - if (extLength > LEN_FILE_EXTENSION_MAX) extLength = LEN_FILE_EXTENSION_MAX; - if (ext) strncpy(extension, ext, extLength); - - const uint8_t maxNameLength = SD_SCREEN_FILE_LENGTH - extLength; - nameLength -= extLength; - if (nameLength > maxNameLength) nameLength = maxNameLength; - memset(reusableBuffer.sdManager.originalName, 0, SD_SCREEN_FILE_LENGTH); - - strncpy(reusableBuffer.sdManager.originalName, name.c_str(), nameLength); - reusableBuffer.sdManager.originalName[nameLength] = '\0'; - - auto newFileName = new TextEdit( - window, rect_t{0, 0, LV_PCT(100), 0}, reusableBuffer.sdManager.originalName, - SD_SCREEN_FILE_LENGTH - extLength, - [=]() { - char *newValue = reusableBuffer.sdManager.originalName; - size_t totalSize = strlen(newValue); - char changedName[SD_SCREEN_FILE_LENGTH + 1]; - memset(changedName, 0, sizeof(changedName)); - strncpy(changedName, newValue, totalSize); - changedName[totalSize] = '\0'; - if (extLength) { - strncpy(changedName + totalSize, extension, extLength); - } - changedName[totalSize + extLength] = '\0'; - f_rename((const TCHAR *)name.c_str(), (const TCHAR *)changedName); - }); - }; -}; - RadioSdManagerPage::RadioSdManagerPage() : PageTab(STR_SD_CARD, ICON_RADIO_SD_MANAGER) { @@ -513,8 +450,23 @@ void RadioSdManagerPage::fileAction(const char* path, const char* name, }); } menu->addLine(STR_RENAME_FILE, [=]() { - auto few = new FileNameEditWindow(name); - few->setCloseHandler([=]() { browser->refresh(); }); + uint8_t nameLength; + uint8_t extLength; + + const char *ext = getFileExtension(name, 0, 0, &nameLength, &extLength); + + const uint8_t maxNameLength = SD_SCREEN_FILE_LENGTH - extLength; + nameLength = min((uint8_t)(nameLength - extLength), maxNameLength); + + std::string fname(name, nameLength); + std::string extension(""); + if (ext) extension = ext; + + new LabelDialog(Layer::back(), fname.c_str(), maxNameLength, STR_RENAME_FILE, [=](std::string label) { + label += extension; + f_rename((const TCHAR *)name, (const TCHAR *)label.c_str()); + browser->refresh(); + }); }); menu->addLine(STR_DELETE_FILE, [=]() { f_unlink(fullpath); diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index 851dec1d877..bb60563f544 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -165,3 +165,53 @@ void ConfirmDialog::onCancel() deleteLater(); if (cancelHandler) cancelHandler(); } + +//----------------------------------------------------------------------------- + +LabelDialog::LabelDialog(Window *parent, const char *label, int length, const char* title, + std::function _saveHandler) : + ModalWindow(parent, false), saveHandler(std::move(_saveHandler)) +{ + assert(length <= MAX_LABEL_LENGTH); + + strncpy(this->label, label, length); + this->label[length] = '\0'; + + auto form = new Window(this, rect_t{}); + form->padAll(PAD_ZERO); + form->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO, LCD_W * 0.8, + LV_SIZE_CONTENT); + etx_solid_bg(form->getLvObj()); + lv_obj_center(form->getLvObj()); + + auto hdr = new StaticText(form, {0, 0, LV_PCT(100), 0}, title, + COLOR_THEME_PRIMARY2); + etx_solid_bg(hdr->getLvObj(), COLOR_THEME_SECONDARY1_INDEX); + hdr->padAll(PAD_MEDIUM); + + auto box = new Window(form, rect_t{}); + box->padAll(PAD_MEDIUM); + box->setFlexLayout(LV_FLEX_FLOW_ROW, 40, LV_PCT(100), LV_SIZE_CONTENT); + lv_obj_set_flex_align(box->getLvObj(), LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_BETWEEN); + + auto edit = new TextEdit(box, rect_t{0, 0, LV_PCT(100), 0}, this->label, + length); + + box = new Window(form, rect_t{}); + box->padAll(PAD_MEDIUM); + box->setFlexLayout(LV_FLEX_FLOW_ROW, 40, LV_PCT(100), LV_SIZE_CONTENT); + lv_obj_set_flex_align(box->getLvObj(), LV_FLEX_ALIGN_CENTER, + LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_BETWEEN); + + new TextButton(box, rect_t{0, 0, 96, 0}, STR_CANCEL, [=]() { + deleteLater(); + return 0; + }); + + new TextButton(box, rect_t{0, 0, 96, 0}, STR_SAVE, [=]() { + if (saveHandler != nullptr) saveHandler(this->label); + deleteLater(); + return 0; + }); +} diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index 604b267b2fd..e82e5001e3c 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -127,3 +127,18 @@ class ConfirmDialog : public BaseDialog void onCancel() override; }; + +//----------------------------------------------------------------------------- + +class LabelDialog : public ModalWindow +{ + public: + LabelDialog(Window *parent, const char *label, int length, const char* title, + std::function _saveHandler = nullptr); + + static constexpr int MAX_LABEL_LENGTH = 255; + + protected: + std::function saveHandler; + char label[MAX_LABEL_LENGTH + 1]; +}; From 8eec2779dcfa8e456e3da3ae07976b2d5f5c1ac8 Mon Sep 17 00:00:00 2001 From: philmoz Date: Tue, 7 May 2024 14:55:22 +1000 Subject: [PATCH 04/21] Cleanup radio setup and radio hardware code. --- radio/src/gui/colorlcd/radio_hardware.cpp | 184 ++++---- radio/src/gui/colorlcd/radio_hardware.h | 11 +- radio/src/gui/colorlcd/radio_setup.cpp | 494 +++++++++++----------- radio/src/gui/colorlcd/radio_setup.h | 26 ++ 4 files changed, 344 insertions(+), 371 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_hardware.cpp b/radio/src/gui/colorlcd/radio_hardware.cpp index fd25dc3a485..80d2ad17836 100644 --- a/radio/src/gui/colorlcd/radio_hardware.cpp +++ b/radio/src/gui/colorlcd/radio_hardware.cpp @@ -31,6 +31,7 @@ #include "radio_calibration.h" #include "radio_diaganas.h" #include "radio_diagkeys.h" +#include "radio_setup.h" #if defined(BLUETOOTH) #include "hw_bluetooth.h" @@ -43,25 +44,17 @@ static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(2), static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; -static Window* hbox(Window* parent) +RadioHardwarePage::RadioHardwarePage() : + PageTab(STR_HARDWARE, ICON_RADIO_HARDWARE, PAD_TINY) { - auto box = new Window(parent, rect_t{}); - box->padAll(PAD_TINY); - box->setFlexLayout(LV_FLEX_FLOW_ROW, PAD_SMALL); - lv_obj_set_style_grid_cell_x_align(box->getLvObj(), LV_GRID_ALIGN_STRETCH, 0); - lv_obj_set_flex_align(box->getLvObj(), LV_FLEX_ALIGN_START, - LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_AROUND); - - return box; + enableVBatBridge(); } -RadioHardwarePage::RadioHardwarePage() : - PageTab(STR_HARDWARE, ICON_RADIO_HARDWARE) +void RadioHardwarePage::cleanup() { + disableVBatBridge(); } -void RadioHardwarePage::checkEvents() { enableVBatBridge(); } - class BatCalEdit : public NumberEdit { public: @@ -87,114 +80,87 @@ class BatCalEdit : public NumberEdit } }; -class HWButtonGroup : public Window -{ - public: - typedef std::function PageFct; - typedef std::pair PageDef; - typedef std::list PageDefs; - -#if LCD_W > LCD_H - static constexpr coord_t COLS = 4; -#else - static constexpr coord_t COLS = 3; -#endif - static constexpr coord_t BTN_W = (LCD_W - PAD_MEDIUM * (COLS + 1) - PAD_TINY * 2) / COLS; - - HWButtonGroup(Window* parent, const rect_t& rect, const char* title, PageDefs pages) : - Window(parent, rect), title(title) +static SetupLineDef setupLines[] = { { - padAll(PAD_ZERO); - int rows = (pages.size() + COLS - 1) / COLS; - setHeight(rows * EdgeTxStyles::UI_ELEMENT_HEIGHT + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2 + EdgeTxStyles::PAGE_LINE_HEIGHT); - - new Subtitle(this, title); - - int n = 0; - for (auto& entry : pages) { - coord_t x = (n % COLS) * (BTN_W + PAD_MEDIUM) + PAD_TINY; - coord_t y = (n / COLS) * (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM) + EdgeTxStyles::PAGE_LINE_HEIGHT + 2; + // Batt meter range - Range 3.0v to 16v + STR_BATTERY_RANGE, + [](Window* parent, coord_t x, coord_t y) { + auto batMin = new NumberEdit( + parent, {x, y, RadioHardwarePage::NUM_EDIT_W, 0}, -60 + 90, g_eeGeneral.vBatMax + 29 + 90, + GET_SET_WITH_OFFSET(g_eeGeneral.vBatMin, 90), PREC1); + batMin->setSuffix("V"); + new StaticText(parent, {x + RadioHardwarePage::NUM_EDIT_W + PAD_SMALL, y + PAD_SMALL + 1, PAD_LARGE, EdgeTxStyles::PAGE_LINE_HEIGHT}, "-"); + auto batMax = new NumberEdit( + parent, {x + RadioHardwarePage::NUM_EDIT_W + PAD_LARGE + PAD_SMALL, y, RadioHardwarePage::NUM_EDIT_W, 0}, g_eeGeneral.vBatMin - 29 + 120, 40 + 120, + GET_SET_WITH_OFFSET(g_eeGeneral.vBatMax, 120), PREC1); + batMax->setSuffix("V"); + + batMin->setSetValueHandler([=](int32_t newValue) { + g_eeGeneral.vBatMin = newValue - 90; + SET_DIRTY(); + batMax->setMin(g_eeGeneral.vBatMin - 29 + 120); + }); - new TextButton(this, rect_t{x, y, BTN_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, entry.first, [&, entry]() { - entry.second(); - return 0; + batMax->setSetValueHandler([=](int32_t newValue) { + g_eeGeneral.vBatMax = newValue - 120; + SET_DIRTY(); + batMin->setMax(g_eeGeneral.vBatMax + 29 + 90); }); - n += 1; } - } - - protected: - std::string title; + }, + { + // Bat calibration + STR_BATT_CALIB, + [](Window* parent, coord_t x, coord_t y) { + new BatCalEdit(parent, {x, y, RadioHardwarePage::NUM_EDIT_W, 0}); + } + }, + { + // RTC Batt check enable + STR_RTC_CHECK, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_SET_INVERTED(g_eeGeneral.disableRtcWarning)); + + // RTC Batt display + std::string s(STR_VALUE); + s += " "; + new DynamicNumber( + parent, {x + ToggleSwitch::TOGGLE_W + PAD_SMALL, y + PAD_SMALL + 1, 0, 0}, [] { return getRTCBatteryVoltage(); }, + COLOR_THEME_PRIMARY1 | PREC2, s.c_str(), "V"); + } + }, + { + // ADC filter + STR_JITTER_FILTER, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_INVERTED(g_eeGeneral.noJitterFilter)); + } + }, +#if defined(AUDIO_MUTE_GPIO) + { + // Mute audio + STR_AUDIO_MUTE, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_eeGeneral.audioMuteEnable)); + } + }, +#endif }; void RadioHardwarePage::build(Window* window) { window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY); - FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY); - // TODO: sub-title? - // Batt meter range - Range 3.0v to 16v - auto line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_BATTERY_RANGE); - - auto box = hbox(line); - auto batMin = new NumberEdit( - box, rect_t{0, 0, NUM_EDIT_W, 0}, -60 + 90, g_eeGeneral.vBatMax + 29 + 90, - GET_SET_WITH_OFFSET(g_eeGeneral.vBatMin, 90), PREC1); - batMin->setSuffix("V"); - new StaticText(box, rect_t{}, "-"); - auto batMax = new NumberEdit( - box, rect_t{0, 0, NUM_EDIT_W, 0}, g_eeGeneral.vBatMin - 29 + 120, 40 + 120, - GET_SET_WITH_OFFSET(g_eeGeneral.vBatMax, 120), PREC1); - batMax->setSuffix("V"); - - batMin->setSetValueHandler([=](int32_t newValue) { - g_eeGeneral.vBatMin = newValue - 90; - SET_DIRTY(); - batMax->setMin(g_eeGeneral.vBatMin - 29 + 120); - }); - - batMax->setSetValueHandler([=](int32_t newValue) { - g_eeGeneral.vBatMax = newValue - 120; - SET_DIRTY(); - batMin->setMax(g_eeGeneral.vBatMax + 29 + 90); - }); - - // Bat calibration - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_BATT_CALIB); - box = hbox(line); - new BatCalEdit(box, rect_t{0, 0, NUM_EDIT_W, 0}); - - // RTC Batt check enable - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_RTC_CHECK); - - box = hbox(line); - new ToggleSwitch(box, rect_t{}, - GET_SET_INVERTED(g_eeGeneral.disableRtcWarning)); - - // RTC Batt display - new StaticText(box, rect_t{}, STR_VALUE); - new DynamicNumber( - box, rect_t{}, [] { return getRTCBatteryVoltage(); }, - COLOR_THEME_PRIMARY1 | PREC2, nullptr, "V"); + for (size_t i = 0; i < DIM(setupLines); i += 1) { + new SetupLine(window, {0, 0, LCD_W - padding * 2, 0}, setupLines[i].title, setupLines[i].createEdit, EDT_X); + } - // ADC filter - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_JITTER_FILTER); - box = hbox(line); - new ToggleSwitch(box, rect_t{}, GET_SET_INVERTED(g_eeGeneral.noJitterFilter)); + FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY); -#if defined(AUDIO_MUTE_GPIO) - // Mute audio - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_AUDIO_MUTE); - box = hbox(line); - new ToggleSwitch(box, rect_t{}, GET_SET_DEFAULT(g_eeGeneral.audioMuteEnable)); -#endif + FormLine* line; #if defined(HARDWARE_INTERNAL_MODULE) new Subtitle(window, STR_INTERNALRF); @@ -221,7 +187,7 @@ void RadioHardwarePage::build(Window* window) new SerialConfigWindow(window, grid); // Calibration - new HWButtonGroup(window, {0, 0, LCD_W - padding * 2, 0}, STR_INPUTS, { + new SetupButtonGroup(window, {0, 0, LCD_W - padding * 2, 0}, STR_INPUTS, BTN_COLS, PAD_ZERO, { {STR_CALIBRATION, []() { new RadioCalibrationPage(); }}, {STR_STICKS, []() { new HWInputDialog(STR_STICKS); }}, {STR_POTS, []() { new HWInputDialog(STR_POTS); }}, @@ -229,7 +195,7 @@ void RadioHardwarePage::build(Window* window) }); // Debugs - new HWButtonGroup(window, {0, 0, LCD_W - padding * 2, 0}, STR_DEBUG, { + new SetupButtonGroup(window, {0, 0, LCD_W - padding * 2, 0}, STR_DEBUG, BTN_COLS, PAD_ZERO, { {STR_ANALOGS_BTN, []() { new RadioAnalogsDiagsViewPageGroup(); }}, {STR_KEYS_BTN, []() { new RadioKeyDiagsPage(); }}, }); diff --git a/radio/src/gui/colorlcd/radio_hardware.h b/radio/src/gui/colorlcd/radio_hardware.h index 34cff494a35..c1e5249b766 100644 --- a/radio/src/gui/colorlcd/radio_hardware.h +++ b/radio/src/gui/colorlcd/radio_hardware.h @@ -25,12 +25,15 @@ class RadioHardwarePage : public PageTab { - void checkEvents() override; - - static LAYOUT_VAL(NUM_EDIT_W, 80, 80) - public: RadioHardwarePage(); void build(Window* window) override; + + static LAYOUT_VAL(NUM_EDIT_W, 80, 80) + static LAYOUT_VAL(BTN_COLS, 4, 3) + static LAYOUT_VAL(EDT_X, 160, 160) + + protected: + void cleanup() override; }; diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index 76ffdc77e19..08d0fe4a72a 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -186,52 +186,50 @@ class DateTimeWindow : public Window } }; -class WindowButtonGroup : public Window +SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, PaddingSize padding, PageDefs pages) : + Window(parent, rect) { - public: - typedef std::function PageFct; - typedef std::pair PageDef; - typedef std::list PageDefs; - -#if LCD_W > LCD_H - static constexpr coord_t COLS = 3; -#else - static constexpr coord_t COLS = 2; -#endif - static constexpr coord_t BTN_W = (LCD_W - PAD_MEDIUM * (COLS + 1) - PAD_TINY * 2) / COLS; + padAll(padding); - WindowButtonGroup(Window* parent, const rect_t& rect, PageDefs pages) : - Window(parent, rect) - { - padAll(PAD_TINY); - - int rows = (pages.size() + COLS - 1) / COLS; - setHeight(rows * EdgeTxStyles::UI_ELEMENT_HEIGHT + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2); - - int n = 0; - int remaining = pages.size(); - coord_t xo = 0; - for (auto& entry : pages) { - if (remaining < COLS && (n % COLS == 0)) - xo = ((COLS - remaining) * (BTN_W + PAD_MEDIUM)) / 2; - coord_t x = xo + (n % COLS) * (BTN_W + PAD_MEDIUM); - coord_t y = (n / COLS) * (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM); - - // TODO: sort out all caps title strings VS quick menu strings - std::string title(entry.first); - std::replace(title.begin(), title.end(), '\n', ' '); - - new TextButton(this, rect_t{x, y, BTN_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, title, [&, entry]() { - entry.second(); - return 0; - }); - n += 1; - remaining -= 1; - } + coord_t buttonWidth = (LCD_W - PAD_MEDIUM * (cols + 1) - PAD_TINY * 2) / cols; + + int rows = (pages.size() + cols - 1) / cols; + int height = rows * EdgeTxStyles::UI_ELEMENT_HEIGHT + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2; + if (title) { + height += EdgeTxStyles::PAGE_LINE_HEIGHT + PAD_TINY; } + setHeight(height); + + if (title) + new Subtitle(this, title); + + int n = 0; + int remaining = pages.size(); + coord_t xo = 0; + coord_t yo = title ? EdgeTxStyles::PAGE_LINE_HEIGHT + PAD_TINY : 0; + coord_t xw = buttonWidth + PAD_MEDIUM; + coord_t x, y; + for (auto& entry : pages) { + if (remaining < cols && (n % cols == 0)) { + coord_t space = ((cols - remaining) * xw) / (remaining + 1); + xw += space; + xo = space; + } + x = xo + (n % cols) * xw; + y = yo + (n / cols) * (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM); - protected: -}; + // TODO: sort out all caps title strings VS quick menu strings + std::string title(entry.first); + std::replace(title.begin(), title.end(), '\n', ' '); + + new TextButton(this, rect_t{x, y, buttonWidth, EdgeTxStyles::UI_ELEMENT_HEIGHT}, title, [&, entry]() { + entry.second(); + return 0; + }); + n += 1; + remaining -= 1; + } +} class SubPage : public Page { @@ -776,32 +774,204 @@ class ManageModelsSetupPage : public SubPage Window* favSelectMatch = nullptr; }; -class SetupLine : public Window +SetupLine::SetupLine(Window* parent, const rect_t& rect, const char* title, std::function createEdit, coord_t col2) : + Window(parent, rect) { - public: - SetupLine(Window* parent, const rect_t& rect, const char* title, std::function createEdit) : - Window(parent, rect) - { - padAll(PAD_ZERO); - coord_t titleY = PAD_MEDIUM + 1; - coord_t titleH = EdgeTxStyles::PAGE_LINE_HEIGHT; + padAll(PAD_ZERO); + coord_t titleY = PAD_MEDIUM + 1; + coord_t titleH = EdgeTxStyles::PAGE_LINE_HEIGHT; + if (createEdit) { coord_t editY = PAD_TINY; - if (getTextWidth(title) >= RadioSetupPage::LBL_W) { - setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2 + PAD_MEDIUM); - titleY = 0; - titleH = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY; - editY = PAD_SMALL + 1; - } else { - setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); + if (title) { + if (getTextWidth(title) >= RadioSetupPage::LBL_W) { + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2 + PAD_MEDIUM); + titleY = 0; + titleH = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY; + editY = PAD_SMALL + 1; + } else { + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); + } + new StaticText(this, {PAD_TINY, titleY, RadioSetupPage::LBL_W, titleH}, title); } - new StaticText(this, {PAD_TINY, titleY, RadioSetupPage::LBL_W, titleH}, title); - createEdit(this, RadioSetupPage::EDT_X, editY); + createEdit(this, col2, editY); + } else { + new StaticText(this, {0, titleY, 0, titleH}, title, COLOR_THEME_PRIMARY1 | FONT(BOLD)); + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); } +} - protected: +static SetupLineDef setupLines[] = { + { + // Splash screen + STR_SPLASHSCREEN, + [](Window* parent, coord_t x, coord_t y) { + new Choice( + parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_SPLASHSCREEN_DELAYS, 0, 7, + [=]() -> int32_t { return 3 - g_eeGeneral.splashMode; }, + [=](int32_t newValue) { + g_eeGeneral.splashMode = 3 - newValue; + SET_DIRTY(); + }); + } + }, + { + // PPM units + STR_UNITS_PPM, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_PPMUNIT, PPM_PERCENT_PREC0, PPM_PERCENT_PREC1, + GET_SET_DEFAULT(g_eeGeneral.ppmunit)); + } + }, + { + // Play startup sound + STR_PLAY_HELLO, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, GET_SET_INVERTED(g_eeGeneral.dontPlayHello)); + } + }, +#if defined(PWR_BUTTON_PRESS) + { + // Pwr Off Delay + STR_PWR_OFF_DELAY, + [](Window* parent, coord_t x, coord_t y) { + new Choice( + parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_PWR_OFF_DELAYS, 0, 3, + [=]() -> int32_t { return 2 - g_eeGeneral.pwrOffSpeed; }, + [=](int32_t newValue) { + g_eeGeneral.pwrOffSpeed = 2 - newValue; + SET_DIRTY(); + }); + } + }, +#endif +#if defined(PXX2) + { + // Owner ID + STR_OWNER_ID, + [](Window* parent, coord_t x, coord_t y) { + new RadioTextEdit(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, g_eeGeneral.ownerRegistrationID, + PXX2_LEN_REGISTRATION_ID); + } + }, +#endif + { + // Country code + STR_COUNTRY_CODE, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_COUNTRY_CODES, 0, 2, + GET_SET_DEFAULT(g_eeGeneral.countryCode)); + } + }, + { + // Audio language + STR_VOICE_LANGUAGE, + [](Window* parent, coord_t x, coord_t y) { + auto choice = + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, DIM(languagePacks) - 2, + GET_VALUE(currentLanguagePackIdx), [](uint8_t newValue) { + currentLanguagePackIdx = newValue; + currentLanguagePack = languagePacks[currentLanguagePackIdx]; + strncpy(g_eeGeneral.ttsLanguage, currentLanguagePack->id, 2); + SET_DIRTY(); + }); + choice->setTextHandler( + [](uint8_t value) { return languagePacks[value]->name; }); + } + }, + { + // Imperial units + STR_UNITS_SYSTEM, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_VUNITSSYSTEM, 0, 1, + GET_SET_DEFAULT(g_eeGeneral.imperial)); + } + }, + { + // Switches delay + STR_SWITCHES_DELAY, + [](Window* parent, coord_t x, coord_t y) { + auto edit = + new NumberEdit(parent, {x, y, RadioSetupPage::NUM_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 100, + GET_SET_VALUE_WITH_OFFSET(g_eeGeneral.switchesDelay, 15)); + edit->setDisplayHandler([](int32_t value) { + return formatNumberAsString(value * 10, 0, 0, nullptr, STR_MS); + }); + } + }, + { + // USB mode + STR_USBMODE, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_USBMODES, USB_UNSELECTED_MODE, USB_MAX_MODE, + GET_SET_DEFAULT(g_eeGeneral.USBMode)); + } + }, +#if defined(ROTARY_ENCODER_NAVIGATION) && !defined(USE_HATS_AS_KEYS) + { + STR_ROTARY_ENC_MODE, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_ROTARY_ENC_OPT, ROTARY_ENCODER_MODE_NORMAL, + ROTARY_ENCODER_MODE_INVERT_BOTH, + GET_SET_DEFAULT(g_eeGeneral.rotEncMode)); + } + }, +#endif +#if defined(USE_HATS_AS_KEYS) + { + STR_HATSMODE, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 120, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_HATSOPT, HATSMODE_TRIMS_ONLY, + HATSMODE_SWITCHABLE, GET_SET_DEFAULT(g_eeGeneral.hatsMode)); + new TextButton(parent, {x + 120 + PAD_MEDIUM, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, "?", [=]() { + new MessageDialog(parent, STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", + LEFT); + return 0; + }); + } + }, +#endif + { + // RX channel order + STR_DEF_CHAN_ORD, + [](Window* parent, coord_t x, coord_t y) { + uint8_t mains = adcGetMaxInputs(ADC_INPUT_MAIN); + auto max_order = inputMappingGetMaxChannelOrder() - 1; + auto choice = new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, max_order, + GET_SET_DEFAULT(g_eeGeneral.templateSetup)); + + choice->setTextHandler([=](uint8_t value) { + std::string s; + for (uint8_t i = 0; i < mains; i++) { + s += getAnalogShortLabel(inputMappingChannelOrder(value, i)); + } + return s; + }); + } + }, + { + // Stick mode + STR_MODE, + [](Window* parent, coord_t x, coord_t y) { + auto choice = new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 3, GET_DEFAULT(g_eeGeneral.stickMode), + [=](uint8_t newValue) { + mixerTaskStop(); + g_eeGeneral.stickMode = newValue; + SET_DIRTY(); + checkThrottleStick(); + mixerTaskStart(); + }); + choice->setTextHandler([](uint8_t value) { + auto stick0 = inputMappingConvertMode(value, 0); + auto stick1 = inputMappingConvertMode(value, 1); + return std::to_string(1 + value) + ": " + STR_LEFT_STICK + " = " + + std::string(getMainControlLabel(stick0)) + "+" + + std::string(getMainControlLabel(stick1)); + }); + } + }, }; -RadioSetupPage::RadioSetupPage() : PageTab(STR_RADIO_SETUP, ICON_RADIO_SETUP) {} +RadioSetupPage::RadioSetupPage() : PageTab(STR_RADIO_SETUP, ICON_RADIO_SETUP, PAD_TINY) {} void RadioSetupPage::build(Window* window) { @@ -814,7 +984,7 @@ void RadioSetupPage::build(Window* window) y += w->height() + padding; // Sub-pages - w = new WindowButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, { + w = new SetupButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, nullptr, BTN_COLS, PAD_TINY, { {STR_SOUND_LABEL, []() { new SoundPage(); }}, #if defined(VARIO) {STR_VARIO, []() { new VarioPage(); }}, @@ -829,200 +999,8 @@ void RadioSetupPage::build(Window* window) {STR_MAIN_MENU_MANAGE_MODELS, []() { new ManageModelsSetupPage(); }}, }); - y += w->height() + padding; - - // Splash screen - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_SPLASHSCREEN, - [=](Window* parent, coord_t x, coord_t y) { - new Choice( - parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_SPLASHSCREEN_DELAYS, 0, 7, - [=]() -> int32_t { return 3 - g_eeGeneral.splashMode; }, - [=](int32_t newValue) { - g_eeGeneral.splashMode = 3 - newValue; - SET_DIRTY(); - }); - }); - - y += w->height() + padding; - - // PPM units - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_UNITS_PPM, - [=](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_PPMUNIT, PPM_PERCENT_PREC0, PPM_PERCENT_PREC1, - GET_SET_DEFAULT(g_eeGeneral.ppmunit)); - }); - - y += w->height() + padding; - - // Play startup sound - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_PLAY_HELLO, - [=](Window* parent, coord_t x, coord_t y) { - new ToggleSwitch(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, GET_SET_INVERTED(g_eeGeneral.dontPlayHello)); - }); - - y += w->height() + padding; - -#if defined(PWR_BUTTON_PRESS) - // Pwr Off Delay - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_PWR_OFF_DELAY, - [=](Window* parent, coord_t x, coord_t y) { - new Choice( - parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_PWR_OFF_DELAYS, 0, 3, - [=]() -> int32_t { return 2 - g_eeGeneral.pwrOffSpeed; }, - [=](int32_t newValue) { - g_eeGeneral.pwrOffSpeed = 2 - newValue; - SET_DIRTY(); - }); - }); - - y += w->height() + padding; -#endif - -#if defined(PXX2) - // Owner ID - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_OWNER_ID, - [=](Window* parent, coord_t x, coord_t y) { - new RadioTextEdit(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, g_eeGeneral.ownerRegistrationID, - PXX2_LEN_REGISTRATION_ID); - }); - - y += w->height() + padding; -#endif - - // Country code - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_COUNTRY_CODE, - [=](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_COUNTRY_CODES, 0, 2, - GET_SET_DEFAULT(g_eeGeneral.countryCode)); - }); - - y += w->height() + padding; - - // Audio language - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_VOICE_LANGUAGE, - [=](Window* parent, coord_t x, coord_t y) { - auto choice = - new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, DIM(languagePacks) - 2, - GET_VALUE(currentLanguagePackIdx), [](uint8_t newValue) { - currentLanguagePackIdx = newValue; - currentLanguagePack = languagePacks[currentLanguagePackIdx]; - strncpy(g_eeGeneral.ttsLanguage, currentLanguagePack->id, 2); - SET_DIRTY(); - }); - choice->setTextHandler( - [](uint8_t value) { return languagePacks[value]->name; }); - }); - - y += w->height() + padding; - - // Imperial units - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_UNITS_SYSTEM, - [=](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_VUNITSSYSTEM, 0, 1, - GET_SET_DEFAULT(g_eeGeneral.imperial)); - }); - - y += w->height() + padding; - -#if defined(FAI_CHOICE) -/* case ITEM_SETUP_FAI: - lcdDrawText(PAD_MEDIUM, y, "FAI Mode"); - if (g_eeGeneral.fai) { - lcdDrawText(RADIO_SETUP_2ND_COLUMN, y, "Locked in FAI Mode"); - } - else { - g_eeGeneral.fai = editCheckBox(g_eeGeneral.fai, RADIO_SETUP_2ND_COLUMN, y, - attr, event); if (attr && checkIncDec_Ret) { g_eeGeneral.fai = false; - POPUP_CONFIRMATION("FAI mode?"); - } - } - break;*/ -#endif - - // Switches delay - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_SWITCHES_DELAY, - [=](Window* parent, coord_t x, coord_t y) { - auto edit = - new NumberEdit(parent, {x, y, NUM_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 100, - GET_SET_VALUE_WITH_OFFSET(g_eeGeneral.switchesDelay, 15)); - edit->setDisplayHandler([](int32_t value) { - return formatNumberAsString(value * 10, 0, 0, nullptr, STR_MS); - }); - }); - - y += w->height() + padding; - - // USB mode - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_USBMODE, - [=](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_USBMODES, USB_UNSELECTED_MODE, USB_MAX_MODE, - GET_SET_DEFAULT(g_eeGeneral.USBMode)); - }); - - y += w->height() + padding; - -#if defined(ROTARY_ENCODER_NAVIGATION) && !defined(USE_HATS_AS_KEYS) - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_ROTARY_ENC_MODE, - [=](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_ROTARY_ENC_OPT, ROTARY_ENCODER_MODE_NORMAL, - ROTARY_ENCODER_MODE_INVERT_BOTH, - GET_SET_DEFAULT(g_eeGeneral.rotEncMode)); - }); - - y += w->height() + padding; -#endif - -#if defined(USE_HATS_AS_KEYS) - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_HATSMODE, - [=](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 120, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_HATSOPT, HATSMODE_TRIMS_ONLY, - HATSMODE_SWITCHABLE, GET_SET_DEFAULT(g_eeGeneral.hatsMode)); - new TextButton(parent, {x + 120 + PAD_MEDIUM, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, "?", [=]() { - new MessageDialog(parent, STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", - LEFT); - return 0; - }); - }); - - y += w->height() + padding; -#endif - - // RX channel order - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_DEF_CHAN_ORD, - [=](Window* parent, coord_t x, coord_t y) { - uint8_t mains = adcGetMaxInputs(ADC_INPUT_MAIN); - auto max_order = inputMappingGetMaxChannelOrder() - 1; - auto choice = new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, max_order, - GET_SET_DEFAULT(g_eeGeneral.templateSetup)); - - choice->setTextHandler([=](uint8_t value) { - std::string s; - for (uint8_t i = 0; i < mains; i++) { - s += getAnalogShortLabel(inputMappingChannelOrder(value, i)); - } - return s; - }); - }); - - y += w->height() + padding; - - // Stick mode - new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, STR_MODE, - [=](Window* parent, coord_t x, coord_t y) { - auto choice = new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 3, GET_DEFAULT(g_eeGeneral.stickMode), - [=](uint8_t newValue) { - mixerTaskStop(); - g_eeGeneral.stickMode = newValue; - SET_DIRTY(); - checkThrottleStick(); - mixerTaskStart(); - }); - choice->setTextHandler([](uint8_t value) { - auto stick0 = inputMappingConvertMode(value, 0); - auto stick1 = inputMappingConvertMode(value, 1); - return std::to_string(1 + value) + ": " + STR_LEFT_STICK + " = " + - std::string(getMainControlLabel(stick0)) + "+" + - std::string(getMainControlLabel(stick1)); - }); - }); + for (size_t i = 0; i < DIM(setupLines); i += 1) { + y += w->height() + padding; + w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, setupLines[i].title, setupLines[i].createEdit, EDT_X); + } } diff --git a/radio/src/gui/colorlcd/radio_setup.h b/radio/src/gui/colorlcd/radio_setup.h index 13fa9e8dd52..9b9feb750ab 100644 --- a/radio/src/gui/colorlcd/radio_setup.h +++ b/radio/src/gui/colorlcd/radio_setup.h @@ -32,4 +32,30 @@ class RadioSetupPage: public PageTab { static LAYOUT_VAL(NUM_W, 80, 80) static LAYOUT_VAL(EDT_X, 220, 144) static constexpr coord_t LBL_W = EDT_X - PAD_TINY - PAD_SMALL; + static LAYOUT_VAL(BTN_COLS, 3, 2) +}; + +class SetupButtonGroup : public Window +{ + public: + typedef std::function PageFct; + typedef std::pair PageDef; + typedef std::list PageDefs; + + SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, PaddingSize padding, PageDefs pages); + + protected: +}; + +struct SetupLineDef { + const char* title; + std::function createEdit; +}; + +class SetupLine : public Window +{ + public: + SetupLine(Window* parent, const rect_t& rect, const char* title, std::function createEdit, coord_t col2); + + protected: }; From e9165eb3c24b9b457cef78095e431158453803a6 Mon Sep 17 00:00:00 2001 From: philmoz Date: Sat, 18 May 2024 07:58:30 +1000 Subject: [PATCH 05/21] Fix portrait layout. --- radio/src/gui/colorlcd/radio_hardware.cpp | 7 +++++++ radio/src/gui/colorlcd/radio_hardware.h | 2 +- radio/src/gui/colorlcd/radio_setup.cpp | 15 ++++++++------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_hardware.cpp b/radio/src/gui/colorlcd/radio_hardware.cpp index 80d2ad17836..6d49e0478be 100644 --- a/radio/src/gui/colorlcd/radio_hardware.cpp +++ b/radio/src/gui/colorlcd/radio_hardware.cpp @@ -39,10 +39,17 @@ #define SET_DIRTY() storageDirty(EE_GENERAL) +#if PORTRAIT_LCD +static const lv_coord_t col_dsc[] = {LV_GRID_FR(13), LV_GRID_FR(19), + LV_GRID_TEMPLATE_LAST}; +static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, + LV_GRID_TEMPLATE_LAST}; +#else static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(2), LV_GRID_TEMPLATE_LAST}; static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; +#endif RadioHardwarePage::RadioHardwarePage() : PageTab(STR_HARDWARE, ICON_RADIO_HARDWARE, PAD_TINY) diff --git a/radio/src/gui/colorlcd/radio_hardware.h b/radio/src/gui/colorlcd/radio_hardware.h index c1e5249b766..a5b513dd4b6 100644 --- a/radio/src/gui/colorlcd/radio_hardware.h +++ b/radio/src/gui/colorlcd/radio_hardware.h @@ -32,7 +32,7 @@ class RadioHardwarePage : public PageTab static LAYOUT_VAL(NUM_EDIT_W, 80, 80) static LAYOUT_VAL(BTN_COLS, 4, 3) - static LAYOUT_VAL(EDT_X, 160, 160) + static LAYOUT_VAL(EDT_X, 160, 131) protected: void cleanup() override; diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index 08d0fe4a72a..11f392f4096 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -191,7 +191,7 @@ SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const cha { padAll(padding); - coord_t buttonWidth = (LCD_W - PAD_MEDIUM * (cols + 1) - PAD_TINY * 2) / cols; + coord_t buttonWidth = (width() - PAD_SMALL * (cols + 1) - PAD_TINY * 2) / cols; int rows = (pages.size() + cols - 1) / cols; int height = rows * EdgeTxStyles::UI_ELEMENT_HEIGHT + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2; @@ -205,15 +205,15 @@ SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const cha int n = 0; int remaining = pages.size(); - coord_t xo = 0; coord_t yo = title ? EdgeTxStyles::PAGE_LINE_HEIGHT + PAD_TINY : 0; - coord_t xw = buttonWidth + PAD_MEDIUM; + coord_t xw = buttonWidth + PAD_SMALL; + coord_t xo = (width() - (cols * xw - PAD_SMALL)) / 2; coord_t x, y; for (auto& entry : pages) { if (remaining < cols && (n % cols == 0)) { coord_t space = ((cols - remaining) * xw) / (remaining + 1); xw += space; - xo = space; + xo += space; } x = xo + (n % cols) * xw; y = yo + (n / cols) * (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM); @@ -782,16 +782,17 @@ SetupLine::SetupLine(Window* parent, const rect_t& rect, const char* title, std: coord_t titleH = EdgeTxStyles::PAGE_LINE_HEIGHT; if (createEdit) { coord_t editY = PAD_TINY; + coord_t lblWidth = col2 - PAD_SMALL - PAD_TINY; if (title) { - if (getTextWidth(title) >= RadioSetupPage::LBL_W) { + if (getTextWidth(title) >= lblWidth) { setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2 + PAD_MEDIUM); titleY = 0; - titleH = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY; + titleH = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY + 1; editY = PAD_SMALL + 1; } else { setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); } - new StaticText(this, {PAD_TINY, titleY, RadioSetupPage::LBL_W, titleH}, title); + new StaticText(this, {PAD_TINY, titleY, lblWidth, titleH}, title); } createEdit(this, col2, editY); } else { From e1665b8d344232290824c3f1d9104eb3a7814a24 Mon Sep 17 00:00:00 2001 From: philmoz Date: Sat, 18 May 2024 08:39:04 +1000 Subject: [PATCH 06/21] Move common classes to window.h / window.cpp. --- radio/src/gui/colorlcd/radio_setup.cpp | 72 ------------------ radio/src/gui/colorlcd/radio_setup.h | 25 ------- radio/src/thirdparty/libopenui/src/window.cpp | 73 +++++++++++++++++++ radio/src/thirdparty/libopenui/src/window.h | 25 +++++++ 4 files changed, 98 insertions(+), 97 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index 11f392f4096..b1f0bc82c7c 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -186,51 +186,6 @@ class DateTimeWindow : public Window } }; -SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, PaddingSize padding, PageDefs pages) : - Window(parent, rect) -{ - padAll(padding); - - coord_t buttonWidth = (width() - PAD_SMALL * (cols + 1) - PAD_TINY * 2) / cols; - - int rows = (pages.size() + cols - 1) / cols; - int height = rows * EdgeTxStyles::UI_ELEMENT_HEIGHT + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2; - if (title) { - height += EdgeTxStyles::PAGE_LINE_HEIGHT + PAD_TINY; - } - setHeight(height); - - if (title) - new Subtitle(this, title); - - int n = 0; - int remaining = pages.size(); - coord_t yo = title ? EdgeTxStyles::PAGE_LINE_HEIGHT + PAD_TINY : 0; - coord_t xw = buttonWidth + PAD_SMALL; - coord_t xo = (width() - (cols * xw - PAD_SMALL)) / 2; - coord_t x, y; - for (auto& entry : pages) { - if (remaining < cols && (n % cols == 0)) { - coord_t space = ((cols - remaining) * xw) / (remaining + 1); - xw += space; - xo += space; - } - x = xo + (n % cols) * xw; - y = yo + (n / cols) * (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM); - - // TODO: sort out all caps title strings VS quick menu strings - std::string title(entry.first); - std::replace(title.begin(), title.end(), '\n', ' '); - - new TextButton(this, rect_t{x, y, buttonWidth, EdgeTxStyles::UI_ELEMENT_HEIGHT}, title, [&, entry]() { - entry.second(); - return 0; - }); - n += 1; - remaining -= 1; - } -} - class SubPage : public Page { public: @@ -774,33 +729,6 @@ class ManageModelsSetupPage : public SubPage Window* favSelectMatch = nullptr; }; -SetupLine::SetupLine(Window* parent, const rect_t& rect, const char* title, std::function createEdit, coord_t col2) : - Window(parent, rect) -{ - padAll(PAD_ZERO); - coord_t titleY = PAD_MEDIUM + 1; - coord_t titleH = EdgeTxStyles::PAGE_LINE_HEIGHT; - if (createEdit) { - coord_t editY = PAD_TINY; - coord_t lblWidth = col2 - PAD_SMALL - PAD_TINY; - if (title) { - if (getTextWidth(title) >= lblWidth) { - setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2 + PAD_MEDIUM); - titleY = 0; - titleH = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY + 1; - editY = PAD_SMALL + 1; - } else { - setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); - } - new StaticText(this, {PAD_TINY, titleY, lblWidth, titleH}, title); - } - createEdit(this, col2, editY); - } else { - new StaticText(this, {0, titleY, 0, titleH}, title, COLOR_THEME_PRIMARY1 | FONT(BOLD)); - setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); - } -} - static SetupLineDef setupLines[] = { { // Splash screen diff --git a/radio/src/gui/colorlcd/radio_setup.h b/radio/src/gui/colorlcd/radio_setup.h index 9b9feb750ab..e02b4a0979a 100644 --- a/radio/src/gui/colorlcd/radio_setup.h +++ b/radio/src/gui/colorlcd/radio_setup.h @@ -34,28 +34,3 @@ class RadioSetupPage: public PageTab { static constexpr coord_t LBL_W = EDT_X - PAD_TINY - PAD_SMALL; static LAYOUT_VAL(BTN_COLS, 3, 2) }; - -class SetupButtonGroup : public Window -{ - public: - typedef std::function PageFct; - typedef std::pair PageDef; - typedef std::list PageDefs; - - SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, PaddingSize padding, PageDefs pages); - - protected: -}; - -struct SetupLineDef { - const char* title; - std::function createEdit; -}; - -class SetupLine : public Window -{ - public: - SetupLine(Window* parent, const rect_t& rect, const char* title, std::function createEdit, coord_t col2); - - protected: -}; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index af7f9cae5ec..dd113150e98 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -20,6 +20,7 @@ #include "button.h" #include "form.h" +#include "static.h" #include "themes/etx_lv_theme.h" std::list Window::trash; @@ -433,3 +434,75 @@ NavWindow::NavWindow(Window *parent, const rect_t &rect, { setWindowFlag(OPAQUE); } + +SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, PaddingSize padding, PageDefs pages) : + Window(parent, rect) +{ + padAll(padding); + + coord_t buttonWidth = (width() - PAD_SMALL * (cols + 1) - PAD_TINY * 2) / cols; + + int rows = (pages.size() + cols - 1) / cols; + int height = rows * EdgeTxStyles::UI_ELEMENT_HEIGHT + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2; + if (title) { + height += EdgeTxStyles::PAGE_LINE_HEIGHT + PAD_TINY; + } + setHeight(height); + + if (title) + new Subtitle(this, title); + + int n = 0; + int remaining = pages.size(); + coord_t yo = title ? EdgeTxStyles::PAGE_LINE_HEIGHT + PAD_TINY : 0; + coord_t xw = buttonWidth + PAD_SMALL; + coord_t xo = (width() - (cols * xw - PAD_SMALL)) / 2; + coord_t x, y; + for (auto& entry : pages) { + if (remaining < cols && (n % cols == 0)) { + coord_t space = ((cols - remaining) * xw) / (remaining + 1); + xw += space; + xo += space; + } + x = xo + (n % cols) * xw; + y = yo + (n / cols) * (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM); + + // TODO: sort out all caps title strings VS quick menu strings + std::string title(entry.first); + std::replace(title.begin(), title.end(), '\n', ' '); + + new TextButton(this, rect_t{x, y, buttonWidth, EdgeTxStyles::UI_ELEMENT_HEIGHT}, title, [&, entry]() { + entry.second(); + return 0; + }); + n += 1; + remaining -= 1; + } +} + +SetupLine::SetupLine(Window* parent, const rect_t& rect, const char* title, std::function createEdit, coord_t col2) : + Window(parent, rect) +{ + padAll(PAD_ZERO); + coord_t titleY = PAD_MEDIUM + 1; + coord_t titleH = EdgeTxStyles::PAGE_LINE_HEIGHT; + if (createEdit) { + coord_t editY = PAD_TINY; + coord_t lblWidth = col2 - PAD_SMALL - PAD_TINY; + if (title) { + if (getTextWidth(title) >= lblWidth) { + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2 + PAD_MEDIUM); + titleY = 0; + titleH = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY + 1; + editY = PAD_SMALL + 1; + } else { + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); + } + new StaticText(this, {PAD_TINY, titleY, lblWidth, titleH}, title); + } + createEdit(this, col2, editY); + } else { + new StaticText(this, {0, titleY, 0, titleH}, title, COLOR_THEME_PRIMARY1 | FONT(BOLD)); + setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); + } +} diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index babe68a63ac..839c64592a2 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -229,3 +229,28 @@ class NavWindow : public Window virtual bool bubbleEvents() { return true; } void onEvent(event_t event) override; }; + +class SetupButtonGroup : public Window +{ + public: + typedef std::function PageFct; + typedef std::pair PageDef; + typedef std::list PageDefs; + + SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, PaddingSize padding, PageDefs pages); + + protected: +}; + +struct SetupLineDef { + const char* title; + std::function createEdit; +}; + +class SetupLine : public Window +{ + public: + SetupLine(Window* parent, const rect_t& rect, const char* title, std::function createEdit, coord_t col2); + + protected: +}; From 186d531dd991df9c5e61232d66a4c5e5f2869229 Mon Sep 17 00:00:00 2001 From: philmoz Date: Sat, 18 May 2024 09:22:53 +1000 Subject: [PATCH 07/21] Fix Github build. --- radio/src/thirdparty/libopenui/src/window.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index dd113150e98..95ef356378b 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -469,7 +469,10 @@ SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const cha // TODO: sort out all caps title strings VS quick menu strings std::string title(entry.first); - std::replace(title.begin(), title.end(), '\n', ' '); + for (std::string::iterator it = title.begin(); it != title.end(); ++it) { + if (*it == '\n') + *it = ' '; + } new TextButton(this, rect_t{x, y, buttonWidth, EdgeTxStyles::UI_ELEMENT_HEIGHT}, title, [&, entry]() { entry.second(); From 681cb8cc8f8a1a0c9a0f76436dd9e825b51b1f83 Mon Sep 17 00:00:00 2001 From: philmoz Date: Mon, 20 May 2024 08:24:06 +1000 Subject: [PATCH 08/21] Remove flex layout from Choice button. --- radio/src/thirdparty/libopenui/src/choice.cpp | 18 +++++------------- radio/src/thirdparty/libopenui/src/choice.h | 4 ++-- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index cf9452500e6..6159b7dffb5 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -66,23 +66,15 @@ void choice_changed_cb(lv_event_t* e) ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type) : FormField(parent, rect, 0, choice_create), type(type) { - lv_obj_set_layout(lvobj, LV_LAYOUT_FLEX); - lv_obj_set_flex_flow(lvobj, LV_FLEX_FLOW_ROW); - lv_obj_set_flex_align(lvobj, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, - LV_FLEX_ALIGN_SPACE_AROUND); - - lv_obj_add_event_cb(lvobj, choice_changed_cb, LV_EVENT_VALUE_CHANGED, lvobj); - label = lv_label_create(lvobj); - - lv_group_t* def_group = lv_group_get_default(); - if (def_group) { - lv_group_add_obj(def_group, lvobj); - } - // add the image lv_obj_t* img = lv_img_create(lvobj); lv_img_set_src( img, type == CHOICE_TYPE_DROPOWN ? LV_SYMBOL_DOWN : LV_SYMBOL_DIRECTORY); + lv_obj_set_pos(img, 0, PAD_TINY); + + lv_obj_add_event_cb(lvobj, choice_changed_cb, LV_EVENT_VALUE_CHANGED, lvobj); + label = lv_label_create(lvobj); + lv_obj_set_pos(label, ICON_W, PAD_TINY); } std::string Choice::getLabelText() diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index 179a3673871..b21be834867 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -32,8 +32,6 @@ typedef struct { class Menu; -constexpr int CHOICE_LABEL_MARGIN_RIGHT = 10; - enum ChoiceType { CHOICE_TYPE_DROPOWN, CHOICE_TYPE_FOLDER, @@ -47,6 +45,8 @@ class ChoiceBase : public FormField void setChoiceType(ChoiceType t) { type = t; } + static LAYOUT_VAL(ICON_W, 18, 18) + protected: ChoiceType type; lv_obj_t *label; From 18a9936258291b05c63e3149cd3f1e96b1a96967 Mon Sep 17 00:00:00 2001 From: philmoz Date: Mon, 20 May 2024 09:03:05 +1000 Subject: [PATCH 09/21] Fix mix list and edit layouts. --- radio/src/gui/colorlcd/gvar_numberedit.cpp | 6 ++---- radio/src/gui/colorlcd/list_line_button.h | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/radio/src/gui/colorlcd/gvar_numberedit.cpp b/radio/src/gui/colorlcd/gvar_numberedit.cpp index db4d49d1980..fd4d83a02b6 100644 --- a/radio/src/gui/colorlcd/gvar_numberedit.cpp +++ b/radio/src/gui/colorlcd/gvar_numberedit.cpp @@ -50,7 +50,7 @@ GVarNumberEdit::GVarNumberEdit(Window* parent, const rect_t& rect, int32_t vmin, // GVAR field gvar_field = new Choice( - this, rect_t{}, -MAX_GVARS, MAX_GVARS - 1, + this, {0, 0, NUM_EDIT_W, 0}, -MAX_GVARS, MAX_GVARS - 1, [=]() { uint16_t gvar1 = GV_GET_GV1_VALUE(vmin, vmax); return GV_INDEX_CALC_DELTA(getValue(), gvar1); @@ -64,13 +64,11 @@ GVarNumberEdit::GVarNumberEdit(Window* parent, const rect_t& rect, int32_t vmin, }); gvar_field->setTextHandler( [=](int32_t value) { return getGVarString(value); }); - gvar_field->setWidth(NUM_EDIT_W); num_field = new NumberEdit( - this, rect_t{}, vmin, vmax, [=]() { return getValue() + voffset; }, + this, {0, 0, NUM_EDIT_W, 0}, vmin, vmax, [=]() { return getValue() + voffset; }, nullptr); num_field->setTextFlag(textFlags); - num_field->setWidth(NUM_EDIT_W); num_field->setDefault(vdefault); #if defined(GVARS) diff --git a/radio/src/gui/colorlcd/list_line_button.h b/radio/src/gui/colorlcd/list_line_button.h index e684d1bfca5..fab50457f18 100644 --- a/radio/src/gui/colorlcd/list_line_button.h +++ b/radio/src/gui/colorlcd/list_line_button.h @@ -66,7 +66,7 @@ class InputMixButtonBase : public ListLineButton static LAYOUT_VAL(BTN_W, 389, 229) static constexpr coord_t WGT_X = PAD_TINY; static constexpr coord_t WGT_Y = PAD_TINY; - static LAYOUT_VAL(WGT_W, 42, 42) + static LAYOUT_VAL(WGT_W, 43, 43) static LAYOUT_VAL(WGT_H, 21, 21) static constexpr coord_t SRC_X = WGT_X + WGT_W + PAD_TINY; static constexpr coord_t SRC_Y = WGT_Y; From c474d9bc381cadbeb944ecd2966ac02ed5b6bd41 Mon Sep 17 00:00:00 2001 From: philmoz Date: Mon, 20 May 2024 09:55:59 +1000 Subject: [PATCH 10/21] Remove flex layout from GVarNumberEdit class. --- radio/src/gui/colorlcd/curve_param.cpp | 2 +- radio/src/gui/colorlcd/gvar_numberedit.cpp | 9 +++------ radio/src/gui/colorlcd/gvar_numberedit.h | 3 ++- radio/src/gui/colorlcd/input_edit.cpp | 4 ++-- radio/src/gui/colorlcd/mixer_edit.cpp | 4 ++-- radio/src/gui/colorlcd/output_edit.cpp | 6 +++--- 6 files changed, 13 insertions(+), 15 deletions(-) diff --git a/radio/src/gui/colorlcd/curve_param.cpp b/radio/src/gui/colorlcd/curve_param.cpp index eaa45e9f9ff..340f415c889 100644 --- a/radio/src/gui/colorlcd/curve_param.cpp +++ b/radio/src/gui/colorlcd/curve_param.cpp @@ -67,7 +67,7 @@ CurveParam::CurveParam(Window* parent, const rect_t& rect, CurveRef* ref, // CURVE_REF_DIFF // CURVE_REF_EXPO - value_edit = new GVarNumberEdit(this, rect_t{}, -100, 100, GET_DEFAULT(ref->value), setRefValue); + value_edit = new GVarNumberEdit(this, -100, 100, GET_DEFAULT(ref->value), setRefValue); value_edit->setSuffix("%"); // CURVE_REF_FUNC diff --git a/radio/src/gui/colorlcd/gvar_numberedit.cpp b/radio/src/gui/colorlcd/gvar_numberedit.cpp index fd4d83a02b6..61e7dd4316a 100644 --- a/radio/src/gui/colorlcd/gvar_numberedit.cpp +++ b/radio/src/gui/colorlcd/gvar_numberedit.cpp @@ -31,11 +31,11 @@ void GVarNumberEdit::value_changed(lv_event_t* e) edit->update(); } -GVarNumberEdit::GVarNumberEdit(Window* parent, const rect_t& rect, int32_t vmin, +GVarNumberEdit::GVarNumberEdit(Window* parent, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue, LcdFlags textFlags, int32_t voffset, int32_t vdefault) : - Window(parent, rect), + Window(parent, {0, 0, NUM_EDIT_W + GV_BTN_W + PAD_TINY * 3, EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2}), vmin(vmin), vmax(vmax), getValue(getValue), @@ -44,9 +44,6 @@ GVarNumberEdit::GVarNumberEdit(Window* parent, const rect_t& rect, int32_t vmin, voffset(voffset) { padAll(PAD_TINY); - lv_obj_set_flex_flow(lvobj, LV_FLEX_FLOW_ROW_WRAP); - lv_obj_set_style_flex_cross_place(lvobj, LV_FLEX_ALIGN_CENTER, 0); - lv_obj_set_size(lvobj, LV_SIZE_CONTENT, LV_SIZE_CONTENT); // GVAR field gvar_field = new Choice( @@ -74,7 +71,7 @@ GVarNumberEdit::GVarNumberEdit(Window* parent, const rect_t& rect, int32_t vmin, #if defined(GVARS) // The GVAR button if (modelGVEnabled()) { - m_gvBtn = new TextButton(this, rect_t{}, STR_GV, [=]() { + m_gvBtn = new TextButton(this, {NUM_EDIT_W + PAD_TINY, 0, GV_BTN_W, 0}, STR_GV, [=]() { switchGVarMode(); return GV_IS_GV_VALUE(getValue(), vmin, vmax); }); diff --git a/radio/src/gui/colorlcd/gvar_numberedit.h b/radio/src/gui/colorlcd/gvar_numberedit.h index 5cb490ec08a..79a6a8f2aed 100644 --- a/radio/src/gui/colorlcd/gvar_numberedit.h +++ b/radio/src/gui/colorlcd/gvar_numberedit.h @@ -31,7 +31,7 @@ class TextButton; class GVarNumberEdit : public Window { public: - GVarNumberEdit(Window* parent, const rect_t& rect, int32_t vmin, int32_t vmax, + GVarNumberEdit(Window* parent, int32_t vmin, int32_t vmax, std::function getValue, std::function setValue, LcdFlags textFlags = 0, int32_t voffset = 0, int32_t vdefault = 0); @@ -43,6 +43,7 @@ class GVarNumberEdit : public Window void setAccelFactor(int value) { num_field->setAccelFactor(value); } static LAYOUT_VAL(NUM_EDIT_W, 70, 70) + static LAYOUT_VAL(GV_BTN_W, 40, 40) protected: Choice* gvar_field = nullptr; diff --git a/radio/src/gui/colorlcd/input_edit.cpp b/radio/src/gui/colorlcd/input_edit.cpp index 44969b22e73..c407b4202aa 100644 --- a/radio/src/gui/colorlcd/input_edit.cpp +++ b/radio/src/gui/colorlcd/input_edit.cpp @@ -106,7 +106,7 @@ void InputEditWindow::buildBody(Window* form) line = form->newLine(grid); new StaticText(line, rect_t{}, STR_WEIGHT); auto gvar = - new GVarNumberEdit(line, rect_t{}, -100, 100, GET_DEFAULT(input->weight), + new GVarNumberEdit(line, -100, 100, GET_DEFAULT(input->weight), [=](int32_t newValue) { input->weight = newValue; preview->update(); @@ -117,7 +117,7 @@ void InputEditWindow::buildBody(Window* form) // Offset line = form->newLine(grid); new StaticText(line, rect_t{}, STR_OFFSET); - gvar = new GVarNumberEdit(line, rect_t{}, -100, 100, + gvar = new GVarNumberEdit(line, -100, 100, GET_DEFAULT(input->offset), [=](int32_t newValue) { input->offset = newValue; preview->update(); diff --git a/radio/src/gui/colorlcd/mixer_edit.cpp b/radio/src/gui/colorlcd/mixer_edit.cpp index 28236852d84..a64a26c11e5 100644 --- a/radio/src/gui/colorlcd/mixer_edit.cpp +++ b/radio/src/gui/colorlcd/mixer_edit.cpp @@ -108,13 +108,13 @@ void MixEditWindow::buildBody(Window *form) // Weight line = form->newLine(grid); new StaticText(line, rect_t{}, STR_WEIGHT); - auto gvar = new GVarNumberEdit(line, rect_t{}, MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, + auto gvar = new GVarNumberEdit(line, MIX_WEIGHT_MIN, MIX_WEIGHT_MAX, GET_SET_DEFAULT(mix->weight)); gvar->setSuffix("%"); // Offset new StaticText(line, rect_t{}, STR_OFFSET); - gvar = new GVarNumberEdit(line, rect_t{}, MIX_OFFSET_MIN, MIX_OFFSET_MAX, + gvar = new GVarNumberEdit(line, MIX_OFFSET_MIN, MIX_OFFSET_MAX, GET_SET_DEFAULT(mix->offset)); gvar->setSuffix("%"); diff --git a/radio/src/gui/colorlcd/output_edit.cpp b/radio/src/gui/colorlcd/output_edit.cpp index 67a3fb2df8e..b41a9761462 100644 --- a/radio/src/gui/colorlcd/output_edit.cpp +++ b/radio/src/gui/colorlcd/output_edit.cpp @@ -136,7 +136,7 @@ void OutputEditWindow::buildBody(Window *form) // Offset new StaticText(line, rect_t{}, TR_LIMITS_HEADERS_SUBTRIM); - auto off = new GVarNumberEdit(line, rect_t{}, -LIMIT_STD_MAX, +LIMIT_STD_MAX, + auto off = new GVarNumberEdit(line, -LIMIT_STD_MAX, +LIMIT_STD_MAX, GET_SET_DEFAULT(output->offset), PREC1); off->setFastStep(20); off->setAccelFactor(8); @@ -146,7 +146,7 @@ void OutputEditWindow::buildBody(Window *form) minText = new StaticText(line, rect_t{}, TR_MIN); etx_solid_bg(minText->getLvObj(), COLOR_THEME_ACTIVE_INDEX, ETX_STATE_MINMAX_HIGHLIGHT); etx_font(minText->getLvObj(), FONT_BOLD_INDEX, ETX_STATE_MINMAX_HIGHLIGHT); - minEdit = new GVarNumberEdit(line, rect_t{}, -limit, 0, + minEdit = new GVarNumberEdit(line, -limit, 0, GET_SET_DEFAULT(output->min), PREC1, -LIMIT_STD_MAX, -limit); etx_font(minEdit->getLvObj(), FONT_BOLD_INDEX, ETX_STATE_MINMAX_HIGHLIGHT); @@ -157,7 +157,7 @@ void OutputEditWindow::buildBody(Window *form) maxText = new StaticText(line, rect_t{}, TR_MAX); etx_solid_bg(maxText->getLvObj(), COLOR_THEME_ACTIVE_INDEX, ETX_STATE_MINMAX_HIGHLIGHT); etx_font(maxText->getLvObj(), FONT_BOLD_INDEX, ETX_STATE_MINMAX_HIGHLIGHT); - maxEdit = new GVarNumberEdit(line, rect_t{}, 0, +limit, + maxEdit = new GVarNumberEdit(line, 0, +limit, GET_SET_DEFAULT(output->max), PREC1, +LIMIT_STD_MAX, limit); etx_font(maxEdit->getLvObj(), FONT_BOLD_INDEX, ETX_STATE_MINMAX_HIGHLIGHT); From e6721e38d8f5d99ef6bc2cad2d03c85b058f40f4 Mon Sep 17 00:00:00 2001 From: philmoz Date: Tue, 21 May 2024 09:13:07 +1000 Subject: [PATCH 11/21] Simplify radio setup page. --- radio/src/gui/colorlcd/radio_hardware.cpp | 2 +- radio/src/gui/colorlcd/radio_setup.cpp | 814 +++++++++--------- radio/src/thirdparty/libopenui/src/window.cpp | 4 +- radio/src/thirdparty/libopenui/src/window.h | 2 +- 4 files changed, 423 insertions(+), 399 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_hardware.cpp b/radio/src/gui/colorlcd/radio_hardware.cpp index 6d49e0478be..97f39d11fdb 100644 --- a/radio/src/gui/colorlcd/radio_hardware.cpp +++ b/radio/src/gui/colorlcd/radio_hardware.cpp @@ -162,7 +162,7 @@ void RadioHardwarePage::build(Window* window) // TODO: sub-title? for (size_t i = 0; i < DIM(setupLines); i += 1) { - new SetupLine(window, {0, 0, LCD_W - padding * 2, 0}, setupLines[i].title, setupLines[i].createEdit, EDT_X); + new SetupLine(window, 0, EDT_X, padding, setupLines[i].title, setupLines[i].createEdit); } FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY); diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index b1f0bc82c7c..15cc7bcc087 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -189,220 +189,240 @@ class DateTimeWindow : public Window class SubPage : public Page { public: - SubPage(EdgeTxIcon icon, const char* title) : Page(icon) + SubPage(EdgeTxIcon icon, const char* title) : Page(icon, PAD_SMALL) { header->setTitle(STR_RADIO_SETUP); header->setTitle2(title); - - body->setFlexLayout(); } -}; -class SoundPage : public SubPage -{ - public: - SoundPage() : SubPage(ICON_RADIO_SETUP, STR_SOUND_LABEL) + SubPage(EdgeTxIcon icon, const char* title, SetupLineDef* setupLines, int lineCount) : Page(icon, PAD_SMALL) { - FlexGridLayout grid(col_two_dsc, row_dsc, PAD_TINY); + header->setTitle(STR_RADIO_SETUP); + header->setTitle2(title); - auto line = body->newLine(grid); + coord_t y = 0; + Window* w; - // Beeps mode - new StaticText(line, rect_t{}, STR_SPEAKER); - new Choice(line, rect_t{}, STR_VBEEPMODE, -2, 1, - GET_SET_DEFAULT(g_eeGeneral.beepMode)); - line = body->newLine(grid); + for (size_t i = 0; i < lineCount; i += 1) { + w = new SetupLine(body, y, RadioSetupPage::EDT_X, PAD_SMALL, setupLines[i].title, setupLines[i].createEdit); + y += w->height() + PAD_TINY; + } + } +}; +static SetupLineDef soundPageSetupLines[] = { + { + // Beeps mode + STR_SPEAKER, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_VBEEPMODE, -2, 1, + GET_SET_DEFAULT(g_eeGeneral.beepMode)); + } + }, + { // Main volume - new StaticText(line, rect_t{}, STR_VOLUME); - new Slider(line, lv_pct(50), -VOLUME_LEVEL_DEF, - VOLUME_LEVEL_MAX - VOLUME_LEVEL_DEF, - GET_SET_DEFAULT(g_eeGeneral.speakerVolume)); - line = body->newLine(grid); - + STR_VOLUME, + [](Window* parent, coord_t x, coord_t y) { + (new Slider(parent, lv_pct(50), -VOLUME_LEVEL_DEF, + VOLUME_LEVEL_MAX - VOLUME_LEVEL_DEF, + GET_SET_DEFAULT(g_eeGeneral.speakerVolume)))->setPos(x, y); + } + }, + { // Beeps volume - new StaticText(line, rect_t{}, STR_BEEP_VOLUME); - new Slider(line, lv_pct(50), -2, +2, - GET_SET_DEFAULT(g_eeGeneral.beepVolume)); - line = body->newLine(grid); - + STR_BEEP_VOLUME, + [](Window* parent, coord_t x, coord_t y) { + (new Slider(parent, lv_pct(50), -2, +2, + GET_SET_DEFAULT(g_eeGeneral.beepVolume)))->setPos(x, y); + } + }, + { // Beeps length - new StaticText(line, rect_t{}, STR_BEEP_LENGTH); - new Slider(line, lv_pct(50), -2, +2, - GET_SET_DEFAULT(g_eeGeneral.beepLength)); - line = body->newLine(grid); - + STR_BEEP_LENGTH, + [](Window* parent, coord_t x, coord_t y) { + (new Slider(parent, lv_pct(50), -2, +2, + GET_SET_DEFAULT(g_eeGeneral.beepLength)))->setPos(x, y); + } + }, + { // Beeps pitch - new StaticText(line, rect_t{}, STR_BEEP_PITCH); - auto edit = new NumberEdit(line, rect_t{}, 0, 300, - GET_DEFAULT(15 * g_eeGeneral.speakerPitch), - [=](int32_t newValue) { - g_eeGeneral.speakerPitch = newValue / 15; - SET_DIRTY(); - }); - edit->setStep(15); - edit->setPrefix("+"); - edit->setSuffix("Hz"); - line = body->newLine(grid); - + STR_BEEP_PITCH, + [](Window* parent, coord_t x, coord_t y) { + auto edit = new NumberEdit(parent, rect_t{x, y, RadioSetupPage::NUM_W, 0}, 0, 300, + GET_DEFAULT(15 * g_eeGeneral.speakerPitch), + [=](int32_t newValue) { + g_eeGeneral.speakerPitch = newValue / 15; + SET_DIRTY(); + }); + edit->setStep(15); + edit->setPrefix("+"); + edit->setSuffix("Hz"); + } + }, + { // Wav volume - new StaticText(line, rect_t{}, STR_WAV_VOLUME); - new Slider(line, lv_pct(50), -2, +2, - GET_SET_DEFAULT(g_eeGeneral.wavVolume)); - line = body->newLine(grid); - + STR_WAV_VOLUME, + [](Window* parent, coord_t x, coord_t y) { + (new Slider(parent, lv_pct(50), -2, +2, + GET_SET_DEFAULT(g_eeGeneral.wavVolume)))->setPos(x, y); + } + }, + { // Background volume - new StaticText(line, rect_t{}, STR_BG_VOLUME); - new Slider(line, lv_pct(50), -2, +2, - GET_SET_DEFAULT(g_eeGeneral.backgroundVolume)); - } + STR_BG_VOLUME, + [](Window* parent, coord_t x, coord_t y) { + (new Slider(parent, lv_pct(50), -2, +2, + GET_SET_DEFAULT(g_eeGeneral.backgroundVolume)))->setPos(x, y); + } + }, }; #if defined(VARIO) -class VarioPage : public SubPage -{ - public: - VarioPage() : SubPage(ICON_RADIO_SETUP, STR_VARIO) +static SetupLineDef varioPageSetupLines[] = { { - FlexGridLayout grid(col_two_dsc, row_dsc); - - auto line = body->newLine(grid); - // Vario volume - new StaticText(line, rect_t{}, STR_VOLUME); - new Slider(line, lv_pct(50), -2, +2, - GET_SET_DEFAULT(g_eeGeneral.varioVolume)); - line = body->newLine(grid); - - new StaticText(line, rect_t{}, STR_PITCH_AT_ZERO); - auto edit = new NumberEdit( - line, rect_t{}, VARIO_FREQUENCY_ZERO - 400, VARIO_FREQUENCY_ZERO + 400, - GET_DEFAULT(VARIO_FREQUENCY_ZERO + (g_eeGeneral.varioPitch * 10)), - SET_VALUE(g_eeGeneral.varioPitch, - (newValue - VARIO_FREQUENCY_ZERO) / 10)); - edit->setStep(10); - edit->setSuffix("Hz"); - line = body->newLine(grid); - - new StaticText(line, rect_t{}, STR_PITCH_AT_MAX); - edit = new NumberEdit( - line, rect_t{}, 900, 2500, - GET_DEFAULT(VARIO_FREQUENCY_ZERO + (g_eeGeneral.varioPitch * 10) + - VARIO_FREQUENCY_RANGE + (g_eeGeneral.varioRange * 10)), - SET_VALUE( - g_eeGeneral.varioRange, - (newValue - VARIO_FREQUENCY_ZERO - VARIO_FREQUENCY_RANGE) / 10 - - g_eeGeneral.varioPitch)); - edit->setStep(10); - edit->setSuffix("Hz"); - line = body->newLine(grid); - - new StaticText(line, rect_t{}, STR_REPEAT_AT_ZERO); - edit = new NumberEdit( - line, rect_t{}, 200, 1000, - GET_DEFAULT(VARIO_REPEAT_ZERO + (g_eeGeneral.varioRepeat * 10)), - SET_VALUE(g_eeGeneral.varioRepeat, - (newValue - VARIO_REPEAT_ZERO) / 10)); - edit->setStep(10); - edit->setSuffix("ms"); - line = body->newLine(grid); - } + STR_VOLUME, + [](Window* parent, coord_t x, coord_t y) { + (new Slider(parent, lv_pct(50), -2, +2, + GET_SET_DEFAULT(g_eeGeneral.varioVolume)))->setPos(x, y); + } + }, + { + STR_PITCH_AT_ZERO, + [](Window* parent, coord_t x, coord_t y) { + auto edit = new NumberEdit( + parent, {x, y, RadioSetupPage::NUM_W, 0}, VARIO_FREQUENCY_ZERO - 400, VARIO_FREQUENCY_ZERO + 400, + GET_DEFAULT(VARIO_FREQUENCY_ZERO + (g_eeGeneral.varioPitch * 10)), + SET_VALUE(g_eeGeneral.varioPitch, + (newValue - VARIO_FREQUENCY_ZERO) / 10)); + edit->setStep(10); + edit->setSuffix("Hz"); + } + }, + { + STR_PITCH_AT_MAX, + [](Window* parent, coord_t x, coord_t y) { + auto edit = new NumberEdit( + parent, {x, y, RadioSetupPage::NUM_W, 0}, 900, 2500, + GET_DEFAULT(VARIO_FREQUENCY_ZERO + (g_eeGeneral.varioPitch * 10) + + VARIO_FREQUENCY_RANGE + (g_eeGeneral.varioRange * 10)), + SET_VALUE( + g_eeGeneral.varioRange, + (newValue - VARIO_FREQUENCY_ZERO - VARIO_FREQUENCY_RANGE) / 10 - + g_eeGeneral.varioPitch)); + edit->setStep(10); + edit->setSuffix("Hz"); + } + }, + { + STR_REPEAT_AT_ZERO, + [](Window* parent, coord_t x, coord_t y) { + auto edit = new NumberEdit( + parent, {x, y, RadioSetupPage::NUM_W, 0}, 200, 1000, + GET_DEFAULT(VARIO_REPEAT_ZERO + (g_eeGeneral.varioRepeat * 10)), + SET_VALUE(g_eeGeneral.varioRepeat, + (newValue - VARIO_REPEAT_ZERO) / 10)); + edit->setStep(10); + edit->setSuffix("ms"); + } + }, }; #endif #if defined(HAPTIC) -class HapticPage : public SubPage -{ - public: - HapticPage() : SubPage(ICON_RADIO_SETUP, STR_HAPTIC_LABEL) +static SetupLineDef hapticPageSetupLines[] = { { - FlexGridLayout grid(col_two_dsc, row_dsc); - - auto line = body->newLine(grid); - // Haptic mode - new StaticText(line, rect_t{}, STR_MODE); - new Choice(line, rect_t{}, STR_VBEEPMODE, -2, 1, - GET_SET_DEFAULT(g_eeGeneral.hapticMode)); - line = body->newLine(grid); - + STR_MODE, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_VBEEPMODE, -2, 1, + GET_SET_DEFAULT(g_eeGeneral.hapticMode)); + } + }, + { // Haptic duration - new StaticText(line, rect_t{}, STR_LENGTH); - new Slider(line, lv_pct(50), -2, +2, - GET_SET_DEFAULT(g_eeGeneral.hapticLength)); - line = body->newLine(grid); - + STR_LENGTH, + [](Window* parent, coord_t x, coord_t y) { + (new Slider(parent, lv_pct(50), -2, +2, + GET_SET_DEFAULT(g_eeGeneral.hapticLength)))->setPos(x, y); + } + }, + { // Haptic strength - new StaticText(line, rect_t{}, STR_STRENGTH); - new Slider(line, lv_pct(50), -2, +2, - GET_SET_DEFAULT(g_eeGeneral.hapticStrength)); - line = body->newLine(grid); - } + STR_STRENGTH, + [](Window* parent, coord_t x, coord_t y) { + (new Slider(parent, lv_pct(50), -2, +2, + GET_SET_DEFAULT(g_eeGeneral.hapticStrength)))->setPos(x, y); + } + }, }; #endif -class AlarmsPage : public SubPage -{ - public: - AlarmsPage() : SubPage(ICON_RADIO_SETUP, STR_ALARMS_LABEL) +static SetupLineDef alarmsPageSetupLines[] = { { - FlexGridLayout grid(col_two_dsc, row_dsc); - - auto line = body->newLine(grid); // Battery warning - new StaticText(line, rect_t{}, STR_BATTERYWARNING); - auto edit = new NumberEdit(line, rect_t{}, 30, 120, - GET_SET_DEFAULT(g_eeGeneral.vBatWarn), PREC1); - edit->setSuffix("V"); - line = body->newLine(grid); - + STR_BATTERYWARNING, + [](Window* parent, coord_t x, coord_t y) { + auto edit = new NumberEdit(parent, {x, y, RadioSetupPage::NUM_W, 0}, 30, 120, + GET_SET_DEFAULT(g_eeGeneral.vBatWarn), PREC1); + edit->setSuffix("V"); + } + }, + { // Inactivity alarm - new StaticText(line, rect_t{}, STR_INACTIVITYALARM); - edit = new NumberEdit(line, rect_t{}, 0, 250, - GET_SET_DEFAULT(g_eeGeneral.inactivityTimer)); - - edit->setDisplayHandler([=](int value) -> std::string { - std::string suffix(STR_MINUTE_PLURAL2); - if (value == 1) { - suffix = std::string(STR_MINUTE_SINGULAR); - } else if (value < g_use_plural2) { - const int secondDecimal = (value / 10) % 10; - if (secondDecimal != 1) { - const int firstDecimal = value % 10; - if (firstDecimal) { - if (firstDecimal < g_min_plural2 && - firstDecimal == g_use_singular_in_plural) { - suffix = std::string(STR_MINUTE_SINGULAR); - } else if (firstDecimal <= g_max_plural2 && - firstDecimal != g_use_plural2_special_case) { - suffix = std::string(STR_MINUTE_PLURAL1); + STR_INACTIVITYALARM, + [](Window* parent, coord_t x, coord_t y) { + auto edit = new NumberEdit(parent, {x, y, RadioSetupPage::NUM_W * 3 / 2, 0}, 0, 250, + GET_SET_DEFAULT(g_eeGeneral.inactivityTimer)); + + edit->setDisplayHandler([=](int value) -> std::string { + std::string suffix(STR_MINUTE_PLURAL2); + if (value == 1) { + suffix = std::string(STR_MINUTE_SINGULAR); + } else if (value < g_use_plural2) { + const int secondDecimal = (value / 10) % 10; + if (secondDecimal != 1) { + const int firstDecimal = value % 10; + if (firstDecimal) { + if (firstDecimal < g_min_plural2 && + firstDecimal == g_use_singular_in_plural) { + suffix = std::string(STR_MINUTE_SINGULAR); + } else if (firstDecimal <= g_max_plural2 && + firstDecimal != g_use_plural2_special_case) { + suffix = std::string(STR_MINUTE_PLURAL1); + } } } } - } - suffix = " " + suffix; - return formatNumberAsString(value, 0, 0, nullptr, suffix.c_str()); - }); - line = body->newLine(grid); - + suffix = " " + suffix; + return formatNumberAsString(value, 0, 0, nullptr, suffix.c_str()); + }); + } + }, + { // Alarms warning - new StaticText(line, rect_t{}, STR_ALARMWARNING); - new ToggleSwitch(line, rect_t{}, - GET_SET_INVERTED(g_eeGeneral.disableAlarmWarning)); - line = body->newLine(grid); - + STR_ALARMWARNING, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_SET_INVERTED(g_eeGeneral.disableAlarmWarning)); + } + }, + { // RSSI shutdown alarm - new StaticText(line, rect_t{}, STR_RSSI_SHUTDOWN_ALARM); - new ToggleSwitch(line, rect_t{}, - GET_SET_INVERTED(g_eeGeneral.disableRssiPoweroffAlarm)); - line = body->newLine(grid); - + STR_RSSI_SHUTDOWN_ALARM, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_SET_INVERTED(g_eeGeneral.disableRssiPoweroffAlarm)); + } + }, + { // Trainer shutdown alarm - new StaticText(line, rect_t{}, STR_TRAINER_SHUTDOWN_ALARM); - new ToggleSwitch(line, rect_t{}, - GET_SET_INVERTED(g_eeGeneral.disableTrainerPoweroffAlarm)); - line = body->newLine(grid); - } + STR_TRAINER_SHUTDOWN_ALARM, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_SET_INVERTED(g_eeGeneral.disableTrainerPoweroffAlarm)); + } + }, }; class BacklightPage : public SubPage @@ -410,89 +430,84 @@ class BacklightPage : public SubPage public: BacklightPage() : SubPage(ICON_RADIO_SETUP, STR_BACKLIGHT_LABEL) { - FlexGridLayout grid(col_two_dsc, row_dsc, PAD_TINY); - - auto line = body->newLine(grid); + body->setFlexLayout(); // Backlight mode - new StaticText(line, rect_t{}, STR_MODE); - - auto blMode = new Choice( - line, rect_t{}, STR_VBLMODE, e_backlight_mode_off, e_backlight_mode_on, - GET_DEFAULT(g_eeGeneral.backlightMode), [=](int32_t newValue) { - g_eeGeneral.backlightMode = newValue; - updateBacklightControls(); - SET_DIRTY(); + new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_MODE, [=](Window* parent, coord_t x, coord_t y) { + auto blMode = new Choice( + parent, {x, y, 0, 0}, STR_VBLMODE, e_backlight_mode_off, e_backlight_mode_on, + GET_DEFAULT(g_eeGeneral.backlightMode), [=](int32_t newValue) { + g_eeGeneral.backlightMode = newValue; + updateBacklightControls(); + SET_DIRTY(); + }); + + blMode->setAvailableHandler( + [=](int newValue) { return newValue != e_backlight_mode_off; }); }); - blMode->setAvailableHandler( - [=](int newValue) { return newValue != e_backlight_mode_off; }); - - backlightTimeout = body->newLine(grid); - // Delay - new StaticText(backlightTimeout, rect_t{}, STR_BACKLIGHT_TIMER); - auto edit = - new NumberEdit(backlightTimeout, rect_t{}, 5, 600, - GET_DEFAULT(g_eeGeneral.lightAutoOff * 5), - SET_VALUE(g_eeGeneral.lightAutoOff, newValue / 5)); - edit->setStep(5); - edit->setSuffix("s"); - - backlightOnBright = body->newLine(grid); + backlightTimeout = new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_BACKLIGHT_TIMER, [=](Window* parent, coord_t x, coord_t y) { + auto edit = + new NumberEdit(parent, {x, y, RadioSetupPage::NUM_W, 0}, 5, 600, + GET_DEFAULT(g_eeGeneral.lightAutoOff * 5), + SET_VALUE(g_eeGeneral.lightAutoOff, newValue / 5)); + edit->setStep(5); + edit->setSuffix("s"); + }); // Backlight ON bright - new StaticText(backlightOnBright, rect_t{}, STR_BLONBRIGHTNESS); - backlightOnSlider = new Slider( - backlightOnBright, lv_pct(50), BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, - [=]() -> int32_t { - return BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright; - }, - [=](int32_t newValue) { - if (newValue >= g_eeGeneral.blOffBright || - g_eeGeneral.backlightMode == e_backlight_mode_on) { - g_eeGeneral.backlightBright = BACKLIGHT_LEVEL_MAX - newValue; - } else { - g_eeGeneral.backlightBright = - BACKLIGHT_LEVEL_MAX - g_eeGeneral.blOffBright; - backlightOnSlider->update(); - } - SET_DIRTY(); + backlightOnBright = new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_BLONBRIGHTNESS, [=](Window* parent, coord_t x, coord_t y) { + backlightOnSlider = new Slider( + parent, lv_pct(50), BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, + [=]() -> int32_t { + return BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright; + }, + [=](int32_t newValue) { + if (newValue >= g_eeGeneral.blOffBright || + g_eeGeneral.backlightMode == e_backlight_mode_on) { + g_eeGeneral.backlightBright = BACKLIGHT_LEVEL_MAX - newValue; + } else { + g_eeGeneral.backlightBright = + BACKLIGHT_LEVEL_MAX - g_eeGeneral.blOffBright; + backlightOnSlider->update(); + } + SET_DIRTY(); + }); + backlightOnSlider->setPos(x, y); }); - backlightOffBright = body->newLine(grid); - // Backlight OFF bright - new StaticText(backlightOffBright, rect_t{}, STR_BLOFFBRIGHTNESS); - backlightOffSlider = new Slider( - backlightOffBright, lv_pct(50), BACKLIGHT_LEVEL_MIN, - BACKLIGHT_LEVEL_MAX, GET_DEFAULT(g_eeGeneral.blOffBright), - [=](int32_t newValue) { - int32_t onBright = BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright; - if (newValue <= onBright || - g_eeGeneral.backlightMode == e_backlight_mode_off) { - g_eeGeneral.blOffBright = newValue; - } else { - g_eeGeneral.blOffBright = onBright; - backlightOffSlider->update(); - } - SET_DIRTY(); + backlightOffBright = new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_BLOFFBRIGHTNESS, [=](Window* parent, coord_t x, coord_t y) { + backlightOffSlider = new Slider( + parent, lv_pct(50), BACKLIGHT_LEVEL_MIN, + BACKLIGHT_LEVEL_MAX, GET_DEFAULT(g_eeGeneral.blOffBright), + [=](int32_t newValue) { + int32_t onBright = BACKLIGHT_LEVEL_MAX - g_eeGeneral.backlightBright; + if (newValue <= onBright || + g_eeGeneral.backlightMode == e_backlight_mode_off) { + g_eeGeneral.blOffBright = newValue; + } else { + g_eeGeneral.blOffBright = onBright; + backlightOffSlider->update(); + } + SET_DIRTY(); + }); + backlightOffSlider->setPos(x, y); }); - line = body->newLine(grid); - #if defined(KEYS_BACKLIGHT_GPIO) // Keys backlight - new StaticText(line, rect_t{}, STR_KEYS_BACKLIGHT); - new ToggleSwitch(line, rect_t{}, - GET_SET_DEFAULT(g_eeGeneral.keysBacklight)); - line = body->newLine(grid); + new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_KEYS_BACKLIGHT, [=](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_SET_DEFAULT(g_eeGeneral.keysBacklight)); + }); #endif // Flash beep - new StaticText(line, rect_t{}, STR_ALARM); - new ToggleSwitch(line, rect_t{}, GET_SET_DEFAULT(g_eeGeneral.alarmsFlash)); - line = body->newLine(grid); + new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_ALARM, [=](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_eeGeneral.alarmsFlash)); + }); updateBacklightControls(); } @@ -535,140 +550,157 @@ class BacklightPage : public SubPage } }; -class GpsPage : public SubPage -{ - public: - GpsPage() : SubPage(ICON_RADIO_SETUP, STR_GPS) +static SetupLineDef gpsPageSetupLines[] = { { - FlexGridLayout grid(col_two_dsc, row_dsc); - - tzIndex = timezoneIndex(g_eeGeneral.timezone, g_eeGeneral.timezoneMinutes); - - auto line = body->newLine(grid); // Timezone - new StaticText(line, rect_t{}, STR_TIMEZONE); - auto tz = new NumberEdit(line, rect_t{}, minTimezone(), maxTimezone(), - GET_DEFAULT(tzIndex), [=](int newTz) { - tzIndex = newTz; - g_eeGeneral.timezone = timezoneHour(newTz); - g_eeGeneral.timezoneMinutes = - timezoneMinute(newTz); - SET_DIRTY(); - }); - tz->setDisplayHandler([](int32_t tz) { return timezoneDisplay(tz); }); - line = body->newLine(grid); - + STR_TIMEZONE, + [](Window* parent, coord_t x, coord_t y) { + auto tz = new NumberEdit(parent, {x, y, RadioSetupPage::NUM_W, 0}, minTimezone(), maxTimezone(), + []() { + return timezoneIndex(g_eeGeneral.timezone, g_eeGeneral.timezoneMinutes); + }, + [](int newTz) { + g_eeGeneral.timezone = timezoneHour(newTz); + g_eeGeneral.timezoneMinutes = + timezoneMinute(newTz); + SET_DIRTY(); + }); + tz->setDisplayHandler([](int32_t tz) { return timezoneDisplay(tz); }); + } + }, + { // Adjust RTC (from telemetry) - new StaticText(line, rect_t{}, STR_ADJUST_RTC); - new ToggleSwitch(line, rect_t{}, GET_SET_DEFAULT(g_eeGeneral.adjustRTC)); - line = body->newLine(grid); - + STR_ADJUST_RTC, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_eeGeneral.adjustRTC)); + } + }, + { // GPS format - new StaticText(line, rect_t{}, STR_GPS_COORDS_FORMAT); - new Choice(line, rect_t{}, STR_GPSFORMAT, 0, 1, - GET_SET_DEFAULT(g_eeGeneral.gpsFormat)); - line = body->newLine(grid); - } - - protected: - int tzIndex; + STR_GPS_COORDS_FORMAT, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_GPSFORMAT, 0, 1, + GET_SET_DEFAULT(g_eeGeneral.gpsFormat)); + } + }, }; -class ViewOptionsPage : public SubPage +static void viewOption(Window* parent, coord_t x, coord_t y, + std::function getValue, + std::function setValue, uint8_t modelOption) { - private: - const lv_coord_t opt_col_dsc[4] = {LV_GRID_FR(5), LV_GRID_FR(2), - LV_GRID_FR(4), LV_GRID_TEMPLATE_LAST}; - - void viewOption(FormLine* line, const char* name, - std::function getValue, - std::function setValue, uint8_t modelOption) - { - line->padLeft(10); - new StaticText(line, rect_t{}, name); - new ToggleSwitch(line, rect_t{}, getValue, setValue); - if (modelOption != OVERRIDE_GLOBAL) { - std::string s(STR_MODEL); - s += " - "; - s += STR_ADCFILTERVALUES[modelOption]; - new StaticText(line, rect_t{}, s.c_str(), COLOR_THEME_SECONDARY1); - } + new ToggleSwitch(parent, {x, y, 0, 0}, getValue, setValue); + if (modelOption != OVERRIDE_GLOBAL) { + std::string s(STR_MODEL); + s += " - "; + s += STR_ADCFILTERVALUES[modelOption]; + new StaticText(parent, {x + ToggleSwitch::TOGGLE_W + PAD_MEDIUM, y + PAD_SMALL, 0, 0}, s.c_str(), COLOR_THEME_SECONDARY1); } +} - public: - ViewOptionsPage() : SubPage(ICON_RADIO_SETUP, STR_ENABLED_FEATURES) +static SetupLineDef viewOptionsPageSetupLines[] = { + { + STR_RADIO_MENU_TABS, nullptr, + }, + { + STR_THEME_EDITOR, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.radioThemesDisabled), + g_model.radioThemesDisabled); + } + }, + { + STR_MENUSPECIALFUNCS, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.radioGFDisabled), + g_model.radioGFDisabled); + } + }, + { + STR_MENUTRAINER, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.radioTrainerDisabled), + g_model.radioTrainerDisabled); + } + }, + { + STR_MODEL_MENU_TABS, nullptr, + }, { - FlexGridLayout grid(opt_col_dsc, row_dsc, PAD_TINY); - - auto line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_RADIO_MENU_TABS); - - line = body->newLine(grid); - viewOption(line, STR_THEME_EDITOR, - GET_SET_INVERTED(g_eeGeneral.radioThemesDisabled), - g_model.radioThemesDisabled); - - line = body->newLine(grid); - viewOption(line, STR_MENUSPECIALFUNCS, - GET_SET_INVERTED(g_eeGeneral.radioGFDisabled), - g_model.radioGFDisabled); - - line = body->newLine(grid); - viewOption(line, STR_MENUTRAINER, - GET_SET_INVERTED(g_eeGeneral.radioTrainerDisabled), - g_model.radioTrainerDisabled); - - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_MODEL_MENU_TABS); - #if defined(HELI) - line = body->newLine(grid); - viewOption(line, STR_MENUHELISETUP, - GET_SET_INVERTED(g_eeGeneral.modelHeliDisabled), - g_model.modelHeliDisabled); + STR_MENUHELISETUP, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.modelHeliDisabled), + g_model.modelHeliDisabled); + } + }, #endif - #if defined(FLIGHT_MODES) - line = body->newLine(grid); - viewOption(line, STR_MENUFLIGHTMODES, - GET_SET_INVERTED(g_eeGeneral.modelFMDisabled), - g_model.modelFMDisabled); + { + STR_MENUFLIGHTMODES, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.modelFMDisabled), + g_model.modelFMDisabled); + } + }, #endif - #if defined(GVARS) - line = body->newLine(grid); - viewOption(line, STR_MENU_GLOBAL_VARS, - GET_SET_INVERTED(g_eeGeneral.modelGVDisabled), - g_model.modelGVDisabled); + { + STR_MENU_GLOBAL_VARS, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.modelGVDisabled), + g_model.modelGVDisabled); + } + }, #endif - - line = body->newLine(grid); - viewOption(line, STR_MENUCURVES, - GET_SET_INVERTED(g_eeGeneral.modelCurvesDisabled), - g_model.modelCurvesDisabled); - - line = body->newLine(grid); - viewOption(line, STR_MENULOGICALSWITCHES, - GET_SET_INVERTED(g_eeGeneral.modelLSDisabled), - g_model.modelLSDisabled); - - line = body->newLine(grid); - viewOption(line, STR_MENUCUSTOMFUNC, - GET_SET_INVERTED(g_eeGeneral.modelSFDisabled), - g_model.modelSFDisabled); - + { + STR_MENUCURVES, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.modelCurvesDisabled), + g_model.modelCurvesDisabled); + } + }, + { + STR_MENULOGICALSWITCHES, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.modelLSDisabled), + g_model.modelLSDisabled); + } + }, + { + STR_MENUCUSTOMFUNC, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.modelSFDisabled), + g_model.modelSFDisabled); + } + }, #if defined(LUA_MODEL_SCRIPTS) - line = body->newLine(grid); - viewOption(line, STR_MENUCUSTOMSCRIPTS, - GET_SET_INVERTED(g_eeGeneral.modelCustomScriptsDisabled), - g_model.modelCustomScriptsDisabled); + { + STR_MENUCUSTOMSCRIPTS, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.modelCustomScriptsDisabled), + g_model.modelCustomScriptsDisabled); + } + }, #endif - - line = body->newLine(grid); - viewOption(line, STR_MENUTELEMETRY, - GET_SET_INVERTED(g_eeGeneral.modelTelemetryDisabled), - g_model.modelTelemetryDisabled); - } + { + STR_MENUTELEMETRY, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_INVERTED(g_eeGeneral.modelTelemetryDisabled), + g_model.modelTelemetryDisabled); + } + }, }; class ManageModelsSetupPage : public SubPage @@ -676,52 +708,44 @@ class ManageModelsSetupPage : public SubPage public: ManageModelsSetupPage() : SubPage(ICON_MODEL, STR_MANAGE_MODELS) { - FlexGridLayout grid(col_two_dsc, row_dsc, PAD_TINY); + body->setFlexLayout(); // Model quick select - auto line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_MODEL_QUICK_SELECT); - new ToggleSwitch(line, rect_t{}, - GET_SET_DEFAULT(g_eeGeneral.modelQuickSelect)); + new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_MODEL_QUICK_SELECT, [=](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_SET_DEFAULT(g_eeGeneral.modelQuickSelect)); + }); // Label single/multi select - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_LABELS_SELECT); - new Choice(line, rect_t{}, STR_LABELS_SELECT_MODE, 0, 1, - GET_DEFAULT(g_eeGeneral.labelSingleSelect), - [=](int newValue) { - g_eeGeneral.labelSingleSelect = newValue; - modelslabels.clearFilter(); - SET_DIRTY(); - }); + new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_LABELS_SELECT, [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_LABELS_SELECT_MODE, 0, 1, + GET_DEFAULT(g_eeGeneral.labelSingleSelect), + [=](int newValue) { + g_eeGeneral.labelSingleSelect = newValue; + modelslabels.clearFilter(); + SET_DIRTY(); + }); + }); // Label multi select matching mode - multiSelectMatch = body->newLine(grid); - new StaticText(multiSelectMatch, rect_t{}, STR_LABELS_MATCH); - new Choice(multiSelectMatch, rect_t{}, STR_LABELS_MATCH_MODE, 0, 1, - GET_SET_DEFAULT(g_eeGeneral.labelMultiMode)); + multiSelectMatch = new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_LABELS_MATCH, [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_LABELS_MATCH_MODE, 0, 1, + GET_SET_DEFAULT(g_eeGeneral.labelMultiMode)); + }); // Favorites multi select matching mode - favSelectMatch = body->newLine(grid); - new StaticText(favSelectMatch, rect_t{}, STR_FAV_MATCH); - new Choice(favSelectMatch, rect_t{}, STR_FAV_MATCH_MODE, 0, 1, - GET_SET_DEFAULT(g_eeGeneral.favMultiMode)); + favSelectMatch = new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_FAV_MATCH, [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_FAV_MATCH_MODE, 0, 1, + GET_SET_DEFAULT(g_eeGeneral.favMultiMode)); + }); checkEvents(); } void checkEvents() override { - if (g_eeGeneral.labelSingleSelect) { - lv_obj_add_flag(multiSelectMatch->getLvObj(), LV_OBJ_FLAG_HIDDEN); - lv_obj_add_flag(favSelectMatch->getLvObj(), LV_OBJ_FLAG_HIDDEN); - } else { - lv_obj_clear_flag(multiSelectMatch->getLvObj(), LV_OBJ_FLAG_HIDDEN); - if (g_eeGeneral.labelMultiMode == 0) - lv_obj_add_flag(favSelectMatch->getLvObj(), LV_OBJ_FLAG_HIDDEN); - else - lv_obj_clear_flag(favSelectMatch->getLvObj(), LV_OBJ_FLAG_HIDDEN); - } + multiSelectMatch->show(!g_eeGeneral.labelSingleSelect); + favSelectMatch->show(!g_eeGeneral.labelSingleSelect && (g_eeGeneral.labelMultiMode != 0)); } protected: @@ -914,22 +938,22 @@ void RadioSetupPage::build(Window* window) // Sub-pages w = new SetupButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, nullptr, BTN_COLS, PAD_TINY, { - {STR_SOUND_LABEL, []() { new SoundPage(); }}, + {STR_SOUND_LABEL, []() { new SubPage(ICON_RADIO_SETUP, STR_SOUND_LABEL, soundPageSetupLines, DIM(soundPageSetupLines)); }}, #if defined(VARIO) - {STR_VARIO, []() { new VarioPage(); }}, + {STR_VARIO, []() { new SubPage(ICON_RADIO_SETUP, STR_VARIO, varioPageSetupLines, DIM(varioPageSetupLines)); }}, #endif #if defined(HAPTIC) - {STR_HAPTIC_LABEL, []() { new HapticPage(); }}, + {STR_HAPTIC_LABEL, []() { new SubPage(ICON_RADIO_SETUP, STR_HAPTIC_LABEL, hapticPageSetupLines, DIM(hapticPageSetupLines)); }}, #endif - {STR_ALARMS_LABEL, []() { new AlarmsPage(); }}, + {STR_ALARMS_LABEL, []() { new SubPage(ICON_RADIO_SETUP, STR_ALARMS_LABEL, alarmsPageSetupLines, DIM(alarmsPageSetupLines)); }}, {STR_BACKLIGHT_LABEL, []() { new BacklightPage(); }}, - {STR_GPS, []() { new GpsPage(); }}, - {STR_ENABLED_FEATURES, []() { new ViewOptionsPage(); }}, + {STR_GPS, []() { new SubPage(ICON_RADIO_SETUP, STR_GPS, gpsPageSetupLines, DIM(gpsPageSetupLines)); }}, + {STR_ENABLED_FEATURES, []() { new SubPage(ICON_RADIO_SETUP, STR_ENABLED_FEATURES, viewOptionsPageSetupLines, DIM(viewOptionsPageSetupLines)); }}, {STR_MAIN_MENU_MANAGE_MODELS, []() { new ManageModelsSetupPage(); }}, }); for (size_t i = 0; i < DIM(setupLines); i += 1) { y += w->height() + padding; - w = new SetupLine(window, {0, y, LCD_W - padding * 2, 0}, setupLines[i].title, setupLines[i].createEdit, EDT_X); + w = new SetupLine(window, y, EDT_X, padding, setupLines[i].title, setupLines[i].createEdit); } } diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 95ef356378b..a5c8b4dca23 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -483,8 +483,8 @@ SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const cha } } -SetupLine::SetupLine(Window* parent, const rect_t& rect, const char* title, std::function createEdit, coord_t col2) : - Window(parent, rect) +SetupLine::SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const char* title, std::function createEdit) : + Window(parent, {0, y, LCD_W - padding * 2, 0}) { padAll(PAD_ZERO); coord_t titleY = PAD_MEDIUM + 1; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 839c64592a2..301d289431c 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -250,7 +250,7 @@ struct SetupLineDef { class SetupLine : public Window { public: - SetupLine(Window* parent, const rect_t& rect, const char* title, std::function createEdit, coord_t col2); + SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const char* title, std::function createEdit); protected: }; From b95858856881d43873bf791fd759db64228eec5c Mon Sep 17 00:00:00 2001 From: philmoz Date: Tue, 21 May 2024 16:19:06 +1000 Subject: [PATCH 12/21] Simplify model setup page. --- radio/src/gui/colorlcd/model_setup.cpp | 468 +++++++----------- radio/src/gui/colorlcd/model_setup.h | 17 +- radio/src/gui/colorlcd/page.cpp | 14 + radio/src/gui/colorlcd/page.h | 9 + radio/src/gui/colorlcd/radio_hardware.cpp | 5 +- radio/src/gui/colorlcd/radio_setup.cpp | 51 +- radio/src/gui/colorlcd/radio_setup.h | 1 + radio/src/thirdparty/libopenui/src/button.h | 7 + radio/src/thirdparty/libopenui/src/window.cpp | 27 +- radio/src/thirdparty/libopenui/src/window.h | 19 +- 10 files changed, 279 insertions(+), 339 deletions(-) diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 7a7ff21ac9f..57758408695 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -97,181 +97,123 @@ struct ModelBitmapEdit : public FileChoice { } }; -class SubScreenButton : public TextButton +static void viewOption(Window* parent, coord_t x, coord_t y, + std::function getValue, + std::function setValue, bool globalState) { - public: - SubScreenButton(Window *parent, const char *text, - std::function pressHandler, - std::function checkActive = nullptr) : - TextButton(parent, rect_t{}, text, - [=]() -> uint8_t { - pressHandler(); - return 0; - }), - m_isActive(std::move(checkActive)) - { - // Room for two lines of text - setHeight(SUBSCR_BTN_H); - setWidth((LCD_W - 30) / 3); - - lv_obj_set_width(label, lv_pct(100)); - etx_obj_add_style(label, styles->text_align_center, LV_PART_MAIN); - lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); - - setCheckHandler([=]() { check(isActive()); }); - check(isActive()); - } - - static LAYOUT_VAL(SUBSCR_BTN_H, 62, 62) - - protected: - std::function m_isActive = nullptr; - - virtual bool isActive() { return m_isActive ? m_isActive() : false; } -}; - -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}; + auto lbl = new StaticText(parent, {x + ModelSetupPage::OPTS_W + PAD_MEDIUM, y + PAD_SMALL + 1, 0, 0}, + STR_ADCFILTERVALUES[globalState ? 1 : 2], COLOR_THEME_SECONDARY1); + new Choice(parent, {x, y, ModelSetupPage::OPTS_W, 0}, STR_ADCFILTERVALUES, 0, 2, getValue, + [=](int newValue) { + setValue(newValue); + lbl->show(newValue == 0); + }); + lbl->show(getValue() == 0); +} -class ModelViewOptions : public Page -{ - public: - class OptChoice : Window +static SetupLineDef viewOptionsPageSetupLines[] = { + { + STR_RADIO_MENU_TABS, nullptr, + }, { - public: - OptChoice(Window *parent, const char *const values[], int vmin, int vmax, - std::function _getValue, - std::function _setValue, bool globalState) : - Window(parent, rect_t{}), - m_getValue(std::move(_getValue)), - m_setValue(std::move(_setValue)) - { - padAll(PAD_TINY); - setFlexLayout(LV_FLEX_FLOW_ROW, PAD_SMALL); - lv_obj_set_flex_align(lvobj, LV_FLEX_ALIGN_START, LV_FLEX_ALIGN_CENTER, - LV_FLEX_ALIGN_SPACE_AROUND); - - new Choice(this, rect_t{}, values, vmin, vmax, m_getValue, - [=](int newValue) { - m_setValue(newValue); - setState(); - }); - m_lbl = new StaticText(this, rect_t{}, - STR_ADCFILTERVALUES[globalState ? 1 : 2], COLOR_THEME_SECONDARY1); - setState(); + STR_THEME_EDITOR, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.radioThemesDisabled), + g_eeGeneral.radioThemesDisabled); } - - protected: - StaticText *m_lbl; - std::function m_getValue; - std::function m_setValue; - - void setState() - { - m_lbl->show(m_getValue() == 0); + }, + { + STR_MENUSPECIALFUNCS, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.radioGFDisabled), + g_eeGeneral.radioGFDisabled); } - }; - - FormLine* optLine(FlexGridLayout& grid) + }, { - auto line = body->newLine(grid); - line->padAll(PAD_ZERO); - line->padLeft(10); - return line; - } - - ModelViewOptions() : Page(ICON_MODEL_SETUP) + STR_MENUTRAINER, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.radioTrainerDisabled), + g_eeGeneral.radioTrainerDisabled); + } + }, + { + STR_MODEL_MENU_TABS, nullptr, + }, { - header->setTitle(STR_MENU_MODEL_SETUP); - header->setTitle2(STR_ENABLED_FEATURES); - - body->padAll(PAD_TINY); - body->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO); - - FlexGridLayout grid(line_col_dsc, line_row_dsc, PAD_TINY); - - auto line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_RADIO_MENU_TABS); - - line = optLine(grid); - new StaticText(line, rect_t{}, STR_THEME_EDITOR); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.radioThemesDisabled), - g_eeGeneral.radioThemesDisabled); - - line = optLine(grid); - new StaticText(line, rect_t{}, STR_MENUSPECIALFUNCS); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.radioGFDisabled), - g_eeGeneral.radioGFDisabled); - - line = optLine(grid); - new StaticText(line, rect_t{}, STR_MENUTRAINER); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.radioTrainerDisabled), - g_eeGeneral.radioTrainerDisabled); - - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_MODEL_MENU_TABS); - #if defined(HELI) - line = optLine(grid); - new StaticText(line, rect_t{}, STR_MENUHELISETUP); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.modelHeliDisabled), - g_eeGeneral.modelHeliDisabled); + STR_MENUHELISETUP, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.modelHeliDisabled), + g_eeGeneral.modelHeliDisabled); + } + }, #endif - #if defined(FLIGHT_MODES) - line = optLine(grid); - new StaticText(line, rect_t{}, STR_MENUFLIGHTMODES); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.modelFMDisabled), - g_eeGeneral.modelFMDisabled); + { + STR_MENUFLIGHTMODES, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.modelFMDisabled), + g_eeGeneral.modelFMDisabled); + } + }, #endif - #if defined(GVARS) - line = optLine(grid); - new StaticText(line, rect_t{}, STR_MENU_GLOBAL_VARS); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.modelGVDisabled), - g_eeGeneral.modelGVDisabled); + { + STR_MENU_GLOBAL_VARS, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.modelGVDisabled), + g_eeGeneral.modelGVDisabled); + } + }, #endif - - line = optLine(grid); - new StaticText(line, rect_t{}, STR_MENUCURVES); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.modelCurvesDisabled), - g_eeGeneral.modelCurvesDisabled); - - line = optLine(grid); - new StaticText(line, rect_t{}, STR_MENULOGICALSWITCHES); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.modelLSDisabled), - g_eeGeneral.modelLSDisabled); - - line = optLine(grid); - new StaticText(line, rect_t{}, STR_MENUCUSTOMFUNC); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.modelSFDisabled), - g_eeGeneral.modelSFDisabled); - + { + STR_MENUCURVES, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.modelCurvesDisabled), + g_eeGeneral.modelCurvesDisabled); + } + }, + { + STR_MENULOGICALSWITCHES, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.modelLSDisabled), + g_eeGeneral.modelLSDisabled); + } + }, + { + STR_MENUCUSTOMFUNC, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.modelSFDisabled), + g_eeGeneral.modelSFDisabled); + } + }, #if defined(LUA_MODEL_SCRIPTS) - line = optLine(grid); - new StaticText(line, rect_t{}, STR_MENUCUSTOMSCRIPTS); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.modelCustomScriptsDisabled), - g_eeGeneral.modelCustomScriptsDisabled); + { + STR_MENUCUSTOMSCRIPTS, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.modelCustomScriptsDisabled), + g_eeGeneral.modelCustomScriptsDisabled); + } + }, #endif - - line = optLine(grid); - new StaticText(line, rect_t{}, STR_MENUTELEMETRY); - new OptChoice(line, STR_ADCFILTERVALUES, 0, 2, - GET_SET_DEFAULT(g_model.modelTelemetryDisabled), - g_eeGeneral.modelTelemetryDisabled); - } + { + STR_MENUTELEMETRY, + [](Window* parent, coord_t x, coord_t y) { + viewOption(parent, x, y, + GET_SET_DEFAULT(g_model.modelTelemetryDisabled), + g_eeGeneral.modelTelemetryDisabled); + } + }, }; struct CenterBeepsMatrix : public ButtonMatrix { @@ -354,134 +296,98 @@ struct CenterBeepsMatrix : public ButtonMatrix { uint8_t ana_idx[MAX_ANALOG_INPUTS]; }; -class ModelOtherOptions : public Page -{ - public: - ModelOtherOptions() : Page(ICON_MODEL_SETUP) - { - header->setTitle(STR_MENU_MODEL_SETUP); - header->setTitle2(STR_MENU_OTHER); - - body->padAll(PAD_TINY); - - body->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_ZERO); - - FlexGridLayout grid(line_col_dsc, line_row_dsc, PAD_SMALL); - - // Model ADC jitter filter - auto line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_JITTER_FILTER); - new Choice(line, rect_t{}, STR_ADCFILTERVALUES, 0, 2, +static SetupLineDef otherPageSetupLines[] = { + { + STR_JITTER_FILTER, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_ADCFILTERVALUES, 0, 2, GET_SET_DEFAULT(g_model.jitterFilter)); + } + }, + { + STR_BEEPCTR, [](Window* parent, coord_t x, coord_t y) {} + }, + { + nullptr, + [](Window* parent, coord_t x, coord_t y) { + new CenterBeepsMatrix(parent, {PAD_MEDIUM, y, 0, 0}); + } + }, +}; - // Center beeps - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_BEEPCTR); - line = body->newLine(grid); - line->padLeft(4); - new CenterBeepsMatrix(line, rect_t{}); +static SetupLineDef setupLines[] = { + { + // Model name + STR_MODELNAME, + [](Window* parent, coord_t x, coord_t y) { + new ModelNameEdit(parent, {x, y, ModelSetupPage::NAM_W, 0}); + } + }, + { + // Model labels + STR_LABELS, + [](Window* parent, coord_t x, coord_t y) { + auto curmod = modelslist.getCurrentModel(); + TextButton* btn = new TextButton(parent, {x, y, 0, 0}, modelslabels.getBulletLabelString(curmod, STR_UNLABELEDMODEL)); + btn->setPressHandler([=]() { + Menu *menu = new Menu(MainWindow::instance(), true); + menu->setTitle(STR_LABELS); + for (auto &label : modelslabels.getLabels()) { + menu->addLineBuffered( + label, + [=]() { + if (!modelslabels.isLabelSelected(label, curmod)) + modelslabels.addLabelToModel(label, curmod); + else + modelslabels.removeLabelFromModel(label, curmod); + btn->setText(modelslabels.getBulletLabelString( + curmod, STR_UNLABELEDMODEL)); + strncpy(g_model.header.labels, + ModelMap::toCSV(modelslabels.getLabelsByModel(curmod)) + .c_str(), + sizeof(g_model.header.labels)); + g_model.header.labels[sizeof(g_model.header.labels) - 1] = '\0'; + SET_DIRTY(); + }, + [=]() { return modelslabels.isLabelSelected(label, curmod); }); + } + menu->updateLines(); + return 0; + }); } + }, + { + // Model bitmap + STR_BITMAP, + [](Window* parent, coord_t x, coord_t y) { + // TODO: show bitmap thumbnail instead? + new ModelBitmapEdit(parent, {x, y, 0, 0}); + } + }, }; void ModelSetupPage::build(Window * window) { - window->setFlexLayout(LV_FLEX_FLOW_COLUMN, 0); - - FlexGridLayout grid(line_col_dsc, line_row_dsc, PAD_TINY); - - // Model name - auto line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_MODELNAME); - new ModelNameEdit(line, {0, 0, LCD_W / 2 - 20, 0}); - - // Model labels - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_LABELS); - auto curmod = modelslist.getCurrentModel(); - labelTextButton = new TextButton( - line, rect_t{}, - modelslabels.getBulletLabelString(curmod, STR_UNLABELEDMODEL), [=]() { - Menu *menu = new Menu(window, true); - menu->setTitle(STR_LABELS); - for (auto &label : modelslabels.getLabels()) { - menu->addLineBuffered( - label, - [=]() { - if (!modelslabels.isLabelSelected(label, curmod)) - modelslabels.addLabelToModel(label, curmod); - else - modelslabels.removeLabelFromModel(label, curmod); - labelTextButton->setText(modelslabels.getBulletLabelString( - curmod, STR_UNLABELEDMODEL)); - strncpy(g_model.header.labels, - ModelMap::toCSV(modelslabels.getLabelsByModel(curmod)) - .c_str(), - sizeof(g_model.header.labels)); - g_model.header.labels[sizeof(g_model.header.labels) - 1] = '\0'; - SET_DIRTY(); - }, - [=]() { return modelslabels.isLabelSelected(label, curmod); }); - } - menu->updateLines(); - return 0; - }); - - // Bitmap - line = window->newLine(grid); - new StaticText(line, rect_t{}, STR_BITMAP); - // TODO: show bitmap thumbnail instead? - new ModelBitmapEdit(line, rect_t{}); - - static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; - static const lv_coord_t row_dsc[] = {LV_GRID_CONTENT, LV_GRID_TEMPLATE_LAST}; - - FlexGridLayout grid2(col_dsc, row_dsc); - - line = window->newLine(grid2); - line->padTop(8); - - // Modules - new SubScreenButton( - line, STR_INTERNALRF, []() { new ModulePage(INTERNAL_MODULE); }, - []() { return g_model.moduleData[INTERNAL_MODULE].type > 0; }); - new SubScreenButton( - line, STR_EXTERNALRF, []() { new ModulePage(EXTERNAL_MODULE); }, - []() { return g_model.moduleData[EXTERNAL_MODULE].type > 0; }); - new SubScreenButton( - line, STR_TRAINER, []() { new TrainerPage(); }, - []() { return g_model.trainerData.mode > 0; }); - - line = window->newLine(grid2); - line->padTop(2); - - // Timer buttons - new SubScreenButton( - line, TR_TIMER "1", []() { new TimerWindow(0); }, - []() { return g_model.timers[0].mode > 0; }); - new SubScreenButton( - line, TR_TIMER "2", []() { new TimerWindow(1); }, - []() { return g_model.timers[1].mode > 0; }); - new SubScreenButton( - line, TR_TIMER "3", []() { new TimerWindow(2); }, - []() { return g_model.timers[2].mode > 0; }); - - line = window->newLine(grid2); - line->padTop(2); - - new SubScreenButton(line, STR_PREFLIGHT, []() { new PreflightChecks(); }); - new SubScreenButton(line, STR_TRIMS, []() { new TrimsSetup(); }); - new SubScreenButton(line, STR_THROTTLE_LABEL, []() { new ThrottleParams(); }); - - line = window->newLine(grid2); - line->padTop(2); - - new SubScreenButton(line, STR_ENABLED_FEATURES, - []() { new ModelViewOptions(); }); - + coord_t y = SetupLine::showLines(window, 0, EDT_X, padding, setupLines, DIM(setupLines)); + + new SetupButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, nullptr, BTN_COLS, PAD_TINY, { + // Modules + {STR_INTERNALRF, []() { new ModulePage(INTERNAL_MODULE); }, []() { return g_model.moduleData[INTERNAL_MODULE].type > 0; }}, + {STR_EXTERNALRF, []() { new ModulePage(EXTERNAL_MODULE); }, []() { return g_model.moduleData[EXTERNAL_MODULE].type > 0; }}, + {STR_TRAINER, []() { new TrainerPage(); }, []() { return g_model.trainerData.mode > 0; }}, + // Timer buttons + {TR_TIMER "1", []() { new TimerWindow(0); }, []() { return g_model.timers[0].mode > 0; }}, + {TR_TIMER "2", []() { new TimerWindow(1); }, []() { return g_model.timers[1].mode > 0; }}, + {TR_TIMER "3", []() { new TimerWindow(2); }, []() { return g_model.timers[2].mode > 0; }}, + + {STR_PREFLIGHT, []() { new PreflightChecks(); }}, + {STR_TRIMS, []() { new TrimsSetup(); }}, + {STR_THROTTLE_LABEL, []() { new ThrottleParams(); }}, + {STR_ENABLED_FEATURES, []() { new SubPage(ICON_MODEL_SETUP, STR_MENU_MODEL_SETUP, STR_ENABLED_FEATURES, viewOptionsPageSetupLines, DIM(viewOptionsPageSetupLines)); }}, #if defined(USBJ_EX) - new SubScreenButton(line, STR_USBJOYSTICK_LABEL, - []() { new ModelUSBJoystickPage(); }); + {STR_USBJOYSTICK_LABEL, []() { new ModelUSBJoystickPage(); }}, #endif - - new SubScreenButton(line, STR_MENU_OTHER, []() { new ModelOtherOptions(); }); + // {STR_MENU_OTHER, []() { new ModelOtherOptions(); }}, + {STR_MENU_OTHER, []() { new SubPage(ICON_MODEL_SETUP, STR_MENU_MODEL_SETUP, STR_MENU_OTHER, otherPageSetupLines, DIM(otherPageSetupLines)); }}, + }, BTN_H); } diff --git a/radio/src/gui/colorlcd/model_setup.h b/radio/src/gui/colorlcd/model_setup.h index 15f38172c03..ca6522b0682 100644 --- a/radio/src/gui/colorlcd/model_setup.h +++ b/radio/src/gui/colorlcd/model_setup.h @@ -24,10 +24,17 @@ #include "tabsgroup.h" class ModelSetupPage: public PageTab { - public: - ModelSetupPage(); + public: + ModelSetupPage(); - void build(Window * window) override; - private: - TextButton *labelTextButton = nullptr; + void build(Window * window) override; + + static LAYOUT_VAL(BTN_COLS, 3, 3) + static LAYOUT_VAL(BTN_H, 62, 62) + static LAYOUT_VAL(OPTS_W, 100, 100) + static LAYOUT_VAL(EDT_X, 220, 144) + static LAYOUT_VAL(NAM_W, 200, 140) + + private: + TextButton *labelTextButton = nullptr; }; diff --git a/radio/src/gui/colorlcd/page.cpp b/radio/src/gui/colorlcd/page.cpp index 5e960c595b9..5441e56350f 100644 --- a/radio/src/gui/colorlcd/page.cpp +++ b/radio/src/gui/colorlcd/page.cpp @@ -93,3 +93,17 @@ void Page::checkEvents() ViewMain::instance()->runBackground(); NavWindow::checkEvents(); } + +SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle) : Page(icon, PAD_SMALL) +{ + header->setTitle(title); + header->setTitle2(subtitle); +} + +SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, SetupLineDef* setupLines, int lineCount) : Page(icon, PAD_SMALL) +{ + header->setTitle(title); + header->setTitle2(subtitle); + + SetupLine::showLines(body, 0, EDT_X, PAD_SMALL, setupLines, lineCount); +} diff --git a/radio/src/gui/colorlcd/page.h b/radio/src/gui/colorlcd/page.h index 6a2815e4e92..5e2651dccfc 100644 --- a/radio/src/gui/colorlcd/page.h +++ b/radio/src/gui/colorlcd/page.h @@ -65,3 +65,12 @@ class Page : public NavWindow void checkEvents() override; bool bubbleEvents() override { return false; } }; + +class SubPage : public Page +{ + public: + SubPage(EdgeTxIcon icon, const char* title, const char* subtitle); + SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, SetupLineDef* setupLines, int lineCount); + + static LAYOUT_VAL(EDT_X, 220, 144) +}; diff --git a/radio/src/gui/colorlcd/radio_hardware.cpp b/radio/src/gui/colorlcd/radio_hardware.cpp index 97f39d11fdb..6cb5a1a4d44 100644 --- a/radio/src/gui/colorlcd/radio_hardware.cpp +++ b/radio/src/gui/colorlcd/radio_hardware.cpp @@ -160,10 +160,7 @@ void RadioHardwarePage::build(Window* window) window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY); // TODO: sub-title? - - for (size_t i = 0; i < DIM(setupLines); i += 1) { - new SetupLine(window, 0, EDT_X, padding, setupLines[i].title, setupLines[i].createEdit); - } + SetupLine::showLines(window, 0, EDT_X, padding, setupLines, DIM(setupLines)); FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY); diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index 15cc7bcc087..8027736abee 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -186,30 +186,6 @@ class DateTimeWindow : public Window } }; -class SubPage : public Page -{ - public: - SubPage(EdgeTxIcon icon, const char* title) : Page(icon, PAD_SMALL) - { - header->setTitle(STR_RADIO_SETUP); - header->setTitle2(title); - } - - SubPage(EdgeTxIcon icon, const char* title, SetupLineDef* setupLines, int lineCount) : Page(icon, PAD_SMALL) - { - header->setTitle(STR_RADIO_SETUP); - header->setTitle2(title); - - coord_t y = 0; - Window* w; - - for (size_t i = 0; i < lineCount; i += 1) { - w = new SetupLine(body, y, RadioSetupPage::EDT_X, PAD_SMALL, setupLines[i].title, setupLines[i].createEdit); - y += w->height() + PAD_TINY; - } - } -}; - static SetupLineDef soundPageSetupLines[] = { { // Beeps mode @@ -428,7 +404,7 @@ static SetupLineDef alarmsPageSetupLines[] = { class BacklightPage : public SubPage { public: - BacklightPage() : SubPage(ICON_RADIO_SETUP, STR_BACKLIGHT_LABEL) + BacklightPage() : SubPage(ICON_RADIO_SETUP, STR_RADIO_SETUP, STR_BACKLIGHT_LABEL) { body->setFlexLayout(); @@ -594,7 +570,7 @@ static void viewOption(Window* parent, coord_t x, coord_t y, std::string s(STR_MODEL); s += " - "; s += STR_ADCFILTERVALUES[modelOption]; - new StaticText(parent, {x + ToggleSwitch::TOGGLE_W + PAD_MEDIUM, y + PAD_SMALL, 0, 0}, s.c_str(), COLOR_THEME_SECONDARY1); + new StaticText(parent, {x + ToggleSwitch::TOGGLE_W + PAD_MEDIUM, y + PAD_SMALL + 1, 0, 0}, s.c_str(), COLOR_THEME_SECONDARY1); } } @@ -706,7 +682,7 @@ static SetupLineDef viewOptionsPageSetupLines[] = { class ManageModelsSetupPage : public SubPage { public: - ManageModelsSetupPage() : SubPage(ICON_MODEL, STR_MANAGE_MODELS) + ManageModelsSetupPage() : SubPage(ICON_MODEL, STR_RADIO_SETUP, STR_MANAGE_MODELS) { body->setFlexLayout(); @@ -933,27 +909,24 @@ void RadioSetupPage::build(Window* window) // Date & time picker including labels w = new DateTimeWindow(window, {0, y, LCD_W - padding * 2, EdgeTxStyles::UI_ELEMENT_HEIGHT * 2 + PAD_TINY * 2 + PAD_MEDIUM}); - y += w->height() + padding; // Sub-pages w = new SetupButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, nullptr, BTN_COLS, PAD_TINY, { - {STR_SOUND_LABEL, []() { new SubPage(ICON_RADIO_SETUP, STR_SOUND_LABEL, soundPageSetupLines, DIM(soundPageSetupLines)); }}, + {STR_SOUND_LABEL, []() { new SubPage(ICON_RADIO_SETUP, STR_RADIO_SETUP, STR_SOUND_LABEL, soundPageSetupLines, DIM(soundPageSetupLines)); }}, #if defined(VARIO) - {STR_VARIO, []() { new SubPage(ICON_RADIO_SETUP, STR_VARIO, varioPageSetupLines, DIM(varioPageSetupLines)); }}, + {STR_VARIO, []() { new SubPage(ICON_RADIO_SETUP, STR_RADIO_SETUP, STR_VARIO, varioPageSetupLines, DIM(varioPageSetupLines)); }}, #endif #if defined(HAPTIC) - {STR_HAPTIC_LABEL, []() { new SubPage(ICON_RADIO_SETUP, STR_HAPTIC_LABEL, hapticPageSetupLines, DIM(hapticPageSetupLines)); }}, + {STR_HAPTIC_LABEL, []() { new SubPage(ICON_RADIO_SETUP, STR_RADIO_SETUP, STR_HAPTIC_LABEL, hapticPageSetupLines, DIM(hapticPageSetupLines)); }}, #endif - {STR_ALARMS_LABEL, []() { new SubPage(ICON_RADIO_SETUP, STR_ALARMS_LABEL, alarmsPageSetupLines, DIM(alarmsPageSetupLines)); }}, + {STR_ALARMS_LABEL, []() { new SubPage(ICON_RADIO_SETUP, STR_RADIO_SETUP, STR_ALARMS_LABEL, alarmsPageSetupLines, DIM(alarmsPageSetupLines)); }}, {STR_BACKLIGHT_LABEL, []() { new BacklightPage(); }}, - {STR_GPS, []() { new SubPage(ICON_RADIO_SETUP, STR_GPS, gpsPageSetupLines, DIM(gpsPageSetupLines)); }}, - {STR_ENABLED_FEATURES, []() { new SubPage(ICON_RADIO_SETUP, STR_ENABLED_FEATURES, viewOptionsPageSetupLines, DIM(viewOptionsPageSetupLines)); }}, + {STR_GPS, []() { new SubPage(ICON_RADIO_SETUP, STR_RADIO_SETUP, STR_GPS, gpsPageSetupLines, DIM(gpsPageSetupLines)); }}, + {STR_ENABLED_FEATURES, []() { new SubPage(ICON_RADIO_SETUP, STR_RADIO_SETUP, STR_ENABLED_FEATURES, viewOptionsPageSetupLines, DIM(viewOptionsPageSetupLines)); }}, {STR_MAIN_MENU_MANAGE_MODELS, []() { new ManageModelsSetupPage(); }}, - }); + }, BTN_H); + y += w->height() + padding; - for (size_t i = 0; i < DIM(setupLines); i += 1) { - y += w->height() + padding; - w = new SetupLine(window, y, EDT_X, padding, setupLines[i].title, setupLines[i].createEdit); - } + SetupLine::showLines(window, y, EDT_X, padding, setupLines, DIM(setupLines)); } diff --git a/radio/src/gui/colorlcd/radio_setup.h b/radio/src/gui/colorlcd/radio_setup.h index e02b4a0979a..d98eb55325c 100644 --- a/radio/src/gui/colorlcd/radio_setup.h +++ b/radio/src/gui/colorlcd/radio_setup.h @@ -33,4 +33,5 @@ class RadioSetupPage: public PageTab { static LAYOUT_VAL(EDT_X, 220, 144) static constexpr coord_t LBL_W = EDT_X - PAD_TINY - PAD_SMALL; static LAYOUT_VAL(BTN_COLS, 3, 2) + static LAYOUT_VAL(BTN_H, 62, 62) }; diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index bee05a899f4..59f8430e31e 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -96,6 +96,13 @@ class TextButton : public ButtonBase } } + void setWrap() + { + lv_obj_set_width(label, lv_pct(100)); + etx_obj_add_style(label, styles->text_align_center, LV_PART_MAIN); + lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); + } + protected: lv_obj_t* label = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index a5c8b4dca23..64b6a1ae63b 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -435,7 +435,8 @@ NavWindow::NavWindow(Window *parent, const rect_t &rect, setWindowFlag(OPAQUE); } -SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, PaddingSize padding, PageDefs pages) : +SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, + PaddingSize padding, PageDefs pages, coord_t btnHeight) : Window(parent, rect) { padAll(padding); @@ -443,7 +444,7 @@ SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const cha coord_t buttonWidth = (width() - PAD_SMALL * (cols + 1) - PAD_TINY * 2) / cols; int rows = (pages.size() + cols - 1) / cols; - int height = rows * EdgeTxStyles::UI_ELEMENT_HEIGHT + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2; + int height = rows * btnHeight + (rows - 1) * PAD_MEDIUM + PAD_TINY * 2; if (title) { height += EdgeTxStyles::PAGE_LINE_HEIGHT + PAD_TINY; } @@ -465,19 +466,21 @@ SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const cha xo += space; } x = xo + (n % cols) * xw; - y = yo + (n / cols) * (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_MEDIUM); + y = yo + (n / cols) * (btnHeight + PAD_MEDIUM); // TODO: sort out all caps title strings VS quick menu strings - std::string title(entry.first); + std::string title(entry.title); for (std::string::iterator it = title.begin(); it != title.end(); ++it) { if (*it == '\n') *it = ' '; } - new TextButton(this, rect_t{x, y, buttonWidth, EdgeTxStyles::UI_ELEMENT_HEIGHT}, title, [&, entry]() { - entry.second(); + auto btn = new TextButton(this, rect_t{x, y, buttonWidth, btnHeight}, title, [&, entry]() { + entry.createPage(); return 0; }); + btn->setWrap(); + if (entry.isActive) btn->setCheckHandler([=]() { btn->check(entry.isActive()); }); n += 1; remaining -= 1; } @@ -509,3 +512,15 @@ SetupLine::SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize paddin setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); } } + +coord_t SetupLine::showLines(Window* parent, coord_t y, coord_t col2, PaddingSize padding, SetupLineDef* setupLines, int lineCount) +{ + Window* w; + + for (size_t i = 0; i < lineCount; i += 1) { + w = new SetupLine(parent, y, col2, padding, setupLines[i].title, setupLines[i].createEdit); + y += w->height() + padding; + } + + return y; +} diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 301d289431c..235cf6cb586 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -230,14 +230,23 @@ class NavWindow : public Window void onEvent(event_t event) override; }; +struct PageButtonDef { + const char* title; + std::function createPage; + std::function isActive; + + PageButtonDef(const char* title, std::function createPage, std::function isActive = nullptr) : + title(title), createPage(std::move(createPage)), isActive(std::move(isActive)) + {} +}; + class SetupButtonGroup : public Window { public: - typedef std::function PageFct; - typedef std::pair PageDef; - typedef std::list PageDefs; + typedef std::list PageDefs; - SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, PaddingSize padding, PageDefs pages); + SetupButtonGroup(Window* parent, const rect_t& rect, const char* title, int cols, + PaddingSize padding, PageDefs pages, coord_t btnHeight = EdgeTxStyles::UI_ELEMENT_HEIGHT); protected: }; @@ -252,5 +261,7 @@ class SetupLine : public Window public: SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const char* title, std::function createEdit); + static coord_t showLines(Window* parent, coord_t y, coord_t col2, PaddingSize padding, SetupLineDef* setupLines, int lineCount); + protected: }; From c7dc69c9507ba096839e38eff41f66f708aeffa9 Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 22 May 2024 14:55:19 +1000 Subject: [PATCH 13/21] Remove grid and flex layouts from model select page. Fix compile warnings. --- radio/src/gui/colorlcd/model_select.cpp | 96 ++++++------------- radio/src/gui/colorlcd/model_select.h | 17 ++-- radio/src/thirdparty/libopenui/src/button.cpp | 4 +- radio/src/thirdparty/libopenui/src/button.h | 2 +- radio/src/thirdparty/libopenui/src/dialog.cpp | 3 +- radio/src/thirdparty/libopenui/src/window.cpp | 2 +- 6 files changed, 42 insertions(+), 82 deletions(-) diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 82cc6842e7a..a0f6985f7ec 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -39,6 +39,7 @@ struct ModelButtonLayout { uint16_t padding; bool hasImage; uint16_t font; + uint16_t columns; }; static LAYOUT_VAL(L0_W, 165, 147) @@ -46,13 +47,12 @@ static LAYOUT_VAL(L0_H, 92, 92) static LAYOUT_VAL(L1_W, 108, 96) static LAYOUT_VAL(L1_H, 61, 61) static LAYOUT_VAL(L3_W, 336, 300) -static LAYOUT_VAL(MODEL_CELL_PADDING, 4, 4) ModelButtonLayout modelLayouts[] = { - {L0_W, L0_H, MODEL_CELL_PADDING, true, FONT(STD)}, - {L1_W, L1_H, MODEL_CELL_PADDING, true, FONT(XS)}, - {L0_W, EdgeTxStyles::UI_ELEMENT_HEIGHT, MODEL_CELL_PADDING, false, FONT(STD)}, - {L3_W, EdgeTxStyles::UI_ELEMENT_HEIGHT, MODEL_CELL_PADDING, false, FONT(STD)}, + {L0_W, L0_H, PAD_SMALL, true, FONT(STD), 2}, + {L1_W, L1_H, PAD_SMALL, true, FONT(XS), 3}, + {L0_W, EdgeTxStyles::UI_ELEMENT_HEIGHT, PAD_SMALL, false, FONT(STD), 2}, + {L3_W, EdgeTxStyles::UI_ELEMENT_HEIGHT, PAD_SMALL, false, FONT(STD), 1}, }; class ModelButton : public Button @@ -68,8 +68,6 @@ class ModelButton : public Button padAll(PAD_ZERO); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); - setWidth(modelLayouts[layout].width); - setHeight(modelLayouts[layout].height); check(modelCell == modelslist.getCurrentModel()); @@ -169,17 +167,12 @@ class ModelsPageBody : public Window ModelsPageBody(Window *parent, const rect_t &rect) : Window(parent, rect) { padAll(PAD_TINY); - padLeft(PAD_MEDIUM); } void update() { clear(); - setFlexLayout(LV_FLEX_FLOW_ROW_WRAP, - modelLayouts[g_eeGeneral.modelSelectLayout].padding); - padRow(modelLayouts[g_eeGeneral.modelSelectLayout].padding); - ModelsVector models; if (selectedLabels.size()) { models = modelslabels.getModelsInLabels(selectedLabels); @@ -195,9 +188,18 @@ class ModelsPageBody : public Window ModelButton *firstButton = nullptr; ModelButton *focusedButton = nullptr; + int n = 0; + int cols = modelLayouts[g_eeGeneral.modelSelectLayout].columns; + coord_t w = modelLayouts[g_eeGeneral.modelSelectLayout].width; + coord_t h = modelLayouts[g_eeGeneral.modelSelectLayout].height; + for (auto &model : models) { + coord_t x = (n % cols) * (w + PAD_TINY); + coord_t y = (n / cols) * (h + PAD_TINY); + n += 1; + auto button = new ModelButton( - this, rect_t{}, model, [=]() { focusedModel = model; }, + this, {x, y, w, h}, model, [=]() { focusedModel = model; }, g_eeGeneral.modelSelectLayout); if (!firstButton) firstButton = button; @@ -434,9 +436,9 @@ class ModelsPageBody : public Window class ModelLayoutButton : public IconButton { public: - ModelLayoutButton(Window *parent, uint8_t layout, + ModelLayoutButton(Window *parent, coord_t x, coord_t y, uint8_t layout, std::function pressHandler) : - IconButton(parent, (EdgeTxIcon)(ICON_MODEL_GRID_LARGE + layout), + IconButton(parent, (EdgeTxIcon)(ICON_MODEL_GRID_LARGE + layout), x, y, pressHandler), layout(layout) { @@ -448,7 +450,6 @@ class ModelLayoutButton : public IconButton { layout = newLayout; setIcon((EdgeTxIcon)(ICON_MODEL_GRID_LARGE + layout)); - // invalidate(); } protected: @@ -619,7 +620,7 @@ void ModelLabelsWindow::buildHead(Window *hdr) setTitle(); // new model button - auto btn = new TextButton(hdr, rect_t{0, 0, NEW_BTN_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_NEW, [=]() { + new TextButton(hdr, {LCD_W - NEW_BTN_W - PAD_LARGE, LAYOUT_BTN_YO, NEW_BTN_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_NEW, [=]() { auto menu = new Menu(this); menu->setTitle(STR_CREATE_NEW); menu->addLine(STR_NEW_MODEL, [=]() { newModel(); }); @@ -627,13 +628,7 @@ void ModelLabelsWindow::buildHead(Window *hdr) return 0; }); - btn->padAll(PAD_SMALL); - - // button placement - hdr->padRight(lv_dpx(8)); - lv_obj_align(btn->getLvObj(), LV_ALIGN_RIGHT_MID, 0, 0); - - mdlLayout = new ModelLayoutButton(this, g_eeGeneral.modelSelectLayout, [=]() { + mdlLayout = new ModelLayoutButton(this, LCD_W - LAYOUT_BTN_XO, LAYOUT_BTN_YO, g_eeGeneral.modelSelectLayout, [=]() { uint8_t l = mdlLayout->getLayout(); l = (l + 1) & 3; mdlLayout->setLayout(l); @@ -642,72 +637,37 @@ void ModelLabelsWindow::buildHead(Window *hdr) mdlselector->update(); return 0; }); - lv_obj_set_pos(mdlLayout->getLvObj(), LCD_W - LAYOUT_BTN_XO, LAYOUT_BTN_YO); } -#if !PORTRAIT_LCD -static const lv_coord_t col_dsc[] = {ModelLabelsWindow::LABELS_WIDTH, LV_GRID_FR(1), - LV_GRID_TEMPLATE_LAST}; -static const lv_coord_t row_dsc[] = {LV_GRID_FR(1), ModelLabelsWindow::BUTTONS_HEIGHT, - LV_GRID_TEMPLATE_LAST}; -#else -static const lv_coord_t col_dsc[] = {LV_GRID_FR(1), LV_GRID_TEMPLATE_LAST}; -static const lv_coord_t row_dsc[] = {LV_GRID_FR(1), ModelLabelsWindow::LABELS_HEIGHT, - ModelLabelsWindow::BUTTONS_HEIGHT, LV_GRID_TEMPLATE_LAST}; -#endif - void ModelLabelsWindow::buildBody(Window *window) { - window->padTop(6); - window->padBottom(6); - - lv_obj_set_grid_dsc_array(window->getLvObj(), col_dsc, row_dsc); - // Models List - mdlselector = new ModelsPageBody(window, rect_t{}); + mdlselector = new ModelsPageBody(window, {MDLS_X, MDLS_Y, MDLS_W, MDLS_H}); mdlselector->setLblRefreshFunc([=]() { labelRefreshRequest(); }); auto mdl_obj = mdlselector->getLvObj(); - lv_obj_set_grid_cell(mdl_obj, LV_GRID_ALIGN_STRETCH, MODELS_COL, 1, - LV_GRID_ALIGN_STRETCH, 0, MODELS_ROW_CNT); + lv_obj_set_style_max_width(mdl_obj, MDLS_W, LV_PART_MAIN); + lv_obj_set_style_max_height(mdl_obj, MDLS_H, LV_PART_MAIN); + etx_scrollbar(mdl_obj); if (mdlselector->getSortOrder() == NO_SORT) mdlselector->setSortOrder(NAME_ASC); // Labels - auto box = new Window(window, rect_t{}); - box->padAll(PAD_ZERO); - box->padLeft(6); -#if PORTRAIT_LCD - box->padRight(6); - box->padTop(6); - box->padBottom(6); -#endif - lv_obj_set_grid_cell(box->getLvObj(), LV_GRID_ALIGN_STRETCH, 0, 1, - LV_GRID_ALIGN_STRETCH, LABELS_ROW, 1); lblselector = - new ListBox(box, rect_t{0, 0, LV_PCT(100), LV_PCT(100)}, getLabels()); + new ListBox(window, rect_t{PAD_SMALL, LABELS_Y, LABELS_WIDTH, LABELS_HEIGHT}, getLabels()); lblselector->setSmallSelectMarker(); auto lbl_obj = lblselector->getLvObj(); + etx_scrollbar(lbl_obj); + + lblselector->setColumnWidth(0, LABELS_WIDTH); // Sort Button - box = new Window(window, rect_t{}); - box->padAll(PAD_TINY); - box->padLeft(6); - box->padRight(6); - lv_obj_set_grid_cell(box->getLvObj(), LV_GRID_ALIGN_STRETCH, 0, 1, - LV_GRID_ALIGN_STRETCH, BUTTONS_ROW, 1); new Choice( - box, {}, STR_SORT_ORDERS, NAME_ASC, DATE_DES, + window, {PAD_SMALL, LABELS_Y + LABELS_HEIGHT + PAD_SMALL, SORT_BUTTON_W, 0}, STR_SORT_ORDERS, NAME_ASC, DATE_DES, [=]() { return mdlselector->getSortOrder(); }, [=](int newValue) { mdlselector->setSortOrder((ModelsSortBy)newValue); }, STR_SORT_MODELS_BY); - lv_obj_update_layout(mdl_obj); - lblselector->setColumnWidth(0, lv_obj_get_content_width(lbl_obj)); - - etx_scrollbar(lbl_obj); - etx_scrollbar(mdl_obj); - std::set filteredLabels = modelslabels.filteredLabels(); if (g_eeGeneral.labelSingleSelect == 0) { diff --git a/radio/src/gui/colorlcd/model_select.h b/radio/src/gui/colorlcd/model_select.h index 67602fafeff..98dca981918 100644 --- a/radio/src/gui/colorlcd/model_select.h +++ b/radio/src/gui/colorlcd/model_select.h @@ -33,16 +33,17 @@ class ModelLabelsWindow : public Page public: ModelLabelsWindow(); - static LAYOUT_VAL(BUTTONS_HEIGHT, 36, 36) - static LAYOUT_VAL(LABELS_WIDTH, 132, 0) - static LAYOUT_VAL(LABELS_HEIGHT, 0, 152) - static LAYOUT_VAL(LABELS_ROW, 0, 1) - static LAYOUT_VAL(MODELS_COL, 1, 0) - static LAYOUT_VAL(MODELS_ROW_CNT, 2, 1) - static LAYOUT_VAL(BUTTONS_ROW, 1, 2) static LAYOUT_VAL(NEW_BTN_W, 60, 60) - static LAYOUT_VAL(LAYOUT_BTN_XO, 105, 105) + static constexpr coord_t LAYOUT_BTN_XO = NEW_BTN_W + PAD_LARGE * 2 + EdgeTxStyles::UI_ELEMENT_HEIGHT; static LAYOUT_VAL(LAYOUT_BTN_YO, 6, 6) + static LAYOUT_VAL(MDLS_X, 137, 4) + static LAYOUT_VAL(MDLS_Y, 4, 4) + static LAYOUT_VAL(MDLS_W, 343, LCD_W - PAD_MEDIUM) + static LAYOUT_VAL(MDLS_H, 219, 219) + static LAYOUT_VAL(LABELS_Y, PAD_SMALL, MDLS_Y + MDLS_H + PAD_SMALL) + static LAYOUT_VAL(LABELS_WIDTH, 131, LCD_W - PAD_SMALL * 2) + static LAYOUT_VAL(LABELS_HEIGHT, 181, 166) + static LAYOUT_VAL(SORT_BUTTON_W, LABELS_WIDTH, 120) protected: ModelsSortBy sort = DEFAULT_MODEL_SORT; diff --git a/radio/src/thirdparty/libopenui/src/button.cpp b/radio/src/thirdparty/libopenui/src/button.cpp index 10ba2126375..99e86bbc838 100644 --- a/radio/src/thirdparty/libopenui/src/button.cpp +++ b/radio/src/thirdparty/libopenui/src/button.cpp @@ -115,9 +115,9 @@ TextButton::TextButton(Window* parent, const rect_t& rect, std::string text, lv_obj_center(label); } -IconButton::IconButton(Window* parent, EdgeTxIcon icon, +IconButton::IconButton(Window* parent, EdgeTxIcon icon, coord_t x, coord_t y, std::function pressHandler) : - ButtonBase(parent, {0, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT, EdgeTxStyles::UI_ELEMENT_HEIGHT}, pressHandler, button_create) + ButtonBase(parent, {x, y, EdgeTxStyles::UI_ELEMENT_HEIGHT, EdgeTxStyles::UI_ELEMENT_HEIGHT}, pressHandler, button_create) { padAll(PAD_ZERO); iconImage = new StaticIcon(this, 0, 0, icon, COLOR_THEME_SECONDARY1); diff --git a/radio/src/thirdparty/libopenui/src/button.h b/radio/src/thirdparty/libopenui/src/button.h index 59f8430e31e..920339c6b01 100644 --- a/radio/src/thirdparty/libopenui/src/button.h +++ b/radio/src/thirdparty/libopenui/src/button.h @@ -112,7 +112,7 @@ class TextButton : public ButtonBase class IconButton : public ButtonBase { public: - IconButton(Window* parent, EdgeTxIcon icon, + IconButton(Window* parent, EdgeTxIcon icon, coord_t x, coord_t y, std::function pressHandler = nullptr); void setIcon(EdgeTxIcon icon); diff --git a/radio/src/thirdparty/libopenui/src/dialog.cpp b/radio/src/thirdparty/libopenui/src/dialog.cpp index bb60563f544..dab721b37b4 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.cpp +++ b/radio/src/thirdparty/libopenui/src/dialog.cpp @@ -195,8 +195,7 @@ LabelDialog::LabelDialog(Window *parent, const char *label, int length, const ch lv_obj_set_flex_align(box->getLvObj(), LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_CENTER, LV_FLEX_ALIGN_SPACE_BETWEEN); - auto edit = new TextEdit(box, rect_t{0, 0, LV_PCT(100), 0}, this->label, - length); + new TextEdit(box, rect_t{0, 0, LV_PCT(100), 0}, this->label, length); box = new Window(form, rect_t{}); box->padAll(PAD_MEDIUM); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 64b6a1ae63b..9cd0d30f53e 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -517,7 +517,7 @@ coord_t SetupLine::showLines(Window* parent, coord_t y, coord_t col2, PaddingSiz { Window* w; - for (size_t i = 0; i < lineCount; i += 1) { + for (int i = 0; i < lineCount; i += 1) { w = new SetupLine(parent, y, col2, padding, setupLines[i].title, setupLines[i].createEdit); y += w->height() + padding; } From 56b514b43f1f830f668f350c8d16a5c07e5bebe0 Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 22 May 2024 21:02:22 +1000 Subject: [PATCH 14/21] Fix label management in single select mode. Fix RTN key not working when creating a new label. --- radio/src/gui/colorlcd/model_select.cpp | 22 ++++++++++++++++++--- radio/src/thirdparty/libopenui/src/dialog.h | 2 ++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index a0f6985f7ec..3017a1b2ab8 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -599,7 +599,7 @@ void ModelLabelsWindow::newModel() void ModelLabelsWindow::newLabel() { tmpLabel[0] = '\0'; - new LabelDialog(parent, tmpLabel, LABEL_LENGTH, STR_ENTER_LABEL, [=](std::string label) { + new LabelDialog(this, tmpLabel, LABEL_LENGTH, STR_ENTER_LABEL, [=](std::string label) { int newlabindex = modelslabels.addLabel(label); if (newlabindex >= 0) { std::set newset; @@ -792,13 +792,29 @@ void ModelLabelsWindow::buildBody(Window *window) if (modelslabels.getLabels().size() > 1) { if (selected != 0) { menu->addLine(STR_MOVE_UP, [=]() { - moveLabel(selected, -1); + modelslabels.moveLabelTo(selected, selected - 1); + std::set newset; + newset.insert(selected - 1); + auto labels = getLabels(); + lblselector->setNames(labels); + lblselector->setSelected(newset); + if (g_eeGeneral.labelSingleSelect) + lblselector->setActiveItem(selected - 1); + updateFilteredLabels(newset); return 0; }); } if (selected != (int)modelslabels.getLabels().size() - 1) { menu->addLine(STR_MOVE_DOWN, [=]() { - moveLabel(selected, 1); + modelslabels.moveLabelTo(selected, selected + 1); + std::set newset; + newset.insert(selected + 1); + auto labels = getLabels(); + lblselector->setNames(labels); + lblselector->setSelected(newset); + if (g_eeGeneral.labelSingleSelect) + lblselector->setActiveItem(selected + 1); + updateFilteredLabels(newset); return 0; }); } diff --git a/radio/src/thirdparty/libopenui/src/dialog.h b/radio/src/thirdparty/libopenui/src/dialog.h index e82e5001e3c..d8ef6021235 100644 --- a/radio/src/thirdparty/libopenui/src/dialog.h +++ b/radio/src/thirdparty/libopenui/src/dialog.h @@ -138,6 +138,8 @@ class LabelDialog : public ModalWindow static constexpr int MAX_LABEL_LENGTH = 255; + void onCancel() override { deleteLater(); } + protected: std::function saveHandler; char label[MAX_LABEL_LENGTH + 1]; From d27382968905ac7c008a02b6095eec4fb5a119be Mon Sep 17 00:00:00 2001 From: philmoz Date: Thu, 23 May 2024 15:56:09 +1000 Subject: [PATCH 15/21] Convert model setup pages to use SubPage class. --- radio/src/gui/colorlcd/model_setup.cpp | 3 +- radio/src/gui/colorlcd/model_setup.h | 1 - radio/src/gui/colorlcd/page.cpp | 13 +- radio/src/gui/colorlcd/page.h | 5 + radio/src/gui/colorlcd/preflight_checks.cpp | 417 ++++++++---------- radio/src/gui/colorlcd/preflight_checks.h | 12 +- radio/src/gui/colorlcd/radio_hardware.cpp | 2 +- radio/src/gui/colorlcd/radio_hardware.h | 1 - radio/src/gui/colorlcd/radio_setup.cpp | 38 +- radio/src/gui/colorlcd/radio_setup.h | 2 - radio/src/gui/colorlcd/throttle_params.cpp | 104 ++--- radio/src/gui/colorlcd/throttle_params.h | 4 +- radio/src/gui/colorlcd/timer_setup.cpp | 173 +++----- radio/src/gui/colorlcd/timer_setup.h | 12 +- radio/src/gui/colorlcd/trims_setup.cpp | 114 +++-- radio/src/gui/colorlcd/trims_setup.h | 6 +- radio/src/thirdparty/libopenui/src/choice.cpp | 4 + radio/src/thirdparty/libopenui/src/window.cpp | 13 +- radio/src/thirdparty/libopenui/src/window.h | 3 +- 19 files changed, 429 insertions(+), 498 deletions(-) diff --git a/radio/src/gui/colorlcd/model_setup.cpp b/radio/src/gui/colorlcd/model_setup.cpp index 57758408695..702236383a2 100644 --- a/radio/src/gui/colorlcd/model_setup.cpp +++ b/radio/src/gui/colorlcd/model_setup.cpp @@ -368,7 +368,7 @@ static SetupLineDef setupLines[] = { void ModelSetupPage::build(Window * window) { - coord_t y = SetupLine::showLines(window, 0, EDT_X, padding, setupLines, DIM(setupLines)); + coord_t y = SetupLine::showLines(window, 0, SubPage::EDT_X, padding, setupLines, DIM(setupLines)); new SetupButtonGroup(window, {0, y, LCD_W - padding * 2, 0}, nullptr, BTN_COLS, PAD_TINY, { // Modules @@ -387,7 +387,6 @@ void ModelSetupPage::build(Window * window) #if defined(USBJ_EX) {STR_USBJOYSTICK_LABEL, []() { new ModelUSBJoystickPage(); }}, #endif - // {STR_MENU_OTHER, []() { new ModelOtherOptions(); }}, {STR_MENU_OTHER, []() { new SubPage(ICON_MODEL_SETUP, STR_MENU_MODEL_SETUP, STR_MENU_OTHER, otherPageSetupLines, DIM(otherPageSetupLines)); }}, }, BTN_H); } diff --git a/radio/src/gui/colorlcd/model_setup.h b/radio/src/gui/colorlcd/model_setup.h index ca6522b0682..627ed77092a 100644 --- a/radio/src/gui/colorlcd/model_setup.h +++ b/radio/src/gui/colorlcd/model_setup.h @@ -32,7 +32,6 @@ class ModelSetupPage: public PageTab { static LAYOUT_VAL(BTN_COLS, 3, 3) static LAYOUT_VAL(BTN_H, 62, 62) static LAYOUT_VAL(OPTS_W, 100, 100) - static LAYOUT_VAL(EDT_X, 220, 144) static LAYOUT_VAL(NAM_W, 200, 140) private: diff --git a/radio/src/gui/colorlcd/page.cpp b/radio/src/gui/colorlcd/page.cpp index 5441e56350f..4215147e3ff 100644 --- a/radio/src/gui/colorlcd/page.cpp +++ b/radio/src/gui/colorlcd/page.cpp @@ -96,14 +96,25 @@ void Page::checkEvents() SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle) : Page(icon, PAD_SMALL) { + body->padBottom(PAD_LARGE * 2); + header->setTitle(title); header->setTitle2(subtitle); } SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, SetupLineDef* setupLines, int lineCount) : Page(icon, PAD_SMALL) { + body->padBottom(PAD_LARGE * 2); + header->setTitle(title); header->setTitle2(subtitle); - SetupLine::showLines(body, 0, EDT_X, PAD_SMALL, setupLines, lineCount); + SetupLine::showLines(body, y, EDT_X, PAD_SMALL, setupLines, lineCount); +} + +Window* SubPage::setupLine(const char* title, std::function createEdit, coord_t lblYOffset) +{ + auto w = new SetupLine(body, y, EDT_X, PAD_SMALL, title, createEdit, lblYOffset); + y += w->height(); + return w; } diff --git a/radio/src/gui/colorlcd/page.h b/radio/src/gui/colorlcd/page.h index 5e2651dccfc..6bfbdcc1f29 100644 --- a/radio/src/gui/colorlcd/page.h +++ b/radio/src/gui/colorlcd/page.h @@ -72,5 +72,10 @@ class SubPage : public Page SubPage(EdgeTxIcon icon, const char* title, const char* subtitle); SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, SetupLineDef* setupLines, int lineCount); + Window* setupLine(const char* title, std::function createEdit, coord_t lblYOffset = 0); + static LAYOUT_VAL(EDT_X, 220, 144) + + protected: + coord_t y = 0; }; diff --git a/radio/src/gui/colorlcd/preflight_checks.cpp b/radio/src/gui/colorlcd/preflight_checks.cpp index d6173201ebe..9265c7be60d 100644 --- a/radio/src/gui/colorlcd/preflight_checks.cpp +++ b/radio/src/gui/colorlcd/preflight_checks.cpp @@ -30,64 +30,74 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -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 void cb_changed(lv_event_t* e) +class SwitchWarnMatrix : public ButtonMatrix { - auto target = lv_event_get_target(e); - auto obj = (lv_obj_t*)lv_event_get_user_data(e); + public: + SwitchWarnMatrix(Window* parent, const rect_t& rect) : + ButtonMatrix(parent, rect) + { + // Setup button layout & texts + uint8_t btn_cnt = 0; + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + if (SWITCH_WARNING_ALLOWED(i)) { + sw_idx[btn_cnt] = i; + btn_cnt++; + } + } - if (lv_obj_has_state(target, LV_STATE_CHECKED)) { - lv_obj_clear_flag(obj, LV_OBJ_FLAG_HIDDEN); - } else { - lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); - } -} + initBtnMap(min((int)btn_cnt, SW_BTNS), btn_cnt); -static void make_conditional(Window* w, ToggleSwitch* cb) -{ - lv_obj_t* w_obj = w->getLvObj(); - if (!cb->getValue()) { - lv_obj_add_flag(w_obj, LV_OBJ_FLAG_HIDDEN); - } + uint8_t btn_id = 0; + for (uint8_t i = 0; i < MAX_SWITCHES; i++) { + if (SWITCH_WARNING_ALLOWED(i)) { + setTextAndState(btn_id); + btn_id++; + } + } - lv_obj_t* cb_obj = cb->getLvObj(); - lv_obj_add_event_cb(cb_obj, cb_changed, LV_EVENT_VALUE_CHANGED, w_obj); -} + update(); -static void choice_changed(lv_event_t* e) -{ - auto target = lv_event_get_target(e); - auto choice = (Choice*)lv_obj_get_user_data(target); - auto obj = (lv_obj_t*)lv_event_get_user_data(e); - - if (choice->getIntValue() != 0) { - lv_obj_clear_flag(obj, LV_OBJ_FLAG_HIDDEN); - } else { - lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); + lv_obj_set_width(lvobj, min((int)btn_cnt, SW_BTNS) * SW_BTN_W + PAD_SMALL); + + uint8_t rows = ((btn_cnt - 1) / SW_BTNS) + 1; + setHeight((rows * SW_BTN_H) + PAD_SMALL); + + padAll(PAD_SMALL); } -} -static void make_conditional(Window* w, Choice* choice) -{ - lv_obj_t* w_obj = w->getLvObj(); - if (choice->getIntValue() == 0) { - lv_obj_add_flag(w_obj, LV_OBJ_FLAG_HIDDEN); + void onPress(uint8_t btn_id) + { + if (btn_id >= MAX_SWITCHES) return; + auto sw = sw_idx[btn_id]; + + swarnstate_t newstate = bfGet(g_model.switchWarning, 3 * sw, 3); + if (newstate == 1 && SWITCH_CONFIG(sw) != SWITCH_3POS) + newstate = 3; + else + newstate = (newstate + 1) % 4; + + g_model.switchWarning = + bfSet(g_model.switchWarning, newstate, 3 * sw, 3); + SET_DIRTY(); + + setTextAndState(btn_id); } - lv_obj_t* choice_obj = choice->getLvObj(); - lv_obj_add_event_cb(choice_obj, choice_changed, LV_EVENT_VALUE_CHANGED, - w_obj); -} + bool isActive(uint8_t btn_id) + { + if (btn_id >= MAX_SWITCHES) return false; + return bfGet(g_model.switchWarning, 3 * sw_idx[btn_id], 3) != 0; + } -struct SwitchWarnMatrix : public ButtonMatrix { - SwitchWarnMatrix(Window* parent, const rect_t& rect); - void onPress(uint8_t btn_id); - bool isActive(uint8_t btn_id); - void setTextAndState(uint8_t btn_id); + void setTextAndState(uint8_t btn_id) + { + swsrc_t index = sw_idx[btn_id]; + auto warn_pos = g_model.switchWarning >> (3 * index) & 0x07; + std::string s = std::string(switchGetName(index)) + + std::string(getSwitchWarnSymbol(warn_pos)); + setText(btn_id, s.c_str()); + setChecked(btn_id); + } static LAYOUT_VAL(SW_BTNS, 8, 4) static LAYOUT_VAL(SW_BTN_W, 56, 72) @@ -97,76 +107,129 @@ struct SwitchWarnMatrix : public ButtonMatrix { uint8_t sw_idx[MAX_SWITCHES]; }; -struct PotWarnMatrix : public ButtonMatrix { - PotWarnMatrix(Window* parent, const rect_t& rect); - void onPress(uint8_t btn_id); - bool isActive(uint8_t btn_id); - void setTextAndState(uint8_t btn_id); +class PotWarnMatrix : public ButtonMatrix +{ + public: + PotWarnMatrix(Window* parent, const rect_t& rect) : + ButtonMatrix(parent, rect) + { + // Setup button layout & texts + uint8_t btn_cnt = 0; + for (uint8_t i = 0; i < MAX_POTS; i++) { + if (IS_POT_AVAILABLE(i)) { + pot_idx[btn_cnt] = i; + btn_cnt++; + } + } + + initBtnMap(min((int)btn_cnt, SwitchWarnMatrix::SW_BTNS), btn_cnt); + + uint8_t btn_id = 0; + for (uint16_t i = 0; i < MAX_POTS; i++) { + if (IS_POT_AVAILABLE(i)) { + setTextAndState(btn_id); + btn_id++; + } + } + + update(); + + lv_obj_set_width(lvobj, min((int)btn_cnt, SwitchWarnMatrix::SW_BTNS) * SwitchWarnMatrix::SW_BTN_W + PAD_SMALL); + + uint8_t rows = ((btn_cnt - 1) / SwitchWarnMatrix::SW_BTNS) + 1; + setHeight((rows * SwitchWarnMatrix::SW_BTN_H) + PAD_SMALL); + + padAll(PAD_SMALL); + } + + void onPress(uint8_t btn_id) + { + if (btn_id >= MAX_POTS) return; + auto pot = pot_idx[btn_id]; + + g_model.potsWarnEnabled ^= (1 << pot); + if ((g_model.potsWarnMode == POTS_WARN_MANUAL) && + (g_model.potsWarnEnabled & (1 << pot))) { + SAVE_POT_POSITION(pot); + } + setTextAndState(btn_id); + SET_DIRTY(); + } + + bool isActive(uint8_t btn_id) + { + if (btn_id >= MAX_POTS) return false; + return (g_model.potsWarnEnabled & (1 << pot_idx[btn_id])) != 0; + } + + void setTextAndState(uint8_t btn_id) + { + setText(btn_id, getPotLabel(pot_idx[btn_id])); + setChecked(btn_id); + } private: uint8_t pot_idx[MAX_POTS]; }; -PreflightChecks::PreflightChecks() : Page(ICON_MODEL_SETUP) +PreflightChecks::PreflightChecks() : SubPage(ICON_MODEL_SETUP, STR_MENU_MODEL_SETUP, STR_PREFLIGHT) { - header->setTitle(STR_MENU_MODEL_SETUP); - header->setTitle2(STR_PREFLIGHT); - - body->padAll(PAD_TINY); - body->setFlexLayout(); - FlexGridLayout grid(line_col_dsc, line_row_dsc, PAD_TINY); // Display checklist - auto line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_CHECKLIST); - auto chkList = new ToggleSwitch(line, rect_t{}, - GET_SET_DEFAULT(g_model.displayChecklist)); + setupLine(STR_CHECKLIST, + [=](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_DEFAULT(g_model.displayChecklist), + [=](uint8_t newValue) { + g_model.displayChecklist = newValue; + SET_DIRTY(); + interactive->enable(g_model.displayChecklist); + }); + }); // Interactive checklist - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_CHECKLIST_INTERACTIVE); - auto interactiveChkList = new ToggleSwitch( - line, rect_t{}, GET_SET_DEFAULT(g_model.checklistInteractive)); - if (!chkList->getValue()) interactiveChkList->disable(); - chkList->setSetValueHandler([=](int32_t newValue) { - g_model.displayChecklist = newValue; - SET_DIRTY(); - (g_model.displayChecklist) ? interactiveChkList->enable() - : interactiveChkList->disable(); - }); + setupLine(STR_CHECKLIST_INTERACTIVE, + [=](Window* parent, coord_t x, coord_t y) { + interactive = new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_model.checklistInteractive)); + interactive->enable(g_model.displayChecklist); + }); // Throttle warning - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_THROTTLE_WARNING); - auto tw = new ToggleSwitch(line, rect_t{}, - GET_SET_INVERTED(g_model.disableThrottleWarning)); + setupLine(STR_THROTTLE_WARNING, + [=](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, + GET_INVERTED(g_model.disableThrottleWarning), + [=](uint8_t newValue) { + g_model.disableThrottleWarning = !newValue; + SET_DIRTY(); + customThrottle->show(!g_model.disableThrottleWarning); + }); + }); // Custom Throttle warning (conditional on previous field) - line = body->newLine(grid); - make_conditional(line, tw); - - new StaticText(line, rect_t{}, STR_CUSTOM_THROTTLE_WARNING); - auto box = new Window(line, rect_t{}); - box->padAll(PAD_TINY); - box->setFlexLayout(LV_FLEX_FLOW_ROW, PAD_TINY, LV_SIZE_CONTENT); - - auto cst_tw = new ToggleSwitch( - box, rect_t{}, GET_SET_DEFAULT(g_model.enableCustomThrottleWarning)); - - // Custom Throttle warning value - auto cst_val = - new NumberEdit(box, rect_t{}, -100, 100, - GET_SET_DEFAULT(g_model.customThrottleWarningPosition)); - make_conditional(cst_val, cst_tw); + customThrottle = setupLine(STR_CUSTOM_THROTTLE_WARNING, + [=](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_DEFAULT(g_model.enableCustomThrottleWarning), + [=](uint8_t newValue) { + g_model.enableCustomThrottleWarning = newValue; + SET_DIRTY(); + customThrottleValue->show(g_model.enableCustomThrottleWarning); + }); + + // Custom Throttle warning value + customThrottleValue = new NumberEdit(parent, {x + ToggleSwitch::TOGGLE_W + PAD_SMALL, 0, 0}, -100, 100, + GET_SET_DEFAULT(g_model.customThrottleWarningPosition)); + customThrottleValue->show(g_model.enableCustomThrottleWarning); + }); // Switch warnings (TODO: add display switch?) - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_SWITCHES); - line = body->newLine(grid); - line->padTop(0); - line->padLeft(4); - new SwitchWarnMatrix(line, rect_t{}); + setupLine(STR_SWITCHES, [](Window*, coord_t, coord_t){}); + setupLine(nullptr, + [=](Window* parent, coord_t x, coord_t y) { + auto w = new SwitchWarnMatrix(parent, rect_t{PAD_SMALL, y, 0, 0}); + parent->setHeight(w->height() + PAD_TINY * 2); + }); // Pots and sliders warning if (adcGetMaxInputs(ADC_INPUT_FLEX) > 0) { @@ -177,144 +240,24 @@ PreflightChecks::PreflightChecks() : Page(ICON_MODEL_SETUP) } } if (pot_cnt > 0) { - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_POTWARNINGSTATE); - auto pots_wm = new Choice(line, rect_t{}, STR_PREFLIGHT_POTSLIDER_CHECK, - 0, 2, GET_SET_DEFAULT(g_model.potsWarnMode)); + setupLine(STR_POTWARNINGSTATE, + [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_PREFLIGHT_POTSLIDER_CHECK, + 0, 2, GET_DEFAULT(g_model.potsWarnMode), + [=](int newValue) { + g_model.potsWarnMode = newValue; + SET_DIRTY(); + potsWarnMatrix->show(g_model.potsWarnMode > 0); + }); + }); // Pot warnings - line = body->newLine(grid); - line->padTop(0); - line->padLeft(4); - auto pwm = new PotWarnMatrix(line, rect_t{}); - make_conditional(pwm, pots_wm); + potsWarnMatrix = setupLine(nullptr, + [=](Window* parent, coord_t x, coord_t y) { + auto w = new PotWarnMatrix(parent, {PAD_SMALL, y, 0, 0}); + parent->setHeight(w->height() + PAD_TINY * 2); + }); + potsWarnMatrix->show(g_model.potsWarnMode > 0); } } } - -static std::string switchWarninglabel(swsrc_t index) -{ - auto warn_pos = g_model.switchWarning >> (3 * index) & 0x07; - return std::string(switchGetName(index)) + - std::string(getSwitchWarnSymbol(warn_pos)); -} - -SwitchWarnMatrix::SwitchWarnMatrix(Window* parent, const rect_t& r) : - ButtonMatrix(parent, r) -{ - // Setup button layout & texts - uint8_t btn_cnt = 0; - for (uint8_t i = 0; i < MAX_SWITCHES; i++) { - if (SWITCH_WARNING_ALLOWED(i)) { - sw_idx[btn_cnt] = i; - btn_cnt++; - } - } - - initBtnMap(min((int)btn_cnt, SW_BTNS), btn_cnt); - - uint8_t btn_id = 0; - for (uint8_t i = 0; i < MAX_SWITCHES; i++) { - if (SWITCH_WARNING_ALLOWED(i)) { - setTextAndState(btn_id); - btn_id++; - } - } - - update(); - - lv_obj_set_width(lvobj, min((int)btn_cnt, SW_BTNS) * SW_BTN_W + PAD_SMALL); - - uint8_t rows = ((btn_cnt - 1) / SW_BTNS) + 1; - lv_obj_set_height(lvobj, (rows * SW_BTN_H) + PAD_SMALL); - - padAll(PAD_SMALL); -} - -void SwitchWarnMatrix::setTextAndState(uint8_t btn_id) -{ - setText(btn_id, switchWarninglabel(sw_idx[btn_id]).c_str()); - setChecked(btn_id); -} - -void SwitchWarnMatrix::onPress(uint8_t btn_id) -{ - if (btn_id >= MAX_SWITCHES) return; - auto sw = sw_idx[btn_id]; - - swarnstate_t newstate = bfGet(g_model.switchWarning, 3 * sw, 3); - if (newstate == 1 && SWITCH_CONFIG(sw) != SWITCH_3POS) - newstate = 3; - else - newstate = (newstate + 1) % 4; - - g_model.switchWarning = - bfSet(g_model.switchWarning, newstate, 3 * sw, 3); - SET_DIRTY(); - - setTextAndState(btn_id); -} - -bool SwitchWarnMatrix::isActive(uint8_t btn_id) -{ - if (btn_id >= MAX_SWITCHES) return false; - return bfGet(g_model.switchWarning, 3 * sw_idx[btn_id], 3) != 0; -} - -PotWarnMatrix::PotWarnMatrix(Window* parent, const rect_t& r) : - ButtonMatrix(parent, r) -{ - // Setup button layout & texts - uint8_t btn_cnt = 0; - for (uint8_t i = 0; i < MAX_POTS; i++) { - if (IS_POT_AVAILABLE(i)) { - pot_idx[btn_cnt] = i; - btn_cnt++; - } - } - - initBtnMap(min((int)btn_cnt, SwitchWarnMatrix::SW_BTNS), btn_cnt); - - uint8_t btn_id = 0; - for (uint16_t i = 0; i < MAX_POTS; i++) { - if (IS_POT_AVAILABLE(i)) { - setTextAndState(btn_id); - btn_id++; - } - } - - update(); - - lv_obj_set_width(lvobj, min((int)btn_cnt, SwitchWarnMatrix::SW_BTNS) * SwitchWarnMatrix::SW_BTN_W + PAD_SMALL); - - uint8_t rows = ((btn_cnt - 1) / SwitchWarnMatrix::SW_BTNS) + 1; - lv_obj_set_height(lvobj, (rows * SwitchWarnMatrix::SW_BTN_H) + PAD_SMALL); - - padAll(PAD_SMALL); -} - -void PotWarnMatrix::setTextAndState(uint8_t btn_id) -{ - setText(btn_id, getPotLabel(pot_idx[btn_id])); - setChecked(btn_id); -} - -void PotWarnMatrix::onPress(uint8_t btn_id) -{ - if (btn_id >= MAX_POTS) return; - auto pot = pot_idx[btn_id]; - - g_model.potsWarnEnabled ^= (1 << pot); - if ((g_model.potsWarnMode == POTS_WARN_MANUAL) && - (g_model.potsWarnEnabled & (1 << pot))) { - SAVE_POT_POSITION(pot); - } - setTextAndState(btn_id); - SET_DIRTY(); -} - -bool PotWarnMatrix::isActive(uint8_t btn_id) -{ - if (btn_id >= MAX_POTS) return false; - return (g_model.potsWarnEnabled & (1 << pot_idx[btn_id])) != 0; -} diff --git a/radio/src/gui/colorlcd/preflight_checks.h b/radio/src/gui/colorlcd/preflight_checks.h index f7a75e4b0f2..d8eaf94935c 100644 --- a/radio/src/gui/colorlcd/preflight_checks.h +++ b/radio/src/gui/colorlcd/preflight_checks.h @@ -23,7 +23,17 @@ #include "page.h" -class PreflightChecks : public Page { +class ToggleSwitch; +class NumberEdit; + +class PreflightChecks : public SubPage +{ public: PreflightChecks(); + + protected: + ToggleSwitch* interactive = nullptr; + Window* customThrottle = nullptr; + NumberEdit* customThrottleValue = nullptr; + Window* potsWarnMatrix = nullptr; }; diff --git a/radio/src/gui/colorlcd/radio_hardware.cpp b/radio/src/gui/colorlcd/radio_hardware.cpp index 6cb5a1a4d44..c825187adbd 100644 --- a/radio/src/gui/colorlcd/radio_hardware.cpp +++ b/radio/src/gui/colorlcd/radio_hardware.cpp @@ -160,7 +160,7 @@ void RadioHardwarePage::build(Window* window) window->setFlexLayout(LV_FLEX_FLOW_COLUMN, PAD_TINY); // TODO: sub-title? - SetupLine::showLines(window, 0, EDT_X, padding, setupLines, DIM(setupLines)); + SetupLine::showLines(window, 0, SubPage::EDT_X, padding, setupLines, DIM(setupLines)); FlexGridLayout grid(col_dsc, row_dsc, PAD_TINY); diff --git a/radio/src/gui/colorlcd/radio_hardware.h b/radio/src/gui/colorlcd/radio_hardware.h index a5b513dd4b6..a56414a8267 100644 --- a/radio/src/gui/colorlcd/radio_hardware.h +++ b/radio/src/gui/colorlcd/radio_hardware.h @@ -32,7 +32,6 @@ class RadioHardwarePage : public PageTab static LAYOUT_VAL(NUM_EDIT_W, 80, 80) static LAYOUT_VAL(BTN_COLS, 4, 3) - static LAYOUT_VAL(EDT_X, 160, 131) protected: void cleanup() override; diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index 8027736abee..15f9f34a07a 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -121,9 +121,9 @@ class DateTimeWindow : public Window m_last_tm = m_tm; // Date - new StaticText(this, rect_t{PAD_TINY, DT_Y1 + PAD_MEDIUM, RadioSetupPage::LBL_W, EdgeTxStyles::PAGE_LINE_HEIGHT}, STR_DATE); + new StaticText(this, rect_t{PAD_TINY, DT_Y1 + PAD_MEDIUM, SubPage::EDT_X - PAD_TINY - PAD_SMALL, EdgeTxStyles::PAGE_LINE_HEIGHT}, STR_DATE); year = new NumberEdit( - this, rect_t{RadioSetupPage::EDT_X, DT_Y1, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 2023, 2037, + this, rect_t{SubPage::EDT_X, DT_Y1, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 2023, 2037, [=]() -> int32_t { return TM_YEAR_BASE + m_tm.tm_year; }, [=](int32_t newValue) { m_last_tm.tm_year = m_tm.tm_year = newValue - TM_YEAR_BASE; @@ -132,7 +132,7 @@ class DateTimeWindow : public Window }); month = new NumberEdit( - this, rect_t{RadioSetupPage::EDT_X + DT_EDT_W + PAD_TINY, DT_Y1, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 1, 12, + this, rect_t{SubPage::EDT_X + DT_EDT_W + PAD_TINY, DT_Y1, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 1, 12, [=]() -> int32_t { return 1 + m_tm.tm_mon; }, [=](int32_t newValue) { m_last_tm.tm_mon = m_tm.tm_mon = newValue - 1; @@ -143,7 +143,7 @@ class DateTimeWindow : public Window [](int32_t value) { return formatNumberAsString(value, LEADING0); }); day = new NumberEdit( - this, rect_t{RadioSetupPage::EDT_X + 2 * DT_EDT_W + PAD_SMALL, DT_Y1, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 1, + this, rect_t{SubPage::EDT_X + 2 * DT_EDT_W + PAD_SMALL, DT_Y1, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 1, daysInMonth(), [=]() -> int32_t { return m_tm.tm_mday; }, [=](int32_t newValue) { m_last_tm.tm_mday = m_tm.tm_mday = newValue; @@ -153,9 +153,9 @@ class DateTimeWindow : public Window [](int32_t value) { return formatNumberAsString(value, LEADING0, 2); }); // Time - new StaticText(this, rect_t{PAD_TINY, DT_Y2 + PAD_MEDIUM, RadioSetupPage::LBL_W, EdgeTxStyles::PAGE_LINE_HEIGHT}, STR_TIME); + new StaticText(this, rect_t{PAD_TINY, DT_Y2 + PAD_MEDIUM, SubPage::EDT_X - PAD_TINY - PAD_SMALL, EdgeTxStyles::PAGE_LINE_HEIGHT}, STR_TIME); hour = new NumberEdit( - this, rect_t{RadioSetupPage::EDT_X, DT_Y2, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 23, + this, rect_t{SubPage::EDT_X, DT_Y2, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 23, [=]() -> int32_t { return m_tm.tm_hour; }, [=](int32_t newValue) { m_last_tm.tm_hour = m_tm.tm_hour = newValue; @@ -165,7 +165,7 @@ class DateTimeWindow : public Window [](int32_t value) { return formatNumberAsString(value, LEADING0, 2); }); minutes = new NumberEdit( - this, rect_t{RadioSetupPage::EDT_X + DT_EDT_W + PAD_TINY, DT_Y2, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 59, + this, rect_t{SubPage::EDT_X + DT_EDT_W + PAD_TINY, DT_Y2, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 59, [=]() -> int32_t { return m_tm.tm_min; }, [=](int32_t newValue) { m_last_tm.tm_min = m_tm.tm_min = newValue; @@ -175,7 +175,7 @@ class DateTimeWindow : public Window [](int32_t value) { return formatNumberAsString(value, LEADING0, 2); }); seconds = new NumberEdit( - this, rect_t{RadioSetupPage::EDT_X + DT_EDT_W * 2 + PAD_SMALL, DT_Y2, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 59, + this, rect_t{SubPage::EDT_X + DT_EDT_W * 2 + PAD_SMALL, DT_Y2, DT_EDT_W, EdgeTxStyles::UI_ELEMENT_HEIGHT}, 0, 59, [=]() -> int32_t { return m_tm.tm_sec; }, [=](int32_t newValue) { m_last_tm.tm_sec = m_tm.tm_sec = newValue; @@ -409,7 +409,7 @@ class BacklightPage : public SubPage body->setFlexLayout(); // Backlight mode - new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_MODE, [=](Window* parent, coord_t x, coord_t y) { + setupLine(STR_MODE, [=](Window* parent, coord_t x, coord_t y) { auto blMode = new Choice( parent, {x, y, 0, 0}, STR_VBLMODE, e_backlight_mode_off, e_backlight_mode_on, GET_DEFAULT(g_eeGeneral.backlightMode), [=](int32_t newValue) { @@ -423,7 +423,7 @@ class BacklightPage : public SubPage }); // Delay - backlightTimeout = new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_BACKLIGHT_TIMER, [=](Window* parent, coord_t x, coord_t y) { + backlightTimeout = setupLine(STR_BACKLIGHT_TIMER, [=](Window* parent, coord_t x, coord_t y) { auto edit = new NumberEdit(parent, {x, y, RadioSetupPage::NUM_W, 0}, 5, 600, GET_DEFAULT(g_eeGeneral.lightAutoOff * 5), @@ -433,7 +433,7 @@ class BacklightPage : public SubPage }); // Backlight ON bright - backlightOnBright = new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_BLONBRIGHTNESS, [=](Window* parent, coord_t x, coord_t y) { + backlightOnBright = setupLine(STR_BLONBRIGHTNESS, [=](Window* parent, coord_t x, coord_t y) { backlightOnSlider = new Slider( parent, lv_pct(50), BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, [=]() -> int32_t { @@ -454,7 +454,7 @@ class BacklightPage : public SubPage }); // Backlight OFF bright - backlightOffBright = new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_BLOFFBRIGHTNESS, [=](Window* parent, coord_t x, coord_t y) { + backlightOffBright = setupLine(STR_BLOFFBRIGHTNESS, [=](Window* parent, coord_t x, coord_t y) { backlightOffSlider = new Slider( parent, lv_pct(50), BACKLIGHT_LEVEL_MIN, BACKLIGHT_LEVEL_MAX, GET_DEFAULT(g_eeGeneral.blOffBright), @@ -474,14 +474,14 @@ class BacklightPage : public SubPage #if defined(KEYS_BACKLIGHT_GPIO) // Keys backlight - new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_KEYS_BACKLIGHT, [=](Window* parent, coord_t x, coord_t y) { + setupLine(STR_KEYS_BACKLIGHT, [=](Window* parent, coord_t x, coord_t y) { new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_eeGeneral.keysBacklight)); }); #endif // Flash beep - new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_ALARM, [=](Window* parent, coord_t x, coord_t y) { + setupLine(STR_ALARM, [=](Window* parent, coord_t x, coord_t y) { new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_eeGeneral.alarmsFlash)); }); @@ -687,13 +687,13 @@ class ManageModelsSetupPage : public SubPage body->setFlexLayout(); // Model quick select - new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_MODEL_QUICK_SELECT, [=](Window* parent, coord_t x, coord_t y) { + setupLine(STR_MODEL_QUICK_SELECT, [=](Window* parent, coord_t x, coord_t y) { new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_eeGeneral.modelQuickSelect)); }); // Label single/multi select - new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_LABELS_SELECT, [=](Window* parent, coord_t x, coord_t y) { + setupLine(STR_LABELS_SELECT, [=](Window* parent, coord_t x, coord_t y) { new Choice(parent, {x, y, 0, 0}, STR_LABELS_SELECT_MODE, 0, 1, GET_DEFAULT(g_eeGeneral.labelSingleSelect), [=](int newValue) { @@ -704,13 +704,13 @@ class ManageModelsSetupPage : public SubPage }); // Label multi select matching mode - multiSelectMatch = new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_LABELS_MATCH, [=](Window* parent, coord_t x, coord_t y) { + multiSelectMatch = setupLine(STR_LABELS_MATCH, [=](Window* parent, coord_t x, coord_t y) { new Choice(parent, {x, y, 0, 0}, STR_LABELS_MATCH_MODE, 0, 1, GET_SET_DEFAULT(g_eeGeneral.labelMultiMode)); }); // Favorites multi select matching mode - favSelectMatch = new SetupLine(body, 0, RadioSetupPage::EDT_X, PAD_SMALL, STR_FAV_MATCH, [=](Window* parent, coord_t x, coord_t y) { + favSelectMatch = setupLine(STR_FAV_MATCH, [=](Window* parent, coord_t x, coord_t y) { new Choice(parent, {x, y, 0, 0}, STR_FAV_MATCH_MODE, 0, 1, GET_SET_DEFAULT(g_eeGeneral.favMultiMode)); }); @@ -928,5 +928,5 @@ void RadioSetupPage::build(Window* window) }, BTN_H); y += w->height() + padding; - SetupLine::showLines(window, y, EDT_X, padding, setupLines, DIM(setupLines)); + SetupLine::showLines(window, y, SubPage::EDT_X, padding, setupLines, DIM(setupLines)); } diff --git a/radio/src/gui/colorlcd/radio_setup.h b/radio/src/gui/colorlcd/radio_setup.h index d98eb55325c..3a1cd0bd23c 100644 --- a/radio/src/gui/colorlcd/radio_setup.h +++ b/radio/src/gui/colorlcd/radio_setup.h @@ -30,8 +30,6 @@ class RadioSetupPage: public PageTab { void build(Window * window) override; static LAYOUT_VAL(NUM_W, 80, 80) - static LAYOUT_VAL(EDT_X, 220, 144) - static constexpr coord_t LBL_W = EDT_X - PAD_TINY - PAD_SMALL; static LAYOUT_VAL(BTN_COLS, 3, 2) static LAYOUT_VAL(BTN_H, 62, 62) }; diff --git a/radio/src/gui/colorlcd/throttle_params.cpp b/radio/src/gui/colorlcd/throttle_params.cpp index a47d921aba5..a737ead87de 100644 --- a/radio/src/gui/colorlcd/throttle_params.cpp +++ b/radio/src/gui/colorlcd/throttle_params.cpp @@ -26,64 +26,52 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -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 SetupLineDef setupLines[] = { + { + // Throttle reversed + STR_THROTTLEREVERSE, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_model.throttleReversed)); + } + }, + { + // Throttle source + STR_TTRACE, + [](Window* parent, coord_t x, coord_t y) { + auto sc = new SourceChoice(parent, {x, y, 0, 0}, 0, MIXSRC_LAST_CH, + []() {return throttleSource2Source(g_model.thrTraceSrc); }, + [](int16_t src) { + int16_t val = source2ThrottleSource(src); + if (val >= 0) { + g_model.thrTraceSrc = val; + SET_DIRTY(); + } + }); + sc->setAvailableHandler(isThrottleSourceAvailable); + } + }, + { + // Throttle trim + STR_TTRIM, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_model.thrTrim)); + } + }, + { + // Throttle trim source + STR_TTRIM_SW, + [](Window* parent, coord_t x, coord_t y) { + new SourceChoice( + parent, {x, y, 0, 0}, MIXSRC_FIRST_TRIM, MIXSRC_LAST_TRIM, + []() { return g_model.getThrottleStickTrimSource(); }, + [](int16_t src) { + g_model.setThrottleStickTrimSource(src); + SET_DIRTY(); + }); + } + }, +}; -static int16_t getThrottleSource() +ThrottleParams::ThrottleParams() : SubPage(ICON_MODEL_SETUP, STR_MENU_MODEL_SETUP, STR_THROTTLE_LABEL, setupLines, DIM(setupLines)) { - return throttleSource2Source(g_model.thrTraceSrc); -} - -static void setThrottleSource(int16_t src) -{ - int16_t val = source2ThrottleSource(src); - if (val >= 0) { - g_model.thrTraceSrc = val; - SET_DIRTY(); - } -} - -static int16_t getThrottleTrimSource() -{ - return g_model.getThrottleStickTrimSource(); -} - -static void setThrottleTrimSource(int16_t src) -{ - g_model.setThrottleStickTrimSource(src); -} - -ThrottleParams::ThrottleParams() : Page(ICON_MODEL_SETUP) -{ - header->setTitle(STR_MENU_MODEL_SETUP); - header->setTitle2(STR_THROTTLE_LABEL); - - body->setFlexLayout(); - FlexGridLayout grid(line_col_dsc, line_row_dsc); - - // Throttle reversed - auto line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_THROTTLEREVERSE); - new ToggleSwitch(line, rect_t{}, GET_SET_DEFAULT(g_model.throttleReversed)); - - // Throttle source - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_TTRACE); - auto sc = new SourceChoice(line, rect_t{}, 0, MIXSRC_LAST_CH, - getThrottleSource, setThrottleSource); - sc->setAvailableHandler(isThrottleSourceAvailable); - - // Throttle trim - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_TTRIM); - new ToggleSwitch(line, rect_t{}, GET_SET_DEFAULT(g_model.thrTrim)); - - // Throttle trim source - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_TTRIM_SW); - new SourceChoice( - line, rect_t{}, MIXSRC_FIRST_TRIM, MIXSRC_LAST_TRIM, - getThrottleTrimSource, setThrottleTrimSource); } diff --git a/radio/src/gui/colorlcd/throttle_params.h b/radio/src/gui/colorlcd/throttle_params.h index 86997affc22..0d481b90421 100644 --- a/radio/src/gui/colorlcd/throttle_params.h +++ b/radio/src/gui/colorlcd/throttle_params.h @@ -23,6 +23,8 @@ #include "page.h" -struct ThrottleParams : public Page { +class ThrottleParams : public SubPage +{ + public: ThrottleParams(); }; diff --git a/radio/src/gui/colorlcd/timer_setup.cpp b/radio/src/gui/colorlcd/timer_setup.cpp index 2f8fe020562..8a5c59e4876 100644 --- a/radio/src/gui/colorlcd/timer_setup.cpp +++ b/radio/src/gui/colorlcd/timer_setup.cpp @@ -27,128 +27,93 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -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 std::function timerValueUpdater(uint8_t timer) -{ - return [=](uint32_t value) { - TimerData* p_timer = &g_model.timers[timer]; - p_timer->start = value; - timerSet(timer, value); - SET_DIRTY(); - }; -} - -static void timer_start_changed(lv_event_t* e) -{ - lv_obj_t* target = lv_event_get_target(e); - auto obj = (lv_obj_t*)lv_event_get_user_data(e); - auto val = (TimeEdit*)lv_obj_get_user_data(target); - - if (!obj || !val) return; - - if (val->getValue() > 0) { - lv_obj_clear_flag(obj, LV_OBJ_FLAG_HIDDEN); - } else { - lv_obj_add_flag(obj, LV_OBJ_FLAG_HIDDEN); - } -} - -TimerWindow::TimerWindow(uint8_t timer) : Page(ICON_STATS_TIMERS) +TimerWindow::TimerWindow(uint8_t timer) : + SubPage(ICON_STATS_TIMERS, STR_MENU_MODEL_SETUP, (std::string(STR_TIMER) + std::to_string(timer + 1)).c_str()) { - std::string title2 = std::string(STR_TIMER) + std::to_string(timer + 1); - header->setTitle(STR_MENU_MODEL_SETUP); - header->setTitle2(title2); - body->setFlexLayout(); - FlexGridLayout grid(line_col_dsc, line_row_dsc, PAD_TINY); - TimerData* p_timer = &g_model.timers[timer]; // Timer name - auto line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_NAME); - new ModelTextEdit(line, rect_t{}, p_timer->name, LEN_TIMER_NAME); + setupLine(STR_NAME, + [=](Window* parent, coord_t x, coord_t y) { + new ModelTextEdit(parent, {x, y, 0, 0}, p_timer->name, LEN_TIMER_NAME); + }); // Timer mode - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_MODE); - new Choice(line, rect_t{}, STR_TIMER_MODES, 0, TMRMODE_MAX, - GET_SET_DEFAULT(p_timer->mode)); + setupLine(STR_MODE, + [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_TIMER_MODES, 0, TMRMODE_MAX, + GET_SET_DEFAULT(p_timer->mode)); + }); // Timer switch - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_SWITCH); - new SwitchChoice(line, rect_t{}, SWSRC_FIRST, SWSRC_LAST, - GET_SET_DEFAULT(p_timer->swtch)); + setupLine(STR_SWITCH, + [=](Window* parent, coord_t x, coord_t y) { + new SwitchChoice(parent, rect_t{x, y, 0, 0}, SWSRC_FIRST, SWSRC_LAST, + GET_SET_DEFAULT(p_timer->swtch)); + }); // Timer start value - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_START); - auto timerValue = - new TimeEdit(line, rect_t{}, 0, TIMER_MAX, - GET_DEFAULT(p_timer->start), timerValueUpdater(timer)); - timerValue->setAccelFactor(16); + setupLine(STR_START, + [=](Window* parent, coord_t x, coord_t y) { + auto timerValue = new TimeEdit(parent, {x, y, 0, 0}, 0, TIMER_MAX, + GET_DEFAULT(p_timer->start), [=](int newValue) { + p_timer->start = newValue; + timerSet(timer, newValue); + timerDirLine->show(newValue > 0); + SET_DIRTY(); + }); + timerValue->setAccelFactor(16); + }); // Timer direction - auto timerDirLine = body->newLine(grid); - new StaticText(timerDirLine, rect_t{}, STR_LIMITS_HEADERS_DIRECTION); - new Choice(timerDirLine, rect_t{}, STR_TIMER_DIR, 0, 1, - GET_SET_DEFAULT(p_timer->showElapsed)); - - if (timerValue->getValue() == 0) { - timerDirLine->hide(); - } + timerDirLine = setupLine(STR_LIMITS_HEADERS_DIRECTION, + [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_TIMER_DIR, 0, 1, + GET_SET_DEFAULT(p_timer->showElapsed)); + }); + timerDirLine->show(p_timer->start> 0); // Timer minute beep - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_MINUTEBEEP); - new ToggleSwitch(line, rect_t{}, GET_SET_DEFAULT(p_timer->minuteBeep)); + setupLine(STR_MINUTEBEEP, + [=](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(p_timer->minuteBeep)); + }); // Timer countdown - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_BEEPCOUNTDOWN); - - auto box = new Window(line, rect_t{}); - box->padAll(PAD_TINY); - box->setFlexLayout(LV_FLEX_FLOW_ROW, PAD_TINY, LV_SIZE_CONTENT); - - new Choice( - box, rect_t{}, STR_VBEEPCOUNTDOWN, COUNTDOWN_SILENT, COUNTDOWN_COUNT - 1, - [=]() -> int { - int value = p_timer->countdownBeep; - if (p_timer->extraHaptic) { - value += (COUNTDOWN_NON_HAPTIC_LAST + 1); - } - return (value); - }, - [=](int value) { - if (value > COUNTDOWN_NON_HAPTIC_LAST + 1) { - p_timer->extraHaptic = 1; - p_timer->countdownBeep = value - (COUNTDOWN_NON_HAPTIC_LAST + 1); - } else { - p_timer->extraHaptic = 0; - p_timer->countdownBeep = value; - } - SET_DIRTY(); - TRACE("value=%d\tcountdownBeep = %d\textraHaptic = %d", value, - p_timer->countdownBeep, p_timer->extraHaptic); - }); - - new Choice(box, rect_t{}, STR_COUNTDOWNVALUES, 0, 3, - GET_SET_WITH_OFFSET(p_timer->countdownStart, 2)); + setupLine(STR_BEEPCOUNTDOWN, + [=](Window* parent, coord_t x, coord_t y) { + new Choice( + parent, rect_t{x, y, COUNTDOWN_W, 0}, STR_VBEEPCOUNTDOWN, COUNTDOWN_SILENT, COUNTDOWN_COUNT - 1, + [=]() -> int { + int value = p_timer->countdownBeep; + if (p_timer->extraHaptic) { + value += (COUNTDOWN_NON_HAPTIC_LAST + 1); + } + return (value); + }, + [=](int value) { + if (value > COUNTDOWN_NON_HAPTIC_LAST + 1) { + p_timer->extraHaptic = 1; + p_timer->countdownBeep = value - (COUNTDOWN_NON_HAPTIC_LAST + 1); + } else { + p_timer->extraHaptic = 0; + p_timer->countdownBeep = value; + } + SET_DIRTY(); + TRACE("value=%d\tcountdownBeep = %d\textraHaptic = %d", value, + p_timer->countdownBeep, p_timer->extraHaptic); + }); + + new Choice(parent, {x + COUNTDOWN_VAL_XO, y + COUNTDOWN_VAL_YO, 0, 0}, STR_COUNTDOWNVALUES, 0, 3, + GET_SET_WITH_OFFSET(p_timer->countdownStart, 2)); + }, COUNTDOWN_LBL_YO); // Timer persistent - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_PERSISTENT); - - new Choice(line, rect_t{}, STR_VPERSISTENT, 0, 2, - GET_SET_DEFAULT(p_timer->persistent)); - - lv_obj_add_event_cb(timerValue->getLvObj(), timer_start_changed, - LV_EVENT_VALUE_CHANGED, timerDirLine->getLvObj()); + setupLine(STR_PERSISTENT, + [=](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_VPERSISTENT, 0, 2, + GET_SET_DEFAULT(p_timer->persistent)); + }); } diff --git a/radio/src/gui/colorlcd/timer_setup.h b/radio/src/gui/colorlcd/timer_setup.h index 28222f3d21d..9feae57022e 100644 --- a/radio/src/gui/colorlcd/timer_setup.h +++ b/radio/src/gui/colorlcd/timer_setup.h @@ -23,6 +23,16 @@ #include "page.h" -struct TimerWindow : public Page { +class TimerWindow : public SubPage +{ + public: TimerWindow(uint8_t timer); + + static LAYOUT_VAL(COUNTDOWN_W, 144, 144) + static LAYOUT_VAL(COUNTDOWN_LBL_YO, 0, (EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY) / 2) + static LAYOUT_VAL(COUNTDOWN_VAL_XO, COUNTDOWN_W + PAD_SMALL, 0) + static LAYOUT_VAL(COUNTDOWN_VAL_YO, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY) + + protected: + Window* timerDirLine = nullptr; }; diff --git a/radio/src/gui/colorlcd/trims_setup.cpp b/radio/src/gui/colorlcd/trims_setup.cpp index dee05cc2889..c565b147e1a 100644 --- a/radio/src/gui/colorlcd/trims_setup.cpp +++ b/radio/src/gui/colorlcd/trims_setup.cpp @@ -25,68 +25,60 @@ #define SET_DIRTY() storageDirty(EE_MODEL) -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 void resetTrims() -{ - for (auto &fm : g_model.flightModeData) memclear(&fm.trim, sizeof(fm.trim)); - SET_DIRTY(); - AUDIO_WARNING1(); -} - -TrimsSetup::TrimsSetup() : Page(ICON_MODEL_SETUP) -{ - header->setTitle(STR_MENU_MODEL_SETUP); - header->setTitle2(STR_TRIMS); - - body->setFlexLayout(); - body->padAll(PAD_LARGE); - FlexGridLayout grid(line_col_dsc, line_row_dsc); - - // Reset trims - auto line = body->newLine(grid); - line->padBottom(4); - auto btn = new TextButton(line, rect_t{}, STR_RESET_BTN, []() -> uint8_t { - resetTrims(); - return 0; - }); - auto btn_obj = btn->getLvObj(); - lv_obj_set_width(btn_obj, lv_pct(100)); - +static SetupLineDef setupLines[] = { + { + // Reset trims + nullptr, + [](Window* parent, coord_t x, coord_t y) { + new TextButton(parent, {PAD_TINY, y, LCD_W - PAD_MEDIUM * 2, 0}, STR_RESET_BTN, []() -> uint8_t { + for (auto &fm : g_model.flightModeData) memclear(&fm.trim, sizeof(fm.trim)); + SET_DIRTY(); + AUDIO_WARNING1(); + return 0; + }); + } + }, #if defined(USE_HATS_AS_KEYS) - // Hats mode for NV14/EL18 - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_HATSMODE); - auto box = new Window(line, rect_t{}); - box->padAll(PAD_TINY); - box->setFlexLayout(LV_FLEX_FLOW_ROW, PAD_TINY); - new Choice(box, rect_t{}, STR_HATSOPT, HATSMODE_TRIMS_ONLY, HATSMODE_GLOBAL, - GET_SET_DEFAULT(g_model.hatsMode)); - new TextButton(box, rect_t{}, "?", [=]() { - new MessageDialog(this, STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", - LEFT); - return 0; - }); + { + // Hats mode for NV14/EL18 + STR_HATSMODE, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, TrimsSetup::HATSMODE_W, 0}, STR_HATSOPT, HATSMODE_TRIMS_ONLY, HATSMODE_GLOBAL, + GET_SET_DEFAULT(g_model.hatsMode)); + new TextButton(parent, {x + TrimsSetup::HATSMODE_W + PAD_SMALL, y, 0, 0}, "?", [=]() { + new MessageDialog(parent, STR_HATSMODE_KEYS, STR_HATSMODE_KEYS_HELP, "", + LEFT); + return 0; + }); + } + }, #endif + { + // Trim step + STR_TRIMINC, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_VTRIMINC, -2, 2, + GET_SET_DEFAULT(g_model.trimInc)); + } + }, + { + // Extended trims + STR_ETRIMS, + [](Window* parent, coord_t x, coord_t y) { + new ToggleSwitch(parent, {x, y, 0, 0}, GET_SET_DEFAULT(g_model.extendedTrims)); + } + }, + { + // Display trims + // TODO: move to "Screen setup" ? + STR_DISPLAY_TRIMS, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, 0}, STR_VDISPLAYTRIMS, 0, 2, + GET_SET_DEFAULT(g_model.displayTrims)); + } + }, +}; - // Trim step - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_TRIMINC); - new Choice(line, rect_t{}, STR_VTRIMINC, -2, 2, - GET_SET_DEFAULT(g_model.trimInc)); - - // Extended trims - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_ETRIMS); - new ToggleSwitch(line, rect_t{}, GET_SET_DEFAULT(g_model.extendedTrims)); - - // Display trims - // TODO: move to "Screen setup" ? - line = body->newLine(grid); - new StaticText(line, rect_t{}, STR_DISPLAY_TRIMS); - new Choice(line, rect_t{}, STR_VDISPLAYTRIMS, 0, 2, - GET_SET_DEFAULT(g_model.displayTrims)); +TrimsSetup::TrimsSetup() : SubPage(ICON_MODEL_SETUP, STR_MENU_MODEL_SETUP, STR_TRIMS, setupLines, DIM(setupLines)) +{ } diff --git a/radio/src/gui/colorlcd/trims_setup.h b/radio/src/gui/colorlcd/trims_setup.h index d5845d3a9b1..9313d947a1b 100644 --- a/radio/src/gui/colorlcd/trims_setup.h +++ b/radio/src/gui/colorlcd/trims_setup.h @@ -23,6 +23,10 @@ #include "page.h" -struct TrimsSetup : public Page { +class TrimsSetup : public SubPage +{ + public: TrimsSetup(); + + static LAYOUT_VAL(HATSMODE_W, 120, 120) }; diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 6159b7dffb5..18d03e161bd 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -75,6 +75,10 @@ ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type) : lv_obj_add_event_cb(lvobj, choice_changed_cb, LV_EVENT_VALUE_CHANGED, lvobj); label = lv_label_create(lvobj); lv_obj_set_pos(label, ICON_W, PAD_TINY); + + lv_group_t* def_group = lv_group_get_default(); + if (def_group) + lv_group_add_obj(def_group, lvobj); } std::string Choice::getLabelText() diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 9cd0d30f53e..f1bb8c79ede 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -486,30 +486,31 @@ SetupButtonGroup::SetupButtonGroup(Window* parent, const rect_t& rect, const cha } } -SetupLine::SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const char* title, std::function createEdit) : +SetupLine::SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const char* title, + std::function createEdit, coord_t lblYOffset) : Window(parent, {0, y, LCD_W - padding * 2, 0}) { padAll(PAD_ZERO); - coord_t titleY = PAD_MEDIUM + 1; + coord_t titleY = PAD_MEDIUM + 1 + lblYOffset; coord_t titleH = EdgeTxStyles::PAGE_LINE_HEIGHT; + coord_t h = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2 + lblYOffset * 2; if (createEdit) { coord_t editY = PAD_TINY; coord_t lblWidth = col2 - PAD_SMALL - PAD_TINY; if (title) { if (getTextWidth(title) >= lblWidth) { - setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2 + PAD_MEDIUM); + h += PAD_MEDIUM; titleY = 0; titleH = EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY + 1; editY = PAD_SMALL + 1; - } else { - setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); } new StaticText(this, {PAD_TINY, titleY, lblWidth, titleH}, title); } + setHeight(h); createEdit(this, col2, editY); } else { + setHeight(h); new StaticText(this, {0, titleY, 0, titleH}, title, COLOR_THEME_PRIMARY1 | FONT(BOLD)); - setHeight(EdgeTxStyles::UI_ELEMENT_HEIGHT + PAD_TINY * 2); } } diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 235cf6cb586..78b1568e0e7 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -259,7 +259,8 @@ struct SetupLineDef { class SetupLine : public Window { public: - SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const char* title, std::function createEdit); + SetupLine(Window* parent, coord_t y, coord_t col2, PaddingSize padding, const char* title, + std::function createEdit, coord_t lblYOffset = 0); static coord_t showLines(Window* parent, coord_t y, coord_t col2, PaddingSize padding, SetupLineDef* setupLines, int lineCount); From fe0d45ee20f6ed995e304b3ca1563c5ea8a9dc3f Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 29 May 2024 11:50:07 +1000 Subject: [PATCH 16/21] Fix PL18 orientation. --- radio/src/targets/pl18/hal.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radio/src/targets/pl18/hal.h b/radio/src/targets/pl18/hal.h index 47dd3747a0e..8d48f7e2bd9 100644 --- a/radio/src/targets/pl18/hal.h +++ b/radio/src/targets/pl18/hal.h @@ -696,8 +696,8 @@ #define MIXER_SCHEDULER_TIMER_IRQn TIM8_BRK_TIM12_IRQn #define MIXER_SCHEDULER_TIMER_IRQHandler TIM8_BRK_TIM12_IRQHandler -#define PORTRAIT_LCD true -#define LANDSCAPE_LCD false +#define PORTRAIT_LCD false +#define LANDSCAPE_LCD true #define LCD_W 480 #define LCD_H 320 From bd298666eb8bc5eb415a7f21d28f98052d4f0483 Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 29 May 2024 12:47:12 +1000 Subject: [PATCH 17/21] Fix rebase --- .../linker/stm32f429_sdram/layout.ld | 2 +- radio/src/gui/colorlcd/LvglWrapper.cpp | 14 ++++-- radio/src/gui/colorlcd/channel_bar.cpp | 5 --- radio/src/gui/colorlcd/hw_bluetooth.cpp | 2 +- radio/src/gui/colorlcd/lcd.cpp | 4 ++ radio/src/gui/colorlcd/model_flightmodes.cpp | 5 +++ radio/src/gui/colorlcd/model_gvars.cpp | 5 +++ .../gui/colorlcd/model_logical_switches.cpp | 5 +++ radio/src/gui/colorlcd/model_outputs.cpp | 5 +++ radio/src/gui/colorlcd/model_select.cpp | 24 +++------- radio/src/gui/colorlcd/model_telemetry.cpp | 5 +++ radio/src/gui/colorlcd/page.cpp | 19 ++++++-- radio/src/gui/colorlcd/page.h | 6 ++- radio/src/gui/colorlcd/radio_setup.cpp | 16 ++++++- radio/src/gui/colorlcd/special_functions.cpp | 5 +++ radio/src/gui/colorlcd/startup_shutdown.cpp | 5 +-- radio/src/gui/colorlcd/tabsgroup.cpp | 6 +++ .../src/gui/colorlcd/themes/etx_lv_theme.cpp | 44 +++++++------------ radio/src/gui/colorlcd/themes/etx_lv_theme.h | 11 ----- radio/src/thirdparty/libopenui/src/choice.cpp | 3 +- radio/src/thirdparty/libopenui/src/static.cpp | 1 - radio/src/thirdparty/libopenui/src/textedit.h | 2 + radio/src/thirdparty/libopenui/src/window.h | 3 -- 23 files changed, 114 insertions(+), 83 deletions(-) diff --git a/radio/src/boards/generic_stm32/linker/stm32f429_sdram/layout.ld b/radio/src/boards/generic_stm32/linker/stm32f429_sdram/layout.ld index 1f83dbe3bb1..b36039941b3 100644 --- a/radio/src/boards/generic_stm32/linker/stm32f429_sdram/layout.ld +++ b/radio/src/boards/generic_stm32/linker/stm32f429_sdram/layout.ld @@ -9,7 +9,7 @@ MAIN_STACK_SIZE = 8192; BOOTLOADER_SIZE = 0x20000; /* Minimum Heap Size (indicative) */ -MIN_HEAP_SIZE = 4096K; +MIN_HEAP_SIZE = 0; /* SDRAM definitions */ SDRAM_START = DEFINED(__sdram_start) ? __sdram_start : 0xD0000000; diff --git a/radio/src/gui/colorlcd/LvglWrapper.cpp b/radio/src/gui/colorlcd/LvglWrapper.cpp index 293eed71465..ea5a41ec520 100644 --- a/radio/src/gui/colorlcd/LvglWrapper.cpp +++ b/radio/src/gui/colorlcd/LvglWrapper.cpp @@ -331,13 +331,19 @@ static void init_lvgl_drivers() void initLvglTheme() { + static lv_theme_t theme; + /* Initialize the ETX theme */ - lv_theme_t* th = etx_lv_theme_init( - NULL, lv_palette_main(LV_PALETTE_BLUE), lv_palette_main(LV_PALETTE_RED), - LV_FONT_DEFAULT); + theme.disp = NULL; + theme.color_primary = lv_palette_main(LV_PALETTE_BLUE); + theme.color_secondary = lv_palette_main(LV_PALETTE_RED); + theme.font_small = LV_FONT_DEFAULT; + theme.font_normal = LV_FONT_DEFAULT; + theme.font_large = LV_FONT_DEFAULT; + theme.flags = 0; /* Assign the theme to the current display*/ - lv_disp_set_theme(NULL, th); + lv_disp_set_theme(NULL, &theme); } LvglWrapper::LvglWrapper() diff --git a/radio/src/gui/colorlcd/channel_bar.cpp b/radio/src/gui/colorlcd/channel_bar.cpp index e9f00953690..8ce12dfc8c2 100644 --- a/radio/src/gui/colorlcd/channel_bar.cpp +++ b/radio/src/gui/colorlcd/channel_bar.cpp @@ -72,8 +72,6 @@ void ChannelBar::checkEvents() if (value != newValue) { value = newValue; - lv_obj_enable_style_refresh(false); - lv_label_set_text_fmt(valText, "%d%%", value); if (newValue < 0) @@ -91,9 +89,6 @@ void ChannelBar::checkEvents() lv_obj_set_pos(bar, x, 0); lv_obj_set_size(bar, size, height()); - - lv_obj_enable_style_refresh(true); - lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } } diff --git a/radio/src/gui/colorlcd/hw_bluetooth.cpp b/radio/src/gui/colorlcd/hw_bluetooth.cpp index 2434c81fbe7..721b9e0d3d4 100644 --- a/radio/src/gui/colorlcd/hw_bluetooth.cpp +++ b/radio/src/gui/colorlcd/hw_bluetooth.cpp @@ -78,7 +78,7 @@ BluetoothConfigWindow::BluetoothConfigWindow(Window* parent, FlexGridLayout& gri lv_obj_set_style_grid_cell_x_align(box->getLvObj(), LV_GRID_ALIGN_STRETCH, 0); lv_obj_set_style_flex_cross_place(box->getLvObj(), LV_FLEX_ALIGN_CENTER, 0); - auto mode = new Choice( + new Choice( box, rect_t{}, STR_BLUETOOTH_MODES, BLUETOOTH_OFF, BLUETOOTH_TRAINER, GET_DEFAULT(g_eeGeneral.bluetoothMode), [=](int value) { g_eeGeneral.bluetoothMode = value; diff --git a/radio/src/gui/colorlcd/lcd.cpp b/radio/src/gui/colorlcd/lcd.cpp index adbbc635749..6e758058564 100644 --- a/radio/src/gui/colorlcd/lcd.cpp +++ b/radio/src/gui/colorlcd/lcd.cpp @@ -26,6 +26,7 @@ #include "bitmapbuffer.h" #include "board.h" #include "dma2d.h" +#include "themes/etx_lv_theme.h" pixel_t LCD_FIRST_FRAME_BUFFER[DISPLAY_BUFFER_SIZE] __SDRAM; pixel_t LCD_SECOND_FRAME_BUFFER[DISPLAY_BUFFER_SIZE] __SDRAM; @@ -141,6 +142,9 @@ void lcdInitDisplayDriver() if (disp != nullptr) return; lv_init(); +#if !defined(BOOT) + useMainStyle(); +#endif // Clear buffers first memset(LCD_FIRST_FRAME_BUFFER, 0, sizeof(LCD_FIRST_FRAME_BUFFER)); diff --git a/radio/src/gui/colorlcd/model_flightmodes.cpp b/radio/src/gui/colorlcd/model_flightmodes.cpp index d7e55c5642d..76c1df4f034 100644 --- a/radio/src/gui/colorlcd/model_flightmodes.cpp +++ b/radio/src/gui/colorlcd/model_flightmodes.cpp @@ -228,6 +228,8 @@ class FlightModeBtn : public ListLineButton { init = true; + lv_obj_enable_style_refresh(false); + check(isActive()); fmID = etx_create(&fm_id_class, lvobj); @@ -253,6 +255,9 @@ class FlightModeBtn : public ListLineButton fmFadeOut = etx_create(&fm_fade_class, lvobj); lv_obj_set_pos(fmFadeOut, FADE_X + FADE_W + 1, FADE_Y); lv_obj_update_layout(lvobj); + + lv_obj_enable_style_refresh(true); + lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } bool isActive() const override { return (getFlightMode() == index); } diff --git a/radio/src/gui/colorlcd/model_gvars.cpp b/radio/src/gui/colorlcd/model_gvars.cpp index 6be86a030f5..f456be3e492 100644 --- a/radio/src/gui/colorlcd/model_gvars.cpp +++ b/radio/src/gui/colorlcd/model_gvars.cpp @@ -115,6 +115,8 @@ class GVarButton : public ListLineButton init =true; + lv_obj_enable_style_refresh(false); + currentFlightMode = getFlightMode(); auto nm = lv_label_create(lvobj); @@ -142,6 +144,9 @@ class GVarButton : public ListLineButton } lv_obj_update_layout(lvobj); + + lv_obj_enable_style_refresh(true); + lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } void updateValueText(uint8_t flightMode) diff --git a/radio/src/gui/colorlcd/model_logical_switches.cpp b/radio/src/gui/colorlcd/model_logical_switches.cpp index 572b700f86c..8409f0a036b 100644 --- a/radio/src/gui/colorlcd/model_logical_switches.cpp +++ b/radio/src/gui/colorlcd/model_logical_switches.cpp @@ -324,6 +324,8 @@ class LogicalSwitchButton : public ListLineButton { init = true; + lv_obj_enable_style_refresh(false); + lsName = lv_label_create(lvobj); etx_obj_add_style(lsName, styles->text_align_left, LV_PART_MAIN); lv_obj_set_pos(lsName, NM_X, NM_Y); @@ -361,6 +363,9 @@ class LogicalSwitchButton : public ListLineButton lv_obj_set_size(lsDelay, DEL_W, DEL_H); lv_obj_update_layout(lvobj); + + lv_obj_enable_style_refresh(true); + lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } bool isActive() const override diff --git a/radio/src/gui/colorlcd/model_outputs.cpp b/radio/src/gui/colorlcd/model_outputs.cpp index 4445a0b0fcc..c710cef49e3 100644 --- a/radio/src/gui/colorlcd/model_outputs.cpp +++ b/radio/src/gui/colorlcd/model_outputs.cpp @@ -59,6 +59,8 @@ class OutputLineButton : public ListLineButton { init = true; + lv_obj_enable_style_refresh(false); + source = lv_label_create(lvobj); lv_obj_set_pos(source, SRC_X, SRC_Y); lv_obj_set_size(source, SRC_W, SRC_H); @@ -104,6 +106,9 @@ class OutputLineButton : public ListLineButton checkEvents(); lv_obj_update_layout(lvobj); + + lv_obj_enable_style_refresh(true); + lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } public: diff --git a/radio/src/gui/colorlcd/model_select.cpp b/radio/src/gui/colorlcd/model_select.cpp index 3017a1b2ab8..07cd546d818 100644 --- a/radio/src/gui/colorlcd/model_select.cpp +++ b/radio/src/gui/colorlcd/model_select.cpp @@ -458,7 +458,7 @@ class ModelLayoutButton : public IconButton //----------------------------------------------------------------------------- -ModelLabelsWindow::ModelLabelsWindow() : Page(ICON_MODEL, PAD_ZERO) +ModelLabelsWindow::ModelLabelsWindow() : Page(ICON_MODEL, PAD_ZERO, true) { buildHead(header); buildBody(body); @@ -479,6 +479,8 @@ ModelLabelsWindow::ModelLabelsWindow() : Page(ICON_MODEL, PAD_ZERO) lblselector->setSelected(getLabels().size() - 1); } } + + enableRefresh(); } #if defined(HARDWARE_KEYS) @@ -792,29 +794,13 @@ void ModelLabelsWindow::buildBody(Window *window) if (modelslabels.getLabels().size() > 1) { if (selected != 0) { menu->addLine(STR_MOVE_UP, [=]() { - modelslabels.moveLabelTo(selected, selected - 1); - std::set newset; - newset.insert(selected - 1); - auto labels = getLabels(); - lblselector->setNames(labels); - lblselector->setSelected(newset); - if (g_eeGeneral.labelSingleSelect) - lblselector->setActiveItem(selected - 1); - updateFilteredLabels(newset); + moveLabel(selected, -1); return 0; }); } if (selected != (int)modelslabels.getLabels().size() - 1) { menu->addLine(STR_MOVE_DOWN, [=]() { - modelslabels.moveLabelTo(selected, selected + 1); - std::set newset; - newset.insert(selected + 1); - auto labels = getLabels(); - lblselector->setNames(labels); - lblselector->setSelected(newset); - if (g_eeGeneral.labelSingleSelect) - lblselector->setActiveItem(selected + 1); - updateFilteredLabels(newset); + moveLabel(selected, 1); return 0; }); } diff --git a/radio/src/gui/colorlcd/model_telemetry.cpp b/radio/src/gui/colorlcd/model_telemetry.cpp index 1fd19b94716..9a1b3df4c85 100644 --- a/radio/src/gui/colorlcd/model_telemetry.cpp +++ b/radio/src/gui/colorlcd/model_telemetry.cpp @@ -303,6 +303,8 @@ class SensorButton : public ListLineButton init = true; + lv_obj_enable_style_refresh(false); + numLabel = tsStyle.newNum(lvobj, index); lv_obj_set_pos(numLabel, PAD_TINY, PAD_MEDIUM/2); @@ -327,6 +329,9 @@ class SensorButton : public ListLineButton lv_obj_set_pos(valLabel, TSStyle::NUM_W + TSStyle::NAME_W + PAD_LARGE * 2, PAD_MEDIUM/2); lv_obj_update_layout(lvobj); + + lv_obj_enable_style_refresh(true); + lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } void refresh() override diff --git a/radio/src/gui/colorlcd/page.cpp b/radio/src/gui/colorlcd/page.cpp index 4215147e3ff..e8537b7e6bf 100644 --- a/radio/src/gui/colorlcd/page.cpp +++ b/radio/src/gui/colorlcd/page.cpp @@ -53,9 +53,12 @@ StaticText* PageHeader::setTitle2(std::string txt) return title2; } -Page::Page(EdgeTxIcon icon, PaddingSize padding) : +Page::Page(EdgeTxIcon icon, PaddingSize padding, bool pauseRefresh) : NavWindow(MainWindow::instance(), {0, 0, LCD_W, LCD_H}) { + if (pauseRefresh) + lv_obj_enable_style_refresh(false); + header = new PageHeader(this, icon); body = new Window(this, {0, EdgeTxStyles::MENU_HEADER_HEIGHT, LCD_W, LCD_H - EdgeTxStyles::MENU_HEADER_HEIGHT}); @@ -94,7 +97,14 @@ void Page::checkEvents() NavWindow::checkEvents(); } -SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle) : Page(icon, PAD_SMALL) +void Page::enableRefresh() +{ + lv_obj_enable_style_refresh(true); + lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); +} + +SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, bool pauseRefresh) : + Page(icon, PAD_SMALL, pauseRefresh) { body->padBottom(PAD_LARGE * 2); @@ -102,7 +112,8 @@ SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle) : Pag header->setTitle2(subtitle); } -SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, SetupLineDef* setupLines, int lineCount) : Page(icon, PAD_SMALL) +SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, SetupLineDef* setupLines, int lineCount) : + Page(icon, PAD_SMALL, true) { body->padBottom(PAD_LARGE * 2); @@ -110,6 +121,8 @@ SubPage::SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, Setup header->setTitle2(subtitle); SetupLine::showLines(body, y, EDT_X, PAD_SMALL, setupLines, lineCount); + + enableRefresh(); } Window* SubPage::setupLine(const char* title, std::function createEdit, coord_t lblYOffset) diff --git a/radio/src/gui/colorlcd/page.h b/radio/src/gui/colorlcd/page.h index 6bfbdcc1f29..80660f95489 100644 --- a/radio/src/gui/colorlcd/page.h +++ b/radio/src/gui/colorlcd/page.h @@ -47,7 +47,7 @@ class PageHeader : public Window class Page : public NavWindow { public: - explicit Page(EdgeTxIcon icon, PaddingSize padding = PAD_MEDIUM); + explicit Page(EdgeTxIcon icon, PaddingSize padding = PAD_MEDIUM, bool pauseRefresh = false); #if defined(DEBUG_WINDOWS) std::string getName() const override { return "Page"; } @@ -58,6 +58,8 @@ class Page : public NavWindow void deleteLater(bool detach = true, bool trash = true) override; + void enableRefresh(); + protected: PageHeader* header = nullptr; Window* body = nullptr; @@ -69,7 +71,7 @@ class Page : public NavWindow class SubPage : public Page { public: - SubPage(EdgeTxIcon icon, const char* title, const char* subtitle); + SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, bool pauseRefresh = false); SubPage(EdgeTxIcon icon, const char* title, const char* subtitle, SetupLineDef* setupLines, int lineCount); Window* setupLine(const char* title, std::function createEdit, coord_t lblYOffset = 0); diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index 15f9f34a07a..bdb42f42ff5 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -404,7 +404,7 @@ static SetupLineDef alarmsPageSetupLines[] = { class BacklightPage : public SubPage { public: - BacklightPage() : SubPage(ICON_RADIO_SETUP, STR_RADIO_SETUP, STR_BACKLIGHT_LABEL) + BacklightPage() : SubPage(ICON_RADIO_SETUP, STR_RADIO_SETUP, STR_BACKLIGHT_LABEL, true) { body->setFlexLayout(); @@ -486,6 +486,8 @@ class BacklightPage : public SubPage }); updateBacklightControls(); + + enableRefresh(); } protected: @@ -682,7 +684,7 @@ static SetupLineDef viewOptionsPageSetupLines[] = { class ManageModelsSetupPage : public SubPage { public: - ManageModelsSetupPage() : SubPage(ICON_MODEL, STR_RADIO_SETUP, STR_MANAGE_MODELS) + ManageModelsSetupPage() : SubPage(ICON_MODEL, STR_RADIO_SETUP, STR_MANAGE_MODELS, true) { body->setFlexLayout(); @@ -716,6 +718,8 @@ class ManageModelsSetupPage : public SubPage }); checkEvents(); + + enableRefresh(); } void checkEvents() override @@ -815,6 +819,14 @@ static SetupLineDef setupLines[] = { GET_SET_DEFAULT(g_eeGeneral.imperial)); } }, + { + // PPM units + STR_UNITS_PPM, + [](Window* parent, coord_t x, coord_t y) { + new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_PPMUNIT, PPM_PERCENT_PREC0, PPM_PERCENT_PREC1, + GET_SET_DEFAULT(g_eeGeneral.ppmunit)); + } + }, { // Switches delay STR_SWITCHES_DELAY, diff --git a/radio/src/gui/colorlcd/special_functions.cpp b/radio/src/gui/colorlcd/special_functions.cpp index 54c9e0cd856..9a3b885b618 100644 --- a/radio/src/gui/colorlcd/special_functions.cpp +++ b/radio/src/gui/colorlcd/special_functions.cpp @@ -103,6 +103,8 @@ void FunctionLineButton::delayed_init() { init = true; + lv_obj_enable_style_refresh(false); + sfName = lv_label_create(lvobj); lv_obj_set_pos(sfName, NM_X, NM_Y); lv_obj_set_size(sfName, NM_W, NM_H); @@ -125,6 +127,9 @@ void FunctionLineButton::delayed_init() lv_obj_set_pos(sfEnable, EN_X, EN_Y); lv_obj_update_layout(lvobj); + + lv_obj_enable_style_refresh(true); + lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } void FunctionLineButton::refresh() diff --git a/radio/src/gui/colorlcd/startup_shutdown.cpp b/radio/src/gui/colorlcd/startup_shutdown.cpp index 47537bb5da8..85ebb6b4a6c 100644 --- a/radio/src/gui/colorlcd/startup_shutdown.cpp +++ b/radio/src/gui/colorlcd/startup_shutdown.cpp @@ -133,7 +133,6 @@ void cancelSplash() splashScreen->deleteLater(); splashScreen = nullptr; MainWindow::instance()->setActiveScreen(); - lv_refr_now(nullptr); splashStartTime = 0; } } @@ -148,7 +147,7 @@ void waitSplash() #endif // defined(SIMU) splashStartTime += SPLASH_TIMEOUT; - do { + while (splashStartTime >= get_tmr10ms()) { LvglWrapper::instance()->run(); MainWindow::instance()->run(); WDG_RESET(); @@ -167,7 +166,7 @@ void waitSplash() break; } #endif // defined(SIMU) - } while (splashStartTime >= get_tmr10ms()); + } // Reset timer so special/global functions set to !1x don't get triggered START_SILENCE_PERIOD(); diff --git a/radio/src/gui/colorlcd/tabsgroup.cpp b/radio/src/gui/colorlcd/tabsgroup.cpp index bf5bf800f08..236dfb483ef 100644 --- a/radio/src/gui/colorlcd/tabsgroup.cpp +++ b/radio/src/gui/colorlcd/tabsgroup.cpp @@ -333,6 +333,8 @@ void TabsGroup::setVisibleTab(PageTab* tab) if (tab != currentTab && !deleted()) { header->setTitle(tab->title.c_str()); + lv_obj_enable_style_refresh(false); + body->clear(); if (currentTab) currentTab->cleanup(); @@ -352,8 +354,12 @@ void TabsGroup::setVisibleTab(PageTab* tab) LV_PART_MAIN); body->padAll(tab->padding); + tab->build(body); + lv_obj_enable_style_refresh(true); + lv_obj_refresh_style(body->getLvObj(), LV_PART_ANY, LV_STYLE_PROP_ANY); + #if defined(DEBUG) end_ms = RTOS_GET_MS(); #endif diff --git a/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp b/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp index edf7720f5bb..af07b3f4e0f 100644 --- a/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp +++ b/radio/src/gui/colorlcd/themes/etx_lv_theme.cpp @@ -29,8 +29,6 @@ extern lv_color_t makeLvColor(uint32_t colorFlags); -static lv_theme_t theme; - /********************** * Constant Styles **********************/ @@ -349,41 +347,33 @@ void EdgeTxStyles::applyColors() lv_style_set_arc_color(&arc_color, makeLvColor(COLOR_THEME_SECONDARY1)); } -static EdgeTxStyles mainStyles; -static EdgeTxStyles* previewStyles; -EdgeTxStyles* styles = &mainStyles; +static EdgeTxStyles *mainStyles = nullptr; +static EdgeTxStyles* previewStyles = nullptr; +EdgeTxStyles* styles = nullptr; /********************** * GLOBAL FUNCTIONS **********************/ -lv_theme_t* etx_lv_theme_init(lv_disp_t* disp, lv_color_t color_primary, - lv_color_t color_secondary, const lv_font_t* font) -{ - theme.disp = disp; - theme.color_primary = color_primary; - theme.color_secondary = color_secondary; - theme.font_small = font; - theme.font_normal = font; - theme.font_large = font; - theme.flags = 0; - - styles->init(); - - if (disp == NULL || lv_disp_get_theme(disp) == &theme) - lv_obj_report_style_change(NULL); - - return (lv_theme_t*)&theme; -} - void usePreviewStyle() { - if (!previewStyles) previewStyles = new EdgeTxStyles(); + if (!previewStyles) { + previewStyles = new EdgeTxStyles(); + previewStyles->init(); + } styles = previewStyles; - styles->init(); + styles->applyColors(); } -void useMainStyle() { styles = &mainStyles; } +void useMainStyle() +{ + if (!mainStyles) { + mainStyles = new EdgeTxStyles(); + mainStyles->init(); + } + styles = mainStyles; + styles->applyColors(); +} /********************** * Custom object creation diff --git a/radio/src/gui/colorlcd/themes/etx_lv_theme.h b/radio/src/gui/colorlcd/themes/etx_lv_theme.h index 519725c5daa..b6fa3219448 100644 --- a/radio/src/gui/colorlcd/themes/etx_lv_theme.h +++ b/radio/src/gui/colorlcd/themes/etx_lv_theme.h @@ -57,17 +57,6 @@ enum PaddingSize { * GLOBAL PROTOTYPES **********************/ -/** - * Initialize the theme - * @param color_primary the primary color of the theme - * @param color_secondary the secondary color for the theme - * @param font pointer to a font to use. - * @return a pointer to reference this theme later - */ -lv_theme_t* etx_lv_theme_init(lv_disp_t* disp, lv_color_t color_primary, - lv_color_t color_secondary, - const lv_font_t* font); - void usePreviewStyle(); void useMainStyle(); diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index 18d03e161bd..ab879cd9235 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -184,7 +184,8 @@ void Choice::onClicked() void Choice::fillMenu(Menu* menu, const FilterFct& filter) { - menu->removeLines(); + if (menu->count() > 0) + menu->removeLines(); auto value = getIntValue(); int count = 0; diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 38a71554f4a..43e11f66e87 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -45,7 +45,6 @@ StaticText::StaticText(Window* parent, const rect_t& rect, std::string txt, LV_PART_MAIN); lv_label_set_text(lvobj, text.c_str()); if (rect.h == 0) lv_obj_set_height(lvobj, LV_SIZE_CONTENT); - lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } #if defined(DEBUG_WINDOWS) diff --git a/radio/src/thirdparty/libopenui/src/textedit.h b/radio/src/thirdparty/libopenui/src/textedit.h index 373be9a6ce2..55e69f9fa07 100644 --- a/radio/src/thirdparty/libopenui/src/textedit.h +++ b/radio/src/thirdparty/libopenui/src/textedit.h @@ -36,6 +36,8 @@ class TextEdit : public TextButton void preview(bool edited, char* text, uint8_t length); void update(); + static LAYOUT_VAL(DEF_W, 100, 100) + protected: std::function updateHandler = nullptr; TextArea* edit = nullptr; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 78b1568e0e7..8f61a96caab 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -82,11 +82,8 @@ class Window void setRect(rect_t value) { rect = value; - lv_obj_enable_style_refresh(false); lv_obj_set_pos(lvobj, rect.x, rect.y); lv_obj_set_size(lvobj, rect.w, rect.h); - lv_obj_enable_style_refresh(true); - lv_obj_refresh_style(lvobj, LV_PART_ANY, LV_STYLE_PROP_ANY); } void setWidth(coord_t value) From d4037c934f091ccb94fc24f1e65d03f613a06473 Mon Sep 17 00:00:00 2001 From: philmoz Date: Wed, 29 May 2024 15:14:11 +1000 Subject: [PATCH 18/21] Use Lvgl built in memory allocator. Compile Lvgl with -O3. --- radio/src/gui/colorlcd/radio_trainer.cpp | 2 +- radio/src/lv_conf.h | 8 +++++++- radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_trainer.cpp b/radio/src/gui/colorlcd/radio_trainer.cpp index c8add3e8c40..9971f779024 100644 --- a/radio/src/gui/colorlcd/radio_trainer.cpp +++ b/radio/src/gui/colorlcd/radio_trainer.cpp @@ -51,7 +51,7 @@ void RadioTrainerPage::build(Window* form) form->padAll(PAD_SMALL); if (SLAVE_MODE()) { - form->setHeight(MENU_BODY_HEIGHT); + form->setHeight(TabsGroup::MENU_BODY_HEIGHT); auto txt = new StaticText(form, rect_t{}, STR_SLAVE, FONT(L)); lv_obj_align(txt->getLvObj(), LV_ALIGN_CENTER, 0, 0); } else { diff --git a/radio/src/lv_conf.h b/radio/src/lv_conf.h index 56186a87c3d..31a644ed4b9 100644 --- a/radio/src/lv_conf.h +++ b/radio/src/lv_conf.h @@ -46,10 +46,14 @@ *=========================*/ /*1: use custom malloc/free, 0: use the built-in `lv_mem_alloc()` and `lv_mem_free()`*/ +#if defined(BOOT) #define LV_MEM_CUSTOM 1 +#else +#define LV_MEM_CUSTOM 0 +#endif #if LV_MEM_CUSTOM == 0 /*Size of the memory available for `lv_mem_alloc()` in bytes (>= 2kB)*/ - #define LV_MEM_SIZE (8U * 1024U) /*[bytes]*/ + #define LV_MEM_SIZE (1024U * 1024U) /*[bytes]*/ /*Set an address for the memory pool instead of allocating it as a normal array. Can be in external SRAM too.*/ #define LV_MEM_ADR 0 /*0: unused*/ @@ -57,6 +61,8 @@ #if LV_MEM_ADR == 0 //#define LV_MEM_POOL_INCLUDE your_alloc_library /* Uncomment if using an external allocator*/ //#define LV_MEM_POOL_ALLOC your_alloc /* Uncomment if using an external allocator*/ + #define LV_MEM_POOL_INCLUDE + #define LV_MEM_POOL_ALLOC sbrk #endif #else /*LV_MEM_CUSTOM*/ diff --git a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt index 303deb2c809..415dfc4daf6 100644 --- a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt @@ -186,5 +186,9 @@ endforeach() foreach(LVGL_FILE ${LVGL_SOURCES}) set(LVGL_SRC_FILES ${LVGL_SRC_FILES} ${LVGL_SRC_DIR}/${LVGL_FILE}) endforeach() + +foreach(LVGL_FILE ${LVGL_SRC_FILES}) + SET_SOURCE_FILES_PROPERTIES( ${LVGL_FILE} PROPERTIES COMPILE_FLAGS -O3 ) +endforeach() set(SRC ${SRC} ${LVGL_SRC_FILES}) From ad23946b6c8a772d8a28a91b3495b9aa078bb6c8 Mon Sep 17 00:00:00 2001 From: philmoz Date: Thu, 30 May 2024 16:53:10 +1000 Subject: [PATCH 19/21] Fix rebase. --- radio/src/gui/colorlcd/radio_setup.cpp | 8 -------- 1 file changed, 8 deletions(-) diff --git a/radio/src/gui/colorlcd/radio_setup.cpp b/radio/src/gui/colorlcd/radio_setup.cpp index bdb42f42ff5..78966d857ad 100644 --- a/radio/src/gui/colorlcd/radio_setup.cpp +++ b/radio/src/gui/colorlcd/radio_setup.cpp @@ -747,14 +747,6 @@ static SetupLineDef setupLines[] = { }); } }, - { - // PPM units - STR_UNITS_PPM, - [](Window* parent, coord_t x, coord_t y) { - new Choice(parent, {x, y, 0, EdgeTxStyles::UI_ELEMENT_HEIGHT}, STR_PPMUNIT, PPM_PERCENT_PREC0, PPM_PERCENT_PREC1, - GET_SET_DEFAULT(g_eeGeneral.ppmunit)); - } - }, { // Play startup sound STR_PLAY_HELLO, From 79f0a677bacc11f9efa05324321ad7186c8995c3 Mon Sep 17 00:00:00 2001 From: philmoz Date: Fri, 3 May 2024 10:41:24 +1000 Subject: [PATCH 20/21] Update to lvgl 8.4 --- radio/src/gui/colorlcd/themes/etx_lv_theme.h | 6 +- radio/src/lv_conf.h | 179 ++++++++++++------ .../images/color/primitives_EN_320x480.png | Bin 10346 -> 10140 bytes .../images/color/primitives_EN_480x272.png | Bin 9717 -> 9498 bytes .../images/color/primitives_EN_480x320.png | Bin 10452 -> 10233 bytes .../images/color/transparency_EN_320x480.png | Bin 9843 -> 9744 bytes .../images/color/transparency_EN_480x272.png | Bin 9268 -> 9136 bytes .../images/color/transparency_EN_480x320.png | Bin 10002 -> 9871 bytes .../libopenui/thirdparty/CMakeLists.txt | 4 + .../src/thirdparty/libopenui/thirdparty/lvgl | 2 +- 10 files changed, 131 insertions(+), 60 deletions(-) diff --git a/radio/src/gui/colorlcd/themes/etx_lv_theme.h b/radio/src/gui/colorlcd/themes/etx_lv_theme.h index b6fa3219448..933f9012521 100644 --- a/radio/src/gui/colorlcd/themes/etx_lv_theme.h +++ b/radio/src/gui/colorlcd/themes/etx_lv_theme.h @@ -97,7 +97,6 @@ void etx_img_color(lv_obj_t* obj, LcdColorIndex colorIdx, #define LV_STYLE_CONST_SINGLE_INIT(var_name, prop, value) \ const lv_style_t var_name = {.v_p = {.value1 = {.num = value}}, \ .prop1 = prop, \ - .is_const = 0, \ .has_group = 1 << ((prop & 0x1FF) >> 4), \ .prop_cnt = 1} @@ -105,10 +104,9 @@ void etx_img_color(lv_obj_t* obj, LcdColorIndex colorIdx, // Copied from lv_style.h and modified to compile with ARM GCC C++ #define LV_STYLE_CONST_MULTI_INIT(var_name, prop_array) \ const lv_style_t var_name = {.v_p = {.const_props = prop_array}, \ - .prop1 = 0, \ - .is_const = 1, \ + .prop1 = LV_STYLE_PROP_ANY, \ .has_group = 0xFF, \ - .prop_cnt = 0} + .prop_cnt = (sizeof(prop_array) / sizeof((prop_array)[0]))} extern const lv_obj_class_t window_base_class; extern const lv_obj_class_t field_edit_class; diff --git a/radio/src/lv_conf.h b/radio/src/lv_conf.h index 31a644ed4b9..1b2174c6dd8 100644 --- a/radio/src/lv_conf.h +++ b/radio/src/lv_conf.h @@ -1,6 +1,6 @@ /** * @file lv_conf.h - * Configuration file for v8.2.0 + * Configuration file for v8.4.0 */ /* @@ -29,9 +29,9 @@ /*Swap the 2 bytes of RGB565 color. Useful if the display has an 8-bit interface (e.g. SPI)*/ #define LV_COLOR_16_SWAP 0 -/*Enable more complex drawing routines to manage screens transparency. - *Can be used if the UI is above another layer, e.g. an OSD menu or video player. - *Requires `LV_COLOR_DEPTH = 32` colors and the screen's `bg_opa` should be set to non LV_OPA_COVER value*/ +/*Enable features to draw on transparent background. + *It's required if opa, and transform_* style properties are used. + *Can be also used if the UI is above another layer, e.g. an OSD menu or video player.*/ #define LV_COLOR_SCREEN_TRANSP 0 /* Adjust color mix functions rounding. GPUs might calculate color mix (blending) differently. @@ -95,6 +95,9 @@ #if LV_TICK_CUSTOM #define LV_TICK_CUSTOM_INCLUDE "Arduino.h" /*Header for the system time function*/ #define LV_TICK_CUSTOM_SYS_TIME_EXPR (millis()) /*Expression evaluating to current system time in ms*/ + /*If using lvgl as ESP32 component*/ + // #define LV_TICK_CUSTOM_INCLUDE "esp_timer.h" + // #define LV_TICK_CUSTOM_SYS_TIME_EXPR ((esp_timer_get_time() / 1000LL)) #endif /*LV_TICK_CUSTOM*/ /*Default Dot Per Inch. Used to initialize default sizes such as widgets sized, style paddings. @@ -132,33 +135,49 @@ #define LV_CIRCLE_CACHE_SIZE 4 #endif /*LV_DRAW_COMPLEX*/ +/** + * "Simple layers" are used when a widget has `style_opa < 255` to buffer the widget into a layer + * and blend it as an image with the given opacity. + * Note that `bg_opa`, `text_opa` etc don't require buffering into layer) + * The widget can be buffered in smaller chunks to avoid using large buffers. + * + * - LV_LAYER_SIMPLE_BUF_SIZE: [bytes] the optimal target buffer size. LVGL will try to allocate it + * - LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE: [bytes] used if `LV_LAYER_SIMPLE_BUF_SIZE` couldn't be allocated. + * + * Both buffer sizes are in bytes. + * "Transformed layers" (where transform_angle/zoom properties are used) use larger buffers + * and can't be drawn in chunks. So these settings affects only widgets with opacity. + */ +#define LV_LAYER_SIMPLE_BUF_SIZE (24 * 1024) +#define LV_LAYER_SIMPLE_FALLBACK_BUF_SIZE (3 * 1024) + /*Default image cache size. Image caching keeps the images opened. *If only the built-in image formats are used there is no real advantage of caching. (I.e. if no new image decoder is added) *With complex image decoders (e.g. PNG or JPG) caching can save the continuous open/decode of images. *However the opened images might consume additional RAM. *0: to disable caching*/ -#define LV_IMG_CACHE_DEF_SIZE 8 +#define LV_IMG_CACHE_DEF_SIZE 8 /*Number of stops allowed per gradient. Increase this to allow more stops. *This adds (sizeof(lv_color_t) + 1) bytes per additional stop*/ -#define LV_GRADIENT_MAX_STOPS 2 +#define LV_GRADIENT_MAX_STOPS 2 /*Default gradient buffer size. *When LVGL calculates the gradient "maps" it can save them into a cache to avoid calculating them again. *LV_GRAD_CACHE_DEF_SIZE sets the size of this cache in bytes. *If the cache is too small the map will be allocated only while it's required for the drawing. *0 mean no caching.*/ -#define LV_GRAD_CACHE_DEF_SIZE 0 +#define LV_GRAD_CACHE_DEF_SIZE 0 /*Allow dithering the gradients (to achieve visual smooth color gradients on limited color depth display) *LV_DITHER_GRADIENT implies allocating one or two more lines of the object's rendering surface *The increase in memory consumption is (32 bits * object width) plus 24 bits * object width if using error diffusion */ -#define LV_DITHER_GRADIENT 0 +#define LV_DITHER_GRADIENT 0 #if LV_DITHER_GRADIENT /*Add support for error diffusion dithering. *Error diffusion dithering gets a much better visual result, but implies more CPU consumption and memory when drawing. *The increase in memory consumption is (24 bits * object's width)*/ - #define LV_DITHER_ERROR_DIFFUSION 0 + #define LV_DITHER_ERROR_DIFFUSION 0 #endif /*Maximum buffer size to allocate for rotation. @@ -169,6 +188,9 @@ * GPU *-----------*/ +/*Use Arm's 2D acceleration library Arm-2D */ +#define LV_USE_GPU_ARM2D 0 + /*Use STM32's DMA2D (aka Chrom Art) GPU*/ #if defined(SIMU) #define LV_USE_GPU_STM32_DMA2D 0 @@ -177,23 +199,22 @@ #endif #if LV_USE_GPU_STM32_DMA2D /*Must be defined to include path of CMSIS header of target processor - e.g. "stm32f769xx.h" or "stm32f429xx.h"*/ - #define LV_GPU_DMA2D_CMSIS_INCLUDE "stm32f4xx.h" - #if !defined(DMA2D_NLR_PL_Pos) - #define DMA2D_NLR_PL_Pos 16 - #endif - #if !defined(DMA2D_NLR_NL_Pos) - #define DMA2D_NLR_NL_Pos 0 - #endif - #if !defined(DMA2D_CR_START_Msk) - #define DMA2D_CR_START_Msk DMA2D_CR_START - #endif - #if !defined(DMA2D_FGPFCCR_AM_Pos) - #define DMA2D_FGPFCCR_AM_Pos 16 - #endif - #if !defined(DMA2D_FGPFCCR_ALPHA_Pos) - #define DMA2D_FGPFCCR_ALPHA_Pos 24 - #endif + e.g. "stm32f7xx.h" or "stm32f4xx.h"*/ + #define LV_GPU_DMA2D_CMSIS_INCLUDE "thirdparty/CMSIS/Device/ST/STM32F4xx/Include/stm32f4xx.h" +#endif + +/*Enable RA6M3 G2D GPU*/ +#define LV_USE_GPU_RA6M3_G2D 0 +#if LV_USE_GPU_RA6M3_G2D + /*include path of target processor + e.g. "hal_data.h"*/ + #define LV_GPU_RA6M3_G2D_INCLUDE "hal_data.h" +#endif + +/*Use SWM341's DMA2D GPU*/ +#define LV_USE_GPU_SWM341_DMA2D 0 +#if LV_USE_GPU_SWM341_DMA2D + #define LV_GPU_SWM341_DMA2D_INCLUDE "SWM341.h" #endif /*Use NXP's PXP GPU iMX RTxxx platforms*/ @@ -471,6 +492,9 @@ #define LV_FONT_SUBPX_BGR 0 /*0: RGB; 1:BGR order*/ #endif +/*Enable drawing placeholders when glyph dsc is not found*/ +#define LV_USE_FONT_PLACEHOLDER 1 + /*================= * TEXT SETTINGS *=================*/ @@ -526,8 +550,6 @@ #if !defined(BOOT) #define LV_USE_ARC 1 -#define LV_USE_ANIMIMG 0 - #define LV_USE_BAR 1 #define LV_USE_BTN 1 @@ -573,6 +595,8 @@ /*----------- * Widgets *----------*/ +#define LV_USE_ANIMIMG 0 + #define LV_USE_CALENDAR 0 #if LV_USE_CALENDAR #define LV_CALENDAR_WEEK_STARTS_MONDAY 0 @@ -605,6 +629,12 @@ #define LV_USE_MSGBOX 0 +#define LV_USE_SPAN 0 +#if LV_USE_SPAN + /*A line text can contain maximum num of span descriptor */ + #define LV_SPAN_SNIPPET_STACK_SIZE 64 +#endif + #define LV_USE_SPINBOX 0 #define LV_USE_SPINNER 0 @@ -615,12 +645,6 @@ #define LV_USE_WIN 0 -#define LV_USE_SPAN 0 -#if LV_USE_SPAN - /*A line text can contain maximum num of span descriptor */ - #define LV_SPAN_SNIPPET_STACK_SIZE 64 -#endif - /*----------- * Themes *----------*/ @@ -657,7 +681,6 @@ #else #define LV_USE_ARC 0 -#define LV_USE_ANIMIMG 0 #define LV_USE_BAR 0 #define LV_USE_BTN 0 #define LV_USE_BTNMATRIX 0 @@ -672,6 +695,7 @@ #define LV_USE_SWITCH 0 #define LV_USE_TEXTAREA 0 /*Requires: lv_label*/ #define LV_USE_TABLE 0 +#define LV_USE_ANIMIMG 0 #define LV_USE_CALENDAR 0 #define LV_USE_CHART 0 #define LV_USE_COLORWHEEL 0 @@ -682,12 +706,12 @@ #define LV_USE_MENU 0 #define LV_USE_METER 0 #define LV_USE_MSGBOX 0 +#define LV_USE_SPAN 0 #define LV_USE_SPINBOX 0 #define LV_USE_SPINNER 0 #define LV_USE_TABVIEW 0 #define LV_USE_TILEVIEW 0 #define LV_USE_WIN 0 -#define LV_USE_SPAN 0 #define LV_USE_THEME_DEFAULT 0 #define LV_USE_THEME_BASIC 0 #define LV_USE_THEME_MONO 0 @@ -705,7 +729,7 @@ #if LV_USE_FS_STDIO #define LV_FS_STDIO_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_STDIO_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ - #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #define LV_FS_STDIO_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif /*API for open, read, etc*/ @@ -713,20 +737,20 @@ #if LV_USE_FS_POSIX #define LV_FS_POSIX_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_POSIX_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ - #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ + #define LV_FS_POSIX_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif /*API for CreateFile, ReadFile, etc*/ #define LV_USE_FS_WIN32 0 #if LV_USE_FS_WIN32 - #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_WIN32_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_WIN32_PATH "" /*Set the working directory. File/directory paths will be appended to it.*/ #define LV_FS_WIN32_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ #endif /*API for FATFS (needs to be added separately). Uses f_open, f_read, etc*/ #if defined(BOOT) -#define LV_USE_FS_FATFS 0 +#define LV_USE_FS_FATFS 0 #if LV_USE_FS_FATFS #define LV_FS_FATFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ #define LV_FS_FATFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ @@ -739,6 +763,13 @@ #endif #endif +/*API for LittleFS (library needs to be added separately). Uses lfs_file_open, lfs_file_read, etc*/ +#define LV_USE_FS_LITTLEFS 0 +#if LV_USE_FS_LITTLEFS + #define LV_FS_LITTLEFS_LETTER '\0' /*Set an upper cased letter on which the drive will accessible (e.g. 'A')*/ + #define LV_FS_LITTLEFS_CACHE_SIZE 0 /*>0 to cache this number of bytes in lv_fs_read()*/ +#endif + /*PNG decoder library*/ #define LV_USE_PNG 0 @@ -777,15 +808,22 @@ #endif #endif +/*Tiny TTF library*/ +#define LV_USE_TINY_TTF 0 +#if LV_USE_TINY_TTF + /*Load TTF data from files*/ + #define LV_TINY_TTF_FILE_SUPPORT 0 +#endif + /*Rlottie library*/ #define LV_USE_RLOTTIE 0 /*FFmpeg library for image decoding and playing videos *Supports all major image formats so do not enable other image decoder with it*/ -#define LV_USE_FFMPEG 0 +#define LV_USE_FFMPEG 0 #if LV_USE_FFMPEG /*Dump input information to stderr*/ - #define LV_FFMPEG_AV_DUMP_FORMAT 0 + #define LV_FFMPEG_DUMP_FORMAT 0 #endif /*----------- @@ -796,10 +834,37 @@ #define LV_USE_SNAPSHOT 1 /*1: Enable Monkey test*/ -#define LV_USE_MONKEY 0 +#define LV_USE_MONKEY 0 /*1: Enable grid navigation*/ -#define LV_USE_GRIDNAV 0 +#define LV_USE_GRIDNAV 0 + +/*1: Enable lv_obj fragment*/ +#define LV_USE_FRAGMENT 0 + +/*1: Support using images as font in label or span widgets */ +#define LV_USE_IMGFONT 0 + +/*1: Enable a published subscriber based messaging system */ +#define LV_USE_MSG 0 + +/*1: Enable Pinyin input method*/ +/*Requires: lv_keyboard*/ +#define LV_USE_IME_PINYIN 0 +#if LV_USE_IME_PINYIN + /*1: Use default thesaurus*/ + /*If you do not use the default thesaurus, be sure to use `lv_ime_pinyin` after setting the thesauruss*/ + #define LV_IME_PINYIN_USE_DEFAULT_DICT 1 + /*Set the maximum number of candidate panels that can be displayed*/ + /*This needs to be adjusted according to the size of the screen*/ + #define LV_IME_PINYIN_CAND_TEXT_NUM 6 + + /*Use 9 key input(k9)*/ + #define LV_IME_PINYIN_USE_K9_MODE 1 + #if LV_IME_PINYIN_USE_K9_MODE == 1 + #define LV_IME_PINYIN_K9_CAND_TEXT_NUM 3 + #endif // LV_IME_PINYIN_USE_K9_MODE +#endif /*================== * EXAMPLES @@ -813,28 +878,32 @@ ====================*/ /*Show some widget. It might be required to increase `LV_MEM_SIZE` */ -#define LV_USE_DEMO_WIDGETS 0 +#define LV_USE_DEMO_WIDGETS 0 #if LV_USE_DEMO_WIDGETS -#define LV_DEMO_WIDGETS_SLIDESHOW 0 +#define LV_DEMO_WIDGETS_SLIDESHOW 0 #endif /*Demonstrate the usage of encoder and keyboard*/ -#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 +#define LV_USE_DEMO_KEYPAD_AND_ENCODER 0 /*Benchmark your system*/ -#define LV_USE_DEMO_BENCHMARK 0 +#define LV_USE_DEMO_BENCHMARK 0 +#if LV_USE_DEMO_BENCHMARK +/*Use RGB565A8 images with 16 bit color depth instead of ARGB8565*/ +#define LV_DEMO_BENCHMARK_RGB565A8 0 +#endif /*Stress test for LVGL*/ -#define LV_USE_DEMO_STRESS 0 +#define LV_USE_DEMO_STRESS 0 /*Music player demo*/ -#define LV_USE_DEMO_MUSIC 0 +#define LV_USE_DEMO_MUSIC 0 #if LV_USE_DEMO_MUSIC -# define LV_DEMO_MUSIC_SQUARE 0 -# define LV_DEMO_MUSIC_LANDSCAPE 0 -# define LV_DEMO_MUSIC_ROUND 0 -# define LV_DEMO_MUSIC_LARGE 0 -# define LV_DEMO_MUSIC_AUTO_PLAY 0 + #define LV_DEMO_MUSIC_SQUARE 0 + #define LV_DEMO_MUSIC_LANDSCAPE 0 + #define LV_DEMO_MUSIC_ROUND 0 + #define LV_DEMO_MUSIC_LARGE 0 + #define LV_DEMO_MUSIC_AUTO_PLAY 0 #endif /*--END OF LV_CONF_H--*/ diff --git a/radio/src/tests/images/color/primitives_EN_320x480.png b/radio/src/tests/images/color/primitives_EN_320x480.png index 982541571dcb90a5854371cc5adcdff39340664e..2ef25205ae4d79007254e197c4c2e4b216808266 100644 GIT binary patch delta 5918 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
&^Ju%((k~H< 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 W6#whlIo|-bwQxaF%$X=+vb@7 diff --git a/radio/src/tests/images/color/primitives_EN_480x320.png b/radio/src/tests/images/color/primitives_EN_480x320.png index 365a2a20fb2443c9d589542db84763eef94ce98d..5f36385a4dfaff50ad33f43fde05b99ba8f05df0 100644 GIT binary patch delta 6198 zcmai2X;>3i+ny!+%9;fN35#rk1Pu@YWfE5Rs8k~&9hGWC#0V~+ykv&3$z~9$h)aX0 zAGX-WiWc_)6cE$~%WF}Q22e};p`y@=itU%$zSs5s`{u{Fu5;$h^PKyB&Uwx=ckj}- zOAH>6H&HoludbdvLB!oI_ZCMah6o%QYfMjDHL*fA-eV(RgIP3_u^^_c#NK&v1mT2C zpC2JhA&LA#NPRvIlLGT>n^_w~1{;f}*hH)k3`%$Qc{99iXGUZ3yU2^|!}I z0j3XM9mYi)RyD-a#*h#>YaI)kj_KcObkewg`oq0y+YYzm|ub1uF zS4fP}tfmr``BBPf=tZdHU?Q1XF!;IUc#!`O{rdtXTNPc^+^xwXxz^HgP{(x&qfoq3 zLi%GL-L3!Z2MvvN*amHRT4nEze0br!azpZyO zqs?Q0+`ve)gBJW=Xv0h@2v^-H^x$IUamJl!CUxKpT+s$G*JatlX|3=X%I_a=&nwrn z(GCg+#Q7}7tlyI`wT_g)-TGV9kxOfD<~c`tu1J~$dP{#Pafe-j2BvB{D57wOw5Je3%D_1LH!3MbIm4pW}J zKVJxh3DmN<2ITR+h+ygjq+&|o-~ zkgX8RP~QXRl!{c^UC@Fh@k^oYzZTkY3&moL{j)tcH=(gvhuutO(^Wm^AXz1?i98}e zPnT?zMJbh#u7n)M9a&_Av1h|)^p<9yTvbn}f4p6?`HR-)A# z!1I8frgqw}a5msOt@1g!wk%lSktV}R@DQZ~yoGe1YM5;O!eAJtrQ4df7RJdX1$ah-cTGNna5POm-;U61xi|H$L40T8# zx#pmN*W6C|j*b#0(ZsyvjRf;`iOGQT#hN>@s$bITfR&VmBbuAj|Iku){yFPF8Vv~< zY}-esrW4)kjGLTYwOJ!HBJTShR(?~h+Y9PDX5TyPy9a|WsHulcgV2?Kv4cb-87Q5+ z_KW6mcO)9DsmFv|`9d%SaD>UY)8JbhI~8Kfg(>l~E1r3xxrGZ^I_T%fX@q^gNF=fk zIYOics)9^Dq!359Gky~OpVht^L&_~U0;hAMio5Y^HA{zqrQFe2WC|LDK2audCg!Rb zb{xc4)%(aN3-b=x;3G#$0zhH48=BNYV@)&ivB6Vra4G{35*dtjE`Q|bi{i9Df$PDU zFprbJ+T4XNx-SJ<}A>xD~^F{ym8??{OQe- zIb8n)2g%!#;hP0)elBrtx>*bIjHLqXly8HEfafa}Ev>C!K%cn(;wuZObBgVZU9%sP z;vu;`qvS%mNk#eH6?-^6M`oBaGH5T62a{}L!a{VRn z?%ns?_$Q1h-1X#wSY=G9gl)?)F2`v@9!A~h-dWzHxkYqaE$?S zw-dy+Xz5_deFe;nD~x0jd5&t}aWk0O2tHB~+fpQq8ZFcp2Y1aYreUSJm6{d!cHB+q zs@@J&0{Z}MAF296dcVXgAri|F!zTvmy&Tb*A5=?eG@8Ls34~GZ0omZ@C~ysJVGChL z-K-P)bc4x4cS#+P@+Ue8?jp*mfmj;To>|<69h$EI6Q;nT>o_HqHU;cR{LCT!%37`2 z0PvO&UiV7*xgsi5Ejx;)EW#Q$bOfndHRbYJtrTqomBxaZ}W{qt2+P<^M2NH094^L|6)bzACd&$PNa0brKYWb z>5Vb)fCa{;0vt5;4NU^IgCzFNKi8@(lY?M-Z^Wb<(6llSesZ3_1bG@xObjm)6KPi)3#7yqvWU#CET zkwoKcW9y6WxvHu!a0To!g8AvN1yM|8LIVw%koWnO<(&<2D^Uk}4RBid=6PaZ-7UvrAS_stOS;~TT3Ap46lU498q zL3N$-p#f)*^CnX8dSlP+EI>~_OSQyS^JA$u7yE6wlBS7&LLB9C zuW98(7>PI?8DHwGnY%k#1^o7J%nM!*?w>Lh9ws+$q>s*m76B)9DQg&vzf*J!S(fqD zBCx+0Ja8DHm^N?Ktu=yQID!|pxRH6%5oCRV$T;BMQ2zTc)g*ik56p$VcJe4svSS** zyBa@+tpnsr#^|FzfWyG(YdDjpa!}tU*Z2T&C}v+nq(!d3$)Rg^IR|uERsgeRrKB@P z{EQ2bF7#`BpF#Qf^jZ@XL8_~h80+fXfG#782#V9OZ7K1azH)2XW}(BKTOyIC%tYHV zk%~}`Q(M0uIXw^V%ZlZQ6ZYU&i-h6nKoNx1e?cA4CN*n8b(jEU)evm*r7AQ_*~+df zp!Ur}WbwFyQJq)qVDmX~4Kk&G?i{dy&X;pA=PL|5h~qBV2VWj1G^7jeZprOBaTx*! zIFK-qdE}PL0{nIsb87=RND`es`-;K&E-MQZ(j6aM5yuCIN@I>TXTUNrYnY;qrj1zO z(7D)#;I*a6*eyGCx)b(3nW%M^lSrfA&of~;)Olx#2{=f!pO5(pkF{G4_{D31ALwTF z?I+YUH|zxV%X_BTQ5WgyATwmzSZkMLH$oZnlBCfBmvLjuP@pI@L?j=}gXIo@==1eJ zd7i|p0xMveH1MnIT7UT%uq;}61oZD$==Xbjrsr7XKpJ%YZJi|VCJu&4O0oJ&k=L}1 z^4EZT)xN$L8?rNx(EeI^9ClQnYu`mP34EH%&0t z;OI;JU8J(9#BLjbj-&W&9i;vxV=5b%a506DgU2cCvZxxS zsTW)P5bH?6=a5w3mU=D9Y!EVCA6T@~rIXr0il+bf4lai>^UapwZ{GmsV}T+UTl2*h zn$8{<#^v^Bjl*)HWHZZ~0AyN)G=lE61zljHemB~T+pQxu?8xgXVzQiw7(PKO@R!3C z2|&>~%;Ownnzvx8g*Mu~pcwE@oF-T20&Q%$95@zDp61+0-_%}i-<-4=OfIA~zQ+r$ zfdg0bqrDPRO6?Ysu=5^rPobSI%>i4KYe9oFxkE5)N{d;|USkWCd&E0GXe0H_nbZ!q zvn@vFD{omJPtuM^?H-)mMn%<%6?fpz@NyQ+WH#RVUJ3PT2eyH-8_$j4!%{66aiU$y zC|pCcOg9EDEG8xbAMVcToMG2Ivvaq9XDv9HELUB3JYPr+(Iw)vrg#KjOU;=kFsatB z9VtFLV*RzbyL?`;R4qqVh6CMs5R*gY-gp)k-@-UgyAbNq+ot;CN#&*|)J)N6iU-%JuQ+{2?Rchum!ZV+9FXT^%aL3) zDt?{it&=%;&Pc)TuXM?B14T}h4Qr-AGB*glNTF{;|KtYJWtpZ$G z(-?miSY($h2mO2Hrt@{!Z*0@=txm^WW)Nf1ja7=$xOh^_W7uDVxf^ZZ00sW*?pGFy zwm(Oi51WI+caEA&LD=mZP>~96;q>3pk;H1kzKrNewOrsBWB7Na0L;@=>p>re^~Rcd z6eKxDj(ewfVY_y&RmLjGi#JG=SP`g!q)m7wlopDlMjJOfX65=9_h6noIvH$qGjOaA zBZr!Ph3?kRZHAIgTT`a{&)7ALdW`Rofao8x;5t&=Y z>uK{RsWpRQlvj`zOnD-el@ zJU}*lL;u+$vU#CuQ)dfUo4p6}U%%BDd;k~Hxw8@GkL7-c4_xmO-^x2%47DV-nOra z#CZ%2)3UF!E9vw*4@8Dw>8^C_XUePR_uc-TqvL4j!;1%$Y57vg_yObg+#x`kf_=`D zRs!9hS^6eQ?77%cu4NLwu?tz1H2lnP1z@f2%x6oF4Bm2_)xS7rX`l2dU@Ctdg{*Hs z<{UbpmE~)I@sEyUc9C{xjJ%R2w7+Z6~X6#W!fq}`yRrnS`nBa>4 zfx{<>W4Rdl(sL_$Ei$$)%^6xCjz3u}T1OOICFVoLm@UJ<5D8zLZH~(tqk5!hd1ut# zGu`TDJJ%#)FY+7bHrceSM5@+a)9YcAU*3s@XhZ8pJ>JHTL@Z?Or*f-CGta@2?rZ7d zSq9uC{3qcYPR==U7{zecrj1N7ybqQkRYG*%+QQZO6pc<`GxJ+gTDUjxUCO}h79hE5 zV(3m$&+%+|0Whpr*w-7zsj&wiET5i@7w%2*^w)m#O7d^_&I3HyzO=_3>$L&yrxJP2 zH5X=UW*@f7Rv8DnH6*rG!V{Kv5Aq?yQyXNNl0ap-)DRN(d>y)4(07;=qCK4)20xM! zXLFM}F=s8tRkP8Z_Hp)vfrIxzy9HjTML;v@xxe`Cd-grf1}$c)NkBf+@juBcDK9)a z1!1o{_mk@Ezm5H&&CI|RvKv9Q+he0p8R=tVh(ae8xAsg(t12aYonA(wl_}sQ4)v97 zXJ6#ukH}5@28`#K`;ukUQT>ikZw&Oj{`e_YV=OU9$7(9~wXLMsk9VF-e=JR$UUnd<-c8yYg>GlTF zx2t305@MI%)6i6NNU^KU^?OsR+WBo&T|(C?iLrHk$M>s(g-at4^s7+qPvZr2TP-~AX+=A^ z(hU=J*5e^gkabl~kio_{xJp!Ca988*{EtpOU-^-X4`o$;BA@A?Q-xxmYh#(nw%+4) zy)g4HFV>x6Pzv&|>f5$e_ z`kpeU_CRb^p(x)8JIN h_@7Y!zXM_(0A%yK%-E+twgJ{>Ud-a?ua#)t{{ffdlCl5* delta 6341 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 V*dWSqMgZHIKQA%nOq44Be*h9$<_Q1* diff --git a/radio/src/tests/images/color/transparency_EN_320x480.png b/radio/src/tests/images/color/transparency_EN_320x480.png index 24ddce74b143367102d447b5e35bc254dbad5ef6..44d47f2cd7eae918dc6bc087913aa7bb852eaee1 100644 GIT binary patch delta 5735 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 delta 5834 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 mGc<{nbmnJq9L#T*0KmJGF{|&S{5271M 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 EziV-Fn*aa+ delta 6256 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 diff --git a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt index 415dfc4daf6..b9750185b5a 100644 --- a/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt +++ b/radio/src/thirdparty/libopenui/thirdparty/CMakeLists.txt @@ -20,6 +20,7 @@ set(LVGL_SOURCES_MINIMAL draw/sw/lv_draw_sw_gradient.c draw/sw/lv_draw_sw.c draw/sw/lv_draw_sw_blend.c + draw/sw/lv_draw_sw_layer.c draw/sw/lv_draw_sw_letter.c draw/sw/lv_draw_sw_arc.c draw/sw/lv_draw_sw_polygon.c @@ -27,7 +28,10 @@ set(LVGL_SOURCES_MINIMAL draw/sw/lv_draw_sw_line.c draw/sw/lv_draw_sw_dither.c draw/sw/lv_draw_sw_rect.c + draw/sw/lv_draw_sw_transform.c + draw/lv_draw_layer.c draw/lv_draw_line.c + draw/lv_draw_transform.c draw/lv_draw.c draw/lv_img_buf.c draw/lv_draw_rect.c diff --git a/radio/src/thirdparty/libopenui/thirdparty/lvgl b/radio/src/thirdparty/libopenui/thirdparty/lvgl index ca2684d58da..189c2bf5c56 160000 --- a/radio/src/thirdparty/libopenui/thirdparty/lvgl +++ b/radio/src/thirdparty/libopenui/thirdparty/lvgl @@ -1 +1 @@ -Subproject commit ca2684d58da8fb2eb3e34f63154f58962bd7a8c9 +Subproject commit 189c2bf5c56d78ac7d502ae09cc4e8744697cc3d From 49caa1f7420b5e9ec5f13cb84310db58330f9e4c Mon Sep 17 00:00:00 2001 From: philmoz Date: Thu, 30 May 2024 15:49:01 +1000 Subject: [PATCH 21/21] Fix for EL18. --- radio/src/gui/colorlcd/lcd.cpp | 24 ++---------------------- radio/src/targets/horus/lcd_driver.cpp | 4 ++-- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/radio/src/gui/colorlcd/lcd.cpp b/radio/src/gui/colorlcd/lcd.cpp index 6e758058564..f3fbe7e8ff0 100644 --- a/radio/src/gui/colorlcd/lcd.cpp +++ b/radio/src/gui/colorlcd/lcd.cpp @@ -108,27 +108,6 @@ static void flushLcd(lv_disp_drv_t* disp_drv, const lv_area_t* area, lcd_flush_cb(disp_drv, (uint16_t*)color_p, copy_area); #if !defined(LCD_VERTICAL_INVERT) - uint16_t* src = (uint16_t*)color_p; - uint16_t* dst = nullptr; - if ((uint16_t*)color_p == LCD_FIRST_FRAME_BUFFER) - dst = LCD_SECOND_FRAME_BUFFER; - else - dst = LCD_FIRST_FRAME_BUFFER; - - lv_disp_t* disp = _lv_refr_get_disp_refreshing(); - for (int i = 0; i < disp->inv_p; i++) { - if (disp->inv_area_joined[i]) continue; - - const lv_area_t& refr_area = disp->inv_areas[i]; - - auto area_w = refr_area.x2 - refr_area.x1 + 1; - auto area_h = refr_area.y2 - refr_area.y1 + 1; - - DMACopyBitmap(dst, LCD_W, LCD_H, refr_area.x1, refr_area.y1, src, LCD_W, - LCD_H, refr_area.x1, refr_area.y1, area_w, area_h); - } - DMAWait(); // wait for the last DMACopyBitmap to be completed before - // sending completion message lv_disp_flush_ready(disp_drv); #endif } else { @@ -164,11 +143,12 @@ void lcdInitDisplayDriver() disp_drv.hor_res = LCD_W; /*Set the horizontal resolution in pixels*/ disp_drv.ver_res = LCD_H; /*Set the vertical resolution in pixels*/ - disp_drv.full_refresh = 0; #if !defined(LCD_VERTICAL_INVERT) + disp_drv.full_refresh = 1; disp_drv.direct_mode = 1; #else + disp_drv.full_refresh = 0; disp_drv.direct_mode = 0; #endif diff --git a/radio/src/targets/horus/lcd_driver.cpp b/radio/src/targets/horus/lcd_driver.cpp index 6ffa7b16142..b8b028d89a4 100644 --- a/radio/src/targets/horus/lcd_driver.cpp +++ b/radio/src/targets/horus/lcd_driver.cpp @@ -159,8 +159,8 @@ static void startLcdRefresh(lv_disp_drv_t *disp_drv, uint16_t *buffer, lv_area_t refr_area; lv_area_copy(&refr_area, &disp->inv_areas[i]); - //TRACE("{%d,%d,%d,%d}", refr_area.x1, - // refr_area.y1, refr_area.x2, refr_area.y2); + // TRACE("Vert invert refresh {%d,%d,%d,%d}", refr_area.x1, + // refr_area.y1, refr_area.x2 - refr_area.x1 + 1, refr_area.y2 - refr_area.y1 + 1); _rotate_area_180(refr_area);