From c6608bf66973df0fee98d137155361b2c8351be5 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Wed, 30 Jul 2025 20:06:35 +0200 Subject: [PATCH] First preview of working config menu for OBP40 --- lib/obp60task/ConfigMenu.cpp | 139 ++++++++++++++++++++++----- lib/obp60task/ConfigMenu.h | 24 +++-- lib/obp60task/OBP60Extensions.cpp | 18 ++++ lib/obp60task/OBP60Extensions.h | 1 + lib/obp60task/PageAnchor.cpp | 150 ++++++++++++++++++++++++------ 5 files changed, 275 insertions(+), 57 deletions(-) diff --git a/lib/obp60task/ConfigMenu.cpp b/lib/obp60task/ConfigMenu.cpp index 9bf9464..1627531 100644 --- a/lib/obp60task/ConfigMenu.cpp +++ b/lib/obp60task/ConfigMenu.cpp @@ -3,7 +3,7 @@ */ #include "ConfigMenu.h" -ConfigMenuItem::ConfigMenuItem(String itemtype, String itemlabel) { +ConfigMenuItem::ConfigMenuItem(String itemtype, String itemlabel, uint16_t itemval, String itemunit) { if (! (itemtype == "int" or itemtype == "bool")) { valtype = "int"; } else { @@ -12,6 +12,8 @@ ConfigMenuItem::ConfigMenuItem(String itemtype, String itemlabel) { label = itemlabel; min = 0; max = std::numeric_limits::max(); + value = itemval; + unit = itemunit; } void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector valsteps) { @@ -20,6 +22,18 @@ void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector= min) and (checkval <= max); +} + +String ConfigMenuItem::getLabel() { + return label; +}; + +uint16_t ConfigMenuItem::getValue() { + return value; +} + bool ConfigMenuItem::setValue(uint16_t newval) { if (valtype == "int") { if (newval >= min and newval <= max) { @@ -34,32 +48,77 @@ bool ConfigMenuItem::setValue(uint16_t newval) { return false; // invalid type }; -void ConfigMenuItem::setPos(int8_t newpos) { - position = newpos; +void ConfigMenuItem::incValue() { + // increase value by step + if (valtype == "int") { + if (value + step < max) { + value += step; + } else { + value = max; + } + } else if (valtype == "bool") { + value = !value; + } }; +void ConfigMenuItem::decValue() { + // decrease value by step + if (valtype == "int") { + if (value - step > min) { + value -= step; + } else { + value = min; + } + } else if (valtype == "bool") { + value = !value; + } +}; + +String ConfigMenuItem::getUnit() { + return unit; +} + +uint16_t ConfigMenuItem::getStep() { + return step; +} + +void ConfigMenuItem::setStep(uint16_t newstep) { + if (std::find(steps.begin(), steps.end(), newstep) == steps.end()) { + return; // invalid step: not in list of possible steps + } + step = newstep; +} + int8_t ConfigMenuItem::getPos() { return position; }; +void ConfigMenuItem::setPos(int8_t newpos) { + position = newpos; +}; + +String ConfigMenuItem::getType() { + return valtype; +} + ConfigMenu::ConfigMenu(String menutitle, uint16_t menu_x, uint16_t menu_y) { title = menutitle; x = menu_x; y = menu_y; }; -ConfigMenuItem* ConfigMenu::addItem(String key, String label, String valtype) { +ConfigMenuItem* ConfigMenu::addItem(String key, String label, String valtype, uint16_t val, String valunit) { if (items.find(key) != items.end()) { // duplicate keys not allowed return nullptr; } - ConfigMenuItem itm(valtype, "Test1"); - return &itm; - // map.insert(std::pair(itm)); - // Append key to index - int8_t ix = items.size(); + ConfigMenuItem *itm = new ConfigMenuItem(valtype, label, val, valunit); + items.insert(std::pair(key, itm)); + // Append key to index, index starting with 0 + int8_t ix = items.size() - 1; index[ix] = key; - itm.setPos(ix); + itm->setPos(ix); + return itm; }; void ConfigMenu::setItemDimension(uint16_t itemwidth, uint16_t itemheight) { @@ -68,34 +127,68 @@ void ConfigMenu::setItemDimension(uint16_t itemwidth, uint16_t itemheight) { }; void ConfigMenu::setItemActive(String key) { - uint8_t ix = - activeitem = ix; + if (items.find(key) != items.end()) { + activeitem = items[key]->getPos(); + } else { + activeitem = -1; + } }; +int8_t ConfigMenu::getActiveIndex() { + return activeitem; +} + ConfigMenuItem* ConfigMenu::getActiveItem() { - return nullptr; + if (activeitem < 0) { + return nullptr; + } + return items[index[activeitem]]; }; -ConfigMenuItem* ConfigMenu::getItemByIndex(uint8_t index) { - return nullptr; +ConfigMenuItem* ConfigMenu::getItemByIndex(uint8_t ix) { + if (ix > index.size() - 1) { + return nullptr; + } + return items[index[ix]]; }; -ConfigMenuItem* ConfigMenu::getItemByKey(String Key) { - return nullptr; +ConfigMenuItem* ConfigMenu::getItemByKey(String key) { + if (items.find(key) == items.end()) { + return nullptr; + } + return items[key]; }; uint8_t ConfigMenu::getItemCount() { return items.size(); }; -Point ConfigMenu::getXY() { - return {x, y}; +void ConfigMenu::goPrev() { + if (activeitem == 0) { + activeitem = items.size() - 1; + } else { + activeitem--; + } } -/* -void getRect(); -void getItemRect(); -*/ +void ConfigMenu::goNext() { + if (activeitem == items.size() - 1) { + activeitem = 0; + } else { + activeitem++; + } +} +Point ConfigMenu::getXY() { + return {static_cast(x), static_cast(y)}; +} +Rect ConfigMenu::getRect() { + return {static_cast(x), static_cast(y), + static_cast(w), static_cast(h)}; +} +Rect ConfigMenu::getItemRect(int8_t index) { + return {static_cast(x), static_cast(y + index * h), + static_cast(w), static_cast(h)}; +} diff --git a/lib/obp60task/ConfigMenu.h b/lib/obp60task/ConfigMenu.h index 5e35e86..439097d 100644 --- a/lib/obp60task/ConfigMenu.h +++ b/lib/obp60task/ConfigMenu.h @@ -18,17 +18,26 @@ private: int8_t position; // counted fom 0 public: - ConfigMenuItem(String itemtype, String itemlabel); + ConfigMenuItem(String itemtype, String itemlabel, uint16_t itemval, String itemunit); void setRange(uint16_t valmin, uint16_t valmax, std::vector steps); + bool checkRange(uint16_t checkval); + String getLabel(); + uint16_t getValue(); bool setValue(uint16_t newval); - void setPos(int8_t newpos); + void incValue(); + void decValue(); + String getUnit(); + uint16_t getStep(); + void setStep(uint16_t newstep); int8_t getPos(); + void setPos(int8_t newpos); + String getType(); }; class ConfigMenu { private: String title; - std::map items; + std::map items; std::map index; int8_t activeitem = -1; // refers to position of item uint16_t x; @@ -38,14 +47,17 @@ private: public: ConfigMenu(String title, uint16_t menu_x, uint16_t menu_y); - ConfigMenuItem* addItem(String key, String label, String valtype); + ConfigMenuItem* addItem(String key, String label, String valtype, uint16_t val, String valunit); void setItemDimension(uint16_t itemwidth, uint16_t itemheight); + int8_t getActiveIndex(); void setItemActive(String key); ConfigMenuItem* getActiveItem(); ConfigMenuItem* getItemByIndex(uint8_t index); ConfigMenuItem* getItemByKey(String key); uint8_t getItemCount(); + void goPrev(); + void goNext(); Point getXY(); - /* void getRect(); - void getItemRect(); */ + Rect getRect(); + Rect getItemRect(int8_t index); }; diff --git a/lib/obp60task/OBP60Extensions.cpp b/lib/obp60task/OBP60Extensions.cpp index 9f97af1..b4b3b47 100644 --- a/lib/obp60task/OBP60Extensions.cpp +++ b/lib/obp60task/OBP60Extensions.cpp @@ -351,6 +351,24 @@ void drawTextRalign(int16_t x, int16_t y, String text) { getdisplay().print(text); } +// Draw text inside box, normal or inverted +void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border) { + if (inverted) { + getdisplay().fillRect(box.x, box.y, box.w, box.h, fg); + getdisplay().setTextColor(bg); + } else { + if (border) { + getdisplay().fillRect(box.x + 1, box.y + 1, box.w - 2, box.h - 2, bg); + getdisplay().drawRect(box.x, box.y, box.w, box.h, fg); + } + getdisplay().setTextColor(fg); + } + uint16_t border_offset = box.h / 4; // 25% of box height + getdisplay().setCursor(box.x + border_offset, box.y + box.h - border_offset); + getdisplay().print(text); + getdisplay().setTextColor(fg); +} + // Show a triangle for trend direction high (x, y is the left edge) void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color){ getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color); diff --git a/lib/obp60task/OBP60Extensions.h b/lib/obp60task/OBP60Extensions.h index 81226ef..46da9c0 100644 --- a/lib/obp60task/OBP60Extensions.h +++ b/lib/obp60task/OBP60Extensions.h @@ -92,6 +92,7 @@ String xdrDelete(String input); // Delete xdr prefix from string void drawTextCenter(int16_t cx, int16_t cy, String text); void drawTextRalign(int16_t x, int16_t y, String text); +void drawTextBoxed(Rect box, String text, uint16_t fg, uint16_t bg, bool inverted, bool border); void displayTrendHigh(int16_t x, int16_t y, uint16_t size, uint16_t color); void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color); diff --git a/lib/obp60task/PageAnchor.cpp b/lib/obp60task/PageAnchor.cpp index 7de8306..476ed20 100644 --- a/lib/obp60task/PageAnchor.cpp +++ b/lib/obp60task/PageAnchor.cpp @@ -74,8 +74,9 @@ private: int anchor_ts; // time stamp anchor dropped char mode = 'N'; // (N)ormal, (C)onfig + int8_t editmode = -1; // marker for menu/edit/set function - // ConfigMenu menu; + ConfigMenu *menu; void displayModeNormal(PageData &pageData) { @@ -117,14 +118,6 @@ private: //drawPoly(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor); drawPoly(pts_boat, commonData->fgcolor); - /*size_t polysize = pts_boat.size(); - for (size_t i = 0; i < polysize - 1; i++) { - getdisplay().drawLine(pts_boat[i].x, pts_boat[i].y, pts_boat[i+1].x, pts_boat[i+1].y, commonData->fgcolor); - } - // close path - getdisplay().drawLine(pts_boat[polysize-1].x, pts_boat[polysize-1].y, pts_boat[0].x, pts_boat[0].y, commonData->fgcolor); - */ - // Draw wind arrow const std::vector pts_wind = { {c.x, c.y - r + 25}, @@ -214,8 +207,6 @@ private: void displayModeConfig() { - // LOG_DEBUG(GwLog::LOG,"Drawing at PageAnchor; Mode=%c", mode); - getdisplay().setTextColor(commonData->fgcolor); getdisplay().setFont(&Ubuntu_Bold12pt8b); getdisplay().setCursor(8, 48); @@ -226,11 +217,33 @@ private: // show lat/lon for boat pos // show distance anchor <-> boat + getdisplay().setFont(&Ubuntu_Bold8pt8b); + for (int i = 0 ; i < menu->getItemCount(); i++) { + ConfigMenuItem *itm = menu->getItemByIndex(i); + if (!itm) { + LOG_DEBUG(GwLog::ERROR, "Menu item not found: %d", i); + } else { + Rect r = menu->getItemRect(i); + bool inverted = (i == menu->getActiveIndex()); + drawTextBoxed(r, itm->getLabel(), commonData->fgcolor, commonData->bgcolor, inverted, false); + if (inverted and editmode > 0) { + // triangle as edit marker + getdisplay().fillTriangle(r.x + r.w + 20, r.y, r.x + r.w + 30, r.y + r.h / 2, r.x + r.w + 20, r.y + r.h, commonData->fgcolor); + } + getdisplay().setCursor(r.x + r.w + 40, r.y + r.h - 4); + if (itm->getType() == "int") { + getdisplay().print(itm->getValue()); + getdisplay().print(itm->getUnit()); + } else { + getdisplay().print(itm->getValue() == 0 ? "No" : "Yes"); + } + } + } + } public: PageAnchor(CommonData &common) - // : menu("Options", 80, 20) { commonData = &common; config = commonData->config; @@ -244,30 +257,38 @@ public: backlightMode = config->getString(config->backlight); lengthformat = config->getString(config->lengthFormat); chain_length = config->getInt(config->chainLength); - + chain = 0; anchor_set = false; alarm_range = 30; - /* + // Initialize config menu + menu = new ConfigMenu("Options", 40, 80); + menu->setItemDimension(150, 20); + ConfigMenuItem *newitem; - menu.setItemDimension(120, 20); - newitem = menu.addItem("chain", "Chain out", "int"); + newitem = menu->addItem("chain", "Chain out", "int", 0, "m"); + if (! newitem) { + // Demo: in case of failure exit here, should never be happen + logger->logDebug(GwLog::ERROR,"Menu item creation failed"); + return; + } newitem->setRange(0, 200, {1, 5, 10}); - newitem = menu.addItem("chainmax", "Chain max", "int"); + newitem = menu->addItem("chainmax", "Chain max", "int", chain_length, "m"); newitem->setRange(0, 200, {1, 5, 10}); - newitem = menu.addItem("zoom", "Zoom", "int"); + newitem = menu->addItem("zoom", "Zoom", "int", 50, "m"); newitem->setRange(0, 200, {1, }); - newitem = menu.addItem("range", "Alarm range", "int"); + newitem = menu->addItem("range", "Alarm range", "int", 40, "m"); newitem->setRange(0, 200, {1, 5, 10}); - // START only for OBP40 - newitem = menu.addItem("anchor", "Anchor down", "bool"); - newitem = menu.addItem("anchor_lat", "Adjust anchor lat.", "int"); + newitem = menu->addItem("alat", "Adjust anchor lat.", "int", 0, "m"); newitem->setRange(0, 200, {1, 5, 10}); - newitem = menu.addItem("anchor_lon", "Adjust anchor lon.", "int"); + newitem = menu->addItem("alon", "Adjust anchor lon.", "int", 0, "m"); newitem->setRange(0, 200, {1, 5, 10}); - // STOP only for OBP40 - menu.setItemActive("chain"); */ +#ifdef BOARD_OBP40S3 + // Intodruced here because of missing keys for OBP40 + newitem = menu->addItem("anchor", "Anchor down", "bool", 0, ""); +#endif + menu->setItemActive("zoom"); } void setupKeys(){ @@ -276,6 +297,7 @@ public: commonData->keydata[1].label = "ALARM"; } +#ifdef BOARD_OBP60S3 int handleKey(int key){ if (key == 1) { // Switch between normal and config mode if (mode == 'N') { @@ -285,9 +307,22 @@ public: } return 0; } - if (key == 2) { // Toggle alarm - alarm_enabled = !alarm_enabled; - return 0; + if (mode == 'N') { + if (key == 2) { // Toggle alarm + alarm_enabled = !alarm_enabled; + return 0; + } + } else { // Config mode + if (key == 3) { + // menu down + menu->goNext(); + return 0; + } + if (key == 4) { + // menu up + menu->goPrev(); + return 0; + } } if (key == 11) { // Code for keylock commonData->keylock = !commonData->keylock; @@ -295,6 +330,65 @@ public: } return key; } +#endif +#ifdef BOARD_OBP40S3 + int handleKey(int key){ + if (key == 1) { // Switch between normal and config mode + if (mode == 'N') { + mode = 'C'; + commonData->keydata[1].label = "EDIT"; + } else { + mode = 'N'; + commonData->keydata[1].label = "ALARM"; + } + return 0; + } + if (mode == 'N') { + if (key == 2) { // Toggle alarm + alarm_enabled = !alarm_enabled; + return 0; + } + } else { // Config mode + // TODO different code for OBP40 / OBP60 + if (key == 9) { + // menu down + if (editmode > 0) { + // decrease item value + menu->getActiveItem()->decValue(); + } else { + menu->goNext(); + } + return 0; + } + if (key == 10) { + // menu up or value up + if (editmode > 0) { + // increase item value + menu->getActiveItem()->incValue(); + } else { + menu->goPrev(); + } + return 0; + } + if (key == 2) { + // enter / leave edit mode for current menu item + if (editmode > 0) { + commonData->keydata[1].label = "EDIT"; + editmode = 0; + } else { + commonData->keydata[1].label = "SET"; + editmode = 1; + } + return 0; + } + } + if (key == 11) { // Code for keylock + commonData->keylock = !commonData->keylock; + return 0; + } + return key; + } +#endif void displayNew(PageData &pageData){ };