From 98e95d44415115f9f7426bb21d3d8f74ba053ec6 Mon Sep 17 00:00:00 2001 From: Dmitry Shishkin Date: Sat, 21 Sep 2024 01:47:41 +0200 Subject: [PATCH 1/9] Add diagrams --- docs/classes.puml | 120 ++++++++++++++++++++++++++++++---------------- docs/items.puml | 93 +++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 40 deletions(-) create mode 100644 docs/items.puml diff --git a/docs/classes.puml b/docs/classes.puml index e4c6f3b9..9569023b 100644 --- a/docs/classes.puml +++ b/docs/classes.puml @@ -2,57 +2,97 @@ ' https://www.plantuml.com/plantuml/uml/ hide empty members +left to right direction + +class InputInterface { + virtual void observe() +} + +class DisplayInterface { + #uint8_t maxCols + #uint8_t maxRows + #uint8_t cursorRow + #uint8_t blinkerPosition + +bool isEditModeEnabled + +virtual void begin() + +uint8_t getMaxRows() const + +uint8_t getMaxCols() const + +virtual void clear() + +virtual void setBacklight(bool enabled) + +virtual void drawItem(uint8_t row, const char* text) + +virtual void drawItem(uint8_t row, const char* text, char separator, char* value) + +virtual void clearCursor() + +virtual void drawCursor() + +virtual void moveCursor(uint8_t newCursorRow) + +void setEditModeEnabled(bool enabled) + +bool getEditModeEnabled() + +uint8_t getCursorRow() const + +virtual void clearBlinker() + +virtual void drawBlinker() + +virtual void resetBlinker(uint8_t blinkerPosition) + +uint8_t getBlinkerPosition() const + +virtual bool drawChar(char c) + +virtual void clearUpIndicator() + +virtual void drawUpIndicator() + +virtual void clearDownIndicator() + +virtual void drawDownIndicator() + +virtual void restartTimer() +} class LcdMenu { - +virtual bool process(char c) + -bool enabled + +DisplayInterface* getDisplay() + +MenuScreen* getScreen() + +void setScreen(MenuScreen* screen) + +uint8_t getCursor() + +MenuItem* getItemAt(uint8_t position) + +void setCursor(uint8_t cursor) + +bool process(const unsigned char c) + +void reset() + +void hide() + +void show() + +void refresh() + } class MenuScreen { - #virtual bool process(Context context) - #virtual bool up() - #virtual bool down() - #virtual bool back() -} + -uint8_t cursor + -uint8_t view + +void setParent(MenuScreen* parent) + +MenuItem* operator[](const uint8_t position) + +uint8_t getCursor() + +MenuItem* getItemAt(uint8_t position) + #void setCursor(DisplayInterface* display, uint8_t position) + #bool process(LcdMenu* menu, const unsigned char command) + #void draw(DisplayInterface* display) + #bool up(DisplayInterface* display) + #bool down(DisplayInterface* display) + #bool back(LcdMenu* menu) + #uint8_t itemsCount() -struct MenuItem::Context { - LcdMenu* menu; - DisplayInterface* display; - const unsigned char command; } class MenuItem { - #virtual bool process(Context context) + #const char* text + +const char* getText() + +void setText(const char* text) + #virtual bool process(LcdMenu* menu, const unsigned char command) + #const void draw(DisplayInterface* display) + #virtual void draw(DisplayInterface* display, uint8_t row) } -class ItemCommand { - #bool process(Context context) override - #bool enter(Context context) -} +InputInterface -r-> "1" LcdMenu : menu +LcdMenu -r-> "1" DisplayInterface : display +LcdMenu -d-> "1" MenuScreen : screen +MenuScreen -d-> "1..*" MenuItem : items +MenuScreen --> "0..1" MenuScreen : parent -class ItemList { - #bool process(Context context) override - #bool up(Context context) - #bool down(Context context) - #bool enter(Context context) - #bool back(Context context) -} +LcdMenu::process .. MenuScreen::process +LcdMenu::getCursor .. MenuScreen::getCursor +LcdMenu::setCursor .. MenuScreen::setCursor +LcdMenu::getItemAt .. MenuScreen::getItemAt -class ItemInput { - #bool process(Context context) override - #bool up(Context context) - #bool down(Context context) - #bool enter(Context context) - #bool back(Context context) - #bool left(Context context) - #bool right(Context context) - #bool backspace(Context context) - #bool typeChar(Context context) - #bool clear(Context context) -} +MenuScreen::process .. MenuItem::process +MenuScreen::draw .. MenuItem::draw -LcdMenu -r-> MenuScreen -MenuScreen -r-> MenuItem -ItemCommand -u-|> MenuItem -ItemList -u-|> MenuItem -ItemInput -u-|> MenuItem -@enduml \ No newline at end of file +@enduml diff --git a/docs/items.puml b/docs/items.puml new file mode 100644 index 00000000..766885eb --- /dev/null +++ b/docs/items.puml @@ -0,0 +1,93 @@ +@startuml +' https://www.plantuml.com/plantuml/uml/ + +hide empty members +left to right direction + +class MenuItem { + #const char* text + +const char* getText() + +void setText(const char* text) + #virtual bool process(LcdMenu* menu, const unsigned char command) + #const void draw(DisplayInterface* display) + #virtual void draw(DisplayInterface* display, uint8_t row) +} + +class ItemBack { + #bool process(LcdMenu* menu, const unsigned char command) override + .. + #void changeScreen(LcdMenu* menu) +} + +class ItemSubMenu { + #bool process(LcdMenu* menu, const unsigned char command) override + .. + #void changeScreen(LcdMenu* menu) +} + +class ItemToggle { + #bool process(LcdMenu* menu, const unsigned char command) override + .. + #void toggle(DisplayInterface* display) +} + +class ItemCommand { + #bool process(LcdMenu* menu, const unsigned char command) override + .. + #void executeCommand() +} + +class ItemInput { + #bool process(LcdMenu* menu, const unsigned char command) override + .. + #void up(DisplayInterface* display) + #void down(DisplayInterface* display) + #void enter(DisplayInterface* display) + #void back(DisplayInterface* display) + #void left(DisplayInterface* display) + #void right(DisplayInterface* display) + #void backspace(DisplayInterface* display) + #void typeChar(DisplayInterface* display, const unsigned char command) + #void clear(DisplayInterface* display) +} + +class ItemInputCharset { + #bool process(LcdMenu* menu, const unsigned char command) override + .. + #void initCharEdit() + #void abortCharEdit(DisplayInterface* display) + #void commitCharEdit(DisplayInterface* display) + #void showNextChar(DisplayInterface* display) + #void showPreviousChar(DisplayInterface* display) +} + +class ItemList { + #bool process(LcdMenu* menu, const unsigned char command) override + .. + #void selectPrevious(DisplayInterface* display) + #void selectNext(DisplayInterface* display) +} + +class ItemProgress { + #bool process(LcdMenu* menu, const unsigned char command) override + .. + +void increment() + +void decrement() +} + +class EditMode as "???" { +} + +note bottom of EditMode : Someone who holds edit mode + +ItemBack -u-|> MenuItem +ItemSubMenu -u-|> MenuItem +ItemToggle -u-|> MenuItem +ItemCommand -u-|> MenuItem +EditMode -u-|> MenuItem +ItemList -u-|> EditMode +ItemProgress -u-|> EditMode +ItemInput -u-|> EditMode +ItemInputCharset -u-|> ItemInput + +@enduml From 20dd56f3a78233036aefff72b7b4a003f4efe60c Mon Sep 17 00:00:00 2001 From: Dmitry Shishkin Date: Sat, 21 Sep 2024 01:49:48 +0200 Subject: [PATCH 2/9] Decom Context use only LcdMenu --- src/ItemBack.h | 14 ++- src/ItemCommand.h | 16 ++-- src/ItemInput.h | 111 +++++++++++------------- src/ItemInputCharset.h | 189 +++++++++++++++++++---------------------- src/ItemList.h | 77 +++++++---------- src/ItemProgress.h | 104 +++++++++-------------- src/ItemSubMenu.h | 20 ++--- src/ItemToggle.h | 21 +++-- src/LcdMenu.cpp | 5 +- src/MenuItem.h | 9 +- src/MenuScreen.cpp | 25 ++++-- src/MenuScreen.h | 38 +++------ src/utils/utils.h | 2 +- 13 files changed, 287 insertions(+), 344 deletions(-) diff --git a/src/ItemBack.h b/src/ItemBack.h index 06897780..0c222369 100644 --- a/src/ItemBack.h +++ b/src/ItemBack.h @@ -25,12 +25,18 @@ class ItemBack : public MenuItem { ItemBack(const char* text = "..") : MenuItem(text) {} protected: - bool process(Context& context) override { - switch (context.command) { - case ENTER: context.menu->process(BACK); return true; - default: return false; + bool process(LcdMenu* menu, const unsigned char command) override { + switch (command) { + case ENTER: + changeScreen(menu); + return true; + default: + return false; } } + void changeScreen(LcdMenu* menu) { + menu->process(BACK); + } }; #define ITEM_BACK(...) (new ItemBack(__VA_ARGS__)) diff --git a/src/ItemCommand.h b/src/ItemCommand.h index 48e57647..3402b3a7 100644 --- a/src/ItemCommand.h +++ b/src/ItemCommand.h @@ -46,19 +46,21 @@ class ItemCommand : public MenuItem { void setCallBack(fptr callback) { this->callback = callback; }; protected: - bool process(Context& context) override { - switch (context.command) { - case ENTER: return enter(context); - default: return false; + bool process(LcdMenu* menu, const unsigned char command) override { + switch (command) { + case ENTER: + executeCommand(); + return true; + default: + return false; } } - bool enter(Context& context) { + void executeCommand() { if (callback != NULL) { callback(); } printLog(F("ItemCommand::enter"), text); - return true; - }; + } }; #define ITEM_COMMAND(...) (new ItemCommand(__VA_ARGS__)) diff --git a/src/ItemInput.h b/src/ItemInput.h index 1dd923a3..40ad46d8 100644 --- a/src/ItemInput.h +++ b/src/ItemInput.h @@ -11,6 +11,7 @@ #ifndef ItemInput_H #define ItemInput_H +#include "LcdMenu.h" #include "MenuItem.h" #include @@ -139,29 +140,47 @@ class ItemInput : public MenuItem { substring(value, view, getViewSize(display), buf); display->drawItem(row, text, ':', buf); } - bool process(Context& context) override { - unsigned char c = context.command; - DisplayInterface* display = context.display; - if (isprint(c)) { - return typeChar(context); - } - switch (c) { - case ENTER: return enter(context); - case BACK: return back(context); - case UP: return display->getEditModeEnabled(); - case DOWN: return display->getEditModeEnabled(); - case LEFT: return left(context); - case RIGHT: return right(context); - case BACKSPACE: return backspace(context); - case CLEAR: return clear(context); - default: return false; - } - } - bool enter(Context& context) { - DisplayInterface* display = context.display; + bool process(LcdMenu* menu, const unsigned char command) override { + DisplayInterface* display = menu->getDisplay(); if (display->getEditModeEnabled()) { - return false; + if (isprint(command)) { + typeChar(display, command); + return true; + } + switch (command) { + case BACK: + back(display); + return true; + case UP: + return true; + case DOWN: + return true; + case LEFT: + left(display); + return true; + case RIGHT: + right(display); + return true; + case BACKSPACE: + backspace(display); + return true; + case CLEAR: + clear(display); + return true; + default: + return false; + } + } else { + switch (command) { + case ENTER: + enter(display); + return true; + default: + return false; + } } + } + void enter(DisplayInterface* display) { // Move cursor to the latest index uint8_t length = strlen(value); cursor = length; @@ -176,13 +195,8 @@ class ItemInput : public MenuItem { display->resetBlinker(constrainBlinkerPosition(display, strlen(text) + 2 + cursor - view)); display->drawBlinker(); printLog(F("ItemInput::enterEditMode"), value); - return true; }; - bool back(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } + void back(DisplayInterface* display) { display->clearBlinker(); display->setEditModeEnabled(false); // Move view to 0 and redraw before exit @@ -193,15 +207,10 @@ class ItemInput : public MenuItem { callback(value); } printLog(F("ItemInput::exitEditMode"), value); - return true; }; - bool left(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } + void left(DisplayInterface* display) { if (cursor == 0) { - return true; + return; } cursor--; if (cursor < view) { @@ -211,15 +220,10 @@ class ItemInput : public MenuItem { display->resetBlinker(constrainBlinkerPosition(display, display->getBlinkerPosition() - 1)); // Log printLog(F("ItemInput::left"), value); - return true; }; - bool right(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } + void right(DisplayInterface* display) { if (cursor == strlen(value)) { - return true; + return; } cursor++; uint8_t viewSize = getViewSize(display); @@ -230,7 +234,6 @@ class ItemInput : public MenuItem { display->resetBlinker(constrainBlinkerPosition(display, display->getBlinkerPosition() + 1)); // Log printLog(F("ItemInput::right"), value); - return true; }; /** * Execute a "backspace cmd" on menu @@ -239,13 +242,9 @@ class ItemInput : public MenuItem { * * Removes the character at the current cursor position. */ - bool backspace(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } + void backspace(DisplayInterface* display) { if (strlen(value) == 0 || cursor == 0) { - return true; + return; } remove(value, cursor - 1, 1); printLog(F("ItemInput::backspace"), value); @@ -255,19 +254,13 @@ class ItemInput : public MenuItem { } MenuItem::draw(display); display->resetBlinker(constrainBlinkerPosition(display, display->getBlinkerPosition() - 1)); - return true; } /** * Display text at the cursor position * used for `Input` type menu items * @param character character to append */ - bool typeChar(Context& context) { - DisplayInterface* display = context.display; - const char character = context.command; - if (!display->getEditModeEnabled()) { - return false; - } + void typeChar(DisplayInterface* display, const unsigned char character) { uint8_t length = strlen(value); if (cursor < length) { char start[length]; @@ -291,16 +284,11 @@ class ItemInput : public MenuItem { display->resetBlinker(constrainBlinkerPosition(display, display->getBlinkerPosition() + 1)); // Log printLog(F("ItemInput::typeChar"), character); - return true; } /** * Clear the value of the input field */ - bool clear(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } + void clear(DisplayInterface* display) { // // set the value // @@ -312,7 +300,6 @@ class ItemInput : public MenuItem { // MenuItem::draw(display); display->resetBlinker(constrainBlinkerPosition(display, strlen(text) + 2)); - return true; } }; diff --git a/src/ItemInputCharset.h b/src/ItemInputCharset.h index 5b76bba4..dd17668c 100644 --- a/src/ItemInputCharset.h +++ b/src/ItemInputCharset.h @@ -2,22 +2,91 @@ #define ItemInputCharset_H #include "ItemInput.h" +#include "LcdMenu.h" #include class ItemInputCharset : public ItemInput { private: const char* charset; - const uint8_t charsetSize; // Active index of the charset int8_t charsetPosition = -1; - bool charEditMode = false; + bool charEdit = false; + public: + ItemInputCharset(const char* text, char* value, const char* charset, fptrStr callback) + : ItemInput(text, value, callback), charset(charset) {} + + ItemInputCharset(const char* text, const char* charset, fptrStr callback) + : ItemInputCharset(text, (char*)"", charset, callback) {} + + protected: + bool process(LcdMenu* menu, const unsigned char command) override { + DisplayInterface* display = menu->getDisplay(); + if (display->getEditModeEnabled()) { + switch (command) { + case ENTER: + if (charEdit) { + commitCharEdit(display); + } + return true; + case BACK: + if (charEdit) { + abortCharEdit(display); + } else { + ItemInput::back(display); + } + return true; + case UP: + if (!charEdit) { + initCharEdit(); + } + showPreviousChar(display); + printLog(F("ItemInputCharset::up"), charset[charsetPosition]); + return true; + case DOWN: + if (!charEdit) { + initCharEdit(); + } + showNextChar(display); + printLog(F("ItemInputCharset::down"), charset[charsetPosition]); + return true; + case LEFT: + if (charEdit) { + abortCharEdit(display); + } + ItemInput::left(display); + return true; + case RIGHT: + if (charEdit) { + abortCharEdit(display); + } + ItemInput::right(display); + return true; + case BACKSPACE: + ItemInput::backspace(display); + return true; + case CLEAR: + ItemInput::clear(display); + return true; + default: + return false; + } + } else { + switch (command) { + case ENTER: + ItemInput::enter(display); + return true; + default: + return false; + } + } + } /** * @brief Initialize `char edit mode`. - * Set `charEditMode` flag and search for currently selected char in charset. + * Set `charEdit` flag and search for currently selected char in charset. */ - void initCharsetEditMode() { - charEditMode = true; + void initCharEdit() { + charEdit = true; if (cursor < strlen(value)) { char* e = strchr(charset, value[cursor]); if (e != NULL) { @@ -27,50 +96,19 @@ class ItemInputCharset : public ItemInput { } charsetPosition = -1; } - /** - * @brief Stop `char edit mode`. - * Unset `charEditMode` flag and draw actual char from `value`. + * @brief Stop `char edit mode` and abort all mde changes. + * Unset `charEdit` flag and draw actual char from `value`. */ - void stopCharsetEditMode(DisplayInterface* display) { - charEditMode = false; + void abortCharEdit(DisplayInterface* display) { + charEdit = false; if (cursor < strlen(value)) { display->drawChar(value[cursor]); } else { display->drawChar(' '); } } - - public: - ItemInputCharset(const char* text, char* value, const char* charset, fptrStr callback) - : ItemInput(text, value, callback), charset(charset), charsetSize(strlen(charset)) {} - ItemInputCharset(const char* text, const char* charset, fptrStr callback) - : ItemInputCharset(text, (char*)"", charset, callback) {} - - protected: - bool process(Context& context) override { - const unsigned char c = context.command; - switch (c) { - case ENTER: return enter(context); - case BACK: return back(context); - case UP: return up(context); - case DOWN: return down(context); - case LEFT: return left(context); - case RIGHT: return right(context); - case BACKSPACE: return ItemInput::backspace(context); - case CLEAR: return ItemInput::clear(context); - default: return false; - } - } - - bool enter(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return ItemInput::enter(context); - } - if (!charEditMode) { - return true; - } + void commitCharEdit(DisplayInterface* display) { uint8_t length = strlen(value); if (cursor < length) { value[cursor] = charset[charsetPosition]; @@ -79,71 +117,20 @@ class ItemInputCharset : public ItemInput { concat(value, charset[charsetPosition], buf); value = buf; } - stopCharsetEditMode(display); - ItemInput::right(context); - return true; - } - - bool back(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } - if (!charEditMode) { - return ItemInput::back(context); - } - stopCharsetEditMode(display); - return true; - }; - - bool left(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } - if (charEditMode) { - stopCharsetEditMode(display); - } - return ItemInput::left(context); + abortCharEdit(display); + ItemInput::right(display); } - - bool right(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } - if (charEditMode) { - stopCharsetEditMode(display); - } - return ItemInput::right(context); - } - - bool down(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } - if (!charEditMode) { - initCharsetEditMode(); + void showNextChar(DisplayInterface* display) { + if (charset[charsetPosition + 1] == '\0') { + charsetPosition = 0; + } else { + charsetPosition++; } - charsetPosition = (charsetPosition + 1) % charsetSize; display->drawChar(charset[charsetPosition]); - printLog(F("ItemInputCharset::down"), charset[charsetPosition]); - return true; } - - bool up(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } - if (!charEditMode) { - initCharsetEditMode(); - } - charsetPosition = constrain(charsetPosition - 1, 0, charsetSize); + void showPreviousChar(DisplayInterface* display) { + charsetPosition = max(charsetPosition - 1, 0); display->drawChar(charset[charsetPosition]); - printLog(F("ItemInputCharset::up"), charset[charsetPosition]); - return true; } }; diff --git a/src/ItemList.h b/src/ItemList.h index fc605734..a09bf818 100644 --- a/src/ItemList.h +++ b/src/ItemList.h @@ -25,6 +25,7 @@ */ #ifndef ItemList_H #define ItemList_H +#include "LcdMenu.h" #include "MenuItem.h" #include @@ -102,69 +103,57 @@ class ItemList : public MenuItem { display->drawItem(row, text, ':', buf); } - bool process(Context& context) override { - const unsigned char c = context.command; - switch (c) { - case ENTER: return enter(context); - case BACK: return back(context); - case UP: return up(context); - case DOWN: return down(context); - default: return false; - } - } - - bool enter(Context& context) { - DisplayInterface* display = context.display; + bool process(LcdMenu* menu, const unsigned char command) override { + DisplayInterface* display = menu->getDisplay(); if (display->getEditModeEnabled()) { - return false; - } - display->setEditModeEnabled(true); - printLog(F("ItemList::enterEditMode"), getValue()); - return true; - }; - - bool back(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; + switch (command) { + case BACK: + display->setEditModeEnabled(false); + if (callback != NULL) { + callback(itemIndex); + } + printLog(F("ItemList::exitEditMode"), getValue()); + return true; + case UP: + selectNext(display); + return true; + case DOWN: + selectPrevious(display); + return true; + default: + return false; + } + } else { + switch (command) { + case ENTER: + display->setEditModeEnabled(true); + printLog(F("ItemList::enterEditMode"), getValue()); + return true; + default: + return false; + } } - display->setEditModeEnabled(false); - if (callback != NULL) { - callback(itemIndex); - } - printLog(F("ItemList::exitEditMode"), getValue()); - return true; - }; + } - bool down(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } + void selectPrevious(DisplayInterface* display) { uint8_t previousIndex = itemIndex; itemIndex = constrain(itemIndex - 1, 0, (uint16_t)(itemCount)-1); if (previousIndex != itemIndex) { MenuItem::draw(display); } printLog(F("ItemList::down"), getValue()); - return true; }; - bool up(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } + void selectNext(DisplayInterface* display) { uint8_t previousIndex = itemIndex; itemIndex = constrain((itemIndex + 1) % itemCount, 0, (uint16_t)(itemCount)-1); if (previousIndex != itemIndex) { MenuItem::draw(display); } printLog(F("ItemList::up"), getValue()); - return true; }; }; #define ITEM_STRING_LIST(...) (new ItemList(__VA_ARGS__)) -#endif \ No newline at end of file +#endif diff --git a/src/ItemProgress.h b/src/ItemProgress.h index 8c700b7d..69a9ae99 100644 --- a/src/ItemProgress.h +++ b/src/ItemProgress.h @@ -6,6 +6,7 @@ #ifndef ItemProgress_H #define ItemProgress_H +#include "LcdMenu.h" #include "MenuItem.h" #include @@ -49,23 +50,25 @@ class ItemProgress : public MenuItem { /** * @brief Increments the progress of the list. */ - void increment() { + bool increment() { if (progress >= MAX_PROGRESS) { - return; + return false; } progress += stepLength; printLog(F("ItemProgress::increment"), getValue()); + return true; } /** * @brief Decrements the progress of the list. */ - void decrement() { + bool decrement() { if (progress <= MIN_PROGRESS) { - return; + return false; } progress -= stepLength; printLog(F("ItemProgress::decrement"), getValue()); + return true; } /** @@ -73,7 +76,7 @@ class ItemProgress : public MenuItem { * @param uint16_t progress for the item */ void setProgress(uint16_t value) { - if (progress < MIN_PROGRESS || progress > MAX_PROGRESS) { + if (value < MIN_PROGRESS || progress > MAX_PROGRESS) { return; } progress = value; @@ -113,66 +116,43 @@ class ItemProgress : public MenuItem { display->drawItem(row, text, ':', buf); } - bool process(Context& context) { - switch (context.command) { - case ENTER: return enter(context); - case BACK: return back(context); - case UP: return up(context); - case DOWN: return down(context); - default: return false; - } - } - - bool enter(Context& context) { - DisplayInterface* display = context.display; + bool process(LcdMenu* menu, const unsigned char command) override { + DisplayInterface* display = menu->getDisplay(); if (display->getEditModeEnabled()) { - return false; - } - display->setEditModeEnabled(true); - printLog(F("ItemProgress::enterEditMode"), getValue()); - return true; - }; - - bool back(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; + switch (command) { + case BACK: + display->setEditModeEnabled(false); + if (callback != NULL) { + callback(progress); + } + printLog(F("ItemProgress::exitEditMode"), getValue()); + return true; + case UP: + if (increment()) { + MenuItem::draw(display); + } + return true; + case DOWN: + if (decrement()) { + MenuItem::draw(display); + } + return true; + default: + return false; + } + } else { + switch (command) { + case ENTER: + display->setEditModeEnabled(true); + printLog(F("ItemProgress::enterEditMode"), getValue()); + return true; + default: + return false; + } } - display->setEditModeEnabled(false); - if (callback != NULL) { - callback(progress); - } - printLog(F("ItemProgress::exitEditMode"), getValue()); - return true; - }; - - bool down(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } - uint16_t oldProgress = progress; - decrement(); - if (progress != oldProgress) { - MenuItem::draw(display); - } - return true; - }; - - bool up(Context& context) { - DisplayInterface* display = context.display; - if (!display->getEditModeEnabled()) { - return false; - } - uint16_t oldProgress = progress; - increment(); - if (progress != oldProgress) { - MenuItem::draw(display); - } - return true; - }; + } }; #define ITEM_PROGRESS(...) (new ItemProgress(__VA_ARGS__)) -#endif // ItemProgress_H \ No newline at end of file +#endif // ItemProgress_H diff --git a/src/ItemSubMenu.h b/src/ItemSubMenu.h index 2e393df9..f95bf35b 100644 --- a/src/ItemSubMenu.h +++ b/src/ItemSubMenu.h @@ -24,20 +24,18 @@ class ItemSubMenu : public MenuItem { ItemSubMenu(const char* text, MenuScreen*& screen) : MenuItem(text), screen(screen) {} protected: - bool process(Context& context) { - switch (context.command) { - case ENTER: return enter(context); - default: return false; + bool process(LcdMenu* menu, const unsigned char command) { + switch (command) { + case ENTER: + return true; + default: + return false; } } - /** - * Open next screen. - */ - bool enter(Context& context) { + void changeScreen(LcdMenu* menu) { printLog(F("ItemSubMenu::enter"), text); - screen->setParent(context.menu->getScreen()); - context.menu->setScreen(screen); - return true; + screen->setParent(menu->getScreen()); + menu->setScreen(screen); } }; diff --git a/src/ItemToggle.h b/src/ItemToggle.h index ec337a7b..f65be936 100644 --- a/src/ItemToggle.h +++ b/src/ItemToggle.h @@ -84,21 +84,24 @@ class ItemToggle : public MenuItem { }; protected: - bool process(Context& context) override { - switch (context.command) { - case ENTER: return enter(context); - default: return false; + bool process(LcdMenu* menu, const unsigned char command) override { + DisplayInterface* display = menu->getDisplay(); + switch (command) { + case ENTER: + toggle(display); + return true; + default: + return false; } }; - bool enter(Context& context) { + void toggle(DisplayInterface* display) { enabled = !enabled; if (callback != NULL) { callback(enabled); - printLog(F("ItemToggle::toggle"), enabled ? textOn : textOff); } - MenuItem::draw(context.display); - return true; - }; + printLog(F("ItemToggle::toggle"), enabled ? textOn : textOff); + MenuItem::draw(display); + } }; #define ITEM_TOGGLE(...) (new ItemToggle(__VA_ARGS__)) diff --git a/src/LcdMenu.cpp b/src/LcdMenu.cpp index 7dba47ae..4775a8e6 100644 --- a/src/LcdMenu.cpp +++ b/src/LcdMenu.cpp @@ -18,12 +18,11 @@ bool LcdMenu::process(const unsigned char c) { if (!enabled) { return false; } - MenuItem::Context context{this, &display, c}; - return screen->process(context); + return screen->process(this, c); }; void LcdMenu::reset() { - this->screen->reset(&display); + this->screen->setCursor(&display, 0); } void LcdMenu::hide() { diff --git a/src/MenuItem.h b/src/MenuItem.h index 48f91d94..196277fe 100644 --- a/src/MenuItem.h +++ b/src/MenuItem.h @@ -56,7 +56,7 @@ class MenuItem { * Get the text of the item * @return `String` - Item's text */ - virtual const char* getText() { return text; } + const char* getText() { return text; } /** * Set the text of the item * @param text text to display for the item @@ -64,11 +64,6 @@ class MenuItem { void setText(const char* text) { this->text = text; }; protected: - struct Context { - LcdMenu* menu; - DisplayInterface* display; - const unsigned char command; - }; /** * @brief Process a command decoded in 1 byte. * It can be a printable character or a control command like `ENTER` or `LEFT`. @@ -79,7 +74,7 @@ class MenuItem { * @param context the context of call, contains at least character command (can be a printable character or a control command) and backreference to menu * @return true if command was successfully handled by item. */ - virtual bool process(Context& context) { return false; }; + virtual bool process(LcdMenu* menu, const unsigned char command) { return false; }; /** * @brief Draw this menu item on specified display on current line. * @param display the display that should be used for draw diff --git a/src/MenuScreen.cpp b/src/MenuScreen.cpp index ab466d52..22648eb1 100644 --- a/src/MenuScreen.cpp +++ b/src/MenuScreen.cpp @@ -1,9 +1,24 @@ #include "MenuScreen.h" -bool MenuScreen::back(MenuItem::Context context) { - if (parent != NULL) { - context.menu->setScreen(parent); +bool MenuScreen::process(LcdMenu* menu, const unsigned char command) { + DisplayInterface* display = menu->getDisplay(); + if (items[cursor]->process(menu, command)) { + return true; + } + switch (command) { + case UP: + up(display); + return true; + case DOWN: + down(display); + return true; + case BACK: + if (parent != NULL) { + menu->setScreen(parent); + } + printLog(F("MenuScreen::back")); + return true; + default: + return false; } - printLog(F("MenuScreen::back")); - return true; } diff --git a/src/MenuScreen.h b/src/MenuScreen.h index e3b97ec9..3e2e18d4 100644 --- a/src/MenuScreen.h +++ b/src/MenuScreen.h @@ -125,9 +125,11 @@ class MenuScreen { draw(display); } void draw(DisplayInterface* display) { + bool notFullView = false; for (uint8_t i = 0; i < display->getMaxRows(); i++) { MenuItem* item = this->items[view + i]; if (item == nullptr) { + notFullView = true; break; } item->draw(display, i); @@ -137,35 +139,24 @@ class MenuScreen { } else { display->drawUpIndicator(); } - if (items[view + display->getMaxRows()] != nullptr) { - display->drawDownIndicator(); - } else { + if (notFullView || items[view + display->getMaxRows()] == nullptr) { display->clearDownIndicator(); + } else { + display->drawDownIndicator(); } display->moveCursor(cursor - view); display->drawCursor(); // In case if currentPosition was not changed between screens } - bool process(MenuItem::Context context) { - if (items[cursor]->process(context)) { - return true; - } - switch (context.command) { - case UP: return up(context); - case DOWN: return down(context); - case BACK: return back(context); - default: return false; - } - } + bool process(LcdMenu* menu, const unsigned char command); /** * Execute an "up press" on menu * When edit mode is enabled, this action is skipped */ - bool up(MenuItem::Context context) { + void up(DisplayInterface* display) { if (cursor == 0) { printLog(F("MenuScreen:up"), cursor); - return false; + return; } - DisplayInterface* display = context.display; cursor--; if (cursor < view) { view--; @@ -174,18 +165,16 @@ class MenuScreen { display->moveCursor(cursor - view); } printLog(F("MenuScreen:up"), cursor); - return true; } /** * Execute a "down press" on menu * When edit mode is enabled, this action is skipped */ - bool down(MenuItem::Context context) { + void down(DisplayInterface* display) { if (cursor == itemsCount() - 1) { printLog(F("MenuScreen:down"), cursor); - return false; + return; } - DisplayInterface* display = context.display; cursor++; if (cursor > view + display->getMaxRows() - 1) { view++; @@ -194,14 +183,7 @@ class MenuScreen { display->moveCursor(cursor - view); } printLog(F("MenuScreen:down"), cursor); - return true; } - /** - * Execute a "backpress" action on menu. - * - * Navigates up once. - */ - bool back(MenuItem::Context context); /** * @brief Reset the screen to initial state. */ diff --git a/src/utils/utils.h b/src/utils/utils.h index a85dff17..d5b330d0 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -1,7 +1,7 @@ #ifndef MenuUtils_H #define MenuUtils_H -#include +#include #include "constants.h" inline void substring(const char* str, uint8_t start, uint8_t size, char* substr) { From ac41b848ab14ee37cbee638635ddf58bebb5326b Mon Sep 17 00:00:00 2001 From: Dmitry Shishkin Date: Sat, 21 Sep 2024 02:00:54 +0200 Subject: [PATCH 3/9] Fix formatting --- src/utils/utils.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/utils.h b/src/utils/utils.h index d5b330d0..33adc752 100644 --- a/src/utils/utils.h +++ b/src/utils/utils.h @@ -1,8 +1,8 @@ #ifndef MenuUtils_H #define MenuUtils_H -#include #include "constants.h" +#include inline void substring(const char* str, uint8_t start, uint8_t size, char* substr) { strncpy(substr, str + start, size); From cefe6cb27795fb330b0223add66927a7a146ee6e Mon Sep 17 00:00:00 2001 From: Dmitry Shishkin Date: Sat, 21 Sep 2024 02:05:57 +0200 Subject: [PATCH 4/9] Fix compile error --- src/ItemToggle.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ItemToggle.h b/src/ItemToggle.h index f65be936..872e2250 100644 --- a/src/ItemToggle.h +++ b/src/ItemToggle.h @@ -10,6 +10,7 @@ #ifndef ItemToggle_H #define ItemToggle_H #include "MenuItem.h" +#include "LcdMenu.h" #include /** From 742f050660a8f163cea26f885da5fe3131d67b54 Mon Sep 17 00:00:00 2001 From: Dmitry Shishkin Date: Sat, 21 Sep 2024 02:07:46 +0200 Subject: [PATCH 5/9] Fix formatting --- src/ItemToggle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ItemToggle.h b/src/ItemToggle.h index 872e2250..5230d0b3 100644 --- a/src/ItemToggle.h +++ b/src/ItemToggle.h @@ -9,8 +9,8 @@ #ifndef ItemToggle_H #define ItemToggle_H -#include "MenuItem.h" #include "LcdMenu.h" +#include "MenuItem.h" #include /** From 9939f52474dbbe9ce852731e7aeb3deb60d7a96f Mon Sep 17 00:00:00 2001 From: Thomas Forntoh Date: Sat, 21 Sep 2024 09:50:21 +0100 Subject: [PATCH 6/9] Update src/ItemList.h --- src/ItemList.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ItemList.h b/src/ItemList.h index a09bf818..cfee7362 100644 --- a/src/ItemList.h +++ b/src/ItemList.h @@ -150,7 +150,7 @@ class ItemList : public MenuItem { if (previousIndex != itemIndex) { MenuItem::draw(display); } - printLog(F("ItemList::up"), getValue()); + printLog(F("ItemList::selectNext"), getValue()); }; }; From d8d787e9435626e7ab6fe433cf3510d727f3f89c Mon Sep 17 00:00:00 2001 From: Thomas Forntoh Date: Sat, 21 Sep 2024 09:50:31 +0100 Subject: [PATCH 7/9] Update src/ItemList.h --- src/ItemList.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ItemList.h b/src/ItemList.h index cfee7362..cbc02244 100644 --- a/src/ItemList.h +++ b/src/ItemList.h @@ -141,7 +141,7 @@ class ItemList : public MenuItem { if (previousIndex != itemIndex) { MenuItem::draw(display); } - printLog(F("ItemList::down"), getValue()); + printLog(F("ItemList::selectPrevious"), getValue()); }; void selectNext(DisplayInterface* display) { From 3b7c3ccc0e8499676db931bc46dd99d2da71718c Mon Sep 17 00:00:00 2001 From: Thomas Forntoh Date: Sat, 21 Sep 2024 09:50:40 +0100 Subject: [PATCH 8/9] Update src/ItemSubMenu.h --- src/ItemSubMenu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ItemSubMenu.h b/src/ItemSubMenu.h index f95bf35b..83be8489 100644 --- a/src/ItemSubMenu.h +++ b/src/ItemSubMenu.h @@ -33,7 +33,7 @@ class ItemSubMenu : public MenuItem { } } void changeScreen(LcdMenu* menu) { - printLog(F("ItemSubMenu::enter"), text); + printLog(F("ItemSubMenu::changeScreen"), text); screen->setParent(menu->getScreen()); menu->setScreen(screen); } From 21ebb8d786b9dcff7f21522ecbaa8c02c3f5e471 Mon Sep 17 00:00:00 2001 From: Dmitry Shishkin Date: Sat, 21 Sep 2024 11:58:27 +0200 Subject: [PATCH 9/9] Resolve review comments --- src/ItemSubMenu.h | 2 +- src/MenuItem.h | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/ItemSubMenu.h b/src/ItemSubMenu.h index f95bf35b..83be8489 100644 --- a/src/ItemSubMenu.h +++ b/src/ItemSubMenu.h @@ -33,7 +33,7 @@ class ItemSubMenu : public MenuItem { } } void changeScreen(LcdMenu* menu) { - printLog(F("ItemSubMenu::enter"), text); + printLog(F("ItemSubMenu::changeScreen"), text); screen->setParent(menu->getScreen()); menu->setScreen(screen); } diff --git a/src/MenuItem.h b/src/MenuItem.h index 196277fe..629d7d07 100644 --- a/src/MenuItem.h +++ b/src/MenuItem.h @@ -53,15 +53,19 @@ class MenuItem { public: MenuItem(const char* text) : text(text) {} /** - * Get the text of the item + * @brief Get the text of the item * @return `String` - Item's text */ - const char* getText() { return text; } + const char* getText() { + return text; + } /** - * Set the text of the item + * @brief Set the text of the item * @param text text to display for the item */ - void setText(const char* text) { this->text = text; }; + void setText(const char* text) { + this->text = text; + }; protected: /** @@ -71,10 +75,13 @@ class MenuItem { * If the parent of item received that handle was ignored it can execute it's own action on this command. * Thus, the item always has priority in processing; if it is ignored, it is delegated to the parent element. * Behaviour is very similar to Even Bubbling in JavaScript. - * @param context the context of call, contains at least character command (can be a printable character or a control command) and backreference to menu + * @param menu the owner menu of the item, can be used to retrieve required object, such as `DisplayInterface` or `MenuScreen` + * @param command the character command, can be a printable character or a control command * @return true if command was successfully handled by item. */ - virtual bool process(LcdMenu* menu, const unsigned char command) { return false; }; + virtual bool process(LcdMenu* menu, const unsigned char command) { + return false; + }; /** * @brief Draw this menu item on specified display on current line. * @param display the display that should be used for draw