From ef6ac8f1fd41ce13f8b0dff0a49ec87e74920473 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 14 Oct 2026 22:06:30 +0200 Subject: [PATCH] Show soundtrack covers and edit alternative names (#49) Covers: statistics cards show the soundtrack's cover on the left, and the statistics popup shows it large. Until it arrives, and without a cover, the soundtrack's initials stand in. Covers load only for cards on or near the screen, three at a time, and are cached in covers/ next to the settings (CoverCache), downloaded again only when cover_modified changes. The index and file handling are shared with CharacterCache in a new FileCache. Alternative names: the popup shows them and has an editor (add, remove, save) that sends PUT /soundtracks/{uuid}/alternative-names. The statistics search and the Search popup find soundtracks by them; Search shows the matching name after the real one. Search now loads /soundtracks, falling back to the plain list. The API types are regenerated from the server's spec, with a SoundtrackCover theme style in every theme. Closes #49 Co-Authored-By: Claude Opus 5.5 --- common/character_cache.gd | 76 +- common/cover_cache.gd | 95 + common/cover_cache.gd.uid | 1 + common/file_cache.gd | 117 + common/file_cache.gd.uid | 1 + common/soundtrack_search.gd | 41 +- main/main_window.gd | 6 + popups/SoundtrackStatsPopup.tscn | 149 +- popups/search_popup.gd | 47 +- popups/soundtrack_stats_popup.gd | 166 +- server/generated/alternative_names.gd | 22 + server/generated/alternative_names.gd.uid | 1 + server/generated/cover_source.gd | 26 + server/generated/cover_source.gd.uid | 1 + server/generated/soundtrack.gd | 8 + server/generated/soundtrack_with_song_list.gd | 8 + server/generated/soundtrack_with_songs.gd | 8 + server/music_server_client.gd | 32 +- server/openapi.json | 589 ++ statistics/SoundtrackCard.tscn | 51 +- statistics/soundtrack_card.gd | 43 + statistics/statistics_view.gd | 53 +- tests/data/layout_baseline.json | 7831 +++++++++++++---- tests/fakes/fake_http_transport.gd | 4 + tests/fakes/ui_tour.gd | 16 + tests/integration/test_covers_and_names.gd | 187 + .../integration/test_covers_and_names.gd.uid | 1 + tests/integration/test_theme.gd | 1 + tests/unit/test_api_types.gd | 5 +- tests/unit/test_cover_cache.gd | 142 + tests/unit/test_cover_cache.gd.uid | 1 + tests/unit/test_soundtrack_search.gd | 22 + themes/README.md | 1 + themes/christmas_theme.tres | 10 + themes/main_theme.tres | 10 + themes/nord_theme.tres | 10 + themes/original_web_theme.tres | 10 + 37 files changed, 8029 insertions(+), 1763 deletions(-) create mode 100644 common/cover_cache.gd create mode 100644 common/cover_cache.gd.uid create mode 100644 common/file_cache.gd create mode 100644 common/file_cache.gd.uid create mode 100644 server/generated/alternative_names.gd create mode 100644 server/generated/alternative_names.gd.uid create mode 100644 server/generated/cover_source.gd create mode 100644 server/generated/cover_source.gd.uid create mode 100644 tests/integration/test_covers_and_names.gd create mode 100644 tests/integration/test_covers_and_names.gd.uid create mode 100644 tests/unit/test_cover_cache.gd create mode 100644 tests/unit/test_cover_cache.gd.uid diff --git a/common/character_cache.gd b/common/character_cache.gd index e292ab6..0818e22 100644 --- a/common/character_cache.gd +++ b/common/character_cache.gd @@ -14,15 +14,15 @@ extends RefCounted ## can't be reached at all, the cached pictures are used as they are. const FOLDER_NAME: String = "characters" -const INDEX_FILE_NAME: String = "index.json" +const INDEX_FILE_NAME: String = FileCache.INDEX_FILE_NAME ## How long a picture is used before downloading it again, when the server can't tell ## whether it changed. const MAX_AGE_SECONDS: int = 7 * 24 * 60 * 60 ## The change time of a picture when the server can't tell it. -const UNKNOWN_TIME: int = -1 +const UNKNOWN_TIME: int = FileCache.UNKNOWN_TIME -# What index.json holds: name -> {"modified": int, "downloaded": int} -var _index: Dictionary = {} +# The pictures on disk and their index +var _files: FileCache = FileCache.new(FOLDER_NAME) # When the server last changed each picture on the list, or UNKNOWN_TIME var _modified: Dictionary[String, int] = {} # True when the server could not be reached and only cached pictures are shown @@ -38,7 +38,7 @@ static func folder() -> String: ## [code]Array[String][/code]. Cached pictures of characters no longer on the server are ## deleted. func load_list(on_done: Callable) -> void: - _load_index() + _files.load_index() _cache_only = false var details_fetched: Callable = func details_fetched(details: Array) -> void: _modified.clear() @@ -51,9 +51,8 @@ func load_list(on_done: Callable) -> void: ## Calls [param on_done] with the picture of [param character_name] as an [Image], from ## disk when the cached one is still fresh, otherwise from the server. func fetch_image(character_name: String, on_done: Callable) -> void: - var path: String = _file_path(character_name) - if (_cache_only || _is_fresh(character_name)) && FileAccess.file_exists(path): - var cached: Image = CharacterCard.image_from_bytes(FileAccess.get_file_as_bytes(path)) + if (_cache_only || _is_fresh(character_name)) && _files.has_file(character_name): + var cached: Image = CharacterCard.image_from_bytes(_files.read(character_name)) if cached != null: on_done.call(cached) return @@ -63,7 +62,7 @@ func fetch_image(character_name: String, on_done: Callable) -> void: if picture == null: GameLog.warning("Could not load the picture of ", character_name) return - _store(character_name, bytes) + _files.store(character_name, bytes, _modified.get(character_name, UNKNOWN_TIME)) on_done.call(picture) MusicServerClient.fetch_character_image(character_name, image_fetched) @@ -84,71 +83,24 @@ func _load_cached_only(on_done: Callable) -> void: GameLog.warning("Could not get the characters from the server, showing the cached ones") _cache_only = true var names: Array[String] = [] - for character_name: String in _index: - if FileAccess.file_exists(_file_path(character_name)): + for character_name: String in _files.keys(): + if _files.has_file(character_name): names.append(character_name) names.sort_custom(func(a: String, b: String) -> bool: return a.naturalnocasecmp_to(b) < 0) on_done.call(names) func _list_loaded(on_done: Callable) -> void: - var removed: bool = false - for character_name: String in _index.keys(): - if !_modified.has(character_name): - DirAccess.remove_absolute(_file_path(character_name)) - _index.erase(character_name) - removed = true - if removed: - _save_index() + _files.keep_only(_modified) var names: Array[String] = [] names.assign(_modified.keys()) on_done.call(names) func _is_fresh(character_name: String) -> bool: - if !_index.has(character_name): + if !_files.keys().has(character_name): return false - var entry: Dictionary = _index[character_name] var modified: int = _modified.get(character_name, UNKNOWN_TIME) if modified != UNKNOWN_TIME: - return int(entry.get("modified", UNKNOWN_TIME)) == modified - var age: float = Time.get_unix_time_from_system() - float(entry.get("downloaded", 0)) - return age < MAX_AGE_SECONDS - - -func _store(character_name: String, bytes: PackedByteArray) -> void: - DirAccess.make_dir_recursive_absolute(folder()) - var file: FileAccess = FileAccess.open(_file_path(character_name), FileAccess.WRITE) - if file == null: - GameLog.warning("Could not cache the picture of ", character_name, ": ", - error_string(FileAccess.get_open_error())) - return - file.store_buffer(bytes) - file.close() - _index[character_name] = { - "modified": _modified.get(character_name, UNKNOWN_TIME), - "downloaded": int(Time.get_unix_time_from_system()), - } - _save_index() - - -func _load_index() -> void: - _index = {} - var path: String = folder().path_join(INDEX_FILE_NAME) - if FileAccess.file_exists(path): - var parsed: Variant = JSON.parse_string(FileAccess.get_file_as_string(path)) - if parsed is Dictionary: - _index = parsed - - -func _save_index() -> void: - var file: FileAccess = FileAccess.open(folder().path_join(INDEX_FILE_NAME), - FileAccess.WRITE) - if file != null: - file.store_string(JSON.stringify(_index, "\t", true)) - - -# The server's names are plain file names, but they are made safe anyway since they -# become paths on this computer -func _file_path(character_name: String) -> String: - return folder().path_join(character_name.validate_filename()) + return _files.modified(character_name) == modified + return _files.age(character_name) < MAX_AGE_SECONDS diff --git a/common/cover_cache.gd b/common/cover_cache.gd new file mode 100644 index 0000000..dd83637 --- /dev/null +++ b/common/cover_cache.gd @@ -0,0 +1,95 @@ +class_name CoverCache +extends RefCounted +## Soundtrack covers kept on disk in the [code]covers[/code] folder next to the settings, +## by soundtrack UUID, so each one is downloaded only when it changed on the server (its +## [code]cover_modified[/code] time). See [FileCache]. +## +## 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. + +const FOLDER_NAME: String = "covers" +## How many covers are downloaded at the same time. +const MAX_DOWNLOADS: int = 3 + +# The covers on disk and their index +var _files: FileCache = FileCache.new(FOLDER_NAME) +# Covers waiting to be downloaded: [uuid, modified] +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 + + +func _init() -> void: + _files.load_index() + + +## The folder the covers are kept in. +static func folder() -> String: + return Settings.config_dir().path_join(FOLDER_NAME) + + +## 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: + var kept: Dictionary = {} + for uuid: String in covers: + if int(covers[uuid]) > 0: + kept[uuid] = true + _files.keep_only(kept) + + +## 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 +## ([param modified] 0) or the download fails. +func fetch_cover(uuid: String, modified: int, on_done: Callable) -> void: + if modified <= 0 || uuid.is_empty(): + return + if _files.has_file(uuid) && _files.modified(uuid) == modified: + var cached: Image = CharacterCard.image_from_bytes(_files.read(uuid)) + if cached != null: + on_done.call(cached) + return + if _callbacks.has(uuid): + _callbacks[uuid].append(on_done) + return + _callbacks[uuid] = [on_done] + _waiting.append([uuid, modified]) + _download_next() + + +## How many covers are waiting for their turn or downloading. +func busy() -> int: + return _callbacks.size() + + +func _download_next() -> void: + while _downloading < MAX_DOWNLOADS && !_waiting.is_empty(): + var next: Array = _waiting.pop_front() + var uuid: String = next[0] + _downloading += 1 + MusicServerClient.fetch_soundtrack_cover(uuid, + _on_downloaded.bind(uuid, next[1]), _on_failed.bind(uuid)) + + +func _on_downloaded(bytes: PackedByteArray, uuid: String, modified: int) -> void: + _downloading -= 1 + var cover: Image = CharacterCard.image_from_bytes(bytes) + if cover == null: + GameLog.warning("The cover of soundtrack ", uuid, " could not be read") + else: + _files.store(uuid, bytes, modified) + for on_done: Callable in _callbacks.get(uuid, []): + # A card may be gone by the time its cover arrives + if cover != null && on_done.is_valid(): + on_done.call(cover) + _callbacks.erase(uuid) + _download_next() + + +# A 404 means the server has no cover for it yet +func _on_failed(uuid: String) -> void: + _downloading -= 1 + _callbacks.erase(uuid) + _download_next() diff --git a/common/cover_cache.gd.uid b/common/cover_cache.gd.uid new file mode 100644 index 0000000..ca0f7a6 --- /dev/null +++ b/common/cover_cache.gd.uid @@ -0,0 +1 @@ +uid://djxm2kkq8hol1 diff --git a/common/file_cache.gd b/common/file_cache.gd new file mode 100644 index 0000000..ea77fcc --- /dev/null +++ b/common/file_cache.gd @@ -0,0 +1,117 @@ +class_name FileCache +extends RefCounted +## Downloaded files kept in a folder next to the settings, with an [code]index.json[/code] +## that records for each file when the server last changed it and when it was +## downloaded: +## [codeblock] +## {"Crono.png": {"modified": 1730000000, "downloaded": 1730100000}} +## [/codeblock] +## Used by [CharacterCache] and [CoverCache], which decide when a file is fresh. + +const INDEX_FILE_NAME: String = "index.json" +## The change time of a file when the server can't tell it. +const UNKNOWN_TIME: int = -1 + +## The folder's name in [method Settings.config_dir]. +var folder_name: String + +# What index.json holds: key -> {"modified": int, "downloaded": int} +var _index: Dictionary = {} + + +func _init(new_folder_name: String) -> void: + folder_name = new_folder_name + + +## The folder the files are kept in. +func folder() -> String: + return Settings.config_dir().path_join(folder_name) + + +## Reads [code]index.json[/code]. A missing or broken index starts empty. +func load_index() -> void: + _index = {} + var path: String = folder().path_join(INDEX_FILE_NAME) + if FileAccess.file_exists(path): + var parsed: Variant = JSON.parse_string(FileAccess.get_file_as_string(path)) + if parsed is Dictionary: + _index = parsed + + +## The keys of the files in the index. +func keys() -> Array: + return _index.keys() + + +## Whether the file for [param key] is in the index and on disk. +func has_file(key: String) -> bool: + return _index.has(key) && FileAccess.file_exists(file_path(key)) + + +## When the server last changed the file for [param key], as recorded when it was +## downloaded, or [constant UNKNOWN_TIME]. +func modified(key: String) -> int: + return int(_index.get(key, {}).get("modified", UNKNOWN_TIME)) + + +## How many seconds ago the file for [param key] was downloaded. +func age(key: String) -> float: + return Time.get_unix_time_from_system() - float(_index.get(key, {}).get("downloaded", 0)) + + +## The file for [param key]. +func read(key: String) -> PackedByteArray: + return FileAccess.get_file_as_bytes(file_path(key)) + + +## Writes [param bytes] as the file for [param key], last changed on the server at +## [param modified_time], and saves the index. Returns false if it could not be written. +func store(key: String, bytes: PackedByteArray, modified_time: int) -> bool: + DirAccess.make_dir_recursive_absolute(folder()) + var file: FileAccess = FileAccess.open(file_path(key), FileAccess.WRITE) + if file == null: + GameLog.warning("Could not cache ", key, " in ", folder_name, ": ", + error_string(FileAccess.get_open_error())) + return false + file.store_buffer(bytes) + file.close() + _index[key] = { + "modified": modified_time, + "downloaded": int(Time.get_unix_time_from_system()), + } + save_index() + return true + + +## Deletes the file for [param key] and its entry, and saves the index. +func remove(key: String) -> void: + DirAccess.remove_absolute(file_path(key)) + if _index.erase(key): + save_index() + + +## Deletes every file whose key is not in [param kept], e.g. files of things the server +## no longer has. +func keep_only(kept: Dictionary) -> void: + var removed: bool = false + for key: String in _index.keys(): + if !kept.has(key): + DirAccess.remove_absolute(file_path(key)) + _index.erase(key) + removed = true + if removed: + save_index() + + +func save_index() -> void: + DirAccess.make_dir_recursive_absolute(folder()) + var file: FileAccess = FileAccess.open(folder().path_join(INDEX_FILE_NAME), + FileAccess.WRITE) + if file != null: + file.store_string(JSON.stringify(_index, "\t", true)) + + +## Where the file for [param key] is. Keys come from the server, so they are made safe +## as file names. +func file_path(key: String) -> String: + return folder().path_join(key.validate_filename()) diff --git a/common/file_cache.gd.uid b/common/file_cache.gd.uid new file mode 100644 index 0000000..ee81db5 --- /dev/null +++ b/common/file_cache.gd.uid @@ -0,0 +1 @@ +uid://0jfq23w54ihw diff --git a/common/soundtrack_search.gd b/common/soundtrack_search.gd index 06ed5f9..60604e1 100644 --- a/common/soundtrack_search.gd +++ b/common/soundtrack_search.gd @@ -5,23 +5,36 @@ extends RefCounted ## letters also find the letters in order with others in between. ## ## Used by the Search popup and the statistics view, so both find the same soundtracks. +## +## A soundtrack is also found by its alternative names, e.g. "FF7" for "Final Fantasy +## VII". The results are always the real names; [method matched_alternative] tells which +## alternative name matched. -## Each name as a Dictionary with its original, lowercase and cleaned form. +## Each searchable name as a Dictionary: the soundtrack's real name ("original"), the name +## searched in ("text"), and its lowercase and cleaned forms. var _names: Array[Dictionary] = [] +# The alternative name that matched in the last search, by real name +var _matched_alternatives: Dictionary = {} ## Compiled patterns for the letters-in-order search, by cleaned search term. var _regex_cache: Dictionary = {} -## Replaces the names to search in. -func set_names(names: Array) -> void: +## Replaces the names to search in. [param alternatives] has the other names of a +## soundtrack, by its real name. +func set_names(names: Array, alternatives: Dictionary = {}) -> void: _names.clear() + _matched_alternatives.clear() for soundtrack: String in names: - _names.append({ - "original": soundtrack, - "lowercase": soundtrack.to_lower(), - "cleaned": clean_term(soundtrack), - }) + _add_name(soundtrack, soundtrack) + for alternative: String in alternatives.get(soundtrack, []): + _add_name(soundtrack, alternative) + + +## The alternative name of [param soundtrack_name] that matched in the last +## [method find_matches], or an empty String when its real name matched. +func matched_alternative(soundtrack_name: String) -> String: + return _matched_alternatives.get(soundtrack_name, "") ## Returns the names matching [param search_text], best matches first: plain @@ -41,11 +54,14 @@ func find_matches(search_text: String) -> Array[String]: func(entry: Dictionary) -> bool: return regex.search(entry.cleaned) != null) var matches: Array[String] = [] + _matched_alternatives.clear() for matches_pass: Callable in passes: for entry: Dictionary in _names: if !seen.has(entry.original) && matches_pass.call(entry): seen[entry.original] = true matches.append(entry.original) + if entry.text != entry.original: + _matched_alternatives[entry.original] = entry.text return matches @@ -107,6 +123,15 @@ static func clean_term(term: String) -> String: .replace("'n", "and").to_lower() +func _add_name(soundtrack: String, text: String) -> void: + _names.append({ + "original": soundtrack, + "text": text, + "lowercase": text.to_lower(), + "cleaned": clean_term(text), + }) + + # Compiles the letters-in-order pattern for [param search_term], once per term func _compile_regex_cached(search_term: String) -> RegEx: if not search_term in _regex_cache: diff --git a/main/main_window.gd b/main/main_window.gd index f99be54..879675d 100644 --- a/main/main_window.gd +++ b/main/main_window.gd @@ -47,6 +47,12 @@ func _ready() -> void: quiz.server_changed.connect(_on_music_source_changed) 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 + # 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()) # A saved quiz may have been played from a music folder top_bar.update_offline_state() diff --git a/popups/SoundtrackStatsPopup.tscn b/popups/SoundtrackStatsPopup.tscn index 95c8e58..88cafed 100644 --- a/popups/SoundtrackStatsPopup.tscn +++ b/popups/SoundtrackStatsPopup.tscn @@ -30,19 +30,78 @@ theme_type_variation = &"Title" horizontal_alignment = 1 text_overrun_behavior = 3 -[node name="SummaryLabel" type="Label" parent="StatsPanel" unique_id=1213000004] +[node name="CoverFrame" type="Panel" parent="StatsPanel"] +unique_name_in_owner = true +clip_children = 2 +layout_mode = 0 +offset_left = 20.0 +offset_top = 64.0 +offset_right = 164.0 +offset_bottom = 264.0 +mouse_filter = 2 +theme_type_variation = &"SoundtrackCover" + +[node name="CoverPlaceholder" type="Label" parent="StatsPanel/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 +theme_type_variation = &"TitleMedium" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="CoverPicture" type="TextureRect" parent="StatsPanel/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="Info" type="VBoxContainer" parent="StatsPanel"] +layout_mode = 0 +offset_left = 180.0 +offset_top = 64.0 +offset_right = 680.0 +offset_bottom = 264.0 + +[node name="SummaryLabel" type="Label" parent="StatsPanel/Info" unique_id=1213000004] +unique_name_in_owner = true +layout_mode = 2 +autowrap_mode = 3 + +[node name="NamesTitle" type="Label" parent="StatsPanel/Info"] +layout_mode = 2 +theme_type_variation = &"HighlightMedium" +text = "Also known as" + +[node name="NamesLabel" type="Label" parent="StatsPanel/Info"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"TextMedium" +autowrap_mode = 3 +text_overrun_behavior = 3 +max_lines_visible = 3 + +[node name="EditNamesButton" type="Button" parent="StatsPanel/Info"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 0 +focus_mode = 0 +text = "Edit names" + +[node name="ScrollContainer" type="ScrollContainer" parent="StatsPanel" unique_id=1213000005] unique_name_in_owner = true layout_mode = 0 offset_left = 20.0 -offset_top = 54.0 -offset_right = 680.0 -offset_bottom = 80.0 -horizontal_alignment = 1 - -[node name="ScrollContainer" type="ScrollContainer" parent="StatsPanel" unique_id=1213000005] -layout_mode = 0 -offset_left = 20.0 -offset_top = 92.0 +offset_top = 280.0 offset_right = 680.0 offset_bottom = 784.0 horizontal_scroll_mode = 0 @@ -51,3 +110,73 @@ horizontal_scroll_mode = 0 unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 + +[node name="NamesEditor" 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="EditorTitle" type="Label" parent="StatsPanel/NamesEditor"] +layout_mode = 2 +theme_type_variation = &"Title" +text = "Alternative names" + +[node name="EditorHelp" type="Label" parent="StatsPanel/NamesEditor"] +layout_mode = 2 +autowrap_mode = 3 +text = "Other names the soundtrack goes by, e.g. short names or the name in another language. Search finds it by any of them." + +[node name="EditorScroll" type="ScrollContainer" parent="StatsPanel/NamesEditor"] +layout_mode = 2 +size_flags_vertical = 3 +horizontal_scroll_mode = 0 + +[node name="EditorNames" type="VBoxContainer" parent="StatsPanel/NamesEditor/EditorScroll"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="AddRow" type="HBoxContainer" parent="StatsPanel/NamesEditor"] +layout_mode = 2 +theme_override_constants/separation = 8 + +[node name="NewNameField" type="LineEdit" parent="StatsPanel/NamesEditor/AddRow"] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "Another name" + +[node name="AddNameButton" type="Button" parent="StatsPanel/NamesEditor/AddRow"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +text = "Add" + +[node name="EditorStatus" type="Label" parent="StatsPanel/NamesEditor"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +theme_type_variation = &"HighlightMedium" +autowrap_mode = 3 + +[node name="EditorButtons" type="HBoxContainer" parent="StatsPanel/NamesEditor"] +layout_mode = 2 +theme_override_constants/separation = 12 +alignment = 2 + +[node name="CancelNamesButton" type="Button" parent="StatsPanel/NamesEditor/EditorButtons"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +text = "Cancel" + +[node name="SaveNamesButton" type="Button" parent="StatsPanel/NamesEditor/EditorButtons"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +text = "Save" diff --git a/popups/search_popup.gd b/popups/search_popup.gd index 75a25a1..9417ee4 100644 --- a/popups/search_popup.gd +++ b/popups/search_popup.gd @@ -1,7 +1,8 @@ class_name SearchPopup extends BasePopup ## Searches every soundtrack on the server as you type, with [SoundtrackSearch], and -## highlights the letters that matched. +## highlights the letters that matched. Soundtracks are also found by their alternative +## names; the alternative name that matched is shown after the real name. ## Every soundtrack on the server, sorted by name. var soundtracks: Array = [] @@ -10,6 +11,8 @@ var _matcher: SoundtrackSearch = SoundtrackSearch.new() # One row per soundtrack, in the order of soundtracks. Rows are reused between searches. var _rows: Array[PanelContainer] = [] var _index_of: Dictionary = {} +# The other names of each soundtrack, by its real name +var _alternatives: Dictionary = {} @onready var search_panel: Panel = %SearchPanel @onready var search_field: LineEdit = %SearchField @@ -86,19 +89,25 @@ func get_list_of_soundtracks() -> void: if Settings.is_offline(): set_soundtracks(Settings.music_folder.soundtrack_names()) return - var handle_soundtracks: Callable = func handle_soundtracks(array: Variant) -> void: - if typeof(array) == TYPE_ARRAY: - set_soundtracks(array) - else: - GameLog.warning("Unexpected data") - MusicServerClient.fetch_all_soundtracks_ordered(handle_soundtracks) + var with_names: Callable = func with_names(list: Array[Soundtrack]) -> void: + var names: Array[String] = [] + var alternatives: Dictionary = {} + for soundtrack: Soundtrack in list: + names.append(soundtrack.name) + alternatives[soundtrack.name] = soundtrack.alternative_names + names.sort_custom(func(a: String, b: String) -> bool: + return a.naturalnocasecmp_to(b) < 0) + set_soundtracks(names, alternatives) + MusicServerClient.fetch_soundtracks(with_names, _load_names_only) ## Replaces the searchable soundtracks and shows them, keeping the current search. -func set_soundtracks(list: Array) -> void: +## [param alternatives] has the other names of a soundtrack, by its real name. +func set_soundtracks(list: Array, alternatives: Dictionary = {}) -> void: soundtracks = [] soundtracks.append_array(list) - _matcher.set_names(soundtracks) + _alternatives = alternatives + _matcher.set_names(soundtracks, alternatives) _index_of.clear() for index: int in range(soundtracks.size() - 1, -1, -1): _index_of[soundtracks[index]] = index @@ -145,9 +154,25 @@ func _make_row() -> PanelContainer: return row +# Servers without /soundtracks only list the names +func _load_names_only() -> void: + MusicServerClient.fetch_all_soundtracks_ordered(func(array: Variant) -> void: + if typeof(array) == TYPE_ARRAY: + set_soundtracks(array) + else: + GameLog.warning("Unexpected data")) + + func _show_name(row: PanelContainer, soundtrack_name: String, search_text: String) -> void: - var text: String = highlighted_name(soundtrack_name, search_text, - get_theme_color(&"font_color", &"HighlightLarge")) + var highlight: Color = get_theme_color(&"font_color", &"HighlightLarge") + var text: String = highlighted_name(soundtrack_name, search_text, highlight) + # Found by another name: show which, dimmer, after the real name + var alternative: String = _matcher.matched_alternative(soundtrack_name) \ + if !search_text.is_empty() else "" + if !alternative.is_empty(): + text += " [color=#%s]%s[/color]" % [ + get_theme_color(&"default_color", &"SongStatsSecondary").to_html(false), + highlighted_name(alternative, search_text, highlight)] var label: RichTextLabel = row.get_child(0) # Setting the same text again would make the label lay it out again for nothing if label.text != text: diff --git a/popups/soundtrack_stats_popup.gd b/popups/soundtrack_stats_popup.gd index b4862c2..41fc257 100644 --- a/popups/soundtrack_stats_popup.gd +++ b/popups/soundtrack_stats_popup.gd @@ -1,27 +1,173 @@ class_name SoundtrackStatsPopup extends BasePopup -## Shows a soundtrack's play statistics and every song in it, most played first. -## Opened by clicking a card in the statistics view. +## Shows a soundtrack's cover, play statistics and alternative names, and every song in +## it, most played first. Opened by clicking a card in the statistics view. +## +## 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. + +## Emitted when the alternative names of [param soundtrack] have been saved. +signal alternative_names_changed(soundtrack: Soundtrack) + +## The covers on disk, shared with the statistics view. No cover is shown without it. +var covers: CoverCache +## The soundtrack shown. +var soundtrack: SoundtrackWithSongs + +# The names in the editor, saved only when Save is pressed +var _edited_names: Array[String] = [] @onready var title_label: Label = %TitleLabel @onready var summary_label: Label = %SummaryLabel +@onready var cover_picture: TextureRect = %CoverPicture +@onready var cover_placeholder: Label = %CoverPlaceholder +@onready var names_label: Label = %NamesLabel +@onready var edit_names_button: Button = %EditNamesButton +@onready var song_scroll: ScrollContainer = %ScrollContainer @onready var song_list: VBoxContainer = %SongList +@onready var names_editor: VBoxContainer = %NamesEditor +@onready var editor_names: VBoxContainer = %EditorNames +@onready var new_name_field: LineEdit = %NewNameField +@onready var add_name_button: Button = %AddNameButton +@onready var editor_status: Label = %EditorStatus +@onready var save_names_button: Button = %SaveNamesButton +@onready var cancel_names_button: Button = %CancelNamesButton -## Shows [param soundtrack] and opens the popup. -func show_soundtrack(soundtrack: SoundtrackWithSongs) -> void: - title_label.text = soundtrack.soundtrack_name - var summary: PackedStringArray = [SoundtrackCard.played_text(soundtrack.soundtrack_played)] - var last_played: String = SoundtrackCard.last_played_text(soundtrack.soundtrack_last_played) +func _ready() -> void: + edit_names_button.pressed.connect(edit_names) + add_name_button.pressed.connect(add_name) + 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) + + +## Shows [param shown] and opens the popup. +func show_soundtrack(shown: SoundtrackWithSongs) -> void: + soundtrack = shown + stop_editing() + title_label.text = shown.soundtrack_name + var summary: PackedStringArray = [SoundtrackCard.played_text(shown.soundtrack_played)] + var last_played: String = SoundtrackCard.last_played_text(shown.soundtrack_last_played) if !last_played.is_empty(): summary.append(last_played) - summary.append("%d %s" % [soundtrack.songs.size(), - "song" if soundtrack.songs.size() == 1 else "songs"]) + summary.append("%d %s" % [shown.songs.size(), + "song" if shown.songs.size() == 1 else "songs"]) summary_label.text = " · ".join(summary) + _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)) Settings.delete_children(song_list) - var songs: Array[SongInfoForStats] = SoundtrackCard.songs_by_plays(soundtrack.songs) + var songs: Array[SongInfoForStats] = SoundtrackCard.songs_by_plays(shown.songs) for row: int in songs.size(): song_list.add_child(ListRow.create(SoundtrackCard.song_text(songs[row]), row, &"TextLarge")) open() + + +## Opens the editor for the alternative names, in place of the song list. +func edit_names() -> void: + _edited_names.assign(soundtrack.alternative_names) + new_name_field.text = "" + editor_status.visible = false + _show_edited_names() + names_editor.visible = true + song_scroll.visible = false + edit_names_button.disabled = true + new_name_field.grab_focus() + + +## Closes the editor without saving. +func stop_editing() -> void: + names_editor.visible = false + song_scroll.visible = true + edit_names_button.disabled = false + _set_saving(false) + + +## Adds the name in the field to the edited names, unless it is empty, the soundtrack's +## own name or already there. +func add_name() -> void: + var new_name: String = new_name_field.text.strip_edges() + if new_name.is_empty() || new_name.to_lower() == soundtrack.soundtrack_name.to_lower() \ + || _edited_names.any(func(name: String) -> bool: + return name.to_lower() == new_name.to_lower()): + return + _edited_names.append(new_name) + new_name_field.text = "" + _show_edited_names() + + +## Takes [param removed] out of the edited names. +func remove_name(removed: String) -> void: + _edited_names.erase(removed) + _show_edited_names() + + +## Sends the edited names to the server. The editor closes when they are saved. +func save_names() -> void: + # A name typed but not added yet is meant to be saved too + add_name() + _set_saving(true) + MusicServerClient.set_alternative_names(soundtrack.soundtrack_uuid, _edited_names, + _on_names_saved, _on_names_not_saved) + + +func _on_names_saved(updated: Soundtrack) -> void: + soundtrack.alternative_names = updated.alternative_names + _show_names() + stop_editing() + alternative_names_changed.emit(updated) + + +func _on_names_not_saved() -> void: + _set_saving(false) + editor_status.text = "Could not save the names. Try again." + editor_status.visible = true + + +func _set_saving(saving: bool) -> void: + save_names_button.disabled = saving + cancel_names_button.disabled = saving + save_names_button.text = "Saving…" if saving else "Save" + + +func _show_names() -> void: + var names: Array[String] = soundtrack.alternative_names + names_label.text = ", ".join(names) if !names.is_empty() else "No other names yet" + + +func _show_edited_names() -> void: + Settings.delete_children(editor_names) + for row: int in _edited_names.size(): + var line: HBoxContainer = HBoxContainer.new() + var label: Label = Label.new() + label.text = _edited_names[row] + label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS + line.add_child(label) + var remove: Button = Button.new() + remove.text = "Remove" + remove.focus_mode = Control.FOCUS_NONE + remove.pressed.connect(remove_name.bind(_edited_names[row])) + line.add_child(remove) + var list_row: PanelContainer = PanelContainer.new() + list_row.theme_type_variation = &"ListRowOdd" if row % 2 != 0 else &"ListRowEven" + list_row.add_child(line) + editor_names.add_child(list_row) + + +func _show_cover(cover: Image) -> void: + cover_picture.texture = ImageTexture.create_from_image(cover) if cover != null else null + cover_placeholder.visible = cover == null + + +# Covers arrive later; one for a soundtrack no longer shown is left out +func _show_cover_of(cover: Image, uuid: String) -> void: + if soundtrack != null && soundtrack.soundtrack_uuid == uuid: + _show_cover(cover) diff --git a/server/generated/alternative_names.gd b/server/generated/alternative_names.gd new file mode 100644 index 0000000..6ceff18 --- /dev/null +++ b/server/generated/alternative_names.gd @@ -0,0 +1,22 @@ +# Generated by tools/generate_api_types.gd from server/openapi.json. +# Do not edit; change the server and generate again. +class_name AlternativeNames +extends RefCounted +## The [code]AlternativeNames[/code] schema of the MusicServer API. + +## The new list of alternative names. Replaces the old list; send [] to remove them all. +var names: Array[String] + + +## Reads the fields from parsed JSON. Missing fields keep their defaults. +static func from_dict(data: Dictionary) -> AlternativeNames: + var result: AlternativeNames = AlternativeNames.new() + result.names.assign(ApiJson.get_array(data, "names")) + return result + + +## Returns the fields under their JSON names, ready for [method JSON.stringify]. +func to_dict() -> Dictionary: + return { + "names": names, + } diff --git a/server/generated/alternative_names.gd.uid b/server/generated/alternative_names.gd.uid new file mode 100644 index 0000000..f3daf64 --- /dev/null +++ b/server/generated/alternative_names.gd.uid @@ -0,0 +1 @@ +uid://bpdabeiw5wupm diff --git a/server/generated/cover_source.gd b/server/generated/cover_source.gd new file mode 100644 index 0000000..112d600 --- /dev/null +++ b/server/generated/cover_source.gd @@ -0,0 +1,26 @@ +# Generated by tools/generate_api_types.gd from server/openapi.json. +# Do not edit; change the server and generate again. +class_name CoverSource +extends RefCounted +## The [code]CoverSource[/code] schema of the MusicServer API. + +## The game's ID on SteamGridDB, or the anime's ID on AniList +var id: String +## Where the cover comes from +var source: String + + +## Reads the fields from parsed JSON. Missing fields keep their defaults. +static func from_dict(data: Dictionary) -> CoverSource: + var result: CoverSource = CoverSource.new() + result.id = ApiJson.get_string(data, "id", "") + result.source = ApiJson.get_string(data, "source", "") + return result + + +## Returns the fields under their JSON names, ready for [method JSON.stringify]. +func to_dict() -> Dictionary: + return { + "id": id, + "source": source, + } diff --git a/server/generated/cover_source.gd.uid b/server/generated/cover_source.gd.uid new file mode 100644 index 0000000..710c7fc --- /dev/null +++ b/server/generated/cover_source.gd.uid @@ -0,0 +1 @@ +uid://06q8sppe2evn diff --git a/server/generated/soundtrack.gd b/server/generated/soundtrack.gd index a1c4fe3..5189e0f 100644 --- a/server/generated/soundtrack.gd +++ b/server/generated/soundtrack.gd @@ -5,6 +5,10 @@ extends RefCounted ## The [code]Soundtrack[/code] schema of the MusicServer API. var added: String +## Other names the soundtrack is known by +var alternative_names: Array[String] +## When the cover last changed, in Unix seconds. Missing when there is no cover. +var cover_modified: int var last_played: String var name: String var times_played: int @@ -15,6 +19,8 @@ var uuid: String static func from_dict(data: Dictionary) -> Soundtrack: var result: Soundtrack = Soundtrack.new() result.added = ApiJson.get_string(data, "added", "") + result.alternative_names.assign(ApiJson.get_array(data, "alternative_names")) + result.cover_modified = ApiJson.get_int(data, "cover_modified", 0) result.last_played = ApiJson.get_string(data, "last_played", "") result.name = ApiJson.get_string(data, "name", "") result.times_played = ApiJson.get_int(data, "times_played", 0) @@ -26,6 +32,8 @@ static func from_dict(data: Dictionary) -> Soundtrack: func to_dict() -> Dictionary: return { "added": added, + "alternative_names": alternative_names, + "cover_modified": cover_modified, "last_played": last_played, "name": name, "times_played": times_played, diff --git a/server/generated/soundtrack_with_song_list.gd b/server/generated/soundtrack_with_song_list.gd index 6cc8fb5..1f62f0b 100644 --- a/server/generated/soundtrack_with_song_list.gd +++ b/server/generated/soundtrack_with_song_list.gd @@ -5,6 +5,10 @@ extends RefCounted ## The [code]SoundtrackWithSongList[/code] schema of the MusicServer API. var added: String +## Other names the soundtrack is known by +var alternative_names: Array[String] +## When the cover last changed, in Unix seconds. Missing when there is no cover. +var cover_modified: int var last_played: String var name: String var songs: Array[Song] @@ -16,6 +20,8 @@ var uuid: String static func from_dict(data: Dictionary) -> SoundtrackWithSongList: var result: SoundtrackWithSongList = SoundtrackWithSongList.new() result.added = ApiJson.get_string(data, "added", "") + result.alternative_names.assign(ApiJson.get_array(data, "alternative_names")) + result.cover_modified = ApiJson.get_int(data, "cover_modified", 0) result.last_played = ApiJson.get_string(data, "last_played", "") result.name = ApiJson.get_string(data, "name", "") for item: Variant in ApiJson.get_array(data, "songs"): @@ -30,6 +36,8 @@ static func from_dict(data: Dictionary) -> SoundtrackWithSongList: func to_dict() -> Dictionary: return { "added": added, + "alternative_names": alternative_names, + "cover_modified": cover_modified, "last_played": last_played, "name": name, "songs": songs.map(func(item: Song) -> Dictionary: return item.to_dict()), diff --git a/server/generated/soundtrack_with_songs.gd b/server/generated/soundtrack_with_songs.gd index e3aa5b9..edb2b94 100644 --- a/server/generated/soundtrack_with_songs.gd +++ b/server/generated/soundtrack_with_songs.gd @@ -4,6 +4,10 @@ class_name SoundtrackWithSongs extends RefCounted ## The [code]SoundtrackWithSongs[/code] schema of the MusicServer API. +## Other names the soundtrack is known by +var alternative_names: Array[String] +## When the cover last changed, in Unix seconds. Missing when there is no cover. +var cover_modified: int var songs: Array[SongInfoForStats] var soundtrack_id: int var soundtrack_last_played: String @@ -15,6 +19,8 @@ var soundtrack_uuid: String ## Reads the fields from parsed JSON. Missing fields keep their defaults. static func from_dict(data: Dictionary) -> SoundtrackWithSongs: var result: SoundtrackWithSongs = SoundtrackWithSongs.new() + result.alternative_names.assign(ApiJson.get_array(data, "alternative_names")) + result.cover_modified = ApiJson.get_int(data, "cover_modified", 0) for item: Variant in ApiJson.get_array(data, "songs"): if item is Dictionary: result.songs.append(SongInfoForStats.from_dict(item)) @@ -29,6 +35,8 @@ static func from_dict(data: Dictionary) -> SoundtrackWithSongs: ## Returns the fields under their JSON names, ready for [method JSON.stringify]. func to_dict() -> Dictionary: return { + "alternative_names": alternative_names, + "cover_modified": cover_modified, "songs": songs.map(func(item: SongInfoForStats) -> Dictionary: return item.to_dict()), "soundtrack_id": soundtrack_id, "soundtrack_last_played": soundtrack_last_played, diff --git a/server/music_server_client.gd b/server/music_server_client.gd index 01d59e6..4b81e26 100644 --- a/server/music_server_client.gd +++ b/server/music_server_client.gd @@ -131,6 +131,31 @@ func fetch_soundtracks(on_done: Callable, on_failed: Callable = Callable()) -> v get_json_list("/soundtracks", Soundtrack, on_done, on_failed) +## Downloads the cover picture of the soundtrack with [param uuid] as bytes (JPEG or +## 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) + + +## Replaces the alternative names of the soundtrack with [param uuid] with +## [param names]. The server trims them, drops duplicates and the soundtrack's own name, +## and calls [param on_done] with the updated [Soundtrack]. +func set_alternative_names(uuid: String, names: Array[String], on_done: Callable, + on_failed: Callable = Callable()) -> void: + var request: AlternativeNames = AlternativeNames.new() + request.names = names + var path: String = "/soundtracks/%s/alternative-names" % uuid.uri_encode() + var saved: Callable = func saved(data: Variant) -> void: + if !(data is Dictionary): + push_warning("Expected a JSON object from " + path) + _call_if_valid(on_failed) + 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())) + + ## Fetches a soundtrack and its songs as a [SoundtrackWithSongList]. func fetch_soundtrack( uuid: String, on_done: Callable, on_failed: Callable = Callable()) -> void: @@ -419,13 +444,14 @@ func _parse_json( # Sends the request with the token, and once more with a new token if the server -# answers 401 +# answers 401. A [param json_body] is sent as the request's body. func _request( path: String, method: int, on_success: Callable, on_failed: Callable, may_retry: bool = true, + json_body: String = "", ) -> void: _with_token(func(token: String) -> void: _send(path, method, token, func(response_code: int, body: PackedByteArray) -> void: @@ -433,10 +459,10 @@ func _request( GameLog.debug("Token refused by the server, getting a new one") if _token == token: clear_token() - _request(path, method, on_success, on_failed, false) + _request(path, method, on_success, on_failed, false, json_body) return _handle_response(path, response_code, body, on_success, on_failed) - ) + , json_body) , on_failed) diff --git a/server/openapi.json b/server/openapi.json index 162d600..585d1f4 100644 --- a/server/openapi.json +++ b/server/openapi.json @@ -17,6 +17,23 @@ ], "type": "object" }, + "AlternativeNames": { + "additionalProperties": false, + "properties": { + "names": { + "description": "The new list of alternative names. Replaces the old list; send [] to remove them all.", + "items": { + "type": "string" + }, + "maxItems": 50, + "type": "array" + } + }, + "required": [ + "names" + ], + "type": "object" + }, "CharacterDetails": { "additionalProperties": false, "properties": { @@ -36,6 +53,30 @@ ], "type": "object" }, + "CoverSource": { + "additionalProperties": false, + "properties": { + "id": { + "description": "The game's ID on SteamGridDB, or the anime's ID on AniList", + "maxLength": 50, + "minLength": 1, + "type": "string" + }, + "source": { + "description": "Where the cover comes from", + "enum": [ + "steamgriddb", + "anilist" + ], + "type": "string" + } + }, + "required": [ + "source", + "id" + ], + "type": "object" + }, "ProgressResponse": { "additionalProperties": false, "properties": { @@ -175,6 +216,18 @@ "format": "date-time", "type": "string" }, + "alternative_names": { + "description": "Other names the soundtrack is known by", + "items": { + "type": "string" + }, + "type": "array" + }, + "cover_modified": { + "description": "When the cover last changed, in Unix seconds. Missing when there is no cover.", + "format": "int64", + "type": "integer" + }, "last_played": { "format": "date-time", "type": "string" @@ -193,6 +246,7 @@ "required": [ "uuid", "name", + "alternative_names", "times_played", "added" ], @@ -205,6 +259,18 @@ "format": "date-time", "type": "string" }, + "alternative_names": { + "description": "Other names the soundtrack is known by", + "items": { + "type": "string" + }, + "type": "array" + }, + "cover_modified": { + "description": "When the cover last changed, in Unix seconds. Missing when there is no cover.", + "format": "int64", + "type": "integer" + }, "last_played": { "format": "date-time", "type": "string" @@ -230,6 +296,7 @@ "songs", "uuid", "name", + "alternative_names", "times_played", "added" ], @@ -238,6 +305,18 @@ "SoundtrackWithSongs": { "additionalProperties": false, "properties": { + "alternative_names": { + "description": "Other names the soundtrack is known by", + "items": { + "type": "string" + }, + "type": "array" + }, + "cover_modified": { + "description": "When the cover last changed, in Unix seconds. Missing when there is no cover.", + "format": "int64", + "type": "integer" + }, "songs": { "items": { "$ref": "#/components/schemas/SongInfoForStats" @@ -267,6 +346,7 @@ "soundtrack_id", "soundtrack_uuid", "soundtrack_name", + "alternative_names", "soundtrack_played", "songs" ], @@ -2663,6 +2743,515 @@ ] } }, + "/api/v1/soundtracks/{uuid}/alternative-names": { + "put": { + "description": "Replaces the soundtrack's alternative names, which search and the cover search also use. Names are trimmed, and empty names, duplicates and the soundtrack's own name (ignoring case) are dropped.", + "operationId": "set-alternative-names", + "parameters": [ + { + "description": "UUID of the soundtrack", + "in": "path", + "name": "uuid", + "required": true, + "schema": { + "description": "UUID of the soundtrack", + "format": "uuid", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AlternativeNames" + } + } + }, + "required": true + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Soundtrack" + } + } + }, + "description": "OK" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Unauthorized" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Not Found" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Unprocessable Entity" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Internal Server Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Set alternative names", + "tags": [ + "library" + ] + } + }, + "/api/v1/soundtracks/{uuid}/cover": { + "delete": { + "description": "Deletes the soundtrack's cover, including one set by hand, so the automatic search looks for a new one.", + "operationId": "delete-soundtrack-cover", + "parameters": [ + { + "description": "UUID of the soundtrack or song", + "in": "path", + "name": "uuid", + "required": true, + "schema": { + "description": "UUID of the soundtrack or song", + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Unauthorized" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Not Found" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Unprocessable Entity" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Internal Server Error" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Service Unavailable" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Remove the cover", + "tags": [ + "covers" + ] + }, + "get": { + "description": "Returns the soundtrack's cover image. cover_modified in the soundtrack responses tells when it last changed.", + "operationId": "get-soundtrack-cover", + "parameters": [ + { + "description": "UUID of the soundtrack or song", + "in": "path", + "name": "uuid", + "required": true, + "schema": { + "description": "UUID of the soundtrack or song", + "format": "uuid", + "type": "string" + } + } + ], + "responses": { + "200": { + "content": { + "image/jpeg": { + "schema": { + "contentMediaType": "application/octet-stream", + "format": "binary", + "type": "string" + } + }, + "image/png": { + "schema": { + "contentMediaType": "application/octet-stream", + "format": "binary", + "type": "string" + } + }, + "image/webp": { + "schema": { + "contentMediaType": "application/octet-stream", + "format": "binary", + "type": "string" + } + } + }, + "description": "The cover image" + }, + "206": { + "content": { + "image/jpeg": { + "schema": { + "contentMediaType": "application/octet-stream", + "format": "binary", + "type": "string" + } + }, + "image/png": { + "schema": { + "contentMediaType": "application/octet-stream", + "format": "binary", + "type": "string" + } + }, + "image/webp": { + "schema": { + "contentMediaType": "application/octet-stream", + "format": "binary", + "type": "string" + } + } + }, + "description": "Part of the file, for a Range request" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Unauthorized" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Not Found" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Unprocessable Entity" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Internal Server Error" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Soundtrack cover", + "tags": [ + "covers" + ] + }, + "put": { + "description": "Stores the image as the soundtrack's cover. The automatic search never replaces it; delete it to go back to automatic.", + "operationId": "upload-soundtrack-cover", + "parameters": [ + { + "description": "UUID of the soundtrack", + "in": "path", + "name": "uuid", + "required": true, + "schema": { + "description": "UUID of the soundtrack", + "format": "uuid", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "image/*": { + "schema": { + "contentMediaType": "application/octet-stream", + "format": "binary", + "type": "string" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Bad Request" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Unauthorized" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Not Found" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Unprocessable Entity" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Internal Server Error" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Service Unavailable" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Upload a cover", + "tags": [ + "covers" + ] + } + }, + "/api/v1/soundtracks/{uuid}/cover/source": { + "put": { + "description": "Downloads the cover of the given SteamGridDB game or AniList anime and stores it as the soundtrack's cover. The automatic search never replaces it. SteamGridDB needs STEAMGRIDDB_API_KEY on the server.", + "operationId": "set-soundtrack-cover-source", + "parameters": [ + { + "description": "UUID of the soundtrack", + "in": "path", + "name": "uuid", + "required": true, + "schema": { + "description": "UUID of the soundtrack", + "format": "uuid", + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CoverSource" + } + } + }, + "required": true + }, + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Bad Request" + }, + "401": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Unauthorized" + }, + "404": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Not Found" + }, + "422": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Unprocessable Entity" + }, + "500": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Internal Server Error" + }, + "502": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Bad Gateway" + }, + "503": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/APIError" + } + } + }, + "description": "Service Unavailable" + } + }, + "security": [ + { + "bearer": [] + } + ], + "summary": "Pick a cover", + "tags": [ + "covers" + ] + } + }, "/api/v1/statistics/songs/least-played": { "get": { "description": "Returns the least played songs with their soundtrack.", diff --git a/statistics/SoundtrackCard.tscn b/statistics/SoundtrackCard.tscn index 237b66d..e32cb92 100644 --- a/statistics/SoundtrackCard.tscn +++ b/statistics/SoundtrackCard.tscn @@ -12,9 +12,52 @@ script = ExtResource("card_script") layout_mode = 2 mouse_filter = 2 -[node name="NameLabel" type="Label" parent="VBox" unique_id=1211000003] +[node name="Header" type="HBoxContainer" parent="VBox"] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/separation = 10 + +[node name="CoverFrame" type="Panel" parent="VBox/Header"] unique_name_in_owner = true -custom_minimum_size = Vector2(0, 62) +clip_children = 2 +custom_minimum_size = Vector2(72, 100) +layout_mode = 2 +mouse_filter = 2 +theme_type_variation = &"SoundtrackCover" + +[node name="CoverPlaceholder" type="Label" parent="VBox/Header/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 +theme_type_variation = &"TextLarge" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="CoverPicture" type="TextureRect" parent="VBox/Header/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="Info" type="VBoxContainer" parent="VBox/Header"] +layout_mode = 2 +size_flags_horizontal = 3 +mouse_filter = 2 + +[node name="NameLabel" type="Label" parent="VBox/Header/Info" unique_id=1211000003] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 50) layout_mode = 2 mouse_filter = 2 theme_type_variation = &"TextLarge" @@ -22,12 +65,12 @@ autowrap_mode = 3 text_overrun_behavior = 3 max_lines_visible = 2 -[node name="PlayedLabel" type="Label" parent="VBox" unique_id=1211000004] +[node name="PlayedLabel" type="Label" parent="VBox/Header/Info" unique_id=1211000004] unique_name_in_owner = true layout_mode = 2 mouse_filter = 2 -[node name="LastPlayedLabel" type="Label" parent="VBox" unique_id=1211000005] +[node name="LastPlayedLabel" type="Label" parent="VBox/Header/Info" unique_id=1211000005] unique_name_in_owner = true layout_mode = 2 mouse_filter = 2 diff --git a/statistics/soundtrack_card.gd b/statistics/soundtrack_card.gd index bfe5178..f089e7c 100644 --- a/statistics/soundtrack_card.gd +++ b/statistics/soundtrack_card.gd @@ -4,6 +4,9 @@ extends PanelContainer ## played, and its most and least played songs. Songs never played are not listed, and ## the least played are only shown when there are enough played songs to not repeat ## any. Clicking it asks for all its songs. +## +## Its cover shows on the left once [method load_cover] has fetched it; until then, and +## when the soundtrack has no cover, the soundtrack's initials stand in for it. ## Emitted when the card is clicked. signal pressed(soundtrack: SoundtrackWithSongs) @@ -15,6 +18,11 @@ const SONGS_SHOWN: int = 3 ## The soundtrack the card shows. var soundtrack: SoundtrackWithSongs +# The cover time asked for, so a cover is only asked for again when it changed +var _cover_asked: int = 0 + +@onready var cover_picture: TextureRect = %CoverPicture +@onready var cover_placeholder: Label = %CoverPlaceholder @onready var name_label: Label = %NameLabel @onready var played_label: Label = %PlayedLabel @onready var last_played_label: Label = %LastPlayedLabel @@ -32,7 +40,14 @@ func _ready() -> void: ## Shows [param data] on the card. func show_soundtrack(data: SoundtrackWithSongs) -> void: + var cover_changed: bool = soundtrack == null \ + || soundtrack.soundtrack_uuid != data.soundtrack_uuid \ + || soundtrack.cover_modified != data.cover_modified soundtrack = data + if cover_changed: + show_cover(null) + _cover_asked = 0 + cover_placeholder.text = initials(data.soundtrack_name) name_label.text = data.soundtrack_name tooltip_text = data.soundtrack_name played_label.text = played_text(data.soundtrack_played) @@ -55,6 +70,34 @@ func show_soundtrack(data: SoundtrackWithSongs) -> void: _fill_song_list(least_played_list, least_played) +## Asks [param covers] for the soundtrack's cover, unless it already has, and shows it +## when it comes. Soundtracks without a cover keep their initials. +func load_cover(covers: CoverCache) -> void: + if soundtrack == null || soundtrack.cover_modified <= 0 \ + || _cover_asked == soundtrack.cover_modified: + return + _cover_asked = soundtrack.cover_modified + covers.fetch_cover(soundtrack.soundtrack_uuid, soundtrack.cover_modified, show_cover) + + +## 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 + cover_placeholder.visible = cover == null + + +## Up to two initials of [param soundtrack_name], e.g. "FF" for "Final Fantasy VI". +static func initials(soundtrack_name: String) -> String: + var letters: String = "" + for word: String in soundtrack_name.split(" ", false): + var first: String = word.left(1) + if first.to_upper() != first.to_lower(): + letters += first.to_upper() + if letters.length() == 2: + break + return letters if !letters.is_empty() else soundtrack_name.left(1) + + ## "Played 3 times", or "Never played" for [code]0[/code]. static func played_text(times_played: int) -> String: if times_played == 0: diff --git a/statistics/statistics_view.gd b/statistics/statistics_view.gd index 96ef224..0965445 100644 --- a/statistics/statistics_view.gd +++ b/statistics/statistics_view.gd @@ -5,6 +5,9 @@ extends Panel ## cards filtered on whether the soundtrack has been played. ## ## Shown instead of the quiz when the Statistics tab is chosen. +## +## Covers are loaded for the cards on screen as they scroll into view, a few at a time, +## not for every soundtrack when the tab opens. See [CoverCache]. ## Emitted when a card is clicked, to show all songs of [param soundtrack]. signal soundtrack_selected(soundtrack: SoundtrackWithSongs) @@ -19,12 +22,18 @@ enum ShowFilter { ALL, PLAYED, NEVER_PLAYED } enum Mode { SOUNDTRACKS, SONGS } const CARD_SCENE: PackedScene = preload("res://statistics/SoundtrackCard.tscn") +## How far outside the visible part cards still get their covers, so they are ready when +## scrolled to. +const COVER_MARGIN: float = 400.0 ## The cards in the order they are shown, hidden ones included. var cards: Array[SoundtrackCard] = [] ## Whether soundtracks or songs are shown. Change it with [method set_mode]. var mode: Mode = Mode.SOUNDTRACKS +## The covers on disk, shared by the cards. +var covers: CoverCache = CoverCache.new() + var _search: SoundtrackSearch = SoundtrackSearch.new() var _loading: bool = false # The soundtracks last loaded, by name @@ -61,6 +70,11 @@ func _ready() -> void: sort_option.item_selected.connect(func(_index: int) -> void: apply_sort()) retry_button.pressed.connect(refresh) retry_button.visible = false + # The cards have moved by the end of the frame + card_scroll.get_v_scroll_bar().value_changed.connect( + func(_value: float) -> void: load_covers_on_screen.call_deferred()) + card_scroll.resized.connect(load_covers_on_screen) + visibility_changed.connect(load_covers_on_screen) ## Loads the statistics from the server. The cards already shown stay until the new @@ -108,6 +122,7 @@ func apply_sort() -> void: return _comes_before(a.soundtrack, b.soundtrack, order)) for i: int in cards.size(): card_grid.move_child(cards[i], i) + load_covers_on_screen.call_deferred() ## Shows only the cards or songs that match the search text and the show menu. @@ -136,6 +151,8 @@ func apply_filter() -> void: count_label.text = "%d soundtracks" % cards.size() else: count_label.text = "%d of %d soundtracks" % [shown, cards.size()] + # The grid has moved the cards by the end of the frame + load_covers_on_screen.call_deferred() func _on_loaded(soundtracks: Array[SoundtrackWithSongs]) -> void: @@ -149,19 +166,49 @@ func _on_loaded(soundtracks: Array[SoundtrackWithSongs]) -> void: cards.append(card) while cards.size() > soundtracks.size(): cards.pop_back().free() - var names: Array[String] = [] _soundtracks.clear() + var cover_times: Dictionary = {} for i: int in soundtracks.size(): cards[i].show_soundtrack(soundtracks[i]) - names.append(soundtracks[i].soundtrack_name) _soundtracks[soundtracks[i].soundtrack_name] = soundtracks[i] - _search.set_names(names) + cover_times[soundtracks[i].soundtrack_uuid] = soundtracks[i].cover_modified + covers.keep_only(cover_times) + _update_search_names() _songs_ready = false apply_sort() if mode == Mode.SOUNDTRACKS: apply_filter() +## Asks for the covers of the cards that are on screen, or nearly. +func load_covers_on_screen() -> void: + if !is_visible_in_tree() || mode != Mode.SOUNDTRACKS: + return + var view: Rect2 = card_scroll.get_global_rect().grow(COVER_MARGIN) + for card: SoundtrackCard in cards: + if card.visible && view.intersects(card.get_global_rect()): + card.load_cover(covers) + + +## Shows the new alternative names of the soundtrack [param updated] and finds it by +## them, without loading the statistics again. +func update_alternative_names(updated: Soundtrack) -> void: + for soundtrack: SoundtrackWithSongs in _soundtracks.values(): + if soundtrack.soundtrack_uuid == updated.uuid: + soundtrack.alternative_names = updated.alternative_names + _update_search_names() + apply_filter() + + +func _update_search_names() -> void: + var names: Array[String] = [] + var alternatives: Dictionary = {} + for soundtrack: SoundtrackWithSongs in _soundtracks.values(): + names.append(soundtrack.soundtrack_name) + alternatives[soundtrack.soundtrack_name] = soundtrack.alternative_names + _search.set_names(names, alternatives) + + # Typing in the song list waits for a pause, since searching every song takes a moment func _on_search_text_changed(_text: String) -> void: if mode == Mode.SONGS: diff --git a/tests/data/layout_baseline.json b/tests/data/layout_baseline.json index d94ed2b..06621c7 100644 --- a/tests/data/layout_baseline.json +++ b/tests/data/layout_baseline.json @@ -3159,33 +3159,204 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPicture": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPlaceholder": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info": { + "rect": [ + 790.0, + 204.0, + 500.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "rect": [ + 790.0, + 204.0, + 110.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesTitle": { + "rect": [ + 790.0, + 204.0, + 125.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/SummaryLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 23.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 2630.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow": { + "rect": [ + 630.0, + 420.0, + 132.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/AddNameButton": { + "rect": [ + 630.0, + 420.0, + 55.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/NewNameField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons": { + "rect": [ + 630.0, + 420.0, + 148.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/CancelNamesButton": { + "rect": [ + 630.0, + 420.0, + 76.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/SaveNamesButton": { + "rect": [ + 630.0, + 420.0, + 60.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 2493.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.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, + 221.0, + 35.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, - 692.0 + 504.0 ], "visible": false }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList": { "rect": [ 630.0, - 232.0, + 420.0, 0.0, 0.0 ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/SummaryLabel": { - "rect": [ - 630.0, - 194.0, - 660.0, - 26.0 - ], - "visible": false - }, "SoundtrackStatsPopup/StatsPanel/TitleLabel": { "rect": [ 630.0, @@ -7472,19 +7643,271 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPicture": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPlaceholder": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info": { + "rect": [ + 790.0, + 204.0, + 500.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "rect": [ + 790.0, + 291.0, + 110.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesLabel": { + "rect": [ + 790.0, + 261.0, + 500.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesTitle": { + "rect": [ + 790.0, + 231.0, + 500.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/SummaryLabel": { + "rect": [ + 790.0, + 204.0, + 500.0, + 23.0 + ], + "visible": false + }, + "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, - 232.0, + 420.0, 660.0, - 692.0 + 504.0 ], "visible": false }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, 176.0 ], @@ -7493,7 +7916,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#0": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, 32.0 ], @@ -7502,7 +7925,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#0/#0": { "rect": [ 638.0, - 234.0, + 422.0, 644.0, 28.0 ], @@ -7511,7 +7934,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#1": { "rect": [ 630.0, - 268.0, + 456.0, 660.0, 32.0 ], @@ -7520,7 +7943,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#1/#0": { "rect": [ 638.0, - 270.0, + 458.0, 644.0, 28.0 ], @@ -7529,7 +7952,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#2": { "rect": [ 630.0, - 304.0, + 492.0, 660.0, 32.0 ], @@ -7538,7 +7961,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#2/#0": { "rect": [ 638.0, - 306.0, + 494.0, 644.0, 28.0 ], @@ -7547,7 +7970,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#3": { "rect": [ 630.0, - 340.0, + 528.0, 660.0, 32.0 ], @@ -7556,7 +7979,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#3/#0": { "rect": [ 638.0, - 342.0, + 530.0, 644.0, 28.0 ], @@ -7565,7 +7988,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#4": { "rect": [ 630.0, - 376.0, + 564.0, 660.0, 32.0 ], @@ -7574,21 +7997,12 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#4/#0": { "rect": [ 638.0, - 378.0, + 566.0, 644.0, 28.0 ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/SummaryLabel": { - "rect": [ - 630.0, - 194.0, - 660.0, - 26.0 - ], - "visible": false - }, "SoundtrackStatsPopup/StatsPanel/TitleLabel": { "rect": [ 630.0, @@ -7652,11 +8066,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header": { "rect": [ 656.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info": { + "rect": [ + 738.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -7673,7 +8150,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/LeastPlayedList": { "rect": [ 656.0, - 779.0, + 767.0, 266.0, 0.0 ], @@ -7682,7 +8159,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedHeader": { "rect": [ 656.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -7691,7 +8168,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 131.0 ], @@ -7700,7 +8177,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -7709,7 +8186,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 671.0, + 659.0, 266.0, 23.0 ], @@ -7718,7 +8195,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 698.0, + 686.0, 266.0, 23.0 ], @@ -7727,7 +8204,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#3": { "rect": [ 656.0, - 725.0, + 713.0, 266.0, 23.0 ], @@ -7736,25 +8213,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#4": { "rect": [ 656.0, - 752.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/NameLabel": { - "rect": [ - 656.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/PlayedLabel": { - "rect": [ - 656.0, - 560.0, + 740.0, 266.0, 23.0 ], @@ -7778,11 +8237,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header": { "rect": [ 962.0, - 587.0, + 494.0, 266.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 584.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 59.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 557.0, + 184.0, 23.0 ], "visible": false @@ -7799,7 +8321,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LeastPlayedList": { "rect": [ 962.0, - 779.0, + 776.0, 266.0, 0.0 ], @@ -7808,7 +8330,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedHeader": { "rect": [ 962.0, - 614.0, + 611.0, 266.0, 26.0 ], @@ -7817,7 +8339,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 131.0 ], @@ -7826,7 +8348,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 23.0 ], @@ -7835,7 +8357,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 671.0, + 668.0, 266.0, 23.0 ], @@ -7844,7 +8366,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 698.0, + 695.0, 266.0, 23.0 ], @@ -7853,7 +8375,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 725.0, + 722.0, 266.0, 23.0 ], @@ -7862,25 +8384,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#4": { "rect": [ 962.0, - 752.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/NameLabel": { - "rect": [ - 962.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/PlayedLabel": { - "rect": [ - 962.0, - 560.0, + 749.0, 266.0, 23.0 ], @@ -7904,11 +8408,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header": { "rect": [ 1268.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1350.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1350.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -7925,7 +8492,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LeastPlayedList": { "rect": [ 1268.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -7934,7 +8501,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedHeader": { "rect": [ 1268.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -7943,7 +8510,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -7952,7 +8519,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#0": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -7961,7 +8528,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#1": { "rect": [ 1268.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -7970,7 +8537,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#2": { "rect": [ 1268.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -7979,25 +8546,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#3": { "rect": [ 1268.0, - 369.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/NameLabel": { - "rect": [ - 1268.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/PlayedLabel": { - "rect": [ - 1268.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -8021,11 +8570,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header": { "rect": [ 962.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -8042,7 +8654,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LeastPlayedList": { "rect": [ 962.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -8051,7 +8663,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedHeader": { "rect": [ 962.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -8060,7 +8672,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -8069,7 +8681,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -8078,7 +8690,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -8087,7 +8699,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -8096,25 +8708,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 369.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/NameLabel": { - "rect": [ - 962.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/PlayedLabel": { - "rect": [ - 962.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -8138,11 +8732,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header": { "rect": [ 656.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { + "rect": [ + 738.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -8159,7 +8816,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LeastPlayedList": { "rect": [ 656.0, - 369.0, + 357.0, 266.0, 0.0 ], @@ -8168,7 +8825,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedHeader": { "rect": [ 656.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -8177,7 +8834,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 77.0 ], @@ -8186,7 +8843,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -8195,7 +8852,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -8204,25 +8861,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 342.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/NameLabel": { - "rect": [ - 656.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/PlayedLabel": { - "rect": [ - 656.0, - 204.0, + 330.0, 266.0, 23.0 ], @@ -8246,11 +8885,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header": { "rect": [ 350.0, - 231.0, + 138.0, 266.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { + "rect": [ + 432.0, + 138.0, + 184.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 228.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 138.0, + 184.0, + 59.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 201.0, + 184.0, 23.0 ], "visible": false @@ -8267,7 +8969,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LeastPlayedList": { "rect": [ 350.0, - 369.0, + 366.0, 266.0, 0.0 ], @@ -8276,7 +8978,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedHeader": { "rect": [ 350.0, - 258.0, + 255.0, 266.0, 26.0 ], @@ -8285,7 +8987,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 77.0 ], @@ -8294,7 +8996,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 23.0 ], @@ -8303,7 +9005,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#1": { "rect": [ 350.0, - 315.0, + 312.0, 266.0, 23.0 ], @@ -8312,25 +9014,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#2": { "rect": [ 350.0, - 342.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/NameLabel": { - "rect": [ - 350.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/PlayedLabel": { - "rect": [ - 350.0, - 204.0, + 339.0, 266.0, 23.0 ], @@ -8354,11 +9038,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header": { "rect": [ 44.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { + "rect": [ + 126.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -8375,7 +9122,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LeastPlayedList": { "rect": [ 44.0, - 698.0, + 686.0, 266.0, 0.0 ], @@ -8384,7 +9131,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedHeader": { "rect": [ 44.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -8393,7 +9140,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 50.0 ], @@ -8402,7 +9149,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#0": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -8411,25 +9158,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#1": { "rect": [ 44.0, - 671.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/NameLabel": { - "rect": [ - 44.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/PlayedLabel": { - "rect": [ - 44.0, - 560.0, + 659.0, 266.0, 23.0 ], @@ -8453,11 +9182,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header": { "rect": [ 350.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { + "rect": [ + 432.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -8474,7 +9266,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LeastPlayedList": { "rect": [ 350.0, - 671.0, + 659.0, 266.0, 0.0 ], @@ -8483,7 +9275,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedHeader": { "rect": [ 350.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -8492,7 +9284,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList": { "rect": [ 350.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -8501,25 +9293,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 644.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/NameLabel": { - "rect": [ - 350.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/PlayedLabel": { - "rect": [ - 350.0, - 560.0, + 632.0, 266.0, 23.0 ], @@ -8543,11 +9317,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header": { "rect": [ 1574.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1656.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1656.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -8564,7 +9401,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LeastPlayedList": { "rect": [ 1574.0, - 315.0, + 303.0, 266.0, 0.0 ], @@ -8573,7 +9410,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedHeader": { "rect": [ 1574.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -8582,7 +9419,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList": { "rect": [ 1574.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -8591,25 +9428,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#0": { "rect": [ 1574.0, - 288.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/NameLabel": { - "rect": [ - 1574.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/PlayedLabel": { - "rect": [ - 1574.0, - 204.0, + 276.0, 266.0, 23.0 ], @@ -8633,15 +9452,78 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header": { "rect": [ 44.0, 138.0, + 266.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { + "rect": [ + 126.0, + 138.0, + 184.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 138.0, 1.0, 23.0 ], "visible": false }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 192.0, + 184.0, + 23.0 + ], + "visible": false + }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedHeader": { "rect": [ 44.0, @@ -8654,7 +9536,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 265.0, + 276.0, 266.0, 0.0 ], @@ -8663,7 +9545,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 231.0, + 242.0, 266.0, 26.0 ], @@ -8672,30 +9554,12 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 261.0, + 272.0, 266.0, 0.0 ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/NameLabel": { - "rect": [ - 44.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/PlayedLabel": { - "rect": [ - 44.0, - 204.0, - 266.0, - 23.0 - ], - "visible": false - }, "StatisticsView/SongList": { "rect": [ 32.0, @@ -14053,33 +14917,204 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPicture": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPlaceholder": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info": { + "rect": [ + 790.0, + 204.0, + 500.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "rect": [ + 790.0, + 204.0, + 110.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesTitle": { + "rect": [ + 790.0, + 204.0, + 125.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/SummaryLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 23.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 2630.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow": { + "rect": [ + 630.0, + 420.0, + 132.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/AddNameButton": { + "rect": [ + 630.0, + 420.0, + 55.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/NewNameField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons": { + "rect": [ + 630.0, + 420.0, + 148.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/CancelNamesButton": { + "rect": [ + 630.0, + 420.0, + 76.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/SaveNamesButton": { + "rect": [ + 630.0, + 420.0, + 60.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 2493.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.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, + 221.0, + 35.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, - 692.0 + 504.0 ], "visible": false }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList": { "rect": [ 630.0, - 232.0, + 420.0, 0.0, 0.0 ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/SummaryLabel": { - "rect": [ - 630.0, - 194.0, - 660.0, - 26.0 - ], - "visible": false - }, "SoundtrackStatsPopup/StatsPanel/TitleLabel": { "rect": [ 630.0, @@ -18204,33 +19239,204 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPicture": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPlaceholder": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info": { + "rect": [ + 790.0, + 204.0, + 500.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "rect": [ + 790.0, + 204.0, + 110.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesTitle": { + "rect": [ + 790.0, + 204.0, + 125.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/SummaryLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 23.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 2630.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow": { + "rect": [ + 630.0, + 420.0, + 132.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/AddNameButton": { + "rect": [ + 630.0, + 420.0, + 55.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/NewNameField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons": { + "rect": [ + 630.0, + 420.0, + 148.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/CancelNamesButton": { + "rect": [ + 630.0, + 420.0, + 76.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/SaveNamesButton": { + "rect": [ + 630.0, + 420.0, + 60.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 2493.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.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, + 221.0, + 35.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, - 692.0 + 504.0 ], "visible": false }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList": { "rect": [ 630.0, - 232.0, + 420.0, 0.0, 0.0 ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/SummaryLabel": { - "rect": [ - 630.0, - 194.0, - 660.0, - 26.0 - ], - "visible": false - }, "SoundtrackStatsPopup/StatsPanel/TitleLabel": { "rect": [ 630.0, @@ -21952,19 +23158,199 @@ ], "visible": true }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPicture": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPlaceholder": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info": { + "rect": [ + 790.0, + 204.0, + 500.0, + 200.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/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, + 2630.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow": { + "rect": [ + 630.0, + 420.0, + 132.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/AddNameButton": { + "rect": [ + 630.0, + 420.0, + 55.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/NewNameField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons": { + "rect": [ + 630.0, + 420.0, + 148.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/CancelNamesButton": { + "rect": [ + 630.0, + 420.0, + 76.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/SaveNamesButton": { + "rect": [ + 630.0, + 420.0, + 60.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 2493.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.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, + 221.0, + 35.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, - 692.0 + 504.0 ], "visible": true }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, 176.0 ], @@ -21973,7 +23359,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#0": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, 32.0 ], @@ -21982,7 +23368,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#0/#0": { "rect": [ 638.0, - 234.0, + 422.0, 644.0, 28.0 ], @@ -21991,7 +23377,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#1": { "rect": [ 630.0, - 268.0, + 456.0, 660.0, 32.0 ], @@ -22000,7 +23386,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#1/#0": { "rect": [ 638.0, - 270.0, + 458.0, 644.0, 28.0 ], @@ -22009,7 +23395,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#2": { "rect": [ 630.0, - 304.0, + 492.0, 660.0, 32.0 ], @@ -22018,7 +23404,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#2/#0": { "rect": [ 638.0, - 306.0, + 494.0, 644.0, 28.0 ], @@ -22027,7 +23413,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#3": { "rect": [ 630.0, - 340.0, + 528.0, 660.0, 32.0 ], @@ -22036,7 +23422,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#3/#0": { "rect": [ 638.0, - 342.0, + 530.0, 644.0, 28.0 ], @@ -22045,7 +23431,7 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#4": { "rect": [ 630.0, - 376.0, + 564.0, 660.0, 32.0 ], @@ -22054,21 +23440,401 @@ "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList/#4/#0": { "rect": [ 638.0, - 378.0, + 566.0, 644.0, 28.0 ], "visible": true }, - "SoundtrackStatsPopup/StatsPanel/SummaryLabel": { + "SoundtrackStatsPopup/StatsPanel/TitleLabel": { "rect": [ 630.0, - 194.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/StatsPanel": { + "rect": [ + 610.0, + 140.0, + 700.0, + 800.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPicture": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPlaceholder": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info": { + "rect": [ + 790.0, + 204.0, + 500.0, + 200.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/Info/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": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow": { + "rect": [ + 630.0, + 852.0, + 660.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/AddNameButton": { + "rect": [ + 1235.0, + 852.0, + 55.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/NewNameField": { + "rect": [ + 630.0, + 852.0, + 597.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons": { + "rect": [ + 630.0, + 893.0, + 660.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/CancelNamesButton": { + "rect": [ + 1142.0, + 893.0, + 76.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/SaveNamesButton": { + "rect": [ + 1230.0, + 893.0, + 60.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorHelp": { + "rect": [ + 630.0, + 465.0, + 660.0, + 49.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll": { + "rect": [ + 630.0, + 524.0, + 660.0, + 318.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames": { + "rect": [ + 630.0, + 524.0, + 660.0, + 74.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#0": { + "rect": [ + 630.0, + 524.0, + 660.0, + 35.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#0/#0": { + "rect": [ + 638.0, + 526.0, + 644.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#0/#0/#0": { + "rect": [ + 638.0, + 530.0, + 553.0, + 23.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#0/#0/#1": { + "rect": [ + 1195.0, + 526.0, + 87.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#1": { + "rect": [ + 630.0, + 563.0, + 660.0, + 35.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#1/#0": { + "rect": [ + 638.0, + 565.0, + 644.0, + 31.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#1/#0/#0": { + "rect": [ + 638.0, + 569.0, + 553.0, + 23.0 + ], + "visible": true + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames/#1/#0/#1": { + "rect": [ + 1195.0, + 565.0, + 87.0, + 31.0 + ], + "visible": true + }, + "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": true + }, + "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, @@ -26525,33 +28291,204 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPicture": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPlaceholder": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info": { + "rect": [ + 790.0, + 204.0, + 500.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "rect": [ + 790.0, + 204.0, + 110.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesTitle": { + "rect": [ + 790.0, + 204.0, + 125.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/SummaryLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 23.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 2630.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow": { + "rect": [ + 630.0, + 420.0, + 132.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/AddNameButton": { + "rect": [ + 630.0, + 420.0, + 55.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/NewNameField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons": { + "rect": [ + 630.0, + 420.0, + 148.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/CancelNamesButton": { + "rect": [ + 630.0, + 420.0, + 76.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/SaveNamesButton": { + "rect": [ + 630.0, + 420.0, + 60.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 2493.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.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, + 221.0, + 35.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, - 692.0 + 504.0 ], "visible": false }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList": { "rect": [ 630.0, - 232.0, + 420.0, 0.0, 0.0 ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/SummaryLabel": { - "rect": [ - 630.0, - 194.0, - 660.0, - 26.0 - ], - "visible": false - }, "SoundtrackStatsPopup/StatsPanel/TitleLabel": { "rect": [ 630.0, @@ -27274,11 +29211,74 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header": { "rect": [ 350.0, - 231.0, + 138.0, 266.0, + 113.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { + "rect": [ + 432.0, + 138.0, + 184.0, + 113.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 228.0, + 184.0, + 23.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 138.0, + 184.0, + 59.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 201.0, + 184.0, 23.0 ], "visible": true @@ -27295,7 +29295,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LeastPlayedList": { "rect": [ 350.0, - 369.0, + 366.0, 266.0, 0.0 ], @@ -27304,7 +29304,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedHeader": { "rect": [ 350.0, - 258.0, + 255.0, 266.0, 26.0 ], @@ -27313,7 +29313,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 77.0 ], @@ -27322,7 +29322,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 23.0 ], @@ -27331,7 +29331,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#1": { "rect": [ 350.0, - 315.0, + 312.0, 266.0, 23.0 ], @@ -27340,25 +29340,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#2": { "rect": [ 350.0, - 342.0, - 266.0, - 23.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/NameLabel": { - "rect": [ - 350.0, - 138.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/PlayedLabel": { - "rect": [ - 350.0, - 204.0, + 339.0, 266.0, 23.0 ], @@ -27382,11 +29364,74 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header": { "rect": [ 656.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { + "rect": [ + 738.0, + 138.0, + 184.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 219.0, + 184.0, + 23.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 138.0, + 184.0, + 50.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 192.0, + 184.0, 23.0 ], "visible": true @@ -27403,7 +29448,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LeastPlayedList": { "rect": [ 656.0, - 369.0, + 357.0, 266.0, 0.0 ], @@ -27412,7 +29457,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedHeader": { "rect": [ 656.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -27421,7 +29466,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 77.0 ], @@ -27430,7 +29475,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -27439,7 +29484,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -27448,25 +29493,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 342.0, - 266.0, - 23.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/NameLabel": { - "rect": [ - 656.0, - 138.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/PlayedLabel": { - "rect": [ - 656.0, - 204.0, + 330.0, 266.0, 23.0 ], @@ -27490,11 +29517,74 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header": { "rect": [ 962.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 219.0, + 184.0, + 23.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 50.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 192.0, + 184.0, 23.0 ], "visible": true @@ -27511,7 +29601,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LeastPlayedList": { "rect": [ 962.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -27520,7 +29610,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedHeader": { "rect": [ 962.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -27529,7 +29619,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -27538,7 +29628,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -27547,7 +29637,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -27556,7 +29646,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -27565,25 +29655,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 369.0, - 266.0, - 23.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/NameLabel": { - "rect": [ - 962.0, - 138.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/PlayedLabel": { - "rect": [ - 962.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -27607,11 +29679,74 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header": { "rect": [ 1268.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1350.0, + 219.0, + 184.0, + 23.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 50.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1350.0, + 192.0, + 184.0, 23.0 ], "visible": true @@ -27628,7 +29763,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LeastPlayedList": { "rect": [ 1268.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -27637,7 +29772,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedHeader": { "rect": [ 1268.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -27646,7 +29781,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -27655,7 +29790,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#0": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -27664,7 +29799,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#1": { "rect": [ 1268.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -27673,7 +29808,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#2": { "rect": [ 1268.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -27682,25 +29817,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#3": { "rect": [ 1268.0, - 369.0, - 266.0, - 23.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/NameLabel": { - "rect": [ - 1268.0, - 138.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/PlayedLabel": { - "rect": [ - 1268.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -27724,11 +29841,74 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header": { "rect": [ 1574.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1656.0, + 219.0, + 184.0, + 23.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 50.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1656.0, + 192.0, + 184.0, 23.0 ], "visible": true @@ -27745,7 +29925,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LeastPlayedList": { "rect": [ 1574.0, - 315.0, + 303.0, 266.0, 0.0 ], @@ -27754,7 +29934,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedHeader": { "rect": [ 1574.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -27763,7 +29943,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList": { "rect": [ 1574.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -27772,25 +29952,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#0": { "rect": [ 1574.0, - 288.0, - 266.0, - 23.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/NameLabel": { - "rect": [ - 1574.0, - 138.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/PlayedLabel": { - "rect": [ - 1574.0, - 204.0, + 276.0, 266.0, 23.0 ], @@ -27814,11 +29976,74 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header": { "rect": [ 44.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { + "rect": [ + 126.0, + 494.0, + 184.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 575.0, + 184.0, + 23.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 494.0, + 184.0, + 50.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 548.0, + 184.0, 23.0 ], "visible": true @@ -27835,7 +30060,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LeastPlayedList": { "rect": [ 44.0, - 698.0, + 686.0, 266.0, 0.0 ], @@ -27844,7 +30069,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedHeader": { "rect": [ 44.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -27853,7 +30078,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 50.0 ], @@ -27862,7 +30087,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#0": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -27871,25 +30096,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#1": { "rect": [ 44.0, - 671.0, - 266.0, - 23.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/NameLabel": { - "rect": [ - 44.0, - 494.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/PlayedLabel": { - "rect": [ - 44.0, - 560.0, + 659.0, 266.0, 23.0 ], @@ -27913,11 +30120,74 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header": { "rect": [ 350.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { + "rect": [ + 432.0, + 494.0, + 184.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 575.0, + 184.0, + 23.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 494.0, + 184.0, + 50.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 548.0, + 184.0, 23.0 ], "visible": true @@ -27934,7 +30204,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LeastPlayedList": { "rect": [ 350.0, - 671.0, + 659.0, 266.0, 0.0 ], @@ -27943,7 +30213,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedHeader": { "rect": [ 350.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -27952,7 +30222,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList": { "rect": [ 350.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -27961,25 +30231,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 644.0, - 266.0, - 23.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/NameLabel": { - "rect": [ - 350.0, - 494.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/PlayedLabel": { - "rect": [ - 350.0, - 560.0, + 632.0, 266.0, 23.0 ], @@ -28003,11 +30255,74 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header": { "rect": [ 656.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { + "rect": [ + 738.0, + 494.0, + 184.0, + 104.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 575.0, + 184.0, + 23.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 494.0, + 184.0, + 50.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 548.0, + 184.0, 23.0 ], "visible": true @@ -28024,7 +30339,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LeastPlayedList": { "rect": [ 656.0, - 779.0, + 767.0, 266.0, 0.0 ], @@ -28033,7 +30348,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedHeader": { "rect": [ 656.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -28042,7 +30357,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 131.0 ], @@ -28051,7 +30366,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -28060,7 +30375,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 671.0, + 659.0, 266.0, 23.0 ], @@ -28069,7 +30384,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 698.0, + 686.0, 266.0, 23.0 ], @@ -28078,7 +30393,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#3": { "rect": [ 656.0, - 725.0, + 713.0, 266.0, 23.0 ], @@ -28087,25 +30402,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#4": { "rect": [ 656.0, - 752.0, - 266.0, - 23.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/NameLabel": { - "rect": [ - 656.0, - 494.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/PlayedLabel": { - "rect": [ - 656.0, - 560.0, + 740.0, 266.0, 23.0 ], @@ -28129,11 +30426,74 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/#9/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header": { "rect": [ 962.0, - 587.0, + 494.0, 266.0, + 113.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 113.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 584.0, + 184.0, + 23.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 59.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 557.0, + 184.0, 23.0 ], "visible": true @@ -28150,7 +30510,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/LeastPlayedList": { "rect": [ 962.0, - 779.0, + 776.0, 266.0, 0.0 ], @@ -28159,7 +30519,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedHeader": { "rect": [ 962.0, - 614.0, + 611.0, 266.0, 26.0 ], @@ -28168,7 +30528,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 131.0 ], @@ -28177,7 +30537,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 23.0 ], @@ -28186,7 +30546,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 671.0, + 668.0, 266.0, 23.0 ], @@ -28195,7 +30555,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 698.0, + 695.0, 266.0, 23.0 ], @@ -28204,7 +30564,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 725.0, + 722.0, 266.0, 23.0 ], @@ -28213,25 +30573,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList/#4": { "rect": [ 962.0, - 752.0, - 266.0, - 23.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#9/VBox/NameLabel": { - "rect": [ - 962.0, - 494.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/#9/VBox/PlayedLabel": { - "rect": [ - 962.0, - 560.0, + 749.0, 266.0, 23.0 ], @@ -28255,15 +30597,78 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header": { "rect": [ 44.0, 138.0, + 266.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { + "rect": [ + 126.0, + 138.0, + 184.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 138.0, 1.0, 23.0 ], "visible": false }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 138.0, + 184.0, + 50.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 192.0, + 184.0, + 23.0 + ], + "visible": true + }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedHeader": { "rect": [ 44.0, @@ -28276,7 +30681,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 265.0, + 276.0, 266.0, 0.0 ], @@ -28285,7 +30690,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 231.0, + 242.0, 266.0, 26.0 ], @@ -28294,30 +30699,12 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 261.0, + 272.0, 266.0, 0.0 ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/NameLabel": { - "rect": [ - 44.0, - 138.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/PlayedLabel": { - "rect": [ - 44.0, - 204.0, - 266.0, - 23.0 - ], - "visible": true - }, "StatisticsView/SongList": { "rect": [ 32.0, @@ -28617,11 +31004,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header": { "rect": [ 350.0, - 231.0, + 138.0, 266.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { + "rect": [ + 432.0, + 138.0, + 184.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 228.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 138.0, + 184.0, + 59.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 201.0, + 184.0, 23.0 ], "visible": false @@ -28638,7 +31088,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LeastPlayedList": { "rect": [ 350.0, - 369.0, + 366.0, 266.0, 0.0 ], @@ -28647,7 +31097,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedHeader": { "rect": [ 350.0, - 258.0, + 255.0, 266.0, 26.0 ], @@ -28656,7 +31106,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 77.0 ], @@ -28665,7 +31115,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 23.0 ], @@ -28674,7 +31124,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#1": { "rect": [ 350.0, - 315.0, + 312.0, 266.0, 23.0 ], @@ -28683,25 +31133,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#2": { "rect": [ 350.0, - 342.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/NameLabel": { - "rect": [ - 350.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/PlayedLabel": { - "rect": [ - 350.0, - 204.0, + 339.0, 266.0, 23.0 ], @@ -28725,11 +31157,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header": { "rect": [ 656.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { + "rect": [ + 738.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -28746,7 +31241,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LeastPlayedList": { "rect": [ 656.0, - 369.0, + 357.0, 266.0, 0.0 ], @@ -28755,7 +31250,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedHeader": { "rect": [ 656.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -28764,7 +31259,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 77.0 ], @@ -28773,7 +31268,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -28782,7 +31277,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -28791,25 +31286,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 342.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/NameLabel": { - "rect": [ - 656.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/PlayedLabel": { - "rect": [ - 656.0, - 204.0, + 330.0, 266.0, 23.0 ], @@ -28833,11 +31310,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header": { "rect": [ 962.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -28854,7 +31394,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LeastPlayedList": { "rect": [ 962.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -28863,7 +31403,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedHeader": { "rect": [ 962.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -28872,7 +31412,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -28881,7 +31421,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -28890,7 +31430,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -28899,7 +31439,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -28908,25 +31448,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 369.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/NameLabel": { - "rect": [ - 962.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/PlayedLabel": { - "rect": [ - 962.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -28950,11 +31472,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header": { "rect": [ 1268.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1350.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1350.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -28971,7 +31556,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LeastPlayedList": { "rect": [ 1268.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -28980,7 +31565,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedHeader": { "rect": [ 1268.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -28989,7 +31574,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -28998,7 +31583,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#0": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -29007,7 +31592,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#1": { "rect": [ 1268.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -29016,7 +31601,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#2": { "rect": [ 1268.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -29025,25 +31610,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#3": { "rect": [ 1268.0, - 369.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/NameLabel": { - "rect": [ - 1268.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/PlayedLabel": { - "rect": [ - 1268.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -29067,11 +31634,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header": { "rect": [ 1574.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1656.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1656.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -29088,7 +31718,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LeastPlayedList": { "rect": [ 1574.0, - 315.0, + 303.0, 266.0, 0.0 ], @@ -29097,7 +31727,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedHeader": { "rect": [ 1574.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -29106,7 +31736,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList": { "rect": [ 1574.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -29115,25 +31745,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#0": { "rect": [ 1574.0, - 288.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/NameLabel": { - "rect": [ - 1574.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/PlayedLabel": { - "rect": [ - 1574.0, - 204.0, + 276.0, 266.0, 23.0 ], @@ -29157,11 +31769,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header": { "rect": [ 44.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { + "rect": [ + 126.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -29178,7 +31853,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LeastPlayedList": { "rect": [ 44.0, - 698.0, + 686.0, 266.0, 0.0 ], @@ -29187,7 +31862,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedHeader": { "rect": [ 44.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -29196,7 +31871,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 50.0 ], @@ -29205,7 +31880,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#0": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -29214,25 +31889,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#1": { "rect": [ 44.0, - 671.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/NameLabel": { - "rect": [ - 44.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/PlayedLabel": { - "rect": [ - 44.0, - 560.0, + 659.0, 266.0, 23.0 ], @@ -29256,11 +31913,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header": { "rect": [ 350.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { + "rect": [ + 432.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -29277,7 +31997,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LeastPlayedList": { "rect": [ 350.0, - 671.0, + 659.0, 266.0, 0.0 ], @@ -29286,7 +32006,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedHeader": { "rect": [ 350.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -29295,7 +32015,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList": { "rect": [ 350.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -29304,25 +32024,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 644.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/NameLabel": { - "rect": [ - 350.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/PlayedLabel": { - "rect": [ - 350.0, - 560.0, + 632.0, 266.0, 23.0 ], @@ -29346,11 +32048,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header": { "rect": [ 656.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { + "rect": [ + 738.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -29367,7 +32132,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LeastPlayedList": { "rect": [ 656.0, - 779.0, + 767.0, 266.0, 0.0 ], @@ -29376,7 +32141,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedHeader": { "rect": [ 656.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -29385,7 +32150,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 131.0 ], @@ -29394,7 +32159,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -29403,7 +32168,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 671.0, + 659.0, 266.0, 23.0 ], @@ -29412,7 +32177,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 698.0, + 686.0, 266.0, 23.0 ], @@ -29421,7 +32186,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#3": { "rect": [ 656.0, - 725.0, + 713.0, 266.0, 23.0 ], @@ -29430,25 +32195,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#4": { "rect": [ 656.0, - 752.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/NameLabel": { - "rect": [ - 656.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/PlayedLabel": { - "rect": [ - 656.0, - 560.0, + 740.0, 266.0, 23.0 ], @@ -29472,11 +32219,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#9/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header": { "rect": [ 962.0, - 587.0, + 494.0, 266.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 584.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 59.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#9/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 557.0, + 184.0, 23.0 ], "visible": false @@ -29493,7 +32303,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/LeastPlayedList": { "rect": [ 962.0, - 779.0, + 776.0, 266.0, 0.0 ], @@ -29502,7 +32312,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedHeader": { "rect": [ 962.0, - 614.0, + 611.0, 266.0, 26.0 ], @@ -29511,7 +32321,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 131.0 ], @@ -29520,7 +32330,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 23.0 ], @@ -29529,7 +32339,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 671.0, + 668.0, 266.0, 23.0 ], @@ -29538,7 +32348,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 698.0, + 695.0, 266.0, 23.0 ], @@ -29547,7 +32357,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 725.0, + 722.0, 266.0, 23.0 ], @@ -29556,25 +32366,7 @@ "StatisticsView/ScrollContainer/CardGrid/#9/VBox/MostPlayedList/#4": { "rect": [ 962.0, - 752.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#9/VBox/NameLabel": { - "rect": [ - 962.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#9/VBox/PlayedLabel": { - "rect": [ - 962.0, - 560.0, + 749.0, 266.0, 23.0 ], @@ -29598,15 +32390,78 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header": { "rect": [ 44.0, 138.0, + 266.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { + "rect": [ + 126.0, + 138.0, + 184.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 138.0, 1.0, 23.0 ], "visible": false }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 138.0, + 184.0, + 50.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 192.0, + 184.0, + 23.0 + ], + "visible": true + }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedHeader": { "rect": [ 44.0, @@ -29619,7 +32474,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 265.0, + 276.0, 266.0, 0.0 ], @@ -29628,7 +32483,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 231.0, + 242.0, 266.0, 26.0 ], @@ -29637,30 +32492,12 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 261.0, + 272.0, 266.0, 0.0 ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/NameLabel": { - "rect": [ - 44.0, - 138.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/PlayedLabel": { - "rect": [ - 44.0, - 204.0, - 266.0, - 23.0 - ], - "visible": true - }, "StatisticsView/SongList": { "rect": [ 32.0, @@ -29960,11 +32797,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header": { "rect": [ 656.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info": { + "rect": [ + 738.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -29981,7 +32881,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/LeastPlayedList": { "rect": [ 656.0, - 779.0, + 767.0, 266.0, 0.0 ], @@ -29990,7 +32890,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedHeader": { "rect": [ 656.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -29999,7 +32899,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 131.0 ], @@ -30008,7 +32908,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -30017,7 +32917,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 671.0, + 659.0, 266.0, 23.0 ], @@ -30026,7 +32926,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 698.0, + 686.0, 266.0, 23.0 ], @@ -30035,7 +32935,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#3": { "rect": [ 656.0, - 725.0, + 713.0, 266.0, 23.0 ], @@ -30044,25 +32944,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#4": { "rect": [ 656.0, - 752.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/NameLabel": { - "rect": [ - 656.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/PlayedLabel": { - "rect": [ - 656.0, - 560.0, + 740.0, 266.0, 23.0 ], @@ -30086,11 +32968,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header": { "rect": [ 962.0, - 587.0, + 494.0, 266.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 584.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 59.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 557.0, + 184.0, 23.0 ], "visible": false @@ -30107,7 +33052,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LeastPlayedList": { "rect": [ 962.0, - 779.0, + 776.0, 266.0, 0.0 ], @@ -30116,7 +33061,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedHeader": { "rect": [ 962.0, - 614.0, + 611.0, 266.0, 26.0 ], @@ -30125,7 +33070,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 131.0 ], @@ -30134,7 +33079,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 23.0 ], @@ -30143,7 +33088,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 671.0, + 668.0, 266.0, 23.0 ], @@ -30152,7 +33097,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 698.0, + 695.0, 266.0, 23.0 ], @@ -30161,7 +33106,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 725.0, + 722.0, 266.0, 23.0 ], @@ -30170,25 +33115,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#4": { "rect": [ 962.0, - 752.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/NameLabel": { - "rect": [ - 962.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/PlayedLabel": { - "rect": [ - 962.0, - 560.0, + 749.0, 266.0, 23.0 ], @@ -30212,11 +33139,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header": { "rect": [ 1268.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1350.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1350.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -30233,7 +33223,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LeastPlayedList": { "rect": [ 1268.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -30242,7 +33232,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedHeader": { "rect": [ 1268.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -30251,7 +33241,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -30260,7 +33250,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#0": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -30269,7 +33259,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#1": { "rect": [ 1268.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -30278,7 +33268,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#2": { "rect": [ 1268.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -30287,25 +33277,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#3": { "rect": [ 1268.0, - 369.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/NameLabel": { - "rect": [ - 1268.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/PlayedLabel": { - "rect": [ - 1268.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -30329,11 +33301,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header": { "rect": [ 962.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -30350,7 +33385,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LeastPlayedList": { "rect": [ 962.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -30359,7 +33394,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedHeader": { "rect": [ 962.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -30368,7 +33403,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -30377,7 +33412,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -30386,7 +33421,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -30395,7 +33430,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -30404,25 +33439,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 369.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/NameLabel": { - "rect": [ - 962.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/PlayedLabel": { - "rect": [ - 962.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -30446,11 +33463,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header": { "rect": [ 656.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { + "rect": [ + 738.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -30467,7 +33547,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LeastPlayedList": { "rect": [ 656.0, - 369.0, + 357.0, 266.0, 0.0 ], @@ -30476,7 +33556,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedHeader": { "rect": [ 656.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -30485,7 +33565,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 77.0 ], @@ -30494,7 +33574,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -30503,7 +33583,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -30512,25 +33592,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 342.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/NameLabel": { - "rect": [ - 656.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/PlayedLabel": { - "rect": [ - 656.0, - 204.0, + 330.0, 266.0, 23.0 ], @@ -30554,11 +33616,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header": { "rect": [ 350.0, - 231.0, + 138.0, 266.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { + "rect": [ + 432.0, + 138.0, + 184.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 228.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 138.0, + 184.0, + 59.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 201.0, + 184.0, 23.0 ], "visible": false @@ -30575,7 +33700,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LeastPlayedList": { "rect": [ 350.0, - 369.0, + 366.0, 266.0, 0.0 ], @@ -30584,7 +33709,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedHeader": { "rect": [ 350.0, - 258.0, + 255.0, 266.0, 26.0 ], @@ -30593,7 +33718,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 77.0 ], @@ -30602,7 +33727,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 23.0 ], @@ -30611,7 +33736,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#1": { "rect": [ 350.0, - 315.0, + 312.0, 266.0, 23.0 ], @@ -30620,25 +33745,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#2": { "rect": [ 350.0, - 342.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/NameLabel": { - "rect": [ - 350.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/PlayedLabel": { - "rect": [ - 350.0, - 204.0, + 339.0, 266.0, 23.0 ], @@ -30662,11 +33769,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header": { "rect": [ 44.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { + "rect": [ + 126.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -30683,7 +33853,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LeastPlayedList": { "rect": [ 44.0, - 698.0, + 686.0, 266.0, 0.0 ], @@ -30692,7 +33862,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedHeader": { "rect": [ 44.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -30701,7 +33871,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 50.0 ], @@ -30710,7 +33880,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#0": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -30719,25 +33889,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#1": { "rect": [ 44.0, - 671.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/NameLabel": { - "rect": [ - 44.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/PlayedLabel": { - "rect": [ - 44.0, - 560.0, + 659.0, 266.0, 23.0 ], @@ -30761,11 +33913,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header": { "rect": [ 350.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { + "rect": [ + 432.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -30782,7 +33997,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LeastPlayedList": { "rect": [ 350.0, - 671.0, + 659.0, 266.0, 0.0 ], @@ -30791,7 +34006,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedHeader": { "rect": [ 350.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -30800,7 +34015,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList": { "rect": [ 350.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -30809,25 +34024,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 644.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/NameLabel": { - "rect": [ - 350.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/PlayedLabel": { - "rect": [ - 350.0, - 560.0, + 632.0, 266.0, 23.0 ], @@ -30851,11 +34048,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header": { "rect": [ 1574.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1656.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1656.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -30872,7 +34132,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LeastPlayedList": { "rect": [ 1574.0, - 315.0, + 303.0, 266.0, 0.0 ], @@ -30881,7 +34141,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedHeader": { "rect": [ 1574.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -30890,7 +34150,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList": { "rect": [ 1574.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -30899,25 +34159,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#0": { "rect": [ 1574.0, - 288.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/NameLabel": { - "rect": [ - 1574.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/PlayedLabel": { - "rect": [ - 1574.0, - 204.0, + 276.0, 266.0, 23.0 ], @@ -30941,15 +34183,78 @@ ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header": { "rect": [ 44.0, 138.0, + 266.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { + "rect": [ + 126.0, + 138.0, + 184.0, + 100.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 138.0, 1.0, 23.0 ], "visible": false }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 138.0, + 184.0, + 50.0 + ], + "visible": true + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 192.0, + 184.0, + 23.0 + ], + "visible": true + }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedHeader": { "rect": [ 44.0, @@ -30962,7 +34267,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 265.0, + 276.0, 266.0, 0.0 ], @@ -30971,7 +34276,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 231.0, + 242.0, 266.0, 26.0 ], @@ -30980,30 +34285,12 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 261.0, + 272.0, 266.0, 0.0 ], "visible": true }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/NameLabel": { - "rect": [ - 44.0, - 138.0, - 266.0, - 62.0 - ], - "visible": true - }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/PlayedLabel": { - "rect": [ - 44.0, - 204.0, - 266.0, - 23.0 - ], - "visible": true - }, "StatisticsView/SongList": { "rect": [ 32.0, @@ -31303,11 +34590,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header": { "rect": [ 656.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info": { + "rect": [ + 738.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -31324,7 +34674,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/LeastPlayedList": { "rect": [ 656.0, - 779.0, + 767.0, 266.0, 0.0 ], @@ -31333,7 +34683,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedHeader": { "rect": [ 656.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -31342,7 +34692,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 131.0 ], @@ -31351,7 +34701,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -31360,7 +34710,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 671.0, + 659.0, 266.0, 23.0 ], @@ -31369,7 +34719,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 698.0, + 686.0, 266.0, 23.0 ], @@ -31378,7 +34728,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#3": { "rect": [ 656.0, - 725.0, + 713.0, 266.0, 23.0 ], @@ -31387,25 +34737,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#4": { "rect": [ 656.0, - 752.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/NameLabel": { - "rect": [ - 656.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/PlayedLabel": { - "rect": [ - 656.0, - 560.0, + 740.0, 266.0, 23.0 ], @@ -31429,11 +34761,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header": { "rect": [ 962.0, - 587.0, + 494.0, 266.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 584.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 59.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 557.0, + 184.0, 23.0 ], "visible": false @@ -31450,7 +34845,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LeastPlayedList": { "rect": [ 962.0, - 779.0, + 776.0, 266.0, 0.0 ], @@ -31459,7 +34854,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedHeader": { "rect": [ 962.0, - 614.0, + 611.0, 266.0, 26.0 ], @@ -31468,7 +34863,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 131.0 ], @@ -31477,7 +34872,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 23.0 ], @@ -31486,7 +34881,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 671.0, + 668.0, 266.0, 23.0 ], @@ -31495,7 +34890,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 698.0, + 695.0, 266.0, 23.0 ], @@ -31504,7 +34899,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 725.0, + 722.0, 266.0, 23.0 ], @@ -31513,25 +34908,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#4": { "rect": [ 962.0, - 752.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/NameLabel": { - "rect": [ - 962.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/PlayedLabel": { - "rect": [ - 962.0, - 560.0, + 749.0, 266.0, 23.0 ], @@ -31555,11 +34932,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header": { "rect": [ 1268.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1350.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1350.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -31576,7 +35016,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LeastPlayedList": { "rect": [ 1268.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -31585,7 +35025,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedHeader": { "rect": [ 1268.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -31594,7 +35034,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -31603,7 +35043,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#0": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -31612,7 +35052,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#1": { "rect": [ 1268.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -31621,7 +35061,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#2": { "rect": [ 1268.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -31630,25 +35070,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#3": { "rect": [ 1268.0, - 369.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/NameLabel": { - "rect": [ - 1268.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/PlayedLabel": { - "rect": [ - 1268.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -31672,11 +35094,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header": { "rect": [ 962.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -31693,7 +35178,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LeastPlayedList": { "rect": [ 962.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -31702,7 +35187,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedHeader": { "rect": [ 962.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -31711,7 +35196,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -31720,7 +35205,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -31729,7 +35214,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -31738,7 +35223,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -31747,25 +35232,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 369.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/NameLabel": { - "rect": [ - 962.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/PlayedLabel": { - "rect": [ - 962.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -31789,11 +35256,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header": { "rect": [ 656.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { + "rect": [ + 738.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -31810,7 +35340,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LeastPlayedList": { "rect": [ 656.0, - 369.0, + 357.0, 266.0, 0.0 ], @@ -31819,7 +35349,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedHeader": { "rect": [ 656.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -31828,7 +35358,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 77.0 ], @@ -31837,7 +35367,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -31846,7 +35376,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -31855,25 +35385,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 342.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/NameLabel": { - "rect": [ - 656.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/PlayedLabel": { - "rect": [ - 656.0, - 204.0, + 330.0, 266.0, 23.0 ], @@ -31897,11 +35409,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header": { "rect": [ 350.0, - 231.0, + 138.0, 266.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { + "rect": [ + 432.0, + 138.0, + 184.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 228.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 138.0, + 184.0, + 59.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 201.0, + 184.0, 23.0 ], "visible": false @@ -31918,7 +35493,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LeastPlayedList": { "rect": [ 350.0, - 369.0, + 366.0, 266.0, 0.0 ], @@ -31927,7 +35502,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedHeader": { "rect": [ 350.0, - 258.0, + 255.0, 266.0, 26.0 ], @@ -31936,7 +35511,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 77.0 ], @@ -31945,7 +35520,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 23.0 ], @@ -31954,7 +35529,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#1": { "rect": [ 350.0, - 315.0, + 312.0, 266.0, 23.0 ], @@ -31963,25 +35538,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#2": { "rect": [ 350.0, - 342.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/NameLabel": { - "rect": [ - 350.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/PlayedLabel": { - "rect": [ - 350.0, - 204.0, + 339.0, 266.0, 23.0 ], @@ -32005,11 +35562,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header": { "rect": [ 44.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { + "rect": [ + 126.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -32026,7 +35646,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LeastPlayedList": { "rect": [ 44.0, - 698.0, + 686.0, 266.0, 0.0 ], @@ -32035,7 +35655,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedHeader": { "rect": [ 44.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -32044,7 +35664,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 50.0 ], @@ -32053,7 +35673,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#0": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -32062,25 +35682,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#1": { "rect": [ 44.0, - 671.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/NameLabel": { - "rect": [ - 44.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/PlayedLabel": { - "rect": [ - 44.0, - 560.0, + 659.0, 266.0, 23.0 ], @@ -32104,11 +35706,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header": { "rect": [ 350.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { + "rect": [ + 432.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -32125,7 +35790,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LeastPlayedList": { "rect": [ 350.0, - 671.0, + 659.0, 266.0, 0.0 ], @@ -32134,7 +35799,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedHeader": { "rect": [ 350.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -32143,7 +35808,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList": { "rect": [ 350.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -32152,25 +35817,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 644.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/NameLabel": { - "rect": [ - 350.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/PlayedLabel": { - "rect": [ - 350.0, - 560.0, + 632.0, 266.0, 23.0 ], @@ -32194,11 +35841,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header": { "rect": [ 1574.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1656.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1656.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -32215,7 +35925,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LeastPlayedList": { "rect": [ 1574.0, - 315.0, + 303.0, 266.0, 0.0 ], @@ -32224,7 +35934,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedHeader": { "rect": [ 1574.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -32233,7 +35943,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList": { "rect": [ 1574.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -32242,25 +35952,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#0": { "rect": [ 1574.0, - 288.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/NameLabel": { - "rect": [ - 1574.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/PlayedLabel": { - "rect": [ - 1574.0, - 204.0, + 276.0, 266.0, 23.0 ], @@ -32284,15 +35976,78 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header": { "rect": [ 44.0, 138.0, + 266.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { + "rect": [ + 126.0, + 138.0, + 184.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 138.0, 1.0, 23.0 ], "visible": false }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 192.0, + 184.0, + 23.0 + ], + "visible": false + }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedHeader": { "rect": [ 44.0, @@ -32305,7 +36060,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 265.0, + 276.0, 266.0, 0.0 ], @@ -32314,7 +36069,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 231.0, + 242.0, 266.0, 26.0 ], @@ -32323,30 +36078,12 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 261.0, + 272.0, 266.0, 0.0 ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/NameLabel": { - "rect": [ - 44.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/PlayedLabel": { - "rect": [ - 44.0, - 204.0, - 266.0, - 23.0 - ], - "visible": false - }, "StatisticsView/SongList": { "rect": [ 32.0, @@ -33951,11 +37688,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header": { "rect": [ 656.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info": { + "rect": [ + 738.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#0/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -33972,7 +37772,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/LeastPlayedList": { "rect": [ 656.0, - 779.0, + 767.0, 266.0, 0.0 ], @@ -33981,7 +37781,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedHeader": { "rect": [ 656.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -33990,7 +37790,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 131.0 ], @@ -33999,7 +37799,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -34008,7 +37808,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 671.0, + 659.0, 266.0, 23.0 ], @@ -34017,7 +37817,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 698.0, + 686.0, 266.0, 23.0 ], @@ -34026,7 +37826,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#3": { "rect": [ 656.0, - 725.0, + 713.0, 266.0, 23.0 ], @@ -34035,25 +37835,7 @@ "StatisticsView/ScrollContainer/CardGrid/#0/VBox/MostPlayedList/#4": { "rect": [ 656.0, - 752.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/NameLabel": { - "rect": [ - 656.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#0/VBox/PlayedLabel": { - "rect": [ - 656.0, - 560.0, + 740.0, 266.0, 23.0 ], @@ -34077,11 +37859,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header": { "rect": [ 962.0, - 587.0, + 494.0, 266.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 494.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 584.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 494.0, + 184.0, + 59.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#1/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 557.0, + 184.0, 23.0 ], "visible": false @@ -34098,7 +37943,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/LeastPlayedList": { "rect": [ 962.0, - 779.0, + 776.0, 266.0, 0.0 ], @@ -34107,7 +37952,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedHeader": { "rect": [ 962.0, - 614.0, + 611.0, 266.0, 26.0 ], @@ -34116,7 +37961,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 131.0 ], @@ -34125,7 +37970,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 644.0, + 641.0, 266.0, 23.0 ], @@ -34134,7 +37979,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 671.0, + 668.0, 266.0, 23.0 ], @@ -34143,7 +37988,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 698.0, + 695.0, 266.0, 23.0 ], @@ -34152,7 +37997,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 725.0, + 722.0, 266.0, 23.0 ], @@ -34161,25 +38006,7 @@ "StatisticsView/ScrollContainer/CardGrid/#1/VBox/MostPlayedList/#4": { "rect": [ 962.0, - 752.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/NameLabel": { - "rect": [ - 962.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#1/VBox/PlayedLabel": { - "rect": [ - 962.0, - 560.0, + 749.0, 266.0, 23.0 ], @@ -34203,11 +38030,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header": { "rect": [ 1268.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1268.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1350.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/NameLabel": { + "rect": [ + 1350.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#2/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1350.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -34224,7 +38114,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/LeastPlayedList": { "rect": [ 1268.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -34233,7 +38123,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedHeader": { "rect": [ 1268.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -34242,7 +38132,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -34251,7 +38141,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#0": { "rect": [ 1268.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -34260,7 +38150,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#1": { "rect": [ 1268.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -34269,7 +38159,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#2": { "rect": [ 1268.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -34278,25 +38168,7 @@ "StatisticsView/ScrollContainer/CardGrid/#2/VBox/MostPlayedList/#3": { "rect": [ 1268.0, - 369.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/NameLabel": { - "rect": [ - 1268.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#2/VBox/PlayedLabel": { - "rect": [ - 1268.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -34320,11 +38192,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header": { "rect": [ 962.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 962.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1044.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/NameLabel": { + "rect": [ + 1044.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#3/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1044.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -34341,7 +38276,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/LeastPlayedList": { "rect": [ 962.0, - 396.0, + 384.0, 266.0, 0.0 ], @@ -34350,7 +38285,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedHeader": { "rect": [ 962.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -34359,7 +38294,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 104.0 ], @@ -34368,7 +38303,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#0": { "rect": [ 962.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -34377,7 +38312,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#1": { "rect": [ 962.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -34386,7 +38321,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#2": { "rect": [ 962.0, - 342.0, + 330.0, 266.0, 23.0 ], @@ -34395,25 +38330,7 @@ "StatisticsView/ScrollContainer/CardGrid/#3/VBox/MostPlayedList/#3": { "rect": [ 962.0, - 369.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/NameLabel": { - "rect": [ - 962.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#3/VBox/PlayedLabel": { - "rect": [ - 962.0, - 204.0, + 357.0, 266.0, 23.0 ], @@ -34437,11 +38354,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header": { "rect": [ 656.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 656.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info": { + "rect": [ + 738.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 738.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/NameLabel": { + "rect": [ + 738.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#4/VBox/Header/Info/PlayedLabel": { + "rect": [ + 738.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -34458,7 +38438,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/LeastPlayedList": { "rect": [ 656.0, - 369.0, + 357.0, 266.0, 0.0 ], @@ -34467,7 +38447,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedHeader": { "rect": [ 656.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -34476,7 +38456,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 77.0 ], @@ -34485,7 +38465,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#0": { "rect": [ 656.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -34494,7 +38474,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#1": { "rect": [ 656.0, - 315.0, + 303.0, 266.0, 23.0 ], @@ -34503,25 +38483,7 @@ "StatisticsView/ScrollContainer/CardGrid/#4/VBox/MostPlayedList/#2": { "rect": [ 656.0, - 342.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/NameLabel": { - "rect": [ - 656.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#4/VBox/PlayedLabel": { - "rect": [ - 656.0, - 204.0, + 330.0, 266.0, 23.0 ], @@ -34545,11 +38507,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header": { "rect": [ 350.0, - 231.0, + 138.0, 266.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 138.0, + 72.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info": { + "rect": [ + 432.0, + 138.0, + 184.0, + 113.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 228.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 138.0, + 184.0, + 59.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#5/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 201.0, + 184.0, 23.0 ], "visible": false @@ -34566,7 +38591,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/LeastPlayedList": { "rect": [ 350.0, - 369.0, + 366.0, 266.0, 0.0 ], @@ -34575,7 +38600,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedHeader": { "rect": [ 350.0, - 258.0, + 255.0, 266.0, 26.0 ], @@ -34584,7 +38609,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 77.0 ], @@ -34593,7 +38618,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 288.0, + 285.0, 266.0, 23.0 ], @@ -34602,7 +38627,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#1": { "rect": [ 350.0, - 315.0, + 312.0, 266.0, 23.0 ], @@ -34611,25 +38636,7 @@ "StatisticsView/ScrollContainer/CardGrid/#5/VBox/MostPlayedList/#2": { "rect": [ 350.0, - 342.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/NameLabel": { - "rect": [ - 350.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#5/VBox/PlayedLabel": { - "rect": [ - 350.0, - 204.0, + 339.0, 266.0, 23.0 ], @@ -34653,11 +38660,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header": { "rect": [ 44.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info": { + "rect": [ + 126.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#6/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -34674,7 +38744,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/LeastPlayedList": { "rect": [ 44.0, - 698.0, + 686.0, 266.0, 0.0 ], @@ -34683,7 +38753,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedHeader": { "rect": [ 44.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -34692,7 +38762,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 50.0 ], @@ -34701,7 +38771,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#0": { "rect": [ 44.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -34710,25 +38780,7 @@ "StatisticsView/ScrollContainer/CardGrid/#6/VBox/MostPlayedList/#1": { "rect": [ 44.0, - 671.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/NameLabel": { - "rect": [ - 44.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#6/VBox/PlayedLabel": { - "rect": [ - 44.0, - 560.0, + 659.0, 266.0, 23.0 ], @@ -34752,11 +38804,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header": { "rect": [ 350.0, - 587.0, + 494.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 350.0, + 494.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info": { + "rect": [ + 432.0, + 494.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 432.0, + 575.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/NameLabel": { + "rect": [ + 432.0, + 494.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#7/VBox/Header/Info/PlayedLabel": { + "rect": [ + 432.0, + 548.0, + 184.0, 23.0 ], "visible": false @@ -34773,7 +38888,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/LeastPlayedList": { "rect": [ 350.0, - 671.0, + 659.0, 266.0, 0.0 ], @@ -34782,7 +38897,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedHeader": { "rect": [ 350.0, - 614.0, + 602.0, 266.0, 26.0 ], @@ -34791,7 +38906,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList": { "rect": [ 350.0, - 644.0, + 632.0, 266.0, 23.0 ], @@ -34800,25 +38915,7 @@ "StatisticsView/ScrollContainer/CardGrid/#7/VBox/MostPlayedList/#0": { "rect": [ 350.0, - 644.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/NameLabel": { - "rect": [ - 350.0, - 494.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#7/VBox/PlayedLabel": { - "rect": [ - 350.0, - 560.0, + 632.0, 266.0, 23.0 ], @@ -34842,11 +38939,74 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header": { "rect": [ 1574.0, - 231.0, + 138.0, 266.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 1574.0, + 138.0, + 72.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 104.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 1656.0, + 219.0, + 184.0, + 23.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/NameLabel": { + "rect": [ + 1656.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/#8/VBox/Header/Info/PlayedLabel": { + "rect": [ + 1656.0, + 192.0, + 184.0, 23.0 ], "visible": false @@ -34863,7 +39023,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/LeastPlayedList": { "rect": [ 1574.0, - 315.0, + 303.0, 266.0, 0.0 ], @@ -34872,7 +39032,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedHeader": { "rect": [ 1574.0, - 258.0, + 246.0, 266.0, 26.0 ], @@ -34881,7 +39041,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList": { "rect": [ 1574.0, - 288.0, + 276.0, 266.0, 23.0 ], @@ -34890,25 +39050,7 @@ "StatisticsView/ScrollContainer/CardGrid/#8/VBox/MostPlayedList/#0": { "rect": [ 1574.0, - 288.0, - 266.0, - 23.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/NameLabel": { - "rect": [ - 1574.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/#8/VBox/PlayedLabel": { - "rect": [ - 1574.0, - 204.0, + 276.0, 266.0, 23.0 ], @@ -34932,15 +39074,78 @@ ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LastPlayedLabel": { + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header": { "rect": [ 44.0, 138.0, + 266.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPicture": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/CoverFrame/CoverPlaceholder": { + "rect": [ + 44.0, + 138.0, + 72.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info": { + "rect": [ + 126.0, + 138.0, + 184.0, + 100.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/LastPlayedLabel": { + "rect": [ + 126.0, + 138.0, 1.0, 23.0 ], "visible": false }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/NameLabel": { + "rect": [ + 126.0, + 138.0, + 184.0, + 50.0 + ], + "visible": false + }, + "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/Header/Info/PlayedLabel": { + "rect": [ + 126.0, + 192.0, + 184.0, + 23.0 + ], + "visible": false + }, "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedHeader": { "rect": [ 44.0, @@ -34953,7 +39158,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/LeastPlayedList": { "rect": [ 44.0, - 265.0, + 276.0, 266.0, 0.0 ], @@ -34962,7 +39167,7 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedHeader": { "rect": [ 44.0, - 231.0, + 242.0, 266.0, 26.0 ], @@ -34971,30 +39176,12 @@ "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/MostPlayedList": { "rect": [ 44.0, - 261.0, + 272.0, 266.0, 0.0 ], "visible": false }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/NameLabel": { - "rect": [ - 44.0, - 138.0, - 266.0, - 62.0 - ], - "visible": false - }, - "StatisticsView/ScrollContainer/CardGrid/SoundtrackCard/VBox/PlayedLabel": { - "rect": [ - 44.0, - 204.0, - 266.0, - 23.0 - ], - "visible": false - }, "StatisticsView/SongList": { "rect": [ 32.0, @@ -40064,33 +44251,204 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPicture": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPlaceholder": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info": { + "rect": [ + 790.0, + 204.0, + 500.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "rect": [ + 790.0, + 204.0, + 110.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesTitle": { + "rect": [ + 790.0, + 204.0, + 125.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/SummaryLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 23.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 2630.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow": { + "rect": [ + 630.0, + 420.0, + 132.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/AddNameButton": { + "rect": [ + 630.0, + 420.0, + 55.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/NewNameField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons": { + "rect": [ + 630.0, + 420.0, + 148.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/CancelNamesButton": { + "rect": [ + 630.0, + 420.0, + 76.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/SaveNamesButton": { + "rect": [ + 630.0, + 420.0, + 60.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 2493.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.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, + 221.0, + 35.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, - 692.0 + 504.0 ], "visible": false }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList": { "rect": [ 630.0, - 232.0, + 420.0, 0.0, 0.0 ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/SummaryLabel": { - "rect": [ - 630.0, - 194.0, - 660.0, - 26.0 - ], - "visible": false - }, "SoundtrackStatsPopup/StatsPanel/TitleLabel": { "rect": [ 630.0, @@ -44813,33 +49171,204 @@ ], "visible": false }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPicture": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/CoverFrame/CoverPlaceholder": { + "rect": [ + 630.0, + 204.0, + 144.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info": { + "rect": [ + 790.0, + 204.0, + 500.0, + 200.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/EditNamesButton": { + "rect": [ + 790.0, + 204.0, + 110.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/NamesTitle": { + "rect": [ + 790.0, + 204.0, + 125.0, + 26.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/Info/SummaryLabel": { + "rect": [ + 790.0, + 204.0, + 1.0, + 23.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor": { + "rect": [ + 630.0, + 420.0, + 660.0, + 2630.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow": { + "rect": [ + 630.0, + 420.0, + 132.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/AddNameButton": { + "rect": [ + 630.0, + 420.0, + 55.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/AddRow/NewNameField": { + "rect": [ + 630.0, + 420.0, + 68.5, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons": { + "rect": [ + 630.0, + 420.0, + 148.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/CancelNamesButton": { + "rect": [ + 630.0, + 420.0, + 76.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorButtons/SaveNamesButton": { + "rect": [ + 630.0, + 420.0, + 60.0, + 31.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorHelp": { + "rect": [ + 630.0, + 420.0, + 1.0, + 2493.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SoundtrackStatsPopup/StatsPanel/NamesEditor/EditorScroll/EditorNames": { + "rect": [ + 630.0, + 420.0, + 0.0, + 0.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, + 221.0, + 35.0 + ], + "visible": false + }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer": { "rect": [ 630.0, - 232.0, + 420.0, 660.0, - 692.0 + 504.0 ], "visible": false }, "SoundtrackStatsPopup/StatsPanel/ScrollContainer/SongList": { "rect": [ 630.0, - 232.0, + 420.0, 0.0, 0.0 ], "visible": false }, - "SoundtrackStatsPopup/StatsPanel/SummaryLabel": { - "rect": [ - 630.0, - 194.0, - 660.0, - 26.0 - ], - "visible": false - }, "SoundtrackStatsPopup/StatsPanel/TitleLabel": { "rect": [ 630.0, diff --git a/tests/fakes/fake_http_transport.gd b/tests/fakes/fake_http_transport.gd index 0178c8a..de850d5 100644 --- a/tests/fakes/fake_http_transport.gd +++ b/tests/fakes/fake_http_transport.gd @@ -25,6 +25,9 @@ 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 +## [member requested_paths]. +var requested_bodies: Array[String] = [] ## How many tokens have been asked for. var token_requests: int = 0 ## The JSON body of the last token request. @@ -120,6 +123,7 @@ func request( requested_paths.append(path) requested_methods.append(method) requested_headers.append(headers) + requested_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 d6fb3f0..ae493c6 100644 --- a/tests/fakes/ui_tour.gd +++ b/tests/fakes/ui_tour.gd @@ -91,6 +91,12 @@ 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(): + server.respond_bytes("/soundtracks/soundtrack-%d/cover" % i, + FileAccess.get_file_as_bytes(CHARACTERS_DIR.path_join( + pictures[i % pictures.size()]))) server.respond_json("/version/history", SERVER_HISTORY) return server @@ -118,6 +124,11 @@ static func soundtrack_statistics() -> Array[Dictionary]: } if played > 0: soundtrack.soundtrack_last_played = "2026-09-%02dT20:00:00Z" % (i + 1) + # Every other soundtrack has a cover, the rest show their initials + if i % 2 == 0: + soundtrack.cover_modified = 1700000000 + if i == 0: + soundtrack.alternative_names = ["CT", "クロノ・トリガー"] statistics.append(soundtrack) return statistics @@ -292,6 +303,11 @@ func run(on_state: Callable) -> void: break await _settle() await on_state.call("popup_SoundtrackStats", stats_popup) + stats_popup.edit_names() + stats_popup.new_name_field.text = "Chrono" + await _settle() + await on_state.call("popup_SoundtrackStats_names", stats_popup) + stats_popup.stop_editing() stats_popup.close() _main.show_tab(&"quiz") await _settle() diff --git a/tests/integration/test_covers_and_names.gd b/tests/integration/test_covers_and_names.gd new file mode 100644 index 0000000..f8234dc --- /dev/null +++ b/tests/integration/test_covers_and_names.gd @@ -0,0 +1,187 @@ +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. + +const STATISTICS: Array[Dictionary] = [ + {"soundtrack_uuid": "st-1", "soundtrack_name": "Chrono Trigger", "soundtrack_played": 5, + "cover_modified": 100, "alternative_names": [], "songs": [ + {"song_name": "Corridors of Time", "times_played": 1}, + ]}, + {"soundtrack_uuid": "st-2", "soundtrack_name": "Terranigma", "soundtrack_played": 0, + "songs": []}, +] + +var _view: StatisticsView +var _popup: SoundtrackStatsPopup +var _png: PackedByteArray + + +func _before_window_starts() -> void: + _server.respond_json("/statistics/soundtracks", STATISTICS) + 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) + + +func before_test() -> void: + # Covers are kept next to the settings, which the tests keep in a folder of their own + OS.set_environment(Settings.CONFIG_DIR_ENV, create_temp_dir("covers_%d" % randi())) + await super() + _view = _window.get_node("StatisticsView") + _popup = _window.get_node("SoundtrackStatsPopup") + + +func _open_statistics() -> void: + _window.show_tab(&"statistics") + await _runner.simulate_frames(3) + + +func _card(soundtrack_name: String) -> SoundtrackCard: + for card: SoundtrackCard in _view.cards: + if card.soundtrack.soundtrack_name == soundtrack_name: + return card + return null + + +func test_card_shows_its_cover() -> void: + await _open_statistics() + + var card: SoundtrackCard = _card("Chrono Trigger") + assert_object(card.cover_picture.texture).is_not_null() + assert_bool(card.cover_placeholder.visible).is_false() + + +func test_card_without_a_cover_shows_initials_and_asks_nothing() -> void: + await _open_statistics() + + var card: SoundtrackCard = _card("Terranigma") + assert_object(card.cover_picture.texture).is_null() + assert_bool(card.cover_placeholder.visible).is_true() + assert_str(card.cover_placeholder.text).is_equal("T") + assert_int(_server.count("/soundtracks/st-2/cover")).is_equal(0) + + +func test_cover_missing_on_the_server_keeps_the_initials() -> void: + _server.respond_bytes("/soundtracks/st-1/cover", PackedByteArray(), 404) + + await _open_statistics() + + assert_bool(_card("Chrono Trigger").cover_placeholder.visible).is_true() + assert_str(_card("Chrono Trigger").cover_placeholder.text).is_equal("CT") + + +func test_covers_load_for_cards_on_screen_as_they_scroll_in() -> void: + var many: Array[Dictionary] = [] + for i: int in 40: + many.append({"soundtrack_uuid": "many-%02d" % i, "soundtrack_name": "Game %02d" % i, + "soundtrack_played": 1, "cover_modified": 100, "songs": []}) + _server.respond_bytes("/soundtracks/many-%02d/cover" % i, _png) + _server.respond_json("/statistics/soundtracks", many) + + await _open_statistics() + + var asked: Callable = func() -> int: + return _server.requested_paths.filter( + func(path: String) -> bool: return path.begins_with("/soundtracks/many-")).size() + var on_screen: int = asked.call() + assert_int(on_screen).is_greater(0) + assert_int(on_screen).is_less(40) + + var scroll_bar: VScrollBar = _view.card_scroll.get_v_scroll_bar() + scroll_bar.value = scroll_bar.max_value + await _runner.simulate_frames(2) + + assert_int(asked.call()).is_greater(on_screen) + + +func test_popup_shows_the_cover_large() -> void: + await _open_statistics() + + _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) + + assert_object(_popup.cover_picture.texture).is_not_null() + assert_bool(_popup.cover_placeholder.visible).is_false() + assert_str(_popup.names_label.text).is_equal("No other names yet") + + +func test_editing_names_saves_them_and_search_finds_the_new_name() -> void: + _server.respond_json("/soundtracks/st-1/alternative-names", {"uuid": "st-1", + "name": "Chrono Trigger", "alternative_names": ["CT", "クロノ・トリガー"]}) + await _open_statistics() + _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) + + _popup.edit_names() + assert_bool(_popup.names_editor.visible).is_true() + _popup.new_name_field.text = "CT" + _popup.add_name() + _popup.new_name_field.text = "クロノ・トリガー" + _popup.save_names() + + var index: int = _server.requested_paths.find("/soundtracks/st-1/alternative-names") + assert_int(index).is_not_equal(-1) + assert_int(_server.requested_methods[index]).is_equal(HTTPClient.METHOD_PUT) + var body: Dictionary = JSON.parse_string(_server.requested_bodies[index]) + assert_array(body.names).contains_exactly(["CT", "クロノ・トリガー"]) + assert_bool(_popup.names_editor.visible).is_false() + assert_str(_popup.names_label.text).is_equal("CT, クロノ・トリガー") + + _view.search_field.text = "クロノ" + _view.apply_filter() + var shown: Array = _view.cards.filter(func(card: SoundtrackCard) -> bool: return card.visible) + assert_array(shown.map(func(card: SoundtrackCard) -> String: + return card.soundtrack.soundtrack_name)).contains_exactly(["Chrono Trigger"]) + + +func test_editor_skips_empty_repeated_and_own_names() -> void: + await _open_statistics() + _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) + _popup.edit_names() + + for name: String in [" ", "CT", "ct", "Chrono Trigger"]: + _popup.new_name_field.text = name + _popup.add_name() + + assert_int(_popup.editor_names.get_child_count()).is_equal(1) + + +func test_removing_a_name_in_the_editor() -> void: + await _open_statistics() + _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) + _popup.edit_names() + _popup.new_name_field.text = "CT" + _popup.add_name() + + _popup.remove_name("CT") + + assert_int(_popup.editor_names.get_child_count()).is_equal(0) + + +func test_failed_save_keeps_the_editor_open() -> void: + _server.respond_json("/soundtracks/st-1/alternative-names", {}, 500) + await _open_statistics() + _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) + _popup.edit_names() + _popup.new_name_field.text = "CT" + + _popup.save_names() + + assert_bool(_popup.names_editor.visible).is_true() + assert_bool(_popup.editor_status.visible).is_true() + assert_bool(_popup.save_names_button.disabled).is_false() + + +func test_search_popup_finds_soundtracks_by_alternative_names() -> void: + _server.respond_json("/soundtracks", [ + {"uuid": "st-1", "name": "Chrono Trigger", "alternative_names": ["CT"]}, + {"uuid": "st-2", "name": "Terranigma", "alternative_names": []}, + ]) + var search: SearchPopup = _window.get_node("SearchPopup") + search.get_list_of_soundtracks() + search.open() + + search.search_field.text = "ct" + search.search() + + 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") diff --git a/tests/integration/test_covers_and_names.gd.uid b/tests/integration/test_covers_and_names.gd.uid new file mode 100644 index 0000000..ed848b5 --- /dev/null +++ b/tests/integration/test_covers_and_names.gd.uid @@ -0,0 +1 @@ +uid://d3oc3e1fsttoh diff --git a/tests/integration/test_theme.gd b/tests/integration/test_theme.gd index ce0539c..fff37af 100644 --- a/tests/integration/test_theme.gd +++ b/tests/integration/test_theme.gd @@ -50,6 +50,7 @@ const STYLES: Dictionary = { "ProgressSlider": "HSlider", "VolumeBar": "HSlider", "StatisticsCard": "PanelContainer", + "SoundtrackCover": "Panel", "StatisticsCardHover": "PanelContainer", "StatisticsGrid": "GridContainer", "TabButton": "Button", diff --git a/tests/unit/test_api_types.gd b/tests/unit/test_api_types.gd index 44eb1f0..12027fc 100644 --- a/tests/unit/test_api_types.gd +++ b/tests/unit/test_api_types.gd @@ -182,9 +182,12 @@ func test_to_dict_gives_back_the_json() -> void: var json: Dictionary = { "soundtrack_id": 5, "soundtrack_uuid": "st-1", "soundtrack_name": "Earthbound", "soundtrack_played": 6, "soundtrack_last_played": "2026-10-01T20:00:00Z", + "alternative_names": ["Mother 2"], "cover_modified": 1730000000, "songs": [{"soundtrack_id": 5, "soundtrack_uuid": "st-1", "song_uuid": "so-1", "soundtrack_name": "Earthbound", "song_name": "Onett", "path": "/onett.mp3", "times_played": 2, "file_name": "onett.mp3"}], } - assert_dict(SoundtrackWithSongs.from_dict(json).to_dict()).is_equal(json) + # Compared as JSON, since the names come back as a typed Array[String] + assert_str(JSON.stringify(SoundtrackWithSongs.from_dict(json).to_dict(), "", true)) \ + .is_equal(JSON.stringify(json, "", true)) diff --git a/tests/unit/test_cover_cache.gd b/tests/unit/test_cover_cache.gd new file mode 100644 index 0000000..20f6f92 --- /dev/null +++ b/tests/unit/test_cover_cache.gd @@ -0,0 +1,142 @@ +extends GdUnitTestSuite +## Soundtrack covers cached on disk: downloaded once, again only when the server's cover +## time changed, a few at a time, and deleted with their soundtrack. + +var _server: FakeHttpTransport +var _config_dir_before: String +var _png: PackedByteArray + + +func before_test() -> void: + _server = FakeHttpTransport.new().install() + _config_dir_before = OS.get_environment(Settings.CONFIG_DIR_ENV) + OS.set_environment(Settings.CONFIG_DIR_ENV, create_temp_dir("cover_cache_%d" % randi())) + var image: Image = Image.create(4, 6, false, Image.FORMAT_RGB8) + _png = image.save_png_to_buffer() + + +func after_test() -> void: + OS.set_environment(Settings.CONFIG_DIR_ENV, _config_dir_before) + _server.uninstall() + + +func _cover_path(uuid: String) -> String: + return "/soundtracks/%s/cover" % uuid + + +func _serve(uuid: String) -> void: + _server.respond_bytes(_cover_path(uuid), _png) + + +# Asks a new cache, like opening the statistics after a restart, and returns the covers +func _fetch(uuid: String, modified: int, cache: CoverCache = null) -> Array[Image]: + var covers: CoverCache = cache if cache != null else CoverCache.new() + var images: Array[Image] = [] + covers.fetch_cover(uuid, modified, func(image: Image) -> void: images.append(image)) + return images + + +func test_first_fetch_downloads_and_keeps_the_cover() -> void: + _serve("st-1") + + var images: Array[Image] = _fetch("st-1", 100) + + assert_int(images.size()).is_equal(1) + assert_int(images[0].get_width()).is_equal(4) + assert_bool(FileAccess.file_exists(CoverCache.folder().path_join("st-1"))).is_true() + + +func test_unchanged_cover_comes_from_disk() -> void: + _serve("st-1") + _fetch("st-1", 100) + + var images: Array[Image] = _fetch("st-1", 100) + + assert_int(images.size()).is_equal(1) + assert_int(_server.count(_cover_path("st-1"))).is_equal(1) + + +func test_changed_cover_is_downloaded_again() -> void: + _serve("st-1") + _fetch("st-1", 100) + + _fetch("st-1", 200) + + assert_int(_server.count(_cover_path("st-1"))).is_equal(2) + + +func test_soundtrack_without_a_cover_asks_nothing() -> void: + var images: Array[Image] = _fetch("st-1", 0) + + assert_array(images).is_empty() + assert_int(_server.count(_cover_path("st-1"))).is_equal(0) + + +func test_missing_cover_on_the_server_gives_nothing() -> void: + var images: Array[Image] = _fetch("st-1", 100) + + assert_array(images).is_empty() + assert_int(_server.count(_cover_path("st-1"))).is_equal(1) + + +func test_cover_that_cannot_be_read_is_not_kept() -> void: + _server.respond_bytes(_cover_path("st-1"), "not a picture".to_utf8_buffer()) + + var images: Array[Image] = _fetch("st-1", 100) + + assert_array(images).is_empty() + assert_bool(FileAccess.file_exists(CoverCache.folder().path_join("st-1"))).is_false() + + +func test_covers_of_removed_soundtracks_are_deleted() -> void: + _serve("st-1") + _serve("st-2") + var covers: CoverCache = CoverCache.new() + _fetch("st-1", 100, covers) + _fetch("st-2", 100, covers) + + covers.keep_only({"st-1": 100, "st-3": 0}) + + assert_bool(FileAccess.file_exists(CoverCache.folder().path_join("st-1"))).is_true() + assert_bool(FileAccess.file_exists(CoverCache.folder().path_join("st-2"))).is_false() + + +func test_cover_removed_on_the_server_is_deleted() -> void: + _serve("st-1") + var covers: CoverCache = CoverCache.new() + _fetch("st-1", 100, covers) + + covers.keep_only({"st-1": 0}) + + assert_bool(FileAccess.file_exists(CoverCache.folder().path_join("st-1"))).is_false() + + +func test_only_a_few_covers_download_at_a_time() -> void: + var covers: CoverCache = CoverCache.new() + for i: int in 5: + _server.hold(_cover_path("st-%d" % i)) + _serve("st-%d" % i) + _fetch("st-%d" % i, 100, covers) + + var asked: int = 0 + for i: int in 5: + asked += _server.count(_cover_path("st-%d" % i)) + assert_int(asked).is_equal(CoverCache.MAX_DOWNLOADS) + + _server.release(_cover_path("st-0")) + assert_int(_server.count(_cover_path("st-3"))).is_equal(1) + assert_int(_server.count(_cover_path("st-4"))).is_equal(0) + + +func test_cover_asked_for_twice_is_downloaded_once() -> void: + var covers: CoverCache = CoverCache.new() + _server.hold(_cover_path("st-1")) + _serve("st-1") + var first: Array[Image] = _fetch("st-1", 100, covers) + var second: Array[Image] = _fetch("st-1", 100, covers) + + _server.release(_cover_path("st-1")) + + assert_int(_server.count(_cover_path("st-1"))).is_equal(1) + assert_int(first.size()).is_equal(1) + assert_int(second.size()).is_equal(1) diff --git a/tests/unit/test_cover_cache.gd.uid b/tests/unit/test_cover_cache.gd.uid new file mode 100644 index 0000000..b2e876d --- /dev/null +++ b/tests/unit/test_cover_cache.gd.uid @@ -0,0 +1 @@ +uid://chubfhdhfqvyu diff --git a/tests/unit/test_soundtrack_search.gd b/tests/unit/test_soundtrack_search.gd index 3947f75..8c7e59b 100644 --- a/tests/unit/test_soundtrack_search.gd +++ b/tests/unit/test_soundtrack_search.gd @@ -90,3 +90,25 @@ func test_matched_letters_in_order() -> void: func test_matched_letters_of_no_match() -> void: assert_array(SoundtrackSearch.matched_letters("Chrono Trigger", "metroid")).is_empty() assert_array(SoundtrackSearch.matched_letters("Super Mario 64", "sm")).is_empty() + + +func test_alternative_names_find_the_real_name() -> void: + _search.set_names(["Final Fantasy VII", "Chrono Trigger"], + {"Final Fantasy VII": ["FF7", "ファイナルファンタジーVII"]}) + + assert_array(_search.find_matches("ff7")).contains_exactly(["Final Fantasy VII"]) + assert_str(_search.matched_alternative("Final Fantasy VII")).is_equal("FF7") + assert_array(_search.find_matches("ファイナル")).contains_exactly(["Final Fantasy VII"]) + + +func test_real_name_match_shows_no_alternative() -> void: + _search.set_names(["Final Fantasy VII"], {"Final Fantasy VII": ["FF7"]}) + + assert_array(_search.find_matches("fantasy")).contains_exactly(["Final Fantasy VII"]) + assert_str(_search.matched_alternative("Final Fantasy VII")).is_empty() + + +func test_soundtrack_is_found_once_when_several_names_match() -> void: + _search.set_names(["Final Fantasy VII"], {"Final Fantasy VII": ["Final Fantasy 7"]}) + + assert_array(_search.find_matches("final")).contains_exactly(["Final Fantasy VII"]) diff --git a/themes/README.md b/themes/README.md index 255c84e..1bdd8e4 100644 --- a/themes/README.md +++ b/themes/README.md @@ -73,6 +73,7 @@ inspector or `theme_type_variation` in code: | `HighlightLarge`, `HighlightMedium` | Label | Orange text: sync headers and every other sync row | | `SongRowLabel`, `SongRowLabelPlaying` | Label | A song's text, and the one that is playing | | `TabButton` | Button | The Quiz and Statistics tabs; the chosen one is orange | +| `SoundtrackCover` | Panel | A soundtrack's cover on a statistics card and in its popup: the colour behind the initials until the cover arrives, and the rounded corners the cover is cut to | | `StatisticsCard`, `StatisticsCardHover` | PanelContainer | A soundtrack card in the statistics, and the one under the mouse | | `StatisticsGrid` | GridContainer | The grid of soundtrack cards, with the space between them | | `PlayerBar` | PanelContainer | The music player's rounded bar | diff --git a/themes/christmas_theme.tres b/themes/christmas_theme.tres index f8af377..39d25e0 100644 --- a/themes/christmas_theme.tres +++ b/themes/christmas_theme.tres @@ -1240,6 +1240,14 @@ content_margin_right = 10.0 content_margin_top = 4.0 content_margin_bottom = 4.0 +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_soundtrackcover"] +bg_color = Color(0.361, 0.059, 0.094, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 8 + [resource] default_font = ExtResource("1_default_font") Button/colors/font_color = Color(0.478, 0.082, 0.125, 1.0) @@ -1495,6 +1503,8 @@ StepButton/styles/disabled = SubResource("StyleBoxFlat_step_disabled") StepButton/styles/hover = SubResource("StyleBoxFlat_step_hover") StepButton/styles/normal = SubResource("StyleBoxFlat_step_normal") StepButton/styles/pressed = SubResource("StyleBoxFlat_step_pressed") +SoundtrackCover/base_type = &"Panel" +SoundtrackCover/styles/panel = SubResource("StyleBoxFlat_soundtrackcover") StatisticsCard/base_type = &"PanelContainer" StatisticsCard/styles/panel = SubResource("StyleBoxFlat_statscard") StatisticsCardHover/base_type = &"PanelContainer" diff --git a/themes/main_theme.tres b/themes/main_theme.tres index afbaa50..ce99ac3 100644 --- a/themes/main_theme.tres +++ b/themes/main_theme.tres @@ -1048,6 +1048,14 @@ corner_detail = 5 [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_playericon"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_soundtrackcover"] +bg_color = Color(0.22, 0.22, 0.22, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 8 + [resource] default_font = ExtResource("1_default_font") Button/colors/font_color = Color(0.875, 0.875, 0.875, 1) @@ -1303,6 +1311,8 @@ StepButton/styles/disabled = SubResource("StyleBoxFlat_step_disabled") StepButton/styles/hover = SubResource("StyleBoxFlat_step_hover") StepButton/styles/normal = SubResource("StyleBoxFlat_step_normal") StepButton/styles/pressed = SubResource("StyleBoxFlat_step_pressed") +SoundtrackCover/base_type = &"Panel" +SoundtrackCover/styles/panel = SubResource("StyleBoxFlat_soundtrackcover") StatisticsCard/base_type = &"PanelContainer" StatisticsCard/styles/panel = SubResource("StyleBoxFlat_statscard") StatisticsCardHover/base_type = &"PanelContainer" diff --git a/themes/nord_theme.tres b/themes/nord_theme.tres index 0eb7214..17e07be 100644 --- a/themes/nord_theme.tres +++ b/themes/nord_theme.tres @@ -1223,6 +1223,14 @@ content_margin_bottom = 4.0 [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_playericon"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_soundtrackcover"] +bg_color = Color(0.161, 0.341, 0.498, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 8 + [resource] default_font = ExtResource("1_default_font") Button/colors/font_color = Color(0.925, 0.937, 0.957, 1.0) @@ -1478,6 +1486,8 @@ StepButton/styles/disabled = SubResource("StyleBoxFlat_step_disabled") StepButton/styles/hover = SubResource("StyleBoxFlat_step_hover") StepButton/styles/normal = SubResource("StyleBoxFlat_step_normal") StepButton/styles/pressed = SubResource("StyleBoxFlat_step_pressed") +SoundtrackCover/base_type = &"Panel" +SoundtrackCover/styles/panel = SubResource("StyleBoxFlat_soundtrackcover") StatisticsCard/base_type = &"PanelContainer" StatisticsCard/styles/panel = SubResource("StyleBoxFlat_statscard") StatisticsCardHover/base_type = &"PanelContainer" diff --git a/themes/original_web_theme.tres b/themes/original_web_theme.tres index 0cfe717..2f0ed88 100644 --- a/themes/original_web_theme.tres +++ b/themes/original_web_theme.tres @@ -1149,6 +1149,14 @@ bg_color = Color(0.2, 0.2, 0.2, 1) [sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_playericon"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_soundtrackcover"] +bg_color = Color(0.259, 0.259, 0.259, 1) +corner_radius_top_left = 8 +corner_radius_top_right = 8 +corner_radius_bottom_right = 8 +corner_radius_bottom_left = 8 +corner_detail = 8 + [resource] default_font = ExtResource("1_default_font") Button/colors/font_color = Color(1, 0.612, 0, 1) @@ -1404,6 +1412,8 @@ StepButton/styles/disabled = SubResource("StyleBoxFlat_step_disabled") StepButton/styles/hover = SubResource("StyleBoxFlat_step_hover") StepButton/styles/normal = SubResource("StyleBoxFlat_step_normal") StepButton/styles/pressed = SubResource("StyleBoxFlat_step_pressed") +SoundtrackCover/base_type = &"Panel" +SoundtrackCover/styles/panel = SubResource("StyleBoxFlat_soundtrackcover") StatisticsCard/base_type = &"PanelContainer" StatisticsCard/styles/panel = SubResource("StyleBoxFlat_statscard") StatisticsCardHover/base_type = &"PanelContainer"