浏览代码

Merge branch 'keyboard' into 'master'

Keyboard

See merge request upm-packages/ui-widgets/com.unity.uiwidgets!114
/main
Shenhua Gu 6 年前
当前提交
5d4cea78
共有 48 个文件被更改,包括 3825 次插入153 次删除
  1. 14
      Runtime/editor/editor_window.cs
  2. 4
      Runtime/engine/UIWidgetsPanel.cs
  3. 278
      Runtime/service/keyboard.cs
  4. 229
      Runtime/service/text_input.cs
  5. 2
      Runtime/service/text_input_utils.cs
  6. 5
      Runtime/ui/painting/canvas_impl.cs
  7. 1
      Runtime/ui/painting/txt/mesh_generator.cs
  8. 2
      Runtime/ui/window.cs
  9. 40
      Runtime/widgets/editable_text.cs
  10. 61
      Samples/UIWidgetSample/TextInputSample.cs
  11. 1
      UIWidgetCleanupPlugin.DotSettings
  12. 8
      Runtime/Plugins/platform.meta
  13. 108
      Runtime/engine/UIWidgetsMessageManager.cs
  14. 11
      Runtime/engine/UIWidgetsMessageManager.cs.meta
  15. 8
      Runtime/external.meta
  16. 8
      Runtime/Plugins/platform/ios.meta
  17. 6
      Runtime/Plugins/platform/ios/UIWidgetsMessageManager.h
  18. 109
      Runtime/Plugins/platform/ios/UIWidgetsMessageManager.h.meta
  19. 39
      Runtime/Plugins/platform/ios/UIWidgetsMessageManager.mm
  20. 104
      Runtime/Plugins/platform/ios/UIWidgetsMessageManager.mm.meta
  21. 32
      Runtime/Plugins/platform/ios/UIWidgetsTextInputDelegate.h
  22. 109
      Runtime/Plugins/platform/ios/UIWidgetsTextInputDelegate.h.meta
  23. 32
      Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.h
  24. 109
      Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.h.meta
  25. 788
      Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.mm
  26. 104
      Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.mm.meta
  27. 8
      Runtime/Plugins/platform/android.meta
  28. 45
      Runtime/Plugins/platform/android/UIWidgetsMessageManager.java
  29. 31
      Runtime/Plugins/platform/android/UIWidgetsMessageManager.java.meta
  30. 5
      Runtime/Plugins/platform/android/Utils.java
  31. 31
      Runtime/Plugins/platform/android/Utils.java.meta
  32. 8
      Runtime/Plugins/platform/android/editing.meta
  33. 215
      Runtime/Plugins/platform/android/editing/InputConnectionAdaptor.java
  34. 31
      Runtime/Plugins/platform/android/editing/InputConnectionAdaptor.java.meta
  35. 257
      Runtime/Plugins/platform/android/editing/TextInputPlugin.java
  36. 31
      Runtime/Plugins/platform/android/editing/TextInputPlugin.java.meta
  37. 48
      Runtime/Plugins/platform/android/editing/TextInputView.java
  38. 31
      Runtime/Plugins/platform/android/editing/TextInputView.java.meta
  39. 5
      Runtime/external/.editorconfig
  40. 8
      Runtime/external/simplejson.meta
  41. 1001
      Runtime/external/simplejson/SimpleJSON.cs
  42. 11
      Runtime/external/simplejson/SimpleJSON.cs.meta

14
Runtime/editor/editor_window.cs


