mirror of
https://github.com/Cuteminded/jellyframe.git
synced 2026-10-17 17:00:38 +02:00
Replaces custom SVG UI icons with Material Symbols assets across player controls, details, and sidebar navigation, including updated import metadata and icon mappings for library types and skip controls. Adds in-player control labels that appear on hover/focus (including timeline and pause/play state changes), plus a new `test_player_tooltips` coverage path wired into Linux and Windows CI. Updates README usage/licensing notes and export presets to bundle Material icon attribution/license files.
98 lines
4.2 KiB
GDScript
98 lines
4.2 KiB
GDScript
class_name PlayerOverlay
|
|
extends MeshInstance3D
|
|
|
|
signal action(name: String)
|
|
signal seek_requested(milliseconds: int)
|
|
var viewport: SubViewport
|
|
var title: Label
|
|
var timeline: PlaybackTimeline
|
|
var pause_button: Button
|
|
var panel_size := Vector2(3.4, 0.75)
|
|
var play_icon: Texture2D
|
|
var pause_icon: Texture2D
|
|
var tooltip: Label
|
|
var hovered_control: Control
|
|
|
|
func _ready() -> void:
|
|
play_icon = load("res://assets/icons/material/play_arrow.svg")
|
|
pause_icon = load("res://assets/icons/material/pause.svg")
|
|
viewport = SubViewport.new()
|
|
viewport.size = Vector2i(1400, 300)
|
|
viewport.transparent_bg = true
|
|
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
|
|
add_child(viewport)
|
|
var material := StandardMaterial3D.new()
|
|
material.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
|
|
material.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
|
|
material.albedo_texture = viewport.get_texture()
|
|
material_override = material
|
|
var panel := PanelContainer.new()
|
|
panel.theme = Widgets.theme()
|
|
panel.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
|
panel.add_theme_stylebox_override("panel", Widgets.surface(Color("101624db"), 22, 24))
|
|
viewport.add_child(panel)
|
|
var body := VBoxContainer.new()
|
|
body.add_theme_constant_override("separation", 8)
|
|
panel.add_child(body)
|
|
title = Widgets.label("Now playing", body, 26)
|
|
title.clip_text = true
|
|
timeline = PlaybackTimeline.new()
|
|
body.add_child(timeline)
|
|
timeline.seek_requested.connect(func(value: int): seek_requested.emit(value))
|
|
timeline.slider.tooltip_text = ""
|
|
timeline.slider.accessibility_name = "Seek to a time"
|
|
timeline.slider.mouse_entered.connect(_show_tooltip.bind(timeline.slider))
|
|
timeline.slider.mouse_exited.connect(_hide_tooltip.bind(timeline.slider))
|
|
timeline.slider.focus_entered.connect(_show_tooltip.bind(timeline.slider))
|
|
timeline.slider.focus_exited.connect(_hide_tooltip.bind(timeline.slider))
|
|
var row := HBoxContainer.new()
|
|
row.add_theme_constant_override("separation", 12)
|
|
body.add_child(row)
|
|
for entry in [["Pause", "pause", "pause"], ["Stop", "stop", "stop"], ["Back 10 seconds", "back", "replay_10"], ["Forward 30 seconds", "forward", "forward_30"], ["Settings", "settings", "settings"], ["Library", "library", "video_library"], ["Hide controls", "hide", "visibility_off"]]:
|
|
var button := Widgets.button("", func(): action.emit(entry[1]), row)
|
|
button.icon = load("res://assets/icons/material/%s.svg" % entry[2])
|
|
button.accessibility_name = entry[0]
|
|
button.custom_minimum_size = Vector2(140, 72)
|
|
button.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
button.add_theme_constant_override("icon_max_width", 44)
|
|
button.mouse_entered.connect(_show_tooltip.bind(button))
|
|
button.mouse_exited.connect(_hide_tooltip.bind(button))
|
|
button.focus_entered.connect(_show_tooltip.bind(button))
|
|
button.focus_exited.connect(_hide_tooltip.bind(button))
|
|
if entry[1] == "pause": pause_button = button
|
|
tooltip = Widgets.label("", body, 20)
|
|
tooltip.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
tooltip.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
|
tooltip.custom_minimum_size.y = 28
|
|
hide()
|
|
|
|
func _show_tooltip(control: Control) -> void:
|
|
hovered_control = control
|
|
tooltip.text = control.accessibility_name
|
|
|
|
func _hide_tooltip(control: Control) -> void:
|
|
if hovered_control != control: return
|
|
hovered_control = null
|
|
tooltip.text = ""
|
|
|
|
func _process(_delta: float) -> void:
|
|
if not is_visible_in_tree():
|
|
hovered_control = null
|
|
tooltip.text = ""
|
|
elif is_instance_valid(hovered_control):
|
|
tooltip.text = hovered_control.accessibility_name
|
|
|
|
func fit_screen(width: float, aspect: float, curvature: float) -> void:
|
|
panel_size = Vector2(width * 0.94, width * 0.94 * 300.0 / 1400.0)
|
|
var quad := QuadMesh.new()
|
|
quad.size = panel_size
|
|
mesh = quad
|
|
position.y = -width / aspect / 2.0 + panel_size.y / 2.0 + width * 0.02
|
|
position.z = (1.0 - cos(curvature / 2.0)) * width / curvature + 0.025 if curvature > 0.001 else 0.025
|
|
|
|
func update_time(position_ms: int, duration_ms: int, ready: bool, paused: bool) -> void:
|
|
timeline.update_time(position_ms, duration_ms, ready)
|
|
pause_button.icon = play_icon if paused else pause_icon
|
|
pause_button.accessibility_name = "Play" if paused else "Pause"
|
|
if hovered_control == pause_button: tooltip.text = pause_button.accessibility_name
|