Repository navigation
Development Strip Editor
How the strip editor is put together: what a tool is, what an object is, and which panel is allowed to talk about which. To add a tool, a shape or a kind of object, the file-by-file recipes are in Extending the strip editor in the repository; this page is the why behind them. Where each part lives in the code is the code map.
Status — built. The property groups and their editors, the tool registry, both panels, the preset store, one history per project, advisories, multi-selection with roles, and the object stack as a tree whose rows include the strip, its pages and a bubble's tails.
The symptom was cosmetic: the editor showed what looked like the same panel twice, once under the tool rail and once on the right. The cause was not.
Objects in this editor are a type hierarchy. Tools are an enum and six if statements in one
function, so a tool has nowhere to keep what it is about to make. The only class able to hold that
state was the properties panel, so the panel was instantiated twice — once bound to the selection, once
standing in as the tool's memory — and told apart by an enum naming which panel it was sitting in.
A widget that knows which panel it is in is a layering violation, and this one produced two identical panels. The framework exists to give the tool somewhere to live, so the panel does not have to be it.
These five words are used precisely throughout this section.
| Noun | Identity | Example |
|---|---|---|
| Object | a uid at top level, a local handle when nested | a bubble, imported artwork, the strip, a page, a tail |
| Property | none — a field |
fill, fontPixelSize, bend
|
| Property group | a name and a struct |
SkinProperties, TextProperties, TailProperties
|
| Role | a name, matched across kinds | position, text colour, typeface |
| Tool | one rail entry; stateless, so it is a row in a table rather than a class | Select, Pan, Colour correction, Bucket paint, Colour picker, Speech bubble, Artwork, Text |
| Mode | none — a state, not an entry | the default select/move state; view panning |
| Advisory | a key | “3 objects unanchored” |
There is no separate type for a nested object. A tail is an Object whose parent is a bubble; a
page is an Object whose parent is the strip. Nesting is a position in the tree, not a kind of thing.
What separates a nested object from a property group is one test: can there be several of it, and can one be deleted? A tail can, so a tail is selected and deleted on its own. A fill colour cannot. A bubble's text cannot either: there is exactly one, and it can be emptied but not removed, so it is a group.
Tools are stateless; objects carry state. A tool that appears to remember something — the preset it will apply, the primary colour — is holding a reference to something stored elsewhere: the preset library, or the application's colour pair.
Five concerns, and one rule that keeps them apart.
| Layer | Owns | May know about | |
|---|---|---|---|
| L0 | Object model | objects, nesting, identity, z-order | the library's models; no widgets |
| L1 | Property model | groups, roles, field ownership, applyTo
|
L0; no widgets |
| L2 | Property editors | one widget per group | L1 and Qt; not which panel it is in |
| L3 | Tool framework | the registry, activation, canvas input routing | L0–L2 |
| L4 | Surfaces | which region hosts which editor, bound to which subject | L2, L3 |
| L5 | History | commands, the undo stack | L0, L1 |
Arrows point down. The layering violation that produced the duplicated panel was an L2 widget carrying an enum describing its L4 position. If an editor must look different in one region, the region asks it to be compact; the editor does not ask where it is.
L1 stays free of QWidget for a second reason: it makes the ownership tests plain console tests. See
Property groups.
┌──────────────────────────────────────────────────────────────────────────┐
│ TOP BAR — document & view: seams, zoom, fit, render preview, undo │
├───────────────┬──────────────────────────────────┬───────────────────────┤
│ TOOL RAIL │ │ OBJECT STATE │
│ ▣ ▣ ▣ ▣ │ │ bubble#7 │
│ ▣ ▣ │ CANVAS │ ▸ Placement │
│ ■/□ colours │ │ ▾ Skin [Shout] │
├───────────────┤ selection chrome, handles, │ [fill] [stroke] │
│ TOOL OPTIONS │ placement ghost ├───────────────────────┤
│ preset ▾ │ │ OBJECT STACK (tree) │
│ [the groups │ │ ▸ 🗨 "Text…" │
│ it fills] │ │ ▪ logo.svg │
│ │ │ ▸ ▬ STRIP │
├───────────────┴──────────────────────────────────┴───────────────────────┤
│ ready. (⛔ 3 objects unanchored) (⚠ 2 fonts missing) │
└──────────────────────────────────────────────────────────────────────────┘
| Region | Subject | Empty when | Code |
|---|---|---|---|
| Top bar | the document and the view | never | editor.ui |
| Tool rail | — | never | toolrail/ |
| Canvas | the strip | never |
canvas/, and what is on it in objects/
|
| Object state | the selection | nothing selected, or the selection has nothing in common | objectstate/ |
| Tool options | the active tool's options | the tool has none | tooloptions/ |
| Object stack | the document | never — the strip is always there | objectstack/ |
| Status bar | the document | nothing to report | the main window's |
The folders are under widgets/stripeditor/; the code map lists
their files and what each may include.
Three rules follow, and they are the whole answer to the duplicated panel:
- OBJECT STATE and TOOL OPTIONS may use the same editor class, never the same instance, and never the same subject. TOOL OPTIONS is bound to what the tool will make; OBJECT STATE is bound to what is selected.
- OBJECT STATE belongs to the selected object, whichever tool is active. Selecting a tool that authors nothing does not empty it and does not hide it. Hiding it resizes the canvas, which makes the strip jump sideways when the tool changes; that was fixed on 2026-09-12 and must not come back.
- TOOL OPTIONS belongs to the active tool. "What the next object will be" is the most common kind of tool option, not the only kind — a transform tool's snapping settings live there too.
A tool has two faces. The colour tool is the clearest case: in OBJECT STATE it edits the selection's fill and stroke; on the rail it carries the application's colour pair and applies it to whatever you click. One tool, one property group, two bindings.
| Surface | A tool contributes | A property group contributes | An object contributes |
|---|---|---|---|
| Tool rail | its icon, always | — | — |
| Tool options | its options page while active | the groups a preset fills, and the preset badge | — |
| Object state | its editor face, bound to the selection | its section | which groups exist; the breadcrumb |
| Object stack | — | reporting badges (blend, unanchored, preset) | a row each, nested |
| Canvas | placement ghost, rubber band, cursor | — | selection chrome, handles |
| Status bar | advisories it raises | — | advisories about it |
| Context menu | tool actions, Convert to ▸, Re-anchor to ▸ | apply this group | delete, reorder |
Selection is a set of paths — strip/bubble#7/tail#2 — shown in three places at once and always
agreeing: the tree row, the canvas chrome, and a breadcrumb at the top of OBJECT STATE. With several objects
selected the breadcrumb reads 3 objects and hovering lists them.
Canvas clicks select outermost first: click a bubble and you get the bubble; click again, or click a tail handle, and you get the tail.
Selection belongs to the canvas, not to a tool. Every object is selectable, movable and resizable under every tool — a tool decides what a placement creates, never whether what is already there can be touched. The one exception is an unanchored object, which is not on the strip and so has nothing to be selected on.
Nested objects carry a GUI-local handle, never a workspace uid — uids belong to the library and a tail is not independently addressable there. A tail's handle is its index (Q60). Deleting tail 0 renumbers tail 1, but nothing is left holding "tail 1": undo restores whole states, and a selection whose bubble changes its number of tails moves up to the bubble.
| Page | Covers |
|---|---|
| Code map | Which folder holds which region, what may include what, how the parts talk |
| Property groups | The structs, ownership, roles, the write contract, the tests |
| Object state and tool options | What OBJECT STATE, TOOL OPTIONS and OBJECT STACK contain, presets and their badge |
| History and undo | One history per project, typed steps, dock attention |
| Advisories and badges | The registry, the status bar, widgets/badge/
|
| Inputs and anchoring | Replacing a page, unanchored objects, the render gate |
See also: Architecture · Contributing · Rendering
Platemaker GUI 1.4.3 · GPL-3.0 · Application repository · libplatemaker · Report an issue
Development pages synced to code commit 1331e01 (2026-10-04).
Overview
Manual
- Manual overview
- Installation
- Quick start
- Concepts
- Main window
- Workspaces
- Projects
- Canvas profiles
- Templates
- Output profiles
- Rendering
- File statuses
- Menu reference
- Troubleshooting
Comic production
Development
- Overview
- Environment
- Building
- Architecture
- Main window
- Workflows
- Releasing
- Contributing
- Windows hardening
Strip editor