Skip to content

GUI Editor

Joël Deffner edited this page Aug 11, 2026 · 1 revision

GUI Editor

A .gui file, drawn by the measured layout engine and editable with a mouse. Open it with Paradox: Open GUI Editor, Ctrl+Alt+P, or the preview icon on a .gui editor title.

CK3 only. The layout engine was calibrated against CK3 in-game screenshots, so Victoria 3 and EU5 get .gui language support and the widget tree, but not the visual editor. See Supported Games.

This replaces the old GUI Layout preview, which is retired: the editor does everything it did and more. If you only ever wanted to look, open the file read-only and the editor is that.

The three things that make it an editor, not a preview

It writes your file, not a copy of it. Every gesture is ONE surgical edit over the exact span the entry occupies, so comments, tabs, CRLF and single-line bodies come back byte for byte, and undoing is ONE Ctrl+Z in the text editor (the editor keeps no undo history of its own). This is verified by round trips over all 373 vanilla .gui files, not just fixtures.

It writes the value, not the cursor. A drag commits the widget's own effective position plus the drag delta, never the world coordinate under the pointer. A widget positioned through anchors, margins or a parent's content box lands where you dropped it instead of jumping.

It turns a gesture down before it moves. The guards are asked when the mouse goes down, so:

  • dragging a child of an hbox or vbox is refused in the server's own words ("places its children itself"), with nothing having moved and nothing to snap back,
  • a child expanding on both axes refuses resize, and one expanding on a single axis writes the other with a warning naming the axis the container owns,
  • a content-sized container ignores an explicit size, and says so,
  • a drag that rounds to less than a pixel says so rather than silently doing nothing.

A refusal is an answer, not an error. The alternative would be writing a position line the game quietly drops.

Selecting and moving

  • Click selects the smallest rect under the cursor, not the anchored box filling the window behind it.
  • Alt+click steps outward through the stack.
  • Ctrl+Shift+click jumps to the declaration in the text editor.
  • Shift+click and marquee build a multi-selection.
  • Drag moves; the resize grips size. Smart guides snap to sibling edges, centers and equal spacing, with an optional grid, and a live x/y/w/h readout plus live inspector values follow the gesture.
  • Dragging a widget inside a box shows a drop line and commits a reorder.
  • Middle-mouse drag pans the canvas.
  • f focuses a subtree: the tree, the canvas and hit-testing all scope to that branch, with a breadcrumb back out.

With several widgets selected, move, nudge, delete, duplicate, align and distribute all commit as one undo step. If one member is refused, it is skipped with its reason shown verbatim and the rest proceed.

The tree and the inspector

The tree lists source children in source order and marks the ones a template or type spliced in. A window_character-sized document opens with its tree collapsed rather than listing 13,702 rows.

The inspector shows every property with the template or type it came from, and editing a row writes an override at the use site. On top of that:

  • An add-property row with completion from the harvested widget vocabulary: per-type property names plus the tree-wide ranking, and values complete too where the engine has a vocabulary (anchors, for instance).
  • Block values such as background = { using = X alpha = 0.7 } open into a sub-editor with one row per entry, rows addable and removable, committed as one write.
  • A display mode per property value (full, abbreviated with the full value on hover, or hidden), remembered per workspace.
  • It holds its place: committing a value does not jump the scroll to the top, and text typed into one field survives a commit in another.

Layers, guides and reordering

A layers panel over the selected widget's container:

  • eye hides a widget in the preview,
  • lock stops it swallowing clicks,
  • solo dims everything else,
  • hover flashes its outline,
  • dragging rows reorders source order through the writer, labeled as layout order inside an hbox or vbox, because that is what source order means there.

Reorder indices count the declarations a preview cannot see. A blockoverride sitting between two widget children used to shift every later index by one; a layers drag now moves exactly the block you dragged.

Adding content

  • Copy puts the widget's verbatim block on the clipboard; paste re-inserts it.
  • A palette inserts new widgets from the harvested widget vocabulary plus the document's own types. Never from memory: if the vocabulary does not have it, the palette does not offer it.
  • An anchor picker offers exactly the anchor words the layout engine parses.
  • Wrap encloses a run of siblings in a new container.
  • Texture and type browsers pick values from the mod and game trees.
  • A selection can be saved as a named component, and property bundles as presets. Both are stored in your workspace; none ship bundled.

Understanding what the engine did

  • A "why is it here" panel sums the engine's own placement terms to the widget's rect origin, names the layout container that dropped an authored position, the clipping ancestor, and the template value each property overrides.
  • A constraint overlay draws parent bounds, the anchor crosshair and link line, the clip rect, and expanding-axis arrows.
  • Heatmaps for depth, clipping and synthetic (template-spliced) widgets, plus optional layout-change pulses.
  • A stats line with the server's per-stage timings.
  • Conditional visibility gets preview modes: show all, hide all, or evaluate with per-check answers the editor remembers per document.
  • A dependency panel links the selected widget to its scripted_guis (file and line, used-by counts), the event chains that reach them, and its loc keys with the missing ones flagged. Every row is click-through.

A container whose content the engine cannot statically measure is drawn as a dashed estimate box and counted in the status line, because the engine invents no pixels and the canvas should not pretend it did.

What the layout engine models

Enough of PdxGui to be trusted on real files:

  • Grid boxes for real: fixedgridbox uses addcolumn / addrow as the cell size and stride, dynamicgridbox packs items at their own size, both fill down a column by default and transpose with flipdirection, maxhorizontalslots caps a row, and setitemsizefromcell makes every cell the widest item's.
  • A hidden child collapses out of an hbox / vbox and its siblings shift up (ignoreinvisible).
  • A resizeparent = yes child resizes its parent to its own content.
  • A container and a datamodel item size to their content, so an empty container collapses instead of holding its size open.
  • scrollbox and scissor = yes clip like scrollarea.
  • A flowcontainer honors a child's parentanchor on the cross axis.
  • A minimumsize floors a shrinking child, and the deficit redistributes over the rest.

For tool authors

The gesture layer is a documented wire method, paradox/guiSourceEdit: one request takes a gesture (set or remove properties, reorder, insert, paste, delete, duplicate, wrap, or copy a block out) and answers with surgical text edits the host applies, or with a refusal that says why. Blank separators and attached comments travel with the widget they belong to, so a reorder is a pure permutation and an insert and a delete are exact inverses.

paradox/guiWidgetEdit still works as a deprecated alias over the same core, with one behavior change: a property it has to insert lands on its own line before the closing brace, where the writer puts every new property, instead of first in the body.

See docs/PROTOCOL.md and Outside VS Code.

Clone this wiki locally