Skip to content

Backgrounds: a panel behind a section, and behind each thing it draws - #20

Merged
ZombieHDGaming merged 3 commits into
masterfrom
claude/section-background-rendering-umtjwf
Aug 26, 2026
Merged

Backgrounds: a panel behind a section, and behind each thing it draws#20
ZombieHDGaming merged 3 commits into
masterfrom
claude/section-background-rendering-umtjwf

Conversation

@ZombieHDGaming

@ZombieHDGamingZombieHDGaming commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

A panel is a rectangle drawn behind something: a flat colour, a linear or radial gradient, or an image fitted by cover, contain, stretch or tile; four corner radii; an optional border; a per-side outset and an opacity.

There is a backgrounds scene in the test harness to look at it with — ./build/tests/closing-time-tests --artifacts out/ writes backgrounds.png, showing the three shapes a panel is actually reached for: a bordered card behind a heading, a band reaching past its section out to the edges of the frame, and a striped list.

The rule the whole thing turns on

A panel never takes part in layout. It is painted behind a box the layout has already decided, and its outset reaches outside that box exactly the way a text shadow does. Switching a panel on — at any size, on any slot — cannot move a section, grow the roll or change how long it runs, so a roll can be given cards and bands after it has been timed. sectionBleed counts the outset, so a band crossing a tile seam is drawn into both tiles instead of being cut at it.

The room inside a panel is the padding a section already has. The outset is for the other case — a band that has to reach past the section's box, out to the edges of the frame — and is per side because reaching past one edge is a different want from reaching past all four.

Eight slots, not eight fields

SlotBehind
Sectioneverything the section draws, over its whole height including its padding
Titlethe primary text block: a heading, or the left-hand column of a bridged row
Subtitlethe secondary text: the line under a heading, or a row's right-hand column
Logoeach logo the section places, hugging the artwork
Entryeach row of a list, or each cell of a grid
EntryAltevery other entry instead, for a striped list
Bridgethe leader joining the two sides of a row
Dividera Section Divider's artwork, over the box that artwork occupies

A slot rather than a named field apiece: eight BackgroundPanel members would be eight copies of the same save/load, eight branches in the editor and eight places for a new panel setting to be forgotten. backgroundSlotsFor says which of them a type has anything to sit behind, derived from the existing predicates rather than tabulated beside them.

A section holds the slots it has been given rather than all eight always. A section with no panels costs nothing to copy onto the undo stack and writes nothing into the scene collection — which is every section in every roll written before this existed. It is also what makes striping work: a list with no alternate draws Entry behind every row, and a list that has one draws it behind the odd rows, so an alternate left on None is how every other row is left bare.

Two mappings are not the obvious one. In a bridged row the two text slots are the row's two columns, which is how the two styles already map. And a plain list is offered no title slot: its line and its entry are one rectangle, and two names for the same place would be two settings drawing over each other.

Performance

A panel goes under something whose rectangle is only known once it has been measured, and measuring text twice is the most expensive thing this renderer could casually start doing. SectionPanels::wants is asked first: a slot with nothing in it — every slot until somebody sets one — costs one comparison and the content is laid out exactly once, as it always was. Only a slot that will really paint pays for the extra measure.

Presets and the library

A slot binds to a named BackgroundPreset on the document exactly the way a section binds to a StylePreset, with the same non-destructive fallback in both directions. They are a second collection beside the text styles — on the document and in the machine-wide library — because a panel and a typeface are not one decision made twice, and because a shared namespace would make a background called Card collide with a style of the same name. The library therefore keeps two rename trails; their mechanics are written once as free functions over a trail and used by both.

refreshLinkedPresets brings both collections up to date in one call, so a caller cannot follow its styles and leave its panels behind.

Removed

Section::stickyBackdrop, stickyBackdropColor and stickyBackdropPadding are gone. That trio was this feature in miniature for one section type — a flat colour and a padding, with no corners, no gradient, no image and no preset behind it. A block now uses its Section slot like everything else, painted into the block's own picture (it leaves a hole in the strip, so it cannot use the pre-switch paint site the other types do). No migration is written: the feature was confirmed unused.

GradientSpec moves to model/Gradient.hpp so a panel can map a sweep over itself through the very code that maps one over a run of glyphs — textFillBrush is now a thin wrapper over the shared gradientBrush.

Designer

One folding group per slot, built from the slot table, showing only the slots the section's type has a use for. Each group is checkable, and the checkbox is what says the slot carries a panel at all — a different question from what it is filled with, and one that genuinely matters for the alternate entry. The corner radii and the outset are each offered as one figure with a switch for four, because a panel with all four corners alike is what nearly every design wants.

The style library manager gains a picker saying which of the two collections its lists are showing; publish, link, copy, rename and delete mean the same thing for either kind, so it is one pair of lists rather than two.

