From 206cafc8bba310e4d4f35f4898ef3ac289abe81a Mon Sep 17 00:00:00 2001 From: Hop311 Date: Sat, 10 Jun 2023 20:48:17 +0100 Subject: Localisation and UI focus cleanup Updated Russian localisation Resolution, window_mode, monitor refactor Locale based number formatting --- game/src/Game/Autoload/Events/Localisation.gd | 11 +- game/src/Game/Autoload/Resolution.gd | 145 ++++++++++++--------- game/src/Game/GameSession/GameSpeedPanel.tscn | 4 + .../GameSession/MapControlPanel/MapControlPanel.gd | 1 + .../MapControlPanel/MapControlPanel.tscn | 5 + .../ProvinceOverviewPanel/ProvinceOverviewPanel.gd | 18 ++- .../ProvinceOverviewPanel.tscn | 1 + game/src/Game/Menu/CreditsMenu/CreditsMenu.gd | 5 + game/src/Game/Menu/MainMenu/ReleaseInfoBox.tscn | 3 + .../Game/Menu/OptionMenu/MonitorDisplaySelector.gd | 27 ++-- game/src/Game/Menu/OptionMenu/OptionsMenu.gd | 12 +- game/src/Game/Menu/OptionMenu/OptionsMenu.tscn | 3 +- .../Game/Menu/OptionMenu/ResolutionRevertDialog.gd | 35 ----- .../src/Game/Menu/OptionMenu/ResolutionSelector.gd | 47 ++++--- .../src/Game/Menu/OptionMenu/ScreenModeSelector.gd | 15 +-- .../OptionMenu/SettingNodes/SettingCheckBox.gd | 53 ++++++++ .../OptionMenu/SettingNodes/SettingRevertButton.gd | 2 +- .../Game/Menu/OptionMenu/SettingRevertDialog.gd | 37 ++++++ game/src/Game/Menu/OptionMenu/SoundTab.gd | 6 +- game/src/Game/Menu/OptionMenu/SoundTab.tscn | 15 ++- game/src/Game/Menu/OptionMenu/VideoTab.tscn | 38 +++--- game/src/Game/MusicConductor/MusicConductor.gd | 26 +++- game/src/Game/MusicConductor/MusicConductor.tscn | 3 +- game/src/Game/MusicConductor/MusicPlayer.tscn | 6 + 24 files changed, 342 insertions(+), 176 deletions(-) delete mode 100644 game/src/Game/Menu/OptionMenu/ResolutionRevertDialog.gd create mode 100644 game/src/Game/Menu/OptionMenu/SettingNodes/SettingCheckBox.gd create mode 100644 game/src/Game/Menu/OptionMenu/SettingRevertDialog.gd (limited to 'game/src/Game') diff --git a/game/src/Game/Autoload/Events/Localisation.gd b/game/src/Game/Autoload/Events/Localisation.gd index eda7e51..9048b7a 100644 --- a/game/src/Game/Autoload/Events/Localisation.gd +++ b/game/src/Game/Autoload/Events/Localisation.gd @@ -14,10 +14,10 @@ func get_default_locale() -> String: return ProjectSettings.get_setting("internationalization/locale/fallback", "en_GB") func load_localisation(dir_path : String) -> void: - if LoadLocalisation.load_localisation_dir(dir_path) == OK: - print("loaded locales: ", TranslationServer.get_loaded_locales()) - else: - push_error("Failed to load localisation directory: ", dir_path) + if LoadLocalisation.load_localisation_dir(dir_path) != OK: + push_error("Error loading localisation directory: ", dir_path) + var loaded_locales : PackedStringArray = TranslationServer.get_loaded_locales() + print("Loaded ", loaded_locales.size(), " locales: ", loaded_locales) # REQUIREMENTS # * SS-57 @@ -28,3 +28,6 @@ func _init(): push_error("Missing localisation_path setting!") else: load_localisation(localisation_dir_path) + +func tr_number(num) -> String: + return TextServerManager.get_primary_interface().format_number(str(num)) diff --git a/game/src/Game/Autoload/Resolution.gd b/game/src/Game/Autoload/Resolution.gd index c973ba9..1c7add7 100644 --- a/game/src/Game/Autoload/Resolution.gd +++ b/game/src/Game/Autoload/Resolution.gd @@ -1,28 +1,26 @@ extends Node -signal resolution_added(value : Vector2i, name : StringName, display_name : StringName) -signal resolution_changed(value : Vector2i) -signal window_mode_changed(value : Window.Mode) +signal resolution_added(value : Vector2i) const error_resolution : Vector2i = Vector2i(-1,-1) @export var minimum_resolution : Vector2i = Vector2i(1,1) -const _starting_resolutions : Dictionary = { - Vector2i(3840,2160): &"4K", - Vector2i(2560,1080): &"UW1080p", - Vector2i(1920,1080): &"1080p", - Vector2i(1366,768) : &"", - Vector2i(1536,864) : &"", - Vector2i(1280,720) : &"720p", - Vector2i(1440,900) : &"", - Vector2i(1600,900) : &"", - Vector2i(1024,600) : &"", - Vector2i(800,600) : &"" -} - -var _resolutions : Dictionary +const _starting_resolutions : Array[Vector2i] = [ + Vector2i(3840,2160), + Vector2i(2560,1080), + Vector2i(1920,1080), + Vector2i(1366,768), + Vector2i(1536,864), + Vector2i(1280,720), + Vector2i(1440,900), + Vector2i(1600,900), + Vector2i(1024,600), + Vector2i(800,600) +] + +var _resolutions : Array[Vector2i] const _regex_pattern : String = "(\\d+)\\s*[xX,]\\s*(\\d+)" var _regex : RegEx @@ -30,72 +28,103 @@ var _regex : RegEx func _ready(): assert(minimum_resolution.x > 0 and minimum_resolution.y > 0, "Minimum resolution must be positive!") for resolution_value in _starting_resolutions: - add_resolution(resolution_value, _starting_resolutions[resolution_value]) + add_resolution(resolution_value) assert(not _resolutions.is_empty(), "No valid starting resolutions!") _regex = RegEx.new() var err := _regex.compile(_regex_pattern) assert(err == OK, "Resolution RegEx failed to compile!") - func has_resolution(resolution_value : Vector2i) -> bool: return resolution_value in _resolutions -func add_resolution(resolution_value : Vector2i, resolution_name : StringName = &"") -> bool: +func add_resolution(resolution_value : Vector2i) -> bool: if has_resolution(resolution_value): return true - var res_dict := { value = resolution_value, name = &"" } - var display_name := "%sx%s" % [resolution_value.x, resolution_value.y] - if not resolution_name.is_empty(): - res_dict.name = resolution_name - display_name = "%s (%s)" % [display_name, resolution_name] - res_dict.display_name = StringName(display_name) if resolution_value.x < minimum_resolution.x or resolution_value.y < minimum_resolution.y: - push_error("Resolution %s is smaller than minimum (%sx%s)" % [res_dict.display_name, minimum_resolution.x, minimum_resolution.y]) + push_error("Resolution %dx%d is smaller than minimum (%dx%d)" % [resolution_value.x, resolution_value.y, minimum_resolution.x, minimum_resolution.y]) return false - resolution_added.emit(resolution_value, resolution_name, display_name) - _resolutions[resolution_value] = res_dict + _resolutions.append(resolution_value) + resolution_added.emit(resolution_value) return true -func get_resolution_value_list() -> Array: - var list := _resolutions.keys() +func get_resolution_value_list() -> Array[Vector2i]: + var list : Array[Vector2i] = [] + # Return a sorted copy instead of a reference to the private array + list.append_array(_resolutions) list.sort_custom(func(a, b): return a > b) return list -func get_resolution_name(resolution_value : Vector2i) -> StringName: - return _resolutions.get(resolution_value, { name = &"unknown resolution" }).name - -func get_resolution_display_name(resolution_value : Vector2i) -> StringName: - return _resolutions.get(resolution_value, { display_name = &"unknown resolution" }).display_name - func get_resolution_value_from_string(resolution_string : String) -> Vector2i: if not resolution_string.is_empty(): - for resolution in _resolutions.values(): - if resolution_string == resolution.name or resolution_string == resolution.display_name: - return resolution.value var result := _regex.search(resolution_string) if result: return Vector2i(result.get_string(1).to_int(), result.get_string(2).to_int()) return error_resolution func get_current_resolution() -> Vector2i: - var window := get_viewport().get_window() - match window.mode: - Window.MODE_EXCLUSIVE_FULLSCREEN, Window.MODE_FULLSCREEN: - return window.content_scale_size - _: - return window.size + var viewport := get_viewport() + if viewport != null: + var window := viewport.get_window() + if window != null: + match window.mode: + Window.MODE_EXCLUSIVE_FULLSCREEN, Window.MODE_FULLSCREEN: + return window.content_scale_size + _: + return window.size + push_error("Trying to get resolution before window exists!") + return error_resolution func set_resolution(resolution : Vector2i) -> void: if not has_resolution(resolution): push_warning("Setting resolution to non-standard value %sx%s" % [resolution.x, resolution.y]) - var window := get_viewport().get_window() - if get_current_resolution() != resolution: - resolution_changed.emit(resolution) - match window.mode: - Window.MODE_EXCLUSIVE_FULLSCREEN, Window.MODE_FULLSCREEN: - window.content_scale_size = resolution - _: - window.size = resolution - window.content_scale_size = Vector2i(0,0) - -func reset_resolution() -> void: - set_resolution(get_current_resolution()) + var viewport := get_viewport() + if viewport != null: + var window := viewport.get_window() + if window != null: + match window.mode: + Window.MODE_EXCLUSIVE_FULLSCREEN, Window.MODE_FULLSCREEN: + window.content_scale_size = resolution + _: + window.size = resolution + window.content_scale_size = Vector2i(0,0) + return + push_error("Trying to set resolution before window exists!") + +func get_current_window_mode() -> Window.Mode: + var viewport := get_viewport() + if viewport != null: + var window := viewport.get_window() + if window != null: + return window.mode + push_error("Trying to get window mode before it exists!") + return Window.MODE_WINDOWED + +func set_window_mode(mode : Window.Mode) -> void: + var viewport := get_viewport() + if viewport != null: + var window := viewport.get_window() + if window != null: + var current_resolution := get_current_resolution() + var current_monitor := window.current_screen + window.mode = mode + window.current_screen = current_monitor + set_resolution(current_resolution) + return + push_error("Trying to set window mode before it exists!") + +func get_current_monitor() -> int: + var viewport := get_viewport() + if viewport != null: + var window := viewport.get_window() + if window != null: + return window.current_screen + push_error("Trying to get monitor index before window exists!") + return 0 + +func set_monitor(index : int) -> void: + var viewport := get_viewport() + if viewport != null: + var window := viewport.get_window() + if window != null: + window.current_screen = index + return + push_error("Trying to set monitor index before window exists!") diff --git a/game/src/Game/GameSession/GameSpeedPanel.tscn b/game/src/Game/GameSession/GameSpeedPanel.tscn index fab988e..2ce5042 100644 --- a/game/src/Game/GameSession/GameSpeedPanel.tscn +++ b/game/src/Game/GameSession/GameSpeedPanel.tscn @@ -15,21 +15,25 @@ layout_mode = 2 [node name="LongformDateButton" type="Button" parent="ButtonList"] custom_minimum_size = Vector2(200, 0) layout_mode = 2 +focus_mode = 0 text = "LONGFORM DATE" [node name="PlayPauseDisplayButton" type="Button" parent="ButtonList"] custom_minimum_size = Vector2(30, 0) layout_mode = 2 +focus_mode = 0 text = "⏸ " [node name="DecreaseSpeedButton" type="Button" parent="ButtonList"] custom_minimum_size = Vector2(30, 0) layout_mode = 2 +focus_mode = 0 text = "-" [node name="IncreaseSpeedButton" type="Button" parent="ButtonList"] custom_minimum_size = Vector2(30, 0) layout_mode = 2 +focus_mode = 0 text = "+" [connection signal="pressed" from="ButtonList/LongformDateButton" to="." method="_on_longform_date_label_pressed"] diff --git a/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd b/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd index 0cef057..01e6623 100644 --- a/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd +++ b/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd @@ -19,6 +19,7 @@ func _add_mapmode_button(identifier : String) -> void: button.toggle_mode = true button.button_group = _mapmode_button_group button.mouse_filter = MOUSE_FILTER_PASS + button.focus_mode = FOCUS_NONE _mapmodes_grid.add_child(button) if _mapmode_button_group.get_pressed_button() == null: button.button_pressed = true diff --git a/game/src/Game/GameSession/MapControlPanel/MapControlPanel.tscn b/game/src/Game/GameSession/MapControlPanel/MapControlPanel.tscn index a00f110..2bb62f1 100644 --- a/game/src/Game/GameSession/MapControlPanel/MapControlPanel.tscn +++ b/game/src/Game/GameSession/MapControlPanel/MapControlPanel.tscn @@ -68,6 +68,7 @@ layout_mode = 2 [node name="GameSessionMenuButton" type="Button" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel"] editor_description = "UI-9" layout_mode = 2 +focus_mode = 0 mouse_filter = 1 shortcut = SubResource("Shortcut_fc1tk") text = "ESC" @@ -75,12 +76,14 @@ text = "ESC" [node name="LedgerButton" type="Button" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel"] editor_description = "UI-860" layout_mode = 2 +focus_mode = 0 mouse_filter = 1 text = "L" [node name="FindButton" type="Button" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel"] editor_description = "UI-861" layout_mode = 2 +focus_mode = 0 mouse_filter = 1 text = "F" @@ -91,12 +94,14 @@ alignment = 1 [node name="ZoomInButton" type="Button" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel/ZoomButtonsContainer"] editor_description = "UI-862" layout_mode = 2 +focus_mode = 0 mouse_filter = 1 text = "+" [node name="ZoomOutButton" type="Button" parent="MapPanelMargin/MapPanelList/AuxiliaryPanel/ZoomButtonsContainer"] editor_description = "UI-863" layout_mode = 2 +focus_mode = 0 mouse_filter = 1 text = "-" diff --git a/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd index 67060bf..bad093d 100644 --- a/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd +++ b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.gd @@ -13,13 +13,18 @@ var _selected_index : int: get: return _selected_index set(v): _selected_index = v - update_info() + _update_info() var _province_info : Dictionary func _ready(): GameSingleton.province_selected.connect(_on_province_selected) - GameSingleton.state_updated.connect(update_info) - update_info() + GameSingleton.state_updated.connect(_update_info) + _update_info() + +func _notification(what : int): + match what: + NOTIFICATION_TRANSLATION_CHANGED: + _update_info() enum { CANNOT_EXPAND, CAN_EXPAND, PREPARING, EXPANDING } @@ -53,6 +58,7 @@ func _add_building_row() -> void: row.button.text = "EXPAND_PROVINCE_BUILDING" row.button.size_flags_horizontal = Control.SIZE_EXPAND_FILL row.button.mouse_filter = Control.MOUSE_FILTER_PASS + row.button.focus_mode = FOCUS_NONE row.button.pressed.connect(func(): _expand_building(row.name.text)) _buildings_container.add_child(row.button) @@ -91,7 +97,7 @@ func _set_building_row(index : int, building : Dictionary) -> void: row.button.disabled = expansion_state != CAN_EXPAND row.button.visible = not show_progress_bar -func update_info() -> void: +func _update_info() -> void: _province_info = GameSingleton.get_province_info_from_index(_selected_index) if _province_info: _province_name_label.text = _province_info.get(GameSingleton.get_province_info_province_key(), @@ -100,7 +106,9 @@ func update_info() -> void: GameSingleton.get_province_info_region_key() + _missing_suffix) _life_rating_bar.value = _province_info.get(GameSingleton.get_province_info_life_rating_key(), 0) - _life_rating_bar.tooltip_text = tr("LIFE_RATING_TOOLTIP").format({ "life_rating": _life_rating_bar.value }) + _life_rating_bar.tooltip_text = tr("LIFE_RATING_TOOLTIP").format({ + "life_rating": Events.Localisation.tr_number(_life_rating_bar.value) + }) _rgo_name_label.text = _province_info.get(GameSingleton.get_province_info_rgo_key(), GameSingleton.get_province_info_rgo_key() + _missing_suffix) diff --git a/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn index f8c1e65..896f8e9 100644 --- a/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn +++ b/game/src/Game/GameSession/ProvinceOverviewPanel/ProvinceOverviewPanel.tscn @@ -51,6 +51,7 @@ mouse_filter = 1 custom_minimum_size = Vector2(30, 30) layout_mode = 2 size_flags_vertical = 0 +focus_mode = 0 mouse_filter = 1 text = "X" diff --git a/game/src/Game/Menu/CreditsMenu/CreditsMenu.gd b/game/src/Game/Menu/CreditsMenu/CreditsMenu.gd index 0db4d7d..61140c0 100644 --- a/game/src/Game/Menu/CreditsMenu/CreditsMenu.gd +++ b/game/src/Game/Menu/CreditsMenu/CreditsMenu.gd @@ -193,6 +193,11 @@ func _ready(): _add_godot_credits() _add_licenses() +func _input(event): + if self.is_visible_in_tree(): + if event.is_action_pressed("ui_cancel"): + _on_back_button_pressed() + # REQUIREMENTS: # * UI-38 # * UIFUN-37 diff --git a/game/src/Game/Menu/MainMenu/ReleaseInfoBox.tscn b/game/src/Game/Menu/MainMenu/ReleaseInfoBox.tscn index a10232e..4dc6f4e 100644 --- a/game/src/Game/Menu/MainMenu/ReleaseInfoBox.tscn +++ b/game/src/Game/Menu/MainMenu/ReleaseInfoBox.tscn @@ -12,6 +12,7 @@ _checksum_label = NodePath("ChecksumLabel") [node name="VersionLabel" type="Button" parent="."] layout_mode = 2 tooltip_text = "VERSION_MISSING" +focus_mode = 0 theme_type_variation = &"VersionLabel" text = "VERSION_MISSING" flat = true @@ -19,6 +20,7 @@ alignment = 0 [node name="CommitLabel" type="Button" parent="."] layout_mode = 2 +focus_mode = 0 theme_type_variation = &"CommitLabel" text = "????????" flat = true @@ -28,6 +30,7 @@ alignment = 0 editor_description = "UI-111" layout_mode = 2 tooltip_text = "CHECKSUM_MISSING" +focus_mode = 0 theme_type_variation = &"ChecksumLabel" text = "(????)" flat = true diff --git a/game/src/Game/Menu/OptionMenu/MonitorDisplaySelector.gd b/game/src/Game/Menu/OptionMenu/MonitorDisplaySelector.gd index 7de033a..028b3df 100644 --- a/game/src/Game/Menu/OptionMenu/MonitorDisplaySelector.gd +++ b/game/src/Game/Menu/OptionMenu/MonitorDisplaySelector.gd @@ -1,18 +1,29 @@ -extends SettingOptionButton +extends SettingRevertButton func _setup_button() -> void: clear() for screen_index in DisplayServer.get_screen_count(): - add_item("Monitor %d" % (screen_index + 1)) - default_selected = get_viewport().get_window().current_screen + # Placeholder option text awaiting _update_monitor_options_text() + add_item(str(screen_index + 1)) + _update_monitor_options_text() + default_selected = Resolution.get_current_monitor() + +func _notification(what : int): + match what: + NOTIFICATION_TRANSLATION_CHANGED: + _update_monitor_options_text() + +func _update_monitor_options_text() -> void: + for index in get_item_count(): + set_item_text(index, tr("OPTIONS_VIDEO_MONITOR").format({ "index": Events.Localisation.tr_number(index + 1) })) func _on_option_selected(index : int, by_user : bool) -> void: if _valid_index(index): - var window := get_viewport().get_window() - var mode := window.mode - window.mode = Window.MODE_WINDOWED - get_viewport().get_window().set_current_screen(index) - window.mode = mode + if by_user: + print("Start Revert Countdown!") + revert_dialog.show_dialog.call_deferred(self) + previous_index = Resolution.get_current_monitor() + Resolution.set_monitor(index) else: push_error("Invalid MonitorDisplaySelector index: %d" % index) reset_setting(not by_user) diff --git a/game/src/Game/Menu/OptionMenu/OptionsMenu.gd b/game/src/Game/Menu/OptionMenu/OptionsMenu.gd index 5f6a088..c74c458 100644 --- a/game/src/Game/Menu/OptionMenu/OptionsMenu.gd +++ b/game/src/Game/Menu/OptionMenu/OptionsMenu.gd @@ -5,9 +5,17 @@ extends Control signal back_button_pressed +@export var _tab_container : TabContainer + func _ready(): + _tab_container.set_tab_title(0, "OPTIONS_GENERAL") + _tab_container.set_tab_title(1, "OPTIONS_VIDEO") + _tab_container.set_tab_title(2, "OPTIONS_SOUND") + _tab_container.set_tab_title(3, "OPTIONS_CONTROLS") + _tab_container.set_tab_title(4, "OPTIONS_OTHER") + # Prepare options menu before loading user settings - var tab_bar : TabBar = $Margin/Tab.get_child(0, true) + var tab_bar : TabBar = _tab_container.get_child(0, true) # This ends up easier to manage then trying to manually recreate the TabContainer's behavior # These buttons can be accessed regardless of the tab @@ -60,7 +68,7 @@ func _save_overrides() -> void: var file := ConfigFile.new() var err_ret := file.load(override_path) if err_ret != OK: push_error("Failed to load overrides from %s" % override_path) - file.set_value("display", "window/size/mode", get_viewport().get_window().mode) + file.set_value("display", "window/size/mode", Resolution.get_current_window_mode()) var resolution : Vector2i = Resolution.get_current_resolution() file.set_value("display", "window/size/viewport_width", resolution.x) file.set_value("display", "window/size/viewport_height", resolution.y) diff --git a/game/src/Game/Menu/OptionMenu/OptionsMenu.tscn b/game/src/Game/Menu/OptionMenu/OptionsMenu.tscn index 017629a..3185f63 100644 --- a/game/src/Game/Menu/OptionMenu/OptionsMenu.tscn +++ b/game/src/Game/Menu/OptionMenu/OptionsMenu.tscn @@ -8,7 +8,7 @@ [ext_resource type="PackedScene" uid="uid://bq7ibhm0txl5p" path="res://addons/keychain/ShortcutEdit.tscn" id="4_vdhjp"] [ext_resource type="PackedScene" uid="uid://dp2grvybtecqu" path="res://src/Game/Menu/OptionMenu/OtherTab.tscn" id="5_ahefp"] -[node name="OptionsMenu" type="PanelContainer"] +[node name="OptionsMenu" type="PanelContainer" node_paths=PackedStringArray("_tab_container")] editor_description = "UI-25" anchors_preset = 15 anchor_right = 1.0 @@ -18,6 +18,7 @@ grow_vertical = 2 theme = ExtResource("1_0up1d") theme_type_variation = &"BackgroundPanel" script = ExtResource("1_tlein") +_tab_container = NodePath("Margin/Tab") [node name="Margin" type="MarginContainer" parent="."] layout_mode = 2 diff --git a/game/src/Game/Menu/OptionMenu/ResolutionRevertDialog.gd b/game/src/Game/Menu/OptionMenu/ResolutionRevertDialog.gd deleted file mode 100644 index 4d2b8f2..0000000 --- a/game/src/Game/Menu/OptionMenu/ResolutionRevertDialog.gd +++ /dev/null @@ -1,35 +0,0 @@ -extends ConfirmationDialog -class_name ResolutionRevertDialog - -signal dialog_accepted(button : SettingRevertButton) -signal dialog_reverted(button : SettingRevertButton) - -@export_group("Nodes") -@export var timer : Timer - -var _revert_node : SettingRevertButton = null - -func show_dialog(button : SettingRevertButton, time : float = 0) -> void: - timer.start(time) - popup_centered(Vector2(1,1)) - _revert_node = button - -func _notification(what): - if what == NOTIFICATION_VISIBILITY_CHANGED: - set_process(visible) - if not visible: _revert_node = null - -func _process(_delta) -> void: - dialog_text = tr("OPTIONS_VIDEO_RESOLUTION_DIALOG_TEXT").format({ "time": int(timer.time_left) }) - -func _on_canceled_or_close_requested() -> void: - timer.stop() - dialog_reverted.emit(_revert_node) - -func _on_confirmed() -> void: - timer.stop() - dialog_accepted.emit(_revert_node) - -func _on_resolution_revert_timer_timeout() -> void: - dialog_reverted.emit(_revert_node) - hide() diff --git a/game/src/Game/Menu/OptionMenu/ResolutionSelector.gd b/game/src/Game/Menu/OptionMenu/ResolutionSelector.gd index ebdf718..2791ecb 100644 --- a/game/src/Game/Menu/OptionMenu/ResolutionSelector.gd +++ b/game/src/Game/Menu/OptionMenu/ResolutionSelector.gd @@ -14,31 +14,20 @@ func _find_resolution_index_by_value(value : Vector2i) -> int: return item_index return -1 -func _sync_resolutions( - value : Vector2i = Resolution.error_resolution, - _resolution_name = null, - _resolution_display_name = null -) -> void: +func _sync_resolutions(value : Vector2i = Resolution.error_resolution) -> void: clear() default_selected = -1 selected = -1 - var resolution_list := Resolution.get_resolution_value_list() - if value != Resolution.error_resolution: - resolution_list.append(value) - for resolution_value in resolution_list: - var display_name := "%sx%s" % [resolution_value.x, resolution_value.y] - var resolution_name := Resolution.get_resolution_name(resolution_value) - if resolution_name == &"Default": - display_name = "Default (%s)" % resolution_name - if not resolution_name.is_empty(): - display_name = "%s (%s)" % [display_name, resolution_name + (", Default" if resolution_value == default_value else "")] - add_item(display_name) + var current_resolution := Resolution.get_current_resolution() + for resolution_value in Resolution.get_resolution_value_list(): + # Placeholder option text awaiting _update_resolution_options_text() + add_item(str(resolution_value)) set_item_metadata(item_count - 1, resolution_value) if resolution_value == default_value: default_selected = item_count - 1 - if resolution_value == Resolution.get_current_resolution(): + if resolution_value == current_resolution: selected = item_count - 1 if default_selected == -1: @@ -46,6 +35,28 @@ func _sync_resolutions( if selected == -1: selected = default_selected + _update_resolution_options_text() + +func _notification(what : int): + match what: + NOTIFICATION_TRANSLATION_CHANGED: + _update_resolution_options_text() + +func _update_resolution_options_text() -> void: + for index in get_item_count(): + var resolution_value : Vector2i = get_item_metadata(index) + var format_dict := { "width": resolution_value.x, "height": resolution_value.y } + format_dict["name"] = tr("OPTIONS_VIDEO_RESOLUTION_{width}x{height}".format(format_dict)) + if format_dict["name"].begins_with("OPTIONS"): format_dict["name"] = "" + var display_name := "OPTIONS_VIDEO_RESOLUTION_DIMS" + if format_dict["name"]: + display_name += "_NAMED" + if resolution_value == default_value: + display_name += "_DEFAULT" + format_dict["width"] = Events.Localisation.tr_number(resolution_value.x) + format_dict["height"] = Events.Localisation.tr_number(resolution_value.y) + display_name = tr(display_name).format(format_dict) + set_item_text(index, display_name) func _setup_button() -> void: Resolution.resolution_added.connect(_sync_resolutions) @@ -54,7 +65,7 @@ func _setup_button() -> void: if default_value.y <= 0: default_value.y = ProjectSettings.get_setting("display/window/size/viewport_height") if not Resolution.has_resolution(default_value): - Resolution.add_resolution(default_value, &"Default") + Resolution.add_resolution(default_value) else: _sync_resolutions() diff --git a/game/src/Game/Menu/OptionMenu/ScreenModeSelector.gd b/game/src/Game/Menu/OptionMenu/ScreenModeSelector.gd index af95901..a1a26a0 100644 --- a/game/src/Game/Menu/OptionMenu/ScreenModeSelector.gd +++ b/game/src/Game/Menu/OptionMenu/ScreenModeSelector.gd @@ -5,7 +5,7 @@ extends SettingRevertButton enum ScreenMode { Unknown = -1, Fullscreen, Borderless, Windowed } -func get_screen_mode_from_window_mode(window_mode : int) -> ScreenMode: +func get_screen_mode_from_window_mode(window_mode : Window.Mode) -> ScreenMode: match window_mode: Window.MODE_EXCLUSIVE_FULLSCREEN: return ScreenMode.Fullscreen @@ -16,7 +16,7 @@ func get_screen_mode_from_window_mode(window_mode : int) -> ScreenMode: _: return ScreenMode.Unknown -func get_window_mode_from_screen_mode(screen_mode : int) -> Window.Mode: +func get_window_mode_from_screen_mode(screen_mode : ScreenMode) -> Window.Mode: match screen_mode: ScreenMode.Fullscreen: return Window.MODE_EXCLUSIVE_FULLSCREEN @@ -28,7 +28,7 @@ func get_window_mode_from_screen_mode(screen_mode : int) -> Window.Mode: return Window.MODE_EXCLUSIVE_FULLSCREEN func _setup_button(): - default_selected = get_screen_mode_from_window_mode(get_viewport().get_window().mode) + default_selected = get_screen_mode_from_window_mode(Resolution.get_current_window_mode()) selected = default_selected func _on_option_selected(index : int, by_user : bool) -> void: @@ -36,13 +36,8 @@ func _on_option_selected(index : int, by_user : bool) -> void: if by_user: print("Start Revert Countdown!") revert_dialog.show_dialog.call_deferred(self) - previous_index = get_screen_mode_from_window_mode(get_viewport().get_window().mode) - - var current_resolution := Resolution.get_current_resolution() - var window_mode := get_window_mode_from_screen_mode(index) - Resolution.window_mode_changed.emit(window_mode) - get_viewport().get_window().mode = window_mode - Resolution.set_resolution(current_resolution) + previous_index = get_screen_mode_from_window_mode(Resolution.get_current_window_mode()) + Resolution.set_window_mode(get_window_mode_from_screen_mode(index)) else: push_error("Invalid ScreenModeSelector index: %d" % index) reset_setting(not by_user) diff --git a/game/src/Game/Menu/OptionMenu/SettingNodes/SettingCheckBox.gd b/game/src/Game/Menu/OptionMenu/SettingNodes/SettingCheckBox.gd new file mode 100644 index 0000000..fcc411e --- /dev/null +++ b/game/src/Game/Menu/OptionMenu/SettingNodes/SettingCheckBox.gd @@ -0,0 +1,53 @@ +extends CheckBox +class_name SettingCheckBox + +signal option_selected(pressed : bool, by_user : bool) + +@export +var section_name : String = "setting" + +@export +var setting_name : String = "setting_checkbox" + +@export +var default_pressed : bool = true + +func _setup_button() -> void: + pass + +func _ready(): + Events.Options.load_settings.connect(load_setting) + Events.Options.save_settings.connect(save_setting) + Events.Options.reset_settings.connect(reset_setting) + toggled.connect(func(pressed : bool): option_selected.emit(pressed, true)) + _setup_button() + +func _set_value_from_file(load_value) -> void: + match typeof(load_value): + TYPE_BOOL, TYPE_INT: + set_pressed_no_signal(load_value as bool) + return + TYPE_STRING, TYPE_STRING_NAME: + var load_str := (load_value as String).to_lower() + if load_str.is_empty() or load_str.begins_with("f") or load_str.begins_with("n"): + set_pressed_no_signal(false) + return + if load_str.begins_with("t") or load_str.begins_with("y"): + set_pressed_no_signal(true) + return + push_error("Setting value '%s' invalid for setting [%s] \"%s\"" % [load_value, section_name, setting_name]) + set_pressed_no_signal(default_pressed) + +func load_setting(file : ConfigFile) -> void: + if file == null: return + _set_value_from_file(file.get_value(section_name, setting_name, default_pressed)) + option_selected.emit(button_pressed, false) + +func save_setting(file : ConfigFile) -> void: + if file == null: return + file.set_value(section_name, setting_name, button_pressed) + +func reset_setting(no_emit : bool = false) -> void: + set_pressed_no_signal(default_pressed) + if not no_emit: + option_selected.emit(button_pressed, false) diff --git a/game/src/Game/Menu/OptionMenu/SettingNodes/SettingRevertButton.gd b/game/src/Game/Menu/OptionMenu/SettingNodes/SettingRevertButton.gd index 945d35b..431e3e5 100644 --- a/game/src/Game/Menu/OptionMenu/SettingNodes/SettingRevertButton.gd +++ b/game/src/Game/Menu/OptionMenu/SettingNodes/SettingRevertButton.gd @@ -2,7 +2,7 @@ extends SettingOptionButton class_name SettingRevertButton @export_group("Nodes") -@export var revert_dialog : ResolutionRevertDialog +@export var revert_dialog : SettingRevertDialog var previous_index : int = -1 diff --git a/game/src/Game/Menu/OptionMenu/SettingRevertDialog.gd b/game/src/Game/Menu/OptionMenu/SettingRevertDialog.gd new file mode 100644 index 0000000..8cde621 --- /dev/null +++ b/game/src/Game/Menu/OptionMenu/SettingRevertDialog.gd @@ -0,0 +1,37 @@ +extends ConfirmationDialog +class_name SettingRevertDialog + +signal dialog_accepted(button : SettingRevertButton) +signal dialog_reverted(button : SettingRevertButton) + +@export var dialog_text_key : String = "< reverting in {time} seconds >" + +@export_group("Nodes") +@export var timer : Timer + +var _revert_node : SettingRevertButton = null + +func show_dialog(button : SettingRevertButton, time : float = 0) -> void: + timer.start(time) + popup_centered(Vector2(1,1)) + _revert_node = button + +func _notification(what): + if what == NOTIFICATION_VISIBILITY_CHANGED: + set_process(visible) + if not visible: _revert_node = null + +func _process(_delta) -> void: + dialog_text = tr(dialog_text_key).format({ "time": Events.Localisation.tr_number(int(timer.time_left)) }) + +func _on_canceled_or_close_requested() -> void: + timer.stop() + dialog_reverted.emit(_revert_node) + +func _on_confirmed() -> void: + timer.stop() + dialog_accepted.emit(_revert_node) + +func _on_resolution_revert_timer_timeout() -> void: + dialog_reverted.emit(_revert_node) + hide() diff --git a/game/src/Game/Menu/OptionMenu/SoundTab.gd b/game/src/Game/Menu/OptionMenu/SoundTab.gd index c707605..e0d9bcf 100644 --- a/game/src/Game/Menu/OptionMenu/SoundTab.gd +++ b/game/src/Game/Menu/OptionMenu/SoundTab.gd @@ -1,4 +1,6 @@ extends HBoxContainer -func _on_ear_exploder_toggled(button_pressed): - print("KABOOM!!!" if button_pressed else "DEFUSED!!!") +@export var _startup_music_button : Button + +func _ready(): + _startup_music_button.option_selected.connect(func (pressed : bool, by_user : bool): MusicConductor.set_startup_music(pressed)) diff --git a/game/src/Game/Menu/OptionMenu/SoundTab.tscn b/game/src/Game/Menu/OptionMenu/SoundTab.tscn index 4bb6948..8bc3679 100644 --- a/game/src/Game/Menu/OptionMenu/SoundTab.tscn +++ b/game/src/Game/Menu/OptionMenu/SoundTab.tscn @@ -1,11 +1,13 @@ -[gd_scene load_steps=3 format=3 uid="uid://cbtgwpx2wxi33"] +[gd_scene load_steps=4 format=3 uid="uid://cbtgwpx2wxi33"] [ext_resource type="Script" path="res://src/Game/Menu/OptionMenu/SoundTab.gd" id="1_a7k0s"] [ext_resource type="PackedScene" uid="uid://dy4si8comamnv" path="res://src/Game/Menu/OptionMenu/VolumeGrid.tscn" id="1_okpft"] +[ext_resource type="Script" path="res://src/Game/Menu/OptionMenu/SettingNodes/SettingCheckBox.gd" id="2_f3aj4"] -[node name="Sound" type="HBoxContainer"] +[node name="Sound" type="HBoxContainer" node_paths=PackedStringArray("_startup_music_button")] alignment = 1 script = ExtResource("1_a7k0s") +_startup_music_button = NodePath("VBoxContainer/ButtonGrid/EarExploder") [node name="VBoxContainer" type="VBoxContainer" parent="."] layout_mode = 2 @@ -27,8 +29,9 @@ columns = 2 layout_mode = 2 size_flags_horizontal = 3 -[node name="EarExploder" type="CheckButton" parent="VBoxContainer/ButtonGrid"] +[node name="EarExploder" type="CheckBox" parent="VBoxContainer/ButtonGrid"] layout_mode = 2 -text = "Explode Eardrums on Startup?" - -[connection signal="toggled" from="VBoxContainer/ButtonGrid/EarExploder" to="." method="_on_ear_exploder_toggled"] +text = "OPTIONS_SOUND_EXPLODE_EARS" +script = ExtResource("2_f3aj4") +section_name = "audio" +setting_name = "startup_music" diff --git a/game/src/Game/Menu/OptionMenu/VideoTab.tscn b/game/src/Game/Menu/OptionMenu/VideoTab.tscn index 4f5151c..5c58304 100644 --- a/game/src/Game/Menu/OptionMenu/VideoTab.tscn +++ b/game/src/Game/Menu/OptionMenu/VideoTab.tscn @@ -7,7 +7,7 @@ [ext_resource type="Script" path="res://src/Game/Menu/OptionMenu/MonitorDisplaySelector.gd" id="3_y6lyb"] [ext_resource type="Script" path="res://src/Game/Menu/OptionMenu/RefreshRateSelector.gd" id="4_381mg"] [ext_resource type="Script" path="res://src/Game/Menu/OptionMenu/QualityPresetSelector.gd" id="5_srg4v"] -[ext_resource type="Script" path="res://src/Game/Menu/OptionMenu/ResolutionRevertDialog.gd" id="8_802cr"] +[ext_resource type="Script" path="res://src/Game/Menu/OptionMenu/SettingRevertDialog.gd" id="8_ug5mo"] [node name="Video" type="HBoxContainer" node_paths=PackedStringArray("initial_focus")] editor_description = "UI-46" @@ -41,7 +41,7 @@ selected = 0 popup/item_0/text = "MISSING" popup/item_0/id = 0 script = ExtResource("1_i8nro") -revert_dialog = NodePath("../../../ResolutionRevertDialog") +revert_dialog = NodePath("../../../VideoRevertDialog") section_name = "video" setting_name = "resolution" @@ -79,7 +79,7 @@ popup/item_1/id = 1 popup/item_2/text = "OPTIONS_VIDEO_WINDOWED" popup/item_2/id = 2 script = ExtResource("2_wa7vw") -revert_dialog = NodePath("../../../ResolutionRevertDialog") +revert_dialog = NodePath("../../../VideoRevertDialog") section_name = "video" setting_name = "mode_selected" @@ -87,7 +87,7 @@ setting_name = "mode_selected" layout_mode = 2 text = "OPTIONS_VIDEO_MONITOR_SELECTION" -[node name="MonitorDisplaySelector" type="OptionButton" parent="VideoSettingList/VideoSettingGrid"] +[node name="MonitorDisplaySelector" type="OptionButton" parent="VideoSettingList/VideoSettingGrid" node_paths=PackedStringArray("revert_dialog")] layout_mode = 2 focus_neighbor_top = NodePath("../ScreenModeSelector") focus_neighbor_bottom = NodePath("../RefreshRateSelector") @@ -96,6 +96,7 @@ selected = 0 popup/item_0/text = "MISSING" popup/item_0/id = 0 script = ExtResource("3_y6lyb") +revert_dialog = NodePath("../../../VideoRevertDialog") section_name = "video" setting_name = "current_screen" @@ -142,32 +143,33 @@ layout_mode = 2 focus_neighbor_top = NodePath("../RefreshRateSelector") item_count = 5 selected = 1 -popup/item_0/text = "Low" +popup/item_0/text = "OPTIONS_VIDEO_QUALITY_LOW" popup/item_0/id = 0 -popup/item_1/text = "Medium" +popup/item_1/text = "OPTIONS_VIDEO_QUALITY_MEDIUM" popup/item_1/id = 1 -popup/item_2/text = "High" +popup/item_2/text = "OPTIONS_VIDEO_QUALITY_HIGH" popup/item_2/id = 2 -popup/item_3/text = "Ultra" +popup/item_3/text = "OPTIONS_VIDEO_QUALITY_ULTRA" popup/item_3/id = 3 -popup/item_4/text = "Custom" +popup/item_4/text = "OPTIONS_VIDEO_QUALITY_CUSTOM" popup/item_4/id = 4 script = ExtResource("5_srg4v") section_name = "video" setting_name = "quality_preset" default_selected = 1 -[node name="ResolutionRevertDialog" type="ConfirmationDialog" parent="." node_paths=PackedStringArray("timer")] +[node name="VideoRevertDialog" type="ConfirmationDialog" parent="." node_paths=PackedStringArray("timer")] editor_description = "UI-873" disable_3d = true -title = "OPTIONS_VIDEO_RESOLUTION_DIALOG_TITLE" +title = "OPTIONS_VIDEO_REVERT_DIALOG_TITLE" size = Vector2i(730, 100) ok_button_text = "DIALOG_OK" cancel_button_text = "DIALOG_CANCEL" -script = ExtResource("8_802cr") -timer = NodePath("ResolutionRevertTimer") +script = ExtResource("8_ug5mo") +dialog_text_key = "OPTIONS_VIDEO_REVERT_DIALOG_TEXT" +timer = NodePath("VideoRevertTimer") -[node name="ResolutionRevertTimer" type="Timer" parent="ResolutionRevertDialog"] +[node name="VideoRevertTimer" type="Timer" parent="VideoRevertDialog"] wait_time = 5.0 one_shot = true @@ -175,7 +177,7 @@ one_shot = true [connection signal="option_selected" from="VideoSettingList/VideoSettingGrid/GuiScaleSelector" to="VideoSettingList/VideoSettingGrid/GuiScaleSelector" method="_on_option_selected"] [connection signal="option_selected" from="VideoSettingList/VideoSettingGrid/ScreenModeSelector" to="VideoSettingList/VideoSettingGrid/ScreenModeSelector" method="_on_option_selected"] [connection signal="option_selected" from="VideoSettingList/VideoSettingGrid/MonitorDisplaySelector" to="VideoSettingList/VideoSettingGrid/MonitorDisplaySelector" method="_on_option_selected"] -[connection signal="canceled" from="ResolutionRevertDialog" to="ResolutionRevertDialog" method="_on_canceled_or_close_requested"] -[connection signal="close_requested" from="ResolutionRevertDialog" to="ResolutionRevertDialog" method="_on_canceled_or_close_requested"] -[connection signal="confirmed" from="ResolutionRevertDialog" to="ResolutionRevertDialog" method="_on_confirmed"] -[connection signal="timeout" from="ResolutionRevertDialog/ResolutionRevertTimer" to="ResolutionRevertDialog" method="_on_resolution_revert_timer_timeout"] +[connection signal="canceled" from="VideoRevertDialog" to="VideoRevertDialog" method="_on_canceled_or_close_requested"] +[connection signal="close_requested" from="VideoRevertDialog" to="VideoRevertDialog" method="_on_canceled_or_close_requested"] +[connection signal="confirmed" from="VideoRevertDialog" to="VideoRevertDialog" method="_on_confirmed"] +[connection signal="timeout" from="VideoRevertDialog/VideoRevertTimer" to="VideoRevertDialog" method="_on_resolution_revert_timer_timeout"] diff --git a/game/src/Game/MusicConductor/MusicConductor.gd b/game/src/Game/MusicConductor/MusicConductor.gd index 98dd0eb..da0fb1e 100644 --- a/game/src/Game/MusicConductor/MusicConductor.gd +++ b/game/src/Game/MusicConductor/MusicConductor.gd @@ -5,6 +5,8 @@ extends Node @export_dir var music_directory : String @export var first_song_name : String +@export var _audio_stream_player : AudioStreamPlayer + var _selected_track = 0 var _available_songs : Array[SongInfo] = [] var _auto_play_next_song : bool = true @@ -13,6 +15,8 @@ var _auto_play_next_song : bool = true ## Used to keep keep consistency between scene changes var is_music_player_visible : bool = true +var _has_startup_happened : bool = false + func get_all_song_names() -> Array[String]: var songNames : Array[String] = [] for si in _available_songs: @@ -26,21 +30,24 @@ func get_current_song_name() -> String: return _available_songs[_selected_track].song_name func scrub_song_by_percentage(percentage: float) -> void: - var percentInSeconds : float = (percentage / 100.0) * $AudioStreamPlayer.stream.get_length() - $AudioStreamPlayer.play(percentInSeconds) + var percentInSeconds : float = (percentage / 100.0) * _audio_stream_player.stream.get_length() + _audio_stream_player.play(percentInSeconds) func get_current_song_progress_percentage() -> float: - return 100 * ($AudioStreamPlayer.get_playback_position() / $AudioStreamPlayer.stream.get_length()) + return 100 * (_audio_stream_player.get_playback_position() / _audio_stream_player.stream.get_length()) func is_paused() -> bool: - return $AudioStreamPlayer.stream_paused + return _audio_stream_player.stream_paused + +func set_paused(paused : bool) -> void: + _audio_stream_player.stream_paused = paused func toggle_play_pause() -> void: - $AudioStreamPlayer.stream_paused = !$AudioStreamPlayer.stream_paused + _audio_stream_player.stream_paused = !_audio_stream_player.stream_paused func start_current_song() -> void: - $AudioStreamPlayer.stream = _available_songs[_selected_track].song_stream - $AudioStreamPlayer.play() + _audio_stream_player.stream = _available_songs[_selected_track].song_stream + _audio_stream_player.play() # REQUIREMENTS # * SS-70 @@ -69,7 +76,12 @@ func _ready(): _selected_track = _available_songs.size() _available_songs.append(SongInfo.new(music_directory, fname)) start_current_song() + set_paused(true) +func set_startup_music(play : bool) -> void: + if not _has_startup_happened: + _has_startup_happened = true + set_paused(not play) func _on_audio_stream_player_finished(): if _auto_play_next_song: diff --git a/game/src/Game/MusicConductor/MusicConductor.tscn b/game/src/Game/MusicConductor/MusicConductor.tscn index 3c288be..a943f24 100644 --- a/game/src/Game/MusicConductor/MusicConductor.tscn +++ b/game/src/Game/MusicConductor/MusicConductor.tscn @@ -2,10 +2,11 @@ [ext_resource type="Script" path="res://src/Game/MusicConductor/MusicConductor.gd" id="1_56t1b"] -[node name="MusicConductor" type="Node"] +[node name="MusicConductor" type="Node" node_paths=PackedStringArray("_audio_stream_player")] script = ExtResource("1_56t1b") music_directory = "res://audio/music" first_song_name = "The_Crown" +_audio_stream_player = NodePath("AudioStreamPlayer") [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] bus = &"MUSIC_BUS" diff --git a/game/src/Game/MusicConductor/MusicPlayer.tscn b/game/src/Game/MusicConductor/MusicPlayer.tscn index 8d47ba0..498750d 100644 --- a/game/src/Game/MusicConductor/MusicPlayer.tscn +++ b/game/src/Game/MusicConductor/MusicPlayer.tscn @@ -20,6 +20,7 @@ _visbility_button = NodePath("MusicUIVisibilityButton") editor_description = "UI-107" custom_minimum_size = Vector2(150, 0) layout_mode = 2 +focus_mode = 0 alignment = 1 text_overrun_behavior = 3 fit_to_longest_item = false @@ -28,6 +29,7 @@ fit_to_longest_item = false custom_minimum_size = Vector2(150, 0) layout_mode = 2 size_flags_vertical = 1 +focus_mode = 0 [node name="ButtonList" type="HBoxContainer" parent="."] layout_mode = 2 @@ -36,21 +38,25 @@ mouse_filter = 2 [node name="PreviousSongButton" type="Button" parent="ButtonList"] layout_mode = 2 +focus_mode = 0 text = "<" [node name="PlayPauseButton" type="Button" parent="ButtonList"] custom_minimum_size = Vector2(30, 0) layout_mode = 2 +focus_mode = 0 text = "▶" [node name="NextSongButton" type="Button" parent="ButtonList"] layout_mode = 2 +focus_mode = 0 text = ">" [node name="MusicUIVisibilityButton" type="Button" parent="."] editor_description = "UI-106" layout_mode = 2 size_flags_horizontal = 4 +focus_mode = 0 toggle_mode = true text = "⬆" -- cgit v1.2.3-56-ga3b1