oficina / slides Public

Clone
git clone https://amadan.net/oficina/slides

Plain git — no account needed to clone.

Download

Download this file

1package layout
2
3// regions.go is where each flow template puts its regions, at 1920 by
4// 1080, and it is the ONE place that is written down (the slice 3 plan
5// says so). Everything else in this package asks for a region by name.
6//
7// It is a reading of internal/slides/static/slides.css's
8// `grid-template-areas`: the stylesheet is the truth, this is that
9// truth evaluated at one size. regions_test.go parses the stylesheet
10// and holds the two to the same set of region names per template — a
11// structural guard, which is the honest one to make there — and
12// internal/slidestest's browser drive checks the boxes themselves
13// against a real Chromium at 1920.
14//
15// A table cannot express a grid's `auto` row, which is as tall as
16// whatever ends up in it, so the numbers here are the one-line case
17// and engine.place adjusts two of them once the content has been
18// measured: `title` centres its two rows as a pair, and a heading that
19// runs to two lines pushes `title-body`'s and `two-column`'s lower row
20// down instead of overlapping it. This table is where they start.
21
22// Align is where a region's content sits inside the region's box: the
23// stack of blocks in the block direction, and each line of text in the
24// inline direction.
25type Align uint8
26
27const (
28 // Start is the top, or the reading-order start of a line.
29 Start Align = iota
30 // Center is `align-content: center` / `text-align: center`.
31 Center
32 // End is the bottom, or the end of a line.
33 End
34)
35
36// Box is a rectangle in frame coordinates. Not the Rect op — that one
37// is something to draw; this one is somewhere to draw in.
38type Box struct{ X, Y, W, H float64 }
39
40// Bottom and Right are the two edges the flow code keeps asking for.
41func (b Box) Bottom() float64 { return b.Y + b.H }
42func (b Box) Right() float64 { return b.X + b.W }
43
44// Region is one grid area: where it is, and how what goes in it sits.
45type Region struct {
46 Box
47 // AlignY is how the stack of blocks sits in the box, and AlignX is
48 // how each line sits in its own width. A block with its own
49 // `align` prop overrides AlignX for that block alone (§2.4).
50 AlignY, AlignX Align
51}
52
53// The content box: the frame less the section's own padding.
54const (
55 contentX = pad
56 contentY = pad
57 contentW = FrameW - 2*pad
58 contentH = FrameH - 2*pad
59)
60
61// The derived measurements each template's grid needs. The type sizes
62// are the padded scale, because every template that has a heading row
63// keeps the section's padding; image-full's are the full one, which is
64// what `padding: 0` gives it (sizes.go says why there are two).
65const (
66 // headingRow is what a template's `grid-template-rows: auto 1fr`
67 // gives its heading: one line of the biggest type. A heading that
68 // runs to two lines overflows into the body region and says so —
69 // which is what the browser does not do (its `auto` row grows),
70 // and is the largest single divergence in this table.
71 headingRow = 6.6 * cqwPadded * leadHeading
72
73 // titleRow is half the content box, for `title`'s two auto rows.
74 titleRow = (contentH - sectionGap) / 2
75 // titleW is the `max-inline-size: 80%` those rows are held to.
76 // The browser shrink-wraps the region to its own content and
77 // centres that; centring the same lines inside the full 80% puts
78 // them in the same place, and wraps at the same width.
79 titleW = 0.80 * contentW
80 titleX = contentX + (contentW-titleW)/2
81
82 // two-column's `column-gap`, which is written on the container —
83 // see sizes.go's sectionColGap.
84 colW = (contentW - sectionColGap) / 2
85
86 // quote's `max-inline-size: 84%`.
87 quoteW = 0.84 * contentW
88 quoteX = contentX + (contentW-quoteW)/2
89
90 // image-full's heading sits over the picture's bottom-left corner:
91 // `margin: var(--slides-pad)`, `padding: calc(gap/2) gap`,
92 // `max-inline-size: 80%`. scrimW is the outer cap; the box below
93 // is the text inside the padding.
94 scrimW = 0.80 * FrameW
95 scrimPadX = 2.6 * cqwFull
96 scrimPadY = scrimPadX / 2
97 scrimRow = 5.0 * cqwFull * leadHeading
98)
99
100// regions is the table. A template is a set of named areas; a region
101// name a template has no area for is not in the map, and regionFor
102// falls back the way the stylesheet does (auto placement into the one
103// column every template has).
104var regions = map[string]map[string]Region{
105 // title — everything centred, in the middle. Two auto rows under
106 // `align-content: center`, so the heading hangs from the bottom of
107 // the top half and the body sits at the top of the lower one.
108 "title": {
109 "heading": {Box{titleX, contentY, titleW, titleRow}, End, Center},
110 "body": {Box{titleX, contentY + titleRow + sectionGap, titleW, titleRow}, Start, Center},
111 },
112 // title-body — the workhorse: `grid-template-rows: auto 1fr`.
113 "title-body": {
114 "heading": {Box{contentX, contentY, contentW, headingRow}, Start, Start},
115 "body": {Box{contentX, contentY + headingRow + sectionGap, contentW, contentH - headingRow - sectionGap}, Start, Start},
116 },
117 // two-column — the heading spans, body and aside sit side by side.
118 "two-column": {
119 "heading": {Box{contentX, contentY, contentW, headingRow}, Start, Start},
120 "body": {Box{contentX, contentY + headingRow + sectionGap, colW, contentH - headingRow - sectionGap}, Start, Start},
121 "aside": {Box{contentX + colW + sectionColGap, contentY + headingRow + sectionGap, colW, contentH - headingRow - sectionGap}, Start, Start},
122 },
123 // section — one heading, centred, over the accent bar drawn by the
124 // region's ::after (the only decoration in the whole stylesheet,
125 // and blocks.go draws it).
126 "section": {
127 "heading": {Box{contentX, contentY, contentW, contentH}, Center, Center},
128 },
129 // image-full — the media fills the frame (`padding: 0`), and the
130 // heading is placed into the same area over it.
131 "image-full": {
132 "media": {Box{0, 0, FrameW, FrameH}, Start, Start},
133 "heading": {Box{contentX + scrimPadX, FrameH - pad - scrimPadY - scrimRow, scrimW - 2*scrimPadX, scrimRow}, End, Start},
134 },
135 // quote — one pull quote, centred, nothing competing with it.
136 "quote": {
137 "body": {Box{quoteX, contentY, quoteW, contentH}, Center, Center},
138 },
139 // blank — a single body region, stacked from the top. It is also
140 // what a canvas slide reflowed onto a phone becomes, though print
141 // never sees that (§2.5).
142 "blank": {
143 "body": {Box{contentX, contentY, contentW, contentH}, Start, Start},
144 },
145}
146
147// regionOrder is the order regions are drawn in, matching
148// render.regionOrder — the reading order a screen reader hears, and
149// therefore the order a paint list stacks in.
150var regionOrder = []string{"heading", "body", "aside", "media"}
151
152// regionFor is where a template puts a named region.
153//
154// A name the template has no area for falls back to its body, or to
155// the whole content box: the stylesheet auto-places such a region into
156// the single column every template's grid has, so it lands where the
157// body would. An aside on a title slide is unusual, not an error
158// (render's own comment: it is what keeps it readable).
159func regionFor(template, name string) Region {
160 byName, ok := regions[template]
161 if !ok {
162 byName = regions["blank"]
163 }
164 if r, ok := byName[name]; ok {
165 return r
166 }
167 if r, ok := byName["body"]; ok {
168 return r
169 }
170 return Region{Box{contentX, contentY, contentW, contentH}, Start, Start}
171}
172
173// Templates is the flow templates this table covers, in the order
174// slides.css declares them. It matches render's own vocabulary; a
175// template outside it renders as blank there and lays out as blank
176// here.
177func Templates() []string {
178 return []string{"title", "title-body", "two-column", "section", "image-full", "quote", "blank"}
179}
180