shithub: puzzles

Download patch

ref: 534384e5de95e8d4d0f60277befc922b6796b705
parent: 229d062d6ce63f0a5e00d2de62ee0fb389ccfdb6
author: Simon Tatham <anakin@pobox.com>
date: Thu Jan 27 13:25:14 EST 2022

Mosaic: fix inconsistently drawn keyboard cursor.

Every call to draw_cell() was drawing a region including the whole
border of the cell, so that the calls overlapped. So if the cursor
moved left or up, then a COL_CURSOR outline would be drawn around the
new cell, and then a COL_GRID outline would be drawn around the old
cell, overwriting part of the cursor border.

I've fixed this in the rigorous way, by making draw_cell() calls cover
disjoint areas of the puzzle canvas, and using clip() to enforce that.
So now the single DRAWFLAG_CURSOR is replaced by a system of four
flags, indicating that the cell being drawn is the actual cursor
position, or the cell below it (hence containing the cursor's bottom
border), or to its right (needing the left border), or below _and_ to
the right (you still need the single pixel at the cursor's bottom
right corner!).

Also, to ensure the cursor edges are drawn even on the bottom or right
grid boundaries, draw_cell() is called for a set of virtual cells
beyond the actual grid bounds, with additional flags telling it not to
draw an actual puzzle cell there, just the relevant pieces of border.

--- a/mosaic.c
+++ b/mosaic.c
@@ -1405,7 +1405,12 @@
 }
 
 /* Extra flags in game_drawstate entries, not in main game state */
-#define DRAWFLAG_CURSOR 0x100
+#define DRAWFLAG_CURSOR    0x100
+#define DRAWFLAG_CURSOR_U  0x200
+#define DRAWFLAG_CURSOR_L  0x400
+#define DRAWFLAG_CURSOR_UL 0x800
+#define DRAWFLAG_MARGIN_R  0x1000
+#define DRAWFLAG_MARGIN_D  0x2000
 
 static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
 {
@@ -1414,8 +1419,8 @@
 
     ds->tilesize = 0;
     ds->state = NULL;
-    ds->state = snewn(state->width * state->height, int);
-    for (i = 0; i < state->width * state->height; i++)
+    ds->state = snewn((state->width + 1) * (state->height + 1), int);
+    for (i = 0; i < (state->width + 1) * (state->height + 1); i++)
         ds->state[i] = -1;
 
     return ds;
@@ -1433,33 +1438,46 @@
     int startX = ((x * ts) + ts / 2) - 1, startY = ((y * ts) + ts / 2) - 1;
     int color, text_color = COL_TEXT_DARK;
 
-    draw_rect_outline(dr, startX - 1, startY - 1, ts + 1, ts + 1,
-                      (cell & DRAWFLAG_CURSOR) ? COL_CURSOR : COL_GRID);
+    clip(dr, startX - 1, startY - 1, ts, ts);
+    if (!(cell & DRAWFLAG_MARGIN_R))
+        draw_rect(dr, startX - 1, startY - 1, ts, 1,
+                  (cell & (DRAWFLAG_CURSOR | DRAWFLAG_CURSOR_U) ?
+                   COL_CURSOR : COL_GRID));
+    if (!(cell & DRAWFLAG_MARGIN_D))
+        draw_rect(dr, startX - 1, startY - 1, 1, ts,
+                  (cell & (DRAWFLAG_CURSOR | DRAWFLAG_CURSOR_L) ?
+                   COL_CURSOR : COL_GRID));
+    if (cell & DRAWFLAG_CURSOR_UL)
+        draw_rect(dr, startX - 1, startY - 1, 1, 1, COL_CURSOR);
 
-    if (cell & STATE_MARKED) {
-        color = COL_MARKED;
-        text_color = COL_TEXT_LIGHT;
-    } else if (cell & STATE_BLANK) {
-        text_color = COL_TEXT_DARK;
-        color = COL_BLANK;
-    } else {
-        text_color = COL_TEXT_DARK;
-        color = COL_UNMARKED;
-    }
-    if (cell & STATE_ERROR) {
-        text_color = COL_ERROR;
-    } else if (cell & STATE_SOLVED) {
-        text_color = COL_TEXT_SOLVED;
-    }
+    if (!(cell & (DRAWFLAG_MARGIN_R | DRAWFLAG_MARGIN_D))) {
+        if (cell & STATE_MARKED) {
+            color = COL_MARKED;
+            text_color = COL_TEXT_LIGHT;
+        } else if (cell & STATE_BLANK) {
+            text_color = COL_TEXT_DARK;
+            color = COL_BLANK;
+        } else {
+            text_color = COL_TEXT_DARK;
+            color = COL_UNMARKED;
+        }
+        if (cell & STATE_ERROR) {
+            text_color = COL_ERROR;
+        } else if (cell & STATE_SOLVED) {
+            text_color = COL_TEXT_SOLVED;
+        }
 
-    draw_rect(dr, startX, startY, ts - 1, ts - 1, color);
-    if (clue_val >= 0) {
-        char clue[80];
-        sprintf(clue, "%d", clue_val);
-        draw_text(dr, startX + ts / 2, startY + ts / 2, 1, ts * 3 / 5,
-                  ALIGN_VCENTRE | ALIGN_HCENTRE, text_color, clue);
+        draw_rect(dr, startX, startY, ts - 1, ts - 1, color);
+        if (clue_val >= 0) {
+            char clue[80];
+            sprintf(clue, "%d", clue_val);
+            draw_text(dr, startX + ts / 2, startY + ts / 2, 1, ts * 3 / 5,
+                      ALIGN_VCENTRE | ALIGN_HCENTRE, text_color, clue);
+        }
     }
-    draw_update(dr, startX, startY, ts - 1, ts - 1);
+
+    unclip(dr);
+    draw_update(dr, startX - 1, startY - 1, ts, ts);
 }
 
 static void game_redraw(drawing *dr, game_drawstate *ds,
@@ -1474,15 +1492,30 @@
     bool flashing = (flashtime > 0 && (flashtime <= FLASH_TIME / 3 ||
                                        flashtime > 2*FLASH_TIME / 3));
 
-    for (y = 0; y < state->height; y++) {
-        for (x = 0; x < state->width; x++) {
-            int cell = state->cells_contents[(y * state->width) + x];
+    for (y = 0; y <= state->height; y++) {
+        for (x = 0; x <= state->width; x++) {
+            bool inbounds = x < state->width && y < state->height;
+            int cell = (inbounds ?
+                        state->cells_contents[(y * state->width) + x] : 0);
+            if (x == state->width)
+                cell |= DRAWFLAG_MARGIN_R;
+            if (y == state->height)
+                cell |= DRAWFLAG_MARGIN_D;
             if (flashing)
                 cell ^= (STATE_BLANK | STATE_MARKED);
-            if (ui->cur_visible && ui->cur_x == x && ui->cur_y == y)
-                cell |= DRAWFLAG_CURSOR;
+            if (ui->cur_visible) {
+                if (ui->cur_x == x && ui->cur_y == y)
+                    cell |= DRAWFLAG_CURSOR;
+                if (ui->cur_x == x-1 && ui->cur_y == y)
+                    cell |= DRAWFLAG_CURSOR_L;
+                if (ui->cur_x == x && ui->cur_y == y-1)
+                    cell |= DRAWFLAG_CURSOR_U;
+                if (ui->cur_x == x-1 && ui->cur_y == y-1)
+                    cell |= DRAWFLAG_CURSOR_UL;
+            }
 
-            if (state->board->actual_board[(y * state->width) + x].shown) {
+            if (inbounds &&
+                state->board->actual_board[(y * state->width) + x].shown) {
                 clue_val = state->board->actual_board[
                     (y * state->width) + x].clue;
             } else {
@@ -1489,9 +1522,9 @@
                 clue_val = -1;
             }
 
-            if (ds->state[(y * state->width) + x] != cell) {
+            if (ds->state[(y * (state->width+1)) + x] != cell) {
                 draw_cell(dr, cell, ds->tilesize, clue_val, x, y);
-                ds->state[(y * state->width) + x] = cell;
+                ds->state[(y * (state->width+1)) + x] = cell;
             }
         }
     }