From 06f457b0272435db1ed7c2e2b98b5ac662e6a374 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Fri, 9 Oct 2026 16:54:45 +0200 Subject: [PATCH] Add a button in Settings that opens the config folder Shows the folder with config.ini, the saved settings and the cached characters and songs in the system's file manager. Closes #45 --- popups/SettingsPopup.tscn | 15 +++++++++++++ popups/settings_popup.gd | 27 +++++++++++++++++++++++- tests/integration/test_offline_mode.gd | 2 ++ tests/integration/test_settings_popup.gd | 13 ++++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/popups/SettingsPopup.tscn b/popups/SettingsPopup.tscn index 4cc0488..b526af9 100644 --- a/popups/SettingsPopup.tscn +++ b/popups/SettingsPopup.tscn @@ -158,6 +158,21 @@ focus_mode = 0 action_mode = 0 text = "Count plays in statistics" +[node name="ConfigFolderRow" type="HBoxContainer" parent="SettingsPanel/VBoxContainer"] +layout_mode = 2 + +[node name="Title" type="Label" parent="SettingsPanel/VBoxContainer/ConfigFolderRow"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_type_variation = &"SettingsRowLabel" +text = "Settings and cached files" + +[node name="OpenConfigFolderButton" type="Button" parent="SettingsPanel/VBoxContainer/ConfigFolderRow"] +unique_name_in_owner = true +layout_mode = 2 +focus_mode = 0 +text = "Open folder" + [node name="FolderDialog" type="FileDialog" parent="."] unique_name_in_owner = true title = "Choose a music folder" diff --git a/popups/settings_popup.gd b/popups/settings_popup.gd index 33d7c76..27f16f6 100644 --- a/popups/settings_popup.gd +++ b/popups/settings_popup.gd @@ -2,7 +2,8 @@ class_name SettingsPopup extends BasePopup ## The application settings: which MusicServer or music folder to play from, fullscreen, ## how many songs to download ahead, whether to prefer less played soundtracks and whether -## plays count in the statistics. Opened from the Settings button in the top bar. +## plays count in the statistics, and a button that opens the folder the settings are +## kept in. Opened from the Settings button in the top bar. ## ## A music folder (see [MusicFolder]) is used instead of the server until Use server is ## pressed or the game is closed. The settings that need the server are off meanwhile. @@ -32,6 +33,11 @@ const PADDING: Vector2 = Vector2(20, 12) @onready var play_order_row: HBoxContainer = %PlayOrderRow @onready var play_order_button: OptionButton = %PlayOrderButton @onready var folder_dialog: FileDialog = %FolderDialog +@onready var open_config_folder_button: Button = %OpenConfigFolderButton + +## Shows a folder in the system's file manager. The tests replace it, so no file manager +## opens while they run. +var show_folder: Callable = _show_in_file_manager # Where the panel's middle is, so it stays centred when rows are shown or hidden var _panel_middle_y: float = 0.0 @@ -48,6 +54,8 @@ func _ready() -> void: choose_folder_button.pressed.connect(folder_dialog.popup_centered) folder_dialog.dir_selected.connect(use_music_folder) use_server_button.pressed.connect(use_server) + open_config_folder_button.pressed.connect(open_config_folder) + open_config_folder_button.tooltip_text = Settings.config_dir() play_order_button.item_selected.connect(_on_play_order_selected) lower_cache_button.pressed.connect(lower_cache) increase_cache_button.pressed.connect(increase_cache) @@ -88,6 +96,14 @@ func use_server() -> void: server_changed.emit() +## Shows the folder with the config file, the saved settings and the cached characters +## and songs in the system's file manager. +func open_config_folder() -> void: + var dir: String = Settings.config_dir() + DirAccess.make_dir_recursive_absolute(dir) + show_folder.call(dir) + + func lower_cache() -> void: Playlist.number_of_tracks_to_preload = maxi(Playlist.number_of_tracks_to_preload - 1, 1) cache_label.text = str(Playlist.number_of_tracks_to_preload) @@ -160,5 +176,14 @@ func _fit_to_rows() -> void: rows.size = Vector2(settings_panel.size.x - PADDING.x * 2, height - PADDING.y * 2) +func _show_in_file_manager(dir: String) -> void: + var error: Error = OS.shell_show_in_file_manager(dir) + if error != OK: + # Not every platform has a file manager to show a folder in; open it instead + error = OS.shell_open(dir) + if error != OK: + GameLog.warning("Could not open %s: %s" % [dir, error_string(error)]) + + func _update_fullscreen_button() -> void: fullscreen_button.set_pressed_no_signal(Settings.fullscreen) diff --git a/tests/integration/test_offline_mode.gd b/tests/integration/test_offline_mode.gd index 9eea745..c091836 100644 --- a/tests/integration/test_offline_mode.gd +++ b/tests/integration/test_offline_mode.gd @@ -124,6 +124,8 @@ func test_server_features_are_off_and_the_folder_is_shown() -> void: assert_str(_top_bar.offline_label.text).is_equal("Offline: Music") assert_bool(_settings.select_server_button.disabled).is_true() assert_bool(_settings.add_to_database_button.disabled).is_true() + # The config folder holds the cached characters, which are still used offline + assert_bool(_settings.open_config_folder_button.disabled).is_false() func test_picking_a_folder_on_the_statistics_tab_goes_to_the_quiz() -> void: diff --git a/tests/integration/test_settings_popup.gd b/tests/integration/test_settings_popup.gd index fcc2ad6..32e6d4d 100644 --- a/tests/integration/test_settings_popup.gd +++ b/tests/integration/test_settings_popup.gd @@ -53,3 +53,16 @@ func test_popup_fits_its_rows_and_is_centred() -> void: # The rows' own height fits, so the last toggle is not squeezed or cut off assert_float(rows.get_combined_minimum_size().y).is_less_equal(rows.size.y) assert_vector(panel.get_center()).is_equal_approx(Vector2(960, 540), Vector2(1, 1)) + + +func test_open_folder_button_shows_the_config_folder() -> void: + var shown: Array[String] = [] + _popup.show_folder = func(dir: String) -> void: shown.append(dir) + _popup.open() + await _runner.simulate_frames(1) + + await _click_at(_popup.open_config_folder_button.get_global_rect().get_center()) + + assert_array(shown).contains_exactly([Settings.config_dir()]) + assert_bool(DirAccess.dir_exists_absolute(Settings.config_dir())).is_true() +