WiP: flex layout

This commit is contained in:
Raphael Coeffic
2022-06-11 15:13:11 +02:00
committed by Raphael Coeffic
parent 22499d1c16
commit f473271f65
10 changed files with 240 additions and 75 deletions
+35 -31
View File
@@ -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<int()> getValue, std::function<void(int)> 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<int()> getValue, std::function<void(int)> 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<int()> getValue,
std::function<void(int)> 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<std::string> values, int vmin, int vmax,
std::function<int()> getValue, std::function<void(int)> 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<std::string> values, int vmin, int vmax,
std::function<int()> getValue, std::function<void(int)> 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<int()> getValue, std::function<void(int)> 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<int()> getValue,
std::function<void(int)> 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<uint8_t>(len, strlen(value))));
this->values.emplace_back(
std::string(value, min<uint8_t>(len, strlen(value))));
value += len;
}
}
+5 -5
View File
@@ -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<int()> getValue,
std::function<void(int)> setValue = nullptr,
WindowFlags windowFlags = 0);
Choice(FormGroup *parent, const rect_t &rect,
Choice(Window* parent, const rect_t &rect,
std::vector<std::string> values, int vmin, int vmax,
std::function<int()> getValue,
std::function<void(int)> 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<int()> getValue,
std::function<void(int)> 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<int()> getValue,
std::function<void(int)> setValue = nullptr,
WindowFlags windowFlags = 0);
+5 -3
View File
@@ -34,9 +34,11 @@ void ChoiceEx::setLongPressHandler(std::function<void(event_t)> handler)
longPressHandler = handler;
}
ChoiceEx::ChoiceEx(FormGroup * parent, const rect_t & rect, int16_t vmin, int16_t vmax, std::function<int16_t()> getValue,
std::function<void(int16_t)> 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<int16_t()> getValue,
std::function<void(int16_t)> setValue,
WindowFlags windowFlags) :
Choice(parent, rect, vmin, vmax, getValue, setValue, windowFlags)
{
#if defined(HARDWARE_TOUCH)
duration10ms = 0;
+7 -5
View File
@@ -25,10 +25,12 @@
class ChoiceEx : public Choice
{
public:
ChoiceEx(FormGroup * parent, const rect_t &rect, int16_t vmin, int16_t vmax, std::function<int16_t()> getValue,
std::function<void(int16_t)> setValue = nullptr, WindowFlags windowFlags = 0);
void setLongPressHandler(std::function<void(event_t)> handler);
ChoiceEx(Window* parent, const rect_t& rect, int16_t vmin, int16_t vmax,
std::function<int16_t()> getValue,
std::function<void(int16_t)> setValue = nullptr,
WindowFlags windowFlags = 0);
void setLongPressHandler(std::function<void(event_t)> handler);
#if defined(HARDWARE_KEYS)
void onEvent(event_t event) override;
@@ -57,4 +59,4 @@ protected:
#endif
};
#endif
#endif
+67
View File
@@ -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 <lvgl/lvgl.h>
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; }
};
+69 -4
View File
@@ -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<FormGroup*>(parent);
if (form) {
form->addField(this, windowFlags & PUSH_FRONT);
auto cont = dynamic_cast<FieldContainer*>(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<FormGroup*>(parent);
form->addField(field, front);
}
void FormGroup::Line::removeField(FormField *field)
{
auto form = static_cast<FormGroup*>(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();
+29 -13
View File
@@ -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<uint32_t(FormField *)> 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; }
+3 -2
View File
@@ -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);
+16 -11
View File
@@ -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;
+4 -1
View File
@@ -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<void()> closeHandler;
std::function<void(bool)> 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);
}