Lots of localization setup, added controller BBCode stuff also.

This commit is contained in:
2025-05-25 14:09:40 -05:00
parent f5cb84e0c9
commit 6a39e1f2df
825 changed files with 16497 additions and 18 deletions

View File

@@ -0,0 +1,49 @@
@tool
extends Button
class_name ControllerButton
## Controller icon for Button nodes.
##
## [b]Deprecated[/b]: Use the new [ControllerIconTexture] texture resource and set it
## directly in [member Button.icon].
##
## @deprecated
func _get_configuration_warnings():
return ["This node is deprecated, and will be removed in a future version.\n\nRemove this script and use the new ControllerIconTexture resource\nby setting it directly in Button's icon property."]
@export var path : String = "":
set(_path):
path = _path
if is_inside_tree():
if force_type > 0:
icon = ControllerIcons.parse_path(path, force_type - 1)
else:
icon = ControllerIcons.parse_path(path)
@export_enum("Both", "Keyboard/Mouse", "Controller") var show_only : int = 0:
set(_show_only):
show_only = _show_only
_on_input_type_changed(ControllerIcons._last_input_type, ControllerIcons._last_controller)
@export_enum("None", "Keyboard/Mouse", "Controller") var force_type : int = 0:
set(_force_type):
force_type = _force_type
_on_input_type_changed(ControllerIcons._last_input_type, ControllerIcons._last_controller)
func _ready():
ControllerIcons.input_type_changed.connect(_on_input_type_changed)
self.path = path
func _on_input_type_changed(input_type, controller):
if show_only == 0 or \
(show_only == 1 and input_type == ControllerIcons.InputType.KEYBOARD_MOUSE) or \
(show_only == 2 and input_type == ControllerIcons.InputType.CONTROLLER):
self.path = path
else:
icon = null
func get_tts_string() -> String:
if force_type:
return ControllerIcons.parse_path_to_tts(path, force_type - 1, ControllerIcons._last_controller)
else:
return ControllerIcons.parse_path_to_tts(path)

View File

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

View File

@@ -0,0 +1,61 @@
extends EditorInspectorPlugin
var path_selector := preload("res://addons/controller_icons/objects/ControllerIconPathEditorProperty.gd")
var editor_interface : EditorInterface
class ControllerIcons_TexturePreview:
var n_root: MarginContainer
var n_background: TextureRect
var n_texture: TextureRect
var background: Texture2D
var texture: Texture2D:
set(_texture):
texture = _texture
n_texture.texture = texture
func _init(editor_interface: EditorInterface):
n_root = MarginContainer.new()
# UPGRADE: In Godot 4.2, there's no need to have an instance to
# EditorInterface, since it's now a static call:
# background = EditorInterface.get_base_control().get_theme_icon("Checkerboard", "EditorIcons")
background = editor_interface.get_base_control().get_theme_icon("Checkerboard", "EditorIcons")
n_background = TextureRect.new()
n_background.stretch_mode = TextureRect.STRETCH_TILE
n_background.texture = background
n_background.texture_repeat = CanvasItem.TEXTURE_REPEAT_ENABLED
n_background.custom_minimum_size = Vector2(0, 256)
n_root.add_child(n_background)
n_texture = TextureRect.new()
n_texture.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST_WITH_MIPMAPS
n_texture.set_anchors_preset(Control.PRESET_FULL_RECT)
n_texture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
n_texture.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
n_root.add_child(n_texture)
func get_root():
return n_root
var preview: ControllerIcons_TexturePreview
func _can_handle(object: Object) -> bool:
return object is ControllerIconTexture
func _parse_begin(object: Object) -> void:
preview = ControllerIcons_TexturePreview.new(editor_interface)
add_custom_control(preview.get_root())
var icon := object as ControllerIconTexture
if icon:
preview.texture = icon
func _parse_property(object: Object, type, name: String, hint_type: PropertyHint, hint_string: String, usage_flags: int, wide: bool) -> bool:
if name == "path":
var path_selector_instance = path_selector.new(editor_interface)
add_property_editor(name, path_selector_instance)
return true
return false

View File

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

View File

@@ -0,0 +1,47 @@
@tool
extends EditorProperty
var selector : ConfirmationDialog
var line_edit : LineEdit
func _init(editor_interface: EditorInterface):
add_child(build_tree(editor_interface))
func build_tree(editor_interface: EditorInterface):
selector = preload("res://addons/controller_icons/objects/ControllerIconPathSelectorPopup.tscn").instantiate()
selector.editor_interface = editor_interface
selector.visible = false
selector.path_selected.connect(
func(path: String):
if not path.is_empty():
emit_changed(get_edited_property(), path)
)
var root := HBoxContainer.new()
line_edit = LineEdit.new()
line_edit.size_flags_horizontal = Control.SIZE_EXPAND_FILL
line_edit.text_changed.connect(func(text):
emit_changed(get_edited_property(), text)
)
var button := Button.new()
# UPGRADE: In Godot 4.2, there's no need to have an instance to
# EditorInterface, since it's now a static call:
# button.icon = EditorInterface.get_base_control().get_theme_icon("ListSelect", "EditorIcons")
button.icon = editor_interface.get_base_control().get_theme_icon("ListSelect", "EditorIcons")
button.tooltip_text = "Select an icon path"
button.pressed.connect(func():
selector.populate()
selector.popup_centered()
)
root.add_child(line_edit)
root.add_child(button)
root.add_child(selector)
return root
func _update_property():
var new_text = get_edited_object()[get_edited_property()]
if line_edit.text != new_text:
line_edit.text = new_text

View File

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

View File

@@ -0,0 +1,58 @@
@tool
extends PanelContainer
signal path_selected(path: String)
@onready var n_tab_container := %TabContainer
@onready var n_input_action := %"Input Action"
@onready var n_joypad_path := %"Joypad Path"
@onready var n_specific_path := %"Specific Path"
var input_action_populated := false
var joypad_path_populated := false
var specific_path_populated := false
var editor_interface : EditorInterface
func populate(editor_interface: EditorInterface) -> void:
self.editor_interface = editor_interface
input_action_populated = false
joypad_path_populated = false
specific_path_populated = false
n_tab_container.current_tab = 0
func get_icon_path() -> String:
return n_tab_container.get_current_tab_control().get_icon_path()
func _on_tab_container_tab_selected(tab = null) -> void:
match n_tab_container.get_current_tab_control():
n_input_action:
if not input_action_populated:
input_action_populated = true
n_input_action.populate(editor_interface)
n_joypad_path:
if not joypad_path_populated:
joypad_path_populated = true
n_joypad_path.populate(editor_interface)
n_specific_path:
if not specific_path_populated:
specific_path_populated = true
n_specific_path.populate(editor_interface)
await get_tree().process_frame
n_tab_container.get_current_tab_control().grab_focus()
func _on_input_action_done() -> void:
path_selected.emit(n_input_action.get_icon_path())
func _on_joypad_path_done() -> void:
path_selected.emit(n_joypad_path.get_icon_path())
func _on_specific_path_done() -> void:
path_selected.emit(n_specific_path.get_icon_path())

View File

@@ -0,0 +1 @@
uid://125nay2twqlr

View File

