generated from SGDA/GodotExampleProject
feat: base
This commit is contained in:
@ -0,0 +1,18 @@
|
||||
@tool
|
||||
extends Label
|
||||
class_name ConfigNameLabel
|
||||
## Displays the value of `application/config/name`, set in project settings.
|
||||
|
||||
const NO_NAME_STRING : String = "Title"
|
||||
|
||||
@export var lock : bool = false
|
||||
|
||||
func update_name_label():
|
||||
if lock: return
|
||||
var config_name : String = ProjectSettings.get_setting("application/config/name", NO_NAME_STRING)
|
||||
if config_name.is_empty():
|
||||
config_name = NO_NAME_STRING
|
||||
text = config_name
|
||||
|
||||
func _ready():
|
||||
update_name_label()
|
@ -0,0 +1 @@
|
||||
uid://bkwlopi4qn32o
|
@ -0,0 +1,18 @@
|
||||
@tool
|
||||
extends Label
|
||||
class_name ConfigVersionLabel
|
||||
## Displays the value of `application/config/version`, set in project settings.
|
||||
|
||||
const NO_VERSION_STRING : String = "0.0.0"
|
||||
|
||||
## Prefixes the value of `application/config/version` when displaying to the user.
|
||||
@export var version_prefix : String = "v"
|
||||
|
||||
func update_version_label() -> void:
|
||||
var config_version : String = ProjectSettings.get_setting("application/config/version", NO_VERSION_STRING)
|
||||
if config_version.is_empty():
|
||||
config_version = NO_VERSION_STRING
|
||||
text = version_prefix + config_version
|
||||
|
||||
func _ready() -> void:
|
||||
update_version_label()
|
@ -0,0 +1 @@
|
||||
uid://dmkubt2nsnsbn
|
@ -0,0 +1,94 @@
|
||||
class_name MainMenu
|
||||
extends Control
|
||||
|
||||
## Defines the path to the game scene. Hides the play button if empty.
|
||||
@export_file("*.tscn") var game_scene_path : String
|
||||
@export var options_packed_scene : PackedScene
|
||||
@export var credits_packed_scene : PackedScene
|
||||
|
||||
var options_scene
|
||||
var credits_scene
|
||||
var sub_menu
|
||||
|
||||
func load_game_scene() -> void:
|
||||
SceneLoader.load_scene(game_scene_path)
|
||||
|
||||
func new_game() -> void:
|
||||
load_game_scene()
|
||||
|
||||
func _open_sub_menu(menu : Control) -> void:
|
||||
sub_menu = menu
|
||||
sub_menu.show()
|
||||
%BackButton.show()
|
||||
%MenuContainer.hide()
|
||||
|
||||
func _close_sub_menu() -> void:
|
||||
if sub_menu == null:
|
||||
return
|
||||
sub_menu.hide()
|
||||
sub_menu = null
|
||||
%BackButton.hide()
|
||||
%MenuContainer.show()
|
||||
|
||||
func _event_is_mouse_button_released(event : InputEvent) -> bool:
|
||||
return event is InputEventMouseButton and not event.is_pressed()
|
||||
|
||||
func _input(event : InputEvent) -> void:
|
||||
if event.is_action_released("ui_cancel"):
|
||||
if sub_menu:
|
||||
_close_sub_menu()
|
||||
else:
|
||||
get_tree().quit()
|
||||
if event.is_action_released("ui_accept") and get_viewport().gui_get_focus_owner() == null:
|
||||
%MenuButtonsBoxContainer.focus_first()
|
||||
|
||||
func _hide_exit_for_web() -> void:
|
||||
if OS.has_feature("web"):
|
||||
%ExitButton.hide()
|
||||
|
||||
func _hide_new_game_if_unset() -> void:
|
||||
if game_scene_path.is_empty():
|
||||
%NewGameButton.hide()
|
||||
|
||||
func _add_or_hide_options() -> void:
|
||||
if options_packed_scene == null:
|
||||
%OptionsButton.hide()
|
||||
else:
|
||||
options_scene = options_packed_scene.instantiate()
|
||||
options_scene.hide()
|
||||
%OptionsContainer.call_deferred("add_child", options_scene)
|
||||
|
||||
func _add_or_hide_credits() -> void:
|
||||
if credits_packed_scene == null:
|
||||
%CreditsButton.hide()
|
||||
else:
|
||||
credits_scene = credits_packed_scene.instantiate()
|
||||
credits_scene.hide()
|
||||
if credits_scene.has_signal("end_reached"):
|
||||
credits_scene.connect("end_reached", _on_credits_end_reached)
|
||||
%CreditsContainer.call_deferred("add_child", credits_scene)
|
||||
|
||||
func _ready() -> void:
|
||||
_hide_exit_for_web()
|
||||
_add_or_hide_options()
|
||||
_add_or_hide_credits()
|
||||
_hide_new_game_if_unset()
|
||||
|
||||
func _on_new_game_button_pressed() -> void:
|
||||
new_game()
|
||||
|
||||
func _on_options_button_pressed() -> void:
|
||||
_open_sub_menu(options_scene)
|
||||
|
||||
func _on_credits_button_pressed() -> void:
|
||||
_open_sub_menu(credits_scene)
|
||||
|
||||
func _on_exit_button_pressed() -> void:
|
||||
get_tree().quit()
|
||||
|
||||
func _on_credits_end_reached() -> void:
|
||||
if sub_menu == credits_scene:
|
||||
_close_sub_menu()
|
||||
|
||||
func _on_back_button_pressed() -> void:
|
||||
_close_sub_menu()
|
@ -0,0 +1 @@
|
||||
uid://bhgs1upaahk3y
|
@ -0,0 +1,218 @@
|
||||
[gd_scene load_steps=9 format=3 uid="uid://c6k5nnpbypshi"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bhgs1upaahk3y" path="res://addons/maaacks_menus_template/base/scenes/menus/main_menu/main_menu.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://hmx6o472ropw" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/master_options_menu_with_tabs.tscn" id="2_qvrrd"]
|
||||
[ext_resource type="PackedScene" uid="uid://osxulxw2oas3" path="res://addons/maaacks_menus_template/base/scenes/credits/scrollable_credits.tscn" id="3_73am8"]
|
||||
[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_menus_template/base/scripts/capture_focus.gd" id="4_l1ebe"]
|
||||
[ext_resource type="PackedScene" uid="uid://bkcsjsk2ciff" path="res://addons/maaacks_menus_template/base/scenes/music_players/background_music_player.tscn" id="4_w8sbm"]
|
||||
[ext_resource type="Script" uid="uid://b5oej1q4h7jvh" path="res://addons/maaacks_menus_template/base/scripts/ui_sound_controller.gd" id="6_bs342"]
|
||||
[ext_resource type="Script" uid="uid://dmkubt2nsnsbn" path="res://addons/maaacks_menus_template/base/scenes/menus/main_menu/config_version_label.gd" id="6_pdiij"]
|
||||
[ext_resource type="Script" uid="uid://bkwlopi4qn32o" path="res://addons/maaacks_menus_template/base/scenes/menus/main_menu/config_name_label.gd" id="7_j7612"]
|
||||
|
||||
[node name="MainMenu" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1")
|
||||
options_packed_scene = ExtResource("2_qvrrd")
|
||||
credits_packed_scene = ExtResource("3_73am8")
|
||||
|
||||
[node name="UISoundController" type="Node" parent="."]
|
||||
script = ExtResource("6_bs342")
|
||||
|
||||
[node name="BackgroundMusicPlayer" parent="." instance=ExtResource("4_w8sbm")]
|
||||
|
||||
[node name="BackgroundTextureRect" type="TextureRect" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
expand_mode = 1
|
||||
stretch_mode = 5
|
||||
|
||||
[node name="VersionMargin" type="MarginContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_left = 8
|
||||
theme_override_constants/margin_top = 8
|
||||
theme_override_constants/margin_right = 8
|
||||
theme_override_constants/margin_bottom = 8
|
||||
|
||||
[node name="VersionContainer" type="Control" parent="VersionMargin"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="VersionLabel" type="Label" parent="VersionMargin/VersionContainer"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 3
|
||||
anchor_left = 1.0
|
||||
anchor_top = 1.0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = -88.0
|
||||
offset_top = -26.0
|
||||
grow_horizontal = 0
|
||||
grow_vertical = 0
|
||||
text = "v0.0.0"
|
||||
horizontal_alignment = 2
|
||||
script = ExtResource("6_pdiij")
|
||||
|
||||
[node name="MenuContainer" type="MarginContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="TitleMargin" type="MarginContainer" parent="MenuContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_top = 24
|
||||
|
||||
[node name="TitleContainer" type="Control" parent="MenuContainer/TitleMargin"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="TitleLabel" type="Label" parent="MenuContainer/TitleMargin/TitleContainer"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 67.0
|
||||
grow_horizontal = 2
|
||||
theme_override_font_sizes/font_size = 48
|
||||
text = "Title"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
script = ExtResource("7_j7612")
|
||||
|
||||
[node name="SubTitleMargin" type="MarginContainer" parent="MenuContainer"]
|
||||
layout_mode = 2
|
||||
theme_override_constants/margin_top = 92
|
||||
|
||||
[node name="SubTitleContainer" type="Control" parent="MenuContainer/SubTitleMargin"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="SubTitleLabel" type="Label" parent="MenuContainer/SubTitleMargin/SubTitleContainer"]
|
||||
layout_mode = 1
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 34.0
|
||||
grow_horizontal = 2
|
||||
theme_override_font_sizes/font_size = 24
|
||||
text = "Subtitle"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="MenuButtonsMargin" type="MarginContainer" parent="MenuContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/margin_top = 136
|
||||
theme_override_constants/margin_bottom = 8
|
||||
|
||||
[node name="MenuButtonsContainer" type="Control" parent="MenuContainer/MenuButtonsMargin"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="MenuButtonsBoxContainer" type="BoxContainer" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer"]
|
||||
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 = -64.0
|
||||
offset_top = -104.0
|
||||
offset_right = 64.0
|
||||
offset_bottom = 104.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 4
|
||||
theme_override_constants/separation = 16
|
||||
alignment = 1
|
||||
vertical = true
|
||||
script = ExtResource("4_l1ebe")
|
||||
|
||||
[node name="NewGameButton" type="Button" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "New Game"
|
||||
|
||||
[node name="OptionsButton" type="Button" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Options"
|
||||
|
||||
[node name="CreditsButton" type="Button" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Credits"
|
||||
|
||||
[node name="ExitButton" type="Button" parent="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Exit"
|
||||
|
||||
[node name="OptionsContainer" type="MarginContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="CreditsContainer" type="MarginContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 16
|
||||
theme_override_constants/margin_top = 32
|
||||
theme_override_constants/margin_right = 16
|
||||
theme_override_constants/margin_bottom = 32
|
||||
|
||||
[node name="FlowControlContainer" type="MarginContainer" parent="."]
|
||||
layout_mode = 0
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
mouse_filter = 2
|
||||
theme_override_constants/margin_left = 16
|
||||
theme_override_constants/margin_top = 16
|
||||
theme_override_constants/margin_right = 16
|
||||
theme_override_constants/margin_bottom = 16
|
||||
|
||||
[node name="FlowControl" type="Control" parent="FlowControlContainer"]
|
||||
layout_mode = 2
|
||||
mouse_filter = 2
|
||||
|
||||
[node name="BackButton" type="Button" parent="FlowControlContainer/FlowControl"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
anchors_preset = 2
|
||||
anchor_top = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_top = -31.0
|
||||
offset_right = 45.0
|
||||
grow_vertical = 0
|
||||
text = "Back"
|
||||
|
||||
[connection signal="pressed" from="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer/NewGameButton" to="." method="_on_new_game_button_pressed"]
|
||||
[connection signal="pressed" from="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer/OptionsButton" to="." method="_on_options_button_pressed"]
|
||||
[connection signal="pressed" from="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer/CreditsButton" to="." method="_on_credits_button_pressed"]
|
||||
[connection signal="pressed" from="MenuContainer/MenuButtonsMargin/MenuButtonsContainer/MenuButtonsBoxContainer/ExitButton" to="." method="_on_exit_button_pressed"]
|
||||
[connection signal="pressed" from="FlowControlContainer/FlowControl/BackButton" to="." method="_on_back_button_pressed"]
|
@ -0,0 +1,37 @@
|
||||
class_name AudioOptionsMenu
|
||||
extends Control
|
||||
|
||||
@export var audio_control_scene : PackedScene
|
||||
@export var hide_busses : Array[String]
|
||||
|
||||
@onready var mute_control = %MuteControl
|
||||
|
||||
func _on_bus_changed(bus_value : float, bus_iter : int) -> void:
|
||||
AppSettings.set_bus_volume(bus_iter, bus_value)
|
||||
|
||||
func _add_audio_control(bus_name : String, bus_value : float, bus_iter : int) -> void:
|
||||
if audio_control_scene == null or bus_name in hide_busses or bus_name.begins_with(AppSettings.SYSTEM_BUS_NAME_PREFIX):
|
||||
return
|
||||
var audio_control = audio_control_scene.instantiate()
|
||||
%AudioControlContainer.call_deferred("add_child", audio_control)
|
||||
if audio_control is OptionControl:
|
||||
audio_control.option_section = OptionControl.OptionSections.AUDIO
|
||||
audio_control.option_name = bus_name
|
||||
audio_control.value = bus_value
|
||||
audio_control.connect("setting_changed", _on_bus_changed.bind(bus_iter))
|
||||
|
||||
func _add_audio_bus_controls() -> void:
|
||||
for bus_iter in AudioServer.bus_count:
|
||||
var bus_name : String = AppSettings.get_audio_bus_name(bus_iter)
|
||||
var linear : float = AppSettings.get_bus_volume(bus_iter)
|
||||
_add_audio_control(bus_name, linear, bus_iter)
|
||||
|
||||
func _update_ui() -> void:
|
||||
_add_audio_bus_controls()
|
||||
mute_control.value = AppSettings.is_muted()
|
||||
|
||||
func _ready() -> void:
|
||||
_update_ui()
|
||||
|
||||
func _on_mute_control_setting_changed(value : bool) -> void:
|
||||
AppSettings.set_mute(value)
|
@ -0,0 +1 @@
|
||||
uid://bwugqn2cjr41e
|
@ -0,0 +1,42 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://c8vnncjwqcpab"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bwugqn2cjr41e" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/audio/audio_options_menu.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://cl416gdb1fgwr" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/slider_option_control.tscn" id="2_raehj"]
|
||||
[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_menus_template/base/scripts/capture_focus.gd" id="3_dtraq"]
|
||||
[ext_resource type="PackedScene" uid="uid://bsxh6v7j0257h" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/toggle_option_control.tscn" id="4_ojfec"]
|
||||
|
||||
[node name="Audio" type="MarginContainer"]
|
||||
custom_minimum_size = Vector2(305, 0)
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
theme_override_constants/margin_top = 24
|
||||
theme_override_constants/margin_bottom = 24
|
||||
script = ExtResource("1")
|
||||
audio_control_scene = ExtResource("2_raehj")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
theme_override_constants/separation = 8
|
||||
alignment = 1
|
||||
script = ExtResource("3_dtraq")
|
||||
search_depth = 3
|
||||
|
||||
[node name="AudioControlContainer" type="VBoxContainer" parent="VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
|
||||
[node name="MuteControl" parent="VBoxContainer" instance=ExtResource("4_ojfec")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
option_name = "Mute"
|
||||
option_section = 2
|
||||
key = "Mute"
|
||||
section = "AudioSettings"
|
||||
|
||||
[connection signal="setting_changed" from="VBoxContainer/MuteControl" to="." method="_on_mute_control_setting_changed"]
|
@ -0,0 +1,325 @@
|
||||
@tool
|
||||
class_name InputActionsList
|
||||
extends Container
|
||||
|
||||
const EMPTY_INPUT_ACTION_STRING = " "
|
||||
|
||||
signal already_assigned(action_name : String, input_name : String)
|
||||
signal minimum_reached(action_name : String)
|
||||
signal button_clicked(action_name : String, readable_input_name : String)
|
||||
|
||||
const BUTTON_NAME_GROUP_STRING : String = "%s:%d"
|
||||
|
||||
@export var vertical : bool = true :
|
||||
set(value):
|
||||
vertical = value
|
||||
if is_inside_tree():
|
||||
%ParentBoxContainer.vertical = vertical
|
||||
|
||||
@export_range(1, 5) var action_groups : int = 2
|
||||
@export var action_group_names : Array[String]
|
||||
@export var input_action_names : Array[StringName] :
|
||||
set(value):
|
||||
var _value_changed = input_action_names != value
|
||||
input_action_names = value
|
||||
if _value_changed:
|
||||
var _new_readable_action_names : Array[String]
|
||||
for action in input_action_names:
|
||||
_new_readable_action_names.append(action.capitalize())
|
||||
readable_action_names = _new_readable_action_names
|
||||
|
||||
@export var readable_action_names : Array[String] :
|
||||
set(value):
|
||||
var _value_changed = readable_action_names != value
|
||||
readable_action_names = value
|
||||
if _value_changed:
|
||||
var _new_action_name_map : Dictionary
|
||||
for iter in range(input_action_names.size()):
|
||||
var _input_name : StringName = input_action_names[iter]
|
||||
var _readable_name : String = readable_action_names[iter]
|
||||
_new_action_name_map[_input_name] = _readable_name
|
||||
action_name_map = _new_action_name_map
|
||||
|
||||
## Show action names that are not explicitely listed in an action name map.
|
||||
@export var show_all_actions : bool = true
|
||||
@export var button_minimum_size : Vector2
|
||||
@export_group("Icons")
|
||||
@export var input_icon_mapper : InputIconMapper
|
||||
@export var expand_icon : bool = false
|
||||
@export_group("Built-in Actions")
|
||||
## Shows Godot's built-in actions (action names starting with "ui_") in the tree.
|
||||
@export var show_built_in_actions : bool = false
|
||||
## Prevents assigning inputs that are already assigned to Godot's built-in actions (action names starting with "ui_"). Not recommended.
|
||||
@export var catch_built_in_duplicate_inputs : bool = false
|
||||
## Maps the names of built-in input actions to readable names for users.
|
||||
@export var built_in_action_name_map := InputEventHelper.BUILT_IN_ACTION_NAME_MAP
|
||||
@export_group("Debug")
|
||||
## Maps the names of input actions to readable names for users.
|
||||
@export var action_name_map : Dictionary
|
||||
|
||||
var action_button_map : Dictionary = {}
|
||||
var button_readable_input_map : Dictionary = {}
|
||||
var assigned_input_events : Dictionary = {}
|
||||
var editing_action_name : String = ""
|
||||
var editing_action_group : int = 0
|
||||
var last_input_readable_name
|
||||
|
||||
func _clear_list() -> void:
|
||||
for child in %ParentBoxContainer.get_children():
|
||||
if child == %ActionBoxContainer:
|
||||
continue
|
||||
child.queue_free()
|
||||
|
||||
func _replace_action(action_name : String, readable_input_name : String = "") -> void:
|
||||
var readable_action_name = tr(_get_action_readable_name(action_name))
|
||||
button_clicked.emit(readable_action_name, readable_input_name)
|
||||
|
||||
func _on_button_pressed(action_name : String, action_group : int) -> void:
|
||||
editing_action_name = action_name
|
||||
editing_action_group = action_group
|
||||
_replace_action(action_name)
|
||||
|
||||
func _new_action_box() -> Node:
|
||||
var new_action_box : Node = %ActionBoxContainer.duplicate()
|
||||
new_action_box.visible = true
|
||||
new_action_box.vertical = !(vertical)
|
||||
return new_action_box
|
||||
|
||||
func _add_header() -> void:
|
||||
if action_group_names.is_empty(): return
|
||||
var new_action_box := _new_action_box()
|
||||
for group_iter in range(action_groups):
|
||||
var group_name := ""
|
||||
if group_iter < action_group_names.size():
|
||||
group_name = action_group_names[group_iter]
|
||||
var new_label := Label.new()
|
||||
if button_minimum_size.x > 0:
|
||||
new_label.custom_minimum_size.x = button_minimum_size.x
|
||||
new_label.size_flags_horizontal = SIZE_SHRINK_CENTER
|
||||
else:
|
||||
new_label.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
if button_minimum_size.y > 0:
|
||||
new_label.custom_minimum_size.y = button_minimum_size.y
|
||||
new_label.size_flags_vertical = SIZE_SHRINK_CENTER
|
||||
else:
|
||||
new_label.size_flags_vertical = SIZE_EXPAND_FILL
|
||||
new_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
new_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
new_label.text = group_name
|
||||
new_action_box.add_child(new_label)
|
||||
%ParentBoxContainer.add_child(new_action_box)
|
||||
|
||||
func _add_to_action_button_map(action_name : String, action_group : int, button_node : BaseButton) -> void:
|
||||
var key_string : String = BUTTON_NAME_GROUP_STRING % [action_name, action_group]
|
||||
action_button_map[key_string] = button_node
|
||||
|
||||
func _get_button_by_action(action_name : String, action_group : int) -> Button:
|
||||
var key_string : String = BUTTON_NAME_GROUP_STRING % [action_name, action_group]
|
||||
if key_string in action_button_map:
|
||||
return action_button_map[key_string]
|
||||
return null
|
||||
|
||||
func _update_next_button_disabled_state(action_name : String, action_group : int) -> void:
|
||||
var button = _get_button_by_action(action_name, action_group)
|
||||
if button:
|
||||
button.disabled = false
|
||||
|
||||
func _update_assigned_inputs_and_button(action_name : String, action_group : int, input_event : InputEvent) -> void:
|
||||
var new_readable_input_name = InputEventHelper.get_text(input_event)
|
||||
var button = _get_button_by_action(action_name, action_group)
|
||||
if not button: return
|
||||
var icon : Texture
|
||||
if input_icon_mapper:
|
||||
icon = input_icon_mapper.get_icon(input_event)
|
||||
if icon:
|
||||
button.icon = icon
|
||||
else:
|
||||
button.icon = null
|
||||
if button.icon == null:
|
||||
button.text = new_readable_input_name
|
||||
else:
|
||||
button.text = ""
|
||||
var old_readable_input_name : String
|
||||
if button in button_readable_input_map:
|
||||
old_readable_input_name = button_readable_input_map[button]
|
||||
assigned_input_events.erase(old_readable_input_name)
|
||||
button_readable_input_map[button] = new_readable_input_name
|
||||
assigned_input_events[new_readable_input_name] = action_name
|
||||
|
||||
func _clear_button(action_name : String, action_group : int) -> void:
|
||||
var button = _get_button_by_action(action_name, action_group)
|
||||
if not button: return
|
||||
button.icon = null
|
||||
button.text = EMPTY_INPUT_ACTION_STRING
|
||||
var old_readable_input_name : String
|
||||
if button in button_readable_input_map:
|
||||
old_readable_input_name = button_readable_input_map[button]
|
||||
assigned_input_events.erase(old_readable_input_name)
|
||||
button_readable_input_map[button] = EMPTY_INPUT_ACTION_STRING
|
||||
|
||||
func _add_new_button(content : Variant, container: Control, disabled : bool = false) -> Button:
|
||||
var new_button := Button.new()
|
||||
if button_minimum_size.x > 0:
|
||||
new_button.custom_minimum_size.x = button_minimum_size.x
|
||||
new_button.size_flags_horizontal = SIZE_SHRINK_CENTER
|
||||
else:
|
||||
new_button.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
if button_minimum_size.y > 0:
|
||||
new_button.custom_minimum_size.y = button_minimum_size.y
|
||||
new_button.size_flags_vertical = SIZE_SHRINK_CENTER
|
||||
else:
|
||||
new_button.size_flags_vertical = SIZE_EXPAND_FILL
|
||||
new_button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
new_button.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
new_button.expand_icon = expand_icon
|
||||
if content is Texture:
|
||||
new_button.icon = content
|
||||
elif content is String:
|
||||
new_button.text = content
|
||||
new_button.disabled = disabled
|
||||
container.add_child(new_button)
|
||||
return new_button
|
||||
|
||||
func _connect_button_and_add_to_maps(button : Button, input_name : String, action_name : String, group_iter : int) -> void:
|
||||
button.pressed.connect(_on_button_pressed.bind(action_name, group_iter))
|
||||
button_readable_input_map[button] = input_name
|
||||
_add_to_action_button_map(action_name, group_iter, button)
|
||||
|
||||
func _add_action_options(action_name : String, readable_action_name : String, input_events : Array[InputEvent]) -> void:
|
||||
var new_action_box = %ActionBoxContainer.duplicate()
|
||||
new_action_box.visible = true
|
||||
new_action_box.vertical = !(vertical)
|
||||
new_action_box.get_child(0).text = readable_action_name
|
||||
for group_iter in range(action_groups):
|
||||
var input_event : InputEvent
|
||||
if group_iter < input_events.size():
|
||||
input_event = input_events[group_iter]
|
||||
var text = InputEventHelper.get_text(input_event)
|
||||
var is_disabled = group_iter > input_events.size()
|
||||
if text.is_empty(): text = EMPTY_INPUT_ACTION_STRING
|
||||
var icon : Texture
|
||||
if input_icon_mapper:
|
||||
icon = input_icon_mapper.get_icon(input_event)
|
||||
var content = icon if icon else text
|
||||
var button : Button = _add_new_button(content, new_action_box, is_disabled)
|
||||
_connect_button_and_add_to_maps(button, text, action_name, group_iter)
|
||||
%ParentBoxContainer.add_child(new_action_box)
|
||||
|
||||
func _get_all_action_names(include_built_in : bool = false) -> Array[StringName]:
|
||||
var action_names : Array[StringName] = input_action_names.duplicate()
|
||||
var full_action_name_map = action_name_map.duplicate()
|
||||
if include_built_in:
|
||||
for action_name in built_in_action_name_map:
|
||||
if action_name is String:
|
||||
action_name = StringName(action_name)
|
||||
if action_name is StringName:
|
||||
action_names.append(action_name)
|
||||
if show_all_actions:
|
||||
var all_actions := AppSettings.get_action_names(include_built_in)
|
||||
for action_name in all_actions:
|
||||
if not action_name in action_names:
|
||||
action_names.append(action_name)
|
||||
return action_names
|
||||
|
||||
func _get_action_readable_name(input_name : StringName) -> String:
|
||||
var readable_name : String
|
||||
if input_name in action_name_map:
|
||||
readable_name = action_name_map[input_name]
|
||||
elif input_name in built_in_action_name_map:
|
||||
readable_name = built_in_action_name_map[input_name]
|
||||
else:
|
||||
readable_name = input_name.capitalize()
|
||||
action_name_map[input_name] = readable_name
|
||||
return readable_name
|
||||
|
||||
func _build_ui_list() -> void:
|
||||
_clear_list()
|
||||
_add_header()
|
||||
var action_names : Array[StringName] = _get_all_action_names(show_built_in_actions)
|
||||
for action_name in action_names:
|
||||
var input_events = InputMap.action_get_events(action_name)
|
||||
if input_events.size() < 1:
|
||||
continue
|
||||
var readable_name : String = _get_action_readable_name(action_name)
|
||||
_add_action_options(action_name, readable_name, input_events)
|
||||
|
||||
func _assign_input_event(input_event : InputEvent, action_name : String) -> void:
|
||||
assigned_input_events[InputEventHelper.get_text(input_event)] = action_name
|
||||
|
||||
func _assign_input_event_to_action_group(input_event : InputEvent, action_name : String, action_group : int) -> void:
|
||||
_assign_input_event(input_event, action_name)
|
||||
var action_events := InputMap.action_get_events(action_name)
|
||||
action_events.resize(action_events.size() + 1)
|
||||
action_events[action_group] = input_event
|
||||
InputMap.action_erase_events(action_name)
|
||||
var final_action_events : Array[InputEvent]
|
||||
for input_action_event in action_events:
|
||||
if input_action_event == null: continue
|
||||
final_action_events.append(input_action_event)
|
||||
InputMap.action_add_event(action_name, input_action_event)
|
||||
AppSettings.set_config_input_events(action_name, final_action_events)
|
||||
action_group = min(action_group, final_action_events.size() - 1)
|
||||
_update_assigned_inputs_and_button(action_name, action_group, input_event)
|
||||
_update_next_button_disabled_state(action_name, action_group)
|
||||
|
||||
func _build_assigned_input_events() -> void:
|
||||
assigned_input_events.clear()
|
||||
var action_names := _get_all_action_names(show_built_in_actions and catch_built_in_duplicate_inputs)
|
||||
for action_name in action_names:
|
||||
var input_events = InputMap.action_get_events(action_name)
|
||||
for input_event in input_events:
|
||||
_assign_input_event(input_event, action_name)
|
||||
|
||||
func _get_action_for_input_event(input_event : InputEvent) -> String:
|
||||
if InputEventHelper.get_text(input_event) in assigned_input_events:
|
||||
return assigned_input_events[InputEventHelper.get_text(input_event)]
|
||||
return ""
|
||||
|
||||
func add_action_event(last_input_text : String, last_input_event : InputEvent) -> void:
|
||||
last_input_readable_name = last_input_text
|
||||
if last_input_event != null:
|
||||
var assigned_action := _get_action_for_input_event(last_input_event)
|
||||
if not assigned_action.is_empty():
|
||||
var readable_action_name = tr(_get_action_readable_name(assigned_action))
|
||||
already_assigned.emit(readable_action_name, last_input_readable_name)
|
||||
else:
|
||||
_assign_input_event_to_action_group(last_input_event, editing_action_name, editing_action_group)
|
||||
editing_action_name = ""
|
||||
|
||||
func _refresh_ui_list_button_content() -> void:
|
||||
var action_names : Array[StringName] = _get_all_action_names(show_built_in_actions)
|
||||
for action_name in action_names:
|
||||
var input_events := InputMap.action_get_events(action_name)
|
||||
if input_events.size() < 1:
|
||||
continue
|
||||
var group_iter : int = 0
|
||||
for input_event in input_events:
|
||||
_update_assigned_inputs_and_button(action_name, group_iter, input_event)
|
||||
group_iter += 1
|
||||
while group_iter < action_groups:
|
||||
_clear_button(action_name, group_iter)
|
||||
group_iter += 1
|
||||
|
||||
func _set_action_box_container_size() -> void:
|
||||
if button_minimum_size.x > 0:
|
||||
%ActionBoxContainer.size_flags_horizontal = SIZE_SHRINK_CENTER
|
||||
else:
|
||||
%ActionBoxContainer.size_flags_horizontal = SIZE_EXPAND_FILL
|
||||
if button_minimum_size.y > 0:
|
||||
%ActionBoxContainer.size_flags_vertical = SIZE_SHRINK_CENTER
|
||||
else:
|
||||
%ActionBoxContainer.size_flags_vertical = SIZE_EXPAND_FILL
|
||||
|
||||
func reset() -> void:
|
||||
AppSettings.reset_to_default_inputs()
|
||||
_build_assigned_input_events()
|
||||
_refresh_ui_list_button_content()
|
||||
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint(): return
|
||||
vertical = vertical
|
||||
_set_action_box_container_size()
|
||||
_build_assigned_input_events()
|
||||
_build_ui_list()
|
||||
if input_icon_mapper:
|
||||
input_icon_mapper.joypad_device_changed.connect(_refresh_ui_list_button_content)
|
@ -0,0 +1 @@
|
||||
uid://b3q5fgjev8gyo
|
@ -0,0 +1,44 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bxp45814v6ydv"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b3q5fgjev8gyo" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/input/input_actions_list.gd" id="1_cxorh"]
|
||||
|
||||
[node name="InputActionsList" type="ScrollContainer"]
|
||||
custom_minimum_size = Vector2(560, 240)
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
follow_focus = true
|
||||
script = ExtResource("1_cxorh")
|
||||
action_groups = 3
|
||||
action_group_names = Array[String](["Primary", "Secondary", "Tertiary", "Quaternary", "Quinary"])
|
||||
input_action_names = Array[StringName]([&"move_forward", &"move_backward", &"move_up", &"move_down", &"move_left", &"move_right", &"interact"])
|
||||
readable_action_names = Array[String](["Move Forward", "Move Backward", "Move Up", "Move Down", "Move Left", "Move Right", "Interact"])
|
||||
action_name_map = {
|
||||
"interact": "Interact",
|
||||
"move_backward": "Move Backward",
|
||||
"move_down": "Move Down",
|
||||
"move_forward": "Move Forward",
|
||||
"move_left": "Move Left",
|
||||
"move_right": "Move Right",
|
||||
"move_up": "Move Up"
|
||||
}
|
||||
|
||||
[node name="ParentBoxContainer" type="BoxContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
vertical = true
|
||||
|
||||
[node name="ActionBoxContainer" type="BoxContainer" parent="ParentBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
|
||||
[node name="ActionNameLabel" type="Label" parent="ParentBoxContainer/ActionBoxContainer"]
|
||||
custom_minimum_size = Vector2(150, 0)
|
||||
layout_mode = 2
|
@ -0,0 +1,215 @@
|
||||
class_name InputActionsTree
|
||||
extends Tree
|
||||
|
||||
signal already_assigned(action_name : String, input_name : String)
|
||||
signal minimum_reached(action_name : String)
|
||||
signal add_button_clicked(action_name : String)
|
||||
signal remove_button_clicked(action_name : String, input_name : String)
|
||||
|
||||
@export var input_action_names : Array[StringName] :
|
||||
set(value):
|
||||
var _value_changed = input_action_names != value
|
||||
input_action_names = value
|
||||
if _value_changed:
|
||||
var _new_readable_action_names : Array[String]
|
||||
for action in input_action_names:
|
||||
_new_readable_action_names.append(action.capitalize())
|
||||
readable_action_names = _new_readable_action_names
|
||||
|
||||
@export var readable_action_names : Array[String] :
|
||||
set(value):
|
||||
var _value_changed = readable_action_names != value
|
||||
readable_action_names = value
|
||||
if _value_changed:
|
||||
var _new_action_name_map : Dictionary
|
||||
for iter in range(input_action_names.size()):
|
||||
var _input_name : StringName = input_action_names[iter]
|
||||
var _readable_name : String = readable_action_names[iter]
|
||||
_new_action_name_map[_input_name] = _readable_name
|
||||
action_name_map = _new_action_name_map
|
||||
|
||||
## Show action names that are not explicitely listed in an action name map.
|
||||
@export var show_all_actions : bool = true
|
||||
@export_group("Icons")
|
||||
@export var add_button_texture : Texture2D
|
||||
@export var remove_button_texture : Texture2D
|
||||
@export var input_icon_mapper : InputIconMapper
|
||||
@export_group("Built-in Actions")
|
||||
## Shows Godot's built-in actions (action names starting with "ui_") in the tree.
|
||||
@export var show_built_in_actions : bool = false
|
||||
## Prevents assigning inputs that are already assigned to Godot's built-in actions (action names starting with "ui_"). Not recommended.
|
||||
@export var catch_built_in_duplicate_inputs : bool = false
|
||||
## Maps the names of built-in input actions to readable names for users.
|
||||
@export var built_in_action_name_map := InputEventHelper.BUILT_IN_ACTION_NAME_MAP
|
||||
@export_group("Debug")
|
||||
## Maps the names of input actions to readable names for users.
|
||||
@export var action_name_map : Dictionary
|
||||
|
||||
var tree_item_add_map : Dictionary = {}
|
||||
var tree_item_remove_map : Dictionary = {}
|
||||
var tree_item_action_map : Dictionary = {}
|
||||
var assigned_input_events : Dictionary = {}
|
||||
var editing_action_name : String = ""
|
||||
var editing_item
|
||||
var last_input_readable_name
|
||||
|
||||
func _start_tree() -> void:
|
||||
clear()
|
||||
create_item()
|
||||
|
||||
func _add_input_event_as_tree_item(action_name : String, input_event : InputEvent, parent_item : TreeItem) -> void:
|
||||
var input_tree_item : TreeItem = create_item(parent_item)
|
||||
var icon : Texture
|
||||
if input_icon_mapper:
|
||||
icon = input_icon_mapper.get_icon(input_event)
|
||||
if icon:
|
||||
input_tree_item.set_icon(0, icon)
|
||||
input_tree_item.set_text(0, InputEventHelper.get_text(input_event))
|
||||
if remove_button_texture != null:
|
||||
input_tree_item.add_button(0, remove_button_texture, -1, false, "Remove")
|
||||
tree_item_remove_map[input_tree_item] = input_event
|
||||
tree_item_action_map[input_tree_item] = action_name
|
||||
|
||||
func _add_action_as_tree_item(readable_name : String, action_name : String, input_events : Array[InputEvent]) -> void:
|
||||
var root_tree_item : TreeItem = get_root()
|
||||
var action_tree_item : TreeItem = create_item(root_tree_item)
|
||||
action_tree_item.set_text(0, readable_name)
|
||||
tree_item_add_map[action_tree_item] = action_name
|
||||
if add_button_texture != null:
|
||||
action_tree_item.add_button(0, add_button_texture, -1, false, "Add")
|
||||
for input_event in input_events:
|
||||
_add_input_event_as_tree_item(action_name, input_event, action_tree_item)
|
||||
|
||||
func _get_all_action_names(include_built_in : bool = false) -> Array[StringName]:
|
||||
var action_names : Array[StringName] = input_action_names.duplicate()
|
||||
var full_action_name_map = action_name_map.duplicate()
|
||||
if include_built_in:
|
||||
for action_name in built_in_action_name_map:
|
||||
if action_name is String:
|
||||
action_name = StringName(action_name)
|
||||
if action_name is StringName:
|
||||
action_names.append(action_name)
|
||||
if show_all_actions:
|
||||
var all_actions := AppSettings.get_action_names(include_built_in)
|
||||
for action_name in all_actions:
|
||||
if not action_name in action_names:
|
||||
action_names.append(action_name)
|
||||
return action_names
|
||||
|
||||
func _get_action_readable_name(input_name : StringName) -> String:
|
||||
var readable_name : String
|
||||
if input_name in action_name_map:
|
||||
readable_name = action_name_map[input_name]
|
||||
elif input_name in built_in_action_name_map:
|
||||
readable_name = built_in_action_name_map[input_name]
|
||||
else:
|
||||
readable_name = input_name.capitalize()
|
||||
action_name_map[input_name] = readable_name
|
||||
return readable_name
|
||||
|
||||
func _build_ui_tree() -> void:
|
||||
_start_tree()
|
||||
var action_names : Array[StringName] = _get_all_action_names(show_built_in_actions)
|
||||
for action_name in action_names:
|
||||
var input_events = InputMap.action_get_events(action_name)
|
||||
if input_events.size() < 1:
|
||||
continue
|
||||
var readable_name : String = _get_action_readable_name(action_name)
|
||||
_add_action_as_tree_item(readable_name, action_name, input_events)
|
||||
|
||||
func _assign_input_event(input_event : InputEvent, action_name : String) -> void:
|
||||
assigned_input_events[InputEventHelper.get_text(input_event)] = action_name
|
||||
|
||||
func _assign_input_event_to_action(input_event : InputEvent, action_name : String) -> void:
|
||||
_assign_input_event(input_event, action_name)
|
||||
InputMap.action_add_event(action_name, input_event)
|
||||
var action_events = InputMap.action_get_events(action_name)
|
||||
AppSettings.set_config_input_events(action_name, action_events)
|
||||
_add_input_event_as_tree_item(action_name, input_event, editing_item)
|
||||
|
||||
func _can_remove_input_event(action_name : String) -> bool:
|
||||
return InputMap.action_get_events(action_name).size() > 1
|
||||
|
||||
func _remove_input_event(input_event : InputEvent) -> void:
|
||||
assigned_input_events.erase(InputEventHelper.get_text(input_event))
|
||||
|
||||
func _remove_input_event_from_action(input_event : InputEvent, action_name : String) -> void:
|
||||
_remove_input_event(input_event)
|
||||
AppSettings.remove_action_input_event(action_name, input_event)
|
||||
|
||||
func _build_assigned_input_events() -> void:
|
||||
assigned_input_events.clear()
|
||||
var action_names := _get_all_action_names(show_built_in_actions and catch_built_in_duplicate_inputs)
|
||||
for action_name in action_names:
|
||||
var input_events = InputMap.action_get_events(action_name)
|
||||
for input_event in input_events:
|
||||
_assign_input_event(input_event, action_name)
|
||||
|
||||
func _get_action_for_input_event(input_event : InputEvent) -> String:
|
||||
if InputEventHelper.get_text(input_event) in assigned_input_events:
|
||||
return assigned_input_events[InputEventHelper.get_text(input_event)]
|
||||
return ""
|
||||
|
||||
func add_action_event(last_input_text : String, last_input_event : InputEvent):
|
||||
last_input_readable_name = last_input_text
|
||||
if last_input_event != null:
|
||||
var assigned_action := _get_action_for_input_event(last_input_event)
|
||||
if not assigned_action.is_empty():
|
||||
var readable_action_name = tr(_get_action_readable_name(assigned_action))
|
||||
already_assigned.emit(readable_action_name, last_input_readable_name)
|
||||
else:
|
||||
_assign_input_event_to_action(last_input_event, editing_action_name)
|
||||
editing_action_name = ""
|
||||
|
||||
func remove_action_event(item : TreeItem) -> void:
|
||||
if item not in tree_item_remove_map:
|
||||
return
|
||||
var action_name = tree_item_action_map[item]
|
||||
var input_event = tree_item_remove_map[item]
|
||||
if not _can_remove_input_event(action_name):
|
||||
var readable_action_name = _get_action_readable_name(action_name)
|
||||
minimum_reached.emit(readable_action_name)
|
||||
return
|
||||
_remove_input_event_from_action(input_event, action_name)
|
||||
var parent_tree_item = item.get_parent()
|
||||
parent_tree_item.remove_child(item)
|
||||
|
||||
func reset() -> void:
|
||||
AppSettings.reset_to_default_inputs()
|
||||
_build_assigned_input_events()
|
||||
_build_ui_tree()
|
||||
|
||||
func _add_item(item : TreeItem) -> void:
|
||||
editing_item = item
|
||||
editing_action_name = tree_item_add_map[item]
|
||||
var readable_action_name = tr(_get_action_readable_name(editing_action_name))
|
||||
add_button_clicked.emit(readable_action_name)
|
||||
|
||||
func _remove_item(item : TreeItem) -> void:
|
||||
editing_item = item
|
||||
editing_action_name = tree_item_action_map[item]
|
||||
var readable_action_name = tr(_get_action_readable_name(editing_action_name))
|
||||
var item_text = item.get_text(0)
|
||||
remove_button_clicked.emit(readable_action_name, item_text)
|
||||
|
||||
func _check_item_actions(item : TreeItem) -> void:
|
||||
if item in tree_item_add_map:
|
||||
_add_item(item)
|
||||
elif item in tree_item_remove_map:
|
||||
_remove_item(item)
|
||||
|
||||
func _on_button_clicked(item : TreeItem, _column, _id, _mouse_button_index) -> void:
|
||||
_check_item_actions(item)
|
||||
|
||||
func _on_item_activated() -> void:
|
||||
var item = get_selected()
|
||||
_check_item_actions(item)
|
||||
|
||||
func _ready() -> void:
|
||||
if Engine.is_editor_hint(): return
|
||||
_build_assigned_input_events()
|
||||
_build_ui_tree()
|
||||
button_clicked.connect(_on_button_clicked)
|
||||
item_activated.connect(_on_item_activated)
|
||||
if input_icon_mapper:
|
||||
input_icon_mapper.joypad_device_changed.connect(_build_ui_tree)
|
@ -0,0 +1 @@
|
||||
uid://bp7d2e5djo2tp
|
@ -0,0 +1,30 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://ci6wgl2ngd35n"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bp7d2e5djo2tp" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/input/input_actions_tree.gd" id="1_o33o4"]
|
||||
[ext_resource type="Texture2D" uid="uid://c1eqf1cse1hch" path="res://addons/maaacks_menus_template/base/assets/remapping_input_icons/addition_symbol.png" id="2_ppi0j"]
|
||||
[ext_resource type="Texture2D" uid="uid://bteq3ica74h30" path="res://addons/maaacks_menus_template/base/assets/remapping_input_icons/subtraction_symbol.png" id="3_hb3xh"]
|
||||
|
||||
[node name="InputActionsTree" type="Tree"]
|
||||
custom_minimum_size = Vector2(400, 240)
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
hide_root = true
|
||||
script = ExtResource("1_o33o4")
|
||||
input_action_names = Array[StringName]([&"move_forward", &"move_backward", &"move_up", &"move_down", &"move_left", &"move_right", &"interact"])
|
||||
readable_action_names = Array[String](["Move Forward", "Move Backward", "Move Up", "Move Down", "Move Left", "Move Right", "Interact"])
|
||||
add_button_texture = ExtResource("2_ppi0j")
|
||||
remove_button_texture = ExtResource("3_hb3xh")
|
||||
action_name_map = {
|
||||
"interact": "Interact",
|
||||
"move_backward": "Move Backward",
|
||||
"move_down": "Move Down",
|
||||
"move_forward": "Move Forward",
|
||||
"move_left": "Move Left",
|
||||
"move_right": "Move Right",
|
||||
"move_up": "Move Up"
|
||||
}
|
@ -0,0 +1,140 @@
|
||||
@tool
|
||||
class_name InputIconMapper
|
||||
extends FileLister
|
||||
|
||||
signal joypad_device_changed
|
||||
|
||||
const COMMON_REPLACE_STRINGS: Dictionary = {
|
||||
"L 1": "Left Shoulder",
|
||||
"R 1": "Right Shoulder",
|
||||
"L 2": "Left Trigger",
|
||||
"R 2": "Right Trigger",
|
||||
"Lt": "Left Trigger",
|
||||
"Rt": "Right Trigger",
|
||||
"Lb": "Left Shoulder",
|
||||
"Rb": "Right Shoulder",
|
||||
} # Dictionary[String, String]
|
||||
## Gives priority to icons with occurrences of the provided strings.
|
||||
@export var prioritized_strings : Array[String]
|
||||
## Replaces the first occurence in icon names of the key with the value.
|
||||
@export var replace_strings : Dictionary # Dictionary[String, String]
|
||||
## Filters the icon names of the provided strings.
|
||||
@export var filtered_strings : Array[String]
|
||||
## Adds entries for "Up", "Down", "Left", "Right" to icon names ending with "Stick".
|
||||
@export var add_stick_directions : bool = false
|
||||
@export var intial_joypad_device : String = InputEventHelper.DEVICE_GENERIC
|
||||
## Attempt to match the icon names to the input names based on the string rules.
|
||||
@export var _match_icons_to_inputs_action : bool = false :
|
||||
set(value):
|
||||
if value and Engine.is_editor_hint():
|
||||
_match_icons_to_inputs()
|
||||
# For Godot 4.4
|
||||
# @export_tool_button("Match Icons to Inputs") var _match_icons_to_inputs_action = _match_icons_to_inputs
|
||||
@export var matching_icons : Dictionary # Dictionary[String, Texture]
|
||||
@export_group("Debug")
|
||||
@export var all_icons : Dictionary # Dictionary[String, Texture]
|
||||
|
||||
@onready var last_joypad_device = intial_joypad_device
|
||||
|
||||
func _is_end_of_word(full_string : String, what : String) -> bool:
|
||||
var string_end_position = full_string.find(what) + what.length()
|
||||
var end_of_word : bool
|
||||
if string_end_position + 1 < full_string.length():
|
||||
var next_character = full_string.substr(string_end_position, 1)
|
||||
end_of_word = next_character == " "
|
||||
return full_string.ends_with(what) or end_of_word
|
||||
|
||||
func _get_standard_joy_name(joy_name : String) -> String:
|
||||
var all_replace_strings := replace_strings.duplicate()
|
||||
all_replace_strings.merge(COMMON_REPLACE_STRINGS)
|
||||
for what in all_replace_strings:
|
||||
if joy_name.contains(what) and _is_end_of_word(joy_name, what):
|
||||
var position = joy_name.find(what)
|
||||
joy_name = joy_name.erase(position, what.length())
|
||||
joy_name = joy_name.insert(position, all_replace_strings[what])
|
||||
var combined_joystick_name : Array[String] = []
|
||||
for part in joy_name.split(" "):
|
||||
if part.to_lower() in filtered_strings:
|
||||
continue
|
||||
if not part.is_empty():
|
||||
combined_joystick_name.append(part)
|
||||
joy_name = " ".join(combined_joystick_name)
|
||||
joy_name = joy_name.strip_edges()
|
||||
return joy_name
|
||||
|
||||
func _match_icon_to_file(file : String) -> void:
|
||||
var matching_string : String = file.get_file().get_basename()
|
||||
var icon : Texture = load(file)
|
||||
if not icon:
|
||||
return
|
||||
all_icons[matching_string] = icon
|
||||
matching_string = matching_string.capitalize()
|
||||
matching_string = _get_standard_joy_name(matching_string)
|
||||
matching_string = matching_string.strip_edges()
|
||||
if add_stick_directions and matching_string.ends_with("Stick"):
|
||||
matching_icons[matching_string + " Up"] = icon
|
||||
matching_icons[matching_string + " Down"] = icon
|
||||
matching_icons[matching_string + " Left"] = icon
|
||||
matching_icons[matching_string + " Right"] = icon
|
||||
return
|
||||
if matching_string in matching_icons:
|
||||
return
|
||||
matching_icons[matching_string] = icon
|
||||
|
||||
func _prioritized_files() -> Array[String]:
|
||||
var priority_levels : Dictionary # Dictionary[String, int]
|
||||
var priortized_files : Array[String]
|
||||
for prioritized_string in prioritized_strings:
|
||||
for file in files:
|
||||
if file.containsn(prioritized_string):
|
||||
if file in priority_levels:
|
||||
priority_levels[file] += 1
|
||||
else:
|
||||
priority_levels[file] = 1
|
||||
var priority_file_map : Dictionary # Dictionary[int, Array]
|
||||
var max_priority_level : int = 0
|
||||
for file in priority_levels:
|
||||
var priority_level = priority_levels[file]
|
||||
max_priority_level = max(priority_level, max_priority_level)
|
||||
if priority_level in priority_file_map:
|
||||
priority_file_map[priority_level].append(file)
|
||||
else:
|
||||
priority_file_map[priority_level] = [file]
|
||||
while max_priority_level > 0:
|
||||
for priority_file in priority_file_map[max_priority_level]:
|
||||
priortized_files.append(priority_file)
|
||||
max_priority_level -= 1
|
||||
return priortized_files
|
||||
|
||||
func _match_icons_to_inputs() -> void:
|
||||
matching_icons.clear()
|
||||
all_icons.clear()
|
||||
for prioritized_file in _prioritized_files():
|
||||
_match_icon_to_file(prioritized_file)
|
||||
for file in files:
|
||||
_match_icon_to_file(file)
|
||||
|
||||
func get_icon(input_event : InputEvent) -> Texture:
|
||||
var specific_text = InputEventHelper.get_device_specific_text(input_event, last_joypad_device)
|
||||
if specific_text in matching_icons:
|
||||
return matching_icons[specific_text]
|
||||
return null
|
||||
|
||||
func _assign_joypad_0_to_last() -> void:
|
||||
if last_joypad_device != intial_joypad_device : return
|
||||
var connected_joypads := Input.get_connected_joypads()
|
||||
if connected_joypads.is_empty(): return
|
||||
last_joypad_device = InputEventHelper.get_device_name_by_id(connected_joypads[0])
|
||||
|
||||
func _input(event : InputEvent) -> void:
|
||||
var device_name = InputEventHelper.get_device_name(event)
|
||||
if device_name != InputEventHelper.DEVICE_GENERIC and device_name != last_joypad_device:
|
||||
last_joypad_device = device_name
|
||||
joypad_device_changed.emit()
|
||||
|
||||
func _ready() -> void:
|
||||
_assign_joypad_0_to_last()
|
||||
if files.size() == 0:
|
||||
_refresh_files()
|
||||
if matching_icons.size() == 0:
|
||||
_match_icons_to_inputs()
|
@ -0,0 +1 @@
|
||||
uid://cqigj1uumknrp
|
@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://qoexj4ptqt8a"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cqigj1uumknrp" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/input/input_icon_mapper.gd" id="1_msrpt"]
|
||||
|
||||
[node name="InputIconMapper" type="Node"]
|
||||
script = ExtResource("1_msrpt")
|
@ -0,0 +1,102 @@
|
||||
@tool
|
||||
class_name InputOptionsMenu
|
||||
extends Control
|
||||
|
||||
const ALREADY_ASSIGNED_TEXT : String = "{key} already assigned to {action}."
|
||||
const ONE_INPUT_MINIMUM_TEXT : String = "%s must have at least one key or button assigned."
|
||||
const KEY_DELETION_TEXT : String = "Are you sure you want to remove {key} from {action}?"
|
||||
|
||||
@export_enum("List", "Tree") var remapping_mode : int = 0 :
|
||||
set(value):
|
||||
remapping_mode = value
|
||||
if is_inside_tree():
|
||||
match(remapping_mode):
|
||||
0:
|
||||
%InputActionsList.show()
|
||||
%InputActionsTree.hide()
|
||||
1:
|
||||
%InputActionsList.hide()
|
||||
%InputActionsTree.show()
|
||||
|
||||
@onready var assignment_placeholder_text = $KeyAssignmentDialog.dialog_text
|
||||
|
||||
var last_input_readable_name
|
||||
|
||||
func _horizontally_align_popup_labels() -> void:
|
||||
$KeyAssignmentDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
$KeyDeletionDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
$OneInputMinimumDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
$AlreadyAssignedDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
$ResetConfirmationDialog.get_label().horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
|
||||
func _ready() -> void:
|
||||
remapping_mode = remapping_mode
|
||||
if Engine.is_editor_hint(): return
|
||||
_horizontally_align_popup_labels()
|
||||
|
||||
func _add_action_event() -> void:
|
||||
var last_input_event = $KeyAssignmentDialog.last_input_event
|
||||
last_input_readable_name = $KeyAssignmentDialog.last_input_text
|
||||
match(remapping_mode):
|
||||
0:
|
||||
%InputActionsList.add_action_event(last_input_readable_name, last_input_event)
|
||||
1:
|
||||
%InputActionsTree.add_action_event(last_input_readable_name, last_input_event)
|
||||
|
||||
func _remove_action_event(item : TreeItem) -> void:
|
||||
%InputActionsTree.remove_action_event(item)
|
||||
|
||||
func _on_reset_button_pressed() -> void:
|
||||
$ResetConfirmationDialog.popup_centered()
|
||||
|
||||
func _on_key_deletion_dialog_confirmed() -> void:
|
||||
var editing_item = %InputActionsTree.editing_item
|
||||
if is_instance_valid(editing_item):
|
||||
_remove_action_event(editing_item)
|
||||
|
||||
func _on_key_assignment_dialog_confirmed() -> void:
|
||||
_add_action_event()
|
||||
|
||||
func _open_key_assignment_dialog(action_name : String, readable_input_name : String = assignment_placeholder_text) -> void:
|
||||
$KeyAssignmentDialog.title = tr("Assign Key for {action}").format({action = action_name})
|
||||
$KeyAssignmentDialog.dialog_text = readable_input_name
|
||||
$KeyAssignmentDialog.get_ok_button().disabled = true
|
||||
$KeyAssignmentDialog.popup_centered()
|
||||
|
||||
func _on_input_actions_tree_add_button_clicked(action_name) -> void:
|
||||
_open_key_assignment_dialog(action_name)
|
||||
|
||||
func _on_input_actions_tree_remove_button_clicked(action_name, input_name) -> void:
|
||||
$KeyDeletionDialog.title = tr("Remove Key for {action}").format({action = action_name})
|
||||
$KeyDeletionDialog.dialog_text = tr(KEY_DELETION_TEXT).format({key = input_name, action = action_name})
|
||||
$KeyDeletionDialog.popup_centered()
|
||||
|
||||
func _popup_already_assigned(action_name, input_name) -> void:
|
||||
$AlreadyAssignedDialog.dialog_text = tr(ALREADY_ASSIGNED_TEXT).format({key = input_name, action = action_name})
|
||||
$AlreadyAssignedDialog.popup_centered.call_deferred()
|
||||
|
||||
func _popup_minimum_reached(action_name : String) -> void:
|
||||
$OneInputMinimumDialog.dialog_text = ONE_INPUT_MINIMUM_TEXT % action_name
|
||||
$OneInputMinimumDialog.popup_centered.call_deferred()
|
||||
|
||||
func _on_input_actions_tree_already_assigned(action_name, input_name) -> void:
|
||||
_popup_already_assigned(action_name, input_name)
|
||||
|
||||
func _on_input_actions_tree_minimum_reached(action_name) -> void:
|
||||
_popup_minimum_reached(action_name)
|
||||
|
||||
func _on_input_actions_list_already_assigned(action_name, input_name) -> void:
|
||||
_popup_already_assigned(action_name, input_name)
|
||||
|
||||
func _on_input_actions_list_minimum_reached(action_name) -> void:
|
||||
_popup_minimum_reached(action_name)
|
||||
|
||||
func _on_input_actions_list_button_clicked(action_name, readable_input_name) -> void:
|
||||
_open_key_assignment_dialog(action_name, readable_input_name)
|
||||
|
||||
func _on_reset_confirmation_dialog_confirmed() -> void:
|
||||
match(remapping_mode):
|
||||
0:
|
||||
%InputActionsList.reset()
|
||||
1:
|
||||
%InputActionsTree.reset()
|
@ -0,0 +1 @@
|
||||
uid://eborw7q4b07h
|
@ -0,0 +1,139 @@
|
||||
[gd_scene load_steps=7 format=3 uid="uid://dp3rgqaehb3xu"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://eborw7q4b07h" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/input/input_options_menu.gd" id="1"]
|
||||
[ext_resource type="PackedScene" uid="uid://qoexj4ptqt8a" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/input/input_icon_mapper.tscn" id="2_627ul"]
|
||||
[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_menus_template/base/scripts/capture_focus.gd" id="2_wft4x"]
|
||||
[ext_resource type="Script" uid="uid://custha7r0uoic" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/input/key_assignment_dialog.gd" id="3_wsh2h"]
|
||||
[ext_resource type="PackedScene" uid="uid://bxp45814v6ydv" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/input/input_actions_list.tscn" id="4_lf2nw"]
|
||||
[ext_resource type="PackedScene" uid="uid://ci6wgl2ngd35n" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/input/input_actions_tree.tscn" id="5_b2whh"]
|
||||
|
||||
[node name="Controls" type="MarginContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/margin_left = 32
|
||||
theme_override_constants/margin_top = 8
|
||||
theme_override_constants/margin_right = 32
|
||||
theme_override_constants/margin_bottom = 8
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="InputIconMapper" parent="." instance=ExtResource("2_627ul")]
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
size_flags_vertical = 4
|
||||
script = ExtResource("2_wft4x")
|
||||
search_depth = 5
|
||||
|
||||
[node name="InputMappingContainer" type="VBoxContainer" parent="VBoxContainer"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
alignment = 1
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/InputMappingContainer"]
|
||||
layout_mode = 2
|
||||
text = "Actions & Inputs"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="InputActionsList" parent="VBoxContainer/InputMappingContainer" node_paths=PackedStringArray("input_icon_mapper") instance=ExtResource("4_lf2nw")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(560, 480)
|
||||
layout_mode = 2
|
||||
input_icon_mapper = NodePath("../../../InputIconMapper")
|
||||
|
||||
[node name="InputActionsTree" parent="VBoxContainer/InputMappingContainer" node_paths=PackedStringArray("input_icon_mapper") instance=ExtResource("5_b2whh")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(400, 480)
|
||||
layout_mode = 2
|
||||
input_icon_mapper = NodePath("../../../InputIconMapper")
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer/InputMappingContainer"]
|
||||
layout_mode = 2
|
||||
alignment = 1
|
||||
|
||||
[node name="ResetButton" type="Button" parent="VBoxContainer/InputMappingContainer/HBoxContainer"]
|
||||
layout_mode = 2
|
||||
text = "Reset"
|
||||
|
||||
[node name="KeyAssignmentDialog" type="ConfirmationDialog" parent="."]
|
||||
title = "Assign Key"
|
||||
size = Vector2i(400, 158)
|
||||
dialog_text = "
|
||||
|
||||
|
||||
"
|
||||
script = ExtResource("3_wsh2h")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="KeyAssignmentDialog"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
offset_left = 8.0
|
||||
offset_top = 8.0
|
||||
offset_right = -8.0
|
||||
offset_bottom = -49.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
|
||||
[node name="InputLabel" type="Label" parent="KeyAssignmentDialog/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "None"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="InputTextEdit" type="TextEdit" parent="KeyAssignmentDialog/VBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
placeholder_text = "Focus here to assign inputs."
|
||||
context_menu_enabled = false
|
||||
shortcut_keys_enabled = false
|
||||
selecting_enabled = false
|
||||
deselect_on_focus_loss_enabled = false
|
||||
drag_and_drop_selection_enabled = false
|
||||
middle_mouse_paste_enabled = false
|
||||
caret_move_on_right_click = false
|
||||
|
||||
[node name="DelayTimer" type="Timer" parent="KeyAssignmentDialog"]
|
||||
unique_name_in_owner = true
|
||||
wait_time = 0.1
|
||||
one_shot = true
|
||||
|
||||
[node name="KeyDeletionDialog" type="ConfirmationDialog" parent="."]
|
||||
title = "Remove Key"
|
||||
size = Vector2i(419, 100)
|
||||
dialog_text = "Are you sure you want to remove KEY from ACTION?"
|
||||
|
||||
[node name="OneInputMinimumDialog" type="AcceptDialog" parent="."]
|
||||
title = "Cannot Remove"
|
||||
size = Vector2i(398, 100)
|
||||
|
||||
[node name="AlreadyAssignedDialog" type="AcceptDialog" parent="."]
|
||||
title = "Already Assigned"
|
||||
size = Vector2i(398, 100)
|
||||
|
||||
[node name="ResetConfirmationDialog" type="ConfirmationDialog" parent="."]
|
||||
size = Vector2i(486, 100)
|
||||
dialog_text = "Are you sure you want to reset controls back to the defaults?"
|
||||
|
||||
[connection signal="already_assigned" from="VBoxContainer/InputMappingContainer/InputActionsList" to="." method="_on_input_actions_list_already_assigned"]
|
||||
[connection signal="button_clicked" from="VBoxContainer/InputMappingContainer/InputActionsList" to="." method="_on_input_actions_list_button_clicked"]
|
||||
[connection signal="minimum_reached" from="VBoxContainer/InputMappingContainer/InputActionsList" to="." method="_on_input_actions_list_minimum_reached"]
|
||||
[connection signal="add_button_clicked" from="VBoxContainer/InputMappingContainer/InputActionsTree" to="." method="_on_input_actions_tree_add_button_clicked"]
|
||||
[connection signal="already_assigned" from="VBoxContainer/InputMappingContainer/InputActionsTree" to="." method="_on_input_actions_tree_already_assigned"]
|
||||
[connection signal="minimum_reached" from="VBoxContainer/InputMappingContainer/InputActionsTree" to="." method="_on_input_actions_tree_minimum_reached"]
|
||||
[connection signal="remove_button_clicked" from="VBoxContainer/InputMappingContainer/InputActionsTree" to="." method="_on_input_actions_tree_remove_button_clicked"]
|
||||
[connection signal="pressed" from="VBoxContainer/InputMappingContainer/HBoxContainer/ResetButton" to="." method="_on_reset_button_pressed"]
|
||||
[connection signal="confirmed" from="KeyAssignmentDialog" to="." method="_on_key_assignment_dialog_confirmed"]
|
||||
[connection signal="visibility_changed" from="KeyAssignmentDialog" to="KeyAssignmentDialog" method="_on_visibility_changed"]
|
||||
[connection signal="focus_entered" from="KeyAssignmentDialog/VBoxContainer/InputTextEdit" to="KeyAssignmentDialog" method="_on_text_edit_focus_entered"]
|
||||
[connection signal="focus_exited" from="KeyAssignmentDialog/VBoxContainer/InputTextEdit" to="KeyAssignmentDialog" method="_on_input_text_edit_focus_exited"]
|
||||
[connection signal="gui_input" from="KeyAssignmentDialog/VBoxContainer/InputTextEdit" to="KeyAssignmentDialog" method="_on_input_text_edit_gui_input"]
|
||||
[connection signal="confirmed" from="KeyDeletionDialog" to="." method="_on_key_deletion_dialog_confirmed"]
|
||||
[connection signal="confirmed" from="ResetConfirmationDialog" to="." method="_on_reset_confirmation_dialog_confirmed"]
|
@ -0,0 +1,104 @@
|
||||
extends ConfirmationDialog
|
||||
|
||||
const LISTENING_TEXT : String = "Listening for input..."
|
||||
const FOCUS_HERE_TEXT : String = "Focus here to assign inputs."
|
||||
const CONFIRM_INPUT_TEXT : String = "Press again to confirm..."
|
||||
const NO_INPUT_TEXT : String = "None"
|
||||
|
||||
enum InputConfirmation {
|
||||
SINGLE,
|
||||
DOUBLE,
|
||||
OK_BUTTON
|
||||
}
|
||||
@export var input_confirmation : InputConfirmation = InputConfirmation.SINGLE
|
||||
|
||||
var last_input_event : InputEvent
|
||||
var last_input_text : String
|
||||
var listening : bool = false
|
||||
var confirming : bool = false
|
||||
|
||||
func _record_input_event(event : InputEvent) -> void:
|
||||
last_input_text = InputEventHelper.get_text(event)
|
||||
if last_input_text.is_empty():
|
||||
return
|
||||
last_input_event = event
|
||||
%InputLabel.text = last_input_text
|
||||
get_ok_button().disabled = false
|
||||
|
||||
func _is_recordable_input(event : InputEvent) -> bool:
|
||||
return event != null and \
|
||||
(event is InputEventKey or \
|
||||
event is InputEventMouseButton or \
|
||||
event is InputEventJoypadButton or \
|
||||
(event is InputEventJoypadMotion and \
|
||||
abs(event.axis_value) > 0.5)) and \
|
||||
event.is_pressed()
|
||||
|
||||
func _start_listening() -> void:
|
||||
%InputTextEdit.placeholder_text = LISTENING_TEXT
|
||||
listening = true
|
||||
%DelayTimer.start()
|
||||
|
||||
func _stop_listening() -> void:
|
||||
%InputTextEdit.placeholder_text = FOCUS_HERE_TEXT
|
||||
listening = false
|
||||
confirming = false
|
||||
|
||||
func _on_text_edit_focus_entered() -> void:
|
||||
_start_listening.call_deferred()
|
||||
|
||||
func _on_input_text_edit_focus_exited() -> void:
|
||||
_stop_listening()
|
||||
|
||||
func _focus_on_ok() -> void:
|
||||
get_ok_button().grab_focus()
|
||||
|
||||
func _ready() -> void:
|
||||
get_ok_button().focus_neighbor_top = ^"../../%InputTextEdit"
|
||||
get_cancel_button().focus_neighbor_top = ^"../../%InputTextEdit"
|
||||
|
||||
func _input_matches_last(event : InputEvent) -> bool:
|
||||
return last_input_text == InputEventHelper.get_text(event)
|
||||
|
||||
func _is_mouse_input(event : InputEvent) -> bool:
|
||||
return event is InputEventMouse
|
||||
|
||||
func _input_confirms_choice(event : InputEvent) -> bool:
|
||||
return confirming and not _is_mouse_input(event) and _input_matches_last(event)
|
||||
|
||||
func _should_process_input_event(event : InputEvent) -> bool:
|
||||
return listening and _is_recordable_input(event) and %DelayTimer.is_stopped()
|
||||
|
||||
func _should_confirm_input_event(event : InputEvent) -> bool:
|
||||
return not _is_mouse_input(event)
|
||||
|
||||
func _confirm_choice() -> void:
|
||||
confirmed.emit()
|
||||
hide()
|
||||
|
||||
func _process_input_event(event : InputEvent) -> void:
|
||||
if not _should_process_input_event(event):
|
||||
return
|
||||
if _input_confirms_choice(event):
|
||||
confirming = false
|
||||
if input_confirmation == InputConfirmation.DOUBLE:
|
||||
_confirm_choice()
|
||||
else:
|
||||
_focus_on_ok.call_deferred()
|
||||
return
|
||||
_record_input_event(event)
|
||||
if input_confirmation == InputConfirmation.SINGLE:
|
||||
_confirm_choice()
|
||||
if _should_confirm_input_event(event):
|
||||
confirming = true
|
||||
%DelayTimer.start()
|
||||
%InputTextEdit.placeholder_text = CONFIRM_INPUT_TEXT
|
||||
|
||||
func _on_input_text_edit_gui_input(event) -> void:
|
||||
%InputTextEdit.set_deferred("text", "")
|
||||
_process_input_event(event)
|
||||
|
||||
func _on_visibility_changed() -> void:
|
||||
if visible:
|
||||
%InputLabel.text = NO_INPUT_TEXT
|
||||
%InputTextEdit.grab_focus()
|
@ -0,0 +1 @@
|
||||
uid://custha7r0uoic
|
@ -0,0 +1,13 @@
|
||||
class_name MasterOptionsMenu
|
||||
extends Control
|
||||
|
||||
func _unhandled_input(event : InputEvent) -> void:
|
||||
if not is_visible_in_tree():
|
||||
return
|
||||
if event.is_action_pressed("ui_page_down"):
|
||||
$TabContainer.current_tab = ($TabContainer.current_tab+1) % $TabContainer.get_tab_count()
|
||||
elif event.is_action_pressed("ui_page_up"):
|
||||
if $TabContainer.current_tab == 0:
|
||||
$TabContainer.current_tab = $TabContainer.get_tab_count()-1
|
||||
else:
|
||||
$TabContainer.current_tab = $TabContainer.current_tab-1
|
@ -0,0 +1 @@
|
||||
uid://c3mignmhuvvq4
|
@ -0,0 +1,23 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bvwl11s2p0hd"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c3mignmhuvvq4" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/master_options_menu.gd" id="1_u08d5"]
|
||||
|
||||
[node name="MasterOptionsMenu" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
script = ExtResource("1_u08d5")
|
||||
|
||||
[node name="TabContainer" type="TabContainer" parent="."]
|
||||
layout_mode = 1
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
tab_alignment = 1
|
@ -0,0 +1,25 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://hmx6o472ropw"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bvwl11s2p0hd" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/master_options_menu.tscn" id="1_uaidt"]
|
||||
[ext_resource type="PackedScene" uid="uid://dp3rgqaehb3xu" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/input/input_options_menu.tscn" id="2_15wl6"]
|
||||
[ext_resource type="PackedScene" uid="uid://c8vnncjwqcpab" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/audio/audio_options_menu.tscn" id="3_qg4me"]
|
||||
[ext_resource type="PackedScene" uid="uid://b2numvphf2kau" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/video/video_options_menu.tscn" id="4_1t848"]
|
||||
|
||||
[node name="MasterOptionsMenu" instance=ExtResource("1_uaidt")]
|
||||
|
||||
[node name="TabContainer" parent="." index="0"]
|
||||
current_tab = 0
|
||||
|
||||
[node name="Controls" parent="TabContainer" index="1" instance=ExtResource("2_15wl6")]
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 0
|
||||
|
||||
[node name="Audio" parent="TabContainer" index="2" instance=ExtResource("3_qg4me")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 1
|
||||
|
||||
[node name="Video" parent="TabContainer" index="3" instance=ExtResource("4_1t848")]
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
metadata/_tab_index = 2
|
@ -0,0 +1,45 @@
|
||||
class_name MiniOptionsMenu
|
||||
extends Control
|
||||
|
||||
@onready var mute_control = %MuteControl
|
||||
@onready var fullscreen_control = %FullscreenControl
|
||||
|
||||
@export var audio_control_scene : PackedScene
|
||||
@export var hide_busses : Array[String]
|
||||
|
||||
func _on_bus_changed(bus_value : float, bus_iter : int) -> void:
|
||||
AppSettings.set_bus_volume(bus_iter, bus_value)
|
||||
|
||||
func _add_audio_control(bus_name : String, bus_value : float, bus_iter : int) -> void:
|
||||
if audio_control_scene == null or bus_name in hide_busses or bus_name.begins_with(AppSettings.SYSTEM_BUS_NAME_PREFIX):
|
||||
return
|
||||
var audio_control = audio_control_scene.instantiate()
|
||||
%AudioControlContainer.call_deferred("add_child", audio_control)
|
||||
if audio_control is OptionControl:
|
||||
audio_control.option_section = OptionControl.OptionSections.AUDIO
|
||||
audio_control.option_name = bus_name
|
||||
audio_control.value = bus_value
|
||||
audio_control.connect("setting_changed", _on_bus_changed.bind(bus_iter))
|
||||
|
||||
func _add_audio_bus_controls() -> void:
|
||||
for bus_iter in AudioServer.bus_count:
|
||||
var bus_name : String = AppSettings.get_audio_bus_name(bus_iter)
|
||||
var linear : float = AppSettings.get_bus_volume(bus_iter)
|
||||
_add_audio_control(bus_name, linear, bus_iter)
|
||||
|
||||
func _update_ui() -> void:
|
||||
_add_audio_bus_controls()
|
||||
mute_control.value = AppSettings.is_muted()
|
||||
fullscreen_control.value = AppSettings.is_fullscreen(get_window())
|
||||
|
||||
func _sync_with_config() -> void:
|
||||
_update_ui()
|
||||
|
||||
func _ready() -> void:
|
||||
_sync_with_config()
|
||||
|
||||
func _on_mute_control_setting_changed(value : bool) -> void:
|
||||
AppSettings.set_mute(value)
|
||||
|
||||
func _on_fullscreen_control_setting_changed(value : bool) -> void:
|
||||
AppSettings.set_fullscreen_enabled(value, get_window())
|
@ -0,0 +1 @@
|
||||
uid://1c0iyo5djoxj
|
@ -0,0 +1,51 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://vh1ucj2rfbby"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://1c0iyo5djoxj" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/mini_options_menu.gd" id="1_32vm2"]
|
||||
[ext_resource type="PackedScene" uid="uid://cl416gdb1fgwr" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/slider_option_control.tscn" id="2_kpc65"]
|
||||
[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_menus_template/base/scripts/capture_focus.gd" id="3_7qt1o"]
|
||||
[ext_resource type="PackedScene" uid="uid://bsxh6v7j0257h" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/toggle_option_control.tscn" id="4_b20fb"]
|
||||
|
||||
[node name="MiniOptionsMenu" type="VBoxContainer"]
|
||||
custom_minimum_size = Vector2(400, 260)
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -200.0
|
||||
offset_top = -130.0
|
||||
offset_right = 200.0
|
||||
offset_bottom = 130.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_horizontal = 4
|
||||
theme_override_constants/separation = 8
|
||||
alignment = 1
|
||||
script = ExtResource("1_32vm2")
|
||||
audio_control_scene = ExtResource("2_kpc65")
|
||||
|
||||
[node name="AudioControlContainer" type="VBoxContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 8
|
||||
script = ExtResource("3_7qt1o")
|
||||
search_depth = 2
|
||||
|
||||
[node name="MuteControl" parent="." instance=ExtResource("4_b20fb")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
option_name = "Mute"
|
||||
option_section = 2
|
||||
key = "Mute"
|
||||
section = "AudioSettings"
|
||||
|
||||
[node name="FullscreenControl" parent="." instance=ExtResource("4_b20fb")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
option_name = "Fullscreen"
|
||||
option_section = 3
|
||||
key = "FullscreenEnabled"
|
||||
section = "VideoSettings"
|
||||
|
||||
[connection signal="setting_changed" from="MuteControl" to="." method="_on_mute_control_setting_changed"]
|
||||
[connection signal="setting_changed" from="FullscreenControl" to="." method="_on_fullscreen_control_setting_changed"]
|
@ -0,0 +1,81 @@
|
||||
@tool
|
||||
class_name ListOptionControl
|
||||
extends OptionControl
|
||||
|
||||
## Locks Option Titles from auto-updating when editing Option Values.
|
||||
## Intentionally put first for initialization.
|
||||
@export var lock_titles : bool = false
|
||||
## Defines the list of possible values for the variable
|
||||
## this option stores in the config file.
|
||||
@export var option_values : Array :
|
||||
set(value) :
|
||||
option_values = value
|
||||
_on_option_values_changed()
|
||||
|
||||
## Defines the list of options displayed to the user.
|
||||
## Length should match with Option Values.
|
||||
@export var option_titles : Array[String] :
|
||||
set(value):
|
||||
option_titles = value
|
||||
if is_inside_tree():
|
||||
_set_option_list(option_titles)
|
||||
|
||||
var custom_option_values : Array
|
||||
|
||||
func _on_option_values_changed() -> void:
|
||||
if option_values.is_empty(): return
|
||||
custom_option_values = option_values.duplicate()
|
||||
var first_value = custom_option_values.front()
|
||||
property_type = typeof(first_value)
|
||||
_set_titles_from_values()
|
||||
|
||||
func _on_setting_changed(value : Variant) -> void:
|
||||
if value < custom_option_values.size() and value >= 0:
|
||||
super._on_setting_changed(custom_option_values[value])
|
||||
|
||||
func _set_titles_from_values() -> void:
|
||||
if lock_titles: return
|
||||
var mapped_titles : Array[String] = []
|
||||
for option_value in custom_option_values:
|
||||
mapped_titles.append(_value_title_map(option_value))
|
||||
option_titles = mapped_titles
|
||||
|
||||
func _value_title_map(value : Variant) -> String:
|
||||
return "%s" % value
|
||||
|
||||
func _match_value_to_other(value : Variant, other : Variant) -> Variant:
|
||||
# Primarily for when the editor saves floats as ints instead
|
||||
if value is int and other is float:
|
||||
return float(value)
|
||||
if value is float and other is int:
|
||||
return int(round(value))
|
||||
return value
|
||||
|
||||
func _set_value(value : Variant) -> Variant:
|
||||
if option_values.is_empty(): return
|
||||
if value == null:
|
||||
return super._set_value(-1)
|
||||
custom_option_values = option_values.duplicate()
|
||||
value = _match_value_to_other(value, custom_option_values.front())
|
||||
if value not in custom_option_values and typeof(value) == property_type:
|
||||
custom_option_values.append(value)
|
||||
custom_option_values.sort()
|
||||
_set_titles_from_values()
|
||||
if value not in option_values:
|
||||
disable_option(custom_option_values.find(value))
|
||||
value = custom_option_values.find(value)
|
||||
return super._set_value(value)
|
||||
|
||||
func _set_option_list(option_titles_list : Array) -> void:
|
||||
%OptionButton.clear()
|
||||
for option_title in option_titles_list:
|
||||
%OptionButton.add_item(option_title)
|
||||
|
||||
func disable_option(option_index : int, disabled : bool = true) -> void:
|
||||
%OptionButton.set_item_disabled(option_index, disabled)
|
||||
|
||||
func _ready() -> void:
|
||||
lock_titles = lock_titles
|
||||
option_titles = option_titles
|
||||
option_values = option_values
|
||||
super._ready()
|
@ -0,0 +1 @@
|
||||
uid://b8xqufg4re3c2
|
@ -0,0 +1,14 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://b6bl3n5mp3m1e"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://d7te75il06t7" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/option_control.tscn" id="1_blo3b"]
|
||||
[ext_resource type="Script" uid="uid://b8xqufg4re3c2" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/list_option_control.gd" id="2_kt4vl"]
|
||||
|
||||
[node name="OptionControl" instance=ExtResource("1_blo3b")]
|
||||
script = ExtResource("2_kt4vl")
|
||||
lock_titles = false
|
||||
option_values = []
|
||||
option_titles = []
|
||||
|
||||
[node name="OptionButton" type="OptionButton" parent="." index="1"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
@ -0,0 +1,140 @@
|
||||
@tool
|
||||
class_name OptionControl
|
||||
extends Control
|
||||
|
||||
signal setting_changed(value)
|
||||
|
||||
enum OptionSections{
|
||||
NONE,
|
||||
INPUT,
|
||||
AUDIO,
|
||||
VIDEO,
|
||||
GAME,
|
||||
APPLICATION,
|
||||
CUSTOM,
|
||||
}
|
||||
|
||||
const OptionSectionNames : Dictionary = {
|
||||
OptionSections.NONE : "",
|
||||
OptionSections.INPUT : AppSettings.INPUT_SECTION,
|
||||
OptionSections.AUDIO : AppSettings.AUDIO_SECTION,
|
||||
OptionSections.VIDEO : AppSettings.VIDEO_SECTION,
|
||||
OptionSections.GAME : AppSettings.GAME_SECTION,
|
||||
OptionSections.APPLICATION : AppSettings.APPLICATION_SECTION,
|
||||
OptionSections.CUSTOM : AppSettings.CUSTOM_SECTION,
|
||||
}
|
||||
|
||||
## Locks config names in case of issues with inherited scenes.
|
||||
## Intentionally put first for initialization.
|
||||
@export var lock_config_names : bool = false
|
||||
## Defines text displayed to the user.
|
||||
@export var option_name : String :
|
||||
set(value):
|
||||
var _update_config : bool = option_name.to_pascal_case() == key and not lock_config_names
|
||||
option_name = value
|
||||
if is_inside_tree():
|
||||
%OptionLabel.text = "%s%s" % [option_name, label_suffix]
|
||||
if _update_config:
|
||||
key = option_name.to_pascal_case()
|
||||
## Defines what section in the config file this option belongs under.
|
||||
@export var option_section : OptionSections :
|
||||
set(value):
|
||||
var _update_config : bool = OptionSectionNames[option_section] == section and not lock_config_names
|
||||
option_section = value
|
||||
if _update_config:
|
||||
section = OptionSectionNames[option_section]
|
||||
|
||||
@export_group("Config Names")
|
||||
## Defines the key for this option variable in the config file.
|
||||
@export var key : String
|
||||
## Defines the section for this option variable in the config file.
|
||||
@export var section : String
|
||||
@export_group("Format")
|
||||
@export var label_suffix : String = " :"
|
||||
@export_group("Properties")
|
||||
## Defines whether the option is editable, or only visible by the user.
|
||||
@export var editable : bool = true : set = set_editable
|
||||
## Defines what kind of variable this option stores in the config file.
|
||||
@export var property_type : Variant.Type = TYPE_BOOL
|
||||
|
||||
## It is advised to use an external editor to set the default value in the scene file.
|
||||
## Godot can experience a bug (caching issue?) that may undo changes.
|
||||
var default_value
|
||||
var _connected_nodes : Array
|
||||
|
||||
func _on_setting_changed(value) -> void:
|
||||
if Engine.is_editor_hint(): return
|
||||
Config.set_config(section, key, value)
|
||||
setting_changed.emit(value)
|
||||
|
||||
func _get_setting(default : Variant = null) -> Variant:
|
||||
return Config.get_config(section, key, default)
|
||||
|
||||
func _connect_option_inputs(node) -> void:
|
||||
if node in _connected_nodes: return
|
||||
if node is Button:
|
||||
if node is OptionButton:
|
||||
node.item_selected.connect(_on_setting_changed)
|
||||
elif node is ColorPickerButton:
|
||||
node.color_changed.connect(_on_setting_changed)
|
||||
else:
|
||||
node.toggled.connect(_on_setting_changed)
|
||||
_connected_nodes.append(node)
|
||||
if node is Range:
|
||||
node.value_changed.connect(_on_setting_changed)
|
||||
_connected_nodes.append(node)
|
||||
if node is LineEdit or node is TextEdit:
|
||||
node.text_changed.connect(_on_setting_changed)
|
||||
_connected_nodes.append(node)
|
||||
|
||||
func _set_value(value : Variant) -> Variant:
|
||||
if value == null:
|
||||
return
|
||||
for node in get_children():
|
||||
if node is Button:
|
||||
if node is OptionButton:
|
||||
node.select(value as int)
|
||||
elif node is ColorPickerButton:
|
||||
node.color = value as Color
|
||||
else:
|
||||
node.button_pressed = value as bool
|
||||
if node is Range:
|
||||
node.value = value as float
|
||||
if node is LineEdit or node is TextEdit:
|
||||
node.text = "%s" % value
|
||||
return value
|
||||
|
||||
func set_value(value : Variant) -> void:
|
||||
value = _set_value(value)
|
||||
_on_setting_changed(value)
|
||||
|
||||
func set_editable(value : bool = true) -> void:
|
||||
editable = value
|
||||
for node in get_children():
|
||||
if node is Button:
|
||||
node.disabled = !editable
|
||||
if node is Slider or node is SpinBox or node is LineEdit or node is TextEdit:
|
||||
node.editable = editable
|
||||
|
||||
func _ready() -> void:
|
||||
lock_config_names = lock_config_names
|
||||
option_section = option_section
|
||||
option_name = option_name
|
||||
property_type = property_type
|
||||
default_value = default_value
|
||||
_set_value(_get_setting(default_value))
|
||||
for child in get_children():
|
||||
_connect_option_inputs(child)
|
||||
child_entered_tree.connect(_connect_option_inputs)
|
||||
|
||||
func _set(property : StringName, value : Variant) -> bool:
|
||||
if property == "value":
|
||||
set_value(value)
|
||||
return true
|
||||
return false
|
||||
|
||||
func _get_property_list() -> Array[Dictionary]:
|
||||
return [
|
||||
{ "name": "value", "type": property_type, "usage": PROPERTY_USAGE_NONE},
|
||||
{ "name": "default_value", "type": property_type}
|
||||
]
|
@ -0,0 +1 @@
|
||||
uid://cafqki2b08kwu
|
@ -0,0 +1,17 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://d7te75il06t7"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cafqki2b08kwu" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/option_control.gd" id="1_jvl5q"]
|
||||
|
||||
[node name="OptionControl" type="HBoxContainer"]
|
||||
custom_minimum_size = Vector2(0, 40)
|
||||
offset_right = 400.0
|
||||
offset_bottom = 40.0
|
||||
script = ExtResource("1_jvl5q")
|
||||
default_value = false
|
||||
|
||||
[node name="OptionLabel" type="Label" parent="."]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = " :"
|
||||
vertical_alignment = 1
|
@ -0,0 +1,19 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://cl416gdb1fgwr"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://d7te75il06t7" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/option_control.tscn" id="1_16hlr"]
|
||||
|
||||
[node name="OptionControl" instance=ExtResource("1_16hlr")]
|
||||
custom_minimum_size = Vector2(0, 28)
|
||||
offset_bottom = 28.0
|
||||
property_type = 3
|
||||
default_value = 1.0
|
||||
|
||||
[node name="HSlider" type="HSlider" parent="." index="1"]
|
||||
custom_minimum_size = Vector2(256, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
max_value = 1.0
|
||||
step = 0.05
|
||||
value = 1.0
|
||||
tick_count = 11
|
||||
ticks_on_borders = true
|
@ -0,0 +1,8 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bsxh6v7j0257h"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://d7te75il06t7" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/option_control.tscn" id="1_8rnmo"]
|
||||
|
||||
[node name="OptionControl" instance=ExtResource("1_8rnmo")]
|
||||
|
||||
[node name="CheckButton" type="CheckButton" parent="." index="1"]
|
||||
layout_mode = 2
|
@ -0,0 +1,9 @@
|
||||
@tool
|
||||
class_name Vector2ListOptionControl
|
||||
extends ListOptionControl
|
||||
|
||||
func _value_title_map(value : Variant) -> String:
|
||||
if value is Vector2 or value is Vector2i:
|
||||
return "%d x %d" % [value.x , value.y]
|
||||
else:
|
||||
return super._value_title_map(value)
|
@ -0,0 +1 @@
|
||||
uid://brntdgf3sv0s0
|
@ -0,0 +1,7 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://c01ayjblhcg1t"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://b6bl3n5mp3m1e" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/list_option_control.tscn" id="1_jqwiw"]
|
||||
[ext_resource type="Script" uid="uid://brntdgf3sv0s0" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/vector_2_list_option_control.gd" id="2_w33vs"]
|
||||
|
||||
[node name="OptionControl" instance=ExtResource("1_jqwiw")]
|
||||
script = ExtResource("2_w33vs")
|
@ -0,0 +1,38 @@
|
||||
class_name VideoOptionsMenu
|
||||
extends Control
|
||||
|
||||
func _preselect_resolution(window : Window) -> void:
|
||||
%ResolutionControl.value = window.size
|
||||
|
||||
func _update_resolution_options_enabled(window : Window) -> void:
|
||||
if OS.has_feature("web"):
|
||||
%ResolutionControl.editable = false
|
||||
%ResolutionControl.tooltip_text = "Disabled for web"
|
||||
elif AppSettings.is_fullscreen(window):
|
||||
%ResolutionControl.editable = false
|
||||
%ResolutionControl.tooltip_text = "Disabled for fullscreen"
|
||||
else:
|
||||
%ResolutionControl.editable = true
|
||||
%ResolutionControl.tooltip_text = "Select a screen size"
|
||||
|
||||
func _update_ui(window : Window) -> void:
|
||||
%FullscreenControl.value = AppSettings.is_fullscreen(window)
|
||||
_preselect_resolution(window)
|
||||
%VSyncControl.value = AppSettings.get_vsync(window)
|
||||
_update_resolution_options_enabled(window)
|
||||
|
||||
func _ready() -> void:
|
||||
var window : Window = get_window()
|
||||
_update_ui(window)
|
||||
window.connect("size_changed", _preselect_resolution.bind(window))
|
||||
|
||||
func _on_fullscreen_control_setting_changed(value) -> void:
|
||||
var window : Window = get_window()
|
||||
AppSettings.set_fullscreen_enabled(value, window)
|
||||
_update_resolution_options_enabled(window)
|
||||
|
||||
func _on_resolution_control_setting_changed(value) -> void:
|
||||
AppSettings.set_resolution(value, get_window(), false)
|
||||
|
||||
func _on_v_sync_control_setting_changed(value) -> void:
|
||||
AppSettings.set_vsync(value, get_window())
|
@ -0,0 +1 @@
|
||||
uid://cpe5r24151r5n
|
@ -0,0 +1,60 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://b2numvphf2kau"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cpe5r24151r5n" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/video/video_options_menu.gd" id="1"]
|
||||
[ext_resource type="Script" uid="uid://1nf36h0gms3q" path="res://addons/maaacks_menus_template/base/scripts/capture_focus.gd" id="2_dgrai"]
|
||||
[ext_resource type="PackedScene" uid="uid://bsxh6v7j0257h" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/toggle_option_control.tscn" id="3_uded6"]
|
||||
[ext_resource type="PackedScene" uid="uid://c01ayjblhcg1t" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/vector_2_list_option_control.tscn" id="4_gwtfq"]
|
||||
[ext_resource type="PackedScene" uid="uid://b6bl3n5mp3m1e" path="res://addons/maaacks_menus_template/base/scenes/menus/options_menu/option_control/list_option_control.tscn" id="5_881de"]
|
||||
|
||||
[node name="Video" type="MarginContainer"]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
theme_override_constants/margin_top = 24
|
||||
theme_override_constants/margin_bottom = 24
|
||||
script = ExtResource("1")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
custom_minimum_size = Vector2(400, 0)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 4
|
||||
alignment = 1
|
||||
script = ExtResource("2_dgrai")
|
||||
search_depth = 2
|
||||
|
||||
[node name="FullscreenControl" parent="VBoxContainer" instance=ExtResource("3_uded6")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
option_name = "Fullscreen"
|
||||
option_section = 3
|
||||
key = "FullscreenEnabled"
|
||||
section = "VideoSettings"
|
||||
|
||||
[node name="ResolutionControl" parent="VBoxContainer" instance=ExtResource("4_gwtfq")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
tooltip_text = "Select a screen size"
|
||||
option_values = [Vector2i(640, 360), Vector2i(960, 540), Vector2i(1024, 576), Vector2i(1280, 720), Vector2i(1600, 900), Vector2i(1920, 1080), Vector2i(2048, 1152), Vector2i(2560, 1440), Vector2i(3200, 1800), Vector2i(3840, 2160)]
|
||||
option_titles = Array[String](["640 x 360", "960 x 540", "1024 x 576", "1280 x 720", "1600 x 900", "1920 x 1080", "2048 x 1152", "2560 x 1440", "3200 x 1800", "3840 x 2160"])
|
||||
option_name = "Resolution"
|
||||
option_section = 3
|
||||
key = "ScreenResolution"
|
||||
section = "VideoSettings"
|
||||
property_type = 6
|
||||
|
||||
[node name="VSyncControl" parent="VBoxContainer" instance=ExtResource("5_881de")]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
lock_titles = true
|
||||
option_values = [0, 1, 2, 3]
|
||||
option_titles = Array[String](["Disabled", "Enabled", "Adaptive", "Mailbox"])
|
||||
option_name = "V-Sync"
|
||||
option_section = 3
|
||||
key = "V-sync"
|
||||
section = "VideoSettings"
|
||||
property_type = 2
|
||||
default_value = 0
|
||||
|
||||
[connection signal="setting_changed" from="VBoxContainer/FullscreenControl" to="." method="_on_fullscreen_control_setting_changed"]
|
||||
[connection signal="setting_changed" from="VBoxContainer/ResolutionControl" to="." method="_on_resolution_control_setting_changed"]
|
||||
[connection signal="setting_changed" from="VBoxContainer/VSyncControl" to="." method="_on_v_sync_control_setting_changed"]
|
Reference in New Issue
Block a user