diff --git a/common/soundtrack_search.gd b/common/soundtrack_search.gd index 00431fa..06ed5f9 100644 --- a/common/soundtrack_search.gd +++ b/common/soundtrack_search.gd @@ -1,7 +1,8 @@ class_name SoundtrackSearch extends RefCounted ## Finds soundtrack names matching a search text. Matches ignore case, spaces and -## special characters, and also find the letters in order with others in between. +## special characters. Searches of at least [code]Settings.LETTERS_FOR_LOOSE_SEARCH[/code] +## 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. @@ -30,12 +31,14 @@ func find_matches(search_text: String) -> Array[String]: var seen: Dictionary = {} var search_text_lower: String = search_text.to_lower() var clean_search_text: String = clean_term(search_text) - var regex: RegEx = _compile_regex_cached(clean_search_text) var passes: Array[Callable] = [ func(entry: Dictionary) -> bool: return entry.lowercase.contains(search_text_lower), func(entry: Dictionary) -> bool: return entry.cleaned.contains(clean_search_text), - func(entry: Dictionary) -> bool: return regex.search(entry.cleaned) != null, ] + if clean_search_text.length() >= Settings.LETTERS_FOR_LOOSE_SEARCH: + var regex: RegEx = _compile_regex_cached(clean_search_text) + passes.append( + func(entry: Dictionary) -> bool: return regex.search(entry.cleaned) != null) var matches: Array[String] = [] for matches_pass: Callable in passes: @@ -46,6 +49,57 @@ func find_matches(search_text: String) -> Array[String]: return matches +## The positions of the letters in [param soundtrack_name] that [param search_text] +## matched, for highlighting them: the text itself if the name contains it, otherwise the +## letters found in order the way [method find_matches] finds them. Empty if it does not +## match. +static func matched_letters(soundtrack_name: String, search_text: String) -> Array[int]: + var positions: Array[int] = [] + if search_text.is_empty(): + return positions + var start: int = soundtrack_name.to_lower().find(search_text.to_lower()) + if start >= 0: + for i: int in search_text.length(): + positions.append(start + i) + return positions + + # Clean the name piece by piece, remembering which letter each cleaned letter came from + var cleaned: String = "" + var source: Array[int] = [] + var i: int = 0 + while i < soundtrack_name.length(): + var piece: String + var length: int = 1 + if soundtrack_name.substr(i, 2).to_lower() == "'n": + piece = "and" + length = 2 + else: + piece = clean_term(soundtrack_name[i]) + for letter: String in piece: + cleaned += letter + source.append(i) + i += length + + var clean_search_text: String = clean_term(search_text) + var found: int = cleaned.find(clean_search_text) + var cleaned_positions: Array[int] = [] + if found >= 0: + for j: int in clean_search_text.length(): + cleaned_positions.append(found + j) + elif clean_search_text.length() >= Settings.LETTERS_FOR_LOOSE_SEARCH: + var at: int = 0 + for letter: String in clean_search_text: + at = cleaned.find(letter, at) + if at < 0: + return positions + cleaned_positions.append(at) + at += 1 + for cleaned_position: int in cleaned_positions: + if !positions.has(source[cleaned_position]): + positions.append(source[cleaned_position]) + return positions + + ## Cleans a term by removing spaces and replacing special characters. ## Matches MusicServer's clean_term() function. static func clean_term(term: String) -> String: diff --git a/data/settings.gd b/data/settings.gd index 3e76b32..f86a8d2 100644 --- a/data/settings.gd +++ b/data/settings.gd @@ -4,6 +4,10 @@ extends Node ## Registered as the [code]Settings[/code] autoload. const MAX_NUMBER_OF_PLAYERS: int = 7 +## How many letters a search needs before it also finds names with the letters in order +## but apart, e.g. "lttp" for "A Link to the Past". Shorter searches only find names +## that contain the text, since a letter or two in order matches most of the library. +const LETTERS_FOR_LOOSE_SEARCH: int = 3 var default_path: String = "https://music.sanplex.xyz" ## Password for getting an API token from the server. Any password works when the diff --git a/popups/SearchPopup.tscn b/popups/SearchPopup.tscn index e946f36..4e339aa 100644 --- a/popups/SearchPopup.tscn +++ b/popups/SearchPopup.tscn @@ -16,37 +16,63 @@ panel = NodePath("SearchPanel") unique_name_in_owner = true layout_mode = 0 offset_right = 1152.0 -offset_bottom = 640.0 +offset_bottom = 880.0 theme_type_variation = &"PopupBackground" -[node name="ClearButton" type="Button" parent="." unique_id=1549439398] +[node name="TopRow" type="HBoxContainer" parent="SearchPanel" unique_id=1262945995] layout_mode = 0 -offset_left = 936.0 -offset_top = 16.0 -offset_right = 1027.0 -offset_bottom = 56.0 -scale = Vector2(2, 2) -focus_mode = 0 -action_mode = 0 -text = "Clear" +offset_left = 24.0 +offset_top = 24.0 +offset_right = 1128.0 +offset_bottom = 80.0 +theme_override_constants/separation = 16 -[node name="Searchbar" type="TextEdit" parent="." unique_id=2112669372] -layout_mode = 0 -offset_left = 32.0 -offset_top = 16.0 -offset_right = 468.0 -offset_bottom = 56.0 -scale = Vector2(2, 2) -placeholder_text = "Search" - -[node name="ScrollContainer" type="ScrollContainer" parent="." unique_id=213733486] -layout_mode = 0 -offset_top = 128.0 -offset_right = 576.0 -offset_bottom = 384.0 -scale = Vector2(2, 2) - -[node name="SearchResults" type="VBoxContainer" parent="ScrollContainer" unique_id=1567403144] +[node name="SearchField" type="LineEdit" parent="SearchPanel/TopRow" unique_id=2112669372] unique_name_in_owner = true layout_mode = 2 size_flags_horizontal = 3 +theme_type_variation = &"SearchField" +placeholder_text = "Search" +caret_blink = true + +[node name="ClearButton" type="Button" parent="SearchPanel/TopRow" unique_id=1549439398] +unique_name_in_owner = true +custom_minimum_size = Vector2(140, 0) +layout_mode = 2 +focus_mode = 0 +theme_type_variation = &"LargeButton" +action_mode = 0 +text = "Clear" + +[node name="CountLabel" type="Label" parent="SearchPanel" unique_id=1262945996] +unique_name_in_owner = true +layout_mode = 0 +offset_left = 24.0 +offset_top = 90.0 +offset_right = 1128.0 +offset_bottom = 116.0 + +[node name="ScrollContainer" type="ScrollContainer" parent="SearchPanel" unique_id=213733486] +layout_mode = 0 +offset_left = 24.0 +offset_top = 124.0 +offset_right = 1128.0 +offset_bottom = 856.0 +horizontal_scroll_mode = 0 + +[node name="SearchResults" type="VBoxContainer" parent="SearchPanel/ScrollContainer" unique_id=1567403144] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="EmptyLabel" type="Label" parent="SearchPanel" unique_id=1262945997] +unique_name_in_owner = true +visible = false +layout_mode = 0 +offset_left = 24.0 +offset_top = 160.0 +offset_right = 1128.0 +offset_bottom = 200.0 +theme_type_variation = &"Title" +text = "No soundtracks found" +horizontal_alignment = 1 diff --git a/popups/search_popup.gd b/popups/search_popup.gd index 0b1c358..eb47664 100644 --- a/popups/search_popup.gd +++ b/popups/search_popup.gd @@ -1,29 +1,28 @@ class_name SearchPopup extends BasePopup -## Searches every soundtrack on the server as you type, with [SoundtrackSearch]. +## Searches every soundtrack on the server as you type, with [SoundtrackSearch], and +## highlights the letters that matched. +## Every soundtrack on the server, sorted by name. var soundtracks: Array = [] -var label_pool: Array[Label] = [] 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 = {} -@onready var search_list: VBoxContainer = %SearchResults @onready var search_panel: Panel = %SearchPanel -@onready var clear_button: Button = $ClearButton -@onready var search_bar: TextEdit = $Searchbar +@onready var search_field: LineEdit = %SearchField +@onready var clear_button: Button = %ClearButton +@onready var count_label: Label = %CountLabel +@onready var empty_label: Label = %EmptyLabel +@onready var search_results: VBoxContainer = %SearchResults func _ready() -> void: clear_button.pressed.connect(clear) - search_bar.grab_focus() - search_bar.text_changed.connect(search) - visibility_changed.connect(focus) - - -func focus() -> void: - if visible: - search_bar.grab_focus() - clear() + search_field.text_changed.connect(func(_text: String) -> void: search()) + visibility_changed.connect(_on_visibility_changed) func close() -> void: @@ -31,94 +30,124 @@ func close() -> void: super() +## Empties the search field and shows every soundtrack. +func clear() -> void: + search_field.text = "" + search() + search_field.grab_focus() + + +## Shows the soundtracks matching the search field, best matches first, or all of them +## when it is empty. func search() -> void: - GameLog.debug(search_bar.text) - var search_text: String = search_bar.text - - if search_text == "": - show_all_soundtracks() - return - - perform_search(search_text) - - -func show_all_soundtracks() -> void: - # Hide all visible labels and add to pool - for child: Label in search_list.get_children(): - if child.visible: - child.visible = false - label_pool.append(child) - - # Show all soundtracks in order - for soundtrack: String in soundtracks: - add_soundtrack_reuse(soundtrack) - - -func perform_search(search_text: String) -> void: - # Hide all visible labels and add to pool - for child: Label in search_list.get_children(): - if child.visible: - child.visible = false - label_pool.append(child) - - for soundtrack: String in _matcher.find_matches(search_text): - add_soundtrack_reuse(soundtrack) - - -func add_soundtrack(soundtrack: String) -> void: - # Backward compatibility - delegates to reuse version - add_soundtrack_reuse(soundtrack) - - -## Shows [param soundtrack], reusing a hidden label from the pool when there is one. -func add_soundtrack_reuse(soundtrack: String) -> void: - var label: Label - if label_pool.size() > 0: - label = label_pool.pop_front() - label.text = soundtrack - label.visible = true - # Move to end to maintain display order - search_list.move_child(label, search_list.get_child_count() - 1) + var search_text: String = search_field.text.strip_edges() + var shown: Array[int] = [] + if search_text.is_empty(): + shown.assign(range(soundtracks.size())) else: - label = Label.new() - label.text = soundtrack - label.autowrap_mode = TextServer.AUTOWRAP_WORD - search_list.add_child(label) - label.visible = true + for soundtrack: String in _matcher.find_matches(search_text): + shown.append(_index_of[soundtrack]) + + var is_shown: Array[bool] = [] + is_shown.resize(_rows.size()) + for position: int in shown.size(): + var row: PanelContainer = _rows[shown[position]] + if row.get_index() != position: + search_results.move_child(row, position) + row.visible = true + row.theme_type_variation = &"ListRowOdd" if position % 2 != 0 else &"ListRowEven" + _show_name(row, soundtracks[shown[position]], search_text) + is_shown[shown[position]] = true + for index: int in _rows.size(): + if !is_shown[index]: + _rows[index].visible = false + + empty_label.visible = shown.is_empty() && !soundtracks.is_empty() + if search_text.is_empty(): + count_label.text = "%d soundtracks" % soundtracks.size() + else: + count_label.text = "%d of %d soundtracks" % [shown.size(), soundtracks.size()] -func check_if_soundtrack_exists(soundtrack: String) -> bool: - var soundtrack_exists: bool = false - for child: Label in search_list.get_children(): - if child.text == soundtrack and child.visible: - soundtrack_exists = true - return soundtrack_exists +## The names of the soundtracks shown, in the order they are shown. +func shown_names() -> Array[String]: + var names: Array[String] = [] + for row: PanelContainer in search_results.get_children(): + if row.visible: + names.append(row.get_meta(&"soundtrack")) + return names func get_list_of_soundtracks() -> void: GameLog.debug("get_list_of_soundtracks") var handle_soundtracks: Callable = func handle_soundtracks(array: Variant) -> void: if typeof(array) == TYPE_ARRAY: - # Reuse existing labels: hide all and add to pool - for child: Label in search_list.get_children(): - child.visible = false - label_pool.append(child) set_soundtracks(array) - for soundtrack: String in soundtracks: - add_soundtrack_reuse(soundtrack) else: GameLog.warning("Unexpected data") MusicServerClient.fetch_all_soundtracks_ordered(handle_soundtracks) -## Replaces the searchable soundtracks. +## Replaces the searchable soundtracks and shows them, keeping the current search. func set_soundtracks(list: Array) -> void: soundtracks = [] soundtracks.append_array(list) _matcher.set_names(soundtracks) + _index_of.clear() + for index: int in range(soundtracks.size() - 1, -1, -1): + _index_of[soundtracks[index]] = index + while _rows.size() < soundtracks.size(): + var row: PanelContainer = _make_row() + search_results.add_child(row) + _rows.append(row) + while _rows.size() > soundtracks.size(): + _rows.pop_back().free() + for index: int in soundtracks.size(): + _rows[index].set_meta(&"soundtrack", soundtracks[index]) + search() -func clear() -> void: - search_bar.text = "" - show_all_soundtracks() - search_bar.grab_focus() +## [param soundtrack_name] as BBCode, with the letters [param search_text] matched in +## [param highlight_color]. +static func highlighted_name( + soundtrack_name: String, search_text: String, highlight_color: Color) -> String: + var matched: Array[int] = SoundtrackSearch.matched_letters(soundtrack_name, search_text) + var text: String = "" + var in_highlight: bool = false + for i: int in soundtrack_name.length(): + var highlight: bool = matched.has(i) + if highlight != in_highlight: + text += "[color=#%s]" % highlight_color.to_html(false) if highlight else "[/color]" + in_highlight = highlight + var letter: String = soundtrack_name[i] + text += "[lb]" if letter == "[" else letter + if in_highlight: + text += "[/color]" + return text + + +func _make_row() -> PanelContainer: + var row: PanelContainer = PanelContainer.new() + var label: RichTextLabel = RichTextLabel.new() + label.theme_type_variation = &"SearchResult" + label.bbcode_enabled = true + label.fit_content = true + label.scroll_active = false + label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + label.mouse_filter = Control.MOUSE_FILTER_IGNORE + row.add_child(label) + return row + + +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 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: + label.text = text + + +func _on_visibility_changed() -> void: + if visible: + clear() diff --git a/tests/data/layout_baseline.json b/tests/data/layout_baseline.json index 9e5be95..530b3fc 100644 --- a/tests/data/layout_baseline.json +++ b/tests/data/layout_baseline.json @@ -2151,138 +2151,255 @@ ], "visible": false }, - "SearchPopup/ClearButton": { - "rect": [ - 992.0, - 112.0, - 182.0, - 80.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer": { - "rect": [ - 56.0, - 224.0, - 1152.0, - 512.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults": { - "rect": [ - 56.0, - 224.0, - 0.0, - 0.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#0": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#1": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#2": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#3": { - "rect": [ - 56.0, - 224.0, - 2.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#4": { - "rect": [ - 56.0, - 224.0, - 2.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#5": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#6": { - "rect": [ - 56.0, - 224.0, - 2.0, - 462.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#7": { - "rect": [ - 56.0, - 224.0, - 2.0, - 202.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#8": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#9": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, "SearchPopup/SearchPanel": { "rect": [ 56.0, 96.0, 1152.0, - 640.0 + 880.0 ], "visible": false }, - "SearchPopup/Searchbar": { + "SearchPopup/SearchPanel/CountLabel": { "rect": [ - 88.0, + 80.0, + 186.0, + 1104.0, + 26.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/EmptyLabel": { + "rect": [ + 80.0, + 256.0, + 1104.0, + 40.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 732.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults": { + "rect": [ + 80.0, + 220.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow": { + "rect": [ + 80.0, + 120.0, + 1104.0, + 56.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow/ClearButton": { + "rect": [ + 80.0, + 120.0, + 140.0, + 42.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow/SearchField": { + "rect": [ + 80.0, + 120.0, 112.0, - 872.0, - 80.0 + 47.0 ], "visible": false }, @@ -5789,138 +5906,255 @@ ], "visible": false }, - "SearchPopup/ClearButton": { - "rect": [ - 992.0, - 112.0, - 182.0, - 80.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer": { - "rect": [ - 56.0, - 224.0, - 1152.0, - 512.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults": { - "rect": [ - 56.0, - 224.0, - 0.0, - 0.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#0": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#1": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#2": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#3": { - "rect": [ - 56.0, - 224.0, - 2.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#4": { - "rect": [ - 56.0, - 224.0, - 2.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#5": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#6": { - "rect": [ - 56.0, - 224.0, - 2.0, - 462.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#7": { - "rect": [ - 56.0, - 224.0, - 2.0, - 202.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#8": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#9": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, "SearchPopup/SearchPanel": { "rect": [ 56.0, 96.0, 1152.0, - 640.0 + 880.0 ], "visible": false }, - "SearchPopup/Searchbar": { + "SearchPopup/SearchPanel/CountLabel": { "rect": [ - 88.0, + 80.0, + 186.0, + 1104.0, + 26.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/EmptyLabel": { + "rect": [ + 80.0, + 256.0, + 1104.0, + 40.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 732.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults": { + "rect": [ + 80.0, + 220.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow": { + "rect": [ + 80.0, + 120.0, + 1104.0, + 56.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow/ClearButton": { + "rect": [ + 80.0, + 120.0, + 140.0, + 42.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow/SearchField": { + "rect": [ + 80.0, + 120.0, 112.0, - 872.0, - 80.0 + 47.0 ], "visible": false }, @@ -9517,138 +9751,255 @@ ], "visible": false }, - "SearchPopup/ClearButton": { - "rect": [ - 992.0, - 112.0, - 182.0, - 80.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer": { - "rect": [ - 56.0, - 224.0, - 1152.0, - 512.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults": { - "rect": [ - 56.0, - 224.0, - 0.0, - 0.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#0": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#1": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#2": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#3": { - "rect": [ - 56.0, - 224.0, - 2.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#4": { - "rect": [ - 56.0, - 224.0, - 2.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#5": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#6": { - "rect": [ - 56.0, - 224.0, - 2.0, - 462.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#7": { - "rect": [ - 56.0, - 224.0, - 2.0, - 202.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#8": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#9": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, "SearchPopup/SearchPanel": { "rect": [ 56.0, 96.0, 1152.0, - 640.0 + 880.0 ], "visible": false }, - "SearchPopup/Searchbar": { + "SearchPopup/SearchPanel/CountLabel": { "rect": [ - 88.0, + 80.0, + 186.0, + 1104.0, + 26.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/EmptyLabel": { + "rect": [ + 80.0, + 256.0, + 1104.0, + 40.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 732.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults": { + "rect": [ + 80.0, + 220.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow": { + "rect": [ + 80.0, + 120.0, + 1104.0, + 56.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow/ClearButton": { + "rect": [ + 80.0, + 120.0, + 140.0, + 42.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow/SearchField": { + "rect": [ + 80.0, + 120.0, 112.0, - 872.0, - 80.0 + 47.0 ], "visible": false }, @@ -11813,120 +12164,266 @@ ], "visible": true }, - "SearchPopup/ClearButton": { - "rect": [ - 992.0, - 112.0, - 182.0, - 80.0 - ], - "visible": true - }, - "SearchPopup/ScrollContainer": { + "SearchPopup/SearchPanel": { "rect": [ 56.0, - 224.0, + 96.0, 1152.0, - 512.0 + 880.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults": { + "SearchPopup/SearchPanel/CountLabel": { "rect": [ - 56.0, - 224.0, - 1136.0, - 532.0 + 80.0, + 186.0, + 1104.0, + 26.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults/#0": { + "SearchPopup/SearchPanel/EmptyLabel": { "rect": [ - 56.0, - 224.0, - 1136.0, - 46.0 + 80.0, + 256.0, + 1104.0, + 40.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 732.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults/#1": { + "SearchPopup/SearchPanel/ScrollContainer/SearchResults": { "rect": [ - 56.0, - 278.0, - 1136.0, - 46.0 + 80.0, + 220.0, + 1104.0, + 386.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults/#2": { + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0": { "rect": [ - 56.0, - 332.0, - 1136.0, - 46.0 + 80.0, + 220.0, + 1104.0, + 35.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults/#3": { + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0/#0": { "rect": [ - 56.0, - 386.0, - 1136.0, - 46.0 + 88.0, + 222.0, + 1088.0, + 31.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults/#4": { + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1": { "rect": [ - 56.0, - 440.0, - 1136.0, - 46.0 + 80.0, + 259.0, + 1104.0, + 35.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults/#5": { + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1/#0": { "rect": [ - 56.0, - 494.0, - 1136.0, - 46.0 + 88.0, + 261.0, + 1088.0, + 31.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults/#6": { + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2": { "rect": [ - 56.0, - 548.0, - 1136.0, - 46.0 + 80.0, + 298.0, + 1104.0, + 35.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults/#7": { + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2/#0": { "rect": [ - 56.0, - 602.0, - 1136.0, - 46.0 + 88.0, + 300.0, + 1088.0, + 31.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults/#8": { + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3": { "rect": [ - 56.0, - 656.0, - 1136.0, - 46.0 + 80.0, + 337.0, + 1104.0, + 35.0 ], "visible": true }, - "SearchPopup/ScrollContainer/SearchResults/#9": { + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3/#0": { + "rect": [ + 88.0, + 339.0, + 1088.0, + 31.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4": { + "rect": [ + 80.0, + 376.0, + 1104.0, + 35.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4/#0": { + "rect": [ + 88.0, + 378.0, + 1088.0, + 31.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5": { + "rect": [ + 80.0, + 415.0, + 1104.0, + 35.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5/#0": { + "rect": [ + 88.0, + 417.0, + 1088.0, + 31.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6": { + "rect": [ + 80.0, + 454.0, + 1104.0, + 35.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6/#0": { + "rect": [ + 88.0, + 456.0, + 1088.0, + 31.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7": { + "rect": [ + 80.0, + 493.0, + 1104.0, + 35.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7/#0": { + "rect": [ + 88.0, + 495.0, + 1088.0, + 31.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8": { + "rect": [ + 80.0, + 532.0, + 1104.0, + 35.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8/#0": { + "rect": [ + 88.0, + 534.0, + 1088.0, + 31.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9": { + "rect": [ + 80.0, + 571.0, + 1104.0, + 35.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9/#0": { + "rect": [ + 88.0, + 573.0, + 1088.0, + 31.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/TopRow": { + "rect": [ + 80.0, + 120.0, + 1104.0, + 56.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/TopRow/ClearButton": { + "rect": [ + 1044.0, + 120.0, + 140.0, + 56.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/TopRow/SearchField": { + "rect": [ + 80.0, + 120.0, + 948.0, + 56.0 + ], + "visible": true + } + }, + "popup_Search_typed": { + "SearchPopup": { "rect": [ 56.0, - 710.0, - 1136.0, - 46.0 + 96.0, + 1920.0, + 1080.0 ], "visible": true }, @@ -11935,16 +12432,250 @@ 56.0, 96.0, 1152.0, - 640.0 + 880.0 ], "visible": true }, - "SearchPopup/Searchbar": { + "SearchPopup/SearchPanel/CountLabel": { + "rect": [ + 80.0, + 186.0, + 1104.0, + 26.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/EmptyLabel": { + "rect": [ + 80.0, + 256.0, + 1104.0, + 40.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 732.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 35.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 35.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0/#0": { "rect": [ 88.0, - 112.0, - 872.0, - 80.0 + 222.0, + 1088.0, + 31.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1/#0": { + "rect": [ + 88.0, + 222.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2": { + "rect": [ + 80.0, + 259.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2/#0": { + "rect": [ + 88.0, + 261.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3": { + "rect": [ + 80.0, + 298.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3/#0": { + "rect": [ + 88.0, + 300.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4": { + "rect": [ + 80.0, + 337.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4/#0": { + "rect": [ + 88.0, + 339.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5": { + "rect": [ + 80.0, + 376.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5/#0": { + "rect": [ + 88.0, + 378.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6": { + "rect": [ + 80.0, + 415.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6/#0": { + "rect": [ + 88.0, + 417.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7": { + "rect": [ + 80.0, + 493.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7/#0": { + "rect": [ + 88.0, + 495.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8": { + "rect": [ + 80.0, + 532.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8/#0": { + "rect": [ + 88.0, + 534.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9": { + "rect": [ + 80.0, + 571.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9/#0": { + "rect": [ + 88.0, + 573.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow": { + "rect": [ + 80.0, + 120.0, + 1104.0, + 56.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/TopRow/ClearButton": { + "rect": [ + 1044.0, + 120.0, + 140.0, + 56.0 + ], + "visible": true + }, + "SearchPopup/SearchPanel/TopRow/SearchField": { + "rect": [ + 80.0, + 120.0, + 948.0, + 56.0 ], "visible": true } @@ -16102,138 +16833,255 @@ ], "visible": false }, - "SearchPopup/ClearButton": { - "rect": [ - 992.0, - 112.0, - 182.0, - 80.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer": { - "rect": [ - 56.0, - 224.0, - 1152.0, - 512.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults": { - "rect": [ - 56.0, - 224.0, - 0.0, - 0.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#0": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#1": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#2": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#3": { - "rect": [ - 56.0, - 224.0, - 2.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#4": { - "rect": [ - 56.0, - 224.0, - 2.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#5": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#6": { - "rect": [ - 56.0, - 224.0, - 2.0, - 462.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#7": { - "rect": [ - 56.0, - 224.0, - 2.0, - 202.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#8": { - "rect": [ - 56.0, - 224.0, - 2.0, - 98.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#9": { - "rect": [ - 56.0, - 224.0, - 2.0, - 150.0 - ], - "visible": false - }, "SearchPopup/SearchPanel": { "rect": [ 56.0, 96.0, 1152.0, - 640.0 + 880.0 ], "visible": false }, - "SearchPopup/Searchbar": { + "SearchPopup/SearchPanel/CountLabel": { "rect": [ - 88.0, + 80.0, + 186.0, + 1104.0, + 26.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/EmptyLabel": { + "rect": [ + 80.0, + 256.0, + 1104.0, + 40.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 732.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults": { + "rect": [ + 80.0, + 220.0, + 0.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9": { + "rect": [ + 80.0, + 220.0, + 17.0, + 4.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9/#0": { + "rect": [ + 80.0, + 220.0, + 1.0, + 0.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow": { + "rect": [ + 80.0, + 120.0, + 1104.0, + 56.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow/ClearButton": { + "rect": [ + 80.0, + 120.0, + 140.0, + 42.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow/SearchField": { + "rect": [ + 80.0, + 120.0, 112.0, - 872.0, - 80.0 + 47.0 ], "visible": false }, @@ -24030,138 +24878,255 @@ ], "visible": false }, - "SearchPopup/ClearButton": { - "rect": [ - 992.0, - 112.0, - 182.0, - 80.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer": { - "rect": [ - 56.0, - 224.0, - 1152.0, - 512.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults": { - "rect": [ - 56.0, - 224.0, - 1136.0, - 532.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#0": { - "rect": [ - 56.0, - 224.0, - 1136.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#1": { - "rect": [ - 56.0, - 278.0, - 1136.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#2": { - "rect": [ - 56.0, - 332.0, - 1136.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#3": { - "rect": [ - 56.0, - 386.0, - 1136.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#4": { - "rect": [ - 56.0, - 440.0, - 1136.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#5": { - "rect": [ - 56.0, - 494.0, - 1136.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#6": { - "rect": [ - 56.0, - 548.0, - 1136.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#7": { - "rect": [ - 56.0, - 602.0, - 1136.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#8": { - "rect": [ - 56.0, - 656.0, - 1136.0, - 46.0 - ], - "visible": false - }, - "SearchPopup/ScrollContainer/SearchResults/#9": { - "rect": [ - 56.0, - 710.0, - 1136.0, - 46.0 - ], - "visible": false - }, "SearchPopup/SearchPanel": { "rect": [ 56.0, 96.0, 1152.0, - 640.0 + 880.0 ], "visible": false }, - "SearchPopup/Searchbar": { + "SearchPopup/SearchPanel/CountLabel": { + "rect": [ + 80.0, + 186.0, + 1104.0, + 26.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/EmptyLabel": { + "rect": [ + 80.0, + 256.0, + 1104.0, + 40.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 732.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 386.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#0/#0": { "rect": [ 88.0, - 112.0, - 872.0, - 80.0 + 222.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1": { + "rect": [ + 80.0, + 259.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#1/#0": { + "rect": [ + 88.0, + 261.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2": { + "rect": [ + 80.0, + 298.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#2/#0": { + "rect": [ + 88.0, + 300.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3": { + "rect": [ + 80.0, + 337.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#3/#0": { + "rect": [ + 88.0, + 339.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4": { + "rect": [ + 80.0, + 376.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#4/#0": { + "rect": [ + 88.0, + 378.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5": { + "rect": [ + 80.0, + 415.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#5/#0": { + "rect": [ + 88.0, + 417.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6": { + "rect": [ + 80.0, + 220.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#6/#0": { + "rect": [ + 88.0, + 222.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7": { + "rect": [ + 80.0, + 493.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#7/#0": { + "rect": [ + 88.0, + 495.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8": { + "rect": [ + 80.0, + 532.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#8/#0": { + "rect": [ + 88.0, + 534.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9": { + "rect": [ + 80.0, + 571.0, + 1104.0, + 35.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/ScrollContainer/SearchResults/#9/#0": { + "rect": [ + 88.0, + 573.0, + 1088.0, + 31.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow": { + "rect": [ + 80.0, + 120.0, + 1104.0, + 56.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow/ClearButton": { + "rect": [ + 1044.0, + 120.0, + 140.0, + 56.0 + ], + "visible": false + }, + "SearchPopup/SearchPanel/TopRow/SearchField": { + "rect": [ + 80.0, + 120.0, + 948.0, + 56.0 ], "visible": false }, diff --git a/tests/fakes/ui_tour.gd b/tests/fakes/ui_tour.gd index 38d2daf..bd99316 100644 --- a/tests/fakes/ui_tour.gd +++ b/tests/fakes/ui_tour.gd @@ -215,6 +215,15 @@ func run(on_state: Callable) -> void: _close_popup(popup) await _settle() + var search_popup: SearchPopup = _node("SearchPopup") + search_popup.open() + search_popup.search_field.text = "lttp" + search_popup.search() + await _settle() + await on_state.call("popup_Search_typed", search_popup) + search_popup.close() + await _settle() + var sync_popup: BasePopup = _node("SyncPopup") Settings.currently_syncing = false sync_popup.has_synced = false diff --git a/tests/integration/test_popups.gd b/tests/integration/test_popups.gd index 54c0af8..1add3dd 100644 --- a/tests/integration/test_popups.gd +++ b/tests/integration/test_popups.gd @@ -103,12 +103,12 @@ func test_button_works_again_after_popup_closed() -> void: func test_closing_search_clears_the_search() -> void: var search: BasePopup = _popup("SearchPopup") search.open() - var search_bar: TextEdit = search.get_node("Searchbar") - search_bar.text = "mario" + var search_field: LineEdit = search.search_field + search_field.text = "mario" search.close() - assert_str(search_bar.text).is_empty() + assert_str(search_field.text).is_empty() func test_closing_character_select_allows_song_clicks_again() -> void: @@ -129,3 +129,4 @@ func test_toggle_opens_and_closes() -> void: shortcuts.toggle() assert_bool(shortcuts.visible).is_false() + diff --git a/tests/integration/test_search_popup.gd b/tests/integration/test_search_popup.gd new file mode 100644 index 0000000..4bee82a --- /dev/null +++ b/tests/integration/test_search_popup.gd @@ -0,0 +1,143 @@ +extends MainWindowTestSuite +## The Search popup: typing, the result list, the count, the highlighted letters and +## the layout. + +const SOUNDTRACKS: Array[String] = [ + "Chrono Trigger", "Final Fantasy VI", "Mario & Luigi: Superstar Saga", "Super Mario 64", + "The Legend of Zelda: A Link to the Past", +] + +var _popup: SearchPopup + + +func _before_window_starts() -> void: + _server.respond_json("/music/all/order", SOUNDTRACKS) + + +func before_test() -> void: + super() + _popup = _window.get_node("SearchPopup") + + +func _open() -> void: + await _click_at(_window.get_node("TopBar/SearchButton").get_global_rect().get_center()) + await _runner.simulate_frames(1) + + +func _type(text: String) -> void: + for letter: String in text: + _popup.search_field.insert_text_at_caret(letter) + _popup.search_field.text_changed.emit(_popup.search_field.text) + + +func _row_texts() -> Array[String]: + var texts: Array[String] = [] + for row: PanelContainer in _popup.search_results.get_children(): + if row.visible: + texts.append((row.get_child(0) as RichTextLabel).text) + return texts + + +func test_shows_every_soundtrack_with_a_count() -> void: + await _open() + + assert_array(_popup.shown_names()).contains_exactly(SOUNDTRACKS) + assert_str(_popup.count_label.text).is_equal("5 soundtracks") + assert_bool(_popup.empty_label.visible).is_false() + assert_bool(_popup.search_field.has_focus()).is_true() + + +func test_typing_filters_and_counts() -> void: + await _open() + + _type("mario") + + assert_array(_popup.shown_names()) \ + .contains_exactly(["Mario & Luigi: Superstar Saga", "Super Mario 64"]) + assert_str(_popup.count_label.text).is_equal("2 of 5 soundtracks") + + +func test_enter_does_not_break_the_search() -> void: + await _open() + _type("chrono") + + _runner.simulate_key_pressed(KEY_ENTER) + await _runner.await_input_processed() + + assert_str(_popup.search_field.text).is_equal("chrono") + assert_array(_popup.shown_names()).contains_exactly(["Chrono Trigger"]) + + +func test_no_match_says_so() -> void: + await _open() + + _type("metroid") + + assert_array(_popup.shown_names()).is_empty() + assert_bool(_popup.empty_label.visible).is_true() + assert_str(_popup.count_label.text).is_equal("0 of 5 soundtracks") + + +func test_short_search_does_not_match_letters_far_apart() -> void: + await _open() + + _type("sm") + + assert_array(_popup.shown_names()).is_empty() + _type("r") + assert_array(_popup.shown_names()).contains_exactly(["Super Mario 64"]) + + +func test_matched_letters_are_highlighted() -> void: + await _open() + var color: String = _popup.get_theme_color(&"font_color", &"HighlightLarge").to_html(false) + + _type("trig") + + assert_array(_row_texts()).contains_exactly(["Chrono [color=#%s]Trig[/color]ger" % color]) + + +func test_letters_in_order_are_highlighted_one_by_one() -> void: + var color: Color = Color.ORANGE + var text: String = SearchPopup.highlighted_name("Super Mario 64", "smr", color) + var c: String = color.to_html(false) + + assert_str(text).is_equal( + "[color=#%s]S[/color]uper [color=#%s]M[/color]a[color=#%s]r[/color]io 64" % [c, c, c]) + + +func test_square_brackets_in_names_are_shown_as_text() -> void: + assert_str(SearchPopup.highlighted_name("Disc [1]", "", Color.ORANGE)) \ + .is_equal("Disc [lb]1]") + + +func test_clear_shows_everything_again_without_highlights() -> void: + await _open() + _type("mario") + + await _click_at(_popup.clear_button.get_global_rect().get_center()) + + assert_str(_popup.search_field.text).is_empty() + assert_array(_row_texts()).contains_exactly(SOUNDTRACKS) + + +func test_rows_alternate_like_the_other_lists() -> void: + await _open() + _type("mario") + + var rows: Array[Node] = _popup.search_results.get_children().filter( + func(row: Control) -> bool: return row.visible) + assert_str(String(rows[0].theme_type_variation)).is_equal("ListRowEven") + assert_str(String(rows[1].theme_type_variation)).is_equal("ListRowOdd") + + +func test_everything_fits_inside_the_panel_at_its_real_size() -> void: + await _open() + + var panel: Rect2 = _popup.search_panel.get_global_rect() + for node: Control in [_popup.search_field, _popup.clear_button, _popup.count_label, + _popup.search_results.get_parent()]: + assert_bool(panel.encloses(node.get_global_rect())) \ + .override_failure_message(node.name + " is outside the panel").is_true() + assert_vector(node.scale).is_equal(Vector2.ONE) + assert_bool(Rect2(Vector2.ZERO, Vector2(1920, 1080)).encloses(panel)).is_true() diff --git a/tests/integration/test_search_popup.gd.uid b/tests/integration/test_search_popup.gd.uid new file mode 100644 index 0000000..b24ad30 --- /dev/null +++ b/tests/integration/test_search_popup.gd.uid @@ -0,0 +1 @@ +uid://s5ps2kikouh7 diff --git a/tests/integration/test_theme.gd b/tests/integration/test_theme.gd index 73dd506..059cb83 100644 --- a/tests/integration/test_theme.gd +++ b/tests/integration/test_theme.gd @@ -34,6 +34,9 @@ const STYLES: Dictionary = { "StatisticsCardHover": "PanelContainer", "StatisticsGrid": "GridContainer", "TabButton": "Button", + "LargeButton": "Button", + "SearchField": "LineEdit", + "SearchResult": "RichTextLabel", } ## Theme types that only scripts read from, so they are not styles a node can pick. diff --git a/tests/unit/test_soundtrack_search.gd b/tests/unit/test_soundtrack_search.gd index 35f81f6..3947f75 100644 --- a/tests/unit/test_soundtrack_search.gd +++ b/tests/unit/test_soundtrack_search.gd @@ -63,16 +63,30 @@ func test_clean_term() -> void: assert_str(SoundtrackSearch.clean_term("Ys I & II")).is_equal("ysiandii") -func test_search_popup_searches_with_soundtrack_search() -> void: - var popup: SearchPopup = auto_free(load("res://popups/SearchPopup.tscn").instantiate()) - add_child(popup) - popup.set_soundtracks(["Chrono Trigger", "Super Mario 64"]) - popup.show_all_soundtracks() +func test_short_search_only_finds_names_containing_it() -> void: + # "sm" is in order in "Super Mario 64" but not next to each other + assert_int(Settings.LETTERS_FOR_LOOSE_SEARCH).is_equal(3) + assert_array(_search.find_matches("sm")).is_empty() + assert_array(_search.find_matches("smr")).contains_exactly(["Super Mario 64"]) - popup.perform_search("smr") - var shown: Array[String] = [] - for label: Label in popup.search_list.get_children(): - if label.visible: - shown.append(label.text) - assert_array(shown).contains_exactly(["Super Mario 64"]) +func test_matched_letters_of_plain_match() -> void: + assert_array(SoundtrackSearch.matched_letters("Chrono Trigger", "trig")) \ + .contains_exactly([7, 8, 9, 10]) + + +func test_matched_letters_ignoring_spaces_and_special_characters() -> void: + # "mario and luigi" matches "Mario & Luigi" with & read as "and" + var letters: Array[int] = SoundtrackSearch.matched_letters( + "Mario & Luigi: Superstar Saga", "mario and luigi") + assert_array(letters).contains_exactly([0, 1, 2, 3, 4, 6, 8, 9, 10, 11, 12]) + + +func test_matched_letters_in_order() -> void: + assert_array(SoundtrackSearch.matched_letters("Super Mario 64", "smr")) \ + .contains_exactly([0, 6, 8]) + + +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() diff --git a/themes/main_theme.tres b/themes/main_theme.tres index d5da8db..7f33e39 100644 --- a/themes/main_theme.tres +++ b/themes/main_theme.tres @@ -830,6 +830,8 @@ Label/constants/shadow_outline_size = 1 Label/font_sizes/font_size = 16 Label/styles/focus = SubResource("StyleBoxFlat_6h40v") Label/styles/normal = SubResource("StyleBoxEmpty_s47sh") +LargeButton/base_type = &"Button" +LargeButton/font_sizes/font_size = 24 ListRowEven/base_type = &"PanelContainer" ListRowEven/styles/panel = SubResource("StyleBoxFlat_u5sc5") ListRowOdd/base_type = &"PanelContainer" @@ -914,6 +916,10 @@ ScrollContainer/colors/scroll_hint_horizontal_color = Color(0, 0, 0, 1) ScrollContainer/colors/scroll_hint_vertical_color = Color(0, 0, 0, 1) ScrollContainer/styles/focus = SubResource("StyleBoxFlat_35dwi") ScrollContainer/styles/panel = SubResource("StyleBoxEmpty_r3gy8") +SearchField/base_type = &"LineEdit" +SearchField/font_sizes/font_size = 28 +SearchResult/base_type = &"RichTextLabel" +SearchResult/font_sizes/normal_font_size = 22 ShortcutRowDark/base_type = &"PanelContainer" ShortcutRowDark/styles/panel = SubResource("StyleBoxFlat_anjdx") SongRow/base_type = &"PanelContainer"