readonly TimeSpan _epoch = new TimeSpan(Stopwatch.GetTimestamp());
readonly MicrotaskQueue _microtaskQueue = new MicrotaskQueue();
readonly TimerProvider _timerProvider = new TimerProvider();
readonly TextInput _textInput = new TextInput();
readonly Rasterizer _rasterizer = new Rasterizer();
readonly ScrollInput _scrollInput = new ScrollInput();

}
}
if (this._textInput != null) {
this._textInput.keyboardManager.OnGUI();
}
TextInput.OnGUI();
}
void _updateScrollInput() {

using (this.getScope()) {
this._updateScrollInput();
if (this._textInput != null) {
this._textInput.keyboardManager.Update();
}
TextInput.Update();
this._timerProvider.update(this.flushMicrotasks);
this.flushMicrotasks();
}

}
}
public override TextInput textInput {
get { return this._textInput; }
}
internal void _forceRepaint() {
using (this.getScope()) {
RenderObjectVisitor visitor = null;

4
Runtime/engine/UIWidgetsPanel.cs


protected override void OnEnable() {
base.OnEnable();
//Disable the default touch -> mouse event conversion on mobile devices
//Disable the default touch -> mouse event conversion on mobile devices
Input.simulateMouseWithTouches = false;
this._displayMetrics = DisplayMetricsProvider.provider();

void Update() {
PerformanceUtils.instance.updateDeltaTime(Time.unscaledDeltaTime);
this._displayMetrics.Update();
UIWidgetsMessageManager.ensureUIWidgetsMessageManagerIfNeeded();
if (EventSystem.current != null && EventSystem.current.currentSelectedGameObject != this.gameObject) {
this.unfocusIfNeeded();
}

278
Runtime/service/keyboard.cs


using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.external.simplejson;
using Unity.UIWidgets.foundation;
public class KeyboadManager {
interface KeyboardDelegate: IDisposable {
void show();
void hide();
void setEditingState(TextEditingValue value);
void setClient(int client, TextInputConfiguration configuration);
void clearClient();
bool hashPreview();
}
public interface TextInputUpdateListener {
void Update();
}
public interface TextInputOnGUIListener {
void OnGUI();
}
class DefaultKeyboardDelegate : KeyboardDelegate, TextInputOnGUIListener {
TextInputConfiguration _configuration;
TouchScreenKeyboard _keyboard;
RangeInt? _pendingSelection;
bool _screenKeyboardDone;
readonly TextInput _textInput;
public KeyboadManager(TextInput textInput) {
this._textInput = textInput;
public void show() {
public void Update() {
if (!TouchScreenKeyboard.isSupported) {
return;
}
public void hide() {
}
if (this._client == 0 || this._keyboard == null) {
return;
}
public void setEditingState(TextEditingValue value) {
this._value = value;
}
public void setClient(int client, TextInputConfiguration configuration) {
this._client = client;
}
if (this._keyboard.canSetSelection && this._pendingSelection != null) {
this._keyboard.selection = this._pendingSelection.Value;
this._pendingSelection = null;
}
public void clearClient() {
this._client = 0;
}
if (this._keyboard.status == TouchScreenKeyboard.Status.Done) {
if (!this._screenKeyboardDone) {
this._screenKeyboardDone = true;
Window.instance.run(() => {
this._textInput._performAction(this._client,
TextInputAction.done);
});
}
}
else if (this._keyboard.status == TouchScreenKeyboard.Status.Visible) {
var keyboardSelection = this._keyboard.selection;
var newValue = new TextEditingValue(
this._keyboard.text,
this._keyboard.canGetSelection
? new TextSelection(keyboardSelection.start, keyboardSelection.end)
: this._value.selection
);
var changed = this._value != newValue;
this._value = newValue;
if (changed) {
Window.instance.run(() => {
this._textInput._updateEditingState(this._client,
this._value);
});
}
}
public bool hashPreview() {
return false;
}
public void OnGUI() {

if (currentEvent != null && currentEvent.type == EventType.KeyDown) {
var action = TextInputUtils.getInputAction(currentEvent);
if (action != null) {
Window.instance.run(() => { this._textInput._performAction(this._client, action.Value); });
Window.instance.run(() => { TextInput._performAction(this._client, action.Value); });
Window.instance.run(() => { this._textInput._updateEditingState(this._client, this._value); });
Window.instance.run(() => { TextInput._updateEditingState(this._client, this._value); });
}
}

if (!string.IsNullOrEmpty(Input.compositionString) &&
this._lastCompositionString != Input.compositionString) {
this._value = this._value.compose(Input.compositionString);
Window.instance.run(() => { this._textInput._updateEditingState(this._client, this._value); });
Window.instance.run(() => { TextInput._updateEditingState(this._client, this._value); });
public void show() {
if (!TouchScreenKeyboard.isSupported) {
public void Dispose() {
}
}
class UnityTouchScreenKeyboardDelegate : KeyboardDelegate, TextInputUpdateListener {
int _client;
string _lastCompositionString;
TextInputConfiguration _configuration;
TextEditingValue _value;
TouchScreenKeyboard _keyboard;
RangeInt? _pendingSelection;
bool _screenKeyboardDone;
readonly TextInput _textInput;
public void Update() {
if (this._client == 0 || this._keyboard == null) {
if (this._keyboard.canSetSelection && this._pendingSelection != null) {
this._keyboard.selection = this._pendingSelection.Value;
this._pendingSelection = null;
}
if (this._keyboard.status == TouchScreenKeyboard.Status.Done) {
if (!this._screenKeyboardDone) {
this._screenKeyboardDone = true;
Window.instance.run(() => {
TextInput._performAction(this._client,
TextInputAction.done);
});
}
}
else if (this._keyboard.status == TouchScreenKeyboard.Status.Visible) {
var keyboardSelection = this._keyboard.selection;
var newValue = new TextEditingValue(
this._keyboard.text,
this._keyboard.canGetSelection
? new TextSelection(keyboardSelection.start, keyboardSelection.end)
: TextSelection.collapsed(0)
);
var changed = this._value != newValue;
this._value = newValue;
if (changed) {
Window.instance.run(() => {
TextInput._updateEditingState(this._client,
this._value);
});
}
}
}
public void show() {
var secure = this._configuration.obscureText;
var multiline = this._configuration.inputType == TextInputType.multiline;
var autocorrection = this._configuration.autocorrect;

this._client = 0;
}
public bool hashPreview() {
return this._keyboard != null;
}
public void setClient(int client, TextInputConfiguration configuration) {
this._client = client;
this._configuration = configuration;

}
}
}
public bool textInputOnKeyboard() {
return TouchScreenKeyboard.isSupported;
}
static TouchScreenKeyboardType getKeyboardTypeForConfiguration(TextInputConfiguration config) {
var inputType = config.inputType;

return TouchScreenKeyboardType.Default;
}
public void Dispose() {
}
#if UNITY_IOS || UNITY_ANDROID
class UIWidgetsTouchScreenKeyboardDelegate : KeyboardDelegate {
public UIWidgetsTouchScreenKeyboardDelegate() {
UIWidgetsMessageManager.instance.AddChannelMessageDelegate("TextInput", this.handleMethodCall);
}
public void Dispose() {
UIWidgetsMessageManager.instance.RemoveChannelMessageDelegate("TextInput", this.handleMethodCall);
}
public void show() {
UIWidgetsTextInputShow();
}
public void hide() {
UIWidgetsTextInputHide();
}
public void setEditingState(TextEditingValue value) {
UIWidgetsTextInputSetTextInputEditingState(value.toJson().ToString());
}
public void setClient(int client, TextInputConfiguration configuration) {
UIWidgetsTextInputSetClient(client, configuration.toJson().ToString());
}
public void clearClient() {
UIWidgetsTextInputClearTextInputClient();
}
public bool hashPreview() {
return false;
}
void handleMethodCall(string method, List<JSONNode> args) {
if (TextInput._currentConnection == null) {
return;
}
int client = args[0].AsInt;
if (client != TextInput._currentConnection._id) {
return;
}
using (TextInput._currentConnection._window.getScope()) {
switch (method) {
case "TextInputClient.updateEditingState":
TextInput._updateEditingState(client, TextEditingValue.fromJson(args[1].AsObject));
break;
case "TextInputClient.performAction":
TextInput._performAction(client, TextInputUtils._toTextInputAction(args[1].Value));
break;
default:
throw new UIWidgetsError($"unknown method ${method}");
}
}
}
#if UNITY_IOS
[DllImport ("__Internal")]
internal static extern void UIWidgetsTextInputShow();
[DllImport ("__Internal")]
internal static extern void UIWidgetsTextInputHide();
[DllImport ("__Internal")]
internal static extern void UIWidgetsTextInputSetClient(int client, string configuration);
[DllImport ("__Internal")]
internal static extern void UIWidgetsTextInputSetTextInputEditingState(string jsonText); // also send to client ?
[DllImport ("__Internal")]
internal static extern void UIWidgetsTextInputClearTextInputClient();
#elif UNITY_ANDROID
internal static void UIWidgetsTextInputShow() {
using (
AndroidJavaClass pluginClass = new AndroidJavaClass("com.unity.uiwidgets.plugin.editing.TextInputPlugin")
) {
pluginClass.CallStatic("show");
}
}
internal static void UIWidgetsTextInputHide() {
using (
AndroidJavaClass pluginClass = new AndroidJavaClass("com.unity.uiwidgets.plugin.editing.TextInputPlugin")
) {
pluginClass.CallStatic("hide");
}
}
internal static void UIWidgetsTextInputSetClient(int client, string configuration) {
using (
AndroidJavaClass pluginClass = new AndroidJavaClass("com.unity.uiwidgets.plugin.editing.TextInputPlugin")
) {
pluginClass.CallStatic("setClient", client, configuration);
}
}
internal static void UIWidgetsTextInputSetTextInputEditingState(string jsonText) {
using (
AndroidJavaClass pluginClass = new AndroidJavaClass("com.unity.uiwidgets.plugin.editing.TextInputPlugin")
) {
pluginClass.CallStatic("setEditingState", jsonText);
}
}
internal static void UIWidgetsTextInputClearTextInputClient() {
using (
AndroidJavaClass pluginClass = new AndroidJavaClass("com.unity.uiwidgets.plugin.editing.TextInputPlugin")
) {
pluginClass.CallStatic("clearClient");
}
}
#endif
#endif
}
}

229
Runtime/service/text_input.cs


using System;
using System.Collections.Generic;
using Unity.UIWidgets.external.simplejson;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.ui;
using UnityEngine;

"text", "multiline", "number", "phone", "datetime", "emailAddress", "url"
};
public Dictionary<string, object> toJson() {
return new Dictionary<string, object>() {
{"name", this._name},
{"signed", this.signed},
{"decimal", this.decimalNum}
};
public JSONNode toJson() {
JSONObject jsonObject = new JSONObject();
jsonObject["name"] = this._name;
jsonObject["signed"] = this.signed;
jsonObject["decimal"] = this.decimalNum;
return jsonObject;
}
string _name {

}
}
static partial class TextInputUtils {
internal static TextAffinity? _toTextAffinity(string affinity) {
switch (affinity) {
case "TextAffinity.downstream":
return TextAffinity.downstream;
case "TextAffinity.upstream":
return TextAffinity.upstream;
}
return null;
}
internal static TextInputAction _toTextInputAction(string action) {
switch (action) {
case "TextInputAction.none":
return TextInputAction.none;
case "TextInputAction.unspecified":
return TextInputAction.unspecified;
case "TextInputAction.go":
return TextInputAction.go;
case "TextInputAction.search":
return TextInputAction.search;
case "TextInputAction.send":
return TextInputAction.send;
case "TextInputAction.next":
return TextInputAction.next;
case "TextInputAction.previuos":
return TextInputAction.previous;
case "TextInputAction.continue_action":
return TextInputAction.continueAction;
case "TextInputAction.join":
return TextInputAction.join;
case "TextInputAction.route":
return TextInputAction.route;
case "TextInputAction.emergencyCall":
return TextInputAction.emergencyCall;
case "TextInputAction.done":
return TextInputAction.done;
case "TextInputAction.newline":
return TextInputAction.newline;
}
throw new UIWidgetsError("Unknown text input action: $action");
}
}
static JSONNode defaultIndexNode = new JSONNumber(-1);
public TextEditingValue(string text = "", TextSelection selection = null, TextRange composing = null) {
this.text = text;
this.selection = selection ?? TextSelection.collapsed(-1);

public static TextEditingValue fromJson(JSONObject json) {
TextAffinity? affinity =
TextInputUtils._toTextAffinity(json["selectionAffinity"].Value);
return new TextEditingValue(
text: json["text"].Value,
selection: new TextSelection(
baseOffset: json.GetValueOrDefault("selectionBase", defaultIndexNode).AsInt,
extentOffset: json.GetValueOrDefault("selectionExtent", defaultIndexNode).AsInt,
affinity: affinity != null ? affinity.Value : TextAffinity.downstream,
isDirectional: json["selectionIsDirectional"].AsBool
),
composing: new TextRange(
start: json.GetValueOrDefault("composingBase", defaultIndexNode).AsInt,
end: json.GetValueOrDefault("composingExtent", defaultIndexNode).AsInt
)
);
}
public TextEditingValue copyWith(string text = null, TextSelection selection = null,
TextRange composing = null) {
return new TextEditingValue(

public override string ToString() {
return $"Text: {this.text}, Selection: {this.selection}, Composing: {this.composing}";
}
public JSONNode toJson() {
var json = new JSONObject();
json["text"] = this.text;
json["selectionBase"] = this.selection.baseOffset;
json["selectionExtent"] = this.selection.extentOffset;
json["selectionAffinity"] = this.selection.affinity.ToString();
json["selectionIsDirectional"] = this.selection.isDirectional;
json["composingBase"] = this.composing.start;
json["composingExtent"] = this.composing.end;
return json;
}
}
public interface TextSelectionDelegate {

scrollPageDown,
}
public class TextInputConfiguration {
public enum TextCapitalization {
words,
sentences,
characters,
none
}
class TextInputConfiguration {
bool obscureText = false, bool autocorrect = true, TextInputAction inputAction = TextInputAction.done) {
bool obscureText = false, bool autocorrect = true, TextInputAction inputAction = TextInputAction.done,
Brightness keyboardAppearance = Brightness.light, TextCapitalization textCapitalization = TextCapitalization.none,
bool unityTouchKeyboard = false) {
this.textCapitalization = textCapitalization;
this.keyboardAppearance = keyboardAppearance;
this.unityTouchKeyboard = unityTouchKeyboard;
}
public readonly TextInputType inputType;

public readonly TextCapitalization textCapitalization;
public readonly Brightness keyboardAppearance;
public readonly bool unityTouchKeyboard;
public Dictionary<string, object> toJson() {
return new Dictionary<string, object>() {
{"inputType", this.inputType.toJson()},
{"obscureText", this.obscureText},
{"autocorrect", this.autocorrect},
{"inputAction", this.inputAction.ToString()}
};
public JSONNode toJson() {
var json = new JSONObject();
json["inputType"] = this.inputType.toJson();
json["obscureText"] = this.obscureText;
json["autocorrect"] = this.autocorrect;
json["inputAction"] = $"TextInputAction.{this.inputAction.ToString()}";
json["unityTouchKeyboard"] = this.unityTouchKeyboard;
json["textCapitalization"] = $"TextCapitalization.{this.textCapitalization.ToString()}";
json["keyboardAppearance"] = $"Brightness.{this.keyboardAppearance.ToString()}";
return json;
internal TextInputConnection(TextInputClient client, TextInput textInput) {
internal TextInputConnection(TextInputClient client) {
D.assert(textInput != null);
this._window = Window.instance;
this._textInput = textInput;
get { return this._textInput._currentConnection == this; }
get { return TextInput._currentConnection == this; }
this._textInput.keyboardManager.setEditingState(value);
TextInput.keyboardDelegate.setEditingState(value);
this._textInput.setCompositionCursorPos(x, y);
TextInput.setCompositionCursorPos(x, y);
this._textInput.keyboardManager.clearClient();
this._textInput._currentConnection = null;
TextInput.keyboardDelegate.clearClient();
TextInput._currentConnection = null;
this._textInput._scheduleHide();
TextInput._scheduleHide();
}
D.assert(!this.attached);

D.assert(this.attached);
Input.imeCompositionMode = IMECompositionMode.On;
this._textInput.keyboardManager.show();
TextInput.keyboardDelegate.show();
internal readonly TextInput _textInput;
internal readonly Window _window;
public class TextInput {
internal TextInputConnection _currentConnection;
class TextInput {
static internal TextInputConnection _currentConnection;
public readonly KeyboadManager keyboardManager;
static internal KeyboardDelegate keyboardDelegate;
this.keyboardManager = new KeyboadManager(this);
public TextInputConnection attach(TextInputClient client, TextInputConfiguration configuration) {
public static TextInputConnection attach(TextInputClient client, TextInputConfiguration configuration) {
var connection = new TextInputConnection(client, this);
this.keyboardManager.setClient(connection._id, configuration);
this._currentConnection = connection;
var connection = new TextInputConnection(client);
_currentConnection = connection;
string x = configuration.toJson();
if (keyboardDelegate != null) {
keyboardDelegate.Dispose();
}
if (TouchScreenKeyboard.isSupported) {
#if UNITY_IOS || UNITY_ANDROID
if (configuration.unityTouchKeyboard) {
keyboardDelegate = new UnityTouchScreenKeyboardDelegate();
}
else {
keyboardDelegate = new UIWidgetsTouchScreenKeyboardDelegate();
}
#else
keyboardDelegate = new UnityTouchScreenKeyboardDelegate();
#endif
}
else {
keyboardDelegate = new DefaultKeyboardDelegate();
}
keyboardDelegate.setClient(connection._id, configuration);
public void setCompositionCursorPos(float x, float y) {
internal static void Update() {
if (_currentConnection != null && _currentConnection._window == Window.instance) {
(keyboardDelegate as TextInputUpdateListener)?.Update();
}
}
internal static void OnGUI() {
if (_currentConnection != null && _currentConnection._window == Window.instance) {
(keyboardDelegate as TextInputOnGUIListener)?.OnGUI();
}
}
public static void setCompositionCursorPos(float x, float y) {
internal void _updateEditingState(int client, TextEditingValue value) {
if (this._currentConnection == null) {
internal static void _updateEditingState(int client, TextEditingValue value) {
if (_currentConnection == null) {
if (client != this._currentConnection._id) {
if (client != _currentConnection._id) {
this._currentConnection._client.updateEditingValue(value);
_currentConnection._client.updateEditingValue(value);
internal void _performAction(int client, TextInputAction action) {
if (this._currentConnection == null) {
internal static void _performAction(int client, TextInputAction action) {
if (_currentConnection == null) {
if (client != this._currentConnection._id) {
if (client != _currentConnection._id) {
this._currentConnection._client.performAction(action);
_currentConnection._client.performAction(action);
bool _hidePending = false;
static bool _hidePending = false;
internal void _scheduleHide() {
if (this._hidePending) {
static internal void _scheduleHide() {
if (_hidePending) {
this._hidePending = true;
_hidePending = true;
this._hidePending = false;
if (this._currentConnection == null) {
this.keyboardManager.hide();
_hidePending = false;
if (_currentConnection == null) {
keyboardDelegate.hide();
}
}
}
}

2
Runtime/service/text_input_utils.cs


using UnityEngine;
namespace Unity.UIWidgets.service {
public class TextInputUtils {
static partial class TextInputUtils {
static Dictionary<Event, TextInputAction> _keyToOperations;
public static TextInputAction? getInputAction(Event evt) {

5
Runtime/ui/painting/canvas_impl.cs


if (cmd.textMesh != null) {
mesh = cmd.textMesh.resovleMesh();
}
if (mesh == null) {
continue;
}
D.assert(mesh.vertices.Count > 0);
cmd.meshObj.SetVertices(mesh.vertices);

1
Runtime/ui/painting/txt/mesh_generator.cs


}
if (vertices.Count == 0) {
this._mesh = null;
return null;
}

2
Runtime/ui/window.cs


public abstract Timer runInMain(Action callback);
public abstract TextInput textInput { get; }
public abstract IDisposable getScope();
}
}

40
Runtime/widgets/editable_text.cs


using Unity.UIWidgets.scheduler;
using Unity.UIWidgets.service;
using Unity.UIWidgets.ui;
using UnityEngine;
using Color = Unity.UIWidgets.ui.Color;
using Rect = Unity.UIWidgets.ui.Rect;
using TextStyle = Unity.UIWidgets.painting.TextStyle;
namespace Unity.UIWidgets.widgets {

public readonly TextDirection? textDirection;
public readonly TextCapitalization textCapitalization;
public readonly float? textScaleFactor;
public readonly Color cursorColor;

public readonly bool rendererIgnoresPointer;
public readonly bool unityTouchKeyboard;
public EditableText(TextEditingController controller, FocusNode focusNode, TextStyle style,
Color cursorColor, bool obscureText = false, bool autocorrect = false,
TextAlign textAlign = TextAlign.left, TextDirection? textDirection = null,

TextCapitalization textCapitalization = TextCapitalization.none,
EdgeInsets scrollPadding = null,
EdgeInsets scrollPadding = null, bool unityTouchKeyboard = false,
Key key = null) : base(key) {
D.assert(controller != null);
D.assert(focusNode != null);

this.textDirection = textDirection;
this.textScaleFactor = textScaleFactor;
this.textInputAction = textInputAction;
this.textCapitalization = textCapitalization;
this.cursorColor = cursorColor;
this.maxLines = maxLines;
this.autofocus = autofocus;

this.onEditingComplete = onEditingComplete;
this.rendererIgnoresPointer = rendererIgnoresPointer;
this.selectionControls = selectionControls;
this.unityTouchKeyboard = unityTouchKeyboard;
if (maxLines == 1) {
this.inputFormatters = new List<TextInputFormatter>();
this.inputFormatters.Add(BlacklistingTextInputFormatter.singleLineFormatter);

this._hideSelectionOverlayIfNeeded();
this._showCaretOnScreen();
if (this.widget.obscureText && value.text.Length == this._value.text.Length + 1) {
this._obscureShowCharTicksPending = _kObscureShowLatestCharCursorTicks;
this._obscureShowCharTicksPending = !this._unityKeyboard() ? _kObscureShowLatestCharCursorTicks : 0;
this._obscureLatestCharIndex = this._value.selection.baseOffset;
}
}

get { return this._textInputConnection != null && this._textInputConnection.attached; }
}
this._textInputConnection = Window.instance.textInput.attach(this, new TextInputConfiguration(
this._textInputConnection = TextInput.attach(this, new TextInputConfiguration(
: TextInputAction.done)
: TextInputAction.done),
textCapitalization: this.widget.textCapitalization,
unityTouchKeyboard: this.widget.unityTouchKeyboard
));
this._textInputConnection.setEditingState(localValue);
}

this._hideSelectionOverlayIfNeeded();
if (this.widget.selectionControls != null) {
if (this.widget.selectionControls != null && !this._unityKeyboard()) {
this._selectionOverlay = new TextSelectionOverlay(
context: this.context,
value: this._value,

}
void _cursorTick() {
this._showCursor.value = !this._showCursor.value;
this._showCursor.value = !this._unityKeyboard() && !this._showCursor.value;
if (this._obscureShowCharTicksPending > 0) {
this.setState(() => { this._obscureShowCharTicksPending--; });
}

this._showCursor.value = true;
this._showCursor.value = !this._unityKeyboard();
this._cursorTimer = Window.instance.run(_kCursorBlinkHalfPeriod, this._cursorTick,
periodic: true);
}

}
void _startOrStopCursorTimerIfNeeded() {
if (this._cursorTimer == null && this._hasFocus && this._value.selection.isCollapsed &&
!Window.instance.textInput.keyboardManager.textInputOnKeyboard()) {
if (this._cursorTimer == null && this._hasFocus && this._value.selection.isCollapsed) {
this._startCursorTimer();
}
else if (this._cursorTimer != null && (!this._hasFocus || !this._value.selection.isCollapsed)) {

if (this.widget.obscureText) {
text = new string(RenderEditable.obscuringCharacter, text.Length);
int o = this._obscureShowCharTicksPending > 0 ? this._obscureLatestCharIndex : -1;
if (!Window.instance.textInput.keyboardManager.textInputOnKeyboard() && o >= 0 && o < text.Length) {
if (o >= 0 && o < text.Length) {
text = text.Substring(0, o) + this._value.text.Substring(o, 1) + text.Substring(o + 1);
}
}

// unity keyboard has a preview view with editing function, text selection & cursor function of this widget is disable
// in the case
bool _unityKeyboard() {
return TouchScreenKeyboard.isSupported && this.widget.unityTouchKeyboard;
}
class _Editable : LeafRenderObjectWidget {
public readonly TextSpan textSpan;

61
Samples/UIWidgetSample/TextInputSample.cs


}
}
public class EditableInputTypeWidget : StatelessWidget {
public class EditableInputTypeWidget : StatefulWidget {
public EditableInputTypeWidget(Key key = null) : base(key) {
}
public override State createState() {
return new _EditableInputTypeWidgetState();
}
}
class _EditableInputTypeWidgetState : State<EditableInputTypeWidget> {
bool unityKeyboard = false;
Widget rowWidgets(string title, Widget widget) {
return new Container(
height: 80,

}
));
}
public override Widget build(BuildContext context) {
List<Widget> widgets = new List<Widget>();
List<Widget> buildInputs(bool unityKeyboard) {
List<Widget> widgets = new List<Widget>();
new EditableText(controller, node, style, cursorColor, selectionColor: selectionColor, onSubmitted: this.textSubmitted)))));
new EditableText(controller, node, style, cursorColor, selectionColor: selectionColor, onSubmitted: this.textSubmitted
, unityTouchKeyboard: unityKeyboard, selectionControls: MaterialUtils.materialTextSelectionControls)))));
onSubmitted: this.textSubmitted)))));
onSubmitted: this.textSubmitted, unityTouchKeyboard: unityKeyboard,
selectionControls: MaterialUtils.materialTextSelectionControls)))));
onSubmitted: this.textSubmitted)))));
onSubmitted: this.textSubmitted, unityTouchKeyboard: unityKeyboard,
selectionControls: MaterialUtils.materialTextSelectionControls)))));
onSubmitted: this.textSubmitted)))));
onSubmitted: this.textSubmitted, unityTouchKeyboard: unityKeyboard,
selectionControls: MaterialUtils.materialTextSelectionControls)))));
onSubmitted: this.textSubmitted)))));
onSubmitted: this.textSubmitted, unityTouchKeyboard: unityKeyboard,
selectionControls: MaterialUtils.materialTextSelectionControls)))));
onSubmitted: this.textSubmitted)))));
onSubmitted: this.textSubmitted, unityTouchKeyboard: unityKeyboard,
selectionControls: MaterialUtils.materialTextSelectionControls)))));
onSubmitted: this.textSubmitted)))));
return new Column(
onSubmitted: this.textSubmitted, unityTouchKeyboard: unityKeyboard,
selectionControls: MaterialUtils.materialTextSelectionControls)))));
return widgets;
}
public override Widget build(BuildContext context) {
List<Widget> widgets = new List<Widget>();
widgets.Add(new Text("UIWidgets Touch Keyboard", style: new TextStyle(fontSize:20, height:2.0f), textAlign: TextAlign.center));
widgets.AddRange(this.buildInputs(false));
widgets.Add(new Text("Unity Touch Keyboard", style: new TextStyle(fontSize:20, height:2.0f), textAlign: TextAlign.center));
widgets.AddRange(this.buildInputs(true));
return new Container(
padding: EdgeInsets.all(12),
child: new SingleChildScrollView(child: new Column(
children: widgets);
children: widgets)));
public class EditStateProvider : StatefulWidget {
public delegate EditableText EditableBuilder(BuildContext context,
TextEditingController controller, FocusNode focusNode);

1
UIWidgetCleanupPlugin.DotSettings


<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/Highlighting/ReadSettingsFromFileLevel/@EntryValue">True</s:Boolean>
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=UIWidgets/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="UIWidgets"&gt;&lt;JsReformatCode&gt;True&lt;/JsReformatCode&gt;&lt;CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="False" RemoveRedundantParentheses="False" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="False" ArrangeArgumentsStyle="False" ArrangeCodeBodyStyle="True" ArrangeVarStyle="False" /&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSReformatCode&gt;True&lt;/CSReformatCode&gt;&lt;CSFixBuiltinTypeReferences&gt;True&lt;/CSFixBuiltinTypeReferences&gt;&lt;CSArrangeQualifiers&gt;True&lt;/CSArrangeQualifiers&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;IDEA_SETTINGS&gt;&amp;lt;profile version="1.0"&amp;gt;
&amp;lt;option name="myName" value="UIWidgets" /&amp;gt;
&amp;lt;inspection_tool class="ES6ShorthandObjectProperty" enabled="false" level="INFORMATION" enabled_by_default="false" /&amp;gt;

8
Runtime/Plugins/platform.meta


fileFormatVersion: 2
guid: e93b613e0d2074e979e1920d32dabdd7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

108
Runtime/engine/UIWidgetsMessageManager.cs


using System.Collections.Generic;
using System.Runtime.InteropServices;
using Unity.UIWidgets.external.simplejson;
using Unity.UIWidgets.foundation;
using UnityEngine;
namespace Unity.UIWidgets.engine {
public class UIWidgetsMessageManager: MonoBehaviour {
public delegate void MethodChannelMessageDelegate(string method, List<JSONNode> args);
static UIWidgetsMessageManager _instance;
readonly Dictionary<string, MethodChannelMessageDelegate> _methodChannelMessageDelegates =
new Dictionary<string, MethodChannelMessageDelegate>();
public static UIWidgetsMessageManager instance {
get { return _instance; }
}
internal static void ensureUIWidgetsMessageManagerIfNeeded() {
if (!Application.isPlaying) {
return;
}
if (UIWidgetsMessageManager.instance != null) {
return;
}
var managerObj = new GameObject("__UIWidgetsMessageManager");
managerObj.AddComponent<UIWidgetsMessageManager>();
}
string _lastObjectName;
void OnEnable() {
D.assert(_instance == null, "Only one instance of UIWidgetsMessageManager should exists");
_instance = this;
this.UpdateNameIfNeed();
}
void OnDisable() {
D.assert(_instance != null, "_instance should not be null");
_instance = null;
}
void Update() {
this.UpdateNameIfNeed();
}
void UpdateNameIfNeed() {
var name = this.gameObject.name;
if (name != this._lastObjectName) {
#if UNITY_IOS || UNITY_ANDROID
if (!Application.isEditor) {
UIWidgetsMessageSetObjectName(name);
}
this._lastObjectName = name;
#endif
}
}
public void AddChannelMessageDelegate(string channel, MethodChannelMessageDelegate del) {
MethodChannelMessageDelegate exists;
this._methodChannelMessageDelegates.TryGetValue(channel, out exists);
this._methodChannelMessageDelegates[channel] = exists + del;
}
public void RemoveChannelMessageDelegate(string channel, MethodChannelMessageDelegate del) {
MethodChannelMessageDelegate exists;
this._methodChannelMessageDelegates.TryGetValue(channel, out exists);
if (exists != null) {
this._methodChannelMessageDelegates[channel] = exists - del;
}
}
void OnUIWidgetsMethodMessage(string message) {
JSONObject jsonObject = (JSONObject)JSON.Parse(message);
string channel = jsonObject["channel"].Value;
string method = jsonObject["method"].Value;
var args = new List<JSONNode>(jsonObject["args"].AsArray.Children);
if (string.IsNullOrEmpty(channel) || string.IsNullOrEmpty(method)) {
Debug.LogError("invalid uiwidgets method message");
}
else {
MethodChannelMessageDelegate exists;
this._methodChannelMessageDelegates.TryGetValue(channel, out exists);
exists?.Invoke(method, args);
}
}
#if UNITY_IOS
[DllImport("__Internal")]
static extern void UIWidgetsMessageSetObjectName(string objectName);
#elif UNITY_ANDROID
static void UIWidgetsMessageSetObjectName(string objectName) {
using (
AndroidJavaClass managerClass = new AndroidJavaClass("com.unity.uiwidgets.plugin.UIWidgetsMessageManager")
) {
using (
AndroidJavaObject managerInstance = managerClass.CallStatic<AndroidJavaObject>("getInstance")
) {
managerInstance.Call("SetObjectName", objectName);
}
}
}
#endif
}
}

11
Runtime/engine/UIWidgetsMessageManager.cs.meta


fileFormatVersion: 2
guid: efd031068fc9d41d6b4c165afaaa25ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

8
Runtime/external.meta


fileFormatVersion: 2
guid: 910527b5287c8499e8381ae8b9787b4a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Runtime/Plugins/platform/ios.meta


fileFormatVersion: 2
guid: d8264d285bf7842f99ef80594606a5ba
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

6
Runtime/Plugins/platform/ios/UIWidgetsMessageManager.h


#ifndef PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSMESSAGEMANAGER_H_
#define PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSMESSAGEMANAGER_H_
void UIWidgetsMethodMessage(NSString* channel, NSString* method, NSArray *args);
#endif

109
Runtime/Plugins/platform/ios/UIWidgetsMessageManager.h.meta


fileFormatVersion: 2
guid: 0705d6959bdbd4e8880b5d9ac52d8bba
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 0
Exclude Linux: 0
Exclude Linux64: 0
Exclude LinuxUniversal: 0
Exclude OSXUniversal: 0
Exclude WebGL: 0
Exclude Win: 0
Exclude Win64: 0
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 1
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 1
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: LinuxUniversal
second:
enabled: 1
settings: {}
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
WebGL: WebGL
second:
enabled: 1
settings: {}
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CompileFlags: -fno-objc-arc
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

39
Runtime/Plugins/platform/ios/UIWidgetsMessageManager.mm


#include "UIWidgetsMessageManager.h"
#define MAX_OBJECT_NAME_LENGTH 256
static char uiwidgetsMessageObjectName[MAX_OBJECT_NAME_LENGTH] = {0};
static char* MakeStringCopy (const char* string)
{
if (string == NULL)
return NULL;
char* res = (char*)malloc(strlen(string) + 1);
strcpy(res, string);
return res;
}
void UIWidgetsMethodMessage(NSString* channel, NSString* method, NSArray *args)
{
const char* msg = NULL;
NSError *error;
NSDictionary* dict = @{
@"channel": channel,
@"method": method,
@"args": args
};
NSData* data = [NSJSONSerialization dataWithJSONObject:dict options:0 error:&error];
NSString* text = [[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
msg = [text UTF8String];
UnitySendMessage(uiwidgetsMessageObjectName, "OnUIWidgetsMethodMessage", msg);
}
extern "C" {
void UIWidgetsMessageSetObjectName(const char* name) {
strlcpy(uiwidgetsMessageObjectName, name, MAX_OBJECT_NAME_LENGTH);
}
}

104
Runtime/Plugins/platform/ios/UIWidgetsMessageManager.mm.meta


fileFormatVersion: 2
guid: 4238c84dc02f445fcb9e8d755dc9c441
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: x86_64
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CompileFlags: -fno-objc-arc
FrameworkDependencies:
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

32
Runtime/Plugins/platform/ios/UIWidgetsTextInputDelegate.h


#ifndef PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSTEXTINPUTDELEGATE_H_
#define PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSTEXTINPUTDELEGATE_H_
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSInteger, UIWidgetsTextInputAction) {
UIWidgetsTextInputActionUnspecified,
UIWidgetsTextInputActionDone,
UIWidgetsTextInputActionGo,
UIWidgetsTextInputActionSend,
UIWidgetsTextInputActionSearch,
UIWidgetsTextInputActionNext,
UIWidgetsTextInputActionContinue,
UIWidgetsTextInputActionJoin,
UIWidgetsTextInputActionRoute,
UIWidgetsTextInputActionEmergencyCall,
UIWidgetsTextInputActionNewline,
};
@protocol UIWidgetsTextInputDelegate <NSObject>
- (void)updateEditingClient:(int)client withState:(NSDictionary*)state;
- (void)performAction:(UIWidgetsTextInputAction)action withClient:(int)client;
@end
@interface DefaultUIWidgetsTextInputDelegate : NSObject <UIWidgetsTextInputDelegate>
- (void)updateEditingClient:(int)client withState:(NSDictionary*)state;
- (void)performAction:(UIWidgetsTextInputAction)action withClient:(int)client;
@end
#endif // PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSTEXTINPUTDELEGATE_H_

109
Runtime/Plugins/platform/ios/UIWidgetsTextInputDelegate.h.meta


fileFormatVersion: 2
guid: 2ea9fef069ce54398a5a9b8ff7b61a1e
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 0
Exclude Linux: 0
Exclude Linux64: 0
Exclude LinuxUniversal: 0
Exclude OSXUniversal: 0
Exclude WebGL: 0
Exclude Win: 0
Exclude Win64: 0
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 1
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 1
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: LinuxUniversal
second:
enabled: 1
settings: {}
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
WebGL: WebGL
second:
enabled: 1
settings: {}
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CompileFlags: -fno-objc-arc
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

32
Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.h


#ifndef PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSTEXTINPUTPLUGIN_H_
#define PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSTEXTINPUTPLUGIN_H_
#import <UIKit/UIKit.h>
#include "UIWidgetsTextInputDelegate.h"
@interface UIWidgetsTextInputPlugin : NSObject
@property(nonatomic, assign) id<UIWidgetsTextInputDelegate> textInputDelegate;
- (UIView<UITextInput>*)textInputView;
@end
@interface UIWidgetsTextPosition : UITextPosition
@property(nonatomic, readonly) NSUInteger index;
+ (instancetype)positionWithIndex:(NSUInteger)index;
- (instancetype)initWithIndex:(NSUInteger)index;
@end
@interface UIWidgetsTextRange : UITextRange <NSCopying>
@property(nonatomic, readonly) NSRange range;
+ (instancetype)rangeWithNSRange:(NSRange)range;
@end
#endif // PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSTEXTINPUTPLUGIN_H_

109
Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.h.meta


fileFormatVersion: 2
guid: e051adc12d2a04dffa17a9ee58501006
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 0
Exclude Editor: 0
Exclude Linux: 0
Exclude Linux64: 0
Exclude LinuxUniversal: 0
Exclude OSXUniversal: 0
Exclude WebGL: 0
Exclude Win: 0
Exclude Win64: 0
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 1
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 1
settings: {}
- first:
Editor: Editor
second:
enabled: 1
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 1
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 1
settings:
CPU: x86_64
- first:
Standalone: LinuxUniversal
second:
enabled: 1
settings: {}
- first:
Standalone: OSXUniversal
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 1
settings:
CPU: AnyCPU
- first:
WebGL: WebGL
second:
enabled: 1
settings: {}
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CompileFlags: -fno-objc-arc
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

788
Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.mm


#include "UIWidgetsTextInputPlugin.h"
#include "UIWidgetsMessageManager.h"
#include <Foundation/Foundation.h>
#include <UIKit/UIKit.h>
static const char _kTextAffinityDownstream[] = "TextAffinity.downstream";
static const char _kTextAffinityUpstream[] = "TextAffinity.upstream";
static UIKeyboardType ToUIKeyboardType(NSDictionary* type) {
NSString* inputType = type[@"name"];
if ([inputType isEqualToString:@"TextInputType.text"])
return UIKeyboardTypeDefault;
if ([inputType isEqualToString:@"TextInputType.multiline"])
return UIKeyboardTypeDefault;
if ([inputType isEqualToString:@"TextInputType.number"]) {
if ([type[@"signed"] boolValue])
return UIKeyboardTypeNumbersAndPunctuation;
return UIKeyboardTypeDecimalPad;
}
if ([inputType isEqualToString:@"TextInputType.phone"])
return UIKeyboardTypePhonePad;
if ([inputType isEqualToString:@"TextInputType.emailAddress"])
return UIKeyboardTypeEmailAddress;
if ([inputType isEqualToString:@"TextInputType.url"])
return UIKeyboardTypeURL;
return UIKeyboardTypeDefault;
}
static UITextAutocapitalizationType ToUITextAutoCapitalizationType(NSDictionary* type) {
NSString* textCapitalization = type[@"textCapitalization"];
if ([textCapitalization isEqualToString:@"TextCapitalization.characters"]) {
return UITextAutocapitalizationTypeAllCharacters;
} else if ([textCapitalization isEqualToString:@"TextCapitalization.sentences"]) {
return UITextAutocapitalizationTypeSentences;
} else if ([textCapitalization isEqualToString:@"TextCapitalization.words"]) {
return UITextAutocapitalizationTypeWords;
}
return UITextAutocapitalizationTypeNone;
}
static UIReturnKeyType ToUIReturnKeyType(NSString* inputType) {
// Where did the term "unspecified" come from? iOS has a "default" and Android
// has "unspecified." These 2 terms seem to mean the same thing but we need
// to pick just one. "unspecified" was chosen because "default" is often a
// reserved word in languages with switch statements (dart, java, etc).
if ([inputType isEqualToString:@"TextInputAction.unspecified"])
return UIReturnKeyDefault;
if ([inputType isEqualToString:@"TextInputAction.done"])
return UIReturnKeyDone;
if ([inputType isEqualToString:@"TextInputAction.go"])
return UIReturnKeyGo;
if ([inputType isEqualToString:@"TextInputAction.send"])
return UIReturnKeySend;
if ([inputType isEqualToString:@"TextInputAction.search"])
return UIReturnKeySearch;
if ([inputType isEqualToString:@"TextInputAction.next"])
return UIReturnKeyNext;
if (@available(iOS 9.0, *))
if ([inputType isEqualToString:@"TextInputAction.continueAction"])
return UIReturnKeyContinue;
if ([inputType isEqualToString:@"TextInputAction.join"])
return UIReturnKeyJoin;
if ([inputType isEqualToString:@"TextInputAction.route"])
return UIReturnKeyRoute;
if ([inputType isEqualToString:@"TextInputAction.emergencyCall"])
return UIReturnKeyEmergencyCall;
if ([inputType isEqualToString:@"TextInputAction.newline"])
return UIReturnKeyDefault;
// Present default key if bad input type is given.
return UIReturnKeyDefault;
}
#pragma mark - UIWidgetsTextPosition
@implementation UIWidgetsTextPosition
+ (instancetype)positionWithIndex:(NSUInteger)index {
return [[[UIWidgetsTextPosition alloc] initWithIndex:index] autorelease];
}
- (instancetype)initWithIndex:(NSUInteger)index {
self = [super init];
if (self) {
_index = index;
}
return self;
}
@end
#pragma mark - UIWidgetsTextRange
@implementation UIWidgetsTextRange
+ (instancetype)rangeWithNSRange:(NSRange)range {
return [[[UIWidgetsTextRange alloc] initWithNSRange:range] autorelease];
}
- (instancetype)initWithNSRange:(NSRange)range {
self = [super init];
if (self) {
_range = range;
}
return self;
}
- (UITextPosition*)start {
return [UIWidgetsTextPosition positionWithIndex:self.range.location];
}
- (UITextPosition*)end {
return [UIWidgetsTextPosition positionWithIndex:self.range.location + self.range.length];
}
- (BOOL)isEmpty {
return self.range.length == 0;
}
- (id)copyWithZone:(NSZone*)zone {
return [[UIWidgetsTextRange allocWithZone:zone] initWithNSRange:self.range];
}
@end
@interface UIWidgetsTextInputView : UIView <UITextInput>
// UITextInput
@property(nonatomic, readonly) NSMutableString* text;
@property(nonatomic, readonly) NSMutableString* markedText;
@property(readwrite, copy) UITextRange* selectedTextRange;
@property(nonatomic, strong) UITextRange* markedTextRange;
@property(nonatomic, copy) NSDictionary* markedTextStyle;
@property(nonatomic, assign) id<UITextInputDelegate> inputDelegate;
// UITextInputTraits
@property(nonatomic) UITextAutocapitalizationType autocapitalizationType;
@property(nonatomic) UITextAutocorrectionType autocorrectionType;
@property(nonatomic) UITextSpellCheckingType spellCheckingType;
@property(nonatomic) BOOL enablesReturnKeyAutomatically;
@property(nonatomic) UIKeyboardAppearance keyboardAppearance;
@property(nonatomic) UIKeyboardType keyboardType;
@property(nonatomic) UIReturnKeyType returnKeyType;
@property(nonatomic, getter=isSecureTextEntry) BOOL secureTextEntry;
@property(nonatomic, assign) id<UIWidgetsTextInputDelegate> textInputDelegate;
@end
@implementation UIWidgetsTextInputView {
int _textInputClient;
const char* _selectionAffinity;
UIWidgetsTextRange* _selectedTextRange;
}
@synthesize tokenizer = _tokenizer;
- (instancetype)init {
self = [super init];
if (self) {
_textInputClient = 0;
_selectionAffinity = _kTextAffinityUpstream;
// UITextInput
_text = [[NSMutableString alloc] init];
_markedText = [[NSMutableString alloc] init];
_selectedTextRange = [[UIWidgetsTextRange alloc] initWithNSRange:NSMakeRange(0, 0)];
// UITextInputTraits
_autocapitalizationType = UITextAutocapitalizationTypeSentences;
_autocorrectionType = UITextAutocorrectionTypeDefault;
_spellCheckingType = UITextSpellCheckingTypeDefault;
_enablesReturnKeyAutomatically = NO;
_keyboardAppearance = UIKeyboardAppearanceDefault;
_keyboardType = UIKeyboardTypeDefault;
_returnKeyType = UIReturnKeyDone;
_secureTextEntry = NO;
}
return self;
}
- (void)dealloc {
[_text release];
[_markedText release];
[_markedTextRange release];
[_selectedTextRange release];
[_tokenizer release];
[super dealloc];
}
- (void)setTextInputClient:(int)client {
_textInputClient = client;
}
- (void)setTextInputState:(NSDictionary*)state {
NSString* newText = state[@"text"];
BOOL textChanged = ![self.text isEqualToString:newText];
if (textChanged) {
[self.inputDelegate textWillChange:self];
[self.text setString:newText];
}
NSInteger selectionBase = [state[@"selectionBase"] intValue];
NSInteger selectionExtent = [state[@"selectionExtent"] intValue];
NSRange selectedRange = [self clampSelection:NSMakeRange(MIN(selectionBase, selectionExtent),
ABS(selectionBase - selectionExtent))
forText:self.text];
NSRange oldSelectedRange = [(UIWidgetsTextRange*)self.selectedTextRange range];
if (selectedRange.location != oldSelectedRange.location ||
selectedRange.length != oldSelectedRange.length) {
[self.inputDelegate selectionWillChange:self];
[self setSelectedTextRange:[UIWidgetsTextRange rangeWithNSRange:selectedRange]
updateEditingState:NO];
_selectionAffinity = _kTextAffinityDownstream;
if ([state[@"selectionAffinity"] isEqualToString:@(_kTextAffinityUpstream)])
_selectionAffinity = _kTextAffinityUpstream;
[self.inputDelegate selectionDidChange:self];
}
NSInteger composingBase = [state[@"composingBase"] intValue];
NSInteger composingExtent = [state[@"composingExtent"] intValue];
NSRange composingRange = [self clampSelection:NSMakeRange(MIN(composingBase, composingExtent),
ABS(composingBase - composingExtent))
forText:self.text];
self.markedTextRange =
composingRange.length > 0 ? [UIWidgetsTextRange rangeWithNSRange:composingRange] : nil;
if (textChanged) {
[self.inputDelegate textDidChange:self];
// For consistency with Android behavior, send an update to the framework.
[self updateEditingState];
}
}
- (NSRange)clampSelection:(NSRange)range forText:(NSString*)text {
int start = MIN(MAX(range.location, 0), text.length);
int length = MIN(range.length, text.length - start);
return NSMakeRange(start, length);
}
#pragma mark - UIResponder Overrides
- (BOOL)canBecomeFirstResponder {
return YES;
}
#pragma mark - UITextInput Overrides
- (id<UITextInputTokenizer>)tokenizer {
if (_tokenizer == nil) {
_tokenizer = [[UITextInputStringTokenizer alloc] initWithTextInput:self];
}
return _tokenizer;
}
- (UITextRange*)selectedTextRange {
return [[_selectedTextRange copy] autorelease];
}
- (void)setSelectedTextRange:(UITextRange*)selectedTextRange {
[self setSelectedTextRange:selectedTextRange updateEditingState:YES];
}
- (void)setSelectedTextRange:(UITextRange*)selectedTextRange updateEditingState:(BOOL)update {
if (_selectedTextRange != selectedTextRange) {
UITextRange* oldSelectedRange = _selectedTextRange;
_selectedTextRange = [selectedTextRange copy];
[oldSelectedRange release];
if (update)
[self updateEditingState];
}
}
- (id)insertDictationResultPlaceholder {
return @"";
}
- (void)removeDictationResultPlaceholder:(id)placeholder willInsertResult:(BOOL)willInsertResult {
}
- (NSString*)textInRange:(UITextRange*)range {
NSRange textRange = ((UIWidgetsTextRange*)range).range;
return [self.text substringWithRange:textRange];
}
- (void)replaceRange:(UITextRange*)range withText:(NSString*)text {
NSRange replaceRange = ((UIWidgetsTextRange*)range).range;
NSRange selectedRange = _selectedTextRange.range;
// Adjust the text selection:
// * reduce the length by the intersection length
// * adjust the location by newLength - oldLength + intersectionLength
NSRange intersectionRange = NSIntersectionRange(replaceRange, selectedRange);
if (replaceRange.location <= selectedRange.location)
selectedRange.location += text.length - replaceRange.length;
if (intersectionRange.location != NSNotFound) {
selectedRange.location += intersectionRange.length;
selectedRange.length -= intersectionRange.length;
}
[self.text replaceCharactersInRange:[self clampSelection:replaceRange forText:self.text]
withString:text];
[self setSelectedTextRange:[UIWidgetsTextRange rangeWithNSRange:[self clampSelection:selectedRange
forText:self.text]]
updateEditingState:NO];
[self updateEditingState];
}
- (BOOL)shouldChangeTextInRange:(UITextRange*)range replacementText:(NSString*)text {
if (self.returnKeyType == UIReturnKeyDefault && [text isEqualToString:@"\n"]) {
[_textInputDelegate performAction:UIWidgetsTextInputActionNewline withClient:_textInputClient];
return YES;
}
if ([text isEqualToString:@"\n"]) {
UIWidgetsTextInputAction action;
switch (self.returnKeyType) {
case UIReturnKeyDefault:
action = UIWidgetsTextInputActionUnspecified;
break;
case UIReturnKeyDone:
action = UIWidgetsTextInputActionDone;
break;
case UIReturnKeyGo:
action = UIWidgetsTextInputActionGo;
break;
case UIReturnKeySend:
action = UIWidgetsTextInputActionSend;
break;
case UIReturnKeySearch:
case UIReturnKeyGoogle:
case UIReturnKeyYahoo:
action = UIWidgetsTextInputActionSearch;
break;
case UIReturnKeyNext:
action = UIWidgetsTextInputActionNext;
break;
case UIReturnKeyContinue:
action = UIWidgetsTextInputActionContinue;
break;
case UIReturnKeyJoin:
action = UIWidgetsTextInputActionJoin;
break;
case UIReturnKeyRoute:
action = UIWidgetsTextInputActionRoute;
break;
case UIReturnKeyEmergencyCall:
action = UIWidgetsTextInputActionEmergencyCall;
break;
}
[_textInputDelegate performAction:action withClient:_textInputClient];
return NO;
}
return YES;
}
- (void)setMarkedText:(NSString*)markedText selectedRange:(NSRange)markedSelectedRange {
NSRange selectedRange = _selectedTextRange.range;
NSRange markedTextRange = ((UIWidgetsTextRange*)self.markedTextRange).range;
if (markedText == nil)
markedText = @"";
if (markedTextRange.length > 0) {
// Replace text in the marked range with the new text.
[self replaceRange:self.markedTextRange withText:markedText];
markedTextRange.length = markedText.length;
} else {
// Replace text in the selected range with the new text.
[self replaceRange:_selectedTextRange withText:markedText];
markedTextRange = NSMakeRange(selectedRange.location, markedText.length);
}
self.markedTextRange =
markedTextRange.length > 0 ? [UIWidgetsTextRange rangeWithNSRange:markedTextRange] : nil;
NSUInteger selectionLocation = markedSelectedRange.location + markedTextRange.location;
selectedRange = NSMakeRange(selectionLocation, markedSelectedRange.length);
[self setSelectedTextRange:[UIWidgetsTextRange rangeWithNSRange:[self clampSelection:selectedRange
forText:self.text]]
updateEditingState:YES];
}
- (void)unmarkText {
self.markedTextRange = nil;
[self updateEditingState];
}
- (UITextRange*)textRangeFromPosition:(UITextPosition*)fromPosition
toPosition:(UITextPosition*)toPosition {
NSUInteger fromIndex = ((UIWidgetsTextPosition*)fromPosition).index;
NSUInteger toIndex = ((UIWidgetsTextPosition*)toPosition).index;
return [UIWidgetsTextRange rangeWithNSRange:NSMakeRange(fromIndex, toIndex - fromIndex)];
}
/** Returns the range of the character sequence at the specified index in the
* text. */
- (NSRange)rangeForCharacterAtIndex:(NSUInteger)index {
if (index < self.text.length)
return [self.text rangeOfComposedCharacterSequenceAtIndex:index];
return NSMakeRange(index, 0);
}
- (NSUInteger)decrementOffsetPosition:(NSUInteger)position {
return [self rangeForCharacterAtIndex:MAX(0, position - 1)].location;
}
- (NSUInteger)incrementOffsetPosition:(NSUInteger)position {
NSRange charRange = [self rangeForCharacterAtIndex:position];
return MIN(position + charRange.length, self.text.length);
}
- (UITextPosition*)positionFromPosition:(UITextPosition*)position offset:(NSInteger)offset {
NSUInteger offsetPosition = ((UIWidgetsTextPosition*)position).index;
if (offset >= 0) {
for (NSInteger i = 0; i < offset && offsetPosition < self.text.length; ++i)
offsetPosition = [self incrementOffsetPosition:offsetPosition];
} else {
for (NSInteger i = 0; i < ABS(offset) && offsetPosition > 0; ++i)
offsetPosition = [self decrementOffsetPosition:offsetPosition];
}
return [UIWidgetsTextPosition positionWithIndex:offsetPosition];
}
- (UITextPosition*)positionFromPosition:(UITextPosition*)position
inDirection:(UITextLayoutDirection)direction
offset:(NSInteger)offset {
// TODO(cbracken) Add RTL handling.
switch (direction) {
case UITextLayoutDirectionLeft:
case UITextLayoutDirectionUp:
return [self positionFromPosition:position offset:offset * -1];
case UITextLayoutDirectionRight:
case UITextLayoutDirectionDown:
return [self positionFromPosition:position offset:1];
}
}
- (UITextPosition*)beginningOfDocument {
return [UIWidgetsTextPosition positionWithIndex:0];
}
- (UITextPosition*)endOfDocument {
return [UIWidgetsTextPosition positionWithIndex:self.text.length];
}
- (NSComparisonResult)comparePosition:(UITextPosition*)position toPosition:(UITextPosition*)other {
NSUInteger positionIndex = ((UIWidgetsTextPosition*)position).index;
NSUInteger otherIndex = ((UIWidgetsTextPosition*)other).index;
if (positionIndex < otherIndex)
return NSOrderedAscending;
if (positionIndex > otherIndex)
return NSOrderedDescending;
return NSOrderedSame;
}
- (NSInteger)offsetFromPosition:(UITextPosition*)from toPosition:(UITextPosition*)toPosition {
return ((UIWidgetsTextPosition*)toPosition).index - ((UIWidgetsTextPosition*)from).index;
}
- (UITextPosition*)positionWithinRange:(UITextRange*)range
farthestInDirection:(UITextLayoutDirection)direction {
NSUInteger index;
switch (direction) {
case UITextLayoutDirectionLeft:
case UITextLayoutDirectionUp:
index = ((UIWidgetsTextPosition*)range.start).index;
break;
case UITextLayoutDirectionRight:
case UITextLayoutDirectionDown:
index = ((UIWidgetsTextPosition*)range.end).index;
break;
}
return [UIWidgetsTextPosition positionWithIndex:index];
}
- (UITextRange*)characterRangeByExtendingPosition:(UITextPosition*)position
inDirection:(UITextLayoutDirection)direction {
NSUInteger positionIndex = ((UIWidgetsTextPosition*)position).index;
NSUInteger startIndex;
NSUInteger endIndex;
switch (direction) {
case UITextLayoutDirectionLeft:
case UITextLayoutDirectionUp:
startIndex = [self decrementOffsetPosition:positionIndex];
endIndex = positionIndex;
break;
case UITextLayoutDirectionRight:
case UITextLayoutDirectionDown:
startIndex = positionIndex;
endIndex = [self incrementOffsetPosition:positionIndex];
break;
}
return [UIWidgetsTextRange rangeWithNSRange:NSMakeRange(startIndex, endIndex - startIndex)];
}
#pragma mark - UITextInput text direction handling
- (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition*)position
inDirection:(UITextStorageDirection)direction {
return UITextWritingDirectionNatural;
}
- (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection
forRange:(UITextRange*)range {
}
#pragma mark - UITextInput cursor, selection rect handling
- (CGRect)firstRectForRange:(UITextRange*)range {
return CGRectZero;
}
- (CGRect)caretRectForPosition:(UITextPosition*)position {
return CGRectZero;
}
- (UITextPosition*)closestPositionToPoint:(CGPoint)point {
NSUInteger currentIndex = ((UIWidgetsTextPosition*)_selectedTextRange.start).index;
return [UIWidgetsTextPosition positionWithIndex:currentIndex];
}
- (NSArray*)selectionRectsForRange:(UITextRange*)range {
return @[];
}
- (UITextPosition*)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange*)range {
return range.start;
}
- (UITextRange*)characterRangeAtPoint:(CGPoint)point {
NSUInteger currentIndex = ((UIWidgetsTextPosition*)_selectedTextRange.start).index;
return [UIWidgetsTextRange rangeWithNSRange:[self rangeForCharacterAtIndex:currentIndex]];
}
#pragma mark - UIKeyInput Overrides
- (void)updateEditingState {
NSUInteger selectionBase = ((UIWidgetsTextPosition*)_selectedTextRange.start).index;
NSUInteger selectionExtent = ((UIWidgetsTextPosition*)_selectedTextRange.end).index;
NSUInteger composingBase = 0;
NSUInteger composingExtent = 0;
if (self.markedTextRange != nil) {
composingBase = ((UIWidgetsTextPosition*)self.markedTextRange.start).index;
composingExtent = ((UIWidgetsTextPosition*)self.markedTextRange.end).index;
}
[_textInputDelegate updateEditingClient:_textInputClient
withState:@{
@"selectionBase" : @(selectionBase),
@"selectionExtent" : @(selectionExtent),
@"selectionAffinity" : @(_selectionAffinity),
@"selectionIsDirectional" : @(false),
@"composingBase" : @(composingBase),
@"composingExtent" : @(composingExtent),
@"text" : [NSString stringWithString:self.text],
}];
}
- (BOOL)hasText {
return self.text.length > 0;
}
- (void)insertText:(NSString*)text {
_selectionAffinity = _kTextAffinityDownstream;
[self replaceRange:_selectedTextRange withText:text];
}
- (void)deleteBackward {
_selectionAffinity = _kTextAffinityDownstream;
if (!_selectedTextRange.isEmpty)
[self replaceRange:_selectedTextRange withText:@""];
}
@end
/**
* Hides `UIWidgetsTextInputView` from iOS accessibility system so it
* does not show up twice, once where it is in the `UIView` hierarchy,
* and a second time as part of the `SemanticsObject` hierarchy.
*/
@interface UIWidgetsTextInputViewAccessibilityHider : UIView {
}
@end
@implementation UIWidgetsTextInputViewAccessibilityHider {
}
- (BOOL)accessibilityElementsHidden {
return YES;
}
@end
@implementation UIWidgetsTextInputPlugin {
UIWidgetsTextInputView* _view;
UIWidgetsTextInputViewAccessibilityHider* _inputHider;
}
@synthesize textInputDelegate = _textInputDelegate;
- (instancetype)init {
self = [super init];
if (self) {
_view = [[UIWidgetsTextInputView alloc] init];
_inputHider = [[UIWidgetsTextInputViewAccessibilityHider alloc] init];
}
return self;
}
- (void)dealloc {
[self hideTextInput];
[_view release];
[_inputHider release];
[super dealloc];
}
- (UIView<UITextInput>*)textInputView {
return _view;
}
- (void)showTextInput {
NSAssert([UIApplication sharedApplication].keyWindow != nullptr,
@"The application must have a key window since the keyboard client "
@"must be part of the responder chain to function");
_view.textInputDelegate = _textInputDelegate;
[_inputHider addSubview:_view];
[[UIApplication sharedApplication].keyWindow addSubview:_inputHider];
[_view becomeFirstResponder];
}
- (void)hideTextInput {
[_view resignFirstResponder];
[_view removeFromSuperview];
[_inputHider removeFromSuperview];
}
- (void)setTextInputClient:(int)client withConfiguration:(NSDictionary*)configuration {
NSDictionary* inputType = configuration[@"inputType"];
NSString* keyboardAppearance = configuration[@"keyboardAppearance"];
_view.keyboardType = ToUIKeyboardType(inputType);
_view.returnKeyType = ToUIReturnKeyType(configuration[@"inputAction"]);
_view.autocapitalizationType = ToUITextAutoCapitalizationType(configuration);
if ([keyboardAppearance isEqualToString:@"Brightness.dark"]) {
_view.keyboardAppearance = UIKeyboardAppearanceDark;
} else if ([keyboardAppearance isEqualToString:@"Brightness.light"]) {
_view.keyboardAppearance = UIKeyboardAppearanceLight;
} else {
_view.keyboardAppearance = UIKeyboardAppearanceDefault;
}
_view.secureTextEntry = [configuration[@"obscureText"] boolValue];
NSString* autocorrect = configuration[@"autocorrect"];
_view.autocorrectionType = autocorrect && ![autocorrect boolValue]
? UITextAutocorrectionTypeNo
: UITextAutocorrectionTypeDefault;
[_view setTextInputClient:client];
[_view reloadInputViews];
}
- (void)setTextInputEditingState:(NSDictionary*)state {
[_view setTextInputState:state];
}
- (void)clearTextInputClient {
[_view setTextInputClient:0];
}
+ (instancetype)sharedInstance {
static UIWidgetsTextInputPlugin *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[UIWidgetsTextInputPlugin alloc] init];
sharedInstance.textInputDelegate = [[DefaultUIWidgetsTextInputDelegate alloc] init];
});
return sharedInstance;
}
@end
@implementation DefaultUIWidgetsTextInputDelegate
- (void)updateEditingClient:(int)client withState:(NSDictionary*)state {
UIWidgetsMethodMessage(@"TextInput", @"TextInputClient.updateEditingState", @[@(client), state]);
}
- (void)performAction:(UIWidgetsTextInputAction)action withClient:(int)client {
NSString* actionString;
switch (action) {
case UIWidgetsTextInputActionUnspecified:
// Where did the term "unspecified" come from? iOS has a "default" and Android
// has "unspecified." These 2 terms seem to mean the same thing but we need
// to pick just one. "unspecified" was chosen because "default" is often a
// reserved word in languages with switch statements (dart, java, etc).
actionString = @"TextInputAction.unspecified";
break;
case UIWidgetsTextInputActionDone:
actionString = @"TextInputAction.done";
break;
case UIWidgetsTextInputActionGo:
actionString = @"TextInputAction.go";
break;
case UIWidgetsTextInputActionSend:
actionString = @"TextInputAction.send";
break;
case UIWidgetsTextInputActionSearch:
actionString = @"TextInputAction.search";
break;
case UIWidgetsTextInputActionNext:
actionString = @"TextInputAction.next";
break;
case UIWidgetsTextInputActionContinue:
actionString = @"TextInputAction.continue";
break;
case UIWidgetsTextInputActionJoin:
actionString = @"TextInputAction.join";
break;
case UIWidgetsTextInputActionRoute:
actionString = @"TextInputAction.route";
break;
case UIWidgetsTextInputActionEmergencyCall:
actionString = @"TextInputAction.emergencyCall";
break;
case UIWidgetsTextInputActionNewline:
actionString = @"TextInputAction.newline";
break;
}
UIWidgetsMethodMessage(@"TextInput", @"TextInputClient.performAction", @[@(client), actionString]);
}
@end
extern "C" {
void UIWidgetsTextInputShow() {
[[UIWidgetsTextInputPlugin sharedInstance] showTextInput];
}
void UIWidgetsTextInputHide() {
[[UIWidgetsTextInputPlugin sharedInstance] hideTextInput];
}
void UIWidgetsTextInputSetClient(int client, const char* configurationJson) {
NSError *jsonError = nil;
NSString *nsJsonString=[NSString stringWithUTF8String:configurationJson];
NSData *objectData = [nsJsonString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:objectData
options:NSJSONReadingMutableContainers
error:&jsonError];
[[UIWidgetsTextInputPlugin sharedInstance] setTextInputClient:client withConfiguration:json];
}
void UIWidgetsTextInputSetTextInputEditingState(const char* jsonText) {
NSError *jsonError;
NSString *nsJsonString=[NSString stringWithUTF8String:jsonText];
NSData *objectData = [nsJsonString dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *args = [NSJSONSerialization JSONObjectWithData:objectData
options:NSJSONReadingMutableContainers
error:&jsonError];
[[UIWidgetsTextInputPlugin sharedInstance] setTextInputEditingState:args];
}
void UIWidgetsTextInputClearTextInputClient() {
[[UIWidgetsTextInputPlugin sharedInstance] clearTextInputClient];
}
}

104
Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.mm.meta


fileFormatVersion: 2
guid: 4bb403cfcbd364d8b830518ed5a17166
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
'': Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 1
Exclude Linux: 1
Exclude Linux64: 1
Exclude LinuxUniversal: 1
Exclude OSXUniversal: 1
Exclude WebGL: 1
Exclude Win: 1
Exclude Win64: 1
Exclude iOS: 0
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
CPU: AnyCPU
DefaultValueInitialized: true
OS: AnyOS
- first:
Facebook: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Facebook: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Linux
second:
enabled: 0
settings:
CPU: x86
- first:
Standalone: Linux64
second:
enabled: 0
settings:
CPU: x86_64
- first:
Standalone: OSXUniversal
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
Standalone: Win64
second:
enabled: 0
settings:
CPU: AnyCPU
- first:
iPhone: iOS
second:
enabled: 1
settings:
AddToEmbeddedBinaries: false
CompileFlags: -fno-objc-arc
FrameworkDependencies:
- first:
tvOS: tvOS
second:
enabled: 1
settings: {}
userData:
assetBundleName:
assetBundleVariant:

8
Runtime/Plugins/platform/android.meta


fileFormatVersion: 2
guid: f583d2115d43b4c77b104bc35805fefb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

45
Runtime/Plugins/platform/android/UIWidgetsMessageManager.java


package com.unity.uiwidgets.plugin;
import android.util.Log;
import com.unity3d.player.UnityPlayer;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
import static com.unity.uiwidgets.plugin.Utils.TAG;
public class UIWidgetsMessageManager {
private static UIWidgetsMessageManager _instance;
private String gameObjectName;
public static UIWidgetsMessageManager getInstance() {
if (_instance == null) {
_instance = new UIWidgetsMessageManager();
}
return _instance;
}
public void SetObjectName(String name) {
gameObjectName = name;
}
public void UIWidgetsMethodMessage(String channel, String method, List<Object> args) {
JSONObject object = new JSONObject();
try {
object.put("channel", channel);
object.put("method", method);
object.put("args", new JSONArray(args));
UnityPlayer.UnitySendMessage(gameObjectName, "OnUIWidgetsMethodMessage", object.toString());
} catch (JSONException e) {
Log.e(TAG, "error parse json", e);
}
}
}

31
Runtime/Plugins/platform/android/UIWidgetsMessageManager.java.meta


fileFormatVersion: 2
guid: 6349deb4a47a44388806597a144b9bfd
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

5
Runtime/Plugins/platform/android/Utils.java


package com.unity.uiwidgets.plugin;
public class Utils {
public static final String TAG = "UIWidgets";
}

31
Runtime/Plugins/platform/android/Utils.java.meta


fileFormatVersion: 2
guid: 6da51e571970348b59198a520437c8b0
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

8
Runtime/Plugins/platform/android/editing.meta


fileFormatVersion: 2
guid: 3483e1b60ba954551952665afcccb2c8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

215
Runtime/Plugins/platform/android/editing/InputConnectionAdaptor.java


// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package com.unity.uiwidgets.plugin.editing;
import android.content.Context;
import android.text.Editable;
import android.text.Selection;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import com.unity.uiwidgets.plugin.UIWidgetsMessageManager;
import java.util.Arrays;
import java.util.HashMap;
class InputConnectionAdaptor extends BaseInputConnection {
private View mTextInputView;
private final int mClient;
private final Editable mEditable;
private int mBatchCount;
private InputMethodManager mImm;
public InputConnectionAdaptor(View view, int client,
Editable editable) {
super(view, true);
mTextInputView = view;
mClient = client;
mEditable = editable;
mBatchCount = 0;
mImm = (InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
}
// Send the current state of the editable to Flutter.
private void updateEditingState() {
// If the IME is in the middle of a batch edit, then wait until it completes.
if (mBatchCount > 0)
return;
int selectionStart = Selection.getSelectionStart(mEditable);
int selectionEnd = Selection.getSelectionEnd(mEditable);
int composingStart = BaseInputConnection.getComposingSpanStart(mEditable);
int composingEnd = BaseInputConnection.getComposingSpanEnd(mEditable);
mImm.updateSelection(mTextInputView,
selectionStart, selectionEnd,
composingStart, composingEnd);
HashMap<Object, Object> state = new HashMap<Object, Object>();
state.put("text", mEditable.toString());
state.put("selectionBase", selectionStart);
state.put("selectionExtent", selectionEnd);
state.put("composingBase", composingStart);
state.put("composingExtent", composingEnd);
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.updateEditingState",
Arrays.asList(mClient, state));
}
@Override
public Editable getEditable() {
return mEditable;
}
@Override
public boolean beginBatchEdit() {
mBatchCount++;
return super.beginBatchEdit();
}
@Override
public boolean endBatchEdit() {
boolean result = super.endBatchEdit();
mBatchCount--;
updateEditingState();
return result;
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
boolean result = super.commitText(text, newCursorPosition);
updateEditingState();
return result;
}
@Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
if (Selection.getSelectionStart(mEditable) == -1)
return true;
boolean result = super.deleteSurroundingText(beforeLength, afterLength);
updateEditingState();
return result;
}
@Override
public boolean setComposingRegion(int start, int end) {
boolean result = super.setComposingRegion(start, end);
updateEditingState();
return result;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
boolean result;
if (text.length() == 0) {
result = super.commitText(text, newCursorPosition);
} else {
result = super.setComposingText(text, newCursorPosition);
}
updateEditingState();
return result;
}
@Override
public boolean setSelection(int start, int end) {
boolean result = super.setSelection(start, end);
updateEditingState();
return result;
}
@Override
public boolean sendKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
int selStart = Selection.getSelectionStart(mEditable);
int selEnd = Selection.getSelectionEnd(mEditable);
if (selEnd > selStart) {
// Delete the selection.
Selection.setSelection(mEditable, selStart);
mEditable.delete(selStart, selEnd);
updateEditingState();
return true;
} else if (selStart > 0) {
// Delete to the left of the cursor.
int newSel = Math.max(selStart - 1, 0);
Selection.setSelection(mEditable, newSel);
mEditable.delete(newSel, selStart);
updateEditingState();
return true;
}
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_LEFT) {
int selStart = Selection.getSelectionStart(mEditable);
int newSel = Math.max(selStart - 1, 0);
setSelection(newSel, newSel);
return true;
} else if (event.getKeyCode() == KeyEvent.KEYCODE_DPAD_RIGHT) {
int selStart = Selection.getSelectionStart(mEditable);
int newSel = Math.min(selStart + 1, mEditable.length());
setSelection(newSel, newSel);
return true;
} else {
// Enter a character.
int character = event.getUnicodeChar();
if (character != 0) {
int selStart = Math.max(0, Selection.getSelectionStart(mEditable));
int selEnd = Math.max(0, Selection.getSelectionEnd(mEditable));
if (selEnd != selStart)
mEditable.delete(selStart, selEnd);
mEditable.insert(selStart, String.valueOf((char) character));
setSelection(selStart + 1, selStart + 1);
updateEditingState();
}
return true;
}
}
return false;
}
@Override
public boolean performEditorAction(int actionCode) {
switch (actionCode) {
// TODO(mattcarroll): is newline an appropriate action for "none"?
case EditorInfo.IME_ACTION_NONE:
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
Arrays.asList(mClient, "TextInputAction.newline"));
break;
case EditorInfo.IME_ACTION_UNSPECIFIED:
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
Arrays.asList(mClient, "TextInputAction.unspecified"));
break;
case EditorInfo.IME_ACTION_GO:
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
Arrays.asList(mClient, "TextInputAction.go"));
break;
case EditorInfo.IME_ACTION_SEARCH:
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
Arrays.asList(mClient, "TextInputAction.search"));
break;
case EditorInfo.IME_ACTION_SEND:
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
Arrays.asList(mClient, "TextInputAction.send"));
break;
case EditorInfo.IME_ACTION_NEXT:
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
Arrays.asList(mClient, "TextInputAction.next"));
break;
case EditorInfo.IME_ACTION_PREVIOUS:
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
Arrays.asList(mClient, "TextInputAction.previous"));
break;
default:
case EditorInfo.IME_ACTION_DONE:
UIWidgetsMessageManager.getInstance().UIWidgetsMethodMessage("TextInput", "TextInputClient.performAction",
Arrays.asList(mClient, "TextInputAction.done"));
break;
}
return true;
}
}

