menu: use lv_table and draw entries dynamically

Much faster than the previous implementation in creation time and rendering.
This commit is contained in:
Raphael Coeffic
2022-06-11 15:13:11 +02:00
committed by Raphael Coeffic
parent 7c409a4454
commit 2ba8619654
3 changed files with 107 additions and 48 deletions
+93 -45
View File
@@ -25,44 +25,88 @@
static void menu_clicked(lv_event_t *e)
{
auto code = lv_event_get_code(e);
if (code == LV_EVENT_CLICKED) {
lv_obj_t* target = lv_event_get_target(e);
if (!target) return;
lv_obj_t* target = lv_event_get_target(e);
if (!target) return;
lv_obj_t* parent = lv_obj_get_parent(target);
if (!parent) return;
uint16_t row;
uint16_t col;
lv_table_get_selected_cell(target, &row, &col);
MenuBody* mb = (MenuBody*)lv_obj_get_user_data(parent);
if (!mb) return;
MenuBody* mb = (MenuBody*)lv_obj_get_user_data(target);
if (!mb) return;
size_t idx = (size_t)lv_event_get_user_data(e);
mb->onPress(idx);
}
mb->onPress(row);
}
static void add_list_button(lv_obj_t* list, const char* icon, const char* txt, size_t idx)
void menu_draw_begin(lv_event_t* e)
{
lv_obj_t* btn = lv_list_add_btn(list, icon, txt);
lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE);
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
if (dsc->part != LV_PART_ITEMS) return;
void* user_data = (void *)idx;
lv_obj_add_event_cb(btn, menu_clicked, LV_EVENT_CLICKED, user_data);
lv_obj_t* table = lv_event_get_target(e);
if (!table) return;
MenuBody* mb = (MenuBody*)lv_obj_get_user_data(table);
if (!mb) return;
uint16_t row = dsc->id;
lv_canvas_t* icon = (lv_canvas_t*)mb->lines[row].getIcon();
if (!icon) return;
lv_img_t* img = &icon->img;
lv_coord_t cell_left = lv_obj_get_style_pad_left(table, LV_PART_ITEMS);
dsc->label_dsc->ofs_x = img->w + cell_left;
}
void menu_draw_end(lv_event_t* e)
{
lv_obj_draw_part_dsc_t* dsc = lv_event_get_draw_part_dsc(e);
if (dsc->part != LV_PART_ITEMS) return;
lv_obj_t* table = lv_event_get_target(e);
if (!table) return;
MenuBody* mb = (MenuBody*)lv_obj_get_user_data(table);
if (!mb) return;
uint16_t row = dsc->id;
lv_obj_t* icon = mb->lines[row].getIcon();
if (!icon) return;
lv_draw_img_dsc_t img_dsc;
lv_draw_img_dsc_init(&img_dsc);
lv_img_dsc_t* img = lv_canvas_get_img(icon);
lv_area_t coords;
lv_coord_t area_h = lv_area_get_height(dsc->draw_area);
lv_coord_t cell_left = lv_obj_get_style_pad_left(table, LV_PART_ITEMS);
coords.x1 = dsc->draw_area->x1 + cell_left;
coords.x2 = coords.x1 + img->header.w - 1;
coords.y1 = dsc->draw_area->y1 + (area_h - img->header.h) / 2;
coords.y2 = coords.y1 + img->header.h - 1;
lv_draw_img(dsc->draw_ctx, &img_dsc, &coords, img);
}
MenuBody::MenuBody(Window * parent, const rect_t & rect):
Window(parent, rect, OPAQUE, 0, lv_list_create)
Window(parent, rect, OPAQUE, 0, lv_table_create)
{
setFocus();
lv_table_set_col_cnt(lvobj, 1);
lv_table_set_col_width(lvobj, 0, rect.w);
lv_obj_add_event_cb(lvobj, menu_clicked, LV_EVENT_VALUE_CHANGED, nullptr);
lv_obj_add_event_cb(lvobj, menu_draw_begin, LV_EVENT_DRAW_PART_BEGIN, nullptr);
lv_obj_add_event_cb(lvobj, menu_draw_end, LV_EVENT_DRAW_PART_END, nullptr);
}
void MenuBody::addLine(const std::string &text, std::function<void()> onPress,
std::function<bool()> isChecked)
{
lines.emplace_back(std::move(onPress), std::move(isChecked), nullptr);
add_list_button(lvobj, nullptr, text.c_str(), lines.size() - 1);
auto idx = lines.size() - 1;
lv_table_set_cell_value_fmt(lvobj, idx, 0, text.c_str());
}
void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text,
@@ -77,8 +121,8 @@ void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text,
void* buf = (void*)(icon_mask + 4);
lv_canvas_set_buffer(canvas, buf, w, h, LV_IMG_CF_ALPHA_8BIT);
const char* icon = (const char *)lv_canvas_get_img(canvas);
add_list_button(lvobj, icon, text.c_str(), lines.size() - 1);
auto idx = lines.size() - 1;
lv_table_set_cell_value_fmt(lvobj, idx, 0, text.c_str());
}
void MenuBody::removeLines()
@@ -89,14 +133,7 @@ void MenuBody::removeLines()
coord_t MenuBody::getContentHeight()
{
coord_t h = 0;
lv_obj_update_layout(lvobj);
for(uint32_t i = 0; i < lv_obj_get_child_cnt(lvobj); i++) {
auto child = lv_obj_get_child(lvobj, i);
h += lv_obj_get_height(child);
}
return h;
return lv_obj_get_self_height(lvobj);
}
void MenuBody::onPress(size_t index)
@@ -131,21 +168,33 @@ int MenuBody::rangeCheck(int index)
void MenuBody::setIndex(int index)
{
if (index < (int)lines.size()) {
lv_obj_t *btn = lv_obj_get_child(lvobj, selectedIndex);
if (btn != nullptr) {
lv_obj_clear_state(btn, LV_STATE_CHECKED);
lv_obj_clear_state(btn, LV_STATE_FOCUS_KEY);
}
if (index == selectedIndex) return;
selectedIndex = index;
btn = lv_obj_get_child(lvobj, selectedIndex);
if (btn != nullptr) {
lv_obj_add_state(btn, LV_STATE_CHECKED);
lv_obj_add_state(btn, LV_STATE_FOCUS_KEY);
lv_obj_scroll_to_view(btn, LV_ANIM_OFF);
lv_group_focus_obj(btn);
lv_obj_invalidate(lvobj);
lv_table_t* table = (lv_table_t*)lvobj;
table->row_act = index;
table->col_act = 0;
lv_coord_t h_before = 0;
for (uint16_t i = 0; i < table->row_act; i++)
h_before += table->row_h[i];
lv_coord_t row_h = table->row_h[table->row_act];
lv_coord_t scroll_y = lv_obj_get_scroll_y(lvobj);
lv_coord_t h = lv_obj_get_height(lvobj);
lv_coord_t diff_y = 0;
if (h_before < scroll_y) {
diff_y = scroll_y - h_before;
} else if (scroll_y + h < h_before + row_h) {
diff_y = scroll_y + h - h_before - row_h;
} else {
return;
}
lv_obj_scroll_by_bounded(lvobj, 0, diff_y, LV_ANIM_OFF);
}
}
@@ -268,13 +317,12 @@ void Menu::updatePosition()
if (!toolbar) {
// there is no navigation bar at the left, we may center the window on screen
auto headerHeight = content->getHeaderHeight();
auto bodyHeight = min<coord_t>(height, MENUS_MAX_HEIGHT);
auto bodyHeight = min<coord_t>(height, MENUS_MAX_HEIGHT - headerHeight);
content->setTop((LCD_H - headerHeight - bodyHeight) / 2);
content->setHeight(headerHeight + bodyHeight);
content->body.setTop(headerHeight);
content->body.setHeight(bodyHeight);
content->body.setHeight(bodyHeight);
}
}
void Menu::setTitle(std::string text)
+5 -1
View File
@@ -61,6 +61,8 @@ class MenuBody: public Window
MenuLine(MenuLine &) = delete;
MenuLine(MenuLine &&) = default;
lv_obj_t* getIcon() { return icon.get(); }
protected:
bool isSeparator = false;
std::function<void()> onPress;
@@ -123,7 +125,9 @@ class MenuBody: public Window
void onPress(size_t index);
protected:
friend void menuBodyEventCallback(lv_event_t *);
friend void menu_draw_begin(lv_event_t *);
friend void menu_draw_end(lv_event_t *);
void selectNext(MENU_DIRECTION direction);
int rangeCheck(int);
void setIndex(int index);
+9 -2
View File
@@ -64,8 +64,15 @@ void ModalWindowContent::setTitle(const std::string& text)
lv_obj_move_to_index(title, 0);
lv_obj_set_width(title, lv_pct(100));
lv_obj_set_style_pad_all(title, PAGE_PADDING, LV_PART_MAIN);
// TODO: styling
// lv_obj_set_style_pad_left(title, FIELD_PADDING_LEFT, 0);
lv_color_t bg_color = makeLvColor(COLOR_THEME_SECONDARY1);
lv_obj_set_style_bg_color(title, bg_color, LV_PART_MAIN);
lv_obj_set_style_bg_opa(title, LV_OPA_100, LV_PART_MAIN);
lv_color_t txt_color = makeLvColor(COLOR_THEME_PRIMARY2);
lv_obj_set_style_text_color(title, txt_color, LV_PART_MAIN);
lv_obj_update_layout(lvobj);
}
lv_label_set_text(title, text.c_str());
}