507 lines
18 KiB
C#
507 lines
18 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using TMPro;
|
|||
|
|
using UnityEngine;
|
|||
|
|
using UnityEngine.UI;
|
|||
|
|
using UnityEngine.EventSystems;
|
|||
|
|
|
|||
|
|
public enum KeyboardLayout
|
|||
|
|
{
|
|||
|
|
Default,
|
|||
|
|
Numeric,
|
|||
|
|
Hex,
|
|||
|
|
Pinyin // 拼音键盘(支持中文输入)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 动态生成自定义键盘并将按键事件转发给 InputDialog
|
|||
|
|
/// 用法:把该脚本挂在 CustomKeyboardPanel 上,面板内部应包含一个用于放按键的容器(可不设置则使用自身)。
|
|||
|
|
/// </summary>
|
|||
|
|
public class CustomKeyboard : MonoBehaviour
|
|||
|
|
{
|
|||
|
|
public Transform KeysContainer; // 按键放置容器
|
|||
|
|
[Tooltip("如果设置了 KeyPrefab,则在运行时使用该 prefab 来生成按键;如果为空,脚本会创建简单的 GameObject(仅在运行时)。")]
|
|||
|
|
public GameObject KeyPrefab;
|
|||
|
|
public Vector2 KeySize = new Vector2(60, 60);
|
|||
|
|
public Vector2 KeySpacing = new Vector2(6, 6);
|
|||
|
|
|
|||
|
|
private object _target; // 可以是 InputDialog 或 KeyboardInputAdapter
|
|||
|
|
private KeyboardLayout _layout = KeyboardLayout.Default;
|
|||
|
|
private bool _isPasswordMode = false;
|
|||
|
|
private bool _isUpperCase = false; // 拼音键盘大小写切换
|
|||
|
|
|
|||
|
|
private GridLayoutGroup _grid;
|
|||
|
|
|
|||
|
|
// 拼音输入相关
|
|||
|
|
private string _pinyinBuffer = ""; // 拼音缓冲区
|
|||
|
|
private Transform _candidateContainer; // 候选字容器
|
|||
|
|
public Action OnKeyboardSizeChanged; // 键盘大小变化回调
|
|||
|
|
|
|||
|
|
// 翻页相关
|
|||
|
|
private int _currentPage = 0; // 当前页码(0基索引)
|
|||
|
|
private const int _candidatesPerPage = 7; // 每页显示的候选字数量
|
|||
|
|
private string[] _allCandidates = null; // 当前拼音的所有候选字
|
|||
|
|
|
|||
|
|
void Awake()
|
|||
|
|
{
|
|||
|
|
// 如果 KeysContainer 未设置,尝试查找;否则 InputDialog.CreateDynamicKeyboard() 已设置
|
|||
|
|
if (KeysContainer == null)
|
|||
|
|
{
|
|||
|
|
var child = this.transform.Find("KeysContainer");
|
|||
|
|
if (child != null)
|
|||
|
|
KeysContainer = child;
|
|||
|
|
else
|
|||
|
|
KeysContainer = this.transform;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_grid = KeysContainer.GetComponent<GridLayoutGroup>();
|
|||
|
|
// 如果容器本身没有 GridLayoutGroup 则添加一个(仅在这里需要,动态创建时已添加)
|
|||
|
|
if (_grid == null)
|
|||
|
|
{
|
|||
|
|
_grid = KeysContainer.gameObject.AddComponent<GridLayoutGroup>();
|
|||
|
|
_grid.cellSize = KeySize;
|
|||
|
|
_grid.spacing = KeySpacing;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void SetTarget(object target, KeyboardLayout layout, bool isPassword)
|
|||
|
|
{
|
|||
|
|
_target = target;
|
|||
|
|
_layout = layout;
|
|||
|
|
_isPasswordMode = isPassword;
|
|||
|
|
_isUpperCase = false;
|
|||
|
|
_pinyinBuffer = "";
|
|||
|
|
RebuildKeys();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public void RebuildKeys()
|
|||
|
|
{
|
|||
|
|
// 注意: 修改 Prefab Asset(例如在 Project 视图中打开的 Prefab)时,
|
|||
|
|
// 直接对其 Transform 做 SetParent/Destroy 会导致 Unity 抛出错误:
|
|||
|
|
// "Setting the parent of a transform which resides in a Prefab Asset is disabled to prevent data corruption"
|
|||
|
|
// 因此此方法只会在运行时(Play mode)修改场景实例的子对象。
|
|||
|
|
if (!Application.isPlaying)
|
|||
|
|
{
|
|||
|
|
Debug.Log("RebuildKeys skipped: only runs in Play mode to avoid editing prefab assets. For editing prefab assets use the Editor -> Prefab editing workflow.");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 清除已有按键(运行时安全)
|
|||
|
|
for (int i = KeysContainer.childCount - 1; i >= 0; --i)
|
|||
|
|
{
|
|||
|
|
var child = KeysContainer.GetChild(i);
|
|||
|
|
if (Application.isPlaying) Destroy(child.gameObject);
|
|||
|
|
else DestroyImmediate(child.gameObject);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch (_layout)
|
|||
|
|
{
|
|||
|
|
case KeyboardLayout.Numeric:
|
|||
|
|
BuildNumeric();
|
|||
|
|
break;
|
|||
|
|
case KeyboardLayout.Hex:
|
|||
|
|
BuildHex();
|
|||
|
|
break;
|
|||
|
|
case KeyboardLayout.Pinyin:
|
|||
|
|
BuildPinyin();
|
|||
|
|
break;
|
|||
|
|
default:
|
|||
|
|
BuildDefault();
|
|||
|
|
break;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// add control row: Clear Backspace Confirm
|
|||
|
|
BuildControlRow();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void BuildKey(string label, Action onClick)
|
|||
|
|
{
|
|||
|
|
// 优先使用 KeyPrefab(需在 Inspector 中赋值),在运行时用 Instantiate 创建实例并设置父物体——这是安全的。
|
|||
|
|
GameObject go = null;
|
|||
|
|
if (KeyPrefab != null)
|
|||
|
|
{
|
|||
|
|
go = Instantiate(KeyPrefab, KeysContainer, false);
|
|||
|
|
go.name = "Key_" + label;
|
|||
|
|
// 如果 prefab 没有自动设置文本,需要查找并设置
|
|||
|
|
var tmp = go.GetComponentInChildren<TextMeshProUGUI>();
|
|||
|
|
if (tmp != null) tmp.text = label;
|
|||
|
|
|
|||
|
|
var btn = go.GetComponent<Button>();
|
|||
|
|
if (btn != null)
|
|||
|
|
{
|
|||
|
|
btn.onClick.RemoveAllListeners();
|
|||
|
|
btn.onClick.AddListener(() => onClick?.Invoke());
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 只有在运行时才创建临时 GameObject;编辑模式下请勿调用此逻辑以避免修改 Prefab Asset
|
|||
|
|
if (!Application.isPlaying)
|
|||
|
|
{
|
|||
|
|
Debug.LogWarning("Cannot create key GameObject in edit mode without KeyPrefab. Assign KeyPrefab or run in Play mode.");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
go = new GameObject("Key_" + label, typeof(RectTransform), typeof(Image), typeof(Button));
|
|||
|
|
go.transform.SetParent(KeysContainer, false);
|
|||
|
|
var img = go.GetComponent<Image>();
|
|||
|
|
img.color = new Color(0.15f, 0.17f, 0.2f, 1f);
|
|||
|
|
|
|||
|
|
var btn = go.GetComponent<Button>();
|
|||
|
|
btn.onClick.AddListener(() => onClick?.Invoke());
|
|||
|
|
|
|||
|
|
var txtGO = new GameObject("Label", typeof(RectTransform));
|
|||
|
|
txtGO.transform.SetParent(go.transform, false);
|
|||
|
|
var txt = txtGO.AddComponent<TextMeshProUGUI>();
|
|||
|
|
txt.text = label;
|
|||
|
|
txt.alignment = TextAlignmentOptions.Center;
|
|||
|
|
txt.color = Color.white;
|
|||
|
|
txt.fontSize = 24;
|
|||
|
|
var rt = txt.GetComponent<RectTransform>();
|
|||
|
|
rt.anchorMin = Vector2.zero;
|
|||
|
|
rt.anchorMax = Vector2.one;
|
|||
|
|
rt.offsetMin = Vector2.zero;
|
|||
|
|
rt.offsetMax = Vector2.zero;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void BuildNumeric()
|
|||
|
|
{
|
|||
|
|
string[] keys = new string[] { "1","2","3","4","5","6","7","8","9","0",".","+" };
|
|||
|
|
foreach (var k in keys)
|
|||
|
|
{
|
|||
|
|
BuildKey(k, () => CallOnKeyPressed(k));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
private void BuildPinyin()
|
|||
|
|
{
|
|||
|
|
// 拼音键盘 - 26个字母用于拼音输入
|
|||
|
|
string[] letters = new string[] { "q","w","e","r","t","y","u","i","o","p","a","s","d","f","g","h","j","k","l","z","x","c","v","b","n","m" };
|
|||
|
|
|
|||
|
|
foreach (var letter in letters)
|
|||
|
|
{
|
|||
|
|
BuildKey(letter, () => OnPinyinLetterPressed(letter));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加空格键(选择第一个候选字)
|
|||
|
|
BuildKey("空格", () => SelectCandidate(0));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 拼音字母按下
|
|||
|
|
/// </summary>
|
|||
|
|
private void OnPinyinLetterPressed(string letter)
|
|||
|
|
{
|
|||
|
|
_pinyinBuffer += letter;
|
|||
|
|
UpdatePinyinCandidates();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 更新拼音候选字
|
|||
|
|
/// </summary>
|
|||
|
|
private void UpdatePinyinCandidates()
|
|||
|
|
{
|
|||
|
|
// 清除旧的候选字
|
|||
|
|
if (_candidateContainer != null)
|
|||
|
|
{
|
|||
|
|
for (int i = _candidateContainer.childCount - 1; i >= 0; --i)
|
|||
|
|
{
|
|||
|
|
if (Application.isPlaying)
|
|||
|
|
Destroy(_candidateContainer.GetChild(i).gameObject);
|
|||
|
|
else
|
|||
|
|
DestroyImmediate(_candidateContainer.GetChild(i).gameObject);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 创建候选字容器(在控制按钮上方)
|
|||
|
|
var candidateGO = new GameObject("CandidateContainer", typeof(RectTransform));
|
|||
|
|
candidateGO.transform.SetParent(KeysContainer.parent, false);
|
|||
|
|
_candidateContainer = candidateGO.transform;
|
|||
|
|
|
|||
|
|
var candidateRT = candidateGO.GetComponent<RectTransform>();
|
|||
|
|
candidateRT.anchorMin = new Vector2(0, 1);
|
|||
|
|
candidateRT.anchorMax = new Vector2(1, 1);
|
|||
|
|
candidateRT.pivot = new Vector2(0.5f, 1);
|
|||
|
|
candidateRT.anchoredPosition = new Vector2(0, 50);
|
|||
|
|
candidateRT.sizeDelta = new Vector2(0, 60);
|
|||
|
|
|
|||
|
|
// 添加背景
|
|||
|
|
var bg = candidateGO.AddComponent<Image>();
|
|||
|
|
bg.color = new Color(0.12f, 0.14f, 0.16f, 1f);
|
|||
|
|
|
|||
|
|
// 添加水平布局
|
|||
|
|
var layout = candidateGO.AddComponent<HorizontalLayoutGroup>();
|
|||
|
|
layout.spacing = 5;
|
|||
|
|
layout.padding = new RectOffset(10, 10, 5, 5);
|
|||
|
|
layout.childControlWidth = false;
|
|||
|
|
layout.childControlHeight = true;
|
|||
|
|
layout.childForceExpandWidth = false;
|
|||
|
|
layout.childForceExpandHeight = true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 显示拼音缓冲区
|
|||
|
|
if (!string.IsNullOrEmpty(_pinyinBuffer))
|
|||
|
|
{
|
|||
|
|
// 获取候选汉字
|
|||
|
|
_allCandidates = PinyinInputManager.GetCandidates(_pinyinBuffer);
|
|||
|
|
|
|||
|
|
if (_allCandidates != null && _allCandidates.Length > 0)
|
|||
|
|
{
|
|||
|
|
// 计算总页数
|
|||
|
|
int totalPages = Mathf.CeilToInt((float)_allCandidates.Length / _candidatesPerPage);
|
|||
|
|
|
|||
|
|
// 确保页码在有效范围内
|
|||
|
|
_currentPage = Mathf.Clamp(_currentPage, 0, totalPages - 1);
|
|||
|
|
|
|||
|
|
CreateCandidateText($"拼音: {_pinyinBuffer} ({_currentPage + 1}/{totalPages})", () => {});
|
|||
|
|
|
|||
|
|
// 添加上一页按钮
|
|||
|
|
// if (_currentPage > 0)
|
|||
|
|
// {
|
|||
|
|
CreateCandidateButton("← 上页", () => {
|
|||
|
|
if (_currentPage > 0)
|
|||
|
|
{
|
|||
|
|
_currentPage--;
|
|||
|
|
UpdatePinyinCandidates();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
// 计算当前页的候选字范围
|
|||
|
|
int startIndex = _currentPage * _candidatesPerPage;
|
|||
|
|
int endIndex = Mathf.Min(startIndex + _candidatesPerPage, _allCandidates.Length);
|
|||
|
|
|
|||
|
|
// 显示当前页的候选字
|
|||
|
|
for (int i = startIndex; i < endIndex; i++)
|
|||
|
|
{
|
|||
|
|
int globalIndex = i; // 全局索引
|
|||
|
|
int displayNumber = (i - startIndex) + 1; // 显示编号1-8
|
|||
|
|
string candidate = _allCandidates[i];
|
|||
|
|
CreateCandidateButton($"{displayNumber}.{candidate}", () => SelectCandidateByGlobalIndex(globalIndex));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 添加下一页按钮
|
|||
|
|
if (_currentPage < totalPages - 1)
|
|||
|
|
{
|
|||
|
|
CreateCandidateButton("下页 →", () => {
|
|||
|
|
_currentPage++;
|
|||
|
|
UpdatePinyinCandidates();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}else{
|
|||
|
|
// 显示拼音
|
|||
|
|
CreateCandidateText($"拼音: {_pinyinBuffer} 暂无数据", () => {});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 清空候选字和重置页码
|
|||
|
|
_allCandidates = null;
|
|||
|
|
_currentPage = 0;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 创建候选字文本
|
|||
|
|
/// </summary>
|
|||
|
|
private void CreateCandidateText(string text, Action onClick)
|
|||
|
|
{
|
|||
|
|
var textGO = new GameObject("CandidateText", typeof(RectTransform));
|
|||
|
|
textGO.transform.SetParent(_candidateContainer, false);
|
|||
|
|
|
|||
|
|
var rt = textGO.GetComponent<RectTransform>();
|
|||
|
|
rt.sizeDelta = new Vector2(150, 50);
|
|||
|
|
|
|||
|
|
var tmp = textGO.AddComponent<TextMeshProUGUI>();
|
|||
|
|
tmp.text = text;
|
|||
|
|
tmp.fontSize = 20;
|
|||
|
|
// 尝试加载中文字体,如果失败则使用默认字体
|
|||
|
|
var chineseFont = Resources.Load<TMP_FontAsset>("Fonts/SIMLI SDF");
|
|||
|
|
if (chineseFont != null)
|
|||
|
|
tmp.font = chineseFont;
|
|||
|
|
tmp.color = Color.cyan;
|
|||
|
|
tmp.alignment = TextAlignmentOptions.MidlineLeft;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 创建候选字按钮
|
|||
|
|
/// </summary>
|
|||
|
|
private void CreateCandidateButton(string text, Action onClick)
|
|||
|
|
{
|
|||
|
|
var btnGO = new GameObject("CandidateBtn", typeof(RectTransform), typeof(Image), typeof(Button));
|
|||
|
|
btnGO.transform.SetParent(_candidateContainer, false);
|
|||
|
|
|
|||
|
|
var rt = btnGO.GetComponent<RectTransform>();
|
|||
|
|
// 根据文本长度调整宽度(翻页按钮需要更宽)
|
|||
|
|
float width = text.Contains("上页") || text.Contains("下页") ? 80 : 70;
|
|||
|
|
rt.sizeDelta = new Vector2(width, 50);
|
|||
|
|
|
|||
|
|
var img = btnGO.GetComponent<Image>();
|
|||
|
|
img.color = new Color(0.2f, 0.25f, 0.3f, 1f);
|
|||
|
|
|
|||
|
|
var btn = btnGO.GetComponent<Button>();
|
|||
|
|
btn.onClick.AddListener(() => onClick?.Invoke());
|
|||
|
|
|
|||
|
|
var txtGO = new GameObject("Label", typeof(RectTransform));
|
|||
|
|
txtGO.transform.SetParent(btnGO.transform, false);
|
|||
|
|
var tmp = txtGO.AddComponent<TextMeshProUGUI>();
|
|||
|
|
tmp.text = text;
|
|||
|
|
// 尝试加载中文字体
|
|||
|
|
var chineseFont = Resources.Load<TMP_FontAsset>("Fonts/SIMLI SDF");
|
|||
|
|
if (chineseFont != null)
|
|||
|
|
tmp.font = chineseFont;
|
|||
|
|
tmp.alignment = TextAlignmentOptions.Center;
|
|||
|
|
tmp.color = Color.white;
|
|||
|
|
tmp.fontSize = 22;
|
|||
|
|
|
|||
|
|
var txtRT = tmp.GetComponent<RectTransform>();
|
|||
|
|
txtRT.anchorMin = Vector2.zero;
|
|||
|
|
txtRT.anchorMax = Vector2.one;
|
|||
|
|
txtRT.offsetMin = Vector2.zero;
|
|||
|
|
txtRT.offsetMax = Vector2.zero;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 选择候选字(通过全局索引)
|
|||
|
|
/// </summary>
|
|||
|
|
private void SelectCandidateByGlobalIndex(int globalIndex)
|
|||
|
|
{
|
|||
|
|
if (_allCandidates != null && globalIndex >= 0 && globalIndex < _allCandidates.Length)
|
|||
|
|
{
|
|||
|
|
// 输入选中的汉字
|
|||
|
|
CallOnKeyPressed(_allCandidates[globalIndex]);
|
|||
|
|
|
|||
|
|
// 清空拼音缓冲区和重置页码
|
|||
|
|
_pinyinBuffer = "";
|
|||
|
|
_allCandidates = null;
|
|||
|
|
_currentPage = 0;
|
|||
|
|
UpdatePinyinCandidates();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 选择候选字(用于空格键,选择第一个)
|
|||
|
|
/// </summary>
|
|||
|
|
private void SelectCandidate(int relativeIndex)
|
|||
|
|
{
|
|||
|
|
if (string.IsNullOrEmpty(_pinyinBuffer))
|
|||
|
|
{
|
|||
|
|
// 如果没有拼音缓冲,空格键输入空格
|
|||
|
|
CallOnKeyPressed(" ");
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 计算全局索引(当前页的第一个 + 相对索引)
|
|||
|
|
int globalIndex = _currentPage * _candidatesPerPage + relativeIndex;
|
|||
|
|
SelectCandidateByGlobalIndex(globalIndex);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void BuildHex()
|
|||
|
|
{
|
|||
|
|
string[] keys = new string[] { "0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F" };
|
|||
|
|
foreach (var k in keys) BuildKey(k, () => CallOnKeyPressed(k));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void BuildDefault()
|
|||
|
|
{
|
|||
|
|
// 简易默认键盘包含数字和常用符号
|
|||
|
|
string[] keys = new string[] {
|
|||
|
|
"1","2","3","4","5","6","7","8","9","0",
|
|||
|
|
"q","w","e","r","t","y","u","i","o","p",
|
|||
|
|
"a","s","d","f","g","h","j","k","l",
|
|||
|
|
"z","x","c","v","b","n","m",
|
|||
|
|
"-","_","@" };
|
|||
|
|
foreach (var k in keys) BuildKey(k, () => CallOnKeyPressed(k));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void BuildControlRow()
|
|||
|
|
{
|
|||
|
|
// Add Clear
|
|||
|
|
BuildKey("Clear", () => {
|
|||
|
|
if (_layout == KeyboardLayout.Pinyin)
|
|||
|
|
{
|
|||
|
|
_pinyinBuffer = "";
|
|||
|
|
UpdatePinyinCandidates();
|
|||
|
|
}
|
|||
|
|
CallOnClearPressed();
|
|||
|
|
});
|
|||
|
|
// Backspace
|
|||
|
|
BuildKey("←", () => {
|
|||
|
|
if (_layout == KeyboardLayout.Pinyin && !string.IsNullOrEmpty(_pinyinBuffer))
|
|||
|
|
{
|
|||
|
|
_pinyinBuffer = _pinyinBuffer.Substring(0, _pinyinBuffer.Length - 1);
|
|||
|
|
UpdatePinyinCandidates();
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
CallOnBackspacePressed();
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
// Confirm -> trigger confirm button and close keyboard
|
|||
|
|
BuildKey("OK", () => {
|
|||
|
|
CallConfirmButton();
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Helper methods to call target methods safely using reflection or direct cast
|
|||
|
|
private void CallOnKeyPressed(string key)
|
|||
|
|
{
|
|||
|
|
if (_target == null) return;
|
|||
|
|
|
|||
|
|
var method = _target.GetType().GetMethod("OnKeyPressed");
|
|||
|
|
if (method != null)
|
|||
|
|
{
|
|||
|
|
method.Invoke(_target, new object[] { key });
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void CallOnBackspacePressed()
|
|||
|
|
{
|
|||
|
|
if (_target == null) return;
|
|||
|
|
|
|||
|
|
var method = _target.GetType().GetMethod("OnBackspacePressed");
|
|||
|
|
if (method != null)
|
|||
|
|
{
|
|||
|
|
method.Invoke(_target, null);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void CallOnClearPressed()
|
|||
|
|
{
|
|||
|
|
if (_target == null) return;
|
|||
|
|
|
|||
|
|
var method = _target.GetType().GetMethod("OnClearPressed");
|
|||
|
|
if (method != null)
|
|||
|
|
{
|
|||
|
|
method.Invoke(_target, null);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void CallConfirmButton()
|
|||
|
|
{
|
|||
|
|
if (_target == null) return;
|
|||
|
|
|
|||
|
|
var confirmButtonProp = _target.GetType().GetProperty("ConfirmButton");
|
|||
|
|
if (confirmButtonProp != null)
|
|||
|
|
{
|
|||
|
|
var btn = confirmButtonProp.GetValue(_target) as Button;
|
|||
|
|
if (btn != null)
|
|||
|
|
{
|
|||
|
|
btn.onClick.Invoke();
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 如果没有 ConfirmButton 属性,尝试直接调用管理器的 ConfirmInput
|
|||
|
|
if (CustomKeyboardManager.Instance != null)
|
|||
|
|
{
|
|||
|
|
CustomKeyboardManager.Instance.ConfirmInput();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
}
|