Files
YataiDONNijiiro/Scripts/song_select/file_navigator/box_folder.lua
T
TaikoZhongandClaude Opus 5.5 a7b0c1291d song select: folders without an explanation show their song count
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>
2026-10-15 23:41:01 +08:00

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 }