mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-01 10:21:59 +00:00
fix(ui): lines and outlines follow the pixel scale on the 4.26" e-ink, opening a conversation no longer re-anchors on its second frame
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,7 @@ inline void draw(DisplayDriver& d, int x, int y, int w, const struct tm& t, uint
|
||||
if (marked & (1UL << (day - 1))) { // under the number, in the gap below it
|
||||
const int uw = d.getTextWidth(buf);
|
||||
d.setColor(today && day == t.tm_mday ? DisplayDriver::DARK : DisplayDriver::LIGHT);
|
||||
d.fillRect(cx + (cw - uw) / 2, cy + (tight ? lh - 2 : lh), uw, 1);
|
||||
d.fillRect(cx + (cw - uw) / 2, cy + (tight ? lh - 2 : lh), uw, d.sepH());
|
||||
d.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,9 @@ static inline void drawLine(DisplayDriver& d, int x0, int y0, int x1, int y1) {
|
||||
int dx = abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = -abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx + dy;
|
||||
const int t = d.sepH(); // 2px on a doubled panel
|
||||
while (true) {
|
||||
d.fillRect(x0, y0, 1, 1);
|
||||
d.fillRect(x0 - t / 2, y0 - t / 2, t, t);
|
||||
if (x0 == x1 && y0 == y1) break;
|
||||
int e2 = err * 2;
|
||||
if (e2 >= dy) { err += dy; x0 += sx; }
|
||||
|
||||
@@ -572,19 +572,17 @@ class HomeScreen : public UIScreen {
|
||||
} else { // icon — scales with lh, same box height as the status icons beside it (ind_h)
|
||||
const int iconH = ind_h;
|
||||
const int iconW = (bar_h > lh) ? ind_h * 2 : lh * 2;
|
||||
const int bm = display.pixelScale() > 1 ? 3 : 2; // inner margin: 3px on a doubled panel, else 2px
|
||||
const int t = display.sepH(); // outline: 1px, 2px on a doubled panel
|
||||
const int bm = 2 * t; // inner margin: the outline plus as much gap
|
||||
battLeftX = display.width() - iconW - 3;
|
||||
// The outline with its corner pixels left out, softly rounded.
|
||||
display.fillRect(battLeftX + 1, 0, iconW - 2, 1);
|
||||
display.fillRect(battLeftX + 1, iconH - 1, iconW - 2, 1);
|
||||
display.fillRect(battLeftX, 1, 1, iconH - 2);
|
||||
display.fillRect(battLeftX + iconW - 1, 1, 1, iconH - 2);
|
||||
display.drawSoftRect(battLeftX, 0, iconW, iconH);
|
||||
// Nub height/2, vertically centred by remaining-space/2 rather than a flat
|
||||
// iconH/4 margin — the flat form only centres when iconH is a multiple of
|
||||
// 4 (true for the old built-in font's lh=8, false for misc-fixed's 7/9),
|
||||
// so it visibly drifted off-centre once the box height changed.
|
||||
const int nub_h = iconH / 2;
|
||||
display.fillRect(battLeftX + iconW, (iconH - nub_h) / 2, 2, nub_h);
|
||||
display.fillRect(battLeftX + iconW, (iconH - nub_h) / 2, 2 * t, nub_h);
|
||||
int fillW = (pct * (iconW - 2 * bm)) / 100;
|
||||
display.fillRect(battLeftX + bm, bm, fillW, iconH - 2 * bm);
|
||||
}
|
||||
@@ -793,7 +791,7 @@ public:
|
||||
if (barpx < 5) barpx = 5;
|
||||
if (barpx > aw / 4) barpx = aw / 4;
|
||||
int bx = ax + 1, mid = ty + display.getLineHeight() / 2;
|
||||
display.fillRect(bx, mid, barpx, 1); // single tick, on the text baseline
|
||||
display.fillRect(bx, mid, barpx, display.sepH()); // single tick, on the text baseline
|
||||
display.setCursor(bx + barpx + 2, ty);
|
||||
display.print(L[sel]);
|
||||
}
|
||||
@@ -1023,8 +1021,9 @@ public:
|
||||
const int bar_y = y + bc.h + bar / 2 + 1;
|
||||
if (show_sec) { // fills over the minute: dotted track, solid part
|
||||
const int x0 = W / 8, bw = W - 2 * x0, fill = bw * ti.tm_sec / 60;
|
||||
d.fillRect(x0, bar_y, fill, 1);
|
||||
for (int x = x0 + fill + (fill & 1); x < x0 + bw; x += 2) d.fillRect(x, bar_y, 1, 1);
|
||||
const int t = d.sepH();
|
||||
d.fillRect(x0, bar_y, fill, t);
|
||||
for (int x = x0 + fill + (fill & 1) * t; x < x0 + bw; x += 2 * t) d.fillRect(x, bar_y, t, t);
|
||||
}
|
||||
// Alarm armed: the bell at the bar's left end (the status bar, which
|
||||
// shows it everywhere else, is hidden on this page).
|
||||
@@ -1375,7 +1374,8 @@ public:
|
||||
int x = display.width() / 2 - pitch * (n - 1) / 2;
|
||||
// The current page: its icon knocked out of a soft pill. During a page
|
||||
// turn the pill glides over from the page being left.
|
||||
int pw = icon_w + 4;
|
||||
const int pad = display.sepH(); // the pill's margin round the icon
|
||||
int pw = icon_w + 4 * pad;
|
||||
if (pw > pitch - 1) pw = pitch - 1;
|
||||
int pill_x = x + pitch * curr_vis;
|
||||
if (slide >= 0) {
|
||||
@@ -1383,7 +1383,7 @@ public:
|
||||
if (order[i] == _slide_from) { pill_x = x + pitch * i + (int)((pill_x - x - pitch * i) * slide + 0.5f); break; }
|
||||
}
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillSoftRect(pill_x - pw / 2, dots_y - pg_half - 1, pw, pg_half * 2 + 3);
|
||||
display.fillSoftRect(pill_x - pw / 2, dots_y - pg_half - pad, pw, pg_half * 2 + 1 + 2 * pad);
|
||||
for (int i = 0; i < n; i++) {
|
||||
const MiniIcon* ic = pageIcon(order[i]);
|
||||
const int d2 = 2 * (x - pill_x < 0 ? pill_x - x : x - pill_x);
|
||||
@@ -1455,8 +1455,9 @@ public:
|
||||
if (sel) { display.fillSoftRect(tx, ty, tw, th); display.setColor(DisplayDriver::DARK); }
|
||||
else if (prefix) display.drawSoftRect(tx, ty, tw, th);
|
||||
else {
|
||||
for (int x = tx + 2; x < tx + tw - 1; x += 2) { display.fillRect(x, ty, 1, 1); display.fillRect(x, ty + th - 1, 1, 1); }
|
||||
for (int y = ty + 2; y < ty + th - 1; y += 2) { display.fillRect(tx, y, 1, 1); display.fillRect(tx + tw - 1, y, 1, 1); }
|
||||
const int t = display.sepH(); // dot size: 2px on a doubled panel
|
||||
for (int x = tx + 2 * t; x < tx + tw - t; x += 2 * t) { display.fillRect(x, ty, t, t); display.fillRect(x, ty + th - t, t, t); }
|
||||
for (int y = ty + 2 * t; y < ty + th - t; y += 2 * t) { display.fillRect(tx, y, t, t); display.fillRect(tx + tw - t, y, t, t); }
|
||||
}
|
||||
|
||||
char name[26];
|
||||
|
||||
@@ -73,10 +73,7 @@ inline void battery(DisplayDriver& d, int x, int y, float frac) {
|
||||
const int s = miniIconScale(d), w = BATT_BODY_W * s, h = BATT_BODY_H * s;
|
||||
if (frac < 0) frac = 0;
|
||||
if (frac > 1) frac = 1;
|
||||
d.fillRect(x + 1, y, w - 2, 1); // the outline without its corner pixels
|
||||
d.fillRect(x + 1, y + h - 1, w - 2, 1);
|
||||
d.fillRect(x, y + 1, 1, h - 2);
|
||||
d.fillRect(x + w - 1, y + 1, 1, h - 2);
|
||||
d.drawSoftRect(x, y, w, h); // the outline without its corner pixels
|
||||
d.fillRect(x + w, y + s, s, h - 2 * s);
|
||||
const int in = MINI_ICONS_LARGE ? 2 * s : s; // the large one keeps a gap inside its outline
|
||||
const int fw = (int)((w - 2 * in) * frac + 0.5f);
|
||||
@@ -92,15 +89,21 @@ inline void meter(DisplayDriver& d, int x, int y, int w, int h, float frac) {
|
||||
if (fw > 0) d.fillRect(x + in, y + in, fw, h - 2 * in);
|
||||
}
|
||||
|
||||
// One point of a line or circle, sepH() square (2px on a doubled panel).
|
||||
inline void plot(DisplayDriver& d, int x, int y) {
|
||||
const int t = d.sepH();
|
||||
d.fillRect(x - t / 2, y - t / 2, t, t);
|
||||
}
|
||||
|
||||
// Midpoint circle; `step` > 1 dots it (every step-th point of each octant).
|
||||
inline void circle(DisplayDriver& d, int cx, int cy, int r, int step = 1) {
|
||||
int x = r, y = 0, err = 1 - r, i = 0;
|
||||
while (x >= y) {
|
||||
if (i++ % step == 0) {
|
||||
d.fillRect(cx + x, cy + y, 1, 1); d.fillRect(cx - x, cy + y, 1, 1);
|
||||
d.fillRect(cx + x, cy - y, 1, 1); d.fillRect(cx - x, cy - y, 1, 1);
|
||||
d.fillRect(cx + y, cy + x, 1, 1); d.fillRect(cx - y, cy + x, 1, 1);
|
||||
d.fillRect(cx + y, cy - x, 1, 1); d.fillRect(cx - y, cy - x, 1, 1);
|
||||
plot(d, cx + x, cy + y); plot(d, cx - x, cy + y);
|
||||
plot(d, cx + x, cy - y); plot(d, cx - x, cy - y);
|
||||
plot(d, cx + y, cy + x); plot(d, cx - y, cy + x);
|
||||
plot(d, cx + y, cy - x); plot(d, cx - y, cy - x);
|
||||
}
|
||||
y++;
|
||||
if (err < 0) err += 2 * y + 1;
|
||||
@@ -108,13 +111,13 @@ inline void circle(DisplayDriver& d, int cx, int cy, int r, int step = 1) {
|
||||
}
|
||||
}
|
||||
|
||||
// A 1 px line (Bresenham).
|
||||
// A line (Bresenham), sepH() thick.
|
||||
inline void line(DisplayDriver& d, int x0, int y0, int x1, int y1) {
|
||||
const int dx = x1 > x0 ? x1 - x0 : x0 - x1, dy = y1 > y0 ? y0 - y1 : y1 - y0;
|
||||
const int sx = x0 < x1 ? 1 : -1, sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx + dy;
|
||||
for (;;) {
|
||||
d.fillRect(x0, y0, 1, 1);
|
||||
plot(d, x0, y0);
|
||||
if (x0 == x1 && y0 == y1) break;
|
||||
const int e2 = 2 * err;
|
||||
if (e2 >= dy) { err += dy; x0 += sx; }
|
||||
|
||||
@@ -568,7 +568,7 @@ struct KeyboardWidget {
|
||||
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 (i == len) { display.fillRect(x, y + lh - 2, cw, display.sepH()); continue; } // next digit goes here
|
||||
if (pin_kb_mask_enabled) {
|
||||
display.fillSoftRect(x, y + 1, cw, lh - 3);
|
||||
} else {
|
||||
@@ -887,7 +887,7 @@ struct KeyboardWidget {
|
||||
// completely differently (one letter vs. every following letter).
|
||||
// caps_lock implies the cell is filled, so the current (inverted)
|
||||
// ink colour is the one that shows against it.
|
||||
if (i == 0 && caps_lock) display.fillRect(sx + 2, spec_y + cell_h - 3, spec_w - 5, 1);
|
||||
if (i == 0 && caps_lock) display.fillRect(sx + 2, spec_y + cell_h - 3, spec_w - 5, display.sepH());
|
||||
}
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
}
|
||||
|
||||
@@ -1083,8 +1083,8 @@ public:
|
||||
int tw = d.getTextWidth(t);
|
||||
int tx = (w - tw) / 2, mid = y + lh / 2;
|
||||
d.setColor(DisplayDriver::LIGHT);
|
||||
d.fillRect(1, mid, tx - 4, 1);
|
||||
d.fillRect(tx + tw + 3, mid, w - 1 - (tx + tw + 3), 1);
|
||||
d.fillRect(1, mid, tx - 4, d.sepH());
|
||||
d.fillRect(tx + tw + 3, mid, w - 1 - (tx + tw + 3), d.sepH());
|
||||
d.setCursor(tx, y + 1);
|
||||
d.print(t);
|
||||
}
|
||||
|
||||
@@ -28,12 +28,13 @@ inline void drawPill(DisplayDriver& display, int x, int w, const char* label, bo
|
||||
}
|
||||
|
||||
// Small solid triangle pointing left (dir < 0) or right, 3 px wide and 5
|
||||
// tall, centred on row y_mid: "more tabs this way".
|
||||
// tall (times sepH()), centred on row y_mid: "more tabs this way".
|
||||
inline void drawEdgeArrow(DisplayDriver& display, int x, int y_mid, int dir) {
|
||||
const int t = display.sepH();
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
for (int c = 0; c < 3; c++) {
|
||||
int h = dir < 0 ? c * 2 + 1 : 5 - c * 2;
|
||||
display.fillRect(x + c, y_mid - h / 2, 1, h);
|
||||
int h = (dir < 0 ? c * 2 + 1 : 5 - c * 2) * t;
|
||||
display.fillRect(x + c * t, y_mid - h / 2, t, h);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,16 +76,17 @@ inline void draw(DisplayDriver& display, const char* const* labels, int count, i
|
||||
return Fit{ shown, lx + gap, rx_limit - rx + gap };
|
||||
};
|
||||
|
||||
const int arr = 3 * display.sepH(); // the edge arrow's width
|
||||
drawPill(display, ax, aw, labels[active], true);
|
||||
Fit f = layout(0, false);
|
||||
if (f.shown < count && f.left_free < 4 && f.right_free < 4)
|
||||
f = layout(5, true); // no room for an arrow anywhere: give up a tab for them
|
||||
if (f.shown < count && f.left_free < arr + 1 && f.right_free < arr + 1)
|
||||
f = layout(arr + 2, true); // no room for an arrow anywhere: give up a tab for them
|
||||
else
|
||||
layout(0, true);
|
||||
if (f.shown < count) { // more tabs off screen: an arrow wherever there's room
|
||||
int y_mid = display.getLineHeight() / 2;
|
||||
if (f.left_free >= 4) drawEdgeArrow(display, 0, y_mid, -1);
|
||||
if (f.right_free >= 4) drawEdgeArrow(display, rx_limit - 3, y_mid, +1);
|
||||
if (f.left_free >= arr + 1) drawEdgeArrow(display, 0, y_mid, -1);
|
||||
if (f.right_free >= arr + 1) drawEdgeArrow(display, rx_limit - arr, y_mid, +1);
|
||||
}
|
||||
display.setColor(DisplayDriver::LIGHT);
|
||||
display.fillRect(0, display.headerH() - display.sepH(), display.width(), display.sepH());
|
||||
|
||||
@@ -1085,9 +1085,12 @@ void UITask::loop() {
|
||||
#endif
|
||||
pollConnection(); // BLE link state -> hasConnection() (see UITaskBase)
|
||||
drainCoreEvents(); // react to what the Core filed during mesh processing (alerts, wake, sounds)
|
||||
{ // a conversation in view again (display on, unlocked): on to what came in meanwhile
|
||||
bool on = messagesInView();
|
||||
if (on && !_display_was_on) ((MessagesScreen*)messages_screen)->onDisplayOn();
|
||||
{ // a conversation in view again (display on, unlocked): on to what came in meanwhile.
|
||||
// Only on the display or lock changing -- not on opening the screen, which
|
||||
// positions itself (a second pass would re-anchor on what the first frame
|
||||
// had just marked read, and the list jumped).
|
||||
bool on = _display && _display->isOn() && !_locked;
|
||||
if (on && !_display_was_on && curr == messages_screen) ((MessagesScreen*)messages_screen)->onDisplayOn();
|
||||
_display_was_on = on;
|
||||
}
|
||||
if (status_screen) ((StatusScreen*)status_screen)->sample(); // Status history lines, once a minute
|
||||
|
||||
@@ -60,7 +60,7 @@ class UITask : public UITaskBase, public UiCoreHost {
|
||||
GenericVibration vibration;
|
||||
#endif
|
||||
unsigned long _next_refresh, _auto_off;
|
||||
bool _display_was_on = true; // loop(): messagesInView() last time round
|
||||
bool _display_was_on = true; // loop(): display on and unlocked last time round
|
||||
NodePrefs* _node_prefs;
|
||||
bool _locked;
|
||||
bool _tool_from_home = false;
|
||||
|
||||
@@ -231,14 +231,14 @@
|
||||
|
||||
<script src="build/meshcore_sim.js"></script>
|
||||
<script src="build/repeater/meshcore_sim_repeater.js"></script>
|
||||
<!-- ?a=meshcore_sim_eink_landscape (or _portrait, _wide): A is that e-ink build. -->
|
||||
<!-- ?a=meshcore_sim_eink_landscape (or _portrait, _wide, _big): A is that e-ink build. -->
|
||||
<script>
|
||||
const A_BUILD = (new URLSearchParams(location.search).get('a') || '').replace(/[^\w]/g, '');
|
||||
if (A_BUILD) document.write('<script src="build/' + A_BUILD + '.js"><\/script>');
|
||||
</script>
|
||||
<script>
|
||||
const logEl = document.getElementById('log');
|
||||
const FactoryA = (opts) => (window.MeshCoreSimEinkLandscape || window.MeshCoreSimEinkPortrait || window.MeshCoreSimEinkWide || MeshCoreSim)(opts);
|
||||
const FactoryA = (opts) => (window.MeshCoreSimEinkLandscape || window.MeshCoreSimEinkPortrait || window.MeshCoreSimEinkWide || window.MeshCoreSimEinkBig || MeshCoreSim)(opts);
|
||||
function log(msg) {
|
||||
const line = document.createElement('div');
|
||||
line.textContent = '[' + new Date().toISOString().substr(11, 12) + '] ' + msg;
|
||||
|
||||
Reference in New Issue
Block a user