who the fuck knows
This commit is contained in:
parent
1c33ea2f59
commit
1da411cacd
31 changed files with 1372 additions and 78 deletions
207
addons/anim_player_refactor/lib/anim_player_refactor.gd
Normal file
207
addons/anim_player_refactor/lib/anim_player_refactor.gd
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
## Core utility class to handle all refactoring logic
|
||||
|
||||
const EditorUtil := preload("res://addons/anim_player_refactor/lib/editor_util.gd")
|
||||
|
||||
var _editor_plugin: EditorPlugin
|
||||
var _undo_redo: EditorUndoRedoManager
|
||||
|
||||
func _init(editor_plugin: EditorPlugin) -> void:
|
||||
_editor_plugin = editor_plugin
|
||||
_undo_redo = editor_plugin.get_undo_redo()
|
||||
|
||||
# Nodes
|
||||
func rename_node_path(anim_player: AnimationPlayer, old: NodePath, new: NodePath):
|
||||
if old == new:
|
||||
return
|
||||
|
||||
_undo_redo.create_action("Refactor node tracks", UndoRedo.MERGE_ALL, anim_player)
|
||||
|
||||
_foreach_animation(anim_player, func(animation: Animation):
|
||||
for i in animation.get_track_count():
|
||||
var path := animation.track_get_path(i)
|
||||
var node_path := path.get_concatenated_names()
|
||||
|
||||
if node_path == old.get_concatenated_names():
|
||||
var new_path := new.get_concatenated_names() + ":" + path.get_concatenated_subnames()
|
||||
animation.track_set_path(i, NodePath(new_path))
|
||||
|
||||
_undo_redo.add_do_property(animation, "tracks/%d/path" % i, new_path)
|
||||
_undo_redo.add_undo_property(animation, "tracks/%d/path" % i, path)
|
||||
)
|
||||
|
||||
_undo_redo.commit_action()
|
||||
|
||||
|
||||
func remove_node_path(anim_player: AnimationPlayer, node_path: NodePath):
|
||||
_undo_redo.create_action("Remove node tracks", UndoRedo.MERGE_ALL, anim_player)
|
||||
|
||||
_foreach_animation_restore(anim_player, _undo_redo, func(animation: Animation):
|
||||
var removed_tracks = 0
|
||||
|
||||
for i in range(animation.get_track_count() - 1, -1, -1):
|
||||
var path = animation.track_get_path(i)
|
||||
|
||||
if NodePath(path.get_concatenated_names()) == node_path:
|
||||
removed_tracks += 1
|
||||
_undo_redo.add_do_method(animation, &'remove_track', i)
|
||||
|
||||
return removed_tracks
|
||||
)
|
||||
|
||||
_undo_redo.commit_action()
|
||||
|
||||
|
||||
# Tracks
|
||||
func rename_track_path(anim_player: AnimationPlayer, old: NodePath, new: NodePath):
|
||||
if old == new:
|
||||
return
|
||||
|
||||
_undo_redo.create_action("Refactor track paths", UndoRedo.MERGE_ALL, anim_player)
|
||||
|
||||
_foreach_animation(anim_player, func(animation: Animation):
|
||||
for i in animation.get_track_count():
|
||||
var path = animation.track_get_path(i)
|
||||
|
||||
if path == old:
|
||||
animation.track_set_path(i, new)
|
||||
|
||||
_undo_redo.add_do_property(animation, "tracks/%d/path" % i, new)
|
||||
_undo_redo.add_undo_property(animation, "tracks/%d/path" % i, old)
|
||||
)
|
||||
|
||||
_undo_redo.commit_action()
|
||||
|
||||
|
||||
func remove_track_path(anim_player: AnimationPlayer, property_path: NodePath):
|
||||
_undo_redo.create_action("Remove tracks", UndoRedo.MERGE_ALL, anim_player)
|
||||
|
||||
_foreach_animation_restore(anim_player, _undo_redo, func(animation: Animation):
|
||||
var removed_tracks = 0
|
||||
|
||||
for i in range(animation.get_track_count() - 1, -1, -1):
|
||||
var path = animation.track_get_path(i)
|
||||
|
||||
if path == property_path:
|
||||
removed_tracks += 1
|
||||
_undo_redo.add_do_method(animation, &'remove_track', i)
|
||||
|
||||
return removed_tracks
|
||||
)
|
||||
|
||||
_undo_redo.commit_action()
|
||||
|
||||
# Method tracks
|
||||
func rename_method(anim_player, old: NodePath, new: NodePath):
|
||||
if old == new:
|
||||
return
|
||||
|
||||
var node_path := NodePath(old.get_concatenated_names())
|
||||
var old_method := old.get_concatenated_subnames()
|
||||
var new_method := new.get_concatenated_subnames()
|
||||
|
||||
_undo_redo.create_action("Rename method keys", UndoRedo.MERGE_ALL, anim_player)
|
||||
|
||||
_foreach_animation(anim_player, func(animation: Animation):
|
||||
for i in animation.get_track_count():
|
||||
if (animation.track_get_type(i) == Animation.TYPE_METHOD and animation.track_get_path(i) == node_path):
|
||||
for j in animation.track_get_key_count(i):
|
||||
var name := animation.method_track_get_name(i, j)
|
||||
if name == old_method:
|
||||
var old_method_params := {
|
||||
"method": old_method,
|
||||
"args": animation.method_track_get_params(i, j)
|
||||
}
|
||||
|
||||
var method_params := {
|
||||
"method": new_method,
|
||||
"args": animation.method_track_get_params(i, j)
|
||||
}
|
||||
|
||||
_undo_redo.add_do_method(animation, &'track_set_key_value', i, j, method_params)
|
||||
_undo_redo.add_undo_method(animation, &'track_set_key_value', i, j, old_method_params)
|
||||
)
|
||||
|
||||
_undo_redo.commit_action()
|
||||
|
||||
|
||||
func remove_method(anim_player: AnimationPlayer, method_path: NodePath):
|
||||
_undo_redo.create_action("Remove method keys", UndoRedo.MERGE_ALL, anim_player)
|
||||
|
||||
_foreach_animation_restore(anim_player, _undo_redo, func(animation: Animation):
|
||||
for i in animation.get_track_count():
|
||||
if (
|
||||
animation.track_get_type(i) == Animation.TYPE_METHOD
|
||||
and StringName(animation.track_get_path(i)) == method_path.get_concatenated_names()
|
||||
):
|
||||
for j in range(animation.track_get_key_count(i) - 1, -1, -1):
|
||||
var name := animation.method_track_get_name(i, j)
|
||||
if name == method_path.get_concatenated_subnames():
|
||||
_undo_redo.add_do_method(animation, &'track_remove_key', i, j)
|
||||
return 0
|
||||
)
|
||||
|
||||
_undo_redo.commit_action()
|
||||
|
||||
|
||||
# Root
|
||||
func change_root(anim_player: AnimationPlayer, new_path: NodePath):
|
||||
var current_root: Node = anim_player.get_node(anim_player.root_node)
|
||||
var new_root: Node = anim_player.get_node_or_null(new_path)
|
||||
|
||||
if new_root == null:
|
||||
return
|
||||
|
||||
_undo_redo.create_action("Change animation player root", UndoRedo.MERGE_ALL, anim_player)
|
||||
|
||||
_foreach_animation(anim_player, func(animation: Animation):
|
||||
for i in animation.get_track_count():
|
||||
var path := animation.track_get_path(i)
|
||||
var node := current_root.get_node_or_null(NodePath(path.get_concatenated_names()))
|
||||
|
||||
if node == null:
|
||||
push_warning("Invalid path: %s. Skipping root change." % path)
|
||||
continue
|
||||
|
||||
var updated_path = str(new_root.get_path_to(node)) + ":" + path.get_concatenated_subnames()
|
||||
|
||||
_undo_redo.add_do_property(animation, "tracks/%d/path" % i, updated_path)
|
||||
_undo_redo.add_undo_property(animation, "tracks/%d/path" % i, path)
|
||||
)
|
||||
|
||||
_undo_redo.add_do_property(anim_player, "root_node", new_path)
|
||||
_undo_redo.add_undo_property(anim_player, "root_node", anim_player.root_node)
|
||||
|
||||
_undo_redo.commit_action()
|
||||
|
||||
|
||||
|
||||
# Helper methods
|
||||
|
||||
## Iterates over all animations in the animation player
|
||||
static func _foreach_animation(anim_player: AnimationPlayer, callback: Callable):
|
||||
for lib_name in anim_player.get_animation_library_list():
|
||||
var lib := anim_player.get_animation_library(lib_name)
|
||||
for animation_name in lib.get_animation_list():
|
||||
var animation := lib.get_animation(animation_name)
|
||||
callback.call(animation)
|
||||
|
||||
|
||||
## Iterates over all animations in the animation player and adds a full revert to the undo stack
|
||||
## Useful for do actions that remove tracks
|
||||
static func _foreach_animation_restore(anim_player: AnimationPlayer, undo_redo: EditorUndoRedoManager, callback: Callable):
|
||||
for lib_name in anim_player.get_animation_library_list():
|
||||
var lib := anim_player.get_animation_library(lib_name)
|
||||
for animation_name in lib.get_animation_list():
|
||||
var animation := lib.get_animation(animation_name)
|
||||
|
||||
var old_anim := animation.duplicate(true)
|
||||
|
||||
var removed_tracked = callback.call(animation)
|
||||
|
||||
for i in range(animation.get_track_count() - 1 - removed_tracked, -1, -1):
|
||||
undo_redo.add_undo_method(animation, &'remove_track', i)
|
||||
|
||||
for i in range(old_anim.get_track_count()):
|
||||
undo_redo.add_undo_method(old_anim, &'copy_track', i, animation)
|
||||
|
||||
undo_redo.add_undo_reference(old_anim)
|
||||
64
addons/anim_player_refactor/lib/editor_util.gd
Normal file
64
addons/anim_player_refactor/lib/editor_util.gd
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
# Utility class for parsing and hacking the editor
|
||||
|
||||
## Find menu button to add option to
|
||||
static func find_animation_menu_button(node: Node) -> MenuButton:
|
||||
var animation_editor := find_editor_control_with_class(node, "AnimationPlayerEditor")
|
||||
if animation_editor:
|
||||
return find_editor_control_with_class(
|
||||
animation_editor,
|
||||
"MenuButton",
|
||||
func(node): return node.text == "Animation"
|
||||
)
|
||||
|
||||
return null
|
||||
|
||||
|
||||
## General utility to find a control in the editor using an iterative search
|
||||
static func find_editor_control_with_class(
|
||||
base: Control,
|
||||
p_class_name: StringName,
|
||||
condition := func(node: Node): return true
|
||||
) -> Node:
|
||||
if base.get_class() == p_class_name and condition.call(base):
|
||||
return base
|
||||
|
||||
for child in base.get_children():
|
||||
if not child is Control:
|
||||
continue
|
||||
|
||||
var found = find_editor_control_with_class(child, p_class_name)
|
||||
if found:
|
||||
return found
|
||||
|
||||
return null
|
||||
|
||||
|
||||
|
||||
# Finds the active animation player (either pinned or selected)
|
||||
static func find_active_anim_player(base_control: Control, scene_tree: Tree) -> AnimationPlayer:
|
||||
var find_anim_player_recursive: Callable
|
||||
|
||||
var pin_icon := scene_tree.get_theme_icon("Pin", "EditorIcons")
|
||||
|
||||
var stack: Array[TreeItem] = []
|
||||
stack.append(scene_tree.get_root())
|
||||
|
||||
while not stack.is_empty():
|
||||
var current := stack.pop_back() as TreeItem
|
||||
|
||||
# Check for pin icon
|
||||
for i in current.get_button_count(0):
|
||||
if current.get_button(0, i) == pin_icon:
|
||||
var node := base_control.get_node_or_null(current.get_metadata(0))
|
||||
if node is AnimationPlayer:
|
||||
return node
|
||||
|
||||
if current.is_selected(0):
|
||||
var node := base_control.get_node_or_null(current.get_metadata(0))
|
||||
if node is AnimationPlayer:
|
||||
return node
|
||||
|
||||
for i in range(current.get_child_count() - 1, -1, -1):
|
||||
stack.push_back(current.get_child(i))
|
||||
|
||||
return null
|
||||
7
addons/anim_player_refactor/plugin.cfg
Normal file
7
addons/anim_player_refactor/plugin.cfg
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
[plugin]
|
||||
|
||||
name="Animation Player Refactor"
|
||||
description="Refactoring tools for AnimationPlayer libraries. Adds \"Refactor\" menu option under AnimationPlayer > Animation."
|
||||
author="poohcom1"
|
||||
version="0.1.4"
|
||||
script="plugin.gd"
|
||||
144
addons/anim_player_refactor/plugin.gd
Normal file
144
addons/anim_player_refactor/plugin.gd
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
const RefactorDialogue := preload("scenes/refactor_dialogue/refactor_dialogue.gd")
|
||||
|
||||
const AnimPlayerInspectorButton := preload("scenes/inspector_button/inspector_button.gd")
|
||||
|
||||
const EditorUtil := preload("lib/editor_util.gd")
|
||||
|
||||
var activate_button: AnimPlayerInspectorButton
|
||||
var refactor_dialogue: RefactorDialogue
|
||||
|
||||
var anim_menu_button: MenuButton
|
||||
|
||||
var _last_anim_player: AnimationPlayer
|
||||
const SCENE_TREE_IDX := 0
|
||||
var _scene_tree: Tree
|
||||
|
||||
func _enter_tree() -> void:
|
||||
# Create dialogue
|
||||
refactor_dialogue = load("res://addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.tscn").instantiate()
|
||||
get_editor_interface().get_base_control().add_child(refactor_dialogue)
|
||||
refactor_dialogue.init(self)
|
||||
# Create menu button
|
||||
_add_refactor_option(func():
|
||||
refactor_dialogue.popup_centered()
|
||||
refactor_dialogue.reset_size()
|
||||
)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
if refactor_dialogue and refactor_dialogue.is_inside_tree():
|
||||
get_editor_interface().get_base_control().remove_child(refactor_dialogue)
|
||||
refactor_dialogue.queue_free()
|
||||
|
||||
_remove_refactor_option()
|
||||
|
||||
|
||||
func _handles(object: Object) -> bool:
|
||||
if object is AnimationPlayer:
|
||||
_last_anim_player = object
|
||||
return false
|
||||
|
||||
|
||||
# Editor methods
|
||||
func get_anim_player() -> AnimationPlayer:
|
||||
# Check for pinned animation
|
||||
if not _scene_tree:
|
||||
var _scene_tree_editor = EditorUtil.find_editor_control_with_class(
|
||||
get_editor_interface().get_base_control(),
|
||||
"SceneTreeEditor"
|
||||
)
|
||||
|
||||
if not _scene_tree_editor:
|
||||
push_error("[Animation Refactor] Could not find scene tree editor. Please report this.")
|
||||
return null
|
||||
|
||||
_scene_tree = _scene_tree_editor.get_child(SCENE_TREE_IDX)
|
||||
|
||||
if not _scene_tree:
|
||||
push_error("[Animation Refactor] Could not find scene tree editor. Please report this.")
|
||||
return null
|
||||
|
||||
var found_anim := EditorUtil.find_active_anim_player(
|
||||
get_editor_interface().get_base_control(),
|
||||
_scene_tree
|
||||
)
|
||||
|
||||
if found_anim:
|
||||
return found_anim
|
||||
|
||||
# Get latest edited
|
||||
return _last_anim_player
|
||||
|
||||
|
||||
# Plugin buttons
|
||||
|
||||
const TOOL_REFACTOR := 999
|
||||
const TOOL_ANIM_LIBRARY := 1
|
||||
|
||||
func _add_refactor_option(on_pressed: Callable):
|
||||
var base_control := get_editor_interface().get_base_control()
|
||||
if not anim_menu_button:
|
||||
anim_menu_button = EditorUtil.find_animation_menu_button(base_control)
|
||||
if not anim_menu_button:
|
||||
push_error("Could not find Animation menu button. Please report this issue.")
|
||||
return
|
||||
|
||||
# Remove item up to "Manage Animations..."
|
||||
var menu_popup := anim_menu_button.get_popup()
|
||||
var items := []
|
||||
var count := menu_popup.item_count - 1
|
||||
|
||||
while count >= 0 and menu_popup.get_item_id(count) != TOOL_ANIM_LIBRARY:
|
||||
if menu_popup.is_item_separator(count):
|
||||
items.append({})
|
||||
else:
|
||||
items.append({
|
||||
"shortcut": menu_popup.get_item_shortcut(count),
|
||||
"id": menu_popup.get_item_id(count),
|
||||
"icon": menu_popup.get_item_icon(count)
|
||||
})
|
||||
|
||||
menu_popup.remove_item(count)
|
||||
count -= 1
|
||||
|
||||
# Add refactor item
|
||||
menu_popup.add_icon_item(
|
||||
base_control.get_theme_icon(&"Reload", &"EditorIcons"),
|
||||
"Refactor",
|
||||
TOOL_REFACTOR,
|
||||
)
|
||||
|
||||
# Re-add items
|
||||
for i in range(items.size() - 1, -1, -1):
|
||||
var item: Dictionary = items[i]
|
||||
|
||||
if not item.is_empty():
|
||||
menu_popup.add_shortcut(item.shortcut, item.id)
|
||||
menu_popup.set_item_icon(menu_popup.get_item_index(item.id), item.icon)
|
||||
else:
|
||||
menu_popup.add_separator()
|
||||
|
||||
menu_popup.notification(NOTIFICATION_TRANSLATION_CHANGED)
|
||||
|
||||
menu_popup.id_pressed.connect(_on_menu_button_pressed)
|
||||
|
||||
|
||||
func _remove_refactor_option():
|
||||
if not anim_menu_button:
|
||||
return
|
||||
|
||||
var base_control := get_editor_interface().get_base_control()
|
||||
|
||||
var menu_popup := anim_menu_button.get_popup()
|
||||
menu_popup.remove_item(menu_popup.get_item_index(TOOL_REFACTOR))
|
||||
|
||||
menu_popup.id_pressed.disconnect(_on_menu_button_pressed)
|
||||
|
||||
|
||||
func _on_menu_button_pressed(id: int):
|
||||
if id == TOOL_REFACTOR:
|
||||
refactor_dialogue.popup_centered()
|
||||
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
extends CodeEdit
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
code_completion_enabled = true
|
||||
add_code_completion_option(CodeEdit.KIND_MEMBER, "Test", "test")
|
||||
add_code_completion_option(CodeEdit.KIND_MEMBER, "Boo", "boo")
|
||||
code_completion_prefixes = ["t", "b"]
|
||||
|
||||
code_completion_requested.connect(func():
|
||||
add_code_completion_option(CodeEdit.KIND_MEMBER, "Test", "test")
|
||||
add_code_completion_option(CodeEdit.KIND_MEMBER, "Boo", "boo")
|
||||
update_code_completion_options(true)
|
||||
)
|
||||
|
||||
text_changed.connect(func(): request_code_completion(true))
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
## Autocomplete Class
|
||||
extends OptionButton
|
||||
|
||||
## LineEdit.text_change_rejected
|
||||
signal text_change_rejected(rejected_substring: String)
|
||||
## LineEdit.text_changed
|
||||
signal text_changed(new_text: String)
|
||||
## LineEdit.text_submitted
|
||||
signal text_submitted(new_text: String)
|
||||
|
||||
## LineEdit component
|
||||
var edit: LineEdit = LineEdit.new()
|
||||
|
||||
@export var get_autocomplete_options: Callable = func(text: String): return []
|
||||
|
||||
func _ready() -> void:
|
||||
focus_mode = Control.FOCUS_NONE
|
||||
edit.custom_minimum_size = size
|
||||
get_popup().unfocusable = true
|
||||
|
||||
add_child(edit)
|
||||
edit.reset_size()
|
||||
|
||||
edit.text_change_rejected.connect(func(arg): text_change_rejected.emit(arg))
|
||||
edit.text_changed.connect(func(arg): text_changed.emit(arg))
|
||||
edit.text_submitted.connect(func(arg): text_submitted.emit(arg))
|
||||
|
||||
edit.text_changed.connect(_update_options)
|
||||
|
||||
edit.focus_entered.connect(_update_options)
|
||||
edit.focus_exited.connect(clear)
|
||||
|
||||
get_autocomplete_options = func(text: String):
|
||||
return [
|
||||
"test",
|
||||
"ashina",
|
||||
"hello"
|
||||
].filter(func(el: String): return el.contains(text))
|
||||
|
||||
func _update_options(text: String = edit.text):
|
||||
clear()
|
||||
var options = get_autocomplete_options.call(text)
|
||||
|
||||
for option in options:
|
||||
if typeof(option) == TYPE_STRING:
|
||||
add_item(option)
|
||||
|
||||
show_popup()
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
extends EditorInspectorPlugin
|
||||
signal button_clicked
|
||||
|
||||
var button: Button
|
||||
var button_text: String
|
||||
|
||||
func _init(text: String) -> void:
|
||||
button_text = text
|
||||
|
||||
func _can_handle(object: Object) -> bool:
|
||||
return object is AnimationPlayer
|
||||
|
||||
func _parse_end(object: Object) -> void:
|
||||
button = Button.new()
|
||||
button.text = button_text
|
||||
button.pressed.connect(func(): button_clicked.emit())
|
||||
|
||||
var margins := MarginContainer.new()
|
||||
margins.add_theme_constant_override("margin_top", 8)
|
||||
margins.add_theme_constant_override("margin_left", 16)
|
||||
margins.add_theme_constant_override("margin_bottom", 8)
|
||||
margins.add_theme_constant_override("margin_right", 16)
|
||||
margins.add_child(button)
|
||||
|
||||
var container = VBoxContainer.new()
|
||||
container.add_theme_constant_override("separation", 0)
|
||||
container.add_child(HSeparator.new())
|
||||
container.add_child(margins)
|
||||
|
||||
var container_margins := MarginContainer.new()
|
||||
container_margins.add_theme_constant_override("margin_top", 8)
|
||||
container_margins.add_theme_constant_override("margin_left", 4)
|
||||
container_margins.add_theme_constant_override("margin_bottom", 8)
|
||||
container_margins.add_theme_constant_override("margin_right", 4)
|
||||
container_margins.add_child(container)
|
||||
|
||||
add_custom_control(container_margins)
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
@tool
|
||||
extends Tree
|
||||
|
||||
signal rendered
|
||||
|
||||
const EditInfo := preload("edit_info.gd")
|
||||
|
||||
@export var edittable_items := false
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
reset_size()
|
||||
|
||||
|
||||
func render(editor_plugin: EditorPlugin, anim_player: AnimationPlayer) -> void:
|
||||
clear()
|
||||
|
||||
# Get paths
|
||||
var animations = anim_player.get_animation_list()
|
||||
var root_node := anim_player.get_node(anim_player.root_node)
|
||||
|
||||
var track_paths := {} # Dictionary[NodePath, Dictionary[NodePath, EditInfo]]
|
||||
|
||||
# Get EditInfo data
|
||||
for anim_name in animations:
|
||||
var animation := anim_player.get_animation(anim_name)
|
||||
|
||||
for i in animation.get_track_count():
|
||||
var path := animation.track_get_path(i)
|
||||
var type := animation.track_get_type(i)
|
||||
|
||||
var node_path := NodePath(path.get_concatenated_names())
|
||||
var property_path := path.get_concatenated_subnames()
|
||||
var node := root_node.get_node_or_null(node_path)
|
||||
|
||||
var edit_infos: Array[EditInfo] = []
|
||||
|
||||
if not node_path in track_paths:
|
||||
track_paths[node_path] = {}
|
||||
match type:
|
||||
Animation.TYPE_METHOD:
|
||||
for j in animation.track_get_key_count(i):
|
||||
var method_path = NodePath(
|
||||
(
|
||||
path.get_concatenated_names()
|
||||
+ ":"
|
||||
+ animation.method_track_get_name(i, j)
|
||||
)
|
||||
)
|
||||
|
||||
var edit_info = EditInfo.new(
|
||||
EditInfo.Type.METHOD_TRACK, method_path, node_path, node, [anim_name]
|
||||
)
|
||||
|
||||
edit_infos.append(edit_info)
|
||||
_:
|
||||
if not property_path.is_empty():
|
||||
var edit_info = EditInfo.new(
|
||||
EditInfo.Type.VALUE_TRACK, path, node_path, node, [anim_name]
|
||||
)
|
||||
|
||||
edit_infos.append(edit_info)
|
||||
|
||||
# Combine
|
||||
for info in edit_infos:
|
||||
if not StringName(info.path) in track_paths[node_path]:
|
||||
track_paths[node_path][StringName(info.path)] = info
|
||||
else:
|
||||
for name in info.animation_names:
|
||||
if name in track_paths[node_path][StringName(info.path)].animation_names: continue
|
||||
track_paths[node_path][StringName(info.path)].animation_names.append(name)
|
||||
|
||||
# Sort
|
||||
var paths := track_paths.keys()
|
||||
paths.sort()
|
||||
|
||||
var tree_root: TreeItem = create_item()
|
||||
hide_root = true
|
||||
|
||||
# Get icons
|
||||
var gui := editor_plugin.get_editor_interface().get_base_control()
|
||||
|
||||
# Render
|
||||
for path in paths:
|
||||
var node := root_node.get_node_or_null(path)
|
||||
var icon := gui.get_theme_icon(node.get_class() if node != null else "", "EditorIcons")
|
||||
|
||||
var path_item = create_item(tree_root)
|
||||
path_item.set_editable(0, edittable_items)
|
||||
if edittable_items:
|
||||
path_item.set_text(0, path)
|
||||
if path.get_concatenated_names() == ".." and node:
|
||||
path_item.set_suffix(0, "(" + node.name + ")")
|
||||
else:
|
||||
path_item.set_text(0, node.name if node else path)
|
||||
path_item.set_icon(0, icon)
|
||||
path_item.set_metadata(0, EditInfo.new(EditInfo.Type.NODE, path, path, node, []))
|
||||
path_item.add_button(0, gui.get_theme_icon("Edit", "EditorIcons"))
|
||||
path_item.add_button(0, gui.get_theme_icon("Remove", "EditorIcons"))
|
||||
|
||||
var property_paths: Array = track_paths[path].keys()
|
||||
property_paths.sort()
|
||||
|
||||
for property_path in property_paths:
|
||||
var info: EditInfo = track_paths[path][property_path]
|
||||
var edit_type = EditInfo.Type.VALUE_TRACK
|
||||
var icon_type = "KeyValue"
|
||||
var invalid = false
|
||||
var property := info.path.get_concatenated_subnames()
|
||||
if node == null:
|
||||
invalid = true
|
||||
icon_type = ""
|
||||
elif node.has_method(StringName(property)):
|
||||
icon_type = "KeyCall"
|
||||
elif str(info.path) in node or node.get_indexed(NodePath(property)) != null:
|
||||
pass
|
||||
else:
|
||||
invalid = true
|
||||
icon_type = ""
|
||||
|
||||
var property_item = create_item(path_item)
|
||||
property_item.set_editable(0, edittable_items)
|
||||
property_item.set_text(0, property)
|
||||
property_item.set_icon(0, gui.get_theme_icon(icon_type, "EditorIcons"))
|
||||
property_item.set_metadata(0, info)
|
||||
property_item.add_button(0, gui.get_theme_icon("Edit", "EditorIcons"))
|
||||
property_item.add_button(0, gui.get_theme_icon("Remove", "EditorIcons"))
|
||||
|
||||
if invalid:
|
||||
property_item.set_custom_color(0, Color.RED)
|
||||
property_item.set_tooltip_text(0, "Possibly invalid value: %s" % info.path)
|
||||
rendered.emit()
|
||||
|
||||
|
||||
func set_filter(filter: String):
|
||||
var item_stack := []
|
||||
var visited := []
|
||||
|
||||
item_stack.append(get_root())
|
||||
|
||||
# Post-order traversal
|
||||
while not item_stack.is_empty():
|
||||
var current: TreeItem = item_stack[item_stack.size() - 1]
|
||||
var children = current.get_children() if current else []
|
||||
|
||||
var children_all_visited := true
|
||||
var child_visible := false
|
||||
|
||||
for child in children:
|
||||
children_all_visited = children_all_visited and child in visited
|
||||
child_visible = child_visible or child.visible
|
||||
|
||||
if children_all_visited:
|
||||
item_stack.pop_back()
|
||||
if current:
|
||||
if current == get_root() or filter.is_empty() or child_visible:
|
||||
current.visible = true
|
||||
else:
|
||||
current.visible = current.get_text(0).to_lower().contains(filter.to_lower())
|
||||
visited.append(current)
|
||||
else:
|
||||
item_stack += children
|
||||
|
||||
|
||||
## Class to cache heirarchy of nodes
|
||||
## Unused
|
||||
class TreeNode:
|
||||
var node: Node
|
||||
var path: String
|
||||
var children: Dictionary
|
||||
var parent: TreeNode
|
||||
|
||||
func debug(level = 0):
|
||||
print(" - ".repeat(level) + node.name)
|
||||
for name in children:
|
||||
children[name].debug(level + 1)
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
## Data class for storing information on tracks
|
||||
extends Object
|
||||
|
||||
enum Type { VALUE_TRACK, METHOD_TRACK, NODE }
|
||||
|
||||
## Type of info being edited
|
||||
var type: Type
|
||||
|
||||
## Full path to property. Same as node_path if type is NODE
|
||||
var path: NodePath
|
||||
|
||||
## Full path to node
|
||||
var node_path: NodePath
|
||||
|
||||
## Cached node
|
||||
var node: Node
|
||||
|
||||
## Animations the track is used in
|
||||
var animation_names: Array[String] = []
|
||||
|
||||
func _init(type: Type, path: NodePath, node_path: NodePath, node: Node, animation_names: Array[String]) -> void:
|
||||
self.type = type
|
||||
self.path = path
|
||||
self.node = node
|
||||
self.node_path = node_path
|
||||
self.animation_names = animation_names
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
@tool
|
||||
extends Tree
|
||||
|
||||
var _editor_plugin: EditorPlugin
|
||||
var _gui: Control
|
||||
|
||||
func init(editor_plugin: EditorPlugin):
|
||||
_editor_plugin = editor_plugin
|
||||
_gui = editor_plugin.get_editor_interface().get_base_control()
|
||||
|
||||
func render(anim_player: AnimationPlayer):
|
||||
clear()
|
||||
|
||||
_create_items(null, anim_player, anim_player.owner)
|
||||
|
||||
|
||||
func _create_items(parent: TreeItem, anim_player: AnimationPlayer, node: Node):
|
||||
var icon := _gui.get_theme_icon(node.get_class(), "EditorIcons")
|
||||
|
||||
var item := create_item(parent)
|
||||
item.set_text(0, node.name)
|
||||
item.set_icon(0, icon)
|
||||
item.set_metadata(0, anim_player.get_path_to(node))
|
||||
|
||||
if anim_player.get_path_to(node) == anim_player.root_node:
|
||||
item.select(0)
|
||||
scroll_to_item(item)
|
||||
|
||||
for child in node.get_children():
|
||||
_create_items(item, anim_player, child)
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
@tool
|
||||
extends AcceptDialog
|
||||
|
||||
const CustomEditorPlugin := preload("res://addons/anim_player_refactor/plugin.gd")
|
||||
const EditorUtil := preload("res://addons/anim_player_refactor/lib/editor_util.gd")
|
||||
|
||||
const AnimPlayerRefactor = preload("res://addons/anim_player_refactor/lib/anim_player_refactor.gd")
|
||||
const AnimPlayerTree := preload("components/anim_player_tree.gd")
|
||||
const EditInfo := preload("components/edit_info.gd")
|
||||
|
||||
const NodeSelect := preload("components/node_select.gd")
|
||||
|
||||
var _editor_plugin: CustomEditorPlugin
|
||||
var _editor_interface: EditorInterface
|
||||
var _anim_player_refactor: AnimPlayerRefactor
|
||||
var _anim_player: AnimationPlayer
|
||||
|
||||
@onready var anim_player_tree: AnimPlayerTree = $%AnimPlayerTree
|
||||
|
||||
@onready var change_root: Button = $%ChangeRoot
|
||||
|
||||
@onready var edit_dialogue: ConfirmationDialog = $%EditDialogue
|
||||
@onready var edit_dialogue_input: LineEdit = $%EditInput
|
||||
@onready var edit_dialogue_button: Button = $%EditDialogueButton # Just for pretty icon display
|
||||
@onready var edit_full_path_toggle: CheckButton = $%EditFullPathToggle
|
||||
@onready var edit_anim_list: Label = $%EditAnimationList
|
||||
|
||||
@onready var node_select_dialogue: ConfirmationDialog = $%NodeSelectDialogue
|
||||
@onready var node_select: NodeSelect = $%NodeSelect
|
||||
|
||||
@onready var confirmation_dialogue: ConfirmationDialog = $%ConfirmationDialog
|
||||
|
||||
|
||||
var is_full_path: bool:
|
||||
set(val): edit_full_path_toggle.button_pressed = val
|
||||
get: return edit_full_path_toggle.button_pressed
|
||||
|
||||
|
||||
func init(editor_plugin: CustomEditorPlugin) -> void:
|
||||
_editor_plugin = editor_plugin
|
||||
_editor_interface = editor_plugin.get_editor_interface()
|
||||
_anim_player_refactor = AnimPlayerRefactor.new(_editor_plugin)
|
||||
node_select.init(_editor_plugin)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
wrap_controls = true
|
||||
about_to_popup.connect(render)
|
||||
|
||||
|
||||
func render():
|
||||
_anim_player = _editor_plugin.get_anim_player()
|
||||
|
||||
if not _anim_player or not _anim_player is AnimationPlayer:
|
||||
push_error("AnimationPlayer is null or invalid")
|
||||
return
|
||||
|
||||
# Render track tree
|
||||
anim_player_tree.render(_editor_plugin, _anim_player)
|
||||
|
||||
# Render root node button
|
||||
var root_node: Node = _anim_player.get_node(_anim_player.root_node)
|
||||
var node_path := str(_anim_player.owner.get_path_to(root_node))
|
||||
if node_path == ".":
|
||||
node_path = _anim_player.owner.name
|
||||
else:
|
||||
node_path = _anim_player.owner.name + "/" + node_path
|
||||
|
||||
change_root.text = "%s (Change)" % node_path
|
||||
change_root.icon = _editor_interface.get_base_control().get_theme_icon(
|
||||
root_node.get_class(), "EditorIcons"
|
||||
)
|
||||
|
||||
|
||||
# Rename
|
||||
enum Action { Rename, Delete }
|
||||
var _current_info: EditInfo
|
||||
|
||||
func _on_tree_activated():
|
||||
_current_info = anim_player_tree.get_selected().get_metadata(0)
|
||||
_on_action(Action.Rename)
|
||||
|
||||
|
||||
func _on_tree_button_clicked(item: TreeItem, column: int, id: int, mouse_button_index: int):
|
||||
_current_info = item.get_metadata(column)
|
||||
if id == 0:
|
||||
_on_action(Action.Rename)
|
||||
elif id == 1:
|
||||
_on_action(Action.Delete)
|
||||
|
||||
|
||||
func _on_action(action: Action):
|
||||
if action == Action.Rename:
|
||||
_render_edit_dialogue()
|
||||
edit_dialogue.popup_centered()
|
||||
edit_dialogue_input.grab_focus()
|
||||
edit_dialogue_input.caret_column = edit_dialogue_input.text.length()
|
||||
elif action == Action.Delete:
|
||||
var track_path = _current_info.path
|
||||
var anim_player = _editor_plugin.get_anim_player()
|
||||
var node = anim_player\
|
||||
.get_node(anim_player.root_node)\
|
||||
.get_node_or_null(_current_info.path)
|
||||
|
||||
if node:
|
||||
track_path = node.name
|
||||
var property := _current_info.path.get_concatenated_subnames()
|
||||
if not property.is_empty():
|
||||
track_path += ":" + property
|
||||
|
||||
var msg = 'Delete all tracks with the path "%s"?' % track_path
|
||||
|
||||
if _current_info.type == EditInfo.Type.NODE:
|
||||
msg = 'Delete tracks belonging to the node "%s"?' % track_path
|
||||
|
||||
_show_confirmation(msg, _remove)
|
||||
|
||||
|
||||
func _render_edit_dialogue():
|
||||
var info := _current_info
|
||||
|
||||
if info.type == EditInfo.Type.METHOD_TRACK:
|
||||
is_full_path = false
|
||||
edit_full_path_toggle.disabled = true
|
||||
edit_full_path_toggle.visible = false
|
||||
else:
|
||||
edit_full_path_toggle.disabled = false
|
||||
edit_full_path_toggle.visible = true
|
||||
|
||||
if is_full_path:
|
||||
edit_dialogue_input.text = info.path
|
||||
else:
|
||||
if info.type == EditInfo.Type.NODE:
|
||||
edit_dialogue.title = "Rename node"
|
||||
edit_dialogue_input.text = info.path.get_name(info.path.get_name_count() - 1)
|
||||
elif info.type == EditInfo.Type.VALUE_TRACK:
|
||||
edit_dialogue.title = "Rename Value"
|
||||
edit_dialogue_input.text = info.path.get_concatenated_subnames()
|
||||
elif info.type == EditInfo.Type.METHOD_TRACK:
|
||||
edit_dialogue.title = "Rename method"
|
||||
edit_dialogue_input.text = info.path.get_concatenated_subnames()
|
||||
edit_dialogue_button.text = info.node_path
|
||||
if str(info.node_path) in [".", ".."] and info.node:
|
||||
# Show name for relatives paths
|
||||
edit_dialogue_button.text = info.node.name
|
||||
if info.node:
|
||||
# Show icon
|
||||
edit_dialogue_button.icon = _editor_interface.get_base_control().get_theme_icon(
|
||||
info.node.get_class(), "EditorIcons"
|
||||
)
|
||||
edit_anim_list.text = ""
|
||||
for name in _current_info.animation_names:
|
||||
edit_anim_list.text += " • " + name + "\n"
|
||||
|
||||
|
||||
## Toggle full path and re-render edit dialogue
|
||||
func _on_full_path_toggled(pressed: bool):
|
||||
_render_edit_dialogue()
|
||||
|
||||
|
||||
## Callback on rename
|
||||
func _on_rename_confirmed(_arg0 = null):
|
||||
var new := edit_dialogue_input.text
|
||||
|
||||
edit_dialogue.hide()
|
||||
if not _anim_player or not _anim_player is AnimationPlayer:
|
||||
push_error("AnimationPlayer is null or invalid")
|
||||
return
|
||||
|
||||
if new.is_empty():
|
||||
return
|
||||
|
||||
var info: EditInfo = _current_info
|
||||
if info.type == EditInfo.Type.NODE:
|
||||
var old := info.path
|
||||
var new_path = new
|
||||
if not is_full_path:
|
||||
new_path = ""
|
||||
for i in range(info.path.get_name_count() - 1):
|
||||
new_path += info.path.get_name(i) + "/"
|
||||
new_path += new
|
||||
_anim_player_refactor.rename_node_path(_anim_player, old, NodePath(new))
|
||||
elif info.type == EditInfo.Type.VALUE_TRACK:
|
||||
var old_path := info.path
|
||||
var new_path := NodePath(new)
|
||||
if not is_full_path:
|
||||
new_path = info.node_path.get_concatenated_names() + ":" + new
|
||||
_anim_player_refactor.rename_track_path(_anim_player, old_path, new_path)
|
||||
elif info.type == EditInfo.Type.METHOD_TRACK:
|
||||
var old_method := info.path
|
||||
var new_method := NodePath(
|
||||
info.path.get_concatenated_names() + ":" + new
|
||||
)
|
||||
_anim_player_refactor.rename_method(_anim_player, old_method, new_method)
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
render()
|
||||
|
||||
|
||||
func _remove():
|
||||
var info: EditInfo = _current_info
|
||||
match info.type:
|
||||
EditInfo.Type.NODE:
|
||||
_anim_player_refactor.remove_node_path(_anim_player, info.node_path)
|
||||
EditInfo.Type.VALUE_TRACK:
|
||||
_anim_player_refactor.remove_track_path(_anim_player, info.path)
|
||||
EditInfo.Type.METHOD_TRACK:
|
||||
_anim_player_refactor.remove_method(_anim_player, info.path)
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
render()
|
||||
|
||||
|
||||
# Change root
|
||||
func _on_change_root_pressed():
|
||||
node_select_dialogue.popup_centered()
|
||||
node_select.render(_editor_plugin.get_anim_player())
|
||||
|
||||
|
||||
func _on_node_select_confirmed():
|
||||
var path: NodePath = node_select.get_selected().get_metadata(0)
|
||||
|
||||
_anim_player_refactor.change_root(_editor_plugin.get_anim_player(), path)
|
||||
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
render()
|
||||
|
||||
|
||||
# Confirmation
|
||||
func _show_confirmation(text: String, on_confirmed: Callable):
|
||||
for c in confirmation_dialogue.confirmed.get_connections():
|
||||
confirmation_dialogue.confirmed.disconnect(c.callable)
|
||||
|
||||
confirmation_dialogue.confirmed.connect(on_confirmed, CONNECT_ONE_SHOT)
|
||||
confirmation_dialogue.popup_centered()
|
||||
confirmation_dialogue.dialog_text = text
|
||||
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
[gd_scene load_steps=4 format=3 uid="uid://cyfxysds8uhnx"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/anim_player_refactor/scenes/refactor_dialogue/refactor_dialogue.gd" id="1_nkqdl"]
|
||||
[ext_resource type="Script" path="res://addons/anim_player_refactor/scenes/refactor_dialogue/components/anim_player_tree.gd" id="2_7pqfs"]
|
||||
[ext_resource type="Script" path="res://addons/anim_player_refactor/scenes/refactor_dialogue/components/node_select.gd" id="3_87x4i"]
|
||||
|
||||
[node name="RefactorDialogue" type="AcceptDialog"]
|
||||
title = "Refactor Animations"
|
||||
size = Vector2i(400, 599)
|
||||
ok_button_text = "Close"
|
||||
script = ExtResource("1_nkqdl")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = 392.0
|
||||
offset_bottom = 550.0
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="TreeContainer" type="VBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/TreeContainer"]
|
||||
layout_mode = 2
|
||||
text = "Properties:"
|
||||
|
||||
[node name="FilterInput" type="LineEdit" parent="VBoxContainer/TreeContainer"]
|
||||
layout_mode = 2
|
||||
placeholder_text = "Filter..."
|
||||
caret_blink = true
|
||||
caret_blink_interval = 0.5
|
||||
|
||||
[node name="AnimPlayerTree" type="Tree" parent="VBoxContainer/TreeContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(300, 400)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
hide_root = true
|
||||
scroll_horizontal_enabled = false
|
||||
script = ExtResource("2_7pqfs")
|
||||
|
||||
[node name="RootNodeContainer" type="VBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/RootNodeContainer"]
|
||||
layout_mode = 2
|
||||
text = "Root Node"
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/RootNodeContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ChangeRoot" type="Button" parent="VBoxContainer/RootNodeContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Change Root"
|
||||
|
||||
[node name="EditDialogue" type="ConfirmationDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
title = "Renaming"
|
||||
position = Vector2i(0, 36)
|
||||
size = Vector2i(230, 239)
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="EditDialogue"]
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = 222.0
|
||||
offset_bottom = 190.0
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="EditDialogue/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="EditDialogueButton" type="Button" parent="EditDialogue/VBoxContainer/HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
|
||||
[node name="EditInput" type="LineEdit" parent="EditDialogue/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="HBoxContainer2" type="HBoxContainer" parent="EditDialogue/VBoxContainer"]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="EditDialogue/VBoxContainer/HBoxContainer2"]
|
||||
layout_mode = 2
|
||||
text = "Used in:"
|
||||
|
||||
[node name="EditFullPathToggle" type="CheckButton" parent="EditDialogue/VBoxContainer/HBoxContainer2"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 10
|
||||
text = "Edit full path"
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="EditDialogue/VBoxContainer"]
|
||||
custom_minimum_size = Vector2(0, 100)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ColorRect" type="ColorRect" parent="EditDialogue/VBoxContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
color = Color(0, 0, 0, 0.211765)
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="EditDialogue/VBoxContainer/MarginContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="MarginContainer" type="MarginContainer" parent="EditDialogue/VBoxContainer/MarginContainer/ScrollContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_left = 2
|
||||
theme_override_constants/margin_top = 2
|
||||
theme_override_constants/margin_right = 2
|
||||
theme_override_constants/margin_bottom = 2
|
||||
|
||||
[node name="EditAnimationList" type="Label" parent="EditDialogue/VBoxContainer/MarginContainer/ScrollContainer/MarginContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Test
|
||||
Test 2"
|
||||
|
||||
[node name="NodeSelectDialogue" type="ConfirmationDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
title = "Select a node..."
|
||||
size = Vector2i(616, 557)
|
||||
ok_button_text = "Change"
|
||||
|
||||
[node name="NodeSelect" type="Tree" parent="NodeSelectDialogue"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(600, 500)
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = 608.0
|
||||
offset_bottom = 508.0
|
||||
scroll_horizontal_enabled = false
|
||||
script = ExtResource("3_87x4i")
|
||||
|
||||
[node name="ConfirmationDialog" type="ConfirmationDialog" parent="."]
|
||||
unique_name_in_owner = true
|
||||
size = Vector2i(300, 200)
|
||||
ok_button_text = "Delete"
|
||||
dialog_autowrap = true
|
||||
|
||||
[connection signal="about_to_popup" from="." to="VBoxContainer/TreeContainer/FilterInput" method="clear"]
|
||||
[connection signal="text_changed" from="VBoxContainer/TreeContainer/FilterInput" to="VBoxContainer/TreeContainer/AnimPlayerTree" method="set_filter"]
|
||||
[connection signal="button_clicked" from="VBoxContainer/TreeContainer/AnimPlayerTree" to="." method="_on_tree_button_clicked"]
|
||||
[connection signal="item_activated" from="VBoxContainer/TreeContainer/AnimPlayerTree" to="." method="_on_tree_activated"]
|
||||
[connection signal="rendered" from="VBoxContainer/TreeContainer/AnimPlayerTree" to="VBoxContainer/TreeContainer/FilterInput" method="clear"]
|
||||
[connection signal="pressed" from="VBoxContainer/RootNodeContainer/HBoxContainer/ChangeRoot" to="." method="_on_change_root_pressed"]
|
||||
[connection signal="confirmed" from="EditDialogue" to="." method="_on_rename_confirmed"]
|
||||
[connection signal="text_submitted" from="EditDialogue/VBoxContainer/EditInput" to="." method="_on_rename_confirmed"]
|
||||
[connection signal="toggled" from="EditDialogue/VBoxContainer/HBoxContainer2/EditFullPathToggle" to="." method="_on_full_path_toggled"]
|
||||
[connection signal="confirmed" from="NodeSelectDialogue" to="." method="_on_node_select_confirmed"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue