# Crossword Editor Panel Components
**Status:** Deprecated | October 2023

**Reviewer:** @federico

## Summary

The crossword editor has migrated to over to use libpanel. As a
result, we have a large collection of `PanelWidgets` planned to go in
the frames around the outside of the dock, as well as some central
widgets. This document describes what they are and how they
interoperate.

### Scope

This is focused on the interface for Standard, Cryptic, and Barred
Crosswords. We'll use a similar approach but have a different set of
widgets for the other crossword types.

## Proposal

As of 0.3.11, we have a new user interface for `EditWindow`.

![Panel Widgets Overview](panel-widgets-overview.png)<br>
_Mockup of `EditWindow`_

Visually, the window consists of a grid **(1)** in the center
displaying the current puzzle. There are `PanelFrames` on the left
**(2)** and bottom **(3)** of the window. Each frame contains a number
of `PanelWidgets`, depending on the mode and puzzle type. On the top,
there is a `GtkStackSwitcher` **(4)** to switch between the different
stages of editing a puzzle, represented by
`PuzzleStackStage`. Currently, four stages are planned and all except
**STYLE** have some of its implementation written.

The first three stages (**GRID**, **EDIT**, and **STYLE**) all have a
similar looking grid in the middle. There's a slight change in
background color to give a quick visual hint to the current stage. The
final stage (**METADATA**) looks very different and doesn't use either
of the side frames.

The left frame has tools for mutating the puzzle — primarily editors
and navigation controls. It currently has a primary and secondary
area, and lets the user switch between different tools.

On the other hand, the bottom grid is purely informational. It
contains information about the current state, such as the definition
of the current word, or details about the grid. There may be many of
these during the `CLUE` stage, as they can be extremely useful for
writing clues.

### Editor components

| **Widget**             | **Stage**         | **Location** | **Description**                                                                            |
|------------------------|-------------------|--------------|--------------------------------------------------------------------------------------------|
| EditGrid               | GRID              | Center       | Displays the current puzzle grid and edit answers. **Changes cursor**                      |
| EditGrid               | EDIT              | Center       | Displays the current puzzle grid. **Changes cursor**                                       |
| _**EditGrid**_         | STYLE             | Center       | Displays the current puzzle grid. **Changes cursor**                                       |
| EditWordList           | GRID              | Left         | Shows potential words that fit in the grid                                                 |
| _**EditAutofill**_     | GRID              | Left         | Autofill the grid depending on                                                             |
| EditClueList           | CLUE              | Left         | Selects the list of answers to set a clue                                                  |
| EditSymmetry           | GRID              | Left         | Sets the symmetry of bars and blocks. **Changes symmetry**                                 |
| EditBars               | GRID & IpuzBarred | Left         | Sets barred lines between cells                                                            |
| _**EditCellType**_     | GRID              | Left         | Selects a cell type and shape for the current cell                                         |
| EditClueDetails        | CLUE              | Left         | Edits the details of the currently selected clue. **changes clue substring**               |
| _**EditStyleList**_    | STYLE             | Left         | Shows the list of current named styles                                                     |
| _**EditStyleDetails**_ | STYLE             | Left         | Edits the style of the current cell                                                        |
| _**EditGridInfo**_     | GRID              | Bottom       | Shows statistics about the current grid, such as distribution of clue lengths and letters. |
| _**EditDict**_         | CLUE              | Bottom       | Gives a definition of the selected clue                                                    |
| _**EditDict**_         | GRID              | Bottom       | Gives a definition of the clue substring                                                   |
| _**EditAnagram**_      | CLUE              | Bottom       | Shows anagrams of the selected clue or subclue                                             |
| _**EditThesaurus**_    | CLUE              | Bottom       | Gives a synonym of the selected clue or subclue                                            |
| _**EditHint**_         | CLUE              | Bottom       | Shows potential clue fragments for selected clue or subclue                                |

