16#include <goodisplay/gdey029T94.h>
25constexpr int HEADER_HEIGHT_DEFAULT = 30;
29 case '0':
return " 0";
30 case '1':
return ".?!,;:'\"()-_@#$%&*+=/\\<>[]{}|^~`1";
31 case '2':
return "abc2";
32 case '3':
return "def3";
33 case '4':
return "ghi4";
34 case '5':
return "jkl5";
35 case '6':
return "mno6";
36 case '7':
return "pqrs7";
37 case '8':
return "tuv8";
38 case '9':
return "wxyz9";
39 default:
return nullptr;
44 return static_cast<uint32_t
>(esp_timer_get_time() / 1000);
49Gdey029T94* CanvasView::gfx()
const {
52 return static_cast<Gdey029T94*
>(
display->getNativeHandle());
55int CanvasView::bodyBottom()
const {
65 textInverted_ =
false;
68 longPressCb_ =
nullptr;
70 headerHeight_ = (title && title[0] !=
'\0') ? HEADER_HEIGHT_DEFAULT : 0;
71 needsFullRefresh_ =
true;
72 keyRepeatInitialMs_ = 0;
73 keyRepeatPeriodMs_ = 0;
74 headerDrawnOnce_ =
false;
78 overflowLogged_ =
false;
89 keyRepeatInitialMs_ = initial_ms;
90 keyRepeatPeriodMs_ = repeat_ms;
101 if (w) *w =
display->getWidth();
102 if (h) *h =
static_cast<uint16_t
>(bodyBottom() - bodyTop());
110uint16_t CanvasView::internText(
const char* text, uint16_t* outLen) {
111 size_t len = text ? strlen(text) : 0;
112 uint16_t off = textArenaUsed_;
116 if (!overflowLogged_) {
117 LOG_W(
"CanvasView",
"draw text arena full, truncating");
118 overflowLogged_ =
true;
121 if (len) memcpy(&textArena_[off], text, len);
122 textArena_[off + len] =
'\0';
123 textArenaUsed_ =
static_cast<uint16_t
>(off + len + 1);
124 *outLen =
static_cast<uint16_t
>(len);
129 if (!text || cmdCount_ >=
MAX_CMDS) {
130 if (text && !overflowLogged_) {
131 LOG_W(
"CanvasView",
"display list full, dropping draw");
132 overflowLogged_ =
true;
137 c.type = CmdType::Text;
141 c.textSize = textSize_;
142 c.inverted = textInverted_;
143 c.strOff = internText(text, &c.strLen);
144 cmds_[cmdCount_++] = c;
148 const char* text, uint8_t align) {
149 if (!text || cmdCount_ >=
MAX_CMDS) {
150 if (text && !overflowLogged_) {
151 LOG_W(
"CanvasView",
"display list full, dropping draw");
152 overflowLogged_ =
true;
157 c.type = CmdType::TextAligned;
163 c.textSize = textSize_;
164 c.inverted = textInverted_;
165 c.strOff = internText(text, &c.strLen);
166 cmds_[cmdCount_++] = c;
172 c.type = CmdType::Rect;
178 cmds_[cmdCount_++] = c;
184 (void)x; (void)y; (void)w; (void)h;
190 c.type = CmdType::HLine;
194 cmds_[cmdCount_++] = c;
200 c.type = CmdType::VLine;
204 cmds_[cmdCount_++] = c;
207void CanvasView::paintText(int16_t x, int16_t y, int16_t w,
const char* text,
208 uint8_t align, uint8_t fontId, uint8_t textSize,
211 if (!g || !text)
return;
214 g->setTextSize(textSize);
215 g->setTextColor(inverted ? EPD_WHITE : EPD_BLACK);
216 g->setTextWrap(
false);
219 if (align == 1 || align == 2) {
224 draw_x = x + (w -
static_cast<int16_t
>(bw)) / 2;
226 draw_x = x + w -
static_cast<int16_t
>(bw);
229 g->setCursor(draw_x, y + bodyTop());
233void CanvasView::replayDisplayList() {
236 for (uint16_t i = 0; i < cmdCount_; ++i) {
237 const DrawCmd& c = cmds_[i];
240 paintText(c.x, c.y, 0, &textArena_[c.strOff], 0,
241 c.fontId, c.textSize, c.inverted);
243 case CmdType::TextAligned:
244 paintText(c.x, c.y, c.w, &textArena_[c.strOff], c.align,
245 c.fontId, c.textSize, c.inverted);
247 case CmdType::Rect: {
248 int16_t yy = c.y + bodyTop();
250 g->fillRect(c.x, yy, c.w, c.h, EPD_BLACK);
252 g->drawRect(c.x, yy, c.w, c.h, EPD_BLACK);
257 g->drawFastHLine(c.x, c.y + bodyTop(), c.w, EPD_BLACK);
260 g->drawFastVLine(c.x, c.y + bodyTop(), c.h, EPD_BLACK);
268 needsFullRefresh_ =
true;
273CanvasView::Widget* CanvasView::findWidget(uint32_t
id) {
274 if (
id == 0)
return nullptr;
275 for (uint8_t i = 0; i < widgetCount_; ++i) {
276 if (widgets_[i].
id ==
id)
return &widgets_[i];
281const CanvasView::Widget* CanvasView::findWidget(uint32_t
id)
const {
282 if (
id == 0)
return nullptr;
283 for (uint8_t i = 0; i < widgetCount_; ++i) {
284 if (widgets_[i].
id ==
id)
return &widgets_[i];
289CanvasView::Widget* CanvasView::focusedWidget() {
290 return findWidget(focused_);
294 int32_t initial, int32_t step) {
295 if (
id == 0 || widgetCount_ >=
MAX_WIDGETS || findWidget(
id)) {
298 Widget& w = widgets_[widgetCount_++];
304 w.step = step > 0 ? step : 1;
305 w.value = std::clamp(initial, min, max);
310 if (
id == 0 || widgetCount_ >=
MAX_WIDGETS || findWidget(
id)) {
313 Widget& w = widgets_[widgetCount_++];
319 size_t n = std::min(strlen(initial),
static_cast<size_t>(w.max_len));
320 memcpy(w.text, initial, n);
322 w.text_len =
static_cast<uint16_t
>(n);
328 if (
id == 0 || widgetCount_ >=
MAX_WIDGETS || findWidget(
id)) {
331 Widget& w = widgets_[widgetCount_++];
339 for (uint8_t i = 0; i < widgetCount_; ++i) {
340 if (widgets_[i].
id ==
id) {
341 for (uint8_t j = i + 1; j < widgetCount_; ++j) {
342 widgets_[j - 1] = widgets_[j];
345 widgets_[widgetCount_] = Widget{};
346 if (focused_ ==
id) focused_ = 0;
354 Widget* w = findWidget(
id);
356 w->value = std::clamp(value, w->min, w->max);
361 const Widget* w = findWidget(
id);
362 if (!w || !out)
return false;
368 Widget* w = findWidget(
id);
370 size_t n = std::min(text ? strlen(text) :
size_t{0},
371 static_cast<size_t>(w->max_len));
372 if (text) memcpy(w->text, text, n);
374 w->text_len =
static_cast<uint16_t
>(n);
376 w->t9_press_count = 0;
381 const Widget* w = findWidget(
id);
382 if (!w || !out || cap == 0)
return -1;
383 size_t n = std::min(
static_cast<size_t>(w->text_len), cap - 1);
384 memcpy(out, w->text, n);
386 return static_cast<int>(n);
394 Widget* w = findWidget(
id);
395 if (!w)
return false;
400void CanvasView::t9_commit_pending(Widget& w) {
402 w.t9_press_count = 0;
405void CanvasView::t9_apply_key(Widget& w,
char key, uint32_t now) {
408 size_t table_len = strlen(table);
409 if (table_len == 0)
return;
411 bool same_key = (w.t9_last_key == key)
416 w.t9_press_count = (w.t9_press_count + 1) %
static_cast<uint8_t
>(table_len);
417 w.text[w.text_len - 1] = table[w.t9_press_count];
419 if (w.text_len >= w.max_len) {
422 w.text[w.text_len++] = table[0];
423 w.text[w.text_len] =
'\0';
424 w.t9_press_count = 0;
427 w.t9_last_time = now;
430InputResult CanvasView::dispatchKeyToWidget(Widget& w,
char key) {
431 uint32_t now = nowMs();
434 if (key ==
'4' || key ==
KEY_UP) {
435 int32_t nv = std::clamp<int32_t>(w.value - w.step, w.min, w.max);
442 if (key ==
'6' || key ==
KEY_DOWN) {
443 int32_t nv = std::clamp<int32_t>(w.value + w.step, w.min, w.max);
461 if (key >=
'0' && key <=
'9') {
462 t9_apply_key(w, key, now);
467 t9_commit_pending(w);
468 if (w.text_len > 0) {
470 w.text[w.text_len] =
'\0';
476 t9_commit_pending(w);
477 if (w.text_len < w.max_len) {
478 w.text[w.text_len++] =
' ';
479 w.text[w.text_len] =
'\0';
485 t9_commit_pending(w);
490 if (w.text_len > 0 && w.t9_last_key != 0
492 t9_commit_pending(w);
495 if (w.text_len > 0) {
497 w.text[w.text_len] =
'\0';
523 Widget* focused = focusedWidget();
525 InputResult r = dispatchKeyToWidget(*focused, key);
532 keyCb_(key, focused_);
545void CanvasView::applyKeypadConfig() {
548 kp->setKeyRepeat(keyRepeatInitialMs_, keyRepeatPeriodMs_);
578 kp->setKeyRepeat(0, 0);
589 const uint16_t width =
display->getWidth();
590 const uint16_t height =
display->getHeight();
592 if (!partial || needsFullRefresh_) {
593 g->fillScreen(EPD_WHITE);
594 needsFullRefresh_ =
false;
595 headerDrawnOnce_ =
false;
599 g->setTextColor(EPD_BLACK);
601 g->setTextWrap(
false);
603 headerDrawnOnce_ =
true;
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
static constexpr uint32_t DEFER_SRC_VIEW
active view (e.g. canvas long-press)
void render(bool partial) override
void getBodySize(uint16_t *w, uint16_t *h) const
bool removeWidget(uint32_t id)
InputResult onLongPress(char key) override
int getText(uint32_t id, char *out, size_t cap) const
void drawText(int16_t x, int16_t y, const char *text)
void(*)(char key) LongPressCallback
void drawHLine(int16_t x, int16_t y, int16_t w)
void init(const char *title)
InputResult onKey(char key) override
void commit(bool full_refresh)
static constexpr uint16_t MAX_CMDS
void setLongPressCallback(LongPressCallback cb)
void onEnter(void *context) override
static constexpr uint8_t MAX_WIDGETS
bool setFocus(uint32_t id)
bool setValue(uint32_t id, int32_t value)
bool addText(uint32_t id, uint16_t max_len, const char *initial)
bool getValue(uint32_t id, int32_t *out) const
bool addButton(uint32_t id)
bool setText(uint32_t id, const char *text)
static constexpr uint16_t T9_SETTLE_MS
static constexpr uint16_t MAX_TEXT_LEN
void invertRect(int16_t x, int16_t y, int16_t w, int16_t h)
void setKeyRepeat(uint16_t initial_ms, uint16_t repeat_ms)
bool addSlider(uint32_t id, int32_t min, int32_t max, int32_t initial, int32_t step)
void setFooter(const char *hint)
static constexpr uint16_t TEXT_ARENA
void drawTextAligned(int16_t x, int16_t y, int16_t w, const char *text, uint8_t align)
void drawRect(int16_t x, int16_t y, int16_t w, int16_t h, bool filled)
void drawVLine(int16_t x, int16_t y, int16_t h)
void markDirty() override
void onEnter(void *context) override
const char * customFooter_
IDisplay * getDisplayInstance()
Returns lazily created singleton display instance.
IKeypad * getKeypadInstance()
Returns the singleton keypad service instance.
constexpr int FOOTER_HEIGHT
Footer bar height in pixels (used by drawFooterBar).
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 drawText(Gdey029T94 *gfx, const char *text, const GFXfont *font)
Draws CP437-encoded text correctly for the given font: the built-in glcdfont (font == nullptr) is CP4...
void drawHeaderLeft(Gdey029T94 *gfx, const char *title, int x, int y, uint16_t width, int underlineOffset=18)
void measureText(Gdey029T94 *gfx, const char *text, const GFXfont *font, int16_t x0, int16_t y0, int16_t *x1, int16_t *y1, uint16_t *w, uint16_t *h)
Measures CP437 text exactly as drawText would render it with font, so width-based layout (centering,...
Centralized key-code constants for cdc_views.
const GFXfont * getGfxFont(FontId id)
Resolves a FontId to its underlying GFX font pointer.
static constexpr char KEY_DOWN
Move selection down (numeric '8').
static constexpr char KEY_NO
Cancel / Back / Backspace.
static constexpr int TITLE_Y
Layout constants mirror the ones used by T9InputView.
static constexpr char KEY_UP
Move selection up (numeric '2').
static constexpr char KEY_YES
Confirm / OK / Save.