Calendar Display: a second source, for the schedule beside the roll - #23
Merged
Merged
Conversation
Closing Time has drawn one thing: a credit roll that scrolls. This adds the other half of what a broadcast puts either side of its content -- a board saying what is on, where, and when. `closing_time_calendar` is a source of its own rather than a mode of the marquee, because what it edits is a different shape: a roll is an ordered list of sections that scrolls, a board is a schedule crossed with a layout that holds. What they share is everything below the layout -- TextStyle, BackgroundPanel, GradientSpec, LogoRef and its cache, the font bundle, the machine-wide style library -- shared by being written against the same pieces rather than by one growing a second personality. One house style dresses both. The board: - Three layouts (a list, a grid, stacked columns) crossed with what the time axis measures (real clock time, or named slots of equal size) and which way round it runs. The five presets the designer offers are settings over one engine, so a fix to how a lane header is measured lands once rather than in one of five places. - A slot may carry a real time as well as a name, which is what keeps a wave board and a clock board from being two different products: the gutter prints both, an event typed in with a clock time drops into the right slot, and every clock feature works on an axis that is not a clock. - An event with no end runs however its lane says -- until the next one, a fixed length, or the smallest span worth calling a duration -- because a published schedule prints start times and leaves the rest to be inferred. - Two events wanting one lane are split or stacked, again per lane, and the designer names the pair either way: a board that silently drew one over the other is the one outcome nobody wants. - Bands (DOORS OPEN) cross every lane and take none of their room. Continuations link to the event they continue, so a list of what is next counts the pair once and the style cascade can hatch the second block. - A block's appearance is five layers merged in order -- board, lane, category, status, event -- each contributing only what it sets, so a category that owns a color need not restate the typeface. Opacity multiplies rather than replaces, because it is the one part that means "less of what is underneath". - Everything that is not the grid -- a clock, a ticker, chips, a legend, an image -- is an element anchored to one of nine points on the canvas, painted into a picture of its own so it neither scales with a fitted board nor scrolls with a moving one. - Five independent clock switches rather than one live mode. A board with all of them off is rasterized once and never again. - One overflow setting with three answers: scale to fit, page through (cut at day boundaries), or scroll. The renderer plans then paints, where the strip renderer measures then draws with the same code: a board is drawn at three different scales and offsets, and a plan can be painted at each of them from one set of measurements. Also: a designer window with a click-to-select preview that can show the board as it will look at another moment; a column-mapped CSV/TSV import that creates the days, lanes and categories the file names; fourteen headless suites; and an architecture chapter covering the decisions and what was left out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T1uxGMgLj1Qd8gyjY6KDwH
A block's logo, a lane header's mark and an image element animate, through the same hole-and-overlay bargain the roll strikes: the artwork is not painted into the page, the space it would have taken is left empty, and it is drawn over the top from a texture of its own. The layout does not move for any of it -- an animation occupies exactly the box its first frame would have as a still. Three things are simpler than in the roll. No shadow frames, because a calendar logo casts no drop shadow, so the blur-per-frame path is absent entirely. No epoch, because a board has no restart. And placements reach the compositor in canvas coordinates, carried through the fit scale and the page offset by the same mapping the hit boxes already used -- so nothing on the graphics thread learns that board space exists. Two things are harder, and both come from a board being a thing that redraws itself: - Decoding happens after the fit scale is known, so a logo is decoded at the size it is really drawn at. The pass that finds animated artwork and marks its holes therefore runs inside the page loop rather than during the layout. Decoding at the layout's size means a blurred bug or a pile of frames being scaled down every time they are drawn. - Playback survives a rebuild. A board with any clock feature on re-rasterizes every thirty seconds, and a naive port of the roll's arrangement would restart every animation on it twice a minute. Each placement carries a key -- the artwork, the page, the rectangle -- and the source keeps elapsed times against it, so a rebuild that moved nothing carries playback across. One that really moved a logo gets a new key and starts it again. Animations advance on the wall clock, which is the one place the two sources deliberately disagree: a roll ties its animations to the roll, so a paused roll is a still frame; a board has no roll to tie them to, and a bug in the corner of a schedule should keep moving whatever the schedule is doing. A renderer built without an animation cache paints every logo as a still, as the strip renderer's does -- which is what the harness gets and what the designer's preview gets, so a board being typed into holds still. Also fixes a hit box being reported on every page rather than the page it is actually on, found while filtering placements the same way. Two new suites, both broken on purpose before being trusted: painting the artwork in anyway fails the hole check, and a volatile key fails the carry-across check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T1uxGMgLj1Qd8gyjY6KDwH
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 free
to 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.
Closing Time has drawn one thing: a credit roll that scrolls. This adds the other half of what a broadcast puts either side of its content — a board saying what is on, where, and when.
closing_time_calendaris a source of its own rather than a mode of the marquee, because what it edits is a different shape: a roll is an ordered list of sections that scrolls, a board is a schedule crossed with a layout that holds. What they share is everything below the layout —TextStyle,BackgroundPanel,GradientSpec,LogoRefand its cache, the font bundle, the machine-wide style library — shared by being written against the same pieces rather than by one of them growing a second personality. One house style dresses both.The board
Three layouts, not five. A list, a grid, and stacked columns, crossed with what the time axis measures (real clock time, or named slots of equal size) and which way round it runs (time down the side, or across the top). The five presets the designer offers are settings over one engine — so a fix to how a lane header is measured lands once rather than in one of five places.
A slot may carry a real time as well as a name. That is what keeps a wave board and a clock board from being two different products: the gutter prints
WAVE Cand1:00 PMtogether, an event typed in with a clock time drops into the right slot, and every clock feature works on an axis that is not a clock.An end that was never stated is resolved by the lane — until the next event in it, a fixed length, or the smallest span worth calling a duration — because a published schedule prints start times and leaves the rest to be inferred, and a lane that is one screen in one room wants a different answer from one standing for a whole platform.
Collisions are never hidden. Two events wanting one lane are split side by side or stacked, again per lane, and the designer names the pair either way.
Bands and continuations. A band (DOORS OPEN) crosses every lane and takes none of their room. A continuation links to the event it continues, which does two things at once: a list of what is next counts the pair once, and the style cascade hatches the second block.
The style cascade is five layers merged in order — board, lane, category, status, event — each contributing only the parts it sets, so a category that owns a color need not restate the typeface. Opacity multiplies rather than replaces, because it is the one part that means "less of whatever is underneath": a finished event in a half-present lane is a quarter present.
The free layer. Everything that is not the grid — a clock, a ticker, a chip row, a legend, an image, a panel — is an element anchored to one of nine points on the canvas, painted into a picture of its own so it neither scales with a fitted board nor scrolls with a moving one.
Animated artwork. A block's logo, a lane header's mark or an image element can be a GIF, APNG or animated WebP, through the same hole-and-overlay bargain the roll strikes. Simpler here in three ways — no shadow frames, no epoch, and placements reach the compositor already in canvas coordinates — and harder in two: a logo is decoded after the fit scale is known so it is decoded at the size it is really drawn at, and each placement carries a key so playback survives the rebuild that every clock refresh causes rather than restarting twice a minute. Animations run on the wall clock, which is the one place the two sources deliberately disagree.
Five independent clock switches rather than one live mode: a now-line, fading the finished, highlighting the current, dropping the finished, and how often to redraw. A board with all of them off is rasterized once and never again — it costs nothing per frame for the rest of its life.
One overflow setting, three answers: scale to fit, page through (cut at day boundaries, so a page never straddles two days), or scroll.
How it is built
The renderer plans then paints, where the strip renderer measures then draws with the same code. A board is drawn at three different scales and offsets, and a plan can be painted at each of them from one set of measurements — where a measure-and-draw walk would have to run once per page. The two orientations are one code path written against a major axis (time) and a minor one (lanes), so every feature works both ways round by construction.
nowis passed into the renderer rather than read from the system clock, which is what lets the designer show the board as it will look at ten past seven, and lets the harness assert what a now-line does without waiting for a minute to pass.Also in here
ARCHITECTURE.mdchapter covering the decisions and their reasons, five entries under Known limitations for what was deliberately left out, and a README section.Verification
cmake --buildclean, no new warnings.closing-time-tests: 109 suites, 6047 checks, 0 failures — the 16 new calendar suites included, and no existing suite moved.clang-formatandgersemiclean.--artifacts.Deliberately not here
No networking of any kind: no iCalendar feed, no bracket-site API, no polling. A board is typed in the designer or imported from a delimited file. No ending action or signals — a board does not finish. An animation restarts when the block under it is retimed, since that is a different placement of it.
🤖 Generated with Claude Code
https://claude.ai/code/session_01T1uxGMgLj1Qd8gyjY6KDwH