- 创建AuthManager.gd:负责所有认证业务逻辑 - 用户登录/注册逻辑 - 表单验证逻辑 - 验证码管理逻辑 - 网络请求管理 - 创建ToastManager.gd:负责Toast消息管理 - Toast创建和显示 - 动画和生命周期管理 - 支持成功/失败样式 - 重构AuthScene.gd:纯视图层实现 - 只负责UI交互和显示 - 通过信号与业务层通信 - 移除所有业务逻辑代码 - 修复GDScript警告: - 未使用参数添加下划线前缀 - 修复变量名与基类方法冲突 - 修复EventSystem中的try语法错误 - 修复AuthManager中的方法名不匹配错误 符合docs中的架构要求,实现完全解耦
234 lines
7.0 KiB
GDScript
234 lines
7.0 KiB
GDScript
class_name ToastManager
|
||
|
||
# ============================================================================
|
||
# ToastManager.gd - Toast消息管理器
|
||
# ============================================================================
|
||
# 负责创建和管理Toast消息的显示
|
||
#
|
||
# 核心功能:
|
||
# - 创建Toast消息实例
|
||
# - 管理Toast动画和生命周期
|
||
# - 支持多个Toast同时显示
|
||
# - 自动排列和清理Toast
|
||
# - 支持中文字体显示
|
||
#
|
||
# 使用方式:
|
||
# var toast_manager = ToastManager.new()
|
||
# toast_manager.setup(toast_container)
|
||
# toast_manager.show_toast("消息内容", true)
|
||
#
|
||
# 注意事项:
|
||
# - 需要提供一个容器节点来承载Toast
|
||
# - 自动处理Toast的位置计算和动画
|
||
# - 支持Web平台的字体处理
|
||
# ============================================================================
|
||
|
||
extends RefCounted
|
||
|
||
# ============ 成员变量 ============
|
||
|
||
# Toast容器和管理
|
||
var toast_container: Control # Toast消息容器
|
||
var active_toasts: Array = [] # 当前显示的Toast消息列表
|
||
var toast_counter: int = 0 # Toast计数器,用于生成唯一ID
|
||
|
||
# ============ 初始化方法 ============
|
||
|
||
# 设置Toast管理器
|
||
#
|
||
# 参数:
|
||
# container: Control - Toast消息的容器节点
|
||
func setup(container: Control):
|
||
toast_container = container
|
||
print("ToastManager 初始化完成")
|
||
|
||
# ============ 公共方法 ============
|
||
|
||
# 显示Toast消息
|
||
#
|
||
# 参数:
|
||
# message: String - 消息内容
|
||
# is_success: bool - 是否为成功消息(影响颜色)
|
||
func show_toast(message: String, is_success: bool = true):
|
||
if toast_container == null:
|
||
print("错误: toast_container 节点不存在")
|
||
return
|
||
|
||
print("显示Toast消息: ", message, " 成功: ", is_success)
|
||
_create_toast_instance(message, is_success)
|
||
|
||
# 清理所有Toast
|
||
func clear_all_toasts():
|
||
for toast in active_toasts:
|
||
if is_instance_valid(toast):
|
||
toast.queue_free()
|
||
active_toasts.clear()
|
||
|
||
# ============ 私有方法 ============
|
||
|
||
# 创建Toast实例
|
||
func _create_toast_instance(message: String, is_success: bool):
|
||
toast_counter += 1
|
||
|
||
# Web平台字体处理
|
||
var is_web = OS.get_name() == "Web"
|
||
|
||
# 1. 创建Toast Panel(方框UI)
|
||
var toast_panel = Panel.new()
|
||
toast_panel.name = "Toast_" + str(toast_counter)
|
||
|
||
# 设置Toast样式
|
||
var style = StyleBoxFlat.new()
|
||
if is_success:
|
||
style.bg_color = Color(0.15, 0.7, 0.15, 0.95)
|
||
style.border_color = Color(0.2, 0.9, 0.2, 0.9)
|
||
else:
|
||
style.bg_color = Color(0.7, 0.15, 0.15, 0.95)
|
||
style.border_color = Color(0.9, 0.2, 0.2, 0.9)
|
||
|
||
style.border_width_left = 3
|
||
style.border_width_top = 3
|
||
style.border_width_right = 3
|
||
style.border_width_bottom = 3
|
||
style.corner_radius_top_left = 12
|
||
style.corner_radius_top_right = 12
|
||
style.corner_radius_bottom_left = 12
|
||
style.corner_radius_bottom_right = 12
|
||
style.shadow_color = Color(0, 0, 0, 0.3)
|
||
style.shadow_size = 4
|
||
style.shadow_offset = Vector2(2, 2)
|
||
|
||
toast_panel.add_theme_stylebox_override("panel", style)
|
||
|
||
# 设置Toast基本尺寸
|
||
var toast_width = 320
|
||
toast_panel.size = Vector2(toast_width, 60)
|
||
|
||
# 2. 创建VBoxContainer
|
||
var vbox = VBoxContainer.new()
|
||
vbox.add_theme_constant_override("separation", 0)
|
||
vbox.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||
vbox.alignment = BoxContainer.ALIGNMENT_CENTER
|
||
|
||
# 3. 创建CenterContainer
|
||
var center_container = CenterContainer.new()
|
||
center_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||
center_container.size_flags_vertical = Control.SIZE_SHRINK_CENTER
|
||
|
||
# 4. 创建Label(文字控件)
|
||
var text_label = Label.new()
|
||
text_label.text = message
|
||
text_label.add_theme_color_override("font_color", Color(1, 1, 1, 1))
|
||
text_label.add_theme_font_size_override("font_size", 14)
|
||
|
||
# 平台特定的字体处理
|
||
if is_web:
|
||
print("Web平台Toast字体处理")
|
||
# Web平台使用主题文件
|
||
var chinese_theme = load("res://assets/ui/chinese_theme.tres")
|
||
if chinese_theme:
|
||
text_label.theme = chinese_theme
|
||
print("Web平台应用中文主题")
|
||
else:
|
||
print("Web平台中文主题加载失败")
|
||
else:
|
||
print("桌面平台Toast字体处理")
|
||
# 桌面平台直接加载中文字体
|
||
var desktop_chinese_font = load("res://assets/fonts/msyh.ttc")
|
||
if desktop_chinese_font:
|
||
text_label.add_theme_font_override("font", desktop_chinese_font)
|
||
print("桌面平台使用中文字体")
|
||
|
||
text_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
text_label.custom_minimum_size = Vector2(280, 0)
|
||
text_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
text_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||
|
||
# 组装控件层级
|
||
center_container.add_child(text_label)
|
||
vbox.add_child(center_container)
|
||
toast_panel.add_child(vbox)
|
||
|
||
# 计算位置
|
||
var margin = 20
|
||
var start_x = toast_container.get_viewport().get_visible_rect().size.x
|
||
var final_x = toast_container.get_viewport().get_visible_rect().size.x - toast_width - margin
|
||
|
||
# 计算Y位置
|
||
var y_position = margin
|
||
for existing_toast in active_toasts:
|
||
if is_instance_valid(existing_toast):
|
||
y_position += existing_toast.size.y + 15
|
||
|
||
# 设置初始位置
|
||
toast_panel.position = Vector2(start_x, y_position)
|
||
|
||
# 添加到容器
|
||
toast_container.add_child(toast_panel)
|
||
active_toasts.append(toast_panel)
|
||
|
||
# 等待一帧让布局系统计算尺寸
|
||
await toast_container.get_tree().process_frame
|
||
|
||
# 让Toast高度自适应内容
|
||
var content_size = vbox.get_combined_minimum_size()
|
||
var final_height = max(60, content_size.y + 20) # 最小60,加20像素边距
|
||
toast_panel.size.y = final_height
|
||
|
||
# 重新排列所有Toast
|
||
_rearrange_toasts()
|
||
|
||
# 开始动画
|
||
_animate_toast_in(toast_panel, final_x)
|
||
|
||
# Toast入场动画
|
||
func _animate_toast_in(toast_panel: Panel, final_x: float):
|
||
var tween = toast_container.create_tween()
|
||
tween.set_ease(Tween.EASE_OUT)
|
||
tween.set_trans(Tween.TRANS_BACK)
|
||
|
||
tween.parallel().tween_property(toast_panel, "position:x", final_x, 0.6)
|
||
tween.parallel().tween_property(toast_panel, "modulate:a", 1.0, 0.4)
|
||
|
||
toast_panel.modulate.a = 0.0
|
||
|
||
# 等待3秒后开始退场动画
|
||
await toast_container.get_tree().create_timer(3.0).timeout
|
||
_animate_toast_out(toast_panel)
|
||
|
||
# Toast退场动画
|
||
func _animate_toast_out(toast_panel: Panel):
|
||
if not is_instance_valid(toast_panel):
|
||
return
|
||
|
||
var tween = toast_container.create_tween()
|
||
tween.set_ease(Tween.EASE_IN)
|
||
tween.set_trans(Tween.TRANS_QUART)
|
||
|
||
var end_x = toast_container.get_viewport().get_visible_rect().size.x + 50
|
||
tween.parallel().tween_property(toast_panel, "position:x", end_x, 0.4)
|
||
tween.parallel().tween_property(toast_panel, "modulate:a", 0.0, 0.3)
|
||
|
||
await tween.finished
|
||
_cleanup_toast(toast_panel)
|
||
|
||
# 清理Toast
|
||
func _cleanup_toast(toast_panel: Panel):
|
||
if not is_instance_valid(toast_panel):
|
||
return
|
||
|
||
active_toasts.erase(toast_panel)
|
||
_rearrange_toasts()
|
||
toast_panel.queue_free()
|
||
|
||
# 重新排列Toast位置
|
||
func _rearrange_toasts():
|
||
var margin = 20
|
||
var current_y = margin
|
||
|
||
for i in range(active_toasts.size()):
|
||
var toast = active_toasts[i]
|
||
if is_instance_valid(toast):
|
||
var tween = toast_container.create_tween()
|
||
tween.tween_property(toast, "position:y", current_y, 0.2)
|
||
current_y += toast.size.y + 15 |