31
Runtime/Plugins/platform/android/editing/InputConnectionAdaptor.java.meta


fileFormatVersion: 2
guid: ee71437b1f6244476b59360f556db8b7
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

257
Runtime/Plugins/platform/android/editing/TextInputPlugin.java


// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package com.unity.uiwidgets.plugin.editing;
import android.content.Context;
import android.text.Editable;
import android.text.InputType;
import android.text.Selection;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import com.unity3d.player.UnityPlayer;
import org.json.JSONException;
import org.json.JSONObject;
import static com.unity.uiwidgets.plugin.Utils.TAG;
public class TextInputPlugin {
private final TextInputView mView;
private final InputMethodManager mImm;
private int mClient = 0;
private JSONObject mConfiguration;
private Editable mEditable;
private boolean mRestartInputPending;
private static TextInputPlugin _instance;
public static TextInputPlugin getInstance() {
if (_instance == null) {
_instance = new TextInputPlugin();
}
return _instance;
}
public TextInputPlugin() {
ViewGroup contentView = (ViewGroup)UnityPlayer.currentActivity.findViewById(android.R.id.content);
mView = new TextInputView(UnityPlayer.currentActivity);
contentView.addView(mView, 0, 0);
mImm = (InputMethodManager) mView.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
}
public static void show() {
UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
TextInputPlugin plugin = getInstance();
plugin.showTextInput(plugin.mView);
}
});
}
public static void hide() {
UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
TextInputPlugin plugin = getInstance();
plugin.hideTextInput(plugin.mView);
}
});
}
public static void setClient(int client, String configurationJson) {
UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
JSONObject configuration = new JSONObject(configurationJson);
TextInputPlugin plugin = getInstance();
plugin.setTextInputClient(plugin.mView, client, configuration);
} catch (JSONException e) {
Log.e(TAG, "error parse json", e);
}
}
});
}
public static void setEditingState(String stateJson) {
UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
TextInputPlugin plugin = getInstance();
JSONObject state = new JSONObject(stateJson);
plugin.setTextInputEditingState(plugin.mView, state);
} catch (JSONException e) {
Log.e(TAG, "error parse json", e);
}
}
});
}
public static void clearClient() {
UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
TextInputPlugin plugin = getInstance();
plugin.clearTextInputClient();
}
});
}
private static int inputTypeFromTextInputType(JSONObject type, boolean obscureText,
boolean autocorrect, String textCapitalization) throws JSONException {
String inputType = type.getString("name");
if (inputType.equals("TextInputType.datetime")) return InputType.TYPE_CLASS_DATETIME;
if (inputType.equals("TextInputType.number")) {
int textType = InputType.TYPE_CLASS_NUMBER;
if (type.optBoolean("signed")) textType |= InputType.TYPE_NUMBER_FLAG_SIGNED;
if (type.optBoolean("decimal")) textType |= InputType.TYPE_NUMBER_FLAG_DECIMAL;
return textType;
}
if (inputType.equals("TextInputType.phone")) return InputType.TYPE_CLASS_PHONE;
int textType = InputType.TYPE_CLASS_TEXT;
if (inputType.equals("TextInputType.multiline"))
textType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE;
else if (inputType.equals("TextInputType.emailAddress"))
textType |= InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
else if (inputType.equals("TextInputType.url"))
textType |= InputType.TYPE_TEXT_VARIATION_URI;
if (obscureText) {
// Note: both required. Some devices ignore TYPE_TEXT_FLAG_NO_SUGGESTIONS.
textType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
textType |= InputType.TYPE_TEXT_VARIATION_PASSWORD;
} else {
if (autocorrect) textType |= InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
}
if (textCapitalization.equals("TextCapitalization.characters")) {
textType |= InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
} else if (textCapitalization.equals("TextCapitalization.words")) {
textType |= InputType.TYPE_TEXT_FLAG_CAP_WORDS;
} else if (textCapitalization.equals("TextCapitalization.sentences")) {
textType |= InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
}
return textType;
}
private static int inputActionFromTextInputAction(String inputAction) {
switch (inputAction) {
case "TextInputAction.newline":
return EditorInfo.IME_ACTION_NONE;
case "TextInputAction.none":
return EditorInfo.IME_ACTION_NONE;
case "TextInputAction.unspecified":
return EditorInfo.IME_ACTION_UNSPECIFIED;
case "TextInputAction.done":
return EditorInfo.IME_ACTION_DONE;
case "TextInputAction.go":
return EditorInfo.IME_ACTION_GO;
case "TextInputAction.search":
return EditorInfo.IME_ACTION_SEARCH;
case "TextInputAction.send":
return EditorInfo.IME_ACTION_SEND;
case "TextInputAction.next":
return EditorInfo.IME_ACTION_NEXT;
case "TextInputAction.previous":
return EditorInfo.IME_ACTION_PREVIOUS;
default:
// Present default key if bad input type is given.
return EditorInfo.IME_ACTION_UNSPECIFIED;
}
}
public InputConnection createInputConnection(View view, EditorInfo outAttrs)
throws JSONException {
if (mClient == 0) return null;
outAttrs.inputType = inputTypeFromTextInputType(mConfiguration.getJSONObject("inputType"),
mConfiguration.optBoolean("obscureText"),
mConfiguration.optBoolean("autocorrect", true),
mConfiguration.getString("textCapitalization"));
outAttrs.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
int enterAction;
if (mConfiguration.isNull("inputAction")) {
// If an explicit input action isn't set, then default to none for multi-line fields
// and done for single line fields.
enterAction = (InputType.TYPE_TEXT_FLAG_MULTI_LINE & outAttrs.inputType) != 0
? EditorInfo.IME_ACTION_NONE
: EditorInfo.IME_ACTION_DONE;
} else {
enterAction = inputActionFromTextInputAction(mConfiguration.getString("inputAction"));
}
if (!mConfiguration.isNull("actionLabel")) {
outAttrs.actionLabel = mConfiguration.getString("actionLabel");
outAttrs.actionId = enterAction;
}
outAttrs.imeOptions |= enterAction;
InputConnectionAdaptor connection =
new InputConnectionAdaptor(view, mClient, mEditable);
outAttrs.initialSelStart = Selection.getSelectionStart(mEditable);
outAttrs.initialSelEnd = Selection.getSelectionEnd(mEditable);
return connection;
}
private void showTextInput(View view) {
view.requestFocus();
mImm.showSoftInput(view, 0);
}
private void hideTextInput(View view) {
mImm.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
}
private void setTextInputClient(View view, int client, JSONObject configuration) {
mClient = client;
mConfiguration = configuration;
mEditable = Editable.Factory.getInstance().newEditable("");
// setTextInputClient will be followed by a call to setTextInputEditingState.
// Do a restartInput at that time.
mRestartInputPending = true;
}
private void applyStateToSelection(JSONObject state) throws JSONException {
int selStart = state.getInt("selectionBase");
int selEnd = state.getInt("selectionExtent");
if (selStart >= 0 && selStart <= mEditable.length() && selEnd >= 0
&& selEnd <= mEditable.length()) {
Selection.setSelection(mEditable, selStart, selEnd);
} else {
Selection.removeSelection(mEditable);
}
}
private void setTextInputEditingState(View view, JSONObject state) throws JSONException {
if (!mRestartInputPending && state.getString("text").equals(mEditable.toString())) {
applyStateToSelection(state);
mImm.updateSelection(mView, Math.max(Selection.getSelectionStart(mEditable), 0),
Math.max(Selection.getSelectionEnd(mEditable), 0),
BaseInputConnection.getComposingSpanStart(mEditable),
BaseInputConnection.getComposingSpanEnd(mEditable));
} else {
mEditable.replace(0, mEditable.length(), state.getString("text"));
applyStateToSelection(state);
mImm.restartInput(view);
mRestartInputPending = false;
}
}
private void clearTextInputClient() {
mClient = 0;
}
}

