Skip to content

Sticky ending blocks, richer bridged rows and dividers, and a section editor that asks easy questions - #17

Merged
ZombieHDGaming merged 7 commits into
masterfrom
claude/credits-designer-features-hd9vxv
Aug 24, 2026
Merged

Sticky ending blocks, richer bridged rows and dividers, and a section editor that asks easy questions#17
ZombieHDGaming merged 7 commits into
masterfrom
claude/credits-designer-features-hd9vxv

Conversation

@ZombieHDGaming

Copy link
Copy Markdown
Contributor

Four features for the credits roll and its designer, plus the docs and tests that go with them.

Sticky Ending Block

StickyBlock is a section that holds other sections. It is laid into the roll like any other and takes exactly the room its content would have taken inline, so nothing above or below it moves, and it travels up with the roll until its slot reaches the anchor — then it detaches and holds on the frame while the rest of the credits run up behind it.

Because the strip is one tall scrolling picture, a thing that stops scrolling cannot be part of it: the slot is left empty and the block is handed out as its own picture for the compositor to draw over the strip — the same bargain an animated logo strikes. The pin is a pair of points (one on the block, one down the canvas) plus a nudge; the canvas share is fractional so a roll survives being resized. What happens when the hold runs out is a setting: end the roll in place, carry on up and off, or both.

Two limitations come with it, documented in ARCHITECTURE.md: an animated logo inside a block is drawn as a still (there is nowhere to put a second hole), and a backdrop is painted into the block's own picture rather than drawn as a quad behind it.

Bridged rows: a gap and subtitles

  • BridgeType::None draws nothing, so two columns of text with plain space between them no longer need a bridge faked in the background colour. It brings bridgeMinGap with it, since an empty bridge has no natural width under Natural sizing, and its fill is forced to Fixed through effectiveBridgeFill.
  • rowSubtitles stacks a second line under each side, each with its own style. Both sides go through the pair helper whatever they carry, so a row without subtitles is measured and drawn by exactly the code that always drew it, and the leader still hangs off whichever line ends up on top.

Divider ends are what the middle is

Both ends are now the same QVector<DividerPiece> the centre has always been, and the shape library has one role for anything drawn once at its own size. A mirrored end draws the list in reverse with each ornament flipped; words and pictures are left unflipped on purpose. An end written as a single shape — every existing document — loads as a one-piece stack under its own key, so no reader meets a key that is a string in one document and an array in the next.

A section editor that asks three easy questions

The picker offers seven base types plus switches beside them: a heading is a Title, with a subtitle, with a logo, rather than one of ten heading types. The document still carries all twenty type ids — decomposeSectionType / composeSectionType are the mapping, and the harness asserts every type survives the round trip. Rows are gathered into named collapsible groups, and rarely-reached settings sit behind one switch that hides nothing type-dependent.

Two bugs fell out of the work: changing a section's type used to throw its entries away (the table is now read out through the old columns and written back through the new ones), and the entry table now carries the whole entry on each row's first cell so a field without a visible column is not dropped on the next read.

Deleting a section now asks first, naming it, with a "don't ask again" for the window.

Tests

New suites for bridged rows, dividers, sticky blocks and section types; existing layout, persistence, fonts and style-library suites updated for the changes above.


Generated by Claude Code

