gd: added input addon

This commit is contained in:
2025-05-27 19:20:46 +02:00
parent d8a1604af9
commit c8d8c7ec25
683 changed files with 21608 additions and 2 deletions

View File

@ -0,0 +1,29 @@
extends HBoxContainer
signal rebind(item:GUIDERemapper.ConfigItem)
@onready var _action_name:Button = %ActionName
@onready var _action_binding:RichTextLabel = %ActionBinding
var _formatter:GUIDEInputFormatter = GUIDEInputFormatter.new(48)
var _item:GUIDERemapper.ConfigItem
func initialize(item:GUIDERemapper.ConfigItem, input:GUIDEInput):
_item = item
_action_name.text = item.display_name
_item.changed.connect(_show_input)
_show_input(input)
func _on_action_name_pressed():
if _item != null:
rebind.emit(_item)
func _show_input(input:GUIDEInput):
if input != null:
var text = await _formatter.input_as_richtext_async(input)
_action_binding.parse_bbcode(text)
else:
_action_binding.parse_bbcode("<not bound>")

View File

@ -0,0 +1 @@
uid://cfukq2iogf0k4

View File

@ -0,0 +1,32 @@
[gd_scene load_steps=2 format=3 uid="uid://bme1y0ikthda7"]
[ext_resource type="Script" path="res://guide_examples/remapping/ui/binding_row.gd" id="1_mc50g"]
[node name="BindingRow" type="HBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/separation = 10
script = ExtResource("1_mc50g")
[node name="ActionName" type="Button" parent="."]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 10
text = "Lorem ipsum"
flat = true
[node name="ActionBinding" type="RichTextLabel" parent="."]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 2
size_flags_vertical = 4
bbcode_enabled = true
fit_content = true
scroll_active = false
autowrap_mode = 0
shortcut_keys_enabled = false
[connection signal="pressed" from="ActionName" to="." method="_on_action_name_pressed"]

View File

@ -0,0 +1,18 @@
@tool
extends MarginContainer
@onready var _label:Label = %Label
@export var text:String:
set(value):
text = value
_refresh()
func _ready():
_refresh()
func _refresh():
if _label != null:
_label.text = text

View File

@ -0,0 +1 @@
uid://dt4gdj0rt5mnw

View File

@ -0,0 +1,36 @@
[gd_scene load_steps=3 format=3 uid="uid://cj1h0wxamje4s"]
[ext_resource type="Script" path="res://guide_examples/remapping/ui/binding_section.gd" id="1_hoxsv"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_h8l7u"]
bg_color = Color(0.355314, 0.355314, 0.355313, 1)
corner_radius_top_left = 10
corner_radius_top_right = 10
corner_radius_bottom_right = 10
corner_radius_bottom_left = 10
[node name="MarginContainer" type="MarginContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_constants/margin_top = 10
theme_override_constants/margin_bottom = 10
script = ExtResource("1_hoxsv")
[node name="Panel" type="Panel" parent="."]
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_h8l7u")
[node name="BindingSection" type="MarginContainer" parent="."]
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="Label" type="Label" parent="BindingSection"]
unique_name_in_owner = true
layout_mode = 2
theme_override_font_sizes/font_size = 22

View File

@ -0,0 +1,198 @@
## The remapping dialog.
extends MarginContainer
signal closed(applied_config:GUIDERemappingConfig)
const Utils = preload("../utils.gd")
# Input
@export var keyboard_context:GUIDEMappingContext
@export var controller_context:GUIDEMappingContext
@export var binding_keyboard_context:GUIDEMappingContext
@export var binding_controller_context:GUIDEMappingContext
@export var close_dialog:GUIDEAction
@export var switch_to_controller:GUIDEAction
@export var switch_to_keyboard:GUIDEAction
@export var previous_tab:GUIDEAction
@export var next_tab:GUIDEAction
# UI
@export var binding_row_scene:PackedScene
@export var binding_section_scene:PackedScene
@onready var _keyboard_bindings:Container = %KeyboardBindings
@onready var _controller_bindings:Container = %ControllerBindings
@onready var _press_prompt:Control = %PressPrompt
@onready var _controller_invert_horizontal:CheckBox = %ControllerInvertHorizontal
@onready var _controller_invert_vertical:CheckBox = %ControllerInvertVertical
@onready var _tab_container:TabContainer = %TabContainer
## The input detector for detecting new input
@onready var _input_detector:GUIDEInputDetector = %GUIDEInputDetector
## The remapper, helps us quickly remap inputs.
var _remapper:GUIDERemapper = GUIDERemapper.new()
## The config we're currently working on
var _remapping_config:GUIDERemappingConfig
## The last control that was focused when we started input detection.
## Used to restore focus afterwards.
var _focused_control:Control = null
func _ready():
# connect the actions that the remapping dialog uses
close_dialog.triggered.connect(_on_close_dialog)
switch_to_controller.triggered.connect(_switch.bind(binding_controller_context))
switch_to_keyboard.triggered.connect(_switch.bind(binding_keyboard_context))
previous_tab.triggered.connect(_switch_tab.bind(-1))
next_tab.triggered.connect(_switch_tab.bind(1))
func open():
# switch the tab to the scheme that is currently enabled
# to make life a bit easier for the player, and also
# enable the correct mapping context for the binding dialog
if GUIDE.is_mapping_context_enabled(controller_context):
_tab_container.current_tab = 1
GUIDE.enable_mapping_context(binding_controller_context, true)
else:
_tab_container.current_tab = 0
GUIDE.enable_mapping_context(binding_keyboard_context, true)
# todo provide specific actions for the tab bar controller
_tab_container.get_tab_bar().grab_focus()
# Open the user's last edited remapping config, if it exists
_remapping_config = Utils.load_remapping_config()
# And initialize the remapper
_remapper.initialize([keyboard_context, controller_context], _remapping_config)
_clear(_keyboard_bindings)
_clear(_controller_bindings)
# fill the keyboard section
_fill_remappable_items(keyboard_context, _keyboard_bindings)
# fill the controller section
_fill_remappable_items(controller_context, _controller_bindings)
_controller_invert_horizontal.button_pressed = _remapper.get_custom_data("invert_horizontal", false)
_controller_invert_vertical.button_pressed = _remapper.get_custom_data("invert_vertical", false)
visible = true
## Fills remappable items and sub-sections into the given container
func _fill_remappable_items(context:GUIDEMappingContext, root:Container):
var items := _remapper.get_remappable_items(context)
var section_name = ""
for item in items:
if item.display_category != section_name:
section_name = item.display_category
var section = binding_section_scene.instantiate()
root.add_child(section)
section.text = section_name
var instance = binding_row_scene.instantiate()
root.add_child(instance)
# Show the current binding.
instance.initialize(item, _remapper.get_bound_input_or_null(item))
instance.rebind.connect(_rebind_item)
func _rebind_item(item:GUIDERemapper.ConfigItem):
_focused_control = get_viewport().gui_get_focus_owner()
_focused_control.release_focus()
_press_prompt.visible = true
# Limit the devices that we can detect based on which
# mapping context we're currently working on. So
# for keyboard only keys can be bound and for controller
# only controller buttons can be bound.
var device := GUIDEInputDetector.DeviceType.KEYBOARD
if item.context == controller_context:
device = GUIDEInputDetector.DeviceType.JOY
# detect a new input
_input_detector.detect(item.value_type, [device])
var input = await _input_detector.input_detected
_press_prompt.visible = false
_focused_control.grab_focus()
# check if the detection was aborted.
if input == null:
return
# check for collisions
var collisions := _remapper.get_input_collisions(item, input)
# if any collision is from a non-bindable mapping, we cannot use this input
if collisions.any(func(it:GUIDERemapper.ConfigItem): return not it.is_remappable):
return
# unbind the colliding entries.
for collision in collisions:
_remapper.set_bound_input(collision, null)
# now bind the new input
_remapper.set_bound_input(item, input)
func _clear(root:Container):
for child in root.get_children():
root.remove_child(child)
child.queue_free()
func _on_abort_detection():
_input_detector.abort_detection()
func _on_close_dialog():
if _input_detector.is_detecting:
return
# same as pressing return to game
_on_return_to_game_pressed()
func _on_controller_invert_horizontal_toggled(toggled_on:bool):
_remapper.set_custom_data(Utils.CUSTOM_DATA_INVERT_HORIZONTAL, toggled_on)
func _on_controller_invert_vertical_toggled(toggled_on:bool):
_remapper.set_custom_data(Utils.CUSTOM_DATA_INVERT_VERTICAL, toggled_on)
func _on_return_to_game_pressed():
# get the modified config
var final_config := _remapper.get_mapping_config()
# store it
Utils.save_remapping_config(final_config)
# restore main mapping context based on what is currently active
if GUIDE.is_mapping_context_enabled(binding_keyboard_context):
GUIDE.enable_mapping_context(keyboard_context, true)
else:
GUIDE.enable_mapping_context(controller_context, true)
# and close the dialog
visible = false
closed.emit(final_config)
func _switch_tab(index:int):
_tab_container.current_tab = posmod(_tab_container.current_tab + index, 2)
func _switch(context:GUIDEMappingContext):
# only do this when the dialog is visible
if not visible:
return
GUIDE.enable_mapping_context(context, true)

View File

@ -0,0 +1 @@
uid://5crxnd8ysf6

View File

@ -0,0 +1,224 @@
[gd_scene load_steps=21 format=3 uid="uid://bq0w7uaotgfct"]
[ext_resource type="Script" path="res://guide_examples/remapping/ui/remapping_dialog.gd" id="1_6hgqj"]
[ext_resource type="Theme" uid="uid://dot0gi1yoqmrl" path="res://guide_examples/shared/ui_theme.tres" id="1_uhsj0"]
[ext_resource type="Resource" uid="uid://cu0dhstc00cj5" path="res://guide_examples/remapping/mapping_contexts/keyboard.tres" id="2_cgour"]
[ext_resource type="Script" path="res://addons/guide/remapping/guide_input_detector.gd" id="3_o0nvn"]
[ext_resource type="Resource" uid="uid://bexjevffjsh3i" path="res://guide_examples/remapping/mapping_contexts/controller.tres" id="3_tgkdx"]
[ext_resource type="PackedScene" uid="uid://bme1y0ikthda7" path="res://guide_examples/remapping/ui/binding_row.tscn" id="4_iojgu"]
[ext_resource type="PackedScene" uid="uid://cj1h0wxamje4s" path="res://guide_examples/remapping/ui/binding_section.tscn" id="5_8v80s"]
[ext_resource type="Resource" uid="uid://bqd45wwsetlyg" path="res://guide_examples/remapping/mapping_contexts/binding_keyboard.tres" id="5_tw3jw"]
[ext_resource type="Resource" uid="uid://dubuepcs1w17f" path="res://guide_examples/remapping/mapping_contexts/binding_controller.tres" id="6_gdrkn"]
[ext_resource type="Resource" uid="uid://bcum2m26we6ct" path="res://guide_examples/remapping/mapping_contexts/shared_actions/close_menu.tres" id="7_djty7"]
[ext_resource type="Resource" uid="uid://3vqfs786vcsa" path="res://guide_examples/remapping/mapping_contexts/keyboard_actions/switch_to_controller.tres" id="8_lj8gw"]
[ext_resource type="Resource" uid="uid://ce3ytxn2tcxxe" path="res://guide_examples/remapping/mapping_contexts/controller_actions/switch_to_keyboard.tres" id="9_brmt1"]
[ext_resource type="Script" path="res://guide_examples/shared/instructions_label.gd" id="10_xsw70"]
[ext_resource type="Script" path="res://addons/guide/inputs/guide_input_key.gd" id="11_e078a"]
[ext_resource type="Resource" uid="uid://cfrx54l1vmjey" path="res://guide_examples/remapping/mapping_contexts/controller_actions/previous_tab.tres" id="11_uxwof"]
[ext_resource type="Resource" uid="uid://dg1or0do0s1ad" path="res://guide_examples/remapping/mapping_contexts/controller_actions/next_tab.tres" id="12_byojv"]
[ext_resource type="Script" path="res://addons/guide/inputs/guide_input_joy_button.gd" id="12_hftpv"]
[sub_resource type="Resource" id="Resource_f3bao"]
script = ExtResource("11_e078a")
key = 4194305
shift = false
control = false
alt = false
meta = false
allow_additional_modifiers = false
[sub_resource type="Resource" id="Resource_twrga"]
script = ExtResource("12_hftpv")
button = 4
joy_index = -1
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_bagfg"]
bg_color = Color(0.266575, 0.266575, 0.266575, 1)
[node name="RemappingDialog" type="MarginContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme = ExtResource("1_uhsj0")
script = ExtResource("1_6hgqj")
keyboard_context = ExtResource("2_cgour")
controller_context = ExtResource("3_tgkdx")
binding_keyboard_context = ExtResource("5_tw3jw")
binding_controller_context = ExtResource("6_gdrkn")
close_dialog = ExtResource("7_djty7")
switch_to_controller = ExtResource("8_lj8gw")
switch_to_keyboard = ExtResource("9_brmt1")
previous_tab = ExtResource("11_uxwof")
next_tab = ExtResource("12_byojv")
binding_row_scene = ExtResource("4_iojgu")
binding_section_scene = ExtResource("5_8v80s")
[node name="Blocker" type="ColorRect" parent="."]
layout_mode = 2
color = Color(8.66354e-07, 0.331199, 0.634906, 0.352941)
[node name="CenterContainer" type="CenterContainer" parent="."]
layout_mode = 2
[node name="PanelContainer" type="PanelContainer" parent="CenterContainer"]
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/PanelContainer"]
layout_mode = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer"]
custom_minimum_size = Vector2(800, 600)
layout_mode = 2
theme_override_constants/separation = 20
[node name="Label" type="Label" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_type_variation = &"HeaderLarge"
text = "Input Rebinding"
horizontal_alignment = 1
[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="TabContainer" type="TabContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_vertical = 3
tab_alignment = 1
current_tab = 1
clip_tabs = false
[node name="Keyboard" type="PanelContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer"]
visible = false
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Keyboard"]
layout_mode = 2
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="ScrollContainer" type="ScrollContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Keyboard/MarginContainer"]
layout_mode = 2
[node name="KeyboardBindings" type="VBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Keyboard/MarginContainer/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="Controller" type="PanelContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer"]
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller"]
layout_mode = 2
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="ScrollContainer" type="ScrollContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer"]
layout_mode = 2
[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="ControllerBindings" type="VBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
[node name="Section" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer" instance=ExtResource("5_8v80s")]
layout_mode = 2
text = "Miscellaneous"
[node name="ControllerInvertHorizontal" type="CheckBox" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "Invert horizontal movement"
[node name="ControllerInvertVertical" type="CheckBox" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "Invert vertical movement"
[node name="HBoxContainer" type="HBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer"]
layout_mode = 2
mouse_filter = 2
[node name="RichTextLabel" type="RichTextLabel" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"]
layout_mode = 2
mouse_filter = 2
fit_content = true
scroll_active = false
autowrap_mode = 0
script = ExtResource("10_xsw70")
instructions_text = "%s"
actions = Array[Resource("res://addons/guide/guide_action.gd")]([ExtResource("11_uxwof")])
[node name="Control" type="Control" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
mouse_filter = 2
[node name="RichTextLabel2" type="RichTextLabel" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/HBoxContainer"]
layout_mode = 2
mouse_filter = 2
fit_content = true
scroll_active = false
autowrap_mode = 0
script = ExtResource("10_xsw70")
instructions_text = "%s"
actions = Array[Resource("res://addons/guide/guide_action.gd")]([ExtResource("12_byojv")])
[node name="HBoxContainer" type="HBoxContainer" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
alignment = 1
[node name="ReturnToGame" type="Button" parent="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "Return to game"
[node name="GUIDEInputDetector" type="Node" parent="."]
unique_name_in_owner = true
editor_description = "This node has two inputs specified which count as abort for detection (Escape and Controller back button)."
script = ExtResource("3_o0nvn")
detection_countdown_seconds = 0.1
abort_detection_on = Array[Resource("res://addons/guide/inputs/guide_input.gd")]([SubResource("Resource_f3bao"), SubResource("Resource_twrga")])
[node name="PressPrompt" type="MarginContainer" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 2
mouse_filter = 0
[node name="CenterContainer" type="CenterContainer" parent="PressPrompt"]
layout_mode = 2
[node name="Panel" type="PanelContainer" parent="PressPrompt/CenterContainer"]
layout_mode = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_bagfg")
[node name="MarginContainer" type="MarginContainer" parent="PressPrompt/CenterContainer/Panel"]
layout_mode = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="Label" type="Label" parent="PressPrompt/CenterContainer/Panel/MarginContainer"]
layout_mode = 2
text = "Press new input..."
[connection signal="toggled" from="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer/ControllerInvertHorizontal" to="." method="_on_controller_invert_horizontal_toggled"]
[connection signal="toggled" from="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/MarginContainer/TabContainer/Controller/MarginContainer/ScrollContainer/VBoxContainer/ControllerInvertVertical" to="." method="_on_controller_invert_vertical_toggled"]
[connection signal="pressed" from="CenterContainer/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/ReturnToGame" to="." method="_on_return_to_game_pressed"]