Development Anchoring - ShadobaDev/Platemaker-qt GitHub Wiki
Inputs and anchoring
What happens to lettering when the page under it is removed, replaced or reordered β and why a render is allowed to stop and ask.
Status β built. Anchoring, the unanchored state, Replace fileβ¦, the delete-time warning, Re-anchor to βΈ and the render gate. One thing described below is deliberately not built: pre-highlighting a page in Re-anchor to βΈ.
How anchoring works
An overlay stores the uid of the input page it rides on plus an offset from that page's top, and both the render and the preview resolve that pair against the strip they just laid out. The preview cannot disagree with the render about where a balloon lands, because they do the same arithmetic.
Every coordinate and the artwork's width are stored as fractions of the render's target width, so re-profiling a chapter from 800 px to 1600 px moves and resizes every object proportionally and nothing has to remember what the numbers used to mean.
The uid matters more than it looks: it is what makes a file replaceable without the lettering noticing, and what lets a lettering step and a project step sit on one history without treading on each other (History and undo).
Unanchored
An overlay whose anchor page is not in the strip is unanchored. It is drawn greyed, is not selectable, and is left out of the composite β the library reports it rather than failing, and the editor shows it rather than dropping it. Nothing is deleted: an unanchored object is waiting, not broken.
An overlay with an empty anchor is unanchored too. An empty anchor names no page, so it can never be placed.
Naming. "Orphan" already means a stale output file from a previous configuration elsewhere in this application. The overlay sense is unanchored everywhere, so the two never have to be told apart from context.
Replacing a page
βI have a newer scan of page 4β should never produce an unanchored object, and deleting the input and adding another is the wrong gesture for it.
Replace file⦠on the input row swaps the file behind an input and keeps the uid. Every overlay stays anchored, because the anchor was never about the file. This is the operation to reach for whenever the page is conceptually the same page, which is why it sits above Remove in the tile's menu.
The swap is the library's β ProjectEditor::replaceInputFile(uid, path) β because a path is what the
project's lookup tables are keyed by, and a consumer editing InputFile::filePath in place would leave
them describing the old file. It keeps uid and order and resets everything that described the old
file (hash, status, recorded size, thumbnail, matched canvas profile); the page is Pending until the
next render hashes it, and every output is marked out of sync, since a page of a different height moves
every slice below it.
It refuses a file that is already another page β case-insensitively in the GUI, exactly in the library β because inputs are matched by path on every rescan and two pages on one file would be folded into one.
Deleting a page
Deleting an input has to stay possible, so delete-then-add has to work too.
At the moment of deletion, the confirmation says what is about to happen β β3 objects of text and bubbles are anchored to the page being removedβ β and offers Remove, keep them (the default: nothing is lost) or Remove with the 3 objects. That does not replace the render gate; the artist may still choose to keep them. It moves the first mention of the problem to the moment that caused it.
Both ways of removing inputs β Clear and the tile menu's Remove β go through one
Project::removeInputs(), so neither can forget to ask.
Removing with the objects is one undo step, though underneath it is two kinds of edit on the
project's one history: the pages are a project step and the lettering an overlay step. A
QUndoStack macro groups them, so one Ctrl+Z puts back both, in the right order β lettering first,
then the pages, whose restore lifts the overlays out and puts back the ones just restored.
Afterwards, the objects are unanchored and there are two ways back:
- an advisory in the status bar carrying its own way in β β3 objects unanchored β show themβ, which opens the strip editor with the first of them selected. An unanchored object is not on the strip, so it is not selectable in the scene: what is reached is its row in the object stack, which is where it can be acted on;
- an explicit Re-anchor to βΈ in the object's context menu β on the canvas and on the object stack β acting on the selection. Dragging an anchored object onto another page also re-anchors it.
Re-anchor to βΈ lists every page as p.NN β file name, named the way the object stack names them, with
the current page checked. Only the anchor changes: placement is measured from the anchor page's top, so
the object lands at the offset it had on its old page β for an unanchored one, where it sat on the page
it lost, which is one drag from wherever it belongs.
Why re-anchoring is never automatic
An automatic re-anchor would have to guess, and the only thing it could guess from is ordinal position β these belonged to the fourth page, here is a new fourth page. Delete page 4 without replacing it and page 5 becomes the fourth page, so the guess silently moves one page's lettering onto another page's art.
Silently wrong lettering is the worst failure this application can produce. It is worth a click to avoid.
Where it is unambiguous β exactly one page carries no objects and one group of objects is unanchored β Re-anchor to βΈ could pre-highlight that page. It does not, yet. A suggestion costs nothing and an assumption costs a chapter, but the rule for "unambiguous" is itself a guess about intent, and the menu lists every page by name in the object stack's own terms, so the choice is never hard to make.
The render gate
A render stops and asks while any unanchored object exists:
"Chapter 04" will not render the way it looks in the strip editor.
Chapter 04: 3 of this chapter's objects are anchored to a page that is not in the strip β¦
[ Show them ] [ Delete the 3 objects ] [ Render anyway ] [ Cancel ]
Show them opens the strip editor and ends the render β the artist went to look. Delete the 3
objects deletes them as one undoable step and then renders, re-deriving everything first (a
resolution returns RenderGate::Again, which runs startRender() from the top), because deleting them
changed the chapter the render was about to be computed from. Cancel is the default
button: a dialog standing between the artist and a wrong deliverable must not be dismissed into
rendering by a reflexive Enter.
Four things about this prompt are deliberate:
- It lives in the GUI, before dispatch. The library's job is to render what it is given and report what it dropped; refuse to start is a policy the application owns. The GUI already knows the answer without asking anyone β it is the same test it draws greyed objects with.
- It is a prompt, not a prohibition. Render anyway is exactly what the library does unaided, so offering it costs nothing. The gate exists to stop the artist finding out an hour later, not to overrule them.
- It is one rule, not a list. Any advisory at Error level stops the render and asks; unanchored objects are the first, and nothing has to be added here when there is a second. Every button comes from an advisory β its way to look and its way to resolve β so a second kind of error brings its own ways out and the dialog does not change.
- A batch does not ask. F6 sweeps every chapter, and a dialog on one would hold every chapter after it hostage to a prompt nobody may be watching. A chapter with an Error standing is skipped, and the batch summary says why.
It sits after "nothing to render" β an up-to-date chapter loses nothing by standing still β and before the settings changed question, so nobody answers that one only to cancel at this one.
Reordering pages
Reordering changes where a page sits, not what it is, so nothing unanchors and every balloon follows its own page. This is the case that page-relative placement makes free, and row 4 of the undo checklist is there to keep it honest.
See also: History and undo Β· Advisories and badges Β· Workspace folder ownership Β· Projects Β· Rendering