diff --git a/radio/src/thirdparty/libopenui/src/choice.cpp b/radio/src/thirdparty/libopenui/src/choice.cpp index cd1ea8cb0..d74dfff7e 100644 --- a/radio/src/thirdparty/libopenui/src/choice.cpp +++ b/radio/src/thirdparty/libopenui/src/choice.cpp @@ -37,12 +37,14 @@ void choicePaintCallback(lv_event_t *e) } } -ChoiceBase::ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type, WindowFlags windowFlags): - FormField(parent, rect, windowFlags), - type(type) +ChoiceBase::ChoiceBase(Window* parent, const rect_t& rect, ChoiceType type, + WindowFlags windowFlags) : + FormField(parent, rect, windowFlags), type(type) { + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); lv_obj_add_event_cb(lvobj, choicePaintCallback, LV_EVENT_DRAW_MAIN_BEGIN, 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); @@ -55,7 +57,6 @@ ChoiceBase::ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type, lv_obj_set_style_border_width(lvobj, 1, LV_PART_MAIN); lv_obj_set_style_border_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY2), LV_PART_MAIN); lv_obj_set_style_text_color(lvobj, makeLvColor(COLOR_THEME_SECONDARY1), LV_PART_MAIN); - lv_obj_set_style_text_font(lvobj, &lv_font_roboto_13, LV_PART_MAIN); // focused lv_obj_set_style_bg_color(lvobj, makeLvColor(COLOR_THEME_FOCUS), LV_PART_MAIN | LV_STATE_FOCUSED); @@ -65,14 +66,12 @@ ChoiceBase::ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type, lv_obj_set_style_pad_left(label, FIELD_PADDING_LEFT, LV_PART_MAIN); lv_obj_set_style_pad_top(label, FIELD_PADDING_TOP, LV_PART_MAIN); - // 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_align(img, LV_ALIGN_RIGHT_MID); } - std::string Choice::getLabelText() { std::string text = ""; @@ -89,7 +88,7 @@ std::string Choice::getLabelText() return text; } -Choice::Choice(FormGroup * parent, const rect_t & rect, int vmin, int vmax, +Choice::Choice(Window* parent, const rect_t & rect, int vmin, int vmax, std::function getValue, std::function setValue, WindowFlags windowFlags) : ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), vmin(vmin), @@ -99,41 +98,46 @@ Choice::Choice(FormGroup * parent, const rect_t & rect, int vmin, int vmax, { } -Choice::Choice(FormGroup * parent, const rect_t & rect, const char * const values[], int vmin, int vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : - ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), - vmin(vmin), - vmax(vmax), - getValue(std::move(getValue)), - setValue(std::move(setValue)) +Choice::Choice(Window* parent, const rect_t& rect, const char* const values[], + int vmin, int vmax, std::function getValue, + std::function setValue, WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) { setValues(values); } -Choice::Choice(FormGroup * parent, const rect_t & rect, std::vector values, int vmin, int vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : - ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), - values(std::move(values)), - vmin(vmin), - vmax(vmax), - getValue(std::move(getValue)), - setValue(std::move(setValue)) +Choice::Choice(Window* parent, const rect_t& rect, + std::vector values, int vmin, int vmax, + std::function getValue, std::function setValue, + WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), + values(std::move(values)), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) { } -Choice::Choice(FormGroup * parent, const rect_t & rect, const char * values, int vmin, int vmax, - std::function getValue, std::function setValue, WindowFlags windowFlags) : - ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), - vmin(vmin), - vmax(vmax), - getValue(std::move(getValue)), - setValue(std::move(setValue)) +Choice::Choice(Window* parent, const rect_t& rect, const char* values, int vmin, + int vmax, std::function getValue, + std::function setValue, WindowFlags windowFlags) : + ChoiceBase(parent, rect, CHOICE_TYPE_DROPOWN, windowFlags), + vmin(vmin), + vmax(vmax), + getValue(std::move(getValue)), + setValue(std::move(setValue)) { if (values) { uint8_t len = values[0]; - const char * value = &values[1]; + const char* value = &values[1]; for (int i = vmin; i <= vmax; i++) { - this->values.emplace_back(std::string(value, min(len, strlen(value)))); + this->values.emplace_back( + std::string(value, min(len, strlen(value)))); value += len; } } diff --git a/radio/src/thirdparty/libopenui/src/choice.h b/radio/src/thirdparty/libopenui/src/choice.h index b54d57676..52e6e513a 100644 --- a/radio/src/thirdparty/libopenui/src/choice.h +++ b/radio/src/thirdparty/libopenui/src/choice.h @@ -32,7 +32,7 @@ enum ChoiceType { class ChoiceBase : public FormField { public: - ChoiceBase(FormGroup * parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN, WindowFlags windowFlags = 0); + ChoiceBase(Window* parent, const rect_t & rect, ChoiceType type = CHOICE_TYPE_DROPOWN, WindowFlags windowFlags = 0); inline ChoiceType getType() const { @@ -48,20 +48,20 @@ class ChoiceBase : public FormField class Choice: public ChoiceBase { public: - Choice(FormGroup *parent, const rect_t &rect, int vmin, int vmax, + Choice(Window* parent, const rect_t &rect, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup *parent, const rect_t &rect, + Choice(Window* parent, const rect_t &rect, std::vector values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup *parent, const rect_t &rect, const char *const values[], + Choice(Window* parent, const rect_t &rect, const char *const values[], int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); - Choice(FormGroup *parent, const rect_t &rect, const char *values, int vmin, + Choice(Window* parent, const rect_t &rect, const char *values, int vmin, int vmax, std::function getValue, std::function setValue = nullptr, WindowFlags windowFlags = 0); diff --git a/radio/src/thirdparty/libopenui/src/choiceex.cpp b/radio/src/thirdparty/libopenui/src/choiceex.cpp index d8a87698d..f1986f904 100644 --- a/radio/src/thirdparty/libopenui/src/choiceex.cpp +++ b/radio/src/thirdparty/libopenui/src/choiceex.cpp @@ -34,9 +34,11 @@ void ChoiceEx::setLongPressHandler(std::function handler) longPressHandler = handler; } -ChoiceEx::ChoiceEx(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vmax, std::function getValue, - std::function setValue, WindowFlags windowFlags) : - Choice(parent, rect, vmin, vmax, getValue, setValue, windowFlags) +ChoiceEx::ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin, + int16_t vmax, std::function getValue, + std::function setValue, + WindowFlags windowFlags) : + Choice(parent, rect, vmin, vmax, getValue, setValue, windowFlags) { #if defined(HARDWARE_TOUCH) duration10ms = 0; diff --git a/radio/src/thirdparty/libopenui/src/choiceex.h b/radio/src/thirdparty/libopenui/src/choiceex.h index 4a63d13cb..4d1348153 100644 --- a/radio/src/thirdparty/libopenui/src/choiceex.h +++ b/radio/src/thirdparty/libopenui/src/choiceex.h @@ -25,10 +25,12 @@ class ChoiceEx : public Choice { public: - ChoiceEx(FormGroup * parent, const rect_t &rect, int16_t vmin, int16_t vmax, std::function getValue, - std::function setValue = nullptr, WindowFlags windowFlags = 0); - - void setLongPressHandler(std::function handler); + ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin, int16_t vmax, + std::function getValue, + std::function setValue = nullptr, + WindowFlags windowFlags = 0); + + void setLongPressHandler(std::function handler); #if defined(HARDWARE_KEYS) void onEvent(event_t event) override; @@ -57,4 +59,4 @@ protected: #endif }; -#endif \ No newline at end of file +#endif diff --git a/radio/src/thirdparty/libopenui/src/flexlayout.h b/radio/src/thirdparty/libopenui/src/flexlayout.h new file mode 100644 index 000000000..e9cf3dc67 --- /dev/null +++ b/radio/src/thirdparty/libopenui/src/flexlayout.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) EdgeTX + * + * Based on code named + * opentx - https://github.com/opentx/opentx + * th9x - http://code.google.com/p/th9x + * er9x - http://code.google.com/p/er9x + * gruvin9x - http://code.google.com/p/gruvin9x + * + * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#pragma once + +#include + +class FlexGridLayout +{ + const lv_coord_t* col_dsc = nullptr; + const lv_coord_t* row_dsc = nullptr; + lv_coord_t padding = 0; + + uint8_t col_pos = 0; + uint8_t col_span = 1; + uint8_t row_pos = 0; + uint8_t row_span = 1; + + public: + FlexGridLayout(const lv_coord_t col_dsc[], const lv_coord_t row_dsc[], + lv_coord_t padding = 4) : + col_dsc(col_dsc), row_dsc(row_dsc), padding(padding) + { + } + + void apply(lv_obj_t* obj) { + + // padding + lv_obj_set_style_pad_all(obj, padding, LV_PART_MAIN); + lv_obj_set_style_pad_left(obj, padding + PAGE_PADDING, LV_PART_MAIN); + + // layout + lv_obj_set_layout(obj, LV_LAYOUT_GRID); + if (col_dsc && row_dsc) { + lv_obj_set_grid_dsc_array(obj, col_dsc, row_dsc); + } + } + + void add(lv_obj_t* obj) { + if (col_dsc && row_dsc) { + lv_obj_set_grid_cell(obj, LV_GRID_ALIGN_START, col_pos, col_span, + LV_GRID_ALIGN_CENTER, row_pos, row_span); + } + } + + void nextColumn() { col_pos++; } + void nextRow() { row_pos++; } + void resetPos() { col_pos = 0; row_pos = 0; } +}; diff --git a/radio/src/thirdparty/libopenui/src/form.cpp b/radio/src/thirdparty/libopenui/src/form.cpp index 52c36af1c..a2c7c2715 100644 --- a/radio/src/thirdparty/libopenui/src/form.cpp +++ b/radio/src/thirdparty/libopenui/src/form.cpp @@ -31,9 +31,9 @@ FormField::FormField(Window* parent, const rect_t& rect, Window(parent, rect, windowFlags, textFlags, objConstruct) { if (!(windowFlags & NO_FOCUS)) { - auto* form = dynamic_cast(parent); - if (form) { - form->addField(this, windowFlags & PUSH_FRONT); + auto cont = dynamic_cast(parent); + if (cont) { + cont->addField(this, windowFlags & PUSH_FRONT); } } @@ -109,6 +109,42 @@ void FormField::setFocus(uint8_t flag, Window * from) } } +FormGroup::Line::Line(FormGroup* parent, lv_obj_t* obj, + FlexGridLayout* layout) : + Window(parent, obj), layout(layout) +{ + // forward scroll and focus + windowFlags |= FORWARD_SCROLL | FORM_FORWARD_FOCUS; + + if (layout) { + layout->apply(obj); + } + + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); + lv_obj_set_width(lvobj, lv_pct(100)); +} + +void FormGroup::Line::addChild(Window* window, bool front) +{ + Window::addChild(window, front); + if (layout) { + layout->add(window->getLvObj()); + layout->nextColumn(); + } +} + +void FormGroup::Line::addField(FormField *field, bool front) +{ + auto form = static_cast(parent); + form->addField(field, front); +} + +void FormGroup::Line::removeField(FormField *field) +{ + auto form = static_cast(parent); + form->removeField(field); +} + static lv_style_t border_style; static lv_style_t focus_border_style; @@ -118,7 +154,6 @@ FormGroup::FormGroup(Window* parent, const rect_t& rect, { lv_obj_set_style_bg_opa(lvobj, LV_OPA_TRANSP, LV_PART_MAIN); - if (!(windowFlags & (FORM_NO_BORDER | FORM_FORWARD_FOCUS))) { lv_style_init(&border_style); lv_style_set_border_width(&border_style, 2); @@ -134,6 +169,36 @@ FormGroup::FormGroup(Window* parent, const rect_t& rect, } } +void FormGroup::clear() +{ + Window::clear(); + first = nullptr; + last = nullptr; + if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { + previous->setNextField(this); + } + lv_obj_set_layout(lvobj, 0); +} + +void FormGroup::setFlexLayout(lv_flex_flow_t flow, lv_coord_t padding) +{ + lv_obj_set_flex_flow(lvobj, flow); + if (_LV_FLEX_COLUMN & flow) { + lv_obj_set_style_pad_row(lvobj, padding, LV_PART_MAIN); + } else { + lv_obj_set_style_pad_column(lvobj, padding, LV_PART_MAIN); + } + lv_obj_set_width(lvobj, lv_pct(100)); + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); +} + +FormGroup::Line* FormGroup::newLine(FlexGridLayout* layout) +{ + layout->resetPos(); + auto lv_line = lv_obj_create(lvobj); + return new Line(this, lv_line, layout); +} + void FormGroup::removeField(FormField * field) { FormField* prev = field->getPreviousField(); diff --git a/radio/src/thirdparty/libopenui/src/form.h b/radio/src/thirdparty/libopenui/src/form.h index 6f75350d3..9e5df9b26 100644 --- a/radio/src/thirdparty/libopenui/src/form.h +++ b/radio/src/thirdparty/libopenui/src/form.h @@ -20,6 +20,7 @@ #pragma once #include "window.h" +#include "flexlayout.h" constexpr WindowFlags FORM_FORWARD_FOCUS = WINDOW_FLAGS_LAST << 1; constexpr WindowFlags FORM_DETACHED = WINDOW_FLAGS_LAST << 2; @@ -70,7 +71,7 @@ class FormField : public Window // } if (editMode && lvobj != nullptr) { - lv_obj_add_state(lvobj, LV_STATE_EDITED | LV_STATE_FOCUSED); + lv_obj_add_state(lvobj, LV_STATE_EDITED); } else if (lvobj != nullptr) { lv_obj_clear_state(lvobj, LV_STATE_EDITED); } @@ -114,9 +115,29 @@ class FormField : public Window std::function backgroundHandler = nullptr; }; -class FormGroup : public FormField +class FieldContainer { public: + virtual void addField(FormField *field, bool front = false) = 0; + virtual void removeField(FormField *field) = 0; +}; + +class FormGroup : public FormField, public FieldContainer +{ + public: + class Line : public Window, public FieldContainer + { + FlexGridLayout* layout; + + public: + Line(FormGroup *parent, lv_obj_t *lvobj, FlexGridLayout* layout); + + protected: + void addChild(Window* window, bool front = false) override; + void addField(FormField *field, bool front = false) override; + void removeField(FormField *field) override; + }; + FormGroup(Window *parent, const rect_t &rect, WindowFlags windowflags = 0, LvglCreate objConstruct = nullptr); @@ -124,21 +145,16 @@ class FormGroup : public FormField std::string getName() const override { return "FormGroup"; } #endif - void clear() - { - Window::clear(); - first = nullptr; - last = nullptr; - if (previous && (windowFlags & FORM_FORWARD_FOCUS)) { - previous->setNextField(this); - } - } + void clear(); + void setFlexLayout(lv_flex_flow_t flow = LV_FLEX_FLOW_COLUMN, lv_coord_t padding = 0); + Line* newLine(FlexGridLayout* layout); + void setFocus(uint8_t flag = SET_FOCUS_DEFAULT, Window *from = nullptr) override; - virtual void addField(FormField *field, bool front = false); - virtual void removeField(FormField *field); + void addField(FormField *field, bool front = false) override; + void removeField(FormField *field) override; void setFirstField(FormField *field) { first = field; } void setLastField(FormField *field) { last = field; } diff --git a/radio/src/thirdparty/libopenui/src/static.cpp b/radio/src/thirdparty/libopenui/src/static.cpp index 19e320b1b..18d84aaf3 100644 --- a/radio/src/thirdparty/libopenui/src/static.cpp +++ b/radio/src/thirdparty/libopenui/src/static.cpp @@ -26,10 +26,11 @@ StaticText::StaticText(Window* parent, const rect_t& rect, std::string txt, text(std::move(txt)) { lv_label_set_text(lvobj, text.c_str()); + lv_obj_set_height(lvobj, LV_SIZE_CONTENT); lv_obj_clear_flag(lvobj, LV_OBJ_FLAG_CLICK_FOCUSABLE); - lv_obj_set_style_text_font(lvobj, LV_FONT_DEFAULT, LV_PART_MAIN); - lv_obj_set_style_text_color(lvobj, makeLvColor(textFlags), LV_PART_MAIN); + // lv_obj_set_style_text_font(lvobj, LV_FONT_DEFAULT, LV_PART_MAIN); + // lv_obj_set_style_text_color(lvobj, makeLvColor(textFlags), LV_PART_MAIN); if (textFlags & CENTERED) lv_obj_set_style_text_align(lvobj, LV_TEXT_ALIGN_CENTER, 0); diff --git a/radio/src/thirdparty/libopenui/src/window.cpp b/radio/src/thirdparty/libopenui/src/window.cpp index 0c6eaf689..af1cf9d59 100644 --- a/radio/src/thirdparty/libopenui/src/window.cpp +++ b/radio/src/thirdparty/libopenui/src/window.cpp @@ -132,12 +132,10 @@ static void window_event_cb(lv_event_t * e) window->setScrollPositionX(scroll_x); } else if (code == LV_EVENT_FOCUSED) { - bool lvgl_focused = lv_obj_has_state(target, LV_STATE_FOCUSED); - bool loiu_focused = ((Window *)target->user_data)->hasFocus(); - TRACE_WINDOWS("FOCUSED[%d|%d] %s", - lvgl_focused, loiu_focused, + bool focused = ((Window *)target->user_data)->hasFocus(); + TRACE_WINDOWS("FOCUSED[%d] %s", focused, window->getWindowDebugString().c_str()); - if (!loiu_focused) { window->setFocus(); } + if (!focused) { window->setFocus(); } } } @@ -327,16 +325,22 @@ void Window::setFocus(uint8_t flag, Window * from) if (focusWindow != this) { // synchronize lvgl focused state with libopenui - if (lvobj != nullptr && !lv_obj_has_state(lvobj, LV_STATE_FOCUSED)) { - lv_obj_add_state(lvobj, LV_STATE_FOCUSED); + if (lvobj != nullptr && + !lv_obj_has_state(lvobj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY)) { + lv_obj_add_state(lvobj, LV_STATE_FOCUSED | LV_STATE_FOCUS_KEY); if (focusWindow != nullptr) { - lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED); - lv_obj_clear_state(focusWindow->lvobj, LV_STATE_EDITED); - // lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED | LV_STATE_EDITED); + lv_obj_clear_state(focusWindow->lvobj, LV_STATE_FOCUSED | + LV_STATE_FOCUS_KEY | + LV_STATE_EDITED); } } - // scroll before calling focusHandler so that the window can adjust the scroll position if needed + // scroll before calling focusHandler so that the window can adjust the + // scroll position if needed +#if 1 + // lv_obj_scroll_to_view(lvobj, LV_ANIM_OFF); + lv_obj_scroll_to_view_recursive(lvobj, LV_ANIM_OFF); +#else Window * parent = this->parent; while (parent && parent->getWindowFlags() & FORWARD_SCROLL) { parent = parent->parent; @@ -345,6 +349,7 @@ void Window::setFocus(uint8_t flag, Window * from) parent->scrollTo(this); invalidate(); } +#endif clearFocus(); focusWindow = this; diff --git a/radio/src/thirdparty/libopenui/src/window.h b/radio/src/thirdparty/libopenui/src/window.h index 67c4befd0..c0fd18435 100644 --- a/radio/src/thirdparty/libopenui/src/window.h +++ b/radio/src/thirdparty/libopenui/src/window.h @@ -143,6 +143,8 @@ class Window auto textColor = COLOR_VAL(flags); auto r = GET_RED(textColor), g = GET_GREEN(textColor), b = GET_BLUE(textColor); lv_obj_set_style_text_color(lvobj, lv_color_make(r, g, b), LV_PART_MAIN); + + // rco: shouldn't this be done via 'setTextFlags()' on the children? for (uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) { auto child = lv_obj_get_child(lvobj, i); lv_obj_set_style_text_color(child, lv_color_make(r, g, b), LV_PART_MAIN); @@ -444,10 +446,11 @@ class Window std::function closeHandler; std::function focusHandler; - void addChild(Window * window, bool front = false) + virtual void addChild(Window * window, bool front = false) { if (lv_obj_get_parent(window->lvobj) != nullptr) { lv_obj_set_parent(window->lvobj, this->lvobj); + // not used anywhere! if (front) lv_obj_move_to_index(window->lvobj, 0); }