406 lines
11 KiB
C#
406 lines
11 KiB
C#
using System;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
|
||
/// <summary>
|
||
/// 可滚动的TMP InputField组件
|
||
/// 支持大量文本输入,带有滚动条,可以复制文本内容
|
||
/// </summary>
|
||
[RequireComponent(typeof(ScrollRect))]
|
||
public class ScrollableInputField : MonoBehaviour
|
||
{
|
||
[Header("组件引用")]
|
||
[Tooltip("TMP InputField组件")]
|
||
[SerializeField] private TMP_InputField inputField;
|
||
|
||
[Tooltip("ScrollRect组件")]
|
||
[SerializeField] private ScrollRect scrollRect;
|
||
|
||
[Tooltip("复制按钮(可选)")]
|
||
[SerializeField] private Button copyButton;
|
||
|
||
[Header("设置")]
|
||
[Tooltip("是否启用自动滚动到底部")]
|
||
[SerializeField] private bool autoScrollToBottom = true;
|
||
|
||
[Tooltip("是否启用调试日志")]
|
||
[SerializeField] private bool enableDebugLog = true;
|
||
|
||
[Tooltip("最大字符数限制(0表示无限制)")]
|
||
[SerializeField] private int maxCharacterLimit = 0;
|
||
|
||
// 文本变化事件
|
||
public event Action<string> OnTextChanged;
|
||
|
||
// 复制成功事件
|
||
public event Action OnCopySuccess;
|
||
|
||
/// <summary>
|
||
/// 初始化组件
|
||
/// </summary>
|
||
private void Awake()
|
||
{
|
||
InitializeComponents();
|
||
SetupInputField();
|
||
SetupCopyButton();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 初始化组件引用
|
||
/// </summary>
|
||
private void InitializeComponents()
|
||
{
|
||
// 获取ScrollRect组件
|
||
if (scrollRect == null)
|
||
{
|
||
scrollRect = GetComponent<ScrollRect>();
|
||
}
|
||
|
||
// 查找TMP InputField组件
|
||
if (inputField == null)
|
||
{
|
||
inputField = GetComponentInChildren<TMP_InputField>();
|
||
}
|
||
|
||
if (inputField == null)
|
||
{
|
||
Debug.LogError($"ScrollableInputField: 在 {gameObject.name} 中未找到TMP_InputField组件!");
|
||
return;
|
||
}
|
||
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: 初始化完成 - {gameObject.name}");
|
||
}
|
||
|
||
SetText("大量文本大量文本内容大量文本内容大量文本大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容大量文本内容内容...");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置InputField属性
|
||
/// </summary>
|
||
private void SetupInputField()
|
||
{
|
||
if (inputField == null) return;
|
||
|
||
// 设置为多行文本
|
||
inputField.lineType = TMP_InputField.LineType.MultiLineNewline;
|
||
|
||
// 设置字符限制
|
||
if (maxCharacterLimit > 0)
|
||
{
|
||
inputField.characterLimit = maxCharacterLimit;
|
||
}
|
||
|
||
// 监听文本变化事件
|
||
inputField.onValueChanged.AddListener(OnInputFieldTextChanged);
|
||
inputField.onEndEdit.AddListener(OnInputFieldEndEdit);
|
||
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: InputField设置完成 - 多行模式: {inputField.lineType}, 字符限制: {inputField.characterLimit}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置复制按钮
|
||
/// </summary>
|
||
private void SetupCopyButton()
|
||
{
|
||
if (copyButton != null)
|
||
{
|
||
copyButton.onClick.AddListener(CopyTextToClipboard);
|
||
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: 复制按钮设置完成");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// InputField文本变化回调
|
||
/// </summary>
|
||
/// <param name="text">新的文本内容</param>
|
||
private void OnInputFieldTextChanged(string text)
|
||
{
|
||
// 触发文本变化事件
|
||
OnTextChanged?.Invoke(text);
|
||
|
||
// 自动滚动到底部
|
||
if (autoScrollToBottom)
|
||
{
|
||
ScrollToBottom();
|
||
}
|
||
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: 文本变化 - 长度: {text.Length}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// InputField编辑结束回调
|
||
/// </summary>
|
||
/// <param name="text">最终文本内容</param>
|
||
private void OnInputFieldEndEdit(string text)
|
||
{
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: 编辑结束 - 最终长度: {text.Length}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 滚动到底部
|
||
/// </summary>
|
||
public void ScrollToBottom()
|
||
{
|
||
if (scrollRect != null)
|
||
{
|
||
scrollRect.verticalNormalizedPosition = 0f;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 滚动到顶部
|
||
/// </summary>
|
||
public void ScrollToTop()
|
||
{
|
||
if (scrollRect != null)
|
||
{
|
||
scrollRect.verticalNormalizedPosition = 1f;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 复制文本到剪贴板
|
||
/// </summary>
|
||
public void CopyTextToClipboard()
|
||
{
|
||
if (inputField == null)
|
||
{
|
||
Debug.LogWarning("ScrollableInputField: InputField为空,无法复制文本");
|
||
return;
|
||
}
|
||
|
||
string textToCopy = inputField.text;
|
||
|
||
if (string.IsNullOrEmpty(textToCopy))
|
||
{
|
||
Debug.LogWarning("ScrollableInputField: 文本为空,无法复制");
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
GUIUtility.systemCopyBuffer = textToCopy;
|
||
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: 文本已复制到剪贴板 - 长度: {textToCopy.Length}");
|
||
}
|
||
|
||
// 触发复制成功事件
|
||
OnCopySuccess?.Invoke();
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Debug.LogError($"ScrollableInputField: 复制文本失败: {ex.Message}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置文本内容
|
||
/// </summary>
|
||
/// <param name="text">要设置的文本</param>
|
||
public void SetText(string text)
|
||
{
|
||
if (inputField != null)
|
||
{
|
||
inputField.text = text;
|
||
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: 设置文本 - 长度: {text?.Length ?? 0}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前文本内容
|
||
/// </summary>
|
||
/// <returns>当前文本内容</returns>
|
||
public string GetText()
|
||
{
|
||
return inputField?.text ?? string.Empty;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清空文本内容
|
||
/// </summary>
|
||
public void ClearText()
|
||
{
|
||
SetText(string.Empty);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 追加文本内容
|
||
/// </summary>
|
||
/// <param name="text">要追加的文本</param>
|
||
public void AppendText(string text)
|
||
{
|
||
if (inputField != null)
|
||
{
|
||
inputField.text += text;
|
||
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: 追加文本 - 追加长度: {text?.Length ?? 0}, 总长度: {inputField.text.Length}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置字符限制
|
||
/// </summary>
|
||
/// <param name="limit">字符限制数量(0表示无限制)</param>
|
||
public void SetCharacterLimit(int limit)
|
||
{
|
||
maxCharacterLimit = limit;
|
||
|
||
if (inputField != null)
|
||
{
|
||
inputField.characterLimit = limit > 0 ? limit : 0;
|
||
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: 设置字符限制: {limit}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置是否自动滚动到底部
|
||
/// </summary>
|
||
/// <param name="autoScroll">是否自动滚动</param>
|
||
public void SetAutoScrollToBottom(bool autoScroll)
|
||
{
|
||
autoScrollToBottom = autoScroll;
|
||
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: 设置自动滚动: {autoScroll}");
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取当前字符数
|
||
/// </summary>
|
||
/// <returns>当前字符数</returns>
|
||
public int GetCharacterCount()
|
||
{
|
||
return inputField?.text?.Length ?? 0;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 检查是否达到字符限制
|
||
/// </summary>
|
||
/// <returns>如果达到限制返回true</returns>
|
||
public bool IsAtCharacterLimit()
|
||
{
|
||
if (maxCharacterLimit <= 0) return false;
|
||
return GetCharacterCount() >= maxCharacterLimit;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置InputField是否可交互
|
||
/// </summary>
|
||
/// <param name="interactable">是否可交互</param>
|
||
public void SetInteractable(bool interactable)
|
||
{
|
||
if (inputField != null)
|
||
{
|
||
inputField.interactable = interactable;
|
||
|
||
if (enableDebugLog)
|
||
{
|
||
Debug.Log($"ScrollableInputField: 设置可交互状态: {interactable}");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取InputField是否可交互
|
||
/// </summary>
|
||
/// <returns>是否可交互</returns>
|
||
public bool IsInteractable()
|
||
{
|
||
return inputField?.interactable ?? false;
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
/// <summary>
|
||
/// 在Inspector中显示调试信息
|
||
/// </summary>
|
||
[ContextMenu("显示调试信息")]
|
||
private void ShowDebugInfo()
|
||
{
|
||
Debug.Log($"ScrollableInputField 调试信息:\n" +
|
||
$"物体名称: {gameObject.name}\n" +
|
||
$"InputField: {(inputField != null ? "已找到" : "未找到")}\n" +
|
||
$"ScrollRect: {(scrollRect != null ? "已找到" : "未找到")}\n" +
|
||
$"复制按钮: {(copyButton != null ? "已设置" : "未设置")}\n" +
|
||
$"当前文本长度: {GetCharacterCount()}\n" +
|
||
$"字符限制: {maxCharacterLimit}\n" +
|
||
$"自动滚动: {autoScrollToBottom}\n" +
|
||
$"可交互: {IsInteractable()}");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在Inspector中测试复制功能
|
||
/// </summary>
|
||
[ContextMenu("测试复制")]
|
||
private void TestCopy()
|
||
{
|
||
CopyTextToClipboard();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在Inspector中测试滚动到底部
|
||
/// </summary>
|
||
[ContextMenu("滚动到底部")]
|
||
private void TestScrollToBottom()
|
||
{
|
||
ScrollToBottom();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在Inspector中测试滚动到顶部
|
||
/// </summary>
|
||
[ContextMenu("滚动到顶部")]
|
||
private void TestScrollToTop()
|
||
{
|
||
ScrollToTop();
|
||
}
|
||
#endif
|
||
|
||
/// <summary>
|
||
/// 组件销毁时清理事件监听
|
||
/// </summary>
|
||
private void OnDestroy()
|
||
{
|
||
if (inputField != null)
|
||
{
|
||
inputField.onValueChanged.RemoveListener(OnInputFieldTextChanged);
|
||
inputField.onEndEdit.RemoveListener(OnInputFieldEndEdit);
|
||
}
|
||
|
||
if (copyButton != null)
|
||
{
|
||
copyButton.onClick.RemoveListener(CopyTextToClipboard);
|
||
}
|
||
}
|
||
}
|