The players' pictures sit on the right in even lines of at most four, each ending at the row's right edge, so five pictures are 3 + 2 and seven 4 + 3. The text takes the room they leave and wraps only when it does not fit, and the row is as tall as the taller of the two, instead of a width fixed by the number of pictures. Rows are rounded and alternate like the other lists, are highlighted under the mouse, and the playing song has an orange edge. Clicking a picture clicks its song.
366 lines
13 KiB
GDScript
366 lines
13 KiB
GDScript
extends GdUnitTestSuite
|
|
## How a song in the music list is laid out: the players' pictures on the right in even
|
|
## lines of at most four, each ending at the row's right edge, the text in the room they leave, wrapping only when it does
|
|
## not fit, and the row as tall as the taller of the two. Checked for every number of
|
|
## players, with short, debug mode and very long song texts.
|
|
|
|
const SHORT: Array[String] = ["Chrono Trigger", "Corridors of Time"]
|
|
const LONG: Array[String] = [
|
|
"Final Fantasy Dissidia 012 Duodecim (Sista Fantasin Meningsskiljaktigheter 012 Tolv)",
|
|
"'Crystal Cave -arrange-' from FINAL FANTASY III",
|
|
]
|
|
## The row's padding, from the SongRow styles in the theme.
|
|
const PADDING: float = 8.0
|
|
|
|
var _snapshot: AutoloadSnapshot
|
|
var _runner: GdUnitSceneRunner
|
|
var _panel: MusicListPanel
|
|
# The panel's width in the quiz, which songs must never make wider
|
|
var _panel_width: float
|
|
|
|
|
|
func before_test() -> void:
|
|
_snapshot = AutoloadSnapshot.take([Settings, Playlist])
|
|
Settings.is_debug = false
|
|
Playlist.reset_playlist()
|
|
Settings.player_array.clear()
|
|
for i: int in Settings.MAX_NUMBER_OF_PLAYERS:
|
|
Settings.player_array.append(PlayerObject.new("Player %d" % (i + 1)))
|
|
_runner = scene_runner("res://quiz/MusicListPanel.tscn")
|
|
_panel = _runner.scene()
|
|
_panel_width = _panel.size.x
|
|
|
|
|
|
func after_test() -> void:
|
|
await await_idle_frame()
|
|
_snapshot.restore()
|
|
|
|
|
|
# Adds a played and answered song with points for the first [param players] players
|
|
func _add_song(title: Array[String], players: int) -> SongObject:
|
|
var song: SongObject = SongObject.new()
|
|
song.song_number = Playlist.song_object_array.size()
|
|
song.soundtrack_title = title[0]
|
|
song.song_title = title[1]
|
|
song.has_played = true
|
|
song.is_answered = true
|
|
for i: int in players:
|
|
song.add_point(Settings.player_array[i].id)
|
|
Playlist.add_to_playlist(song)
|
|
return song
|
|
|
|
|
|
func _show() -> void:
|
|
_panel.refresh()
|
|
await _runner.simulate_frames(3)
|
|
|
|
|
|
func _rows() -> Array[SongRow]:
|
|
var rows: Array[SongRow] = []
|
|
rows.assign(_panel.get_node("%MusicList").get_children())
|
|
return rows
|
|
|
|
|
|
func _pictures(row: SongRow) -> Array[TextureRect]:
|
|
return row.pictures()
|
|
|
|
|
|
# How wide the row's text would be on one line
|
|
func _text_width(row: SongRow) -> float:
|
|
var label: Label = row.song_list_label
|
|
return label.get_theme_font(&"font").get_string_size(label.text, HORIZONTAL_ALIGNMENT_LEFT,
|
|
-1, label.get_theme_font_size(&"font_size")).x
|
|
|
|
|
|
func _one_line_height() -> float:
|
|
_add_song(SHORT, 0)
|
|
await _show()
|
|
var height: float = _rows()[0].size.y
|
|
Playlist.reset_playlist()
|
|
return height
|
|
|
|
|
|
# Every number of players with every kind of text, in normal and debug mode
|
|
func _each_layout(check: Callable) -> void:
|
|
for debug: bool in [false, true]:
|
|
Settings.is_debug = debug
|
|
for title: Array[String] in [SHORT, LONG]:
|
|
Playlist.reset_playlist()
|
|
for players: int in Settings.MAX_NUMBER_OF_PLAYERS + 1:
|
|
_add_song(title, players)
|
|
await _show()
|
|
assert_float(_panel.size.x).override_failure_message(
|
|
"The songs made the list wider than its place in the quiz") \
|
|
.is_equal(_panel_width)
|
|
for row: SongRow in _rows():
|
|
check.call(row, "%d players, %s text%s" % [_pictures(row).size(),
|
|
"long" if title == LONG else "short", " (debug)" if debug else ""])
|
|
|
|
|
|
# Columns
|
|
|
|
func test_pictures_are_spread_evenly_over_as_few_lines_as_possible() -> void:
|
|
var columns: Array[int] = []
|
|
for pictures: int in range(0, 9):
|
|
columns.append(SongRow.picture_columns(pictures))
|
|
|
|
# 5 pictures are 3 + 2, not 4 + 1; 7 are 4 + 3
|
|
assert_array(columns).contains_exactly([1, 1, 2, 3, 4, 3, 3, 4, 4])
|
|
|
|
|
|
# Placement
|
|
|
|
func test_pictures_are_at_the_right_end_of_the_row() -> void:
|
|
await _each_layout(func(row: SongRow, layout: String) -> void:
|
|
if _pictures(row).is_empty():
|
|
assert_bool(row.song_list_players.visible).override_failure_message(layout).is_false()
|
|
return
|
|
var pictures: Rect2 = row.song_list_players.get_global_rect()
|
|
assert_float(pictures.end.x).override_failure_message(layout) \
|
|
.is_equal_approx(row.get_global_rect().end.x - PADDING, 1.0))
|
|
|
|
|
|
func test_every_line_of_pictures_ends_at_the_right_edge() -> void:
|
|
await _each_layout(func(row: SongRow, layout: String) -> void:
|
|
var right_edge: float = row.get_global_rect().end.x - PADDING
|
|
for line: HBoxContainer in row.song_list_players.get_children():
|
|
var last: TextureRect = line.get_child(line.get_child_count() - 1)
|
|
assert_float(last.get_global_rect().end.x).override_failure_message(layout) \
|
|
.is_equal_approx(right_edge, 1.0))
|
|
|
|
|
|
func test_text_and_pictures_never_overlap_and_stay_inside_the_row() -> void:
|
|
await _each_layout(func(row: SongRow, layout: String) -> void:
|
|
var row_rect: Rect2 = row.get_global_rect().grow(0.5)
|
|
var text: Rect2 = row.song_list_label.get_global_rect()
|
|
assert_bool(row_rect.encloses(text)).override_failure_message(
|
|
"Text outside the row: " + layout).is_true()
|
|
for picture: TextureRect in _pictures(row):
|
|
var rect: Rect2 = picture.get_global_rect()
|
|
assert_bool(row_rect.encloses(rect)).override_failure_message(
|
|
"Picture outside the row: " + layout).is_true()
|
|
assert_float(rect.position.x).override_failure_message(
|
|
"Picture over the text: " + layout).is_greater_equal(text.end.x))
|
|
|
|
|
|
func test_text_gets_all_the_room_the_pictures_leave() -> void:
|
|
await _each_layout(func(row: SongRow, layout: String) -> void:
|
|
var text: Rect2 = row.song_list_label.get_global_rect()
|
|
assert_float(text.position.x).override_failure_message(layout) \
|
|
.is_equal_approx(row.get_global_rect().position.x + PADDING, 1.0)
|
|
var right_end: float = row.get_global_rect().end.x - PADDING
|
|
if row.song_list_players.visible:
|
|
right_end = row.song_list_players.get_global_rect().position.x \
|
|
- row.get_node("HBoxContainer").get_theme_constant(&"separation")
|
|
assert_float(text.end.x).override_failure_message(layout) \
|
|
.is_equal_approx(right_end, 1.0))
|
|
|
|
|
|
func test_text_wraps_only_when_it_does_not_fit_on_one_line() -> void:
|
|
await _each_layout(func(row: SongRow, layout: String) -> void:
|
|
var lines: int = row.song_list_label.get_line_count()
|
|
var room: float = row.song_list_label.size.x
|
|
if _text_width(row) <= room - 1:
|
|
assert_int(lines).override_failure_message("Wrapped though it fits: " + layout) \
|
|
.is_equal(1)
|
|
elif _text_width(row) > room + 1:
|
|
assert_int(lines).override_failure_message("Not wrapped: " + layout) \
|
|
.is_greater(1))
|
|
|
|
|
|
func test_every_layout_holds_in_a_narrower_list() -> void:
|
|
_panel.size.x = 640
|
|
_panel_width = 640
|
|
await _runner.simulate_frames(1)
|
|
|
|
await _each_layout(func(row: SongRow, layout: String) -> void:
|
|
var text: Rect2 = row.song_list_label.get_global_rect()
|
|
assert_bool(row.get_global_rect().grow(0.5).encloses(text)).override_failure_message(
|
|
"Text outside the row: " + layout).is_true()
|
|
if row.song_list_players.visible:
|
|
assert_float(row.song_list_players.get_global_rect().position.x) \
|
|
.override_failure_message("Pictures over the text: " + layout) \
|
|
.is_greater_equal(text.end.x))
|
|
|
|
|
|
func test_short_text_is_one_line_even_with_every_player() -> void:
|
|
_add_song(SHORT, Settings.MAX_NUMBER_OF_PLAYERS)
|
|
await _show()
|
|
|
|
assert_int(_rows()[0].song_list_label.get_line_count()).is_equal(1)
|
|
|
|
|
|
func test_long_text_wraps_and_keeps_every_word() -> void:
|
|
_add_song(LONG, 2)
|
|
await _show()
|
|
var label: Label = _rows()[0].song_list_label
|
|
|
|
assert_int(label.get_line_count()).is_greater(1)
|
|
assert_str(label.text).ends_with("FINAL FANTASY III")
|
|
assert_int(label.get_visible_line_count()).is_equal(label.get_line_count())
|
|
|
|
|
|
# Height
|
|
|
|
func test_row_is_one_line_high_with_up_to_four_pictures() -> void:
|
|
var one_line: float = await _one_line_height()
|
|
for players: int in range(0, SongRow.MAX_PICTURES_PER_LINE + 1):
|
|
_add_song(SHORT, players)
|
|
await _show()
|
|
|
|
for row: SongRow in _rows():
|
|
assert_float(row.size.y).override_failure_message(
|
|
"%d pictures" % _pictures(row).size()).is_equal_approx(one_line, 0.5)
|
|
|
|
|
|
func test_five_pictures_go_on_two_lines_of_three_and_two() -> void:
|
|
_add_song(SHORT, 5)
|
|
await _show()
|
|
|
|
assert_array(_lines_of_pictures(_rows()[0])).contains_exactly([3, 2])
|
|
|
|
|
|
func test_seven_pictures_go_on_two_lines_of_four_and_three() -> void:
|
|
_add_song(SHORT, 7)
|
|
await _show()
|
|
|
|
assert_array(_lines_of_pictures(_rows()[0])).contains_exactly([4, 3])
|
|
|
|
|
|
# How many pictures are on each line, top to bottom
|
|
func _lines_of_pictures(row: SongRow) -> Array[int]:
|
|
var by_top: Dictionary[float, int] = {}
|
|
for picture: TextureRect in _pictures(row):
|
|
var top: float = roundf(picture.global_position.y)
|
|
by_top[top] = by_top.get(top, 0) + 1
|
|
var tops: Array[float] = []
|
|
tops.assign(by_top.keys())
|
|
tops.sort()
|
|
var lines: Array[int] = []
|
|
for top: float in tops:
|
|
lines.append(by_top[top])
|
|
return lines
|
|
|
|
|
|
func test_row_grows_to_fit_two_lines_of_pictures_and_centres_the_text() -> void:
|
|
var one_line: float = await _one_line_height()
|
|
_add_song(SHORT, 5)
|
|
await _show()
|
|
var row: SongRow = _rows()[0]
|
|
var pictures: Rect2 = row.song_list_players.get_global_rect()
|
|
|
|
assert_float(row.size.y).is_greater(one_line)
|
|
assert_float(pictures.size.y).is_equal_approx(
|
|
SongRow.PICTURE_SIZE.y * 2 + row.song_list_players.get_theme_constant(
|
|
&"separation"), 0.5)
|
|
assert_bool(row.get_global_rect().encloses(pictures)).is_true()
|
|
# The single line of text is in the middle, beside the pictures
|
|
assert_int(row.song_list_label.vertical_alignment).is_equal(VERTICAL_ALIGNMENT_CENTER)
|
|
assert_float(row.song_list_label.get_global_rect().get_center().y) \
|
|
.is_equal_approx(pictures.get_center().y, 1.0)
|
|
|
|
|
|
func test_pictures_are_centred_beside_text_on_many_lines() -> void:
|
|
_add_song(LONG, 1)
|
|
await _show()
|
|
var row: SongRow = _rows()[0]
|
|
var text: Rect2 = row.song_list_label.get_global_rect()
|
|
var pictures: Rect2 = row.song_list_players.get_global_rect()
|
|
|
|
assert_int(row.song_list_label.get_line_count()).is_greater(1)
|
|
assert_float(pictures.size.y).is_less(text.size.y)
|
|
assert_float(pictures.get_center().y).is_equal_approx(text.get_center().y, 1.0)
|
|
|
|
|
|
func test_every_picture_has_the_same_size() -> void:
|
|
await _each_layout(func(row: SongRow, layout: String) -> void:
|
|
for picture: TextureRect in _pictures(row):
|
|
assert_vector(picture.size).override_failure_message(layout) \
|
|
.is_equal(SongRow.PICTURE_SIZE))
|
|
|
|
|
|
func test_rows_never_need_sideways_scrolling() -> void:
|
|
Settings.is_debug = true
|
|
for players: int in Settings.MAX_NUMBER_OF_PLAYERS + 1:
|
|
_add_song(LONG, players)
|
|
await _show()
|
|
var scroll: ScrollContainer = _panel.get_node("ScrollContainer")
|
|
|
|
assert_bool(scroll.get_h_scroll_bar().visible).is_false()
|
|
assert_float(_panel.size.x).is_equal(_panel_width)
|
|
for row: SongRow in _rows():
|
|
assert_float(row.get_global_rect().end.x) \
|
|
.is_less_equal(scroll.get_global_rect().end.x + 0.5)
|
|
|
|
|
|
# Pictures
|
|
|
|
func test_pictures_show_who_got_a_point_in_player_list_order() -> void:
|
|
var song: SongObject = _add_song(SHORT, 0)
|
|
# Points given in another order than the player list
|
|
song.add_point(Settings.player_array[2].id)
|
|
song.add_point(Settings.player_array[0].id)
|
|
await _show()
|
|
|
|
var names: Array[String] = []
|
|
for picture: TextureRect in _pictures(_rows()[0]):
|
|
names.append(picture.tooltip_text)
|
|
assert_array(names).contains_exactly(["Player 1", "Player 3"])
|
|
assert_object(_pictures(_rows()[0])[0].texture).is_same(Settings.player_array[0].character)
|
|
|
|
|
|
func test_pictures_are_redone_when_points_change() -> void:
|
|
var song: SongObject = _add_song(SHORT, 4)
|
|
await _show()
|
|
song.add_point(Settings.player_array[4].id)
|
|
|
|
_rows()[0].update_players()
|
|
await _runner.simulate_frames(2)
|
|
|
|
assert_array(_lines_of_pictures(_rows()[0])).contains_exactly([3, 2])
|
|
|
|
|
|
func test_clicking_a_picture_clicks_the_song() -> void:
|
|
_add_song(SHORT, 0)
|
|
_add_song(SHORT, 3)
|
|
await _show()
|
|
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_panel))
|
|
var picture: TextureRect = _pictures(_rows()[1])[1]
|
|
|
|
_runner.set_mouse_position(
|
|
_panel.get_viewport().get_final_transform() * picture.get_global_rect().get_center())
|
|
await _runner.simulate_mouse_button_pressed(MOUSE_BUTTON_LEFT)
|
|
await _runner.await_input_processed()
|
|
|
|
await signals.is_emitted("song_clicked", [1])
|
|
|
|
|
|
# Styles
|
|
|
|
func test_rows_alternate_and_the_playing_song_stands_out() -> void:
|
|
for i: int in 4:
|
|
_add_song(SHORT, 0)
|
|
Playlist.song_object_array[2].is_playing = true
|
|
await _show()
|
|
|
|
var styles: Array[StringName] = []
|
|
for row: SongRow in _rows():
|
|
styles.append(row.theme_type_variation)
|
|
assert_array(styles).contains_exactly(
|
|
[&"SongRow", &"SongRowOdd", &"SongRowPlaying", &"SongRowOdd"])
|
|
assert_str(_rows()[2].song_list_label.theme_type_variation).is_equal("SongRowLabelPlaying")
|
|
assert_str(_rows()[1].song_list_label.theme_type_variation).is_equal("SongRowLabel")
|
|
|
|
|
|
func test_hovering_a_row_highlights_it_until_the_mouse_leaves() -> void:
|
|
_add_song(SHORT, 0)
|
|
_add_song(SHORT, 0)
|
|
await _show()
|
|
var row: SongRow = _rows()[1]
|
|
|
|
row.mouse_entered.emit()
|
|
assert_str(row.theme_type_variation).is_equal("SongRowHover")
|
|
|
|
row.mouse_exited.emit()
|
|
assert_str(row.theme_type_variation).is_equal("SongRowOdd")
|