mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-07-26 14:58:12 +00:00
fix: adaptive layout for e-ink landscape across all remaining screens
- GxEPDDisplay: sz2 uses 4× scale on landscape (clock 32px vs body 16px) - UITask: clock date_y and splash date_y computed from lh2 after setTextSize(2) - UITask: battery/mute/BT/advert indicators scale with lh; muted uses text 'M' - UITask: hibernate hint split into two lines to prevent wrapping - ToolsScreen: full refactor — headerH, listStart, lineStep, getCharWidth - KeyboardWidget: all cell sizes computed from display metrics (cell_w, cell_h, spec_w) - RingtoneEditorScreen: cell_w = charWidth×2+6 (was hardcoded 18) - SettingsScreen: BRIGHTNESS hidden on e-ink; renderBar boxes scale with lh Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -15,12 +15,6 @@ static const int KB_ROWS_CHAR = 4;
|
||||
static const int KB_COLS_CHAR = 10;
|
||||
static const int KB_SPECIAL = 5; // [^] [Sp] [De] [{}] [OK]
|
||||
static const int KB_MAX_LEN = 139;
|
||||
static const int KB_CELL_W = 12; // 10 cols × 12px = 120px of 128px display
|
||||
static const int KB_CELL_H = 9;
|
||||
static const int KB_TEXT_Y = 0;
|
||||
static const int KB_SEP_Y = 9;
|
||||
static const int KB_CHARS_Y = 11;
|
||||
static const int KB_SPECIAL_Y = 48;
|
||||
|
||||
static const int KB_PH_MAX = 12; // max placeholders in list
|
||||
static const int KB_PH_LEN = 9; // max placeholder string length incl. null
|
||||
@@ -64,33 +58,44 @@ struct KeyboardWidget {
|
||||
display.setTextSize(1);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
// text preview: last 20 chars + cursor
|
||||
const int lh = display.getLineHeight();
|
||||
const int cw = display.getCharWidth();
|
||||
const int cell_w = display.width() / KB_COLS_CHAR;
|
||||
const int cell_h = lh + 1;
|
||||
const int sep_y = lh + 1;
|
||||
const int chars_y = sep_y + 2;
|
||||
const int spec_y = chars_y + KB_ROWS_CHAR * cell_h;
|
||||
const int spec_w = display.width() / KB_SPECIAL;
|
||||
|
||||
// text preview: last N chars + cursor (fit within screen width)
|
||||
int max_preview = display.width() / cw - 1;
|
||||
if (max_preview > 30) max_preview = 30;
|
||||
const char* disp_start = buf;
|
||||
int disp_len = len;
|
||||
if (disp_len > 20) { disp_start = buf + (disp_len - 20); disp_len = 20; }
|
||||
char preview[24];
|
||||
if (disp_len > max_preview) { disp_start = buf + (disp_len - max_preview); disp_len = max_preview; }
|
||||
char preview[32];
|
||||
snprintf(preview, sizeof(preview), "%.*s_", disp_len, disp_start);
|
||||
display.setCursor(0, KB_TEXT_Y);
|
||||
display.setCursor(0, 0);
|
||||
display.print(preview);
|
||||
display.fillRect(0, KB_SEP_Y, display.width(), 1);
|
||||
display.fillRect(0, sep_y, display.width(), 1);
|
||||
|
||||
// character grid
|
||||
for (int r = 0; r < KB_ROWS_CHAR; r++) {
|
||||
int y = KB_CHARS_Y + r * KB_CELL_H;
|
||||
int y = chars_y + r * cell_h;
|
||||
for (int c = 0; c < KB_COLS_CHAR; c++) {
|
||||
bool sel = (row == r && col == c);
|
||||
char ch = KB_CHARS[r][c];
|
||||
if (caps && ch >= 'a' && ch <= 'z') ch = ch - 'a' + 'A';
|
||||
char ch_buf[2] = { ch == ' ' ? '_' : ch, '\0' };
|
||||
int cx = c * KB_CELL_W;
|
||||
int cx = c * cell_w;
|
||||
if (sel) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(cx, y - 1, KB_CELL_W - 1, KB_CELL_H);
|
||||
display.fillRect(cx, y - 1, cell_w - 1, cell_h);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
} else {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
display.setCursor(cx + 3, y);
|
||||
display.setCursor(cx + (cell_w - cw) / 2, y);
|
||||
display.print(ch_buf);
|
||||
}
|
||||
}
|
||||
@@ -100,15 +105,16 @@ struct KeyboardWidget {
|
||||
for (int i = 0; i < KB_SPECIAL; i++) {
|
||||
bool sel = (row == KB_ROWS_CHAR && col == i);
|
||||
bool active = (i == 0 && caps);
|
||||
int sx = i * 25;
|
||||
int sx = i * spec_w;
|
||||
if (sel || active) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(sx, KB_SPECIAL_Y - 1, 24, KB_CELL_H);
|
||||
display.fillRect(sx, spec_y - 1, spec_w - 1, cell_h);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
} else {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
display.setCursor(sx + 1, KB_SPECIAL_Y);
|
||||
int tw = display.getTextWidth(spec[i]);
|
||||
display.setCursor(sx + (spec_w - tw) / 2, spec_y);
|
||||
display.print(spec[i]);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ class RingtoneEditorScreen : public UIScreen {
|
||||
NodePrefs* _prefs;
|
||||
|
||||
static const int MAX_NOTES = 32;
|
||||
static const int CELL_W = 18;
|
||||
static const int MENU_VISIBLE = 5;
|
||||
|
||||
int _visible_notes = 7; // updated in render(); used by clampScroll()
|
||||
@@ -93,9 +92,11 @@ public:
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
const int lh = display.getLineHeight();
|
||||
const int cw = display.getCharWidth();
|
||||
const int cell_w = cw * 2 + 6; // fits 2-char label with margin
|
||||
const int notes_y = display.listStart();
|
||||
const int cell_h = lh + 6;
|
||||
_visible_notes = display.width() / CELL_W;
|
||||
_visible_notes = display.width() / cell_w;
|
||||
|
||||
char hdr[32];
|
||||
snprintf(hdr, sizeof(hdr), "M%d BPM:%u %d/%d", _slot + 1, BPM_OPTS[_bpm_idx], _len, MAX_NOTES);
|
||||
@@ -105,7 +106,7 @@ public:
|
||||
|
||||
for (int i = 0; i < _visible_notes; i++) {
|
||||
int ni = _scroll + i;
|
||||
int cx = i * CELL_W;
|
||||
int cx = i * cell_w;
|
||||
bool sel = (ni == _cursor);
|
||||
if (ni < _len) {
|
||||
uint8_t pitch = notePitch(_notes[ni]);
|
||||
@@ -120,24 +121,24 @@ public:
|
||||
}
|
||||
if (sel) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(cx, notes_y, CELL_W - 1, cell_h);
|
||||
display.fillRect(cx, notes_y, cell_w - 1, cell_h);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
} else {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawRect(cx, notes_y, CELL_W - 1, cell_h);
|
||||
display.drawRect(cx, notes_y, cell_w - 1, cell_h);
|
||||
}
|
||||
display.setCursor(cx + 3, notes_y + 3);
|
||||
display.setCursor(cx + (cell_w - cw * 2) / 2, notes_y + 3);
|
||||
display.print(label);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
} else if (ni == _len && _len < MAX_NOTES) {
|
||||
if (sel) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(cx, notes_y, CELL_W - 1, cell_h);
|
||||
display.fillRect(cx, notes_y, cell_w - 1, cell_h);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
} else {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
display.setCursor(cx + 6, notes_y + 3);
|
||||
display.setCursor(cx + (cell_w - cw) / 2, notes_y + 3);
|
||||
display.print("+");
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
|
||||
@@ -8,7 +8,9 @@ class SettingsScreen : public UIScreen {
|
||||
enum SettingItem {
|
||||
// Display section
|
||||
SECTION_DISPLAY,
|
||||
#if !defined(EINK_DISPLAY_MODEL)
|
||||
BRIGHTNESS,
|
||||
#endif
|
||||
AUTO_OFF,
|
||||
AUTO_LOCK,
|
||||
BATT_DISPLAY,
|
||||
@@ -85,7 +87,9 @@ class SettingsScreen : public UIScreen {
|
||||
}
|
||||
|
||||
void renderBar(DisplayDriver& display, int x, int y, int value, int max_val) {
|
||||
const int box_w = 7, box_h = 7, gap = 2;
|
||||
const int box_h = display.getLineHeight() - 2;
|
||||
const int box_w = box_h;
|
||||
const int gap = 2;
|
||||
for (int i = 0; i < max_val; i++) {
|
||||
int bx = x + i * (box_w + gap);
|
||||
display.drawRect(bx, y, box_w, box_h);
|
||||
@@ -236,10 +240,13 @@ class SettingsScreen : public UIScreen {
|
||||
display.print(sel ? ">" : " ");
|
||||
display.setCursor(display.getCharWidth() + 2, y);
|
||||
|
||||
#if !defined(EINK_DISPLAY_MODEL)
|
||||
if (item == BRIGHTNESS) {
|
||||
display.print("Bright");
|
||||
renderBar(display, display.valCol(), y, (p ? p->display_brightness : 2) + 1, 5);
|
||||
} else if (item == BUZZER) {
|
||||
} else
|
||||
#endif
|
||||
if (item == BUZZER) {
|
||||
display.print("Buzzer");
|
||||
display.setCursor(display.valCol(), y);
|
||||
#ifdef PIN_BUZZER
|
||||
@@ -357,7 +364,7 @@ class SettingsScreen : public UIScreen {
|
||||
|
||||
public:
|
||||
SettingsScreen(UITask* task)
|
||||
: _task(task), _selected(BRIGHTNESS), _scroll(0), _visible(4), _dirty(false), _edit_slot(-1) {}
|
||||
: _task(task), _selected(AUTO_OFF), _scroll(0), _visible(4), _dirty(false), _edit_slot(-1) {}
|
||||
|
||||
|
||||
void markClean() { _dirty = false; }
|
||||
@@ -441,12 +448,14 @@ public:
|
||||
bool left = (c == KEY_LEFT || c == KEY_PREV);
|
||||
bool enter = (c == KEY_ENTER);
|
||||
|
||||
#if !defined(EINK_DISPLAY_MODEL)
|
||||
if (_selected == BRIGHTNESS) {
|
||||
uint8_t lvl = _task->getBrightnessLevel();
|
||||
if (right && lvl < 4) { _task->setBrightnessLevel(lvl + 1); _dirty = true; return true; }
|
||||
if (left && lvl > 0) { _task->setBrightnessLevel(lvl - 1); _dirty = true; return true; }
|
||||
return right || left;
|
||||
}
|
||||
#endif
|
||||
if (_selected == BUZZER && (left || right || enter)) {
|
||||
_task->cycleBuzzerMode();
|
||||
_dirty = true;
|
||||
|
||||
@@ -16,21 +16,25 @@ public:
|
||||
display.setTextSize(1);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawTextCentered(display.width() / 2, 0, "TOOLS");
|
||||
display.fillRect(0, 10, display.width(), 1);
|
||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
||||
|
||||
int item_h = display.lineStep();
|
||||
int start_y = display.listStart();
|
||||
int cw = display.getCharWidth();
|
||||
|
||||
for (int i = 0; i < ITEM_COUNT; i++) {
|
||||
int y = 12 + i * 12;
|
||||
int y = start_y + i * item_h;
|
||||
bool sel = (i == _sel);
|
||||
if (sel) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(0, y - 1, display.width(), 11);
|
||||
display.fillRect(0, y - 1, display.width(), item_h);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
} else {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
display.setCursor(0, y);
|
||||
display.print(sel ? ">" : " ");
|
||||
display.setCursor(8, y);
|
||||
display.setCursor(cw + 2, y);
|
||||
display.print(ITEMS[i]);
|
||||
}
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
@@ -78,10 +78,11 @@ public:
|
||||
// version info at sz2
|
||||
int ver_y = logo_y + 13 + 2;
|
||||
display.setTextSize(2);
|
||||
int lh2 = display.getLineHeight();
|
||||
display.drawTextCentered(display.width()/2, ver_y, _version_info);
|
||||
|
||||
// build date at sz1 — sz2 lh is always 16
|
||||
int date_y = ver_y + 16 + 2;
|
||||
// build date at sz1, below sz2 version
|
||||
int date_y = ver_y + lh2 + 2;
|
||||
display.setTextSize(1);
|
||||
display.drawTextCentered(display.width()/2, date_y, FIRMWARE_BUILD_DATE);
|
||||
|
||||
@@ -239,6 +240,10 @@ class HomeScreen : public UIScreen {
|
||||
display.setTextSize(1);
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
|
||||
const int lh = display.getLineHeight();
|
||||
const int cw = display.getCharWidth();
|
||||
const int ind = cw + 2; // single-char indicator width
|
||||
|
||||
int battLeftX;
|
||||
if (mode == 1) { // percent
|
||||
char buf[6];
|
||||
@@ -252,52 +257,54 @@ class HomeScreen : public UIScreen {
|
||||
battLeftX = display.width() - display.getTextWidth(buf) - 1;
|
||||
display.setCursor(battLeftX, 0);
|
||||
display.print(buf);
|
||||
} else { // icon
|
||||
const int iconWidth = 24, iconHeight = 8;
|
||||
battLeftX = display.width() - iconWidth - 5;
|
||||
display.drawRect(battLeftX, 0, iconWidth, iconHeight);
|
||||
display.fillRect(battLeftX + iconWidth, iconHeight / 4, 3, iconHeight / 2);
|
||||
int fillWidth = (pct * (iconWidth - 4)) / 100;
|
||||
display.fillRect(battLeftX + 2, 2, fillWidth, iconHeight - 4);
|
||||
} else { // icon — scales with lh
|
||||
const int iconH = lh;
|
||||
const int iconW = iconH * 3;
|
||||
battLeftX = display.width() - iconW - 3;
|
||||
display.drawRect(battLeftX, 0, iconW, iconH);
|
||||
display.fillRect(battLeftX + iconW, iconH / 4, 2, iconH / 2);
|
||||
int fillW = (pct * (iconW - 4)) / 100;
|
||||
display.fillRect(battLeftX + 2, 2, fillW, iconH - 4);
|
||||
}
|
||||
|
||||
#ifdef PIN_BUZZER
|
||||
if (_task->isBuzzerQuiet()) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.drawXbm(battLeftX - 9, 0, muted_icon, 8, 8);
|
||||
int mx = battLeftX - ind - 1;
|
||||
display.fillRect(mx, 0, ind, lh);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
display.setCursor(mx + 1, 0);
|
||||
display.print("M");
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
battLeftX = mx;
|
||||
}
|
||||
#endif
|
||||
|
||||
// BT connection indicator (left of muted/battery icons)
|
||||
int leftmostX = battLeftX;
|
||||
if (_task->isSerialEnabled()) {
|
||||
#ifdef PIN_BUZZER
|
||||
int btX = battLeftX - 18;
|
||||
#else
|
||||
int btX = battLeftX - 9;
|
||||
#endif
|
||||
int btX = battLeftX - ind - 1;
|
||||
if (_task->hasConnection()) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(btX - 1, 0, 7, 7);
|
||||
display.fillRect(btX, 0, ind, lh);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
display.setCursor(btX, 0);
|
||||
display.setCursor(btX + 1, 0);
|
||||
display.print("B");
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
} else {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.setCursor(btX, 0);
|
||||
display.setCursor(btX + 1, 0);
|
||||
display.print("b");
|
||||
}
|
||||
leftmostX = btX - 1;
|
||||
|
||||
// "A" indicator — left of BT, blinks 50% duty at 1s period
|
||||
// "A" indicator — left of BT, blinks 50% duty at 4s period
|
||||
if (_node_prefs && _node_prefs->advert_auto_interval_sec > 0) {
|
||||
int aX = leftmostX - 8;
|
||||
int aX = leftmostX - ind;
|
||||
if ((millis() % 4000) < 2000) {
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(aX - 1, 0, 7, 7);
|
||||
display.fillRect(aX, 0, ind, lh);
|
||||
display.setColor(DisplayDriver::DARK);
|
||||
display.setCursor(aX, 0);
|
||||
display.setCursor(aX + 1, 0);
|
||||
display.print("A");
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
@@ -413,15 +420,17 @@ public:
|
||||
if (show_sec) sprintf(buf, "%02d:%02d:%02d", ti->tm_hour, ti->tm_min, ti->tm_sec);
|
||||
else sprintf(buf, "%02d:%02d", ti->tm_hour, ti->tm_min);
|
||||
}
|
||||
int lh2 = display.getLineHeight(); // sz2 height: 16 (OLED) or 32 (landscape)
|
||||
display.drawTextCentered(display.width() / 2, 0, buf);
|
||||
|
||||
display.setTextSize(1);
|
||||
static const char* wd[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
|
||||
static const char* mo[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"};
|
||||
sprintf(buf, "%s %d %s %d", wd[ti->tm_wday], ti->tm_mday, mo[ti->tm_mon], 1900 + ti->tm_year);
|
||||
display.drawTextCentered(display.width() / 2, 19, buf);
|
||||
int date_y = lh2 + 2;
|
||||
display.drawTextCentered(display.width() / 2, date_y, buf);
|
||||
|
||||
int sep_y = 19 + lh + 1;
|
||||
int sep_y = date_y + lh + 1;
|
||||
int dash0 = sep_y + 3;
|
||||
display.fillRect(0, sep_y, display.width(), 1);
|
||||
|
||||
@@ -706,7 +715,9 @@ public:
|
||||
display.drawTextCentered(display.width() / 2, content_y + step, "hibernating...");
|
||||
} else {
|
||||
display.drawXbm((display.width() - 32) / 2, content_y, power_icon, 32, 32);
|
||||
display.drawTextCentered(display.width() / 2, content_y + 32 + 3, "hibernate:" PRESS_LABEL);
|
||||
int hint_base = content_y + 32 + 3;
|
||||
display.drawTextCentered(display.width() / 2, hint_base, "hibernate:");
|
||||
display.drawTextCentered(display.width() / 2, hint_base + step, PRESS_LABEL);
|
||||
}
|
||||
}
|
||||
bool auto_adv = _node_prefs && _node_prefs->advert_auto_interval_sec > 0;
|
||||
|
||||
Reference in New Issue
Block a user