extends PopupPanel class _Item: var label : String var icon : Texture var metadata var current_list_index := -1 func _init(label_ : String, icon_ : Texture, metadata_): self.label = label_ self.icon = icon_ self.metadata = metadata_ export var match_case := false onready var _ledit_search : LineEdit = GDBUtility.find_node_by_name(self, "ledit_search") onready var _ilist_content : ItemList = GDBUtility.find_node_by_name(self, "ilist_content") var _items := [] var __needs_update := false var __caret := 0 var __submitted := false ################ # overridables # ################ func _should_show(item : _Item) -> bool: var search_text := _ledit_search.text.strip_edges() if search_text == "": return true var item_text := item.label if !match_case: search_text = search_text.to_lower() item_text = item_text.to_lower() return search_text in item_text ################ # public stuff # ################ func add_item(label : String, icon : Texture = null, metadata = null) -> void: _items.append(_Item.new(label, icon, metadata)) invalidate() func clear_items() -> void: _items.clear() invalidate() func get_item_label(idx : int) -> String: if idx < 0 || idx >= _items.size(): return "" return _items[idx].label func get_item_metadata(idx : int): if idx < 0 || idx >= _items.size(): return null return _items[idx].metadata func invalidate() -> void: # only updates once if multiple calls are done in a single frame __needs_update = true yield(get_tree(), "idle_frame") if __needs_update: __needs_update = false __update() ################# # private stuff # ################# func __update() -> void: var idx_increment := 0 var insert_index := 0 for item in self._items: var should_show := self._should_show(item) var is_visible : bool = (item.current_list_index > -1) if is_visible: item.current_list_index += idx_increment if should_show && !is_visible: self.__add_item(item, insert_index) item.current_list_index = insert_index idx_increment += 1 elif !should_show && is_visible: _ilist_content.remove_item(item.current_list_index) item.current_list_index = -1 idx_increment -= 1 if should_show: insert_index += 1 if !_ilist_content.is_anything_selected() && _ilist_content.get_item_count() > 0: __select(0) func __add_item(item : _Item, insert_index : int) -> void: var new_idx := _ilist_content.get_item_count() _ilist_content.add_item(item.label, item.icon) _ilist_content.set_item_metadata(new_idx, item) if insert_index != new_idx: _ilist_content.move_item(new_idx, insert_index) func __select(idx : int) -> void: _ilist_content.select(idx) _ilist_content.ensure_current_is_visible() func __select_next() -> void: var selected := _ilist_content.get_selected_items() var idx := 0 if !selected.empty(): idx = selected[0] + 1 if idx >= _ilist_content.get_item_count(): return __select(idx) func __select_prev() -> void: var selected := _ilist_content.get_selected_items() var idx := 0 if !selected.empty(): idx = selected[0] - 1 if idx < 0: return __select(idx) func __item_from_list_index(idx : int) -> _Item: return _ilist_content.get_item_metadata(idx) as _Item func __item_index(itm : _Item) -> int: return _items.find(itm) ################### # signal handlers # ################### func _on_selector_popup_about_to_show() -> void: __submitted = false _ledit_search.clear() _ledit_search.grab_focus() _ilist_content.unselect_all() __update() func _on_ledit_search_text_changed(new_text : String) -> void: __update() func _on_ledit_search_gui_input(event : InputEvent) -> void: if !event.is_pressed(): return if event.is_action("ui_down"): __select_next() elif event.is_action("ui_up"): __select_prev() else: __caret = _ledit_search.caret_position return _ledit_search.caret_position = __caret func _on_ledit_search_text_entered(new_text : String) -> void: var selected := _ilist_content.get_selected_items() if selected.empty(): return var idx := __item_index(__item_from_list_index(selected[0])) __submitted = true emit_signal("selected", idx) hide() func _on_selector_popup_popup_hide() -> void: if !__submitted: emit_signal("selected", -1) ########### # signals # ########### signal selected(index)