Files
gen1recomp/tools/save-editor/Theme.lua
T
bryanthaboi af47e19e1a Rebuild the launcher and save editor on a small immediate-mode UI kit
The launcher spent ~9ms per frame building and drawing, and the Find Mods
tab could hang the window for minutes. Both had the same root cause: a
retained UI tree rebuilt every frame, and blocking curl calls made from the
draw path.

Replace the vendored FlexLove engine (28.5k lines) with src/ui/kit/ (Kit,
Theme, Layout, Loader). The kit caches Text objects and all measurement,
allocates nothing in the steady state, and draws flat. Build+draw is now
under 1ms at every window size and on every tab (POKEPORT_LAUNCHER_PROF).

Move every network call off the render thread onto a love.thread pool
(src/net/Fetch.lua): mod index fetches, per-mod release checks, find-tab
stats, thumbnails and mod installs. Mod indexes prewarm at boot so the
Find Mods tab is populated before it is opened.

Paginate every list -- mods, find, save slots, settings, release notes,
versions -- with the page size derived from the real viewport height, so a
500-mod index costs what a 10-mod one does. Scrolling is gone.

Anything that waits now raises a non-dismissable loader; per-row background
work shows an inline spinner instead. The in-app updater moves to the top
right beside the settings gear and pulses when an update is waiting.

Theme is black with white outlines, no gradients or glows, and solid
colour-coded embossed buttons with bold labels. The game tabs keep their
cartridge colours. Everything is 1.3x larger. The save editor shares the
theme, and adding an item there is now a searchable pop-up like adding a
Pokemon.

Also:
- Reset rebinds, in Settings and under Touch Controls. Rebinds are additive
  (Input:applyBindings layers them over the defaults), so there was no
  in-game way to undo one.
- Launch options: --game red [--slot N] / POKEPORT_GAME boots straight into
  a game for shortcuts and frontends, falling back to that game's tab when
  its ROM is not imported.

Fixes found while porting:
- Ellipsis and letterspacing truncated bytes, not codepoints, so a
  multi-byte mod name crashed the first frame on a Japanese index.
  Measurement no longer throws on malformed input either.
- The new font set missed UiFont's kana fallback, rendering translated
  builds as tofu.
- Fetch workers idle in Channel:demand() and LOVE waits for live threads at
  exit, so the process outlived the window; quitting mid-download also
  waited on curl's 300s ceiling. Shut the pool down in love.quit and bound
  its transfer timeouts.
- In one column the save-slot card drew below the fold, over the footer,
  with no scrollbar left to reach it.

The two FlexLove engine tests guarded a scroll manager and an auto-height
propagation bug that no longer exist; replace them with a kit suite covering
page bounds, viewport sizing and UTF-8 truncation, and retarget the NX test
to assert the dependency is gone rather than that its perf guards are set.
2026-08-04 15:17:09 -04:00

90 lines
3.2 KiB
Lua

-- The save editor's look, delegated to the shared high-contrast theme
-- (src/ui/kit/Theme.lua).
--
-- The editor is reachable straight off a launcher save row (Edit), so the two
-- windows have to read as one app. They used to do that by keeping two
-- copies of the same navy palette in sync by hand; now there is one palette
-- and this file is the adapter. Everything below is either a re-export or a
-- shim for a primitive the old navy design had and the flat one does not:
--
-- gradRounded -> flat fill of the bottom colour (there are no gradients)
-- glow -> nothing (there are no glows)
-- dashed -> a plain hairline (the dashed path sampled a rounded
-- outline into a polyline every frame for an empty-state
-- box, which is a lot of work to say "nothing here")
--
-- Keeping the old signatures means the editor's panels did not have to be
-- rewritten to change theme, and a panel that has not been revisited yet
-- still lands on the new palette instead of drawing navy into a black window.
local Shared = require("src.ui.kit.Theme")
local Theme = {}
-- The palette itself, plus the aliases the editor's panels already use.
local PAL = {}
for k, v in pairs(Shared.PAL) do PAL[k] = v end
-- Names the editor uses that the shared palette spells differently.
PAL.cardTint = PAL.bg
PAL.cardBody = PAL.bg
PAL.chipTop = PAL.bg
PAL.chipBot = PAL.bg
PAL.chipInk = PAL.text
PAL.bgTop = PAL.bg
PAL.bgMid = PAL.bg
PAL.bgBot = PAL.bg
Theme.PAL = PAL
Theme.col = Shared.col
Theme.clamp = Shared.clamp
Theme.snap = Shared.snap
-- The editor's stroke carries a corner radius as its 5th argument (the
-- shared one takes the colour there). Route it to the rounded variant so
-- the radius the panels already pass is honoured rather than dropped.
function Theme.stroke(x, y, w, h, r, c, a, lw)
Shared.strokeRounded(x, y, w, h, c, a, lw, math.min(r or 0, 8))
end
Theme.spaced = Shared.spaced
Theme.spacedWidth = Shared.spacedWidth
Theme.ellipsize = Shared.ellipsize
Theme.ellipsizeLeft = Shared.ellipsizeLeft
Theme.meter = Shared.meter
Theme.versionRail = Shared.versionRail
Theme.fonts = Shared.fonts
Theme.radius = Shared.radius
Theme.fillRounded = Shared.fillRounded
Theme.strokeRounded = Shared.strokeRounded
Theme.emboss = Shared.emboss
Theme.BOLD_OFFSET = Shared.BOLD_OFFSET
-- The editor calls card/row with a trailing radius (and row with an alpha)
-- that the flat theme has no use for; accept and ignore them.
function Theme.card(x, y, w, h, _r)
Shared.card(x, y, w, h)
end
function Theme.row(x, y, w, h, _r, _alpha)
Shared.row(x, y, w, h)
end
-- Flat fill of the "bottom" colour: the old gradient's endpoint, so a control
-- that used to fade into it keeps roughly its old weight.
function Theme.gradRounded(x, y, w, h, _r, _cTop, cBot, _aTop, aBot)
Shared.fill(x, y, w, h, cBot, aBot)
end
-- No glows in this theme. Kept so call sites do not have to be edited, and
-- so nothing silently starts setting blend modes again.
function Theme.glow() end
-- The empty-state box is a hairline now, not a dashed outline.
function Theme.dashed(x, y, w, h, _r, _dash, _gap)
Shared.stroke(x, y, w, h, PAL.line, 0.22, 1)
end
function Theme.field(w, h)
Shared.field(w, h)
end
return Theme