@@ -0,0 +1,41 @@
[gd_scene load_steps=5 format=3 uid="uid://dijn7haax0boi"]
[ext_resource type="Script" path="res://addons/controller_icons/objects/ControllerIconPathSelector.gd" id="1_0ucf4"]
[ext_resource type="PackedScene" uid="uid://bituity863qe4" path="res://addons/controller_icons/objects/path_selection/input_action.tscn" id="2_wlqmh"]
[ext_resource type="PackedScene" uid="uid://b3lplrf2w6kh7" path="res://addons/controller_icons/objects/path_selection/joypad_path.tscn" id="3_6ffwr"]
[ext_resource type="PackedScene" uid="uid://d2ow6e2ba86b6" path="res://addons/controller_icons/objects/path_selection/specific_path.tscn" id="4_3ai5v"]
[node name="ControllerIconPathSelector" type="PanelContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_0ucf4")
[node name="TabContainer" type="TabContainer" parent="."]
unique_name_in_owner = true
custom_minimum_size = Vector2(850, 450)
layout_mode = 2
size_flags_horizontal = 3
theme_override_constants/side_margin = 0
tab_alignment = 1
[node name="Input Action" parent="TabContainer" instance=ExtResource("2_wlqmh")]
unique_name_in_owner = true
layout_mode = 2
[node name="Joypad Path" parent="TabContainer" instance=ExtResource("3_6ffwr")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[node name="Specific Path" parent="TabContainer" instance=ExtResource("4_3ai5v")]
unique_name_in_owner = true
visible = false
layout_mode = 2
[connection signal="tab_selected" from="TabContainer" to="." method="_on_tab_container_tab_selected"]
[connection signal="done" from="TabContainer/Input Action" to="." method="_on_input_action_done"]
[connection signal="done" from="TabContainer/Joypad Path" to="." method="_on_joypad_path_done"]
[connection signal="done" from="TabContainer/Specific Path" to="." method="_on_specific_path_done"]

View File

@@ -0,0 +1,20 @@
@tool
extends ConfirmationDialog
signal path_selected(path: String)
var editor_interface : EditorInterface
@onready var n_selector := $ControllerIconPathSelector
func populate():
n_selector.populate(editor_interface)
func _on_controller_icon_path_selector_path_selected(path) -> void:
path_selected.emit(path)
hide()
func _on_confirmed() -> void:
path_selected.emit(n_selector.get_icon_path())

View File

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

View File

@@ -0,0 +1,20 @@
[gd_scene load_steps=3 format=3 uid="uid://ib8ydhcaklwy"]
[ext_resource type="Script" path="res://addons/controller_icons/objects/ControllerIconPathSelectorPopup.gd" id="1_6s0ph"]
[ext_resource type="PackedScene" uid="uid://dijn7haax0boi" path="res://addons/controller_icons/objects/ControllerIconPathSelector.tscn" id="2_gfs7t"]
[node name="ControllerIconPathSelectorPopup" type="ConfirmationDialog"]
title = "Select an icon path"
position = Vector2i(0, 36)
size = Vector2i(866, 507)
visible = true
script = ExtResource("1_6s0ph")
[node name="ControllerIconPathSelector" parent="." instance=ExtResource("2_gfs7t")]
offset_left = 8.0
offset_top = 8.0
offset_right = -8.0
offset_bottom = -49.0
[connection signal="confirmed" from="." to="." method="_on_confirmed"]
[connection signal="path_selected" from="ControllerIconPathSelector" to="." method="_on_controller_icon_path_selector_path_selected"]

View File

@@ -0,0 +1,391 @@
@tool
@icon("res://addons/controller_icons/objects/controller_texture_icon.svg")
extends Texture2D
class_name ControllerIconTexture
## [Texture2D] proxy for displaying controller icons
##
## A 2D texture representing a controller icon. The underlying system provides
## a [Texture2D] that may react to changes in the current input method, and also detect the user's controller type.
## Specify the [member path] property to setup the desired icon and behavior.[br]
## [br]
## For a more technical overview, this resource functions as a proxy for any
## node that accepts a [Texture2D], redefining draw commands to use an
## underlying plain [Texture2D], which may be swapped by the remapping system.[br]
## [br]
## This resource works out-of-the box with many default nodes, such as [Sprite2D],
## [Sprite3D], [TextureRect], [RichTextLabel], and others. If you are
## integrating this resource on a custom node, you will need to connect to the
## [signal Resource.changed] signal to properly handle changes to the underlying
## texture. You might also need to force a redraw with methods such as
## [method CanvasItem.queue_redraw].
##
## @tutorial(Online documentation): https://github.com/rsubtil/controller_icons/blob/master/DOCS.md
## A path describing the desired icon. This is a generic path that can be one
## of three different types:
## [br][br]
## [b]- Input Action[/b]: Specify the exact name of an existing input action. The
## icon will be swapped automatically depending on whether the keyboard/mouse or the
## controller is being used. When using a controller, it also changes according to
## the controller type.[br][br]
## [i]This is the recommended approach, as it will handle all input methods
## automatically, and supports any input remapping done at runtime[/i].
## [codeblock]
## # "Enter" on keyboard, "Cross" on Sony,
## # "A" on Xbox, "B" on Nintendo
## path = "ui_accept"
## [/codeblock]
## [b]- Joypad Path[/b]: Specify a generic joypad path resembling the layout of a
## Xbox 360 controller, starting with the [code]joypad/[/code] prefix. The icon will only
## display controller icons, but it will still change according to the controller type.
## [codeblock]
## # "Square" on Sony, "X" on Xbox, "Y" on Nintendo
## path = "joypad/x"
## [/codeblock]
## [b]- Specific Path[/b]: Specify a direct asset path from the addon assets.
## With this path type, there is no dynamic remapping, and the icon will always
## remain the same. The path to use is the path to an icon file, minus the base
## path and extension.
## [codeblock]
## # res://addons/controller_icons/assets/steam/gyro.png
## path = "steam/gyro"
## [/codeblock]
@export var path : String = "":
set(_path):
path = _path
_load_texture_path()
enum ShowMode {
ANY, ## Icon will be display on any input method.
KEYBOARD_MOUSE, ## Icon will be display only when the keyboard/mouse is being used.
CONTROLLER ## Icon will be display only when a controller is being used.
}
## Show the icon only if a specific input method is being used. When hidden,
## the icon will not occupy have any space (no width and height).
@export var show_mode : ShowMode = ShowMode.ANY:
set(_show_mode):
show_mode = _show_mode
_load_texture_path()
enum ForceType {
NONE, ## Icon will swap according to the used input method.
KEYBOARD_MOUSE, ## Icon will always show the keyboard/mouse action.
CONTROLLER, ## Icon will always show the controller action.
}
## Forces the icon to show either the keyboard/mouse or controller icon,
## regardless of the currently used input method.
##[br][br]
## This is only relevant for paths using input actions, and has no effect on
## other scenarios.
@export var force_type : ForceType = ForceType.NONE:
set(_force_type):
force_type = _force_type
_load_texture_path()
@export_subgroup("Text Rendering")
## Custom LabelSettings. If set, overrides the addon's global label settings.
@export var custom_label_settings : LabelSettings:
set(_custom_label_settings):
custom_label_settings = _custom_label_settings
_load_texture_path()
# Call _textures setter, which handles signal connections for label settings
_textures = _textures
## Returns a text representation of the displayed icon, useful for TTS
## (text-to-speech) scenarios.
## [br][br]
## This takes into consideration the currently displayed icon, and will thus be
## different if the icon is from keyboard/mouse or controller. It also takes
## into consideration the controller type, and will thus use native button
## names (e.g. [code]A[/code] for Xbox, [code]Cross[/code] for PlayStation, etc).
func get_tts_string() -> String:
if force_type:
return ControllerIcons.parse_path_to_tts(path, force_type - 1)
else:
return ControllerIcons.parse_path_to_tts(path)
func _can_be_shown():
match show_mode:
1:
return ControllerIcons._last_input_type == ControllerIcons.InputType.KEYBOARD_MOUSE
2:
return ControllerIcons._last_input_type == ControllerIcons.InputType.CONTROLLER
0, _:
return true
var _textures : Array[Texture2D]:
set(__textures):
# UPGRADE: In Godot 4.2, for-loop variables can be
# statically typed:
# for tex:Texture in __textures:
for tex in __textures:
if tex and tex.is_connected("changed", _reload_resource):
tex.disconnect("changed", _reload_resource)
if _label_settings and _label_settings.is_connected("changed", _on_label_settings_changed):
_label_settings.disconnect("changed", _on_label_settings_changed)
_textures = __textures
_label_settings = null
if _textures and _textures.size() > 1:
_label_settings = custom_label_settings
if not _label_settings:
_label_settings = ControllerIcons._settings.custom_label_settings
if not _label_settings:
_label_settings = LabelSettings.new()
_label_settings.connect("changed", _on_label_settings_changed)
_font = ThemeDB.fallback_font if not _label_settings.font else _label_settings.font
_on_label_settings_changed()
# UPGRADE: In Godot 4.2, for-loop variables can be
# statically typed:
# for tex:Texture in __textures:
for tex in __textures:
if tex:
tex.connect("changed", _reload_resource)
var _font : Font
var _label_settings : LabelSettings
var _text_size : Vector2
func _on_label_settings_changed():
_font = ThemeDB.fallback_font if not _label_settings.font else _label_settings.font
_text_size = _font.get_string_size("+", HORIZONTAL_ALIGNMENT_LEFT, -1, _label_settings.font_size)
_reload_resource()
func _reload_resource():
_dirty = true
emit_changed()
func _load_texture_path_impl():
var textures : Array[Texture2D] = []
if ControllerIcons.is_node_ready() and _can_be_shown():
var input_type = ControllerIcons._last_input_type if force_type == ForceType.NONE else force_type - 1
if ControllerIcons.get_path_type(path) == ControllerIcons.PathType.INPUT_ACTION:
var event := ControllerIcons.get_matching_event(path, input_type)
textures.append_array(ControllerIcons.parse_event_modifiers(event))
var tex := ControllerIcons.parse_path(path, input_type)
if tex:
textures.append(tex)
_textures = textures
_reload_resource()
func _load_texture_path():
# Ensure loading only occurs on the main thread
if OS.get_thread_caller_id() != OS.get_main_thread_id():
# In Godot 4.3, call_deferred no longer makes this function
# execute on the main thread due to changes in resource loading.
# To ensure this, we instead rely on ControllerIcons for this
ControllerIcons._defer_texture_load(_load_texture_path_impl)
else:
_load_texture_path_impl()
func _init():
ControllerIcons.input_type_changed.connect(_on_input_type_changed)
func _on_input_type_changed(input_type: int, controller: int):
_load_texture_path()
#region "Draw functions"
const _NULL_SIZE := 2
func _get_width() -> int:
if _can_be_shown():
var ret := _textures.reduce(func(accum: int, texture: Texture2D):
if texture:
return accum + texture.get_width()
return accum
, 0)
if _label_settings:
ret += max(0, _textures.size()-1) * _text_size.x
# If ret is 0, return a size of 2 to prevent triggering engine checks
# for null sizes. The correct size will be set at a later frame.
return ret if ret > 0 else _NULL_SIZE
return _NULL_SIZE
func _get_height() -> int:
if _can_be_shown():
var ret := _textures.reduce(func(accum: int, texture: Texture2D):
if texture:
return max(accum, texture.get_height())
return accum
, 0)
if _label_settings and _textures.size() > 1:
ret = max(ret, _text_size.y)
# If ret is 0, return a size of 2 to prevent triggering engine checks
# for null sizes. The correct size will be set at a later frame.
return ret if ret > 0 else _NULL_SIZE
return _NULL_SIZE
func _has_alpha() -> bool:
return _textures.any(func(texture: Texture2D):
return texture.has_alpha()
)
func _is_pixel_opaque(x, y) -> bool:
# TODO: Not exposed to GDScript; however, since this seems to be used for editor stuff, it's
# seemingly fine to just report all pixels as opaque. Otherwise, mouse picking for Sprite2D
# stops working.
return true
func _draw(to_canvas_item: RID, pos: Vector2, modulate: Color, transpose: bool):
var position := pos
for i in range(_textures.size()):
var tex:Texture2D = _textures[i]
if !tex: continue
if i != 0:
# Draw text char '+'
var font_position := Vector2(
position.x,
position.y + (get_height() - _text_size.y) / 2.0
)
_draw_text(to_canvas_item, font_position, "+")
position.x += _text_size.x
tex.draw(to_canvas_item, position, modulate, transpose)
position.x += tex.get_width()
func _draw_rect(to_canvas_item: RID, rect: Rect2, tile: bool, modulate: Color, transpose: bool):
var position := rect.position
var width_ratio := rect.size.x / _get_width()
var height_ratio := rect.size.y / _get_height()
for i in range(_textures.size()):
var tex:Texture2D = _textures[i]
if !tex: continue
if i != 0:
# Draw text char '+'
var font_position := Vector2(
position.x + (_text_size.x * width_ratio) / 2 - (_text_size.x / 2),
position.y + (rect.size.y - _text_size.y) / 2.0
)
_draw_text(to_canvas_item, font_position, "+")
position.x += _text_size.x * width_ratio
var size := tex.get_size() * Vector2(width_ratio, height_ratio)
tex.draw_rect(to_canvas_item, Rect2(position, size), tile, modulate, transpose)
position.x += size.x
func _draw_rect_region(to_canvas_item: RID, rect: Rect2, src_rect: Rect2, modulate: Color, transpose: bool, clip_uv: bool):
var position := rect.position
var width_ratio := rect.size.x / _get_width()
var height_ratio := rect.size.y / _get_height()
for i in range(_textures.size()):
var tex:Texture2D = _textures[i]
if !tex: continue
if i != 0:
# Draw text char '+'
var font_position := Vector2(
position.x + (_text_size.x * width_ratio) / 2 - (_text_size.x / 2),
position.y + (rect.size.y - _text_size.y) / 2.0
)
_draw_text(to_canvas_item, font_position, "+")
position.x += _text_size.x * width_ratio
var size := tex.get_size() * Vector2(width_ratio, height_ratio)
var src_rect_ratio := Vector2(
tex.get_width() / float(_get_width()),
tex.get_height() / float(_get_height())
)
var tex_src_rect := Rect2(
src_rect.position * src_rect_ratio,
src_rect.size * src_rect_ratio
)
tex.draw_rect_region(to_canvas_item, Rect2(position, size), tex_src_rect, modulate, transpose, clip_uv)
position.x += size.x
func _draw_text(to_canvas_item: RID, font_position: Vector2, text: String):
font_position.y += _font.get_ascent(_label_settings.font_size)
if _label_settings.shadow_color.a > 0:
_font.draw_string(to_canvas_item, font_position + _label_settings.shadow_offset, text, HORIZONTAL_ALIGNMENT_LEFT, -1, _label_settings.font_size, _label_settings.shadow_color)
if _label_settings.shadow_size > 0:
_font.draw_string_outline(to_canvas_item, font_position + _label_settings.shadow_offset, text, HORIZONTAL_ALIGNMENT_LEFT, -1, _label_settings.font_size, _label_settings.shadow_size, _label_settings.shadow_color)
if _label_settings.outline_color.a > 0 and _label_settings.outline_size > 0:
_font.draw_string_outline(to_canvas_item, font_position, text, HORIZONTAL_ALIGNMENT_LEFT, -1, _label_settings.font_size, _label_settings.outline_size, _label_settings.outline_color)
_font.draw_string(to_canvas_item, font_position, text, HORIZONTAL_ALIGNMENT_CENTER, -1, _label_settings.font_size, _label_settings.font_color)
var _helper_viewport : Viewport
var _is_stitching_texture : bool = false
func _stitch_texture():
if _textures.is_empty():
return
_is_stitching_texture = true
var font_image : Image
if _textures.size() > 1:
# Generate a viewport to draw the text
_helper_viewport = SubViewport.new()
# FIXME: We need a 3px margin for some reason
_helper_viewport.size = _text_size + Vector2(3, 0)
_helper_viewport.render_target_update_mode = SubViewport.UPDATE_ONCE
_helper_viewport.render_target_clear_mode = SubViewport.CLEAR_MODE_ONCE
_helper_viewport.transparent_bg = true
var label := Label.new()
label.label_settings = _label_settings
label.text = "+"
label.position = Vector2.ZERO
_helper_viewport.add_child(label)
ControllerIcons.add_child(_helper_viewport)
await RenderingServer.frame_post_draw
font_image = _helper_viewport.get_texture().get_image()
ControllerIcons.remove_child(_helper_viewport)
_helper_viewport.free()
var position := Vector2i(0, 0)
var img : Image
for i in range(_textures.size()):
if !_textures[i]: continue
if i != 0:
# Draw text char '+'
var region := font_image.get_used_rect()
var font_position := Vector2i(
position.x,
position.y + (get_height() - region.size.y) / 2
)
img.blit_rect(font_image, region, font_position)
position.x += ceili(region.size.x)
var texture_raw := _textures[i].get_image()
texture_raw.decompress()
if not img:
img = Image.create(_get_width(), _get_height(), true, texture_raw.get_format())
img.blit_rect(texture_raw, Rect2i(0, 0, texture_raw.get_width(), texture_raw.get_height()), position)
position.x += texture_raw.get_width()
_is_stitching_texture = false
_dirty = false
_texture_3d = ImageTexture.create_from_image(img)
emit_changed()
# This is necessary for 3D sprites, as the texture is assigned to a material, and not drawn directly.
# For multi prompts, we need to generate a texture
var _dirty := true
var _texture_3d : Texture
func _get_rid():
if _dirty:
if not _is_stitching_texture:
# FIXME: Function may await, but because this is an internal engine call, we can't do anything about it.
# This results in a one-frame white texture being displayed, which is not ideal. Investigate later.
_stitch_texture()
if _is_stitching_texture:
return 0
else:
return 0
return _texture_3d.get_rid() if not _textures.is_empty() else 0
#endregion

View File

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

View File

@@ -0,0 +1,50 @@
@tool
extends Sprite2D
class_name ControllerSprite2D
## Controller icon for Sprite2D nodes.
##
## [b]Deprecated[/b]: Use the new [ControllerIconTexture] texture resource and set it
## directly in [member Sprite2D.texture].
##
## @deprecated
func _get_configuration_warnings():
return ["This node is deprecated, and will be removed in a future version.\n\nRemove this script and use the new ControllerIconTexture resource\nby setting it directly in Sprite2D's texture property."]
@export var path : String = "":
set(_path):
path = _path
if is_inside_tree():
if force_type > 0:
texture = ControllerIcons.parse_path(path, force_type - 1)
else:
texture = ControllerIcons.parse_path(path)
@export_enum("Both", "Keyboard/Mouse", "Controller") var show_only : int = 0:
set(_show_only):
show_only = _show_only
_on_input_type_changed(ControllerIcons._last_input_type, ControllerIcons._last_controller)
@export_enum("None", "Keyboard/Mouse", "Controller") var force_type : int = 0:
set(_force_type):
force_type = _force_type
_on_input_type_changed(ControllerIcons._last_input_type, ControllerIcons._last_controller)
func _ready():
ControllerIcons.input_type_changed.connect(_on_input_type_changed)
self.path = path
func _on_input_type_changed(input_type, controller):
if show_only == 0 or \
(show_only == 1 and input_type == ControllerIcons.InputType.KEYBOARD_MOUSE) or \
(show_only == 2 and input_type == ControllerIcons.InputType.CONTROLLER):
visible = true
self.path = path
else:
visible = false
func get_tts_string() -> String:
if force_type:
return ControllerIcons.parse_path_to_tts(path, force_type - 1)
else:
return ControllerIcons.parse_path_to_tts(path)

View File

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

View File

@@ -0,0 +1,50 @@
@tool
extends Sprite3D
class_name ControllerSprite3D
## Controller icon for Sprite3D nodes.
##
## [b]Deprecated[/b]: Use the new [ControllerIconTexture] texture resource and set it
## directly in [member Sprite3D.texture].
##
## @deprecated
func _get_configuration_warnings():
return ["This node is deprecated, and will be removed in a future version.\n\nRemove this script and use the new ControllerIconTexture resource\nby setting it directly in Sprite3D's texture property."]
@export var path : String = "":
set(_path):
path = _path
if is_inside_tree():
if force_type > 0:
texture = ControllerIcons.parse_path(path, force_type - 1)
else:
texture = ControllerIcons.parse_path(path)
@export_enum("Both", "Keyboard/Mouse", "Controller") var show_only := 0:
set(_show_only):
show_only = _show_only
_on_input_type_changed(ControllerIcons._last_input_type, ControllerIcons._last_controller)
@export_enum("None", "Keyboard/Mouse", "Controller") var force_type : int = 0:
set(_force_type):
force_type = _force_type
_on_input_type_changed(ControllerIcons._last_input_type, ControllerIcons._last_controller)
func _ready():
ControllerIcons.input_type_changed.connect(_on_input_type_changed)
self.path = path
func _on_input_type_changed(input_type, controller):
if show_only == 0 or \
(show_only == 1 and input_type == ControllerIcons.InputType.KEYBOARD_MOUSE) or \
(show_only == 2 and input_type == ControllerIcons.InputType.CONTROLLER):
visible = true
self.path = path
else:
visible = false
func get_tts_string() -> String:
if force_type:
return ControllerIcons.parse_path_to_tts(path, force_type - 1)
else:
return ControllerIcons.parse_path_to_tts(path)

View File

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

View File

@@ -0,0 +1,66 @@
@tool
extends TextureRect
class_name ControllerTextureRect
## Controller icon for TextureRect nodes.
##
## [b]Deprecated[/b]: Use the new [ControllerIconTexture] texture resource and set it
## directly in [member TextureRect.texture].
##
## @deprecated
func _get_configuration_warnings():
return ["This node is deprecated, and will be removed in a future version.\n\nRemove this script and use the new ControllerIconTexture resource\nby setting it directly in TextureRect's texture property."]
@export var path : String = "":
set(_path):
path = _path
if is_inside_tree():
if force_type > 0:
texture = ControllerIcons.parse_path(path, force_type - 1)
else:
texture = ControllerIcons.parse_path(path)
@export_enum("Both", "Keyboard/Mouse", "Controller") var show_only : int = 0:
set(_show_only):
show_only = _show_only
_on_input_type_changed(ControllerIcons._last_input_type, ControllerIcons._last_controller)
@export_enum("None", "Keyboard/Mouse", "Controller") var force_type : int = 0:
set(_force_type):
force_type = _force_type
_on_input_type_changed(ControllerIcons._last_input_type, ControllerIcons._last_controller)
@export var max_width : int = 40:
set(_max_width):
max_width = _max_width
if is_inside_tree():
if max_width < 0:
expand_mode = TextureRect.EXPAND_KEEP_SIZE
else:
expand_mode = TextureRect.EXPAND_IGNORE_SIZE
custom_minimum_size.x = max_width
if texture:
custom_minimum_size.y = texture.get_height() * max_width / texture.get_width()
else:
custom_minimum_size.y = custom_minimum_size.x
func _ready():
ControllerIcons.input_type_changed.connect(_on_input_type_changed)
self.path = path
self.max_width = max_width
func _on_input_type_changed(input_type, controller):
if show_only == 0 or \
(show_only == 1 and input_type == ControllerIcons.InputType.KEYBOARD_MOUSE) or \
(show_only == 2 and input_type == ControllerIcons.InputType.CONTROLLER):
visible = true
self.path = path
else:
visible = false
func get_tts_string() -> String:
if force_type:
return ControllerIcons.parse_path_to_tts(path, force_type - 1)
else:
return ControllerIcons.parse_path_to_tts(path)

View File

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

View File

@@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><circle cx="8" cy="3.5" r="2" fill="#fff" fill-opacity="0" stroke="#fff"/><circle cx="3.5" cy="8" r="2" fill="#fff" fill-opacity="0" stroke="#fff"/><circle cx="12.5" cy="8" r="2" fill="#fff" stroke="#fff"/><circle cx="8" cy="12.5" r="2" fill="#fff" fill-opacity="0" stroke="#fff"/></svg>

After

Width:  |  Height:  |  Size: 350 B

View File

@@ -0,0 +1,38 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d4cwx85k03lt7"
path="res://.godot/imported/controller_texture_icon.svg-c7feb059f1c41a49e9f62f5b03706d01.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/controller_icons/objects/controller_texture_icon.svg"
dest_files=["res://.godot/imported/controller_texture_icon.svg-c7feb059f1c41a49e9f62f5b03706d01.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=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=true

View File

@@ -0,0 +1,115 @@
@tool
extends Panel
signal done
@onready var n_name_filter := %NameFilter
@onready var n_builtin_action_button := %BuiltinActionButton
@onready var n_tree := %Tree
class ControllerIcons_Item:
func _init(tree: Tree, root: TreeItem, path: String, is_default: bool):
self.is_default = is_default
self.filtered = true
tree_item = tree.create_item(root)
tree_item.set_text(0, path)
controller_icon_key = ControllerIconTexture.new()
controller_icon_key.path = path
controller_icon_key.force_type = 1
controller_icon_joy = ControllerIconTexture.new()
controller_icon_joy.path = path
controller_icon_joy.force_type = 2
tree_item.set_icon_max_width(1, 48 * controller_icon_key._textures.size())
tree_item.set_icon_max_width(2, 48 * controller_icon_key._textures.size())
tree_item.set_icon(1, controller_icon_key)
tree_item.set_icon(2, controller_icon_joy)
var is_default : bool
var tree_item : TreeItem
var controller_icon_key : ControllerIconTexture
var controller_icon_joy : ControllerIconTexture
var show_default : bool:
set(_show_default):
show_default = _show_default
_query_visibility()
var filtered : bool:
set(_filtered):
filtered = _filtered
_query_visibility()
func _query_visibility():
if is_instance_valid(tree_item):
tree_item.visible = show_default and filtered
var root : TreeItem
var items : Array[ControllerIcons_Item]
func populate(editor_interface: EditorInterface) -> void:
# Clear
n_tree.clear()
## Using clear() triggers a signal and uses freed nodes.
## Setting the text directly does not.
n_name_filter.text = ""
items.clear()
n_name_filter.right_icon = editor_interface.get_base_control().get_theme_icon("Search", "EditorIcons")
# Setup tree columns
n_tree.set_column_title(0, "Action")
n_tree.set_column_title(1, "Preview")
n_tree.set_column_expand(1, false)
n_tree.set_column_expand(2, false)
# Force ControllerIcons to reload the input map
ControllerIcons._parse_input_actions()
# List with all default input actions
var default_actions := ControllerIcons._builtin_keys.map(
func(value: String):
return value.trim_prefix("input/")
)
# Map with all input actions
root = n_tree.create_item()
for data in ControllerIcons._custom_input_actions:
var child := ControllerIcons_Item.new(n_tree, root, data, data in default_actions)
items.push_back(child)
set_default_actions_visibility(n_builtin_action_button.button_pressed)
func get_icon_path() -> String:
var item : TreeItem = n_tree.get_selected()
if is_instance_valid(item):
return item.get_text(0)
return ""
func set_default_actions_visibility(display: bool):
# UPGRADE: In Godot 4.2, for-loop variables can be
# statically typed:
# for item:ControllerIcons_Item in items:
for item in items:
item.show_default = display or not item.is_default
func grab_focus() -> void:
n_name_filter.grab_focus()
func _on_builtin_action_button_toggled(toggled_on: bool) -> void:
set_default_actions_visibility(toggled_on)
func _on_tree_item_activated() -> void:
done.emit()
func _on_name_filter_text_changed(new_text: String):
# UPGRADE: In Godot 4.2, for-loop variables can be
# statically typed:
# for item:ControllerIcons_Item in items:
for item in items:
var filtered := true if new_text.is_empty() else item.tree_item.get_text(0).findn(new_text) != -1
item.filtered = filtered

View File

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

View File

@@ -0,0 +1,200 @@
@tool
extends Panel
signal done
@onready var n_button_label := %ButtonLabel
@onready var button_nodes := [
%LT, %RT,
%LStick, %RStick,
%LStickClick, %RStickClick,
%LB, %RB, %A, %B, %X, %Y,
%Select, %Start,
%Home, %Share, %DPAD,
%DPADDown, %DPADRight,
%DPADLeft, %DPADUp
]
var _last_pressed_button : Button
var _last_pressed_timestamp : int
func populate(editor_interface: EditorInterface) -> void:
# UPGRADE: In Godot 4.2, for-loop variables can be
# statically typed:
# for button:Button in button_nodes:
for button in button_nodes:
button.button_pressed = false
func get_icon_path() -> String:
# UPGRADE: In Godot 4.2, for-loop variables can be
# statically typed:
# for button:Button in button_nodes:
for button in button_nodes:
if button.button_pressed:
return button.icon.path
return ""
func grab_focus() -> void:
pass
func _input(event):
if not visible: return
if event is InputEventJoypadMotion:
_input_motion(event)
elif event is InputEventJoypadButton:
_input_button(event)
func _input_motion(event: InputEventJoypadMotion):
if abs(event.axis_value) < 0.5: return
match event.axis:
JOY_AXIS_LEFT_X, JOY_AXIS_LEFT_Y:
_simulate_button_press(%LStick)
JOY_AXIS_RIGHT_X, JOY_AXIS_RIGHT_Y:
_simulate_button_press(%RStick)
JOY_AXIS_TRIGGER_LEFT:
_simulate_button_press(%LT)
JOY_AXIS_TRIGGER_RIGHT:
_simulate_button_press(%RT)
func _input_button(event: InputEventJoypadButton):
if not event.pressed: return
match event.button_index:
JOY_BUTTON_A:
_simulate_button_press(%A)
JOY_BUTTON_B:
_simulate_button_press(%B)
JOY_BUTTON_X:
_simulate_button_press(%X)
JOY_BUTTON_Y:
_simulate_button_press(%Y)
JOY_BUTTON_LEFT_SHOULDER:
_simulate_button_press(%LB)
JOY_BUTTON_RIGHT_SHOULDER:
_simulate_button_press(%RB)
JOY_BUTTON_LEFT_STICK:
_simulate_button_press(%LStickClick)
JOY_BUTTON_RIGHT_STICK:
_simulate_button_press(%RStickClick)
JOY_BUTTON_DPAD_DOWN:
_simulate_button_press(%DPADDown)
JOY_BUTTON_DPAD_RIGHT:
_simulate_button_press(%DPADRight)
JOY_BUTTON_DPAD_LEFT:
_simulate_button_press(%DPADLeft)
JOY_BUTTON_DPAD_UP:
_simulate_button_press(%DPADUp)
JOY_BUTTON_BACK:
_simulate_button_press(%Select)
JOY_BUTTON_START:
_simulate_button_press(%Start)
JOY_BUTTON_GUIDE:
_simulate_button_press(%Home)
JOY_BUTTON_MISC1:
_simulate_button_press(%Share)
func _simulate_button_press(button: Button):
button.grab_focus()
button.button_pressed = true
button.set_meta("from_ui", false)
button.pressed.emit()
button.set_meta("from_ui", true)
func _on_button_pressed():
# UPGRADE: In Godot 4.2, for-loop variables can be
# statically typed:
# for button:Button in button_nodes:
for button in button_nodes:
if button.has_meta("from_ui") and not button.get_meta("from_ui", true): return
if button.button_pressed:
if _last_pressed_button == button:
if Time.get_ticks_msec() < _last_pressed_timestamp:
done.emit()
else:
_last_pressed_timestamp = Time.get_ticks_msec() + 1000
else:
_last_pressed_button = button
_last_pressed_timestamp = Time.get_ticks_msec() + 1000
func _on_l_stick_pressed():
n_button_label.text = "Axis 0/1\n(Left Stick, Joystick 0)\n[joypad/l_stick]"
func _on_l_stick_click_pressed():
n_button_label.text = "Button 7\n(Left Stick, Sony L3, Xbox L/LS)\n[joypad/l_stick_click]"
func _on_r_stick_pressed():
n_button_label.text = "Axis 2/3\n(Right Stick, Joystick 1)\n[joypad/r_stick]"
func _on_r_stick_click_pressed():
n_button_label.text = "Button 8\n(Right Stick, Sony R3, Xbox R/RS)\n[joypad/r_stick_click]"
func _on_lb_pressed():
n_button_label.text = "Button 9\n(Left Shoulder, Sony L1, Xbox LB)\n[joypad/lb]"
func _on_lt_pressed():
n_button_label.text = "Axis 4\n(Left Trigger, Sony L2, Xbox LT, Joystick 2 Right)\n[joypad/lt]"
func _on_rb_pressed():
n_button_label.text = "Button 10\n(Right Shoulder, Sony R1, Xbox RB)\n[joypad/rb]"
func _on_rt_pressed():
n_button_label.text = "Axis 5\n(Right Trigger, Sony R2, Xbox RT, Joystick 2 Down)\n[joypad/rt]"
func _on_a_pressed():
n_button_label.text = "Button 0\n(Bottom Action, Sony Cross, Xbox A, Nintendo B)\n[joypad/a]"
func _on_b_pressed():
n_button_label.text = "Button 1\n(Right Action, Sony Circle, Xbox B, Nintendo A)\n[joypad/b]"
func _on_x_pressed():
n_button_label.text = "Button 2\n(Left Action, Sony Square, Xbox X, Nintendo Y)\n[joypad/x]"
func _on_y_pressed():
n_button_label.text = "Button 3\n(Top Action, Sony Triangle, Xbox Y, Nintendo X)\n[joypad/y]"
func _on_select_pressed():
n_button_label.text = "Button 4\n(Back, Sony Select, Xbox Back, Nintendo -)\n[joypad/select]"
func _on_start_pressed():
n_button_label.text = "Button 6\n(Start, Xbox Menu, Nintendo +)\n[joypad/start]"
func _on_home_pressed():
n_button_label.text = "Button 5\n(Guide, Sony PS, Xbox Home)\n[joypad/home]"
func _on_share_pressed():
n_button_label.text = "Button 15\n(Xbox Share, PS5 Microphone, Nintendo Capture)\n[joypad/share]"
func _on_dpad_pressed():
n_button_label.text = "Button 11/12/13/14\n(D-pad)\n[joypad/dpad]"
func _on_dpad_down_pressed():
n_button_label.text = "Button 12\n(D-pad Down)\n[joypad/dpad_down]"
func _on_dpad_right_pressed():
n_button_label.text = "Button 14\n(D-pad Right)\n[joypad/dpad_right]"
func _on_dpad_left_pressed():
n_button_label.text = "Button 13\n(D-pad Left)\n[joypad/dpad_left]"
func _on_dpad_up_pressed():
n_button_label.text = "Button 11\n(D-pad Up)\n[joypad/dpad_up]"

View File

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

View File

@@ -0,0 +1,185 @@
@tool
extends Panel
signal done
@onready var n_name_filter := %NameFilter
@onready var n_base_asset_names := %BaseAssetNames
@onready var n_assets_container := %AssetsContainer
var _last_pressed_icon : ControllerIcons_Icon
var _last_pressed_timestamp : int
var color_text_enabled : Color
var color_text_disabled : Color
class ControllerIcons_Icon:
static var group := ButtonGroup.new()
func _init(category: String, path: String):
self.category = category
self.filtered = true
self.path = path.get_slice("/", 1)
button = Button.new()
button.custom_minimum_size = Vector2(100, 100)
button.clip_text = true
button.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
button.vertical_icon_alignment = VERTICAL_ALIGNMENT_TOP
button.expand_icon = true
button.toggle_mode = true
button.button_group = group
button.text = self.path
var icon = ControllerIconTexture.new()
icon.path = path
button.icon = icon
var button : Button
var category : String
var path : String
var selected: bool:
set(_selected):
selected = _selected
_query_visibility()
var filtered: bool:
set(_filtered):
filtered = _filtered
_query_visibility()
func _query_visibility():
if is_instance_valid(button):
button.visible = selected and filtered
var button_nodes := {}
var asset_names_root : TreeItem
func populate(editor_interface: EditorInterface) -> void:
## Using clear() triggers a signal and uses freed nodes.
## Setting the text directly does not.
n_name_filter.text = ""
n_base_asset_names.clear()
button_nodes.clear()
for child in n_assets_container.get_children():
n_assets_container.remove_child(child)
child.queue_free()
# UPGRADE: In Godot 4.2, there's no need to have an instance to
# EditorInterface, since it's now a static call:
# var editor_control := EditorInterface.get_base_control()
var editor_control := editor_interface.get_base_control()
color_text_enabled = editor_control.get_theme_color("font_color", "Editor")
color_text_disabled = editor_control.get_theme_color("disabled_font_color", "Editor")
n_name_filter.right_icon = editor_control.get_theme_icon("Search", "EditorIcons")
asset_names_root = n_base_asset_names.create_item()
var base_paths := [
ControllerIcons._settings.custom_asset_dir,
"res://addons/controller_icons/assets"
]
# UPGRADE: In Godot 4.2, for-loop variables can be
# statically typed:
# for base_path:String in base_paths:
for base_path in base_paths:
if base_path.is_empty() or not base_path.begins_with("res://"):
continue
# Files first
handle_files("", base_path)
# Directories next
for dir in DirAccess.get_directories_at(base_path):
handle_files(dir, base_path.path_join(dir))
var child : TreeItem = asset_names_root.get_next_in_tree()
if child:
child.select(0)
func handle_files(category: String, base_path: String):
for file in DirAccess.get_files_at(base_path):
if file.get_extension() == ControllerIcons._base_extension:
create_icon(category, base_path.path_join(file))
func create_icon(category: String, path: String):
var map_category := "<no category>" if category.is_empty() else category
if not button_nodes.has(map_category):
button_nodes[map_category] = {}
var item : TreeItem = n_base_asset_names.create_item(asset_names_root)
item.set_text(0, map_category)
var filename := path.get_file()
if button_nodes[map_category].has(filename): return
var icon_path = ("" if category.is_empty() else category + "/") + path.get_file().get_basename()
var icon := ControllerIcons_Icon.new(map_category, icon_path)
button_nodes[map_category][filename] = icon
n_assets_container.add_child(icon.button)
icon.button.pressed.connect(func():
if _last_pressed_icon == icon:
if Time.get_ticks_msec() < _last_pressed_timestamp:
done.emit()
else:
_last_pressed_timestamp = Time.get_ticks_msec() + 1000
else:
_last_pressed_icon = icon
_last_pressed_timestamp = Time.get_ticks_msec() + 1000
)
func get_icon_path() -> String:
var button := ControllerIcons_Icon.group.get_pressed_button()
if button:
return button.icon.path
return ""
func grab_focus() -> void:
n_name_filter.grab_focus()
func _on_base_asset_names_item_selected():
var selected : TreeItem = n_base_asset_names.get_selected()
if not selected: return
var category := selected.get_text(0)
if not button_nodes.has(category): return
# UPGRADE: In Godot 4.2, for-loop variables can be
# statically typed:
# for key:String in button_nodes.keys():
# for icon:ControllerIcon_Icon in button_nodes[key].values():
for key in button_nodes.keys():
for icon in button_nodes[key].values():
icon.selected = key == category
func _on_name_filter_text_changed(new_text:String):
var any_visible := {}
var asset_name := asset_names_root.get_next_in_tree()
while asset_name:
any_visible[asset_name.get_text(0)] = false
asset_name = asset_name.get_next_in_tree()
var selected_category : TreeItem = n_base_asset_names.get_selected()
# UPGRADE: In Godot 4.2, for-loop variables can be
# statically typed:
# for key:String in button_nodes.keys():
# for icon:Icon in button_nodes[key].values():
for key in button_nodes.keys():
for icon in button_nodes[key].values():
var filtered : bool = true if new_text.is_empty() else icon.path.findn(new_text) != -1
icon.filtered = filtered
any_visible[key] = any_visible[key] or filtered
asset_name = asset_names_root.get_next_in_tree()
while asset_name:
var category := asset_name.get_text(0)
if any_visible.has(category):
var selectable : bool = any_visible[category]
asset_name.set_selectable(0, selectable)
if not selectable:
asset_name.deselect(0)
asset_name.set_custom_color(0, color_text_enabled if selectable else color_text_disabled)
asset_name = asset_name.get_next_in_tree()

View File

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

View File

@@ -0,0 +1,59 @@
[gd_scene load_steps=2 format=3 uid="uid://bituity863qe4"]
[ext_resource type="Script" path="res://addons/controller_icons/objects/path_selection/InputActionSelector.gd" id="1_shxks"]
[node name="Input Action" type="Panel"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_shxks")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Label" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "The icon will be tied to an input action, swapping between keyboard/mouse and controller automatically."
horizontal_alignment = 1
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="NameFilter" type="LineEdit" parent="VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "Filter by name..."
clear_button_enabled = true
[node name="BuiltinActionButton" type="CheckButton" parent="VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
text = "Show Built-in Actions"
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="Tree" type="Tree" parent="VBoxContainer/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
columns = 3
column_titles_visible = true
hide_folding = true
hide_root = true
select_mode = 1
scroll_horizontal_enabled = false
[connection signal="text_changed" from="VBoxContainer/HBoxContainer/NameFilter" to="." method="_on_name_filter_text_changed"]
[connection signal="toggled" from="VBoxContainer/HBoxContainer/BuiltinActionButton" to="." method="_on_builtin_action_button_toggled"]
[connection signal="item_activated" from="VBoxContainer/ScrollContainer/Tree" to="." method="_on_tree_item_activated"]

View File

@@ -0,0 +1,678 @@
[gd_scene load_steps=25 format=3 uid="uid://b3lplrf2w6kh7"]
[ext_resource type="Script" path="res://addons/controller_icons/objects/path_selection/JoypadPathSelector.gd" id="1_4ryog"]
[ext_resource type="Script" path="res://addons/controller_icons/objects/ControllerIconTexture.gd" id="2_yxfq6"]
[sub_resource type="ButtonGroup" id="ButtonGroup_haylq"]
[sub_resource type="Texture2D" id="Texture2D_nls54"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/l_stick"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_6klsb"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/l_stick_click"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_bij8j"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/r_stick"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_2h0w3"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/r_stick_click"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_kvj2q"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/lb"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_wtcrq"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/lt"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_lfpf5"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/rb"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_b3bsp"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/rt"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_u40go"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/a"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_rnqww"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/b"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_1a2yv"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/x"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_hktfi"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/y"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_2ksy6"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/select"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_08sqi"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/start"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_tivgf"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/home"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_87fow"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/share"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_n4i6p"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/dpad"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_eoyuo"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/dpad_down"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_c660e"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/dpad_right"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_riwus"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/dpad_left"
show_only = 0
force_type = 0
[sub_resource type="Texture2D" id="Texture2D_onmvf"]
resource_local_to_scene = false
resource_name = ""
script = ExtResource("2_yxfq6")
_horizontal_repeat = 1
path = "joypad/dpad_up"
show_only = 0
force_type = 0
[node name="Joypad Path" type="Panel"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_4ryog")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Label" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "The icon will only display a controller icon, changing dynamically according to the connected controller brand."
horizontal_alignment = 1
[node name="Control" type="Control" parent="VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="ButtonLabel" type="Label" parent="VBoxContainer/Control"]
unique_name_in_owner = true
custom_minimum_size = Vector2(350, 0)
layout_mode = 1
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -150.0
offset_top = 18.0
offset_right = 150.0
offset_bottom = 41.0
grow_horizontal = 2
text = "Press or click on the desired button..."
horizontal_alignment = 1
autowrap_mode = 3
[node name="LStick" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -126.0
offset_top = 47.0
offset_right = -76.0
offset_bottom = 97.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_nls54")
expand_icon = true
[node name="LStickClick" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -101.0
offset_top = 97.0
offset_right = -51.0
offset_bottom = 147.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_6klsb")
expand_icon = true
[node name="RStick" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = 74.0
offset_top = 47.0
offset_right = 124.0
offset_bottom = 97.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_bij8j")
expand_icon = true
[node name="RStickClick" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = 49.0
offset_top = 97.0
offset_right = 99.0
offset_bottom = 147.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_2h0w3")
expand_icon = true
[node name="LB" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -276.0
offset_top = -153.0
offset_right = -226.0
offset_bottom = -103.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_kvj2q")
expand_icon = true
[node name="LT" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -326.0
offset_top = -178.0
offset_right = -276.0
offset_bottom = -128.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_wtcrq")
expand_icon = true
[node name="RB" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = 224.0
offset_top = -153.0
offset_right = 274.0
offset_bottom = -103.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_lfpf5")
expand_icon = true
[node name="RT" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = 274.0
offset_top = -178.0
offset_right = 324.0
offset_bottom = -128.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_b3bsp")
expand_icon = true
[node name="A" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = 174.0
offset_top = 22.0
offset_right = 224.0
offset_bottom = 72.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_u40go")
expand_icon = true
[node name="B" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = 224.0
offset_top = -28.0
offset_right = 274.0
offset_bottom = 22.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_rnqww")
expand_icon = true
[node name="X" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = 124.0
offset_top = -28.0
offset_right = 174.0
offset_bottom = 22.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_1a2yv")
expand_icon = true
[node name="Y" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = 174.0
offset_top = -78.0
offset_right = 224.0
offset_bottom = -28.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_hktfi")
expand_icon = true
[node name="Select" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -76.0
offset_top = -53.0
offset_right = -26.0
offset_bottom = -3.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_2ksy6")
expand_icon = true
[node name="Start" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = 24.0
offset_top = -53.0
offset_right = 74.0
offset_bottom = -3.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_08sqi")
expand_icon = true
[node name="Home" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -26.0
offset_top = -78.0
offset_right = 24.0
offset_bottom = -28.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_tivgf")
expand_icon = true
[node name="Share" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -26.0
offset_top = -28.0
offset_right = 24.0
offset_bottom = 22.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_87fow")
expand_icon = true
[node name="DPAD" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -226.0
offset_top = -28.0
offset_right = -176.0
offset_bottom = 22.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_n4i6p")
expand_icon = true
[node name="DPADDown" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -226.0
offset_top = 22.0
offset_right = -176.0
offset_bottom = 72.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_eoyuo")
expand_icon = true
[node name="DPADRight" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -176.0
offset_top = -28.0
offset_right = -126.0
offset_bottom = 22.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_c660e")
expand_icon = true
[node name="DPADLeft" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -276.0
offset_top = -28.0
offset_right = -226.0
offset_bottom = 22.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_riwus")
expand_icon = true
[node name="DPADUp" type="Button" parent="VBoxContainer/Control"]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -226.0
offset_top = -78.0
offset_right = -176.0
offset_bottom = -28.0
grow_horizontal = 2
grow_vertical = 2
toggle_mode = true
button_group = SubResource("ButtonGroup_haylq")
icon = SubResource("Texture2D_onmvf")
expand_icon = true
[connection signal="pressed" from="VBoxContainer/Control/LStick" to="." method="_on_l_stick_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/LStick" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/LStickClick" to="." method="_on_l_stick_click_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/LStickClick" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/RStick" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/RStick" to="." method="_on_r_stick_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/RStickClick" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/RStickClick" to="." method="_on_r_stick_click_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/LB" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/LB" to="." method="_on_lb_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/LT" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/LT" to="." method="_on_lt_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/RB" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/RB" to="." method="_on_rb_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/RT" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/RT" to="." method="_on_rt_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/A" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/A" to="." method="_on_a_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/B" to="." method="_on_b_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/B" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/X" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/X" to="." method="_on_x_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/Y" to="." method="_on_y_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/Y" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/Select" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/Select" to="." method="_on_select_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/Start" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/Start" to="." method="_on_start_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/Home" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/Home" to="." method="_on_home_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/Share" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/Share" to="." method="_on_share_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/DPAD" to="." method="_on_dpad_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/DPAD" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/DPADDown" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/DPADDown" to="." method="_on_dpad_down_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/DPADRight" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/DPADRight" to="." method="_on_dpad_right_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/DPADLeft" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/DPADLeft" to="." method="_on_dpad_left_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/DPADUp" to="." method="_on_button_pressed"]
[connection signal="pressed" from="VBoxContainer/Control/DPADUp" to="." method="_on_dpad_up_pressed"]

View File

@@ -0,0 +1,67 @@
[gd_scene load_steps=2 format=3 uid="uid://d2ow6e2ba86b6"]
[ext_resource type="Script" path="res://addons/controller_icons/objects/path_selection/SpecificPathSelector.gd" id="1_iqwfd"]
[node name="Specific Path" type="Panel"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_iqwfd")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="Label" type="Label" parent="VBoxContainer"]
layout_mode = 2
text = "The icon will be set to a specific asset, without any dynamic remapping."
horizontal_alignment = 1
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
layout_mode = 2
[node name="NameFilter" type="LineEdit" parent="VBoxContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
placeholder_text = "Filter by name..."
clear_button_enabled = true
[node name="HSplitContainer" type="HSplitContainer" parent="VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer/HSplitContainer"]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
size_flags_horizontal = 0
[node name="BaseAssetNames" type="Tree" parent="VBoxContainer/HSplitContainer/ScrollContainer"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
hide_folding = true
hide_root = true
[node name="ScrollContainer2" type="ScrollContainer" parent="VBoxContainer/HSplitContainer"]
custom_minimum_size = Vector2(200, 0)
layout_mode = 2
size_flags_horizontal = 3
follow_focus = true
horizontal_scroll_mode = 0
[node name="AssetsContainer" type="HFlowContainer" parent="VBoxContainer/HSplitContainer/ScrollContainer2"]
unique_name_in_owner = true
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[connection signal="text_changed" from="VBoxContainer/HBoxContainer/NameFilter" to="." method="_on_name_filter_text_changed"]
[connection signal="item_selected" from="VBoxContainer/HSplitContainer/ScrollContainer/BaseAssetNames" to="." method="_on_base_asset_names_item_selected"]