Update Sixteen keyboard system for faster entry.

Pressing Ctrl-arrow or Shift-arrow on a tile now moves the row or
column under the tile. With Ctrl, the cursor moves as well so you can
keep making moves that affect the same tile; with Shift, the cursor
stays put so you can keep making moves that affect the same location.
This commit is contained in:
Jonas Kölker
2015-10-01 16:44:43 +02:00
committed by Simon Tatham
parent d0500732f7
commit fcf573c672
2 changed files with 100 additions and 19 deletions

View File

@ -662,6 +662,13 @@ Alternatively, use the cursor keys to move the position indicator
around the edge of the grid, and use the return key to move the around the edge of the grid, and use the return key to move the
row/column in the direction indicated. row/column in the direction indicated.
You can also move the tiles directly. Move the cursor onto a tile,
hold Control and press an arrow key to move the tile under the
cursor and move the cursor along with the tile. Or, hold Shift to
move only the tile. Pressing Enter simulates holding down Control
(press Enter again to release), while pressing Space simulates
holding down shift.
(All the actions described in \k{common-actions} are also available.) (All the actions described in \k{common-actions} are also available.)
\H{sixteen-params} \I{parameters, for Sixteen}Sixteen parameters \H{sixteen-params} \I{parameters, for Sixteen}Sixteen parameters

112
sixteen.c
View File

