From 45e4f05b165ca0efe4d3d456aede7012072046b8 Mon Sep 17 00:00:00 2001 From: "kkoenig1@optonline.com" Date: Thu, 23 Sep 2021 08:03:00 -0400 Subject: [PATCH] readded separators correctly this time --- radio/src/thirdparty/libopenui/src/menu.cpp | 123 ++++++++++++++++---- radio/src/thirdparty/libopenui/src/menu.h | 34 +++++- 2 files changed, 133 insertions(+), 24 deletions(-) diff --git a/radio/src/thirdparty/libopenui/src/menu.cpp b/radio/src/thirdparty/libopenui/src/menu.cpp index 753fc9ccd..8283e6bc3 100644 --- a/radio/src/thirdparty/libopenui/src/menu.cpp +++ b/radio/src/thirdparty/libopenui/src/menu.cpp @@ -21,15 +21,59 @@ #include "font.h" #include "theme.h" -void MenuBody::select(int index) + +// ensure index is in range and also handle wrapping index +int MenuBody::rangeCheck(int index) +{ + if (index < 0) + index = lines.size() - 1; + else if (index > lines.size() - 1) + index = 0; + + return index; +} + +void MenuBody::setIndex(int index) { selectedIndex = index; + // calculate the correct scroll postion based on there being separators + int scrollY = 0; + for (int i = 0; i < selectedIndex; i++) + scrollY += lines[i].lineHeight(); + if (innerHeight > height()) { - setScrollPositionY(MENUS_LINE_HEIGHT * index - 3 * MENUS_LINE_HEIGHT); + setScrollPositionY(scrollY - 3 * MENUS_LINE_HEIGHT); } + invalidate(); } +void MenuBody::select(int index) +{ + // adjust the selection based on separators + for (int i = 0; i <= index; i++) { + if (lines[i].isSeparator) + index++; + index = rangeCheck(index); + } + + setIndex(index); +} + +void MenuBody::selectNext(MENU_DIRECTION direction) +{ + // look for the next non separator line + int index = selectedIndex + direction; + index = rangeCheck(index); + while (lines[index].isSeparator) { + index += direction; + index = rangeCheck(index); + } + + setIndex(index); +} + + #if defined(HARDWARE_KEYS) void MenuBody::onEvent(event_t event) { @@ -37,13 +81,13 @@ void MenuBody::onEvent(event_t event) if (event == EVT_ROTARY_RIGHT) { if (!lines.empty()) { - select(int((selectedIndex + 1) % lines.size())); + selectNext(DIRECTION_UP); onKeyPress(); } } else if (event == EVT_ROTARY_LEFT) { if (!lines.empty()) { - select(int(selectedIndex <= 0 ? lines.size() - 1 : selectedIndex - 1)); + selectNext(DIRECTION_DOWN); onKeyPress(); } } @@ -83,22 +127,39 @@ void MenuBody::onEvent(event_t event) bool MenuBody::onTouchEnd(coord_t /*x*/, coord_t y) { Menu * menu = getParentMenu(); - int index = y / MENUS_LINE_HEIGHT; + + int index = 0; + + // dynamically find the selected element based on variable line heights + coord_t yAccumulator = 0; + for (int i = 0; i < lines.size(); i++) { + if (y >= yAccumulator && y <= yAccumulator + lines[i].lineHeight()) { + index = i; + break; + } + yAccumulator += lines[i].lineHeight(); + } + + // dont allow selecting separators + if (lines[index].isSeparator) + return false; + if (index < (int)lines.size()) { onKeyPress(); if (menu->multiple) { if (selectedIndex == index) lines[index].onPress(); else - select(index); + setIndex(index); menu->invalidate(); } else { - select(index); + setIndex(index); menu->deleteLater(); lines[index].onPress(); } } + return true; } #endif @@ -107,37 +168,43 @@ void MenuBody::paint(BitmapBuffer * dc) { dc->clear(COLOR_THEME_PRIMARY2); - for (unsigned i = 0; i < lines.size(); i++) { + int y = 0; + for (int i = 0; i < lines.size(); i++) { auto& line = lines[i]; LcdFlags flags = COLOR_THEME_SECONDARY1 | MENU_FONT; - if (selectedIndex == (int)i) { + + // draw selection if appropriate + if (selectedIndex == i && ! line.isSeparator) { flags = COLOR_THEME_PRIMARY2 | MENU_FONT; if (COLOR_THEME_FOCUS != COLOR_THEME_PRIMARY2) { - dc->drawSolidFilledRect(0, i * MENUS_LINE_HEIGHT, width(), + dc->drawSolidFilledRect(0, y, width(), MENUS_LINE_HEIGHT, COLOR_THEME_FOCUS); } } - if (line.drawLine) { - line.drawLine(dc, 0, i * MENUS_LINE_HEIGHT, flags); + + if (line.isSeparator) { + dc->drawHorizontalLine(5, y + MENUS_SEPARATOR_HEIGHT / 2, width() - 10, 255, COLOR_THEME_SECONDARY2); + } else if (line.drawLine) { + line.drawLine(dc, 0, y, flags); } else { const char* text = line.text.data(); dc->drawText(10, - i * MENUS_LINE_HEIGHT + - (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, + y + (MENUS_LINE_HEIGHT - getFontHeight(MENU_FONT)) / 2, text[0] == '\0' ? "---" : text, flags); } Menu* menu = getParentMenu(); if (menu->multiple && line.isChecked) { theme->drawCheckBox(dc, line.isChecked(), width() - 35, - i * MENUS_LINE_HEIGHT + (MENUS_LINE_HEIGHT - 20) / 2, + y + (MENUS_LINE_HEIGHT - 20) / 2, 0); } - if (i > 0) { - dc->drawSolidHorizontalLine(0, i * MENUS_LINE_HEIGHT, MENUS_WIDTH, - COLOR_THEME_SECONDARY2); - } + // if (i > 0 && ! lines[i].isSeparator && ! lines[i-1].isSeparator) { + // dc->drawSolidHorizontalLine(0, y, MENUS_WIDTH, COLOR_THEME_SECONDARY2); + // } + + y += line.lineHeight(); } } @@ -173,18 +240,22 @@ 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 < content->body.lines.size(); i++) + height += content->body.lines[i].lineHeight(); + if (!toolbar) { // there is no navigation bar at the left, we may center the window on screen auto headerHeight = content->title.empty() ? 0 : POPUP_HEADER_HEIGHT; - auto bodyHeight = limit( - MENUS_MIN_HEIGHT, content->body.lines.size() * MENUS_LINE_HEIGHT, - MENUS_MAX_HEIGHT); + auto bodyHeight = limit(MENUS_MIN_HEIGHT, height, MENUS_MAX_HEIGHT); content->setTop((LCD_H - headerHeight - bodyHeight) / 2 + MENUS_OFFSET_TOP); content->setHeight(headerHeight + bodyHeight); content->body.setTop(headerHeight); content->body.setHeight(bodyHeight); } - content->body.setInnerHeight(content->body.lines.size() * MENUS_LINE_HEIGHT); + + content->body.setInnerHeight(height); } void Menu::setTitle(std::string text) @@ -199,6 +270,12 @@ void Menu::addLine(const std::string & text, std::function onPress, std: 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)); diff --git a/radio/src/thirdparty/libopenui/src/menu.h b/radio/src/thirdparty/libopenui/src/menu.h index 9fdc382e5..cf16aff9e 100644 --- a/radio/src/thirdparty/libopenui/src/menu.h +++ b/radio/src/thirdparty/libopenui/src/menu.h @@ -32,6 +32,12 @@ class MenuBody: public Window friend class MenuWindowContent; friend class Menu; + enum MENU_DIRECTION + { + DIRECTION_UP = 1, + DIRECTION_DOWN = -1 + }; + class MenuLine { friend class MenuBody; @@ -50,11 +56,21 @@ class MenuBody: public Window { } + MenuLine(bool isSeparator) : + isSeparator(true), + height(MENUS_SEPARATOR_HEIGHT) + { + } + + inline coord_t lineHeight() { return height; } + MenuLine(MenuLine &) = delete; MenuLine(MenuLine &&) = default; protected: + bool isSeparator = false; + coord_t height = MENUS_LINE_HEIGHT; std::string text; std::function drawLine; std::function onPress; @@ -79,7 +95,12 @@ class MenuBody: public Window int selection() const { - return selectedIndex; + int index = selectedIndex; + for (int i = 0; i < selectedIndex; i++) + if (lines[i].isSeparator) + index--; + + return index; } int count() const @@ -107,6 +128,11 @@ class MenuBody: public Window invalidate(); } + void addSeparator() + { + lines.emplace_back(true); + } + void removeLines() { lines.clear(); @@ -121,6 +147,10 @@ class MenuBody: public Window void paint(BitmapBuffer * dc) override; protected: + void selectNext(MENU_DIRECTION direction); + int rangeCheck(int); + void setIndex(int index); + std::vector lines; int selectedIndex = 0; std::function onCancel; @@ -200,6 +230,8 @@ class Menu: public ModalWindow void addCustomLine(std::function drawLine, std::function onPress, std::function isChecked = nullptr); + void addSeparator(); + void removeLines(); unsigned count() const