Flutter Engine
The Flutter Engine
Public Member Functions | Static Public Attributes | List of all members
io.flutter.plugin.editing.FlutterTextUtils Class Reference

Public Member Functions

 FlutterTextUtils (FlutterJNI flutterJNI)
 
boolean isEmoji (int codePoint)
 
boolean isEmojiModifier (int codePoint)
 
boolean isEmojiModifierBase (int codePoint)
 
boolean isVariationSelector (int codePoint)
 
boolean isRegionalIndicatorSymbol (int codePoint)
 
boolean isTagSpecChar (int codePoint)
 
boolean isKeycapBase (int codePoint)
 
int getOffsetBefore (CharSequence text, int offset)
 
int getOffsetAfter (CharSequence text, int offset)
 

Static Public Attributes

static final int LINE_FEED = 0x0A
 
static final int CARRIAGE_RETURN = 0x0D
 
static final int COMBINING_ENCLOSING_KEYCAP = 0x20E3
 
static final int CANCEL_TAG = 0xE007F
 
static final int ZERO_WIDTH_JOINER = 0x200D
 

Detailed Description

Definition at line 9 of file FlutterTextUtils.java.

Constructor & Destructor Documentation

◆ FlutterTextUtils()

io.flutter.plugin.editing.FlutterTextUtils.FlutterTextUtils ( FlutterJNI  flutterJNI)
inline

Definition at line 17 of file FlutterTextUtils.java.

17 {
18 this.flutterJNI = flutterJNI;
19 }

Member Function Documentation

◆ getOffsetAfter()

int io.flutter.plugin.editing.FlutterTextUtils.getOffsetAfter ( CharSequence  text,
int  offset 
)
inline

Gets the offset of the next character following the given offset, with consideration for multi-byte characters.

See also
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android10-s3-release/core/java/android/text/method/BaseKeyListener.java#111

Definition at line 197 of file FlutterTextUtils.java.

