More menu work.

This commit is contained in:
Scott Duensing 2026-09-13 22:13:33 -05:00
parent 08415da805
commit 9bf4d9beb2
5 changed files with 95 additions and 27 deletions

View file

@ -1259,11 +1259,14 @@ Fixes
has both may delete the ".game" one; what was in it is rebuilt. has both may delete the ".game" one; what was in it is rebuilt.
- Both menus scroll their details. When a games.dat entry has more to say - Both menus scroll their details. When a games.dat entry has more to say
than fits, the text walks down by itself a line at a time and rests at than fits, the text walks down by itself a line at a time, rests, and
each end: up and down belong to the library, and a cabinet has no key to walks back up to the start rather than jumping there: up and down belong
spare for scrolling a paragraph. The document renderer's description box to the library, and a cabinet has no key to spare for scrolling a
had a scroll bar the style sheet gave it and nothing to drive it with; paragraph. The document renderer's description box had a scroll bar the
the overlay renderer clipped and left it at that. style sheet gave it and nothing to drive it with; the overlay renderer
clipped and left it at that. Page up and page down override the walk for
whoever does have a keyboard, and it carries on from where they leave it.
Both menus say so along the bottom.
- The overlay menu -- the one a machine with no GPU gets -- shows what a - The overlay menu -- the one a machine with no GPU gets -- shows what a
games.dat says about a game. Its details had the attract video's column games.dat says about a game. Its details had the attract video's column

View file

@ -28,7 +28,7 @@
<div id="description"></div> <div id="description"></div>
</div> </div>
<div id="footer"> <div id="footer">
<div id="hints">Up / Down: select &nbsp; Left / Right: page &nbsp; Start or button: play &nbsp; Service: tools</div> <div id="hints">Up / Down: select &nbsp; Left / Right: page &nbsp; PgUp / PgDn: details &nbsp; Start or button: play &nbsp; Service: tools</div>
</div> </div>
</div> </div>
<!-- Every service tool draws through this one page: Singe/Tools.singe hands the menu a list of <!-- Every service tool draws through this one page: Singe/Tools.singe hands the menu a list of

View file

@ -197,6 +197,17 @@ function onOverlayUpdate()
-- Are we displaying the grid background? -- Are we displaying the grid background?
if menuActive() then if menuActive() then
-- The page keys take the details over from the walk they do by themselves; the walk picks
-- up from wherever they leave it once its pause is up. They arrive as a scancode rather
-- than a switch because they are not one: a cabinet has no page keys and loses nothing.
local key = keyboardGetLastDown()
if key == SCANCODE.PAGEDOWN.value then
MENU_RENDER.scrollDetails(1)
elseif key == SCANCODE.PAGEUP.value then
MENU_RENDER.scrollDetails(-1)
end
-- The attract clip runs between the two frames the game's entry names. -- The attract clip runs between the two frames the game's entry names.
if videoGetFrame(VIDEO_ATTRACT) > GAME_LIST[GAME_SELECTED].ATTRACT_END then if videoGetFrame(VIDEO_ATTRACT) > GAME_LIST[GAME_SELECTED].ATTRACT_END then
videoSeek(VIDEO_ATTRACT, GAME_LIST[GAME_SELECTED].ATTRACT_START) videoSeek(VIDEO_ATTRACT, GAME_LIST[GAME_SELECTED].ATTRACT_START)

View file

