530 lines
17 KiB
GDScript
530 lines
17 KiB
GDScript
@tool
|
|
class_name DialogicSidebar extends Control
|
|
|
|
## Script that handles the editor sidebar.
|
|
|
|
signal content_item_activated(item_name)
|
|
signal show_sidebar(show: bool)
|
|
|
|
# References
|
|
@onready var editors_manager = get_parent().get_parent()
|
|
@onready var resource_tree: Tree = %ResourceTree
|
|
|
|
var current_resource_list: Array = []
|
|
|
|
enum GroupMode {
|
|
NONE,
|
|
TYPE,
|
|
FOLDER,
|
|
PATH,
|
|
}
|
|
var group_mode: GroupMode = GroupMode.TYPE
|
|
|
|
|
|
func _ready() -> void:
|
|
if owner != null and owner.get_parent() is SubViewport:
|
|
return
|
|
if editors_manager is SubViewportContainer:
|
|
return
|
|
|
|
## CONNECTIONS
|
|
editors_manager.resource_opened.connect(_on_editors_resource_opened)
|
|
editors_manager.editor_changed.connect(_on_editors_editor_changed)
|
|
|
|
resource_tree.item_activated.connect(_on_resources_tree_item_activated)
|
|
resource_tree.item_mouse_selected.connect(_on_resources_tree_item_clicked)
|
|
resource_tree.item_collapsed.connect(_on_resources_tree_item_collapsed)
|
|
|
|
%ContentList.item_selected.connect(
|
|
func(idx: int): content_item_activated.emit(%ContentList.get_item_text(idx))
|
|
)
|
|
|
|
%OpenButton.pressed.connect(_show_sidebar)
|
|
%CloseButton.pressed.connect(_hide_sidebar)
|
|
|
|
var editor_scale := DialogicUtil.get_editor_scale()
|
|
|
|
## ICONS
|
|
%Logo.texture = load("res://addons/dialogic/Editor/Images/dialogic-logo.svg")
|
|
%Logo.custom_minimum_size.y = 30 * editor_scale
|
|
%Search.right_icon = get_theme_icon("Search", "EditorIcons")
|
|
%Options.icon = get_theme_icon("GuiTabMenuHl", "EditorIcons")
|
|
%OptionsPanel.add_theme_stylebox_override("panel", get_theme_stylebox("PanelForeground", "EditorStyles"))
|
|
%OptionsPopup.hide()
|
|
|
|
%ContentList.add_theme_color_override(
|
|
"font_hovered_color", get_theme_color("warning_color", "Editor")
|
|
)
|
|
%ContentList.add_theme_color_override(
|
|
"font_selected_color", get_theme_color("property_color_z", "Editor")
|
|
)
|
|
|
|
## RIGHT CLICK MENU
|
|
%RightClickMenu.clear()
|
|
%RightClickMenu.add_icon_item(get_theme_icon("Remove", "EditorIcons"), "Remove From List", 1)
|
|
%RightClickMenu.add_separator()
|
|
%RightClickMenu.add_icon_item(get_theme_icon("ActionCopy", "EditorIcons"), "Copy Identifier", 4)
|
|
%RightClickMenu.add_separator()
|
|
%RightClickMenu.add_icon_item(
|
|
get_theme_icon("Filesystem", "EditorIcons"), "Show in FileSystem", 2
|
|
)
|
|
%RightClickMenu.add_icon_item(
|
|
get_theme_icon("ExternalLink", "EditorIcons"), "Open in External Program", 3
|
|
)
|
|
|
|
## SORT MENU
|
|
%GroupingOptions.set_item_icon(0, get_theme_icon("AnimationTrackGroup", "EditorIcons"))
|
|
%GroupingOptions.set_item_icon(1, get_theme_icon("Folder", "EditorIcons"))
|
|
%GroupingOptions.set_item_icon(2, get_theme_icon("FolderBrowse", "EditorIcons"))
|
|
%GroupingOptions.set_item_icon(3, get_theme_icon("AnimationTrackList", "EditorIcons"))
|
|
%GroupingOptions.item_selected.connect(_on_grouping_changed)
|
|
|
|
await get_tree().process_frame
|
|
if DialogicUtil.get_editor_setting("sidebar_collapsed", false):
|
|
_hide_sidebar()
|
|
|
|
%MainVSplit.split_offset = DialogicUtil.get_editor_setting("sidebar_v_split", 0)
|
|
group_mode = DialogicUtil.get_editor_setting("sidebar_group_mode", 0)
|
|
%GroupingOptions.select(%GroupingOptions.get_item_index(group_mode))
|
|
|
|
%FolderColors.button_pressed = DialogicUtil.get_editor_setting("sidebar_use_folder_colors", true)
|
|
%TrimFolderPaths.button_pressed = DialogicUtil.get_editor_setting("sidebar_trim_folder_paths", true)
|
|
|
|
update_resource_list()
|
|
|
|
|
|
func set_unsaved_indicator(saved: bool = true) -> void:
|
|
if saved and %CurrentResource.text.ends_with("(*)"):
|
|
%CurrentResource.text = %CurrentResource.text.trim_suffix("(*)")
|
|
if not saved and not %CurrentResource.text.ends_with("(*)"):
|
|
%CurrentResource.text = %CurrentResource.text + "(*)"
|
|
|
|
|
|
func _on_logo_gui_input(event: InputEvent) -> void:
|
|
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
|
editors_manager.open_editor(editors_manager.editors["HomePage"].node)
|
|
|
|
|
|
#region SHOW/HIDE SIDEBAR
|
|
################################################################################
|
|
|
|
func _show_sidebar() -> void:
|
|
%VBoxPrimary.show()
|
|
%VBoxHidden.hide()
|
|
DialogicUtil.set_editor_setting("sidebar_collapsed", false)
|
|
show_sidebar.emit(true)
|
|
|
|
|
|
func _hide_sidebar() -> void:
|
|
%VBoxPrimary.hide()
|
|
%VBoxHidden.show()
|
|
DialogicUtil.set_editor_setting("sidebar_collapsed", true)
|
|
show_sidebar.emit(false)
|
|
|
|
#endregion
|
|
|
|
|
|
################################################################################
|
|
## RESOURCE LIST
|
|
################################################################################
|
|
|
|
|
|
func _on_editors_resource_opened(_resource: Resource) -> void:
|
|
update_resource_list()
|
|
|
|
|
|
func _on_editors_editor_changed(_previous: DialogicEditor, current: DialogicEditor) -> void:
|
|
%ContentListSection.visible = current.current_resource is DialogicTimeline
|
|
update_resource_list()
|
|
|
|
|
|
## Cleans resources that have been deleted from the resource list
|
|
func clean_resource_list(resources_list: Array = []) -> PackedStringArray:
|
|
return PackedStringArray(resources_list.filter(func(x): return ResourceLoader.exists(x)))
|
|
|
|
|
|
#region BULDING/FILTERING THE RESOURCE LIST
|
|
|
|
func update_resource_list(resources_list: PackedStringArray = []) -> void:
|
|
var filter: String = %Search.text
|
|
var current_file := ""
|
|
if editors_manager.current_editor and editors_manager.current_editor.current_resource:
|
|
current_file = editors_manager.current_editor.current_resource.resource_path
|
|
|
|
var character_directory: Dictionary = DialogicResourceUtil.get_character_directory()
|
|
var timeline_directory: Dictionary = DialogicResourceUtil.get_timeline_directory()
|
|
if resources_list.is_empty():
|
|
resources_list = DialogicUtil.get_editor_setting("last_resources", [])
|
|
if not current_file in resources_list:
|
|
resources_list.append(current_file)
|
|
|
|
resources_list = clean_resource_list(resources_list)
|
|
|
|
%CurrentResource.text = "No Resource"
|
|
%CurrentResource.add_theme_color_override(
|
|
"font_uneditable_color", get_theme_color("disabled_font_color", "Editor")
|
|
)
|
|
|
|
resource_tree.clear()
|
|
|
|
var character_items: Array = get_directory_items.call(character_directory, filter, load("res://addons/dialogic/Editor/Images/Resources/character.svg"), resources_list)
|
|
var timeline_items: Array = get_directory_items.call(timeline_directory, filter, get_theme_icon("TripleBar", "EditorIcons"), resources_list)
|
|
var all_items := character_items + timeline_items
|
|
|
|
# BUILD TREE
|
|
var root: TreeItem = resource_tree.create_item()
|
|
|
|
match group_mode:
|
|
GroupMode.NONE:
|
|
all_items.sort_custom(_sort_by_item_text)
|
|
for item in all_items:
|
|
add_item(item, root, current_file)
|
|
|
|
|
|
GroupMode.TYPE:
|
|
character_items.sort_custom(_sort_by_item_text)
|
|
timeline_items.sort_custom(_sort_by_item_text)
|
|
if character_items.size() > 0:
|
|
var character_tree := add_folder_item("Characters", root)
|
|
for item in character_items:
|
|
add_item(item, character_tree, current_file)
|
|
|
|
if timeline_items.size() > 0:
|
|
var timeline_tree := add_folder_item("Timelines", root)
|
|
for item in timeline_items:
|
|
add_item(item, timeline_tree, current_file)
|
|
|
|
|
|
GroupMode.FOLDER:
|
|
var dirs := {}
|
|
for item in all_items:
|
|
var dir := item.get_parent_directory() as String
|
|
if not dirs.has(dir):
|
|
dirs[dir] = []
|
|
dirs[dir].append(item)
|
|
|
|
for dir in dirs:
|
|
var dir_item := add_folder_item(dir, root)
|
|
|
|
for item in dirs[dir]:
|
|
add_item(item, dir_item, current_file)
|
|
|
|
|
|
GroupMode.PATH:
|
|
# Collect all different directories that contain resources
|
|
var dirs := {}
|
|
for item in all_items:
|
|
var path := (item.metadata.get_base_dir() as String).trim_prefix("res://")
|
|
if not dirs.has(path):
|
|
dirs[path] = []
|
|
dirs[path].append(item)
|
|
|
|
# Sort them into ones with the same folder name
|
|
var dir_names := {}
|
|
for dir in dirs:
|
|
var sliced: String = dir.get_slice("/", dir.get_slice_count("/")-1)
|
|
if not sliced in dir_names:
|
|
dir_names[sliced] = {"folders":[dir]}
|
|
else:
|
|
dir_names[sliced].folders.append(dir)
|
|
|
|
# Create a dictionary mapping a unique name to each directory
|
|
# If two have been found to have the same folder name, the parent directory is added
|
|
var unique_folder_names := {}
|
|
for dir_name in dir_names:
|
|
if dir_names[dir_name].folders.size() > 1:
|
|
for i in dir_names[dir_name].folders:
|
|
if "/" in i:
|
|
unique_folder_names[i.get_slice("/", i.get_slice_count("/")-2)+"/"+i.get_slice("/", i.get_slice_count("/")-1)] = i
|
|
else:
|
|
unique_folder_names[i] = i
|
|
else:
|
|
unique_folder_names[dir_name] = dir_names[dir_name].folders[0]
|
|
|
|
# Sort the folder names by their folder name (not by the full path)
|
|
var sorted_dir_keys := unique_folder_names.keys()
|
|
sorted_dir_keys.sort_custom(
|
|
func(x, y):
|
|
return x.get_slice("/", x.get_slice_count("/")-1) < y.get_slice("/", y.get_slice_count("/")-1)
|
|
)
|
|
var folder_colors: Dictionary = ProjectSettings.get_setting("file_customization/folder_colors", {})
|
|
|
|
for dir in sorted_dir_keys:
|
|
var display_name: String = dir
|
|
if not %TrimFolderPaths.button_pressed:
|
|
display_name = unique_folder_names[dir]
|
|
var dir_path: String = unique_folder_names[dir]
|
|
var dir_color_path := ""
|
|
var dir_color := Color.BLACK
|
|
if %FolderColors.button_pressed:
|
|
for path in folder_colors:
|
|
if String("res://"+dir_path+"/").begins_with(path) and len(path) > len(dir_color_path):
|
|
dir_color_path = path
|
|
dir_color = folder_colors[path]
|
|
|
|
var dir_item := add_folder_item(display_name, root, dir_color, dir_path)
|
|
|
|
for item in dirs[dir_path]:
|
|
add_item(item, dir_item, current_file)
|
|
|
|
|
|
if %CurrentResource.text != "No Resource":
|
|
%CurrentResource.add_theme_color_override(
|
|
"font_uneditable_color", get_theme_color("font_color", "Editor")
|
|
)
|
|
|
|
DialogicUtil.set_editor_setting("last_resources", resources_list)
|
|
|
|
|
|
func add_item(item:ResourceListItem, parent:TreeItem, current_file := "") -> TreeItem:
|
|
var tree_item := resource_tree.create_item(parent)
|
|
tree_item.set_text(0, item.text)
|
|
tree_item.set_icon(0, item.icon)
|
|
tree_item.set_metadata(0, item.metadata)
|
|
tree_item.set_tooltip_text(0, item.tooltip)
|
|
|
|
if item.metadata == current_file:
|
|
%CurrentResource.text = item.metadata.get_file()
|
|
resource_tree.set_selected(tree_item, 0)
|
|
|
|
var bg_color := parent.get_custom_bg_color(0)
|
|
if bg_color != get_theme_color("base_color", "Editor"):
|
|
bg_color.a = 0.1
|
|
tree_item.set_custom_bg_color(0, bg_color)
|
|
|
|
return tree_item
|
|
|
|
|
|
func add_folder_item(label: String, parent:TreeItem, color:= Color.BLACK, tooltip:="") -> TreeItem:
|
|
var folder_item := resource_tree.create_item(parent)
|
|
folder_item.set_text(0, label)
|
|
folder_item.set_icon(0, get_theme_icon("Folder", "EditorIcons"))
|
|
folder_item.set_tooltip_text(0, tooltip)
|
|
if color == Color.BLACK:
|
|
folder_item.set_custom_bg_color(0, get_theme_color("base_color", "Editor"))
|
|
else:
|
|
color.a = 0.2
|
|
folder_item.set_custom_bg_color(0, color)
|
|
|
|
if label in DialogicUtil.get_editor_setting("resource_list_collapsed_info", []):
|
|
folder_item.collapsed = true
|
|
|
|
return folder_item
|
|
|
|
|
|
func get_directory_items(directory:Dictionary, filter:String, icon:Texture2D, resources_list:Array) -> Array:
|
|
var items := []
|
|
for item_name in directory:
|
|
if (directory[item_name] in resources_list) and (filter.is_empty() or filter.to_lower() in item_name.to_lower()):
|
|
var item := ResourceListItem.new()
|
|
item.text = item_name
|
|
item.icon = icon
|
|
item.metadata = directory[item_name]
|
|
item.tooltip = directory[item_name]
|
|
items.append(item)
|
|
return items
|
|
|
|
|
|
class ResourceListItem:
|
|
extends Object
|
|
|
|
var text: String
|
|
var index: int = -1
|
|
var icon: Texture
|
|
var metadata: String
|
|
var tooltip: String
|
|
|
|
func _to_string() -> String:
|
|
return JSON.stringify(
|
|
{
|
|
"text": text,
|
|
"index": index,
|
|
"icon": icon.resource_path,
|
|
"metadata": metadata,
|
|
"tooltip": tooltip,
|
|
"parent_dir": get_parent_directory()
|
|
},
|
|
"\t",
|
|
false
|
|
)
|
|
|
|
func get_parent_directory() -> String:
|
|
return (metadata.get_base_dir() as String).split("/")[-1]
|
|
|
|
|
|
func _sort_by_item_text(a: ResourceListItem, b: ResourceListItem) -> bool:
|
|
return a.text < b.text
|
|
|
|
#endregion
|
|
|
|
|
|
#region INTERACTING WITH RESOURCES
|
|
|
|
|
|
func _on_resources_tree_item_activated() -> void:
|
|
if resource_tree.get_selected() == null:
|
|
return
|
|
var item := resource_tree.get_selected()
|
|
if item.get_metadata(0) == null:
|
|
return
|
|
edit_resource(item.get_metadata(0))
|
|
|
|
|
|
func _on_resources_tree_item_clicked(_pos: Vector2, mouse_button_index: int) -> void:
|
|
match mouse_button_index:
|
|
MOUSE_BUTTON_LEFT:
|
|
var selected_item := resource_tree.get_selected()
|
|
if selected_item == null:
|
|
return
|
|
if selected_item.get_metadata(0) == null:
|
|
return
|
|
var resource_item := load(selected_item.get_metadata(0))
|
|
call_deferred("edit_resource", resource_item)
|
|
|
|
MOUSE_BUTTON_MIDDLE:
|
|
remove_item_from_list(resource_tree.get_selected())
|
|
|
|
MOUSE_BUTTON_RIGHT:
|
|
if resource_tree.get_selected().get_metadata(0):
|
|
%RightClickMenu.popup_on_parent(Rect2(get_global_mouse_position(), Vector2()))
|
|
%RightClickMenu.set_meta("item_clicked", resource_tree.get_selected())
|
|
|
|
|
|
func _on_resources_tree_item_collapsed(item:TreeItem) -> void:
|
|
var collapsed_info := DialogicUtil.get_editor_setting("resource_list_collapsed_info", [])
|
|
if item.get_text(0) in collapsed_info:
|
|
if not item.collapsed:
|
|
collapsed_info.erase(item.get_text(0))
|
|
else:
|
|
if item.collapsed:
|
|
collapsed_info.append(item.get_text(0))
|
|
DialogicUtil.set_editor_setting("resource_list_collapsed_info", collapsed_info)
|
|
|
|
|
|
func edit_resource(resource_item: Variant) -> void:
|
|
if resource_item is Resource:
|
|
editors_manager.edit_resource(resource_item)
|
|
else:
|
|
editors_manager.edit_resource(load(resource_item))
|
|
|
|
|
|
func remove_item_from_list(item: TreeItem) -> void:
|
|
var new_list := []
|
|
for entry in DialogicUtil.get_editor_setting("last_resources", []):
|
|
if entry != item.get_metadata(0):
|
|
new_list.append(entry)
|
|
DialogicUtil.set_editor_setting("last_resources", new_list)
|
|
update_resource_list(new_list)
|
|
|
|
|
|
func _on_right_click_menu_id_pressed(id: int) -> void:
|
|
match id:
|
|
1: # REMOVE ITEM FROM LIST
|
|
remove_item_from_list(%RightClickMenu.get_meta("item_clicked"))
|
|
2: # OPEN IN FILESYSTEM
|
|
EditorInterface.get_file_system_dock().navigate_to_path(
|
|
%RightClickMenu.get_meta("item_clicked").get_metadata(0)
|
|
)
|
|
3: # OPEN IN EXTERNAL EDITOR
|
|
OS.shell_open(
|
|
ProjectSettings.globalize_path(
|
|
%RightClickMenu.get_meta("item_clicked").get_metadata(0)
|
|
)
|
|
)
|
|
4: # COPY IDENTIFIER
|
|
DisplayServer.clipboard_set(
|
|
DialogicResourceUtil.get_unique_identifier(
|
|
%RightClickMenu.get_meta("item_clicked").get_metadata(0)
|
|
)
|
|
)
|
|
#endregion
|
|
|
|
|
|
#region FILTERING
|
|
|
|
func _on_search_text_changed(_new_text: String) -> void:
|
|
update_resource_list()
|
|
for item in resource_tree.get_root().get_children():
|
|
if item.get_children().size() > 0:
|
|
resource_tree.set_selected(item.get_child(0), 0)
|
|
break
|
|
|
|
|
|
func _on_search_text_submitted(_new_text: String) -> void:
|
|
if resource_tree.get_selected() == null:
|
|
return
|
|
var item := resource_tree.get_selected()
|
|
if item.get_metadata(0) == null:
|
|
return
|
|
edit_resource(item.get_metadata(0))
|
|
%Search.clear()
|
|
|
|
#endregion
|
|
|
|
|
|
#region CONTENT LIST
|
|
|
|
func update_content_list(list: PackedStringArray) -> void:
|
|
var prev_selected := ""
|
|
if %ContentList.is_anything_selected():
|
|
prev_selected = %ContentList.get_item_text(%ContentList.get_selected_items()[0])
|
|
%ContentList.clear()
|
|
%ContentList.add_item("~ Top")
|
|
for i in list:
|
|
if i.is_empty():
|
|
continue
|
|
%ContentList.add_item(i)
|
|
if i == prev_selected:
|
|
%ContentList.select(%ContentList.item_count - 1)
|
|
if list.is_empty():
|
|
return
|
|
|
|
var current_resource: Resource = editors_manager.get_current_editor().current_resource
|
|
|
|
var timeline_directory := DialogicResourceUtil.get_timeline_directory()
|
|
var label_directory := DialogicResourceUtil.get_label_cache()
|
|
if current_resource != null:
|
|
for i in timeline_directory:
|
|
if timeline_directory[i] == current_resource.resource_path:
|
|
label_directory[i] = list
|
|
|
|
# also always store the current timelines labels for easy access
|
|
label_directory[""] = list
|
|
|
|
DialogicResourceUtil.set_label_cache(label_directory)
|
|
|
|
#endregion
|
|
|
|
|
|
#region RESOURCE LIST OPTIONS
|
|
|
|
func _on_options_pressed() -> void:
|
|
%OptionsPopup.popup_on_parent(Rect2(%Options.global_position+%Options.size*Vector2(0,1), Vector2()))
|
|
|
|
|
|
func _on_grouping_changed(idx: int) -> void:
|
|
var id: int = %GroupingOptions.get_item_id(idx)
|
|
if (GroupMode as Dictionary).values().has(id):
|
|
group_mode = (id as GroupMode)
|
|
DialogicUtil.set_editor_setting("sidebar_group_mode", id)
|
|
update_resource_list()
|
|
|
|
%FolderColors.disabled = group_mode != GroupMode.PATH
|
|
%TrimFolderPaths.disabled = group_mode != GroupMode.PATH
|
|
|
|
|
|
func _on_folder_colors_toggled(toggled_on: bool) -> void:
|
|
DialogicUtil.set_editor_setting("sidebar_use_folder_colors", toggled_on)
|
|
update_resource_list()
|
|
|
|
|
|
func _on_trim_folder_paths_toggled(toggled_on: bool) -> void:
|
|
DialogicUtil.set_editor_setting("sidebar_trim_folder_paths", toggled_on)
|
|
update_resource_list()
|
|
|
|
#endregion
|
|
|
|
|
|
func _on_main_v_split_dragged(offset: int) -> void:
|
|
DialogicUtil.set_editor_setting("sidebar_v_split", offset)
|