197 {
198 final int len = text.length();
199
200 if (offset >= len - 1) {
201 return len;
202 }
203
204 int codePoint = Character.codePointAt(text, offset);
205 int nextCharCount = Character.charCount(codePoint);
206 int nextOffset = offset + nextCharCount;
207
208 if (nextOffset == 0) {
209 return 0;
210 }
211
212 // Line Feed
213 if (codePoint == LINE_FEED) {
214 codePoint = Character.codePointAt(text, nextOffset);
215 if (codePoint == CARRIAGE_RETURN) {
216 ++nextCharCount;
217 }
218 return offset + nextCharCount;
219 }
220
221 // Flags
222 if (isRegionalIndicatorSymbol(codePoint)) {
223 if (nextOffset >= len - 1
224 || !isRegionalIndicatorSymbol(Character.codePointAt(text, nextOffset))) {
225 return offset + nextCharCount;
226 }
227 // In this case there are at least two regional indicator symbols ahead of
228 // offset. If those two regional indicator symbols are a pair that
229 // represent a region together, the next offset should be after both of
230 // them.
231 int regionalIndicatorSymbolCount = 0;
232 int regionOffset = offset;
233 while (regionOffset > 0
234 && isRegionalIndicatorSymbol(Character.codePointBefore(text, offset))) {
235 regionOffset -= Character.charCount(Character.codePointBefore(text, offset));
236 regionalIndicatorSymbolCount++;
237 }
238 if (regionalIndicatorSymbolCount % 2 == 0) {
239 nextCharCount += 2;
240 }
241 return offset + nextCharCount;
242 }
243
244 // Keycaps
245 if (isKeycapBase(codePoint)) {
246 nextCharCount += Character.charCount(codePoint);
247 }
248 if (codePoint == COMBINING_ENCLOSING_KEYCAP) {
249 codePoint = Character.codePointBefore(text, nextOffset);
250 nextOffset += Character.charCount(codePoint);
251 if (nextOffset < len && isVariationSelector(codePoint)) {
252 int tmpCodePoint = Character.codePointAt(text, nextOffset);
253 if (isKeycapBase(tmpCodePoint)) {
254 nextCharCount += Character.charCount(codePoint) + Character.charCount(tmpCodePoint);
255 }
256 } else if (isKeycapBase(codePoint)) {
257 nextCharCount += Character.charCount(codePoint);
258 }
259 return offset + nextCharCount;
260 }
261
262 if (isEmoji(codePoint)) {
263 boolean isZwj = false;
264 int lastSeenVariantSelectorCharCount = 0;
265 do {
266 if (isZwj) {
267 nextCharCount += Character.charCount(codePoint) + lastSeenVariantSelectorCharCount + 1;
268 isZwj = false;
269 }
270 lastSeenVariantSelectorCharCount = 0;
271 if (isEmojiModifier(codePoint)) {
272 break;
273 }
274
275 if (nextOffset < len) {
276 codePoint = Character.codePointAt(text, nextOffset);
277 nextOffset += Character.charCount(codePoint);
278 if (codePoint == COMBINING_ENCLOSING_KEYCAP) {
279 codePoint = Character.codePointBefore(text, nextOffset);
280 nextOffset += Character.charCount(codePoint);
281 if (nextOffset < len && isVariationSelector(codePoint)) {
282 int tmpCodePoint = Character.codePointAt(text, nextOffset);
283 if (isKeycapBase(tmpCodePoint)) {
284 nextCharCount += Character.charCount(codePoint) + Character.charCount(tmpCodePoint);
285 }
286 } else if (isKeycapBase(codePoint)) {
287 nextCharCount += Character.charCount(codePoint);
288 }
289 return offset + nextCharCount;
290 }
291 if (isEmojiModifier(codePoint)) {
292 nextCharCount += lastSeenVariantSelectorCharCount + Character.charCount(codePoint);
293 break;
294 }
295 if (isVariationSelector(codePoint)) {
296 nextCharCount += lastSeenVariantSelectorCharCount + Character.charCount(codePoint);
297 break;
298 }
299 if (codePoint == ZERO_WIDTH_JOINER) {
300 isZwj = true;
301 codePoint = Character.codePointAt(text, nextOffset);
302 nextOffset += Character.charCount(codePoint);
303 if (nextOffset < len && isVariationSelector(codePoint)) {
304 codePoint = Character.codePointAt(text, nextOffset);
305 lastSeenVariantSelectorCharCount = Character.charCount(codePoint);
306 nextOffset += Character.charCount(codePoint);
307 }
308 }
309 }
310
311 if (nextOffset >= len) {
312 break;
313 }
314 } while (isZwj && isEmoji(codePoint));
315 }
316
317 return offset + nextCharCount;
318 }
std::u16string text
SeparatedVector2 offset

◆ getOffsetBefore()

int io.flutter.plugin.editing.FlutterTextUtils.getOffsetBefore ( CharSequence  text,
int  offset 
)
inline

Start offset for backspace key or moving left from the current offset. Same methods are also included in Android APIs but they don't work as expected in API Levels lower than 24. Reference for the logic in this code is the Android source code.

See also
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/android10-s3-release/core/java/android/text/method/BaseKeyListener.java#111

Following if statements for Emoji tag sequence and Variation selector are skipping these modifiers for going through the last statement that is for handling emojis. They return the offset if they don't find proper base characters

Definition at line 57 of file FlutterTextUtils.java.