@ -63,7 +63,8 @@ local VIDEO_ASPECT_H = 200
local videoX, videoY, videoW, videoH = 0, 0, 0, 0 local videoX, videoY, videoW, videoH = 0, 0, 0, 0
local scrollPending = false local scrollPending = false
local detailScroll = 0 -- How far down the description has walked, in pixels. local detailScroll = 0 -- How far down the description has walked, in pixels,
local detailDown = true -- and which way it is walking.
local detailClock = 0 local detailClock = 0
local SCROLL_HOLD = 3.0 -- Seconds it rests at each end before moving, as in the overlay local SCROLL_HOLD = 3.0 -- Seconds it rests at each end before moving, as in the overlay
local SCROLL_STEP = 1.1 -- renderer, and seconds a step while it is moving. local SCROLL_STEP = 1.1 -- renderer, and seconds a step while it is moving.
@ -281,6 +282,7 @@ MENU_RENDER.showGame = function(game)
guiSetValue(GUI, DOCUMENT, "description", menuEscape(game.DESCRIPTION)) guiSetValue(GUI, DOCUMENT, "description", menuEscape(game.DESCRIPTION))
menuElement("description").scroll_top = 0 menuElement("description").scroll_top = 0
detailScroll = 0 detailScroll = 0
detailDown = true
detailClock = 0 detailClock = 0
end end
@ -305,10 +307,10 @@ MENU_RENDER.pageSize = function()
end end
-- The description walks down by itself when there is more of it than fits, the same way the overlay -- The description walks by itself when there is more of it than fits -- down to the end, a rest,
-- renderer's details do. The style sheet gives this box a scroll bar, but nothing drives one from a -- and back up to the start -- the same way the overlay renderer's details do. The style sheet
-- joystick and up and down belong to the game list, so a long description was simply unreadable -- gives this box a scroll bar, but nothing drives one from a joystick and up and down belong to the
-- past its first few lines. -- game list, so a long description was simply unreadable past its first few lines.
local function menuScrollDetails() local function menuScrollDetails()
local element = menuElement("description") local element = menuElement("description")
local room = element.scroll_height - element.client_height local room = element.scroll_height - element.client_height
@ -322,11 +324,32 @@ local function menuScrollDetails()
detailClock = singeGetTicks() detailClock = singeGetTicks()
end end
local last = detailScroll >= room local resting = (detailScroll <= 0) or (detailScroll >= room)
local rest = (last or (detailScroll == 0)) and SCROLL_HOLD or SCROLL_STEP
if (singeGetTicks() - detailClock) >= (rest * 1000) then if (singeGetTicks() - detailClock) >= ((resting and SCROLL_HOLD or SCROLL_STEP) * 1000) then
detailScroll = last and 0 or math.min(detailScroll + SCROLL_LINE, room) if detailDown and (detailScroll >= room) then
detailDown = false
elseif not detailDown and (detailScroll <= 0) then
detailDown = true
end
detailScroll = math.max(math.min(detailScroll + (detailDown and SCROLL_LINE or -SCROLL_LINE), room), 0)
detailClock = singeGetTicks()
element.scroll_top = detailScroll
end
end
-- Page up and page down, by as much as is on show less a line to read across. The walk resumes
-- from where this leaves it, in the direction it was sent, after its usual rest.
MENU_RENDER.scrollDetails = function(direction)
local element = menuElement("description")
local room = element.scroll_height - element.client_height
if room > 0 then
local page = math.max(element.client_height - SCROLL_LINE, SCROLL_LINE)
detailScroll = math.max(math.min(detailScroll + direction * page, room), 0)
detailDown = (direction > 0)
detailClock = singeGetTicks() detailClock = singeGetTicks()
element.scroll_top = detailScroll element.scroll_top = detailScroll
end end

View file

