class_name SearchPopup extends BasePopup ## Searches every soundtrack on the server as you type, with [SoundtrackSearch]. var soundtracks: Array = [] var label_pool: Array[Label] = [] var _matcher: SoundtrackSearch = SoundtrackSearch.new() @onready var search_list: VBoxContainer = %SearchResults @onready var search_panel: Panel = %SearchPanel @onready var clear_button: Button = $ClearButton @onready var search_bar: TextEdit = $Searchbar 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() func close() -> void: clear() super() 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) else: label = Label.new() label.text = soundtrack label.autowrap_mode = TextServer.AUTOWRAP_WORD search_list.add_child(label) label.visible = true 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 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. func set_soundtracks(list: Array) -> void: soundtracks = [] soundtracks.append_array(list) _matcher.set_names(soundtracks) func clear() -> void: search_bar.text = "" show_all_soundtracks() search_bar.grab_focus()