Files
PyTaikoGreen/Scripts/song_select/file_navigator/box_folder.lua
T
TaikoZhongandClaude Opus 5.5 2e694d4952 song select: refresh a folder's song count text when the count changes
The folder texts are cached on first draw, but the engine fills in a
folder's song count with a background scan after the boxes are first
drawn, so the count text stayed at 0 for the rest of the session (e.g.
the folder under the cursor at startup kept showing 0 songs). The cache
now remembers the count it was built with and rebuilds that one text
when box.tja_count changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 14:12:42 +08:00

233 lines
9.3 KiB
Lua

-- FolderBox rendering (genre folder closed/open), split out of song_select.lua.
-- Mirrors src/objects/song_select/file_navigator/box_folder.cpp.
local base = require("song_select/file_navigator/box_base")
local T = base.T
local draw_shadows = base.draw_shadows
local draw_base_closed = base.draw_base_closed
local vertical_name = base.vertical_name
local WHITE = { 255, 255, 255, 255 }
local BLACK = { 0, 0, 0, 255 }
local function is_2p_screen()
return (tex.current_screen and tex.current_screen() or "") == "SONG_SELECT_2P"
end
local function make_count_text(count)
local count_cfg = tex.get_skin_config("song_tja_count") or { font_size = 35 }
return text.create_raw_text(base.format_song_count(count), count_cfg.font_size or 35, WHITE, BLACK, false, 5.0, 2.0, 1.0)
end
-- Folder text objects (hori_name, tja_count_text, explanation) from FolderBox::load_text,
-- cached per box on self. The song count is filled in by a background scan after the box is
-- first drawn, so its text is rebuilt whenever the count changes (it stayed at 0 otherwise).
local function folder_texts(self, box)
self._folder_texts = self._folder_texts or {}
local key = box:path()
local t = self._folder_texts[key]
if t == nil then
local name = box.text_name or ""
local hori_cfg = tex.get_skin_config("song_hori_name") or { font_size = 40 }
local hori_name = text.create_raw_text(name, hori_cfg.font_size or 40, WHITE, BLACK, false, 5.0, 2.0, 1.0)
local count = box.tja_count or 0
local tja_count_text = make_count_text(count)
local explanation = box:explanation() or {}
local exp_cfg = tex.get_skin_config("box_folder_explanation") or { font_size = 34 }
local exp_box = tex.get_skin_config("box_folder_explanation_box") or { width = 160 }
local lang = tex.language()
local is_cjk = text.language_is_cjk(lang)
local exp_texts = {}
for i = 1, 3 do
local s = explanation[i]
if s and s ~= "" then
local content = s
if not is_cjk then
content = text.word_wrap(s, exp_cfg.font_size or 34, 2.0, (exp_box.width or 160) - 20.0)
end
exp_texts[i] = text.create_raw_text(content, exp_cfg.font_size or 34, WHITE, BLACK, is_cjk, 5.0, 2.0, 1.0)
end
end
t = { hori_name = hori_name, tja_count_text = tja_count_text, count = count, explanation = exp_texts }
self._folder_texts[key] = t
else
local count = box.tja_count or 0
if count ~= t.count then
t.tja_count_text = make_count_text(count)
t.count = count
end
end
return t
end
-- FolderBox::draw_closed crown badge (best crown, no fade).
local function draw_folder_crown(box, bx, by)
local function draw_one(info, px)
if not info then return end
base.draw_crown(info.crown, info.frame, px, by, 1.0)
end
local crown_1p = box:highest_crown()
local crown_2p
if is_2p_screen() then crown_2p = box:highest_crown_p2() end
if crown_2p then
local info = tex.get_texture_info("yellow_box", "crown_dfc")
local half = (info and info.width or 0) * 0.35
draw_one(crown_1p, bx - half)
draw_one(crown_2p, bx + half)
elseif crown_1p then
draw_one(crown_1p, bx)
end
end
function SongSelect:draw_folder_closed(box)
draw_base_closed(box)
local bx = box:box_x()
local by = box:box_y()
local fade = box.fade.attribute
local tex_index = box:texture_index()
local use_shader = box:has_recolor()
if use_shader then box:begin_recolor() end
tex.draw_texture(T("box", "folder_clip"), { frame = tex_index, x = bx - (1.0 * tex.get_screen_scale()), y = by, fade = fade })
if use_shader then box:end_recolor() end
local name = vertical_name(self, box)
local name_cfg = tex.get_skin_config("song_box_name") or { x = 47, y = 129, height = 417 }
local name_h = math.min(name.height, name_cfg.height)
name:draw({
x = bx + name_cfg.x - math.floor(name.width / 2),
y = name_cfg.y + by,
y2 = name_h - name.height,
fade = fade
})
draw_folder_crown(box, bx, by)
end
local function draw_open_bg(self, box, fade)
local bx = box:box_x()
local by = box:box_y()
local shadow_fade = math.min(fade, box.open_fade.attribute)
draw_shadows(bx, by, shadow_fade, 1)
local tex_index = box:texture_index()
local use_shader = box:has_recolor()
local open_anim = box.open_anim.attribute
if open_anim >= (100.0 * tex.get_screen_scale()) then
if use_shader then box:begin_recolor() end
tex.draw_texture(T("box", "folder_top_edge"), { frame = tex_index, mirror = "horizontal", y = by - open_anim, fade = fade })
tex.draw_texture(T("box", "folder_top"), { frame = tex_index, y = by - open_anim, fade = fade })
local top_cfg = tex.get_skin_config("song_folder_top") or { x = 268 }
tex.draw_texture(T("box", "folder_top_edge"), { frame = tex_index, x = top_cfg.x, y = by - open_anim, fade = fade })
if use_shader then box:end_recolor() end
end
if use_shader then box:begin_recolor() end
tex.draw_texture(T("box", "folder_texture_left"), { frame = tex_index, x = bx - open_anim, y = by, fade = fade })
tex.draw_texture(T("box", "folder_texture"), { frame = tex_index, x = bx - open_anim, y = by, x2 = (open_anim * 2.0) + base.song_box_bg_width(), fade = fade })
tex.draw_texture(T("box", "folder_texture_right"), { frame = tex_index, x = bx + open_anim, y = by, fade = fade })
if use_shader then box:end_recolor() end
end
local function draw_open_fg(self, box, fade)
local texts = folder_texts(self, box)
local open_anim = box.open_anim.attribute
if open_anim >= (100.0 * tex.get_screen_scale()) then
local hori_cfg = tex.get_skin_config("song_hori_name") or { x = 642, y = 204, width = 300 }
local dest_width = math.min(hori_cfg.width, texts.hori_name.width)
texts.hori_name:draw({
x = hori_cfg.x - dest_width / 2.0,
y = hori_cfg.y - open_anim,
x2 = dest_width - texts.hori_name.width,
fade = fade
})
end
if box:texture_index() == base.TI_DEFAULT then
tex.draw_texture(T("box", "genre_overlay_large"), { fade = fade })
end
if box:genre_index() == base.GI_DIFFICULTY then
tex.draw_texture(T("box", "diff_overlay_large"), { fade = fade })
end
if box:genre_index() ~= base.GI_DIFFICULTY then
tex.draw_texture(T("yellow_box", "song_count_back"), { fade = math.min(fade, 0.5) })
local count_cfg = tex.get_skin_config("song_tja_count") or { x = 560, y = 130, width = 184 }
local dest_width = math.min(count_cfg.width, texts.tja_count_text.width)
texts.tja_count_text:draw({
x = count_cfg.x - dest_width / 2.0,
y = count_cfg.y,
x2 = dest_width - texts.tja_count_text.width,
fade = fade
})
end
if box:has_box_texture() then
local box_cfg = tex.get_skin_config("box_texture") or { x = 48, y = 330 }
local max_size = tex.get_skin_config("box_folder_max_size") or { width = 344, height = 424 }
box:draw_box_texture({
cx = box:box_x() + box_cfg.x,
cy = box_cfg.y + box:box_y(),
max_w = max_size.width,
max_h = max_size.height,
fade = fade
})
elseif box:texture_index() ~= base.TI_DEFAULT then
tex.draw_texture(T("box", "folder_graphic"), { frame = box:genre_index(), fade = fade })
local exp_cfg = tex.get_skin_config("box_folder_explanation") or { x = 776, y = 131, width = 43, height = 73 }
local exp_box = tex.get_skin_config("box_folder_explanation_box") or { x = 650, y = 125, width = 160, height = 400 }
local lang = tex.language()
local is_cjk = text.language_is_cjk(lang)
if is_cjk then
for i = 1, 3 do
local t = texts.explanation[i]
if t then
local idx = i - 1
local center_x = exp_cfg.x - exp_cfg.width * idx
t:draw({ x = center_x - t.width / 2.0, y = exp_cfg.y + exp_cfg.height * idx, fade = fade })
end
end
else
local total_h = 0.0
for i = 1, 3 do
if texts.explanation[i] then total_h = total_h + texts.explanation[i].height end
end
local scale = total_h > exp_box.height and (exp_box.height / total_h) or 1.0
local y = exp_box.y
for i = 1, 3 do
local t = texts.explanation[i]
if t then
local dest_h = t.height * scale
t:draw({
x = exp_box.x + (exp_box.width - t.width) / 2.0,
y = y,
y2 = dest_h - t.height,
fade = fade
})
y = y + dest_h
end
end
end
end
end
function SongSelect:draw_folder_open(box)
if box.entered then
draw_open_bg(self, box, 0.0)
draw_open_fg(self, box, box:enter_fade().attribute)
else
draw_open_bg(self, box, math.min(1.0, box.fade.attribute))
draw_open_fg(self, box, math.min(box.open_fade.attribute, box.fade.attribute))
end
end