Skip to content

Commit cb32724

Browse files
authored
Merge pull request #19 from oroderico/fix-icons-and-digit-layout
Fix the drawn icons and centre the digit field
2 parents 5c4b598 + 9635c7b commit cb32724

1 file changed

Lines changed: 111 additions & 58 deletions

File tree

‎main/seedtool_render.c‎

Lines changed: 111 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@
4848
#define LIST_TOP 21
4949
#define LIST_ROW_HEIGHT 33
5050
#define LIST_FOOTER_Y 122
51+
52+
/* The plain screens put their title at the top and their footer near the
53+
* bottom; the band between is what the screen is for. Named because the digit
54+
* field centres itself in it. */
55+
#define SCREEN_TITLE_Y 2
56+
#define SCREEN_TITLE_BOTTOM (SCREEN_TITLE_Y + 16) /* the 16px face's own height */
57+
#define SCREEN_FOOTER_Y 111
5158
#define LIST_GUTTER 10
5259
#define LIST_BAR_X 2
5360
#define LIST_BAR_WIDTH (SEEDTOOL_DISPLAY_WIDTH - LIST_GUTTER - 2 * LIST_BAR_X)
@@ -69,6 +76,15 @@
6976
#define NAV_BACK_HEIGHT 19
7077
#define NAV_ARROW_WIDTH 7
7178
#define NAV_ARROW_HEIGHT 11
79+
/* The tick gets its own box rather than the arrow's: the arrow is tall and
80+
* narrow to read as a triangle, a check this small has to be wider than tall.
81+
* The legs and stroke are the shape; the box is what they add up to, derived
82+
* so it cannot drift from them. */
83+
#define NAV_TICK_STROKE 2
84+
#define NAV_TICK_SHORT 3
85+
#define NAV_TICK_LONG 6
86+
#define NAV_TICK_WIDTH (NAV_TICK_SHORT + NAV_TICK_LONG + NAV_TICK_STROKE)
87+
#define NAV_TICK_HEIGHT (NAV_TICK_LONG + NAV_TICK_STROKE)
7288
#define NAV_ROW_HEIGHT 32
7389
/* The rows' own height, less the 2px the last one leaves under itself: a
7490
* plain list can let its track overhang that gap, having nothing below it,
@@ -425,7 +441,7 @@ void seedtool_render_screen(const char* title, const char* line1, const char* li
425441
* small because 16px there would run off the bottom of the display. */
426442
draw_centered(tft_Ubuntu16, line1, 39);
427443
draw_centered(tft_Ubuntu16, line2, 65);
428-
draw_centered(tft_DefaultFont, footer, 111);
444+
draw_centered(tft_DefaultFont, footer, SCREEN_FOOTER_Y);
429445
}
430446

431447
seedtool_thumb_t seedtool_list_thumb(const size_t count, const size_t top, const int track)
@@ -471,31 +487,54 @@ size_t seedtool_list_top(const size_t count, const size_t selected, size_t previ
471487
return previous_top;
472488
}
473489

