Add cap sprite to child scene and update project configuration

This commit is contained in:
2025-04-26 03:52:45 +02:00
parent d95176fba0
commit 0c1192536c
374 changed files with 11968 additions and 1276 deletions

View File

@@ -0,0 +1,138 @@
@tool
extends MarginContainer
const ActionSlot = preload("../action_slot/action_slot.gd")
const Utils = preload("../utils.gd")
const ArrayEdit = preload("../array_edit/array_edit.gd")
signal delete_requested()
signal duplicate_requested()
@export var input_mapping_editor_scene:PackedScene
@onready var _action_slot:ActionSlot = %ActionSlot
@onready var _input_mappings:ArrayEdit = %InputMappings
const ClassScanner = preload("../class_scanner.gd")
var _plugin:EditorPlugin
var _scanner:ClassScanner
var _undo_redo:EditorUndoRedoManager
var _mapping:GUIDEActionMapping
func _ready():
_action_slot.action_changed.connect(_on_action_changed)
_input_mappings.delete_requested.connect(_on_input_mapping_delete_requested)
_input_mappings.add_requested.connect(_on_input_mappings_add_requested)
_input_mappings.move_requested.connect(_on_input_mappings_move_requested)
_input_mappings.clear_requested.connect(_on_input_mappings_clear_requested)
_input_mappings.duplicate_requested.connect(_on_input_mappings_duplicate_requested)
_input_mappings.collapse_state_changed.connect(_on_input_mappings_collapse_state_changed)
func initialize(plugin:EditorPlugin, scanner:ClassScanner):
_plugin = plugin
_scanner = scanner
_undo_redo = _plugin.get_undo_redo()
func edit(mapping:GUIDEActionMapping):
assert(_mapping == null)
_mapping = mapping
_mapping.changed.connect(_update)
_update()
func _update():
_input_mappings.clear()
_action_slot.action = _mapping.action
for i in _mapping.input_mappings.size():
var input_mapping = _mapping.input_mappings[i]
var input_mapping_editor = input_mapping_editor_scene.instantiate()
_input_mappings.add_item(input_mapping_editor)
input_mapping_editor.initialize(_plugin, _scanner)
input_mapping_editor.edit(input_mapping)
_input_mappings.collapsed = _mapping.get_meta("_guide_input_mappings_collapsed", false)
func _on_action_changed():
_undo_redo.create_action("Change action")
_undo_redo.add_do_property(_mapping, "action", _action_slot.action)
_undo_redo.add_undo_property(_mapping, "action", _mapping.action)
_undo_redo.commit_action()
func _on_input_mappings_add_requested():
var values = _mapping.input_mappings.duplicate()
var new_mapping = GUIDEInputMapping.new()
values.append(new_mapping)
_undo_redo.create_action("Add input mapping")
_undo_redo.add_do_property(_mapping, "input_mappings", values)
_undo_redo.add_undo_property(_mapping, "input_mappings", _mapping.input_mappings)
_undo_redo.commit_action()
func _on_input_mapping_delete_requested(index:int):
var values = _mapping.input_mappings.duplicate()
values.remove_at(index)
_undo_redo.create_action("Delete input mapping")
_undo_redo.add_do_property(_mapping, "input_mappings", values)
_undo_redo.add_undo_property(_mapping, "input_mappings", _mapping.input_mappings)
_undo_redo.commit_action()
func _on_input_mappings_move_requested(from:int, to:int):
var values = _mapping.input_mappings.duplicate()
var mapping = values[from]
values.remove_at(from)
if from < to:
to -= 1
values.insert(to, mapping)
_undo_redo.create_action("Move input mapping")
_undo_redo.add_do_property(_mapping, "input_mappings", values)
_undo_redo.add_undo_property(_mapping, "input_mappings", _mapping.input_mappings)
_undo_redo.commit_action()
func _on_input_mappings_clear_requested():
var values:Array[GUIDEInputMapping] = []
_undo_redo.create_action("Clear input mappings")
_undo_redo.add_do_property(_mapping, "input_mappings", values)
_undo_redo.add_undo_property(_mapping, "input_mappings", _mapping.input_mappings)
_undo_redo.commit_action()
func _on_input_mappings_duplicate_requested(index:int):
var values = _mapping.input_mappings.duplicate()
var copy:GUIDEInputMapping = values[index].duplicate()
copy.input = Utils.duplicate_if_inline(copy.input)
for i in copy.modifiers.size():
copy.modifiers[i] = Utils.duplicate_if_inline(copy.modifiers[i])
for i in copy.triggers.size():
copy.triggers[i] = Utils.duplicate_if_inline(copy.triggers[i])
# insert copy after original
values.insert(index+1, copy)
_undo_redo.create_action("Duplicate input mapping")
_undo_redo.add_do_property(_mapping, "input_mappings", values)
_undo_redo.add_undo_property(_mapping, "input_mappings", _mapping.input_mappings)
_undo_redo.commit_action()
func _on_input_mappings_collapse_state_changed(new_state:bool):
_mapping.set_meta("_guide_input_mappings_collapsed", new_state)

View File

