Split the main window into TopBar and Quiz scenes

The quiz (song, playlist, players, music player and the popups opened from
it) moves into Quiz.tscn and the top panel into TopBar.tscn. The main window
keeps only the top bar, the quiz and the popups opened from the top bar, so
other views can be added next to the quiz later. Every node keeps its place
on screen.

Screenshots of every UI state before and after are pixel-identical, and a
layout test checks each node against a baseline taken before the split.
This commit is contained in:
Sansan committed 2026-10-06 10:02:22 +02:00
1 parent 75c59eb223
commit 3c7b4e5c9d
27 files changed
+3432 -980

No files matched your search

+32 -27
View File
@@ -1,28 +1,27 @@
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.
## The main window and the quiz each have their own popups and their own backdrop.
const POPUP_NODES: Array[String] = [
"Search", "SyncWindow", "SettingsWindow", "QRWindow", "NewCharacterSelect",
"ShortcutsWindow", "AboutWindow", "WinnerWindow", "Log",
## Popups opened from the top bar, in the main window.
const MAIN_POPUPS: Array[String] = [
"Search", "SyncWindow", "QRWindow", "ShortcutsWindow", "AboutWindow",
]
## Popups opened from the quiz, in the Quiz scene.
const QUIZ_POPUPS: Array[String] = [
"SettingsWindow", "NewCharacterSelect", "WinnerWindow", "Log",
]
func _popup(node_name: String) -> PopupWindow:
return _window.get_node(node_name)
return _quiz.get_node(node_name) if node_name in QUIZ_POPUPS \
else _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()
## The backdrop that shows behind [param popup].
func _backdrop(popup: PopupWindow = _popup("SettingsWindow")) -> ColorRect:
return popup.get_parent().get_node("PopupBackdrop")
## A point on screen outside the popup's panel.
@@ -32,34 +31,40 @@ func _outside(popup: PopupWindow) -> Vector2:
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_backdrops_are_hidden_without_popups() -> void:
assert_bool(_backdrop(_popup("SettingsWindow")).visible).is_false()
assert_bool(_backdrop(_popup("AboutWindow")).visible).is_false()
func test_backdrop_covers_the_whole_window() -> void:
_popup("SettingsWindow").open()
func test_backdrops_cover_the_whole_window() -> void:
for node_name: String in ["SettingsWindow", "AboutWindow"]:
var popup: PopupWindow = _popup(node_name)
popup.open()
assert_vector(_backdrop().get_global_rect().size).is_equal(_window.get_viewport_rect().size)
assert_object(_backdrop(popup).get_global_rect()).override_failure_message(
"%s: backdrop does not cover the window" % node_name) \
.is_equal(_window.get_viewport_rect())
popup.close()
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)
for node_name: String in MAIN_POPUPS + QUIZ_POPUPS:
assert_object(_popup(node_name)).is_instanceof(PopupWindow)
func test_every_popup_shows_backdrop_and_closes_on_outside_click() -> void:
for node_name: String in POPUP_NODES:
for node_name: String in MAIN_POPUPS + QUIZ_POPUPS:
var popup: PopupWindow = _popup(node_name)
popup.open()
await _runner.simulate_frames(1)
assert_bool(_backdrop().visible).override_failure_message(
assert_bool(_backdrop(popup).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(
assert_bool(_backdrop(popup).visible).override_failure_message(
"%s: backdrop still shown" % node_name).is_false()
@@ -75,7 +80,7 @@ func test_click_inside_popup_keeps_it_open() -> void:
func test_closing_click_does_not_press_button_behind_popup() -> void:
var settings: PopupWindow = _popup("SettingsWindow")
var reset_button: Button = _window.get_node("ResetPlaylistButton")
var reset_button: Button = _quiz.get_node("ResetPlaylistButton")
settings.open()
await _runner.simulate_frames(1)
assert_bool(settings.panel.get_global_rect().intersects(reset_button.get_global_rect())) \
@@ -88,7 +93,7 @@ func test_closing_click_does_not_press_button_behind_popup() -> void:
func test_button_works_again_after_popup_closed() -> void:
var reset_button: Button = _window.get_node("ResetPlaylistButton")
var reset_button: Button = _quiz.get_node("ResetPlaylistButton")
await _click_at(reset_button.get_global_rect().get_center())