-
Notifications
You must be signed in to change notification settings - Fork 113
/
OLEDMenuManager.h
146 lines (138 loc) · 4.46 KB
/
OLEDMenuManager.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
/*
See https://github.com/PSHarold/OLED-SSD1306-Menu
for original code, license and documentation
*/
#ifndef OLED_MENU_MANAGER_H_
#define OLED_MENU_MANAGER_H_
#include "OLEDMenuItem.h"
#include "OLEDMenuConfig.h"
#define IMAGE_ITEM(name) name##_WIDTH, name##_HEIGHT, name
#define CENTER_IMAGE(name) (OLED_MENU_WIDTH - name##_WIDTH) / 2, (OLED_MENU_HEIGHT - name##_HEIGHT) / 2, name##_WIDTH, name##_HEIGHT, name
enum class OLEDMenuState
{
IDLE = 0,
ITEM_HANDLING,
FREEZING,
GOING_BACK,
};
enum class OLEDMenuNav
{
IDLE = 0,
UP,
DOWN,
ENTER,
};
class OLEDMenuManager
{
private:
OLEDDisplay *const display;
OLEDMenuItem allItems[OLED_MENU_MAX_ITEMS_NUM];
OLEDMenuItem *itemStack[OLED_MENU_MAX_DEPTH];
uint8_t itemSP;
OLEDMenuItem *itemUnderCursor; // null means the status bar is currently selected
OLEDMenuState state;
uint8_t cursor; // just a cache of the index of itemUnderCursor. may not be updated in time. use itemUnderCursor to control the cursor instead
uint16_t numTotalItem;
int8_t scrollDir;
uint16_t numRegisteredItem;
unsigned long lastUpdateTime;
unsigned long lastKeyPressedTime;
unsigned long screenSaverLastUpdateTime;
int8_t leadInFramesLeft = OLED_MENU_SCROLL_LEAD_IN_FRAMES;
const uint8_t *font;
char statusBarBuffer[16];
bool disabled;
friend void setup();
void resetScroll();
void drawStatusBar(bool negative = false);
inline void drawOneItem(OLEDMenuItem *item, uint16_t yOffset, bool negative);
void drawSubItems(OLEDMenuItem *parent);
inline void enterItem(OLEDMenuItem *item, OLEDMenuNav btn, bool isFirstTime);
void nextItem();
void prevItem();
void pushItem(OLEDMenuItem *item)
{
if (itemSP == OLED_MENU_MAX_DEPTH - 1)
{
char msg[30];
sprintf(msg, "Maximum depth reached: %d", OLED_MENU_MAX_DEPTH);
panicAndDisable(msg);
}
itemStack[itemSP++] = item;
}
OLEDMenuItem *popItem(bool preserveCursor = true);
OLEDMenuItem *peakItem()
{
if (itemSP == 0)
{
return rootItem;
}
return itemStack[itemSP - 1];
}
void panicAndDisable(const char *msg)
{
this->display->clear();
this->display->setColor(OLEDDISPLAY_COLOR::WHITE);
this->display->fillRect(0, 0, OLED_MENU_WIDTH, OLED_MENU_HEIGHT);
this->display->setColor(OLEDDISPLAY_COLOR::BLACK);
this->display->setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT::TEXT_ALIGN_LEFT);
this->display->setFont(ArialMT_Plain_10);
this->display->drawString(0, 0, msg);
this->display->display();
while (1);
}
void drawScreenSaver()
{
display->clear();
display->setColor(OLEDDISPLAY_COLOR::WHITE);
constexpr int16_t max_x = OLED_MENU_WIDTH - OM_SCREEN_SAVER_WIDTH;
constexpr int16_t max_y = OLED_MENU_HEIGHT - OM_SCREEN_SAVER_HEIGHT;
display->drawXbm(rand() % max_x, rand() % max_y, IMAGE_ITEM(OM_SCREEN_SAVER));
display->display();
}
public:
OLEDMenuManager(SSD1306Wire *display);
OLEDMenuItem *allocItem();
OLEDMenuItem *registerItem(OLEDMenuItem *parent, uint16_t tag, uint16_t imageWidth, uint16_t imageHeight, const uint8_t *xbmImage, MenuItemHandler handler = nullptr, OLEDDISPLAY_TEXT_ALIGNMENT alignment = TEXT_ALIGN_CENTER);
OLEDMenuItem *registerItem(OLEDMenuItem *parent, uint16_t tag, const char *string, MenuItemHandler handler = nullptr, const uint8_t *font = nullptr, OLEDDISPLAY_TEXT_ALIGNMENT alignment = TEXT_ALIGN_CENTER);
void tick(OLEDMenuNav btn);
void goBack(bool preserveCursor = true);
void goMain(bool preserveCursor = true);
OLEDMenuItem *const rootItem;
void clearSubItems(OLEDMenuItem *item)
{
if (item)
{
item->clearSubItems();
}
}
void freeze()
{
if (this->state == OLEDMenuState::ITEM_HANDLING)
{
// can only be called by item handlers
this->state = OLEDMenuState::FREEZING;
}
}
void unfreeze()
{
if (state == OLEDMenuState::FREEZING)
{
state = OLEDMenuState::ITEM_HANDLING;
delay(OLED_MENU_REFRESH_INTERVAL_IN_MS); // avoid retriggering current button
}
}
OLEDDisplay *getDisplay()
{
return this->display;
}
void disable()
{
this->disabled = true;
}
void enable()
{
this->disabled = false;
}
};
#endif