Files
MusicPlayer/quiz/song_row.gd
T
Sansan 78e1facaf0 Lay out the song list's pictures and text to fit any number of players
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.
2026-10-09 17:25:38 +02:00

106 lines
3.6 KiB
GDScript

class_name SongRow
extends PanelContainer
## One song in the music list: its number, soundtrack and title, and the character of
## each player who got a point for it.
##
## The pictures are on the right and the text takes the room they leave, wrapping only
## when it does not fit on one line. With many pictures they go on more lines, as even as
## possible and at most [constant MAX_PICTURES_PER_LINE] on each, so the text keeps most
## of the row. Every line ends at the row's right edge. The row is as tall as the taller
## of the text and the pictures.
## The most pictures on one line before they go on another.
const MAX_PICTURES_PER_LINE: int = 4
## The size of each picture. Characters are twice as wide as they are tall.
const PICTURE_SIZE: Vector2 = Vector2(56, 28)
var song_object: SongObject
## The row's place in the list, for the alternating background.
var index: int = 0
var _hovered: bool = false
@onready var song_list_label: Label = %SongListLabel
## The lines of pictures, top to bottom, each an HBoxContainer.
@onready var song_list_players: VBoxContainer = %SongListPlayers
func _ready() -> void:
mouse_entered.connect(_set_hovered.bind(true))
mouse_exited.connect(_set_hovered.bind(false))
set_label()
update_players()
## The pictures shown, line by line.
func pictures() -> Array[TextureRect]:
var shown: Array[TextureRect] = []
for line: Node in song_list_players.get_children():
shown.append_array(line.get_children())
return shown
## The number of pictures on a full line for [param pictures] pictures: as few lines as
## [constant MAX_PICTURES_PER_LINE] allows, with the pictures spread evenly over them,
## e.g. 3 and 2 for five pictures instead of 4 and 1.
static func picture_columns(pictures: int) -> int:
if pictures <= 0:
return 1
var lines: int = ceili(float(pictures) / MAX_PICTURES_PER_LINE)
return ceili(float(pictures) / lines)
func set_label() -> void:
if song_object != null:
song_list_label.theme_type_variation = &"SongRowLabelPlaying" \
if song_object.is_playing else &"SongRowLabel"
song_list_label.text = song_object.get_song_info()
_update_style()
func update_players() -> void:
Settings.delete_children(song_list_players)
var scorers: Array[PlayerObject] = []
if song_object != null:
# Show players in the same order as the player list
for player: PlayerObject in Settings.player_array:
if song_object.players_given_point.has(player.id):
scorers.append(player)
var per_line: int = picture_columns(scorers.size())
var line: HBoxContainer = null
for i: int in scorers.size():
if i % per_line == 0:
line = HBoxContainer.new()
line.theme_type_variation = &"SongRowPictureLine"
line.alignment = BoxContainer.ALIGNMENT_END
line.mouse_filter = Control.MOUSE_FILTER_IGNORE
song_list_players.add_child(line)
line.add_child(_make_picture(scorers[i]))
song_list_players.visible = !scorers.is_empty()
func _make_picture(player: PlayerObject) -> TextureRect:
var picture: TextureRect = TextureRect.new()
picture.texture = player.character
picture.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
picture.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
picture.custom_minimum_size = PICTURE_SIZE
picture.tooltip_text = player.player_name
# Clicks on a picture still click the row
picture.mouse_filter = Control.MOUSE_FILTER_PASS
return picture
func _set_hovered(hovered: bool) -> void:
_hovered = hovered
_update_style()
func _update_style() -> void:
if _hovered:
theme_type_variation = &"SongRowHover"
elif song_object != null && song_object.is_playing:
theme_type_variation = &"SongRowPlaying"
else:
theme_type_variation = &"SongRowOdd" if index % 2 != 0 else &"SongRow"