addon for remapping inputs
update to godot4
- remove the fullscreen key selector - add custom joy mappings, per gamepad type (eg triangle instead of y) - add icons - doc all the things
bendn 2022-11-25
parent 81097a8 · commit 7625136
-rw-r--r--.github/workflows/export.yml68
-rw-r--r--.gitignore2
-rw-r--r--README.md3
-rw-r--r--Test.gd8
-rw-r--r--Test.tscn41
-rw-r--r--addons/remap/ActionIcons.gd27
-rw-r--r--addons/remap/ActionLabel.gd69
-rw-r--r--addons/remap/IconMap.gd120
-rw-r--r--addons/remap/KeyPromptLabel.gd13
-rw-r--r--addons/remap/KeySelector.gd20
-rw-r--r--addons/remap/KeySelector.tscn60
-rw-r--r--addons/remap/PromptFont.tres7
-rw-r--r--addons/remap/PromptFont.ttf.import36
-rw-r--r--addons/remap/README.md1
-rw-r--r--addons/remap/RemapButton.gd150
-rw-r--r--addons/remap/RemapUtilities.gd19
-rw-r--r--addons/remap/SaveLoadUtils.gd55
-rw-r--r--addons/remap/icons/action_icon.svg1
-rw-r--r--addons/remap/icons/action_icon.svg.import37
-rw-r--r--addons/remap/icons/action_icons.svg1
-rw-r--r--addons/remap/icons/action_icons.svg.import37
-rw-r--r--addons/remap/icons/action_label.svg52
-rw-r--r--addons/remap/icons/action_label.svg.import37
-rw-r--r--addons/remap/icons/remap_button.svg1
-rw-r--r--addons/remap/icons/remap_button.svg.import37
-rw-r--r--addons/remap/package.json2
-rw-r--r--addons/remap/private/ActionIcon.gd16
-rw-r--r--addons/remap/private/ActionIcons.gd29
-rw-r--r--addons/remap/private/IconMap.gd195
-rw-r--r--addons/remap/private/RemapUtilities.gd22
-rw-r--r--addons/remap/private/SaveLoadUtils.gd54
-rw-r--r--export_presets.cfg24
-rw-r--r--project.godot63
33 files changed, 739 insertions, 568 deletions
diff --git a/.github/workflows/export.yml b/.github/workflows/export.yml
deleted file mode 100644
index 0d33f16..0000000
--- a/.github/workflows/export.yml
+++ /dev/null
@@ -1,68 +0,0 @@
-name: "export"
-on:
- workflow_dispatch:
- push:
- paths:
- - "**.gd"
- - "**.tscn"
- - "**.import"
- - "**.tres"
- - "**.ttf"
- - "**.yml"
- branches:
- - main
-
-env:
- GODOT_VERSION: 3.5
- NAME: ${{ github.event.repository.name }}
-
-jobs:
- export:
- runs-on: ubuntu-latest
- container:
- image: ghcr.io/bend-n/godot-2d:3.5
- name: ${{ matrix.name }}
- strategy:
- matrix:
- include:
- - name: Linux export
- platform: linux
-
- steps:
- - name: Build (Windows)
- if: matrix.platform == 'windows'
- uses: bend-n/godot-actions/.github/actions/export-windows@main
-
- - name: Build (Linux)
- if: matrix.platform == 'linux'
- uses: bend-n/godot-actions/.github/actions/export-linux@main
-
- - name: Build (Mac)
- if: matrix.platform == 'mac'
- uses: bend-n/godot-actions/.github/actions/export-mac@main
-
- - name: Build (Web)
- if: matrix.platform == 'web'
- uses: bend-n/godot-actions/.github/actions/export-web@main
-
- - name: Build (Android)
- if: matrix.platform == 'android'
- uses: bend-n/godot-actions/.github/actions/export-android@main
- with:
- android-keystore-base64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
- android-password: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
-
- push-itch:
- needs: [export]
- name: Push to itch.io
- runs-on: ubuntu-20.04
- steps:
- - name: Check for api key
- id: secret
- run: echo '::set-output name=secret::${{ secrets.BUTLER_CREDENTIALS }}'
-
- - name: Push
- if: steps.secret.outputs.secret
- uses: bend-n/godot-actions/.github/actions/itch-push@main
- with:
- api-key: ${{ secrets.BUTLER_CREDENTIALS }}
diff --git a/.gitignore b/.gitignore
index 5bbe921..5e86342 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
-.import/
+.godot/
logs/
*.sh
*.py
diff --git a/README.md b/README.md
index ee762ce..b531e3b 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# godot cli parser
-[![version](https://img.shields.io/badge/3.x-blue?logo=godot-engine&logoColor=white&label=godot&style=for-the-badge)](https://godotengine.org "Made with godot")
+[![version](https://img.shields.io/badge/4.x-blue?logo=godot-engine&logoColor=white&label=godot&style=for-the-badge)](https://godotengine.org "Made with godot")
[![package](https://img.shields.io/npm/v/@bendn/remap?label=version&style=for-the-badge)](https://www.npmjs.com/package/@bendn/remap)
<a href='https://ko-fi.com/bendn' title='Buy me a coffee' target='_blank'><img height='28' src='https://storage.ko-fi.com/cdn/brandasset/kofi_button_red.png' alt='Buy me a coffee'> </a>
@@ -9,6 +9,7 @@ A utility for parsing command line arguments for godot.
## Usage
```gdscript
+const RemapButton = preload("res://addons/remap/RemapButton.gd")
var label = RemapButton.new()
label.action = "ui_left"
label._name = "left"
diff --git a/Test.gd b/Test.gd
deleted file mode 100644
index 539ff35..0000000
--- a/Test.gd
+++ /dev/null
@@ -1,8 +0,0 @@
-extends Control
-
-
-func _ready():
- var label = RemapButton.new()
- label.action = "ui_left"
- label._name = "left"
- add_child(label)
diff --git a/Test.tscn b/Test.tscn
index a88303b..2390284 100644
--- a/Test.tscn
+++ b/Test.tscn
@@ -1,19 +1,34 @@
-[gd_scene load_steps=3 format=2]
+[gd_scene load_steps=3 format=3 uid="uid://bueg0v45altvi"]
-[ext_resource path="res://Test.gd" type="Script" id=3]
-[ext_resource path="res://addons/remap/ActionLabel.gd" type="Script" id=4]
+[ext_resource type="Script" path="res://addons/remap/RemapButton.gd" id="2_dj1bu"]
+[ext_resource type="Script" path="res://addons/remap/ActionLabel.gd" id="4"]
-[node name="Test" type="Control"]
+[node name="Test" type="CenterContainer"]
+anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
-script = ExtResource( 3 )
+grow_horizontal = 2
+grow_vertical = 2
-[node name="HBoxContainer" type="HBoxContainer" parent="."]
-margin_left = 5.0
-margin_top = 42.0
-margin_right = 316.0
-margin_bottom = 75.0
-script = ExtResource( 4 )
-_name = " lef"
+[node name="v" type="VBoxContainer" parent="."]
+layout_mode = 2
+offset_left = 576.0
+offset_top = 322.0
+offset_right = 576.0
+offset_bottom = 326.0
+
+[node name="actionlabel" type="HBoxContainer" parent="v"]
+layout_mode = 2
+script = ExtResource("4")
+_name = "left"
+action = "ui_left"
+continuous_updating = true
+
+[node name="remapbutton" type="HBoxContainer" parent="v"]
+layout_mode = 2
+offset_top = 4.0
+offset_bottom = 4.0
+script = ExtResource("2_dj1bu")
+_name = "left"
action = "ui_left"
-icon_size = Vector2( 10, 10 )
+continuous_updating = true
diff --git a/addons/remap/ActionIcons.gd b/addons/remap/ActionIcons.gd
deleted file mode 100644
index 8bd3e2d..0000000
--- a/addons/remap/ActionIcons.gd
+++ /dev/null
@@ -1,27 +0,0 @@
-extends HBoxContainer
-class_name ActionIcons
-
-export(String) var action: String
-export(Vector2) var size := Vector2(20, 20)
-export(bool) var override_font := true
-
-const font = preload("./PromptFont.tres")
-
-
-func _update():
- for i in get_children():
- i.queue_free()
- var act_list := InputMap.get_action_list(action)
- for e in act_list:
- var icon = IconMap.get_icon(e)
- if icon:
- var p: PanelContainer = PanelContainer.new()
- var i: Label = Label.new()
- if override_font:
- i.add_font_override("font", font)
- i.text = icon
- i.align = Label.ALIGN_CENTER
- i.valign = Label.VALIGN_CENTER
- p.add_child(i)
- add_child(p)
- i.rect_min_size = size
diff --git a/addons/remap/ActionLabel.gd b/addons/remap/ActionLabel.gd
index d4fd8a9..956357f 100644
--- a/addons/remap/ActionLabel.gd
+++ b/addons/remap/ActionLabel.gd
@@ -1,30 +1,55 @@
+## A action label. Displays the inputevents for a action, with a label.
+
extends HBoxContainer
-class_name ActionLabel
+@icon("./icons/action_label.svg")
+
+const IconMap := preload("./private/IconMap.gd")
+const ActionIcons := preload("./private/ActionIcons.gd")
+const SaveLoadUtils := preload("./private/SaveLoadUtils.gd")
+
+## The label text.
+@export var _name := ""
+
+## The action to follow.
+@export var action := ""
+
+## The minimum ActionIcon size.
+@export var icon_size := Vector2(20, 20)
-export(String) var _name: String
-export(String) var action: String
-export(Vector2) var icon_size := Vector2(20, 20)
-export(bool) var override_font := true
+## The font to use. Only change if your font has the [url=https://github.com/Shinmera/promptfont/blob/c27797b49dee560e3ea3eaa40e87f9a7f35e8913/glyphs.json]necessary glyphs[/url].
+@export var font: Font = preload("./PromptFont.ttf")
-var icons := ActionIcons.new()
+## Wether to update continuously. Usefull if you have RemapButtons on this action.
+@export var continuous_updating := false
+
+## The ActionIcons object to use. This is a required internal node.
+var icons: ActionIcons = null
+
+## The label to display the button text on. This is not a required internal node.
var name_label := Label.new()
+
+## The background for the label to display the button text on. This is not a required internal node.
var name_label_bg := PanelContainer.new()
func _ready() -> void:
- SaveLoadUtils.create_dir(SaveLoadUtils.dir)
- SaveLoadUtils.load2inputmap(action)
- add_child(name_label_bg)
- name_label_bg.add_child(name_label)
- name_label.text = _name
- name_label.rect_min_size = icon_size
- name_label.valign = Label.VALIGN_CENTER
- name_label.align = Label.ALIGN_CENTER
- var spacer := Control.new()
- spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
- add_child(spacer)
- add_child(icons)
- icons.action = action
- icons.size = icon_size
- icons.override_font = override_font
- icons._update()
+ set_process(continuous_updating)
+ DirAccess.make_dir_absolute(SaveLoadUtils.dir)
+ SaveLoadUtils.load_action_to_inputmap(action)
+ add_child(name_label_bg)
+ name_label_bg.add_child(name_label)
+ name_label.text = _name
+ name_label.custom_minimum_size = icon_size
+ name_label.vertical_alignment = ALIGNMENT_CENTER
+ name_label.horizontal_alignment = ALIGNMENT_CENTER
+ var spacer := Control.new()
+ spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ icons = ActionIcons.new(action, icon_size, font)
+ add_child(spacer)
+ add_child(icons)
+ if not continuous_updating:
+ icons.update()
+
+
+func _process(_delta: float) -> void:
+ icons.update() \ No newline at end of file
diff --git a/addons/remap/IconMap.gd b/addons/remap/IconMap.gd
deleted file mode 100644
index e8b8923..0000000
--- a/addons/remap/IconMap.gd
+++ /dev/null
@@ -1,120 +0,0 @@
-extends Reference
-class_name IconMap
-
-const key_map := {
- KEY_LEFT: "←",
- KEY_RIGHT: "→",
- KEY_UP: "↑",
- KEY_DOWN: "↓",
- KEY_ENTER: "␮",
- KEY_KP_ENTER: "␮",
- KEY_HOME: "␵",
- KEY_CONTROL: "␧",
- KEY_ALT: "␨",
- KEY_SHIFT: "␩",
- KEY_SUPER_L: "␪",
- KEY_SUPER_R: "␫",
- KEY_TAB: "␫",
- KEY_CAPSLOCK: "␬",
- KEY_BACKSPACE: "␭",
- KEY_ESCAPE: "␯",
- KEY_PRINT: "␰",
- KEY_SCROLLLOCK: "␱",
- KEY_PAUSE: "␲",
- KEY_NUMLOCK: "␳",
- KEY_DELETE: "␷",
- KEY_INSERT: "␴",
- KEY_PAGEUP: "␶",
- KEY_PAGEDOWN: "␹",
- KEY_SPACE: "␺",
- KEY_F1: "①",
- KEY_F2: "②",
- KEY_F3: "③",
- KEY_F4: "④",
- KEY_F5: "⑤",
- KEY_F6: "⑥",
- KEY_F7: "⑦",
- KEY_F8: "⑧",
- KEY_F9: "⑨",
- KEY_F10: "⑩",
- KEY_F11: "⑪",
- KEY_F12: "⑫",
- KEY_SEMICOLON: ";",
- KEY_QUOTELEFT: "`",
- KEY_COMMA: ",",
- KEY_PERIOD: ".",
- KEY_SLASH: "/",
- KEY_BACKSLASH: "\\",
- KEY_MINUS: "-",
- KEY_EQUAL: "=",
- KEY_BRACKETLEFT: "[",
- KEY_BRACKETRIGHT: "]",
- KEY_BRACELEFT: "{",
- KEY_BRACERIGHT: "}",
- KEY_APOSTROPHE: "'",
- KEY_MENU: "⇻",
- KEY_END: "␸"
-}
-
-# JOY_SONY_CIRCLE and JOY_XBOX_B are the same as JOY_DS_A
-# TODO: use a different map for each controller after finding out what kind it is
-const gamepad_button_map := {
- JOY_XBOX_X: "↤",
- JOY_XBOX_A: "↧",
- JOY_XBOX_B: "↦",
- JOY_XBOX_Y: "↥",
- JOY_L: "↘",
- JOY_R: "↙",
- JOY_L2: "↖",
- JOY_R2: "↗",
- JOY_DPAD_LEFT: "↞",
- JOY_DPAD_RIGHT: "↠",
- JOY_DPAD_UP: "↟",
- JOY_DPAD_DOWN: "↡",
-}
-
-const joystick_map := {
- -1:
- {
- JOY_ANALOG_LX: "↼",
- JOY_ANALOG_LY: "⇈",
- JOY_ANALOG_RX: "↽",
- JOY_ANALOG_RY: "↿",
- },
- 1:
- {
- JOY_ANALOG_LX: "⇀",
- JOY_ANALOG_LY: "⇂",
- JOY_ANALOG_RX: "⇁",
- JOY_ANALOG_RY: "⇃",
- }
-}
-
-const mouse_button_map := {
- BUTTON_LEFT: "⟵", BUTTON_RIGHT: "⟶", BUTTON_MIDDLE: "⟷", BUTTON_WHEEL_DOWN: "⟱", BUTTON_WHEEL_UP: "⟰"
-}
-
-
-static func get_icon(e: InputEvent) -> String:
- if e is InputEventKey:
- if key_map.has(e.scancode):
- return key_map[e.scancode]
- elif OS.get_scancode_string(e.scancode):
- return OS.get_scancode_string(e.scancode)
- elif OS.keyboard_get_scancode_from_physical(e.physical_scancode):
- var scancode = OS.keyboard_get_scancode_from_physical(e.physical_scancode)
- if key_map.has(scancode):
- return key_map[scancode]
- elif OS.get_scancode_string(scancode):
- return OS.get_scancode_string(scancode)
-
- elif e is InputEventJoypadButton:
- if gamepad_button_map.has(e.button_index):
- return gamepad_button_map[e.button_index]
- elif e is InputEventJoypadMotion:
- if joystick_map[int(e.axis_value)].has(e.axis):
- return joystick_map[int(e.axis_value)][e.axis]
- elif e is InputEventMouseButton:
- if mouse_button_map.has(e.button_index):
- return mouse_button_map[e.button_index]
- return "❓"
diff --git a/addons/remap/KeyPromptLabel.gd b/addons/remap/KeyPromptLabel.gd
deleted file mode 100644
index 47aa19f..0000000
--- a/addons/remap/KeyPromptLabel.gd
+++ /dev/null
@@ -1,13 +0,0 @@
-extends Label
-
-var selected: InputEvent
-
-
-func _input(event: InputEvent) -> void:
- if !RemapUtilities.is_valid_action(event) or event is InputEventMouseButton:
- return
- if event is InputEventJoypadMotion:
- event.axis_value = sign(event.axis_value)
- selected = event
- text = IconMap.get_icon(event)
- $"%ok".disabled = false
diff --git a/addons/remap/KeySelector.gd b/addons/remap/KeySelector.gd
deleted file mode 100644
index 13234c2..0000000
--- a/addons/remap/KeySelector.gd
+++ /dev/null
@@ -1,20 +0,0 @@
-extends Node
-class_name KeySelector
-
-signal confirmed(event)
-signal cancelled
-
-
-func _ready():
- $"%ok".connect("pressed", self, "confirmed")
- $"%cancel".connect("pressed", self, "cancelled")
-
-
-func confirmed():
- emit_signal("confirmed", $"%KeyPrompter".selected)
- queue_free()
-
-
-func cancelled():
- emit_signal("cancelled")
- queue_free()
diff --git a/addons/remap/KeySelector.tscn b/addons/remap/KeySelector.tscn
deleted file mode 100644
index 0dcf4fa..0000000
--- a/addons/remap/KeySelector.tscn
+++ /dev/null
@@ -1,60 +0,0 @@
-[gd_scene load_steps=5 format=2]
-
-[ext_resource path="res://addons/remap/KeyPromptLabel.gd" type="Script" id=2]
-[ext_resource path="res://addons/remap/PromptFont.ttf" type="DynamicFontData" id=3]
-[ext_resource path="res://addons/remap/KeySelector.gd" type="Script" id=4]
-
-[sub_resource type="DynamicFont" id=1]
-size = 40
-font_data = ExtResource( 3 )
-
-[node name="KeySelector" type="CanvasLayer"]
-script = ExtResource( 4 )
-
-[node name="ColorRect" type="ColorRect" parent="."]
-anchor_right = 1.0
-anchor_bottom = 1.0
-color = Color( 0.160156, 0.160156, 0.160156, 0.705882 )
-
-[node name="Center" type="CenterContainer" parent="ColorRect"]
-anchor_right = 1.0
-anchor_bottom = 1.0
-
-[node name="V" type="VBoxContainer" parent="ColorRect/Center"]
-margin_left = 35.0
-margin_top = 53.0
-margin_right = 285.0
-margin_bottom = 126.0
-
-[node name="KeyPrompter" type="Label" parent="ColorRect/Center/V"]
-unique_name_in_owner = true
-margin_right = 250.0
-margin_bottom = 49.0
-rect_min_size = Vector2( 125, 0 )
-custom_fonts/font = SubResource( 1 )
-text = "press a key"
-align = 1
-script = ExtResource( 2 )
-
-[node name="H" type="HBoxContainer" parent="ColorRect/Center/V"]
-margin_top = 53.0
-margin_right = 250.0
-margin_bottom = 73.0
-
-[node name="ok" type="Button" parent="ColorRect/Center/V/H"]
-unique_name_in_owner = true
-margin_right = 123.0
-margin_bottom = 20.0
-rect_min_size = Vector2( 50, 0 )
-size_flags_horizontal = 3
-disabled = true
-text = "ok"
-
-[node name="cancel" type="Button" parent="ColorRect/Center/V/H"]
-unique_name_in_owner = true
-margin_left = 127.0
-margin_right = 250.0
-margin_bottom = 20.0
-rect_min_size = Vector2( 100, 0 )
-size_flags_horizontal = 3
-text = "cancel"
diff --git a/addons/remap/PromptFont.tres b/addons/remap/PromptFont.tres
deleted file mode 100644
index 181c50e..0000000
--- a/addons/remap/PromptFont.tres
+++ /dev/null
@@ -1,7 +0,0 @@
-[gd_resource type="DynamicFont" load_steps=2 format=2]
-
-[ext_resource path="res://addons/remap/PromptFont.ttf" type="DynamicFontData" id=1]
-
-[resource]
-size = 15
-font_data = ExtResource( 1 )
diff --git a/addons/remap/PromptFont.ttf.import b/addons/remap/PromptFont.ttf.import
new file mode 100644
index 0000000..dd70868
--- /dev/null
+++ b/addons/remap/PromptFont.ttf.import
@@ -0,0 +1,36 @@
+[remap]
+
+importer="font_data_dynamic"
+type="FontFile"
+uid="uid://ck4uai24afp11"
+path="res://.godot/imported/PromptFont.ttf-7aa5ceab00464524ed719deff6b514eb.fontdata"
+
+[deps]
+
+source_file="res://addons/remap/PromptFont.ttf"
+dest_files=["res://.godot/imported/PromptFont.ttf-7aa5ceab00464524ed719deff6b514eb.fontdata"]
+
+[params]
+
+Rendering=null
+antialiasing=1
+generate_mipmaps=false
+multichannel_signed_distance_field=false
+msdf_pixel_range=8
+msdf_size=48
+force_autohinter=false
+hinting=2
+subpixel_positioning=1
+oversampling=0.0
+Fallbacks=null
+fallbacks=[]
+Compress=null
+compress=true
+preload=[{
+"chars": [],
+"glyphs": [],
+"name": "New Configuration"
+}]
+language_support={}
+script_support={}
+opentype_features={}
diff --git a/addons/remap/README.md b/addons/remap/README.md
index 157dafa..c337743 100644
--- a/addons/remap/README.md
+++ b/addons/remap/README.md
@@ -5,6 +5,7 @@ A utility for parsing command line arguments for godot.
## Usage
```gdscript
+const RemapButton = preload("res://addons/remap/RemapButton.gd")
var label = RemapButton.new()
label.action = "ui_left"
label._name = "left"
diff --git a/addons/remap/RemapButton.gd b/addons/remap/RemapButton.gd
index c1310e8..190262a 100644
--- a/addons/remap/RemapButton.gd
+++ b/addons/remap/RemapButton.gd
@@ -1,69 +1,111 @@
+## Provides a button that allows remapping [InputEventAction]s.
+
extends HBoxContainer
-class_name RemapButton
+@icon("./icons/remap_button.svg")
+
+const ActionIcons := preload("./private/ActionIcons.gd")
+const RemapUtilities := preload("./private/RemapUtilities.gd")
+const SaveLoadUtils := preload("./private/SaveLoadUtils.gd")
+
+## Wether to have a clear button.
+@export var clear_button := true
+
+## The text for said clear button.
+@export var clear_text := "✗"
+
+## The text used to prompt the player to press a key.
+@export var prompt_text := "[press any key]"
+
+## The button text.
+@export var _name := ""
-# if this is overriden, the new scene must
-# - have a confirmed(action: InputEvent) signal
-# - have a cancelled() signal
-# - free itself when one of them is emitted
-export(PackedScene) var popup = preload("./KeySelector.tscn")
+## The action to follow.
+@export var action := ""
+## The minimum ActionIcon size.
+@export var icon_size := Vector2(20, 20)
-export(bool) var clear_button := true
-export(String) var clear_text := "✗"
-export(String) var _name: String
-export(String) var action: String
-export(Vector2) var icon_size := Vector2(20, 20)
-export(bool) var override_font := true
+## The font to use.
+@export var font: Font = preload("./PromptFont.ttf")
-var icons := ActionIcons.new()
+## Wether to update continuously. Usefull if you have multple RemapButtons following this action.
+@export var continuous_updating := false
+
+## The internal ActionIcons object. This is a required internal node.
+var icons: ActionIcons = null
+
+## The internal [Button] object. This is a required internal node.
var button := Button.new()
+
+## The clear button.
var clear: Button
func _ready() -> void:
- rect_min_size = icon_size
- SaveLoadUtils.create_dir(SaveLoadUtils.dir)
- SaveLoadUtils.load2inputmap(action)
- if clear_button:
- clear = Button.new()
- clear.text = clear_text
- clear.connect("pressed", self, "clear")
- clear.size_flags_vertical = SIZE_EXPAND_FILL
- clear.rect_min_size = icon_size
- add_child(clear)
- clear.visible = InputMap.get_action_list(action).size() > 0
- if override_font:
- clear.add_font_override("font", preload("./PromptFont.tres"))
- button.text = _name
- button.rect_min_size = icon_size
- button.size_flags_vertical = SIZE_EXPAND_FILL
- button.connect("pressed", self, "_pressed")
- var spacer := Control.new()
- spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
- icons.action = action
- icons.size = icon_size
- icons.override_font = override_font
- icons._update()
- add_child(button)
- add_child(spacer)
- add_child(icons)
-
-
+ set_process(continuous_updating)
+ set_process_input(false)
+ custom_minimum_size = icon_size
+ DirAccess.make_dir_absolute(SaveLoadUtils.dir)
+ SaveLoadUtils.load_action_to_inputmap(action)
+ if clear_button:
+ clear = Button.new()
+ clear.text = clear_text
+ clear.pressed.connect(clear_mappings)
+ clear.size_flags_vertical = SIZE_EXPAND_FILL
+ clear.custom_minimum_size = icon_size
+ add_child(clear)
+ clear.visible = InputMap.action_get_events(action).size() > 0
+ if font:
+ clear.add_theme_font_override("font", font)
+ button.text = _name
+ button.custom_minimum_size = icon_size
+ button.size_flags_vertical = SIZE_EXPAND_FILL
+ button.pressed.connect(_pressed)
+ var spacer := Control.new()
+ spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
+ icons = ActionIcons.new(action, icon_size, font)
+ add_child(button)
+ add_child(spacer)
+ add_child(icons)
+ if not continuous_updating:
+ icons.update()
func _pressed():
- var selector = popup.instance()
- add_child(selector)
- selector.connect("confirmed", self, "_on_key_selected")
+ button.text = prompt_text
+ set_process_input(true)
+func _input(event: InputEvent) -> void:
+ if (
+ not event.is_pressed()
+ or event in [InputEventMouseMotion, InputEventScreenDrag]
+ or (
+ (event is InputEventJoypadMotion or event is InputEventJoypadButton) and
+ Input.get_joy_name(event.device) == "HTIX5288:00 0911:5288 Touchpad" # work around https://github.com/godotengine/godot/issues/69153
+ )
+ ):
+ return
-func _on_key_selected(event: InputEvent):
- RemapUtilities.add_action(action, event)
- icons._update()
- SaveLoadUtils.inputmap2file(action)
- clear.show()
+ if event is InputEventKey and event.physical_keycode == KEY_ESCAPE or not RemapUtilities.is_valid_action(event):
+ get_viewport().set_input_as_handled()
+ button.text = _name
+ set_process_input(false)
+ return
+ if event is InputEventJoypadMotion:
+ event.axis_value = sign(event.axis_value)
+ get_viewport().set_input_as_handled()
+ RemapUtilities.add_action(action, event)
+ if not continuous_updating:
+ icons.update()
+ SaveLoadUtils.action_to_file(action)
+ clear.show()
+ button.text = _name
+ set_process_input(false)
+## Clears the mappings for this action.
+func clear_mappings():
+ RemapUtilities.clear_mappings(action)
+ icons.update()
+ SaveLoadUtils.action_to_file(action)
+ clear.hide()
-func clear():
- RemapUtilities.clear_mappings(action)
- icons._update()
- SaveLoadUtils.inputmap2file(action)
- clear.hide()
+func _process(_delta: float) -> void:
+ icons.update()
diff --git a/addons/remap/RemapUtilities.gd b/addons/remap/RemapUtilities.gd
deleted file mode 100644
index bacadbf..0000000
--- a/addons/remap/RemapUtilities.gd
+++ /dev/null
@@ -1,19 +0,0 @@
-extends Reference
-class_name RemapUtilities
-
-const invalid_actions = [InputEventMouseMotion, InputEventScreenDrag, InputEventScreenTouch, InputEventMIDI]
-
-
-static func clear_mappings(action: String) -> void:
- InputMap.action_erase_events(action)
-
-
-static func add_action(action: String, event: InputEvent) -> void:
- InputMap.action_add_event(action, event)
-
-
-static func is_valid_action(e: InputEvent) -> bool:
- for i in invalid_actions:
- if e is i:
- return false
- return true
diff --git a/addons/remap/SaveLoadUtils.gd b/addons/remap/SaveLoadUtils.gd
deleted file mode 100644
index fb92310..0000000
--- a/addons/remap/SaveLoadUtils.gd
+++ /dev/null
@@ -1,55 +0,0 @@
-#warning-ignore-all: return_value_discarded
-extends Node
-class_name SaveLoadUtils
-
-const dir := "user://__inputs__"
-const path_template := "user://__inputs__/%s.res"
-
-
-static func save(path: String, data: Dictionary) -> void:
- var file := File.new()
- file.open(path, File.WRITE)
- file.store_string(var2str(data))
- file.close()
-
-
-static func load_file(path: String) -> Dictionary:
- var file := File.new()
- if file.file_exists(path):
- file.open(path, File.READ)
- var text := file.get_as_text()
- var dict := {}
- if text:
- dict = str2var(text)
- file.close()
- return dict
- save(path, {}) # create file if it doesn't exist
- return {}
-
-
-# mkdir -p
-static func create_dir(path: String) -> int:
- var dir := Directory.new()
- return dir.make_dir_recursive(path)
-
-
-static func inputmap2file(action: String) -> void:
- var list := InputMap.get_action_list(action)
- var data := {actions = list}
- save(path_template % action, data)
-
-
-static func load2inputmap(action: String) -> void:
- var data := load_file(path_template % action)
- if typeof(data) == TYPE_DICTIONARY and typeof(data.get("actions", false)) == TYPE_ARRAY:
- for e in data.actions: # validate
- if e is InputEvent and RemapUtilities.is_valid_action(e):
- continue
- push_error("Invalid action: %s" % e)
- inputmap2file(action) # reset if invalid
- return
- RemapUtilities.clear_mappings(action)
- for e in data.actions:
- RemapUtilities.add_action(action, e)
- return
- inputmap2file(action)
diff --git a/addons/remap/icons/action_icon.svg b/addons/remap/icons/action_icon.svg
new file mode 100644
index 0000000..467602e
--- /dev/null
+++ b/addons/remap/icons/action_icon.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 2a6 6 0 0 0-6 6 6 6 0 0 0 6 6 6 6 0 0 0 6-6 6 6 0 0 0-6-6Zm0 2a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4Z" fill="#8eef97"/><path d="M10.008 9.939H8.974l-.268-.784H7.269L7 9.94H5.992l1.433-3.878h1.15zM8.464 8.444l-.477-1.39-.476 1.39Z" aria-label="A" style="font-weight:700;font-size:5.33333px;line-height:1.25;font-family:Verdana;-inkscape-font-specification:'Verdana Bold';letter-spacing:2px;fill:#8eef97;stroke:#8eef97;stroke-width:.2"/></svg> \ No newline at end of file
diff --git a/addons/remap/icons/action_icon.svg.import b/addons/remap/icons/action_icon.svg.import
new file mode 100644
index 0000000..7b93f6c
--- /dev/null
+++ b/addons/remap/icons/action_icon.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://csndvekc7ypb2"
+path="res://.godot/imported/action_icon.svg-d3a92058e49a58847b5682749062f7e6.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/remap/icons/action_icon.svg"
+dest_files=["res://.godot/imported/action_icon.svg-d3a92058e49a58847b5682749062f7e6.ctex"]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/bptc_ldr=0
+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=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/remap/icons/action_icons.svg b/addons/remap/icons/action_icons.svg
new file mode 100644
index 0000000..712cf1e
--- /dev/null
+++ b/addons/remap/icons/action_icons.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M8 1.444a3.193 3.193 0 0 0-3.193 3.193A3.193 3.193 0 0 0 8 7.83a3.193 3.193 0 0 0 3.193-3.193A3.193 3.193 0 0 0 8 1.444Zm0 1.064a2.129 2.129 0 0 1 2.129 2.129A2.129 2.129 0 0 1 8 6.766a2.129 2.129 0 0 1-2.129-2.129A2.129 2.129 0 0 1 8 2.508Z" fill="#8eef97"/><path d="M10.008 9.939H8.974l-.268-.784H7.269L7 9.94H5.992l1.433-3.878h1.15zM8.464 8.444l-.477-1.39-.476 1.39Z" aria-label="A" style="font-weight:700;font-size:5.33333px;line-height:1.25;font-family:Verdana;-inkscape-font-specification:'Verdana Bold';letter-spacing:2px;fill:#8eef97;stroke:#8eef97;stroke-width:.2" transform="translate(3.743 .38) scale(.53217)"/><path d="M1.325 1.444a3.193 3.193 0 0 0-3.193 3.193A3.193 3.193 0 0 0 1.325 7.83a3.193 3.193 0 0 0 3.193-3.193 3.193 3.193 0 0 0-3.193-3.193Zm0 1.064a2.129 2.129 0 0 1 2.129 2.129 2.129 2.129 0 0 1-2.129 2.129A2.129 2.129 0 0 1-.804 4.637a2.129 2.129 0 0 1 2.13-2.129Z" fill="#8eef97"/><path d="M10.008 9.939H8.974l-.268-.784H7.269L7 9.94H5.992l1.433-3.878h1.15zM8.464 8.444l-.477-1.39-.476 1.39Z" aria-label="A" style="font-weight:700;font-size:5.33333px;line-height:1.25;font-family:Verdana;-inkscape-font-specification:'Verdana Bold';letter-spacing:2px;fill:#8eef97;stroke:#8eef97;stroke-width:.2" transform="translate(-2.932 .38) scale(.53217)"/><path d="M14.675 1.444a3.193 3.193 0 0 0-3.193 3.193 3.193 3.193 0 0 0 3.193 3.193 3.193 3.193 0 0 0 3.193-3.193 3.193 3.193 0 0 0-3.193-3.193Zm0 1.064a2.129 2.129 0 0 1 2.129 2.129 2.129 2.129 0 0 1-2.13 2.129 2.129 2.129 0 0 1-2.128-2.129 2.129 2.129 0 0 1 2.129-2.129Z" fill="#8eef97"/><path d="M10.008 9.939H8.974l-.268-.784H7.269L7 9.94H5.992l1.433-3.878h1.15zM8.464 8.444l-.477-1.39-.476 1.39Z" aria-label="A" style="font-weight:700;font-size:5.33333px;line-height:1.25;font-family:Verdana;-inkscape-font-specification:'Verdana Bold';letter-spacing:2px;fill:#8eef97;stroke:#8eef97;stroke-width:.2" transform="translate(10.418 .38) scale(.53217)"/><path d="M8 8.17a3.193 3.193 0 0 0-3.193 3.193A3.193 3.193 0 0 0 8 14.556a3.193 3.193 0 0 0 3.193-3.193A3.193 3.193 0 0 0 8 8.17Zm0 1.064a2.129 2.129 0 0 1 2.129 2.129A2.129 2.129 0 0 1 8 13.492a2.129 2.129 0 0 1-2.129-2.129A2.129 2.129 0 0 1 8 9.234Z" fill="#8eef97"/><path d="M10.008 9.939H8.974l-.268-.784H7.269L7 9.94H5.992l1.433-3.878h1.15zM8.464 8.444l-.477-1.39-.476 1.39Z" aria-label="A" style="font-weight:700;font-size:5.33333px;line-height:1.25;font-family:Verdana;-inkscape-font-specification:'Verdana Bold';letter-spacing:2px;fill:#8eef97;stroke:#8eef97;stroke-width:.2" transform="translate(3.743 7.106) scale(.53217)"/><path d="M1.325 8.17a3.193 3.193 0 0 0-3.193 3.193 3.193 3.193 0 0 0 3.193 3.193 3.193 3.193 0 0 0 3.193-3.193A3.193 3.193 0 0 0 1.325 8.17Zm0 1.064a2.129 2.129 0 0 1 2.129 2.129 2.129 2.129 0 0 1-2.129 2.129 2.129 2.129 0 0 1-2.129-2.129 2.129 2.129 0 0 1 2.13-2.129Z" fill="#8eef97"/><path d="M10.008 9.939H8.974l-.268-.784H7.269L7 9.94H5.992l1.433-3.878h1.15zM8.464 8.444l-.477-1.39-.476 1.39Z" aria-label="A" style="font-weight:700;font-size:5.33333px;line-height:1.25;font-family:Verdana;-inkscape-font-specification:'Verdana Bold';letter-spacing:2px;fill:#8eef97;stroke:#8eef97;stroke-width:.2" transform="translate(-2.932 7.106) scale(.53217)"/><path d="M14.675 8.17a3.193 3.193 0 0 0-3.193 3.193 3.193 3.193 0 0 0 3.193 3.193 3.193 3.193 0 0 0 3.193-3.193 3.193 3.193 0 0 0-3.193-3.193Zm0 1.064a2.129 2.129 0 0 1 2.129 2.129 2.129 2.129 0 0 1-2.13 2.129 2.129 2.129 0 0 1-2.128-2.129 2.129 2.129 0 0 1 2.129-2.129Z" fill="#8eef97"/><path d="M10.008 9.939H8.974l-.268-.784H7.269L7 9.94H5.992l1.433-3.878h1.15zM8.464 8.444l-.477-1.39-.476 1.39Z" aria-label="A" style="font-weight:700;font-size:5.33333px;line-height:1.25;font-family:Verdana;-inkscape-font-specification:'Verdana Bold';letter-spacing:2px;fill:#8eef97;stroke:#8eef97;stroke-width:.2" transform="translate(10.418 7.106) scale(.53217)"/></svg> \ No newline at end of file
diff --git a/addons/remap/icons/action_icons.svg.import b/addons/remap/icons/action_icons.svg.import
new file mode 100644
index 0000000..ad11b85
--- /dev/null
+++ b/addons/remap/icons/action_icons.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://c2gbd0p5byx4q"
+path="res://.godot/imported/action_icons.svg-3932be3a3b1cd8f58e4071f6b1c0b727.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/remap/icons/action_icons.svg"
+dest_files=["res://.godot/imported/action_icons.svg-3932be3a3b1cd8f58e4071f6b1c0b727.ctex"]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/bptc_ldr=0
+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=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/remap/icons/action_label.svg b/addons/remap/icons/action_label.svg
new file mode 100644
index 0000000..5de9a33
--- /dev/null
+++ b/addons/remap/icons/action_label.svg
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="16"
+ viewBox="0 0 16 16"
+ width="16"
+ version="1.1"
+ id="svg4"
+ sodipodi:docname="action_label.svg"
+ inkscape:version="1.2.1 (9c6d41e410, 2022-07-14, custom)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ id="namedview6"
+ pagecolor="#505050"
+ bordercolor="#ffffff"
+ borderopacity="1"
+ inkscape:showpageshadow="0"
+ inkscape:pageopacity="0"
+ inkscape:pagecheckerboard="1"
+ inkscape:deskcolor="#505050"
+ showgrid="false"
+ inkscape:zoom="9.4933989"
+ inkscape:cx="-18.907875"
+ inkscape:cy="30.758214"
+ inkscape:window-width="1920"
+ inkscape:window-height="1025"
+ inkscape:window-x="0"
+ inkscape:window-y="55"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg4" />
+ <path
+ id="path2"
+ d="M 6 3 A 1.0001 1.0001 0 0 0 5.2929688 3.2929688 L 1.2929688 7.2929688 A 1.0001 1.0001 0 0 0 1.2929688 8.7070312 L 5.2929688 12.707031 A 1.0001 1.0001 0 0 0 6 13 L 14 13 A 1.0001 1.0001 0 0 0 15 12 L 15 4 A 1.0001 1.0001 0 0 0 14 3 L 6 3 z M 10.902344 5.0742188 A 2.9254353 2.9254353 0 0 1 13.826172 8 A 2.9254353 2.9254353 0 0 1 10.902344 10.925781 A 2.9254353 2.9254353 0 0 1 7.9765625 8 A 2.9254353 2.9254353 0 0 1 10.902344 5.0742188 z M 5 7 A 1 1 0 0 1 6 8 A 1 1 0 0 1 5 9 A 1 1 0 0 1 4 8 A 1 1 0 0 1 5 7 z "
+ style="fill:#8eef97;fill-opacity:1" />
+ <g
+ id="g443-3"
+ transform="matrix(0.37008351,0,0,0.36026328,3.3156431,3.6702108)">
+ <path
+ d="m 20.500831,6.0177285 a 6,6 0 0 0 -6,6.0000005 6,6 0 0 0 6,5.999999 6,6 0 0 0 6,-5.999999 6,6 0 0 0 -6,-6.0000005 z m 0,2 a 4,4 0 0 1 4,4.0000005 4,4 0 0 1 -4,3.999999 4,4 0 0 1 -4,-3.999999 4,4 0 0 1 4,-4.0000005 z"
+ fill="#8eef97"
+ id="path287-5" />
+ <path
+ d="m 22.508831,13.956729 h -1.034 l -0.268,-0.784001 h -1.437 l -0.269,0.785 h -1.008 l 1.433,-3.877999 h 1.15 z m -1.544,-1.495 -0.477,-1.390001 -0.476,1.390001 z"
+ aria-label="A"
+ style="font-weight:700;font-size:5.33333px;line-height:1.25;font-family:Verdana;-inkscape-font-specification:'Verdana Bold';letter-spacing:2px;fill:#8eef97;stroke:#8eef97;stroke-width:0.2"
+ id="path289-6" />
+ </g>
+</svg>
diff --git a/addons/remap/icons/action_label.svg.import b/addons/remap/icons/action_label.svg.import
new file mode 100644
index 0000000..743550b
--- /dev/null
+++ b/addons/remap/icons/action_label.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dxilcw87tjnni"
+path="res://.godot/imported/action_label.svg-95462f1979e6b228dc8e5c5c198a8a1a.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/remap/icons/action_label.svg"
+dest_files=["res://.godot/imported/action_label.svg-95462f1979e6b228dc8e5c5c198a8a1a.ctex"]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/bptc_ldr=0
+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=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/remap/icons/remap_button.svg b/addons/remap/icons/remap_button.svg
new file mode 100644
index 0000000..9034fce
--- /dev/null
+++ b/addons/remap/icons/remap_button.svg
@@ -0,0 +1 @@
+<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M5.5 9.43c-.831 0-1.5.668-1.5 1.5v1.5H2v2h12v-2h-2v-1.5c0-.832-.669-1.5-1.5-1.5zM8 1.57a3.503 3.503 0 0 0-3.503 3.504A3.503 3.503 0 0 0 8 8.578a3.503 3.503 0 0 0 3.503-3.504A3.503 3.503 0 0 0 8 1.571zm0 1.17a2.336 2.336 0 0 1 2.336 2.335A2.336 2.336 0 0 1 8 7.41a2.336 2.336 0 0 1-2.336-2.336A2.336 2.336 0 0 1 8 2.74z" fill="#8eef97"/><path d="M10.008 4.255H8.974l-.268-.784H7.269L7 4.256H5.992L7.425.378h1.15zM8.464 2.76l-.477-1.39-.476 1.39z" aria-label="A" style="font-weight:700;font-size:5.33333px;line-height:1.25;font-family:Verdana;-inkscape-font-specification:'Verdana Bold';letter-spacing:2px;fill:#8eef97;stroke:#8eef97;stroke-width:.2" transform="translate(3.329 3.722) scale(.5839)"/></svg> \ No newline at end of file
diff --git a/addons/remap/icons/remap_button.svg.import b/addons/remap/icons/remap_button.svg.import
new file mode 100644
index 0000000..8defbe5
--- /dev/null
+++ b/addons/remap/icons/remap_button.svg.import
@@ -0,0 +1,37 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://r5t2tpw6of2r"
+path="res://.godot/imported/remap_button.svg-8932cf4f00007d2b2e1f55aeb0105c47.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/remap/icons/remap_button.svg"
+dest_files=["res://.godot/imported/remap_button.svg-8932cf4f00007d2b2e1f55aeb0105c47.ctex"]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/bptc_ldr=0
+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=1.0
+editor/scale_with_editor_scale=false
+editor/convert_colors_with_editor_theme=false
diff --git a/addons/remap/package.json b/addons/remap/package.json
index 09be8a7..08a8c93 100644
--- a/addons/remap/package.json
+++ b/addons/remap/package.json
@@ -1,6 +1,6 @@
{
"name": "@bendn/remap",
- "version": "1.2.2",
+ "version": "4.0.0",
"description": "godot input remapping",
"main": "InteractiveActionLabel.gd",
"scripts": {
diff --git a/addons/remap/private/ActionIcon.gd b/addons/remap/private/ActionIcon.gd
new file mode 100644
index 0000000..3f923e7
--- /dev/null
+++ b/addons/remap/private/ActionIcon.gd
@@ -0,0 +1,16 @@
+## A ActionIcon used by ActionIcons.
+
+extends PanelContainer
+@icon("../icons/action_icon.svg")
+
+## The inner label.
+var label := Label.new()
+
+func _init(text: String, min_size: Vector2, font: Font) -> void:
+ label.horizontal_alignment = BoxContainer.ALIGNMENT_CENTER
+ label.vertical_alignment = BoxContainer.ALIGNMENT_CENTER
+ add_child(label)
+ label.custom_minimum_size = min_size
+ label.text = text
+ if font:
+ label.add_theme_font_override("font", font)
diff --git a/addons/remap/private/ActionIcons.gd b/addons/remap/private/ActionIcons.gd
new file mode 100644
index 0000000..ea94cea
--- /dev/null
+++ b/addons/remap/private/ActionIcons.gd
@@ -0,0 +1,29 @@
+## Displays multiple ActionIcons.
+
+extends HBoxContainer
+@icon("../icons/action_icons.svg")
+
+const ActionIcon := preload("./ActionIcon.gd")
+const IconMap := preload("./IconMap.gd")
+
+## The action to follow.
+var action: String
+
+## The minimum icon size.
+var min_size := Vector2(20, 20)
+
+## The font to use
+var font: Font
+
+func _init(p_action: String, p_min_size: Vector2, p_font: Font) -> void:
+ action = p_action
+ min_size = p_min_size
+ font = p_font
+
+## Updates the icons to the latest inputs.
+func update():
+ for i in get_children():
+ i.queue_free()
+ for e in InputMap.action_get_events(action):
+ var icon := IconMap.get_icon(e)
+ add_child(ActionIcon.new(icon, min_size, font))
diff --git a/addons/remap/private/IconMap.gd b/addons/remap/private/IconMap.gd
new file mode 100644
index 0000000..27e8922
--- /dev/null
+++ b/addons/remap/private/IconMap.gd
@@ -0,0 +1,195 @@
+## Maps inputevents to a [url=https://shinmera.github.io/promptfont/]prompt font[/url] icon.
+
+extends RefCounted
+
+## The map for keys.
+const KEY_MAP := {
+ KEY_LEFT: "←",
+ KEY_RIGHT: "→",
+ KEY_UP: "↑",
+ KEY_DOWN: "↓",
+ KEY_ENTER: "␮",
+ KEY_KP_ENTER: "␮",
+ KEY_HOME: "␵",
+ KEY_CTRL: "␧",
+ KEY_ALT: "␨",
+ KEY_SHIFT: "␩",
+ KEY_SUPER_L: "␪",
+ KEY_SUPER_R: "␫",
+ KEY_TAB: "␫",
+ KEY_CAPSLOCK: "␬",
+ KEY_BACKSPACE: "␭",
+ KEY_ESCAPE: "␯",
+ KEY_PRINT: "␰",
+ KEY_SCROLLLOCK: "␱",
+ KEY_PAUSE: "␲",
+ KEY_NUMLOCK: "␳",
+ KEY_DELETE: "␷",
+ KEY_INSERT: "␴",
+ KEY_PAGEUP: "␶",
+ KEY_PAGEDOWN: "␹",
+ KEY_SPACE: "␺",
+ KEY_F1: "①",
+ KEY_F2: "②",
+ KEY_F3: "③",
+ KEY_F4: "④",
+ KEY_F5: "⑤",
+ KEY_F6: "⑥",
+ KEY_F7: "⑦",
+ KEY_F8: "⑧",
+ KEY_F9: "⑨",
+ KEY_F10: "⑩",
+ KEY_F11: "⑪",
+ KEY_F12: "⑫",
+ KEY_SEMICOLON: ";",
+ KEY_QUOTELEFT: "`",
+ KEY_COMMA: ",",
+ KEY_PERIOD: ".",
+ KEY_SLASH: "/",
+ KEY_BACKSLASH: "\\",
+ KEY_MINUS: "-",
+ KEY_EQUAL: "=",
+ KEY_BRACKETLEFT: "[",
+ KEY_BRACKETRIGHT: "]",
+ KEY_BRACELEFT: "{",
+ KEY_BRACERIGHT: "}",
+ KEY_APOSTROPHE: "'",
+ KEY_MENU: "⇻",
+ KEY_END: "␸"
+}
+
+## Pad enum. Used to decide which button set to use.
+enum PADS { XBOX, PLAYSTATION, NINTENDO, GENERIC }
+
+## Generic joypad button mappings.
+const JOYPAD_BUTTON_MAP := {
+ JOY_BUTTON_LEFT_SHOULDER: "↘",
+ JOY_BUTTON_RIGHT_SHOULDER: "↙",
+ JOY_BUTTON_DPAD_LEFT: "↞",
+ JOY_BUTTON_DPAD_RIGHT: "↠",
+ JOY_BUTTON_DPAD_UP: "↟",
+ JOY_BUTTON_DPAD_DOWN: "↡",
+}
+
+## Xbox button map.
+const XBOX_BUTTON_MAP := {
+ JOY_BUTTON_A: "↧",
+ JOY_BUTTON_B: "↦",
+ JOY_BUTTON_X: "↤",
+ JOY_BUTTON_Y: "↥",
+ JOY_BUTTON_BACK: "⇺",
+ JOY_BUTTON_GUIDE: "",
+ JOY_BUTTON_START: "⇻",
+}
+
+## PS button map.
+const PLAYSTATION_BUTTON_MAP := {
+ JOY_BUTTON_A: "⇣",
+ JOY_BUTTON_B: "⇢",
+ JOY_BUTTON_X: "⇠",
+ JOY_BUTTON_Y: "⇡",
+ JOY_BUTTON_BACK: "⇦",
+ JOY_BUTTON_GUIDE: "",
+ JOY_BUTTON_START: "⇨",
+}
+
+## Nintendo switch button map.
+const NINTENDO_BUTTON_MAP := {
+ JOY_BUTTON_A: "↥",
+ JOY_BUTTON_B: "↧",
+ JOY_BUTTON_X: "↥",
+ JOY_BUTTON_Y: "↤",
+ JOY_BUTTON_BACK: "⇽",
+ JOY_BUTTON_GUIDE: "❓", # there is no joy_button_guide on switch
+ JOY_BUTTON_START: "⇾",
+}
+
+## Joystick axis map.
+## Index by joystick index (left or right) first.
+const JOYSTICK_MAP := {
+ -1:
+ {
+ JOY_AXIS_LEFT_X: "↼",
+ JOY_AXIS_LEFT_Y: "⇈",
+ JOY_AXIS_RIGHT_X: "↽",
+ JOY_AXIS_RIGHT_Y: "↿",
+ },
+ 1:
+ {
+ JOY_AXIS_LEFT_X: "⇀",
+ JOY_AXIS_LEFT_Y: "⇂",
+ JOY_AXIS_RIGHT_X: "⇁",
+ JOY_AXIS_RIGHT_Y: "⇃",
+ }
+}
+
+## Joypad trigger map.
+const trigger_map := {JOY_AXIS_TRIGGER_LEFT: "↲", JOY_AXIS_TRIGGER_RIGHT: "↳"}
+
+## Mouse button map.
+const mouse_button_map := {
+ MOUSE_BUTTON_LEFT: "⟵",
+ MOUSE_BUTTON_RIGHT: "⟶",
+ MOUSE_BUTTON_MIDDLE: "⟷",
+ MOUSE_BUTTON_WHEEL_DOWN: "⟱",
+ MOUSE_BUTTON_WHEEL_UP: "⟰"
+}
+
+
+## Tries to find the icon for a physical key [param scancode].
+static func check_scn(scancode: int, __r := false) -> String:
+ if KEY_MAP.has(scancode):
+ return KEY_MAP[scancode]
+ var kcs := OS.get_keycode_string(scancode)
+ if kcs:
+ return kcs
+ if !__r:
+ var fall := check_scn(DisplayServer.keyboard_get_keycode_from_physical(scancode), true)
+ if fall:
+ return fall
+ return ""
+
+
+## Partially lifted from [url=https://github.com/nathanhoad/godot_input_helper/blob/a6140576d3dc48e0296615c4f486456b331f2332/addons/input_helper/input_helper.gd#L48-L60]nathanhoad/godot_input_helper[/url].
+## Naively tries to get the device, returning a PAD enum.
+static func get_device(raw_name: String) -> int:
+ raw_name = raw_name.to_lower()
+ match raw_name:
+ "xinput gamepad", "xbox series controller", "xbox 360 controller":
+ return PADS.XBOX
+ "sony dualsense", "ps5 controller", "ps4 controller":
+ return PADS.PLAYSTATION
+ "switch":
+ return PADS.NINTENDO
+ _:
+ return PADS.GENERIC
+
+
+## Gets the icon for a input[param e]vent. Returns [code]?[/code] on failure.
+static func get_icon(e: InputEvent) -> String:
+ if e is InputEventKey:
+ var res := check_scn(e.physical_keycode)
+ if res:
+ return res
+ elif e is InputEventJoypadButton:
+ if e.button_index < 7:
+ match get_device(Input.get_joy_name(e.device)):
+ PADS.XBOX:
+ return XBOX_BUTTON_MAP[e.button_index]
+ PADS.PLAYSTATION:
+ return PLAYSTATION_BUTTON_MAP[e.button_index]
+ PADS.NINTENDO:
+ return NINTENDO_BUTTON_MAP[e.button_index]
+ PADS.GENERIC:
+ return XBOX_BUTTON_MAP[e.button_index] # fallback to xbox
+ elif JOYPAD_BUTTON_MAP.has(e.button_index):
+ return JOYPAD_BUTTON_MAP[e.button_index]
+ elif e is InputEventJoypadMotion:
+ if JOYSTICK_MAP[int(e.axis_value)].has(e.axis):
+ return JOYSTICK_MAP[int(e.axis_value)][e.axis]
+ if trigger_map.has(e.axis):
+ return trigger_map[e.axis]
+ elif e is InputEventMouseButton:
+ if mouse_button_map.has(e.button_index):
+ return mouse_button_map[e.button_index]
+ return "❓"
diff --git a/addons/remap/private/RemapUtilities.gd b/addons/remap/private/RemapUtilities.gd
new file mode 100644
index 0000000..81e77e9
--- /dev/null
+++ b/addons/remap/private/RemapUtilities.gd
@@ -0,0 +1,22 @@
+## Utilities for remapping.
+
+extends RefCounted
+
+
+## Clear the mappings for a action.
+static func clear_mappings(action: String) -> void:
+ InputMap.action_erase_events(action)
+
+
+## Adds a input to a action.
+static func add_action(action: String, event: InputEvent) -> void:
+ InputMap.action_add_event(action, event)
+
+
+## Checks for action validity.
+static func is_valid_action(e: InputEvent) -> bool:
+ var good_events := [InputEventKey, InputEventMouseButton, InputEventJoypadButton, InputEventJoypadMotion]
+ for g_e in good_events:
+ if e is g_e:
+ return true
+ return false
diff --git a/addons/remap/private/SaveLoadUtils.gd b/addons/remap/private/SaveLoadUtils.gd
new file mode 100644
index 0000000..541a2bc
--- /dev/null
+++ b/addons/remap/private/SaveLoadUtils.gd
@@ -0,0 +1,54 @@
+## Saveing and loading utilities.
+## Provides functions to save [InputEventAction]s.
+
+extends RefCounted
+const RemapUtilities := preload("./RemapUtilities.gd")
+
+## The directory to put the inputs in.
+const dir := "user://__inputs__/"
+
+## The complete path template.
+const path_template := dir + "%s.res"
+
+
+## Saves a basic dictionary to a path.
+static func save(path: String, data: Dictionary) -> void:
+ var file := FileAccess.open(path, FileAccess.WRITE)
+ file.store_string(var_to_str(data))
+
+
+## Loads a basic dictionary out of a file.
+static func load_file(path: String) -> Dictionary:
+ if FileAccess.file_exists(path):
+ var file := FileAccess.open(path, FileAccess.READ)
+ var text := file.get_as_text()
+ var dict := {}
+ if text:
+ dict = str_to_var(text)
+ return dict
+ save(path, {}) # create file if it doesn't exist
+ return {}
+
+
+## Saves a [InputEventAction] to a file.
+static func action_to_file(action: String) -> void:
+ var list := InputMap.action_get_events(action)
+ var data := {actions = list}
+ save(path_template % action, data)
+
+
+## Loads a [InputEventAction] from a file into the [InputMap].
+static func load_action_to_inputmap(action: String) -> void:
+ var data := load_file(path_template % action)
+ if typeof(data) == TYPE_DICTIONARY and typeof(data.get("actions", false)) == TYPE_ARRAY:
+ for e in data.actions: # validate
+ if RemapUtilities.is_valid_action(e):
+ continue
+ push_error("Invalid action: %s" % e)
+ action_to_file(action) # reset if invalid
+ return
+ RemapUtilities.clear_mappings(action)
+ for e in data.actions:
+ RemapUtilities.add_action(action, e)
+ return
+ action_to_file(action) # reset if invalid
diff --git a/export_presets.cfg b/export_presets.cfg
deleted file mode 100644
index fb88f7e..0000000
--- a/export_presets.cfg
+++ /dev/null
@@ -1,24 +0,0 @@
-[preset.0]
-
-name="Linux"
-platform="Linux/X11"
-runnable=true
-custom_features=""
-export_filter="all_resources"
-include_filter=""
-exclude_filter=""
-export_path=""
-script_export_mode=1
-script_encryption_key=""
-
-[preset.0.options]
-
-custom_template/debug=""
-custom_template/release=""
-binary_format/64_bits=true
-binary_format/embed_pck=true
-texture_format/bptc=false
-texture_format/s3tc=true
-texture_format/etc=false
-texture_format/etc2=false
-texture_format/no_bptc_fallbacks=true
diff --git a/project.godot b/project.godot
index a758d14..99e5261 100644
--- a/project.godot
+++ b/project.godot
@@ -6,53 +6,7 @@
; [section] ; section goes between []
; param=value ; assign values to parameters
-config_version=4
-
-_global_script_classes=[ {
-"base": "HBoxContainer",
-"class": "ActionIcons",
-"language": "GDScript",
-"path": "res://addons/remap/ActionIcons.gd"
-}, {
-"base": "HBoxContainer",
-"class": "ActionLabel",
-"language": "GDScript",
-"path": "res://addons/remap/ActionLabel.gd"
-}, {
-"base": "Reference",
-"class": "IconMap",
-"language": "GDScript",
-"path": "res://addons/remap/IconMap.gd"
-}, {
-"base": "Node",
-"class": "KeySelector",
-"language": "GDScript",
-"path": "res://addons/remap/KeySelector.gd"
-}, {
-"base": "HBoxContainer",
-"class": "RemapButton",
-"language": "GDScript",
-"path": "res://addons/remap/RemapButton.gd"
-}, {
-"base": "Reference",
-"class": "RemapUtilities",
-"language": "GDScript",
-"path": "res://addons/remap/RemapUtilities.gd"
-}, {
-"base": "Node",
-"class": "SaveLoadUtils",
-"language": "GDScript",
-"path": "res://addons/remap/SaveLoadUtils.gd"
-} ]
-_global_script_class_icons={
-"ActionIcons": "",
-"ActionLabel": "",
-"IconMap": "",
-"KeySelector": "",
-"RemapButton": "",
-"RemapUtilities": "",
-"SaveLoadUtils": ""
-}
+config_version=5
[application]
@@ -60,6 +14,7 @@ config/name="test"
run/main_scene="res://Test.tscn"
config/use_custom_user_dir=true
config/custom_user_dir_name="remap"
+config/features=PackedStringArray("4.0")
[debug]
@@ -67,24 +22,22 @@ gdscript/warnings/return_value_discarded=false
[display]
+window/stretch/mode="2d"
+window/stretch/aspect="expand"
window/size/width=320
window/size/height=180
window/size/test_width=1280
window/size/test_height=720
-window/dpi/allow_hidpi=true
-window/stretch/mode="2d"
-window/stretch/aspect="expand"
[input]
ui_left={
"deadzone": 0.5,
-"events": [ Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":-1.0,"script":null)
-, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":65,"physical_scancode":0,"unicode":0,"echo":false,"script":null)
+"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":3,"axis_value":-1.0,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":0,"pressure":0.0,"pressed":false,"script":null)
-, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"pressed":false,"scancode":0,"physical_scancode":16777229,"unicode":0,"echo":false,"script":null)
-, Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":0,"alt":false,"shift":false,"control":false,"meta":false,"command":false,"button_mask":0,"position":Vector2( 0, 0 ),"global_position":Vector2( 0, 0 ),"factor":1.0,"button_index":1,"pressed":false,"doubleclick":false,"script":null)
- ]
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"unicode":0,"echo":false,"script":null)
+, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":66,"physical_keycode":0,"unicode":0,"echo":false,"script":null)
+]
}
[logging]