From a20792a2113bd3e2bf6307a26c7f3be0054cef82 Mon Sep 17 00:00:00 2001 From: Raphael Coeffic Date: Tue, 19 Apr 2022 10:42:50 +0200 Subject: [PATCH] menu: added support for icons rather than custom draw function --- radio/src/thirdparty/libopenui/src/menu.cpp | 65 +++++++++++++---- radio/src/thirdparty/libopenui/src/menu.h | 78 ++++++++++----------- 2 files changed, 87 insertions(+), 56 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 3b4440649..6019d573f 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -21,6 +21,8 @@ #include "font.h" #include "theme.h" +#include "lvgl/src/widgets/lv_canvas.h" + void menuBodyEventCallback(lv_event_t *e) { auto code = lv_event_get_code(e); @@ -40,8 +42,8 @@ void menuBodyEventCallback(lv_event_t *e) } else { mb->setIndex(index); - menu->deleteLater(); mb->lines[index].onPress(); + menu->deleteLater(); } } } @@ -58,19 +60,52 @@ MenuBody::MenuBody(Window * parent, const rect_t & rect): void MenuBody::addLine(const std::string &text, std::function onPress, std::function isChecked) { - lines.emplace_back(text, std::move(onPress), std::move(isChecked)); + lines.emplace_back(std::move(onPress), std::move(isChecked), nullptr); lv_obj_t *btn = lv_list_add_btn(lvobj, nullptr, text.c_str()); lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); lv_obj_add_event_cb(btn, menuBodyEventCallback, LV_EVENT_CLICKED, (void *)(lines.size() - 1)); } +void MenuBody::addLine(const uint8_t *icon_mask, const std::string &text, + std::function onPress, + std::function isChecked) +{ + lv_obj_t* canvas = lv_canvas_create(nullptr); + lines.emplace_back(std::move(onPress), std::move(isChecked), canvas); + + lv_coord_t w = *((uint16_t *)icon_mask); + lv_coord_t h = *(((uint16_t *)icon_mask)+1); + void* buf = (void*)(icon_mask + 4); + lv_canvas_set_buffer(canvas, buf, w, h, LV_IMG_CF_ALPHA_8BIT); + + lv_obj_t *btn = lv_list_add_btn( + lvobj, (const char *)lv_canvas_get_img(canvas), text.c_str()); + + lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); + lv_obj_add_event_cb(btn, menuBodyEventCallback, LV_EVENT_CLICKED, + (void *)(lines.size() - 1)); +} + void MenuBody::removeLines() { lines.clear(); lv_obj_clean(lvobj); } +coord_t MenuBody::getContentHeight() +{ + coord_t h = 0; + lv_obj_update_layout(lvobj); + for(int 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; +} + + // ensure index is in range and also handle wrapping index int MenuBody::rangeCheck(int index) { @@ -88,6 +123,7 @@ void MenuBody::setIndex(int index) 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); } selectedIndex = index; @@ -95,6 +131,7 @@ void MenuBody::setIndex(int 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); } } @@ -156,8 +193,8 @@ void MenuBody::onEvent(event_t event) menu->invalidate(); } else { - menu->deleteLater(); lines[selectedIndex].onPress(); + menu->deleteLater(); } } } @@ -193,10 +230,7 @@ Menu::Menu(Window * parent, bool multiple): void Menu::updatePosition() { - // calcualte the correct menu height given that the line heights are variable - coord_t height = 0; - for (int i = 0; i < (signed)content->body.lines.size(); i++) - height += content->body.lines[i].lineHeight(); + coord_t height = content->body.getContentHeight(); if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen @@ -217,24 +251,27 @@ void Menu::setTitle(std::string text) updatePosition(); } -void Menu::addLine(const std::string & text, std::function onPress, std::function isChecked) +void Menu::addLine(const std::string &text, std::function onPress, + std::function isChecked) { content->body.addLine(text, std::move(onPress), std::move(isChecked)); updatePosition(); } +void Menu::addLine(const uint8_t *icon_mask, const std::string &text, + std::function onPress, + std::function isChecked) +{ + content->body.addLine(icon_mask, text, std::move(onPress), std::move(isChecked)); + updatePosition(); +} + void Menu::addSeparator() { content->body.addSeparator(); updatePosition(); } -void Menu::addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) -{ - content->body.addCustomLine(std::move(drawLine), std::move(onPress), std::move(isChecked)); - updatePosition(); -} - void Menu::removeLines() { content->body.removeLines(); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index c539719b1..0798ec8d5 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -27,6 +27,13 @@ class Menu; class MenuWindowContent; +struct lvobj_delete { + constexpr lvobj_delete() = default; + void operator()(lv_obj_t* obj) const { + lv_obj_del(obj); + } +}; + class MenuBody: public Window { friend class MenuWindowContent; @@ -38,44 +45,27 @@ class MenuBody: public Window DIRECTION_DOWN = -1 }; - class MenuLine { + class MenuLine + { friend class MenuBody; - public: - MenuLine(std::string text, std::function onPress, std::function isChecked): - text(std::move(text)), - onPress(std::move(onPress)), - isChecked(std::move(isChecked)) - { - } + public: + MenuLine(std::function onPress, std::function isChecked, + lv_obj_t *icon) : + onPress(std::move(onPress)), isChecked(std::move(isChecked)), icon(icon) + { + } - MenuLine(std::function drawLine, std::function onPress, std::function isChecked): - drawLine(std::move(drawLine)), - onPress(std::move(onPress)), - isChecked(std::move(isChecked)) - { - } + MenuLine(bool isSeparator) : isSeparator(true) {} + MenuLine(MenuLine &) = delete; + MenuLine(MenuLine &&) = default; - MenuLine(bool isSeparator) : - isSeparator(true), - height(MENUS_SEPARATOR_HEIGHT) - { - } - - inline coord_t lineHeight() { return height; } - - MenuLine(MenuLine &) = delete; - - MenuLine(MenuLine &&) = default; - - protected: - friend void menuBodyEventCallback(lv_event_t *e); - bool isSeparator = false; - coord_t height = MENUS_LINE_HEIGHT + 10; - std::string text; - std::function drawLine; - std::function onPress; - std::function isChecked; + protected: + friend void menuBodyEventCallback(lv_event_t *e); + bool isSeparator = false; + std::function onPress; + std::function isChecked; + std::unique_ptr icon; }; public: @@ -109,13 +99,12 @@ class MenuBody: public Window void onEvent(event_t event) override; #endif - void addLine(const std::string & text, std::function onPress, std::function isChecked); + void addLine(const std::string &text, std::function onPress, + std::function isChecked); - void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked) - { - lines.emplace_back(std::move(drawLine), std::move(onPress), std::move(isChecked)); - invalidate(); - } + void addLine(const uint8_t *icon_mask, const std::string &text, + std::function onPress, + std::function isChecked); void addSeparator() { @@ -129,6 +118,8 @@ class MenuBody: public Window onCancel = std::move(handler); } + coord_t getContentHeight(); + protected: friend void menuBodyEventCallback(lv_event_t *); void selectNext(MENU_DIRECTION direction); @@ -208,9 +199,12 @@ class Menu: public ModalWindow void setTitle(std::string text); - void addLine(const std::string & text, std::function onPress, std::function isChecked = nullptr); + void addLine(const std::string &text, std::function onPress, + std::function isChecked = nullptr); - void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked = nullptr); + void addLine(const uint8_t *icon_mask, const std::string &text, + std::function onPress, + std::function isChecked = nullptr); void addSeparator();