Verification

  • 93 suites, 5905 checks, 0 failures (up from 76 / 4882).
  • A new Backgrounds suite covers the never-reflow rule across all eight slots, the outset, a band crossing a tile seam, the corner path and its scaling, the border staying inside the panel's bounds, an image clipped to the shape rather than the box around it, the four fits, striping, preset binding and its fallback, and both library collections with a rename trail each.
  • Builds clean against libobs and Qt 6 with -Wall -Wextra -Werror; clang-format-19 and gersemi are clean.

Known limitations, documented

  • A panel's outset is bounded by the roll's own extent: the strip is exactly as tall as the layout made it, so an outset on the very first or last section has nothing outside the roll to be drawn into. Giving a panel the power to grow the strip is exactly the power to change the roll's duration the feature is built not to have.
  • A panel's image is a still. An animated file contributes its first frame — the strip is rasterised once and scrolled, and unlike a logo a panel has no quad of its own to be drawn over.

A background is a fill, a shape and an edge: a colour, a linear or radial
gradient, or an image fitted by cover, contain, stretch or tile; four
corner radii; an optional border; a per-side outset and an opacity.
It sits in one of eight slots on a section -- the section itself, its
title, its subtitle, each logo, each list entry, every other entry, the
bridge and a divider's artwork -- held as the slots a section has been
given rather than as all eight always, so a section with no panels costs
nothing to copy and writes nothing into the scene collection. That is
also what lets an alternate entry panel be told apart from one set to
draw nothing, which is how a striped list leaves every other row bare.
A panel never takes part in layout. It is painted behind a box that has
already been decided and its outset reaches outside that box the way a
text shadow does, so switching one on never moves a section or changes
the roll's duration; sectionBleed grows to count it, which keeps a band
from being cut at a tile seam. The room inside a panel is the padding a
section already had.
Slots bind to named BackgroundPresets, a second collection beside the
text styles on the document and in the machine-wide library, with their
own linking, forking and rename trail -- separate because a panel and a
typeface are not one decision, and because a shared namespace would make
a background called "Card" collide with a style of the same name.
A sticky block's stickyBackdrop, stickyBackdropColor and
stickyBackdropPadding are gone: a block now uses its section slot like
everything else, painted into the block's own picture.
GradientSpec moves to model/Gradient.hpp so a panel can map a sweep over
itself through the very code that maps one over a run of glyphs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUfbS9hZrjGeRN3NNg369X
A BackgroundEditor holds everything one panel carries, and the section
editor puts up one folding group per slot -- built from the slot table,
so a slot added to the model turns up here with nothing to write. Which
groups are on screen follows backgroundSlotsFor(), which is the model's
own answer rather than a second one assembled out of the predicates the
visibility pass had already gathered.
Each group is checkable, and the checkbox is what says the slot carries
a panel at all -- a different question from what the panel is filled
with. For an alternate entry the two really do differ: a list that
alternates onto a panel filled with nothing is how every other row is
left bare, where a list with no alternate draws the same panel behind
every row. All eight read the same way rather than seven plus a footnote.
The corner radii and the outset are each offered as one figure with a
switch for four, because a panel with all four corners alike is what
nearly every design wants and a row of four spin boxes to say so is four
times the question.
The style library manager grows a picker saying which of the library's
two collections its lists are showing. One pair of lists rather than two,
because publish, link, copy, rename and delete mean exactly the same
thing for either kind. An import brings both collections in whichever
way the picker is set: a file is imported whole.
The sticky block's three backdrop rows are gone from the editor with the
fields behind them.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUfbS9hZrjGeRN3NNg369X
The suite pins down the rule the whole feature turns on -- a panel in any
slot, at any size, changes no height and moves no section -- and then the
things that rule costs: the outset reaching past the box, a band crossing
a tile seam being drawn into both tiles rather than cut at it, and a
Spacer's panel bleeding at all, which is why the bleed is taken over
every type rather than only the ones that set text.
Also the corner path and its scaling, the border staying inside the
panel's bounds, an image clipped to the shape rather than the box around
it, the four fits, striping by an alternate entry slot, preset binding
and its fallback, and both collections in the library with a rename trail
each.
The fit checks use a solid image of their own rather than the harness's
test logo: that one is a ring, so its ink reaches only the middle of each
edge and its centre is transparent, which makes "cover fills the panel"
an approximation rather than a measurement.
A `backgrounds` scene shows the three shapes a panel is actually reached
for -- a bordered card behind a heading, a band reaching past its section
out to the edges of the frame, and a striped list.
ARCHITECTURE.md gains sections on the panels and their presets, and two
limitations: an outset is bounded by the roll's own extent, and a panel's
image is a still.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NUfbS9hZrjGeRN3NNg369X
@ZombieHDGaming
ZombieHDGaming merged commit 0fcfa20 into masterAug 26, 2026
6 checks passed
@ZombieHDGaming
ZombieHDGaming deleted the claude/section-background-rendering-umtjwf branch August 26, 2026 13:26
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