The folder board now shows the box explanation lines in place of the song count. Folders whose box.def has no explanation had an empty board; they show "N songs" as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
176 lines
8.3 KiB
Lua
176 lines
8.3 KiB
Lua
-- FolderBox rendering (genre kanban), split out of song_select.lua. Shared helpers and
|
|
-- the wheel/folder animation state come from box_base.
|
|
|
|
local base = require("song_select/file_navigator/box_base")
|
|
|
|
local folder_leg = base.folder_leg
|
|
local BOARD_W = base.BOARD_W
|
|
local CAP_H = base.CAP_H
|
|
local CEN_CLOSED_H, CEN_OPEN_H = base.CEN_CLOSED_H, base.CEN_OPEN_H
|
|
local FOLDER_CLOSE_MS, STALE_TIMER_MS = base.FOLDER_CLOSE_MS, base.STALE_TIMER_MS
|
|
local draw_board, draw_glow, glow_pulse = base.draw_board, base.draw_glow, base.glow_pulse
|
|
local draw_title = base.draw_title
|
|
local outline, board_outline = base.outline, base.board_outline
|
|
local box_id = base.box_id
|
|
local go_get, leg_f, box_fade, fly_offset = base.go_get, base.leg_f, base.box_fade, base.fly_offset
|
|
local is_decided = base.is_decided
|
|
local wheel_open_t, wheel_close_step = base.wheel_open_t, base.wheel_close_step
|
|
local folder_open_timeline, folder_close_timeline = base.folder_open_timeline, base.folder_close_timeline
|
|
|
|
-- Event/search folders are matched by display name (the engine folds DEFAULT/RECOMMENDED/
|
|
-- FAVORITE/DAN/DIFFICULTY into genre frame 0).
|
|
local EVENT_FOLDER_NAMES = { ["ドドンと!新曲祭り!"] = true, ["New Songs"] = true, ["New"] = true,
|
|
["おすすめ曲"] = true, ["Recommended"] = true, ["推薦歌曲"] = true }
|
|
local SORT_FOLDER_NAMES = { ["曲検索"] = true, ["Search Song"] = true, ["搜尋歌曲"] = true,
|
|
["むずかしさで探す"] = true, ["Difficulty Sort"] = true, ["難易度排序"] = true,
|
|
["むずかしさからえらぶ"] = true, ["Diff Sort"] = true }
|
|
local FOLDER_FRAME_EVENT, FOLDER_FRAME_SORT, FOLDER_FRAME_DANI = 11, 12, 10
|
|
|
|
local SORT_COLLECTIONS = { SEARCH = true, DIFFICULTY = true }
|
|
local EVENT_COLLECTIONS = { NEW = true, RECOMMENDED = true }
|
|
|
|
local function folder_frame(box)
|
|
local n = box.text_name
|
|
if n and EVENT_FOLDER_NAMES[n] then return FOLDER_FRAME_EVENT, true end
|
|
local col = box.collection
|
|
if col and col ~= "" then
|
|
if EVENT_COLLECTIONS[col] then return FOLDER_FRAME_EVENT, true end
|
|
if SORT_COLLECTIONS[col] then return FOLDER_FRAME_SORT, false end
|
|
end
|
|
if box.kind and box:kind() == "dan" then return FOLDER_FRAME_DANI, false end
|
|
if n and SORT_FOLDER_NAMES[n] then return FOLDER_FRAME_SORT, false end
|
|
return box:genre_frame(), false
|
|
end
|
|
|
|
-- instance_board_pattern: 960x120 strip centred on the board, y follows the title
|
|
function SongSelect:draw_pattern(bx, by, p, fade)
|
|
if not self._id_pattern then return end
|
|
tex.draw_texture(self._id_pattern, { x = bx - 480, y = by - 94 * p - 60, fade = fade })
|
|
end
|
|
|
|
-- The genre board title, re-made at the skin_config folder_box_name size.
|
|
local FOLDER_TITLE_SIZE = 54
|
|
function SongSelect:folder_title(box)
|
|
local key = box.text_name
|
|
if not key or key == "" then return box:name() end
|
|
local ff = folder_frame(box)
|
|
key = ff .. "\0" .. key
|
|
local t = self._folder_titles[key]
|
|
if t == nil then
|
|
t = text.create_raw_text(box.text_name, FOLDER_TITLE_SIZE, { 255, 255, 255, 255 },
|
|
board_outline(ff), false, outline(7.5), 2.0)
|
|
self._folder_titles[key] = t or false
|
|
end
|
|
return t or box:name()
|
|
end
|
|
|
|
function SongSelect:draw_folder_board(box, pb, ch_alpha, ch_off, ta, glow_a)
|
|
local bx, by = box:box_x(), box:box_y()
|
|
local fdx, fdy = fly_offset(box); bx, by = bx + fdx, by + fdy -- wheel fly-in
|
|
local cen_h = CEN_CLOSED_H + (CEN_OPEN_H - CEN_CLOSED_H) * pb
|
|
local fade = box_fade(box)
|
|
local frame, has_pattern = folder_frame(box)
|
|
|
|
-- the genre board furls/unfurls horizontally across the folder transition
|
|
local ltitle = 1.0
|
|
local lf = (box:draw_state() == "open" or is_decided(box)) and leg_f() or nil
|
|
if lf then
|
|
local sx, ba = go_get("genre_board_sx", lf, 1), go_get("genre_board_sx", lf, 2)
|
|
if sx and ba then
|
|
if ba <= 0.0 then return end -- the board is not on screen
|
|
if sx < 1.0 then -- furl / unfurl: the open slab alone
|
|
draw_board(self._id_bar_folder, frame, bx, by, CEN_OPEN_H,
|
|
fade * ba, false, sx)
|
|
return
|
|
end
|
|
ch_alpha = math.min(ch_alpha, go_get("genre_chara_a", lf, 1) or 1.0)
|
|
ta = math.min(ta, go_get("genre_intro_a", lf, 1) or 1.0)
|
|
ltitle = go_get("genre_title_a", lf, 1) or 1.0
|
|
end
|
|
end
|
|
|
|
if glow_a > 0 then draw_glow(self._id_glow, bx, by, cen_h, fade * glow_a * glow_pulse()) end
|
|
-- closed folder = bar_genre, open folder = folder_graphic (title band on top)
|
|
if pb <= 0.0 then
|
|
draw_board(self._id_bar_genre, frame, bx, by, cen_h, fade)
|
|
else
|
|
draw_board(self._id_bar_folder, frame, bx, by, cen_h, fade, pb >= 1.0)
|
|
if pb < 1.0 then draw_board(self._id_bar_genre, frame, bx, by, cen_h, fade * (1.0 - pb)) end
|
|
end
|
|
if has_pattern then self:draw_pattern(bx, by, pb, fade) end
|
|
|
|
if box:has_box_texture() then
|
|
-- a library-supplied box.png is fitted (aspect kept, shrink only) into the board rect
|
|
if ch_alpha > 0 then
|
|
box:draw_box_texture({ cx = bx, cy = by,
|
|
max_w = BOARD_W, max_h = CAP_H * 2 + CEN_OPEN_H,
|
|
fade = fade * ch_alpha })
|
|
end
|
|
-- title drawn after the art, matching the cabinet's depth order
|
|
draw_title(self:folder_title(box), bx, by - 94 * pb, fade * ltitle)
|
|
return
|
|
end
|
|
|
|
-- characters: texture has L at x=510 and R at x=1410
|
|
if ch_alpha > 0 then
|
|
local slide = 450.0 - ch_off
|
|
local gframe = box:genre_frame()
|
|
tex.draw_texture(self._id_box_chara, { frame = gframe, x = bx - 960 + slide, y = by - 240, x2 = 960 - 1920, fade = fade * ch_alpha,
|
|
src = { x = 0, y = 0, width = 960, height = 480 } })
|
|
tex.draw_texture(self._id_box_chara, { frame = gframe, x = bx - slide, y = by - 240, x2 = 960 - 1920, fade = fade * ch_alpha,
|
|
src = { x = 960, y = 0, width = 960, height = 480 } })
|
|
end
|
|
-- title rises to y-94 when open; the box explanation lines stand in for the intro text
|
|
draw_title(self:folder_title(box), bx, by - 94 * pb, fade * ltitle)
|
|
if ta > 0 then
|
|
local lines = {}
|
|
for _, l in ipairs(box:explanation() or {}) do
|
|
if l ~= "" then lines[#lines + 1] = l end
|
|
end
|
|
-- folders without an explanation show their song count, as before
|
|
local n = box.tja_count
|
|
if #lines == 0 and n and n > 0 then lines[1] = tostring(n) .. " songs" end
|
|
for i, l in ipairs(lines) do
|
|
local ckey = frame .. "\0" .. l
|
|
local label = self._count_cache[ckey]
|
|
if not label then
|
|
label = text.create_raw_text(l, 32, { 255, 255, 255, 255 }, board_outline(frame), false, outline(4.0), 2.0, 1.0, "FOT-Seurat_Pro_B.otf")
|
|
self._count_cache[ckey] = label
|
|
end
|
|
draw_title(label, bx, by + (49 + (i - (#lines + 1) / 2) * 36) * pb, fade * ta, BOARD_W - 120)
|
|
end
|
|
end
|
|
end
|
|
|
|
function SongSelect:draw_folder_closed(box)
|
|
local key = "f\0" .. box_id(box)
|
|
local e = wheel_close_step(key, FOLDER_CLOSE_MS, STALE_TIMER_MS)
|
|
if e then
|
|
local pb, ch_alpha, ch_off, ta = folder_close_timeline(e)
|
|
self:draw_folder_board(box, pb, ch_alpha, ch_off, ta, pb)
|
|
return
|
|
end
|
|
self:draw_folder_board(box, 0.0, 0.0, 340.0, 0.0, 0.0)
|
|
end
|
|
|
|
function SongSelect:draw_folder_open(box)
|
|
-- remember the decided board: the engine removes it from the wheel while its songs
|
|
-- load, and draw_overlays keeps drawing it until it has furled away
|
|
folder_leg.last_open_id, folder_leg.last_open_box = box_id(box), box
|
|
if is_decided(box) then
|
|
self._dec_frame = self._frame_no
|
|
self:draw_folder_board(box, 1.0, 1.0, 440.0, 1.0, 1.0)
|
|
return
|
|
end
|
|
local key = "f\0" .. box_id(box)
|
|
local t = wheel_open_t(key)
|
|
if t == nil then -- 508 ms pre-growth hold: fully closed
|
|
self:draw_folder_board(box, 0.0, 0.0, 340.0, 0.0, 0.0)
|
|
return
|
|
end
|
|
local pb, ch_alpha, ch_off, ta = folder_open_timeline(t)
|
|
self:draw_folder_board(box, pb, ch_alpha, ch_off, ta, pb)
|
|
end
|
|
|
|
return { folder_frame = folder_frame }
|