@@ -47,13 +47,10 @@ const linkPrefixes = [
47
47
'http'
48
48
];
49
49
50
- abstract class EditorState extends State <RawEditor > {
50
+ abstract class EditorState extends State <RawEditor >
51
+ implements TextSelectionDelegate {
51
52
ScrollController get scrollController;
52
53
53
- TextEditingValue getTextEditingValue ();
54
-
55
- void setTextEditingValue (TextEditingValue value, SelectionChangedCause cause);
56
-
57
54
RenderEditor ? getRenderEditor ();
58
55
59
56
EditorTextSelectionOverlay ? getSelectionOverlay ();
@@ -64,15 +61,11 @@ abstract class EditorState extends State<RawEditor> {
64
61
65
62
bool showToolbar ();
66
63
67
- void hideToolbar ();
68
-
69
64
void requestKeyboard ();
70
-
71
- bool get readOnly;
72
65
}
73
66
74
67
/// Base interface for editable render objects.
75
- abstract class RenderAbstractEditor {
68
+ abstract class RenderAbstractEditor implements TextLayoutMetrics {
76
69
TextSelection selectWordAtPosition (TextPosition position);
77
70
78
71
TextSelection selectLineAtPosition (TextPosition position);
@@ -684,6 +677,7 @@ const EdgeInsets _kFloatingCaretSizeIncrease =
684
677
EdgeInsets .symmetric (horizontal: 0.5 , vertical: 1 );
685
678
686
679
class RenderEditor extends RenderEditableContainerBox
680
+ with RelayoutWhenSystemFontsChangeMixin
687
681
implements RenderAbstractEditor {
688
682
RenderEditor (
689
683
ViewportOffset ? offset,
@@ -985,15 +979,8 @@ class RenderEditor extends RenderEditableContainerBox
985
979
986
980
@override
987
981
TextSelection selectWordAtPosition (TextPosition position) {
988
- final child = childAtPosition (position);
989
- final nodeOffset = child.getContainer ().offset;
990
- final localPosition = TextPosition (
991
- offset: position.offset - nodeOffset, affinity: position.affinity);
992
- final localWord = child.getWordBoundary (localPosition);
993
- final word = TextRange (
994
- start: localWord.start + nodeOffset,
995
- end: localWord.end + nodeOffset,
996
- );
982
+ final word = getWordBoundary (position);
983
+ // When long-pressing past the end of the text, we want a collapsed cursor.
997
984
if (position.offset >= word.end) {
998
985
return TextSelection .fromPosition (position);
999
986
}
@@ -1002,16 +989,9 @@ class RenderEditor extends RenderEditableContainerBox
1002
989
1003
990
@override
1004
991
TextSelection selectLineAtPosition (TextPosition position) {
1005
- final child = childAtPosition (position);
1006
- final nodeOffset = child.getContainer ().offset;
1007
- final localPosition = TextPosition (
1008
- offset: position.offset - nodeOffset, affinity: position.affinity);
1009
- final localLineRange = child.getLineBoundary (localPosition);
1010
- final line = TextRange (
1011
- start: localLineRange.start + nodeOffset,
1012
- end: localLineRange.end + nodeOffset,
1013
- );
992
+ final line = getLineAtOffset (position);
1014
993
994
+ // When long-pressing past the end of the text, we want a collapsed cursor.
1015
995
if (position.offset >= line.end) {
1016
996
return TextSelection .fromPosition (position);
1017
997
}
@@ -1266,7 +1246,126 @@ class RenderEditor extends RenderEditableContainerBox
1266
1246
_floatingCursorPainter.paint (context.canvas);
1267
1247
}
1268
1248
1269
- // End floating cursor
1249
+ // End floating cursor
1250
+
1251
+ // Start TextLayoutMetrics implementation
1252
+
1253
+ /// Return a [TextSelection] containing the line of the given [TextPosition] .
1254
+ @override
1255
+ TextSelection getLineAtOffset (TextPosition position) {
1256
+ final child = childAtPosition (position);
1257
+ final nodeOffset = child.getContainer ().offset;
1258
+ final localPosition = TextPosition (
1259
+ offset: position.offset - nodeOffset, affinity: position.affinity);
1260
+ final localLineRange = child.getLineBoundary (localPosition);
1261
+ final line = TextRange (
1262
+ start: localLineRange.start + nodeOffset,
1263
+ end: localLineRange.end + nodeOffset,
1264
+ );
1265
+ return TextSelection (baseOffset: line.start, extentOffset: line.end);
1266
+ }
1267
+
1268
+ @override
1269
+ TextRange getWordBoundary (TextPosition position) {
1270
+ final child = childAtPosition (position);
1271
+ final nodeOffset = child.getContainer ().offset;
1272
+ final localPosition = TextPosition (
1273
+ offset: position.offset - nodeOffset, affinity: position.affinity);
1274
+ final localWord = child.getWordBoundary (localPosition);
1275
+ return TextRange (
1276
+ start: localWord.start + nodeOffset,
1277
+ end: localWord.end + nodeOffset,
1278
+ );
1279
+ }
1280
+
1281
+ /// Returns the TextPosition above the given offset into the text.
1282
+ ///
1283
+ /// If the offset is already on the first line, the offset of the first
1284
+ /// character will be returned.
1285
+ @override
1286
+ TextPosition getTextPositionAbove (TextPosition position) {
1287
+ final child = childAtPosition (position);
1288
+ final localPosition = TextPosition (
1289
+ offset: position.offset - child.getContainer ().documentOffset);
1290
+
1291
+ var newPosition = child.getPositionAbove (localPosition);
1292
+
1293
+ if (newPosition == null ) {
1294
+ // There was no text above in the current child, check the direct
1295
+ // sibling.
1296
+ final sibling = childBefore (child);
1297
+ if (sibling == null ) {
1298
+ // reached beginning of the document, move to the
1299
+ // first character
1300
+ newPosition = const TextPosition (offset: 0 );
1301
+ } else {
1302
+ final caretOffset = child.getOffsetForCaret (localPosition);
1303
+ final testPosition =
1304
+ TextPosition (offset: sibling.getContainer ().length - 1 );
1305
+ final testOffset = sibling.getOffsetForCaret (testPosition);
1306
+ final finalOffset = Offset (caretOffset.dx, testOffset.dy);
1307
+ final siblingPosition = sibling.getPositionForOffset (finalOffset);
1308
+ newPosition = TextPosition (
1309
+ offset:
1310
+ sibling.getContainer ().documentOffset + siblingPosition.offset);
1311
+ }
1312
+ } else {
1313
+ newPosition = TextPosition (
1314
+ offset: child.getContainer ().documentOffset + newPosition.offset);
1315
+ }
1316
+ return newPosition;
1317
+ }
1318
+
1319
+ /// Returns the TextPosition below the given offset into the text.
1320
+ ///
1321
+ /// If the offset is already on the last line, the offset of the last
1322
+ /// character will be returned.
1323
+ @override
1324
+ TextPosition getTextPositionBelow (TextPosition position) {
1325
+ final child = childAtPosition (position);
1326
+ final localPosition = TextPosition (
1327
+ offset: position.offset - child.getContainer ().documentOffset);
1328
+
1329
+ var newPosition = child.getPositionBelow (localPosition);
1330
+
1331
+ if (newPosition == null ) {
1332
+ // There was no text above in the current child, check the direct
1333
+ // sibling.
1334
+ final sibling = childAfter (child);
1335
+ if (sibling == null ) {
1336
+ // reached beginning of the document, move to the
1337
+ // last character
1338
+ newPosition = TextPosition (offset: document.length - 1 );
1339
+ } else {
1340
+ final caretOffset = child.getOffsetForCaret (localPosition);
1341
+ const testPosition = TextPosition (offset: 0 );
1342
+ final testOffset = sibling.getOffsetForCaret (testPosition);
1343
+ final finalOffset = Offset (caretOffset.dx, testOffset.dy);
1344
+ final siblingPosition = sibling.getPositionForOffset (finalOffset);
1345
+ newPosition = TextPosition (
1346
+ offset:
1347
+ sibling.getContainer ().documentOffset + siblingPosition.offset);
1348
+ }
1349
+ } else {
1350
+ newPosition = TextPosition (
1351
+ offset: child.getContainer ().documentOffset + newPosition.offset);
1352
+ }
1353
+ return newPosition;
1354
+ }
1355
+
1356
+ // End TextLayoutMetrics implementation
1357
+
1358
+ @override
1359
+ void systemFontsDidChange () {
1360
+ super .systemFontsDidChange ();
1361
+ markNeedsLayout ();
1362
+ }
1363
+
1364
+ void debugAssertLayoutUpToDate () {
1365
+ // no-op?
1366
+ // this assert was added by Flutter TextEditingActionTarge
1367
+ // so we have to comply here.
1368
+ }
1270
1369
}
1271
1370
1272
1371
class EditableContainerParentData
0 commit comments