gduibasics/scenes/scripts/selector_popup.gd
2021-08-21 17:15:07 +02:00

175 lines
4.2 KiB
GDScript

extends PopupPanel
class_name SelectorPopup
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)