mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-02 19:02:00 +00:00
feat(l1): L2 splash, hovering Home icons, soft selections, two-line DM bubbles
- Splash: the L2 wordmark and lettering, shared through ui-core/Lettering.h, rise in with loading dots under the version line (static frame on e-ink). - Home: the header shows the time once the clock is set; icon pages carry only their title. One 32 px icon family (3 px strokes) bobs 2 px over a dithered blob shadow that shrinks as it rises; the unread badge rides on the Messages bubble; Bluetooth off is the icon at half density. - Soft corners on every selection, the page pill, tabs and popups; dotted scrollbar track with a rounded thumb; the tab bar lays out whole tabs and shows edge arrows only in free space. - DM bubbles drop the sender row and wrap to a second line, with the delivery mark and age closing it. - Keyboards name what is typed (channel, contact, Password, Scope...) and show characters left near the limit; PIN entry has its own preview. - Loading dots for waits (login, scan, GPS) and signal bars in place of raw RSSI in Nearby. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -271,7 +271,10 @@ struct KeyboardWidget {
|
||||
// The number pad (beginPin) instead of the user's ABC / T9 layout; its ABC
|
||||
// key drops back to that layout for a password with letters.
|
||||
bool pin_mode = false;
|
||||
const char* pin_prompt = nullptr; // e.g. "New PIN", shown in the preview; kept past the ABC switch
|
||||
// What is being typed ("New PIN", "Password", a channel name...), shown at
|
||||
// the right of the preview; set it after begin(), which clears it. Kept past
|
||||
// the PIN pad's ABC switch.
|
||||
const char* prompt = nullptr;
|
||||
static const int PIN_MAX_LEN = 16;
|
||||
int row, col;
|
||||
int page; // see totalPages()/scriptAt()/pageIsSymbols() below
|
||||
@@ -416,7 +419,7 @@ struct KeyboardWidget {
|
||||
caps_lock = false;
|
||||
pin_kb_mask_enabled = false;
|
||||
pin_mode = false;
|
||||
pin_prompt = nullptr;
|
||||
prompt = nullptr;
|
||||
t9_cell = -1;
|
||||
t9_cycle = 0;
|
||||
_ph_menu.active = false;
|
||||
@@ -430,10 +433,10 @@ struct KeyboardWidget {
|
||||
}
|
||||
|
||||
// Open keyboard in PIN mode
|
||||
void beginPin(const char* initial = "", int max = PIN_MAX_LEN, bool mask = false, const char* prompt = nullptr) {
|
||||
void beginPin(const char* initial = "", int max = PIN_MAX_LEN, bool mask = false, const char* label = nullptr) {
|
||||
begin(initial, max);
|
||||
pin_mode = true;
|
||||
pin_prompt = prompt;
|
||||
prompt = label;
|
||||
pin_kb_mask_enabled = mask; // Mask input of number field
|
||||
}
|
||||
|
||||
@@ -518,6 +521,29 @@ struct KeyboardWidget {
|
||||
else if (key == KEY_DOWN) { cursor_pos = len; }
|
||||
}
|
||||
|
||||
// PIN preview: what it's for, centred, and the digits below it spaced out
|
||||
// (filled soft squares when masked), with an underline on the next slot.
|
||||
void renderPinPreview(DisplayDriver& display, int sep_y) {
|
||||
const int lh = display.getLineHeight(), cw = display.getCharWidth();
|
||||
const int pitch = cw + 3;
|
||||
const int y = sep_y - lh; // digits on the last preview line
|
||||
int title_y = y - lh - 1;
|
||||
if (title_y < 0) title_y = 0;
|
||||
if (prompt && title_y + lh <= y) display.drawTextCentered(display.width() / 2, title_y, prompt);
|
||||
const int slots = len + (len < max_len ? 1 : 0);
|
||||
int x = display.width() / 2 - (slots * pitch - 3) / 2;
|
||||
for (int i = 0; i < slots; i++, x += pitch) {
|
||||
if (i == len) { display.fillRect(x, y + lh - 2, cw, 1); continue; } // next digit goes here
|
||||
if (pin_kb_mask_enabled) {
|
||||
display.fillSoftRect(x, y + 1, cw, lh - 3);
|
||||
} else {
|
||||
char d[2] = { buf[i], 0 };
|
||||
display.setCursor(x, y);
|
||||
display.print(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int render(DisplayDriver& display) {
|
||||
_visible = true;
|
||||
// A stale mid-cycle T9 press (no further input since) finalizes on its own —
|
||||
@@ -576,6 +602,9 @@ struct KeyboardWidget {
|
||||
// ...and the byte offset that line starts at.
|
||||
int ps = 0;
|
||||
for (int n = first_line * cpl; n > 0 && ps < len; n--) ps += kbUtf8CharBytesAt(buf, ps, len);
|
||||
if (isPin()) {
|
||||
renderPinPreview(display, sep_y);
|
||||
} else
|
||||
for (int pl = 0; pl < prev_lines; pl++) {
|
||||
int pe = ps; // byte offset cpl codepoints further along (or end of text)
|
||||
for (int k = 0; k < cpl && pe < len; k++) pe += kbUtf8CharBytesAt(buf, pe, len);
|
||||
@@ -622,9 +651,27 @@ struct KeyboardWidget {
|
||||
display.print(linebuf_t);
|
||||
ps = pe;
|
||||
}
|
||||
if (pin_prompt) { // what the PIN is for, bottom-right of the preview (alerts don't show over the keyboard)
|
||||
display.setCursor(display.width() - display.getTextWidth(pin_prompt), (prev_lines - 1) * lh);
|
||||
display.print(pin_prompt);
|
||||
if (!isPin()) {
|
||||
// Bottom-right of the preview: what the field is, or near the limit how
|
||||
// much room is left. Only when it clears the text on that line.
|
||||
char left[12];
|
||||
const char* tag = prompt;
|
||||
if (max_len > 0 && len * 4 >= max_len * 3) { snprintf(left, sizeof(left), "%d left", max_len - len); tag = left; }
|
||||
if (tag) {
|
||||
int tw = display.getTextWidth(tag);
|
||||
// Codepoints on the last preview line, plus the cursor if it's there.
|
||||
int total_chars = 0;
|
||||
for (int p = 0; p < len; ) { p += kbUtf8CharBytesAt(buf, p, len); total_chars++; }
|
||||
const int last_line = first_line + prev_lines - 1;
|
||||
int last_chars = total_chars - last_line * cpl;
|
||||
if (last_chars < 0) last_chars = 0;
|
||||
if (last_chars > cpl) last_chars = cpl;
|
||||
if (cursor_line == last_line) last_chars++;
|
||||
if (last_chars * cw + tw + cw <= display.width()) {
|
||||
display.setCursor(display.width() - tw, (prev_lines - 1) * lh);
|
||||
display.print(tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
display.fillRect(0, sep_y, display.width(), display.sepH());
|
||||
|
||||
@@ -808,7 +855,7 @@ struct KeyboardWidget {
|
||||
int x = bx + i * seg_w;
|
||||
if (i == accent_sel) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(x + 1, by + 1, seg_w - 1, bh - 2);
|
||||
display.fillSoftRect(x + 1, by + 1, seg_w - 1, bh - 2);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
} else {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
Reference in New Issue
Block a user