# GridState Modes refactor, take 2 **Status**: Implemented | Jan 2024 **Status**: Updated | Dec 2024 **See also**: xword-state-modes.md ## Summary Supplement the `GridStateMode` enum with a set of distinct behaviors. These behaviors can be set individually on a state. This will make grid-state.c easier to understand and debug (and test), as well as let us create modes on demand later on. ## Rationale The current grid modes have been created on demand when we need a new grid type. The modes combine some rendering options, cursor behaviors, and thematic functionality all in one. They are hard to reason about, and have proven fragile and brittle over time. ## Proposal We decompose the current _modes_ into a set of _behaviors_ that are independently settable. The current modes are: ```C typedef enum { GRID_STATE_SOLVE, /* @ Solve a crossword @ */ GRID_STATE_BROWSE, /* @ Browse through a board without modifying it @ */ GRID_STATE_EDIT, /* @ Edit the grid @ */ GRID_STATE_EDIT_BROWSE, /* @ Browse through an editable board without modifying it @ */ GRID_STATE_SELECT, /* @ Select cells @ */ GRID_STATE_VIEW, /* @ Display a board with no interaction @ */ } GridStateMode; ``` ## Tenets The behaviors indicate how the grid behaves. We have the following rules for behaviors: * Each behavior acts independently, and shouldn't depend on another behavior. That is to say, we never have `if (behavior1 && behavior2) {...` in the code. `||` statements are fine. * We add new behaviors rather than special casing the code. While occasionally complex to implement, it's better to add a new one rather than overloading them. * Modes are just a convenience. It's possible and plausible to just use a set of behaviors. ### Decomposition Here's the decomposition into behaviors: | | **SOLVE** | **BROWSE** | **EDIT** | **EDIT_BROWSE** | **SELECT** | **VIEW** | |--------------------|-----------|------------|----------|-----------------|------------|----------| | **USE_CURSOR** | _true_ | _true_ | _true_ | _true_ | _true_ | _false_ | | **SHOW_GUESS** | _true_ | _true_ | _false_ | _false_ | _true_ | _false_ | | **SELECTABLE** | _false_ | _false_ | _false_ | _false_ | _true_ | _false_ | | **EDIT_CELLS** | _true_ | _false_ | _true_ | _false_ | _false_ | _false_ | | **NORMAL_ONLY** | _true_ | _true_ | _false_ | _true_ | _true_ | _false_ | | **QUIRKS_ADVANCE** | _true_ | _false_ | _false_ | _false_ | _false_ | _false_ | | **EMIT_DRAG** | _false_ | _false_ | _true_ | _false_ | _true_ | _false_ | Each behavior is described below: * **USE_CURSOR:** The state has a valid cursor, and a direction * **SHOW_GUESS:** The grid displays the guess instead of the solution in a cell * **SELECTABLE:** The grid can select cells * **EDIT_CELLS:** The grid allows the user to edit the cell * **NORMAL_ONLY:** The grid only allows the cursor to exist on normal cells. BLOCK and NULL cells can't have a cursor set * **QUIRKS_ADVANCE:** Whether to honor the quirks GuessAdvance setting when going to the next cell after a guess. * **EMIT_DRAG:** Emit the drag signals. This can be used to either trigger a selection, or update an existing one. This set of booleans is represented by the `GridState.behavior` bitfield. Setting the mode will set the bitfield to a well known state. Changing the behavior afterwards is possible. We will add a `GRID_STATE_CUSTOM` mode to capture when that has happened. > **NOTE:** Not all combinations of behaviors are valid ## Other Considerations * Selection may need multiple selection types in the future (like `GtkEditable`, for instance.) * We want to move an overlay mode for things like _reveal errors_, pencil markings, or heat maps. That might factor into this design.