skins and skin studio

This commit is contained in:
bryanthaboi
2026-08-17 06:47:33 -04:00
parent a5b674f9da
commit 3cca70608f
45 changed files with 4194 additions and 64 deletions
+123 -11
View File
@@ -18,6 +18,7 @@
local SaveData = require("src.core.SaveData")
local TouchControls = require("src.core.TouchControls")
local TouchSkin = require("src.core.TouchSkin")
local PadCursor = require("src.ui.PadCursor")
local GamepadMap = require("src.core.GamepadMap")
@@ -46,6 +47,15 @@ local function roundRect(mode, x, y, w, h, r)
love.graphics.rectangle(mode, x, y, w, h, r, r)
end
local function fitText(font, text, maxW)
text = tostring(text)
if maxW <= 0 or font:getWidth(text) <= maxW then return text end
while #text > 1 and font:getWidth(text .. "...") > maxW do
text = text:sub(1, #text - 1)
end
return text .. "..."
end
function Editor.load(opts)
opts = opts or {}
Editor.onClose = opts.onClose
@@ -74,9 +84,52 @@ function Editor.load(opts)
TouchControls:applyOptions(applied)
TouchControls:setPreview(true)
Editor.enabled = TouchControls.enabled ~= false
Editor.skins = TouchSkin.list()
Editor.exportMsg = nil
PadCursor.reset()
end
function Editor.skinIndex()
for i, entry in ipairs(Editor.skins or {}) do
if entry.id == TouchControls.skinId then return i + 1 end
end
return 1
end
function Editor.skinLabel()
local entry = (Editor.skins or {})[Editor.skinIndex() - 1]
if not entry then return "Built-in pad" end
local skin = TouchSkin.active
local pages = skin and #skin.pages or 1
if pages > 1 then return entry.id .. " (" .. pages .. " pages)" end
return entry.id
end
function Editor.cycleSkin(dir)
local n = #(Editor.skins or {}) + 1
local idx = ((Editor.skinIndex() - 1 + (dir or 1)) % n) + 1
local entry = Editor.skins[idx - 1]
Editor.exportMsg = nil
TouchControls:selectSkin(entry and entry.id or nil)
TouchControls:ensureImages()
end
function Editor.exportSkin()
local skin = TouchSkin.active
if not skin then return end
local path, missing = TouchSkin.export(skin)
if not path then
Editor.exportMsg = "Export failed: " .. tostring(missing)
return
end
Editor.exportMsg = "Exported to " .. path .. " in your save folder."
if type(missing) == "table" and missing[1] then
Editor.exportMsg = Editor.exportMsg
.. " " .. #missing .. " image(s) were missing and left out."
end
Editor.skins = TouchSkin.list()
end
function Editor.unload()
TouchControls:setPreview(false)
TouchControls:reset()
@@ -93,6 +146,7 @@ local function persist()
local cfg = TouchControls:config()
local block = {
enabled = cfg.enabled,
skin = cfg.skin,
layouts = cfg.layouts,
}
if Editor.version == "gold" then
@@ -158,6 +212,8 @@ function Editor.draw()
col(PAL.bgTop, 0.85)
love.graphics.circle("fill", ox + ww * 0.5, oy + wh * 0.15, math.max(ww, wh) * 0.55)
if TouchSkin.active then TouchControls:draw() end
local pad = 18 * s
local barH = 56 * s
local btnH = 40 * s
@@ -170,16 +226,18 @@ function Editor.draw()
love.graphics.setLineWidth(1)
love.graphics.line(0, oy + barH + pad, fullW, oy + barH + pad)
love.graphics.setFont(Editor.fonts.title)
col(PAL.white)
love.graphics.print("Touch Controls", ox + pad, oy + pad + 4 * s)
-- Done / Reset
local done = { x = ox + ww - pad - btnW, y = oy + pad + (barH - btnH) / 2,
w = btnW, h = btnH }
local reset = { x = done.x - 10 * s - btnW, y = done.y, w = btnW, h = btnH }
Editor.rects.done, Editor.rects.reset = done, reset
love.graphics.setFont(Editor.fonts.title)
col(PAL.white)
love.graphics.print(
fitText(Editor.fonts.title, "Touch Controls", reset.x - 10 * s - (ox + pad)),
ox + pad, oy + pad + 4 * s)
local function chromeBtn(r, label, fill)
col(fill, 0.9)
roundRect("fill", r.x, r.y, r.w, r.h, 8 * s)
@@ -223,9 +281,48 @@ function Editor.draw()
chromeBtn(toggle, on and "Disable" or "Enable",
on and PAL.red or PAL.green)
local skinY = cardY + cardH + 10 * s
col(PAL.card, 0.88)
roundRect("fill", cardX, skinY, cardW, cardH, 12 * s)
col(PAL.stroke, 0.4)
roundRect("line", cardX, skinY, cardW, cardH, 12 * s)
local stepW = 52 * s
local skinNext = { x = cardX + cardW - 16 * s - stepW,
y = skinY + (cardH - btnH) / 2, w = stepW, h = btnH }
local skinPrev = { x = skinNext.x - 10 * s - stepW, y = skinNext.y,
w = stepW, h = btnH }
Editor.rects.skinNext, Editor.rects.skinPrev = skinNext, skinPrev
local leftmost = skinPrev.x
if TouchSkin.active then
local expW = 86 * s
local export = { x = skinPrev.x - 10 * s - expW, y = skinPrev.y,
w = expW, h = btnH }
Editor.rects.export = export
leftmost = export.x
else
Editor.rects.export = nil
end
local skinTextW = leftmost - 10 * s - (cardX + 16 * s)
love.graphics.setFont(Editor.fonts.body)
col(PAL.label)
love.graphics.print("Skin", cardX + 16 * s, skinY + 12 * s)
love.graphics.setFont(Editor.fonts.btn)
col(TouchControls.skinId and PAL.green or PAL.white)
love.graphics.print(fitText(Editor.fonts.btn, Editor.skinLabel(), skinTextW),
cardX + 16 * s, skinY + 34 * s)
chromeBtn(skinPrev, "<", { 60, 70, 110 })
chromeBtn(skinNext, ">", { 60, 70, 110 })
if Editor.rects.export then
chromeBtn(Editor.rects.export, "Export", { 60, 70, 110 })
end
-- size card (#633): -/+ resize every control in the orientation on
-- screen; the heading names it so it is plain the other one is untouched
local sizeY = cardY + cardH + 10 * s
local sizeY = skinY + cardH + 10 * s
col(PAL.card, 0.88)
roundRect("fill", cardX, sizeY, cardW, cardH, 12 * s)
col(PAL.stroke, 0.4)
@@ -243,7 +340,6 @@ function Editor.draw()
string.format("%d%%", math.floor((bucket.scale or 1) * 100 + 0.5)),
cardX + 16 * s, sizeY + 34 * s)
local stepW = 52 * s
local plus = { x = cardX + cardW - 16 * s - stepW,
y = sizeY + (cardH - btnH) / 2, w = stepW, h = btnH }
local minus = { x = plus.x - 10 * s - stepW, y = plus.y, w = stepW, h = btnH }
@@ -254,13 +350,19 @@ function Editor.draw()
-- hint
love.graphics.setFont(Editor.fonts.body)
col(PAL.label, 0.9)
local hint = on
and "Drag each button to reposition, -/+ to resize. Portrait and landscape are saved separately when you tap Done."
or "Controls are hidden in-game. Enable them to show and edit the layout."
local hint
if Editor.exportMsg then
hint = Editor.exportMsg
elseif not on then
hint = "Controls are hidden in-game. Enable them to show and edit the layout."
elseif TouchSkin.active then
hint = "This skin owns its own art, hitboxes and screen position, so size and dragging are off. Drop a RetroArch overlay folder or .zip into the skins folder of your save directory to add more."
else
hint = "Drag each button to reposition, -/+ to resize. Portrait and landscape are saved separately when you tap Done."
end
love.graphics.printf(hint, ox + pad, sizeY + cardH + 12 * s, ww - 2 * pad, "left")
-- the overlay itself (preview mode; dimmed when disabled)
TouchControls:draw()
if not TouchSkin.active then TouchControls:draw() end
-- highlight the control under drag
if Editor.drag then
@@ -282,6 +384,9 @@ local function beginDrag(id, x, y)
if inside(Editor.rects.done, x, y) then close(); return end
if inside(Editor.rects.reset, x, y) then resetLayout(); return end
if inside(Editor.rects.toggle, x, y) then toggleEnabled(); return end
if inside(Editor.rects.skinPrev, x, y) then Editor.cycleSkin(-1); return end
if inside(Editor.rects.skinNext, x, y) then Editor.cycleSkin(1); return end
if inside(Editor.rects.export, x, y) then Editor.exportSkin(); return end
if inside(Editor.rects.sizeDown, x, y) then
TouchControls:nudgeScale(-TouchControls.SCALE_STEP); return
end
@@ -289,6 +394,7 @@ local function beginDrag(id, x, y)
TouchControls:nudgeScale(TouchControls.SCALE_STEP); return
end
if TouchSkin.active then return end
local name = TouchControls:hitTest(x, y)
if not name then return end
local zone = TouchControls:layout()[name]
@@ -439,6 +545,12 @@ function Editor.keypressed(key)
close()
elseif key == "r" then
resetLayout()
elseif key == "[" then
Editor.cycleSkin(-1)
elseif key == "]" then
Editor.cycleSkin(1)
elseif key == "e" then
Editor.exportSkin()
-- desktop shortcuts for the size -/+ (POKEPORT_TOUCH=1 testing, #633)
elseif key == "-" or key == "kp-" then
TouchControls:nudgeScale(-TouchControls.SCALE_STEP)