31
Runtime/Plugins/platform/android/editing/TextInputPlugin.java.meta


fileFormatVersion: 2
guid: 867d39559763e4ea9b82b9a5987a1930
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

48
Runtime/Plugins/platform/android/editing/TextInputView.java


package com.unity.uiwidgets.plugin.editing;
import android.content.Context;
import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import org.json.JSONException;
import static com.unity.uiwidgets.plugin.Utils.TAG;
public class TextInputView extends View {
private InputConnection mLastInputConnection;
private final InputMethodManager mImm;
public TextInputView(Context context) {
super(context);
setFocusable(true);
setFocusableInTouchMode(true);
mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
}
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
Log.i(TAG, "onCreateInputConnection");
try {
mLastInputConnection = TextInputPlugin.getInstance().createInputConnection(this, outAttrs);
return mLastInputConnection;
} catch (JSONException e) {
Log.e(TAG, "Failed to create input connection", e);
return null;
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) {
if (mLastInputConnection != null && mImm.isAcceptingText()) {
mLastInputConnection.sendKeyEvent(event);
}
}
return super.onKeyDown(keyCode, event);
}
}

31
Runtime/Plugins/platform/android/editing/TextInputView.java.meta


fileFormatVersion: 2
guid: acd0b1bb7c09d4b5ca41554aa9c30257
PluginImporter:
externalObjects: {}
serializedVersion: 2
iconMap: {}
executionOrder: {}
defineConstraints: []
isPreloaded: 0
isOverridable: 1
isExplicitlyReferenced: 0
platformData:
- first:
Android: Android
second:
enabled: 1
settings: {}
- first:
Any:
second:
enabled: 0
settings: {}
- first:
Editor: Editor
second:
enabled: 0
settings:
DefaultValueInitialized: true
userData:
assetBundleName:
assetBundleVariant:

5
Runtime/external/.editorconfig


root = true
# For CSharp Only
[*.cs]
csharp_instance_members_qualify_members=none

8
Runtime/external/simplejson.meta


fileFormatVersion: 2
guid: 32a991dd6b6164b14976cfb586cc658f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

1001
Runtime/external/simplejson/SimpleJSON.cs
文件差异内容过多而无法显示
查看文件

11
Runtime/external/simplejson/SimpleJSON.cs.meta


fileFormatVersion: 2
guid: c1511f3e459f641589f4f62495cd64e6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
正在加载...
取消
保存