Split settings into a quiz card and an application Settings popup
Build / test (push) Successful in 44s
Build / build (push) Successful in 1m13s

- A Quiz settings button under the player bar opens a small card above
  it with the quiz settings, including Auto repeat, which moves there
  from under the player bar. The card does not dim the quiz, so it can
  be changed during a song, and grows to fit its rows.
- A Settings button in the top bar opens the application settings:
  server, fullscreen and songs to download ahead. Quick sync is gone,
  as the server no longer has it, and the server list is in Settings.
- Labels use sentence case, and seconds to hide show as whole numbers.
- Debug builds switch to the local server and a low winning score in
  the Settings autoload, before any scene asks the server for anything.
  The DEBUG label moves to the top bar, so it shows on every tab.
- Search moves from the top bar back to the quiz toolbar, as the
  Statistics tab has its own search.
This commit is contained in:
Sansan committed 2026-10-07 16:05:13 +02:00
1 parent 8d21b0617d
commit b510aa47f2
34 files changed
+3074 -2525

No files matched your search

+19 -19
View File
@@ -3,7 +3,8 @@
[ext_resource type="Script" path="res://quiz/quiz.gd" id="quiz_script"]
[ext_resource type="PackedScene" uid="uid://ds15cgsf8vpvc" path="res://player/MusicPlayer.tscn" id="5_emn36"]
[ext_resource type="PackedScene" uid="uid://cdy4kvemwaiom" path="res://popups/CharacterSelectPopup.tscn" id="6_dhrvg"]
[ext_resource type="PackedScene" uid="uid://dldpeo5y3l5hq" path="res://popups/SettingsPopup.tscn" id="11_k62u5"]
[ext_resource type="PackedScene" path="res://quiz/QuizSettingsCard.tscn" id="scene_QuizSettingsCard"]
[ext_resource type="Texture2D" path="res://icons/settings_light_icon.svg" id="settings_icon"]
[ext_resource type="PackedScene" uid="uid://cxlsmxnrp0agh" path="res://popups/WinnerPopup.tscn" id="17_winner_window"]
[ext_resource type="PackedScene" path="res://quiz/MusicListPanel.tscn" id="scene_MusicListPanel"]
[ext_resource type="PackedScene" path="res://quiz/PlayersPanel.tscn" id="scene_PlayersPanel"]
@@ -90,14 +91,6 @@ offset_top = 944.0
offset_right = 368.0
offset_bottom = 975.0
[node name="SettingsPopup" parent="." unique_id=1323246978 instance=ExtResource("11_k62u5")]
visible = false
layout_mode = 1
offset_left = 710.0
offset_top = 220.0
offset_right = 710.0
offset_bottom = 220.0
[node name="CharacterSelectPopup" parent="." unique_id=1679856322 instance=ExtResource("6_dhrvg")]
unique_name_in_owner = true
visible = false
@@ -116,18 +109,25 @@ offset_top = 80.0
offset_right = 160.0
offset_bottom = 80.0
[node name="DebugLabel" type="Label" parent="." unique_id=1959221257]
[node name="QuizSettingsButton" type="Button" parent="." unique_id=1323246979]
layout_mode = 0
offset_left = 64.0
offset_top = 1030.0
offset_right = 224.0
offset_bottom = 1066.0
focus_mode = 0
action_mode = 0
text = "Quiz settings"
icon = ExtResource("settings_icon")
expand_icon = true
[node name="QuizSettingsCard" parent="." unique_id=1323246980 instance=ExtResource("scene_QuizSettingsCard")]
visible = false
layout_mode = 0
offset_left = 974.0
offset_top = 904.0
offset_right = 1014.0
offset_bottom = 927.0
scale = Vector2(3, 3)
text = "DEBUG"
horizontal_alignment = 1
vertical_alignment = 1
autowrap_mode = 2
offset_left = 64.0
offset_top = 984.0
offset_right = 64.0
offset_bottom = 984.0
[node name="SongLoader" type="Node" parent="." unique_id=1520867349]
script = ExtResource("song_loader_script")
+183
View File
@@ -0,0 +1,183 @@
[gd_scene format=3]
[ext_resource type="Script" path="res://quiz/quiz_settings_card.gd" id="1_card_script"]
[node name="QuizSettingsCard" type="Control" node_paths=PackedStringArray("panel")]
layout_mode = 3
anchors_preset = 0
mouse_filter = 2
script = ExtResource("1_card_script")
panel = NodePath("CardPanel")
[node name="CardPanel" type="Panel" parent="."]
unique_name_in_owner = true
layout_mode = 0
offset_top = -470.0
offset_right = 440.0
theme_type_variation = &"PopupBackground"
[node name="VBox" type="VBoxContainer" parent="CardPanel"]
unique_name_in_owner = true
layout_mode = 0
offset_left = 16.0
offset_top = 12.0
offset_right = 424.0
[node name="TitleLabel" type="Label" parent="CardPanel/VBox"]
layout_mode = 2
theme_type_variation = &"Title"
text = "Quiz settings"
horizontal_alignment = 1
[node name="AutoRepeatCheckButton" type="CheckButton" parent="CardPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "Auto repeat"
[node name="StopAfterCurrentCheckButton" type="CheckButton" parent="CardPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "Stop after current"
[node name="HideNextTrackCheckButton" type="CheckButton" parent="CardPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "Hide next track"
[node name="HideBeginningCheckButton" type="CheckButton" parent="CardPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "Hide the beginning of songs"
[node name="HideForSecondsRow" type="HBoxContainer" parent="CardPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
[node name="Title" type="Label" parent="CardPanel/VBox/HideForSecondsRow"]
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"SettingsRowLabel"
text = "Seconds to hide"
[node name="HideForSecondsLabel" type="Label" parent="CardPanel/VBox/HideForSecondsRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(48, 0)
layout_mode = 2
text = "5"
horizontal_alignment = 1
[node name="LowerHideForSecondsButton" type="Button" parent="CardPanel/VBox/HideForSecondsRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(30, 0)
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "-1"
[node name="IncreaseHideForSecondsButton" type="Button" parent="CardPanel/VBox/HideForSecondsRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(30, 0)
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "+1"
[node name="HideLengthCheckButton" type="CheckButton" parent="CardPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "Hide song length"
[node name="HideTicksCheckButton" type="CheckButton" parent="CardPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "Hide ticks in the playbar"
[node name="LowPlayedCheckButton" type="CheckButton" parent="CardPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "Prefer less played soundtracks"
[node name="AddToDatabaseCheckButton" type="CheckButton" parent="CardPanel/VBox"]
unique_name_in_owner = true
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "Count plays in statistics"
[node name="WinningScoreRow" type="HBoxContainer" parent="CardPanel/VBox"]
layout_mode = 2
[node name="Title" type="Label" parent="CardPanel/VBox/WinningScoreRow"]
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"SettingsRowLabel"
text = "Winning score"
[node name="ScoreLabel" type="Label" parent="CardPanel/VBox/WinningScoreRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(48, 0)
layout_mode = 2
text = "20"
horizontal_alignment = 1
[node name="LowerButton" type="Button" parent="CardPanel/VBox/WinningScoreRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(30, 0)
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "-1"
[node name="IncreaseButton" type="Button" parent="CardPanel/VBox/WinningScoreRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(30, 0)
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "+1"
[node name="InspirationRow" type="HBoxContainer" parent="CardPanel/VBox"]
layout_mode = 2
[node name="Title" type="Label" parent="CardPanel/VBox/InspirationRow"]
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"SettingsRowLabel"
text = "Inspiration list speed"
[node name="InspirationListSpeedLabel" type="Label" parent="CardPanel/VBox/InspirationRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(48, 0)
layout_mode = 2
text = "1"
horizontal_alignment = 1
[node name="LowerInspirationSpeedButton" type="Button" parent="CardPanel/VBox/InspirationRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(30, 0)
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "-1"
[node name="IncreaseInspirationSpeedButton" type="Button" parent="CardPanel/VBox/InspirationRow"]
unique_name_in_owner = true
custom_minimum_size = Vector2(30, 0)
layout_mode = 2
focus_mode = 0
action_mode = 0
text = "+1"
+20 -11
View File
@@ -2,6 +2,14 @@
[ext_resource type="Script" path="res://quiz/quiz_toolbar.gd" id="part_script"]
[sub_resource type="InputEventKey" id="InputEventKey_search"]
device = -1
alt_pressed = true
keycode = 83
[sub_resource type="Shortcut" id="Shortcut_search"]
events = [SubResource("InputEventKey_search")]
[node name="QuizToolbar" type="Control" unique_id=1873301941]
layout_mode = 3
offset_right = 495.0
@@ -9,31 +17,32 @@ offset_bottom = 31.0
mouse_filter = 2
script = ExtResource("part_script")
[node name="ResetPlaylistButton" type="Button" parent="." unique_id=1754155620]
[node name="SearchButton" type="Button" parent="." unique_id=1782453581]
layout_mode = 0
offset_right = 111.0
offset_bottom = 31.0
focus_mode = 0
action_mode = 0
shortcut = SubResource("Shortcut_search")
text = "Search"
[node name="ResetPlaylistButton" type="Button" parent="." unique_id=1754155620]
layout_mode = 0
offset_left = 128.0
offset_right = 239.0
offset_bottom = 31.0
focus_mode = 0
action_mode = 0
text = "Reset playlist"
[node name="ResetPointsButton" type="Button" parent="." unique_id=942880776]
layout_mode = 0
offset_left = 128.0
offset_right = 239.0
offset_bottom = 31.0
focus_mode = 0
action_mode = 0
text = "Reset points"
[node name="SettingsButton" type="Button" parent="." unique_id=1193400323]
layout_mode = 0
offset_left = 256.0
offset_right = 367.0
offset_bottom = 31.0
focus_mode = 0
action_mode = 0
text = "Settings"
text = "Reset points"
[node name="SoundTestButton" type="Button" parent="." unique_id=1332971879]
layout_mode = 0
+13 -27
View File
@@ -1,7 +1,7 @@
class_name Quiz
extends Control
## The quiz: the song that is playing, the playlist, the players and their points, and
## the popups opened from here (settings, character select, winner and log).
## the popups opened from here (the quiz settings card, character select and winner).
##
## The main window shows the quiz below its top bar. Pictures and buttons keep the
## positions they had in the main window, so the quiz fills the whole window and lets
@@ -12,6 +12,9 @@ extends Control
signal server_changed
## Emitted when Search is pressed. The Search popup is in the main window.
signal search_pressed
## Emitted with a line for the game log, e.g. when a player gets a point.
signal event_logged(text: String)
@@ -30,28 +33,23 @@ var popup_backdrop: PopupBackdrop
@onready var now_playing: NowPlaying = $NowPlaying
@onready var players_panel: PlayersPanel = $PlayersPanel
@onready var toolbar: QuizToolbar = $QuizToolbar
@onready var settings_popup: SettingsPopup = $SettingsPopup
@onready var quiz_settings_card: QuizSettingsCard = $QuizSettingsCard
@onready var quiz_settings_button: Button = $QuizSettingsButton
@onready var winner_popup: WinnerPopup = %WinnerPopup
@onready var song_controls: SongControls = $SongControls
@onready var song_loader: SongLoader = $SongLoader
@onready var music_list_panel: MusicListPanel = $MusicListPanel
@onready var character_select_popup: CharacterSelectPopup = %CharacterSelectPopup
@onready var music_player: MusicPlayer = $MusicPlayer
@onready var debug_label: Label = $DebugLabel
func _ready() -> void:
popup_backdrop = PopupBackdrop.add_to(self, _popups())
if OS.has_feature("debug"):
GameLog.debug("is_debug")
debug_label.visible = true
Settings.is_debug = true
settings_popup.set_winning_score(5)
settings_popup.set_selected_server(4)
song_controls.next_pressed.connect(play_next_song)
song_controls.show_answer_pressed.connect(show_answer_pressed)
toolbar.sound_test_pressed.connect(music_player.get_sound_test_song)
toolbar.settings_pressed.connect(show_settings)
toolbar.search_pressed.connect(search_pressed.emit)
quiz_settings_button.pressed.connect(quiz_settings_card.toggle)
toolbar.reset_playlist_pressed.connect(reset_playlist)
toolbar.reset_points_pressed.connect(reset_points)
song_loader.song_list_loaded.connect(update_song_list)
@@ -64,8 +62,6 @@ func _ready() -> void:
players_panel.change_character_requested.connect(_on_change_character_requested)
music_player.play_next_song.connect(play_next_song)
music_player.update_song_list.connect(update_song_list)
settings_popup.server_changed.connect(_on_server_changed)
settings_popup.visibility_changed.connect(_on_settings_popup_visibility_changed)
open_button.pressed.connect(open)
@@ -115,8 +111,9 @@ func refresh_from_server() -> void:
character_select_popup.load_characters()
func _on_server_changed() -> void:
GameLog.debug("_on_server_changed")
## Reloads everything from the server picked in the settings and starts a new game.
func on_server_changed() -> void:
GameLog.debug("on_server_changed")
refresh_from_server()
reset_points()
server_changed.emit()
@@ -139,15 +136,9 @@ func play_next_song() -> void:
song_loader.fetch_song_if_idle()
# The quiz settings card is left out: it does not dim the quiz
func _popups() -> Array[BasePopup]:
return [settings_popup, character_select_popup, winner_popup]
# 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_popup_visibility_changed() -> void:
if !settings_popup.visible:
song_loader.fill_cache()
return [character_select_popup, winner_popup]
func _on_song_added() -> void:
@@ -204,11 +195,6 @@ func reset_points() -> void:
stats_paused_by_win = false
func show_settings() -> void:
GameLog.debug("show_settings")
settings_popup.open()
# Each new player's points reach the music player, the log and the winner popup.
func _on_player_added(new_player_object: PlayerObject) -> void:
new_player_object.first_point_triggered.connect(music_player._on_point_triggered.bind("first"))
+130
View File
@@ -0,0 +1,130 @@
class_name QuizSettingsCard
extends BasePopup
## The quiz settings, in a small card that opens above the Quiz settings button by the
## music player: how songs are played and hidden, statistics, the winning score and the
## inspiration list. It does not dim the quiz, so it can be changed during a song.
##
## The card's bottom edge is where the card is placed, just above the player bar, and
## it grows upward to fit its rows.
##
## The application settings, such as the server, are in [SettingsPopup].
## The most seconds of a song's beginning that can be hidden.
const MAX_HIDE_SECONDS: float = 10.0
## Space between the rows and the card's edges.
const PADDING: Vector2 = Vector2(16, 12)
@onready var card_panel: Panel = %CardPanel
@onready var rows: VBoxContainer = %VBox
@onready var auto_repeat_button: CheckButton = %AutoRepeatCheckButton
@onready var stop_after_current_button: CheckButton = %StopAfterCurrentCheckButton
@onready var hide_next_track_button: CheckButton = %HideNextTrackCheckButton
@onready var hide_beginning_button: CheckButton = %HideBeginningCheckButton
@onready var hide_for_seconds_row: HBoxContainer = %HideForSecondsRow
@onready var hide_for_seconds_label: Label = %HideForSecondsLabel
@onready var lower_hide_for_seconds_button: Button = %LowerHideForSecondsButton
@onready var increase_hide_for_seconds_button: Button = %IncreaseHideForSecondsButton
@onready var hide_length_button: CheckButton = %HideLengthCheckButton
@onready var hide_ticks_button: CheckButton = %HideTicksCheckButton
@onready var low_played_button: CheckButton = %LowPlayedCheckButton
@onready var add_to_database_button: CheckButton = %AddToDatabaseCheckButton
@onready var score_label: Label = %ScoreLabel
@onready var lower_winning_score_button: Button = %LowerButton
@onready var increase_winning_score_button: Button = %IncreaseButton
@onready var inspiration_speed_label: Label = %InspirationListSpeedLabel
@onready var lower_inspiration_speed_button: Button = %LowerInspirationSpeedButton
@onready var increase_inspiration_speed_button: Button = %IncreaseInspirationSpeedButton
func _ready() -> void:
auto_repeat_button.toggled.connect(func(on: bool) -> void: Settings.auto_repeat_song = on)
stop_after_current_button.toggled.connect(
func(on: bool) -> void: Settings.stop_after_current = on)
hide_next_track_button.toggled.connect(
func(on: bool) -> void: Settings.hide_next_track = on)
hide_beginning_button.toggled.connect(_on_hide_beginning_toggled)
hide_length_button.toggled.connect(func(on: bool) -> void: Settings.hide_length = on)
hide_ticks_button.toggled.connect(func(on: bool) -> void: Settings.hide_ticks = on)
low_played_button.toggled.connect(
func(on: bool) -> void: Settings.use_low_played_mode = on)
add_to_database_button.toggled.connect(func(on: bool) -> void: Settings.add_to_stats = on)
lower_winning_score_button.pressed.connect(lower_winning_score)
increase_winning_score_button.pressed.connect(increase_winning_score)
lower_inspiration_speed_button.pressed.connect(lower_inspiration_speed)
increase_inspiration_speed_button.pressed.connect(increase_inspiration_speed)
lower_hide_for_seconds_button.pressed.connect(lower_hide_for_seconds)
increase_hide_for_seconds_button.pressed.connect(increase_hide_for_seconds)
visibility_changed.connect(_on_visibility_changed)
show_settings()
## Shows the current settings. Some change outside this card, e.g. winning turns
## statistics off, so this runs every time the card opens.
func show_settings() -> void:
auto_repeat_button.set_pressed_no_signal(Settings.auto_repeat_song)
stop_after_current_button.set_pressed_no_signal(Settings.stop_after_current)
hide_next_track_button.set_pressed_no_signal(Settings.hide_next_track)
hide_beginning_button.set_pressed_no_signal(Settings.hide_beginning)
hide_length_button.set_pressed_no_signal(Settings.hide_length)
hide_ticks_button.set_pressed_no_signal(Settings.hide_ticks)
low_played_button.set_pressed_no_signal(Settings.use_low_played_mode)
add_to_database_button.set_pressed_no_signal(Settings.add_to_stats)
hide_for_seconds_row.visible = Settings.hide_beginning
hide_for_seconds_label.text = "%d" % Settings.hide_for_seconds
score_label.text = str(Settings.winning_score)
inspiration_speed_label.text = str(Settings.inspiration_list_speed)
_fit_to_rows()
func lower_winning_score() -> void:
set_winning_score(Settings.winning_score - 1)
func increase_winning_score() -> void:
set_winning_score(Settings.winning_score + 1)
## Sets the winning score, never below 1.
func set_winning_score(new_score: int) -> void:
Settings.winning_score = maxi(new_score, 1)
score_label.text = str(Settings.winning_score)
func lower_inspiration_speed() -> void:
Settings.inspiration_list_speed = maxi(Settings.inspiration_list_speed - 1, 1)
inspiration_speed_label.text = str(Settings.inspiration_list_speed)
func increase_inspiration_speed() -> void:
Settings.inspiration_list_speed += 1
inspiration_speed_label.text = str(Settings.inspiration_list_speed)
func lower_hide_for_seconds() -> void:
Settings.hide_for_seconds = maxf(Settings.hide_for_seconds - 1.0, 0.0)
hide_for_seconds_label.text = "%d" % Settings.hide_for_seconds
func increase_hide_for_seconds() -> void:
Settings.hide_for_seconds = minf(Settings.hide_for_seconds + 1.0, MAX_HIDE_SECONDS)
hide_for_seconds_label.text = "%d" % Settings.hide_for_seconds
func _on_hide_beginning_toggled(on: bool) -> void:
Settings.hide_beginning = on
hide_for_seconds_row.visible = on
_fit_to_rows()
# Grows or shrinks the card upward, keeping its bottom edge in place
func _fit_to_rows() -> void:
var height: float = rows.get_combined_minimum_size().y + PADDING.y * 2
card_panel.position.y = -height
card_panel.size.y = height
rows.position = PADDING
rows.size = Vector2(card_panel.size.x - PADDING.x * 2, height - PADDING.y * 2)
func _on_visibility_changed() -> void:
if visible:
show_settings()
+1
View File
@@ -0,0 +1 @@
uid://dsap67d5cdg34
+4 -4
View File
@@ -1,11 +1,11 @@
class_name QuizToolbar
extends Control
## The row of quiz buttons under the top bar: reset playlist, reset points, settings
## and sound test.
## The row of quiz buttons under the top bar: search, reset playlist, reset points and
## sound test.
signal search_pressed
signal reset_playlist_pressed
signal reset_points_pressed
signal settings_pressed
signal sound_test_pressed
@onready var reset_playlist_button: Button = $ResetPlaylistButton
@@ -13,9 +13,9 @@ signal sound_test_pressed
func _ready() -> void:
$SearchButton.pressed.connect(search_pressed.emit)
reset_playlist_button.pressed.connect(reset_playlist_pressed.emit)
$ResetPointsButton.pressed.connect(reset_points_pressed.emit)
$SettingsButton.pressed.connect(settings_pressed.emit)
sound_test_button.pressed.connect(sound_test_pressed.emit)