Restyle the music player bar
Build / test (push) Successful in 1m11s
Build / build (push) Successful in 1m20s

The bar is rounded and a little taller, with a round orange play button
and a restart button that lights up orange under the mouse. The icons sit
inside their buttons with room around them, and are one colour, cropped to
their shape. Progress fills in orange, the volume slider gets a speaker
icon, and the buttons say their shortcut keys in their tooltips.

Buttons grow a little under the mouse and shrink while held down, and the
play button pops when it switches between play and pause. ButtonBounce
holds these animations so other buttons can use them too.
This commit is contained in:
Sansan committed 2026-10-09 17:38:38 +02:00
1 parent 78e1facaf0
commit 4a934840f6
23 files changed
+1037 -247

No files matched your search

+69
View File
@@ -0,0 +1,69 @@
class_name ButtonBounce
extends RefCounted
## Small animations that make buttons feel alive: a button grows a little under the mouse,
## shrinks while it is held down, and can pop when its icon changes.
##
## The button is scaled around its middle, so its place in a container does not change.
## How big a button is under the mouse.
const HOVER_SCALE: Vector2 = Vector2(1.08, 1.08)
## How big a button is while it is held down.
const PRESSED_SCALE: Vector2 = Vector2(0.92, 0.92)
## How small a button starts when it pops.
const POP_SCALE: Vector2 = Vector2(0.8, 0.8)
## How long growing or shrinking takes, in seconds.
const DURATION: float = 0.08
## How long a pop takes, in seconds.
const POP_DURATION: float = 0.18
const _TWEEN_META: StringName = &"button_bounce_tween"
const _PIVOT_META: StringName = &"button_bounce_pivot"
## Makes [param button] grow under the mouse and shrink while held down.
static func add_to(button: BaseButton) -> void:
_keep_pivot_centred(button)
button.mouse_entered.connect(func() -> void:
if !button.disabled:
_scale_to(button, HOVER_SCALE))
button.mouse_exited.connect(func() -> void: _scale_to(button, Vector2.ONE))
button.button_down.connect(func() -> void: _scale_to(button, PRESSED_SCALE))
button.button_up.connect(func() -> void: _scale_to(button, resting_scale(button)))
## The size [param button] goes back to when it is not held down: bigger under the mouse.
static func resting_scale(button: BaseButton) -> Vector2:
return HOVER_SCALE if button.is_hovered() && !button.disabled else Vector2.ONE
## Shrinks [param button] and lets it spring back, e.g. when its icon changes.
static func pop(button: BaseButton) -> void:
_keep_pivot_centred(button)
button.scale = POP_SCALE
var tween: Tween = _new_tween(button)
tween.set_trans(Tween.TRANS_BACK).set_ease(Tween.EASE_OUT)
tween.tween_property(button, ^"scale", resting_scale(button), POP_DURATION)
static func _scale_to(button: BaseButton, target: Vector2) -> void:
var tween: Tween = _new_tween(button)
tween.set_trans(Tween.TRANS_QUAD).set_ease(Tween.EASE_OUT)
tween.tween_property(button, ^"scale", target, DURATION)
# A new tween for [param button], stopping the one before so they never fight
static func _new_tween(button: BaseButton) -> Tween:
if button.has_meta(_TWEEN_META):
var running: Tween = button.get_meta(_TWEEN_META)
if running.is_valid():
running.kill()
var tween: Tween = button.create_tween()
button.set_meta(_TWEEN_META, tween)
return tween
static func _keep_pivot_centred(button: BaseButton) -> void:
button.pivot_offset = button.size / 2
if !button.has_meta(_PIVOT_META):
button.set_meta(_PIVOT_META, true)
button.resized.connect(func() -> void: button.pivot_offset = button.size / 2)
+1
View File
@@ -0,0 +1 @@
uid://c4vekt3m8q1p7
+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="416" viewBox="48 48 416 416">
<!-- The pause shape of pause_icon_light.svg in one colour -->
<path fill="#f9f9f9" d="M224,435.8V76.1c0-6.7-5.4-12.1-12.2-12.1h-71.6c-6.8,0-12.2,5.4-12.2,12.1v359.7c0,6.7,5.4,12.2,12.2,12.2h71.6 C218.6,448,224,442.6,224,435.8z"/>
<path fill="#f9f9f9" d="M371.8,64h-71.6c-6.7,0-12.2,5.4-12.2,12.1v359.7c0,6.7,5.4,12.2,12.2,12.2h71.6c6.7,0,12.2-5.4,12.2-12.2V76.1 C384,69.4,378.6,64,371.8,64z"/>
</svg>

After

Width:  |  Height:  |  Size: 502 B

