Make the buttons consistent and rework the player list
Buttons get 12 px of side padding. The top bar buttons, the quiz toolbar and Show answer/Next are laid out in rows with 12 px gaps and are all 36 px tall, and the ±1 buttons use a compact StepButton style. "Randomize new track" is now "Next song". Player rows show the name and the score apart, alternate like the other lists and highlight the character under the mouse. The name field is a LineEdit with a 10-letter limit; blank and duplicate names are refused, and a full list says "Up to 7 players". Remove buttons update when editing is switched instead of every frame, and the spacer panels are gone. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e4352a9f34
commit
70e970b24c
39 files changed
+2054
-2177
No files matched your search
+1615
-1903
File diff suppressed because it is too large.
Load diff
@@ -48,6 +48,8 @@ func before_test() -> void:
|
||||
_runner = scene_runner("res://main/MainWindow.tscn")
|
||||
_window = _runner.scene()
|
||||
_quiz = _window.get_node("Quiz")
|
||||
# Rows of buttons are laid out by their containers at the end of the first frame
|
||||
await _runner.simulate_frames(1)
|
||||
|
||||
|
||||
## Override to change the fake server before the window starts and sends its first
|
||||
@@ -82,7 +84,7 @@ func _play_first_song() -> void:
|
||||
|
||||
## Adds a player through the name field, the same way the user does.
|
||||
func _add_player(player_name: String) -> PlayerObject:
|
||||
var name_field: TextEdit = _quiz.get_node(
|
||||
var name_field: LineEdit = _quiz.get_node(
|
||||
"PlayersPanel/VBoxContainer/AddPlayerContainer/PlayerNameField")
|
||||
name_field.text = player_name
|
||||
_quiz.players_panel.add_player()
|
||||
@@ -101,7 +103,7 @@ func _player_row(player_object: PlayerObject) -> PlayerRow:
|
||||
|
||||
## Removes a player with the remove button in its row.
|
||||
func _remove_player(player_object: PlayerObject) -> void:
|
||||
(_player_row(player_object).get_node("HBoxContainer/RemovePlayer") as BaseButton) \
|
||||
(_player_row(player_object).get_node("%RemovePlayer") as BaseButton) \
|
||||
.pressed.emit()
|
||||
await _runner.simulate_frames(1)
|
||||
|
||||
|
||||
@@ -435,7 +435,7 @@ func _settle() -> void:
|
||||
|
||||
func _add_player(player_name: String) -> void:
|
||||
(_node("PlayersPanel").get_node("VBoxContainer/AddPlayerContainer/PlayerNameField") \
|
||||
as TextEdit).text = player_name
|
||||
as LineEdit).text = player_name
|
||||
_call("add_player")
|
||||
|
||||
|
||||
|
||||
@@ -14,12 +14,12 @@ func _before_window_starts() -> void:
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_popup = _window.get_node("AboutPopup")
|
||||
|
||||
|
||||
func _open_with_top_bar_button() -> void:
|
||||
await _click_at((_window.get_node("TopBar/AboutButton") as Control).get_global_rect()
|
||||
await _click_at((_window.get_node("TopBar/%AboutButton") as Control).get_global_rect()
|
||||
.get_center())
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ func _before_window_starts() -> void:
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_settings = _window.settings_popup
|
||||
await _runner.simulate_frames(2)
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ func _before_window_starts() -> void:
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_picked.clear()
|
||||
_popup = _quiz.get_node("CharacterSelectPopup")
|
||||
_popup.character_selected.connect(
|
||||
|
||||
@@ -167,7 +167,8 @@ func test_players_rows_show_the_saved_scores() -> void:
|
||||
await _restart_game([SONG_INFO[0], SECOND_SONG])
|
||||
|
||||
var row: PlayerRow = _player_row(Settings.player_array[0])
|
||||
assert_str(row.player_name_field.text).ends_with("Crono: 1")
|
||||
assert_str(row.player_name_field.text).is_equal("Crono")
|
||||
assert_str(row.score_label.text).is_equal("1")
|
||||
|
||||
|
||||
func test_song_downloaded_ahead_is_kept_and_not_downloaded_again() -> void:
|
||||
|
||||
@@ -6,7 +6,7 @@ var _qr: QRPopup
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_log = _window.get_node("LogPopup")
|
||||
_qr = _window.get_node("QRPopup")
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@ func test_cache_keeps_filling_after_a_failed_download() -> void:
|
||||
|
||||
|
||||
func _open_settings() -> void:
|
||||
(_window.get_node("TopBar/SettingsButton") as Button).pressed.emit()
|
||||
(_window.get_node("TopBar/%SettingsButton") as Button).pressed.emit()
|
||||
|
||||
|
||||
func _close_settings() -> void:
|
||||
|
||||
@@ -9,7 +9,7 @@ var _top_bar: TopBar
|
||||
|
||||
func before_test() -> void:
|
||||
_root = create_temp_dir("offline_%d" % randi())
|
||||
super()
|
||||
await super()
|
||||
_settings = _window.get_node("SettingsPopup")
|
||||
_top_bar = _window.get_node("TopBar")
|
||||
|
||||
@@ -314,7 +314,7 @@ func test_new_game_turns_show_winner_back_into_next() -> void:
|
||||
await _runner.simulate_frames(2)
|
||||
|
||||
var next_button: Button = _quiz.get_node("SongControls/NextButton")
|
||||
assert_str(next_button.text).is_equal("Randomize new track")
|
||||
assert_str(next_button.text).is_equal("Next song")
|
||||
await _play_first_song()
|
||||
assert_str(Playlist.song_object_array[Playlist.currently_playing_song].song_title) \
|
||||
.is_equal("01. Opening")
|
||||
|
||||
@@ -10,7 +10,7 @@ var _lucca: PlayerObject
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_crono = _add_player("Crono")
|
||||
_marle = _add_player("Marle")
|
||||
_lucca = _add_player("Lucca")
|
||||
|
||||
@@ -118,7 +118,7 @@ func test_remove_button_removes_the_player_and_row() -> void:
|
||||
var crono: PlayerObject = Settings.player_array[0]
|
||||
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_panel))
|
||||
|
||||
(_rows()[0].get_node("HBoxContainer/RemovePlayer") as BaseButton).pressed.emit()
|
||||
(_rows()[0].get_node("%RemovePlayer") as BaseButton).pressed.emit()
|
||||
|
||||
await signals.is_emitted("player_removed", [crono])
|
||||
assert_array(Settings.player_array.map(
|
||||
@@ -132,7 +132,7 @@ func test_character_button_asks_for_a_new_character() -> void:
|
||||
var row: PlayerRow = _rows()[0]
|
||||
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_panel))
|
||||
|
||||
(row.get_node("HBoxContainer/Character") as BaseButton).pressed.emit()
|
||||
(row.get_node("%Character") as BaseButton).pressed.emit()
|
||||
|
||||
await signals.is_emitted("change_character_requested", [row, "Crono"])
|
||||
|
||||
@@ -143,5 +143,95 @@ func test_reload_shows_new_scores() -> void:
|
||||
|
||||
_panel.reload()
|
||||
|
||||
assert_str(((_rows()[0] as PlayerRow).get_node("HBoxContainer/Name") as Label).text) \
|
||||
.ends_with("Crono: 3")
|
||||
assert_str((_rows()[0] as PlayerRow).score_label.text).is_equal("3")
|
||||
|
||||
|
||||
func test_row_shows_the_name_and_the_score_apart() -> void:
|
||||
_add("Crono")
|
||||
var row: PlayerRow = _rows()[0]
|
||||
|
||||
row.add_point()
|
||||
row.add_point()
|
||||
|
||||
assert_str(row.player_name_field.text).is_equal("Crono")
|
||||
assert_str(row.score_label.text).is_equal("2")
|
||||
|
||||
|
||||
func test_names_are_trimmed_and_blank_ones_refused() -> void:
|
||||
_add(" Crono ")
|
||||
_add(" ")
|
||||
|
||||
assert_array(Settings.player_array.map(
|
||||
func(p: PlayerObject) -> String: return p.player_name)).contains_exactly(["Crono"])
|
||||
|
||||
|
||||
func test_a_name_can_only_be_used_once() -> void:
|
||||
_add("Crono")
|
||||
_add("crono")
|
||||
|
||||
assert_int(Settings.player_array.size()).is_equal(1)
|
||||
assert_str(_panel.new_player_name_field.text).is_equal("crono")
|
||||
|
||||
|
||||
func test_name_field_takes_at_most_ten_letters() -> void:
|
||||
_panel.new_player_name_field.insert_text_at_caret("Abcdefghijklmnop")
|
||||
|
||||
assert_str(_panel.new_player_name_field.text).is_equal("Abcdefghij")
|
||||
|
||||
|
||||
func test_full_list_says_so_instead_of_offering_the_name_field() -> void:
|
||||
await _click(_panel.add_players_button)
|
||||
for i: int in Settings.MAX_NUMBER_OF_PLAYERS:
|
||||
_add("Player %d" % i)
|
||||
|
||||
assert_bool(_panel.add_player_hint.visible).is_true()
|
||||
assert_str(_panel.add_player_hint.text).is_equal("Up to 7 players")
|
||||
assert_bool(_panel.new_player_name_field.visible).is_false()
|
||||
|
||||
(_rows()[0].get_node("%RemovePlayer") as BaseButton).pressed.emit()
|
||||
|
||||
assert_bool(_panel.add_player_hint.visible).is_false()
|
||||
assert_bool(_panel.new_player_name_field.visible).is_true()
|
||||
|
||||
|
||||
func test_remove_buttons_show_only_while_editing() -> void:
|
||||
_add("Crono")
|
||||
var remove: Control = _rows()[0].get_node("%RemovePlayer")
|
||||
assert_bool(remove.visible).is_false()
|
||||
|
||||
await _click(_panel.add_players_button)
|
||||
assert_bool((_rows()[0].get_node("%RemovePlayer") as Control).visible).is_true()
|
||||
|
||||
await _click(_panel.add_players_button)
|
||||
assert_bool((_rows()[0].get_node("%RemovePlayer") as Control).visible).is_false()
|
||||
|
||||
|
||||
func test_rows_alternate_like_the_other_lists() -> void:
|
||||
_add("Crono")
|
||||
_add("Marle")
|
||||
_add("Lucca")
|
||||
|
||||
assert_array(_rows().map(func(row: PlayerRow) -> String: return row.theme_type_variation)) \
|
||||
.contains_exactly(["ListRowEven", "ListRowOdd", "ListRowEven"])
|
||||
|
||||
|
||||
func test_character_is_highlighted_under_the_mouse() -> void:
|
||||
_add("Crono")
|
||||
var row: PlayerRow = _rows()[0]
|
||||
|
||||
row.character.mouse_entered.emit()
|
||||
assert_str(row.character_frame.theme_type_variation).is_equal("PlayerCharacterHover")
|
||||
row.character.mouse_exited.emit()
|
||||
assert_str(row.character_frame.theme_type_variation).is_equal("PlayerCharacter")
|
||||
assert_str(row.character.tooltip_text).is_equal("Change character")
|
||||
|
||||
|
||||
func test_seven_players_fit_without_scrolling_while_adding() -> void:
|
||||
await _click(_panel.add_players_button)
|
||||
for i: int in Settings.MAX_NUMBER_OF_PLAYERS:
|
||||
_add("Player %d" % i)
|
||||
await _runner.simulate_frames(2)
|
||||
|
||||
var panel: Rect2 = _panel.get_global_rect()
|
||||
var last: Rect2 = (_rows().back() as Control).get_global_rect()
|
||||
assert_bool(panel.encloses(last)).is_true()
|
||||
@@ -7,7 +7,7 @@ var _button: Button
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_card = _quiz.get_node("QuizSettingsCard")
|
||||
_button = _quiz.get_node("QuizSettingsButton")
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ var _toolbar: QuizToolbar
|
||||
func before_test() -> void:
|
||||
_runner = scene_runner("res://quiz/QuizToolbar.tscn")
|
||||
_toolbar = _runner.scene()
|
||||
await _runner.simulate_frames(1)
|
||||
|
||||
|
||||
func _button(button_name: String) -> Button:
|
||||
|
||||
@@ -28,9 +28,12 @@ const LOCKED_BY_SYNC: Array[String] = [
|
||||
]
|
||||
|
||||
const TOP_BAR_CHILDREN: Array[String] = [
|
||||
"TopPanelContainer", "Title", "SyncButton", "SettingsButton", "ShortcutsButton",
|
||||
"DebugLabel", "UpdateLabel", "OfflineLabel",
|
||||
"AboutButton", "QRButton", "QuizTab", "StatisticsTab",
|
||||
"TopPanelContainer", "Title", "DebugLabel", "UpdateLabel", "OfflineLabel", "Buttons",
|
||||
"QuizTab", "StatisticsTab",
|
||||
]
|
||||
## The buttons in the top bar's row on the right, in order.
|
||||
const TOP_BAR_BUTTONS: Array[String] = [
|
||||
"SyncButton", "SettingsButton", "ShortcutsButton", "AboutButton", "QRButton",
|
||||
]
|
||||
|
||||
## Each top bar button and the popup it opens in the main window.
|
||||
@@ -44,7 +47,7 @@ const TOP_BAR_POPUPS: Dictionary = {
|
||||
|
||||
|
||||
func _top_bar_button(button_name: String) -> Button:
|
||||
return _window.get_node("TopBar/" + button_name)
|
||||
return _window.get_node("TopBar/%" + button_name)
|
||||
|
||||
|
||||
func _children(node: Node) -> Array[String]:
|
||||
@@ -90,6 +93,7 @@ func test_quiz_settings_button_sits_under_the_player_bar() -> void:
|
||||
func test_top_bar_holds_only_title_and_buttons() -> void:
|
||||
assert_array(_children(_window.get_node("TopBar"))) \
|
||||
.contains_exactly_in_any_order(TOP_BAR_CHILDREN)
|
||||
assert_array(_children(_window.get_node("TopBar/Buttons"))).contains_exactly(TOP_BAR_BUTTONS)
|
||||
|
||||
|
||||
func test_quiz_and_top_bar_let_clicks_through_where_they_are_empty() -> void:
|
||||
@@ -148,7 +152,7 @@ func test_points_from_the_player_list_reach_the_song_and_log() -> void:
|
||||
await _play_first_song()
|
||||
var crono: PlayerObject = _add_player("Crono")
|
||||
|
||||
(_player_row(crono).get_node("HBoxContainer/AddPoint") as BaseButton).pressed.emit()
|
||||
(_player_row(crono).get_node("%AddPoint") as BaseButton).pressed.emit()
|
||||
|
||||
assert_array(Playlist.song_object_array[0].players_given_point).contains_exactly([crono.id])
|
||||
var log_rows: Node = _window.get_node("%LogPopup").get_node("%LogVBoxContainer")
|
||||
@@ -160,7 +164,7 @@ func test_character_button_in_the_player_list_picks_a_new_character() -> void:
|
||||
var crono: PlayerObject = _add_player("Crono")
|
||||
var character_select: BasePopup = _quiz.get_node("CharacterSelectPopup")
|
||||
|
||||
(_player_row(crono).get_node("HBoxContainer/Character") as BaseButton).pressed.emit()
|
||||
(_player_row(crono).get_node("%Character") as BaseButton).pressed.emit()
|
||||
|
||||
assert_bool(character_select.visible).is_true()
|
||||
var texture: ImageTexture = ImageTexture.create_from_image(
|
||||
|
||||
@@ -15,7 +15,7 @@ func _before_window_starts() -> void:
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_popup = _window.get_node("SearchPopup")
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ var _popup: SettingsPopup
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_popup = _window.get_node("SettingsPopup")
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ var _popup: ShortcutsPopup
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_popup = _window.get_node("ShortcutsPopup")
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ var _controls: SongControls
|
||||
func before_test() -> void:
|
||||
_runner = scene_runner("res://quiz/SongControls.tscn")
|
||||
_controls = _runner.scene()
|
||||
await _runner.simulate_frames(1)
|
||||
|
||||
|
||||
func _click(button: Button) -> void:
|
||||
|
||||
@@ -33,12 +33,12 @@ func _before_window_starts() -> void:
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_view = _window.get_node("StatisticsView")
|
||||
|
||||
|
||||
func _open_statistics() -> void:
|
||||
await _click_at(_center(_window.get_node("TopBar/StatisticsTab")))
|
||||
await _click_at(_center(_window.get_node("TopBar/%StatisticsTab")))
|
||||
await _runner.simulate_frames(2)
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ func _visible_texts(list: VBoxContainer) -> Array[String]:
|
||||
|
||||
func test_quiz_tab_is_chosen_at_startup() -> void:
|
||||
assert_bool(_view.visible).is_false()
|
||||
assert_bool((_window.get_node("TopBar/QuizTab") as Button).button_pressed).is_true()
|
||||
assert_bool((_window.get_node("TopBar/%QuizTab") as Button).button_pressed).is_true()
|
||||
assert_int(_server.count("/statistics/soundtracks")).is_equal(0)
|
||||
|
||||
|
||||
@@ -82,8 +82,8 @@ func test_clicking_statistics_tab_shows_statistics_instead_of_the_quiz() -> void
|
||||
|
||||
assert_bool(_view.visible).is_true()
|
||||
assert_bool(_quiz.visible).is_false()
|
||||
assert_bool((_window.get_node("TopBar/StatisticsTab") as Button).button_pressed).is_true()
|
||||
assert_bool((_window.get_node("TopBar/QuizTab") as Button).button_pressed).is_false()
|
||||
assert_bool((_window.get_node("TopBar/%StatisticsTab") as Button).button_pressed).is_true()
|
||||
assert_bool((_window.get_node("TopBar/%QuizTab") as Button).button_pressed).is_false()
|
||||
assert_vector(_view.get_global_rect().position).is_equal(Vector2(0, 56))
|
||||
assert_vector(_view.get_global_rect().size).is_equal(Vector2(1920, 1024))
|
||||
|
||||
@@ -91,11 +91,11 @@ func test_clicking_statistics_tab_shows_statistics_instead_of_the_quiz() -> void
|
||||
func test_clicking_quiz_tab_goes_back_to_the_quiz() -> void:
|
||||
await _open_statistics()
|
||||
|
||||
await _click_at(_center(_window.get_node("TopBar/QuizTab")))
|
||||
await _click_at(_center(_window.get_node("TopBar/%QuizTab")))
|
||||
|
||||
assert_bool(_view.visible).is_false()
|
||||
assert_bool(_quiz.visible).is_true()
|
||||
assert_bool((_window.get_node("TopBar/QuizTab") as Button).button_pressed).is_true()
|
||||
assert_bool((_window.get_node("TopBar/%QuizTab") as Button).button_pressed).is_true()
|
||||
|
||||
|
||||
func test_clicks_on_statistics_do_not_reach_the_quiz_behind() -> void:
|
||||
@@ -119,7 +119,7 @@ func test_song_keeps_playing_behind_statistics() -> void:
|
||||
|
||||
func test_statistics_are_loaded_again_each_time_the_tab_opens() -> void:
|
||||
await _open_statistics()
|
||||
await _click_at(_center(_window.get_node("TopBar/QuizTab")))
|
||||
await _click_at(_center(_window.get_node("TopBar/%QuizTab")))
|
||||
await _open_statistics()
|
||||
|
||||
assert_int(_server.count("/statistics/soundtracks")).is_equal(2)
|
||||
|
||||
@@ -5,7 +5,7 @@ var _crono: PlayerObject
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_crono = _add_player("Crono")
|
||||
await _play_first_song()
|
||||
Settings.winning_score = 1
|
||||
|
||||
@@ -14,7 +14,11 @@ const GAME_FOLDERS: Array[String] = [
|
||||
const STYLES: Dictionary = {
|
||||
"WindowBackground": "Panel",
|
||||
"PopupBackground": "Panel",
|
||||
"DarkSpacer": "Panel",
|
||||
"StepButton": "Button",
|
||||
"PlayerList": "PanelContainer",
|
||||
"PlayerCharacter": "PanelContainer",
|
||||
"PlayerCharacterHover": "PanelContainer",
|
||||
"PlayerScore": "Label",
|
||||
"CharacterCard": "Panel",
|
||||
"CharacterCardHover": "Panel",
|
||||
"SettingsRowLabel": "Label",
|
||||
|
||||
@@ -5,7 +5,7 @@ var _top_bar: TopBar
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_top_bar = _window.get_node("TopBar")
|
||||
|
||||
|
||||
@@ -33,9 +33,9 @@ func test_message_fits_between_the_tabs_and_the_buttons() -> void:
|
||||
var text_width: float = _top_bar.update_label.get_minimum_size().x
|
||||
assert_float(text_width).is_less_equal(label.size.x)
|
||||
assert_float(label.position.x).is_greater(
|
||||
(_top_bar.get_node("StatisticsTab") as Control).get_global_rect().end.x)
|
||||
(_top_bar.get_node("%StatisticsTab") as Control).get_global_rect().end.x)
|
||||
assert_float(label.end.x).is_less(
|
||||
(_top_bar.get_node("SyncButton") as Control).get_global_rect().position.x)
|
||||
(_top_bar.get_node("%SyncButton") as Control).get_global_rect().position.x)
|
||||
|
||||
|
||||
func test_message_stays_on_the_statistics_tab() -> void:
|
||||
|
||||
@@ -8,7 +8,7 @@ var _popup: WinnerPopup
|
||||
|
||||
|
||||
func before_test() -> void:
|
||||
super()
|
||||
await super()
|
||||
_crono = _add_player("Crono")
|
||||
_marle = _add_player("Marle")
|
||||
_popup = _quiz.get_node("WinnerPopup")
|
||||
|
||||
Reference in new issue
Block a user