Files

183 lines
5.3 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Threading;
using System.Threading.Tasks;
using Febucci.UI.Core;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using AibisDream.Framework;
using AibisDream.Utility;
namespace AibisDream
{
public class OnelineBox : MonoBehaviour, ILineView
{
[SerializeField] private TypewriterCore content;
[SerializeField] private GameObject box;
[Header("排版参数")]
[SerializeField] private int maxWidth = 800;
[SerializeField] private int minWidth = 10;
private LayoutElement _contentLayoutElement;
private TMP_Text _contentText;
private RectTransform _rectTransform;
private CancellationTokenSource _autoHideToken;
private event Action OnTextShowed;
private bool _isInitialized;
// ---------------------------- 显示逻辑 ----------------------------
public void RunLine(LineSyncToken syncToken)
{
// 先中断结束事件
_autoHideToken?.Cancel();
_autoHideToken = null;
OnTextShowed = null;
// 先激活并清空文本,再订阅 onTextShowed。
// box 从 HideDialog 恢复时会触发 Typewriter OnEnable → StartShowingText → 同步 onTextShowed
// 若此时尚未注册回调,或旧文本已全可见,会在 TextStart 之前把 token 推进到 AdvanceDelay。
if (box != null && !box.activeSelf)
box.SetActive(true);
content.StopShowingText();
content.TextAnimator.SetText(string.Empty);
var lineText = GetOneLineText(syncToken.lineInfo);
HandleLayout(lineText);
ApplyTextSpeedSnapshot();
// 注册事件(UI 就绪后再挂回调)
OnTextShowed += syncToken.TextShown;
syncToken.SkipTextAnima += () => content.SkipTypewriter();
syncToken.OnAdvance += AfterAdvance;
syncToken.TextStart();
content.ShowText(lineText);
}
private void ApplyTextSpeedSnapshot()
{
content.resetTypingSpeedAtStartup = false;
content.SetTypewriterSpeed(TextSpeedSettings.CurrentMultiplier);
}
private string GetOneLineText(LineInfo lineInfo)
{
if (string.IsNullOrEmpty(lineInfo.character.GetActorName()))
{
return lineInfo.lineText;
}
return $"<notype>{lineInfo.character.GetActorName()}: </notype>{lineInfo.lineText}";
}
private void HandleLayout(string line)
{
if (!_isInitialized)
{
InitRefs();
}
// 去除富文本标签后计算宽度
var width = DialogUIManager.Instance.EstimateTextWidth(line.RemoveRichTextTags(), _contentText.fontSize);
if (width > maxWidth)
{
_contentLayoutElement.preferredWidth = maxWidth;
}
else
{
_contentLayoutElement.preferredWidth = -1; // 自适应
}
_contentLayoutElement.minWidth = minWidth;
LayoutRebuilder.ForceRebuildLayoutImmediate(_rectTransform);
}
private async void AfterAdvance()
{
_autoHideToken = new CancellationTokenSource();
try
{
await Task.Delay(ConstRef.AutoHideDelay, _autoHideToken.Token);
HideDialog();
}
catch (TaskCanceledException)
{
// 忽略取消
}
}
public void HideDialog()
{
box.SetActive(false);
}
// ---------------------------- 生命周期 ----------------------------
private void Awake()
{
box.SetActive(false);
InitRefs();
}
private void InitRefs()
{
if (_isInitialized) return;
_rectTransform = box.GetComponent<RectTransform>();
_contentText = content.GetComponent<TMP_Text>();
_contentLayoutElement = content.GetComponent<LayoutElement>();
_isInitialized = true;
}
private void Start()
{
DialogUIManager.Instance.RegisterScreenView(DialogViewType.OnelineBox, this);
RegisterEvents();
}
private void OnDestroy()
{
DialogUIManager.Instance.UnRegisterScreenView(DialogViewType.OnelineBox);
UnregisterEvents();
OnTextShowed = null;
}
private void RegisterEvents()
{
// 全局事件
EnumEventSystem.Global.Register(DialogEventEnum.OptionShow, OnOptionShow);
// 文本事件
content.onTextShowed.AddListener(TextShowed);
}
private void UnregisterEvents()
{
// 全局事件
EnumEventSystem.Global.UnRegister(DialogEventEnum.OptionShow, OnOptionShow);
// 文本事件
content.onTextShowed.RemoveListener(TextShowed);
}
private void TextShowed()
{
OnTextShowed?.Invoke();
OnTextShowed = null;
}
private void OnOptionShow()
{
_autoHideToken?.Cancel();
_autoHideToken = null;
HideDialog();
}
}
}