CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
DateInputView.cpp
Go to the documentation of this file.
1
6
10#include "cdc_ui/I18n.h"
11#include "cdc_ui/ViewStack.h"
12#include "cdc_hal/IDisplay.h"
13#include "cdc_log.h"
14#include <goodisplay/gdey029T94.h>
15#include <cstdio>
16
17static const char* TAG = "DateInputView";
18
22static constexpr int TITLE_Y = 20;
23static constexpr int DATE_Y = 60;
24static constexpr int UNDERLINE_Y = DATE_Y + 20;
25static constexpr int HINT_Y = 90;
26
27namespace cdc::ui {
28
37void DateInputView::init(const char* title, uint8_t day, uint8_t month, uint16_t year) {
38 title_ = title;
39 day_ = (day >= 1 && day <= 31) ? day : 1;
40 month_ = (month >= 1 && month <= 12) ? month : 1;
41 year_ = year;
42 currentField_ = Field::DAY;
43 digitPos_ = 0;
44 onCancel_ = nullptr;
45 dirty_ = true;
46}
47
52void DateInputView::nextField() {
53 if (currentField_ == Field::DAY) {
54 currentField_ = Field::MONTH;
55 } else if (currentField_ == Field::MONTH) {
56 currentField_ = Field::YEAR;
57 }
58 digitPos_ = 0;
59 dirty_ = true;
60}
61
66void DateInputView::prevField() {
67 if (currentField_ == Field::YEAR) {
68 currentField_ = Field::MONTH;
69 } else if (currentField_ == Field::MONTH) {
70 currentField_ = Field::DAY;
71 }
72 digitPos_ = 0;
73 dirty_ = true;
74}
75
80void DateInputView::clearField() {
81 switch (currentField_) {
82 case Field::DAY:
83 day_ = 0;
84 break;
85 case Field::MONTH:
86 month_ = 0;
87 break;
88 case Field::YEAR:
89 year_ = 0;
90 break;
91 }
92 digitPos_ = 0;
93 dirty_ = true;
94}
95
101void DateInputView::enterDigit(char digit) {
102 uint8_t d = digit - '0';
103
104 switch (currentField_) {
105 case Field::DAY: {
106 if (digitPos_ == 0) {
107 day_ = d * 10;
108 digitPos_ = 1;
109 } else {
110 day_ = (day_ / 10) * 10 + d;
111 if (day_ < 1) day_ = 1;
112 if (day_ > 31) day_ = 31;
113 nextField(); // Auto-advance
114 }
115 break;
116 }
117 case Field::MONTH: {
118 if (digitPos_ == 0) {
119 month_ = d * 10;
120 digitPos_ = 1;
121 } else {
122 month_ = (month_ / 10) * 10 + d;
123 if (month_ < 1) month_ = 1;
124 if (month_ > 12) month_ = 12;
125 nextField(); // Auto-advance
126 }
127 break;
128 }
129 case Field::YEAR: {
130 if (digitPos_ == 0) {
131 year_ = d * 1000;
132 digitPos_ = 1;
133 } else if (digitPos_ == 1) {
134 year_ = (year_ / 1000) * 1000 + d * 100;
135 digitPos_ = 2;
136 } else if (digitPos_ == 2) {
137 year_ = (year_ / 100) * 100 + d * 10;
138 digitPos_ = 3;
139 } else {
140 year_ = (year_ / 10) * 10 + d;
141 digitPos_ = 0; // Wrap around, stay in year
142 }
143 break;
144 }
145 }
146
147 dirty_ = true;
148}
149
154bool DateInputView::validateAndClamp() {
155 if (day_ < 1) day_ = 1;
156 if (day_ > 31) day_ = 31;
157 if (month_ < 1) month_ = 1;
158 if (month_ > 12) month_ = 12;
159 if (year_ < 2000) year_ = 2000;
160 if (year_ > 2099) year_ = 2099;
161 return true;
162}
163
170 // Digit input (all 0-9 keys are digits, auto-advances between fields)
171 if (key >= '0' && key <= '9') {
172 enterDigit(key);
174 }
175
176 switch (key) {
177 case KEY_NO: // Clear or cancel
178 if (digitPos_ > 0 ||
179 (currentField_ == Field::DAY && day_ > 0) ||
180 (currentField_ == Field::MONTH && month_ > 0) ||
181 (currentField_ == Field::YEAR && year_ > 0)) {
182 clearField();
184 }
186 if (onCancel_) {
187 onCancel_();
188 }
190
191 case KEY_YES: // Confirm
192 validateAndClamp();
193 LOG_I(TAG, "Date confirmed: %02d.%02d.%04d", day_, month_, year_);
195 if (onConfirm_) {
196 onConfirm_(day_, month_, year_);
197 }
199
200 default:
202 }
203}
204
209const char* DateInputView::getFooterHint() const {
210 return ui::tr("core.hint_date_input");
211}
212
218void DateInputView::render(bool partial) {
220 if (!display) return;
221
222 auto* gfx = static_cast<Gdey029T94*>(display->getNativeHandle());
223 if (!gfx) return;
224
225 const uint16_t width = display->getWidth();
226 const uint16_t height = display->getHeight();
227
228 if (!partial) {
229 gfx->fillScreen(EPD_WHITE);
230 }
231
232 gfx->setTextColor(EPD_BLACK);
233
234 if (title_) {
235 gfx->setTextSize(1);
236 render::drawHeaderCentered(gfx, title_, TITLE_Y, width);
237 }
238
239 char dateStr[20];
240 snprintf(dateStr, sizeof(dateStr), "%02d / %02d / %04d", day_, month_, year_);
241
242 gfx->setTextSize(2);
243 int16_t x1, y1;
244 uint16_t w, h;
245 gfx->getTextBounds(dateStr, 0, 0, &x1, &y1, &w, &h);
246 int startX = (width - w) / 2;
247 gfx->setCursor(startX, DATE_Y);
248 gfx->print(dateStr);
249
250 gfx->fillRect(0, UNDERLINE_Y, width, 4, EPD_WHITE);
251
252 int charWidth = 12;
253 int underlineX = startX;
254 int underlineW = 0;
255
256 switch (currentField_) {
257 case Field::DAY:
258 underlineX = startX;
259 underlineW = 2 * charWidth;
260 break;
261 case Field::MONTH:
262 underlineX = startX + 5 * charWidth;
263 underlineW = 2 * charWidth;
264 break;
265 case Field::YEAR:
266 underlineX = startX + 10 * charWidth;
267 underlineW = 4 * charWidth;
268 break;
269 }
270
271 gfx->fillRect(underlineX, UNDERLINE_Y, underlineW, 3, EPD_BLACK);
272
273 const char* hint = getFooterHint();
274 render::drawFooterBar(gfx, width, height, nullptr, hint, false);
275
276 dirty_ = false;
277}
278
279} // namespace cdc::ui
static const char * TAG
static constexpr int UNDERLINE_Y
static constexpr int HINT_Y
Internationalization with English fallbacks in code and overlay translations loaded at runtime from a...
static constexpr int DATE_Y
static constexpr int TITLE_Y
Display layout constants.
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_I(tag, fmt,...)
Definition cdc_log.h:147
InputResult onKey(char key) override
Handles key input for the date editor.
void render(bool partial) override
Renders the date input view.
void init(const char *title, uint8_t day, uint8_t month, uint16_t year)
Initializes date input state.
const char * getFooterHint() const override
Returns localized footer hint text.
static ViewStack & instance()
Returns singleton view-stack instance.
Definition ViewStack.cpp:34
IDisplay * getDisplayInstance()
Returns lazily created singleton display instance.
void drawFooterBar(Gdey029T94 *gfx, uint16_t width, uint16_t height, const char *prefix, const char *hint, bool force=false)
Draws footer bar with optional prefix and hint text.
void drawHeaderCentered(Gdey029T94 *gfx, const char *title, int y, uint16_t width)
Draws a centered header title.
Centralized key-code constants for cdc_views.
Definition IModule.h:8
const char * tr(const char *key)
Look up a translation by string key.
Definition I18n.h:208
Gdey029T94 * display
InputResult
Definition IView.h:10
static constexpr char KEY_NO
Cancel / Back / Backspace.
Definition KeyCodes.h:44
static constexpr int TITLE_Y
Layout constants mirror the ones used by T9InputView.
static const char * TAG
static constexpr char KEY_YES
Confirm / OK / Save.
Definition KeyCodes.h:41