57 {
58 if (offset <= 1) {
59 return 0;
60 }
61
62 int codePoint = Character.codePointBefore(text, offset);
63 int deleteCharCount = Character.charCount(codePoint);
64 int lastOffset = offset - deleteCharCount;
65
66 if (lastOffset == 0) {
67 return 0;
68 }
69
70 // Line Feed
71 if (codePoint == LINE_FEED) {
72 codePoint = Character.codePointBefore(text, lastOffset);
73 if (codePoint == CARRIAGE_RETURN) {
74 ++deleteCharCount;
75 }
76 return offset - deleteCharCount;
77 }
78
79 // Flags
80 if (isRegionalIndicatorSymbol(codePoint)) {
81 codePoint = Character.codePointBefore(text, lastOffset);
82 lastOffset -= Character.charCount(codePoint);
83 int regionalIndicatorSymbolCount = 1;
84 while (lastOffset > 0 && isRegionalIndicatorSymbol(codePoint)) {
85 codePoint = Character.codePointBefore(text, lastOffset);
86 lastOffset -= Character.charCount(codePoint);
87 regionalIndicatorSymbolCount++;
88 }
89 if (regionalIndicatorSymbolCount % 2 == 0) {
90 deleteCharCount += 2;
91 }
92 return offset - deleteCharCount;
93 }
94
95 // Keycaps
96 if (codePoint == COMBINING_ENCLOSING_KEYCAP) {
97 codePoint = Character.codePointBefore(text, lastOffset);
98 lastOffset -= Character.charCount(codePoint);
99 if (lastOffset > 0 && isVariationSelector(codePoint)) {
100 int tmpCodePoint = Character.codePointBefore(text, lastOffset);
101 if (isKeycapBase(tmpCodePoint)) {
102 deleteCharCount += Character.charCount(codePoint) + Character.charCount(tmpCodePoint);
103 }
104 } else if (isKeycapBase(codePoint)) {
105 deleteCharCount += Character.charCount(codePoint);
106 }
107 return offset - deleteCharCount;
108 }
109
110 /**
111 * Following if statements for Emoji tag sequence and Variation selector are skipping these
112 * modifiers for going through the last statement that is for handling emojis. They return the
113 * offset if they don't find proper base characters
114 */
115 // Emoji Tag Sequence
116 if (codePoint == CANCEL_TAG) { // tag_end
117 codePoint = Character.codePointBefore(text, lastOffset);
118 lastOffset -= Character.charCount(codePoint);
119 while (lastOffset > 0 && isTagSpecChar(codePoint)) { // tag_spec
120 deleteCharCount += Character.charCount(codePoint);
121 codePoint = Character.codePointBefore(text, lastOffset);
122 lastOffset -= Character.charCount(codePoint);
123 }
124 if (!isEmoji(codePoint)) { // tag_base not found. Just delete the end.
125 return offset - 2;
126 }
127 deleteCharCount += Character.charCount(codePoint);
128 }
129
130 if (isVariationSelector(codePoint)) {
131 codePoint = Character.codePointBefore(text, lastOffset);
132 if (!isEmoji(codePoint)) {
133 return offset - deleteCharCount;
134 }
135 deleteCharCount += Character.charCount(codePoint);
136
137 lastOffset -= deleteCharCount;
138 }
139
140 if (isEmoji(codePoint)) {
141 boolean isZwj = false;
142 int lastSeenVariantSelectorCharCount = 0;
143 do {
144 if (isZwj) {
145 deleteCharCount += Character.charCount(codePoint) + lastSeenVariantSelectorCharCount + 1;
146 isZwj = false;
147 }
148 lastSeenVariantSelectorCharCount = 0;
149 if (isEmojiModifier(codePoint)) {
150 codePoint = Character.codePointBefore(text, lastOffset);
151 lastOffset -= Character.charCount(codePoint);
152 if (lastOffset > 0 && isVariationSelector(codePoint)) {
153 codePoint = Character.codePointBefore(text, lastOffset);
154 if (!isEmoji(codePoint)) {
155 return offset - deleteCharCount;
156 }
157 lastSeenVariantSelectorCharCount = Character.charCount(codePoint);
158 lastOffset -= Character.charCount(codePoint);
159 }
160 if (isEmojiModifierBase(codePoint)) {
161 deleteCharCount += lastSeenVariantSelectorCharCount + Character.charCount(codePoint);
162 }
163 break;
164 }
165
166 if (lastOffset > 0) {
167 codePoint = Character.codePointBefore(text, lastOffset);
168 lastOffset -= Character.charCount(codePoint);
169 if (codePoint == ZERO_WIDTH_JOINER) {
170 isZwj = true;
171 codePoint = Character.codePointBefore(text, lastOffset);
172 lastOffset -= Character.charCount(codePoint);
173 if (lastOffset > 0 && isVariationSelector(codePoint)) {
174 codePoint = Character.codePointBefore(text, lastOffset);
175 lastSeenVariantSelectorCharCount = Character.charCount(codePoint);
176 lastOffset -= Character.charCount(codePoint);
177 }
178 }
179 }
180
181 if (lastOffset == 0) {
182 break;
183 }
184 } while (isZwj && isEmoji(codePoint));
185 }
186
187 return offset - deleteCharCount;
188 }

