shithub: puzzles

Download patch

ref: a11ee53ef844522b48b06ddffed268fa86d7b2ec
parent: 503f1c4ab81928c2792a2cf73bc97587460dac86
author: Simon Tatham <anakin@pobox.com>
date: Sun Aug 13 10:03:26 EDT 2023

Keen, Solo, Towers, Undead, Unequal, Group: new UI preference.

If you're using the mouse to change pencil marks, you have to
right-click to pencil-highlight a square, then press a number or
letter key to add or remove a highlight. That causes the highlight to
vanish again. So adding or removing multiple pencil marks requires a
right-click + keypress per mark.

Chris's Android port reversed that decision, making the pencil
highlight persist so that you could 'click' just once and then press
multiple pencil keys. That makes it easier to add lots of highlights,
but harder to just remove a single one (click + press + click to
remove the highlight), unless you don't mind keeping the highlight
around afterwards cluttering up your view.

In other words, this is just the sort of thing users might reasonably
disagree on. So now we have an organised preferences system, we can
let them disagree, and each configure it whichever way they like!

This only affects mouse-based play. The keyboard cursor has _always_
worked this way, because it doesn't disappear at all; its behaviour is
unchanged, and independent of the new preference.

--- a/keen.c
+++ b/keen.c
@@ -1525,6 +1525,17 @@
      * allowed on immutable squares.
      */
     bool hcursor;
+
+    /*
+     * User preference option: if the user right-clicks in a square
+     * and presses a number key to add/remove a pencil mark, do we
+     * hide the mouse highlight again afterwards?
+     *
+     * Historically our answer was yes. The Android port prefers no.
+     * There are advantages both ways, depending how much you dislike
+     * the highlight cluttering your view. So it's a preference.
+     */
+    bool pencil_keep_highlight;
 };
 
 static game_ui *new_ui(const game_state *state)
@@ -1535,6 +1546,8 @@
     ui->hpencil = false;
     ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
 
+    ui->pencil_keep_highlight = false;
+
     return ui;
 }
 
@@ -1543,6 +1556,28 @@
     sfree(ui);
 }
 
+static config_item *get_prefs(game_ui *ui)
+{
+    config_item *ret;
+
+    ret = snewn(2, config_item);
+
+    ret[0].name = "Keep mouse highlight after changing a pencil mark";
+    ret[0].kw = "pencil-keep-highlight";
+    ret[0].type = C_BOOLEAN;
+    ret[0].u.boolean.bval = ui->pencil_keep_highlight;
+
+    ret[1].name = NULL;
+    ret[1].type = C_END;
+
+    return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+    ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
+}
+
 static void game_changed_state(game_ui *ui, const game_state *oldstate,
                                const game_state *newstate)
 {
@@ -1791,7 +1826,14 @@
 	sprintf(buf, "%c%d,%d,%d",
 		(char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
 
-        if (!ui->hcursor) ui->hshow = false;
+        /*
+         * Hide the highlight after a keypress, if it was mouse-
+         * generated. Also, don't hide it if this move has changed
+         * pencil marks and the user preference says not to hide the
+         * highlight in that situation.
+         */
+        if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
+            ui->hshow = false;
 
 	return dupstr(buf);
     }
@@ -2479,7 +2521,7 @@
     free_game,
     true, solve_game,
     false, NULL, NULL, /* can_format_as_text_now, text_format */
-    NULL, NULL, /* get_prefs, set_prefs */
+    get_prefs, set_prefs,
     new_ui,
     free_ui,
     NULL, /* encode_ui */
--- a/solo.c
+++ b/solo.c
@@ -4557,6 +4557,17 @@
      * allowed on immutable squares.
      */
     bool hcursor;
+
+    /*
+     * User preference option: if the user right-clicks in a square
+     * and presses a number or letter key to add/remove a pencil mark,
+     * do we hide the mouse highlight again afterwards?
+     *
+     * Historically our answer was yes. The Android port prefers no.
+     * There are advantages both ways, depending how much you dislike
+     * the highlight cluttering your view. So it's a preference.
+     */
+    bool pencil_keep_highlight;
 };
 
 static game_ui *new_ui(const game_state *state)
@@ -4567,6 +4578,8 @@
     ui->hpencil = false;
     ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
 
+    ui->pencil_keep_highlight = false;
+
     return ui;
 }
 
@@ -4575,6 +4588,28 @@
     sfree(ui);
 }
 
