234 lines
6.4 KiB
GDScript
234 lines
6.4 KiB
GDScript
## Quick open panel to quickly access all resources that are in the project.
|
|
## Initially shows all resources, but can be changed to more specific resources
|
|
## or filtered down with text.
|
|
@tool
|
|
extends PopupPanel
|
|
|
|
const ADDONS: StringName = &"res://addons"
|
|
const SEPARATOR: StringName = &" - "
|
|
const STRUCTURE_START: StringName = &"("
|
|
const STRUCTURE_END: StringName = &")"
|
|
|
|
#region UI
|
|
@onready var filter_bar: TabBar = %FilterBar
|
|
@onready var search_option_btn: OptionButton = %SearchOptionBtn
|
|
@onready var filter_txt: LineEdit = %FilterTxt
|
|
@onready var files_list: ItemList = %FilesList
|
|
#endregion
|
|
|
|
var plugin: EditorPlugin
|
|
|
|
var scenes: Array[FileData]
|
|
var scripts: Array[FileData]
|
|
var resources: Array[FileData]
|
|
var others: Array[FileData]
|
|
|
|
# For performance and memory considerations, we add all files into one reusable array.
|
|
var all_files: Array[FileData]
|
|
|
|
var is_rebuild_cache: bool = true
|
|
|
|
#region Plugin and Shortcut processing
|
|
func _ready() -> void:
|
|
files_list.item_selected.connect(open_file)
|
|
search_option_btn.item_selected.connect(rebuild_cache_and_ui.unbind(1))
|
|
filter_txt.text_changed.connect(fill_files_list.unbind(1))
|
|
|
|
filter_bar.tab_changed.connect(change_fill_files_list.unbind(1))
|
|
|
|
about_to_popup.connect(on_show)
|
|
|
|
var file_system: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
|
file_system.filesystem_changed.connect(schedule_rebuild)
|
|
|
|
if (plugin != null):
|
|
filter_txt.gui_input.connect(plugin.navigate_on_list.bind(files_list, open_file))
|
|
|
|
func _shortcut_input(event: InputEvent) -> void:
|
|
if (!event.is_pressed() || event.is_echo()):
|
|
return
|
|
|
|
if (plugin.tab_cycle_forward_shc.matches_event(event)):
|
|
get_viewport().set_input_as_handled()
|
|
|
|
var new_tab: int = filter_bar.current_tab + 1
|
|
if (new_tab == filter_bar.get_tab_count()):
|
|
new_tab = 0
|
|
filter_bar.current_tab = new_tab
|
|
elif (plugin.tab_cycle_backward_shc.matches_event(event)):
|
|
get_viewport().set_input_as_handled()
|
|
|
|
var new_tab: int = filter_bar.current_tab - 1
|
|
if (new_tab == -1):
|
|
new_tab = filter_bar.get_tab_count() - 1
|
|
filter_bar.current_tab = new_tab
|
|
#endregion
|
|
|
|
func open_file(index: int):
|
|
hide()
|
|
|
|
var file: String = files_list.get_item_metadata(index)
|
|
|
|
if (ResourceLoader.exists(file)):
|
|
var res: Resource = load(file)
|
|
EditorInterface.edit_resource(res)
|
|
|
|
if (res is PackedScene):
|
|
EditorInterface.open_scene_from_path(file)
|
|
|
|
func schedule_rebuild():
|
|
is_rebuild_cache = true
|
|
|
|
func on_show():
|
|
if (search_option_btn.selected != 0):
|
|
search_option_btn.selected = 0
|
|
|
|
is_rebuild_cache = true
|
|
|
|
var rebuild_ui: bool = false
|
|
var all_tab_not_pressed: bool = filter_bar.current_tab != 0
|
|
rebuild_ui = is_rebuild_cache || all_tab_not_pressed
|
|
|
|
if (is_rebuild_cache):
|
|
rebuild_cache()
|
|
|
|
if (rebuild_ui):
|
|
if (all_tab_not_pressed):
|
|
# Triggers the ui update.
|
|
filter_bar.current_tab = 0
|
|
else:
|
|
fill_files_list()
|
|
|
|
filter_txt.select_all()
|
|
focus_and_select_first()
|
|
|
|
func rebuild_cache():
|
|
is_rebuild_cache = false
|
|
|
|
all_files.clear()
|
|
scenes.clear()
|
|
scripts.clear()
|
|
resources.clear()
|
|
others.clear()
|
|
|
|
build_file_cache()
|
|
|
|
func rebuild_cache_and_ui():
|
|
rebuild_cache()
|
|
fill_files_list()
|
|
|
|
focus_and_select_first()
|
|
|
|
func focus_and_select_first():
|
|
filter_txt.grab_focus()
|
|
|
|
if (files_list.item_count > 0):
|
|
files_list.select(0)
|
|
|
|
func build_file_cache():
|
|
var dir: EditorFileSystemDirectory = EditorInterface.get_resource_filesystem().get_filesystem()
|
|
build_file_cache_dir(dir)
|
|
|
|
all_files.append_array(scenes)
|
|
all_files.append_array(scripts)
|
|
all_files.append_array(resources)
|
|
all_files.append_array(others)
|
|
|
|
func build_file_cache_dir(dir: EditorFileSystemDirectory):
|
|
for index: int in dir.get_subdir_count():
|
|
build_file_cache_dir(dir.get_subdir(index))
|
|
|
|
for index: int in dir.get_file_count():
|
|
var file: String = dir.get_file_path(index)
|
|
if (search_option_btn.get_selected_id() == 0 && file.begins_with(ADDONS)):
|
|
continue
|
|
|
|
var last_delimiter: int = file.rfind(&"/")
|
|
|
|
var file_name: String = file.substr(last_delimiter + 1)
|
|
var file_structure: String = &""
|
|
if (file_name.length() + 6 != file.length()):
|
|
file_structure = SEPARATOR + STRUCTURE_START + file.substr(6, last_delimiter - 6) + STRUCTURE_END
|
|
|
|
var file_data: FileData = FileData.new()
|
|
file_data.file = file
|
|
file_data.file_name = file_name
|
|
file_data.file_name_structure = file_name + file_structure
|
|
file_data.file_type = dir.get_file_type(index)
|
|
|
|
# Needed, as otherwise we have no icon.
|
|
if (file_data.file_type == &"Resource"):
|
|
file_data.file_type = &"Object"
|
|
|
|
match (file.get_extension()):
|
|
&"tscn": scenes.append(file_data)
|
|
&"gd": scripts.append(file_data)
|
|
&"tres": resources.append(file_data)
|
|
&"gdshader": resources.append(file_data)
|
|
_: others.append(file_data)
|
|
|
|
func change_fill_files_list():
|
|
fill_files_list()
|
|
|
|
focus_and_select_first()
|
|
|
|
func fill_files_list():
|
|
files_list.clear()
|
|
|
|
if (filter_bar.current_tab == 0):
|
|
fill_files_list_with(all_files)
|
|
elif (filter_bar.current_tab == 1):
|
|
fill_files_list_with(scenes)
|
|
elif (filter_bar.current_tab == 2):
|
|
fill_files_list_with(scripts)
|
|
elif (filter_bar.current_tab == 3):
|
|
fill_files_list_with(resources)
|
|
elif (filter_bar.current_tab == 4):
|
|
fill_files_list_with(others)
|
|
|
|
func fill_files_list_with(files: Array[FileData]):
|
|
var filter_text: String = filter_txt.text
|
|
files.sort_custom(sort_by_filter)
|
|
|
|
for file_data: FileData in files:
|
|
var file: String = file_data.file
|
|
if (filter_text.is_empty() || filter_text.is_subsequence_ofn(file)):
|
|
var icon: Texture2D = EditorInterface.get_base_control().get_theme_icon(file_data.file_type, &"EditorIcons")
|
|
|
|
files_list.add_item(file_data.file_name_structure, icon)
|
|
files_list.set_item_metadata(files_list.item_count - 1, file)
|
|
files_list.set_item_tooltip(files_list.item_count - 1, file)
|
|
|
|
func sort_by_filter(file_data1: FileData, file_data2: FileData) -> bool:
|
|
var filter_text: String = filter_txt.text
|
|
var name1: String = file_data1.file_name
|
|
var name2: String = file_data2.file_name
|
|
|
|
for index: int in filter_text.length():
|
|
var a_oob: bool = index >= name1.length()
|
|
var b_oob: bool = index >= name2.length()
|
|
|
|
if (a_oob):
|
|
if (b_oob):
|
|
return false;
|
|
return true
|
|
if (b_oob):
|
|
return false
|
|
|
|
var char: String = filter_text[index]
|
|
var a_match: bool = char == name1[index]
|
|
var b_match: bool = char == name2[index]
|
|
|
|
if (a_match && !b_match):
|
|
return true
|
|
|
|
if (b_match && !a_match):
|
|
return false
|
|
|
|
return name1 < name2
|
|
|
|
class FileData:
|
|
var file: String
|
|
var file_name: String
|
|
var file_name_structure: String
|
|
var file_type: StringName
|