Uh oh!
There was an error while loading. Please reload this page.
Sticky ending blocks, richer bridged rows and dividers, and a section editor that asks easy questions - #17
Merged
Conversation
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
Uh oh!
There was an error while loading. Please reload this page.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four features for the credits roll and its designer, plus the docs and tests that go with them.
Sticky Ending Block
StickyBlockis 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::Nonedraws nothing, so two columns of text with plain space between them no longer need a bridge faked in the background colour. It bringsbridgeMinGapwith it, since an empty bridge has no natural width under Natural sizing, and its fill is forced to Fixed througheffectiveBridgeFill.rowSubtitlesstacks 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/composeSectionTypeare 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