First preview of working config menu for OBP40

This commit is contained in:
Thomas Hooge 2025-07-30 20:06:35 +02:00
parent fbe5e343b4
commit 2e797644db
5 changed files with 275 additions and 57 deletions

View File

@ -3,7 +3,7 @@
*/ */
#include "ConfigMenu.h" #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")) { if (! (itemtype == "int" or itemtype == "bool")) {
valtype = "int"; valtype = "int";
} else { } else {
@ -12,6 +12,8 @@ ConfigMenuItem::ConfigMenuItem(String itemtype, String itemlabel) {
label = itemlabel; label = itemlabel;
min = 0; min = 0;
max = std::numeric_limits<uint16_t>::max(); max = std::numeric_limits<uint16_t>::max();
value = itemval;
unit = itemunit;
} }
void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector<uint16_t> valsteps) { void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector<uint16_t> valsteps) {
@ -20,6 +22,18 @@ void ConfigMenuItem::setRange(uint16_t valmin, uint16_t valmax, std::vector<uint
steps = valsteps; steps = valsteps;
}; };
bool ConfigMenuItem::checkRange(uint16_t checkval) {
return (checkval >= min) and (checkval <= max);
}
String ConfigMenuItem::getLabel() {
return label;
};
uint16_t ConfigMenuItem::getValue() {
return value;
}
bool ConfigMenuItem::setValue(uint16_t newval) { bool ConfigMenuItem::setValue(uint16_t newval) {
if (valtype == "int") { if (valtype == "int") {
if (newval >= min and newval <= max) { if (newval >= min and newval <= max) {
@ -34,32 +48,77 @@ bool ConfigMenuItem::setValue(uint16_t newval) {
return false; // invalid type return false; // invalid type
}; };
void ConfigMenuItem::setPos(int8_t newpos) { void ConfigMenuItem::incValue() {
position = newpos; // 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() { int8_t ConfigMenuItem::getPos() {
return position; 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) { ConfigMenu::ConfigMenu(String menutitle, uint16_t menu_x, uint16_t menu_y) {
title = menutitle; title = menutitle;
x = menu_x; x = menu_x;
y = menu_y; 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()) { if (items.find(key) != items.end()) {
// duplicate keys not allowed // duplicate keys not allowed
return nullptr; return nullptr;
} }
ConfigMenuItem itm(valtype, "Test1"); ConfigMenuItem *itm = new ConfigMenuItem(valtype, label, val, valunit);
return &itm; items.insert(std::pair<String, ConfigMenuItem*>(key, itm));
// map.insert(std::pair<String, ConfigMenuItem>(itm)); // Append key to index, index starting with 0
// Append key to index int8_t ix = items.size() - 1;
int8_t ix = items.size();
index[ix] = key; index[ix] = key;
itm.setPos(ix); itm->setPos(ix);
return itm;
}; };
void ConfigMenu::setItemDimension(uint16_t itemwidth, uint16_t itemheight) { 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) { void ConfigMenu::setItemActive(String key) {
uint8_t ix = if (items.find(key) != items.end()) {
activeitem = ix; activeitem = items[key]->getPos();
} else {
activeitem = -1;
}
}; };
int8_t ConfigMenu::getActiveIndex() {
return activeitem;
}
ConfigMenuItem* ConfigMenu::getActiveItem() { ConfigMenuItem* ConfigMenu::getActiveItem() {
return nullptr; if (activeitem < 0) {
return nullptr;
}
return items[index[activeitem]];
}; };
ConfigMenuItem* ConfigMenu::getItemByIndex(uint8_t index) { ConfigMenuItem* ConfigMenu::getItemByIndex(uint8_t ix) {
return nullptr; if (ix > index.size() - 1) {
return nullptr;
}
return items[index[ix]];
}; };
ConfigMenuItem* ConfigMenu::getItemByKey(String Key) { ConfigMenuItem* ConfigMenu::getItemByKey(String key) {
return nullptr; if (items.find(key) == items.end()) {
return nullptr;
}
return items[key];
}; };
uint8_t ConfigMenu::getItemCount() { uint8_t ConfigMenu::getItemCount() {
return items.size(); return items.size();
}; };
Point ConfigMenu::getXY() { void ConfigMenu::goPrev() {
return {x, y}; if (activeitem == 0) {
activeitem = items.size() - 1;
} else {
activeitem--;
}
} }
/* void ConfigMenu::goNext() {
void getRect(); if (activeitem == items.size() - 1) {
void getItemRect(); activeitem = 0;
*/ } else {
activeitem++;
}
}
Point ConfigMenu::getXY() {
return {static_cast<double>(x), static_cast<double>(y)};
}
Rect ConfigMenu::getRect() {
return {static_cast<double>(x), static_cast<double>(y),
static_cast<double>(w), static_cast<double>(h)};
}
Rect ConfigMenu::getItemRect(int8_t index) {
return {static_cast<double>(x), static_cast<double>(y + index * h),
static_cast<double>(w), static_cast<double>(h)};
}

View File

@ -18,17 +18,26 @@ private:
int8_t position; // counted fom 0 int8_t position; // counted fom 0
public: 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<uint16_t> steps); void setRange(uint16_t valmin, uint16_t valmax, std::vector<uint16_t> steps);
bool checkRange(uint16_t checkval);
String getLabel();
uint16_t getValue();
bool setValue(uint16_t newval); 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(); int8_t getPos();
void setPos(int8_t newpos);
String getType();
}; };
class ConfigMenu { class ConfigMenu {
private: private:
String title; String title;
std::map <String,ConfigMenuItem> items; std::map <String,ConfigMenuItem*> items;
std::map <uint8_t,String> index; std::map <uint8_t,String> index;
int8_t activeitem = -1; // refers to position of item int8_t activeitem = -1; // refers to position of item
uint16_t x; uint16_t x;
@ -38,14 +47,17 @@ private:
public: public:
ConfigMenu(String title, uint16_t menu_x, uint16_t menu_y); 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); void setItemDimension(uint16_t itemwidth, uint16_t itemheight);
int8_t getActiveIndex();
void setItemActive(String key); void setItemActive(String key);
ConfigMenuItem* getActiveItem(); ConfigMenuItem* getActiveItem();
ConfigMenuItem* getItemByIndex(uint8_t index); ConfigMenuItem* getItemByIndex(uint8_t index);
ConfigMenuItem* getItemByKey(String key); ConfigMenuItem* getItemByKey(String key);
uint8_t getItemCount(); uint8_t getItemCount();
void goPrev();
void goNext();
Point getXY(); Point getXY();
/* void getRect(); Rect getRect();
void getItemRect(); */ Rect getItemRect(int8_t index);
}; };

