提交 d185552c 编写于 作者: A Adam Barth 提交者: GitHub

Implement TextInputPlugin on iOS (#3145)

上级 808009d4
......@@ -37,6 +37,9 @@ shared_library("flutter_framework_dylib") {
"framework/Source/FlutterJSONMessageListener.mm",
"framework/Source/FlutterPlatformPlugin.h",
"framework/Source/FlutterPlatformPlugin.mm",
"framework/Source/FlutterTextInputDelegate.h",
"framework/Source/FlutterTextInputPlugin.h",
"framework/Source/FlutterTextInputPlugin.mm",
"framework/Source/FlutterView.h",
"framework/Source/FlutterView.mm",
"framework/Source/FlutterViewController.mm",
......
......@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORM_PLUGIN_H_
#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORM_PLUGIN_H_
#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMPLUGIN_H_
#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMPLUGIN_H_
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterJSONMessageListener.h"
......@@ -20,4 +20,4 @@ extern const char* const kOverlayStyleUpdateNotificationKey;
} // namespace shell
#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORM_PLUGIN_H_
#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERPLATFORMPLUGIN_H_
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTDELEGATE_H_
#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTDELEGATE_H_
#import <Foundation/Foundation.h>
@protocol FlutterTextInputDelegate<NSObject>
- (void)updateEditingClient:(int)client withState:(NSDictionary*)state;
@end
#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTDELEGATE_H_
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTPLUGIN_H_
#define SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTPLUGIN_H_
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterJSONMessageListener.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
@interface FlutterTextInputPlugin : FlutterJSONMessageListener
@property(nonatomic, assign) id<FlutterTextInputDelegate> textInputDelegate;
@end
#endif // SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_FLUTTERTEXTINPUTPLUGIN_H_
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
#include <UIKit/UIKit.h>
#include <unicode/utf16.h>
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
static const char _kTextAffinityDownstream[] = "TextAffinity.downstream";
static const char _kTextAffinityUpstream[] = "TextAffinity.upstream";
static UIKeyboardType ToUIKeyboardType(NSString* inputType) {
if ([inputType isEqualToString:@"TextInputType.text"])
return UIKeyboardTypeDefault;
if ([inputType isEqualToString:@"TextInputType.number"])
return UIKeyboardTypeDecimalPad;
if ([inputType isEqualToString:@"TextInputType.phone"])
return UIKeyboardTypePhonePad;
return UIKeyboardTypeDefault;
}
@interface FlutterTextInputView : UIView<UIKeyInput>
@property(nonatomic, assign) id<FlutterTextInputDelegate> textInputDelegate;
@end
@implementation FlutterTextInputView {
int _textInputClient;
int _selectionBase;
int _selectionExtent;
const char* _selectionAffinity;
base::string16 _text;
}
@synthesize keyboardType = _keyboardType;
@synthesize textInputDelegate = _textInputDelegate;
- (instancetype)init {
self = [super init];
if (self) {
_selectionBase = -1;
_selectionExtent = -1;
}
return self;
}
- (void)setTextInputClient:(int)client {
_textInputClient = client;
}
- (void)setTextInputState:(NSDictionary*)state {
_selectionBase = [state[@"selectionBase"] intValue];
_selectionExtent = [state[@"selectionExtent"] intValue];
_selectionAffinity = _kTextAffinityDownstream;
if ([state[@"selectionAffinity"] isEqualToString:@(_kTextAffinityUpstream)])
_selectionAffinity = _kTextAffinityUpstream;
_text = base::SysNSStringToUTF16(state[@"text"]);
}
- (UITextAutocorrectionType)autocorrectionType {
return UITextAutocorrectionTypeNo;
}
#pragma mark - UIResponder Overrides
- (BOOL)canBecomeFirstResponder {
return YES;
}
#pragma mark - UIKeyInput Overrides
- (void)updateEditingState {
[_textInputDelegate updateEditingClient:_textInputClient
withState:@{
@"selectionBase": @(_selectionBase),
@"selectionExtent": @(_selectionExtent),
@"selectionAffinity": @(_selectionAffinity),
@"selectionIsDirectional": @(false),
@"composingBase": @(0),
@"composingExtent": @(0),
@"text": base::SysUTF16ToNSString(_text),
}];
}
- (BOOL)hasText {
return YES;
}
- (void)insertText:(NSString*)text {
int start = std::max(0, std::min(_selectionBase, _selectionExtent));
int end = std::max(0, std::max(_selectionBase, _selectionExtent));
int len = end - start;
_text.replace(start, len, base::SysNSStringToUTF16(text));
int caret = start + text.length;
_selectionBase = caret;
_selectionExtent = caret;
_selectionAffinity = _kTextAffinityUpstream;
[self updateEditingState];
}
- (void)deleteBackward {
int start = std::max(0, std::min(_selectionBase, _selectionExtent));
int end = std::max(0, std::max(_selectionBase, _selectionExtent));
int len = end - start;
if (len > 0) {
_text.erase(start, len);
} else if (start > 0) {
start -= 1;
len = 1;
if (start > 0 &&
UTF16_IS_LEAD(_text[start - 1]) &&
UTF16_IS_TRAIL(_text[start])) {
start -= 1;
len += 1;
}
_text.erase(start, len);
}
_selectionBase = start;
_selectionExtent = start;
_selectionAffinity = _kTextAffinityDownstream;
[self updateEditingState];
}
@end
@implementation FlutterTextInputPlugin {
FlutterTextInputView* _view;
}
@synthesize textInputDelegate = _textInputDelegate;
- (instancetype)init {
self = [super init];
if (self) {
_view = [[FlutterTextInputView alloc] init];
}
return self;
}
- (void)dealloc {
[self hideTextInput];
[_view release];
[super dealloc];
}
- (NSString *)messageName {
return @"flutter/textinput";
}
- (NSDictionary*)didReceiveJSON:(NSDictionary*)message {
NSString* method = message[@"method"];
NSArray* args = message[@"args"];
if (!args)
return nil;
if ([method isEqualToString:@"TextInput.show"]) {
[self showTextInput];
} else if ([method isEqualToString:@"TextInput.hide"]) {
[self hideTextInput];
} else if ([method isEqualToString:@"TextInput.setClient"]) {
[self setTextInputClient:[args[0] intValue] withConfiguration:args[1]];
} else if ([method isEqualToString:@"TextInput.setEditingState"]) {
[self setTextInputEditingState:args.firstObject];
} else if ([method isEqualToString:@"TextInput.clearClient"]) {
[self clearTextInputClient];
} else {
// TODO(abarth): We should signal an error here that gets reported back to
// Dart.
}
return nil;
}
- (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;
[[UIApplication sharedApplication].keyWindow addSubview:_view];
[_view becomeFirstResponder];
}
- (void)hideTextInput {
[_view resignFirstResponder];
[_view removeFromSuperview];
}
- (void)setTextInputClient:(int)client withConfiguration:(NSDictionary*)configuration {
_view.keyboardType = ToUIKeyboardType(configuration[@"inputType"]);
[_view setTextInputClient:client];
}
- (void)setTextInputEditingState:(NSDictionary*)state {
[_view setTextInputState:state];
}
- (void)clearTextInputClient {
[_view setTextInputClient:0];
}
@end
......@@ -16,11 +16,13 @@
#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_touch_mapper.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
#include "flutter/shell/platform/darwin/ios/platform_view_ios.h"
#include "lib/ftl/functional/make_copyable.h"
#include "lib/ftl/time/time_delta.h"
@interface FlutterViewController ()<UIAlertViewDelegate>
@interface FlutterViewController ()<UIAlertViewDelegate, FlutterTextInputDelegate>
@end
void FlutterInit(int argc, const char* argv[]) {
......@@ -37,6 +39,7 @@ void FlutterInit(int argc, const char* argv[]) {
shell::TouchMapper _touchMapper;
std::unique_ptr<shell::PlatformViewIOS> _platformView;
base::scoped_nsprotocol<FlutterPlatformPlugin*> _platformPlugin;
base::scoped_nsprotocol<FlutterTextInputPlugin*> _textInputPlugin;
BOOL _initialized;
}
......@@ -87,6 +90,11 @@ void FlutterInit(int argc, const char* argv[]) {
_platformPlugin.reset([[FlutterPlatformPlugin alloc] init]);
[self addMessageListener:_platformPlugin.get()];
_textInputPlugin.reset([[FlutterTextInputPlugin alloc] init]);
_textInputPlugin.get().textInputDelegate = self;
[self addMessageListener:_textInputPlugin.get()];
[self setupNotificationCenterObservers];
[self connectToEngineAndLoad];
......@@ -170,14 +178,14 @@ void FlutterInit(int argc, const char* argv[]) {
#pragma mark - Loading the view
- (void)loadView {
FlutterView* surface = [[FlutterView alloc] init];
FlutterView* view = [[FlutterView alloc] init];
self.view = surface;
self.view = view;
self.view.multipleTouchEnabled = YES;
self.view.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[surface release];
[view release];
}
#pragma mark - Application lifecycle notifications
......@@ -328,6 +336,15 @@ static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(
_viewportMetrics.Clone());
}
#pragma mark - Text input delegate
- (void)updateEditingClient:(int)client withState:(NSDictionary*)state {
[self sendJSON:@{
@"method": @"TextInputClient.updateEditingState",
@"args": @[@(client), state],
} withMessageName:@"flutter/textinputclient"];
}
#pragma mark - Orientation updates
- (void)onOrientationPreferencesUpdated:(NSNotification*)notification {
......@@ -467,6 +484,19 @@ static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(
});
}
// TODO(abarth): Switch sendString over to using platform messages.
- (void)sendJSON:(NSDictionary*)message withMessageName:(NSString*)messageName {
NSData* data = [NSJSONSerialization dataWithJSONObject:message options:0 error:nil];
if (!data)
return;
const char* bytes = static_cast<const char*>(data.bytes);
_platformView->DispatchPlatformMessage(
ftl::MakeRefCounted<blink::PlatformMessage>(
messageName.UTF8String,
std::vector<char>(bytes, bytes + data.length),
nullptr));
}
- (void)addMessageListener:(NSObject<FlutterMessageListener>*)listener {
NSAssert(listener, @"The listener must not be null");
NSString* messageName = listener.messageName;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册