This commit is contained in:
2026-02-23 11:37:27 +01:00
commit 13dbb551c8
94 changed files with 2682 additions and 0 deletions

View File

@@ -0,0 +1 @@
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8 9H1V2l2 2a3.875 5 30 0 1 9 11 3.5 5 10 0 0-6-8z" fill="#e0e0e0"/></svg>

After

Width:  |  Height:  |  Size: 167 B

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b23e4kqj4o6dv"
path="res://.godot/imported/Revert.svg-83732f7446608123e95cc2f2a2cf35c6.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://ui/components/settings-menu/Revert.svg"
dest_files=["res://.godot/imported/Revert.svg-83732f7446608123e95cc2f2a2cf35c6.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=2.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,33 @@
extends Button
var event_idx = 0
signal action_changed(action: String)
func _ready():
set_process_unhandled_key_input(false)
func _toggled(b_pressed):
set_process_unhandled_key_input(b_pressed)
if b_pressed:
text = "..."
release_focus()
func _input(event):
if not button_pressed:
return
if event is InputEventKey or event is InputEventJoypadButton or event is InputEventMouseButton:
action_changed.emit(event)
get_viewport().set_input_as_handled()
button_pressed = false
func _display_event(event: InputEvent):
if event == null:
text = "Unassigned"
else:
text = event.as_text()
tooltip_text = text
func set_event(event: InputEvent):
_display_event(event)

View File

@@ -0,0 +1,9 @@
[gd_scene format=3 uid="uid://ngjm8kk8u7fm"]
[ext_resource type="Script" uid="uid://b7fb4ravo24q0" path="res://ui/components/settings-menu/action-remapping-button/action_remapping_button.gd" id="1_jb8u1"]
[node name="InputRemappingButton" type="Button" unique_id=740968636]
custom_minimum_size = Vector2(200, 0)
toggle_mode = true
text_overrun_behavior = 3
script = ExtResource("1_jb8u1")

View File

@@ -0,0 +1,184 @@
extends TabContainer
@onready var action_remapping_button_scene: PackedScene = load("res://ui/components/settings-menu/action-remapping-button/action_remapping_button.tscn")
func _ready() -> void:
call_deferred("_populate_menu")
func _populate_menu() -> void:
var tab_idx = 0
# Iterate through root categories (these become tabs)
for root_category in Settings.get_root_categories():
var margin_container = MarginContainer.new()
add_child(margin_container)
set_tab_title(tab_idx, root_category.display_text)
var scroll_container = ScrollContainer.new()
scroll_container.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
margin_container.add_child(scroll_container)
var margin_container2 = MarginContainer.new()
margin_container2.size_flags_horizontal = Control.SIZE_EXPAND_FILL
margin_container2.size_flags_vertical = Control.SIZE_EXPAND_FILL
scroll_container.add_child(margin_container2)
var cat_vbox = VBoxContainer.new()
cat_vbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
cat_vbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
margin_container2.add_child(cat_vbox)
# Add subcategories as sections, and their settings
_populate_category(cat_vbox, root_category, root_category.name)
tab_idx += 1
## Recursively populate a category and its subcategories
## Subcategories become section headers, deeper ones are flattened
func _populate_category(container: VBoxContainer, category: Settings.SettingCategory, path_prefix: String, depth: int = 0) -> void:
# If this is a subcategory (depth > 0), add a section header
if depth > 0:
var lbl_section_title = Label.new()
lbl_section_title.text = category.display_text
lbl_section_title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
lbl_section_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
lbl_section_title.theme_type_variation = "SettingsGroupLabel"
container.add_child(lbl_section_title)
container.add_child(HSeparator.new())
# Add all settings in this category
for setting in category.settings:
_add_setting_ui(container, setting, path_prefix + "/" + setting.name)
# Recursively add all subcategories
for sub_category in category.sub_categories:
var sub_path = path_prefix + "/" + sub_category.name
_populate_category(container, sub_category, sub_path, depth + 1)
func _add_setting_ui(container: VBoxContainer, setting: Settings.Setting, setting_path: String) -> void:
var hbox = HBoxContainer.new()
hbox.custom_minimum_size = Vector2(0, 40)
container.add_child(hbox)
# Another HBoxContainer for the label and revert button
var hbox_lbl = HBoxContainer.new()
hbox_lbl.size_flags_horizontal = Control.SIZE_EXPAND_FILL
hbox.add_child(hbox_lbl)
# Label for the name of the setting
var lbl_name = Label.new()
lbl_name.text = setting.display_text
lbl_name.size_flags_horizontal = Control.SIZE_EXPAND_FILL
hbox_lbl.add_child(lbl_name)
# Revert button for changing the setting back to its default value
# For input bindings a new button for each remapping is created later
if not setting is Settings.InputRemappingSetting:
var revert_btn = Button.new()
revert_btn.icon = get_theme_icon("icon", "RevertButton")
revert_btn.custom_minimum_size = Vector2(32, 32)
revert_btn.theme_type_variation = "FlatButton"
revert_btn.connect("pressed", func(): Settings.reset_value(setting_path))
hbox_lbl.add_child(revert_btn)
setting.value_changed.connect(func(__): _update_revert_button(setting_path, revert_btn))
_update_revert_button(setting_path, revert_btn)
# TODO: Tooltip
# Setting-specific editor
if setting is Settings.FloatSetting:
var slider_setting = setting as Settings.FloatSetting
var slider = HSlider.new()
slider.min_value = slider_setting.min_value
slider.max_value = slider_setting.max_value
slider.step = slider_setting.step
slider.value = slider_setting.value
slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL
slider.size_flags_vertical = Control.SIZE_FILL
slider.connect("value_changed", setting.set_value)
setting.value_changed.connect(slider.set_value_no_signal)
hbox.add_child(slider)
elif setting is Settings.OptionSetting:
var dropdown_setting = setting as Settings.OptionSetting
var option_button = OptionButton.new()
for i in range(dropdown_setting.options.size()):
option_button.add_item(dropdown_setting.options[i], i)
# Find the index of the current value
var current_idx = dropdown_setting.options.find(dropdown_setting.value)
if current_idx >= 0:
option_button.selected = current_idx
option_button.connect("item_selected", func(idx: int): setting.set_value(dropdown_setting.options[idx]))
setting.value_changed.connect(func(val): option_button.select(dropdown_setting.options.find(val)))
hbox.add_child(option_button)
elif setting is Settings.BoolSetting:
var checkbox = CheckBox.new()
checkbox.button_pressed = setting.value
checkbox.connect("toggled", setting.set_value)
setting.value_changed.connect(checkbox.set_pressed_no_signal)
hbox.add_child(checkbox)
elif setting is Settings.InputRemappingSetting:
_add_input_remapping_ui(hbox, setting as Settings.InputRemappingSetting, setting_path)
else:
# Assume it's a basic boolean setting if it has a bool value
if typeof(setting.value) == TYPE_BOOL:
var checkbox = CheckBox.new()
checkbox.button_pressed = setting.value
checkbox.connect("toggled", setting.set_value)
setting.value_changed.connect(checkbox.set_pressed_no_signal)
hbox.add_child(checkbox)
else:
push_warning("Unknown setting type for: " + setting.display_text)
func _add_input_remapping_ui(hbox: HBoxContainer, setting: Settings.InputRemappingSetting, setting_path: String) -> void:
var events = setting.value as Array[InputEvent]
# First reset button
var input_revert_btn_1 = Button.new()
input_revert_btn_1.icon = get_theme_icon("icon", "RevertButton")
input_revert_btn_1.custom_minimum_size = Vector2(32, 32)
input_revert_btn_1.theme_type_variation = "FlatButton"
input_revert_btn_1.connect("pressed", func(): Settings.reset_input_binding(setting_path, 0))
hbox.add_child(input_revert_btn_1)
setting.value_changed.connect(func(__): _update_input_mapping_revert_button(setting_path, 0, input_revert_btn_1))
_update_input_mapping_revert_button(setting_path, 0, input_revert_btn_1)
# First remapping button
var remapping_btn1 = action_remapping_button_scene.instantiate()
remapping_btn1.set_event(events[0] if events.size() > 0 else null)
remapping_btn1.connect("action_changed", func(event: InputEvent): _keymap_changed(event, 0, setting_path))
setting.value_changed.connect(func(value: Array[InputEvent]): _update_remapping_button(value, 0, remapping_btn1))
hbox.add_child(remapping_btn1)
# Second reset button
var input_revert_btn_2 = Button.new()
input_revert_btn_2.icon = get_theme_icon("icon", "RevertButton")
input_revert_btn_2.custom_minimum_size = Vector2(32, 32)
input_revert_btn_2.theme_type_variation = "FlatButton"
input_revert_btn_2.connect("pressed", func(): Settings.reset_input_binding(setting_path, 1))
hbox.add_child(input_revert_btn_2)
setting.value_changed.connect(func(__): _update_input_mapping_revert_button(setting_path, 1, input_revert_btn_2))
_update_input_mapping_revert_button(setting_path, 1, input_revert_btn_2)
# Second remapping button
var remapping_btn2 = action_remapping_button_scene.instantiate()
remapping_btn2.set_event(events[1] if events.size() > 1 else null)
remapping_btn2.connect("action_changed", func(event: InputEvent): _keymap_changed(event, 1, setting_path))
setting.value_changed.connect(func(value: Array[InputEvent]): _update_remapping_button(value, 1, remapping_btn2))
hbox.add_child(remapping_btn2)
func _keymap_changed(event: InputEvent, event_idx: int, setting_path: String) -> void:
var events = Settings.get_value(setting_path).duplicate()
if event_idx >= events.size():
events.push_back(null)
events[event_idx] = event
print("Keymap changed " + str(event_idx) + ":" + str(events))
Settings.set_value(setting_path, events)
func _update_revert_button(p_setting_path: String, button: Button) -> void:
button.visible = not Settings.is_value_default(p_setting_path)
func _update_remapping_button(value: Array[InputEvent], index: int, btn: Button):
btn.set_event(value[index] if value.size() > index else null)
func _update_input_mapping_revert_button(p_setting_path: String, index: int, button: Button) -> void:
button.modulate = Color.WHITE if not Settings.is_input_binding_default(p_setting_path, index) else Color.TRANSPARENT

View File

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

View File

@@ -0,0 +1,12 @@
[gd_scene format=3 uid="uid://cv271fh4d2p13"]
[ext_resource type="Script" uid="uid://ij8xcd6np4vt" path="res://ui/components/settings-menu/settings_menu.gd" id="1_eae2p"]
[ext_resource type="Theme" uid="uid://hheneshfv1b2" path="res://ui/themes/your_theme.tres" id="1_scglv"]
[node name="SettingsMenu" type="TabContainer" unique_id=1538995254]
clip_contents = true
custom_minimum_size = Vector2(600, 400)
size_flags_vertical = 3
theme = ExtResource("1_scglv")
tab_alignment = 1
script = ExtResource("1_eae2p")