mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-19 12:15:31 +02:00
skin studio updates, save sync CLOSES #1533
This commit is contained in:
@@ -0,0 +1,324 @@
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
local T = require("tests.harness")
|
||||
local check, eq = T.check, T.eq
|
||||
love = love or require("tests.love_stub")
|
||||
|
||||
love.graphics.setLineJoin = love.graphics.setLineJoin or function() end
|
||||
love.graphics.newShader = love.graphics.newShader or function() return {} end
|
||||
|
||||
local Kit = require("src.ui.kit.Kit")
|
||||
local RomImporter = require("src.import.RomImporter")
|
||||
local LauncherView = require("src.import.LauncherView")
|
||||
|
||||
local function window(w, h)
|
||||
love.graphics.getDimensions = function() return w, h end
|
||||
love.graphics.getPixelDimensions = function() return w, h end
|
||||
end
|
||||
|
||||
local function pointer(x, y)
|
||||
love.mouse.getPosition = function() return x, y end
|
||||
end
|
||||
|
||||
eq(Kit.scrollExtent(800, 500), 300, "the extent is exactly the overflow")
|
||||
eq(Kit.scrollExtent(400, 500), 0, "content that fits has no extent")
|
||||
eq(Kit.scrollExtent(400, -50), 400, "a negative viewport is no room, not more")
|
||||
eq(Kit.scrollExtent(nil, nil), 0, "an unmeasured region has no extent")
|
||||
|
||||
eq(Kit.scrollClamp(-10, 300), 0, "an offset above the top clamps to it")
|
||||
eq(Kit.scrollClamp(5000, 300), 300, "an offset past the end clamps to it")
|
||||
eq(Kit.scrollClamp(120, 0), 0, "a region with no travel sits at the top")
|
||||
|
||||
local at, left = Kit.scrollHandoff(0, 300, 120)
|
||||
eq(at, 120, "a move inside the extent is taken in full")
|
||||
eq(left, 0, "and hands nothing on")
|
||||
at, left = Kit.scrollHandoff(250, 300, 120)
|
||||
eq(at, 300, "a move past the end stops at the end")
|
||||
eq(left, 70, "and hands the remainder to whatever is behind it")
|
||||
at, left = Kit.scrollHandoff(0, 300, -80)
|
||||
eq(at, 0, "a move above the top stops at the top")
|
||||
eq(left, -80, "and hands that remainder on with its sign")
|
||||
|
||||
local function wheelCase(offset, maxScroll, wheel, mx, my)
|
||||
Kit.blockClicks = false
|
||||
Kit.mouseX, Kit.mouseY = mx, my
|
||||
Kit.wheelY = wheel
|
||||
Kit._clipRect = nil
|
||||
local moved, took = Kit.scrollWheel(offset, maxScroll, 0, 0, 100, 100, 50)
|
||||
return moved, took, Kit.wheelY
|
||||
end
|
||||
|
||||
local moved, took, leftWheel = wheelCase(0, 300, -1, 50, 50)
|
||||
eq(moved, 50, "a notch over the region moves it by one step")
|
||||
eq(took, true, "and reports the region took it")
|
||||
eq(leftWheel, 0, "so nothing reaches the surface behind it")
|
||||
|
||||
moved, took, leftWheel = wheelCase(300, 300, -1, 50, 50)
|
||||
eq(moved, 300, "a region already at its bottom does not move")
|
||||
eq(took, false, "and does not claim the notch")
|
||||
eq(leftWheel, -1, "which is what lets the page scroll take over")
|
||||
|
||||
moved, took, leftWheel = wheelCase(0, 300, 1, 50, 50)
|
||||
eq(moved, 0, "a region at the top ignores an upward notch")
|
||||
eq(leftWheel, 1, "and passes it on")
|
||||
|
||||
moved, took, leftWheel = wheelCase(0, 300, -1, 400, 400)
|
||||
eq(took, false, "a notch outside the region is not the region's")
|
||||
eq(leftWheel, -1, "and stays queued")
|
||||
|
||||
Kit.blockClicks = true
|
||||
Kit.mouseX, Kit.mouseY, Kit.wheelY = 50, 50, -1
|
||||
moved, took = Kit.scrollWheel(0, 300, 0, 0, 100, 100, 50)
|
||||
eq(took, false, "a shielded frame (modal up) leaves the region alone")
|
||||
eq(Kit.wheelY, -1, "so the modal's own scroller still sees the notch")
|
||||
Kit.blockClicks = false
|
||||
|
||||
local function skinLauncher(count)
|
||||
local imp = RomImporter.new(function() end, { launcher = true })
|
||||
imp.tab = "skins"
|
||||
local skins = {}
|
||||
for i = 1, count do
|
||||
skins[i] = { id = "skin" .. i, source = "user", controls = 8, pages = 1 }
|
||||
end
|
||||
imp._skins = skins
|
||||
imp._ensureSkins = function() return skins end
|
||||
return imp
|
||||
end
|
||||
|
||||
window(360, 780)
|
||||
local imp = skinLauncher(12)
|
||||
LauncherView.draw(imp)
|
||||
LauncherView.draw(imp)
|
||||
local rect = imp._tabRegionRect
|
||||
check(rect ~= nil, "the panel publishes the rect its region occupies")
|
||||
check((imp._tabScrollMax.skins or 0) > 0,
|
||||
"a panel with more rows than its viewport scrolls")
|
||||
eq(imp._tabScroll.skins, 0, "a freshly drawn panel sits at the top")
|
||||
check(imp._tabContentH.skins > rect.h,
|
||||
"the region's content is taller than the viewport it is clipped to")
|
||||
|
||||
pointer(rect.x + 10, rect.y + 10)
|
||||
imp._wheelY = -1
|
||||
LauncherView.draw(imp)
|
||||
local step = math.floor(48 * Kit.scale)
|
||||
eq(imp._tabScroll.skins, step, "one notch scrolls the panel by one step")
|
||||
eq(imp._pageScroll, 0,
|
||||
"and the page under it does not move while the panel still can")
|
||||
|
||||
for _ = 1, 30 do
|
||||
imp._wheelY = -1
|
||||
LauncherView.draw(imp)
|
||||
end
|
||||
eq(imp._tabScroll.skins, imp._tabScrollMax.skins,
|
||||
"held down, the panel reaches its own bottom")
|
||||
eq(imp._pageScroll, imp._pageScrollMax,
|
||||
"and only then does the leftover scroll the page")
|
||||
|
||||
for _ = 1, 40 do
|
||||
imp._wheelY = 1
|
||||
LauncherView.draw(imp)
|
||||
end
|
||||
eq(imp._tabScroll.skins, 0, "scrolling back up returns the panel to the top")
|
||||
eq(imp._pageScroll, 0, "and the page with it")
|
||||
|
||||
imp._wheelY = -1
|
||||
LauncherView.draw(imp)
|
||||
local parked = imp._tabScroll.skins
|
||||
check(parked > 0, "the skins panel is parked mid-scroll")
|
||||
imp:_switchTab("red")
|
||||
LauncherView.draw(imp)
|
||||
eq(imp._tabScroll.red or 0, 0, "the game tab has its own offset")
|
||||
eq(imp._tabScroll.skins, parked, "and the skins offset survives the switch")
|
||||
imp:_switchTab("skins")
|
||||
LauncherView.draw(imp)
|
||||
eq(imp._tabScroll.skins, parked, "coming back lands where the player left")
|
||||
|
||||
imp._skins = {}
|
||||
imp._ensureSkins = function() return {} end
|
||||
LauncherView.draw(imp)
|
||||
LauncherView.draw(imp)
|
||||
eq(imp._tabScrollMax.skins, 0, "a panel that now fits has no travel")
|
||||
eq(imp._tabScroll.skins, 0, "and its offset comes back with it")
|
||||
|
||||
local mods = {}
|
||||
for i = 1, 60 do
|
||||
mods[#mods + 1] = {
|
||||
id = "mod" .. i, name = "Mod " .. i, version = "1.0.0",
|
||||
status = "ok", badge = "gameplay", description = "a mod",
|
||||
enabledByVersion = { red = true },
|
||||
}
|
||||
end
|
||||
window(360, 780)
|
||||
local modImp = RomImporter.new(function() end, { launcher = true })
|
||||
modImp.tab = "mods"
|
||||
modImp.mods = mods
|
||||
modImp._ensureMods = function() return mods end
|
||||
LauncherView.draw(modImp)
|
||||
LauncherView.draw(modImp)
|
||||
check((modImp._modScrollMax or 0) > 0,
|
||||
"60 mods overflow the list viewport inside the panel")
|
||||
local list = modImp._modListRect
|
||||
check(list.x + list.w
|
||||
<= modImp._tabRegionRect.x + modImp._tabRegionRect.w - Kit.scrollBarW(),
|
||||
"the rows stop short of the region's scrollbar gutter")
|
||||
pointer(list.x + 10, list.y + 10)
|
||||
modImp._wheelY = -1
|
||||
LauncherView.draw(modImp)
|
||||
check((modImp.modScroll or 0) > 0, "a notch over the mod list scrolls the list")
|
||||
eq(modImp._tabScroll.mods or 0, 0, "not the panel region around it")
|
||||
eq(modImp._pageScroll, 0, "and not the page behind that")
|
||||
|
||||
modImp._modActions = "mod1"
|
||||
local shielded = modImp.modScroll
|
||||
local shieldedPage = modImp._pageScroll
|
||||
pointer(list.x + 10, list.y + 10)
|
||||
modImp._wheelY = -1
|
||||
LauncherView.draw(modImp)
|
||||
eq(modImp.modScroll, shielded, "a shielded mod list ignores the notch")
|
||||
eq(modImp._tabScroll.mods or 0, 0, "and so does the region under the scrim")
|
||||
eq(modImp._pageScroll, shieldedPage, "and the page behind that")
|
||||
modImp._modActions = nil
|
||||
modImp._wheelY = 0
|
||||
LauncherView.draw(modImp)
|
||||
|
||||
window(360, 780)
|
||||
local gameImp = RomImporter.new(function() end, { launcher = true })
|
||||
gameImp.tab = "red"
|
||||
gameImp.ready = { red = true }
|
||||
gameImp.slots = { red = {} }
|
||||
for i = 1, 8 do
|
||||
gameImp.slots.red[i] = { id = "slot" .. i, name = "Slot " .. i }
|
||||
end
|
||||
gameImp._ensureSlots = function() end
|
||||
LauncherView.draw(gameImp)
|
||||
LauncherView.draw(gameImp)
|
||||
check((gameImp._tabScrollMax.red or 0) > 0,
|
||||
"a game tab whose cart and slots outgrow the viewport scrolls too")
|
||||
check(gameImp._tabContentH.red > gameImp._tabRegionRect.h,
|
||||
"because it reports its NATURAL height, not the height it was given")
|
||||
pointer(gameImp._tabRegionRect.x + 10, gameImp._tabRegionRect.y + 10)
|
||||
gameImp._wheelY = -1
|
||||
LauncherView.draw(gameImp)
|
||||
check((gameImp._tabScroll.red or 0) > 0, "and a notch over it moves it")
|
||||
|
||||
window(360, 780)
|
||||
local touchImp = skinLauncher(12)
|
||||
LauncherView.draw(touchImp)
|
||||
LauncherView.draw(touchImp)
|
||||
local treg = touchImp._tabRegionRect
|
||||
local tmax = touchImp._tabScrollMax.skins
|
||||
check(tmax > 0, "the touched panel has travel")
|
||||
LauncherView.touchpressed(touchImp, 1, treg.x + 20, treg.y + 40)
|
||||
LauncherView.touchmoved(touchImp, 1, treg.x + 20, treg.y + 40 - 200)
|
||||
eq(touchImp._tabScroll.skins, math.min(200, tmax),
|
||||
"dragging up scrolls the panel by the finger's travel")
|
||||
eq(touchImp._pageScroll, 0, "while the panel still has travel, the page waits")
|
||||
LauncherView.touchmoved(touchImp, 1, treg.x + 20, treg.y + 40 - 200 - tmax * 2)
|
||||
eq(touchImp._tabScroll.skins, tmax, "a longer drag reaches the panel's bottom")
|
||||
check((touchImp._pageScroll or 0) > 0, "and spills into the page from there")
|
||||
LauncherView.touchreleased(touchImp, 1, treg.x + 20, treg.y - 400)
|
||||
|
||||
window(360, 780)
|
||||
local dragMods = RomImporter.new(function() end, { launcher = true })
|
||||
dragMods.tab = "mods"
|
||||
dragMods.mods = mods
|
||||
dragMods._ensureMods = function() return mods end
|
||||
LauncherView.draw(dragMods)
|
||||
LauncherView.draw(dragMods)
|
||||
local dlist = dragMods._modListRect
|
||||
local dListMax = dragMods._modScrollMax
|
||||
local dRegionMax = dragMods._tabScrollMax.mods
|
||||
check(dListMax > 0 and dRegionMax > 0,
|
||||
"the mods tab has both an inner list and a region to scroll")
|
||||
LauncherView.touchpressed(dragMods, 7, dlist.x + 20, dlist.y + 30)
|
||||
LauncherView.touchmoved(dragMods, 7, dlist.x + 20, dlist.y + 30 - 60)
|
||||
eq(dragMods.modScroll, math.min(60, dListMax),
|
||||
"the first pixels of the drag move the list")
|
||||
eq(dragMods._tabScroll.mods or 0, 0, "and nothing else")
|
||||
LauncherView.touchmoved(dragMods, 7, dlist.x + 20,
|
||||
dlist.y + 30 - 60 - dListMax - dRegionMax * 2)
|
||||
eq(dragMods.modScroll, dListMax, "carrying on saturates the list")
|
||||
eq(dragMods._tabScroll.mods, dRegionMax,
|
||||
"then the same gesture walks the region to its bottom")
|
||||
check((dragMods._pageScroll or 0) > 0, "and only then reaches the page")
|
||||
LauncherView.touchreleased(dragMods, 7, dlist.x + 20, dlist.y - 900)
|
||||
|
||||
dragMods._skins = { { id = "s1", source = "user", controls = 8, pages = 1 } }
|
||||
for i = 2, 12 do
|
||||
dragMods._skins[i] = { id = "s" .. i, source = "user", controls = 8, pages = 1 }
|
||||
end
|
||||
dragMods._ensureSkins = function() return dragMods._skins end
|
||||
dragMods.modScroll = 0
|
||||
local heldModScroll = dragMods.modScroll
|
||||
local overList = dlist.y + 30
|
||||
dragMods:_switchTab("skins")
|
||||
LauncherView.draw(dragMods)
|
||||
LauncherView.draw(dragMods)
|
||||
local sreg = dragMods._tabRegionRect
|
||||
check((dragMods._tabScrollMax.skins or 0) > 0, "the skins tab has travel")
|
||||
LauncherView.touchpressed(dragMods, 9, sreg.x + 20, overList)
|
||||
LauncherView.touchmoved(dragMods, 9, sreg.x + 20, overList - 200)
|
||||
check((dragMods._tabScroll.skins or 0) > 0,
|
||||
"a drag on the skins tab scrolls the skins tab")
|
||||
eq(dragMods.modScroll, heldModScroll,
|
||||
"and leaves the mod list where the player parked it")
|
||||
LauncherView.touchreleased(dragMods, 9, sreg.x + 20, sreg.y - 400)
|
||||
|
||||
love.graphics.polygon = love.graphics.polygon or function() end
|
||||
window(360, 780)
|
||||
local padImp = skinLauncher(12)
|
||||
LauncherView.draw(padImp)
|
||||
LauncherView.draw(padImp)
|
||||
local preg = padImp._tabRegionRect
|
||||
padImp._padCursorActive = true
|
||||
padImp._padCursor = { x = preg.x + 10, y = preg.y + preg.h - 4 }
|
||||
LauncherView.wheelmoved(padImp, 0, -1)
|
||||
LauncherView.draw(padImp)
|
||||
check((padImp._tabScroll.skins or 0) > 0,
|
||||
"the pad's synthesized wheel scrolls the region its cursor sits in")
|
||||
eq(padImp._pageScroll, 0, "and not the page behind it")
|
||||
|
||||
window(1280, 720)
|
||||
local edgeImp = skinLauncher(40)
|
||||
LauncherView.draw(edgeImp)
|
||||
LauncherView.draw(edgeImp)
|
||||
local ereg = edgeImp._tabRegionRect
|
||||
check((edgeImp._tabScrollMax.skins or 0) > 0, "the wide window still overflows")
|
||||
eq(edgeImp._pageScrollMax, 0, "with no page scroll left to catch the notch")
|
||||
check(ereg.y + ereg.h < 720, "and a region that ends above the safe area")
|
||||
edgeImp._padCursorActive = true
|
||||
edgeImp._padCursor = { x = ereg.x + 20, y = 719 }
|
||||
edgeImp._padAxis = { lefty = 1 }
|
||||
edgeImp._padDir = {}
|
||||
pointer(ereg.x + 20, 719)
|
||||
edgeImp:_updatePadCursor(0.5)
|
||||
check((edgeImp._wheelY or 0) < 0, "the edge push synthesizes a notch")
|
||||
LauncherView.draw(edgeImp)
|
||||
check((edgeImp._tabScroll.skins or 0) > 0,
|
||||
"which reaches the tab region even though the cursor is below it")
|
||||
|
||||
local function read(path)
|
||||
local f = assert(io.open(path, "r"))
|
||||
local src = f:read("*a")
|
||||
f:close()
|
||||
return src
|
||||
end
|
||||
|
||||
local view = read("src/import/LauncherView.lua")
|
||||
check(view:find("Kit.scrollBegin(", 1, true) ~= nil,
|
||||
"the panel dispatch opens a scroll region")
|
||||
check(view:find("Kit.scrollEnd(", 1, true) ~= nil, "and closes it")
|
||||
check(view:find("modListWantsWheel", 1, true) ~= nil,
|
||||
"the nested mod list is asked before the region takes a notch")
|
||||
check(view:find("start.region", 1, true) ~= nil,
|
||||
"a touch drag that began in the region scrolls the region")
|
||||
check(view:find("Kit.scrollGutter(", 1, true) ~= nil,
|
||||
"the panels lay out inside a gutter, so the thumb covers no control")
|
||||
check(view:find("Kit.scrollHandoff(tabScrollAt(imp)", 1, true) ~= nil,
|
||||
"and hands its leftover to the page, like the wheel does")
|
||||
|
||||
local kit = read("src/ui/kit/Kit.lua")
|
||||
check(kit:find("function Kit.scrollWheel", 1, true) ~= nil,
|
||||
"the kit owns the wheel rule, so no panel hand-rolls a fifth copy")
|
||||
|
||||
T.finish("launcher scroll regions")
|
||||
Reference in New Issue
Block a user