fix: stabilize controller menu navigation

This commit is contained in:
Alexander Sellite 2026-08-16 19:57:38 -04:00
parent 5df8b2e7cc
commit a8be597d27
32 changed files with 910 additions and 2376 deletions

View file

@ -1,8 +1,8 @@
class_name InterfaceFontController
extends Node
const FileDialogControllerNavigationType = preload(
"res://ui/file_dialog_controller_navigation.gd"
const FileDialogControllerType = preload(
"res://ui/file_dialog_controller.gd"
)
const STANDARD_FONT: Font = preload("res://ui/fonts/Tuffy_Bold.otf")
const COMPACT_FILE_DIALOG_LIMIT := Vector2i(800, 600)
@ -14,13 +14,19 @@ var _controller_text_entry_is_open: Callable
var _game_theme: Theme = preload("res://ui/game_theme.tres")
var _utility_theme: Theme
var _compact_file_dialog_theme: Theme
var _tracked_file_dialogs: Dictionary[int, WeakRef] = {}
var _file_dialog_controller: FileDialogControllerType
func _ready() -> void:
enforce_standard_font()
_utility_theme = _game_theme.duplicate(true)
_utility_theme.default_font = STANDARD_FONT
_file_dialog_controller = FileDialogControllerType.new()
add_child(_file_dialog_controller)
_file_dialog_controller.setup_text_entry(
_controller_text_entry_request,
_controller_text_entry_is_open,
)
func enforce_standard_font() -> void:
@ -35,6 +41,8 @@ func set_controller_text_entry_request(
) -> void:
_controller_text_entry_request = request
_controller_text_entry_is_open = is_open
if _file_dialog_controller != null:
_file_dialog_controller.setup_text_entry(request, is_open)
func set_readable_font_enabled(_enabled: bool) -> void:
@ -61,7 +69,7 @@ func apply_utility_theme(themed_node: Node) -> void:
func popup_file_dialog(dialog: FileDialog) -> void:
if dialog == null or not dialog.is_inside_tree():
return
_tracked_file_dialogs[dialog.get_instance_id()] = weakref(dialog)
_file_dialog_controller.track_dialog(dialog)
var host_window: Window = dialog.get_parent().get_window()
var host_size: Vector2i = host_window.size
var compact: bool = (
@ -98,113 +106,6 @@ func popup_file_dialog(dialog: FileDialog) -> void:
)
func _process(_delta: float) -> void:
if (
_controller_text_entry_is_open.is_valid()
and bool(_controller_text_entry_is_open.call())
):
return
for instance_id: int in _tracked_file_dialogs.keys():
var reference: WeakRef = _tracked_file_dialogs[instance_id]
var dialog := reference.get_ref() as FileDialog
if dialog == null or not is_instance_valid(dialog):
_tracked_file_dialogs.erase(instance_id)
continue
if not dialog.visible:
continue
FileDialogControllerNavigationType.configure(dialog)
_connect_file_dialog_text_controls(dialog)
func _input(event: InputEvent) -> void:
if (
_controller_text_entry_is_open.is_valid()
and bool(_controller_text_entry_is_open.call())
):
return
if not (event is InputEventJoypadButton or event is InputEventJoypadMotion):
return
var scope: Window = _active_file_dialog_scope()
if scope == null:
return
var focused: Control = scope.gui_get_focus_owner()
if focused == null:
return
var direction: Vector2 = _controller_direction(event)
var item_list := focused as ItemList
if (
item_list != null
and direction != Vector2.ZERO
and FileDialogControllerNavigationType.move_from_item_list(
item_list, direction
)
):
get_viewport().set_input_as_handled()
return
var button_event := event as InputEventJoypadButton
if (
button_event != null
and button_event.pressed
and event.is_action_pressed(&"ui_accept")
and (focused is LineEdit or focused is TextEdit)
and _controller_text_entry_request.is_valid()
and bool(_controller_text_entry_request.call(focused))
):
get_viewport().set_input_as_handled()
func _active_file_dialog_scope() -> Window:
for reference_value: WeakRef in _tracked_file_dialogs.values():
var dialog := reference_value.get_ref() as FileDialog
if dialog == null or not is_instance_valid(dialog) or not dialog.visible:
continue
return FileDialogControllerNavigationType.active_scope(dialog)
return null
func _controller_direction(event: InputEvent) -> Vector2:
if event.is_action_pressed(&"ui_up"):
return Vector2.UP
if event.is_action_pressed(&"ui_down"):
return Vector2.DOWN
if event.is_action_pressed(&"ui_left"):
return Vector2.LEFT
if event.is_action_pressed(&"ui_right"):
return Vector2.RIGHT
return Vector2.ZERO
func _connect_file_dialog_text_controls(dialog: FileDialog) -> void:
var scope: Window = FileDialogControllerNavigationType.active_scope(dialog)
for control: Control in (
FileDialogControllerNavigationType.interactive_controls(scope)
):
if not (control is LineEdit or control is TextEdit):
continue
var callback := _on_file_dialog_text_gui_input.bind(control)
if not control.gui_input.is_connected(callback):
control.gui_input.connect(callback)
func _on_file_dialog_text_gui_input(
event: InputEvent,
control: Control,
) -> void:
var button_event := event as InputEventJoypadButton
if (
button_event == null
or not button_event.pressed
or (
button_event.button_index != JOY_BUTTON_A
and not event.is_action_pressed("ui_accept")
)
or not _controller_text_entry_request.is_valid()
):
return
if bool(_controller_text_entry_request.call(control)):
control.accept_event()
func _finalize_compact_file_dialog(
dialog: FileDialog,
host_size: Vector2i,