Improve interface accessibility and UI reliability

This commit is contained in:
Alexander Sellite 2026-07-30 00:54:53 -04:00
parent ba4476511f
commit 3734d4a168
29 changed files with 514 additions and 224 deletions

View file

@ -0,0 +1,11 @@
We, the copyright holders of this work, hereby release it into the
public domain. This applies worldwide.
In case this is not legally possible,
We grant any entity the right to use this work for any purpose, without
any conditions, unless such conditions are required by law.
Thatcher Ulrich <tu@tulrich.com> http://tulrich.com
Karoly Barta bartakarcsi@gmail.com
Michael Evans http://www.evertype.com

BIN
ui/fonts/Tuffy_Bold.otf Normal file

Binary file not shown.

View file

@ -0,0 +1,36 @@
[remap]
importer="font_data_dynamic"
type="FontFile"
uid="uid://xhdms1j01k2q"
path="res://.godot/imported/Tuffy_Bold.otf-1ed903e9af3906035b0cf09ea141ce25.fontdata"
[deps]
source_file="res://ui/fonts/Tuffy_Bold.otf"
dest_files=["res://.godot/imported/Tuffy_Bold.otf-1ed903e9af3906035b0cf09ea141ce25.fontdata"]
[params]
Rendering=null
antialiasing=1
generate_mipmaps=false
disable_embedded_bitmaps=true
multichannel_signed_distance_field=false
msdf_pixel_range=8
msdf_size=48
allow_system_fallback=true
force_autohinter=false
modulate_color_glyphs=false
hinting=3
subpixel_positioning=4
keep_rounding_remainders=true
oversampling=0.0
Fallbacks=null
fallbacks=[]
Compress=null
compress=true
preload=[]
language_support={}
script_support={}
opentype_features={}

View file

@ -184,6 +184,7 @@ func setup_data_and_identity(
player_identity: PlayerIdentityStore,
host_identity: HostIdentityStore,
network_session: NetworkSession,
interface_fonts: InterfaceFontController,
) -> void:
for panel: SettingsPanelType in [
_title_settings_panel, _pause_settings_panel
@ -194,6 +195,7 @@ func setup_data_and_identity(
player_identity,
host_identity,
network_session,
interface_fonts,
)
@ -219,7 +221,7 @@ func _process(_delta: float) -> void:
}.get(item_id, "")
parts.append(
"%s %d:%02d"
% [label, int(remaining) / 60, int(remaining) % 60]
% [label, floori(remaining / 60.0), int(remaining) % 60]
)
_effect_status.text = " ".join(parts)
_effect_status.visible = not parts.is_empty()

View file

@ -0,0 +1,55 @@
class_name InterfaceFontController
extends Node
signal font_mode_changed(use_readable_font: bool)
const READABLE_FONT_PATH: String = "res://ui/fonts/Tuffy_Bold.otf"
var _game_theme: Theme = preload("res://ui/game_theme.tres")
var _game_font: Font = preload("res://ui/fonts/seattle_avenue.otf")
var _readable_font: Font
var _utility_theme: Theme
var _use_readable_font: bool = false
func _ready() -> void:
if ResourceLoader.exists(READABLE_FONT_PATH):
_readable_font = load(READABLE_FONT_PATH) as Font
if _readable_font == null:
push_error("Readable interface font resource is unavailable.")
else:
_readable_font.fallbacks = [_game_font]
_utility_theme = _game_theme.duplicate(true)
_utility_theme.default_font = readable_font()
func set_readable_font_enabled(enabled: bool) -> void:
_use_readable_font = enabled
_game_theme.default_font = (
_readable_font
if enabled and _readable_font != null
else _game_font
)
font_mode_changed.emit(_use_readable_font)
func is_readable_font_enabled() -> bool:
return _use_readable_font
func apply_utility_theme(themed_node: Node) -> void:
if themed_node == null:
return
if _utility_theme == null:
_utility_theme = _game_theme.duplicate(true)
_utility_theme.default_font = (
_readable_font if _readable_font != null else _game_font
)
if themed_node is Control:
(themed_node as Control).theme = _utility_theme
elif themed_node is Window:
(themed_node as Window).theme = _utility_theme
func readable_font() -> Font:
return _readable_font if _readable_font != null else _game_font