◆ isEmoji()

boolean io.flutter.plugin.editing.FlutterTextUtils.isEmoji ( int  codePoint)
inline

Definition at line 21 of file FlutterTextUtils.java.

21 {
22 return flutterJNI.isCodePointEmoji(codePoint);
23 }
boolean isCodePointEmoji(int codePoint)

◆ isEmojiModifier()

boolean io.flutter.plugin.editing.FlutterTextUtils.isEmojiModifier ( int  codePoint)
inline

Definition at line 25 of file FlutterTextUtils.java.

25 {
26 return flutterJNI.isCodePointEmojiModifier(codePoint);
27 }
boolean isCodePointEmojiModifier(int codePoint)

◆ isEmojiModifierBase()

boolean io.flutter.plugin.editing.FlutterTextUtils.isEmojiModifierBase ( int  codePoint)
inline

Definition at line 29 of file FlutterTextUtils.java.

29 {
30 return flutterJNI.isCodePointEmojiModifierBase(codePoint);
31 }
boolean isCodePointEmojiModifierBase(int codePoint)

◆ isKeycapBase()

boolean io.flutter.plugin.editing.FlutterTextUtils.isKeycapBase ( int  codePoint)
inline

Definition at line 45 of file FlutterTextUtils.java.

45 {
46 return ('0' <= codePoint && codePoint <= '9') || codePoint == '#' || codePoint == '*';
47 }

◆ isRegionalIndicatorSymbol()

boolean io.flutter.plugin.editing.FlutterTextUtils.isRegionalIndicatorSymbol ( int  codePoint)
inline

Definition at line 37 of file FlutterTextUtils.java.

37 {
38 return flutterJNI.isCodePointRegionalIndicator(codePoint);
39 }
boolean isCodePointRegionalIndicator(int codePoint)

◆ isTagSpecChar()

boolean io.flutter.plugin.editing.FlutterTextUtils.isTagSpecChar ( int  codePoint)
inline

Definition at line 41 of file FlutterTextUtils.java.

41 {
42 return 0xE0020 <= codePoint && codePoint <= 0xE007E;
43 }

◆ isVariationSelector()

boolean io.flutter.plugin.editing.FlutterTextUtils.isVariationSelector ( int  codePoint)
inline

Definition at line 33 of file FlutterTextUtils.java.

33 {
34 return flutterJNI.isCodePointVariantSelector(codePoint);
35 }
boolean isCodePointVariantSelector(int codePoint)

Member Data Documentation

◆ CANCEL_TAG

final int io.flutter.plugin.editing.FlutterTextUtils.CANCEL_TAG = 0xE007F
static

Definition at line 13 of file FlutterTextUtils.java.

◆ CARRIAGE_RETURN

final int io.flutter.plugin.editing.FlutterTextUtils.CARRIAGE_RETURN = 0x0D
static

Definition at line 11 of file FlutterTextUtils.java.

◆ COMBINING_ENCLOSING_KEYCAP

final int io.flutter.plugin.editing.FlutterTextUtils.COMBINING_ENCLOSING_KEYCAP = 0x20E3
static

Definition at line 12 of file FlutterTextUtils.java.

◆ LINE_FEED

final int io.flutter.plugin.editing.FlutterTextUtils.LINE_FEED = 0x0A
static

Definition at line 10 of file FlutterTextUtils.java.

◆ ZERO_WIDTH_JOINER

final int io.flutter.plugin.editing.FlutterTextUtils.ZERO_WIDTH_JOINER = 0x200D
static

Definition at line 14 of file FlutterTextUtils.java.


The documentation for this class was generated from the following file: