Merge origin/dev (v0.1.74) into feat/switch-ota-unified-launcher.

Keep Switch version chip alongside upstream's new quit button in the launcher header.
This commit is contained in:
Andrew Quenehen
2026-08-06 09:28:26 -03:00
115 changed files with 10474 additions and 2013 deletions
+47 -3
View File
@@ -312,8 +312,20 @@ local function setPage(imp, key, v)
imp._pages[key] = v
end
-- A hand-drawn X, for the same reason drawCheck exists below: the UI font has
-- no guaranteed glyph, and the launcher ships no icon asset for it.
local function drawCross(x, y, size, color)
love.graphics.push("all")
love.graphics.setColor(color)
love.graphics.setLineWidth(math.max(2, size * 0.16))
love.graphics.setLineJoin("bevel")
love.graphics.line(x, y, x + size, y + size)
love.graphics.line(x + size, y, x, y + size)
love.graphics.pop()
end
-- ------------------------------------------------------------- header
-- Rail, logo row (settings on the right), tab bar.
-- Rail, logo row (settings and quit on the right), tab bar.
-- Returns the y at which content may start. Its vertical arithmetic is
-- mirrored by headerHeight() at the bottom of this file (the short-window
-- scroll decision needs the height before anything draws) -- keep in sync.
@@ -356,7 +368,14 @@ local function buildHeader(imp, m)
by + math.floor((gear - th) / 2), PAL.yellow)
end
-- Settings gear, top-right corner.
-- The right cluster is laid out right to left -- Quit outermost, the gear
-- inboard of it -- but the two are REGISTERED gear first, because the first
-- focusable of the first frame adopts the keyboard ring and that must not be
-- the button that exits the app.
local quitX = rx - gear
rx = quitX - math.floor(6 * m.s)
-- Settings gear.
imp._gearIcon = imp._gearIcon
or love.graphics.newImage("assets/launcher/gear.png")
rx = rx - gear
@@ -381,6 +400,23 @@ local function buildHeader(imp, m)
end
end
-- Quit, top-right corner.
do
local x = quitX
Kit._audit("control", x, by, gear, gear, "quit")
local focused = Kit.focusable("quit", x, by, gear, gear)
local hot = focused or Kit.hover(x, by, gear, gear)
Theme.fill(x, by, gear, gear, hot and PAL.ink or PAL.bg, 1)
Theme.stroke(x, by, gear, gear, PAL.line,
hot and Theme.A.focus or Theme.A.hairline, 1)
local pad = math.floor(gear * 0.32)
drawCross(x + pad, by + pad, gear - 2 * pad,
hot and { 0, 0, 0, 1 } or { 1, 1, 1, 0.85 })
if Kit.press(x, by, gear, gear) or Kit._activateId == "quit" then
queueAction(imp, "quit", function() imp:_quitApp() end)
end
end
-- The self-update control lives in the FOOTER next to the BCG mark (small,
-- out of the wordmark's way -- it used to overlap the logo on a phone). It
-- still GLOWS through Kit.button when there is something to act on.
@@ -2180,7 +2216,15 @@ end
-- pinned Play block used to walk up over the cards on a short window, which
-- is unusable, and the footer simply lives below the fold until scrolled to.
local function minPanelHeight(m)
return math.floor(460 * m.s)
-- One column stacks the actions card, the slot card and the pinned Play
-- block in a single pile, so it needs more room than the side-by-side
-- layout: 460 was tuned for two columns, and on a squat one-column window
-- (a 4:3 device, a phone held upright) it left the slot card clipped
-- inert against the pinned buttons -- Kit's clip bounds hit-testing, so
-- no slot could be picked at all (#852). 660 fits the actions card, one
-- slot row with its pager and New button, and the pinned block; whatever
-- the window cannot show, the page scroll above reaches.
return math.floor((m.twoCol and 460 or 660) * m.s)
end
function LauncherView.draw(imp)