Files
MusicPlayer/popups/sync_popup.gd
T
SansanandClaude Opus 5.5 0fc3be79e1
Build / test (push) Successful in 1m39s
Build / build (push) Successful in 1m14s
Add animations to popups, points, songs, the winner and the tabs (#48)
Popups fade in while growing and close the reverse way, with the backdrop
fading along. The winner gets confetti, bouncing pictures and a score graph
that draws itself from the left. Filtered statistics cards and search rows
fade in, the tabs slide in from their side, and every button bounces on hover
and click (ButtonBounce, now added to all buttons).

In the player list the score pops on every point, the first point flashes the
row, and a player on match point glows with a "Match point!" tag. A song
starting fades in the title, flashes its row (kept across list rebuilds) and
pops the play button.

Common/anim.gd holds the helpers. A saved "Reduce animations" switch in
Settings turns all movement off; the tests use it. The flash colours are
theme colours (Palette first_point, match_point, flash) in every theme.

Closes #48

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 21:49:01 +02:00

225 lines
8.2 KiB
GDScript

class_name SyncPopup
extends BasePopup
## Asks the server to sync its soundtracks with the music folder, shows the progress,
## and then lists what was added, changed or removed.
## Emitted when the server has started syncing.
signal sync_started
## Emitted when the sync is done and its result is shown.
signal sync_finished
var changes: bool = false
var has_synced: bool = false
@onready var sync_panel: Panel = %SyncPanel
@onready var sync_title_label: Label = %SyncTitleLabel
@onready var no_changes_label: Label = %NoChangesLabel
@onready var progress_hbox: HBoxContainer = %ProgressHBoxContainer
@onready var sync_hbox: HBoxContainer = %SyncHBoxContainer
@onready var sync_progress_bar: ProgressBar = %SyncProgressBar
@onready var soundtracks_added_vbox: VBoxContainer = %SoundtracksAddedVBox
@onready var soundtracks_added_separator: VSeparator = %SoundtracksAddedSeparator
@onready var soundtracks_readded_vbox: VBoxContainer = %SoundtracksReAddedVBox
@onready var soundtracks_readded_separator: VSeparator = %SoundtracksReAddedSeparator
@onready var soundtracks_changed_title_vbox: VBoxContainer = %SoundtracksChangedTitleVBox
@onready var soundtracks_changed_title_separator: VSeparator = %SoundtracksChangedTitleSeparator
@onready var soundtracks_changed_content_vbox: VBoxContainer = %SoundtracksChangedContentVBox
@onready var soundtracks_changed_content_separator: VSeparator = %SoundtracksChangedContentSeparator
@onready var soundtracks_removed_vbox: VBoxContainer = %SoundtracksRemovedVBox
@onready var soundtracks_removed_separator: VSeparator = %SoundtracksRemovedSeparator
@onready var caught_errors_vbox: VBoxContainer = %CaughtErrorsVBox
@onready var sync_time: Label = %SyncTime
@onready var sync_timer: Timer = %SyncTimer
@onready var restart_sync_button: Button = %RestartSyncButton
func _ready() -> void:
sync_timer.timeout.connect(_on_sync_timeout)
restart_sync_button.pressed.connect(start_sync)
restart_sync_button.visible = false
## Lists [param data] under [param header_text] in [param vbox] and returns true, or hides
## the column and its separator when there is nothing to list.
func show_section_if_has_data(data: Variant, vbox: VBoxContainer, separator: VSeparator,
header_text: String) -> bool:
if data != null && data.size() > 0:
vbox.visible = true
make_list(data, vbox, header_text)
return true
else:
vbox.visible = false
separator.visible = false
return false
func start_sync() -> void:
GameLog.debug("start_sync")
open()
if has_synced:
# Already synced, just show the results
show_sync_result()
return
var sync_request_sent: Callable = func sync_request_sent() -> void:
sync_timer.start(0.75)
Settings.currently_syncing = true
sync_started.emit()
show_sync_progress()
if !Settings.currently_syncing:
MusicServerClient.start_sync(sync_request_sent)
func _on_sync_timeout() -> void:
GameLog.debug("_on_sync_timeout!")
var get_progress: Callable = func get_progress(data_received: Dictionary) -> void:
GameLog.debug("get_progress")
GameLog.debug("data_received: ", data_received)
if data_received != null:
if data_received.has("progress"):
sync_progress_bar.value = int(data_received.progress)
sync_time.text = data_received.time_spent if data_received.has("time_spent") else ""
else:
on_sync_finished(data_received)
MusicServerClient.fetch_sync_progress(get_progress)
func on_sync_finished(data_received: Dictionary) -> void:
has_synced = true
reset_sync_result()
if show_section_if_has_data(data_received.soundtracks_added, soundtracks_added_vbox,
soundtracks_added_separator, "Added"):
changes = true
if show_section_if_has_data(data_received.soundtracks_re_added, soundtracks_readded_vbox,
soundtracks_readded_separator, "Re-added"):
changes = true
var changed_titles: Variant = data_received.soundtracks_changed_title
if changed_titles != null && changed_titles.size() > 0:
changes = true
Settings.delete_children(soundtracks_changed_title_vbox)
make_header_label(soundtracks_changed_title_vbox, "Changed Title")
var row: int = 0
for old_soundtrack_title: String in changed_titles:
var new_soundtrack_title: String = changed_titles[old_soundtrack_title]
soundtracks_changed_title_vbox.add_child(make_soundtrack_row(
old_soundtrack_title + "\nchanged title to\n" + new_soundtrack_title, row))
row += 1
soundtracks_changed_title_vbox.visible = true
if show_section_if_has_data(data_received.soundtracks_changed_content,
soundtracks_changed_content_vbox, soundtracks_changed_content_separator,
"Changed Content"):
changes = true
if show_section_if_has_data(data_received.soundtracks_removed, soundtracks_removed_vbox,
soundtracks_removed_separator, "Removed"):
changes = true
# The server's name for the errors it caught
if data_received.catched_errors != null && data_received.catched_errors.size() > 0:
caught_errors_vbox.visible = true
make_list(data_received.catched_errors, caught_errors_vbox, "Errors")
# Separators go only between visible columns, not after the last one
var visible_sections: Array = []
if soundtracks_added_vbox.visible:
visible_sections.append("added")
if soundtracks_readded_vbox.visible:
visible_sections.append("readded")
if soundtracks_changed_title_vbox.visible:
visible_sections.append("changed_title")
if soundtracks_changed_content_vbox.visible:
visible_sections.append("changed_content")
if soundtracks_removed_vbox.visible:
visible_sections.append("removed")
# Show section separators only if there's a visible section after them
for i: int in range(visible_sections.size()):
var section: String = visible_sections[i]
var separator_map: Dictionary = {
"added": soundtracks_added_separator,
"readded": soundtracks_readded_separator,
"changed_title": soundtracks_changed_title_separator,
"changed_content": soundtracks_changed_content_separator,
"removed": soundtracks_removed_separator
}
# Show this section's separator only if there's a next section
if i < visible_sections.size() - 1:
separator_map[section].visible = true
else:
separator_map[section].visible = false
if !changes:
no_changes_label.visible = true
sync_time.text = "Total time: " + data_received.total_time \
if data_received.has("total_time") else ""
Settings.currently_syncing = false
sync_timer.stop()
GameLog.debug("sync finished")
show_sync_result()
restart_sync_button.visible = true
sync_finished.emit()
func list_visible(vbox: VBoxContainer, separator: VSeparator) -> void:
vbox.visible = true
separator.visible = true
func make_header_label(vbox: VBoxContainer, text: String) -> void:
var label: Label = Label.new()
label.theme_type_variation = &"HighlightLarge"
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.text = text
vbox.add_child(label)
# Items alternate between white and orange text as well as background shade
func make_soundtrack_row(text: String, row: int) -> ListRow:
var label_style: StringName = &"HighlightMedium" if row % 2 != 0 else &"TextMedium"
return ListRow.create(text, row, label_style, HORIZONTAL_ALIGNMENT_CENTER)
func reset_sync_result() -> void:
sync_progress_bar.value = 0
sync_time.text = ""
changes = false
no_changes_label.visible = false
soundtracks_added_vbox.visible = false
soundtracks_added_separator.visible = false
soundtracks_readded_vbox.visible = false
soundtracks_readded_separator.visible = false
soundtracks_changed_title_vbox.visible = false
soundtracks_changed_title_separator.visible = false
soundtracks_changed_content_vbox.visible = false
soundtracks_changed_content_separator.visible = false
soundtracks_removed_vbox.visible = false
soundtracks_removed_separator.visible = false
caught_errors_vbox.visible = false
func make_list(list: Array, vbox: VBoxContainer, text: String) -> void:
Settings.delete_children(vbox)
make_header_label(vbox, text)
for row: int in list.size():
vbox.add_child(make_soundtrack_row(list[row], row))
func show_sync_result() -> void:
sync_title_label.text = "Sync Result"
progress_hbox.visible = false
sync_hbox.visible = true
restart_sync_button.visible = true
func show_sync_progress() -> void:
sync_title_label.text = "Sync in Progress"
progress_hbox.visible = true
no_changes_label.visible = false
sync_hbox.visible = false
caught_errors_vbox.visible = false
restart_sync_button.visible = false