CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
ToastView.cpp
Go to the documentation of this file.
1
6
10#include "cdc_ui/ViewStack.h"
11#include "cdc_hal/IDisplay.h"
12#include <goodisplay/gdey029T94.h>
13#include <cstring>
14#include "esp_timer.h"
15
16namespace cdc::ui {
17
26void ToastView::init(const char* message, Icon icon, uint16_t durationMs, bool dismissible) {
27 if (message) {
28 strncpy(message_, message, MAX_MSG_LEN - 1);
29 message_[MAX_MSG_LEN - 1] = '\0';
30 } else {
31 message_[0] = '\0';
32 }
33
34 icon_ = icon;
35 durationMs_ = durationMs;
36 dismissible_ = dismissible;
37 startMs_ = 0;
38 expired_ = false;
39 dirty_ = true;
40}
41
47void ToastView::onTick(uint32_t nowMs) {
48 if (startMs_ == 0 || expired_) return;
49 if (durationMs_ == 0) return;
50 if (nowMs < startMs_) return;
51 static constexpr uint32_t MIN_DISPLAY_MS = 1000;
52 uint32_t effective = durationMs_ < MIN_DISPLAY_MS ? MIN_DISPLAY_MS : durationMs_;
53 if (nowMs - startMs_ >= effective) {
54 expired_ = true;
56 }
57}
58
65 // Any Y or N key dismisses the toast (if dismissible)
66 if (dismissible_ && (key == KEY_YES || key == KEY_NO)) {
67 expired_ = true;
70 }
72}
73
79void ToastView::render(bool partial) {
80 (void)partial;
81
82 if (startMs_ == 0) {
83 startMs_ = esp_timer_get_time() / 1000;
84 }
85
87 if (!display) return;
88
89 auto* gfx = static_cast<Gdey029T94*>(display->getNativeHandle());
90 if (!gfx) return;
91
92 const uint16_t width = display->getWidth();
93 const uint16_t height = display->getHeight();
94
95 // Calculate centered position
96 int boxX = (width - BOX_WIDTH) / 2;
97 int boxY = (height - BOX_HEIGHT) / 2;
98
99 // Draw white box with double black border
100 render::drawDialogFrame(gfx, boxX, boxY, BOX_WIDTH, BOX_HEIGHT);
101
102 gfx->setTextColor(EPD_BLACK);
103 gfx->setTextSize(1);
104
105 // Text position (adjusted if icon present). y is the TOP of the glyph for
106 // size-1 glcdfont (8px tall), so subtract half the height to vertically center.
107 int textX = boxX + 15;
108 int textY = boxY + (BOX_HEIGHT / 2) - 4;
109
110 // Draw icon if present
111 if (icon_ != Icon::NONE) {
112 int iconX = boxX + 20;
113 int iconY = boxY + (BOX_HEIGHT / 2);
114
115 switch (icon_) {
116 case Icon::SUCCESS:
117 // Checkmark
118 gfx->drawLine(iconX - 5, iconY, iconX - 2, iconY + 4, EPD_BLACK);
119 gfx->drawLine(iconX - 2, iconY + 4, iconX + 6, iconY - 5, EPD_BLACK);
120 // Thicker
121 gfx->drawLine(iconX - 5, iconY + 1, iconX - 2, iconY + 5, EPD_BLACK);
122 gfx->drawLine(iconX - 2, iconY + 5, iconX + 6, iconY - 4, EPD_BLACK);
123 break;
124
125 case Icon::ERROR:
126 // X mark
127 gfx->drawLine(iconX - 5, iconY - 5, iconX + 5, iconY + 5, EPD_BLACK);
128 gfx->drawLine(iconX - 5, iconY + 5, iconX + 5, iconY - 5, EPD_BLACK);
129 // Thicker
130 gfx->drawLine(iconX - 4, iconY - 5, iconX + 6, iconY + 5, EPD_BLACK);
131 gfx->drawLine(iconX - 4, iconY + 5, iconX + 6, iconY - 5, EPD_BLACK);
132 break;
133
134 case Icon::INFO:
135 // Circle with i
136 gfx->drawCircle(iconX, iconY, 6, EPD_BLACK);
137 gfx->fillRect(iconX - 1, iconY - 3, 2, 2, EPD_BLACK); // Dot
138 gfx->fillRect(iconX - 1, iconY, 2, 5, EPD_BLACK); // Stem
139 break;
140 case Icon::TASK:
141 // Simple hourglass icon
142 gfx->drawLine(iconX - 5, iconY - 6, iconX + 5, iconY - 6, EPD_BLACK);
143 gfx->drawLine(iconX - 5, iconY + 6, iconX + 5, iconY + 6, EPD_BLACK);
144 gfx->drawLine(iconX - 5, iconY - 6, iconX + 5, iconY + 6, EPD_BLACK);
145 gfx->drawLine(iconX + 5, iconY - 6, iconX - 5, iconY + 6, EPD_BLACK);
146 gfx->fillTriangle(iconX - 3, iconY - 4, iconX + 3, iconY - 4, iconX, iconY - 1, EPD_BLACK);
147 gfx->fillTriangle(iconX - 3, iconY + 4, iconX + 3, iconY + 4, iconX, iconY + 1, EPD_BLACK);
148 break;
149 case Icon::ALERT:
150 // Warning triangle with exclamation
151 gfx->drawTriangle(iconX, iconY - 7, iconX - 6, iconY + 6, iconX + 6, iconY + 6, EPD_BLACK);
152 gfx->fillRect(iconX - 1, iconY - 2, 2, 5, EPD_BLACK);
153 gfx->fillRect(iconX - 1, iconY + 4, 2, 2, EPD_BLACK);
154 break;
155
156 default:
157 break;
158 }
159
160 textX = boxX + 40; // Shift text right when icon present
161 }
162
163 // Draw message text. Adafruit-GFX print() resets cursor_x to 0 on '\n',
164 // which would shoot the second line to the left edge of the screen. Render
165 // each line manually so it stays inside the modal frame, and shift the
166 // first line up to keep the whole block vertically centred.
167 constexpr int kLineHeight = 10; // size-1 font (~8px) + 2px spacing
168
169 size_t lineCount = 1;
170 for (const char* p = message_; *p; ++p) {
171 if (*p == '\n') ++lineCount;
172 }
173
174 int blockY = textY - static_cast<int>((lineCount - 1) * kLineHeight / 2);
175 const char* lineStart = message_;
176 int lineY = blockY;
177 for (const char* p = message_;; ++p) {
178 if (*p == '\n' || *p == '\0') {
179 char lineBuf[96];
180 size_t len = static_cast<size_t>(p - lineStart);
181 if (len >= sizeof(lineBuf)) len = sizeof(lineBuf) - 1;
182 memcpy(lineBuf, lineStart, len);
183 lineBuf[len] = '\0';
184 gfx->setCursor(textX, lineY);
185 // Clip to the box interior: text drawn past the frame is never
186 // cleared by drawDialogFrame, so on a partial refresh the overflow
187 // of the previous message stays on screen (ghosting).
188 render::printTruncated(gfx, lineBuf, (boxX + BOX_WIDTH) - textX - 8);
189 if (*p == '\0') break;
190 lineStart = p + 1;
191 lineY += kLineHeight;
192 }
193 }
194
195 dirty_ = false;
196}
197
201
203
212static void showToastInternal(const char* message, ToastView::Icon icon, uint16_t durationMs,
213 bool dismissible = true) {
214 s_sharedToast.init(message, icon, durationMs, dismissible);
216 ViewStack::instance().render(); // Immediate render for toast
217}
218
225void showToast(const char* message, uint16_t durationMs) {
226 showToastInternal(message, ToastView::Icon::NONE, durationMs);
227}
228
235void showToastSuccess(const char* message, uint16_t durationMs) {
236 showToastInternal(message, ToastView::Icon::SUCCESS, durationMs);
237}
238
245void showToastError(const char* message, uint16_t durationMs) {
246 showToastInternal(message, ToastView::Icon::ERROR, durationMs);
247}
248
255void showToastInfo(const char* message, uint16_t durationMs) {
256 showToastInternal(message, ToastView::Icon::INFO, durationMs);
257}
258
265void showToastTask(const char* message, uint16_t durationMs) {
266 showToastInternal(message, ToastView::Icon::TASK, durationMs);
267}
268
275void showToastAlert(const char* message, uint16_t durationMs) {
276 showToastInternal(message, ToastView::Icon::ALERT, durationMs);
277}
278
284void showToastAlertSticky(const char* message) {
285 showToastInternal(message, ToastView::Icon::ALERT, 0, false);
286}
287
288} // namespace cdc::ui
InputResult onKey(char key) override
Handles key input for optional toast dismissal.
Definition ToastView.cpp:64
void init(const char *message, Icon icon=Icon::NONE, uint16_t durationMs=1500, bool dismissible=true)
Initializes toast message content and timing behavior.
Definition ToastView.cpp:26
void onTick(uint32_t nowMs) override
Updates auto-dismiss timeout state.
Definition ToastView.cpp:47
void render(bool partial) override
Renders the toast overlay.
Definition ToastView.cpp:79
void render(bool synchronous=false)
Render current view (and modal if present) and flush to display.
static ViewStack & instance()
Returns singleton view-stack instance.
Definition ViewStack.cpp:34
void showModal(IView *modal)
IDisplay * getDisplayInstance()
Returns lazily created singleton display instance.
void drawDialogFrame(Gdey029T94 *gfx, int x, int y, int w, int h)
Draws a framed dialog box with double border.
void printTruncated(Gdey029T94 *gfx, const char *text, int maxWidthPx)
Print text at the current cursor, truncated with an ellipsis to fit maxWidthPx. Caller must have alre...
Centralized key-code constants for cdc_views.
Definition IModule.h:8
void showToast(const char *message, uint16_t durationMs=1500)
Shows a plain toast message.
static void showToastInternal(const char *message, ToastView::Icon icon, uint16_t durationMs, bool dismissible=true)
Shows the shared toast instance with custom icon and behavior.
void showToastTask(const char *message, uint16_t durationMs=0)
Shows a task/progress toast message.
Gdey029T94 * display
void showToastAlertSticky(const char *message)
Shows a non-dismissible alert toast.
InputResult
Definition IView.h:10
static constexpr char KEY_NO
Cancel / Back / Backspace.
Definition KeyCodes.h:44
void showToastAlert(const char *message, uint16_t durationMs=1500)
Shows an alert toast message.
void showToastSuccess(const char *message, uint16_t durationMs=1500)
Shows a success toast message.
static ToastView s_sharedToast
Convenience helper functions.
void showToastInfo(const char *message, uint16_t durationMs=1500)
Shows an informational toast message.
void showToastError(const char *message, uint16_t durationMs=1500)
Shows an error toast message.
static constexpr char KEY_YES
Confirm / OK / Save.
Definition KeyCodes.h:41