+static config_item *get_prefs(game_ui *ui)
+{
+    config_item *ret;
+
+    ret = snewn(2, config_item);
+
+    ret[0].name = "Keep mouse highlight after changing a pencil mark";
+    ret[0].kw = "pencil-keep-highlight";
+    ret[0].type = C_BOOLEAN;
+    ret[0].u.boolean.bval = ui->pencil_keep_highlight;
+
+    ret[1].name = NULL;
+    ret[1].type = C_END;
+
+    return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+    ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
+}
+
 static void game_changed_state(game_ui *ui, const game_state *oldstate,
                                const game_state *newstate)
 {
@@ -4723,7 +4758,14 @@
 	sprintf(buf, "%c%d,%d,%d",
 		(char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
 
-        if (!ui->hcursor) ui->hshow = false;
+        /*
+         * Hide the highlight after a keypress, if it was mouse-
+         * generated. Also, don't hide it if this move has changed
+         * pencil marks and the user preference says not to hide the
+         * highlight in that situation.
+         */
+        if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
+            ui->hshow = false;
 
 	return dupstr(buf);
     }
@@ -5632,7 +5674,7 @@
     free_game,
     true, solve_game,
     true, game_can_format_as_text_now, game_text_format,
-    NULL, NULL, /* get_prefs, set_prefs */
+    get_prefs, set_prefs,
     new_ui,
     free_ui,
     NULL, /* encode_ui */
--- a/towers.c
+++ b/towers.c
@@ -1174,6 +1174,17 @@
      * it in just in case.
      */
     int three_d;
+
+    /*
+     * User preference option: if the user right-clicks in a square
+     * and presses a number key to add/remove a pencil mark, do we
+     * hide the mouse highlight again afterwards?
+     *
+     * Historically our answer was yes. The Android port prefers no.
+     * There are advantages both ways, depending how much you dislike
+     * the highlight cluttering your view. So it's a preference.
+     */
+    bool pencil_keep_highlight;
 };
 
 static void legacy_prefs_override(struct game_ui *ui_out)
@@ -1199,6 +1210,7 @@
     ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
 
     ui->three_d = true;
+    ui->pencil_keep_highlight = false;
     legacy_prefs_override(ui);
 
     return ui;
@@ -1213,24 +1225,30 @@
 {
     config_item *ret;
 
-    ret = snewn(2, config_item);
+    ret = snewn(3, config_item);
 
-    ret[0].name = "Puzzle appearance";
-    ret[0].kw = "appearance";
-    ret[0].type = C_CHOICES;
-    ret[0].u.choices.choicenames = ":2D:3D";
-    ret[0].u.choices.choicekws = ":2d:3d";
-    ret[0].u.choices.selected = ui->three_d;
+    ret[0].name = "Keep mouse highlight after changing a pencil mark";
+    ret[0].kw = "pencil-keep-highlight";
+    ret[0].type = C_BOOLEAN;
+    ret[0].u.boolean.bval = ui->pencil_keep_highlight;
 
-    ret[1].name = NULL;
-    ret[1].type = C_END;
+    ret[1].name = "Puzzle appearance";
+    ret[1].kw = "appearance";
+    ret[1].type = C_CHOICES;
+    ret[1].u.choices.choicenames = ":2D:3D";
+    ret[1].u.choices.choicekws = ":2d:3d";
+    ret[1].u.choices.selected = ui->three_d;
 
+    ret[2].name = NULL;
+    ret[2].type = C_END;
+
     return ret;
 }
 
 static void set_prefs(game_ui *ui, const config_item *cfg)
 {
-    ui->three_d = cfg[0].u.choices.selected;
+    ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
+    ui->three_d = cfg[1].u.choices.selected;
 }
 
 static void game_changed_state(game_ui *ui, const game_state *oldstate,
@@ -1550,7 +1568,14 @@
 	sprintf(buf, "%c%d,%d,%d",
 		(char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
 
-        if (!ui->hcursor) ui->hshow = false;
+        /*
+         * Hide the highlight after a keypress, if it was mouse-
+         * generated. Also, don't hide it if this move has changed
+         * pencil marks and the user preference says not to hide the
+         * highlight in that situation.
+         */
+        if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
+            ui->hshow = false;
 
 	return dupstr(buf);
     }
--- a/undead.c
+++ b/undead.c
@@ -1644,6 +1644,17 @@
     int hx, hy;                         /* as for solo.c, highlight pos */
     bool hshow, hpencil, hcursor;       /* show state, type, and ?cursor. */
     bool ascii;
+
+    /*
+     * User preference option: if the user right-clicks in a square
+     * and presses a monster key to add/remove a pencil mark, do we
+     * hide the mouse highlight again afterwards?
+     *
+     * Historically our answer was yes. The Android port prefers no.
+     * There are advantages both ways, depending how much you dislike
+     * the highlight cluttering your view. So it's a preference.
+     */
+    bool pencil_keep_highlight;
 };
 
 static game_ui *new_ui(const game_state *state)
@@ -1653,6 +1664,9 @@
     ui->hx = ui->hy = ui->hshow = ui->hcursor =
         getenv_bool("PUZZLES_SHOW_CURSOR", false);
     ui->ascii = false;
+
+    ui->pencil_keep_highlight = false;
+
     return ui;
 }
 
@@ -1660,24 +1674,30 @@
 {
     config_item *ret;
 
-    ret = snewn(2, config_item);
+    ret = snewn(3, config_item);
 
-    ret[0].name = "Monster representation";
-    ret[0].kw = "monsters";
-    ret[0].type = C_CHOICES;
-    ret[0].u.choices.choicenames = ":Pictures:Letters";
-    ret[0].u.choices.choicekws = ":pictures:letters";
-    ret[0].u.choices.selected = ui->ascii;
+    ret[0].name = "Keep mouse highlight after changing a pencil mark";
+    ret[0].kw = "pencil-keep-highlight";
+    ret[0].type = C_BOOLEAN;
+    ret[0].u.boolean.bval = ui->pencil_keep_highlight;
 
-    ret[1].name = NULL;
-    ret[1].type = C_END;
+    ret[1].name = "Monster representation";
+    ret[1].kw = "monsters";
+    ret[1].type = C_CHOICES;
+    ret[1].u.choices.choicenames = ":Pictures:Letters";
+    ret[1].u.choices.choicekws = ":pictures:letters";
+    ret[1].u.choices.selected = ui->ascii;
 
+    ret[2].name = NULL;
+    ret[2].type = C_END;
+
     return ret;
 }
 
 static void set_prefs(game_ui *ui, const config_item *cfg)
 {
-    ui->ascii = cfg[0].u.choices.selected;
+    ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
+    ui->ascii = cfg[1].u.choices.selected;
 }
 
 static void free_ui(game_ui *ui) {
@@ -1840,39 +1860,34 @@
     if (ui->hshow && ui->hpencil) {
         xi = state->common->xinfo[ui->hx + ui->hy*(state->common->params.w+2)];
         if (xi >= 0 && !state->common->fixed[xi]) {
+            buf[0] = '\0';
+
             if (button == 'g' || button == 'G' || button == '1') {
                 sprintf(buf,"g%d",xi);
-                if (!ui->hcursor) {
-                    ui->hpencil = false;
-                    ui->hshow = false;
-                }
-                return dupstr(buf);
-            }
-            if (button == 'v' || button == 'V' || button == '2') {
+            } else if (button == 'v' || button == 'V' || button == '2') {
                 sprintf(buf,"v%d",xi);
-                if (!ui->hcursor) {
-                    ui->hpencil = false;
-                    ui->hshow = false;
-                }
-                return dupstr(buf);
-            }
-            if (button == 'z' || button == 'Z' || button == '3') {
+            } else if (button == 'z' || button == 'Z' || button == '3') {
                 sprintf(buf,"z%d",xi);
-                if (!ui->hcursor) {
-                    ui->hpencil = false;
-                    ui->hshow = false;
-                }
-                return dupstr(buf);
+            } else if (button == 'e' || button == 'E' ||
+                       button == CURSOR_SELECT2 || button == '0' ||
+                       button == '\b') {
+                if (state->pencils[xi] == 0)
+                    return ui->hcursor ? NULL : MOVE_UI_UPDATE;
+                sprintf(buf,"E%d",xi);
             }
-            if (button == 'e' || button == 'E' || button == CURSOR_SELECT2 ||
-                button == '0' || button == '\b') {
-                if (!ui->hcursor) {
+
+            if (buf[0]) {
+                /*
+                 * Hide the highlight after a keypress, if it was mouse-
+                 * generated. Also, don't hide it if this move has changed
+                 * pencil marks and the user preference says not to hide the
+                 * highlight in that situation.
+                 */
+                if (!ui->hcursor &&
+                    !(ui->hpencil && ui->pencil_keep_highlight)) {
                     ui->hpencil = false;
                     ui->hshow = false;
                 }
-                if (state->pencils[xi] == 0)
-                    return ui->hcursor ? NULL : MOVE_UI_UPDATE;
-                sprintf(buf,"E%d",xi);
                 return dupstr(buf);
             }
         }       
--- a/unequal.c
+++ b/unequal.c
@@ -1436,6 +1436,17 @@
 struct game_ui {
     int hx, hy;                         /* as for solo.c, highlight pos */
     bool hshow, hpencil, hcursor;       /* show state, type, and ?cursor. */
+
+    /*
+     * User preference option: if the user right-clicks in a square
+     * and presses a number key to add/remove a pencil mark, do we
+     * hide the mouse highlight again afterwards?
+     *
+     * Historically our answer was yes. The Android port prefers no.
+     * There are advantages both ways, depending how much you dislike
+     * the highlight cluttering your view. So it's a preference.
+     */
+    bool pencil_keep_highlight;
 };
 
 static game_ui *new_ui(const game_state *state)
@@ -1446,6 +1457,8 @@
     ui->hpencil = false;
     ui->hshow = ui->hcursor = getenv_bool("PUZZLES_SHOW_CURSOR", false);
 
+    ui->pencil_keep_highlight = false;
+
     return ui;
 }
 
@@ -1454,6 +1467,28 @@
     sfree(ui);
 }
 
+static config_item *get_prefs(game_ui *ui)
+{
+    config_item *ret;
+
+    ret = snewn(2, config_item);
+
+    ret[0].name = "Keep mouse highlight after changing a pencil mark";
+    ret[0].kw = "pencil-keep-highlight";
+    ret[0].type = C_BOOLEAN;
+    ret[0].u.boolean.bval = ui->pencil_keep_highlight;
+
+    ret[1].name = NULL;
+    ret[1].type = C_END;
+
+    return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+    ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
+}
+
 static void game_changed_state(game_ui *ui, const game_state *oldstate,
                                const game_state *newstate)
 {
@@ -1632,7 +1667,14 @@
         sprintf(buf, "%c%d,%d,%d",
                 (char)(ui->hpencil && n > 0 ? 'P' : 'R'), ui->hx, ui->hy, n);
 
-        if (!ui->hcursor) ui->hshow = false;
+        /*
+         * Hide the highlight after a keypress, if it was mouse-
+         * generated. Also, don't hide it if this move has changed
+         * pencil marks and the user preference says not to hide the
+         * highlight in that situation.
+         */
+        if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
+            ui->hshow = false;
 
         return dupstr(buf);
     }
@@ -2155,7 +2197,7 @@
     free_game,
     true, solve_game,
     true, game_can_format_as_text_now, game_text_format,
-    NULL, NULL, /* get_prefs, set_prefs */
+    get_prefs, set_prefs,
     new_ui,
     free_ui,
     NULL, /* encode_ui */
--- a/unfinished/group.c
+++ b/unfinished/group.c
@@ -1252,6 +1252,17 @@
     int dragnum;                       /* element being dragged */
     int dragpos;                       /* its current position */
     int edgepos;
+
+    /*
+     * User preference option: if the user right-clicks in a square
+     * and presses a letter key to add/remove a pencil mark, do we
+     * hide the mouse highlight again afterwards?
+     *
+     * Historically our answer was yes. The Android port prefers no.
+     * There are advantages both ways, depending how much you dislike
+     * the highlight cluttering your view. So it's a preference.
+     */
+    bool pencil_keep_highlight;
 };
 
 static game_ui *new_ui(const game_state *state)
@@ -1264,6 +1275,8 @@
     ui->hcursor = false;
     ui->drag = 0;
 
+    ui->pencil_keep_highlight = false;
+
     return ui;
 }
 
@@ -1272,6 +1285,28 @@
     sfree(ui);
 }
 
