diff --git a/common/cover_cache.gd b/common/cover_cache.gd index dd83637..5d2f3bc 100644 --- a/common/cover_cache.gd +++ b/common/cover_cache.gd @@ -6,6 +6,13 @@ extends RefCounted ## ## Covers are downloaded a few at a time, in the order they are asked for, so opening the ## statistics doesn't send a request for every soundtrack at once. +## +## It also remembers each soundtrack's cover time from the soundtrack lists it is given, +## so [method fetch_cover_of] finds a cover by the soundtrack alone, e.g. in the quiz. + +## Emitted when the cover time of a soundtrack is new or changed, e.g. once the soundtrack +## list has loaded or a cover was changed. +signal cover_times_changed const FOLDER_NAME: String = "covers" ## How many covers are downloaded at the same time. @@ -18,6 +25,8 @@ var _waiting: Array[Array] = [] # Who is waiting for each cover that is asked for or downloading: uuid -> Array[Callable] var _callbacks: Dictionary = {} var _downloading: int = 0 +# The server's cover time of each soundtrack seen: uuid -> int, 0 for none +var _times: Dictionary = {} func _init() -> void: @@ -32,6 +41,7 @@ static func folder() -> String: ## Deletes the covers of soundtracks that are not in [param covers] (UUID -> cover ## time), and those whose soundtrack no longer has a cover. func keep_only(covers: Dictionary) -> void: + set_cover_times(covers) var kept: Dictionary = {} for uuid: String in covers: if int(covers[uuid]) > 0: @@ -39,6 +49,45 @@ func keep_only(covers: Dictionary) -> void: _files.keep_only(kept) +## Remembers the cover times in [param covers] (UUID -> cover time, 0 for no cover) and +## emits [signal cover_times_changed] when any is new or changed. +func set_cover_times(covers: Dictionary) -> void: + var changed: bool = false + for uuid: String in covers: + var modified: int = int(covers[uuid]) + if _times.get(uuid, -1) != modified: + _times[uuid] = modified + changed = true + if changed: + cover_times_changed.emit() + + +## The cover time of the soundtrack [param uuid], 0 when it has no cover or none is known. +func cover_time(uuid: String) -> int: + return _times.get(uuid, 0) + + +## Like [method fetch_cover], with the cover time remembered for the soundtrack. +func fetch_cover_of(uuid: String, on_done: Callable) -> void: + fetch_cover(uuid, cover_time(uuid), on_done) + + +## Asks the server for the cover time of the soundtrack [param uuid] again, e.g. after +## its cover was changed, then calls [param on_done] with the new time, 0 for no cover. +## A removed cover is deleted from disk too. When the server doesn't answer, it is called +## with the time it had. +func refresh(uuid: String, on_done: Callable) -> void: + var with_soundtrack: Callable = func with_soundtrack( + soundtrack: SoundtrackWithSongList) -> void: + if soundtrack.cover_modified <= 0: + _files.remove(uuid) + set_cover_times({uuid: soundtrack.cover_modified}) + on_done.call(soundtrack.cover_modified) + # Without an answer the cover is left as it was + MusicServerClient.fetch_soundtrack(uuid, with_soundtrack, + func() -> void: on_done.call(cover_time(uuid))) + + ## Calls [param on_done] with the cover of the soundtrack [param uuid] as an [Image]: ## from disk when the cached one has the server's time [param modified], otherwise once ## it has been downloaded. Nothing is called when the soundtrack has no cover diff --git a/data/playlist.gd b/data/playlist.gd index b419280..1f05775 100644 --- a/data/playlist.gd +++ b/data/playlist.gd @@ -108,6 +108,10 @@ func get_current_soundtrack_title() -> String: return song_object_array[currently_playing_song].get_soundtrack_title() +func get_current_soundtrack_uuid() -> String: + return song_object_array[currently_playing_song].get_soundtrack_uuid() + + func get_current_song_title() -> String: return song_object_array[currently_playing_song].get_song_title() diff --git a/data/song_object.gd b/data/song_object.gd index 7f9324d..df0c2db 100644 --- a/data/song_object.gd +++ b/data/song_object.gd @@ -5,6 +5,8 @@ class_name SongObject var song_number: int var song_title: String var soundtrack_title: String +## The UUID of the song's soundtrack on the server, empty for songs from a music folder. +var soundtrack_uuid: String = "" ## Ids of the players who got a point for this song. var players_given_point: Array[String] = [] @@ -36,6 +38,11 @@ func get_song_title() -> String: return "??????" +## The soundtrack's UUID once the answer is shown, otherwise empty. +func get_soundtrack_uuid() -> String: + return soundtrack_uuid if is_answered else "" + + func get_song_info() -> String: if Settings.is_debug: return "%d. %s | played: %s | answered: %s" % [(song_number + 1), _full_title(), @@ -71,6 +78,7 @@ func to_dict() -> Dictionary: "number": song_number, "soundtrack": soundtrack_title, "song": song_title, "points": players_given_point.duplicate(), "answered": is_answered, "played": has_played, "playing": is_playing, "path": song_path, + "soundtrack_uuid": soundtrack_uuid, } @@ -83,6 +91,7 @@ static func from_dict(data: Variant) -> SongObject: song_object.song_number = int(data.number) song_object.soundtrack_title = str(data.get("soundtrack", "")) song_object.song_title = str(data.get("song", "")) + song_object.soundtrack_uuid = str(data.get("soundtrack_uuid", "")) if data.get("points") is Array: for player_id: Variant in data.points: song_object.add_point(str(player_id)) diff --git a/main/main_window.gd b/main/main_window.gd index 879675d..9591934 100644 --- a/main/main_window.gd +++ b/main/main_window.gd @@ -48,11 +48,14 @@ func _ready() -> void: quiz.event_logged.connect(log_popup.add_log_row) statistics_view.soundtrack_selected.connect(soundtrack_stats_popup.show_soundtrack) soundtrack_stats_popup.covers = statistics_view.covers + quiz.now_playing.covers = statistics_view.covers + search_popup.soundtracks_loaded.connect(_remember_cover_times) # New alternative names are found by the statistics search and the Search popup soundtrack_stats_popup.alternative_names_changed.connect( statistics_view.update_alternative_names) soundtrack_stats_popup.alternative_names_changed.connect( func(_soundtrack: Soundtrack) -> void: search_popup.get_list_of_soundtracks()) + soundtrack_stats_popup.cover_changed.connect(statistics_view.update_cover) # A saved quiz may have been played from a music folder top_bar.update_offline_state() @@ -60,6 +63,14 @@ func _ready() -> void: show_about_if_new_version() +# The quiz finds a soundtrack's cover by the cover times in the soundtrack list +func _remember_cover_times(soundtracks: Array[Soundtrack]) -> void: + var times: Dictionary = {} + for soundtrack: Soundtrack in soundtracks: + times[soundtrack.uuid] = soundtrack.cover_modified + statistics_view.covers.set_cover_times(times) + + # After another server or a music folder was picked. Statistics need the server. func _on_music_source_changed() -> void: top_bar.update_offline_state() diff --git a/popups/SoundtrackStatsPopup.tscn b/popups/SoundtrackStatsPopup.tscn index 88cafed..1e6f247 100644 --- a/popups/SoundtrackStatsPopup.tscn +++ b/popups/SoundtrackStatsPopup.tscn @@ -36,8 +36,8 @@ clip_children = 2 layout_mode = 0 offset_left = 20.0 offset_top = 64.0 -offset_right = 164.0 -offset_bottom = 264.0 +offset_right = 160.0 +offset_bottom = 274.0 mouse_filter = 2 theme_type_variation = &"SoundtrackCover" @@ -90,13 +90,22 @@ autowrap_mode = 3 text_overrun_behavior = 3 max_lines_visible = 3 -[node name="EditNamesButton" type="Button" parent="StatsPanel/Info"] +[node name="InfoButtons" type="HBoxContainer" parent="StatsPanel/Info"] +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="EditNamesButton" type="Button" parent="StatsPanel/Info/InfoButtons"] unique_name_in_owner = true layout_mode = 2 -size_flags_horizontal = 0 focus_mode = 0 text = "Edit names" +[node name="ChangeCoverButton" type="Button" parent="StatsPanel/Info/InfoButtons"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +text = "Change cover" + [node name="ScrollContainer" type="ScrollContainer" parent="StatsPanel" unique_id=1213000005] unique_name_in_owner = true layout_mode = 0 @@ -180,3 +189,111 @@ unique_name_in_owner = true layout_mode = 2 focus_mode = 0 text = "Save" + +[node name="CoverEditor" type="VBoxContainer" parent="StatsPanel"] +unique_name_in_owner = true +visible = false +layout_mode = 0 +offset_left = 20.0 +offset_top = 280.0 +offset_right = 680.0 +offset_bottom = 784.0 +theme_override_constants/separation = 10 + +[node name="CoverEditorTitle" type="Label" parent="StatsPanel/CoverEditor"] +layout_mode = 2 +theme_type_variation = &"Title" +text = "Cover" + +[node name="CoverEditorHelp" type="Label" parent="StatsPanel/CoverEditor"] +layout_mode = 2 +autowrap_mode = 3 +text = "The server finds covers by itself. When it found the wrong one, or none, choose a picture of your own or the cover of a game on SteamGridDB or an anime on AniList. The server keeps a cover set here until it is removed." + +[node name="PictureRow" type="HBoxContainer" parent="StatsPanel/CoverEditor"] +layout_mode = 2 +theme_override_constants/separation = 12 + +[node name="ChoosePictureButton" type="Button" parent="StatsPanel/CoverEditor/PictureRow"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +text = "Choose picture…" + +[node name="RemoveCoverButton" type="Button" parent="StatsPanel/CoverEditor/PictureRow"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +text = "Remove cover" + +[node name="SourceTitle" type="Label" parent="StatsPanel/CoverEditor"] +layout_mode = 2 +theme_type_variation = &"HighlightMedium" +text = "Pick by ID" + +[node name="SourceHelp" type="Label" parent="StatsPanel/CoverEditor"] +layout_mode = 2 +theme_type_variation = &"TextMedium" +autowrap_mode = 3 +text = "The ID is the number in the game's or anime's web address." + +[node name="SourceRow" type="HBoxContainer" parent="StatsPanel/CoverEditor"] +layout_mode = 2 +theme_override_constants/separation = 8 + +[node name="CoverSourceButton" type="OptionButton" parent="StatsPanel/CoverEditor/SourceRow"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +selected = 0 +item_count = 2 +popup/item_0/text = "SteamGridDB" +popup/item_0/id = 0 +popup/item_1/text = "AniList" +popup/item_1/id = 1 + +[node name="CoverIdField" type="LineEdit" parent="StatsPanel/CoverEditor/SourceRow"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "ID" +max_length = 50 + +[node name="UseSourceButton" type="Button" parent="StatsPanel/CoverEditor/SourceRow"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +text = "Use" + +[node name="CoverEditorSpace" type="Control" parent="StatsPanel/CoverEditor"] +layout_mode = 2 +size_flags_vertical = 3 +mouse_filter = 2 + +[node name="CoverStatus" type="Label" parent="StatsPanel/CoverEditor"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +theme_type_variation = &"HighlightMedium" +autowrap_mode = 3 + +[node name="CoverEditorButtons" type="HBoxContainer" parent="StatsPanel/CoverEditor"] +layout_mode = 2 +alignment = 2 + +[node name="CloseCoverButton" type="Button" parent="StatsPanel/CoverEditor/CoverEditorButtons"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +text = "Done" + +[node name="PictureDialog" type="FileDialog" parent="."] +unique_name_in_owner = true +title = "Choose a cover picture" +initial_position = 2 +size = Vector2i(800, 500) +ok_button_text = "Open" +file_mode = 0 +access = 2 +filters = PackedStringArray("*.jpg, *.jpeg, *.png, *.webp ; Pictures") +use_native_dialog = true diff --git a/popups/search_popup.gd b/popups/search_popup.gd index 9417ee4..1842090 100644 --- a/popups/search_popup.gd +++ b/popups/search_popup.gd @@ -4,6 +4,9 @@ extends BasePopup ## highlights the letters that matched. Soundtracks are also found by their alternative ## names; the alternative name that matched is shown after the real name. +## Emitted with every soundtrack on the server when the list has loaded. +signal soundtracks_loaded(list: Array[Soundtrack]) + ## Every soundtrack on the server, sorted by name. var soundtracks: Array = [] @@ -98,6 +101,7 @@ func get_list_of_soundtracks() -> void: names.sort_custom(func(a: String, b: String) -> bool: return a.naturalnocasecmp_to(b) < 0) set_soundtracks(names, alternatives) + soundtracks_loaded.emit(list) MusicServerClient.fetch_soundtracks(with_names, _load_names_only) diff --git a/popups/soundtrack_stats_popup.gd b/popups/soundtrack_stats_popup.gd index 41fc257..2fe124f 100644 --- a/popups/soundtrack_stats_popup.gd +++ b/popups/soundtrack_stats_popup.gd @@ -5,9 +5,19 @@ extends BasePopup ## ## Edit names opens an editor for the alternative names in place of the song list. Saving ## sends them to the server, which search then finds the soundtrack by. +## +## Change cover opens the cover menu there instead: upload a picture, pick a game's or +## anime's cover by its ID on SteamGridDB or AniList, or remove the cover so the server +## looks for one again. ## Emitted when the alternative names of [param soundtrack] have been saved. signal alternative_names_changed(soundtrack: Soundtrack) +## Emitted when the cover of the soundtrack [param uuid] changed, with its new cover time +## [param modified], 0 when it was removed. +signal cover_changed(uuid: String, modified: int) + +## The largest picture the server takes as a cover. +const MAX_COVER_BYTES: int = 10 << 20 ## The covers on disk, shared with the statistics view. No cover is shown without it. var covers: CoverCache @@ -32,6 +42,16 @@ var _edited_names: Array[String] = [] @onready var editor_status: Label = %EditorStatus @onready var save_names_button: Button = %SaveNamesButton @onready var cancel_names_button: Button = %CancelNamesButton +@onready var change_cover_button: Button = %ChangeCoverButton +@onready var cover_editor: VBoxContainer = %CoverEditor +@onready var choose_picture_button: Button = %ChoosePictureButton +@onready var remove_cover_button: Button = %RemoveCoverButton +@onready var cover_source_button: OptionButton = %CoverSourceButton +@onready var cover_id_field: LineEdit = %CoverIdField +@onready var use_source_button: Button = %UseSourceButton +@onready var cover_status: Label = %CoverStatus +@onready var close_cover_button: Button = %CloseCoverButton +@onready var picture_dialog: FileDialog = %PictureDialog func _ready() -> void: @@ -40,6 +60,13 @@ func _ready() -> void: new_name_field.text_submitted.connect(func(_text: String) -> void: add_name()) save_names_button.pressed.connect(save_names) cancel_names_button.pressed.connect(stop_editing) + change_cover_button.pressed.connect(change_cover) + choose_picture_button.pressed.connect(picture_dialog.popup_centered) + picture_dialog.file_selected.connect(upload_picture) + remove_cover_button.pressed.connect(remove_cover) + use_source_button.pressed.connect(use_cover_source) + cover_id_field.text_submitted.connect(func(_text: String) -> void: use_cover_source()) + close_cover_button.pressed.connect(stop_editing) ## Shows [param shown] and opens the popup. @@ -57,10 +84,7 @@ func show_soundtrack(shown: SoundtrackWithSongs) -> void: _show_names() cover_placeholder.text = SoundtrackCard.initials(shown.soundtrack_name) - _show_cover(null) - if covers != null: - covers.fetch_cover(shown.soundtrack_uuid, shown.cover_modified, - _show_cover_of.bind(shown.soundtrack_uuid)) + _load_cover() Settings.delete_children(song_list) var songs: Array[SongInfoForStats] = SoundtrackCard.songs_by_plays(shown.songs) @@ -76,18 +100,71 @@ func edit_names() -> void: new_name_field.text = "" editor_status.visible = false _show_edited_names() - names_editor.visible = true - song_scroll.visible = false - edit_names_button.disabled = true + _show_editor(names_editor) new_name_field.grab_focus() -## Closes the editor without saving. +## Opens the cover menu, in place of the song list. +func change_cover() -> void: + cover_id_field.text = "" + cover_status.visible = false + _set_cover_busy(false) + _show_editor(cover_editor) + + +## Closes the editors, without saving the names. func stop_editing() -> void: - names_editor.visible = false - song_scroll.visible = true - edit_names_button.disabled = false + _show_editor(null) _set_saving(false) + _set_cover_busy(false) + + +## Uploads the picture at [param path] as the soundtrack's cover. +func upload_picture(path: String) -> void: + var picture: PackedByteArray = FileAccess.get_file_as_bytes(path) + var content_type: String = picture_type(picture) + if picture.size() > MAX_COVER_BYTES: + _show_cover_status("The picture is larger than %d MB." % (MAX_COVER_BYTES >> 20)) + return + if content_type.is_empty() || CharacterCard.image_from_bytes(picture) == null: + _show_cover_status("Only JPEG, PNG and WebP pictures can be used.") + return + _set_cover_busy(true, "Uploading the picture…") + MusicServerClient.upload_soundtrack_cover(soundtrack.soundtrack_uuid, picture, + content_type, _on_cover_saved.bind("The new cover is saved."), _on_cover_not_saved) + + +## Makes the cover of the game or anime with the ID in the field, on the site picked, +## the soundtrack's cover. +func use_cover_source() -> void: + var id: String = cover_id_field.text.strip_edges() + if id.is_empty() || use_source_button.disabled: + return + var source: String = MusicServerClient.COVER_SOURCES[cover_source_button.selected] + _set_cover_busy(true, "Getting the cover from %s…" + % cover_source_button.get_item_text(cover_source_button.selected)) + MusicServerClient.set_soundtrack_cover_source(soundtrack.soundtrack_uuid, source, id, + _on_cover_saved.bind("The new cover is saved."), _on_cover_not_saved) + + +## Removes the soundtrack's cover, so the server looks for one again. +func remove_cover() -> void: + _set_cover_busy(true, "Removing the cover…") + MusicServerClient.delete_soundtrack_cover(soundtrack.soundtrack_uuid, + _on_cover_saved.bind("The cover is removed. The server will look for a new one."), + _on_cover_not_saved) + + +## The type of [param picture] to upload it as, e.g. [code]"image/png"[/code], or empty +## when it is not a JPEG, PNG or WebP picture. +static func picture_type(picture: PackedByteArray) -> String: + if picture.size() >= 4 && picture.slice(0, 4) == PackedByteArray([0x89, 0x50, 0x4E, 0x47]): + return "image/png" + if picture.size() >= 3 && picture.slice(0, 3) == PackedByteArray([0xFF, 0xD8, 0xFF]): + return "image/jpeg" + if picture.size() >= 12 && picture.slice(8, 12).get_string_from_ascii() == "WEBP": + return "image/webp" + return "" ## Adds the name in the field to the edited names, unless it is empty, the soundtrack's @@ -162,6 +239,61 @@ func _show_edited_names() -> void: editor_names.add_child(list_row) +# Shows [param editor] in place of the song list, or the song list when it is null +func _show_editor(editor: Control) -> void: + names_editor.visible = editor == names_editor + cover_editor.visible = editor == cover_editor + song_scroll.visible = editor == null + edit_names_button.disabled = editor != null + change_cover_button.disabled = editor != null + + +# The cover changed on the server; its new time tells whether it has one and fetches it +func _on_cover_saved(message: String) -> void: + var uuid: String = soundtrack.soundtrack_uuid + var with_time: Callable = func with_time(modified: int) -> void: + if soundtrack == null || soundtrack.soundtrack_uuid != uuid: + return + soundtrack.cover_modified = modified + _load_cover() + _set_cover_busy(false) + _show_cover_status(message) + cover_changed.emit(uuid, modified) + if covers != null: + covers.refresh(uuid, with_time) + else: + with_time.call(soundtrack.cover_modified) + + +func _on_cover_not_saved() -> void: + _set_cover_busy(false) + var reason: String = MusicServerClient.last_error + _show_cover_status("Could not change the cover: %s." + % (reason if !reason.is_empty() else "the server could not be reached")) + + +func _set_cover_busy(busy: bool, message: String = "") -> void: + for button: Button in [choose_picture_button, use_source_button, close_cover_button]: + button.disabled = busy + remove_cover_button.disabled = busy || soundtrack == null \ + || soundtrack.cover_modified <= 0 + cover_source_button.disabled = busy + if busy: + _show_cover_status(message) + + +func _show_cover_status(message: String) -> void: + cover_status.text = message + cover_status.visible = true + + +func _load_cover() -> void: + _show_cover(null) + if covers != null: + covers.fetch_cover(soundtrack.soundtrack_uuid, soundtrack.cover_modified, + _show_cover_of.bind(soundtrack.soundtrack_uuid)) + + func _show_cover(cover: Image) -> void: cover_picture.texture = ImageTexture.create_from_image(cover) if cover != null else null cover_placeholder.visible = cover == null diff --git a/quiz/NowPlaying.tscn b/quiz/NowPlaying.tscn index 38d1c30..cb7a973 100644 --- a/quiz/NowPlaying.tscn +++ b/quiz/NowPlaying.tscn @@ -2,22 +2,57 @@ [ext_resource type="Script" path="res://quiz/now_playing.gd" id="part_script"] -[node name="NowPlaying" type="VBoxContainer" unique_id=42468498] +[node name="NowPlaying" type="HBoxContainer" unique_id=42468498] layout_mode = 3 offset_right = 1088.0 offset_bottom = 320.0 +theme_override_constants/separation = 32 +alignment = 1 script = ExtResource("part_script") -[node name="SoundtrackLabel" type="Label" parent="." unique_id=173147793] +[node name="CoverFrame" type="Panel" parent="."] +unique_name_in_owner = true +visible = false +clip_children = 2 +custom_minimum_size = Vector2(180, 270) +layout_mode = 2 +size_flags_vertical = 4 +mouse_filter = 2 +theme_type_variation = &"SoundtrackCover" + +[node name="CoverPicture" type="TextureRect" parent="CoverFrame"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +expand_mode = 1 +stretch_mode = 6 + +[node name="Titles" type="VBoxContainer" parent="."] +unique_name_in_owner = true +layout_mode = 2 +alignment = 1 + +[node name="SoundtrackLabel" type="Label" parent="Titles" unique_id=173147793] +unique_name_in_owner = true custom_minimum_size = Vector2(1, 1) layout_mode = 2 theme_type_variation = &"TitleLarge" horizontal_alignment = 1 autowrap_mode = 2 +text_overrun_behavior = 3 +max_lines_visible = 2 -[node name="SongLabel" type="Label" parent="." unique_id=1687490027] +[node name="SongLabel" type="Label" parent="Titles" unique_id=1687490027] +unique_name_in_owner = true custom_minimum_size = Vector2(1, 1) layout_mode = 2 theme_type_variation = &"TitleMedium" horizontal_alignment = 1 autowrap_mode = 2 +text_overrun_behavior = 3 +max_lines_visible = 2 diff --git a/quiz/Quiz.tscn b/quiz/Quiz.tscn index 230df88..6acfcb7 100644 --- a/quiz/Quiz.tscn +++ b/quiz/Quiz.tscn @@ -41,7 +41,7 @@ offset_bottom = 512.0 [node name="NowPlaying" parent="." unique_id=42468498 instance=ExtResource("scene_NowPlaying")] layout_mode = 0 offset_left = 64.0 -offset_top = 64.0 +offset_top = 56.0 offset_right = 1152.0 offset_bottom = 384.0 diff --git a/quiz/now_playing.gd b/quiz/now_playing.gd index 2c8167c..9ccaf6f 100644 --- a/quiz/now_playing.gd +++ b/quiz/now_playing.gd @@ -1,16 +1,105 @@ class_name NowPlaying -extends VBoxContainer +extends HBoxContainer ## The soundtrack and song that are playing, in large text at the top of the quiz. They -## fade in when they change. +## fade in when they change. Once the answer is shown, the soundtrack's cover shows on +## their left, when it has one, and the cover and titles are centered together. Both are +## centered up and down too, so the titles only move sideways when a cover comes. Each +## title has at most two lines. + +## The covers on disk. No cover is shown without it. +var covers: CoverCache: + set(value): + covers = value + if covers != null: + covers.cover_times_changed.connect(_load_cover) + _load_cover() + +# The soundtrack whose cover is shown or on its way, and the cover time asked for +var _cover_uuid: String = "" +var _cover_time: int = 0 + +@onready var soundtrack_label: Label = %SoundtrackLabel +@onready var song_label: Label = %SongLabel +@onready var cover_frame: Panel = %CoverFrame +@onready var cover_picture: TextureRect = %CoverPicture +@onready var titles: VBoxContainer = %Titles + + +func _ready() -> void: + resized.connect(_fit_titles) + _fit_titles() ## Shows [param soundtrack_title] and [param song_title]. The playlist hides them as -## question marks until the answer is shown. -func show_song(soundtrack_title: String, song_title: String) -> void: - var changed: bool = $SoundtrackLabel.text != soundtrack_title \ - || $SongLabel.text != song_title - $SoundtrackLabel.text = soundtrack_title - $SongLabel.text = song_title +## question marks until the answer is shown, and only then gives the +## [param soundtrack_uuid] whose cover is shown. +func show_song(soundtrack_title: String, song_title: String, + soundtrack_uuid: String = "") -> void: + var changed: bool = soundtrack_label.text != soundtrack_title \ + || song_label.text != song_title + soundtrack_label.text = soundtrack_title + song_label.text = song_title + _fit_titles() + if soundtrack_uuid != _cover_uuid: + _cover_uuid = soundtrack_uuid + _cover_time = 0 + _show_cover(null) + _load_cover() # A new song, or the answer being shown, fades in if changed: Anim.fade_in(self, Anim.SLOW) + + +## Whether a cover is shown. +func has_cover() -> bool: + return cover_frame.visible + + +# Asks for the cover, again when its time changed, e.g. once the soundtrack list loaded +func _load_cover() -> void: + if covers == null || _cover_uuid.is_empty() || !is_node_ready(): + return + var time: int = covers.cover_time(_cover_uuid) + if time == _cover_time: + return + _cover_time = time + if time <= 0: + _show_cover(null) + return + covers.fetch_cover(_cover_uuid, time, _show_cover_of.bind(_cover_uuid, time)) + + +# Covers arrive later; one for a song no longer shown is left out +func _show_cover_of(cover: Image, uuid: String, time: int) -> void: + if uuid == _cover_uuid && time == _cover_time: + _show_cover(cover) + + +func _show_cover(cover: Image) -> void: + var was_shown: bool = cover_frame.visible + cover_picture.texture = ImageTexture.create_from_image(cover) if cover != null else null + cover_frame.visible = cover != null + _fit_titles() + if cover_frame.visible && !was_shown: + Anim.fade_in(cover_frame, Anim.SLOW) + + +# The titles take the width of the longer one, so they stay next to the cover, but no +# more than there is room for; longer titles wrap. Beside a cover they line up with it +# on the left. +func _fit_titles() -> void: + var room: float = size.x + if cover_frame.visible: + room -= cover_frame.custom_minimum_size.x + get_theme_constant(&"separation") + var widest: float = maxf(_text_width(soundtrack_label), _text_width(song_label)) + titles.custom_minimum_size.x = clampf(ceilf(widest), 1.0, maxf(room, 1.0)) + var alignment: HorizontalAlignment = HORIZONTAL_ALIGNMENT_LEFT if cover_frame.visible \ + else HORIZONTAL_ALIGNMENT_CENTER + soundtrack_label.horizontal_alignment = alignment + song_label.horizontal_alignment = alignment + + +static func _text_width(label: Label) -> float: + var font: Font = label.get_theme_font(&"font") + return font.get_string_size(label.text, HORIZONTAL_ALIGNMENT_LEFT, -1, + label.get_theme_font_size(&"font_size")).x diff --git a/quiz/quiz.gd b/quiz/quiz.gd index b5a8639..2e8ddb9 100644 --- a/quiz/quiz.gd +++ b/quiz/quiz.gd @@ -171,7 +171,7 @@ func update_song_list() -> void: GameLog.debug("update_song_list") if Playlist.currently_playing_song >= 0: now_playing.show_song(Playlist.get_current_soundtrack_title(), - Playlist.get_current_song_title()) + Playlist.get_current_song_title(), Playlist.get_current_soundtrack_uuid()) music_list_panel.refresh() save_soon() diff --git a/quiz/song_loader.gd b/quiz/song_loader.gd index b0274b6..6857981 100644 --- a/quiz/song_loader.gd +++ b/quiz/song_loader.gd @@ -52,6 +52,7 @@ func load_song_list(saved: QuizSave = null) -> void: new_song_object.song_number = info.song_no new_song_object.soundtrack_title = info.soundtrack new_song_object.song_title = info.song + new_song_object.soundtrack_uuid = info.soundtrack_uuid new_song_object.is_answered = true new_song_object.has_played = true Playlist.add_to_playlist(new_song_object) @@ -139,6 +140,7 @@ func _fetch_song_info(new_song_object: SongObject) -> void: new_song_object.song_number = info.song_no new_song_object.soundtrack_title = info.soundtrack new_song_object.song_title = info.song + new_song_object.soundtrack_uuid = info.soundtrack_uuid GameLog.debug("new_song_object: ", new_song_object.to_string()) Playlist.add_to_playlist(new_song_object) if Playlist.should_preload_more_songs(): diff --git a/server/http_transport.gd b/server/http_transport.gd index 9908b15..d0621ef 100644 --- a/server/http_transport.gd +++ b/server/http_transport.gd @@ -18,14 +18,14 @@ func _init(owner: Node) -> void: ## Requests [param url] and calls [param on_complete] with ## [code](response_code: int, body: PackedByteArray, headers: PackedStringArray)[/code], ## the headers as [code]"Name: value"[/code] lines. Connection errors and timeouts are -## reported with response code 0 and no headers. [param headers] are sent as-is; a -## [param body] is sent as JSON. +## reported with response code 0 and no headers. [param headers], which name the +## [param body]'s type, are sent as-is. func request( url: String, method: int, on_complete: Callable, headers: PackedStringArray = PackedStringArray(), - body: String = "", + body: PackedByteArray = PackedByteArray(), ) -> void: var http_request: HTTPRequest = HTTPRequest.new() http_request.timeout = timeout_seconds @@ -43,10 +43,7 @@ func request( return on_complete.call(response_code, response_body, response_headers) ) - var all_headers: PackedStringArray = headers.duplicate() - if method == HTTPClient.METHOD_PUT || !body.is_empty(): - all_headers.append("Content-Type: application/json") - var error: int = http_request.request(url, all_headers, method, body) + var error: int = http_request.request_raw(url, headers, method, body) if error != OK: http_request.queue_free() push_error("Could not send request to %s (error %d)" % [url, error]) diff --git a/server/music_server_client.gd b/server/music_server_client.gd index 4b81e26..9030c6a 100644 --- a/server/music_server_client.gd +++ b/server/music_server_client.gd @@ -19,7 +19,7 @@ extends Node ## JSON objects are handed to [code]on_done[/code] as the classes generated from the ## server's OpenAPI spec (see [code]server/generated/[/code]), lists of them as typed ## Arrays. [code]on_failed[/code] is called with no arguments when a request fails, -## including when no token could be had. +## including when no token could be had; [member last_error] then has the server's reason. ## Path of the API on the server, added between [code]Settings.default_path[/code] and ## each endpoint. @@ -28,9 +28,17 @@ const API_PATH: String = "/api/v1" const CLIENT_TYPE: String = "godot" ## Header with the ID of the client's play queue, in requests and responses. const QUEUE_HEADER: String = "X-Queue-Id" +## The type of the JSON bodies sent. +const JSON_TYPE: String = "application/json" +## Where the cover of a soundtrack can be picked from, by the game's or anime's ID there. +const COVER_SOURCES: Array[String] = ["steamgriddb", "anilist"] ## Sends the actual HTTP requests. Swap it out in tests. var transport: HttpTransport +## The server's message for the request that failed last, e.g. [code]"covers are +## disabled"[/code], or empty when it gave none. Read it in an [code]on_failed[/code] +## callback, before another request can fail. +var last_error: String = "" var _token: String = "" # The server the token is for; switching server in the settings needs a new token @@ -135,7 +143,37 @@ func fetch_soundtracks(on_done: Callable, on_failed: Callable = Callable()) -> v ## PNG). The server answers 404 while it has no cover for it. func fetch_soundtrack_cover( uuid: String, on_done: Callable, on_failed: Callable = Callable()) -> void: - get_bytes("/soundtracks/%s/cover" % uuid.uri_encode(), on_done, on_failed) + get_bytes(_cover_path(uuid), on_done, on_failed) + + +## Stores [param image], a JPEG, PNG or WebP picture of [param content_type] such as +## [code]"image/png"[/code], as the cover of the soundtrack with [param uuid]. The +## server's automatic search never replaces it. +func upload_soundtrack_cover(uuid: String, image: PackedByteArray, content_type: String, + on_done: Callable, on_failed: Callable = Callable()) -> void: + _request(_cover_path(uuid), HTTPClient.METHOD_PUT, _call_ignoring_body.bind(on_done), + on_failed, true, image, content_type) + + +## Makes the cover of the game or anime with [param id] on [param source], one of +## [constant COVER_SOURCES], the cover of the soundtrack with [param uuid]. The server +## downloads it before answering. +func set_soundtrack_cover_source(uuid: String, source: String, id: String, + on_done: Callable, on_failed: Callable = Callable()) -> void: + var request: CoverSource = CoverSource.new() + request.source = source + request.id = id + _request(_cover_path(uuid) + "/source", HTTPClient.METHOD_PUT, + _call_ignoring_body.bind(on_done), on_failed, true, + JSON.stringify(request.to_dict()).to_utf8_buffer()) + + +## Deletes the cover of the soundtrack with [param uuid], also one set by hand, so the +## server's automatic search looks for a new one. +func delete_soundtrack_cover( + uuid: String, on_done: Callable, on_failed: Callable = Callable()) -> void: + _request(_cover_path(uuid), HTTPClient.METHOD_DELETE, _call_ignoring_body.bind(on_done), + on_failed) ## Replaces the alternative names of the soundtrack with [param uuid] with @@ -153,7 +191,7 @@ func set_alternative_names(uuid: String, names: Array[String], on_done: Callable return on_done.call(Soundtrack.from_dict(data)) _request(path, HTTPClient.METHOD_PUT, _parse_json.bind(path, saved, on_failed), on_failed, - true, JSON.stringify(request.to_dict())) + true, JSON.stringify(request.to_dict()).to_utf8_buffer()) ## Fetches a soundtrack and its songs as a [SoundtrackWithSongList]. @@ -444,25 +482,26 @@ func _parse_json( # Sends the request with the token, and once more with a new token if the server -# answers 401. A [param json_body] is sent as the request's body. +# answers 401. A [param body] of the type [param content_type] is sent with it. func _request( path: String, method: int, on_success: Callable, on_failed: Callable, may_retry: bool = true, - json_body: String = "", + body: PackedByteArray = PackedByteArray(), + content_type: String = JSON_TYPE, ) -> void: _with_token(func(token: String) -> void: - _send(path, method, token, func(response_code: int, body: PackedByteArray) -> void: + _send(path, method, token, func(response_code: int, response: PackedByteArray) -> void: if response_code == 401 && may_retry: GameLog.debug("Token refused by the server, getting a new one") if _token == token: clear_token() - _request(path, method, on_success, on_failed, false, json_body) + _request(path, method, on_success, on_failed, false, body, content_type) return - _handle_response(path, response_code, body, on_success, on_failed) - , json_body) + _handle_response(path, response_code, response, on_success, on_failed) + , body, content_type) , on_failed) @@ -476,12 +515,19 @@ func _send_once( func _send( - path: String, method: int, token: String, on_complete: Callable, body: String = "", + path: String, + method: int, + token: String, + on_complete: Callable, + body: PackedByteArray = PackedByteArray(), + content_type: String = JSON_TYPE, ) -> void: var server: String = Settings.default_path var headers: PackedStringArray = [] if !token.is_empty(): headers.append("Authorization: Bearer " + token) + if !body.is_empty() || method == HTTPClient.METHOD_PUT: + headers.append("Content-Type: " + content_type) var queue: String = Settings.queue_ids.get(server, "") if !queue.is_empty(): headers.append("%s: %s" % [QUEUE_HEADER, queue]) @@ -500,7 +546,9 @@ func _handle_response( on_success: Callable, on_failed: Callable, ) -> void: - if response_code != 200: + # Requests that change something may answer 204 No Content + if response_code < 200 || response_code >= 300: + last_error = _error_message(body).trim_prefix(": ") push_warning("Request to %s failed with response code %d%s" % [path, response_code, _error_message(body)]) _call_if_valid(on_failed) @@ -539,7 +587,7 @@ func _request_token() -> void: _token_server = server for waiter: Array in waiters: waiter[0].call(token) - , JSON.stringify(token_request.to_dict())) + , JSON.stringify(token_request.to_dict()).to_utf8_buffer()) func _read_token(response_code: int, body: PackedByteArray) -> String: @@ -568,6 +616,14 @@ static func _parse_or_null(body: PackedByteArray) -> Variant: return json.data if json.parse(body.get_string_from_utf8()) == OK else null +static func _cover_path(uuid: String) -> String: + return "/soundtracks/%s/cover" % uuid.uri_encode() + + +static func _call_ignoring_body(_body: PackedByteArray, callback: Callable) -> void: + _call_if_valid(callback) + + static func _call_if_valid(callback: Callable) -> void: if callback.is_valid(): callback.call() diff --git a/statistics/SoundtrackCard.tscn b/statistics/SoundtrackCard.tscn index e32cb92..21fc1ea 100644 --- a/statistics/SoundtrackCard.tscn +++ b/statistics/SoundtrackCard.tscn @@ -20,7 +20,7 @@ theme_override_constants/separation = 10 [node name="CoverFrame" type="Panel" parent="VBox/Header"] unique_name_in_owner = true clip_children = 2 -custom_minimum_size = Vector2(72, 100) +custom_minimum_size = Vector2(68, 102) layout_mode = 2 mouse_filter = 2 theme_type_variation = &"SoundtrackCover" diff --git a/statistics/soundtrack_card.gd b/statistics/soundtrack_card.gd index f089e7c..47ba9d1 100644 --- a/statistics/soundtrack_card.gd +++ b/statistics/soundtrack_card.gd @@ -80,6 +80,14 @@ func load_cover(covers: CoverCache) -> void: covers.fetch_cover(soundtrack.soundtrack_uuid, soundtrack.cover_modified, show_cover) +## Takes the soundtrack's new cover time [param modified], 0 when its cover was removed, +## and shows the initials until [method load_cover] fetches the new cover. +func set_cover_time(modified: int) -> void: + soundtrack.cover_modified = modified + show_cover(null) + _cover_asked = 0 + + ## Shows [param cover], or the initials when it is null. func show_cover(cover: Image) -> void: cover_picture.texture = ImageTexture.create_from_image(cover) if cover != null else null diff --git a/statistics/statistics_view.gd b/statistics/statistics_view.gd index 0965445..9a4f256 100644 --- a/statistics/statistics_view.gd +++ b/statistics/statistics_view.gd @@ -200,6 +200,15 @@ func update_alternative_names(updated: Soundtrack) -> void: apply_filter() +## Shows the new cover of the soundtrack [param uuid], with the cover time +## [param modified], or its initials when [param modified] is 0. +func update_cover(uuid: String, modified: int) -> void: + for card: SoundtrackCard in cards: + if card.soundtrack.soundtrack_uuid == uuid: + card.set_cover_time(modified) + load_covers_on_screen() + + func _update_search_names() -> void: var names: Array[String] = [] var alternatives: Dictionary = {} diff --git a/tests/data/covers/cover_0.jpg b/tests/data/covers/cover_0.jpg new file mode 100644 index 0000000..e507b1f Binary files /dev/null and b/tests/data/covers/cover_0.jpg differ diff --git a/tests/data/covers/cover_1.jpg b/tests/data/covers/cover_1.jpg new file mode 100644 index 0000000..36bb15c Binary files /dev/null and b/tests/data/covers/cover_1.jpg differ diff --git a/tests/data/covers/cover_2.jpg b/tests/data/covers/cover_2.jpg new file mode 100644 index 0000000..08275d5 Binary files /dev/null and b/tests/data/covers/cover_2.jpg differ diff --git a/tests/data/covers/cover_3.jpg b/tests/data/covers/cover_3.jpg new file mode 100644 index 0000000..273b73d Binary files /dev/null and b/tests/data/covers/cover_3.jpg differ diff --git a/tests/data/covers/cover_4.jpg b/tests/data/covers/cover_4.jpg new file mode 100644 index 0000000..16bf280 Binary files /dev/null and b/tests/data/covers/cover_4.jpg differ diff --git a/tests/data/layout_baseline.json b/tests/data/layout_baseline.json index 06621c7..bd6fbca 100644 --- a/tests/data/layout_baseline.json +++ b/tests/data/layout_baseline.json @@ -1137,26 +1137,53 @@ "Quiz/NowPlaying": { "rect": [ 64.0, - 64.0, + 56.0, 1088.0, - 320.0 + 328.0 ], "visible": true }, - "Quiz/NowPlaying/SongLabel": { + "Quiz/NowPlaying/CoverFrame": { "rect": [ 64.0, - 137.0, - 1088.0, + 56.0, + 180.0, + 270.0 + ], + "visible": false + }, + "Quiz/NowPlaying/CoverFrame/CoverPicture": { + "rect": [ + 64.0, + 56.0, + 180.0, + 270.0 + ], + "visible": false + }, + "Quiz/NowPlaying/Titles": { + "rect": [ + 607.0, + 56.0, + 1.0, + 328.0 + ], + "visible": true + }, + "Quiz/NowPlaying/Titles/SongLabel": { + "rect": [ + 607.0, + 232.0, + 1.0, 49.0 ], "visible": true }, - "Quiz/NowPlaying/SoundtrackLabel": { + "Quiz/NowPlaying/Titles/SoundtrackLabel": { "rect": [ - 64.0, - 64.0, - 1088.0, + 607.0, + 159.0, + 1.0, 69.0 ], "visible": true @@ -3150,6 +3177,15 @@ ], "visible": false }, + "SoundtrackStatsPopup/PictureDialog": { + "rect": [ + 0.0, + 0.0, + 800.0, + 500.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel": { "rect": [ 610.0, @@ -3159,12 +3195,156 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 6160.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons/CloseCoverButton": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 4547.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorSpace": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorTitle": { + "rect": [ + 630.0, + 420.0, + 70.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow": { + "rect": [ + 630.0, + 420.0, + 299.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/ChoosePictureButton": { + "rect": [ + 630.0, + 420.0, + 154.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/RemoveCoverButton": { + "rect": [ + 630.0, + 420.0, + 133.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 1389.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow": { + "rect": [ + 630.0, + 420.0, + 283.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverIdField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverSourceButton": { + "rect": [ + 630.0, + 420.0, + 145.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/UseSourceButton": { + "rect": [ + 630.0, + 420.0, + 53.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceTitle": { + "rect": [ + 630.0, + 420.0, + 85.0, + 26.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/CoverFrame": { "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -3172,8 +3352,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -3181,8 +3361,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -3195,7 +3375,25 @@ ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons": { + "rect": [ + 790.0, + 204.0, + 251.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/ChangeCoverButton": { + "rect": [ + 790.0, + 204.0, + 129.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/EditNamesButton": { "rect": [ 790.0, 204.0, @@ -5126,26 +5324,53 @@ "Quiz/NowPlaying": { "rect": [ 64.0, - 64.0, + 56.0, 1088.0, - 320.0 + 328.0 ], "visible": true }, - "Quiz/NowPlaying/SongLabel": { + "Quiz/NowPlaying/CoverFrame": { "rect": [ - 64.0, - 137.0, - 1088.0, + 321.0, + 85.0, + 180.0, + 270.0 + ], + "visible": false + }, + "Quiz/NowPlaying/CoverFrame/CoverPicture": { + "rect": [ + 321.0, + 85.0, + 180.0, + 270.0 + ], + "visible": false + }, + "Quiz/NowPlaying/Titles": { + "rect": [ + 427.0, + 56.0, + 361.0, + 328.0 + ], + "visible": true + }, + "Quiz/NowPlaying/Titles/SongLabel": { + "rect": [ + 427.0, + 232.0, + 361.0, 49.0 ], "visible": true }, - "Quiz/NowPlaying/SoundtrackLabel": { + "Quiz/NowPlaying/Titles/SoundtrackLabel": { "rect": [ - 64.0, - 64.0, - 1088.0, + 427.0, + 159.0, + 361.0, 69.0 ], "visible": true @@ -7634,6 +7859,15 @@ ], "visible": false }, + "SoundtrackStatsPopup/PictureDialog": { + "rect": [ + 0.0, + 0.0, + 800.0, + 500.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel": { "rect": [ 610.0, @@ -7643,12 +7877,156 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 504.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons": { + "rect": [ + 630.0, + 893.0, + 660.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons/CloseCoverButton": { + "rect": [ + 1225.0, + 893.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorHelp": { + "rect": [ + 630.0, + 465.0, + 660.0, + 75.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorSpace": { + "rect": [ + 630.0, + 704.0, + 660.0, + 179.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorTitle": { + "rect": [ + 630.0, + 420.0, + 660.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow": { + "rect": [ + 630.0, + 550.0, + 660.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/ChoosePictureButton": { + "rect": [ + 630.0, + 550.0, + 154.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/RemoveCoverButton": { + "rect": [ + 796.0, + 550.0, + 133.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceHelp": { + "rect": [ + 630.0, + 627.0, + 660.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow": { + "rect": [ + 630.0, + 663.0, + 660.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverIdField": { + "rect": [ + 783.0, + 663.0, + 446.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverSourceButton": { + "rect": [ + 630.0, + 663.0, + 145.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/UseSourceButton": { + "rect": [ + 1237.0, + 663.0, + 53.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceTitle": { + "rect": [ + 630.0, + 591.0, + 660.0, + 26.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/CoverFrame": { "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -7656,8 +8034,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -7665,8 +8043,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -7679,7 +8057,25 @@ ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons": { + "rect": [ + 790.0, + 291.0, + 500.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/ChangeCoverButton": { + "rect": [ + 912.0, + 291.0, + 129.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/EditNamesButton": { "rect": [ 790.0, 291.0, @@ -8079,7 +8475,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -8088,7 +8484,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -8097,43 +8493,43 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -8250,7 +8646,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -8259,7 +8655,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -8268,43 +8664,43 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 584.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 59.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 557.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -8421,7 +8817,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -8430,7 +8826,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -8439,43 +8835,43 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1350.0, + 1346.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { "rect": [ - 1350.0, + 1346.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -8583,7 +8979,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -8592,7 +8988,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -8601,43 +8997,43 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -8745,7 +9141,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -8754,7 +9150,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -8763,43 +9159,43 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -8898,7 +9294,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -8907,7 +9303,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -8916,43 +9312,43 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 228.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 59.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 201.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -9051,7 +9447,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -9060,7 +9456,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -9069,43 +9465,43 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -9195,7 +9591,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -9204,7 +9600,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -9213,43 +9609,43 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -9330,7 +9726,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -9339,7 +9735,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -9348,43 +9744,43 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1656.0, + 1652.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { "rect": [ - 1656.0, + 1652.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -9457,7 +9853,7 @@ 44.0, 138.0, 266.0, - 100.0 + 102.0 ], "visible": false }, @@ -9465,8 +9861,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, @@ -9474,8 +9870,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, @@ -9483,23 +9879,23 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, - 100.0 + 188.0, + 102.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 138.0, 1.0, 23.0 @@ -9508,18 +9904,18 @@ }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -9536,7 +9932,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 276.0, + 278.0, 266.0, 0.0 ], @@ -9545,7 +9941,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 242.0, + 244.0, 266.0, 26.0 ], @@ -9554,7 +9950,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 272.0, + 274.0, 266.0, 0.0 ], @@ -12715,26 +13111,53 @@ "Quiz/NowPlaying": { "rect": [ 64.0, - 64.0, + 56.0, 1088.0, - 320.0 + 328.0 ], "visible": true }, - "Quiz/NowPlaying/SongLabel": { + "Quiz/NowPlaying/CoverFrame": { "rect": [ 64.0, - 137.0, - 1088.0, + 56.0, + 180.0, + 270.0 + ], + "visible": false + }, + "Quiz/NowPlaying/CoverFrame/CoverPicture": { + "rect": [ + 64.0, + 56.0, + 180.0, + 270.0 + ], + "visible": false + }, + "Quiz/NowPlaying/Titles": { + "rect": [ + 607.0, + 56.0, + 1.0, + 328.0 + ], + "visible": true + }, + "Quiz/NowPlaying/Titles/SongLabel": { + "rect": [ + 607.0, + 232.0, + 1.0, 49.0 ], "visible": true }, - "Quiz/NowPlaying/SoundtrackLabel": { + "Quiz/NowPlaying/Titles/SoundtrackLabel": { "rect": [ - 64.0, - 64.0, - 1088.0, + 607.0, + 159.0, + 1.0, 69.0 ], "visible": true @@ -14908,6 +15331,15 @@ ], "visible": false }, + "SoundtrackStatsPopup/PictureDialog": { + "rect": [ + 0.0, + 0.0, + 800.0, + 500.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel": { "rect": [ 610.0, @@ -14917,12 +15349,156 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 6160.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons/CloseCoverButton": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 4547.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorSpace": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorTitle": { + "rect": [ + 630.0, + 420.0, + 70.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow": { + "rect": [ + 630.0, + 420.0, + 299.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/ChoosePictureButton": { + "rect": [ + 630.0, + 420.0, + 154.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/RemoveCoverButton": { + "rect": [ + 630.0, + 420.0, + 133.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 1389.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow": { + "rect": [ + 630.0, + 420.0, + 283.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverIdField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverSourceButton": { + "rect": [ + 630.0, + 420.0, + 145.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/UseSourceButton": { + "rect": [ + 630.0, + 420.0, + 53.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceTitle": { + "rect": [ + 630.0, + 420.0, + 85.0, + 26.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/CoverFrame": { "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -14930,8 +15506,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -14939,8 +15515,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -14953,7 +15529,25 @@ ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons": { + "rect": [ + 790.0, + 204.0, + 251.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/ChangeCoverButton": { + "rect": [ + 790.0, + 204.0, + 129.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/EditNamesButton": { "rect": [ 790.0, 204.0, @@ -17037,26 +17631,53 @@ "Quiz/NowPlaying": { "rect": [ 64.0, - 64.0, + 56.0, 1088.0, - 320.0 + 328.0 ], "visible": true }, - "Quiz/NowPlaying/SongLabel": { + "Quiz/NowPlaying/CoverFrame": { "rect": [ - 64.0, - 137.0, - 1088.0, + 321.0, + 85.0, + 180.0, + 270.0 + ], + "visible": true + }, + "Quiz/NowPlaying/CoverFrame/CoverPicture": { + "rect": [ + 321.0, + 85.0, + 180.0, + 270.0 + ], + "visible": true + }, + "Quiz/NowPlaying/Titles": { + "rect": [ + 533.0, + 56.0, + 361.0, + 328.0 + ], + "visible": true + }, + "Quiz/NowPlaying/Titles/SongLabel": { + "rect": [ + 533.0, + 232.0, + 361.0, 49.0 ], "visible": true }, - "Quiz/NowPlaying/SoundtrackLabel": { + "Quiz/NowPlaying/Titles/SoundtrackLabel": { "rect": [ - 64.0, - 64.0, - 1088.0, + 533.0, + 159.0, + 361.0, 69.0 ], "visible": true @@ -19230,6 +19851,15 @@ ], "visible": false }, + "SoundtrackStatsPopup/PictureDialog": { + "rect": [ + 0.0, + 0.0, + 800.0, + 500.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel": { "rect": [ 610.0, @@ -19239,12 +19869,156 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 6160.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons/CloseCoverButton": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 4547.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorSpace": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorTitle": { + "rect": [ + 630.0, + 420.0, + 70.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow": { + "rect": [ + 630.0, + 420.0, + 299.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/ChoosePictureButton": { + "rect": [ + 630.0, + 420.0, + 154.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/RemoveCoverButton": { + "rect": [ + 630.0, + 420.0, + 133.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 1389.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow": { + "rect": [ + 630.0, + 420.0, + 283.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverIdField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverSourceButton": { + "rect": [ + 630.0, + 420.0, + 145.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/UseSourceButton": { + "rect": [ + 630.0, + 420.0, + 53.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceTitle": { + "rect": [ + 630.0, + 420.0, + 85.0, + 26.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/CoverFrame": { "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -19252,8 +20026,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -19261,8 +20035,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -19275,7 +20049,25 @@ ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons": { + "rect": [ + 790.0, + 204.0, + 251.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/ChangeCoverButton": { + "rect": [ + 790.0, + 204.0, + 129.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/EditNamesButton": { "rect": [ 790.0, 204.0, @@ -21650,7 +22442,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7": { "rect": [ 408.0, - 497.0, + 458.0, 1104.0, 35.0 ], @@ -21659,7 +22451,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7/#0": { "rect": [ 416.0, - 499.0, + 460.0, 1088.0, 31.0 ], @@ -21668,7 +22460,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8": { "rect": [ 408.0, - 536.0, + 497.0, 1104.0, 35.0 ], @@ -21677,7 +22469,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8/#0": { "rect": [ 416.0, - 538.0, + 499.0, 1088.0, 31.0 ], @@ -21686,7 +22478,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9": { "rect": [ 408.0, - 575.0, + 536.0, 1104.0, 35.0 ], @@ -21695,7 +22487,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9/#0": { "rect": [ 416.0, - 577.0, + 538.0, 1088.0, 31.0 ], @@ -23149,6 +23941,15 @@ ], "visible": true }, + "SoundtrackStatsPopup/PictureDialog": { + "rect": [ + 0.0, + 0.0, + 800.0, + 500.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel": { "rect": [ 610.0, @@ -23158,12 +23959,156 @@ ], "visible": true }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 6160.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons/CloseCoverButton": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 4547.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorSpace": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorTitle": { + "rect": [ + 630.0, + 420.0, + 70.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow": { + "rect": [ + 630.0, + 420.0, + 299.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/ChoosePictureButton": { + "rect": [ + 630.0, + 420.0, + 154.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/RemoveCoverButton": { + "rect": [ + 630.0, + 420.0, + 133.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 1389.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow": { + "rect": [ + 630.0, + 420.0, + 283.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverIdField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverSourceButton": { + "rect": [ + 630.0, + 420.0, + 145.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/UseSourceButton": { + "rect": [ + 630.0, + 420.0, + 53.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceTitle": { + "rect": [ + 630.0, + 420.0, + 85.0, + 26.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/CoverFrame": { "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": true }, @@ -23171,8 +24116,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": true }, @@ -23180,8 +24125,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -23194,7 +24139,25 @@ ], "visible": true }, - "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons": { + "rect": [ + 790.0, + 291.0, + 500.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/ChangeCoverButton": { + "rect": [ + 912.0, + 291.0, + 129.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/EditNamesButton": { "rect": [ 790.0, 291.0, @@ -23456,7 +24419,7 @@ "visible": true } }, - "popup_SoundtrackStats_names": { + "popup_SoundtrackStats_cover": { "SoundtrackStatsPopup": { "rect": [ 610.0, @@ -23466,6 +24429,15 @@ ], "visible": true }, + "SoundtrackStatsPopup/PictureDialog": { + "rect": [ + 0.0, + 0.0, + 800.0, + 500.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel": { "rect": [ 610.0, @@ -23475,12 +24447,156 @@ ], "visible": true }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 504.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons": { + "rect": [ + 630.0, + 893.0, + 660.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons/CloseCoverButton": { + "rect": [ + 1225.0, + 893.0, + 65.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorHelp": { + "rect": [ + 630.0, + 465.0, + 660.0, + 75.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorSpace": { + "rect": [ + 630.0, + 704.0, + 660.0, + 179.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorTitle": { + "rect": [ + 630.0, + 420.0, + 660.0, + 35.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow": { + "rect": [ + 630.0, + 550.0, + 660.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/ChoosePictureButton": { + "rect": [ + 630.0, + 550.0, + 154.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/RemoveCoverButton": { + "rect": [ + 796.0, + 550.0, + 133.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceHelp": { + "rect": [ + 630.0, + 627.0, + 660.0, + 26.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow": { + "rect": [ + 630.0, + 663.0, + 660.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverIdField": { + "rect": [ + 783.0, + 663.0, + 446.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverSourceButton": { + "rect": [ + 630.0, + 663.0, + 145.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/UseSourceButton": { + "rect": [ + 1237.0, + 663.0, + 53.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceTitle": { + "rect": [ + 630.0, + 591.0, + 660.0, + 26.0 + ], + "visible": true + }, "SoundtrackStatsPopup/StatsPanel/CoverFrame": { "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": true }, @@ -23488,8 +24604,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": true }, @@ -23497,8 +24613,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -23511,7 +24627,585 @@ ], "visible": true }, - "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons": { + "rect": [ + 790.0, + 291.0, + 500.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/ChangeCoverButton": { + "rect": [ + 912.0, + 291.0, + 129.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/EditNamesButton": { + "rect": [ + 790.0, + 291.0, + 110.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesLabel": { + "rect": [ + 790.0, + 261.0, + 500.0, + 26.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesTitle": { + "rect": [ + 790.0, + 231.0, + 500.0, + 26.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/SummaryLabel": { + "rect": [ + 790.0, + 204.0, + 500.0, + 23.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 504.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow": { + "rect": [ + 630.0, + 852.0, + 660.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/AddNameButton": { + "rect": [ + 1235.0, + 852.0, + 55.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/NewNameField": { + "rect": [ + 630.0, + 852.0, + 597.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons": { + "rect": [ + 630.0, + 893.0, + 660.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/CancelNamesButton": { + "rect": [ + 1142.0, + 893.0, + 76.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/SaveNamesButton": { + "rect": [ + 1230.0, + 893.0, + 60.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorHelp": { + "rect": [ + 630.0, + 465.0, + 660.0, + 49.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll": { + "rect": [ + 630.0, + 524.0, + 660.0, + 318.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames": { + "rect": [ + 630.0, + 524.0, + 660.0, + 74.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#0": { + "rect": [ + 630.0, + 524.0, + 660.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#0/#0": { + "rect": [ + 638.0, + 526.0, + 644.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#0/#0/#0": { + "rect": [ + 638.0, + 530.0, + 553.0, + 23.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#0/#0/#1": { + "rect": [ + 1195.0, + 526.0, + 87.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#1": { + "rect": [ + 630.0, + 563.0, + 660.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#1/#0": { + "rect": [ + 638.0, + 565.0, + 644.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#1/#0/#0": { + "rect": [ + 638.0, + 569.0, + 553.0, + 23.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#1/#0/#1": { + "rect": [ + 1195.0, + 565.0, + 87.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorTitle": { + "rect": [ + 630.0, + 420.0, + 660.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer": { + "rect": [ + 630.0, + 420.0, + 660.0, + 504.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList": { + "rect": [ + 630.0, + 420.0, + 660.0, + 176.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#0": { + "rect": [ + 630.0, + 420.0, + 660.0, + 32.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#0/#0": { + "rect": [ + 638.0, + 422.0, + 644.0, + 28.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#1": { + "rect": [ + 630.0, + 456.0, + 660.0, + 32.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#1/#0": { + "rect": [ + 638.0, + 458.0, + 644.0, + 28.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#2": { + "rect": [ + 630.0, + 492.0, + 660.0, + 32.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#2/#0": { + "rect": [ + 638.0, + 494.0, + 644.0, + 28.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#3": { + "rect": [ + 630.0, + 528.0, + 660.0, + 32.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#3/#0": { + "rect": [ + 638.0, + 530.0, + 644.0, + 28.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#4": { + "rect": [ + 630.0, + 564.0, + 660.0, + 32.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#4/#0": { + "rect": [ + 638.0, + 566.0, + 644.0, + 28.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/TitleLabel": { + "rect": [ + 630.0, + 152.0, + 660.0, + 38.0 + ], + "visible": true + } + }, + "popup_SoundtrackStats_names": { + "SoundtrackStatsPopup": { + "rect": [ + 610.0, + 140.0, + 1920.0, + 1080.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/PictureDialog": { + "rect": [ + 0.0, + 0.0, + 800.0, + 500.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel": { + "rect": [ + 610.0, + 140.0, + 700.0, + 800.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 6160.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons/CloseCoverButton": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 4547.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorSpace": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorTitle": { + "rect": [ + 630.0, + 420.0, + 70.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow": { + "rect": [ + 630.0, + 420.0, + 299.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/ChoosePictureButton": { + "rect": [ + 630.0, + 420.0, + 154.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/RemoveCoverButton": { + "rect": [ + 630.0, + 420.0, + 133.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 1389.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow": { + "rect": [ + 630.0, + 420.0, + 283.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverIdField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverSourceButton": { + "rect": [ + 630.0, + 420.0, + 145.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/UseSourceButton": { + "rect": [ + 630.0, + 420.0, + 53.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceTitle": { + "rect": [ + 630.0, + 420.0, + 85.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame": { + "rect": [ + 630.0, + 204.0, + 140.0, + 210.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPicture": { + "rect": [ + 630.0, + 204.0, + 140.0, + 210.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPlaceholder": { + "rect": [ + 630.0, + 204.0, + 140.0, + 210.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info": { + "rect": [ + 790.0, + 204.0, + 500.0, + 200.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons": { + "rect": [ + 790.0, + 291.0, + 500.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/ChangeCoverButton": { + "rect": [ + 912.0, + 291.0, + 129.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/EditNamesButton": { "rect": [ 790.0, 291.0, @@ -26269,26 +27963,53 @@ "Quiz/NowPlaying": { "rect": [ 64.0, - 64.0, + 56.0, 1088.0, - 320.0 + 328.0 ], "visible": true }, - "Quiz/NowPlaying/SongLabel": { + "Quiz/NowPlaying/CoverFrame": { "rect": [ 64.0, - 137.0, - 1088.0, + 56.0, + 180.0, + 270.0 + ], + "visible": false + }, + "Quiz/NowPlaying/CoverFrame/CoverPicture": { + "rect": [ + 64.0, + 56.0, + 180.0, + 270.0 + ], + "visible": false + }, + "Quiz/NowPlaying/Titles": { + "rect": [ + 607.0, + 56.0, + 1.0, + 328.0 + ], + "visible": true + }, + "Quiz/NowPlaying/Titles/SongLabel": { + "rect": [ + 607.0, + 232.0, + 1.0, 49.0 ], "visible": true }, - "Quiz/NowPlaying/SoundtrackLabel": { + "Quiz/NowPlaying/Titles/SoundtrackLabel": { "rect": [ - 64.0, - 64.0, - 1088.0, + 607.0, + 159.0, + 1.0, 69.0 ], "visible": true @@ -28282,6 +30003,15 @@ ], "visible": false }, + "SoundtrackStatsPopup/PictureDialog": { + "rect": [ + 0.0, + 0.0, + 800.0, + 500.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel": { "rect": [ 610.0, @@ -28291,12 +30021,156 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 6160.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons/CloseCoverButton": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 4547.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorSpace": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorTitle": { + "rect": [ + 630.0, + 420.0, + 70.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow": { + "rect": [ + 630.0, + 420.0, + 299.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/ChoosePictureButton": { + "rect": [ + 630.0, + 420.0, + 154.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/RemoveCoverButton": { + "rect": [ + 630.0, + 420.0, + 133.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 1389.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow": { + "rect": [ + 630.0, + 420.0, + 283.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverIdField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverSourceButton": { + "rect": [ + 630.0, + 420.0, + 145.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/UseSourceButton": { + "rect": [ + 630.0, + 420.0, + 53.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceTitle": { + "rect": [ + 630.0, + 420.0, + 85.0, + 26.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/CoverFrame": { "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -28304,8 +30178,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -28313,8 +30187,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -28327,7 +30201,25 @@ ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons": { + "rect": [ + 790.0, + 204.0, + 251.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/ChangeCoverButton": { + "rect": [ + 790.0, + 204.0, + 129.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/EditNamesButton": { "rect": [ 790.0, 204.0, @@ -29224,7 +31116,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": true @@ -29233,7 +31125,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": true @@ -29242,43 +31134,43 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 113.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 228.0, - 184.0, + 188.0, 23.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 59.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 201.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -29377,7 +31269,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -29386,7 +31278,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -29395,43 +31287,43 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -29530,7 +31422,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -29539,7 +31431,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -29548,43 +31440,43 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -29692,7 +31584,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -29701,7 +31593,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -29710,43 +31602,43 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1350.0, + 1346.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { "rect": [ - 1350.0, + 1346.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -29854,7 +31746,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -29863,7 +31755,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -29872,43 +31764,43 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1656.0, + 1652.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { "rect": [ - 1656.0, + 1652.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -29989,7 +31881,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -29998,7 +31890,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -30007,43 +31899,43 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -30133,7 +32025,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -30142,7 +32034,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -30151,43 +32043,43 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -30268,7 +32160,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -30277,7 +32169,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": true @@ -30286,43 +32178,43 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -30439,7 +32331,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": true @@ -30448,7 +32340,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": true @@ -30457,43 +32349,43 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 113.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 584.0, - 184.0, + 188.0, 23.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 59.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 557.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -30602,7 +32494,7 @@ 44.0, 138.0, 266.0, - 100.0 + 102.0 ], "visible": true }, @@ -30610,8 +32502,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": true }, @@ -30619,8 +32511,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": true }, @@ -30628,23 +32520,23 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, - 100.0 + 188.0, + 102.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 138.0, 1.0, 23.0 @@ -30653,18 +32545,18 @@ }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -30681,7 +32573,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 276.0, + 278.0, 266.0, 0.0 ], @@ -30690,7 +32582,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 242.0, + 244.0, 266.0, 26.0 ], @@ -30699,7 +32591,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 272.0, + 274.0, 266.0, 0.0 ], @@ -31017,7 +32909,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -31026,7 +32918,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -31035,43 +32927,43 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 228.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 59.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 201.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -31170,7 +33062,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31179,7 +33071,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31188,43 +33080,43 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -31323,7 +33215,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31332,7 +33224,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31341,43 +33233,43 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -31485,7 +33377,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31494,7 +33386,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31503,43 +33395,43 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1350.0, + 1346.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { "rect": [ - 1350.0, + 1346.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -31647,7 +33539,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31656,7 +33548,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31665,43 +33557,43 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1656.0, + 1652.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { "rect": [ - 1656.0, + 1652.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -31782,7 +33674,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31791,7 +33683,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31800,43 +33692,43 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -31926,7 +33818,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31935,7 +33827,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -31944,43 +33836,43 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -32061,7 +33953,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -32070,7 +33962,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -32079,43 +33971,43 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -32232,7 +34124,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -32241,7 +34133,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -32250,43 +34142,43 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 584.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 59.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 557.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -32395,7 +34287,7 @@ 44.0, 138.0, 266.0, - 100.0 + 102.0 ], "visible": true }, @@ -32403,8 +34295,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": true }, @@ -32412,8 +34304,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": true }, @@ -32421,23 +34313,23 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, - 100.0 + 188.0, + 102.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 138.0, 1.0, 23.0 @@ -32446,18 +34338,18 @@ }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -32474,7 +34366,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 276.0, + 278.0, 266.0, 0.0 ], @@ -32483,7 +34375,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 242.0, + 244.0, 266.0, 26.0 ], @@ -32492,7 +34384,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 272.0, + 274.0, 266.0, 0.0 ], @@ -32810,7 +34702,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -32819,7 +34711,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -32828,43 +34720,43 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -32981,7 +34873,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -32990,7 +34882,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -32999,43 +34891,43 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 584.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 59.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 557.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -33152,7 +35044,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -33161,7 +35053,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -33170,43 +35062,43 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1350.0, + 1346.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { "rect": [ - 1350.0, + 1346.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -33314,7 +35206,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -33323,7 +35215,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -33332,43 +35224,43 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -33476,7 +35368,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -33485,7 +35377,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -33494,43 +35386,43 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -33629,7 +35521,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -33638,7 +35530,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -33647,43 +35539,43 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 228.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 59.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 201.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -33782,7 +35674,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -33791,7 +35683,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -33800,43 +35692,43 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -33926,7 +35818,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -33935,7 +35827,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -33944,43 +35836,43 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -34061,7 +35953,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -34070,7 +35962,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -34079,43 +35971,43 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1656.0, + 1652.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { "rect": [ - 1656.0, + 1652.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -34188,7 +36080,7 @@ 44.0, 138.0, 266.0, - 100.0 + 102.0 ], "visible": true }, @@ -34196,8 +36088,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": true }, @@ -34205,8 +36097,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": true }, @@ -34214,23 +36106,23 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, - 100.0 + 188.0, + 102.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 138.0, 1.0, 23.0 @@ -34239,18 +36131,18 @@ }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": true }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": true @@ -34267,7 +36159,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 276.0, + 278.0, 266.0, 0.0 ], @@ -34276,7 +36168,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 242.0, + 244.0, 266.0, 26.0 ], @@ -34285,7 +36177,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 272.0, + 274.0, 266.0, 0.0 ], @@ -34603,7 +36495,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -34612,7 +36504,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -34621,43 +36513,43 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -34774,7 +36666,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -34783,7 +36675,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -34792,43 +36684,43 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 584.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 59.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 557.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -34945,7 +36837,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -34954,7 +36846,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -34963,43 +36855,43 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1350.0, + 1346.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { "rect": [ - 1350.0, + 1346.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -35107,7 +36999,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -35116,7 +37008,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -35125,43 +37017,43 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -35269,7 +37161,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -35278,7 +37170,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -35287,43 +37179,43 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -35422,7 +37314,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -35431,7 +37323,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -35440,43 +37332,43 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 228.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 59.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 201.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -35575,7 +37467,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -35584,7 +37476,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -35593,43 +37485,43 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -35719,7 +37611,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -35728,7 +37620,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -35737,43 +37629,43 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -35854,7 +37746,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -35863,7 +37755,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -35872,43 +37764,43 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1656.0, + 1652.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { "rect": [ - 1656.0, + 1652.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -35981,7 +37873,7 @@ 44.0, 138.0, 266.0, - 100.0 + 102.0 ], "visible": false }, @@ -35989,8 +37881,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, @@ -35998,8 +37890,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, @@ -36007,23 +37899,23 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, - 100.0 + 188.0, + 102.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 138.0, 1.0, 23.0 @@ -36032,18 +37924,18 @@ }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -36060,7 +37952,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 276.0, + 278.0, 266.0, 0.0 ], @@ -36069,7 +37961,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 242.0, + 244.0, 266.0, 26.0 ], @@ -36078,7 +37970,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 272.0, + 274.0, 266.0, 0.0 ], @@ -37701,7 +39593,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -37710,7 +39602,7 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -37719,43 +39611,43 @@ "rect": [ 656.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -37872,7 +39764,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -37881,7 +39773,7 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -37890,43 +39782,43 @@ "rect": [ 962.0, 494.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 584.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 494.0, - 184.0, + 188.0, 59.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 557.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -38043,7 +39935,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38052,7 +39944,7 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38061,43 +39953,43 @@ "rect": [ 1268.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1350.0, + 1346.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { "rect": [ - 1350.0, + 1346.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { "rect": [ - 1350.0, + 1346.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -38205,7 +40097,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38214,7 +40106,7 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38223,43 +40115,43 @@ "rect": [ 962.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1044.0, + 1040.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { "rect": [ - 1044.0, + 1040.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { "rect": [ - 1044.0, + 1040.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -38367,7 +40259,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38376,7 +40268,7 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38385,43 +40277,43 @@ "rect": [ 656.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 738.0, + 734.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { "rect": [ - 738.0, + 734.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { "rect": [ - 738.0, + 734.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -38520,7 +40412,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -38529,7 +40421,7 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false @@ -38538,43 +40430,43 @@ "rect": [ 350.0, 138.0, - 72.0, + 68.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 113.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 228.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 138.0, - 184.0, + 188.0, 59.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 201.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -38673,7 +40565,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38682,7 +40574,7 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38691,43 +40583,43 @@ "rect": [ 44.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -38817,7 +40709,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38826,7 +40718,7 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38835,43 +40727,43 @@ "rect": [ 350.0, 494.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 432.0, + 428.0, 575.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { "rect": [ - 432.0, + 428.0, 494.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { "rect": [ - 432.0, + 428.0, 548.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -38952,7 +40844,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38961,7 +40853,7 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false @@ -38970,43 +40862,43 @@ "rect": [ 1574.0, 138.0, - 72.0, + 68.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 104.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 1656.0, + 1652.0, 219.0, - 184.0, + 188.0, 23.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { "rect": [ - 1656.0, + 1652.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { "rect": [ - 1656.0, + 1652.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -39079,7 +40971,7 @@ 44.0, 138.0, 266.0, - 100.0 + 102.0 ], "visible": false }, @@ -39087,8 +40979,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, @@ -39096,8 +40988,8 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, @@ -39105,23 +40997,23 @@ "rect": [ 44.0, 138.0, - 72.0, - 100.0 + 68.0, + 102.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, - 100.0 + 188.0, + 102.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { "rect": [ - 126.0, + 122.0, 138.0, 1.0, 23.0 @@ -39130,18 +41022,18 @@ }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { "rect": [ - 126.0, + 122.0, 138.0, - 184.0, + 188.0, 50.0 ], "visible": false }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { "rect": [ - 126.0, + 122.0, 192.0, - 184.0, + 188.0, 23.0 ], "visible": false @@ -39158,7 +41050,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 276.0, + 278.0, 266.0, 0.0 ], @@ -39167,7 +41059,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 242.0, + 244.0, 266.0, 26.0 ], @@ -39176,7 +41068,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 272.0, + 274.0, 266.0, 0.0 ], @@ -41986,26 +43878,53 @@ "Quiz/NowPlaying": { "rect": [ 64.0, - 64.0, + 56.0, 1088.0, - 320.0 + 328.0 ], "visible": true }, - "Quiz/NowPlaying/SongLabel": { + "Quiz/NowPlaying/CoverFrame": { "rect": [ - 64.0, - 137.0, - 1088.0, + 321.0, + 85.0, + 180.0, + 270.0 + ], + "visible": true + }, + "Quiz/NowPlaying/CoverFrame/CoverPicture": { + "rect": [ + 321.0, + 85.0, + 180.0, + 270.0 + ], + "visible": true + }, + "Quiz/NowPlaying/Titles": { + "rect": [ + 533.0, + 56.0, + 361.0, + 328.0 + ], + "visible": true + }, + "Quiz/NowPlaying/Titles/SongLabel": { + "rect": [ + 533.0, + 232.0, + 361.0, 49.0 ], "visible": true }, - "Quiz/NowPlaying/SoundtrackLabel": { + "Quiz/NowPlaying/Titles/SoundtrackLabel": { "rect": [ - 64.0, - 64.0, - 1088.0, + 533.0, + 159.0, + 361.0, 69.0 ], "visible": true @@ -43102,7 +45021,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6": { "rect": [ 408.0, - 224.0, + 458.0, 1104.0, 35.0 ], @@ -43111,7 +45030,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6/#0": { "rect": [ 416.0, - 226.0, + 460.0, 1088.0, 31.0 ], @@ -43156,7 +45075,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9": { "rect": [ 408.0, - 575.0, + 224.0, 1104.0, 35.0 ], @@ -43165,7 +45084,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9/#0": { "rect": [ 416.0, - 577.0, + 226.0, 1088.0, 31.0 ], @@ -44242,6 +46161,15 @@ ], "visible": false }, + "SoundtrackStatsPopup/PictureDialog": { + "rect": [ + 0.0, + 0.0, + 800.0, + 500.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel": { "rect": [ 610.0, @@ -44251,12 +46179,156 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 6160.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons/CloseCoverButton": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 4547.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorSpace": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorTitle": { + "rect": [ + 630.0, + 420.0, + 70.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow": { + "rect": [ + 630.0, + 420.0, + 299.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/ChoosePictureButton": { + "rect": [ + 630.0, + 420.0, + 154.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/RemoveCoverButton": { + "rect": [ + 630.0, + 420.0, + 133.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 1389.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow": { + "rect": [ + 630.0, + 420.0, + 283.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverIdField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverSourceButton": { + "rect": [ + 630.0, + 420.0, + 145.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/UseSourceButton": { + "rect": [ + 630.0, + 420.0, + 53.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceTitle": { + "rect": [ + 630.0, + 420.0, + 85.0, + 26.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/CoverFrame": { "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -44264,8 +46336,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -44273,8 +46345,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -44287,7 +46359,25 @@ ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons": { + "rect": [ + 790.0, + 204.0, + 251.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/ChangeCoverButton": { + "rect": [ + 790.0, + 204.0, + 129.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/EditNamesButton": { "rect": [ 790.0, 204.0, @@ -46906,26 +48996,53 @@ "Quiz/NowPlaying": { "rect": [ 64.0, - 64.0, + 56.0, 1088.0, - 320.0 + 328.0 ], "visible": true }, - "Quiz/NowPlaying/SongLabel": { + "Quiz/NowPlaying/CoverFrame": { "rect": [ - 64.0, - 137.0, - 1088.0, + 321.0, + 85.0, + 180.0, + 270.0 + ], + "visible": true + }, + "Quiz/NowPlaying/CoverFrame/CoverPicture": { + "rect": [ + 321.0, + 85.0, + 180.0, + 270.0 + ], + "visible": true + }, + "Quiz/NowPlaying/Titles": { + "rect": [ + 533.0, + 56.0, + 361.0, + 328.0 + ], + "visible": true + }, + "Quiz/NowPlaying/Titles/SongLabel": { + "rect": [ + 533.0, + 232.0, + 361.0, 49.0 ], "visible": true }, - "Quiz/NowPlaying/SoundtrackLabel": { + "Quiz/NowPlaying/Titles/SoundtrackLabel": { "rect": [ - 64.0, - 64.0, - 1088.0, + 533.0, + 159.0, + 361.0, 69.0 ], "visible": true @@ -48022,7 +50139,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6": { "rect": [ 408.0, - 224.0, + 458.0, 1104.0, 35.0 ], @@ -48031,7 +50148,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6/#0": { "rect": [ 416.0, - 226.0, + 460.0, 1088.0, 31.0 ], @@ -48076,7 +50193,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9": { "rect": [ 408.0, - 575.0, + 224.0, 1104.0, 35.0 ], @@ -48085,7 +50202,7 @@ "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9/#0": { "rect": [ 416.0, - 577.0, + 226.0, 1088.0, 31.0 ], @@ -49162,6 +51279,15 @@ ], "visible": false }, + "SoundtrackStatsPopup/PictureDialog": { + "rect": [ + 0.0, + 0.0, + 800.0, + 500.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel": { "rect": [ 610.0, @@ -49171,12 +51297,156 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 6160.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorButtons/CloseCoverButton": { + "rect": [ + 630.0, + 420.0, + 65.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 4547.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorSpace": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverEditorTitle": { + "rect": [ + 630.0, + 420.0, + 70.0, + 35.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/CoverStatus": { + "rect": [ + 630.0, + 420.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow": { + "rect": [ + 630.0, + 420.0, + 299.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/ChoosePictureButton": { + "rect": [ + 630.0, + 420.0, + 154.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/PictureRow/RemoveCoverButton": { + "rect": [ + 630.0, + 420.0, + 133.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 1389.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow": { + "rect": [ + 630.0, + 420.0, + 283.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverIdField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/CoverSourceButton": { + "rect": [ + 630.0, + 420.0, + 145.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceRow/UseSourceButton": { + "rect": [ + 630.0, + 420.0, + 53.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverEditor/SourceTitle": { + "rect": [ + 630.0, + 420.0, + 85.0, + 26.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/CoverFrame": { "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -49184,8 +51454,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -49193,8 +51463,8 @@ "rect": [ 630.0, 204.0, - 144.0, - 200.0 + 140.0, + 210.0 ], "visible": false }, @@ -49207,7 +51477,25 @@ ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons": { + "rect": [ + 790.0, + 204.0, + 251.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/ChangeCoverButton": { + "rect": [ + 790.0, + 204.0, + 129.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/InfoButtons/EditNamesButton": { "rect": [ 790.0, 204.0, diff --git a/tests/fakes/fake_http_transport.gd b/tests/fakes/fake_http_transport.gd index de850d5..ff0094c 100644 --- a/tests/fakes/fake_http_transport.gd +++ b/tests/fakes/fake_http_transport.gd @@ -25,9 +25,12 @@ var requested_paths: Array[String] = [] var requested_methods: Array[int] = [] ## Headers of each request, in the same order as [member requested_paths]. var requested_headers: Array[PackedStringArray] = [] -## The JSON body of each request, empty for none, in the same order as +## The body of each request as text, e.g. its JSON, empty for none, in the same order as ## [member requested_paths]. var requested_bodies: Array[String] = [] +## The body of each request as bytes, e.g. an uploaded picture, in the same order as +## [member requested_paths]. +var requested_raw_bodies: Array[PackedByteArray] = [] ## How many tokens have been asked for. var token_requests: int = 0 ## The JSON body of the last token request. @@ -110,20 +113,22 @@ func request( method: int, on_complete: Callable, headers: PackedStringArray = PackedStringArray(), - body: String = "", + body: PackedByteArray = PackedByteArray(), ) -> void: var path: String = url.trim_prefix(Settings.default_path + MusicServerClient.API_PATH) if path == "/token" && method == HTTPClient.METHOD_POST: token_requests += 1 if _held_paths.has(path): - _held_paths[path].append(_answer_token_request.bind(body, on_complete)) + _held_paths[path].append( + _answer_token_request.bind(body.get_string_from_utf8(), on_complete)) return - _answer_token_request(body, on_complete) + _answer_token_request(body.get_string_from_utf8(), on_complete) return requested_paths.append(path) requested_methods.append(method) requested_headers.append(headers) - requested_bodies.append(body) + requested_bodies.append(body.get_string_from_utf8()) + requested_raw_bodies.append(body) if !OPEN_PATHS.has(path.get_slice("?", 0)) && !_has_valid_token(headers): on_complete.call(401, JSON.stringify({"error": "Unauthorized"}).to_utf8_buffer(), PackedStringArray()) diff --git a/tests/fakes/ui_tour.gd b/tests/fakes/ui_tour.gd index ae493c6..0e93c47 100644 --- a/tests/fakes/ui_tour.gd +++ b/tests/fakes/ui_tour.gd @@ -15,7 +15,8 @@ const SOUNDTRACKS: Array[String] = [ "Donkey Kong Country 2", "Lufia II", "Illusion of Gaia", ] const SONG_INFO: Dictionary = { - "SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time", + "SongNo": 0, "Soundtrack": "Chrono Trigger", "SoundtrackUuid": "soundtrack-0", + "Song": "Corridors of Time", } const SYNC_PROGRESS: Dictionary = {"progress": 40, "time_spent": "00:00:02"} const SYNC_RESULT: Dictionary = { @@ -48,6 +49,8 @@ const JAPANESE_SONGS: Array[String] = [ ## The character pictures the fake server hands out. const CHARACTERS_DIR: String = "res://characters" +## Small copies of covers from the MusicServer, 2:3 like all its covers. +const COVERS_DIR: String = "res://tests/data/covers" ## Popups in the order the tour opens them: the state name, then the popup's node. The ## state names are what the popups were first called, so screenshots taken before they @@ -91,12 +94,18 @@ static func make_server() -> FakeHttpTransport: server.respond_bytes("/sync", PackedByteArray()) server.respond_json("/sync/progress", SYNC_PROGRESS) server.respond_json("/statistics/soundtracks", soundtrack_statistics()) - # The character pictures stand in for covers - var pictures: Array[String] = character_names() - for i: int in SOUNDTRACKS.size(): + # Real covers from the server, made smaller, for every other soundtrack + for i: int in range(0, SOUNDTRACKS.size(), 2): server.respond_bytes("/soundtracks/soundtrack-%d/cover" % i, - FileAccess.get_file_as_bytes(CHARACTERS_DIR.path_join( - pictures[i % pictures.size()]))) + FileAccess.get_file_as_bytes(COVERS_DIR.path_join("cover_%d.jpg" % (i >> 1)))) + # The soundtrack list tells the quiz which soundtracks have covers + var soundtracks: Array[Dictionary] = [] + for soundtrack: Dictionary in soundtrack_statistics(): + soundtracks.append({"uuid": soundtrack.soundtrack_uuid, + "name": soundtrack.soundtrack_name, + "cover_modified": soundtrack.get("cover_modified", 0), + "alternative_names": soundtrack.get("alternative_names", [])}) + server.respond_json("/soundtracks", soundtracks) server.respond_json("/version/history", SERVER_HISTORY) return server @@ -307,6 +316,10 @@ func run(on_state: Callable) -> void: stats_popup.new_name_field.text = "Chrono" await _settle() await on_state.call("popup_SoundtrackStats_names", stats_popup) + stats_popup.change_cover() + stats_popup.cover_id_field.text = "5248061" + await _settle() + await on_state.call("popup_SoundtrackStats_cover", stats_popup) stats_popup.stop_editing() stats_popup.close() _main.show_tab(&"quiz") diff --git a/tests/integration/test_animations.gd b/tests/integration/test_animations.gd index 6c82f21..d5f48e6 100644 --- a/tests/integration/test_animations.gd +++ b/tests/integration/test_animations.gd @@ -206,7 +206,7 @@ func test_every_button_bounces() -> void: func test_helpers_do_nothing_with_reduced_animations() -> void: Settings.reduce_animations = true - var label: Label = _quiz.get_node("NowPlaying/SongLabel") + var label: Label = _quiz.get_node("NowPlaying/Titles/SongLabel") Anim.pop(label) Anim.fade_in(label) diff --git a/tests/integration/test_continue_quiz.gd b/tests/integration/test_continue_quiz.gd index 2d5e830..7c85481 100644 --- a/tests/integration/test_continue_quiz.gd +++ b/tests/integration/test_continue_quiz.gd @@ -55,8 +55,8 @@ func _play_a_little() -> Array[PlayerObject]: func _now_playing() -> Array[String]: var now_playing: Node = _quiz.get_node("NowPlaying") - return [(now_playing.get_node("SoundtrackLabel") as Label).text, - (now_playing.get_node("SongLabel") as Label).text] + return [(now_playing.get_node("Titles/SoundtrackLabel") as Label).text, + (now_playing.get_node("Titles/SongLabel") as Label).text] # Saving diff --git a/tests/integration/test_covers_and_names.gd b/tests/integration/test_covers_and_names.gd index f8234dc..0c6bd8f 100644 --- a/tests/integration/test_covers_and_names.gd +++ b/tests/integration/test_covers_and_names.gd @@ -1,6 +1,7 @@ extends MainWindowTestSuite -## Soundtrack covers on the statistics cards and in the popup, loaded for the cards on -## screen, and editing a soundtrack's alternative names, which search then finds it by. +## Soundtrack covers on the statistics cards, in the popup and next to the answer in the +## quiz, loaded for the cards on screen; changing a cover from the popup; and editing a +## soundtrack's alternative names, which search then finds it by. const STATISTICS: Array[Dictionary] = [ {"soundtrack_uuid": "st-1", "soundtrack_name": "Chrono Trigger", "soundtrack_played": 5, @@ -21,6 +22,13 @@ func _before_window_starts() -> void: var image: Image = Image.create(6, 8, false, Image.FORMAT_RGB8) _png = image.save_png_to_buffer() _server.respond_bytes("/soundtracks/st-1/cover", _png) + # The soundtrack list tells the quiz which soundtracks have covers + _server.respond_json("/soundtracks", [ + {"uuid": "st-1", "name": "Chrono Trigger", "cover_modified": 100}, + {"uuid": "st-2", "name": "Terranigma"}, + ]) + _next_song_is({"SongNo": 0, "Soundtrack": "Chrono Trigger", "SoundtrackUuid": "st-1", + "Song": "Corridors of Time"}) func before_test() -> void: @@ -185,3 +193,200 @@ func test_search_popup_finds_soundtracks_by_alternative_names() -> void: assert_array(search.shown_names()).contains(["Chrono Trigger"]) var row: PanelContainer = search.search_results.get_child(0) assert_str((row.get_child(0) as RichTextLabel).get_parsed_text()).contains("CT") + + +func _open_cover_menu() -> void: + await _open_statistics() + _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) + _popup.change_cover() + + +# The server's answer for the soundtrack once its cover changed +func _cover_time_becomes(modified: int) -> void: + _server.respond_json("/soundtracks/st-1", {"uuid": "st-1", "name": "Chrono Trigger", + "cover_modified": modified, "songs": []}) + + +func _picture_file(bytes: PackedByteArray) -> String: + var path: String = create_temp_dir("pictures_%d" % randi()).path_join("cover.jpg") + var file: FileAccess = FileAccess.open(path, FileAccess.WRITE) + file.store_buffer(bytes) + file.close() + return path + + +func _downloads(path: String) -> int: + var count: int = 0 + for i: int in _server.requested_paths.size(): + if _server.requested_paths[i] == path \ + && _server.requested_methods[i] == HTTPClient.METHOD_GET: + count += 1 + return count + + +func _now_playing() -> NowPlaying: + return _quiz.get_node("NowPlaying") + + +func test_cover_menu_opens_in_place_of_the_songs() -> void: + await _open_cover_menu() + + assert_bool(_popup.cover_editor.visible).is_true() + assert_bool(_popup.song_scroll.visible).is_false() + assert_bool(_popup.edit_names_button.disabled).is_true() + assert_bool(_popup.remove_cover_button.disabled).is_false() + + _popup.stop_editing() + + assert_bool(_popup.cover_editor.visible).is_false() + assert_bool(_popup.song_scroll.visible).is_true() + + +func test_soundtrack_without_a_cover_has_nothing_to_remove() -> void: + await _open_statistics() + _popup.show_soundtrack(_card("Terranigma").soundtrack) + + _popup.change_cover() + + assert_bool(_popup.remove_cover_button.disabled).is_true() + + +func test_uploaded_picture_becomes_the_cover() -> void: + var bytes: PackedByteArray = FileAccess.get_file_as_bytes( + "res://tests/data/covers/cover_0.jpg") + _cover_time_becomes(200) + await _open_cover_menu() + + _popup.upload_picture(_picture_file(bytes)) + + var index: int = _server.requested_methods.find(HTTPClient.METHOD_PUT) + assert_str(_server.requested_paths[index]).is_equal("/soundtracks/st-1/cover") + assert_array(_server.requested_raw_bodies[index]).is_equal(bytes) + assert_array(_server.requested_headers[index]).contains(["Content-Type: image/jpeg"]) + # The new cover is downloaded again for the popup and the card + assert_int(_card("Chrono Trigger").soundtrack.cover_modified).is_equal(200) + await _runner.simulate_frames(2) + # Downloaded once more, for the popup and the card together + assert_int(_downloads("/soundtracks/st-1/cover")).is_equal(2) + assert_object(_card("Chrono Trigger").cover_picture.texture).is_not_null() + assert_str(_popup.cover_status.text).is_equal("The new cover is saved.") + + +func test_file_that_is_not_a_picture_is_not_uploaded() -> void: + await _open_cover_menu() + + _popup.upload_picture(_picture_file("not a picture".to_utf8_buffer())) + + assert_int(_server.requested_methods.count(HTTPClient.METHOD_PUT)).is_equal(0) + assert_str(_popup.cover_status.text).contains("JPEG, PNG and WebP") + + +func test_cover_picked_by_id() -> void: + _server.respond_bytes("/soundtracks/st-1/cover/source", PackedByteArray(), 204) + _cover_time_becomes(300) + await _open_cover_menu() + + _popup.cover_source_button.select(1) + _popup.cover_id_field.text = " 21 " + _popup.use_cover_source() + + var index: int = _server.requested_paths.find("/soundtracks/st-1/cover/source") + assert_int(_server.requested_methods[index]).is_equal(HTTPClient.METHOD_PUT) + var body: Dictionary = JSON.parse_string(_server.requested_bodies[index]) + assert_str(body.source).is_equal("anilist") + assert_str(body.id).is_equal("21") + assert_int(_popup.soundtrack.cover_modified).is_equal(300) + + +func test_no_id_asks_nothing() -> void: + await _open_cover_menu() + + _popup.use_cover_source() + + assert_int(_server.count("/soundtracks/st-1/cover/source")).is_equal(0) + + +func test_removed_cover_shows_the_initials() -> void: + _cover_time_becomes(0) + await _open_cover_menu() + + _popup.remove_cover() + + var index: int = _server.requested_methods.find(HTTPClient.METHOD_DELETE) + assert_str(_server.requested_paths[index]).is_equal("/soundtracks/st-1/cover") + assert_bool(_popup.cover_placeholder.visible).is_true() + assert_bool(_card("Chrono Trigger").cover_placeholder.visible).is_true() + assert_bool(FileAccess.file_exists(CoverCache.folder().path_join("st-1"))).is_false() + assert_bool(_popup.remove_cover_button.disabled).is_true() + + +func test_refused_change_shows_the_servers_reason() -> void: + _server.respond_json("/soundtracks/st-1/cover/source", + {"error": "covers are not enabled on this server"}, 503) + await _open_cover_menu() + _popup.cover_id_field.text = "21" + + _popup.use_cover_source() + + assert_str(_popup.cover_status.text).is_equal( + "Could not change the cover: covers are not enabled on this server.") + assert_bool(_popup.use_source_button.disabled).is_false() + + +func test_answer_shows_the_soundtracks_cover() -> void: + await _play_first_song() + + assert_bool(_now_playing().has_cover()).is_false() + + _press("SongControls/ShowAnswerButton") + + assert_bool(_now_playing().has_cover()).is_true() + await _runner.simulate_frames(1) + # The cover sits just left of the titles, and the two are centered together + var area: Rect2 = _now_playing().get_global_rect() + var cover: Rect2 = _now_playing().cover_frame.get_global_rect() + var titles: Rect2 = _now_playing().titles.get_global_rect() + assert_float(titles.position.x - cover.end.x).is_equal(32.0) + assert_float(absf((cover.position.x - area.position.x) - (area.end.x - titles.end.x))) \ + .is_less_equal(1.0) + # Up and down, the cover is centered between the top bar and the song list, and the + # titles on the cover + assert_float(absf((cover.position.y - area.position.y) - (area.end.y - cover.end.y))) \ + .is_less_equal(1.0) + var text_middle: float = (_now_playing().soundtrack_label.get_global_rect().position.y + + _now_playing().song_label.get_global_rect().end.y) / 2.0 + assert_float(absf(text_middle - cover.get_center().y)).is_less_equal(1.0) + + +func test_answer_without_a_cover_shows_only_the_titles() -> void: + _view.covers.set_cover_times({"st-1": 0}) + await _play_first_song() + + _press("SongControls/ShowAnswerButton") + + assert_bool(_now_playing().has_cover()).is_false() + assert_int(_server.count("/soundtracks/st-1/cover")).is_equal(0) + + +func test_next_song_hides_the_cover_until_its_answer() -> void: + await _play_first_song() + _press("SongControls/ShowAnswerButton") + + _press("SongControls/NextButton") + # The playing song is stopped first, which takes half a second + await await_millis(700) + + assert_int(Playlist.currently_playing_song).is_equal(1) + assert_bool(_now_playing().has_cover()).is_false() + + +func test_changed_cover_shows_in_the_quiz_too() -> void: + await _play_first_song() + _press("SongControls/ShowAnswerButton") + _cover_time_becomes(0) + _popup.show_soundtrack(SoundtrackWithSongs.from_dict(STATISTICS[0])) + _popup.change_cover() + + _popup.remove_cover() + + assert_bool(_now_playing().has_cover()).is_false() diff --git a/tests/integration/test_main_window.gd b/tests/integration/test_main_window.gd index aa5e2c3..b833d69 100644 --- a/tests/integration/test_main_window.gd +++ b/tests/integration/test_main_window.gd @@ -33,7 +33,7 @@ func test_next_plays_preloaded_song_and_fetches_another() -> void: func test_title_is_hidden_until_answer_is_shown() -> void: - var soundtrack_label: Label = _quiz.get_node("NowPlaying/SoundtrackLabel") + var soundtrack_label: Label = _quiz.get_node("NowPlaying/Titles/SoundtrackLabel") _press("SongControls/NextButton") await _runner.simulate_frames(2) @@ -224,7 +224,7 @@ func test_show_answer_before_first_song_does_not_reveal_next_song() -> void: await _play_first_song() - assert_str((_quiz.get_node("NowPlaying/SoundtrackLabel") as Label).text) \ + assert_str((_quiz.get_node("NowPlaying/Titles/SoundtrackLabel") as Label).text) \ .is_equal("????????") diff --git a/tests/integration/test_now_playing.gd b/tests/integration/test_now_playing.gd index f813dfd..26efb12 100644 --- a/tests/integration/test_now_playing.gd +++ b/tests/integration/test_now_playing.gd @@ -11,7 +11,7 @@ func before_test() -> void: func _label(label_name: String) -> Label: - return _now_playing.get_node(label_name) + return _now_playing.get_node("Titles/" + label_name) func test_starts_empty() -> void: @@ -32,3 +32,16 @@ func test_soundtrack_is_above_song() -> void: assert_float(_label("SoundtrackLabel").get_global_rect().end.y) \ .is_less_equal(_label("SongLabel").get_global_rect().position.y) + + +func test_long_titles_keep_to_two_lines_each() -> void: + _now_playing.size = Vector2(1088, 328) + _now_playing.show_song("Final Fantasy Dissidia 012 Duodecim (Sista Fantasin " + + "Meningsskiljaktigheter 012 Tolv) Final Mix Special Edition", + "Cosmos (Arrangement) - Main Theme of the Warriors of Light, Long Version") + await _runner.simulate_frames(2) + + for label_name: String in ["SoundtrackLabel", "SongLabel"]: + assert_int(_label(label_name).get_visible_line_count()).is_less_equal(2) + assert_float(_label("SongLabel").get_global_rect().end.y) \ + .is_less_equal(_now_playing.get_global_rect().end.y) diff --git a/tests/integration/test_offline_mode.gd b/tests/integration/test_offline_mode.gd index bb366b0..049a372 100644 --- a/tests/integration/test_offline_mode.gd +++ b/tests/integration/test_offline_mode.gd @@ -54,8 +54,8 @@ func _next() -> void: func _now_playing() -> Array[String]: var now_playing: Node = _quiz.get_node("NowPlaying") - return [(now_playing.get_node("SoundtrackLabel") as Label).text, - (now_playing.get_node("SongLabel") as Label).text] + return [(now_playing.get_node("Titles/SoundtrackLabel") as Label).text, + (now_playing.get_node("Titles/SongLabel") as Label).text] func test_picking_a_folder_makes_no_server_requests() -> void: diff --git a/tests/integration/test_scene_split.gd b/tests/integration/test_scene_split.gd index 74afe90..1f7d4a0 100644 --- a/tests/integration/test_scene_split.gd +++ b/tests/integration/test_scene_split.gd @@ -121,12 +121,12 @@ func test_clicking_quiz_buttons_plays_and_shows_answer() -> void: await _click_at(_center(_quiz.get_node("SongControls/NextButton"))) await _runner.simulate_frames(2) assert_int(Playlist.currently_playing_song).is_equal(0) - assert_str((_quiz.get_node("NowPlaying/SoundtrackLabel") as Label).text) \ + assert_str((_quiz.get_node("NowPlaying/Titles/SoundtrackLabel") as Label).text) \ .is_not_equal("Chrono Trigger") await _click_at(_center(_quiz.get_node("SongControls/ShowAnswerButton"))) - assert_str((_quiz.get_node("NowPlaying/SoundtrackLabel") as Label).text) \ + assert_str((_quiz.get_node("NowPlaying/Titles/SoundtrackLabel") as Label).text) \ .is_equal("Chrono Trigger") diff --git a/tests/unit/test_music_server_client.gd b/tests/unit/test_music_server_client.gd index 09805db..1ee329c 100644 --- a/tests/unit/test_music_server_client.gd +++ b/tests/unit/test_music_server_client.gd @@ -639,7 +639,7 @@ class _HeaderTransport: _method: int, on_complete: Callable, _request_headers: PackedStringArray = PackedStringArray(), - _body: String = "", + _body: PackedByteArray = PackedByteArray(), ) -> void: on_complete.call(200, JSON.stringify({"token": "t"} if _url.ends_with("/token") else []) .to_utf8_buffer(), _headers) @@ -659,7 +659,7 @@ class _RecordingTransport: _method: int, on_complete: Callable, _headers: PackedStringArray = PackedStringArray(), - _body: String = "", + _body: PackedByteArray = PackedByteArray(), ) -> void: _urls.append(url) if url.ends_with("/token"):