View file

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

View file

@ -194,8 +194,6 @@ enum CloseReason {
@onready var _selection_status: BubbleStatusBubbleType = %SelectionStatus
@onready var _offer_status: BubbleStatusBubbleType = %OfferStatus
@onready var _inventory_section: Control = %InventorySection
@onready var _inventory_body: BoxContainer = %InventoryBody
@onready var _inventory_scroll: ScrollContainer = %InventoryScroll
@onready var _bag_section: Control = %BagSection
@onready var _logbook_section: Control = %LogbookSection
@onready var _bag_empty: Label = %BagEmpty
@ -210,9 +208,6 @@ enum CloseReason {
@onready var _held_value: Label = %HeldValue
@onready var _cooler_count: Label = %CoolerCount
@onready var _inventory_empty: Label = %InventoryEmpty
@onready var _inventory_grid: GridContainer = %InventoryGrid
@onready var _inventory_list: Control = %InventoryList
@onready var _detail_panel: Control = %DetailPanel
@onready var _detail_texture: TextureRect = %DetailTexture
@onready var _detail_name: Label = %DetailName
@onready var _detail_data: Label = %DetailData
@ -227,8 +222,6 @@ enum CloseReason {
@onready var _cancel_sale_button: Button = %CancelSaleButton
@onready var _logbook_empty: Label = %LogbookEmpty
@onready var _logbook_grid: GridContainer = %LogbookGrid
@onready var _status_shoal: HBoxContainer = %StatusShoal
@onready var _sort_bar: HBoxContainer = %SortBar
var _compact_layout: bool = false
var _player: PlayerType
@ -1181,6 +1174,7 @@ func _update_shell_layout() -> void:
_logbook_page.size = reference_size
_logbook_page.position = Vector2.ZERO
_logbook_rest_position = Vector2.ZERO
_mail_page.set_anchors_preset(Control.PRESET_TOP_LEFT)
_mail_page.size = reference_size
_mail_page.position = Vector2.ZERO
_mail_rest_position = Vector2.ZERO
@ -1732,7 +1726,10 @@ func _reset_page_transition_visuals() -> void:
func _on_sort_selected(index: int, source: OptionButton) -> void:
_sort_mode = source.get_item_id(index)
var selected_id: int = source.get_item_id(index)
if selected_id < SortMode.CATCH_ORDER or selected_id > SortMode.RARITY:
return
_sort_mode = selected_id as SortMode
_sort_option.select(_sort_mode)
_cooler_sort_option.select(_sort_mode)
_refresh_inventory()
@ -1891,7 +1888,7 @@ func _layout_bag_items() -> void:
var row: int = floori(float(index) / float(columns))
var stable_offset := Vector2(
float(identity_hash % 13) - 6.0,
float((identity_hash / 19) % 11) - 5.0,
float(floori(float(identity_hash) / 19.0) % 11) - 5.0,
)
var lane_offset: float = 18.0 if row % 2 == 1 else 0.0
item_node.custom_minimum_size = item_size
@ -2225,7 +2222,7 @@ func _layout_cooler_fish(animate: bool = true) -> void:
var offset_center: float = 2.0 if _compact_layout else 3.0
var stable_offset := Vector2(
float(identity_hash % offset_span) - offset_center,
float((identity_hash / 17) % offset_span) - offset_center,
float(floori(float(identity_hash) / 17.0) % offset_span) - offset_center,
)
var lane_offset: float = 5.0 if row % 2 == 1 else 0.0
var target := Vector2(

View file

@ -48,6 +48,7 @@ enum PresentationMode {
@onready var _invert_y_toggle: BubbleButton = %InvertYToggle
@onready var _auto_click_toggle: BubbleButton = %AutoClickToggle
@onready var _auto_click_interval: BubbleButton = %AutoClickIntervalValue
@onready var _readable_font_toggle: BubbleButton = %ReadableFontToggle
@onready var _world_options: Array[BubbleButton] = [
%WorldLegible,
@ -83,6 +84,7 @@ var _data_root: PlayerDataRoot
var _identity_backups: IdentityBackupService
var _player_identity: PlayerIdentityStore
var _host_identity: HostIdentityStore
var _interface_fonts: InterfaceFontController
var _data_folder_dialog: FileDialog
var _backup_file_dialog: FileDialog
var _export_file_dialog: FileDialog
@ -148,6 +150,7 @@ func _ready() -> void:
)
_invert_y_toggle.pressed.connect(_toggle_invert_y)
_auto_click_toggle.pressed.connect(_toggle_auto_click)
_readable_font_toggle.pressed.connect(_toggle_readable_font)
%IntervalDecrease.pressed.connect(_adjust_auto_click_interval.bind(-1))
%IntervalIncrease.pressed.connect(_adjust_auto_click_interval.bind(1))
_auto_click_interval.pressed.connect(
@ -185,13 +188,16 @@ func setup_data_and_identity(
player_identity: PlayerIdentityStore,
host_identity: HostIdentityStore,
session: NetworkSession,
interface_fonts: InterfaceFontController,
) -> void:
_data_root = data_root
_identity_backups = identity_backups
_player_identity = player_identity
_host_identity = host_identity
_network_session = session
_interface_fonts = interface_fonts
_identity_backups.operation_finished.connect(_on_identity_operation_finished)
_interface_fonts.apply_utility_theme(_data_page)
_refresh_data_page()
@ -410,14 +416,18 @@ func _choose_data_folder() -> void:
_data_folder_dialog = FileDialog.new()
_data_folder_dialog.file_mode = FileDialog.FILE_MODE_OPEN_DIR
_data_folder_dialog.access = FileDialog.ACCESS_FILESYSTEM
_data_folder_dialog.use_native_dialog = false
_data_folder_dialog.dir_selected.connect(_change_data_folder)
_interface_fonts.apply_utility_theme(_data_folder_dialog)
add_child(_data_folder_dialog)
_data_folder_dialog.current_dir = _data_root.root_path.get_base_dir()
_data_folder_dialog.popup_centered_ratio(0.75)
func _change_data_folder(path: String) -> void:
var result := PortableDataMigration.migrate_active_to(_data_root, path)
var result: Dictionary = PortableDataMigration.migrate_active_to(
_data_root, path
)
if bool(result.get("requires_existing_root_decision", false)):
_show_existing_data_folder_choice(path)
return
@ -430,7 +440,7 @@ func _change_data_folder(path: String) -> void:
func _show_existing_data_folder_choice(path: String) -> void:
var dialog := ConfirmationDialog.new()
var dialog: ConfirmationDialog = ConfirmationDialog.new()
dialog.title = "Existing NETFISHING data"
dialog.ok_button_text = "Use Selected Data"
dialog.dialog_text = (
@ -449,7 +459,7 @@ func _show_existing_data_folder_choice(path: String) -> void:
dialog.custom_action.connect(func(action: StringName) -> void:
if action != &"replace":
return
var result := PortableDataMigration.replace_existing_with_active(
var result: Dictionary = PortableDataMigration.replace_existing_with_active(
_data_root, path
)
_feedback.text = str(result.get("message", "Could not replace selected data."))
@ -457,6 +467,7 @@ func _show_existing_data_folder_choice(path: String) -> void:
get_tree().call_deferred("quit")
dialog.queue_free()
)
_interface_fonts.apply_utility_theme(dialog)
add_child(dialog)
dialog.popup_centered(Vector2i(620, 340))
@ -466,13 +477,15 @@ func _choose_identity_export(identity_type: String) -> void:
return
_pending_identity_operation = "export"
_pending_identity_type = identity_type
var suggested := _identity_backups.default_export_path(identity_type)
var suggested: String = _identity_backups.default_export_path(identity_type)
if _export_file_dialog == null:
_export_file_dialog = FileDialog.new()
_export_file_dialog.file_mode = FileDialog.FILE_MODE_SAVE_FILE
_export_file_dialog.access = FileDialog.ACCESS_FILESYSTEM
_export_file_dialog.use_native_dialog = false
_export_file_dialog.filters = PackedStringArray(["*.nfidentity ; NETFISHING identity backup"])
_export_file_dialog.file_selected.connect(_identity_export_file_selected)
_interface_fonts.apply_utility_theme(_export_file_dialog)
add_child(_export_file_dialog)
_export_file_dialog.current_dir = suggested.get_base_dir()
_export_file_dialog.current_file = suggested.get_file()
@ -495,8 +508,10 @@ func _choose_identity_import(identity_type: String) -> void:
_backup_file_dialog = FileDialog.new()
_backup_file_dialog.file_mode = FileDialog.FILE_MODE_OPEN_FILE
_backup_file_dialog.access = FileDialog.ACCESS_FILESYSTEM
_backup_file_dialog.use_native_dialog = false
_backup_file_dialog.filters = PackedStringArray(["*.nfidentity ; NETFISHING identity backup"])
_backup_file_dialog.file_selected.connect(_identity_import_file_selected)
_interface_fonts.apply_utility_theme(_backup_file_dialog)
add_child(_backup_file_dialog)
_backup_file_dialog.current_dir = _data_root.identity_backup_directory()
_backup_file_dialog.popup_centered_ratio(0.75)
@ -512,8 +527,8 @@ func _show_passphrase_dialog(exporting: bool) -> void:
_passphrase_dialog = ConfirmationDialog.new()
_passphrase_dialog.title = "Encrypted identity backup"
_passphrase_dialog.confirmed.connect(_submit_identity_passphrase)
var fields := VBoxContainer.new()
var warning := Label.new()
var fields: VBoxContainer = VBoxContainer.new()
var warning: Label = Label.new()
warning.text = (
"Anyone with this backup and passphrase can use your identity."
)
@ -528,6 +543,7 @@ func _show_passphrase_dialog(exporting: bool) -> void:
_passphrase_confirm.secret = true
fields.add_child(_passphrase_confirm)
_passphrase_dialog.add_child(fields)
_interface_fonts.apply_utility_theme(_passphrase_dialog)
add_child(_passphrase_dialog)
_passphrase_confirm.visible = exporting
_passphrase_entry.clear()
@ -541,7 +557,7 @@ func _show_passphrase_dialog(exporting: bool) -> void:
func _submit_identity_passphrase() -> void:
var passphrase := _passphrase_entry.text
var passphrase: String = _passphrase_entry.text
if _pending_identity_operation == "export":
_identity_backups.export_backup(
_pending_identity_type,
@ -550,7 +566,7 @@ func _submit_identity_passphrase() -> void:
_passphrase_confirm.text,
)
return
var inspected := _identity_backups.import_backup(
var inspected: Dictionary = _identity_backups.import_backup(
_pending_identity_type,
_pending_identity_path,
passphrase,
@ -566,7 +582,7 @@ func _submit_identity_passphrase() -> void:
func _show_identity_replacement_confirmation() -> void:
var dialog := ConfirmationDialog.new()
var dialog: ConfirmationDialog = ConfirmationDialog.new()
dialog.title = "Replace active identity?"
dialog.ok_button_text = "Review Replacement"
dialog.dialog_text = (
@ -579,6 +595,7 @@ func _show_identity_replacement_confirmation() -> void:
dialog.set_meta("confirmation_step", 1)
dialog.confirmed.connect(_advance_identity_replacement.bind(dialog))
dialog.canceled.connect(dialog.queue_free)
_interface_fonts.apply_utility_theme(dialog)
add_child(dialog)
dialog.popup_centered(Vector2i(620, 360))
@ -659,6 +676,9 @@ func _load_controls() -> void:
_mouse_sensitivity = settings.mouse_camera_sensitivity
_controller_sensitivity = settings.controller_camera_sensitivity
_invert_camera_y = settings.invert_camera_y
_readable_font_toggle.button_pressed = (
settings.use_readable_interface_font
)
_refresh_value_labels()
@ -686,6 +706,14 @@ func _refresh_value_labels() -> void:
"accessibility\nauto-click\n"
+ ("on" if _auto_click_enabled else "off")
)
_readable_font_toggle.text = (
"readable\ninterface font\n"
+ (
"on"
if settings.use_readable_interface_font
else "off"
)
)
_auto_click_interval.text = (
"auto-click\ninterval\n%.2f s" % _auto_click_interval_value
)
@ -709,6 +737,19 @@ func _set_world_pixelation(pixel_size: int) -> void:
_refresh_value_labels()
func _toggle_readable_font() -> void:
if _settings_manager == null:
return
var edited: PlayerSettings = _settings_manager.current_settings.copy()
edited.use_readable_interface_font = (
not edited.use_readable_interface_font
)
if not _settings_manager.apply_settings(edited):
_feedback.text = "failed to save accessibility settings."
return
_load_controls()
func _set_ui_pixelation(pixel_size: int) -> void:
if _settings_manager == null:
return

View file

@ -486,8 +486,8 @@ grow_horizontal = 2
grow_vertical = 2
script = ExtResource("3_page")
page_id = &"accessibility"
bubble_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/IntervalHelp"), NodePath("BubbleCluster/AccessibilityBackButton")])
focus_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/AccessibilityBackButton")])
bubble_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/ReadableFontToggle"), NodePath("BubbleCluster/IntervalHelp"), NodePath("BubbleCluster/AccessibilityBackButton")])
focus_paths = Array[NodePath]([NodePath("BubbleCluster/AutoClickToggle"), NodePath("BubbleCluster/IntervalDecrease"), NodePath("BubbleCluster/AutoClickIntervalValue"), NodePath("BubbleCluster/IntervalIncrease"), NodePath("BubbleCluster/ReadableFontToggle"), NodePath("BubbleCluster/AccessibilityBackButton")])
initial_focus_path = NodePath("BubbleCluster/AutoClickToggle")
back_focus_path = NodePath("BubbleCluster/AccessibilityBackButton")
@ -562,6 +562,41 @@ minimum_font_size = 13
maximum_font_size = 22
motion_phase = 4.1
[node name="ReadableFontToggle" parent="AccessibilityPage/BubbleCluster" instance=ExtResource("4_bubble")]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 0)
toggle_mode = true
text = "readable\ninterface font\noff"
neutral_size = Vector2(180, 132)
desktop_anchor = Vector2(92, 405)
compact_anchor = Vector2(102, 374)
compact_minimum_size = Vector2(156, 118)
minimum_font_size = 13
maximum_font_size = 21
motion_phase = 4.6
[node name="ReadableFontSample" type="Label" parent="AccessibilityPage/BubbleCluster"]
layout_mode = 0
offset_left = 250.0
offset_top = 20.0
offset_right = 700.0
offset_bottom = 82.0
mouse_filter = 2
text = "The quick brown fox jumps over the lazy dog.\n0123456789"
horizontal_alignment = 1
vertical_alignment = 1
autowrap_mode = 2
[node name="ReadableFontHelper" type="Label" parent="AccessibilityPage/BubbleCluster"]
layout_mode = 0
offset_left = 250.0
offset_top = 82.0
offset_right = 700.0
offset_bottom = 112.0
mouse_filter = 2
text = "Uses a clearer font for menus and interface text."
horizontal_alignment = 1
[node name="AccessibilityBackButton" parent="AccessibilityPage/BubbleCluster" instance=ExtResource("4_bubble")]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 0)