Flutter Engine
FlutterTextInputModel.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #import "flutter/shell/platform/darwin/macos/framework/Source/FlutterTextInputModel.h"
6 
7 static NSString* const kTextAffinityDownstream = @"TextAffinity.downstream";
8 static NSString* const kTextAffinityUpstream = @"TextAffinity.upstream";
9 
10 static NSString* const kTextInputAction = @"inputAction";
11 static NSString* const kTextInputType = @"inputType";
12 static NSString* const kTextInputTypeName = @"name";
13 
14 static NSString* const kSelectionBaseKey = @"selectionBase";
15 static NSString* const kSelectionExtentKey = @"selectionExtent";
16 static NSString* const kSelectionAffinityKey = @"selectionAffinity";
17 static NSString* const kSelectionIsDirectionalKey = @"selectionIsDirectional";
18 static NSString* const kComposingBaseKey = @"composingBase";
19 static NSString* const kComposingExtentKey = @"composingExtent";
20 static NSString* const kTextKey = @"text";
21 
22 /**
23  * These three static methods are necessary because Cocoa and Flutter have different idioms for
24  * signaling an empty range: Flutter uses {-1, -1} while Cocoa uses {NSNotFound, 0}. Also,
25  * despite the name, the "extent" fields are actually end indices, not lengths.
26  */
27 
28 /**
29  * Updates a range given base and extent fields.
30  */
31 static NSRange UpdateRangeFromBaseExtent(NSNumber* base, NSNumber* extent, NSRange range) {
32  if (base == nil || extent == nil) {
33  return range;
34  }
35  if (base.intValue == -1 && extent.intValue == -1) {
36  range.location = NSNotFound;
37  range.length = 0;
38  } else {
39  range.location = [base unsignedLongValue];
40  range.length = [extent unsignedLongValue] - range.location;
41  }
42  return range;
43 }
44 
45 /**
46  * Returns the appropriate base field for a given range.
47  */
48 static long GetBaseForRange(NSRange range) {
49  if (range.location == NSNotFound) {
50  return -1;
51  }
52  return range.location;
53 }
54 
55 /**
56  * Returns the appropriate extent field for a given range.
57  */
58 static long GetExtentForRange(NSRange range) {
59  if (range.location == NSNotFound) {
60  return -1;
61  }
62  return range.location + range.length;
63 }
64 
65 @implementation FlutterTextInputModel
66 
67 - (instancetype)initWithClientID:(NSNumber*)clientID configuration:(NSDictionary*)config {
68  self = [super init];
69  if (self != nil) {
70  _clientID = clientID;
71  _inputAction = config[kTextInputAction];
72  // There's more information that can be used from this dictionary.
73  // Add more as needed.
74  NSDictionary* inputTypeInfo = config[kTextInputType];
75  _inputType = inputTypeInfo[kTextInputTypeName];
76  if (!_clientID || !_inputAction || !_inputType) {
77  NSLog(@"Missing arguments for %@ init.", [self class]);
78  return nil;
79  }
80 
81  _text = [[NSMutableString alloc] init];
82  _selectedRange = NSMakeRange(NSNotFound, 0);
83  _markedRange = NSMakeRange(NSNotFound, 0);
84  _textAffinity = FlutterTextAffinityUpstream;
85  }
86  return self;
87 }
88 
89 - (NSDictionary*)state {
90  NSString* const textAffinity = (_textAffinity == FlutterTextAffinityUpstream)
93  NSDictionary* state = @{
94  kSelectionBaseKey : @(GetBaseForRange(_selectedRange)),
95  kSelectionExtentKey : @(GetExtentForRange(_selectedRange)),
96  kSelectionAffinityKey : textAffinity,
98  kComposingBaseKey : @(GetBaseForRange(_markedRange)),
99  kComposingExtentKey : @(GetExtentForRange(_markedRange)),
100  kTextKey : _text
101  };
102  return state;
103 }
104 
105 - (void)setState:(NSDictionary*)state {
106  if (state == nil)
107  return;
108 
110  _selectedRange);
111  NSString* selectionAffinity = state[kSelectionAffinityKey];
112  if (selectionAffinity != nil) {
113  _textAffinity = [selectionAffinity isEqualToString:kTextAffinityUpstream]
114  ? FlutterTextAffinityUpstream
115  : FlutterTextAffinityDownstream;
116  }
117  _markedRange =
119  NSString* text = state[kTextKey];
120  if (text != nil)
121  [_text setString:text];
122 }
123 
124 @end
static NSString *const kTextAffinityDownstream
static NSString *const kSelectionExtentKey
static NSString *const kTextInputType
static long GetBaseForRange(NSRange range)
static NSString *const kTextAffinityUpstream
static NSString *const kTextKey
static NSString *const kComposingExtentKey
static NSString *const kTextInputAction
FlutterTextAffinity textAffinity
static NSString *const kSelectionBaseKey
static NSRange UpdateRangeFromBaseExtent(NSNumber *base, NSNumber *extent, NSRange range)
static long GetExtentForRange(NSRange range)
static NSString *const kSelectionIsDirectionalKey
static NSString *const kComposingBaseKey
static NSString *const kTextInputTypeName
static NSString *const kSelectionAffinityKey