Two things a Bridged section could not do. A bridge could be a string or a
piece of artwork but never nothing, so two columns of text with plain space
between them had to be faked with a bridge drawn in the background colour;
and a row was one line a side, so a role under a name meant a second section
that could not be kept in step with the first.
`BridgeType::None` draws nothing. It is a type in the table rather than an
absence so every other setting on the row goes on meaning what it meant, and
it brings one number with it -- `bridgeMinGap` -- because an empty bridge has
no natural width to keep the two texts off each other with under Natural
sizing. Its fill is forced to Fixed through `effectiveBridgeFill`, since three
ways to fill a gap with nothing differ only in how the columns come out, and
the editor hides the row to match.
`rowSubtitles` stacks a second line under each side, from two new fields on
the entry and with a style each: the two sides of a bridged row are already
styled apart, and a subtitle that could not follow the line above it would be
the one part of the row unable to. Both sides now go through the pair helper
whatever they carry, so a row without subtitles is measured and drawn by the
same code that has always drawn it -- an empty line takes no height and no
gap. The leader hangs off whichever line ends up on top, the rule a bridged
logo row already follows, so adding a subtitle leaves it where it was.
The entry table now carries the whole entry on each row's first cell, because
reading a section back rebuilds its entries from the columns on screen and a
field without a column would otherwise be dropped by the next read -- a
subtitle whose switch is off, or the text behind a type that shows logos.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SctZkVkj2RxyH7oJUvH6wQ
The middle of a divider was a list of pieces -- ornaments, a word, an image,
each sized where it sits -- and an end was a single shape out of a shorter
library. So a diamond could break a rule but a heart could not cap one, and
MMXXVI could sit in the middle of a rule but never at the end of it, for no
reason either half could explain.
Both ends are now the same `QVector<DividerPiece>` the centre has always
been, and the shape library has one role for anything drawn once at its own
size instead of one for caps and another for centrepieces. An arm keeps its
own role: it is the rule itself, tiled along a span, which is a different job.
A mirrored end draws the list in reverse with each ornament flipped, so a
lopsided end still comes out symmetric; a word and a picture are deliberately
left unflipped, because mirrored type is a mistake rather than a design.
An end written as a single shape -- every document there is -- loads as the
one-piece stack that draws the same divider, under its own key so a reader of
either shape cannot meet a key that is a string in one document and an array
in the next.
In the designer the three stacks are three tabs over one table, since they
hold the same kind of piece and tabs are what say so; the right-hand tab goes
away while the ends are mirrored.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SctZkVkj2RxyH7oJUvH6wQ
A roll could only ever scroll past. What it could not do is hold: a closing
card that stays put on the frame while the last of the credits runs up behind
it, and then ends the roll when it has been up long enough.
`StickyBlock` is a section that holds other sections. It is laid into the roll
like any other -- it takes exactly the room its content would have taken
inline, so nothing above or below it moves -- and travels up with the roll
until its slot reaches the anchor. Then it detaches. The strip is one tall
picture that scrolls, and a thing that stops scrolling while the rest of it
carries on cannot be part of that picture, so the block is not painted into
the strip at all: the slot is left empty and the block is handed out as a
picture of its own, drawn over the strip by whoever composites. The same
bargain an animated logo strikes, and for the same reason.
The pin is a pair of points -- one on the block, one down the canvas -- plus a
nudge, because "the middle of the block, halfway down the frame" needs both
halves and a single number can only say one of them. The share is of the
canvas height rather than a pixel offset, so a roll survives being resized.
What happens when the hold runs out is a setting, since it answers two
independent questions: the block stays and ends the roll, it carries on up and
off and the roll ends as it always did, or both at once. `advance` keeps its
own job -- a block that still has something to do simply holds the roll open,
and finishes it itself when its hold is over. A backdrop is painted into the
block's own picture rather than drawn as a quad behind it: libobs draws solids
and textures from different effects, and a rectangle costs nothing at rebuild
time.
In the designer a block's children are indented under it in the section list,
and a drag decides its container by the row it lands under -- drop it beneath
a block or one of its rows and it joins the block, drop it anywhere else and
it is top-level. That is one gesture for putting a section in and taking it
back out. A section list that is no longer flat means every operation on the
selected row now goes through a path rather than an index.
Deleting a section now asks first, naming it, with a "don't ask again" for the
window: undo has always covered it, but a misclick in a column of short labels
is not a thing to find out about from the preview.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SctZkVkj2RxyH7oJUvH6wQ
The editor was one column of some forty rows with a picker of nineteen types
at the top of it. Everything was reachable and almost nothing was findable.
The picker now offers seven base types and a few switches beside them: a
heading is a Title, with a subtitle, with a logo -- three plain answers --
rather than "Title w/ Subtitle & Logo" chosen from a list of ten headings, and
a list is a List that holds lines, pairs or images over so many columns rather
than six types that differ in exactly those two ways. The document still
carries all twenty types under their own ids: `decomposeSectionType` and
`composeSectionType` are the mapping, they live in the model because taking a
type apart is a property of the type table, and the harness asserts that every
type survives the round trip.
The rows below are gathered into named groups that fold away -- what the
section says, how this kind of section is put together, where it sits -- with
the middle one named after the type in it, since "Bridge settings" and
"Divider settings" are never both on screen. A group with every row hidden
goes away with them, which is asked of the layout rather than of the widgets:
a child of a window that has not been shown yet reads as hidden whether or not
anything hid it.
The settings reached for rarely sit behind one switch. Nothing is switched off
by it -- a held-back row still comes and goes with the type exactly as it did.
The section box is deliberately not among them: it is what places a section,
and hiding it would leave the side margins looking like the only way to.
One bug fell out of it. Changing a section's type rebuilt the entry table and
left it empty for the next read to believe, which threw the entries away --
the one thing changing a type is documented never to do. The table's entries
are now taken out through the old columns and put back through the new ones.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SctZkVkj2RxyH7oJUvH6wQ
A sticky block is drawn as one quad, and the strip's trick for animation is to
leave a hole and draw the artwork over it. Inside a block there is nowhere to
put a second hole, so an animated logo in one was a hole with nothing over it:
not drawn at all. Its children are now laid out without the animation cache,
which is the renderer's own way of asking for every logo as a still.
ARCHITECTURE.md picks up the four features and the two limitations that come
with the block.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SctZkVkj2RxyH7oJUvH6wQ
The sticky ending block, the gap and the subtitles a bridged row can carry,
the divider's ends being what its middle is, and a designer that asks for a
kind of section and then a couple of plain questions about it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SctZkVkj2RxyH7oJUvH6wQ
A patch inserted the sticky block's helper between a comment and the
declaration it described, leaving the animated-logo comment orphaned above the
wrong one and the declaration under it indented into the middle of nowhere.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SctZkVkj2RxyH7oJUvH6wQ
@ZombieHDGaming
ZombieHDGaming merged commit 76cd3ce into masterAug 24, 2026
5 of 6 checks passed
@ZombieHDGaming
ZombieHDGaming deleted the claude/credits-designer-features-hd9vxv branch August 24, 2026 20:28
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ZombieHDGaming@claude