What's On v2 design: one top bar, quiet Home, single-screen What's On with pop-up, one-screen Drive-In, restyled TV Guide, navigation map (design only, mock data)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,3 +20,16 @@ The full set of boards (states, components, navigation map, wireframe) is on the
|
||||
The spec is https://claude.ai/artifact/BBUQyr3BUAva8uhaFodKU1
|
||||
|
||||
Fonts: Barlow, Barlow Condensed and Anton, from design/fonts and the app's res/font.
|
||||
|
||||
## v2: simplified (Paul, 4 November 2026, 16:59)
|
||||
|
||||
These are now the current designs. v1 above is superseded.
|
||||
- `build-v2.py` writes the `v2/` frames. It reuses v1's helpers and assets.
|
||||
- The frames:
|
||||
1. Home: the categories in the centre;
|
||||
2. What's On on one screen: Tonight's Pick, Tonight on Orbit, Coming Up;
|
||||
3. the What's On pop-up, with Remind me and Back;
|
||||
4. the Drive-In on one screen, with its art blended into navy;
|
||||
5. the TV Guide in the new colours, opened on a category;
|
||||
6. the remote navigation map.
|
||||
- Each frame is in `v2/` as `.figcraft`, `.fig` and `.png`. Mock data throughout.
|
||||
|
||||
@@ -0,0 +1,323 @@
|
||||
"""What's On v2 (Paul, 4 November 2026, 16:59): one top bar, a quiet Home of categories, What's On on one screen with a
|
||||
pop-up, the Drive-In on one screen, and the TV Guide in the same colours. Design only; every programme, time and date is
|
||||
MOCK DATA. Run: python3 design/whats-on/build-v2.py, then design/whats-on/convert.sh v2.
|
||||
"""
|
||||
import importlib.util, io, base64, math
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
|
||||
HERE = Path(__file__).parent
|
||||
spec = importlib.util.spec_from_file_location("v1", HERE / "build-whats-on.py")
|
||||
v1 = importlib.util.module_from_spec(spec); spec.loader.exec_module(v1)
|
||||
Screen, LOGO, LOT, TICKET = v1.Screen, v1.LOGO, v1.LOT, v1.TICKET
|
||||
NAVY, DEEP, PANEL, BLUE, GOLD, MUTED, WHITE = v1.NAVY, v1.DEEP, v1.PANEL, v1.BLUE, v1.GOLD, v1.MUTED, v1.WHITE
|
||||
SKY, MINT, PINK, LIVE = v1.SKY, v1.MINT, v1.PINK, v1.LIVE
|
||||
BODY, COND, HEAD = v1.BODY, v1.COND, v1.HEAD
|
||||
RES = v1.RES
|
||||
OUT = HERE / "v2"
|
||||
OUT.mkdir(exist_ok=True)
|
||||
|
||||
for name, file in {"one": "orbit_one_epg", "gold": "orbit_gold_epg", "toonz": "toonz_epg", "wild": "orbit_wild_epg", "odyssey": "orbit_odyssey_epg",
|
||||
"evidence": "evidence_epg", "heroes": "heroes_epg", "scifi": "orbit_sci_fi_epg"}.items():
|
||||
LOGO[name] = v1.data_uri(RES / f"{file}.webp")
|
||||
|
||||
|
||||
def icon(name, colour):
|
||||
"""A category icon, recoloured: white on blue, navy on the gold focused tile, as the app tints it."""
|
||||
im = Image.open(RES / f"orbit_icon_{name}.webp").convert("RGBA")
|
||||
r, g, b = int(colour[1:3], 16), int(colour[3:5], 16), int(colour[5:7], 16)
|
||||
tinted = Image.new("RGBA", im.size, (r, g, b, 0)); tinted.putalpha(im.getchannel("A"))
|
||||
buf = io.BytesIO(); tinted.save(buf, "PNG")
|
||||
return "data:image/png;base64," + base64.b64encode(buf.getvalue()).decode()
|
||||
|
||||
|
||||
class Screen2(Screen):
|
||||
def save(self):
|
||||
self.parts.append("</svg>")
|
||||
(OUT / f"{self.name}.svg").write_text("\n".join(self.parts))
|
||||
|
||||
def bar(self, active, clock=True):
|
||||
"""The one top bar, on every screen. The focused tab is gold; the current destination is underlined in gold."""
|
||||
self.group("Top bar")
|
||||
self.img(96, 60, 208, 52, LOGO["orbit"], "Orbit logo")
|
||||
x = 344
|
||||
for tab in ["Home", "What's On", "TV Guide", "Drive-In", "Arcade"]:
|
||||
w = int(len(tab) * 14.5) + 44
|
||||
on = tab == active
|
||||
self.text(x + w / 2, 96, tab, 26, WHITE if on else MUTED, BODY, 600, "middle")
|
||||
if on:
|
||||
self.rect(x + 14, 108, w - 28, 4, GOLD, rx=2, id="Current destination")
|
||||
x += w + 8
|
||||
if clock:
|
||||
self.text(1824, 92, "18:42", 40, WHITE, COND, 500, "end")
|
||||
self.text(1824, 116, "Saturday 14 November", 20, MUTED, BODY, 400, "end")
|
||||
self.rect(96, 134, 1728, 2, BLUE)
|
||||
self.end()
|
||||
|
||||
def shade(self, id, stops, vertical=False):
|
||||
"""A navy gradient over artwork, so it blends into the interface."""
|
||||
a = 'x1="0" x2="0" y1="0" y2="1"' if vertical else 'x1="0" x2="1" y1="0" y2="0"'
|
||||
s = "".join(f'<stop offset="{o}" stop-color="{NAVY}" stop-opacity="{op}"/>' for o, op in stops)
|
||||
self.parts.append(f'<defs><linearGradient id="{id}" {a}>{s}</linearGradient></defs>')
|
||||
|
||||
|
||||
def home():
|
||||
s = Screen2("1-home", "Home")
|
||||
s.bar("Home")
|
||||
s.mock(y=150)
|
||||
s.group("Categories")
|
||||
s.text(960, 290, "What would you like to watch?", 40, WHITE, HEAD, anchor="middle")
|
||||
cats = [("all", "All Channels"), ("entertainment", "Entertainment"), ("movies", "Movies"), ("comedy", "Comedy"),
|
||||
("cartoons", "Cartoons"), ("kids", "Kids"), ("documentaries", "Documentaries"), ("sport", "Sport")]
|
||||
for i, (key, label) in enumerate(cats):
|
||||
col, row = i % 4, i // 4
|
||||
x, y = 270 + col * 352, 350 + row * 262
|
||||
focused = i == 2
|
||||
s.group(f"Category {label}")
|
||||
s.rect(x, y, 328, 238, GOLD if focused else PANEL, rx=14)
|
||||
s.img(x + 124, y + 46, 80, 80, icon(key, NAVY if focused else WHITE), f"{label} icon")
|
||||
s.text(x + 164, y + 176, label, 30, NAVY if focused else WHITE, BODY, 600, "middle")
|
||||
if focused:
|
||||
s.focus(x, y, 328, 238, 14)
|
||||
s.end()
|
||||
s.end()
|
||||
s.group("Gentle pointer to What's On")
|
||||
s.text(960, 930, "Tonight's Pick and the evening's highlights are in What's On", 22, MUTED, anchor="middle")
|
||||
s.end()
|
||||
s.hints([("OK", "Open the category's channels"), ("◀ ▶ ▲ ▼", "Move"), ("▲", "Top bar"), ("Back", "Close Orbit")])
|
||||
s.save()
|
||||
|
||||
|
||||
def tonights_pick(s, focused=True):
|
||||
s.group("Tonight's Pick")
|
||||
x, y, w, h = 96, 168, 1120, 560
|
||||
s.img(x, y, w, h, LOT, "Programme artwork (Emby backdrop; this frame uses the Drive-In art as a stand-in)", "xMidYMid slice")
|
||||
s.shade("pickfade", [(0, 0.97), (0.45, 0.88), (0.75, 0.35), (1, 0.15)])
|
||||
s.rect(x, y, w, h, "url(#pickfade)", id="Navy blend")
|
||||
s.rect(x, y, w, h, "none", rx=14, stroke=BLUE)
|
||||
s.tag(x + 32, y + 30, "TONIGHT'S PICK", GOLD, 22)
|
||||
s.img(x + 32, y + 92, 192, 60, LOGO["afterdark"], "Channel badge L")
|
||||
s.text(x + 32, y + 200, "Tonight 21:00", 40, GOLD, COND, 500)
|
||||
s.text(x + 32, y + 270, "JOE BOB SATURDAY", 62, WHITE, HEAD)
|
||||
s.text(x + 32, y + 318, "Tourist Trap (1979)", 30, WHITE, BODY, 600)
|
||||
s.text(x + 32, y + 368, "The drive-in host introduces a cult mannequin", 24, "#E4ECF8")
|
||||
s.text(x + 32, y + 400, "chiller. A weekly After Dark event.", 24, "#E4ECF8")
|
||||
s.tag(x + 32, y + 490, "WEEKLY EVENT", GOLD, 18)
|
||||
s.text(x + 230, y + 510, "131 min · OK for details and Remind me", 20, MUTED)
|
||||
s.text(x + w - 24, y + h - 20, "Artwork: the programme's Emby backdrop (stand-in shown)", 16, MUTED, anchor="end")
|
||||
if focused:
|
||||
s.focus(x, y, w, h, 14)
|
||||
s.end()
|
||||
|
||||
|
||||
def whats_on(name="2-whats-on", dim=False):
|
||||
s = Screen2(name, "What's On")
|
||||
s.bar("What's On")
|
||||
s.mock(y=150)
|
||||
tonights_pick(s, not dim)
|
||||
s.group("Tonight on Orbit")
|
||||
s.text(1248, 196, "TONIGHT ON ORBIT", 30, WHITE, HEAD)
|
||||
rows = [("19:30", "riot", "Futurama", "Next in turn"), ("20:00", "drivein", "Back to the Future", "Orbit Marquee"),
|
||||
("21:00", "three", "Scottish Comedy Night", "Still Game, Limmy's Show!"), ("22:00", "paradox", "The X-Files", "A complete two-part story"),
|
||||
("22:30", "ringx", "Saturday Night Main Event", "Ring X Saturday")]
|
||||
for i, (t, logo, title, sub) in enumerate(rows):
|
||||
y = 218 + i * 102
|
||||
s.group(f"Tonight row {i + 1}")
|
||||
s.rect(1248, y, 576, 92, PANEL, rx=10)
|
||||
s.text(1268, y + 58, t, 36, GOLD, COND, 500)
|
||||
s.img(1372, y + 10, 112, 35, LOGO[logo], "Channel badge S")
|
||||
s.text(1372, y + 70, title, 22, WHITE, BODY, 600)
|
||||
s.text(1804, y + 30, sub, 16, MUTED, anchor="end")
|
||||
s.end()
|
||||
s.end()
|
||||
s.group("Coming Up")
|
||||
s.text(96, 790, "COMING UP", 30, WHITE, HEAD)
|
||||
s.text(320, 788, "Premieres, specials and events, already booked", 20, MUTED)
|
||||
cards = [("PPV SUNDAY", GOLD, "ringx", "WrestleMania III", "Sunday 19:08", None),
|
||||
("FIRST ON ORBIT", SKY, "premiere", "A new-arrival film", "Friday 20:00", None),
|
||||
("AT THE DRIVE-IN", GOLD, "drivein", "The Back to the Future Trilogy", "Saturday 19:00", "Go to the Drive-In ▶")]
|
||||
for i, (tag, colour, logo, title, when, link) in enumerate(cards):
|
||||
x = 96 + i * 584
|
||||
s.group(f"Coming Up {i + 1}")
|
||||
s.rect(x, 812, 560, 172, PANEL, rx=12)
|
||||
s.tag(x + 22, 830, tag, colour)
|
||||
s.img(x + 426, 826, 112, 35, LOGO[logo], "Channel badge S")
|
||||
s.text(x + 22, 906, title, 26, WHITE, BODY, 600)
|
||||
s.text(x + 22, 950, when, 30, GOLD, COND, 500)
|
||||
if link:
|
||||
s.text(x + 538, 950, link, 19, GOLD, BODY, 600, "end")
|
||||
s.end()
|
||||
s.end()
|
||||
s.group("Remote hints")
|
||||
s.rect(96, 1006, 1728, 1, BLUE)
|
||||
s.text(96, 1036, "OK", 19, GOLD, BODY, 600); s.text(132, 1036, "Details, then Watch or Remind me", 19, MUTED)
|
||||
s.text(470, 1036, "◀ ▶ ▲ ▼", 19, GOLD, BODY, 600); s.text(568, 1036, "Move", 19, MUTED)
|
||||
s.text(650, 1036, "Back", 19, GOLD, BODY, 600); s.text(706, 1036, "Home", 19, MUTED)
|
||||
s.text(1824, 1036, "Chosen from confirmed bookings · refreshed daily at 12:00", 19, MUTED, anchor="end")
|
||||
s.end()
|
||||
if dim:
|
||||
popup(s)
|
||||
s.save()
|
||||
|
||||
|
||||
def popup(s):
|
||||
"""Details as a pop-up over What's On: the style Paul liked, with Remind me and Back (Watch live when it's on now)."""
|
||||
s.rect(0, 0, 1920, 1080, NAVY, id="Dim", opacity=0.78)
|
||||
s.group("Details pop-up")
|
||||
x, y, w, h = 360, 170, 1200, 740
|
||||
s.rect(x, y, w, h, DEEP, rx=18, stroke=BLUE)
|
||||
s.img(x + 40, y + 36, 192, 60, LOGO["paradox"], "Channel badge L")
|
||||
s.text(x + 40, y + 160, "THE X-FILES", 72, WHITE, HEAD)
|
||||
s.text(x + 40, y + 210, "Season 2, Episode 25 · Anasazi", 30, WHITE, BODY, 600)
|
||||
bx = x + 40
|
||||
bx += s.tag(bx, y + 236, "STORY · PART 1 OF 2", SKY, 18) + 12
|
||||
s.rect(bx, y + 236, 104, 30, "none", rx=4, stroke=MUTED); s.text(bx + 52, y + 258, "45 min", 18, WHITE, BODY, 600, "middle")
|
||||
s.rect(x + 40, y + 290, w - 80, 120, PANEL, rx=12, id="Booked time")
|
||||
s.text(x + 66, y + 346, "Tonight 22:00 to 22:45", 44, GOLD, COND, 500)
|
||||
s.rect(x + 470, y + 316, 96, 32, "none", rx=4, stroke=MINT); s.text(x + 518, y + 339, "BOOKED", 17, MINT, BODY, 600, "middle")
|
||||
s.text(x + 66, y + 390, "Then part 2, The Blessing Way, straight after at 22:45", 21, MUTED)
|
||||
for i, line in enumerate(["Mulder receives leaked files that could prove the existence of extraterrestrial", "life, and the trail leads to a buried railcar in the New Mexico desert. [Emby synopsis]"]):
|
||||
s.text(x + 40, y + 466 + i * 34, line, 23, "#E4ECF8")
|
||||
s.text(x + 40, y + 552, "Why it's here: a complete two-part story tonight, on Paradox's late block.", 20, MUTED)
|
||||
s.rect(x + 40, y + 610, 318, 72, GOLD, rx=8); s.text(x + 199, y + 656, "Remind me at 22:00", 26, NAVY, BODY, 600, "middle")
|
||||
s.focus(x + 40, y + 610, 318, 72, 8)
|
||||
s.rect(x + 378, y + 610, 150, 72, PANEL, rx=8); s.text(x + 453, y + 656, "Back", 26, WHITE, BODY, 600, "middle")
|
||||
s.text(x + w - 40, y + 656, "On now? The first button is Watch live: one press to tune in.", 19, MUTED, anchor="end")
|
||||
s.end()
|
||||
|
||||
|
||||
def drive_in():
|
||||
s = Screen2("4-drive-in", "Orbit Drive-In")
|
||||
s.img(0, 0, 1920, 1080, LOT, "Drive-In lot (approved artwork)", "xMidYMid slice")
|
||||
s.shade("lotfade", [(0, 0.96), (0.42, 0.9), (0.62, 0.55), (1, 0.35)])
|
||||
s.rect(0, 0, 1920, 1080, "url(#lotfade)", id="Navy blend (horizontal)")
|
||||
s.shade("lotfadev", [(0, 0.9), (0.2, 0.2), (0.75, 0.25), (1, 0.95)], vertical=True)
|
||||
s.rect(0, 0, 1920, 1080, "url(#lotfadev)", id="Navy blend (vertical)")
|
||||
s.bar("Drive-In")
|
||||
s.mock(y=150)
|
||||
s.group("Tonight's film")
|
||||
s.img(96, 170, 220, 69, LOGO["drivein"], "Drive-In logo")
|
||||
s.tag(96, 268, "TONIGHT · ORBIT MARQUEE", GOLD, 22)
|
||||
s.text(96, 360, "Gates 19:55 · Feature 20:00", 42, GOLD, COND, 500)
|
||||
s.text(96, 460, "BACK TO THE FUTURE", 92, WHITE, HEAD)
|
||||
s.text(96, 512, "1985 · 116 min · then The Late Show at 23:00", 28, "#E4ECF8")
|
||||
s.img(96, 590, 300, 93, TICKET, "Ticket")
|
||||
s.text(420, 626, "Ticket held", 28, MINT, BODY, 600)
|
||||
s.text(420, 662, "Reminder 5 minutes before, and at the start", 21, MUTED)
|
||||
s.rect(96, 730, 300, 72, GOLD, rx=8); s.text(246, 776, "Return ticket", 26, NAVY, BODY, 600, "middle")
|
||||
s.focus(96, 730, 300, 72, 8)
|
||||
s.rect(416, 730, 300, 72, "none", rx=8, stroke=GOLD, sw=2); s.text(566, 776, "Go to the big screen", 24, GOLD, BODY, 600, "middle")
|
||||
s.end()
|
||||
s.group("Upcoming screenings")
|
||||
s.rect(1216, 196, 608, 790, NAVY, rx=14, opacity=0.86)
|
||||
s.text(1244, 246, "THIS WEEK'S BILLS", 30, WHITE, HEAD)
|
||||
bills = [("SAT", "23:00", "THE LATE SHOW", "Evil Dead II", False), ("SUN", "20:00", "ORBIT MARQUEE", "Jaws", True), ("MON", "20:00", "ORBIT MARQUEE", "Ghostbusters", False),
|
||||
("FRI", "20:00", "DOUBLE BILL", "Terminator Double Bill", False), ("SAT", "19:00", "TRIPLE BILL", "Back to the Future Trilogy", False)]
|
||||
for i, (dow, t, kind, title, held) in enumerate(bills):
|
||||
y = 270 + i * 136
|
||||
s.rect(1236, y, 568, 124, PANEL, rx=10)
|
||||
s.text(1258, y + 40, dow, 18, MUTED, BODY, 600)
|
||||
s.text(1258, y + 84, t, 38, GOLD, COND, 500)
|
||||
s.text(1360, y + 46, kind, 15, GOLD, BODY, 600, spacing=0.5)
|
||||
s.text(1360, y + 82, title, 23, WHITE, BODY, 600)
|
||||
if held:
|
||||
s.tag(1360, y + 92, "TICKET HELD", MINT, 14)
|
||||
else:
|
||||
s.text(1782, y + 108, "OK: take a ticket", 16, GOLD, BODY, 600, "end")
|
||||
s.end()
|
||||
s.hints([("OK", "Ticket or big screen"), ("▶", "This week's bills"), ("◀ ▶ ▲ ▼", "Move"), ("Back", "Home")], "Bills come from the Drive-In's own booked calendar")
|
||||
s.save()
|
||||
|
||||
|
||||
def guide():
|
||||
s = Screen2("5-tv-guide", "TV Guide, opened on Movies")
|
||||
s.bar("TV Guide")
|
||||
s.mock(y=150)
|
||||
s.group("Category chips (the guide's own filter, restyled)")
|
||||
x = 96
|
||||
for i, label in enumerate(["All Channels", "Entertainment", "Movies", "Comedy", "Cartoons", "Kids", "Documentaries", "Sport"]):
|
||||
w = int(len(label) * 12) + 40
|
||||
on = label == "Movies"
|
||||
s.rect(x, 154, w, 44, GOLD if on else PANEL, rx=22)
|
||||
s.text(x + w / 2, 184, label, 21, NAVY if on else WHITE, BODY, 600, "middle")
|
||||
x += w + 10
|
||||
s.end()
|
||||
s.group("Now and Next preview")
|
||||
s.rect(96, 222, 1728, 190, DEEP, rx=12)
|
||||
s.img(124, 246, 192, 60, LOGO["premiere"], "Channel badge L")
|
||||
s.text(124, 350, "Raiders of the Lost Ark", 34, WHITE, HEAD)
|
||||
s.text(124, 388, "Premiere · 18:15 to 20:10 · 1981 · 115 min", 21, MUTED)
|
||||
s.rect(1180, 246, 620, 142, PANEL, rx=10, id="Live picture")
|
||||
s.text(1490, 324, "Live picture of the focused channel", 20, MUTED, anchor="middle")
|
||||
s.end()
|
||||
s.group("Grid")
|
||||
s.rect(96, 428, 1728, 46, BLUE, rx=6)
|
||||
s.text(116, 459, "Saturday", 20, WHITE, BODY, 600)
|
||||
for i, t in enumerate(["18:30", "19:00", "19:30", "20:00"]):
|
||||
s.text(400 + i * 356, 459, t, 22, WHITE, COND, 500)
|
||||
s.rect(400 + int(0.4 * 356), 474, 4, 472, GOLD, id="Now line", opacity=0.9)
|
||||
nx = 400 + int(0.4 * 356) + 2
|
||||
s.parts.append(f'<polygon id="Now marker" points="{nx - 10},428 {nx + 10},428 {nx},444" fill="{GOLD}"/>')
|
||||
chans = [("premiere", "301", [("Raiders of the Lost Ark", 0, 1.6), ("The Goonies", 1.6, 2.4)]),
|
||||
("scifi", "303", [("Aliens", 0, 2.3), ("Tron", 2.3, 1.7)]),
|
||||
("afterdark", "305", [("Night of the Demons", 0, 1.2), ("The Fog", 1.2, 2.8)]),
|
||||
("drivein", "300", [("Gates open", 0, 2.9), ("Back to the Future", 2.9, 1.1)])]
|
||||
for r, (logo, num, progs) in enumerate(chans):
|
||||
y = 482 + r * 118
|
||||
s.group(f"Row {num}")
|
||||
s.rect(96, y, 296, 110, PANEL, rx=8)
|
||||
s.text(116, y + 40, num, 22, GOLD, COND, 500)
|
||||
s.img(170, y + 36, 192, 60, LOGO[logo], "Channel badge")
|
||||
for j, (title, start, span) in enumerate(progs):
|
||||
px = 400 + int(start * 356); pw = int(span * 356) - 8
|
||||
focused = r == 0 and j == 0
|
||||
s.rect(px, y, pw, 110, GOLD if focused else "#0F2C5C", rx=8)
|
||||
s.text(px + 18, y + 48, title, 23, NAVY if focused else WHITE, BODY, 600)
|
||||
s.text(px + 18, y + 82, "18:15 to 20:10" if focused else "", 18, NAVY if focused else MUTED)
|
||||
if focused:
|
||||
s.focus(px, y, pw, 110, 8)
|
||||
s.end()
|
||||
s.end()
|
||||
s.hints([("OK", "Watch, or details if later"), ("▲ ▼", "Channels"), ("◀ ▶", "Time"), ("▲ at the top", "Categories, then the top bar"), ("Back", "Home")])
|
||||
s.save()
|
||||
|
||||
|
||||
def nav_map():
|
||||
s = Screen2("6-navigation", "Remote navigation between the screens")
|
||||
s.bar("Home", clock=False)
|
||||
s.group("Map")
|
||||
s.text(96, 200, "ONE TOP BAR, FIVE DESTINATIONS, FEW PRESSES", 36, GOLD, HEAD)
|
||||
s.text(96, 246, "The top bar: ◀ ▶ between the five destinations, ▼ into the screen, ▲ back up to the bar", 21, GOLD, BODY, 600)
|
||||
top = ["Home", "What's On", "TV Guide", "Drive-In", "Arcade"]
|
||||
for k, label in enumerate(top):
|
||||
x = 96 + k * 352
|
||||
s.rect(x, 270, 320, 130, PANEL, rx=12, stroke=GOLD if label == "What's On" else BLUE, sw=3 if label == "What's On" else 2)
|
||||
s.text(x + 160, 346, label, 30, WHITE, BODY, 600, "middle")
|
||||
if k < 4:
|
||||
s.text(x + 336, 346, "⇄", 26, GOLD, BODY, 600, "middle")
|
||||
below = [(1, "Details pop-up", "OK on a card", "Back closes it"), (2, "Live TV (full screen)", "OK on a programme on now", "Back to the guide"),
|
||||
(3, "Live TV (Drive-In)", "OK on Go to the big screen", "Back to the Drive-In")]
|
||||
for k, label, down, up in below:
|
||||
x = 96 + k * 352
|
||||
s.rect(x + 158, 400, 4, 130, GOLD, id=f"Arrow to {label}")
|
||||
s.parts.append(f'<polygon points="{x + 150},522 {x + 170},522 {x + 160},540" fill="{GOLD}"/>')
|
||||
s.text(x + 172, 446, down, 17, GOLD, BODY, 600)
|
||||
s.text(x + 172, 474, up, 17, MUTED)
|
||||
s.rect(x, 544, 320, 130, DEEP, rx=12, stroke=BLUE)
|
||||
s.text(x + 160, 620, label, 26, WHITE, BODY, 600, "middle")
|
||||
s.text(96, 620, "Home: OK on a", 19, GOLD, BODY, 600); s.text(96, 646, "category opens the", 19, MUTED); s.text(96, 672, "TV Guide filtered to it", 19, MUTED)
|
||||
rules = ["Every screen has the same top bar. ▲ from a screen's top row reaches it; ◀ ▶ moves along it; ▼ goes into the screen.",
|
||||
"Tune in fast: on What's On, OK on a Tonight row that's on now opens the pop-up with Watch live focused, so it's 2 presses.",
|
||||
"In the TV Guide, OK on a programme on now tunes straight in: 1 press. Home's categories open the TV Guide filtered to them.",
|
||||
"Back is always one step: the pop-up closes to its card; a screen goes to Home; Home leaves Orbit, as it does today.",
|
||||
"Focus: 4 px gold ring 3 px outside the item, a 4% lift and a shadow; gold fill on tabs, chips and buttons. 120 ms moves.",
|
||||
"What's On opens on Tonight's Pick. It is rebuilt once a day at 12:00 from confirmed bookings, never from proposals."]
|
||||
for i, r in enumerate(rules):
|
||||
s.text(96, 790 + i * 38, "· " + r, 21, "#E4ECF8")
|
||||
s.end()
|
||||
s.save()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
home(); whats_on(); whats_on("3-whats-on-popup", dim=True); drive_in(); guide(); nav_map()
|
||||
print("wrote", sorted(p.name for p in OUT.glob("*.svg")))
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 163 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 846 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 282 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 190 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 274 KiB |
Reference in New Issue
Block a user