Add layout dialog helper.

This commit is contained in:
Greyson Parrelli
2026-08-25 15:54:43 -04:00
parent 448e31ae88
commit 3721ec38ba
9 changed files with 1185 additions and 25 deletions
+61 -7
View File
@@ -135,6 +135,8 @@ web/ the review UI: React + Vite, built by build.zig and embedded
Layouts.zig saved tab templates: model, JSON on disk, {{substitution}}
OpenLayoutDialog.zig prompts for a layout's parameters
SaveLayoutDialog.zig turns the current tab into a saved layout
PathEntry.zig a directory field: completion popover plus a Browse button
paths.zig what a half-typed path completes to, GTK-free and tested
Session.zig libghostty-vt Terminal + parser, fed by the PTY
Pty.zig openpt/fork/exec, controlling terminal setup
script.zig runs a command for `$(...)` in a layout's directory
@@ -232,6 +234,46 @@ leading `~` is expanded afterwards. An unknown `{{name}}` is left as written
rather than blanked, so a typo shows up in the pane instead of silently
producing an empty path.
### Parameter types
A parameter has a **type**, picked beside its name in the layout editor. The
type only ever changes how the value is *asked for* — every value is text and is
substituted as text — so it is a better prompt rather than a different layout.
- **Text** is a plain entry, and what every parameter was before types existed.
- **Directory** is a path field: it completes directory names as you type, and
carries a **Browse…** button that opens the file chooser. Which is what most
layout parameters already are — a layout is usually aimed at a project.
The completion is shell-shaped rather than GTK-shaped. `Tab` completes as far as
the matches agree — `~/src/pl` to `~/src/playpen/`, and no further than the
common prefix when several match — the arrow keys walk the list, `Enter` takes
the highlighted one, and `Escape` puts the list away. With nothing highlighted,
`Enter` still opens the layout, so typing a path you already know stays
type-and-go. Only directories are offered, hidden ones only once you have typed
the `.`, and a long list says how many it is not showing rather than looking
like the whole answer. A value still holding `{{a parameter}}` or `$(a command)`
is left alone: it is not a path yet, so there is nothing to complete it against.
`GtkEntryCompletion` would have been the stock answer. It is deprecated as of
GTK 4.10, and its inline completion goes to the *first* match rather than to the
longest common prefix, which among sibling directories guesses wrong more often
than it helps. The list is a popover that deliberately does not autohide: an
autohiding popover takes a grab, and the grab would send the next keystroke —
the one that narrows the list — to the popover instead of the field.
In the file, a type is a string beside the parameter's name, and saying nothing
means `string`:
```json
{ "name": "path", "description": "Project directory", "type": "directory" }
```
A type this build doesn't recognise is asked for in a text box rather than
refusing the file — unlike a pane `kind`, which is a pane it cannot build. The
startup list asks for its values the same way, so a directory parameter gets the
same field there.
### Directories from a script
A **directory** can also be `$(a command)`, and what the command prints becomes
@@ -293,7 +335,8 @@ one.
{
"name": "Project",
"parameters": [
{ "name": "path", "description": "Project directory", "default": "~" }
{ "name": "path", "description": "Project directory",
"default": "~", "type": "directory" }
],
"root": {
"split": "horizontal",
@@ -432,7 +475,9 @@ in principle, but a terminal grid is small.
reply inline. See [Code review](#code-review)
- **Saved layouts**: whole tabs — panes, splits, ratios, per-pane directories
and scripts — opened in one go, parameterised by `{{name}}`, authored by
arranging a tab and saving it. See [Layouts](#layouts)
arranging a tab and saving it. A parameter can be declared a **directory**, and
is then asked for with a completing path field and a file chooser. See
[Layouts](#layouts)
- **A startup list**: the tabs to open at launch, each a layout with its
parameters filled in, fillable from the tabs you have open now. See
[Startup tabs](#startup-tabs)
@@ -1115,17 +1160,26 @@ throwaway key has to go inside the same invocation:
Note that `shot.sh` quotes `$KEYS`, so a chord needs the quoting relaxed for
the words to reach `wtype` as separate arguments.
**Typed text is unreliable, chords and named keys are not.** `wtype` uploads its
own keymap for the characters it needs, and GTK goes on interpreting keycodes
with the keymap it already had until it catches up — so `wtype hello` lands as
mangled characters or as nothing, while `-k Tab`, `-k Down` and
`-M ctrl -k comma` land. Drive the UI with chords and named keys; check anything
that depends on what was *typed* with `zig build test` instead.
**Synthetic clicks do not work.** `swaymsg seat seat0 cursor set/press` is
accepted and reports success, but the headless backend has no pointer device to
emit from and the client never sees it. Anything reachable only by clicking has
emit from and the client never sees it. Neither does the virtual-pointer
protocol: `wlrctl pointer move/click` reports success and the client sees
nothing. Anything reachable only by clicking has
to be reached another way — a keyboard shortcut, or `zig build test` if the
thing being checked is logic rather than pixels.
`zig build test` runs the unit tests. They cover layout parsing, the
parameter/`$(...)` substitution pipeline, the palette, the settings file, the
shortcut table, the emoji table and its search, and — for the review server —
git's own output formats, the rule for picking a base ref, and the round trip a
review file makes through disk. Each is its own binary with its own root, since a
parameter/`$(...)` substitution pipeline, what a half-typed directory completes
to, the palette, the settings file, the shortcut table, the emoji table and its
search, and — for the review server — git's own output formats, the rule for
picking a base ref, and the round trip a review file makes through disk. Each is its own binary with its own root, since a
test binary has exactly one, and none of those roots links GTK, so they all run
without a display.