-
-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy pathBaseItemManyWidgets.h
169 lines (154 loc) · 6.17 KB
/
BaseItemManyWidgets.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
// Creator: @ShishkinDmitriy
#ifndef BASE_ITEM_MANY_WIDGETS_H
#define BASE_ITEM_MANY_WIDGETS_H
#include "LcdMenu.h"
#include "MenuItem.h"
#include "widget/BaseWidget.h"
#include <utils/utils.h>
class BaseItemManyWidgets : public MenuItem {
protected:
BaseWidget** widgets = nullptr;
const uint8_t size = 0;
uint8_t activeWidget = 0;
public:
BaseItemManyWidgets(const char* text, BaseWidget** widgets, const uint8_t size, uint8_t activeWidget = 0)
: MenuItem(text), widgets(widgets), size(size), activeWidget(constrain(activeWidget, 0, size)) {}
uint8_t getActiveWidget() const { return activeWidget; }
void setActiveWidget(const uint8_t activeWidget) {
if (activeWidget < size) {
this->activeWidget = activeWidget;
}
}
virtual ~BaseItemManyWidgets() {
for (uint8_t i = 0; i < size; ++i)
delete widgets[i];
delete[] widgets;
}
protected:
virtual void handleCommit() = 0;
/**
* @brief Reset the active widget to the first widget.
*/
void reset() { activeWidget = 0; }
/**
* @brief Draws the menu item using the provided renderer.
*
* This function is responsible for rendering the menu item and its associated widgets.
* It iterates through each widget, drawing them into a buffer, and then uses the renderer
* to display the combined result. If the renderer is in edit mode, it also handles the
* cursor positioning for editing.
*
* @param renderer A pointer to the MenuRenderer object used for drawing the item.
*
* The function performs the following steps:
* 1. Initializes a buffer to hold the drawn content and variables for indexing and cursor positioning.
* 2. Iterates through each widget, drawing it into the buffer and updating the index.
* 3. If the active widget is being edited, it draws the item using the renderer and calculates the cursor position.
* 4. Draws the final item using the renderer.
* 5. If in edit mode, moves the cursor to the appropriate position for editing.
*/
void draw(MenuRenderer* renderer) override {
char buf[ITEM_DRAW_BUFFER_SIZE];
uint8_t index = 0;
uint8_t cursorCol = 0;
for (uint8_t i = 0; i < size; i++) {
index += widgets[i]->draw(buf, index);
if (i == activeWidget && renderer->isInEditMode()) {
// Calculate the available space for the widgets after the text
size_t v_size = renderer->getEffectiveCols() - strlen(text) - 1;
// Adjust the view shift to ensure the active widget is visible
renderer->viewShift = index > v_size ? index - v_size : 0;
// Draw the item with the renderer, indicating if it's the last widget
renderer->drawItem(text, buf, i == size - 1);
// Calculate the cursor column position for the active widget
cursorCol = renderer->getCursorCol() - 1 - widgets[i]->cursorOffset;
}
}
renderer->drawItem(text, buf);
if (renderer->isInEditMode()) {
renderer->moveCursor(cursorCol, renderer->getCursorRow());
}
}
/**
* @brief Processes a command for the active widget in the menu.
*
* This function handles the processing of commands for the active widget in the menu.
* It first attempts to process the command using the active widget. If the widget
* processes the command successfully, it redraws the menu and returns true.
*
* If the renderer is in edit mode, it handles navigation commands (ENTER, RIGHT, LEFT, BACK)
* to navigate through the widgets or exit edit mode.
*
* When the ENTER command is received while the renderer is in edit mode and the active widget
* is not the last widget, it moves to the next widget. If the active widget is the last widget,
* it exits edit mode and calls the handleCommit function to commit the changes.
*
* If the renderer is not in edit mode and the ENTER command is received, it sets the renderer
* to edit mode, redraws the menu, and draws a blinker.
*
* @param menu Pointer to the LcdMenu object.
* @param command The command to be processed.
* @return true if the command was processed successfully, false otherwise.
*/
bool process(LcdMenu* menu, const unsigned char command) override {
MenuRenderer* renderer = menu->getRenderer();
if (widgets[activeWidget]->process(menu, command)) {
draw(renderer);
return true;
}
if (renderer->isInEditMode()) {
switch (command) {
case ENTER:
if (activeWidget < this->size - 1) {
right(renderer);
} else {
back(renderer);
}
return true;
case RIGHT:
right(renderer);
return true;
case LEFT:
left(renderer);
return true;
case BACK:
back(renderer);
return true;
default:
return false;
}
}
if (command == ENTER) {
renderer->setEditMode(true);
draw(renderer);
renderer->drawBlinker();
LOG(F("ItemWidget::enterEditMode"), this->text);
return true;
}
return false;
}
void left(MenuRenderer* renderer) {
if (activeWidget == 0) {
activeWidget = this->size - 1;
} else {
activeWidget--;
}
draw(renderer);
LOG(F("ItemWidget::left"), activeWidget);
}
void right(MenuRenderer* renderer) {
activeWidget = (activeWidget + 1) % this->size;
draw(renderer);
LOG(F("ItemWidget::right"), activeWidget);
}
void back(MenuRenderer* renderer) {
renderer->setEditMode(false);
renderer->viewShift = 0;
reset();
handleCommit();
renderer->clearBlinker();
draw(renderer);
LOG(F("ItemWidget::exitEditMode"), this->text);
}
};
#endif