+43
View File
@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://djdsgbhw8gpj"
path="res://.godot/imported/pause_button_icon.svg-cf86876de39506a769c5610b329fdb75.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/pause_button_icon.svg"
dest_files=["res://.godot/imported/pause_button_icon.svg-cf86876de39506a769c5610b329fdb75.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="416" viewBox="64 48 416 416">
<!-- The play shape of play_icon_light.svg in one colour, shifted right so it looks centred -->
<path fill="#f9f9f9" transform="translate(16 0)" d="M405.2,232.9L126.8,67.2c-3.4-2-6.9-3.2-10.9-3.2c-10.9,0-19.8,9-19.8,20H96v344h0.1c0,11,8.9,20,19.8,20 c4.1,0,7.5-1.4,11.2-3.4l278.1-165.5c6.6-5.5,10.8-13.8,10.8-23.1C416,246.7,411.8,238.5,405.2,232.9z"/>
</svg>

After

Width:  |  Height:  |  Size: 454 B

+43
View File
@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dkweun1f426gl"
path="res://.godot/imported/play_button_icon.svg-76b19c7150b176f215ad401b4dd9488d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/play_button_icon.svg"
dest_files=["res://.godot/imported/play_button_icon.svg-76b19c7150b176f215ad401b4dd9488d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 28 28">
<!-- reload_light_icon.svg mirrored, so the arrow turns back like a restart -->
<path fill="#f9f9f9" transform="translate(28 0) scale(-1 1)" d="M 12.273759,27.821814 C 8.301606,27.305128 4.6414458,25.025364 2.4172439,21.682595 -0.6740156,17.036719 -0.6987372,11.050866 2.3541959,6.4167276 6.4831292,0.1492961 14.7875,-1.7884148 21.095349,2.0437368 l 0.883303,0.5366251 0.09577,-0.6963111 c 0.07593,-0.5520493 0.198918,-0.7994615 0.593645,-1.1941885 0.682393,-0.6823936 1.29683,-0.7876518 2.135162,-0.3657721 1.11162,0.5594083 1.142977,0.6968977 1.099029,4.8188242 C 25.866598,8.48735 25.848168,8.714727 25.583585,9.075118 24.993194,9.879284 24.711316,9.931761 20.980939,9.931993 17.03905,9.932238 16.871859,9.891713 16.324094,8.803226 15.94065,8.041272 16.00249,7.4396205 16.528498,6.8144942 17.041282,6.2050862 17.302265,6.1106614 18.732442,6.0170974 l 1.29765,-0.084894 -0.711421,-0.4557502 C 16.407302,3.611373 12.42501,3.4754273 9.250891,5.1327637 6.9185825,6.3505563 5.186242,8.47902 4.3514391,11.152542 c -0.4596099,1.471937 -0.4596099,4.222979 0,5.694916 1.0532717,3.373185 3.4279179,5.747831 6.8011029,6.801103 0.874696,0.273122 1.291973,0.316826 2.983051,0.31243 1.791555,-0.0047 2.062404,-0.03855 3.050848,-0.381822 2.095349,-0.727675 4.004905,-2.225187 5.276499,-4.137935 0.712133,-1.071201 1.028577,-1.77101 1.500599,-3.318525 0.234713,-0.769505 0.466198,-1.242244 0.733848,-1.49867 0.768275,-0.736054 1.953395,-0.694987 2.67526,0.0927 0.56162,0.612834 0.678351,1.318164 0.398766,2.409492 -0.7664,2.991551 -3.210883,6.444937 -5.916304,8.35812 -2.688606,1.901292 -6.251776,2.77056 -9.58135,2.337459 z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+43
View File
@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://4t7vk634k0v4"
path="res://.godot/imported/restart_light_icon.svg-3822a30546bff23ba1adb74c1723ba80.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/restart_light_icon.svg"
dest_files=["res://.godot/imported/restart_light_icon.svg-3822a30546bff23ba1adb74c1723ba80.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false
+4
View File
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<path fill="#f9f9f9" d="M3 9.5v5c0 .55.45 1 1 1h3.2l4.1 3.6c.65.57 1.7.11 1.7-.76V5.66c0-.87-1.05-1.33-1.7-.76L7.2 8.5H4c-.55 0-1 .45-1 1z"/>
<path fill="none" stroke="#f9f9f9" stroke-width="2" stroke-linecap="round" d="M16 8.5a5 5 0 0 1 0 7M18.8 5.8a9 9 0 0 1 0 12.4"/>
</svg>

After

Width:  |  Height:  |  Size: 366 B

+43
View File
@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d0uyxivsv58uh"
path="res://.godot/imported/volume_light_icon.svg-a1b08f489924a01123c9c4f7fa5e16a0.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icons/volume_light_icon.svg"
dest_files=["res://.godot/imported/volume_light_icon.svg-a1b08f489924a01123c9c4f7fa5e16a0.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false
+52 -22
View File
@@ -1,9 +1,10 @@
[gd_scene load_steps=10 format=3 uid="uid://ds15cgsf8vpvc"] [gd_scene format=3 uid="uid://ds15cgsf8vpvc"]
[ext_resource type="Script" uid="uid://d1v0rquma6nt8" path="res://player/music_player.gd" id="1_t24ra"] [ext_resource type="Script" uid="uid://d1v0rquma6nt8" path="res://player/music_player.gd" id="1_t24ra"]
[ext_resource type="AudioStream" uid="uid://n2g8jddr85h2" path="res://sounds/01. Opening.mp3" id="2_xti80"] [ext_resource type="AudioStream" uid="uid://n2g8jddr85h2" path="res://sounds/01. Opening.mp3" id="2_xti80"]
[ext_resource type="Texture2D" uid="uid://comxqfiykp54f" path="res://icons/play_icon_light.svg" id="3_6g308"] [ext_resource type="Texture2D" path="res://icons/play_button_icon.svg" id="3_6g308"]
[ext_resource type="Texture2D" uid="uid://ccb6rvbldlgdg" path="res://icons/reload_light_icon.svg" id="4_jleuo"] [ext_resource type="Texture2D" path="res://icons/restart_light_icon.svg" id="4_jleuo"]
[ext_resource type="Texture2D" path="res://icons/volume_light_icon.svg" id="6_volume"]
[ext_resource type="PackedScene" uid="uid://w400rnew7453" path="res://player/VolumeSlider.tscn" id="5_iifuj"] [ext_resource type="PackedScene" uid="uid://w400rnew7453" path="res://player/VolumeSlider.tscn" id="5_iifuj"]
[sub_resource type="InputEventKey" id="InputEventKey_r4qo3"] [sub_resource type="InputEventKey" id="InputEventKey_r4qo3"]
@@ -29,7 +30,7 @@ events = [SubResource("InputEventKey_2nvce")]
[node name="MusicPlayer" type="Control"] [node name="MusicPlayer" type="Control"]
layout_mode = 3 layout_mode = 3
offset_right = 1792.0 offset_right = 1792.0
offset_bottom = 71.0 offset_bottom = 42.0
mouse_filter = 2 mouse_filter = 2
script = ExtResource("1_t24ra") script = ExtResource("1_t24ra")
@@ -46,52 +47,81 @@ stream = ExtResource("2_xti80")
bus = &"music" bus = &"music"
[node name="PlayerBar" type="PanelContainer" parent="."] [node name="PlayerBar" type="PanelContainer" parent="."]
layout_mode = 0 layout_mode = 1
offset_left = 1.0 anchors_preset = 15
offset_right = 1792.0 anchor_right = 1.0
offset_bottom = 32.0 anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
theme_type_variation = &"PlayerBar"
[node name="MusicPlayerContainer" type="HBoxContainer" parent="PlayerBar"] [node name="MusicPlayerContainer" type="HBoxContainer" parent="PlayerBar"]
layout_mode = 2 layout_mode = 2
size_flags_vertical = 4 mouse_filter = 2
theme_override_constants/separation = 10
[node name="PlayTextureButton" type="TextureButton" parent="PlayerBar/MusicPlayerContainer"] [node name="PlayButton" type="Button" parent="PlayerBar/MusicPlayerContainer"]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(32, 32) custom_minimum_size = Vector2(34, 34)
layout_mode = 2 layout_mode = 2
size_flags_vertical = 4
tooltip_text = "Play or pause (Alt+X)"
focus_mode = 0 focus_mode = 0
theme_type_variation = &"PlayerButtonPrimary"
action_mode = 0 action_mode = 0
shortcut = SubResource("Shortcut_ipcfh") shortcut = SubResource("Shortcut_ipcfh")
texture_normal = ExtResource("3_6g308") shortcut_feedback = false
ignore_texture_size = true shortcut_in_tooltip = false
stretch_mode = 0 icon = ExtResource("3_6g308")
icon_alignment = 1
expand_icon = true
[node name="RestartTextureButton" type="TextureButton" parent="PlayerBar/MusicPlayerContainer"] [node name="RestartButton" type="Button" parent="PlayerBar/MusicPlayerContainer"]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(32, 32) custom_minimum_size = Vector2(34, 34)
layout_mode = 2 layout_mode = 2
size_flags_vertical = 4
tooltip_text = "Restart the song (Alt+Z)"
focus_mode = 0 focus_mode = 0
theme_type_variation = &"PlayerButton"
action_mode = 0 action_mode = 0
shortcut = SubResource("Shortcut_i7swq") shortcut = SubResource("Shortcut_i7swq")
texture_normal = ExtResource("4_jleuo") shortcut_feedback = false
ignore_texture_size = true shortcut_in_tooltip = false
stretch_mode = 0 icon = ExtResource("4_jleuo")
flip_h = true icon_alignment = 1
expand_icon = true
[node name="MusicPlayerSlider" type="HSlider" parent="PlayerBar/MusicPlayerContainer"] [node name="MusicPlayerSlider" type="HSlider" parent="PlayerBar/MusicPlayerContainer"]
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
size_flags_vertical = 4 size_flags_vertical = 4
theme_type_variation = &"ProgressSlider"
scrollable = false scrollable = false
[node name="MusicTimeLabel" type="Label" parent="PlayerBar/MusicPlayerContainer"] [node name="MusicTimeLabel" type="Label" parent="PlayerBar/MusicPlayerContainer"]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(120, 0)
layout_mode = 2 layout_mode = 2
text = "0:00 / 0:00" theme_type_variation = &"PlayerTime"
text = "00:00 / 00:00"
horizontal_alignment = 1
[node name="VolumeIcon" type="TextureRect" parent="PlayerBar/MusicPlayerContainer"]
custom_minimum_size = Vector2(22, 22)
layout_mode = 2
size_flags_vertical = 4
mouse_filter = 2
texture = ExtResource("6_volume")
expand_mode = 1
stretch_mode = 5
[node name="VolumeSlider" parent="PlayerBar/MusicPlayerContainer" instance=ExtResource("5_iifuj")] [node name="VolumeSlider" parent="PlayerBar/MusicPlayerContainer" instance=ExtResource("5_iifuj")]
unique_name_in_owner = true unique_name_in_owner = true
custom_minimum_size = Vector2(100, 0) custom_minimum_size = Vector2(110, 0)
layout_mode = 2 layout_mode = 2
size_flags_vertical = 4 size_flags_vertical = 4
tooltip_text = "Volume"
theme_type_variation = &"VolumeBar"
+29 -11
View File
@@ -2,19 +2,22 @@ class_name MusicPlayer
extends Control extends Control
## The bar that plays the songs, with play, restart, progress and volume. Whether a song ## The bar that plays the songs, with play, restart, progress and volume. Whether a song
## repeats is a quiz setting ([code]Settings.auto_repeat_song[/code]). ## repeats is a quiz setting ([code]Settings.auto_repeat_song[/code]).
##
## The buttons grow a little under the mouse and the play button pops when it switches
## between play and pause, see [ButtonBounce].
signal update_song_list signal update_song_list
signal play_next_song signal play_next_song
var play_icon: Texture = preload("res://icons/play_icon_light.svg") var play_icon: Texture = preload("res://icons/play_button_icon.svg")
var pause_icon: Texture = preload("res://icons/pause_icon_light.svg") var pause_icon: Texture = preload("res://icons/pause_button_icon.svg")
var is_changing: bool = false var is_changing: bool = false
var playback_position: float var playback_position: float
var stream: AudioStream var stream: AudioStream
var song_finished: bool = false var song_finished: bool = false
@onready var play_button: TextureButton = %PlayTextureButton @onready var play_button: Button = %PlayButton
@onready var restart_button: TextureButton = %RestartTextureButton @onready var restart_button: Button = %RestartButton
@onready var audio_player: AudioStreamPlayer = $AudioStreamPlayer @onready var audio_player: AudioStreamPlayer = $AudioStreamPlayer
@onready var sound_player: AudioStreamPlayer = $AudioSoundPlayer @onready var sound_player: AudioStreamPlayer = $AudioSoundPlayer
@onready var sound_effect_player: AudioStreamPlayer = $AudioSoundEffectPlayer @onready var sound_effect_player: AudioStreamPlayer = $AudioSoundEffectPlayer
@@ -26,6 +29,8 @@ var song_finished: bool = false
func _ready() -> void: func _ready() -> void:
play_button.pressed.connect(play_or_pause) play_button.pressed.connect(play_or_pause)
restart_button.pressed.connect(restart) restart_button.pressed.connect(restart)
ButtonBounce.add_to(play_button)
ButtonBounce.add_to(restart_button)
progress_slider.drag_started.connect(_on_drag_started) progress_slider.drag_started.connect(_on_drag_started)
progress_slider.drag_ended.connect(_on_drag_ended) progress_slider.drag_ended.connect(_on_drag_ended)
@@ -43,6 +48,19 @@ func _process(_delta: float) -> void:
music_time_label.text = format_text(progress_slider.value, 0.0) music_time_label.text = format_text(progress_slider.value, 0.0)
## Whether the play button shows pause, because a song is playing.
func shows_playing() -> bool:
return play_button.icon == pause_icon
# Shows pause on the play button while a song plays, and play otherwise
func _show_playing(playing: bool) -> void:
var icon: Texture = pause_icon if playing else play_icon
if play_button.icon != icon:
play_button.icon = icon
ButtonBounce.pop(play_button)
func format_time(time: float) -> String: func format_time(time: float) -> String:
var total_seconds: int = roundi(time) var total_seconds: int = roundi(time)
@warning_ignore("integer_division") @warning_ignore("integer_division")
@@ -64,7 +82,7 @@ func play_or_pause() -> void:
if song_finished: if song_finished:
restart() restart()
elif audio_player.stream_paused: elif audio_player.stream_paused:
play_button.texture_normal = pause_icon _show_playing(true)
audio_player.stream_paused = false audio_player.stream_paused = false
song_finished = false song_finished = false
audio_player.seek(playback_position) audio_player.seek(playback_position)
@@ -81,7 +99,7 @@ func play_or_pause() -> void:
func pause() -> void: func pause() -> void:
audio_player.stream_paused = true audio_player.stream_paused = true
playback_position = audio_player.get_playback_position() playback_position = audio_player.get_playback_position()
play_button.texture_normal = play_icon _show_playing(false)
func restart() -> void: func restart() -> void:
@@ -91,12 +109,12 @@ func restart() -> void:
progress_slider.value = 0 progress_slider.value = 0
playback_position = audio_player.get_playback_position() playback_position = audio_player.get_playback_position()
audio_player.seek(playback_position) audio_player.seek(playback_position)
play_button.texture_normal = pause_icon _show_playing(true)
audio_player.play() audio_player.play()
func _on_finished() -> void: func _on_finished() -> void:
play_button.texture_normal = play_icon _show_playing(false)
song_finished = true song_finished = true
if !Settings.stop_after_current: if !Settings.stop_after_current:
play_next_song.emit() play_next_song.emit()
@@ -150,7 +168,7 @@ func play_sound(sound_name: AudioStream) -> void:
audio_player.stop() audio_player.stop()
sound_player.play() sound_player.play()
song_finished = true song_finished = true
play_button.texture_normal = play_icon _show_playing(false)
progress_slider.value = 0 progress_slider.value = 0
@@ -159,7 +177,7 @@ func play_song(song: AudioStream) -> void:
audio_player.play() audio_player.play()
sound_player.stop() sound_player.stop()
song_finished = false song_finished = false
play_button.texture_normal = pause_icon _show_playing(true)
stream = audio_player.stream stream = audio_player.stream
progress_slider.value = 0 progress_slider.value = 0
progress_slider.max_value = round(stream.get_length()) progress_slider.max_value = round(stream.get_length())
@@ -179,7 +197,7 @@ func play_song_object(song_object_no: int) -> void:
sound_player.stop() sound_player.stop()
audio_player.play() audio_player.play()
song_finished = false song_finished = false
play_button.texture_normal = pause_icon _show_playing(true)
stream = audio_player.stream stream = audio_player.stream
progress_slider.value = 0 progress_slider.value = 0
progress_slider.max_value = round(stream.get_length()) progress_slider.max_value = round(stream.get_length())
+2 -2
View File
@@ -48,9 +48,9 @@ offset_bottom = 384.0
[node name="MusicPlayer" parent="." unique_id=1990459832 instance=ExtResource("5_emn36")] [node name="MusicPlayer" parent="." unique_id=1990459832 instance=ExtResource("5_emn36")]
layout_mode = 0 layout_mode = 0
offset_left = 64.0 offset_left = 64.0
offset_top = 992.0 offset_top = 984.0
offset_right = 1856.0 offset_right = 1856.0
offset_bottom = 1063.0 offset_bottom = 1026.0
[node name="PlayersPanel" parent="." unique_id=1708375945 instance=ExtResource("scene_PlayersPanel")] [node name="PlayersPanel" parent="." unique_id=1708375945 instance=ExtResource("scene_PlayersPanel")]
layout_mode = 0 layout_mode = 0
+273 -210
View File
@@ -1056,71 +1056,80 @@
"Quiz/MusicPlayer": { "Quiz/MusicPlayer": {
"rect": [ "rect": [
64.0, 64.0,
992.0, 984.0,
1792.0, 1792.0,
71.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar": { "Quiz/MusicPlayer/PlayerBar": {
"rect": [ "rect": [
65.0, 64.0,
992.0, 984.0,
1791.0, 1792.0,
32.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
1791.0, 1772.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": {
"rect": [ "rect": [
137.0, 158.0,
1000.0, 997.0,
1532.0, 1402.0,
16.0 16.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": {
"rect": [ "rect": [
1673.0, 1570.0,
996.0, 992.0,
79.0, 120.0,
23.0 26.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayButton": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartButton": {
"rect": [ "rect": [
101.0, 114.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
],
"visible": true
},
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeIcon": {
"rect": [
1700.0,
994.0,
22.0,
22.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": {
"rect": [ "rect": [
1756.0, 1732.0,
1000.0, 997.0,
100.0, 110.0,
16.0 16.0
], ],
"visible": true "visible": true
@@ -4685,71 +4694,80 @@
"Quiz/MusicPlayer": { "Quiz/MusicPlayer": {
"rect": [ "rect": [
64.0, 64.0,
992.0, 984.0,
1792.0, 1792.0,
71.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar": { "Quiz/MusicPlayer/PlayerBar": {
"rect": [ "rect": [
65.0, 64.0,
992.0, 984.0,
1791.0, 1792.0,
32.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
1791.0, 1772.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": {
"rect": [ "rect": [
137.0, 158.0,
1000.0, 997.0,
1514.0, 1402.0,
16.0 16.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": {
"rect": [ "rect": [
1655.0, 1570.0,
996.0, 992.0,
97.0, 120.0,
23.0 26.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayButton": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartButton": {
"rect": [ "rect": [
101.0, 114.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
],
"visible": true
},
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeIcon": {
"rect": [
1700.0,
994.0,
22.0,
22.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": {
"rect": [ "rect": [
1756.0, 1732.0,
1000.0, 997.0,
100.0, 110.0,
16.0 16.0
], ],
"visible": true "visible": true
@@ -11482,71 +11500,80 @@
"Quiz/MusicPlayer": { "Quiz/MusicPlayer": {
"rect": [ "rect": [
64.0, 64.0,
992.0, 984.0,
1792.0, 1792.0,
71.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar": { "Quiz/MusicPlayer/PlayerBar": {
"rect": [ "rect": [
65.0, 64.0,
992.0, 984.0,
1791.0, 1792.0,
32.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
1791.0, 1772.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": {
"rect": [ "rect": [
137.0, 158.0,
1000.0, 997.0,
1532.0, 1402.0,
16.0 16.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": {
"rect": [ "rect": [
1673.0, 1570.0,
996.0, 992.0,
79.0, 120.0,
23.0 26.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayButton": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartButton": {
"rect": [ "rect": [
101.0, 114.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
],
"visible": true
},
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeIcon": {
"rect": [
1700.0,
994.0,
22.0,
22.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": {
"rect": [ "rect": [
1756.0, 1732.0,
1000.0, 997.0,
100.0, 110.0,
16.0 16.0
], ],
"visible": true "visible": true
@@ -15480,71 +15507,80 @@
"Quiz/MusicPlayer": { "Quiz/MusicPlayer": {
"rect": [ "rect": [
64.0, 64.0,
992.0, 984.0,
1792.0, 1792.0,
71.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar": { "Quiz/MusicPlayer/PlayerBar": {
"rect": [ "rect": [
65.0, 64.0,
992.0, 984.0,
1791.0, 1792.0,
32.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
1791.0, 1772.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": {
"rect": [ "rect": [
137.0, 158.0,
1000.0, 997.0,
1514.0, 1402.0,
16.0 16.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": {
"rect": [ "rect": [
1655.0, 1570.0,
996.0, 992.0,
97.0, 120.0,
23.0 26.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayButton": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartButton": {
"rect": [ "rect": [
101.0, 114.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
],
"visible": true
},
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeIcon": {
"rect": [
1700.0,
994.0,
22.0,
22.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": {
"rect": [ "rect": [
1756.0, 1732.0,
1000.0, 997.0,
100.0, 110.0,
16.0 16.0
], ],
"visible": true "visible": true
@@ -23540,71 +23576,80 @@
"Quiz/MusicPlayer": { "Quiz/MusicPlayer": {
"rect": [ "rect": [
64.0, 64.0,
992.0, 984.0,
1792.0, 1792.0,
71.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar": { "Quiz/MusicPlayer/PlayerBar": {
"rect": [ "rect": [
65.0, 64.0,
992.0, 984.0,
1791.0, 1792.0,
32.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
1791.0, 1772.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": {
"rect": [ "rect": [
137.0, 158.0,
1000.0, 997.0,
1532.0, 1402.0,
16.0 16.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": {
"rect": [ "rect": [
1673.0, 1570.0,
996.0, 992.0,
79.0, 120.0,
23.0 26.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayButton": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartButton": {
"rect": [ "rect": [
101.0, 114.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
],
"visible": true
},
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeIcon": {
"rect": [
1700.0,
994.0,
22.0,
22.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": {
"rect": [ "rect": [
1756.0, 1732.0,
1000.0, 997.0,
100.0, 110.0,
16.0 16.0
], ],
"visible": true "visible": true
@@ -36647,71 +36692,80 @@
"Quiz/MusicPlayer": { "Quiz/MusicPlayer": {
"rect": [ "rect": [
64.0, 64.0,
992.0, 984.0,
1792.0, 1792.0,
71.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar": { "Quiz/MusicPlayer/PlayerBar": {
"rect": [ "rect": [
65.0, 64.0,
992.0, 984.0,
1791.0, 1792.0,
32.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
1791.0, 1772.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": {
"rect": [ "rect": [
137.0, 158.0,
1000.0, 997.0,
1514.0, 1402.0,
16.0 16.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": {
"rect": [ "rect": [
1655.0, 1570.0,
996.0, 992.0,
97.0, 120.0,
23.0 26.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayButton": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartButton": {
"rect": [ "rect": [
101.0, 114.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
],
"visible": true
},
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeIcon": {
"rect": [
1700.0,
994.0,
22.0,
22.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": {
"rect": [ "rect": [
1756.0, 1732.0,
1000.0, 997.0,
100.0, 110.0,
16.0 16.0
], ],
"visible": true "visible": true
@@ -41243,71 +41297,80 @@
"Quiz/MusicPlayer": { "Quiz/MusicPlayer": {
"rect": [ "rect": [
64.0, 64.0,
992.0, 984.0,
1792.0, 1792.0,
71.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar": { "Quiz/MusicPlayer/PlayerBar": {
"rect": [ "rect": [
65.0, 64.0,
992.0, 984.0,
1791.0, 1792.0,
32.0 42.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
1791.0, 1772.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicPlayerSlider": {
"rect": [ "rect": [
137.0, 158.0,
1000.0, 997.0,
1514.0, 1402.0,
16.0 16.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/MusicTimeLabel": {
"rect": [ "rect": [
1655.0, 1570.0,
996.0, 992.0,
97.0, 120.0,
23.0 26.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/PlayButton": {
"rect": [ "rect": [
65.0, 70.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartTextureButton": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/RestartButton": {
"rect": [ "rect": [
101.0, 114.0,
992.0, 988.0,
32.0, 34.0,
32.0 34.0
],
"visible": true
},
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeIcon": {
"rect": [
1700.0,
994.0,
22.0,
22.0
], ],
"visible": true "visible": true
}, },
"Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": { "Quiz/MusicPlayer/PlayerBar/MusicPlayerContainer/VolumeSlider": {
"rect": [ "rect": [
1756.0, 1732.0,
1000.0, 997.0,
100.0, 110.0,
16.0 16.0
], ],
"visible": true "visible": true
+5
View File
@@ -446,6 +446,11 @@ func _freeze_music_player() -> void:
music_player.sound_player.stop() music_player.sound_player.stop()
music_player.progress_slider.value = 0 music_player.progress_slider.value = 0
music_player.music_time_label.text = "00:00 / 00:01" music_player.music_time_label.text = "00:00 / 00:01"
# The buttons' animations would otherwise be caught halfway, see ButtonBounce
for button: Button in [music_player.play_button, music_player.restart_button]:
if button.has_meta(&"button_bounce_tween"):
(button.get_meta(&"button_bounce_tween") as Tween).kill()
button.scale = Vector2.ONE
## The log starts each row with the time of day; use a fixed time instead. ## The log starts each row with the time of day; use a fixed time instead.
@@ -26,3 +26,129 @@ func _click(control: Control) -> void:
func test_empty_space_lets_clicks_through() -> void: func test_empty_space_lets_clicks_through() -> void:
assert_int(_player.mouse_filter).is_equal(Control.MOUSE_FILTER_IGNORE) assert_int(_player.mouse_filter).is_equal(Control.MOUSE_FILTER_IGNORE)
func _song() -> AudioStreamMP3:
var song: AudioStreamMP3 = AudioStreamMP3.new()
song.data = FileAccess.get_file_as_bytes("res://tests/data/short.mp3")
return song
func _buttons() -> Array[Button]:
return [_player.play_button, _player.restart_button]
# Waits until the buttons' animations have finished
func _settle() -> void:
await await_millis(int(maxf(ButtonBounce.DURATION, ButtonBounce.POP_DURATION) * 1000) + 80)
# Icons
func test_icons_sit_inside_their_buttons_with_room_around_them() -> void:
await _runner.simulate_frames(1)
for button: Button in _buttons():
var style: StyleBox = button.get_theme_stylebox(&"normal")
for side: Side in [SIDE_LEFT, SIDE_TOP, SIDE_RIGHT, SIDE_BOTTOM]:
assert_float(style.get_content_margin(side)).override_failure_message(button.name) \
.is_greater_equal(8.0)
assert_bool(button.expand_icon).override_failure_message(button.name).is_true()
assert_int(button.icon_alignment).is_equal(HORIZONTAL_ALIGNMENT_CENTER)
# A square button, so the round background is a circle around the icon
assert_float(button.size.x).override_failure_message(button.name) \
.is_equal(button.size.y)
var icon_room: Vector2 = button.size - style.get_minimum_size()
assert_float(icon_room.x).override_failure_message(button.name).is_greater(12.0)
func test_buttons_are_centred_in_the_bar() -> void:
await _runner.simulate_frames(1)
var bar: Rect2 = _player.get_node("PlayerBar").get_global_rect()
for button: Button in _buttons():
var rect: Rect2 = button.get_global_rect()
assert_bool(bar.encloses(rect)).override_failure_message(button.name).is_true()
assert_float(rect.get_center().y).override_failure_message(button.name) \
.is_equal_approx(bar.get_center().y, 1.0)
func test_play_button_stands_out_from_restart() -> void:
assert_str(_player.play_button.theme_type_variation).is_equal("PlayerButtonPrimary")
assert_str(_player.restart_button.theme_type_variation).is_equal("PlayerButton")
assert_str(_player.progress_slider.theme_type_variation).is_equal("ProgressSlider")
assert_str(_player.volume_slider.theme_type_variation).is_equal("VolumeBar")
assert_str(_player.music_time_label.theme_type_variation).is_equal("PlayerTime")
func test_buttons_have_tooltips_with_their_keys() -> void:
assert_str(_player.play_button.tooltip_text).contains("Alt+X")
assert_str(_player.restart_button.tooltip_text).contains("Alt+Z")
# Play and pause
func test_play_button_shows_play_until_a_song_plays() -> void:
assert_bool(_player.shows_playing()).is_false()
_player.play_song(_song())
assert_bool(_player.shows_playing()).is_true()
assert_object(_player.play_button.icon).is_same(_player.pause_icon)
func test_pausing_shows_play_again() -> void:
await _runner.simulate_frames(1)
_player.play_song(_song())
await _click(_player.play_button)
assert_bool(_player.audio_player.stream_paused).is_true()
assert_bool(_player.shows_playing()).is_false()
func test_play_button_pops_when_it_changes() -> void:
await _runner.simulate_frames(1)
_player.play_song(_song())
assert_vector(_player.play_button.scale).is_equal(ButtonBounce.POP_SCALE)
await _settle()
assert_vector(_player.play_button.scale).is_equal_approx(Vector2.ONE, Vector2(0.01, 0.01))
func test_play_button_does_not_pop_when_it_stays_the_same() -> void:
_player.play_song(_song())
await _settle()
_player.play_song(_song())
assert_vector(_player.play_button.scale).is_equal_approx(Vector2.ONE, Vector2(0.01, 0.01))
# Hover
func test_buttons_grow_under_the_mouse_and_shrink_back() -> void:
await _runner.simulate_frames(1)
for button: Button in _buttons():
_runner.set_mouse_position(_player.get_viewport().get_final_transform()
* button.get_global_rect().get_center())
await _runner.simulate_frames(2)
await _settle()
assert_vector(button.scale).override_failure_message(button.name) \
.is_equal_approx(ButtonBounce.HOVER_SCALE, Vector2(0.01, 0.01))
_runner.set_mouse_position(Vector2.ZERO)
await _runner.simulate_frames(2)
await _settle()
assert_vector(button.scale).override_failure_message(button.name) \
.is_equal_approx(Vector2.ONE, Vector2(0.01, 0.01))
func test_growing_keeps_the_button_in_place() -> void:
await _runner.simulate_frames(1)
var before: Vector2 = _player.restart_button.get_global_rect().get_center()
_player.restart_button.mouse_entered.emit()
await _settle()
assert_vector(_player.restart_button.get_global_rect().get_center()) \
.is_equal_approx(before, Vector2(0.5, 0.5))
+12
View File
@@ -313,3 +313,15 @@ func test_alt_s_opens_search() -> void:
await _press_keys([KEY_ALT, KEY_S]) await _press_keys([KEY_ALT, KEY_S])
assert_bool((_window.get_node("SearchPopup") as Control).visible).is_true() assert_bool((_window.get_node("SearchPopup") as Control).visible).is_true()
func test_music_player_fits_between_the_song_buttons_and_quiz_settings() -> void:
var bar: Rect2 = _quiz.get_node("MusicPlayer/PlayerBar").get_global_rect()
var above: Rect2 = _quiz.get_node("SongControls/NextButton").get_global_rect()
var below: Rect2 = _quiz.get_node("QuizSettingsButton").get_global_rect()
assert_float(bar.position.y).is_greater_equal(above.end.y + 4)
assert_float(bar.end.y).is_less_equal(below.position.y - 2)
# As wide as the music list above it
var list: Rect2 = _quiz.get_node("MusicListPanel").get_global_rect()
assert_float(bar.position.x).is_equal(list.position.x)
+2 -2
View File
@@ -58,8 +58,8 @@ func test_buttons_do_what_the_list_says() -> void:
var expected: Dictionary = { var expected: Dictionary = {
"SongControls/NextButton": "Next song", "SongControls/NextButton": "Next song",
"SongControls/ShowAnswerButton": "Show answer", "SongControls/ShowAnswerButton": "Show answer",
"MusicPlayer/PlayerBar/MusicPlayerContainer/PlayTextureButton": "Play or pause", "MusicPlayer/PlayerBar/MusicPlayerContainer/PlayButton": "Play or pause",
"MusicPlayer/PlayerBar/MusicPlayerContainer/RestartTextureButton": "Restart song", "MusicPlayer/PlayerBar/MusicPlayerContainer/RestartButton": "Restart song",
} }
for path: String in expected: for path: String in expected:
var button: BaseButton = _quiz.get_node(path) var button: BaseButton = _quiz.get_node(path)
+6
View File
@@ -37,6 +37,12 @@ const STYLES: Dictionary = {
"SongRowPlaying": "PanelContainer", "SongRowPlaying": "PanelContainer",
"SongRowPictureLine": "HBoxContainer", "SongRowPictureLine": "HBoxContainer",
"SongRowPictureLines": "VBoxContainer", "SongRowPictureLines": "VBoxContainer",
"PlayerBar": "PanelContainer",
"PlayerButton": "Button",
"PlayerButtonPrimary": "Button",
"PlayerTime": "Label",
"ProgressSlider": "HSlider",
"VolumeBar": "HSlider",
"StatisticsCard": "PanelContainer", "StatisticsCard": "PanelContainer",
"StatisticsCardHover": "PanelContainer", "StatisticsCardHover": "PanelContainer",
"StatisticsGrid": "GridContainer", "StatisticsGrid": "GridContainer",
+80
View File
@@ -0,0 +1,80 @@
extends GdUnitTestSuite
## ButtonBounce: buttons grow under the mouse, shrink while held down, pop when asked to,
## and always around their middle.
var _button: Button
func before_test() -> void:
_button = auto_free(Button.new())
_button.size = Vector2(40, 40)
add_child(_button)
ButtonBounce.add_to(_button)
func _settle() -> void:
await await_millis(int(maxf(ButtonBounce.DURATION, ButtonBounce.POP_DURATION) * 1000) + 80)
func test_starts_at_its_normal_size_scaled_around_its_middle() -> void:
assert_vector(_button.scale).is_equal(Vector2.ONE)
assert_vector(_button.pivot_offset).is_equal(Vector2(20, 20))
func test_pivot_follows_the_size() -> void:
_button.size = Vector2(60, 30)
assert_vector(_button.pivot_offset).is_equal(Vector2(30, 15))
func test_grows_when_the_mouse_enters_and_shrinks_back_when_it_leaves() -> void:
_button.mouse_entered.emit()
await _settle()
assert_vector(_button.scale).is_equal_approx(ButtonBounce.HOVER_SCALE, Vector2(0.01, 0.01))
_button.mouse_exited.emit()
await _settle()
assert_vector(_button.scale).is_equal_approx(Vector2.ONE, Vector2(0.01, 0.01))
func test_shrinks_while_held_down() -> void:
_button.button_down.emit()
await _settle()
assert_vector(_button.scale).is_equal_approx(ButtonBounce.PRESSED_SCALE, Vector2(0.01, 0.01))
func test_goes_back_to_normal_when_let_go_away_from_the_mouse() -> void:
_button.button_down.emit()
await _settle()
_button.button_up.emit()
await _settle()
assert_vector(_button.scale).is_equal_approx(Vector2.ONE, Vector2(0.01, 0.01))
func test_disabled_button_does_not_grow() -> void:
_button.disabled = true
_button.mouse_entered.emit()
await _settle()
assert_vector(_button.scale).is_equal(Vector2.ONE)
func test_pop_starts_small_and_springs_back() -> void:
ButtonBounce.pop(_button)
assert_vector(_button.scale).is_equal(ButtonBounce.POP_SCALE)
await _settle()
assert_vector(_button.scale).is_equal_approx(Vector2.ONE, Vector2(0.01, 0.01))
func test_a_new_animation_stops_the_one_before() -> void:
_button.mouse_entered.emit()
_button.button_down.emit()
await _settle()
# Only the last one ran to the end
assert_vector(_button.scale).is_equal_approx(ButtonBounce.PRESSED_SCALE, Vector2(0.01, 0.01))
+1
View File
@@ -0,0 +1 @@
uid://b8tyllgau53y6
+4
View File
@@ -51,6 +51,10 @@ inspector or `theme_type_variation` in code:
| `TabButton` | Button | The Quiz and Statistics tabs; the chosen one is orange | | `TabButton` | Button | The Quiz and Statistics tabs; the chosen one is orange |
| `StatisticsCard`, `StatisticsCardHover` | PanelContainer | A soundtrack card in the statistics, and the one under the mouse | | `StatisticsCard`, `StatisticsCardHover` | PanelContainer | A soundtrack card in the statistics, and the one under the mouse |
| `StatisticsGrid` | GridContainer | The grid of soundtrack cards, with the space between them | | `StatisticsGrid` | GridContainer | The grid of soundtrack cards, with the space between them |
| `PlayerBar` | PanelContainer | The music player's rounded bar |
| `PlayerButtonPrimary`, `PlayerButton` | Button | The round play button, and the restart button; the icon's colour on hover comes from here |
| `ProgressSlider`, `VolumeBar` | HSlider | How far the song has played (orange), and the volume |
| `PlayerTime` | Label | The time played and the song's length |
| `SongStatsHeader` | PanelContainer | The column titles above the statistics song list, spaced like its rows | | `SongStatsHeader` | PanelContainer | The column titles above the statistics song list, spaced like its rows |
| `SongStatsText`, `SongStatsSecondary` | RichTextLabel | A song's name and its soundtrack (dimmer) in the statistics song list | | `SongStatsText`, `SongStatsSecondary` | RichTextLabel | A song's name and its soundtrack (dimmer) in the statistics song list |
+186
View File
@@ -819,6 +819,158 @@ corner_radius_top_right = 6
corner_radius_bottom_right = 6 corner_radius_bottom_right = 6
corner_radius_bottom_left = 6 corner_radius_bottom_left = 6
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerbar"]
content_margin_left = 6.0
content_margin_top = 4.0
content_margin_right = 14.0
content_margin_bottom = 4.0
bg_color = Color(0.16, 0.16, 0.16, 1)
corner_radius_top_left = 8
corner_radius_top_right = 8
corner_radius_bottom_right = 8
corner_radius_bottom_left = 8
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerbutton"]
content_margin_left = 8.0
content_margin_top = 8.0
content_margin_right = 8.0
content_margin_bottom = 8.0
bg_color = Color(1, 1, 1, 0)
corner_radius_top_left = 17
corner_radius_top_right = 17
corner_radius_bottom_right = 17
corner_radius_bottom_left = 17
corner_detail = 12
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerbuttonhover"]
content_margin_left = 8.0
content_margin_top = 8.0
content_margin_right = 8.0
content_margin_bottom = 8.0
bg_color = Color(1, 1, 1, 0.12)
corner_radius_top_left = 17
corner_radius_top_right = 17
corner_radius_bottom_right = 17
corner_radius_bottom_left = 17
corner_detail = 12
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerbuttonpressed"]
content_margin_left = 8.0
content_margin_top = 8.0
content_margin_right = 8.0
content_margin_bottom = 8.0
bg_color = Color(1, 1, 1, 0.2)
corner_radius_top_left = 17
corner_radius_top_right = 17
corner_radius_bottom_right = 17
corner_radius_bottom_left = 17
corner_detail = 12
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerprimary"]
content_margin_left = 9.0
content_margin_top = 9.0
content_margin_right = 9.0
content_margin_bottom = 9.0
bg_color = Color(1, 0.5, 0, 1)
corner_radius_top_left = 17
corner_radius_top_right = 17
corner_radius_bottom_right = 17
corner_radius_bottom_left = 17
corner_detail = 12
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerprimaryhover"]
content_margin_left = 9.0
content_margin_top = 9.0
content_margin_right = 9.0
content_margin_bottom = 9.0
bg_color = Color(1, 0.6, 0.18, 1)
corner_radius_top_left = 17
corner_radius_top_right = 17
corner_radius_bottom_right = 17
corner_radius_bottom_left = 17
corner_detail = 12
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerprimarypressed"]
content_margin_left = 9.0
content_margin_top = 9.0
content_margin_right = 9.0
content_margin_bottom = 9.0
bg_color = Color(0.82, 0.4, 0, 1)
corner_radius_top_left = 17
corner_radius_top_right = 17
corner_radius_bottom_right = 17
corner_radius_bottom_left = 17
corner_detail = 12
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playerprimarydisabled"]
content_margin_left = 9.0
content_margin_top = 9.0
content_margin_right = 9.0
content_margin_bottom = 9.0
bg_color = Color(0.4, 0.4, 0.4, 1)
corner_radius_top_left = 17
corner_radius_top_right = 17
corner_radius_bottom_right = 17
corner_radius_bottom_left = 17
corner_detail = 12
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_playertrack"]
content_margin_left = 0.0
content_margin_top = 3.0
content_margin_right = 0.0
content_margin_bottom = 3.0
bg_color = Color(0, 0, 0, 0.35)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_progressfill"]
content_margin_left = 0.0
content_margin_top = 3.0
content_margin_right = 0.0
content_margin_bottom = 3.0
bg_color = Color(1, 0.5, 0, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_progressfillhover"]
content_margin_left = 0.0
content_margin_top = 3.0
content_margin_right = 0.0
content_margin_bottom = 3.0
bg_color = Color(1, 0.62, 0.22, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_volumefill"]
content_margin_left = 0.0
content_margin_top = 3.0
content_margin_right = 0.0
content_margin_bottom = 3.0
bg_color = Color(0.8, 0.8, 0.8, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_volumefillhover"]
content_margin_left = 0.0
content_margin_top = 3.0
content_margin_right = 0.0
content_margin_bottom = 3.0
bg_color = Color(1, 1, 1, 1)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_playerfocus"]
[resource] [resource]
default_font = ExtResource("1_default_font") default_font = ExtResource("1_default_font")
Button/colors/font_color = Color(0.875, 0.875, 0.875, 1) Button/colors/font_color = Color(0.875, 0.875, 0.875, 1)
@@ -940,6 +1092,32 @@ OptionButton/styles/pressed_mirrored = SubResource("StyleBoxFlat_a0ksy")
Palette/colors/backdrop = Color(0, 0, 0, 0.55) Palette/colors/backdrop = Color(0, 0, 0, 0.55)
Panel/styles/panel = SubResource("StyleBoxFlat_hwwyi") Panel/styles/panel = SubResource("StyleBoxFlat_hwwyi")
PanelContainer/styles/panel = SubResource("StyleBoxFlat_wl3gy") PanelContainer/styles/panel = SubResource("StyleBoxFlat_wl3gy")
PlayerBar/base_type = &"PanelContainer"
PlayerBar/styles/panel = SubResource("StyleBoxFlat_playerbar")
PlayerButton/base_type = &"Button"
PlayerButton/colors/icon_hover_color = Color(1, 0.5, 0, 1)
PlayerButton/colors/icon_hover_pressed_color = Color(1, 0.5, 0, 1)
PlayerButton/colors/icon_normal_color = Color(0.88, 0.88, 0.88, 1)
PlayerButton/colors/icon_pressed_color = Color(1, 0.6, 0.2, 1)
PlayerButton/styles/focus = SubResource("StyleBoxEmpty_playerfocus")
PlayerButton/styles/hover = SubResource("StyleBoxFlat_playerbuttonhover")
PlayerButton/styles/hover_pressed = SubResource("StyleBoxFlat_playerbuttonpressed")
PlayerButton/styles/normal = SubResource("StyleBoxFlat_playerbutton")
PlayerButton/styles/pressed = SubResource("StyleBoxFlat_playerbuttonpressed")
PlayerButtonPrimary/base_type = &"Button"
PlayerButtonPrimary/colors/icon_hover_color = Color(1, 1, 1, 1)
PlayerButtonPrimary/colors/icon_hover_pressed_color = Color(1, 1, 1, 1)
PlayerButtonPrimary/colors/icon_normal_color = Color(1, 1, 1, 1)
PlayerButtonPrimary/colors/icon_pressed_color = Color(1, 1, 1, 1)
PlayerButtonPrimary/styles/disabled = SubResource("StyleBoxFlat_playerprimarydisabled")
PlayerButtonPrimary/styles/focus = SubResource("StyleBoxEmpty_playerfocus")
PlayerButtonPrimary/styles/hover = SubResource("StyleBoxFlat_playerprimaryhover")
PlayerButtonPrimary/styles/hover_pressed = SubResource("StyleBoxFlat_playerprimarypressed")
PlayerButtonPrimary/styles/normal = SubResource("StyleBoxFlat_playerprimary")
PlayerButtonPrimary/styles/pressed = SubResource("StyleBoxFlat_playerprimarypressed")
PlayerTime/base_type = &"Label"
PlayerTime/colors/font_color = Color(0.82, 0.82, 0.82, 1)
PlayerTime/font_sizes/font_size = 18
PopupBackground/base_type = &"Panel" PopupBackground/base_type = &"Panel"
PopupBackground/styles/panel = SubResource("StyleBoxFlat_vputg") PopupBackground/styles/panel = SubResource("StyleBoxFlat_vputg")
PopupMenu/colors/font_accelerator_color = Color(0.7, 0.7, 0.7, 0.8) PopupMenu/colors/font_accelerator_color = Color(0.7, 0.7, 0.7, 0.8)
@@ -990,6 +1168,10 @@ ScrollContainer/colors/scroll_hint_horizontal_color = Color(0, 0, 0, 1)
ScrollContainer/colors/scroll_hint_vertical_color = Color(0, 0, 0, 1) ScrollContainer/colors/scroll_hint_vertical_color = Color(0, 0, 0, 1)
ScrollContainer/styles/focus = SubResource("StyleBoxFlat_35dwi") ScrollContainer/styles/focus = SubResource("StyleBoxFlat_35dwi")
ScrollContainer/styles/panel = SubResource("StyleBoxEmpty_r3gy8") ScrollContainer/styles/panel = SubResource("StyleBoxEmpty_r3gy8")
ProgressSlider/base_type = &"HSlider"
ProgressSlider/styles/grabber_area = SubResource("StyleBoxFlat_progressfill")
ProgressSlider/styles/grabber_area_highlight = SubResource("StyleBoxFlat_progressfillhover")
ProgressSlider/styles/slider = SubResource("StyleBoxFlat_playertrack")
SearchField/base_type = &"LineEdit" SearchField/base_type = &"LineEdit"
SearchField/font_sizes/font_size = 28 SearchField/font_sizes/font_size = 28
SearchResult/base_type = &"RichTextLabel" SearchResult/base_type = &"RichTextLabel"
@@ -1079,5 +1261,9 @@ VScrollBar/styles/scroll = SubResource("StyleBoxFlat_6fedo")
VScrollBar/styles/scroll_focus = SubResource("StyleBoxFlat_8sdcv") VScrollBar/styles/scroll_focus = SubResource("StyleBoxFlat_8sdcv")
VSeparator/constants/separation = 4 VSeparator/constants/separation = 4
VSeparator/styles/separator = SubResource("StyleBoxLine_r3erx") VSeparator/styles/separator = SubResource("StyleBoxLine_r3erx")
VolumeBar/base_type = &"HSlider"
VolumeBar/styles/grabber_area = SubResource("StyleBoxFlat_volumefill")
VolumeBar/styles/grabber_area_highlight = SubResource("StyleBoxFlat_volumefillhover")
VolumeBar/styles/slider = SubResource("StyleBoxFlat_playertrack")
WindowBackground/base_type = &"Panel" WindowBackground/base_type = &"Panel"
WindowBackground/styles/panel = SubResource("StyleBoxFlat_2mdtn") WindowBackground/styles/panel = SubResource("StyleBoxFlat_2mdtn")