Skip to content

Commit 7c8137e

Browse files
committed
Say when a row was cut, instead of just stopping
Every row of the address list is an address that does not fit, cut at whatever pixel ran out. A row that stops looks exactly like a row that ended, so the reader has nothing telling them the value continues - and on this list every value does. Rows that do not fit now end in an ellipsis. It is paid for out of the same width rather than drawn past it: the fit is recomputed against what is left after reserving room for the dots, so a truncated row is never wider than one that fits. This is in the list renderer rather than at the address list, so any row that overflows says so. In practice that is only this list - labels_fit_a_row already holds every menu label to fitting - which is why the change is narrow in effect while being general in the code. The self-test checks both halves, because either alone passes while the other is broken: that the dots stay inside the row's own column rather than running under the scrollbar, and that they are drawn at all. The second compares the rendered row against its own fit_row prefix - what a silent cut would have produced - and requires them to differ. Verified it fails when the ellipsis is removed.
1 parent 7e72b6f commit 7c8137e

2 files changed

Lines changed: 83 additions & 2 deletions

File tree

‎host/origo_simulator.c‎

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1620,6 +1620,57 @@ static bool nav_left_slot_is_drawn(void)
16201620
* alternating ink meaningful across a break: a line ending mid-group would
16211621
* put two same-coloured groups against each other with only the gap between
16221622
* them, and the gap is the signal the colour is there to back up. */
1623+
/* A truncated row has to say so, and has to pay for saying it out of the same
1624+
* width. Two things are checked, because either alone passes while the other
1625+
* is broken: that the dots stay inside the row's own column rather than
1626+
* running under the scrollbar, and that they are drawn at all.
1627+
*
1628+
* The second is the one worth explaining. A row drawn without the ellipsis
1629+
* would be exactly its own fit_row prefix, so rendering that prefix as a row
1630+
* in its own right and comparing the two framebuffers says whether anything
1631+
* extra reached the screen. Without it this test would pass on a draw_row
1632+
* that silently cut, which is the bug it exists for. */
1633+
static bool truncated_rows_show_an_ellipsis(void)
1634+
{
1635+
static uint16_t truncated[SEEDTOOL_DISPLAY_WIDTH * SEEDTOOL_DISPLAY_HEIGHT];
1636+
/* Longer than any row can hold, which is what every address row is: an
1637+
* index, two spaces and a full address. */
1638+
static const char* const row = " 0 bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4";
1639+
const seedtool_nav_t nav = { .selected = 0, .back = true };
1640+
const char* const items[] = { row };
1641+
seedtool_render_nav_list(&nav, "m/84'/0'/0'/0", items, 1, 0);
1642+
memcpy(truncated, seedtool_render_pixels(), sizeof(truncated));
1643+
1644+
int rightmost = -1;
1645+
for (int y = 21; y < 55; ++y) {
1646+
for (int x = 0; x < SEEDTOOL_DISPLAY_WIDTH; ++x) {
1647+
if (truncated[y * SEEDTOOL_DISPLAY_WIDTH + x] != 0x0000 && x > rightmost) {
1648+
rightmost = x;
1649+
}
1650+
}
1651+
}
1652+
if (rightmost < 0) {
1653+
return false; /* the row never reached the screen */
1654+
}
1655+
/* LIST_TEXT_X + LIST_TEXT_WIDTH in seedtool_render.c, kept in sync with
1656+
* the 6 + 222 below: the row's own column, which the ellipsis has to stay
1657+
* inside rather than run past into the scrollbar's strip. */
1658+
if (rightmost > 6 + 222) {
1659+
return false;
1660+
}
1661+
1662+
/* What a silent cut would have drawn. */
1663+
char prefix[64] = { 0 };
1664+
const size_t fit = seedtool_render_fit_row(row);
1665+
if (fit >= sizeof(prefix) || fit >= strlen(row)) {
1666+
return false; /* the row was meant to be too long for one */
1667+
}
1668+
memcpy(prefix, row, fit);
1669+
const char* const cut[] = { prefix };
1670+
seedtool_render_nav_list(&nav, "m/84'/0'/0'/0", cut, 1, 0);
1671+
return memcmp(truncated, seedtool_render_pixels(), sizeof(truncated)) != 0;
1672+
}
1673+
16231674
static bool grouped_paging_preserves_the_value(void)
16241675
{
16251676
static const char* const values[] = {
@@ -2284,6 +2335,10 @@ static int self_test(void)
22842335
fputs("Origo backup confirmation screen self-test failed\n", stderr);
22852336
return 1;
22862337
}
2338+
if (!truncated_rows_show_an_ellipsis()) {
2339+
fputs("Origo truncated row self-test failed\n", stderr);
2340+
return 1;
2341+
}
22872342
if (!grouped_paging_preserves_the_value()) {
22882343
fputs("Origo grouped address paging self-test failed\n", stderr);
22892344
return 1;

‎main/seedtool_render.c‎

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,32 @@ size_t seedtool_render_fit_grouped(const char* text, const size_t limit)
463463
}
464464

465465

466+
/* A row's text, with an ellipsis when it did not all fit. A row that simply
467+
* stops is indistinguishable from a row that ended - which is fine for a menu
468+
* label, since those are held to fitting by a self-test, and wrong for the
469+
* address list, where every row is a value cut short and the reader has no
470+
* way to tell how much is missing. The three dots say "there is more" without
471+
* claiming how much; the address's own page says the rest.
472+
*
473+
* The ellipsis is paid for out of the same width, not drawn past it: the fit
474+
* is recomputed against what is left after reserving room for the dots, so a
475+
* truncated row is never wider than one that fits. */
476+
#define ROW_ELLIPSIS "..."
477+
478+
static void draw_row(const uint8_t* font, const char* text, const int x, const int y, const int width,
479+
const uint16_t ink)
480+
{
481+
const size_t fit = fit_in(font, text, SIZE_MAX, width);
482+
if (!text[fit]) {
483+
draw_line_at(font, text, fit, x, y, ink);
484+
return;
485+
}
486+
const int dots = text_width(font, ROW_ELLIPSIS, strlen(ROW_ELLIPSIS));
487+
const size_t shortened = fit_in(font, text, SIZE_MAX, width - dots);
488+
draw_line_at(font, text, shortened, x, y, ink);
489+
draw_line_at(font, ROW_ELLIPSIS, strlen(ROW_ELLIPSIS), x + text_width(font, text, shortened), y, ink);
490+
}
491+
466492
size_t seedtool_render_fit_row(const char* text) { return fit_in(tft_Ubuntu16, text, SIZE_MAX, LIST_TEXT_WIDTH); }
467493

468494
size_t seedtool_render_fit_tail(const char* text)
@@ -807,8 +833,8 @@ void seedtool_render_nav_list(
807833
if (highlighted) {
808834
fill_rect(LIST_BAR_X, y, LIST_BAR_WIDTH, NAV_ROW_HEIGHT - 2, COLOR_HIGHLIGHT);
809835
}
810-
draw_line_at(tft_Ubuntu16, item, seedtool_render_fit_row(item), LIST_TEXT_X,
811-
y + (NAV_ROW_HEIGHT - tft_Ubuntu16[1]) / 2, highlighted ? COLOR_BLACK : COLOR_WHITE);
836+
draw_row(tft_Ubuntu16, item, LIST_TEXT_X, y + (NAV_ROW_HEIGHT - tft_Ubuntu16[1]) / 2, LIST_TEXT_WIDTH,
837+
highlighted ? COLOR_BLACK : COLOR_WHITE);
812838
}
813839
if (count > SEEDTOOL_LIST_ROWS) {
814840
const seedtool_thumb_t thumb = seedtool_list_thumb(count, top, NAV_SCROLL_HEIGHT);

0 commit comments

Comments
 (0)