CDC Badge OS
Firmware for the CDC Badge v1.0 hardware security key
Loading...
Searching...
No Matches
TimeInputView.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 = "TimeInputView";
18
22static constexpr int TITLE_Y = 20;
23static constexpr int TIME_Y = 55;
24static constexpr int UNDERLINE_Y = TIME_Y + 25;
25static constexpr int HINT_Y = 90;
26
27namespace cdc::ui {
28
36void TimeInputView::init(const char* title, uint8_t hour, uint8_t minute) {
37 title_ = title;
38 hour_ = (hour <= 23) ? hour : 0;
39 minute_ = (minute <= 59) ? minute : 0;
40 currentField_ = Field::HOUR;
41 digitPos_ = 0;
42 onCancel_ = nullptr;
43 dirty_ = true;
44}
45
50void TimeInputView::nextField() {
51 if (currentField_ == Field::HOUR) {
52 currentField_ = Field::MINUTE;
53 digitPos_ = 0;
54 dirty_ = true;
55 }
56}
57
62void TimeInputView::prevField() {
63 if (currentField_ == Field::MINUTE) {
64 currentField_ = Field::HOUR;
65 digitPos_ = 0;
66 dirty_ = true;
67 }
68}
69
74void TimeInputView::clearField() {
75 switch (currentField_) {
76 case Field::HOUR:
77 hour_ = 0;
78 break;
79 case Field::MINUTE:
80 minute_ = 0;
81 break;
82 }
83 digitPos_ = 0;
84 dirty_ = true;
85}
86
92void TimeInputView::enterDigit(char digit) {
93 uint8_t d = digit - '0';
94
95 switch (currentField_) {
96 case Field::HOUR: {
97 if (digitPos_ == 0) {
98 hour_ = d * 10;
99 digitPos_ = 1;
100 } else {
101 hour_ = (hour_ / 10) * 10 + d;
102 if (hour_ > 23) hour_ = 23;
103 nextField(); // Auto-advance to minute
104 }
105 break;
106 }
107 case Field::MINUTE: {
108 if (digitPos_ == 0) {
109 minute_ = d * 10;
110 digitPos_ = 1;
111 } else {
112 minute_ = (minute_ / 10) * 10 + d;
113 if (minute_ > 59) minute_ = 59;
114 digitPos_ = 0; // Stay in minute field after completion
115 }
116 break;
117 }
118 }
119
120 dirty_ = true;
121}
122
129 // Digit input (all 0-9 keys are digits, auto-advances between fields)
130 if (key >= '0' && key <= '9') {
131 enterDigit(key);
133 }
134
135 switch (key) {
136 case KEY_NO: // Clear or cancel
137 if (digitPos_ > 0 ||
138 (currentField_ == Field::HOUR && hour_ > 0) ||
139 (currentField_ == Field::MINUTE && minute_ > 0)) {
140 clearField();
142 }
144 if (onCancel_) {
145 onCancel_();
146 }
148
149 case KEY_YES: // Confirm
150 if (hour_ > 23) hour_ = 23;
151 if (minute_ > 59) minute_ = 59;
152 LOG_I(TAG, "Time confirmed: %02d:%02d", hour_, minute_);
154 if (onConfirm_) {
155 onConfirm_(hour_, minute_);
156 }
158
159 default:
161 }
162}
163
168const char* TimeInputView::getFooterHint() const {
169 return ui::tr("core.hint_time_input");
170}
171
177void TimeInputView::render(bool partial) {
179 if (!display) return;
180
181 auto* gfx = static_cast<Gdey029T94*>(display->getNativeHandle());
182 if (!gfx) return;
183
184 const uint16_t width = display->getWidth();
185 const uint16_t height = display->getHeight();
186
187 if (!partial) {
188 gfx->fillScreen(EPD_WHITE);
189 }
190
191 gfx->setTextColor(EPD_BLACK);
192
193 if (title_) {
194 gfx->setTextSize(1);
195 render::drawHeaderCentered(gfx, title_, TITLE_Y, width);
196 }
197
198 char timeStr[12];
199 snprintf(timeStr, sizeof(timeStr), "%02d : %02d", hour_, minute_);
200
201 gfx->setTextSize(3);
202 int16_t x1, y1;
203 uint16_t w, h;
204 gfx->getTextBounds(timeStr, 0, 0, &x1, &y1, &w, &h);
205 int startX = (width - w) / 2;
206 gfx->setCursor(startX, TIME_Y);
207 gfx->print(timeStr);
208
209 gfx->fillRect(0, UNDERLINE_Y, width, 4, EPD_WHITE);
210
211 int charWidth = 18;
212 int underlineX = startX;
213 int underlineW = 2 * charWidth;
214
215 switch (currentField_) {
216 case Field::HOUR:
217 underlineX = startX;
218 break;
219 case Field::MINUTE:
220 underlineX = startX + 5 * charWidth;
221 break;
222 }
223
224 gfx->fillRect(underlineX, UNDERLINE_Y, underlineW, 3, EPD_BLACK);
225
226 const char* hint = getFooterHint();
227 render::drawFooterBar(gfx, width, height, nullptr, hint, false);
228
229 dirty_ = false;
230}
231
232} // 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 TITLE_Y
Display layout constants.
static constexpr int TIME_Y
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 time editor.
void init(const char *title, uint8_t hour, uint8_t minute)
Initializes time input state.
const char * getFooterHint() const override
Returns localized footer hint text.
void render(bool partial) override
Renders the time input view.
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