@@ -0,0 +1,43 @@
[gd_scene load_steps=5 format=3 uid="uid://361aipcef24h"]
[ext_resource type="Script" path="res://addons/guide/editor/action_mapping_editor/action_mapping_editor.gd" id="1_2k0pi"]
[ext_resource type="PackedScene" uid="uid://du4x7ng6ntuk4" path="res://addons/guide/editor/action_slot/action_slot.tscn" id="1_hguf2"]
[ext_resource type="PackedScene" uid="uid://c323mdijdhktg" path="res://addons/guide/editor/input_mapping_editor/input_mapping_editor.tscn" id="2_a8nbp"]
[ext_resource type="PackedScene" uid="uid://cly0ff32fvpb2" path="res://addons/guide/editor/array_edit/array_edit.tscn" id="4_ehr5j"]
[node name="ActionMappingEditor" type="MarginContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_vertical = 0
theme_override_constants/margin_bottom = 5
script = ExtResource("1_2k0pi")
input_mapping_editor_scene = ExtResource("2_a8nbp")
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_vertical = 0
[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
[node name="ActionSlot" parent="HBoxContainer/HBoxContainer" instance=ExtResource("1_hguf2")]
unique_name_in_owner = true
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
size_flags_stretch_ratio = 4.0
[node name="InputMappings" parent="HBoxContainer/VBoxContainer" instance=ExtResource("4_ehr5j")]
unique_name_in_owner = true
layout_mode = 2
title = "Input mappings"
add_tooltip = "Add input mapping"
clear_tooltip = "Clear input mappings"

View File

@@ -0,0 +1,57 @@
@tool
extends LineEdit
signal action_changed()
var index:int
var action:GUIDEAction:
set(value):
if is_instance_valid(action):
action.changed.disconnect(_refresh)
action = value
if is_instance_valid(action):
action.changed.connect(_refresh)
# action_changed can only be emitted by
# dragging an action into this, not when setting
# the property
_refresh()
func _refresh():
if not is_instance_valid(action):
text = "<none>"
tooltip_text = ""
else:
text = action._editor_name()
tooltip_text = action.resource_path
func _can_drop_data(at_position, data) -> bool:
if not data is Dictionary:
return false
if data.has("files"):
for file in data["files"]:
if ResourceLoader.load(file) is GUIDEAction:
return true
return false
func _drop_data(at_position, data) -> void:
for file in data["files"]:
var item = ResourceLoader.load(file)
if item is GUIDEAction:
action = item
action_changed.emit()
func _gui_input(event):
if event is InputEventMouseButton:
if event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
if is_instance_valid(action):
EditorInterface.edit_resource(action)

View File

@@ -0,0 +1,16 @@
[gd_scene load_steps=2 format=3 uid="uid://du4x7ng6ntuk4"]
[ext_resource type="Script" path="res://addons/guide/editor/action_slot/action_slot.gd" id="1_w5nxd"]
[node name="ActionSlot" type="LineEdit"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 0
text = "Name"
editable = false
selecting_enabled = false
script = ExtResource("1_w5nxd")

View File

@@ -0,0 +1,113 @@
@tool
extends Container
const Utils = preload("../utils.gd")
@export var item_scene:PackedScene
@export var title:String = "":
set(value):
title = value
_refresh()
@export var add_tooltip:String:
set(value):
add_tooltip = value
_refresh()
@export var clear_tooltip:String:
set(value):
clear_tooltip = value
_refresh()
@export var item_separation:int = 8:
set(value):
item_separation = value
_refresh()
@export var collapsed:bool = false:
set(value):
collapsed = value
_refresh()
signal add_requested()
signal delete_requested(index:int)
signal move_requested(from:int, to:int)
signal insert_requested(index:int)
signal duplicate_requested(index:int)
signal clear_requested()
signal collapse_state_changed(collapsed:bool)
@onready var _add_button:Button = %AddButton
@onready var _clear_button:Button = %ClearButton
@onready var _contents:Container = %Contents
@onready var _title_label:Label = %TitleLabel
@onready var _collapse_button:Button = %CollapseButton
@onready var _expand_button:Button = %ExpandButton
@onready var _count_label:Label = %CountLabel
func _ready():
_add_button.icon = get_theme_icon("Add", "EditorIcons")
_add_button.pressed.connect(func(): add_requested.emit())
_clear_button.icon = get_theme_icon("Clear", "EditorIcons")
_clear_button.pressed.connect(func(): clear_requested.emit())
_collapse_button.icon = get_theme_icon("Collapse", "EditorIcons")
_collapse_button.pressed.connect(_on_collapse_pressed)
_expand_button.icon = get_theme_icon("Forward", "EditorIcons")
_expand_button.pressed.connect(_on_expand_pressed)
_refresh()
func _refresh():
if is_instance_valid(_add_button):
_add_button.tooltip_text = add_tooltip
if is_instance_valid(_clear_button):
_clear_button.tooltip_text = clear_tooltip
_clear_button.visible = _contents.get_child_count() > 0
if is_instance_valid(_contents):
_contents.add_theme_constant_override("separation", item_separation)
_contents.visible = not collapsed
if is_instance_valid(_collapse_button):
_collapse_button.visible = not collapsed
if is_instance_valid(_expand_button):
_expand_button.visible = collapsed
if is_instance_valid(_title_label):
_title_label.text = title
if is_instance_valid(_count_label):
_count_label.text = "(%s)" % [_contents.get_child_count()]
func clear():
Utils.clear(_contents)
_refresh()
func add_item(new_item:Control):
var item_wrapper = item_scene.instantiate()
_contents.add_child(item_wrapper)
item_wrapper.initialize(new_item)
item_wrapper.move_requested.connect(func(from:int, to:int): move_requested.emit(from, to))
item_wrapper.delete_requested.connect(func(idx:int): delete_requested.emit(idx) )
item_wrapper.duplicate_requested.connect(func(idx:int): duplicate_requested.emit(idx) )
_refresh()
func _on_collapse_pressed():
collapsed = true
collapse_state_changed.emit(true)
func _on_expand_pressed():
collapsed = false
collapse_state_changed.emit(false)

View File

@@ -0,0 +1,88 @@
[gd_scene load_steps=5 format=3 uid="uid://cly0ff32fvpb2"]
[ext_resource type="Script" path="res://addons/guide/editor/array_edit/array_edit.gd" id="1_y3qyt"]
[ext_resource type="PackedScene" uid="uid://cjabwsa4gmlpp" path="res://addons/guide/editor/array_edit/array_edit_item.tscn" id="2_n3ncl"]
[sub_resource type="Image" id="Image_efj5n"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_uapko"]
image = SubResource("Image_efj5n")
[node name="Array" type="MarginContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_y3qyt")
item_scene = ExtResource("2_n3ncl")
item_separation = 10
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="Panel" type="Panel" parent="VBoxContainer/MarginContainer"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/MarginContainer"]
layout_mode = 2
[node name="CollapseButton" type="Button" parent="VBoxContainer/MarginContainer/HBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(32, 0)
layout_mode = 2
size_flags_horizontal = 0
tooltip_text = "Collapse"
icon = SubResource("ImageTexture_uapko")
[node name="ExpandButton" type="Button" parent="VBoxContainer/MarginContainer/HBoxContainer"]
unique_name_in_owner = true
visible = false
custom_minimum_size = Vector2(48, 0)
layout_mode = 2
size_flags_horizontal = 0
tooltip_text = "Expand"
icon = SubResource("ImageTexture_uapko")
[node name="AddButton" type="Button" parent="VBoxContainer/MarginContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 0
icon = SubResource("ImageTexture_uapko")
[node name="ClearButton" type="Button" parent="VBoxContainer/MarginContainer/HBoxContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
size_flags_horizontal = 0
icon = SubResource("ImageTexture_uapko")
[node name="MarginContainer" type="MarginContainer" parent="VBoxContainer/MarginContainer/HBoxContainer"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/MarginContainer/HBoxContainer/MarginContainer"]
layout_mode = 2
[node name="TitleLabel" type="Label" parent="VBoxContainer/MarginContainer/HBoxContainer/MarginContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="CountLabel" type="Label" parent="VBoxContainer/MarginContainer/HBoxContainer/MarginContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "(0)"
[node name="Contents" type="VBoxContainer" parent="VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
theme_override_constants/separation = 10

View File

@@ -0,0 +1,84 @@
@tool
extends Container
const Utils = preload("../utils.gd")
const Dragger = preload("dragger.gd")
signal move_requested(from:int, to:int)
signal delete_requested(index:int)
signal duplicate_requested(index:int)
@onready var _dragger:Dragger = %Dragger
@onready var _content:Container = %Content
@onready var _before_indicator:ColorRect = %BeforeIndicator
@onready var _after_indicator:ColorRect = %AfterIndicator
@onready var _popup_menu:PopupMenu = %PopupMenu
const ID_DELETE = 2
const ID_DUPLICATE = 3
func _ready():
_dragger.icon = get_theme_icon("GuiSpinboxUpdown", "EditorIcons")
_before_indicator.color = get_theme_color("box_selection_stroke_color", "Editor")
_after_indicator.color = get_theme_color("box_selection_stroke_color", "Editor")
_before_indicator.visible = false
_after_indicator.visible = false
_dragger._parent_array = get_parent()
_dragger._index = get_index()
_dragger.pressed.connect(_show_popup_menu)
_popup_menu.clear()
_popup_menu.add_icon_item(get_theme_icon("Duplicate", "EditorIcons"), "Duplicate", ID_DUPLICATE)
_popup_menu.add_icon_item(get_theme_icon("Remove", "EditorIcons"), "Delete", ID_DELETE)
_popup_menu.id_pressed.connect(_on_popup_menu_id_pressed)
func initialize(content:Control):
Utils.clear(_content)
_content.add_child(content)
func _can_drop_data(at_position:Vector2, data) -> bool:
if data is Dictionary and data.has("parent_array") and data.parent_array == get_parent() and data.index != get_index():
var height = size.y
var is_before = not _is_last_child() or (at_position.y < height/2.0)
if is_before and data.index == get_index() - 1:
# don't allow the previous child to be inserted at its
# own position
return false
_before_indicator.visible = is_before
_after_indicator.visible = not is_before
return true
return false
func _drop_data(at_position, data):
var height = size.y
var is_before = not _is_last_child() or (at_position.y < height/2.0)
var from = data.index
var to = get_index() if is_before else get_index() + 1
move_requested.emit(data.index, to)
_before_indicator.visible = false
_after_indicator.visible = false
func _is_last_child() -> bool:
return get_index() == get_parent().get_child_count() - 1
func _on_mouse_exited():
_before_indicator.visible = false
_after_indicator.visible = false
func _show_popup_menu():
_popup_menu.popup(Rect2(get_global_mouse_position(), Vector2.ZERO))
func _on_popup_menu_id_pressed(id:int):
match id:
ID_DELETE:
delete_requested.emit(get_index())
ID_DUPLICATE:
duplicate_requested.emit(get_index())

View File

@@ -0,0 +1,83 @@
[gd_scene load_steps=5 format=3 uid="uid://cjabwsa4gmlpp"]
[ext_resource type="Script" path="res://addons/guide/editor/array_edit/array_edit_item.gd" id="1_ujx05"]
[ext_resource type="Script" path="res://addons/guide/editor/array_edit/dragger.gd" id="2_53e2r"]
[sub_resource type="Image" id="Image_efj5n"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_uapko"]
image = SubResource("Image_efj5n")
[node name="ArrayEditItem" type="MarginContainer"]
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 8.0
grow_horizontal = 2
script = ExtResource("1_ujx05")
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
theme_override_constants/margin_top = 2
theme_override_constants/margin_bottom = 2
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
layout_mode = 2
[node name="Dragger" type="Button" parent="MarginContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 0
tooltip_text = "Drag to reorder, click for options."
focus_mode = 0
mouse_filter = 1
icon = SubResource("ImageTexture_uapko")
script = ExtResource("2_53e2r")
[node name="Content" type="MarginContainer" parent="MarginContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 2
mouse_filter = 2
[node name="BeforeIndicator" type="ColorRect" parent="VBoxContainer"]
unique_name_in_owner = true
visible = false
custom_minimum_size = Vector2(0, 2)
layout_mode = 2
mouse_filter = 2
color = Color(0, 0, 0, 1)
[node name="Control" type="Control" parent="VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
mouse_filter = 2
[node name="AfterIndicator" type="ColorRect" parent="VBoxContainer"]
unique_name_in_owner = true
visible = false
custom_minimum_size = Vector2(0, 2)
layout_mode = 2
mouse_filter = 2
color = Color(0, 0, 0, 1)
[node name="PopupMenu" type="PopupMenu" parent="."]
unique_name_in_owner = true
item_count = 2
item_0/text = "Duplicate"
item_0/icon = SubResource("ImageTexture_uapko")
item_0/id = 3
item_1/text = "Delete"
item_1/icon = SubResource("ImageTexture_uapko")
item_1/id = 2
[connection signal="mouse_exited" from="." to="." method="_on_mouse_exited"]

View File

@@ -0,0 +1,8 @@
@tool
extends Button
var _parent_array:Variant
var _index:int
func _get_drag_data(at_position):
return { "parent_array" : _parent_array, "index" : _index }

View File

@@ -0,0 +1,148 @@
@tool
extends Window
const ClassScanner = preload("../class_scanner.gd")
const Utils = preload("../utils.gd")
signal input_selected(input:GUIDEInput)
@onready var _input_display = %InputDisplay
@onready var _available_types:Container = %AvailableTypes
@onready var _none_available:Control = %NoneAvailable
@onready var _some_available:Control = %SomeAvailable
@onready var _select_bool_button:Button = %SelectBoolButton
@onready var _select_1d_button:Button = %Select1DButton
@onready var _select_2d_button:Button = %Select2DButton
@onready var _select_3d_button:Button = %Select3DButton
@onready var _instructions_label:Label = %InstructionsLabel
@onready var _accept_detection_button:Button = %AcceptDetectionButton
@onready var _input_detector:GUIDEInputDetector = %InputDetector
@onready var _detect_bool_button:Button = %DetectBoolButton
@onready var _detect_1d_button:Button = %Detect1DButton
@onready var _detect_2d_button:Button = %Detect2DButton
@onready var _detect_3d_button:Button = %Detect3DButton
var _scanner:ClassScanner
var _last_detected_input:GUIDEInput
func initialize(scanner:ClassScanner):
_scanner = scanner
_setup_dialog()
func _setup_dialog():
# we need to bind this here. if we bind it in the editor, the editor
# will crash when opening the scene because it will delete the node it
# just tries to edit.
focus_exited.connect(_on_close_requested)
_show_inputs_of_value_type(GUIDEAction.GUIDEActionValueType.BOOL)
_instructions_label.text = tr("Press one of the buttons above to detect an input.")
_accept_detection_button.visible = false
func _on_close_requested():
hide()
queue_free()
func _show_inputs_of_value_type(type:GUIDEAction.GUIDEActionValueType) -> void:
var items:Array[GUIDEInput] = []
_select_bool_button.set_pressed_no_signal(type == GUIDEAction.GUIDEActionValueType.BOOL)
_select_1d_button.set_pressed_no_signal(type == GUIDEAction.GUIDEActionValueType.AXIS_1D)
_select_2d_button.set_pressed_no_signal(type == GUIDEAction.GUIDEActionValueType.AXIS_2D)
_select_3d_button.set_pressed_no_signal(type == GUIDEAction.GUIDEActionValueType.AXIS_3D)
var all_inputs = _scanner.find_inheritors("GUIDEInput")
for script in all_inputs.values():
var dummy:GUIDEInput = script.new()
if dummy._native_value_type() == type:
items.append(dummy)
_some_available.visible = not items.is_empty()
_none_available.visible = items.is_empty()
if items.is_empty():
return
items.sort_custom(func(a,b): return a._editor_name().nocasecmp_to(b._editor_name()) < 0)
Utils.clear(_available_types)
for item in items:
var button = Button.new()
button.text = item._editor_name()
button.tooltip_text = item._editor_description()
button.pressed.connect(_deliver.bind(item))
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_available_types.add_child(button)
func _deliver(input:GUIDEInput):
input_selected.emit(input)
hide()
queue_free()
func _on_select_bool_button_pressed():
_show_inputs_of_value_type(GUIDEAction.GUIDEActionValueType.BOOL)
func _on_select_1d_button_pressed():
_show_inputs_of_value_type(GUIDEAction.GUIDEActionValueType.AXIS_1D)
func _on_select_2d_button_pressed():
_show_inputs_of_value_type(GUIDEAction.GUIDEActionValueType.AXIS_2D)
func _on_select_3d_button_pressed():
_show_inputs_of_value_type(GUIDEAction.GUIDEActionValueType.AXIS_3D)
func _on_input_detector_detection_started():
_instructions_label.text = tr("Actuate the input now...")
func _on_input_detector_input_detected(input:GUIDEInput):
_instructions_label.visible = false
_input_display.visible = true
_input_display.input = input
_accept_detection_button.visible = true
_last_detected_input = input
func _begin_detect_input(type:GUIDEAction.GUIDEActionValueType):
_last_detected_input = null
_instructions_label.visible = true
_instructions_label.text = tr("Get ready...")
_accept_detection_button.visible = false
_input_display.visible = false
_input_detector.detect(type)
func _on_detect_bool_button_pressed():
_detect_bool_button.release_focus()
_begin_detect_input(GUIDEAction.GUIDEActionValueType.BOOL)
func _on_detect_1d_button_pressed():
_detect_1d_button.release_focus()
_begin_detect_input(GUIDEAction.GUIDEActionValueType.AXIS_1D)
func _on_detect_2d_button_pressed():
_detect_2d_button.release_focus()
_begin_detect_input(GUIDEAction.GUIDEActionValueType.AXIS_2D)
func _on_detect_3d_button_pressed():
_detect_3d_button.release_focus()
_begin_detect_input(GUIDEAction.GUIDEActionValueType.AXIS_3D)
func _on_accept_detection_button_pressed():
input_selected.emit(_last_detected_input)
hide()
queue_free

View File

@@ -0,0 +1,216 @@
[gd_scene load_steps=5 format=3 uid="uid://dic27bm4pfw3q"]
[ext_resource type="Script" path="res://addons/guide/editor/binding_dialog/binding_dialog.gd" id="1_tknjd"]
[ext_resource type="PackedScene" uid="uid://dsv7s6tfmnsrs" path="res://addons/guide/editor/input_display/input_display.tscn" id="2_83ieu"]
[ext_resource type="Script" path="res://addons/guide/remapping/guide_input_detector.gd" id="3_c6q6r"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_3e874"]
content_margin_left = 4.0
content_margin_top = 4.0
content_margin_right = 4.0
content_margin_bottom = 4.0
bg_color = Color(1, 0.365, 0.365, 1)
draw_center = false
border_width_left = 2
border_width_top = 2
border_width_right = 2
border_width_bottom = 2
corner_detail = 1
[node name="BindingDialog" type="Window"]
title = "Input Configuration"
initial_position = 4
size = Vector2i(1200, 600)
popup_window = true
min_size = Vector2i(1200, 600)
script = ExtResource("1_tknjd")
[node name="MarginContainer" type="MarginContainer" parent="."]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_bottom = 5
[node name="BGPanel" type="Panel" parent="MarginContainer"]
unique_name_in_owner = true
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_3e874")
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer"]
layout_mode = 2
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer"]
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="LeftPanel" type="Panel" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer"]
layout_mode = 2
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 10
[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
text = "Detect Input"
horizontal_alignment = 1
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="DetectBoolButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "Boolean"
[node name="Detect1DButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "1D"
[node name="Detect2DButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
text = "2D"
[node name="Detect3DButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(80, 0)
layout_mode = 2
size_flags_horizontal = 3
text = "3D"
[node name="InstructionsLabel" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 6
text = "3..2..1.."
horizontal_alignment = 1
vertical_alignment = 1
autowrap_mode = 2
[node name="InputDisplay" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer" instance=ExtResource("2_83ieu")]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 6
[node name="AcceptDetectionButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 4
text = "Accept"
[node name="MarginContainer2" type="MarginContainer" parent="MarginContainer/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="RightPanel" type="Panel" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2"]
unique_name_in_owner = true
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2"]
layout_mode = 2
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/separation = 10
[node name="Label" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer"]
layout_mode = 2
text = "Select Input"
horizontal_alignment = 1
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="SelectBoolButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(80, 0)
layout_mode = 2
size_flags_horizontal = 3
toggle_mode = true
text = "Boolean"
[node name="Select1DButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(80, 0)
layout_mode = 2
size_flags_horizontal = 3
toggle_mode = true
text = "1D"
[node name="Select2DButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(80, 0)
layout_mode = 2
size_flags_horizontal = 3
toggle_mode = true
text = "2D"
[node name="Select3DButton" type="Button" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(80, 0)
layout_mode = 2
size_flags_horizontal = 3
toggle_mode = true
text = "3D"
[node name="NoneAvailable" type="Label" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 6
size_flags_vertical = 6
text = "No matching inputs available."
[node name="SomeAvailable" type="ScrollContainer" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
[node name="AvailableTypes" type="VBoxContainer" parent="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/SomeAvailable"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="InputDetector" type="Node" parent="."]
unique_name_in_owner = true
script = ExtResource("3_c6q6r")
[connection signal="close_requested" from="." to="." method="_on_close_requested"]
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer/DetectBoolButton" to="." method="_on_detect_bool_button_pressed"]
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer/Detect1DButton" to="." method="_on_detect_1d_button_pressed"]
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer/Detect2DButton" to="." method="_on_detect_2d_button_pressed"]
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/HBoxContainer/Detect3DButton" to="." method="_on_detect_3d_button_pressed"]
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/MarginContainer/MarginContainer/VBoxContainer/AcceptDetectionButton" to="." method="_on_accept_detection_button_pressed"]
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer/SelectBoolButton" to="." method="_on_select_bool_button_pressed"]
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer/Select1DButton" to="." method="_on_select_1d_button_pressed"]
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer/Select2DButton" to="." method="_on_select_2d_button_pressed"]
[connection signal="pressed" from="MarginContainer/MarginContainer/HBoxContainer/MarginContainer2/MarginContainer/VBoxContainer/HBoxContainer/Select3DButton" to="." method="_on_select_3d_button_pressed"]
[connection signal="detection_started" from="InputDetector" to="." method="_on_input_detector_detection_started"]
[connection signal="input_detected" from="InputDetector" to="." method="_on_input_detector_input_detected"]

View File

@@ -0,0 +1,91 @@
## Scanner to find inheriting classes. Used to detect inheritors of
## modifiers and triggers. Ideally this would be built into the editor
## but sometimes one has to hack their way around the limitations.
## This only scans to the extent needed to drive the UI, it's not a general
## purpose implementation.
@tool
const GUIDESet = preload("../guide_set.gd")
var _dirty:bool = true
# looks like we only get very limited access to the script's inheritance tree,
# so we need to do a little caching ourselves
var _script_lut:Dictionary = {}
func _init():
EditorInterface.get_resource_filesystem().script_classes_updated.connect(_mark_dirty)
func _mark_dirty():
_dirty = true
## Returns all classes that directly or indirectly inherit from the
## given class. Only works for scripts in the project, e.g. doesn't
## scan the whole class_db. Key is class name, value is the Script instance
func find_inheritors(clazz_name:StringName) -> Dictionary:
var result:Dictionary = {}
var root := EditorInterface.get_resource_filesystem().get_filesystem()
# rebuild the LUT when needed
if _dirty:
_script_lut.clear()
_scan(root)
_dirty = false
var open_set:GUIDESet = GUIDESet.new()
# a closed set just to avoid infinite loops, we'll never
# look at the same class more than once.
var closed_set:GUIDESet = GUIDESet.new()
open_set.add(clazz_name)
while not open_set.is_empty():
var next = open_set.pull()
closed_set.add(next)
if not _script_lut.has(next):
# we don't know this script, ignore, move on
continue
# now find all scripts that extend the one we
# are looking at
for item:ScriptInfo in _script_lut.values():
if item.extendz == next:
# put them into the result
result[item.clazz_name] = item.clazz_script
# and put their class in the open set
# unless we already looked at it.
if not closed_set.has(item.clazz_name):
open_set.add(item.clazz_name)
return result
func _scan(folder:EditorFileSystemDirectory):
for i in folder.get_file_count():
var script_clazz = folder.get_file_script_class_name(i)
if script_clazz != "":
var info := _script_lut.get(script_clazz)
if info == null:
info = ScriptInfo.new()
info.clazz_name = script_clazz
info.clazz_script = ResourceLoader.load(folder.get_file_path(i))
_script_lut[script_clazz] = info
var script_extendz = folder.get_file_script_class_extends(i)
info.extendz = script_extendz
for i in folder.get_subdir_count():
_scan(folder.get_subdir(i))
class ScriptInfo:
var clazz_name:StringName
var extendz:StringName
var clazz_script:Script
func _to_string() -> String:
return clazz_name + ":" + extendz

View File

@@ -0,0 +1,39 @@
@tool
extends RichTextLabel
signal clicked()
var _formatter:GUIDEInputFormatter = GUIDEInputFormatter.new(64)
var input:GUIDEInput:
set(value):
if value == input:
return
if is_instance_valid(input):
input.changed.disconnect(_refresh)
input = value
if is_instance_valid(input):
input.changed.connect(_refresh)
_refresh()
func _refresh():
if not is_instance_valid(input):
parse_bbcode("[center][i]<not bound>[/i][/center]")
tooltip_text = ""
return
var text := await _formatter.input_as_richtext_async(input, false)
parse_bbcode("[center]" + text + "[/center]")
tooltip_text = _formatter.input_as_text(input)
func _gui_input(event):
if event is InputEventMouseButton:
if event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
clicked.emit()

View File

@@ -0,0 +1,18 @@
[gd_scene load_steps=3 format=3 uid="uid://dsv7s6tfmnsrs"]
[ext_resource type="Script" path="res://addons/guide/editor/input_display/input_display.gd" id="1_ne6sd"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_0bp65"]
[node name="InputDisplay" type="RichTextLabel"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_styles/normal = SubResource("StyleBoxEmpty_0bp65")
bbcode_enabled = true
fit_content = true
script = ExtResource("1_ne6sd")

View File

@@ -0,0 +1,299 @@
@tool
extends MarginContainer
const ArrayEdit = preload("../array_edit/array_edit.gd")
const ClassScanner = preload("../class_scanner.gd")
const Utils = preload("../utils.gd")
@export var modifier_slot_scene:PackedScene
@export var trigger_slot_scene:PackedScene
@export var binding_dialog_scene:PackedScene
@onready var _edit_input_mapping_button:Button = %EditInputMappingButton
@onready var _input_display = %InputDisplay
@onready var _edit_input_button:Button = %EditInputButton
@onready var _clear_input_button:Button = %ClearInputButton
@onready var _modifiers:ArrayEdit = %Modifiers
@onready var _add_modifier_popup:PopupMenu = %AddModifierPopup
@onready var _triggers:ArrayEdit = %Triggers
@onready var _add_trigger_popup:PopupMenu = %AddTriggerPopup
var _plugin:EditorPlugin
var _scanner:ClassScanner
var _undo_redo:EditorUndoRedoManager
var _mapping:GUIDEInputMapping
func _ready():
_edit_input_button.icon = get_theme_icon("Edit", "EditorIcons")
_clear_input_button.icon = get_theme_icon("Remove", "EditorIcons")
_edit_input_mapping_button.icon = get_theme_icon("Tools", "EditorIcons")
_modifiers.add_requested.connect(_on_modifiers_add_requested)
_modifiers.delete_requested.connect(_on_modifier_delete_requested)
_modifiers.duplicate_requested.connect(_on_modifier_duplicate_requested)
_modifiers.move_requested.connect(_on_modifier_move_requested)
_modifiers.clear_requested.connect(_on_modifiers_clear_requested)
_modifiers.collapse_state_changed.connect(_on_modifiers_collapse_state_changed)
_triggers.add_requested.connect(_on_triggers_add_requested)
_triggers.delete_requested.connect(_on_trigger_delete_requested)
_triggers.duplicate_requested.connect(_on_trigger_duplicate_requested)
_triggers.move_requested.connect(_on_trigger_move_requested)
_triggers.clear_requested.connect(_on_triggers_clear_requested)
_triggers.collapse_state_changed.connect(_on_triggers_collapse_state_changed)
func initialize(plugin:EditorPlugin, scanner:ClassScanner) -> void:
_plugin = plugin
_scanner = scanner
_undo_redo = plugin.get_undo_redo()
_input_display.clicked.connect(_on_input_display_clicked)
func edit(mapping:GUIDEInputMapping) -> void:
assert(_mapping == null)
_mapping = mapping
_mapping.changed.connect(_update)
_update()
func _update():
_modifiers.clear()
_triggers.clear()
_input_display.input = _mapping.input
for i in _mapping.modifiers.size():
var modifier_slot = modifier_slot_scene.instantiate()
_modifiers.add_item(modifier_slot)
modifier_slot.modifier = _mapping.modifiers[i]
modifier_slot.changed.connect(_on_modifier_changed.bind(i, modifier_slot))
for i in _mapping.triggers.size():
var trigger_slot = trigger_slot_scene.instantiate()
_triggers.add_item(trigger_slot)
trigger_slot.trigger = _mapping.triggers[i]
trigger_slot.changed.connect(_on_trigger_changed.bind(i, trigger_slot))
_modifiers.collapsed = _mapping.get_meta("_guide_modifiers_collapsed", false)
_triggers.collapsed = _mapping.get_meta("_guide_triggers_collapsed", false)
func _on_modifiers_add_requested():
_fill_popup(_add_modifier_popup, "GUIDEModifier")
_add_modifier_popup.popup(Rect2(get_global_mouse_position(), Vector2.ZERO))
func _on_triggers_add_requested():
_fill_popup(_add_trigger_popup, "GUIDETrigger")
_add_trigger_popup.popup(Rect2(get_global_mouse_position(), Vector2.ZERO))
func _fill_popup(popup:PopupMenu, base_clazz:StringName):
popup.clear(true)
var inheritors := _scanner.find_inheritors(base_clazz)
for type in inheritors.keys():
var class_script:Script = inheritors[type]
var dummy = class_script.new()
popup.add_item(dummy._editor_name())
popup.set_item_tooltip(popup.item_count -1, dummy._editor_description())
popup.set_item_metadata(popup.item_count - 1, class_script)
func _on_input_display_clicked():
if is_instance_valid(_mapping.input):
EditorInterface.edit_resource(_mapping.input)
func _on_input_changed(input:GUIDEInput):
_undo_redo.create_action("Change input")
_undo_redo.add_do_property(_mapping, "input", input)
_undo_redo.add_undo_property(_mapping, "input", _mapping.input)
_undo_redo.commit_action()
if is_instance_valid(input):
EditorInterface.edit_resource(input)
func _on_edit_input_button_pressed():
var dialog:Window = binding_dialog_scene.instantiate()
EditorInterface.popup_dialog_centered(dialog)
dialog.initialize(_scanner)
dialog.input_selected.connect(_on_input_changed)
func _on_clear_input_button_pressed():
_undo_redo.create_action("Delete bound input")
_undo_redo.add_do_property(_mapping, "input", null)
_undo_redo.add_undo_property(_mapping, "triggers", _mapping.input)
_undo_redo.commit_action()
func _on_add_modifier_popup_index_pressed(index:int) -> void:
var script = _add_modifier_popup.get_item_metadata(index)
var new_modifier = script.new()
_undo_redo.create_action("Add " + new_modifier._editor_name() + " modifier")
var modifiers = _mapping.modifiers.duplicate()
modifiers.append(new_modifier)
_undo_redo.add_do_property(_mapping, "modifiers", modifiers)
_undo_redo.add_undo_property(_mapping, "modifiers", _mapping.modifiers)
_undo_redo.commit_action()
func _on_add_trigger_popup_index_pressed(index):
var script = _add_trigger_popup.get_item_metadata(index)
var new_trigger = script.new()
_undo_redo.create_action("Add " + new_trigger._editor_name() + " trigger")
var triggers = _mapping.triggers.duplicate()
triggers.append(new_trigger)
_undo_redo.add_do_property(_mapping, "triggers", triggers)
_undo_redo.add_undo_property(_mapping, "triggers", _mapping.triggers)
_undo_redo.commit_action()
func _on_modifier_changed(index:int, slot) -> void:
var new_modifier = slot.modifier
_undo_redo.create_action("Replace modifier")
var modifiers = _mapping.modifiers.duplicate()
modifiers[index] = new_modifier
_undo_redo.add_do_property(_mapping, "modifiers", modifiers)
_undo_redo.add_undo_property(_mapping, "modifiers", _mapping.modifiers)
_undo_redo.commit_action()
func _on_trigger_changed(index:int, slot) -> void:
var new_trigger = slot.trigger
_undo_redo.create_action("Replace trigger")
var triggers = _mapping.triggers.duplicate()
triggers[index] = new_trigger
_undo_redo.add_do_property(_mapping, "triggers", triggers)
_undo_redo.add_undo_property(_mapping, "triggers", _mapping.triggers)
_undo_redo.commit_action()
func _on_modifier_move_requested(from:int, to:int) -> void:
_undo_redo.create_action("Move modifier")
var modifiers = _mapping.modifiers.duplicate()
var modifier = modifiers[from]
modifiers.remove_at(from)
if from < to:
to -= 1
modifiers.insert(to, modifier)
_undo_redo.add_do_property(_mapping, "modifiers", modifiers)
_undo_redo.add_undo_property(_mapping, "modifiers", _mapping.modifiers)
_undo_redo.commit_action()
func _on_trigger_move_requested(from:int, to:int) -> void:
_undo_redo.create_action("Move trigger")
var triggers = _mapping.triggers.duplicate()
var trigger = triggers[from]
triggers.remove_at(from)
if from < to:
to -= 1
triggers.insert(to, trigger)
_undo_redo.add_do_property(_mapping, "triggers", triggers)
_undo_redo.add_undo_property(_mapping, "triggers", _mapping.triggers)
_undo_redo.commit_action()
func _on_modifier_duplicate_requested(index:int) -> void:
_undo_redo.create_action("Duplicate modifier")
var modifiers = _mapping.modifiers.duplicate()
var copy = Utils.duplicate_if_inline(modifiers[index])
modifiers.insert(index+1, copy)
_undo_redo.add_do_property(_mapping, "modifiers", modifiers)
_undo_redo.add_undo_property(_mapping, "modifiers", _mapping.modifiers)
_undo_redo.commit_action()
func _on_trigger_duplicate_requested(index:int) -> void:
_undo_redo.create_action("Duplicate trigger")
var triggers = _mapping.triggers.duplicate()
var copy = Utils.duplicate_if_inline(triggers[index])
triggers.insert(index+1, copy)
_undo_redo.add_do_property(_mapping, "triggers", triggers)
_undo_redo.add_undo_property(_mapping, "triggers", _mapping.triggers)
_undo_redo.commit_action()
func _on_modifier_delete_requested(index:int) -> void:
_undo_redo.create_action("Delete modifier")
var modifiers = _mapping.modifiers.duplicate()
modifiers.remove_at(index)
_undo_redo.add_do_property(_mapping, "modifiers", modifiers)
_undo_redo.add_undo_property(_mapping, "modifiers", _mapping.modifiers)
_undo_redo.commit_action()
func _on_trigger_delete_requested(index:int) -> void:
_undo_redo.create_action("Delete trigger")
var triggers = _mapping.triggers.duplicate()
triggers.remove_at(index)
_undo_redo.add_do_property(_mapping, "triggers", triggers)
_undo_redo.add_undo_property(_mapping, "triggers", _mapping.triggers)
_undo_redo.commit_action()
func _on_modifiers_clear_requested() -> void:
_undo_redo.create_action("Clear modifiers")
# if this is inlined into the do_property, then it doesn't work
# so lets keep it a local variable
var value:Array[GUIDEModifier] = []
_undo_redo.add_do_property(_mapping, "modifiers", value)
_undo_redo.add_undo_property(_mapping, "modifiers", _mapping.modifiers)
_undo_redo.commit_action()
func _on_triggers_clear_requested() -> void:
_undo_redo.create_action("Clear triggers")
# if this is inlined into the do_property, then it doesn't work
# so lets keep it a local variable
var value:Array[GUIDETrigger] = []
_undo_redo.add_do_property(_mapping, "triggers", value)
_undo_redo.add_undo_property(_mapping, "triggers", _mapping.triggers)
_undo_redo.commit_action()
func _on_modifiers_collapse_state_changed(new_state:bool):
_mapping.set_meta("_guide_modifiers_collapsed", new_state)
func _on_triggers_collapse_state_changed(new_state:bool):
_mapping.set_meta("_guide_triggers_collapsed", new_state)
func _on_edit_input_mapping_button_pressed():
EditorInterface.edit_resource(_mapping)

View File

@@ -0,0 +1,140 @@
[gd_scene load_steps=9 format=3 uid="uid://c323mdijdhktg"]
[ext_resource type="PackedScene" uid="uid://dsv7s6tfmnsrs" path="res://addons/guide/editor/input_display/input_display.tscn" id="1_pg8n3"]
[ext_resource type="Script" path="res://addons/guide/editor/input_mapping_editor/input_mapping_editor.gd" id="1_xsluc"]
[ext_resource type="PackedScene" uid="uid://ck5a30syo6bpo" path="res://addons/guide/editor/modifier_slot/modifier_slot.tscn" id="2_uhbrq"]
[ext_resource type="PackedScene" uid="uid://tk30wnstb0ku" path="res://addons/guide/editor/trigger_slot/trigger_slot.tscn" id="3_e0jys"]
[ext_resource type="PackedScene" uid="uid://dic27bm4pfw3q" path="res://addons/guide/editor/binding_dialog/binding_dialog.tscn" id="4_oepf3"]
[ext_resource type="PackedScene" uid="uid://cly0ff32fvpb2" path="res://addons/guide/editor/array_edit/array_edit.tscn" id="6_jekhk"]
[sub_resource type="Image" id="Image_m1w1j"]
data = {
"data": PackedByteArray(255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 94, 94, 127, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 231, 255, 94, 94, 54, 255, 94, 94, 57, 255, 93, 93, 233, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 93, 93, 41, 255, 255, 255, 0, 255, 255, 255, 0, 255, 97, 97, 42, 255, 93, 93, 233, 255, 93, 93, 232, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 44, 255, 255, 255, 0, 255, 97, 97, 42, 255, 97, 97, 42, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 96, 96, 45, 255, 93, 93, 235, 255, 94, 94, 234, 255, 95, 95, 43, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 93, 93, 235, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 233, 255, 95, 95, 59, 255, 96, 96, 61, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 93, 93, 255, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0),
"format": "RGBA8",
"height": 16,
"mipmaps": false,
"width": 16
}
[sub_resource type="ImageTexture" id="ImageTexture_y0eyy"]
image = SubResource("Image_m1w1j")
[node name="InputMappingEditor" type="MarginContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_vertical = 0
script = ExtResource("1_xsluc")
modifier_slot_scene = ExtResource("2_uhbrq")
trigger_slot_scene = ExtResource("3_e0jys")
binding_dialog_scene = ExtResource("4_oepf3")
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 2
size_flags_vertical = 0
theme_override_constants/separation = 8
[node name="MarginContainer" type="MarginContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_vertical = 0
[node name="Panel" type="Panel" parent="HBoxContainer/MarginContainer"]
visible = false
layout_mode = 2
[node name="EditInputMappingButton" type="Button" parent="HBoxContainer/MarginContainer"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Open input mapping in inspector"
icon = SubResource("ImageTexture_y0eyy")
flat = true
[node name="MarginContainer1" type="MarginContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Panel" type="Panel" parent="HBoxContainer/MarginContainer1"]
visible = false
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/MarginContainer1"]
layout_mode = 2
[node name="InputDisplay" parent="HBoxContainer/MarginContainer1/HBoxContainer" instance=ExtResource("1_pg8n3")]
unique_name_in_owner = true
layout_mode = 2
scroll_active = false
[node name="EditInputButton" type="Button" parent="HBoxContainer/MarginContainer1/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 0
tooltip_text = "Edit bound input..."
icon = SubResource("ImageTexture_y0eyy")
flat = true
[node name="ClearInputButton" type="Button" parent="HBoxContainer/MarginContainer1/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 0
tooltip_text = "Delete bound input"
icon = SubResource("ImageTexture_y0eyy")
flat = true
[node name="MarginContainer2" type="MarginContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
[node name="Panel" type="Panel" parent="HBoxContainer/MarginContainer2"]
visible = false
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/MarginContainer2"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
size_flags_stretch_ratio = 2.0
[node name="Modifiers" parent="HBoxContainer/MarginContainer2/VBoxContainer" instance=ExtResource("6_jekhk")]
unique_name_in_owner = true
layout_mode = 2
title = "Modifiers"
add_tooltip = "Add modifier..."
clear_tooltip = "Clear modifiers"
[node name="AddModifierPopup" type="PopupMenu" parent="HBoxContainer/MarginContainer2/VBoxContainer"]
unique_name_in_owner = true
[node name="MarginContainer3" type="MarginContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 2.0
[node name="Panel" type="Panel" parent="HBoxContainer/MarginContainer3"]
visible = false
layout_mode = 2
[node name="VBoxContainer2" type="VBoxContainer" parent="HBoxContainer/MarginContainer3"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 0
size_flags_stretch_ratio = 2.0
[node name="Triggers" parent="HBoxContainer/MarginContainer3/VBoxContainer2" instance=ExtResource("6_jekhk")]
unique_name_in_owner = true
layout_mode = 2
title = "Triggers"
add_tooltip = "Add trigger..."
clear_tooltip = "Clear triggers"
[node name="AddTriggerPopup" type="PopupMenu" parent="HBoxContainer/MarginContainer3/VBoxContainer2"]
unique_name_in_owner = true
[connection signal="pressed" from="HBoxContainer/MarginContainer/EditInputMappingButton" to="." method="_on_edit_input_mapping_button_pressed"]
[connection signal="pressed" from="HBoxContainer/MarginContainer1/HBoxContainer/EditInputButton" to="." method="_on_edit_input_button_pressed"]
[connection signal="pressed" from="HBoxContainer/MarginContainer1/HBoxContainer/ClearInputButton" to="." method="_on_clear_input_button_pressed"]
[connection signal="index_pressed" from="HBoxContainer/MarginContainer2/VBoxContainer/AddModifierPopup" to="." method="_on_add_modifier_popup_index_pressed"]
[connection signal="index_pressed" from="HBoxContainer/MarginContainer3/VBoxContainer2/AddTriggerPopup" to="." method="_on_add_trigger_popup_index_pressed"]

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
<g transform="matrix(1,0,0,1.16508,0,-1.89607)">
<path d="M11.289,19.641L0.424,19.641L0.424,11.08L11.289,11.08L11.289,1.973L21.263,1.973L21.263,11.08L31.576,11.08L31.576,19.641L21.263,19.641L21.263,28.711L11.289,28.711L11.289,19.641Z" style="fill:rgb(235,235,235);"/>
<path d="M11.289,19.641L0.424,19.641L0.424,11.08L11.289,11.08L11.289,1.973L21.263,1.973L21.263,11.08L31.576,11.08L31.576,19.641L21.263,19.641L21.263,28.711L11.289,28.711L11.289,19.641ZM11.567,19.641L11.567,28.473L20.985,28.473L20.985,19.641C20.985,19.509 21.109,19.402 21.263,19.402L31.298,19.402L31.298,11.318L21.263,11.318C21.109,11.318 20.985,11.212 20.985,11.08L20.985,2.212L11.567,2.212L11.567,11.08C11.567,11.212 11.442,11.318 11.289,11.318L0.702,11.318L0.702,19.402L11.289,19.402C11.442,19.402 11.567,19.509 11.567,19.641Z" style="fill:rgb(102,102,102);"/>
</g>
<g transform="matrix(1,0,0,1,-1.63395,-1.35279)">
<path d="M17.634,3.353L21.284,10.653L13.984,10.653L17.634,3.353Z" style="fill:rgb(102,102,102);"/>
<path d="M17.634,3.353L21.284,10.653L13.984,10.653L17.634,3.353ZM17.634,3.973C17.634,3.973 14.433,10.375 14.433,10.375L20.835,10.375L17.634,3.973Z" style="fill:rgb(102,102,102);"/>
</g>
<g transform="matrix(6.12323e-17,1,-1,6.12323e-17,33.3528,-1.63395)">
<path d="M17.634,3.353L21.284,10.653L13.984,10.653L17.634,3.353Z" style="fill:rgb(102,102,102);"/>
<path d="M17.634,3.353L21.284,10.653L13.984,10.653L17.634,3.353ZM17.634,3.973C17.634,3.973 14.433,10.375 14.433,10.375L20.835,10.375L17.634,3.973Z" style="fill:rgb(102,102,102);"/>
</g>
<g transform="matrix(-1,1.22465e-16,-1.22465e-16,-1,33.634,33.3528)">
<path d="M17.634,3.353L21.284,10.653L13.984,10.653L17.634,3.353Z" style="fill:rgb(102,102,102);"/>
<path d="M17.634,3.353L21.284,10.653L13.984,10.653L17.634,3.353ZM17.634,3.973C17.634,3.973 14.433,10.375 14.433,10.375L20.835,10.375L17.634,3.973Z" style="fill:rgb(102,102,102);"/>
</g>
<g transform="matrix(-1.83697e-16,-1,1,-1.83697e-16,-1.33687,33.6127)">
<path d="M17.634,3.353L21.284,10.653L13.984,10.653L17.634,3.353Z" style="fill:rgb(102,102,102);"/>
<path d="M17.634,3.353L21.284,10.653L13.984,10.653L17.634,3.353ZM17.634,3.973C17.634,3.973 14.433,10.375 14.433,10.375L20.835,10.375L17.634,3.973Z" style="fill:rgb(102,102,102);"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -0,0 +1,38 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cap7e0f05pj8j"
path="res://.godot/imported/logo_editor_small.svg-a18f1eaff840dcdf5215ef26c289caf9.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/guide/editor/logo_editor_small.svg"
dest_files=["res://.godot/imported/logo_editor_small.svg-a18f1eaff840dcdf5215ef26c289caf9.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=0.5
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,159 @@
@tool
extends MarginContainer
const ClassScanner = preload("../class_scanner.gd")
const Utils = preload("../utils.gd")
const ArrayEdit = preload("../array_edit/array_edit.gd")
@export var action_mapping_editor_scene:PackedScene
@onready var _title_label:Label = %TitleLabel
@onready var _action_mappings:ArrayEdit = %ActionMappings
@onready var _editing_view:Control = %EditingView
@onready var _empty_view = %EmptyView
var _plugin:EditorPlugin
var _current_context:GUIDEMappingContext
var _undo_redo:EditorUndoRedoManager
var _scanner:ClassScanner
func _ready():
_title_label.add_theme_font_override("font", get_theme_font("title", "EditorFonts"))
_scanner = ClassScanner.new()
_editing_view.visible = false
_empty_view.visible = true
_action_mappings.add_requested.connect(_on_action_mappings_add_requested)
_action_mappings.move_requested.connect(_on_action_mappings_move_requested)
_action_mappings.delete_requested.connect(_on_action_mapping_delete_requested)
_action_mappings.clear_requested.connect(_on_action_mappings_clear_requested)
_action_mappings.duplicate_requested.connect(_on_action_mapping_duplicate_requested)
_action_mappings.collapse_state_changed.connect(_on_action_mappings_collapse_state_changed)
func initialize(plugin:EditorPlugin) -> void:
_plugin = plugin
_undo_redo = plugin.get_undo_redo()
func edit(context:GUIDEMappingContext) -> void:
if is_instance_valid(_current_context):
_current_context.changed.disconnect(_refresh)
_current_context = context
if is_instance_valid(_current_context):
_current_context.changed.connect(_refresh)
_refresh()
func _refresh():
_editing_view.visible = is_instance_valid(_current_context)
_empty_view.visible = not is_instance_valid(_current_context)
if not is_instance_valid(_current_context):
return
_title_label.text = _current_context._editor_name()
_title_label.tooltip_text = _current_context.resource_path
_action_mappings.clear()
for i in _current_context.mappings.size():
var mapping = _current_context.mappings[i]
var mapping_editor = action_mapping_editor_scene.instantiate()
mapping_editor.initialize(_plugin, _scanner)
_action_mappings.add_item(mapping_editor)
mapping_editor.edit(mapping)
_action_mappings.collapsed = _current_context.get_meta("_guide_action_mappings_collapsed", false)
func _on_action_mappings_add_requested():
var mappings = _current_context.mappings.duplicate()
var new_mapping := GUIDEActionMapping.new()
# don't set an action because they should come from the file system
mappings.append(new_mapping)
_undo_redo.create_action("Add action mapping")
_undo_redo.add_do_property(_current_context, "mappings", mappings)
_undo_redo.add_undo_property(_current_context, "mappings", _current_context.mappings)
_undo_redo.commit_action()
func _on_action_mappings_move_requested(from:int, to:int):
var mappings = _current_context.mappings.duplicate()
var mapping = mappings[from]
mappings.remove_at(from)
if from < to:
to -= 1
mappings.insert(to, mapping)
_undo_redo.create_action("Move action mapping")
_undo_redo.add_do_property(_current_context, "mappings", mappings)
_undo_redo.add_undo_property(_current_context, "mappings", _current_context.mappings)
_undo_redo.commit_action()
func _on_action_mapping_delete_requested(index:int):
var mappings = _current_context.mappings.duplicate()
mappings.remove_at(index)
_undo_redo.create_action("Delete action mapping")
_undo_redo.add_do_property(_current_context, "mappings", mappings)
_undo_redo.add_undo_property(_current_context, "mappings", _current_context.mappings)
_undo_redo.commit_action()
func _on_action_mappings_clear_requested():
var mappings:Array[GUIDEActionMapping] = []
_undo_redo.create_action("Clear action mappings")
_undo_redo.add_do_property(_current_context, "mappings", mappings)
_undo_redo.add_undo_property(_current_context, "mappings", _current_context.mappings)
_undo_redo.commit_action()
func _on_action_mapping_duplicate_requested(index:int):
var mappings = _current_context.mappings.duplicate()
var to_duplicate:GUIDEActionMapping = mappings[index]
var copy = GUIDEActionMapping.new()
# don't set the action, because each mapping should have a unique mapping
for input_mapping:GUIDEInputMapping in to_duplicate.input_mappings:
var copied_input_mapping := GUIDEInputMapping.new()
copied_input_mapping.input = Utils.duplicate_if_inline(input_mapping.input)
for modifier in input_mapping.modifiers:
copied_input_mapping.modifiers.append(Utils.duplicate_if_inline(modifier))
for trigger in input_mapping.triggers:
copied_input_mapping.triggers.append(Utils.duplicate_if_inline(trigger))
copy.input_mappings.append(copied_input_mapping)
# insert the copy after the copied mapping
mappings.insert(index+1, copy)
_undo_redo.create_action("Duplicate action mapping")
_undo_redo.add_do_property(_current_context, "mappings", mappings)
_undo_redo.add_undo_property(_current_context, "mappings", _current_context.mappings)
_undo_redo.commit_action()
func _on_action_mappings_collapse_state_changed(new_state:bool):
_current_context.set_meta("_guide_action_mappings_collapsed", new_state)

View File

@@ -0,0 +1,58 @@
[gd_scene load_steps=4 format=3 uid="uid://dm3hott3tfvwe"]
[ext_resource type="Script" path="res://addons/guide/editor/mapping_context_editor/mapping_context_editor.gd" id="1_vytdu"]
[ext_resource type="PackedScene" uid="uid://361aipcef24h" path="res://addons/guide/editor/action_mapping_editor/action_mapping_editor.tscn" id="2_qb3p8"]
[ext_resource type="PackedScene" uid="uid://cly0ff32fvpb2" path="res://addons/guide/editor/array_edit/array_edit.tscn" id="3_x7h5x"]
[node name="MappingContextEditor" type="MarginContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
script = ExtResource("1_vytdu")
action_mapping_editor_scene = ExtResource("2_qb3p8")
[node name="EditingView" type="VBoxContainer" parent="."]
unique_name_in_owner = true
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="EditingView"]
layout_mode = 2
[node name="TitleLabel" type="Label" parent="EditingView/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 6
text = "narf.tres"
horizontal_alignment = 1
[node name="MarginContainer" type="MarginContainer" parent="EditingView"]
layout_mode = 2
theme_override_constants/margin_bottom = 5
[node name="ScrollContainer" type="ScrollContainer" parent="EditingView"]
layout_mode = 2
size_flags_vertical = 3
[node name="ActionMappings" parent="EditingView/ScrollContainer" instance=ExtResource("3_x7h5x")]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
title = "Action mappings"
add_tooltip = "Add action mapping"
clear_tooltip = "Clear action mappings"
[node name="EmptyView" type="CenterContainer" parent="."]
unique_name_in_owner = true
layout_mode = 2
[node name="Label" type="Label" parent="EmptyView"]
layout_mode = 2
text = "Create and open a GUIDEMappingContext to get started."

View File

@@ -0,0 +1,14 @@
@tool
extends "../resource_slot/resource_slot.gd"
var modifier:GUIDEModifier:
set(value):
_value = value
get:
return _value
func _accepts_drop_data(data:Resource) -> bool:
return data is GUIDEModifier

View File

@@ -0,0 +1,18 @@
[gd_scene load_steps=2 format=3 uid="uid://ck5a30syo6bpo"]
[ext_resource type="Script" path="res://addons/guide/editor/modifier_slot/modifier_slot.gd" id="1_273m5"]
[node name="LineEdit" type="LineEdit"]
offset_right = 1920.0
offset_bottom = 31.0
size_flags_horizontal = 3
size_flags_vertical = 0
text = "Name"
editable = false
context_menu_enabled = false
virtual_keyboard_enabled = false
shortcut_keys_enabled = false
middle_mouse_paste_enabled = false
selecting_enabled = false
drag_and_drop_selection_enabled = false
script = ExtResource("1_273m5")

View File

@@ -0,0 +1,106 @@
@tool
extends LineEdit
signal changed()
const Utils = preload("../utils.gd")
func _ready():
editable = false
context_menu_enabled = false
virtual_keyboard_enabled = false
shortcut_keys_enabled = false
selecting_enabled = false
drag_and_drop_selection_enabled = false
middle_mouse_paste_enabled = false
## The underlying resource. This is opened for editing when the user clicks on the control. Its also
## used when dragging from the control.
var _value:Resource = null:
set(value):
if _value == value:
return
# stop tracking changes to the old resource (if any)
if is_instance_valid(_value):
_value.changed.disconnect(_update_from_value)
_value = value
# track changes to the resource itself
if is_instance_valid(_value):
_value.changed.connect(_update_from_value)
_update_from_value()
changed.emit()
func _update_from_value():
if not is_instance_valid(_value):
text = "<none>"
tooltip_text = ""
remove_theme_color_override("font_uneditable_color")
else:
text = _value._editor_name()
tooltip_text = _value.resource_path
# if the value is shared, we override the font color to indicate that
if not Utils.is_inline(_value):
add_theme_color_override("font_uneditable_color", get_theme_color("accent_color", "Editor"))
queue_redraw()
else:
remove_theme_color_override("font_uneditable_color")
## Can be overridden to handle the drop data. This method is called when the user drops something on the control.
## If the value should be updated ,this method should set the _value property.
func _do_drop_data(data:Resource):
_value = data
## Whether this control can accept drop data. This method is called when the user drags something over the control.
func _accepts_drop_data(data:Resource) -> bool:
return false
func _can_drop_data(at_position, data) -> bool:
if data is Resource:
return _accepts_drop_data(data)
if not data is Dictionary:
return false
if data.has("files"):
for file in data["files"]:
if _accepts_drop_data(ResourceLoader.load(file)):
return true
return false
func _drop_data(at_position, data) -> void:
if data is Resource:
_do_drop_data(data)
return
for file in data["files"]:
var item := ResourceLoader.load(file)
_do_drop_data(item)
func _get_drag_data(at_position: Vector2) -> Variant:
if is_instance_valid(_value):
var _preview := TextureRect.new()
_preview.texture = get_theme_icon("File", "EditorIcons")
set_drag_preview(_preview)
# if the value is shared, we just hand out the resource path
if not Utils.is_inline(_value):
return {"files": [_value.resource_path]}
else:
# otherwise we hand out a shallow copy
return _value.duplicate()
else:
return null
func _gui_input(event):
if event is InputEventMouseButton:
if event.pressed and event.button_index == MOUSE_BUTTON_LEFT:
if is_instance_valid(_value):
EditorInterface.edit_resource(_value)

View File

@@ -0,0 +1,14 @@
@tool
extends "../resource_slot/resource_slot.gd"
var trigger:GUIDETrigger:
set(value):
_value = value
get:
return _value
func _accepts_drop_data(data:Resource) -> bool:
return data is GUIDETrigger

View File

@@ -0,0 +1,20 @@
[gd_scene load_steps=2 format=3 uid="uid://tk30wnstb0ku"]
[ext_resource type="Script" path="res://addons/guide/editor/trigger_slot/trigger_slot.gd" id="1_wxafc"]
[node name="LineEdit" type="LineEdit"]
unique_name_in_owner = true
offset_right = 1920.0
offset_bottom = 31.0
size_flags_horizontal = 3
size_flags_vertical = 0
tooltip_text = "Delete trigger"
text = "Name"
editable = false
context_menu_enabled = false
virtual_keyboard_enabled = false
shortcut_keys_enabled = false
middle_mouse_paste_enabled = false
selecting_enabled = false
drag_and_drop_selection_enabled = false
script = ExtResource("1_wxafc")

View File

@@ -0,0 +1,22 @@
## Removes and frees all children of a node.
static func clear(node:Node):
if not is_instance_valid(node):
return
for child in node.get_children():
node.remove_child(child)
child.queue_free()
## Checks if the given resource is an inline resource. If so, returns a shallow copy,
## otherwise returns the resource. If the resource is null, returns null.
static func duplicate_if_inline(resource:Resource) -> Resource:
if is_inline(resource):
return resource.duplicate()
return resource
## Checks if the given resource is an inline resource.
static func is_inline(resource:Resource) -> bool:
if resource == null:
return false
return resource.resource_path.contains("::") or resource.resource_path == ""