153 lines
4.8 KiB
GDScript
153 lines
4.8 KiB
GDScript
# Toast.gd - 合并的Toast系统
|
||
extends Node
|
||
|
||
# Toast节点缓存
|
||
var toast_container: Control
|
||
|
||
func _ready():
|
||
# 延迟创建Toast容器,避免在节点初始化期间添加子节点的冲突
|
||
setup_toast_container.call_deferred()
|
||
|
||
func setup_toast_container():
|
||
# 防止重复创建
|
||
if toast_container and is_instance_valid(toast_container):
|
||
return
|
||
|
||
# 创建一个CanvasLayer来确保Toast始终在最顶层
|
||
var canvas_layer = CanvasLayer.new()
|
||
canvas_layer.name = "ToastCanvasLayer"
|
||
canvas_layer.layer = 100 # 设置一个很高的层级值
|
||
|
||
# 创建一个容器来放置所有Toast
|
||
toast_container = Control.new()
|
||
toast_container.name = "ToastContainer"
|
||
toast_container.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||
toast_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
|
||
# 将容器添加到CanvasLayer中
|
||
canvas_layer.add_child(toast_container)
|
||
|
||
# 尝试添加到main/UI节点,如果失败则添加到根节点
|
||
var ui_node = get_node_or_null("/root/main/UI")
|
||
if ui_node:
|
||
ui_node.add_child.call_deferred(canvas_layer)
|
||
print("Toast容器已添加到 main/UI 节点")
|
||
else:
|
||
# 如果没有UI节点,直接添加到根节点
|
||
get_tree().root.add_child.call_deferred(canvas_layer)
|
||
print("Toast容器已添加到根节点")
|
||
|
||
|
||
func show(text: String,
|
||
color: Color = Color.WHITE,
|
||
duration: float = 2.0,
|
||
fade_duration: float = 0.5) -> void:
|
||
|
||
# 确保容器存在且有效
|
||
if not toast_container or not is_instance_valid(toast_container) or not toast_container.get_parent():
|
||
setup_toast_container.call_deferred()
|
||
# 等待容器设置完成
|
||
await get_tree().process_frame
|
||
await get_tree().process_frame # 额外等待一帧确保完成
|
||
|
||
# 再次检查容器是否有效
|
||
if not toast_container or not is_instance_valid(toast_container):
|
||
print("警告:Toast容器初始化失败,无法显示Toast")
|
||
return
|
||
|
||
# 创建Toast UI
|
||
var toast_panel = create_toast_ui(text, color)
|
||
toast_container.add_child(toast_panel)
|
||
|
||
# 显示动画和自动消失
|
||
show_toast_animation(toast_panel, duration, fade_duration)
|
||
|
||
func create_toast_ui(text: String, color: Color) -> PanelContainer:
|
||
# 创建主容器
|
||
var panel = PanelContainer.new()
|
||
panel.name = "Toast_" + str(Time.get_ticks_msec())
|
||
|
||
# 设置样式
|
||
var style_box = StyleBoxFlat.new()
|
||
style_box.bg_color = Color(0, 0, 0, 0.8) # 半透明黑色背景
|
||
style_box.corner_radius_top_left = 8
|
||
style_box.corner_radius_top_right = 8
|
||
style_box.corner_radius_bottom_left = 8
|
||
style_box.corner_radius_bottom_right = 8
|
||
style_box.content_margin_top = 12
|
||
style_box.content_margin_bottom = 12
|
||
style_box.content_margin_left = 16
|
||
style_box.content_margin_right = 16
|
||
panel.add_theme_stylebox_override("panel", style_box)
|
||
|
||
# 创建文本标签
|
||
var label = Label.new()
|
||
label.text = text
|
||
label.modulate = color
|
||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||
|
||
# 设置字体大小
|
||
label.add_theme_font_size_override("font_size", 16)
|
||
|
||
panel.add_child(label)
|
||
|
||
# 定位Toast(屏幕中央偏下)
|
||
position_toast(panel)
|
||
|
||
return panel
|
||
|
||
func position_toast(toast_panel: PanelContainer):
|
||
# 设置位置为屏幕中央偏下
|
||
toast_panel.set_anchors_and_offsets_preset(Control.PRESET_CENTER)
|
||
toast_panel.position.y += 100 # 向下偏移
|
||
|
||
# 调整现有Toast的位置,避免重叠
|
||
var existing_toasts = []
|
||
for child in toast_container.get_children():
|
||
if child != toast_panel and child is PanelContainer:
|
||
existing_toasts.append(child)
|
||
|
||
# 向上堆叠
|
||
for i in range(existing_toasts.size()):
|
||
var existing_toast = existing_toasts[existing_toasts.size() - 1 - i]
|
||
var tween = create_tween()
|
||
tween.tween_property(existing_toast, "position:y",
|
||
existing_toast.position.y - 60, 0.3)
|
||
|
||
func show_toast_animation(toast_panel: PanelContainer, duration: float, fade_duration: float):
|
||
# 初始状态:完全透明并稍微向上
|
||
toast_panel.modulate.a = 0.0
|
||
toast_panel.position.y += 20
|
||
|
||
# 淡入动画
|
||
var fade_in_tween = create_tween()
|
||
fade_in_tween.parallel().tween_property(toast_panel, "modulate:a", 1.0, 0.3)
|
||
fade_in_tween.parallel().tween_property(toast_panel, "position:y",
|
||
toast_panel.position.y - 20, 0.3)
|
||
fade_in_tween.set_ease(Tween.EASE_OUT)
|
||
|
||
# 等待显示时间
|
||
await get_tree().create_timer(duration).timeout
|
||
|
||
# 淡出动画
|
||
var fade_out_tween = create_tween()
|
||
fade_out_tween.tween_property(toast_panel, "modulate:a", 0.0, fade_duration)
|
||
await fade_out_tween.finished
|
||
|
||
# 移除节点
|
||
toast_panel.queue_free()
|
||
|
||
# 便捷方法
|
||
func show_success(text: String, duration: float = 2.0):
|
||
show(text, Color.GREEN, duration)
|
||
|
||
func show_error(text: String, duration: float = 3.0):
|
||
show(text, Color.RED, duration)
|
||
|
||
func show_warning(text: String, duration: float = 2.5):
|
||
show(text, Color.YELLOW, duration)
|
||
|
||
func show_info(text: String, duration: float = 2.0):
|
||
show(text, Color.CYAN, duration)
|