Show soundtrack covers and edit alternative names (#49)
Covers: statistics cards show the soundtrack's cover on the left, and the
statistics popup shows it large. Until it arrives, and without a cover, the
soundtrack's initials stand in. Covers load only for cards on or near the
screen, three at a time, and are cached in covers/ next to the settings
(CoverCache), downloaded again only when cover_modified changes. The index and
file handling are shared with CharacterCache in a new FileCache.
Alternative names: the popup shows them and has an editor (add, remove, save)
that sends PUT /soundtracks/{uuid}/alternative-names. The statistics search and
the Search popup find soundtracks by them; Search shows the matching name after
the real one. Search now loads /soundtracks, falling back to the plain list.
The API types are regenerated from the server's spec, with a SoundtrackCover
theme style in every theme.
Closes #49
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a735c515bf
commit
ef6ac8f1fd
37 files changed
+8029
-1763
No files matched your search
@@ -5,6 +5,9 @@ extends Panel
|
||||
## cards filtered on whether the soundtrack has been played.
|
||||
##
|
||||
## Shown instead of the quiz when the Statistics tab is chosen.
|
||||
##
|
||||
## Covers are loaded for the cards on screen as they scroll into view, a few at a time,
|
||||
## not for every soundtrack when the tab opens. See [CoverCache].
|
||||
|
||||
## Emitted when a card is clicked, to show all songs of [param soundtrack].
|
||||
signal soundtrack_selected(soundtrack: SoundtrackWithSongs)
|
||||
@@ -19,12 +22,18 @@ enum ShowFilter { ALL, PLAYED, NEVER_PLAYED }
|
||||
enum Mode { SOUNDTRACKS, SONGS }
|
||||
|
||||
const CARD_SCENE: PackedScene = preload("res://statistics/SoundtrackCard.tscn")
|
||||
## How far outside the visible part cards still get their covers, so they are ready when
|
||||
## scrolled to.
|
||||
const COVER_MARGIN: float = 400.0
|
||||
|
||||
## The cards in the order they are shown, hidden ones included.
|
||||
var cards: Array[SoundtrackCard] = []
|
||||
## Whether soundtracks or songs are shown. Change it with [method set_mode].
|
||||
var mode: Mode = Mode.SOUNDTRACKS
|
||||
|
||||
## The covers on disk, shared by the cards.
|
||||
var covers: CoverCache = CoverCache.new()
|
||||
|
||||
var _search: SoundtrackSearch = SoundtrackSearch.new()
|
||||
var _loading: bool = false
|
||||
# The soundtracks last loaded, by name
|
||||
@@ -61,6 +70,11 @@ func _ready() -> void:
|
||||
sort_option.item_selected.connect(func(_index: int) -> void: apply_sort())
|
||||
retry_button.pressed.connect(refresh)
|
||||
retry_button.visible = false
|
||||
# The cards have moved by the end of the frame
|
||||
card_scroll.get_v_scroll_bar().value_changed.connect(
|
||||
func(_value: float) -> void: load_covers_on_screen.call_deferred())
|
||||
card_scroll.resized.connect(load_covers_on_screen)
|
||||
visibility_changed.connect(load_covers_on_screen)
|
||||
|
||||
|
||||
## Loads the statistics from the server. The cards already shown stay until the new
|
||||
@@ -108,6 +122,7 @@ func apply_sort() -> void:
|
||||
return _comes_before(a.soundtrack, b.soundtrack, order))
|
||||
for i: int in cards.size():
|
||||
card_grid.move_child(cards[i], i)
|
||||
load_covers_on_screen.call_deferred()
|
||||
|
||||
|
||||
## Shows only the cards or songs that match the search text and the show menu.
|
||||
@@ -136,6 +151,8 @@ func apply_filter() -> void:
|
||||
count_label.text = "%d soundtracks" % cards.size()
|
||||
else:
|
||||
count_label.text = "%d of %d soundtracks" % [shown, cards.size()]
|
||||
# The grid has moved the cards by the end of the frame
|
||||
load_covers_on_screen.call_deferred()
|
||||
|
||||
|
||||
func _on_loaded(soundtracks: Array[SoundtrackWithSongs]) -> void:
|
||||
@@ -149,19 +166,49 @@ func _on_loaded(soundtracks: Array[SoundtrackWithSongs]) -> void:
|
||||
cards.append(card)
|
||||
while cards.size() > soundtracks.size():
|
||||
cards.pop_back().free()
|
||||
var names: Array[String] = []
|
||||
_soundtracks.clear()
|
||||
var cover_times: Dictionary = {}
|
||||
for i: int in soundtracks.size():
|
||||
cards[i].show_soundtrack(soundtracks[i])
|
||||
names.append(soundtracks[i].soundtrack_name)
|
||||
_soundtracks[soundtracks[i].soundtrack_name] = soundtracks[i]
|
||||
_search.set_names(names)
|
||||
cover_times[soundtracks[i].soundtrack_uuid] = soundtracks[i].cover_modified
|
||||
covers.keep_only(cover_times)
|
||||
_update_search_names()
|
||||
_songs_ready = false
|
||||
apply_sort()
|
||||
if mode == Mode.SOUNDTRACKS:
|
||||
apply_filter()
|
||||
|
||||
|
||||
## Asks for the covers of the cards that are on screen, or nearly.
|
||||
func load_covers_on_screen() -> void:
|
||||
if !is_visible_in_tree() || mode != Mode.SOUNDTRACKS:
|
||||
return
|
||||
var view: Rect2 = card_scroll.get_global_rect().grow(COVER_MARGIN)
|
||||
for card: SoundtrackCard in cards:
|
||||
if card.visible && view.intersects(card.get_global_rect()):
|
||||
card.load_cover(covers)
|
||||
|
||||
|
||||
## Shows the new alternative names of the soundtrack [param updated] and finds it by
|
||||
## them, without loading the statistics again.
|
||||
func update_alternative_names(updated: Soundtrack) -> void:
|
||||
for soundtrack: SoundtrackWithSongs in _soundtracks.values():
|
||||
if soundtrack.soundtrack_uuid == updated.uuid:
|
||||
soundtrack.alternative_names = updated.alternative_names
|
||||
_update_search_names()
|
||||
apply_filter()
|
||||
|
||||
|
||||
func _update_search_names() -> void:
|
||||
var names: Array[String] = []
|
||||
var alternatives: Dictionary = {}
|
||||
for soundtrack: SoundtrackWithSongs in _soundtracks.values():
|
||||
names.append(soundtrack.soundtrack_name)
|
||||
alternatives[soundtrack.soundtrack_name] = soundtrack.alternative_names
|
||||
_search.set_names(names, alternatives)
|
||||
|
||||
|
||||
# Typing in the song list waits for a pause, since searching every song takes a moment
|
||||
func _on_search_text_changed(_text: String) -> void:
|
||||
if mode == Mode.SONGS:
|
||||
|
||||
Reference in new issue
Block a user