+static config_item *get_prefs(game_ui *ui)
+{
+    config_item *ret;
+
+    ret = snewn(2, config_item);
+
+    ret[0].name = "Keep mouse highlight after changing a pencil mark";
+    ret[0].kw = "pencil-keep-highlight";
+    ret[0].type = C_BOOLEAN;
+    ret[0].u.boolean.bval = ui->pencil_keep_highlight;
+
+    ret[1].name = NULL;
+    ret[1].type = C_END;
+
+    return ret;
+}
+
+static void set_prefs(game_ui *ui, const config_item *cfg)
+{
+    ui->pencil_keep_highlight = cfg[0].u.boolean.bval;
+}
+
 static void game_changed_state(game_ui *ui, const game_state *oldstate,
                                const game_state *newstate)
 {
@@ -1676,7 +1711,14 @@
         }
         movebuf = sresize(movebuf, buflen+1, char);
 
-        if (!ui->hcursor) ui->hshow = false;
+        /*
+         * Hide the highlight after a keypress, if it was mouse-
+         * generated. Also, don't hide it if this move has changed
+         * pencil marks and the user preference says not to hide the
+         * highlight in that situation.
+         */
+        if (!ui->hcursor && !(ui->hpencil && ui->pencil_keep_highlight))
+            ui->hshow = false;
 
 	return movebuf;
     }
@@ -2323,7 +2365,7 @@
     free_game,
     true, solve_game,
     true, game_can_format_as_text_now, game_text_format,
-    NULL, NULL, /* get_prefs, set_prefs */
+    get_prefs, set_prefs,
     new_ui,
     free_ui,
     NULL, /* encode_ui */