├── README.md └── WebGLInputField ├── Plugins.meta ├── Plugins ├── WebGLInputField.jslib └── WebGLInputField.jslib.meta ├── WebGLInputField.cs ├── WebGLInputField.cs.meta ├── WebGLInputFieldHelper.cs └── WebGLInputFieldHelper.cs.meta /README.md: -------------------------------------------------------------------------------- 1 | # UnityWebGLInputfield 2 | 支持在webgl输入中文 3 | -------------------------------------------------------------------------------- /WebGLInputField/Plugins.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 817d05e37c348394d8c41173525d88e8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /WebGLInputField/Plugins/WebGLInputField.jslib: -------------------------------------------------------------------------------- 1 | var WebGLInputField = { 2 | //打开输入框 3 | ShowInputFieldDialog:function(defaultValue){ 4 | 5 | try { 6 | defaultValue = Pointer_stringify(defaultValue); 7 | } catch (e) { 8 | alert(e); 9 | return; 10 | } 11 | 12 | if(!document.getElementById("nativeInputDialog")) { 13 | var element = document.createElement('div'); 14 | // setup html 15 | var html = '
' + 16 | '' + 17 | '
'; 18 | element.innerHTML = html; 19 | document.body.appendChild(element); 20 | 21 | var m_nativeInputDialogInput = document.getElementById("nativeInputDialogInput"); 22 | m_nativeInputDialogInput.onkeypress = function (event) { 23 | //点击回车键,隐藏输入框 24 | if (event.keyCode == 13) { 25 | document.getElementById("nativeInputDialog").style.display="none"; 26 | } 27 | }; 28 | 29 | document.onmousemove=function(event){ 30 | event = event||window.event; 31 | document.getElementById("nativeInputDialog").style.left = event.clientX + 'px'; 32 | document.getElementById("nativeInputDialog").style.top = event.clientY + 20 + 'px'; 33 | } 34 | } 35 | var m_nativeInputDialog = document.getElementById("nativeInputDialogInput"); 36 | m_nativeInputDialog.value = defaultValue; 37 | document.getElementById("nativeInputDialog").style.display=""; 38 | document.getElementById("nativeInputDialogInput").focus(); 39 | 40 | }, 41 | //隐藏输入框 42 | HideInputFieldDialog :function(){ 43 | document.getElementById("nativeInputDialog").style.display="none"; 44 | }, 45 | IsInputFieldDialogActive:function(){ 46 | var nativeDialog = document.getElementById("nativeInputDialog" ); 47 | if(!nativeDialog ){ 48 | return false; 49 | } 50 | return ( nativeDialog.style.display != 'none' ); 51 | }, 52 | GetInputFieldValue:function(){ 53 | var elem = document.getElementById("nativeInputDialogInput"); 54 | var returnStr = elem.value; 55 | var bufferSize = lengthBytesUTF8(returnStr) + 1; 56 | var buffer = _malloc(bufferSize); 57 | stringToUTF8(returnStr, buffer, bufferSize); 58 | return buffer; 59 | }, 60 | GetInputFieldCursortPosition:function () { 61 | var dialog = document.getElementById("nativeInputDialogInput"); 62 | var index = dialog.selectionStart; 63 | return index; 64 | }, 65 | GetInputFieldCursortFocusPosition:function () { 66 | var dialog = document.getElementById("nativeInputDialogInput"); 67 | var index = dialog.selectionEnd; 68 | return index; 69 | }, 70 | SetInputFieldCursortPosition:function (selectionStart,selectionEnd) { 71 | var elem = document.getElementById("nativeInputDialogInput"); 72 | var val = elem.value 73 | var len = val.length 74 | 75 | // 超过文本长度直接返回 76 | if (len < selectionStart || len < selectionEnd) return; 77 | 78 | setTimeout(function() { 79 | elem.focus() 80 | if (elem.setSelectionRange) { // 标准浏览器 81 | elem.setSelectionRange(selectionStart, selectionEnd); 82 | } 83 | }, 10) 84 | } 85 | 86 | }; 87 | mergeInto(LibraryManager.library , WebGLInputField ); -------------------------------------------------------------------------------- /WebGLInputField/Plugins/WebGLInputField.jslib.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 71bd5924bbc149849866762a69ebaf9a 3 | PluginImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | iconMap: {} 7 | executionOrder: {} 8 | isPreloaded: 0 9 | isOverridable: 0 10 | platformData: 11 | - first: 12 | Any: 13 | second: 14 | enabled: 0 15 | settings: {} 16 | - first: 17 | Editor: Editor 18 | second: 19 | enabled: 0 20 | settings: 21 | DefaultValueInitialized: true 22 | - first: 23 | Facebook: WebGL 24 | second: 25 | enabled: 1 26 | settings: {} 27 | - first: 28 | WebGL: WebGL 29 | second: 30 | enabled: 1 31 | settings: {} 32 | userData: 33 | assetBundleName: 34 | assetBundleVariant: 35 | -------------------------------------------------------------------------------- /WebGLInputField/WebGLInputField.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using UnityEngine; 4 | using UnityEngine.UI; 5 | using UnityEngine.EventSystems; 6 | using System.Runtime.InteropServices; 7 | using System; 8 | 9 | public class WebGLInputField : InputField 10 | { 11 | #if UNITY_WEBGL && !UNITY_EDITOR 12 | [DllImport("__Internal")]//显示对话框 13 | private static extern void ShowInputFieldDialog(string text); 14 | [DllImport("__Internal")]//隐藏对话框 15 | private static extern void HideInputFieldDialog(); 16 | [DllImport("__Internal")]//对话框是否显示中 17 | private static extern bool IsInputFieldDialogActive(); 18 | [DllImport("__Internal")]//获取对话框的数据 19 | private static extern string GetInputFieldValue(); 20 | [DllImport("__Internal")]//获取光标选中坐标(起点点) 21 | private static extern int GetInputFieldCursortPosition(); 22 | [DllImport("__Internal")]//获取光标选中坐标(终点) 23 | private static extern int GetInputFieldCursortFocusPosition(); 24 | [DllImport("__Internal")]//设置光标选择 25 | private static extern void SetInputFieldCursortPosition(int selectionStart, int selectionEnd); 26 | private bool captureAllKeyboardInput 27 | { 28 | get 29 | { 30 | return WebGLInput.captureAllKeyboardInput; 31 | } 32 | set 33 | { 34 | WebGLInput.captureAllKeyboardInput = value; 35 | } 36 | } 37 | private float timer; 38 | private Coroutine overlayhtml; 39 | private Coroutine setposCoroutine; 40 | 41 | public override void OnPointerClick(PointerEventData eventData) 42 | { 43 | base.OnPointerClick(eventData); 44 | 45 | captureAllKeyboardInput = false; 46 | 47 | ShowInputFieldDialog(text); 48 | 49 | if (IsInputFieldDialogActive() && overlayhtml != null) 50 | { 51 | //更新光标 52 | if(setposCoroutine != null) 53 | { 54 | SetSelection(); 55 | } 56 | else 57 | { 58 | setposCoroutine = StartCoroutine(DelySetPostion()); 59 | } 60 | } 61 | else 62 | { 63 | //打开html端的输入框 64 | overlayhtml = StartCoroutine(this.OverlayHtmlCoroutine()); 65 | } 66 | } 67 | 68 | private IEnumerator DelySetPostion() 69 | { 70 | captureAllKeyboardInput = true; 71 | yield return null; 72 | SetSelection(); 73 | captureAllKeyboardInput = false; 74 | setposCoroutine = null; 75 | System.GC.Collect(); 76 | } 77 | 78 | 79 | 80 | private IEnumerator OverlayHtmlCoroutine() 81 | { 82 | yield return DelySetPostion(); 83 | //设置选中对象为 84 | while (IsInputFieldDialogActive() && isFocused) 85 | { 86 | yield return null; 87 | var textFromHtml = GetInputFieldValue(); 88 | if (textFromHtml != this.text) 89 | { 90 | this.text = textFromHtml; 91 | ForceLabelUpdate(); 92 | yield return null; 93 | } 94 | 95 | if (!captureAllKeyboardInput && setposCoroutine == null && !Input.GetMouseButton(0)) 96 | { 97 | UpdateCaretPositions(); 98 | yield return null; 99 | } 100 | } 101 | HideInputFieldDialog(); 102 | EventSystem.current.SetSelectedGameObject(null); 103 | captureAllKeyboardInput = true; 104 | overlayhtml = null; 105 | System.GC.Collect(); 106 | } 107 | 108 | /// 109 | /// 设置选中区域 110 | /// 111 | private void SetSelection() 112 | { 113 | var selectionStart = selectionAnchorPosition < selectionFocusPosition ? selectionAnchorPosition : selectionFocusPosition; 114 | var selectionEnd = selectionAnchorPosition > selectionFocusPosition ? selectionAnchorPosition : selectionFocusPosition; 115 | SetInputFieldCursortPosition(selectionStart, selectionEnd); 116 | } 117 | 118 | /// 119 | /// 从html更新caretPosition 120 | /// 121 | private void UpdateCaretPositions() 122 | { 123 | var cpos = GetInputFieldCursortPosition(); 124 | var fpos = GetInputFieldCursortFocusPosition(); 125 | var changed = false; 126 | if (cpos != caretPosition) 127 | { 128 | caretPosition = cpos; 129 | changed = true; 130 | } 131 | if (fpos != selectionFocusPosition) 132 | { 133 | selectionFocusPosition = fpos; 134 | changed = true; 135 | } 136 | 137 | if (changed) 138 | { 139 | ForceLabelUpdate(); 140 | } 141 | } 142 | 143 | 144 | #endif 145 | //注意Time.timeScale = 0 会无法更新信息 146 | //private void Update() 147 | //{ 148 | // if(Input.GetMouseButtonDown(1) && isFocused) 149 | // { 150 | // Debug.Log("caretPosition:" + caretPosition);//光标坐标 151 | // Debug.Log("currentSelectionState:" + currentSelectionState);//选中状态 152 | // Debug.Log("selectionAnchorPosition:" + selectionAnchorPosition);//选择起点 153 | // Debug.Log("selectionFocusPosition:" + selectionFocusPosition);//选择结束点 154 | // } 155 | //} 156 | } -------------------------------------------------------------------------------- /WebGLInputField/WebGLInputField.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38b726831cb5bdd4981bb7b9961d4957 3 | timeCreated: 1531893617 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /WebGLInputField/WebGLInputFieldHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | using UnityEngine; 5 | using UnityEngine.EventSystems; 6 | using UnityEngine.UI; 7 | 8 | //可以直接拖到inputfield上 对原项目无影响 9 | public class WebGLInputFieldHelper : MonoBehaviour, IPointerClickHandler 10 | { 11 | private InputField inputField; 12 | private string text; 13 | private bool isFocused; 14 | 15 | 16 | //#if UNITY_WEBGL && !UNITY_EDITOR 17 | [DllImport("__Internal")]//显示对话框 18 | private static extern void ShowInputFieldDialog(string text); 19 | [DllImport("__Internal")]//隐藏对话框 20 | private static extern void HideInputFieldDialog(); 21 | [DllImport("__Internal")]//对话框是否显示中 22 | private static extern bool IsInputFieldDialogActive(); 23 | [DllImport("__Internal")]//获取对话框的数据 24 | private static extern string GetInputFieldValue(); 25 | [DllImport("__Internal")]//获取光标选中坐标(起点点) 26 | private static extern int GetInputFieldCursortPosition(); 27 | [DllImport("__Internal")]//获取光标选中坐标(终点) 28 | private static extern int GetInputFieldCursortFocusPosition(); 29 | [DllImport("__Internal")]//设置光标选择 30 | private static extern void SetInputFieldCursortPosition(int selectionStart, int selectionEnd); 31 | 32 | private bool captureAllKeyboardInput 33 | { 34 | get 35 | { 36 | return WebGLInput.captureAllKeyboardInput; 37 | } 38 | set 39 | { 40 | WebGLInput.captureAllKeyboardInput = value; 41 | } 42 | } 43 | private float timer; 44 | private Coroutine overlayhtml; 45 | private Coroutine setposCoroutine; 46 | 47 | void Start() 48 | { 49 | inputField = GetComponent(); 50 | text = inputField.text; 51 | isFocused = inputField.isFocused; 52 | } 53 | 54 | public void OnPointerClick(PointerEventData eventData) 55 | { 56 | 57 | captureAllKeyboardInput = false; 58 | 59 | ShowInputFieldDialog(text); 60 | 61 | if (IsInputFieldDialogActive() && overlayhtml != null) 62 | { 63 | //更新光标 64 | if(setposCoroutine != null) 65 | { 66 | SetSelection(); 67 | } 68 | else 69 | { 70 | setposCoroutine = StartCoroutine(DelySetPostion()); 71 | } 72 | } 73 | else 74 | { 75 | //打开html端的输入框 76 | overlayhtml = StartCoroutine(this.OverlayHtmlCoroutine()); 77 | } 78 | } 79 | 80 | private IEnumerator DelySetPostion() 81 | { 82 | captureAllKeyboardInput = true; 83 | yield return null; 84 | SetSelection(); 85 | captureAllKeyboardInput = false; 86 | setposCoroutine = null; 87 | System.GC.Collect(); 88 | } 89 | 90 | private IEnumerator OverlayHtmlCoroutine() 91 | { 92 | yield return DelySetPostion(); 93 | //设置选中对象为 94 | while (IsInputFieldDialogActive() && isFocused) 95 | { 96 | yield return null; 97 | var textFromHtml = GetInputFieldValue(); 98 | if (textFromHtml != this.text) 99 | { 100 | this.text = textFromHtml; 101 | inputField.ForceLabelUpdate(); 102 | yield return null; 103 | } 104 | 105 | if (!captureAllKeyboardInput && setposCoroutine == null && !Input.GetMouseButton(0)) 106 | { 107 | UpdateCaretPositions(); 108 | yield return null; 109 | } 110 | } 111 | HideInputFieldDialog(); 112 | EventSystem.current.SetSelectedGameObject(null); 113 | captureAllKeyboardInput = true; 114 | overlayhtml = null; 115 | System.GC.Collect(); 116 | } 117 | 118 | /// 119 | /// 设置选中区域 120 | /// 121 | private void SetSelection() 122 | { 123 | var selectionStart =inputField.selectionAnchorPosition < inputField.selectionFocusPosition ? inputField.selectionAnchorPosition : inputField.selectionFocusPosition; 124 | var selectionEnd = inputField.selectionAnchorPosition > inputField.selectionFocusPosition ? inputField.selectionAnchorPosition : inputField.selectionFocusPosition; 125 | SetInputFieldCursortPosition(selectionStart, selectionEnd); 126 | } 127 | 128 | /// 129 | /// 从html更新caretPosition 130 | /// 131 | private void UpdateCaretPositions() 132 | { 133 | var cpos = GetInputFieldCursortPosition(); 134 | var fpos = GetInputFieldCursortFocusPosition(); 135 | var changed = false; 136 | if (cpos != inputField.caretPosition) 137 | { 138 | inputField.caretPosition = cpos; 139 | changed = true; 140 | } 141 | if (fpos != inputField.selectionFocusPosition) 142 | { 143 | inputField.selectionFocusPosition = fpos; 144 | changed = true; 145 | } 146 | 147 | if (changed) 148 | { 149 | inputField.ForceLabelUpdate(); 150 | } 151 | } 152 | 153 | //#endif 154 | } 155 | -------------------------------------------------------------------------------- /WebGLInputField/WebGLInputFieldHelper.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b9a89c14dfba72c40b88ab7d3155335c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | --------------------------------------------------------------------------------