mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-08-03 10:46:12 +00:00
fix: e-ink portrait/landscape layout — separators, battery, keyboard, screens
- DisplayDriver: add sepH() (2px landscape, 1px OLED); fix transliterateCodepoint fallback '?' (was '\xDB' which broke UTF-8 word-wrap and rendered tiny block char) - GxEPDDisplay: drawRect draws double border on landscape for visible 2px weight - UITask: navigation dots scale with lh; content_y saves 16px by using dots_y+6; battery iconW=lh*2, fill margin bm scales with orientation; indicator spacing fixed; clock separator uses sepH(); alert popup tight and vertically centred - KeyboardWidget: compact cell height (no stretch), multi-line preview, cursor always on last visible line - BotScreen / NearbyScreen detail: use lineStep() as natural item height, shrink only when items don't fit (fixes portrait stretch) - QuickMsgScreen: send button pinned to display bottom (height-lh-2), not floating - SettingsScreen: AUTO_OFF guarded with #if AUTO_OFF_MILLIS>0; default sel=AUTO_LOCK - All screens: separators updated to sepH() Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
118f29b0a1
commit
9c671891d9
@@ -31,7 +31,7 @@ public:
|
|||||||
int tip_y = bar_y + bar_h + 4;
|
int tip_y = bar_y + bar_h + 4;
|
||||||
|
|
||||||
display.drawTextCentered(display.width() / 2, 0, "AUTO-ADVERT");
|
display.drawTextCentered(display.width() / 2, 0, "AUTO-ADVERT");
|
||||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
|
|
||||||
display.setCursor(2, label_y);
|
display.setCursor(2, label_y);
|
||||||
display.print("Interval:");
|
display.print("Interval:");
|
||||||
|
|||||||
@@ -55,12 +55,14 @@ public:
|
|||||||
return _kb.render(display);
|
return _kb.render(display);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int avail_h = display.height() - display.listStart();
|
||||||
int item_h = display.lineStep();
|
int item_h = display.lineStep();
|
||||||
|
if (item_h * ITEM_COUNT > avail_h) item_h = avail_h / ITEM_COUNT;
|
||||||
int start_y = display.listStart();
|
int start_y = display.listStart();
|
||||||
int val_x = display.valCol();
|
int val_x = display.valCol();
|
||||||
|
|
||||||
display.drawTextCentered(display.width() / 2, 0, "AUTO-REPLY BOT");
|
display.drawTextCentered(display.width() / 2, 0, "AUTO-REPLY BOT");
|
||||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
|
|
||||||
static const char* labels[] = { "Enable", "Channel", "Trigger", "Reply DM", "Reply Ch" };
|
static const char* labels[] = { "Enable", "Channel", "Trigger", "Reply DM", "Reply Ch" };
|
||||||
for (int i = 0; i < ITEM_COUNT; i++) {
|
for (int i = 0; i < ITEM_COUNT; i++) {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public:
|
|||||||
int val_x = display.valCol();
|
int val_x = display.valCol();
|
||||||
|
|
||||||
display.drawTextCentered(display.width() / 2, 0, "CLOCK FIELDS");
|
display.drawTextCentered(display.width() / 2, 0, "CLOCK FIELDS");
|
||||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
|
|
||||||
static const char* labels[] = { "Field 1", "Field 2", "Field 3" };
|
static const char* labels[] = { "Field 1", "Field 2", "Field 3" };
|
||||||
for (int i = 0; i < FIELD_SLOTS; i++) {
|
for (int i = 0; i < FIELD_SLOTS; i++) {
|
||||||
|
|||||||
@@ -61,22 +61,39 @@ struct KeyboardWidget {
|
|||||||
const int lh = display.getLineHeight();
|
const int lh = display.getLineHeight();
|
||||||
const int cw = display.getCharWidth();
|
const int cw = display.getCharWidth();
|
||||||
const int cell_w = display.width() / KB_COLS_CHAR;
|
const int cell_w = display.width() / KB_COLS_CHAR;
|
||||||
const int sep_y = lh; // tight separator: preview row height only
|
// compact: don't stretch cells beyond lh; freed vertical space goes to preview lines
|
||||||
|
const int kb_h = (KB_ROWS_CHAR + 1) * lh;
|
||||||
|
const int preview_h = display.height() - kb_h - 1;
|
||||||
|
const int prev_lines = (preview_h / lh) > 1 ? (preview_h / lh) : 1;
|
||||||
|
const int sep_y = prev_lines * lh;
|
||||||
const int chars_y = sep_y + 1;
|
const int chars_y = sep_y + 1;
|
||||||
const int cell_h = (display.height() - chars_y) / (KB_ROWS_CHAR + 1);
|
const int cell_h = (display.height() - chars_y) / (KB_ROWS_CHAR + 1);
|
||||||
const int spec_y = chars_y + KB_ROWS_CHAR * cell_h;
|
const int spec_y = chars_y + KB_ROWS_CHAR * cell_h;
|
||||||
const int spec_w = display.width() / KB_SPECIAL;
|
const int spec_w = display.width() / KB_SPECIAL;
|
||||||
|
|
||||||
// text preview: last N chars + cursor (fit within screen width)
|
// multi-line text preview: cursor always on last preview line
|
||||||
int max_preview = display.width() / cw - 1;
|
int cpl = display.width() / cw; // chars per preview line
|
||||||
if (max_preview > 30) max_preview = 30;
|
if (cpl < 1) cpl = 1;
|
||||||
const char* disp_start = buf;
|
int cursor_line = len / cpl;
|
||||||
int disp_len = len;
|
int first_line = (cursor_line >= prev_lines) ? (cursor_line - prev_lines + 1) : 0;
|
||||||
if (disp_len > max_preview) { disp_start = buf + (disp_len - max_preview); disp_len = max_preview; }
|
int start = first_line * cpl;
|
||||||
char preview[32];
|
for (int pl = 0; pl < prev_lines; pl++) {
|
||||||
snprintf(preview, sizeof(preview), "%.*s_", disp_len, disp_start);
|
int ps = start + pl * cpl;
|
||||||
display.setCursor(0, 0);
|
int pe = ps + cpl;
|
||||||
display.print(preview);
|
bool cursor_here = (ps <= len && (len < pe || pl == prev_lines - 1));
|
||||||
|
char linebuf[32];
|
||||||
|
if (cursor_here) {
|
||||||
|
int nc = len - ps; if (nc < 0) nc = 0; if (nc > cpl - 1) nc = cpl - 1;
|
||||||
|
snprintf(linebuf, sizeof(linebuf), "%.*s_", nc, buf + ps);
|
||||||
|
} else if (len > ps) {
|
||||||
|
int nc = (len < pe) ? (len - ps) : cpl;
|
||||||
|
snprintf(linebuf, sizeof(linebuf), "%.*s", nc, buf + ps);
|
||||||
|
} else {
|
||||||
|
linebuf[0] = '\0';
|
||||||
|
}
|
||||||
|
display.setCursor(0, pl * lh);
|
||||||
|
display.print(linebuf);
|
||||||
|
}
|
||||||
display.fillRect(0, sep_y, display.width(), 1);
|
display.fillRect(0, sep_y, display.width(), 1);
|
||||||
|
|
||||||
// character grid
|
// character grid
|
||||||
|
|||||||
@@ -234,8 +234,9 @@ class NearbyScreen : public UIScreen {
|
|||||||
display.print(b64_line);
|
display.print(b64_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
// distribute 4 remaining lines across available height below b64
|
// distribute 4 remaining lines below b64 — compact step, shrink only if needed
|
||||||
int step = (display.height() - hdr) / 5;
|
int step = display.lineStep();
|
||||||
|
if (step * 5 > display.height() - hdr) step = (display.height() - hdr) / 5;
|
||||||
char buf[32];
|
char buf[32];
|
||||||
snprintf(buf, sizeof(buf), "RSSI: %d dBm", (int)r.rssi);
|
snprintf(buf, sizeof(buf), "RSSI: %d dBm", (int)r.rssi);
|
||||||
display.setCursor(2, hdr + step); display.print(buf);
|
display.setCursor(2, hdr + step); display.print(buf);
|
||||||
@@ -419,8 +420,9 @@ public:
|
|||||||
display.drawTextEllipsized(2, 1, display.width() - 4, filtered);
|
display.drawTextEllipsized(2, 1, display.width() - 4, filtered);
|
||||||
display.setColor(DisplayDriver::LIGHT);
|
display.setColor(DisplayDriver::LIGHT);
|
||||||
|
|
||||||
// 5 lines: lat, lon, dist+bearing, type, seen — distributed across available height
|
// 5 lines: lat, lon, dist+bearing, type, seen — compact step, shrink only if needed
|
||||||
int step = (display.height() - hdr) / 5;
|
int step = display.lineStep();
|
||||||
|
if (step * 5 > display.height() - hdr) step = (display.height() - hdr) / 5;
|
||||||
char buf[32];
|
char buf[32];
|
||||||
snprintf(buf, sizeof(buf), "Lat: %.5f", e.lat_e6 / 1e6);
|
snprintf(buf, sizeof(buf), "Lat: %.5f", e.lat_e6 / 1e6);
|
||||||
display.setCursor(2, hdr); display.print(buf);
|
display.setCursor(2, hdr); display.print(buf);
|
||||||
@@ -459,7 +461,7 @@ public:
|
|||||||
char title[22];
|
char title[22];
|
||||||
snprintf(title, sizeof(title), "NEARBY[%s]", FILTER_LABELS[_filter]);
|
snprintf(title, sizeof(title), "NEARBY[%s]", FILTER_LABELS[_filter]);
|
||||||
display.drawTextCentered(display.width() / 2, 0, title);
|
display.drawTextCentered(display.width() / 2, 0, title);
|
||||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
|
|
||||||
if (_count == 0) {
|
if (_count == 0) {
|
||||||
display.drawTextCentered(display.width() / 2, display.height() / 2 - display.lineStep() / 2, "No contacts found");
|
display.drawTextCentered(display.width() / 2, display.height() / 2 - display.lineStep() / 2, "No contacts found");
|
||||||
|
|||||||
@@ -468,7 +468,7 @@ public:
|
|||||||
|
|
||||||
if (_phase == MODE_SELECT) {
|
if (_phase == MODE_SELECT) {
|
||||||
display.drawTextCentered(display.width()/2, 0, "MESSAGE");
|
display.drawTextCentered(display.width()/2, 0, "MESSAGE");
|
||||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
const char* opts[] = { "Direct message", "Channels", "Room Servers" };
|
const char* opts[] = { "Direct message", "Channels", "Room Servers" };
|
||||||
int badges[3] = {
|
int badges[3] = {
|
||||||
getDMUnreadTotal(),
|
getDMUnreadTotal(),
|
||||||
@@ -501,7 +501,7 @@ public:
|
|||||||
|
|
||||||
} else if (_phase == CONTACT_PICK) {
|
} else if (_phase == CONTACT_PICK) {
|
||||||
display.drawTextCentered(display.width()/2, 0, _room_mode ? "SELECT ROOM" : "SELECT CONTACT");
|
display.drawTextCentered(display.width()/2, 0, _room_mode ? "SELECT ROOM" : "SELECT CONTACT");
|
||||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
|
|
||||||
if (_num_contacts == 0) {
|
if (_num_contacts == 0) {
|
||||||
display.drawTextCentered(display.width()/2, display.height()/2, _room_mode ? "No room servers" : "No favourites");
|
display.drawTextCentered(display.width()/2, display.height()/2, _room_mode ? "No room servers" : "No favourites");
|
||||||
@@ -551,7 +551,7 @@ public:
|
|||||||
|
|
||||||
} else if (_phase == CHANNEL_PICK) {
|
} else if (_phase == CHANNEL_PICK) {
|
||||||
display.drawTextCentered(display.width()/2, 0, "SELECT CHANNEL");
|
display.drawTextCentered(display.width()/2, 0, "SELECT CHANNEL");
|
||||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
|
|
||||||
if (_num_channels == 0) {
|
if (_num_channels == 0) {
|
||||||
display.drawTextCentered(display.width()/2, display.height()/2, "No channels");
|
display.drawTextCentered(display.width()/2, display.height()/2, "No channels");
|
||||||
@@ -618,15 +618,16 @@ public:
|
|||||||
int hist_box_h = 2 * lh + 3;
|
int hist_box_h = 2 * lh + 3;
|
||||||
int hist_item_h = hist_box_h + 2;
|
int hist_item_h = hist_box_h + 2;
|
||||||
int hist_start_y = display.headerH();
|
int hist_start_y = display.headerH();
|
||||||
_hist_visible = (display.height() - hist_start_y) / hist_item_h;
|
int btn_h = lh + 4;
|
||||||
|
_hist_visible = (display.height() - hist_start_y - btn_h) / hist_item_h;
|
||||||
if (_hist_visible < 1) _hist_visible = 1;
|
if (_hist_visible < 1) _hist_visible = 1;
|
||||||
int cby = hist_start_y + _hist_visible * hist_item_h + 2;
|
int cby = display.height() - lh - 2;
|
||||||
|
|
||||||
char title[24];
|
char title[24];
|
||||||
display.setColor(DisplayDriver::LIGHT);
|
display.setColor(DisplayDriver::LIGHT);
|
||||||
snprintf(title, sizeof(title), "%.23s", filtered_name);
|
snprintf(title, sizeof(title), "%.23s", filtered_name);
|
||||||
display.drawTextCentered(display.width()/2, 0, title);
|
display.drawTextCentered(display.width()/2, 0, title);
|
||||||
display.fillRect(0, lh + 1, display.width(), 1);
|
display.fillRect(0, lh + 1, display.width(), display.sepH());
|
||||||
|
|
||||||
int dm_count = dmHistCountForContact(_sel_contact.id.pub_key);
|
int dm_count = dmHistCountForContact(_sel_contact.id.pub_key);
|
||||||
|
|
||||||
@@ -720,16 +721,17 @@ public:
|
|||||||
int hist_box_h = 2 * lh + 3;
|
int hist_box_h = 2 * lh + 3;
|
||||||
int hist_item_h = hist_box_h + 2;
|
int hist_item_h = hist_box_h + 2;
|
||||||
int hist_start_y = display.headerH();
|
int hist_start_y = display.headerH();
|
||||||
_hist_visible = (display.height() - hist_start_y) / hist_item_h;
|
int btn_h = lh + 4;
|
||||||
|
_hist_visible = (display.height() - hist_start_y - btn_h) / hist_item_h;
|
||||||
if (_hist_visible < 1) _hist_visible = 1;
|
if (_hist_visible < 1) _hist_visible = 1;
|
||||||
int cby = hist_start_y + _hist_visible * hist_item_h + 2;
|
int cby = display.height() - lh - 2;
|
||||||
|
|
||||||
ChannelDetails ch;
|
ChannelDetails ch;
|
||||||
the_mesh.getChannel(_sel_channel_idx, ch);
|
the_mesh.getChannel(_sel_channel_idx, ch);
|
||||||
char title[24];
|
char title[24];
|
||||||
snprintf(title, sizeof(title), "%.23s", ch.name);
|
snprintf(title, sizeof(title), "%.23s", ch.name);
|
||||||
display.drawTextCentered(display.width()/2, 0, title);
|
display.drawTextCentered(display.width()/2, 0, title);
|
||||||
display.fillRect(0, lh + 1, display.width(), 1);
|
display.fillRect(0, lh + 1, display.width(), display.sepH());
|
||||||
|
|
||||||
int ch_hist_count = histCountForChannel(_sel_channel_idx);
|
int ch_hist_count = histCountForChannel(_sel_channel_idx);
|
||||||
|
|
||||||
@@ -832,7 +834,7 @@ public:
|
|||||||
snprintf(title, sizeof(title), "TO:%.14s", _sel_contact.name);
|
snprintf(title, sizeof(title), "TO:%.14s", _sel_contact.name);
|
||||||
}
|
}
|
||||||
display.drawTextCentered(display.width()/2, 0, title);
|
display.drawTextCentered(display.width()/2, 0, title);
|
||||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
|
|
||||||
int total_msg_items = 1 + _active_msg_count;
|
int total_msg_items = 1 + _active_msg_count;
|
||||||
for (int i = 0; i < _visible && (_msg_scroll+i) < total_msg_items; i++) {
|
for (int i = 0; i < _visible && (_msg_scroll+i) < total_msg_items; i++) {
|
||||||
|
|||||||
@@ -102,7 +102,7 @@ public:
|
|||||||
snprintf(hdr, sizeof(hdr), "M%d BPM:%u %d/%d", _slot + 1, BPM_OPTS[_bpm_idx], _len, MAX_NOTES);
|
snprintf(hdr, sizeof(hdr), "M%d BPM:%u %d/%d", _slot + 1, BPM_OPTS[_bpm_idx], _len, MAX_NOTES);
|
||||||
display.setCursor(0, 0);
|
display.setCursor(0, 0);
|
||||||
display.print(hdr);
|
display.print(hdr);
|
||||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
|
|
||||||
for (int i = 0; i < _visible_notes; i++) {
|
for (int i = 0; i < _visible_notes; i++) {
|
||||||
int ni = _scroll + i;
|
int ni = _scroll + i;
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ class SettingsScreen : public UIScreen {
|
|||||||
#if !defined(EINK_DISPLAY_MODEL)
|
#if !defined(EINK_DISPLAY_MODEL)
|
||||||
BRIGHTNESS,
|
BRIGHTNESS,
|
||||||
#endif
|
#endif
|
||||||
|
#if AUTO_OFF_MILLIS > 0
|
||||||
AUTO_OFF,
|
AUTO_OFF,
|
||||||
|
#endif
|
||||||
AUTO_LOCK,
|
AUTO_LOCK,
|
||||||
BATT_DISPLAY,
|
BATT_DISPLAY,
|
||||||
#if !defined(EINK_DISPLAY_MODEL)
|
#if !defined(EINK_DISPLAY_MODEL)
|
||||||
@@ -64,9 +66,11 @@ class SettingsScreen : public UIScreen {
|
|||||||
int _visible; // items fitting on screen; updated each render, used by handleInput
|
int _visible; // items fitting on screen; updated each render, used by handleInput
|
||||||
bool _dirty;
|
bool _dirty;
|
||||||
|
|
||||||
|
#if AUTO_OFF_MILLIS > 0
|
||||||
static const uint16_t AUTO_OFF_OPTS[5];
|
static const uint16_t AUTO_OFF_OPTS[5];
|
||||||
static const char* AUTO_OFF_LABELS[5];
|
static const char* AUTO_OFF_LABELS[5];
|
||||||
static const int AUTO_OFF_COUNT = 5;
|
static const int AUTO_OFF_COUNT = 5;
|
||||||
|
#endif
|
||||||
#if ENV_INCLUDE_GPS == 1
|
#if ENV_INCLUDE_GPS == 1
|
||||||
static const uint32_t GPS_INTERVAL_OPTS[6];
|
static const uint32_t GPS_INTERVAL_OPTS[6];
|
||||||
static const char* GPS_INTERVAL_LABELS[6];
|
static const char* GPS_INTERVAL_LABELS[6];
|
||||||
@@ -101,6 +105,7 @@ class SettingsScreen : public UIScreen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if AUTO_OFF_MILLIS > 0
|
||||||
int autoOffIndex() {
|
int autoOffIndex() {
|
||||||
NodePrefs* p = _task->getNodePrefs();
|
NodePrefs* p = _task->getNodePrefs();
|
||||||
if (!p) return 1;
|
if (!p) return 1;
|
||||||
@@ -108,6 +113,7 @@ class SettingsScreen : public UIScreen {
|
|||||||
if (AUTO_OFF_OPTS[i] == p->auto_off_secs) return i;
|
if (AUTO_OFF_OPTS[i] == p->auto_off_secs) return i;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if ENV_INCLUDE_GPS == 1
|
#if ENV_INCLUDE_GPS == 1
|
||||||
int gpsIntervalIndex() {
|
int gpsIntervalIndex() {
|
||||||
@@ -289,10 +295,12 @@ class SettingsScreen : public UIScreen {
|
|||||||
sprintf(buf, "%ddBm", p ? p->tx_power_dbm : 0);
|
sprintf(buf, "%ddBm", p ? p->tx_power_dbm : 0);
|
||||||
display.setCursor(display.valCol(), y);
|
display.setCursor(display.valCol(), y);
|
||||||
display.print(buf);
|
display.print(buf);
|
||||||
|
#if AUTO_OFF_MILLIS > 0
|
||||||
} else if (item == AUTO_OFF) {
|
} else if (item == AUTO_OFF) {
|
||||||
display.print("AutoOff");
|
display.print("AutoOff");
|
||||||
display.setCursor(display.valCol(), y);
|
display.setCursor(display.valCol(), y);
|
||||||
display.print(AUTO_OFF_LABELS[autoOffIndex()]);
|
display.print(AUTO_OFF_LABELS[autoOffIndex()]);
|
||||||
|
#endif
|
||||||
} else if (item == AUTO_LOCK) {
|
} else if (item == AUTO_LOCK) {
|
||||||
display.print("AutoLock");
|
display.print("AutoLock");
|
||||||
display.setCursor(display.valCol(), y);
|
display.setCursor(display.valCol(), y);
|
||||||
@@ -367,7 +375,7 @@ class SettingsScreen : public UIScreen {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
SettingsScreen(UITask* task)
|
SettingsScreen(UITask* task)
|
||||||
: _task(task), _selected(AUTO_OFF), _scroll(0), _visible(4), _dirty(false), _edit_slot(-1) {}
|
: _task(task), _selected(AUTO_LOCK), _scroll(0), _visible(4), _dirty(false), _edit_slot(-1) {}
|
||||||
|
|
||||||
|
|
||||||
void markClean() { _dirty = false; }
|
void markClean() { _dirty = false; }
|
||||||
@@ -385,7 +393,7 @@ public:
|
|||||||
|
|
||||||
display.setColor(DisplayDriver::LIGHT);
|
display.setColor(DisplayDriver::LIGHT);
|
||||||
display.drawTextCentered(display.width() / 2, 0, "SETTINGS");
|
display.drawTextCentered(display.width() / 2, 0, "SETTINGS");
|
||||||
display.fillRect(0, display.headerH() - 2, display.width(), 1);
|
display.fillRect(0, display.headerH() - display.sepH(), display.width(), display.sepH());
|
||||||
|
|
||||||
for (int i = 0; i < _visible && (_scroll + i) < SettingItem::Count; i++) {
|
for (int i = 0; i < _visible && (_scroll + i) < SettingItem::Count; i++) {
|
||||||
renderItem(display, _scroll + i, start_y + i * item_h);
|
renderItem(display, _scroll + i, start_y + i * item_h);
|
||||||
@@ -489,12 +497,14 @@ public:
|
|||||||
if (right && p->tx_power_dbm < 22) { p->tx_power_dbm++; _task->applyTxPower(); _dirty = true; return true; }
|
if (right && p->tx_power_dbm < 22) { p->tx_power_dbm++; _task->applyTxPower(); _dirty = true; return true; }
|
||||||
if (left && p->tx_power_dbm > 2) { p->tx_power_dbm--; _task->applyTxPower(); _dirty = true; return true; }
|
if (left && p->tx_power_dbm > 2) { p->tx_power_dbm--; _task->applyTxPower(); _dirty = true; return true; }
|
||||||
}
|
}
|
||||||
|
#if AUTO_OFF_MILLIS > 0
|
||||||
if (_selected == AUTO_OFF && p) {
|
if (_selected == AUTO_OFF && p) {
|
||||||
int idx = autoOffIndex();
|
int idx = autoOffIndex();
|
||||||
if (right) idx = (idx + 1) % AUTO_OFF_COUNT;
|
if (right) idx = (idx + 1) % AUTO_OFF_COUNT;
|
||||||
if (left) idx = (idx + AUTO_OFF_COUNT - 1) % AUTO_OFF_COUNT;
|
if (left) idx = (idx + AUTO_OFF_COUNT - 1) % AUTO_OFF_COUNT;
|
||||||
if (left || right) { p->auto_off_secs = AUTO_OFF_OPTS[idx]; _dirty = true; return true; }
|
if (left || right) { p->auto_off_secs = AUTO_OFF_OPTS[idx]; _dirty = true; return true; }
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
if (_selected == AUTO_LOCK && p && (left || right || enter)) {
|
if (_selected == AUTO_LOCK && p && (left || right || enter)) {
|
||||||
p->auto_lock ^= 1;
|
p->auto_lock ^= 1;
|
||||||
_dirty = true;
|
_dirty = true;
|
||||||
@@ -576,8 +586,10 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#if AUTO_OFF_MILLIS > 0
|
||||||
const uint16_t SettingsScreen::AUTO_OFF_OPTS[5] = { 5, 15, 30, 60, 0 };
|
const uint16_t SettingsScreen::AUTO_OFF_OPTS[5] = { 5, 15, 30, 60, 0 };
|
||||||
const char* SettingsScreen::AUTO_OFF_LABELS[5] = { "5s", "15s", "30s", "60s", "never" };
|
const char* SettingsScreen::AUTO_OFF_LABELS[5] = { "5s", "15s", "30s", "60s", "never" };
|
||||||
|
#endif
|
||||||
#if ENV_INCLUDE_GPS == 1
|
#if ENV_INCLUDE_GPS == 1
|
||||||
const uint32_t SettingsScreen::GPS_INTERVAL_OPTS[6] = { 0, 30, 60, 300, 900, 1800 };
|
const uint32_t SettingsScreen::GPS_INTERVAL_OPTS[6] = { 0, 30, 60, 300, 900, 1800 };
|
||||||
const char* SettingsScreen::GPS_INTERVAL_LABELS[6] = { "off", "30s", "1min", "5min", "15min", "30min" };
|
const char* SettingsScreen::GPS_INTERVAL_LABELS[6] = { "off", "30s", "1min", "5min", "15min", "30min" };
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ public:
|
|||||||
display.setTextSize(1);
|
display.setTextSize(1);
|
||||||
display.setColor(DisplayDriver::LIGHT);
|
display.setColor(DisplayDriver::LIGHT);
|
||||||
display.drawTextCentered(display.width() / 2, 0, "TOOLS");
|
display.drawTextCentered(display.width() / 2, 0, "TOOLS");
|
||||||
display.fillRect(0, display.headerH() - 1, display.width(), 1);
|
display.fillRect(0, display.headerH() - 1, display.width(), display.sepH());
|
||||||
|
|
||||||
int item_h = display.lineStep();
|
int item_h = display.lineStep();
|
||||||
int start_y = display.listStart();
|
int start_y = display.listStart();
|
||||||
|
|||||||
@@ -240,9 +240,10 @@ class HomeScreen : public UIScreen {
|
|||||||
display.setTextSize(1);
|
display.setTextSize(1);
|
||||||
display.setColor(DisplayDriver::LIGHT);
|
display.setColor(DisplayDriver::LIGHT);
|
||||||
|
|
||||||
const int lh = display.getLineHeight();
|
const int lh = display.getLineHeight();
|
||||||
const int cw = display.getCharWidth();
|
const int cw = display.getCharWidth();
|
||||||
const int ind = cw + 2; // single-char indicator width
|
const int ind = cw + 2; // single-char indicator width
|
||||||
|
const int ind_gap = (lh >= 16) ? 3 : 1; // gap between indicator boxes
|
||||||
|
|
||||||
int battLeftX;
|
int battLeftX;
|
||||||
if (mode == 1) { // percent
|
if (mode == 1) { // percent
|
||||||
@@ -259,18 +260,19 @@ class HomeScreen : public UIScreen {
|
|||||||
display.print(buf);
|
display.print(buf);
|
||||||
} else { // icon — scales with lh
|
} else { // icon — scales with lh
|
||||||
const int iconH = lh;
|
const int iconH = lh;
|
||||||
const int iconW = iconH * 3;
|
const int iconW = lh * 2;
|
||||||
|
const int bm = (lh >= 16) ? 3 : 2; // inner margin: 3px on landscape (2px border), 2px on OLED
|
||||||
battLeftX = display.width() - iconW - 3;
|
battLeftX = display.width() - iconW - 3;
|
||||||
display.drawRect(battLeftX, 0, iconW, iconH);
|
display.drawRect(battLeftX, 0, iconW, iconH);
|
||||||
display.fillRect(battLeftX + iconW, iconH / 4, 2, iconH / 2);
|
display.fillRect(battLeftX + iconW, iconH / 4, 2, iconH / 2);
|
||||||
int fillW = (pct * (iconW - 4)) / 100;
|
int fillW = (pct * (iconW - 2 * bm)) / 100;
|
||||||
display.fillRect(battLeftX + 2, 2, fillW, iconH - 4);
|
display.fillRect(battLeftX + bm, bm, fillW, iconH - 2 * bm);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef PIN_BUZZER
|
#ifdef PIN_BUZZER
|
||||||
if (_task->isBuzzerQuiet()) {
|
if (_task->isBuzzerQuiet()) {
|
||||||
display.setColor(DisplayDriver::LIGHT);
|
display.setColor(DisplayDriver::LIGHT);
|
||||||
int mx = battLeftX - ind - 1;
|
int mx = battLeftX - ind - ind_gap;
|
||||||
display.fillRect(mx, 0, ind, lh);
|
display.fillRect(mx, 0, ind, lh);
|
||||||
display.setColor(DisplayDriver::DARK);
|
display.setColor(DisplayDriver::DARK);
|
||||||
display.setCursor(mx + 1, 0);
|
display.setCursor(mx + 1, 0);
|
||||||
@@ -283,7 +285,7 @@ class HomeScreen : public UIScreen {
|
|||||||
// BT connection indicator (left of muted/battery icons)
|
// BT connection indicator (left of muted/battery icons)
|
||||||
int leftmostX = battLeftX;
|
int leftmostX = battLeftX;
|
||||||
if (_task->isSerialEnabled()) {
|
if (_task->isSerialEnabled()) {
|
||||||
int btX = battLeftX - ind - 1;
|
int btX = battLeftX - ind - ind_gap;
|
||||||
if (_task->hasConnection()) {
|
if (_task->hasConnection()) {
|
||||||
display.setColor(DisplayDriver::LIGHT);
|
display.setColor(DisplayDriver::LIGHT);
|
||||||
display.fillRect(btX, 0, ind, lh);
|
display.fillRect(btX, 0, ind, lh);
|
||||||
@@ -295,7 +297,7 @@ class HomeScreen : public UIScreen {
|
|||||||
display.setCursor(btX + 1, 0);
|
display.setCursor(btX + 1, 0);
|
||||||
display.print("b");
|
display.print("b");
|
||||||
}
|
}
|
||||||
leftmostX = btX - 1;
|
leftmostX = btX - ind_gap;
|
||||||
|
|
||||||
// "A" indicator — left of BT, blinks 50% duty at 4s period
|
// "A" indicator — left of BT, blinks 50% duty at 4s period
|
||||||
if (_node_prefs && _node_prefs->advert_auto_interval_sec > 0) {
|
if (_node_prefs && _node_prefs->advert_auto_interval_sec > 0) {
|
||||||
@@ -358,7 +360,7 @@ public:
|
|||||||
const int lh = display.getLineHeight(); // line height at sz1
|
const int lh = display.getLineHeight(); // line height at sz1
|
||||||
const int step = display.lineStep(); // lh + 2
|
const int step = display.lineStep(); // lh + 2
|
||||||
const int dots_y = lh + 4; // page-dot row: just below header
|
const int dots_y = lh + 4; // page-dot row: just below header
|
||||||
const int content_y = dots_y + step; // first content row
|
const int content_y = dots_y + 6; // first content row (6px gap keeps dots visible)
|
||||||
|
|
||||||
// node name + battery — hidden on CLOCK page (full screen used for dashboard)
|
// node name + battery — hidden on CLOCK page (full screen used for dashboard)
|
||||||
if (_page != CLOCK) {
|
if (_page != CLOCK) {
|
||||||
@@ -385,8 +387,9 @@ public:
|
|||||||
int vi = 0;
|
int vi = 0;
|
||||||
for (int i = 0; i < (int)Count; i++) {
|
for (int i = 0; i < (int)Count; i++) {
|
||||||
if (!isPageVisible(i)) continue;
|
if (!isPageVisible(i)) continue;
|
||||||
if (vi == curr_vis) display.fillRect(x-1, dots_y-1, 3, 3);
|
int ds = (lh >= 16) ? 2 : 1;
|
||||||
else display.fillRect(x, dots_y, 1, 1);
|
if (vi == curr_vis) display.fillRect(x-ds, dots_y-ds, 2*ds+1, 2*ds+1);
|
||||||
|
else display.fillRect(x-ds+1, dots_y-ds+1, 2*ds-1, 2*ds-1);
|
||||||
x += 10; vi++;
|
x += 10; vi++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -431,8 +434,8 @@ public:
|
|||||||
display.drawTextCentered(display.width() / 2, date_y, buf);
|
display.drawTextCentered(display.width() / 2, date_y, buf);
|
||||||
|
|
||||||
int sep_y = date_y + lh + 1;
|
int sep_y = date_y + lh + 1;
|
||||||
int dash0 = sep_y + 3;
|
int dash0 = sep_y + display.sepH() + 2;
|
||||||
display.fillRect(0, sep_y, display.width(), 1);
|
display.fillRect(0, sep_y, display.width(), display.sepH());
|
||||||
|
|
||||||
// dashboard data fields
|
// dashboard data fields
|
||||||
if (_node_prefs) {
|
if (_node_prefs) {
|
||||||
@@ -1448,13 +1451,17 @@ void UITask::loop() {
|
|||||||
int delay_millis = curr->render(*_display);
|
int delay_millis = curr->render(*_display);
|
||||||
if (millis() < _alert_expiry && curr == home) { // render alert only on home screen
|
if (millis() < _alert_expiry && curr == home) { // render alert only on home screen
|
||||||
_display->setTextSize(1);
|
_display->setTextSize(1);
|
||||||
int y = _display->height() / 3;
|
int lh = _display->getLineHeight();
|
||||||
int p = _display->height() / 32;
|
int pad = 3;
|
||||||
|
int box_h = lh + pad * 2;
|
||||||
|
int box_w = _display->width() - 8;
|
||||||
|
int box_x = 4;
|
||||||
|
int box_y = (_display->height() - box_h) / 2;
|
||||||
_display->setColor(DisplayDriver::DARK);
|
_display->setColor(DisplayDriver::DARK);
|
||||||
_display->fillRect(p, y, _display->width() - p*2, y);
|
_display->fillRect(box_x, box_y, box_w, box_h);
|
||||||
_display->setColor(DisplayDriver::LIGHT); // draw box border
|
_display->setColor(DisplayDriver::LIGHT);
|
||||||
_display->drawRect(p, y, _display->width() - p*2, y);
|
_display->drawRect(box_x, box_y, box_w, box_h);
|
||||||
_display->drawTextCentered(_display->width() / 2, y + p*3, _alert);
|
_display->drawTextCentered(_display->width() / 2, box_y + pad, _alert);
|
||||||
_next_refresh = _alert_expiry; // will need refresh when alert is dismissed
|
_next_refresh = _alert_expiry; // will need refresh when alert is dismissed
|
||||||
} else {
|
} else {
|
||||||
_next_refresh = millis() + delay_millis;
|
_next_refresh = millis() + delay_millis;
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ public:
|
|||||||
int listVisible() const { return listVisible(lineStep()); }
|
int listVisible() const { return listVisible(lineStep()); }
|
||||||
// x where a right-side value column starts (leaves ~8 chars for the value)
|
// x where a right-side value column starts (leaves ~8 chars for the value)
|
||||||
int valCol() const { return width() - getCharWidth() * 8; }
|
int valCol() const { return width() - getCharWidth() * 8; }
|
||||||
|
// separator line thickness: 2px on landscape e-ink, 1px on OLED
|
||||||
|
int sepH() const { return (width() >= height()) ? 2 : 1; }
|
||||||
virtual void drawTextCentered(int mid_x, int y, const char* str) { // helper method (override to optimise)
|
virtual void drawTextCentered(int mid_x, int y, const char* str) { // helper method (override to optimise)
|
||||||
int w = getTextWidth(str);
|
int w = getTextWidth(str);
|
||||||
setCursor(mid_x - w/2, y);
|
setCursor(mid_x - w/2, y);
|
||||||
@@ -135,7 +137,7 @@ public:
|
|||||||
case 0x00E7: return 'c'; case 0x00C7: return 'C'; // ç Ç
|
case 0x00E7: return 'c'; case 0x00C7: return 'C'; // ç Ç
|
||||||
case 0x00F1: return 'n'; case 0x00D1: return 'N'; // ñ Ñ
|
case 0x00F1: return 'n'; case 0x00D1: return 'N'; // ñ Ñ
|
||||||
case 0x00FD: return 'y'; case 0x00DD: return 'Y'; // ý Ý
|
case 0x00FD: return 'y'; case 0x00DD: return 'Y'; // ý Ý
|
||||||
default: return '\xDB'; // CP437 full block █
|
default: return '?';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,8 @@ void GxEPDDisplay::drawRect(int x, int y, int w, int h) {
|
|||||||
display_crc.update<int>(w);
|
display_crc.update<int>(w);
|
||||||
display_crc.update<int>(h);
|
display_crc.update<int>(h);
|
||||||
display.drawRect(x, y, w, h, _curr_color);
|
display.drawRect(x, y, w, h, _curr_color);
|
||||||
|
if (width() >= height() && w > 2 && h > 2)
|
||||||
|
display.drawRect(x + 1, y + 1, w - 2, h - 2, _curr_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
|
void GxEPDDisplay::drawXbm(int x, int y, const uint8_t* bits, int w, int h) {
|
||||||
|
|||||||
Reference in New Issue
Block a user