@@ -3069,7 +3069,18 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
30693069 }
30703070 late final Action <ReplaceTextIntent > _replaceTextAction = CallbackAction <ReplaceTextIntent >(onInvoke: _replaceText);
30713071
3072+ // Scrolls either to the beginning or end of the document depending on the
3073+ // intent's `forward` parameter.
3074+ void _scrollToDocumentBoundary (ScrollToDocumentBoundaryIntent intent) {
3075+ if (intent.forward) {
3076+ bringIntoView (TextPosition (offset: _value.text.length));
3077+ } else {
3078+ bringIntoView (const TextPosition (offset: 0 ));
3079+ }
3080+ }
3081+
30723082 void _updateSelection (UpdateSelectionIntent intent) {
3083+ bringIntoView (intent.newSelection.extent);
30733084 userUpdateTextEditingValue (
30743085 intent.currentTextEditingValue.copyWith (selection: intent.newSelection),
30753086 intent.cause,
@@ -3079,28 +3090,38 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
30793090
30803091 late final _UpdateTextSelectionToAdjacentLineAction <ExtendSelectionVerticallyToAdjacentLineIntent > _adjacentLineAction = _UpdateTextSelectionToAdjacentLineAction <ExtendSelectionVerticallyToAdjacentLineIntent >(this );
30813092
3082- void _expandSelection (ExpandSelectionToLineBreakIntent intent) {
3093+ void _expandSelectionToDocumentBoundary (ExpandSelectionToDocumentBoundaryIntent intent) {
3094+ final _TextBoundary textBoundary = _documentBoundary (intent);
3095+ _expandSelection (intent.forward, textBoundary, true );
3096+ }
3097+
3098+ void _expandSelectionToLinebreak (ExpandSelectionToLineBreakIntent intent) {
30833099 final _TextBoundary textBoundary = _linebreak (intent);
3100+ _expandSelection (intent.forward, textBoundary);
3101+ }
3102+
3103+ void _expandSelection (bool forward, _TextBoundary textBoundary, [bool extentAtIndex = false ]) {
30843104 final TextSelection textBoundarySelection = textBoundary.textEditingValue.selection;
30853105 if (! textBoundarySelection.isValid) {
30863106 return ;
30873107 }
30883108
30893109 final bool inOrder = textBoundarySelection.baseOffset <= textBoundarySelection.extentOffset;
3090- final bool towardsExtent = intent. forward == inOrder;
3110+ final bool towardsExtent = forward == inOrder;
30913111 final TextPosition position = towardsExtent
30923112 ? textBoundarySelection.extent
30933113 : textBoundarySelection.base ;
30943114
3095- final TextPosition newExtent = intent. forward
3115+ final TextPosition newExtent = forward
30963116 ? textBoundary.getTrailingTextBoundaryAt (position)
30973117 : textBoundary.getLeadingTextBoundaryAt (position);
30983118
3099- final TextSelection newSelection = textBoundarySelection.expandTo (newExtent, textBoundarySelection.isCollapsed);
3119+ final TextSelection newSelection = textBoundarySelection.expandTo (newExtent, textBoundarySelection.isCollapsed || extentAtIndex );
31003120 userUpdateTextEditingValue (
31013121 _value.copyWith (selection: newSelection),
31023122 SelectionChangedCause .keyboard,
31033123 );
3124+ bringIntoView (newSelection.extent);
31043125 }
31053126
31063127 late final Map <Type , Action <Intent >> _actions = < Type , Action <Intent >> {
@@ -3118,10 +3139,12 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
31183139 ExtendSelectionByCharacterIntent : _makeOverridable (_UpdateTextSelectionAction <ExtendSelectionByCharacterIntent >(this , false , _characterBoundary,)),
31193140 ExtendSelectionToNextWordBoundaryIntent : _makeOverridable (_UpdateTextSelectionAction <ExtendSelectionToNextWordBoundaryIntent >(this , true , _nextWordBoundary)),
31203141 ExtendSelectionToLineBreakIntent : _makeOverridable (_UpdateTextSelectionAction <ExtendSelectionToLineBreakIntent >(this , true , _linebreak)),
3121- ExpandSelectionToLineBreakIntent : _makeOverridable (CallbackAction <ExpandSelectionToLineBreakIntent >(onInvoke: _expandSelection)),
3142+ ExpandSelectionToLineBreakIntent : _makeOverridable (CallbackAction <ExpandSelectionToLineBreakIntent >(onInvoke: _expandSelectionToLinebreak)),
3143+ ExpandSelectionToDocumentBoundaryIntent : _makeOverridable (CallbackAction <ExpandSelectionToDocumentBoundaryIntent >(onInvoke: _expandSelectionToDocumentBoundary)),
31223144 ExtendSelectionVerticallyToAdjacentLineIntent : _makeOverridable (_adjacentLineAction),
31233145 ExtendSelectionToDocumentBoundaryIntent : _makeOverridable (_UpdateTextSelectionAction <ExtendSelectionToDocumentBoundaryIntent >(this , true , _documentBoundary)),
31243146 ExtendSelectionToNextWordBoundaryOrCaretLocationIntent : _makeOverridable (_ExtendSelectionOrCaretPositionAction (this , _nextWordBoundary)),
3147+ ScrollToDocumentBoundaryIntent : _makeOverridable (CallbackAction <ScrollToDocumentBoundaryIntent >(onInvoke: _scrollToDocumentBoundary)),
31253148
31263149 // Copy Paste
31273150 SelectAllTextIntent : _makeOverridable (_SelectAllAction (this )),
@@ -3763,7 +3786,10 @@ class _WordBoundary extends _TextBoundary {
37633786// interpreted as caret locations because [TextPainter.getLineAtOffset] is
37643787// text-affinity-aware.
37653788class _LineBreak extends _TextBoundary {
3766- const _LineBreak (this .textLayout, this .textEditingValue);
3789+ const _LineBreak (
3790+ this .textLayout,
3791+ this .textEditingValue,
3792+ );
37673793
37683794 final TextLayoutMetrics textLayout;
37693795
@@ -3776,6 +3802,7 @@ class _LineBreak extends _TextBoundary {
37763802 offset: textLayout.getLineAtOffset (position).start,
37773803 );
37783804 }
3805+
37793806 @override
37803807 TextPosition getTrailingTextBoundaryAt (TextPosition position) {
37813808 return TextPosition (
@@ -3945,12 +3972,39 @@ class _DeleteTextAction<T extends DirectionalTextEditingIntent> extends ContextA
39453972}
39463973
39473974class _UpdateTextSelectionAction <T extends DirectionalCaretMovementIntent > extends ContextAction <T > {
3948- _UpdateTextSelectionAction (this .state, this .ignoreNonCollapsedSelection, this .getTextBoundariesForIntent);
3975+ _UpdateTextSelectionAction (
3976+ this .state,
3977+ this .ignoreNonCollapsedSelection,
3978+ this .getTextBoundariesForIntent,
3979+ );
39493980
39503981 final EditableTextState state;
39513982 final bool ignoreNonCollapsedSelection;
39523983 final _TextBoundary Function (T intent) getTextBoundariesForIntent;
39533984
3985+ static const int NEWLINE_CODE_UNIT = 10 ;
3986+
3987+ // Returns true iff the given position is at a wordwrap boundary in the
3988+ // upstream position.
3989+ bool _isAtWordwrapUpstream (TextPosition position) {
3990+ final TextPosition end = TextPosition (
3991+ offset: state.renderEditable.getLineAtOffset (position).end,
3992+ affinity: TextAffinity .upstream,
3993+ );
3994+ return end == position && end.offset != state.textEditingValue.text.length
3995+ && state.textEditingValue.text.codeUnitAt (position.offset) != NEWLINE_CODE_UNIT ;
3996+ }
3997+
3998+ // Returns true iff the given position at a wordwrap boundary in the
3999+ // downstream position.
4000+ bool _isAtWordwrapDownstream (TextPosition position) {
4001+ final TextPosition start = TextPosition (
4002+ offset: state.renderEditable.getLineAtOffset (position).start,
4003+ );
4004+ return start == position && start.offset != 0
4005+ && state.textEditingValue.text.codeUnitAt (position.offset - 1 ) != NEWLINE_CODE_UNIT ;
4006+ }
4007+
39544008 @override
39554009 Object ? invoke (T intent, [BuildContext ? context]) {
39564010 final TextSelection selection = state._value.selection;
@@ -3986,7 +4040,23 @@ class _UpdateTextSelectionAction<T extends DirectionalCaretMovementIntent> exten
39864040 );
39874041 }
39884042
3989- final TextPosition extent = textBoundarySelection.extent;
4043+ TextPosition extent = textBoundarySelection.extent;
4044+
4045+ // If continuesAtWrap is true extent and is at the relevant wordwrap, then
4046+ // move it just to the other side of the wordwrap.
4047+ if (intent.continuesAtWrap) {
4048+ if (intent.forward && _isAtWordwrapUpstream (extent)) {
4049+ extent = TextPosition (
4050+ offset: extent.offset,
4051+ );
4052+ } else if (! intent.forward && _isAtWordwrapDownstream (extent)) {
4053+ extent = TextPosition (
4054+ offset: extent.offset,
4055+ affinity: TextAffinity .upstream,
4056+ );
4057+ }
4058+ }
4059+
39904060 final TextPosition newExtent = intent.forward
39914061 ? textBoundary.getTrailingTextBoundaryAt (extent)
39924062 : textBoundary.getLeadingTextBoundaryAt (extent);
0 commit comments