diff --git a/AboutWindow.gd b/AboutWindow.gd index ef74e11..ee2ad4f 100644 --- a/AboutWindow.gd +++ b/AboutWindow.gd @@ -1,4 +1,4 @@ -extends Control +extends PopupWindow @onready var about_panel: Panel = %AboutPanel @@ -51,16 +51,8 @@ const WHATS_NEW: Dictionary = { } -func _input(event: InputEvent) -> void: - if self.visible == true: - if event is InputEventMouseButton && event.is_pressed(): - var evLocal: InputEvent = make_input_local(event) - if !Rect2(Vector2(0, 0), about_panel.size).has_point(evLocal.position): - self.visible = false - - -func show_window() -> void: - visible = true +func open() -> void: + super() version_label.text = Settings.version # Build changelog text with version headers @@ -70,7 +62,3 @@ func show_window() -> void: changelog_text += WHATS_NEW[version] + "\n" whats_new_label.text = changelog_text - - -func hide_window() -> void: - visible = false diff --git a/AboutWindow.tscn b/AboutWindow.tscn index b746c05..c0ffcdb 100644 --- a/AboutWindow.tscn +++ b/AboutWindow.tscn @@ -1,8 +1,10 @@ [gd_scene format=3 uid="uid://cp54idaudb8dp"] [ext_resource type="Script" uid="uid://b7bh0of7olbcb" path="res://AboutWindow.gd" id="1_about_script"] +[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"] -[node name="AboutControl" type="Control" unique_id=1626010355] +[node name="AboutControl" type="Control" unique_id=1626010355 node_paths=PackedStringArray("panel")] +theme = ExtResource("popup_theme") layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -10,6 +12,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_about_script") +panel = NodePath("AboutPanel") [node name="AboutPanel" type="Panel" parent="." unique_id=1907299275] unique_name_in_owner = true @@ -18,7 +21,6 @@ offset_right = 600.0 offset_bottom = 640.0 [node name="TitleLabel" type="Label" parent="AboutPanel" unique_id=200295721] -texture_filter = 1 layout_mode = 0 offset_right = 600.0 offset_bottom = 50.0 @@ -40,13 +42,11 @@ size_flags_vertical = 3 [node name="VersionLabel" type="Label" parent="AboutPanel/ScrollContainer/ContentVBox" unique_id=352884126] unique_name_in_owner = true -texture_filter = 1 layout_mode = 2 size_flags_horizontal = 3 horizontal_alignment = 1 [node name="DescriptionLabel" type="Label" parent="AboutPanel/ScrollContainer/ContentVBox" unique_id=543094263] -texture_filter = 1 custom_minimum_size = Vector2(560, 0) layout_mode = 2 size_flags_horizontal = 3 @@ -56,7 +56,6 @@ autowrap_mode = 2 [node name="WhatsNewLabel" type="Label" parent="AboutPanel/ScrollContainer/ContentVBox" unique_id=1363040291] unique_name_in_owner = true -texture_filter = 1 custom_minimum_size = Vector2(560, 0) layout_mode = 2 size_flags_horizontal = 3 diff --git a/Log.tscn b/Log.tscn index 8d0d540..47e231f 100644 --- a/Log.tscn +++ b/Log.tscn @@ -1,8 +1,10 @@ [gd_scene load_steps=2 format=3 uid="uid://bijh5h5yrivm3"] [ext_resource type="Script" uid="uid://cw41y87l64qo7" path="res://log.gd" id="1_q72ua"] +[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"] -[node name="Control" type="Control"] +[node name="Control" type="Control" node_paths=PackedStringArray("panel")] +theme = ExtResource("popup_theme") custom_minimum_size = Vector2(700, 700) layout_mode = 3 anchors_preset = 15 @@ -11,6 +13,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_q72ua") +panel = NodePath("LogPanel") [node name="LogPanel" type="Panel" parent="."] unique_name_in_owner = true diff --git a/MainWindow.gd b/MainWindow.gd index 7009b5b..2eae59a 100644 --- a/MainWindow.gd +++ b/MainWindow.gd @@ -140,11 +140,15 @@ var current_player: Node ## True while statistics are off because a player won, until points are reset. var stats_paused_by_win: bool = false +## Dims the window behind an open popup and keeps clicks from reaching the buttons under it. +var popup_backdrop: ColorRect + ## Seconds to wait before retrying a song download step that failed. var song_fetch_retry_seconds: float = 5.0 # Called when the node enters the scene tree for the first time. func _ready() -> void: + _setup_popup_backdrop() var debug: bool = OS.has_feature("debug") var is_mac: bool = OS.has_feature("macos") if debug: @@ -214,9 +218,9 @@ func _input(event: InputEvent) -> void: if Settings.fullscreen == false: DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) if event.alt_pressed && event.keycode == KEY_K: - shortcuts_window.visible = !shortcuts_window.visible + shortcuts_window.toggle() if event.alt_pressed && event.keycode == KEY_L: - log_window.visible = !log_window.visible + log_window.toggle() func server_updated() -> void: print("server_updated") @@ -277,6 +281,28 @@ func play_next_song() -> void: #"/music/rand" #"/music/addQue" #"/music/info" +func _popups() -> Array[PopupWindow]: + return [search_view, sync_window, settings_window, qr_window, new_character_select, + shortcuts_window, about_window, winner_window, log_window] + +# The backdrop sits above the main window and below every popup. Popups close +# themselves when clicked outside, and the backdrop keeps that click from reaching +# the buttons behind them. +func _setup_popup_backdrop() -> void: + popup_backdrop = ColorRect.new() + popup_backdrop.name = "PopupBackdrop" + popup_backdrop.color = Color(0, 0, 0, 0.55) + popup_backdrop.mouse_filter = Control.MOUSE_FILTER_STOP + add_child(popup_backdrop) + popup_backdrop.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) + for popup: PopupWindow in _popups(): + move_child(popup, -1) + popup.visibility_changed.connect(_update_popup_backdrop) + _update_popup_backdrop() + +func _update_popup_backdrop() -> void: + popup_backdrop.visible = _popups().any(func(popup: PopupWindow) -> bool: return popup.visible) + # The cache size can be changed in the settings. Waiting until the settings are closed # avoids starting downloads while the user is still clicking the number up and down. func _on_settings_window_visibility_changed() -> void: @@ -374,19 +400,19 @@ func reset_points() -> void: func show_about() -> void: print("show_about") - about_window.show_window() + about_window.open() func show_shortcuts() -> void: print("show_shortcuts") - shortcuts_window.visible = true + shortcuts_window.open() func show_settings() -> void: print("show_settings") - settings_window.visible = true + settings_window.open() func show_qr() -> void: print("show_qr") - qr_window.show_window() + qr_window.open() func get_statistics() -> void: print("get_statistics") @@ -422,7 +448,6 @@ func get_suggestion_list() -> void: soundtracks.append_array(array) for soundtrack: String in soundtracks: var inspiration_label: Label = Label.new() - inspiration_label.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) inspiration_label.add_theme_font_size_override("font_size", 20) inspiration_label.text = soundtrack inspiration_label.autowrap_mode = TextServer.AUTOWRAP_WORD @@ -514,10 +539,7 @@ func _on_new_character_selected(character_texture: ImageTexture) -> void: func show_search() -> void: print("show_search") - if search_view.visible == false: - search_view.visible = true - else: - search_view.visible = false + search_view.toggle() ###Local diff --git a/MainWindow.tscn b/MainWindow.tscn index a858016..ccdbb69 100644 --- a/MainWindow.tscn +++ b/MainWindow.tscn @@ -16,6 +16,7 @@ [ext_resource type="PackedScene" uid="uid://btupbowehiyyu" path="res://QRWindow.tscn" id="15_qr_window"] [ext_resource type="PackedScene" uid="uid://cp54idaudb8dp" path="res://AboutWindow.tscn" id="16_about_window"] [ext_resource type="PackedScene" uid="uid://cxlsmxnrp0agh" path="res://WinnerWindow.tscn" id="17_winner_window"] +[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"] [sub_resource type="LabelSettings" id="LabelSettings_ychxr"] font_size = 25 @@ -69,7 +70,11 @@ events = [SubResource("InputEventKey_ujjlu")] [node name="Control" type="Control" unique_id=407149696] layout_mode = 3 -anchors_preset = 0 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 script = ExtResource("1_eu0t5") [node name="TopPanelContainer" type="PanelContainer" parent="." unique_id=663404711] @@ -78,7 +83,6 @@ offset_right = 1920.0 offset_bottom = 56.0 [node name="Title" type="Label" parent="." unique_id=1187907861] -texture_filter = 1 layout_mode = 0 offset_left = 64.0 offset_top = 12.0 @@ -120,14 +124,12 @@ follow_focus = true script = ExtResource("7_dj026") [node name="MusicList" type="VBoxContainer" parent="MusicListPanel/ScrollContainer" unique_id=373398568] -texture_filter = 1 layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 theme_override_constants/separation = 10 [node name="ScrollContainer" type="ScrollContainer" parent="." unique_id=498283761] -texture_filter = 1 layout_mode = 0 offset_left = 1216.0 offset_top = 128.0 @@ -138,7 +140,6 @@ vertical_scroll_mode = 3 script = ExtResource("2_gxtxm") [node name="VBoxContainer" type="VBoxContainer" parent="ScrollContainer" unique_id=1215162949] -texture_filter = 1 layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 @@ -151,7 +152,6 @@ offset_right = 1152.0 offset_bottom = 384.0 [node name="SoundtrackLabel" type="Label" parent="VBoxContainer" unique_id=173147793] -texture_filter = 1 custom_minimum_size = Vector2(1, 1) layout_mode = 2 label_settings = SubResource("LabelSettings_qspbu") @@ -159,7 +159,6 @@ horizontal_alignment = 1 autowrap_mode = 2 [node name="SongLabel" type="Label" parent="VBoxContainer" unique_id=1687490027] -texture_filter = 1 custom_minimum_size = Vector2(1, 1) layout_mode = 2 label_settings = SubResource("LabelSettings_3m52w") @@ -195,7 +194,6 @@ custom_minimum_size = Vector2(2.08165e-12, 40) layout_mode = 2 [node name="Label" type="Label" parent="Players/VBoxContainer/HBoxContainer" unique_id=1331357386] -texture_filter = 1 custom_minimum_size = Vector2(2.08165e-12, 40) layout_mode = 2 size_flags_horizontal = 3 @@ -205,7 +203,6 @@ horizontal_alignment = 1 vertical_alignment = 1 [node name="AddPlayersButton" type="TextureButton" parent="Players/VBoxContainer/HBoxContainer" unique_id=634410148] -texture_filter = 1 custom_minimum_size = Vector2(2.08165e-12, 40) layout_mode = 2 size_flags_horizontal = 8 @@ -232,7 +229,6 @@ placeholder_text = "Player name" script = ExtResource("7_qsdfy") [node name="AddPlayerButton" type="Button" parent="Players/VBoxContainer/AddPlayerContainer" unique_id=245001313] -texture_filter = 1 custom_minimum_size = Vector2(2.08165e-12, 40) layout_mode = 2 text = "Add" @@ -246,13 +242,14 @@ theme_override_styles/panel = SubResource("StyleBoxFlat_k1ygi") [node name="ScrollContainer" type="ScrollContainer" parent="Players/VBoxContainer" unique_id=1000000001] layout_mode = 2 size_flags_vertical = 15 +horizontal_scroll_mode = 0 [node name="PlayersListVBoxContainer" type="VBoxContainer" parent="Players/VBoxContainer/ScrollContainer" unique_id=1000000002] layout_mode = 2 +size_flags_horizontal = 3 size_flags_vertical = 15 [node name="ResetPlaylistButton" type="Button" parent="." unique_id=1754155620] -texture_filter = 1 layout_mode = 0 offset_left = 1360.0 offset_top = 64.0 @@ -263,7 +260,6 @@ action_mode = 0 text = "Reset playlist" [node name="ResetPointsButton" type="Button" parent="." unique_id=942880776] -texture_filter = 1 layout_mode = 0 offset_left = 1488.0 offset_top = 64.0 @@ -274,7 +270,6 @@ action_mode = 0 text = "Reset points" [node name="SyncButton" type="Button" parent="." unique_id=1228313556] -texture_filter = 1 layout_mode = 0 offset_left = 1616.0 offset_top = 64.0 @@ -285,7 +280,6 @@ action_mode = 0 text = "Sync soundtracks" [node name="SoundTestButton" type="Button" parent="." unique_id=1332971879] -texture_filter = 1 layout_mode = 0 offset_left = 1776.0 offset_top = 64.0 @@ -296,7 +290,6 @@ action_mode = 0 text = "Sound test" [node name="ShowAnswerButton" type="Button" parent="." unique_id=415388039] -texture_filter = 1 layout_mode = 0 offset_left = 65.0 offset_top = 944.0 @@ -308,7 +301,6 @@ shortcut = SubResource("Shortcut_a7fvb") text = "Show answer" [node name="NextButton" type="Button" parent="." unique_id=1744703488] -texture_filter = 1 layout_mode = 0 offset_left = 193.0 offset_top = 944.0 @@ -321,7 +313,6 @@ shortcut = SubResource("Shortcut_d6fml") text = "Randomize new track" [node name="SearchButton" type="Button" parent="." unique_id=1782453581] -texture_filter = 1 layout_mode = 0 offset_left = 1354.0 offset_top = 8.0 @@ -333,7 +324,6 @@ shortcut = SubResource("Shortcut_fbju4") text = "Search" [node name="SettingsButton" type="Button" parent="." unique_id=1193400323] -texture_filter = 1 layout_mode = 0 offset_left = 1442.0 offset_top = 8.0 @@ -344,7 +334,6 @@ action_mode = 0 text = "Settings" [node name="StatisticsButton" type="Button" parent="." unique_id=2045021269] -texture_filter = 1 layout_mode = 0 offset_left = 1530.0 offset_top = 8.0 @@ -355,7 +344,6 @@ action_mode = 0 text = "Statistics" [node name="ShortcutsButton" type="Button" parent="." unique_id=2066498226] -texture_filter = 1 layout_mode = 0 offset_left = 1626.0 offset_top = 8.0 @@ -366,7 +354,6 @@ action_mode = 0 text = "Shortcuts" [node name="AboutButton" type="Button" parent="." unique_id=1057035253] -texture_filter = 1 layout_mode = 0 offset_left = 1722.0 offset_top = 8.0 @@ -377,7 +364,6 @@ action_mode = 0 text = "About" [node name="QRButton" type="Button" parent="." unique_id=2021908559] -texture_filter = 1 layout_mode = 0 offset_left = 1802.0 offset_top = 8.0 @@ -440,12 +426,12 @@ offset_right = 1262.0 offset_bottom = 860.0 [node name="StatisticsPopupPanel" type="PopupPanel" parent="." unique_id=298311407] +theme = ExtResource("popup_theme") oversampling_override = 1.0 initial_position = 2 size = Vector2i(450, 100) [node name="Label" type="Label" parent="StatisticsPopupPanel" unique_id=548316493] -texture_filter = 1 offset_left = 4.0 offset_top = 4.0 offset_right = 446.0 @@ -454,7 +440,6 @@ text = "Statistics" horizontal_alignment = 1 [node name="StatisticsLabel" type="Label" parent="StatisticsPopupPanel" unique_id=58226477] -texture_filter = 1 offset_left = 4.0 offset_top = 4.0 offset_right = 446.0 diff --git a/Player.tscn b/Player.tscn index 97cc18a..563a62a 100644 --- a/Player.tscn +++ b/Player.tscn @@ -29,9 +29,12 @@ script = ExtResource("1_if4kc") [node name="HBoxContainer" type="HBoxContainer" parent="."] custom_minimum_size = Vector2(2.08165e-12, 40) -layout_mode = 0 -offset_right = 40.0 -offset_bottom = 40.0 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 [node name="Panel4" type="Panel" parent="HBoxContainer"] custom_minimum_size = Vector2(5, 40) @@ -41,12 +44,15 @@ theme_override_styles/panel = SubResource("StyleBoxFlat_821k2") [node name="Name" type="Label" parent="HBoxContainer"] custom_minimum_size = Vector2(130, 40) layout_mode = 2 +size_flags_horizontal = 3 size_flags_vertical = 1 text = ": 0" vertical_alignment = 1 +clip_text = true +text_overrun_behavior = 3 [node name="Panel3" type="Panel" parent="HBoxContainer"] -custom_minimum_size = Vector2(80, 2.08165e-12) +custom_minimum_size = Vector2(16, 2.08165e-12) layout_mode = 2 theme_override_styles/panel = SubResource("StyleBoxFlat_w10e8") @@ -91,7 +97,6 @@ theme_override_styles/panel = SubResource("StyleBoxFlat_b2crt") [node name="RemovePlayer" type="TextureButton" parent="HBoxContainer"] custom_minimum_size = Vector2(40, 40) layout_mode = 2 -size_flags_horizontal = 3 focus_mode = 0 action_mode = 0 texture_normal = ExtResource("3_j3uxe") diff --git a/PlayerObject.gd b/PlayerObject.gd index 5e9d146..d885d40 100644 --- a/PlayerObject.gd +++ b/PlayerObject.gd @@ -19,7 +19,10 @@ var player_score: int var is_first_point: bool = true -var character: Texture = load("res://noCharacter.png") +## Picture shown until the player picks a character. +const NO_CHARACTER_PATH: String = "res://noCharacter.png" + +var character: Texture = load(NO_CHARACTER_PATH) func _init(new_name: String) -> void: player_name = new_name diff --git a/QRWindow.gd b/QRWindow.gd index 52a02db..d13392a 100644 --- a/QRWindow.gd +++ b/QRWindow.gd @@ -1,4 +1,4 @@ -extends Control +extends PopupWindow @onready var qr_panel: Panel = $QRPanel @@ -21,21 +21,10 @@ func _ready() -> void: url_label.text = qr_code_rect.data print("QR code generated for: ", url) -func show_window() -> void: - visible = true +func open() -> void: + super() # Regenerate QR code in case settings changed var url: String = Settings.default_path + "/search" qr_code_rect.data = url url_label.text = qr_code_rect.data - -func _input(event: InputEvent) -> void: - if self.visible == true: - if event is InputEventMouseButton && event.is_pressed(): - var evLocal: InputEvent = make_input_local(event) - if !Rect2(Vector2(0, 0), qr_panel.size).has_point(evLocal.position): - self.visible = false - - -func hide_window() -> void: - visible = false diff --git a/QRWindow.tscn b/QRWindow.tscn index 34ff866..770a930 100644 --- a/QRWindow.tscn +++ b/QRWindow.tscn @@ -2,8 +2,10 @@ [ext_resource type="Script" uid="uid://bsa6xg5xos6h7" path="res://QRWindow.gd" id="1_qr_script"] [ext_resource type="Script" uid="uid://dynulhnyq5hpq" path="res://addons/kenyoni/qr_code/qr_code_rect.gd" id="2_jpsxt"] +[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"] -[node name="QRControl" type="Control" unique_id=1996957585] +[node name="QRControl" type="Control" unique_id=1996957585 node_paths=PackedStringArray("panel")] +theme = ExtResource("popup_theme") layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -11,6 +13,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_qr_script") +panel = NodePath("QRPanel") [node name="QRPanel" type="Panel" parent="." unique_id=1021365843] unique_name_in_owner = true diff --git a/SearchWindow.gd b/SearchWindow.gd index 6de885b..d29d3ed 100644 --- a/SearchWindow.gd +++ b/SearchWindow.gd @@ -1,4 +1,4 @@ -extends Control +extends PopupWindow @onready var search_list: VBoxContainer = $ScrollContainer/VBoxContainer @@ -36,7 +36,7 @@ func focus() -> void: func close() -> void: clear() - self.visible = false + super() func search() -> void: print(search_bar.text) @@ -179,13 +179,6 @@ func set_soundtracks(list: Array) -> void: "cleaned": clean_term(soundtrack) }) -func _input(event: InputEvent) -> void: - if self.visible == true: - if event is InputEventMouseButton && event.is_pressed(): - var evLocal: InputEvent = make_input_local(event) - if !Rect2(Vector2(0, 0), search_panel.size).has_point(evLocal.position): - self.visible = false - func clear() -> void: search_bar.text = "" show_all_soundtracks() diff --git a/SearchWindow.tscn b/SearchWindow.tscn index 02b3fef..e483a5a 100644 --- a/SearchWindow.tscn +++ b/SearchWindow.tscn @@ -1,8 +1,10 @@ [gd_scene format=3 uid="uid://bxydgil1yifps"] [ext_resource type="Script" uid="uid://bg5yo08638600" path="res://SearchWindow.gd" id="1_m7bij"] +[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"] -[node name="Control" type="Control" unique_id=616238617] +[node name="Control" type="Control" unique_id=616238617 node_paths=PackedStringArray("panel")] +theme = ExtResource("popup_theme") layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -10,6 +12,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_m7bij") +panel = NodePath("SearchPanel") [node name="SearchPanel" type="Panel" parent="." unique_id=1262945994] unique_name_in_owner = true @@ -18,7 +21,6 @@ offset_right = 1152.0 offset_bottom = 640.0 [node name="ClearButton" type="Button" parent="." unique_id=1549439398] -texture_filter = 1 layout_mode = 0 offset_left = 936.0 offset_top = 16.0 @@ -30,7 +32,6 @@ action_mode = 0 text = "Clear" [node name="Searchbar" type="TextEdit" parent="." unique_id=2112669372] -texture_filter = 1 layout_mode = 0 offset_left = 32.0 offset_top = 16.0 diff --git a/SettingsWindow.gd b/SettingsWindow.gd index 9eb8daa..f0a1267 100644 --- a/SettingsWindow.gd +++ b/SettingsWindow.gd @@ -1,4 +1,4 @@ -extends Control +extends PopupWindow @onready var settings_panel: Panel = %SettingsPanel @@ -116,13 +116,6 @@ func _on_visibility_changed() -> void: if visible: add_to_database_button.button_pressed = Settings.add_to_stats -func _input(event: InputEvent) -> void: - if self.visible == true: - if event is InputEventMouseButton && event.is_pressed(): - var evLocal: InputEvent = make_input_local(event) - if !Rect2(Vector2(0, 0), settings_panel.size).has_point(evLocal.position): - self.visible = false - func fullscreen() -> void: Settings.fullscreen = !Settings.fullscreen if Settings.fullscreen == true: diff --git a/SettingsWindow.tscn b/SettingsWindow.tscn index 8271407..a35edc0 100644 --- a/SettingsWindow.tscn +++ b/SettingsWindow.tscn @@ -1,11 +1,13 @@ [gd_scene format=3 uid="uid://dldpeo5y3l5hq"] [ext_resource type="Script" uid="uid://dukqyjoduf1af" path="res://SettingsWindow.gd" id="1_bt55j"] +[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"] [sub_resource type="LabelSettings" id="LabelSettings_3xrlm"] font_size = 25 -[node name="SettingsControl" type="Control" unique_id=1298840328] +[node name="SettingsControl" type="Control" unique_id=1298840328 node_paths=PackedStringArray("panel")] +theme = ExtResource("popup_theme") layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -13,6 +15,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_bt55j") +panel = NodePath("SettingsPanel") [node name="SettingsPanel" type="Panel" parent="." unique_id=45537159] unique_name_in_owner = true diff --git a/SyncWindow.tscn b/SyncWindow.tscn index 747fb45..7c3075f 100644 --- a/SyncWindow.tscn +++ b/SyncWindow.tscn @@ -1,11 +1,13 @@ [gd_scene format=3 uid="uid://dpdem7pdxweb5"] [ext_resource type="Script" uid="uid://c3ejukwth512a" path="res://sync_window.gd" id="1_8ba47"] +[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"] [sub_resource type="LabelSettings" id="LabelSettings_anbfi"] font_size = 25 -[node name="Control2" type="Control" unique_id=1302914356] +[node name="Control2" type="Control" unique_id=1302914356 node_paths=PackedStringArray("panel")] +theme = ExtResource("popup_theme") layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -13,6 +15,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_8ba47") +panel = NodePath("SyncPanel") [node name="SyncPanel" type="Panel" parent="." unique_id=1714634204] unique_name_in_owner = true diff --git a/WinnerWindow.tscn b/WinnerWindow.tscn index c7a9415..c2e0ddf 100644 --- a/WinnerWindow.tscn +++ b/WinnerWindow.tscn @@ -2,11 +2,13 @@ [ext_resource type="Script" uid="uid://dxpb22xbwf3t1" path="res://winner_window.gd" id="1_winner_script"] [ext_resource type="Texture2D" uid="uid://r4as0nmtoa7p" path="res://noCharacter.png" id="2_no_character"] +[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"] [sub_resource type="LabelSettings" id="LabelSettings_hr75l"] font_size = 35 -[node name="WinnerControl" type="Control" unique_id=1111111111] +[node name="WinnerControl" type="Control" unique_id=1111111111 node_paths=PackedStringArray("panel")] +theme = ExtResource("popup_theme") layout_mode = 3 anchors_preset = 15 anchor_right = 1.0 @@ -14,6 +16,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_winner_script") +panel = NodePath("WinnerPanel") [node name="WinnerPanel" type="Panel" parent="." unique_id=89212869] unique_name_in_owner = true @@ -40,7 +43,6 @@ stretch_mode = 5 [node name="WinnerLabel" type="Label" parent="WinnerPanel/VBoxContainer" unique_id=1437597286] unique_name_in_owner = true -texture_filter = 1 layout_mode = 2 text = "Winner!" label_settings = SubResource("LabelSettings_hr75l") diff --git a/log.gd b/log.gd index 8a0c113..6ec7c59 100644 --- a/log.gd +++ b/log.gd @@ -1,4 +1,4 @@ -extends Control +extends PopupWindow @onready var log_panel: Panel = %LogPanel @@ -15,7 +15,6 @@ func add_log_row(text: String) -> void: func make_log_label(row: String) -> Label: var log_label: Label = Label.new() - log_label.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) log_label.add_theme_font_size_override("font_size", 20) log_label.text = row log_label.autowrap_mode = TextServer.AUTOWRAP_WORD diff --git a/new_character_select.gd b/new_character_select.gd index 8bf5ef0..fd720b9 100644 --- a/new_character_select.gd +++ b/new_character_select.gd @@ -1,4 +1,4 @@ -extends Control +extends PopupWindow @onready var character_panel: Panel = %CharacterPanel @@ -46,22 +46,20 @@ func show_grid(player_name: String) -> void: print("show_grid") title_label.text = "Select character for " + player_name close_timer.stop() - self.visible = true + open() can_select_character = true Settings.character_select_open = true +func close() -> void: + super() + Settings.character_select_open = false + func _on_character_selected(texture: ImageTexture) -> void: print("_on_character_selected") + # Not close(): character_select_open stays true until close_timer fires, so the click + # that picked the character does not also select a song behind it self.visible = false if can_select_character: new_new_character_selected.emit(texture) close_timer.start() can_select_character = false - -func _input(event: InputEvent) -> void: - if self.visible == true: - if event is InputEventMouseButton && event.is_pressed(): - var evLocal: InputEvent = make_input_local(event) - if !Rect2(Vector2(0, 0), character_panel.size).has_point(evLocal.position): - self.visible = false - Settings.character_select_open = false diff --git a/new_character_select.tscn b/new_character_select.tscn index 038b505..0161caf 100644 --- a/new_character_select.tscn +++ b/new_character_select.tscn @@ -1,14 +1,17 @@ [gd_scene load_steps=3 format=3 uid="uid://cdy4kvemwaiom"] [ext_resource type="Script" uid="uid://cvffg87wr6r3y" path="res://new_character_select.gd" id="1_h4tdp"] +[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"] [sub_resource type="LabelSettings" id="LabelSettings_p4iny"] font_size = 25 -[node name="Control" type="Control"] +[node name="Control" type="Control" node_paths=PackedStringArray("panel")] +theme = ExtResource("popup_theme") layout_mode = 3 anchors_preset = 0 script = ExtResource("1_h4tdp") +panel = NodePath("CharacterPanel") [node name="CharacterPanel" type="Panel" parent="."] unique_name_in_owner = true diff --git a/popup_theme.tres b/popup_theme.tres new file mode 100644 index 0000000..e20aa7d --- /dev/null +++ b/popup_theme.tres @@ -0,0 +1,21 @@ +[gd_resource type="Theme" format=3] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_popup"] +bg_color = Color(0.180392, 0.180392, 0.180392, 1) +border_width_left = 2 +border_width_top = 2 +border_width_right = 2 +border_width_bottom = 2 +border_color = Color(0.36, 0.36, 0.36, 1) +corner_radius_top_left = 6 +corner_radius_top_right = 6 +corner_radius_bottom_right = 6 +corner_radius_bottom_left = 6 +content_margin_left = 12.0 +content_margin_top = 12.0 +content_margin_right = 12.0 +content_margin_bottom = 12.0 + +[resource] +Panel/styles/panel = SubResource("StyleBoxFlat_popup") +PopupPanel/styles/panel = SubResource("StyleBoxFlat_popup") diff --git a/popup_window.gd b/popup_window.gd new file mode 100644 index 0000000..46698e3 --- /dev/null +++ b/popup_window.gd @@ -0,0 +1,37 @@ +class_name PopupWindow +extends Control +## A window shown on top of the main window, such as Settings or About. +## +## Clicking outside [member panel] closes it. Popups that need to prepare or clean up +## override [method open] or [method close] and call [code]super()[/code]. + +## The visible part of the popup. Clicks outside it close the popup. +@export var panel: Control + + +## Shows the popup. +func open() -> void: + visible = true + + +## Hides the popup. +func close() -> void: + visible = false + + +## Opens the popup if it is closed and closes it if it is open. +func toggle() -> void: + if visible: + close() + else: + open() + + +func _input(event: InputEvent) -> void: + if !visible || !(event is InputEventMouseButton) || !event.pressed: + return + var click: InputEventMouseButton = panel.make_input_local(event) + if !Rect2(Vector2.ZERO, panel.size).has_point(click.position): + close() + # The click only closes the popup; it must not also press what is behind it + get_viewport().set_input_as_handled() diff --git a/popup_window.gd.uid b/popup_window.gd.uid new file mode 100644 index 0000000..1c572c6 --- /dev/null +++ b/popup_window.gd.uid @@ -0,0 +1 @@ +uid://dwd4dcbujpv6p diff --git a/shortcutsWindow.tscn b/shortcutsWindow.tscn index 11ddc5f..31cf931 100644 --- a/shortcutsWindow.tscn +++ b/shortcutsWindow.tscn @@ -1,8 +1,10 @@ [gd_scene load_steps=2 format=3 uid="uid://bd1by80q1v27e"] [ext_resource type="Script" uid="uid://rolypd0cqd6h" path="res://shortcuts_window.gd" id="1_12pxi"] +[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"] -[node name="ShorcutsControl" type="Control"] +[node name="ShorcutsControl" type="Control" node_paths=PackedStringArray("panel")] +theme = ExtResource("popup_theme") custom_minimum_size = Vector2(700, 700) layout_mode = 3 anchors_preset = 15 @@ -11,6 +13,7 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 script = ExtResource("1_12pxi") +panel = NodePath("ShortcutsPanel") [node name="ShortcutsPanel" type="Panel" parent="."] unique_name_in_owner = true diff --git a/shortcuts_window.gd b/shortcuts_window.gd index 62a2282..82e3c1c 100644 --- a/shortcuts_window.gd +++ b/shortcuts_window.gd @@ -1,4 +1,4 @@ -extends Control +extends PopupWindow @onready var shortcuts_vbox: VBoxContainer = %shortcutsVBox @@ -29,22 +29,11 @@ var shortcuts_dic: Dictionary = { #Color(0.302, 0.302, 0.302) #Color(0.259, 0.259, 0.259) -func _input(event: InputEvent) -> void: - if self.visible == true: - if shortcuts_panel == null: - return - if event is InputEventMouseButton && event.is_pressed(): - var evLocal: InputEvent = make_input_local(event) - if !Rect2(Vector2(0, 0), shortcuts_panel.size).has_point(evLocal.position): - self.visible = false - - func _ready() -> void: shortcuts_dic.sort() Settings.delete_children(shortcuts_vbox) var title: Label = Label.new() - title.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) title.add_theme_font_size_override("font_size", 20) title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title.text = "Shortcuts" @@ -57,7 +46,6 @@ func _ready() -> void: hbox.alignment = BoxContainer.ALIGNMENT_CENTER var label: Label = Label.new() - label.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) label.add_theme_font_size_override("font_size", 20) label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.text = shortcut @@ -65,7 +53,6 @@ func _ready() -> void: label.size_flags_horizontal = Control.SIZE_EXPAND_FILL var label2: Label = Label.new() - label2.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) label2.add_theme_font_size_override("font_size", 20) label2.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label2.text = shortcuts_dic[shortcut] diff --git a/sync_window.gd b/sync_window.gd index f71784b..4eba0c8 100644 --- a/sync_window.gd +++ b/sync_window.gd @@ -1,4 +1,4 @@ -extends Control +extends PopupWindow @onready var sync_panel: Panel = %SyncPanel @@ -75,13 +75,6 @@ func _ready() -> void: restart_sync_button.pressed.connect(start_sync) restart_sync_button.visible = false -func _input(event: InputEvent) -> void: - if self.visible == true: - if event is InputEventMouseButton && event.is_pressed(): - var evLocal: InputEvent = make_input_local(event) - if !Rect2(Vector2(0, 0), sync_panel.size).has_point(evLocal.position): - self.visible = false - func show_section_if_has_data(data: Variant, vbox: VBoxContainer, separator: VSeparator, header_text: String) -> bool: if data != null && data.size() > 0: vbox.visible = true @@ -202,7 +195,6 @@ func list_visible(vbox: VBoxContainer, seperator: VSeparator) -> void: func make_header_label(vbox: VBoxContainer, text: String) -> void: var label: Label = Label.new() - label.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) label.add_theme_font_size_override("font_size", 20) label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.add_theme_color_override("font_color", Color(1, 0.5, 0)) @@ -211,7 +203,6 @@ func make_header_label(vbox: VBoxContainer, text: String) -> void: func make_soundtrack_label(text: String) -> Label: var label: Label = Label.new() - label.set_texture_filter(TextureFilter.TEXTURE_FILTER_NEAREST) label.add_theme_font_size_override("font_size", 18) label.text = text label.autowrap_mode = TextServer.AUTOWRAP_OFF diff --git a/tests/integration/test_popups.gd b/tests/integration/test_popups.gd new file mode 100644 index 0000000..7c79a34 --- /dev/null +++ b/tests/integration/test_popups.gd @@ -0,0 +1,126 @@ +extends MainWindowTestSuite +## All popups share PopupWindow: clicking outside the panel closes them, the backdrop +## dims the window behind them, and the closing click does not reach the main window. + +const POPUP_NODES: Array[String] = [ + "Search", "SyncWindow", "SettingsWindow", "QRWindow", "NewCharacterSelect", + "ShortcutsWindow", "AboutWindow", "WinnerWindow", "Log", +] + + +func _popup(node_name: String) -> PopupWindow: + return _window.get_node(node_name) + + +func _backdrop() -> ColorRect: + return _window.get_node("PopupBackdrop") + + +## Clicks at [param position], given in the window's 1920x1080 layout coordinates. +func _click_at(position: Vector2) -> void: + # The runner takes window pixels; the headless test window is much smaller than the + # layout, so convert like a real mouse position would be + _runner.set_mouse_position(_window.get_viewport().get_final_transform() * position) + await _runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT) + await _runner.await_input_processed() + + +## A point on screen outside the popup's panel. +func _outside(popup: PopupWindow) -> Vector2: + var rect: Rect2 = popup.panel.get_global_rect() + return Vector2(rect.end.x + 5, rect.end.y + 5) if rect.end.x < 1900 \ + else Vector2(rect.position.x - 5, rect.position.y - 5) + + +func test_backdrop_is_hidden_without_popups() -> void: + assert_bool(_backdrop().visible).is_false() + + +func test_backdrop_covers_the_whole_window() -> void: + _popup("SettingsWindow").open() + + assert_vector(_backdrop().get_global_rect().size).is_equal(_window.get_viewport_rect().size) + + +func test_every_popup_is_a_popup_window() -> void: + for node_name: String in POPUP_NODES: + assert_object(_window.get_node(node_name)).is_instanceof(PopupWindow) + + +func test_every_popup_shows_backdrop_and_closes_on_outside_click() -> void: + for node_name: String in POPUP_NODES: + var popup: PopupWindow = _popup(node_name) + popup.open() + await _runner.simulate_frames(1) + assert_bool(_backdrop().visible).override_failure_message( + "%s: backdrop not shown" % node_name).is_true() + + await _click_at(_outside(popup)) + + assert_bool(popup.visible).override_failure_message( + "%s: not closed by outside click" % node_name).is_false() + assert_bool(_backdrop().visible).override_failure_message( + "%s: backdrop still shown" % node_name).is_false() + + +func test_click_inside_popup_keeps_it_open() -> void: + var settings: PopupWindow = _popup("SettingsWindow") + settings.open() + await _runner.simulate_frames(1) + + await _click_at(settings.panel.get_global_rect().get_center()) + + assert_bool(settings.visible).is_true() + + +func test_closing_click_does_not_press_button_behind_popup() -> void: + var settings: PopupWindow = _popup("SettingsWindow") + var reset_button: Button = _window.get_node("ResetPlaylistButton") + settings.open() + await _runner.simulate_frames(1) + assert_bool(settings.panel.get_global_rect().intersects(reset_button.get_global_rect())) \ + .is_false() + + await _click_at(reset_button.get_global_rect().get_center()) + + assert_bool(settings.visible).is_false() + assert_int(_server.count("/music/reset")).is_equal(0) + + +func test_button_works_again_after_popup_closed() -> void: + var reset_button: Button = _window.get_node("ResetPlaylistButton") + + await _click_at(reset_button.get_global_rect().get_center()) + + assert_int(_server.count("/music/reset")).is_equal(1) + + +func test_closing_search_clears_the_search() -> void: + var search: PopupWindow = _popup("Search") + search.open() + var search_bar: TextEdit = search.get_node("Searchbar") + search_bar.text = "mario" + + search.close() + + assert_str(search_bar.text).is_empty() + + +func test_closing_character_select_allows_song_clicks_again() -> void: + var character_select: PopupWindow = _popup("NewCharacterSelect") + character_select.show_grid("Crono") + assert_bool(Settings.character_select_open).is_true() + + character_select.close() + + assert_bool(Settings.character_select_open).is_false() + + +func test_toggle_opens_and_closes() -> void: + var shortcuts: PopupWindow = _popup("ShortcutsWindow") + + shortcuts.toggle() + assert_bool(shortcuts.visible).is_true() + + shortcuts.toggle() + assert_bool(shortcuts.visible).is_false() diff --git a/tests/integration/test_popups.gd.uid b/tests/integration/test_popups.gd.uid new file mode 100644 index 0000000..8fc3dee --- /dev/null +++ b/tests/integration/test_popups.gd.uid @@ -0,0 +1 @@ +uid://kcnjdw4ke7hd diff --git a/winner_window.gd b/winner_window.gd index 7e9a1c2..1b4807e 100644 --- a/winner_window.gd +++ b/winner_window.gd @@ -1,4 +1,4 @@ -extends Control +extends PopupWindow @onready var winner_panel: Panel = %WinnerPanel @@ -23,6 +23,11 @@ func show_winner(winner: PlayerObject) -> void: winner_label.text = winner.player_name + " won with " + str(winner.player_score) + \ " points!!" winner_picture.texture = winner.character + # The "no character" placeholder is pixel art, which blurs when scaled up smoothly + if winner.character.resource_path == PlayerObject.NO_CHARACTER_PATH: + winner_picture.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST + else: + winner_picture.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR winner_picture.custom_minimum_size = Vector2(480, 200) winner_picture.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL winner_picture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED @@ -50,13 +55,4 @@ func show_winner(winner: PlayerObject) -> void: placing += 1 -func hide_window() -> void: - visible = false - -func _input(event: InputEvent) -> void: - if visible: - if event is InputEventMouseButton and event.is_pressed(): - var ev_local: InputEvent = make_input_local(event) - if !Rect2(Vector2(0, 0), winner_panel.size).has_point(ev_local.position): - visible = false