Crossword State
Status: Deprecated (Jan 6, 2024)
Changes
NOTE: There have been some substantial updates to this struct since this was authored. Most of the information included is not wrong, but it’s insufficient to describe this object. In particular:
XwordState
has now been renamed toGridState
to more closely align with what it is.We capture more than just the cursor position in the struct, as selection and quirks have been added to the state.
For the editor, more state is needed to be stored. This is kept in the
EditState
object (which itself contains multipleGridStates
The
load_state()
/update_state()
pair has been simplified with just a singleupdate()
method.
Original Doc
Our goal with this game is to keep the playing board (and Crossword Editor) as stateless as possible. To implement this, we have the following invariants we adhere to:
We encapsulate the current state completely in
XwordState
. This is enough to turn any widget to a known state, independent of path taken to get there.All
Play*
widgets andEdit*
widgets should be able to recreate their state completely.Only the toplevels (
PlayXword
andEditWindow
) keeps a copy of that puzzle. Nothing else does.After loading the puzzle, we load a separate
IpuzSaved
grid from saved file, or the puzzle file itself if it exists. Otherwise, a new one is created. This is used to store any guesses the user makes.Each
Play*
widget has_load_state()
and_update_state()
functions that will take aPuzzleState
boxed and update its visual state based on it. They will pass it on to their children._load_state()
is called once the puzzle is loaded and sets up widgets based on the puzzle._update_state()
is called multiple times._update_state()
should queue_redraws where appropriate.No
Play*
widget should cache aPuzzleState
or IpuzCrossword. It copies any appropriate identifying information directly into its widgets.PlayCells
will keep their coordinates;PlayClueRows
will keep a copy of theirIpuzClues
.Each
Play*
widget will emit a signal indicating if any change in the state has occurred. Current valid signals are :"PlayGrid::guess"
– Text changed in a PlayCell"PlayGrid::focus-changed"
– A potential move of the focus (from an arrow key or Tab)"PlayGrid::cell-selected"
– A click on a cell"PlayClues::clue-selected"
– A click on a clueWhen those changes occur, they bubble up to the
PlayWindow
. ThePlayWindow
is solely responsible for forwarding changes to the state as appropriate. Once updated, it will call_update_state()
again to propagate down.
Invariants
The only code that updates the board lives in
XwordState
.PlayWindow is responsible for forwarding events to the
XwordState
The XwordState keeps a copy of both the clue and the current cell for convenience. The cell must be in the clue’s cell extents.
Focus
One result of this is we are completely replacing GTK’s focus handling with our own. It’s close to correct, but not good enough and it will fight with the state handling.
Only PlayCell
s can have focus right now (along with the menu). The Clues
cannot. We will have to confirm this works with Accessibility.
As a result, PlayCell captures all focus keybindings and propagates that up
to the top. XwordState
figures out the new focused widget.
A new PlayCell
calls grab_focus()
every time we set the state, which will
pull focus away from any other theoretical other widget. Time will tell if this
becomes a problem.