@ -27,6 +27,9 @@
#define Y(state, i) ( (i) / (state)->w ) #define Y(state, i) ( (i) / (state)->w )
#define C(state, x, y) ( (y) * (state)->w + (x) ) #define C(state, x, y) ( (y) * (state)->w + (x) )
#define TILE_CURSOR(i, state, x, y) ((i) == C((state), (x), (y)) && \
0 <= (x) && (x) < (state)->w && \
0 <= (y) && (y) < (state)->h)
enum { enum {
COL_BACKGROUND, COL_BACKGROUND,
COL_TEXT, COL_TEXT,
@ -555,24 +558,28 @@ static char *game_text_format(const game_state *state)
return ret; return ret;
} }
enum cursor_mode { unlocked, lock_tile, lock_position };
struct game_ui { struct game_ui {
int cur_x, cur_y; int cur_x, cur_y;
int cur_visible; int cur_visible;
enum cursor_mode cur_mode;
}; };
static game_ui *new_ui(const game_state *state) static game_ui *new_ui(const game_state *state)
{ {
game_ui *ui = snew(game_ui); game_ui *ui = snew(game_ui);
ui->cur_x = 0; ui->cur_x = 0;
ui->cur_y = -1; ui->cur_y = 0;
ui->cur_visible = FALSE; ui->cur_visible = FALSE;
ui->cur_mode = unlocked;
return ui; return ui;
} }
static void free_ui(game_ui *ui) static void free_ui(game_ui *ui)
{ {
sfree(ui); sfree(ui);
} }
static char *encode_ui(const game_ui *ui) static char *encode_ui(const game_ui *ui)
@ -603,21 +610,71 @@ static char *interpret_move(const game_state *state, game_ui *ui,
{ {
int cx = -1, cy = -1, dx, dy; int cx = -1, cy = -1, dx, dy;
char buf[80]; char buf[80];
int shift = button & MOD_SHFT, control = button & MOD_CTRL,
pad = button & MOD_NUM_KEYPAD;
button &= ~MOD_MASK; button &= ~MOD_MASK;
if (IS_CURSOR_MOVE(button)) { if (IS_CURSOR_MOVE(button) || pad) {
/* right/down rotates cursor clockwise, if (!ui->cur_visible) {
* left/up rotates anticlockwise. */ ui->cur_visible = 1;
int cpos, diff; return "";
cpos = c2pos(state->w, state->h, ui->cur_x, ui->cur_y); }
diff = c2diff(state->w, state->h, ui->cur_x, ui->cur_y, button);
cpos += diff; if (control || shift || ui->cur_mode) {
pos2c(state->w, state->h, cpos, &ui->cur_x, &ui->cur_y); int x = ui->cur_x, y = ui->cur_y, xwrap = x, ywrap = y;
if (x < 0 || x >= state->w || y < 0 || y >= state->h)
return NULL;
move_cursor(button | pad, &x, &y,
state->w, state->h, FALSE);
move_cursor(button | pad, &xwrap, &ywrap,
state->w, state->h, TRUE);
ui->cur_visible = 1; if (x != xwrap) {
return ""; sprintf(buf, "R%d,%c1", y, x ? '+' : '-');
} else if (y != ywrap) {
sprintf(buf, "C%d,%c1", x, y ? '+' : '-');
} else if (x == ui->cur_x)
sprintf(buf, "C%d,%d", x, y - ui->cur_y);
else
sprintf(buf, "R%d,%d", y, x - ui->cur_x);
if (control || (!shift && ui->cur_mode == lock_tile)) {
ui->cur_x = xwrap;
ui->cur_y = ywrap;
}
return dupstr(buf);
} else {
int x = ui->cur_x + 1, y = ui->cur_y + 1;
move_cursor(button | pad, &x, &y,
state->w + 2, state->h + 2, FALSE);
if (x == 0 && y == 0) {
int t = ui->cur_x;
ui->cur_x = ui->cur_y;
ui->cur_y = t;
} else if (x == 0 && y == state->h + 1) {
int t = ui->cur_x;
ui->cur_x = (state->h - 1) - ui->cur_y;
ui->cur_y = (state->h - 1) - t;
} else if (x == state->w + 1 && y == 0) {
int t = ui->cur_x;
ui->cur_x = (state->w - 1) - ui->cur_y;
ui->cur_y = (state->w - 1) - t;
} else if (x == state->w + 1 && y == state->h + 1) {
int t = ui->cur_x;
ui->cur_x = state->w - state->h + ui->cur_y;
ui->cur_y = state->h - state->w + t;
} else {
ui->cur_x = x - 1;
ui->cur_y = y - 1;
}
ui->cur_visible = 1;
return "";
}
} }
if (button == LEFT_BUTTON || button == RIGHT_BUTTON) { if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
@ -626,8 +683,16 @@ static char *interpret_move(const game_state *state, game_ui *ui,
ui->cur_visible = 0; ui->cur_visible = 0;
} else if (IS_CURSOR_SELECT(button)) { } else if (IS_CURSOR_SELECT(button)) {
if (ui->cur_visible) { if (ui->cur_visible) {
cx = ui->cur_x; if (ui->cur_x == -1 || ui->cur_x == state->w ||
cy = ui->cur_y; ui->cur_y == -1 || ui->cur_y == state->h) {
cx = ui->cur_x;
cy = ui->cur_y;
} else {
const enum cursor_mode m = (button == CURSOR_SELECT2 ?
lock_position : lock_tile);
ui->cur_mode = (ui->cur_mode == m ? unlocked : m);
return "";
}
} else { } else {
ui->cur_visible = 1; ui->cur_visible = 1;
return ""; return "";
@ -853,7 +918,7 @@ static void draw_arrow_for_cursor(drawing *dr, game_drawstate *ds,
else if (cur_y == ds->h) /* Bottom row */ else if (cur_y == ds->h) /* Bottom row */
draw_arrow(dr, ds, COORD(cur_x+1), COORD(ds->h), -1, 0, cur); draw_arrow(dr, ds, COORD(cur_x+1), COORD(ds->h), -1, 0, cur);
else else
assert(!"Invalid cursor position"); return;
draw_update(dr, COORD(cur_x), COORD(cur_y), draw_update(dr, COORD(cur_x), COORD(cur_y),
TILE_SIZE, TILE_SIZE); TILE_SIZE, TILE_SIZE);
@ -922,11 +987,11 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
if (ui->cur_visible) { if (ui->cur_visible) {
cur_x = ui->cur_x; cur_y = ui->cur_y; cur_x = ui->cur_x; cur_y = ui->cur_y;
} }
if (cur_x != ds->cur_x || cur_y != ds->cur_y) { if (cur_x != ds->cur_x || cur_y != ds->cur_y) {
/* Cursor has changed; redraw two (prev and curr) arrows. */ /* Cursor has changed; redraw two (prev and curr) arrows. */
draw_arrow_for_cursor(dr, ds, cur_x, cur_y, 1); draw_arrow_for_cursor(dr, ds, cur_x, cur_y, 1);
draw_arrow_for_cursor(dr, ds, ds->cur_x, ds->cur_y, 0); draw_arrow_for_cursor(dr, ds, ds->cur_x, ds->cur_y, 0);
ds->cur_x = cur_x; ds->cur_y = cur_y;
} }
/* /*
@ -952,8 +1017,11 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
t0 = t; t0 = t;
if (ds->bgcolour != bgcolour || /* always redraw when flashing */ if (ds->bgcolour != bgcolour || /* always redraw when flashing */
ds->tiles[i] != t || ds->tiles[i] == -1 || t == -1) { ds->tiles[i] != t || ds->tiles[i] == -1 || t == -1 ||
int x, y, x2, y2; ((ds->cur_x != cur_x || ds->cur_y != cur_y) && /* cursor moved */
(TILE_CURSOR(i, state, ds->cur_x, ds->cur_y) ||
TILE_CURSOR(i, state, cur_x, cur_y)))) {
int x, y, x2, y2;
/* /*
* Figure out what to _actually_ draw, and where to * Figure out what to _actually_ draw, and where to
@ -1021,13 +1089,19 @@ static void game_redraw(drawing *dr, game_drawstate *ds,
x2 = y2 = -1; x2 = y2 = -1;
} }
draw_tile(dr, ds, state, x, y, t, bgcolour); draw_tile(dr, ds, state, x, y, t,
(x2 == -1 && TILE_CURSOR(i, state, cur_x, cur_y)) ?
COL_LOWLIGHT : bgcolour);
if (x2 != -1 || y2 != -1) if (x2 != -1 || y2 != -1)
draw_tile(dr, ds, state, x2, y2, t, bgcolour); draw_tile(dr, ds, state, x2, y2, t, bgcolour);
} }
ds->tiles[i] = t0; ds->tiles[i] = t0;
} }
ds->cur_x = cur_x;
ds->cur_y = cur_y;
unclip(dr); unclip(dr);
ds->bgcolour = bgcolour; ds->bgcolour = bgcolour;