浏览代码
Merge branch 'keyboard' into 'master'
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 次删除
-
14Runtime/editor/editor_window.cs
-
4Runtime/engine/UIWidgetsPanel.cs
-
278Runtime/service/keyboard.cs
-
229Runtime/service/text_input.cs
-
2Runtime/service/text_input_utils.cs
-
5Runtime/ui/painting/canvas_impl.cs
-
1Runtime/ui/painting/txt/mesh_generator.cs
-
2Runtime/ui/window.cs
-
40Runtime/widgets/editable_text.cs
-
61Samples/UIWidgetSample/TextInputSample.cs
-
1UIWidgetCleanupPlugin.DotSettings
-
8Runtime/Plugins/platform.meta
-
108Runtime/engine/UIWidgetsMessageManager.cs
-
11Runtime/engine/UIWidgetsMessageManager.cs.meta
-
8Runtime/external.meta
-
8Runtime/Plugins/platform/ios.meta
-
6Runtime/Plugins/platform/ios/UIWidgetsMessageManager.h
-
109Runtime/Plugins/platform/ios/UIWidgetsMessageManager.h.meta
-
39Runtime/Plugins/platform/ios/UIWidgetsMessageManager.mm
-
104Runtime/Plugins/platform/ios/UIWidgetsMessageManager.mm.meta
-
32Runtime/Plugins/platform/ios/UIWidgetsTextInputDelegate.h
-
109Runtime/Plugins/platform/ios/UIWidgetsTextInputDelegate.h.meta
-
32Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.h
-
109Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.h.meta
-
788Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.mm
-
104Runtime/Plugins/platform/ios/UIWidgetsTextInputPlugin.mm.meta
-
8Runtime/Plugins/platform/android.meta
-
45Runtime/Plugins/platform/android/UIWidgetsMessageManager.java
-
31Runtime/Plugins/platform/android/UIWidgetsMessageManager.java.meta
-
5Runtime/Plugins/platform/android/Utils.java
-
31Runtime/Plugins/platform/android/Utils.java.meta
-
8Runtime/Plugins/platform/android/editing.meta
-
215Runtime/Plugins/platform/android/editing/InputConnectionAdaptor.java
-
31Runtime/Plugins/platform/android/editing/InputConnectionAdaptor.java.meta
-
257Runtime/Plugins/platform/android/editing/TextInputPlugin.java
-
31Runtime/Plugins/platform/android/editing/TextInputPlugin.java.meta
-
48Runtime/Plugins/platform/android/editing/TextInputView.java
-
31Runtime/Plugins/platform/android/editing/TextInputView.java.meta
-
5Runtime/external/.editorconfig
-
8Runtime/external/simplejson.meta
-
1001Runtime/external/simplejson/SimpleJSON.cs
-
11Runtime/external/simplejson/SimpleJSON.cs.meta
|
|||
fileFormatVersion: 2 |
|||
guid: e93b613e0d2074e979e1920d32dabdd7 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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
|
|||
|
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: efd031068fc9d41d6b4c165afaaa25ba |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: 910527b5287c8499e8381ae8b9787b4a |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
fileFormatVersion: 2 |
|||
guid: d8264d285bf7842f99ef80594606a5ba |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#ifndef PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSMESSAGEMANAGER_H_ |
|||
#define PLATFORM_IOS_FRAMEWORK_SOURCE_UIWIDGETSMESSAGEMANAGER_H_ |
|||
|
|||
void UIWidgetsMethodMessage(NSString* channel, NSString* method, NSArray *args); |
|||
|
|||
#endif |
|
|||
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: |
|
|||
#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); |
|||
} |
|||
} |
|
|||
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: |
|
|||
#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_ |
|
|||
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: |
|
|||
#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_ |
|
|||
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: |
|
|||
#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]; |
|||
} |
|||
} |
|||
|
|
|||
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: |
|
|||
fileFormatVersion: 2 |
|||
guid: f583d2115d43b4c77b104bc35805fefb |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
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); |
|||
} |
|||
} |
|||
} |
|
|||
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: |
|
|||
package com.unity.uiwidgets.plugin; |
|||
|
|||
public class Utils { |
|||
public static final String TAG = "UIWidgets"; |
|||
} |
|
|||
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: |
|
|||
fileFormatVersion: 2 |
|||
guid: 3483e1b60ba954551952665afcccb2c8 |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// 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; |
|||
} |
|||
} |
|
|||
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: |
|
|||
// 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; |
|||
} |
|||
} |
|
|||
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: |
|
|||
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); |
|||
} |
|||
} |
|
|||
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: |
|
|||
root = true |
|||
|
|||
# For CSharp Only |
|||
[*.cs] |
|||
csharp_instance_members_qualify_members=none |
|
|||
fileFormatVersion: 2 |
|||
guid: 32a991dd6b6164b14976cfb586cc658f |
|||
folderAsset: yes |
|||
DefaultImporter: |
|||
externalObjects: {} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
1001
Runtime/external/simplejson/SimpleJSON.cs
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: c1511f3e459f641589f4f62495cd64e6 |
|||
MonoImporter: |
|||
externalObjects: {} |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue