Tanoda
WebGLInput.cs
Go to the documentation of this file.
1#if UNITY_2018_2_OR_NEWER
2#define TMP_WEBGL_SUPPORT
3#endif
4
5using System.Collections.Generic;
6using UnityEngine;
7using UnityEngine.UI;
8using System;
9using AOT;
10using System.Runtime.InteropServices; // for DllImport
11using System.Collections;
12
13namespace WebGLSupport
14{
15 internal class WebGLInputPlugin
16 {
17#if UNITY_WEBGL && !UNITY_EDITOR
18 [DllImport("__Internal")]
19 public static extern int WebGLInputCreate(string canvasId, int x, int y, int width, int height, int fontsize, string text, string placeholder, bool isMultiLine, bool isPassword, bool isHidden);
20
21 [DllImport("__Internal")]
22 public static extern void WebGLInputEnterSubmit(int id, bool flag);
23
24 [DllImport("__Internal")]
25 public static extern void WebGLInputTab(int id, Action<int, int> cb);
26
27 [DllImport("__Internal")]
28 public static extern void WebGLInputFocus(int id);
29
30 [DllImport("__Internal")]
31 public static extern void WebGLInputOnFocus(int id, Action<int> cb);
32
33 [DllImport("__Internal")]
34 public static extern void WebGLInputOnBlur(int id, Action<int> cb);
35
36 [DllImport("__Internal")]
37 public static extern void WebGLInputOnValueChange(int id, Action<int, string> cb);
38
39 [DllImport("__Internal")]
40 public static extern void WebGLInputOnEditEnd(int id, Action<int, string> cb);
41
42 [DllImport("__Internal")]
43 public static extern int WebGLInputSelectionStart(int id);
44
45 [DllImport("__Internal")]
46 public static extern int WebGLInputSelectionEnd(int id);
47
48 [DllImport("__Internal")]
49 public static extern int WebGLInputSelectionDirection(int id);
50
51 [DllImport("__Internal")]
52 public static extern void WebGLInputSetSelectionRange(int id, int start, int end);
53
54 [DllImport("__Internal")]
55 public static extern void WebGLInputMaxLength(int id, int maxlength);
56
57 [DllImport("__Internal")]
58 public static extern void WebGLInputText(int id, string text);
59
60 [DllImport("__Internal")]
61 public static extern bool WebGLInputIsFocus(int id);
62
63 [DllImport("__Internal")]
64 public static extern void WebGLInputDelete(int id);
65
66#if WEBGLINPUT_TAB
67 [DllImport("__Internal")]
68 public static extern void WebGLInputEnableTabText(int id, bool enable);
69#endif
70#else
71
72 public static int WebGLInputCreate(string canvasId, int x, int y, int width, int height, int fontsize, string text, string placeholder, bool isMultiLine, bool isPassword, bool isHidden) { return 0; }
73 public static void WebGLInputEnterSubmit(int id, bool flag) { }
74 public static void WebGLInputTab(int id, Action<int, int> cb) { }
75 public static void WebGLInputFocus(int id) { }
76 public static void WebGLInputOnFocus(int id, Action<int> cb) { }
77 public static void WebGLInputOnBlur(int id, Action<int> cb) { }
78 public static void WebGLInputOnValueChange(int id, Action<int, string> cb) { }
79 public static void WebGLInputOnEditEnd(int id, Action<int, string> cb) { }
80 public static int WebGLInputSelectionStart(int id) { return 0; }
81 public static int WebGLInputSelectionEnd(int id) { return 0; }
82 public static int WebGLInputSelectionDirection(int id) { return 0; }
83 public static void WebGLInputSetSelectionRange(int id, int start, int end) { }
84 public static void WebGLInputMaxLength(int id, int maxlength) { }
85 public static void WebGLInputText(int id, string text) { }
86 public static bool WebGLInputIsFocus(int id) { return false; }
87 public static void WebGLInputDelete(int id) { }
88
89#if WEBGLINPUT_TAB
90 public static void WebGLInputEnableTabText(int id, bool enable) { }
91#endif
92
93#endif
94 }
95
96 public class WebGLInput : MonoBehaviour, IComparable<WebGLInput>
97 {
98 static Dictionary<int, WebGLInput> instances = new Dictionary<int, WebGLInput>();
99 public static string CanvasId { get; set; }
100
101#if WEBGLINPUT_TAB
102 public bool enableTabText = false;
103#endif
104
105 static WebGLInput()
106 {
107#if UNITY_2020_1_OR_NEWER
108 WebGLInput.CanvasId = "unity-container";
109#elif UNITY_2019_1_OR_NEWER
110 WebGLInput.CanvasId = "unityContainer";
111#else
112 WebGLInput.CanvasId = "gameContainer";
113#endif
114 }
115
116 internal int id = -1;
117 IInputField input;
118 bool blurBlock = false;
119
120 [TooltipAttribute("show input element on canvas. this will make you select text by drag.")]
121 public bool showHtmlElement = false;
122
123 private IInputField Setup()
124 {
125 if (GetComponent<InputField>()) return new WrappedInputField(GetComponent<InputField>());
126#if TMP_WEBGL_SUPPORT
127 if (GetComponent<TMPro.TMP_InputField>()) return new WrappedTMPInputField(GetComponent<TMPro.TMP_InputField>());
128#endif // TMP_WEBGL_SUPPORT
129 throw new Exception("Can not Setup WebGLInput!!");
130 }
131
132 private void Awake()
133 {
134 input = Setup();
135#if !(UNITY_WEBGL && !UNITY_EDITOR)
136 // WebGL 以外、更新メソッドは動作しないようにします
137 enabled = false;
138#endif
139 // モバイルの入力対応
140 if (Application.isMobilePlatform)
141 {
142 gameObject.AddComponent<WebGLInputMobile>();
143 }
144 }
145
150 public void OnSelect()
151 {
152 var rect = GetScreenCoordinates(input.RectTransform());
153 bool isPassword = input.contentType == ContentType.Password;
154
155 var fontSize = Mathf.Max(14, input.fontSize); // limit font size : 14 !!
156
157 // モバイルの場合、強制表示する
158 if (showHtmlElement || Application.isMobilePlatform)
159 {
160 var x = (int)(rect.x);
161 var y = (int)(Screen.height - (rect.y + rect.height));
162 id = WebGLInputPlugin.WebGLInputCreate(WebGLInput.CanvasId, x, y, (int)rect.width, (int)rect.height, fontSize, input.text, input.placeholder, input.lineType != LineType.SingleLine, isPassword, false);
163 }
164 else
165 {
166 var x = (int)(rect.x);
167 var y = (int)(Screen.height - (rect.y));
168 id = WebGLInputPlugin.WebGLInputCreate(WebGLInput.CanvasId, x, y, (int)rect.width, (int)1, fontSize, input.text, input.placeholder, input.lineType != LineType.SingleLine, isPassword, true);
169 }
170
171 instances[id] = this;
172 WebGLInputPlugin.WebGLInputEnterSubmit(id, input.lineType != LineType.MultiLineNewline);
173 WebGLInputPlugin.WebGLInputOnFocus(id, OnFocus);
174 WebGLInputPlugin.WebGLInputOnBlur(id, OnBlur);
175 WebGLInputPlugin.WebGLInputOnValueChange(id, OnValueChange);
176 WebGLInputPlugin.WebGLInputOnEditEnd(id, OnEditEnd);
177 WebGLInputPlugin.WebGLInputTab(id, OnTab);
178 // default value : https://www.w3schools.com/tags/att_input_maxlength.asp
179 WebGLInputPlugin.WebGLInputMaxLength(id, (input.characterLimit > 0) ? input.characterLimit : 524288);
180 WebGLInputPlugin.WebGLInputFocus(id);
181#if WEBGLINPUT_TAB
182 WebGLInputPlugin.WebGLInputEnableTabText(id, enableTabText);
183#endif
184 if (input.OnFocusSelectAll)
185 {
186 WebGLInputPlugin.WebGLInputSetSelectionRange(id, 0, input.text.Length);
187 }
188
189 WebGLWindow.OnBlurEvent += OnWindowBlur;
190 }
191
192 void OnWindowBlur()
193 {
194 blurBlock = true;
195 }
196
202 Rect GetScreenCoordinates(RectTransform uiElement)
203 {
204 var worldCorners = new Vector3[4];
205 uiElement.GetWorldCorners(worldCorners);
206
207 // try to support RenderMode:WorldSpace
208 var canvas = uiElement.GetComponentInParent<Canvas>();
209 var useCamera = (canvas.renderMode != RenderMode.ScreenSpaceOverlay);
210 if (canvas && useCamera)
211 {
212 var camera = canvas.worldCamera;
213 if (!camera) camera = Camera.main;
214
215 for (var i = 0; i < worldCorners.Length; i++)
216 {
217 worldCorners[i] = camera.WorldToScreenPoint(worldCorners[i]);
218 }
219 }
220
221 var min = new Vector3(float.MaxValue, float.MaxValue);
222 var max = new Vector3(float.MinValue, float.MinValue);
223 for (var i = 0; i < worldCorners.Length; i++)
224 {
225 min.x = Mathf.Min(min.x, worldCorners[i].x);
226 min.y = Mathf.Min(min.y, worldCorners[i].y);
227 max.x = Mathf.Max(max.x, worldCorners[i].x);
228 max.y = Mathf.Max(max.y, worldCorners[i].y);
229 }
230
231 return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
232 }
233
234 internal void DeactivateInputField()
235 {
236 if (!instances.ContainsKey(id)) return;
237
238 WebGLInputPlugin.WebGLInputDelete(id);
239 input.DeactivateInputField();
240 instances.Remove(id);
241 id = -1; // reset id to -1;
242 WebGLWindow.OnBlurEvent -= OnWindowBlur;
243 }
244
245 [MonoPInvokeCallback(typeof(Action<int>))]
246 static void OnFocus(int id)
247 {
248#if UNITY_WEBGL && !UNITY_EDITOR
249 Input.ResetInputAxes(); // Inputの状態リセット
250 UnityEngine.WebGLInput.captureAllKeyboardInput = false;
251#endif
252 }
253
254 [MonoPInvokeCallback(typeof(Action<int>))]
255 static void OnBlur(int id)
256 {
257#if UNITY_WEBGL && !UNITY_EDITOR
258 UnityEngine.WebGLInput.captureAllKeyboardInput = true;
259 Input.ResetInputAxes(); // Inputの状態リセット
260#endif
261 instances[id].StartCoroutine(Blur(id));
262 }
263
264 static IEnumerator Blur(int id)
265 {
266 yield return null;
267 if (!instances.ContainsKey(id)) yield break;
268
269 var block = instances[id].blurBlock; // get blur block state
270 instances[id].blurBlock = false; // reset instalce block state
271 if (block) yield break; // if block. break it!!
272
273 instances[id].DeactivateInputField();
274 }
275
276 [MonoPInvokeCallback(typeof(Action<int, string>))]
277 static void OnValueChange(int id, string value)
278 {
279 if (!instances.ContainsKey(id)) return;
280
281 var instance = instances[id];
282 if (!instance.input.ReadOnly)
283 {
284 instance.input.text = value;
285 }
286
287 // InputField.ContentType.Name が Name の場合、先頭文字が強制的大文字になるため小文字にして比べる
288 if (instance.input.contentType == ContentType.Name)
289 {
290 if (string.Compare(instance.input.text, value, true) == 0)
291 {
292 value = instance.input.text;
293 }
294 }
295
296 // InputField の ContentType による整形したテキストを HTML の input に再設定します
297 if (value != instance.input.text)
298 {
299 var start = WebGLInputPlugin.WebGLInputSelectionStart(id);
300 var end = WebGLInputPlugin.WebGLInputSelectionEnd(id);
301 // take the offset.when char remove from input.
302 var offset = instance.input.text.Length - value.Length;
303
304 WebGLInputPlugin.WebGLInputText(id, instance.input.text);
305 // reset the input element selection range!!
306 WebGLInputPlugin.WebGLInputSetSelectionRange(id, start + offset, end + offset);
307 }
308 }
309 [MonoPInvokeCallback(typeof(Action<int, string>))]
310 static void OnEditEnd(int id, string value)
311 {
312 if (!instances[id].input.ReadOnly)
313 {
314 instances[id].input.text = value;
315 }
316 }
317 [MonoPInvokeCallback(typeof(Action<int, int>))]
318 static void OnTab(int id, int value)
319 {
320 WebGLInputTabFocus.OnTab(instances[id], value);
321 }
322
323 void Update()
324 {
325 if (input == null || !input.isFocused) return;
326 // 未登録の場合、選択する
327 if (!instances.ContainsKey(id))
328 {
329 if (Application.isMobilePlatform) return;
330 OnSelect();
331
332 }
333 else if (!WebGLInputPlugin.WebGLInputIsFocus(id))
334 {
335 // focus this id
336 WebGLInputPlugin.WebGLInputFocus(id);
337 }
338
339 var start = WebGLInputPlugin.WebGLInputSelectionStart(id);
340 var end = WebGLInputPlugin.WebGLInputSelectionEnd(id);
341 // 選択方向によって設定します
342 if (WebGLInputPlugin.WebGLInputSelectionDirection(id) == -1)
343 {
344 input.selectionFocusPosition = start;
345 input.selectionAnchorPosition = end;
346 }
347 else
348 {
349 input.selectionFocusPosition = end;
350 input.selectionAnchorPosition = start;
351 }
352
353 input.Rebuild();
354 }
355
356 private void OnDestroy()
357 {
358 if (!instances.ContainsKey(id)) return;
359
360#if UNITY_WEBGL && !UNITY_EDITOR
361 UnityEngine.WebGLInput.captureAllKeyboardInput = true;
362 Input.ResetInputAxes(); // Inputの状態リセット
363#endif
364 instances[id].DeactivateInputField();
365 }
366
367 private void OnEnable()
368 {
369 WebGLInputTabFocus.Add(this);
370 }
371 private void OnDisable()
372 {
373 WebGLInputTabFocus.Remove(this);
374 }
375 public int CompareTo(WebGLInput other)
376 {
377 var a = GetScreenCoordinates(input.RectTransform());
378 var b = GetScreenCoordinates(other.input.RectTransform());
379 var res = b.y.CompareTo(a.y);
380 if (res == 0) res = a.x.CompareTo(b.x);
381 return res;
382 }
383
388 static class WebGLInputTabFocus
389 {
390 static List<WebGLInput> inputs = new List<WebGLInput>();
391
392 public static void Add(WebGLInput input)
393 {
394 inputs.Add(input);
395 inputs.Sort();
396 }
397
398 public static void Remove(WebGLInput input)
399 {
400 inputs.Remove(input);
401 }
402
403 public static void OnTab(WebGLInput input, int value)
404 {
405 if (inputs.Count <= 1) return;
406 var index = inputs.IndexOf(input);
407 index += value;
408 if (index < 0) index = inputs.Count - 1;
409 else if (index >= inputs.Count) index = 0;
410 inputs[index].input.ActivateInputField();
411 }
412 }
413 }
414}
Definition: Remove.cs:9
static string CanvasId
Definition: WebGLInput.cs:99
void OnSelect()
対象が選択されたとき
Definition: WebGLInput.cs:150
int CompareTo(WebGLInput other)
Definition: WebGLInput.cs:375
Wrapper for UnityEngine.UI.InputField
RectTransform RectTransform()