175 lines
4.2 KiB
GDScript
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)
|