:::{important}
In the table above, _**Widgets**_ written as bold/italic
are just proposed and not written yet (as of Sept. 2023)
:::

## Implementation Notes

With this shift, our old approach of using the `PuzzleStack` to
contain the full state is no longer sufficient. We need other
transient state — such as the cursor position — in order for
everything to update correctly. Different `PanelWidgets` can affect
the behaviors of others, as can the central dock widget. This change
is correct: we don't expect the **undo** command to undo each movement
around the grid or change tools.

As an example of this transient state, `EditSymmetry` modifies the
current selected symmetry of the puzzle, and other widgets can read
from it for making changes. Similarly, `EditClueDetails` produces a
substring of a clue that can be queried. The main grid's cursor will
also affect things.

To handle this at scale and keep things straight forward in the code,
we've taken the following approach:

* All state for a given `EditWindow` is stored in the `EditState`
  struct. This is the cannonical state of the application and
  crossword. It has a few constraints that are checked with the
  `VALIDATE_EDIT_STATE()` macro.

* Each `PanelWidget` does three things:
    * It can update its display when given state from `EditState`
    * It will emit events when aspect of the `EditState` need
      changing.
    * It can optionally push transient state on the puzzle-stack. This
      is ppredmoninantly focus data and cursor postition.

  In addition, each widget can be told to commit any unsaved changes
  due to external events. As an example, widgets are committed before
  we save the puzzle to disk.

* The only object that modifies `EditState` is the `EditWindow`. This
  happens exclusively in callbacks for _edit-window-controls.h_. Each
  `PanelWidget` is relatively simple as a result.

* The only widget that deals with the undo/redo stack is
  `EditWindow`. This also happens in _edit-window-controls.h_. The
  exception to this is that `PanelWidgets` are asked to save/restore
  transient state but the window.

* Each panel widget is defined in the `EditWindow.ui`, but not put in a
  widget tree. The `EditWindow` has an additional ref count for each
  widget beyond what the widget tree contains. This is because we have
  to add/remove the widgets to the panel when the Editor switches
  modes. `PanelFrame` does not handle widgets being hidden and shown
  gracefully, so we have to create it from scratch on each stage
  change. See [libpanel issue
  #31](https://gitlab.gnome.org/GNOME/libpanel/-/issues/31)

* Finally, during the course of writing this doc, it's become clear
  that `XwordState` is named incorrectly. We will rename it to be
  `GridState` in the future.

This approach is an example of a pattern we've adopted in writing
Crosswords: _Concentrate the complexity_. Where possible, we put all
the complexity in one area. That lets us test that particular area,
and limit side-effects across the code base.

## Open Questions

Some open questions about this approach:

* [X] There is no common base class or interface for the
      `PanelWidgets`. I started writing an `EditPanelWidget` for that
      role, and realized that other than sharing the PuzzleStack, it
      didn't have a lot of value. Different widgets need different
      things, and I didn't want to add a union of property types --
      mostly empty. However, the result of this is a lot of duplicate
      code as each widget has to add its own property handling for the
      stack, at a minimum.
    * This base type doesn't have a ton of value, but we can
      centralize an `::update()` function as well as the
      `puzzle-stack` and `grid` properties. It also gives us
      flexibility in case we need it for future libpanel interactions.
    * **UPDATE:** With the shared `puzzle-stack` not being used, we
      don't need `EditPanelWidget`. This can go away for good.

* [ ] One of the main strengths of libpanel and gnome-builder is that
      you can reorder / resize the frames and panel widgets within the
      application. This breaks pretty badly with the stages, and is
      currently fully disabled. That means that we're not taking
      advantage of libpanel.
    * **UPDATE:** After talking with Christian, it's not clear
      libpanel will be able to provide us what we need. It's a small
      enough amount of work that its worth looking to see if we can
      modify it, but it may be more work than its
      worth. `AdwSplitView` may be a better approach.