474-
/* A solid left-pointing triangle with its apex at (x, y + height / 2): the
475-
* back arrow, drawn rather than set, since the 16px face carries no glyph for
476-
* one and Jade's own symbols font is not linked here. */
477-
static void draw_back_arrow(const int x, const int y, const int width, const int height, const uint16_t color)
490+
/* A solid isoceles triangle filling a width x height box, apex pointing the
491+
* way asked - drawn rather than set, since the 16px face carries no glyph for
492+
* one and Jade's own symbols font is not linked here.
493+
*
494+
* Up and down walk the rows drawing horizontal bars; left walks the columns
495+
* drawing vertical ones. That is the same arithmetic transposed, which is why
496+
* the header's back arrow and the digit field's scroll hints are one function
497+
* rather than two that drift apart.
498+
*
499+
* The `+ 1` on the step is what keeps the apex a single pixel: without it the
500+
* first two steps round to the same half-width and the point comes out as a
501+
* two-pixel spike. */
502+
typedef enum { TRIANGLE_UP, TRIANGLE_DOWN, TRIANGLE_LEFT } triangle_dir_t;
503+
504+
static void draw_triangle(
505+
const int x, const int y, const int width, const int height, const triangle_dir_t dir, const uint16_t color)
478506
{
479-
for (int column = 0; column < width; ++column) {
480-
const int half = (column + 1) * height / (2 * width);
481-
fill_rect(x + column, y + height / 2 - half, 1, 2 * half + 1, color);
507+
const bool vertical = dir != TRIANGLE_LEFT;
508+
const int steps = vertical ? height : width;
509+
const int across = vertical ? width : height;
510+
for (int i = 0; i < steps; ++i) {
511+
const int from_point = dir == TRIANGLE_DOWN ? steps - 1 - i : i;
512+
const int half = (from_point + 1) * across / (2 * steps);
513+
if (vertical) {
514+
fill_rect(x + width / 2 - half, y + i, 2 * half + 1, 1, color);
515+
} else {
516+
fill_rect(x + i, y + height / 2 - half, 1, 2 * half + 1, color);
517+
}
482518
}
483519
}
484520

485521
/* A tick, drawn rather than set, for the same reason the arrow above is: the
486522
* 16px face has no glyph for one and Jade's symbols font is a component the
487-
* audit rejects by name. Two strokes from a common low point - a short one up
488-
* to the left, a long one up to the right - each a stack of 2px squares so the
489-
* diagonals read as solid rather than as a dotted line of single pixels. */
490-
static void draw_tick(const int x, const int y, const int width, const int height, const uint16_t color)
523+
* audit rejects by name. Two strokes up from a common low point, each a stack
524+
* of squares so the diagonals read as solid rather than as dotted pixels.
525+
*
526+
* The legs are fixed lengths rather than fractions of one width - at this size
527+
* that rounds the short leg away entirely - and roughly 1:2 is what reads as a
528+
* check. `x, y` is the top-left of the NAV_TICK_WIDTH x NAV_TICK_HEIGHT box. */
529+
static void draw_tick(const int x, const int y, const uint16_t color)
491530
{
492-
const int foot_x = x + width / 3;
493-
const int foot_y = y + height - 2;
494-
for (int i = 0; i < width / 3; ++i) {
495-
fill_rect(foot_x - i, foot_y - i, 2, 2, color);
531+
const int foot_x = x + NAV_TICK_SHORT;
532+
const int foot_y = y + NAV_TICK_LONG;
533+
for (int i = 0; i <= NAV_TICK_SHORT; ++i) {
534+
fill_rect(foot_x - i, foot_y - i, NAV_TICK_STROKE, NAV_TICK_STROKE, color);
496535
}
497-
for (int i = 0; i < width - width / 3; ++i) {
498-
fill_rect(foot_x + i, foot_y - i, 2, 2, color);
536+
for (int i = 0; i <= NAV_TICK_LONG; ++i) {
537+
fill_rect(foot_x + i, foot_y - i, NAV_TICK_STROKE, NAV_TICK_STROKE, color);
499538
}
500539
}
501540

@@ -533,8 +572,8 @@ static void draw_nav_header(const seedtool_nav_t* nav, const char* title)
533572
fill_rect(NAV_BACK_X, NAV_BACK_Y, NAV_BACK_WIDTH, 1, edge);
534573
fill_rect(NAV_BACK_X, NAV_BACK_Y, 1, NAV_BACK_HEIGHT, edge);
535574
fill_rect(NAV_BACK_X + NAV_BACK_WIDTH - 1, NAV_BACK_Y, 1, NAV_BACK_HEIGHT, edge);
536-
draw_back_arrow(NAV_BACK_X + (NAV_BACK_WIDTH - NAV_ARROW_WIDTH) / 2,
537-
NAV_BACK_Y + (NAV_BACK_HEIGHT - NAV_ARROW_HEIGHT) / 2, NAV_ARROW_WIDTH, NAV_ARROW_HEIGHT,
575+
draw_triangle(NAV_BACK_X + (NAV_BACK_WIDTH - NAV_ARROW_WIDTH) / 2,
576+
NAV_BACK_Y + (NAV_BACK_HEIGHT - NAV_ARROW_HEIGHT) / 2, NAV_ARROW_WIDTH, NAV_ARROW_HEIGHT, TRIANGLE_LEFT,
538577
on_back ? COLOR_BLACK : COLOR_WHITE);
539578
}
540579
/* The confirm as a tick in the right slot, mirroring the arrow: same box,
@@ -561,8 +600,7 @@ static void draw_nav_header(const seedtool_nav_t* nav, const char* title)
561600
fill_rect(x, NAV_BACK_Y, NAV_BACK_WIDTH, 1, edge);
562601
fill_rect(x, NAV_BACK_Y, 1, NAV_BACK_HEIGHT, edge);
563602
fill_rect(x + NAV_BACK_WIDTH - 1, NAV_BACK_Y, 1, NAV_BACK_HEIGHT, edge);
564-
draw_tick(x + (NAV_BACK_WIDTH - NAV_ARROW_WIDTH) / 2,
565-
NAV_BACK_Y + (NAV_BACK_HEIGHT - NAV_ARROW_HEIGHT) / 2, NAV_ARROW_WIDTH, NAV_ARROW_HEIGHT,
603+
draw_tick(x + (NAV_BACK_WIDTH - NAV_TICK_WIDTH) / 2, NAV_BACK_Y + (NAV_BACK_HEIGHT - NAV_TICK_HEIGHT) / 2,
566604
!nav->confirm_enabled ? COLOR_DIM : on_tick ? COLOR_BLACK : COLOR_WHITE);
567605
}
568606
/* Centred between two margins the width of the arrow's box, not across the
@@ -821,11 +859,23 @@ static void draw_progress_bar(const seedtool_progress_t* progress)
821859
#define DIGIT_BOX_WIDTH 34
822860
#define DIGIT_BOX_HEIGHT 32
823861
#define DIGIT_BOX_GAP 6
824-
#define DIGIT_BOX_Y 36
825862
#define DIGIT_ARROW_WIDTH 11
826-
#define DIGIT_ARROW_HEIGHT 7
827-
#define DIGIT_ARROW_UP_Y 26
828-
#define DIGIT_ARROW_DOWN_Y 71
863+
/* Six rows, not seven: the half-width steps in whole pixels, so an 11-wide
864+
* triangle has exactly six distinct rows (1, 3, 5, 7, 9, 11). A seventh row
865+
* has no width left of its own and repeats one, which reads as a flat spot in
866+
* the edge rather than a straight diagonal. */
867+
#define DIGIT_ARROW_HEIGHT 6
868+
869+
/* An arrow, the boxes, an arrow: one stack, sitting at a different height on
870+
* each screen that draws it because what is under it differs - the value box
871+
* shares its screen with the entropy quality bar, the digit field has only the
872+
* footer. Derived from the stack's own parts, so changing an arrow's height
873+
* moves the stack rather than silently unbalancing it. */
874+
#define DIGIT_FIELD_GAP 4
875+
#define DIGIT_FIELD_HEIGHT (2 * (DIGIT_ARROW_HEIGHT + DIGIT_FIELD_GAP) + DIGIT_BOX_HEIGHT)
876+
#define DIGIT_FIELD_TOP(bottom) (SCREEN_TITLE_BOTTOM + ((bottom) -SCREEN_TITLE_BOTTOM - DIGIT_FIELD_HEIGHT) / 2)
877+
#define DIGIT_FIELD_BOX_Y(top) ((top) + DIGIT_ARROW_HEIGHT + DIGIT_FIELD_GAP)
878+
#define DIGIT_FIELD_DOWN_Y(top) (DIGIT_FIELD_BOX_Y(top) + DIGIT_BOX_HEIGHT + DIGIT_FIELD_GAP)
829879

830880
/* Held rather than eyeballed. The widest field is one box per digit a word
831881
* number can have, and it is centred, so a box or gap grown by a few pixels
@@ -836,22 +886,14 @@ static void draw_progress_bar(const seedtool_progress_t* progress)
836886
_Static_assert(SEEDTOOL_DIGIT_BOXES_MAX * DIGIT_BOX_WIDTH + (SEEDTOOL_DIGIT_BOXES_MAX - 1) * DIGIT_BOX_GAP
837887
<= SEEDTOOL_DISPLAY_WIDTH,
838888
"the digit boxes are wider than the display");
839-
_Static_assert(DIGIT_ARROW_UP_Y + DIGIT_ARROW_HEIGHT <= DIGIT_BOX_Y, "the up arrow runs into the digit boxes");
840-
_Static_assert(DIGIT_BOX_Y + DIGIT_BOX_HEIGHT <= DIGIT_ARROW_DOWN_Y, "the digit boxes run into the down arrow");
841-
_Static_assert(DIGIT_ARROW_DOWN_Y + DIGIT_ARROW_HEIGHT <= LIST_FOOTER_Y, "the down arrow runs into the footer");
842-
843-
/* A solid triangle, `up` pointing to the top. Drawn row by row like the
844-
* backspace glyph above rather than from a font, so it scales with the box
845-
* rather than with whatever glyph a face happens to carry. */
846-
static void draw_triangle(const int x, const int y, const bool up, const uint16_t color)
847-
{
848-
for (int row = 0; row < DIGIT_ARROW_HEIGHT; ++row) {
849-
/* Widest at the base, one pixel at the point. */
850-
const int from_point = up ? row : DIGIT_ARROW_HEIGHT - 1 - row;
851-
const int half = from_point * (DIGIT_ARROW_WIDTH / 2) / (DIGIT_ARROW_HEIGHT - 1);
852-
fill_rect(x + DIGIT_ARROW_WIDTH / 2 - half, y + row, 2 * half + 1, 1, color);
853-
}
854-
}
889+
/* Both placements, each against the thing that actually bounds it. */
890+
_Static_assert(DIGIT_FIELD_TOP(SCREEN_FOOTER_Y) >= SCREEN_TITLE_BOTTOM, "the digit field runs into the title");
891+
_Static_assert(DIGIT_FIELD_DOWN_Y(DIGIT_FIELD_TOP(SCREEN_FOOTER_Y)) + DIGIT_ARROW_HEIGHT <= SCREEN_FOOTER_Y,
892+
"the digit field runs into the footer");
893+
_Static_assert(DIGIT_FIELD_TOP(DICE_BAR_Y) >= SCREEN_TITLE_BOTTOM, "the value box runs into the title");
894+
_Static_assert(DIGIT_FIELD_DOWN_Y(DIGIT_FIELD_TOP(DICE_BAR_Y)) + DIGIT_ARROW_HEIGHT <= DICE_BAR_Y,
895+
"the value box runs into the quality bar");
896+
855897

856898
/* One wider box for a value scrolled whole rather than digit by digit: a die
857899
* face is 1-6 and a D20 is 1-20, so splitting it into per-digit boxes would
@@ -863,21 +905,27 @@ void seedtool_render_value_box(
863905
const char* title, const char* text, const bool back, const char* footer, const seedtool_progress_t* progress)
864906
{
865907
seedtool_render_clear();
866-
draw_centered(tft_Ubuntu16, title, 2);
908+
draw_centered(tft_Ubuntu16, title, SCREEN_TITLE_Y);
909+
/* Centred above the quality bar, not the footer: the bar is what shares
910+
* this screen, whether or not `progress` draws one this time - the box
911+
* should not jump between runs that grade their entropy and runs that do
912+
* not. */
913+
const int top = DIGIT_FIELD_TOP(DICE_BAR_Y);
914+
const int box_y = DIGIT_FIELD_BOX_Y(top);
867915
const int x = (SEEDTOOL_DISPLAY_WIDTH - VALUE_BOX_WIDTH) / 2;
868-
fill_rect(x, DIGIT_BOX_Y, VALUE_BOX_WIDTH, DIGIT_BOX_HEIGHT, COLOR_HIGHLIGHT);
869-
draw_border(x, DIGIT_BOX_Y, VALUE_BOX_WIDTH, DIGIT_BOX_HEIGHT, COLOR_HIGHLIGHT);
916+
fill_rect(x, box_y, VALUE_BOX_WIDTH, DIGIT_BOX_HEIGHT, COLOR_HIGHLIGHT);
917+
draw_border(x, box_y, VALUE_BOX_WIDTH, DIGIT_BOX_HEIGHT, COLOR_HIGHLIGHT);
870918
if (back) {
871919
draw_backspace(x + (VALUE_BOX_WIDTH - BACKSPACE_WIDTH) / 2,
872-
DIGIT_BOX_Y + (DIGIT_BOX_HEIGHT - BACKSPACE_HEIGHT) / 2, COLOR_BLACK, COLOR_HIGHLIGHT);
920+
box_y + (DIGIT_BOX_HEIGHT - BACKSPACE_HEIGHT) / 2, COLOR_BLACK, COLOR_HIGHLIGHT);
873921
} else {
874922
draw_centered_in(tft_Ubuntu16, text, x, VALUE_BOX_WIDTH,
875-
DIGIT_BOX_Y + (DIGIT_BOX_HEIGHT - tft_Ubuntu16[1]) / 2, COLOR_BLACK);
923+
box_y + (DIGIT_BOX_HEIGHT - tft_Ubuntu16[1]) / 2, COLOR_BLACK);
876924
}
877925
const int arrow_x = x + (VALUE_BOX_WIDTH - DIGIT_ARROW_WIDTH) / 2;
878-
draw_triangle(arrow_x, DIGIT_ARROW_UP_Y, true, COLOR_WHITE);
879-
draw_triangle(arrow_x, DIGIT_ARROW_DOWN_Y, false, COLOR_WHITE);
880-
draw_centered(tft_DefaultFont, footer, 111);
926+
draw_triangle(arrow_x, top, DIGIT_ARROW_WIDTH, DIGIT_ARROW_HEIGHT, TRIANGLE_UP, COLOR_WHITE);
927+
draw_triangle(arrow_x, DIGIT_FIELD_DOWN_Y(top), DIGIT_ARROW_WIDTH, DIGIT_ARROW_HEIGHT, TRIANGLE_DOWN, COLOR_WHITE);
928+
draw_centered(tft_DefaultFont, footer, SCREEN_FOOTER_Y);
881929
if (progress) {
882930
draw_progress_bar(progress);
883931
}
@@ -887,29 +935,33 @@ void seedtool_render_digits(const char* title, const char* digits, const size_t
887935
const char* footer, const seedtool_progress_t* progress)
888936
{
889937
seedtool_render_clear();
890-
draw_centered(tft_Ubuntu16, title, 2);
938+
draw_centered(tft_Ubuntu16, title, SCREEN_TITLE_Y);
891939

940+
/* Nothing under this field but the footer, so it centres in that whole
941+
* band rather than sharing the value box's higher placement. */
942+
const int top = DIGIT_FIELD_TOP(SCREEN_FOOTER_Y);
943+
const int box_y = DIGIT_FIELD_BOX_Y(top);
892944
const int span = (int)count * DIGIT_BOX_WIDTH + ((int)count - 1) * DIGIT_BOX_GAP;
893945
const int left = (SEEDTOOL_DISPLAY_WIDTH - span) / 2;
894946
for (size_t i = 0; i < count; ++i) {
895947
const int x = left + (int)i * (DIGIT_BOX_WIDTH + DIGIT_BOX_GAP);
896948
const bool selected = i == active;
897949
const bool set = i < active;
898950
if (selected) {
899-
fill_rect(x, DIGIT_BOX_Y, DIGIT_BOX_WIDTH, DIGIT_BOX_HEIGHT, COLOR_HIGHLIGHT);
951+
fill_rect(x, box_y, DIGIT_BOX_WIDTH, DIGIT_BOX_HEIGHT, COLOR_HIGHLIGHT);
900952
}
901-
draw_border(x, DIGIT_BOX_Y, DIGIT_BOX_WIDTH, DIGIT_BOX_HEIGHT,
953+
draw_border(x, box_y, DIGIT_BOX_WIDTH, DIGIT_BOX_HEIGHT,
902954
selected ? COLOR_HIGHLIGHT : set ? COLOR_WHITE : COLOR_DIM);
903955

904956
const char shown = digits[i];
905-
const int glyph_y = DIGIT_BOX_Y + (DIGIT_BOX_HEIGHT - tft_Ubuntu16[1]) / 2;
957+
const int glyph_y = box_y + (DIGIT_BOX_HEIGHT - tft_Ubuntu16[1]) / 2;
906958
if (shown == SEEDTOOL_KEY_ACCEPT) {
907959
draw_centered_in(tft_Ubuntu16, "OK", x, DIGIT_BOX_WIDTH, glyph_y, selected ? COLOR_BLACK : COLOR_WHITE);
908960
} else if (shown == SEEDTOOL_KEY_BACKSPACE) {
909961
/* The way out of the field rides the same ring as the digits, so
910962
* the box shows the backspace glyph the keypad already uses for
911963
* it rather than a letter the reader has to decode. */
912-
draw_backspace(x + (DIGIT_BOX_WIDTH - BACKSPACE_WIDTH) / 2, DIGIT_BOX_Y + (DIGIT_BOX_HEIGHT - BACKSPACE_HEIGHT) / 2,
964+
draw_backspace(x + (DIGIT_BOX_WIDTH - BACKSPACE_WIDTH) / 2, box_y + (DIGIT_BOX_HEIGHT - BACKSPACE_HEIGHT) / 2,
913965
COLOR_BLACK, COLOR_HIGHLIGHT);
914966
} else if (shown && shown != ' ') {
915967
const char text[2] = { shown, '\0' };
@@ -918,12 +970,13 @@ void seedtool_render_digits(const char* title, const char* digits, const size_t
918970

919971
if (selected) {
920972
const int arrow_x = x + (DIGIT_BOX_WIDTH - DIGIT_ARROW_WIDTH) / 2;
921-
draw_triangle(arrow_x, DIGIT_ARROW_UP_Y, true, COLOR_WHITE);
922-
draw_triangle(arrow_x, DIGIT_ARROW_DOWN_Y, false, COLOR_WHITE);
973+
draw_triangle(arrow_x, top, DIGIT_ARROW_WIDTH, DIGIT_ARROW_HEIGHT, TRIANGLE_UP, COLOR_WHITE);
974+
draw_triangle(
975+
arrow_x, DIGIT_FIELD_DOWN_Y(top), DIGIT_ARROW_WIDTH, DIGIT_ARROW_HEIGHT, TRIANGLE_DOWN, COLOR_WHITE);
923976
}
924977
}
925978

926-
draw_centered(tft_DefaultFont, footer, 111);
979+
draw_centered(tft_DefaultFont, footer, SCREEN_FOOTER_Y);
927980
if (progress) {
928981
draw_progress_bar(progress);
929982
}

0 commit comments

Comments
 (0)