@ -31,8 +31,9 @@
-- One game is on screen at a time, as it always was here, so there is no list to page through -- One game is on screen at a time, as it always was here, so there is no list to page through
-- visually: up and down move one game, left and right move a page, and the line above the details -- visually: up and down move one game, left and right move a page, and the line above the details
-- says where in the library the selection is. An entry with more to say than fits walks down by -- says where in the library the selection is. An entry with more to say than fits walks down by
-- itself, a line at a time, resting at each end: up and down belong to the library, and a cabinet -- itself a line at a time, rests, and walks back up again: up and down belong to the library, and
-- has no key to spare for scrolling a paragraph. -- a cabinet has no key to spare for scrolling a paragraph. Page up and page down override it for
-- whoever does have a keyboard.
local MARGIN_X = 25 local MARGIN_X = 25
@ -50,9 +51,11 @@ local LABEL_W = 150
local SUB_INDENT = 20 local SUB_INDENT = 20
local PAGE_ROWS = 10 local PAGE_ROWS = 10
local WRAP_BREAK = " [!wb!] " local WRAP_BREAK = " [!wb!] "
local HINTS = "Up / Down: select Left / Right: page Start or button: play Service: tools" local HINTS = "Up / Down: select Left / Right: page PgUp / PgDn: details Start or button: play Service: tools"
local font = nil local font = nil
local hintFont = nil -- Smaller: the hints are a footnote, and at the body's size the line
-- of them ran off the end of a 720 pixel overlay.
local introDone = false local introDone = false
local videoX, videoY, videoW, videoH = 0, 0, 0, 0 local videoX, videoY, videoW, videoH = 0, 0, 0, 0
local marqueeX, marqueeY, marqueeW, marqueeH = 0, 0, 0, 0 local marqueeX, marqueeY, marqueeW, marqueeH = 0, 0, 0, 0
@ -63,7 +66,8 @@ local spriteCabinet = nil
local spriteMarquee = nil local spriteMarquee = nil
local textSprites = {} local textSprites = {}
local textShadows = {} local textShadows = {}
local textFirst = 0 -- The first line of the details on show, which walks down by itself. local textFirst = 0 -- The first line of the details on show, which walks by itself.
local textDown = true -- and which way it is walking.
local textClock = 0 local textClock = 0
local SHADOW = 2 -- How far the shadow sits down and across from the text. local SHADOW = 2 -- How far the shadow sits down and across from the text.
local SCROLL_HOLD = 3.0 -- Seconds the details rest at each end before moving. local SCROLL_HOLD = 3.0 -- Seconds the details rest at each end before moving.
@ -224,7 +228,8 @@ MENU_RENDER = {}
MENU_RENDER.begin = function() MENU_RENDER.begin = function()
font = fontLoad("Singe/FreeSansBold.ttf", 18) font = fontLoad("Singe/FreeSansBold.ttf", 18)
hintFont = fontLoad("Singe/FreeSansBold.ttf", 13)
fontQuality(FONT_QUALITY_BLENDED) fontQuality(FONT_QUALITY_BLENDED)
fontSelect(font) fontSelect(font)
end end
@ -235,6 +240,10 @@ MENU_RENDER.finish = function()
fontUnload(font) fontUnload(font)
font = nil font = nil
end end
if hintFont then
fontUnload(hintFont)
hintFont = nil
end
end end
@ -359,6 +368,7 @@ MENU_RENDER.showGame = function(game)
end end
textLimit = (textHeight > 0) and math.max(math.floor((textH - LINE_HEIGHT) / textHeight), 1) or 1 textLimit = (textHeight > 0) and math.max(math.floor((textH - LINE_HEIGHT) / textHeight), 1) or 1
textFirst = 0 textFirst = 0
textDown = true
textClock = 0 textClock = 0
end end
@ -387,6 +397,19 @@ MENU_RENDER.hideGame = function()
end end
-- Page up and page down, by as much as is on show less a line to read across. The walk resumes
-- from where this leaves it, in the direction it was sent, after its usual rest.
MENU_RENDER.scrollDetails = function(direction)
local room = #textSprites - textLimit
if room > 0 then
textFirst = math.max(math.min(textFirst + direction * math.max(textLimit - 1, 1), room), 0)
textDown = (direction > 0)
textClock = singeGetTicks()
end
end
MENU_RENDER.pageSize = function() MENU_RENDER.pageSize = function()
return PAGE_ROWS return PAGE_ROWS
end end
@ -418,9 +441,10 @@ MENU_RENDER.frame = function()
colorForeground(255, 211, 90, 255) colorForeground(255, 211, 90, 255)
fontPrint(textX, textY, title) fontPrint(textX, textY, title)
-- What the game is. More of it than fits walks down a line at a time and starts over, so -- What the game is. More of it than fits walks a line at a time, down to the end and back up
-- everything an entry says is seen without a key to scroll it. -- to the start, so everything an entry says is seen without a key to scroll it -- and without
local last = textFirst + textLimit >= #textSprites -- the jump back to the top that reading the last line used to end in.
local room = #textSprites - textLimit
-- The clock starts when the details are first drawn, not when they were built: the menu is -- The clock starts when the details are first drawn, not when they were built: the menu is
-- behind the intro for the first seven seconds, and a page that had already scrolled itself by -- behind the intro for the first seven seconds, and a page that had already scrolled itself by
@ -428,11 +452,16 @@ MENU_RENDER.frame = function()
if textClock == 0 then if textClock == 0 then
textClock = singeGetTicks() textClock = singeGetTicks()
end end
if #textSprites > textLimit then if room > 0 then
local rest = (last or (textFirst == 0)) and SCROLL_HOLD or SCROLL_STEP local resting = (textFirst <= 0) or (textFirst >= room)
if (singeGetTicks() - textClock) >= (rest * 1000) then if (singeGetTicks() - textClock) >= ((resting and SCROLL_HOLD or SCROLL_STEP) * 1000) then
textFirst = last and 0 or (textFirst + 1) if textDown and (textFirst >= room) then
textDown = false
elseif not textDown and (textFirst <= 0) then
textDown = true
end
textFirst = math.max(math.min(textFirst + (textDown and 1 or -1), room), 0)
textClock = singeGetTicks() textClock = singeGetTicks()
end end
end end
@ -449,10 +478,12 @@ MENU_RENDER.frame = function()
-- Which key does what. The document renderer says the same thing along its footer; here -- Which key does what. The document renderer says the same thing along its footer; here
-- there was nothing at all, so the only way to learn that up and down move one game and left -- there was nothing at all, so the only way to learn that up and down move one game and left
-- and right move a page was to be told. -- and right move a page was to be told.
fontSelect(hintFont)
colorForeground(0, 0, 0, 255) colorForeground(0, 0, 0, 255)
fontPrint(hintsX + SHADOW, hintsY + SHADOW, HINTS) fontPrint(hintsX + SHADOW, hintsY + SHADOW, HINTS)
colorForeground(178, 196, 224, 255) colorForeground(178, 196, 224, 255)
fontPrint(hintsX, hintsY, HINTS) fontPrint(hintsX, hintsY, HINTS)
fontSelect(font)
end end