View File

@ -351,6 +351,24 @@ void drawTextRalign(int16_t x, int16_t y, String text) {
getdisplay().print(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) // 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){ 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); getdisplay().fillTriangle(x, y, x+size*2, y, x+size, y-size*2, color);

View File

@ -92,6 +92,7 @@ String xdrDelete(String input); // Delete xdr prefix from string
void drawTextCenter(int16_t cx, int16_t cy, String text); void drawTextCenter(int16_t cx, int16_t cy, String text);
void drawTextRalign(int16_t x, int16_t y, 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 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); void displayTrendLow(int16_t x, int16_t y, uint16_t size, uint16_t color);

View File

@ -74,8 +74,9 @@ private:
int anchor_ts; // time stamp anchor dropped int anchor_ts; // time stamp anchor dropped
char mode = 'N'; // (N)ormal, (C)onfig 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) { void displayModeNormal(PageData &pageData) {
@ -117,14 +118,6 @@ private:
//drawPoly(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor); //drawPoly(rotatePoints(c, pts, RadToDeg(value2)), commonData->fgcolor);
drawPoly(pts_boat, 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 // Draw wind arrow
const std::vector<Point> pts_wind = { const std::vector<Point> pts_wind = {
{c.x, c.y - r + 25}, {c.x, c.y - r + 25},
@ -214,8 +207,6 @@ private:
void displayModeConfig() { void displayModeConfig() {
// LOG_DEBUG(GwLog::LOG,"Drawing at PageAnchor; Mode=%c", mode);
getdisplay().setTextColor(commonData->fgcolor); getdisplay().setTextColor(commonData->fgcolor);
getdisplay().setFont(&Ubuntu_Bold12pt8b); getdisplay().setFont(&Ubuntu_Bold12pt8b);
getdisplay().setCursor(8, 48); getdisplay().setCursor(8, 48);
@ -226,11 +217,33 @@ private:
// show lat/lon for boat pos // show lat/lon for boat pos
// show distance anchor <-> boat // 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: public:
PageAnchor(CommonData &common) PageAnchor(CommonData &common)
// : menu("Options", 80, 20)
{ {
commonData = &common; commonData = &common;
config = commonData->config; config = commonData->config;
@ -244,30 +257,38 @@ public:
backlightMode = config->getString(config->backlight); backlightMode = config->getString(config->backlight);
lengthformat = config->getString(config->lengthFormat); lengthformat = config->getString(config->lengthFormat);
chain_length = config->getInt(config->chainLength); chain_length = config->getInt(config->chainLength);
chain = 0; chain = 0;
anchor_set = false; anchor_set = false;
alarm_range = 30; alarm_range = 30;
/*
// Initialize config menu // Initialize config menu
menu = new ConfigMenu("Options", 40, 80);
menu->setItemDimension(150, 20);
ConfigMenuItem *newitem; ConfigMenuItem *newitem;
menu.setItemDimension(120, 20); newitem = menu->addItem("chain", "Chain out", "int", 0, "m");
newitem = menu.addItem("chain", "Chain out", "int"); 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->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->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->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}); newitem->setRange(0, 200, {1, 5, 10});
// START only for OBP40 newitem = menu->addItem("alat", "Adjust anchor lat.", "int", 0, "m");
newitem = menu.addItem("anchor", "Anchor down", "bool");
newitem = menu.addItem("anchor_lat", "Adjust anchor lat.", "int");
newitem->setRange(0, 200, {1, 5, 10}); 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}); newitem->setRange(0, 200, {1, 5, 10});
// STOP only for OBP40 #ifdef BOARD_OBP40S3
menu.setItemActive("chain"); */ // Intodruced here because of missing keys for OBP40
newitem = menu->addItem("anchor", "Anchor down", "bool", 0, "");
#endif
menu->setItemActive("zoom");
} }
void setupKeys(){ void setupKeys(){
@ -276,6 +297,7 @@ public:
commonData->keydata[1].label = "ALARM"; commonData->keydata[1].label = "ALARM";
} }
#ifdef BOARD_OBP60S3
int handleKey(int key){ int handleKey(int key){
if (key == 1) { // Switch between normal and config mode if (key == 1) { // Switch between normal and config mode
if (mode == 'N') { if (mode == 'N') {
@ -285,9 +307,22 @@ public:
} }
return 0; return 0;
} }
if (key == 2) { // Toggle alarm if (mode == 'N') {
alarm_enabled = !alarm_enabled; if (key == 2) { // Toggle alarm
return 0; 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 if (key == 11) { // Code for keylock
commonData->keylock = !commonData->keylock; commonData->keylock = !commonData->keylock;
@ -295,6 +330,65 @@ public:
} }
return key; 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){ void displayNew(PageData &pageData){
}; };