Page Up / Page Down in text fields (#107602)
This commit is contained in:
parent
b375b4ac87
commit
563e0a4aae
1
AUTHORS
1
AUTHORS
@ -98,3 +98,4 @@ Elsabe Ros <hello@elsabe.dev>
|
|||||||
Nguyễn Phúc Lợi <nploi1998@gmail.com>
|
Nguyễn Phúc Lợi <nploi1998@gmail.com>
|
||||||
Jingyi Chen <jingyichen@link.cuhk.edu.cn>
|
Jingyi Chen <jingyichen@link.cuhk.edu.cn>
|
||||||
Junhua Lin <1075209054@qq.com>
|
Junhua Lin <1075209054@qq.com>
|
||||||
|
Tomasz Gucio <tgucio@gmail.com>
|
||||||
|
@ -108,10 +108,13 @@ class TextSelectionPoint {
|
|||||||
/// false. Similarly the [moveNext] method moves the caret to the next line, and
|
/// false. Similarly the [moveNext] method moves the caret to the next line, and
|
||||||
/// returns false if the caret is already on the last line.
|
/// returns false if the caret is already on the last line.
|
||||||
///
|
///
|
||||||
|
/// The [moveByOffset] method takes a pixel offset from the current position to move
|
||||||
|
/// the caret up or down.
|
||||||
|
///
|
||||||
/// If the underlying paragraph's layout changes, [isValid] becomes false and
|
/// If the underlying paragraph's layout changes, [isValid] becomes false and
|
||||||
/// the [VerticalCaretMovementRun] must not be used. The [isValid] property must
|
/// the [VerticalCaretMovementRun] must not be used. The [isValid] property must
|
||||||
/// be checked before calling [movePrevious] and [moveNext], or accessing
|
/// be checked before calling [movePrevious], [moveNext] and [moveByOffset],
|
||||||
/// [current].
|
/// or accessing [current].
|
||||||
class VerticalCaretMovementRun extends Iterator<TextPosition> {
|
class VerticalCaretMovementRun extends Iterator<TextPosition> {
|
||||||
VerticalCaretMovementRun._(
|
VerticalCaretMovementRun._(
|
||||||
this._editable,
|
this._editable,
|
||||||
@ -134,8 +137,8 @@ class VerticalCaretMovementRun extends Iterator<TextPosition> {
|
|||||||
/// A [VerticalCaretMovementRun] run is valid if the underlying text layout
|
/// A [VerticalCaretMovementRun] run is valid if the underlying text layout
|
||||||
/// hasn't changed.
|
/// hasn't changed.
|
||||||
///
|
///
|
||||||
/// The [current] value and the [movePrevious] and [moveNext] methods must not
|
/// The [current] value and the [movePrevious], [moveNext] and [moveByOffset]
|
||||||
/// be accessed when [isValid] is false.
|
/// methods must not be accessed when [isValid] is false.
|
||||||
bool get isValid {
|
bool get isValid {
|
||||||
if (!_isValid) {
|
if (!_isValid) {
|
||||||
return false;
|
return false;
|
||||||
@ -200,6 +203,30 @@ class VerticalCaretMovementRun extends Iterator<TextPosition> {
|
|||||||
_currentTextPosition = position.value;
|
_currentTextPosition = position.value;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Move forward or backward by a number of elements determined
|
||||||
|
/// by pixel [offset].
|
||||||
|
///
|
||||||
|
/// If [offset] is negative, move backward; otherwise move forward.
|
||||||
|
///
|
||||||
|
/// Returns true and updates [current] if successful.
|
||||||
|
bool moveByOffset(double offset) {
|
||||||
|
final Offset initialOffset = _currentOffset;
|
||||||
|
if (offset >= 0.0) {
|
||||||
|
while (_currentOffset.dy < initialOffset.dy + offset) {
|
||||||
|
if (!moveNext()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (_currentOffset.dy > initialOffset.dy + offset) {
|
||||||
|
if (!movePrevious()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return initialOffset != _currentOffset;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Displays some text in a scrollable container with a potentially blinking
|
/// Displays some text in a scrollable container with a potentially blinking
|
||||||
|
@ -171,13 +171,13 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||||||
SingleActivator(LogicalKeyboardKey.delete, alt: true, shift: pressShift): const DeleteToLineBreakIntent(forward: true),
|
SingleActivator(LogicalKeyboardKey.delete, alt: true, shift: pressShift): const DeleteToLineBreakIntent(forward: true),
|
||||||
},
|
},
|
||||||
|
|
||||||
// Arrow: Move Selection.
|
// Arrow: Move selection.
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowLeft): const ExtendSelectionByCharacterIntent(forward: false, collapseSelection: true),
|
const SingleActivator(LogicalKeyboardKey.arrowLeft): const ExtendSelectionByCharacterIntent(forward: false, collapseSelection: true),
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowRight): const ExtendSelectionByCharacterIntent(forward: true, collapseSelection: true),
|
const SingleActivator(LogicalKeyboardKey.arrowRight): const ExtendSelectionByCharacterIntent(forward: true, collapseSelection: true),
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowUp): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: true),
|
const SingleActivator(LogicalKeyboardKey.arrowUp): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: true),
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowDown): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: true, collapseSelection: true),
|
const SingleActivator(LogicalKeyboardKey.arrowDown): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: true, collapseSelection: true),
|
||||||
|
|
||||||
// Shift + Arrow: Extend Selection.
|
// Shift + Arrow: Extend selection.
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): const ExtendSelectionByCharacterIntent(forward: false, collapseSelection: false),
|
const SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): const ExtendSelectionByCharacterIntent(forward: false, collapseSelection: false),
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): const ExtendSelectionByCharacterIntent(forward: true, collapseSelection: false),
|
const SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): const ExtendSelectionByCharacterIntent(forward: true, collapseSelection: false),
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: false),
|
const SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: false),
|
||||||
@ -199,6 +199,14 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||||||
const SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true, control: true): const ExtendSelectionToNextWordBoundaryIntent(forward: false, collapseSelection: false),
|
const SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true, control: true): const ExtendSelectionToNextWordBoundaryIntent(forward: false, collapseSelection: false),
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowRight, shift: true, control: true): const ExtendSelectionToNextWordBoundaryIntent(forward: true, collapseSelection: false),
|
const SingleActivator(LogicalKeyboardKey.arrowRight, shift: true, control: true): const ExtendSelectionToNextWordBoundaryIntent(forward: true, collapseSelection: false),
|
||||||
|
|
||||||
|
// Page Up / Down: Move selection by page.
|
||||||
|
const SingleActivator(LogicalKeyboardKey.pageUp): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: false, collapseSelection: true),
|
||||||
|
const SingleActivator(LogicalKeyboardKey.pageDown): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: true, collapseSelection: true),
|
||||||
|
|
||||||
|
// Shift + Page Up / Down: Extend selection by page.
|
||||||
|
const SingleActivator(LogicalKeyboardKey.pageUp, shift: true): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: false, collapseSelection: false),
|
||||||
|
const SingleActivator(LogicalKeyboardKey.pageDown, shift: true): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: true, collapseSelection: false),
|
||||||
|
|
||||||
const SingleActivator(LogicalKeyboardKey.keyX, control: true): const CopySelectionTextIntent.cut(SelectionChangedCause.keyboard),
|
const SingleActivator(LogicalKeyboardKey.keyX, control: true): const CopySelectionTextIntent.cut(SelectionChangedCause.keyboard),
|
||||||
const SingleActivator(LogicalKeyboardKey.keyC, control: true): CopySelectionTextIntent.copy,
|
const SingleActivator(LogicalKeyboardKey.keyC, control: true): CopySelectionTextIntent.copy,
|
||||||
const SingleActivator(LogicalKeyboardKey.keyV, control: true): const PasteTextIntent(SelectionChangedCause.keyboard),
|
const SingleActivator(LogicalKeyboardKey.keyV, control: true): const PasteTextIntent(SelectionChangedCause.keyboard),
|
||||||
@ -258,10 +266,7 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||||||
// macOS document shortcuts: https://support.apple.com/en-us/HT201236.
|
// macOS document shortcuts: https://support.apple.com/en-us/HT201236.
|
||||||
// The macOS shortcuts uses different word/line modifiers than most other
|
// The macOS shortcuts uses different word/line modifiers than most other
|
||||||
// platforms.
|
// platforms.
|
||||||
static final Map<ShortcutActivator, Intent> _macShortcuts = _iOSShortcuts;
|
static final Map<ShortcutActivator, Intent> _macShortcuts = <ShortcutActivator, Intent>{
|
||||||
|
|
||||||
// There is no complete documentation of iOS shortcuts.
|
|
||||||
static final Map<ShortcutActivator, Intent> _iOSShortcuts = <ShortcutActivator, Intent>{
|
|
||||||
for (final bool pressShift in const <bool>[true, false])
|
for (final bool pressShift in const <bool>[true, false])
|
||||||
...<SingleActivator, Intent>{
|
...<SingleActivator, Intent>{
|
||||||
SingleActivator(LogicalKeyboardKey.backspace, shift: pressShift): const DeleteCharacterIntent(forward: false),
|
SingleActivator(LogicalKeyboardKey.backspace, shift: pressShift): const DeleteCharacterIntent(forward: false),
|
||||||
@ -277,7 +282,7 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||||||
const SingleActivator(LogicalKeyboardKey.arrowUp): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: true),
|
const SingleActivator(LogicalKeyboardKey.arrowUp): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: true),
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowDown): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: true, collapseSelection: true),
|
const SingleActivator(LogicalKeyboardKey.arrowDown): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: true, collapseSelection: true),
|
||||||
|
|
||||||
// Shift + Arrow: Extend Selection.
|
// Shift + Arrow: Extend selection.
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): const ExtendSelectionByCharacterIntent(forward: false, collapseSelection: false),
|
const SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): const ExtendSelectionByCharacterIntent(forward: false, collapseSelection: false),
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): const ExtendSelectionByCharacterIntent(forward: true, collapseSelection: false),
|
const SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): const ExtendSelectionByCharacterIntent(forward: true, collapseSelection: false),
|
||||||
const SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: false),
|
const SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): const ExtendSelectionVerticallyToAdjacentLineIntent(forward: false, collapseSelection: false),
|
||||||
@ -310,6 +315,9 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||||||
const SingleActivator(LogicalKeyboardKey.home, shift: true): const ExpandSelectionToDocumentBoundaryIntent(forward: false),
|
const SingleActivator(LogicalKeyboardKey.home, shift: true): const ExpandSelectionToDocumentBoundaryIntent(forward: false),
|
||||||
const SingleActivator(LogicalKeyboardKey.end, shift: true): const ExpandSelectionToDocumentBoundaryIntent(forward: true),
|
const SingleActivator(LogicalKeyboardKey.end, shift: true): const ExpandSelectionToDocumentBoundaryIntent(forward: true),
|
||||||
|
|
||||||
|
const SingleActivator(LogicalKeyboardKey.pageUp, shift: true): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: false, collapseSelection: false),
|
||||||
|
const SingleActivator(LogicalKeyboardKey.pageDown, shift: true): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: true, collapseSelection: false),
|
||||||
|
|
||||||
const SingleActivator(LogicalKeyboardKey.keyX, meta: true): const CopySelectionTextIntent.cut(SelectionChangedCause.keyboard),
|
const SingleActivator(LogicalKeyboardKey.keyX, meta: true): const CopySelectionTextIntent.cut(SelectionChangedCause.keyboard),
|
||||||
const SingleActivator(LogicalKeyboardKey.keyC, meta: true): CopySelectionTextIntent.copy,
|
const SingleActivator(LogicalKeyboardKey.keyC, meta: true): CopySelectionTextIntent.copy,
|
||||||
const SingleActivator(LogicalKeyboardKey.keyV, meta: true): const PasteTextIntent(SelectionChangedCause.keyboard),
|
const SingleActivator(LogicalKeyboardKey.keyV, meta: true): const PasteTextIntent(SelectionChangedCause.keyboard),
|
||||||
@ -335,6 +343,8 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||||||
// * Control + shift? + Z
|
// * Control + shift? + Z
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// There is no complete documentation of iOS shortcuts: use macOS ones.
|
||||||
|
static final Map<ShortcutActivator, Intent> _iOSShortcuts = _macShortcuts;
|
||||||
|
|
||||||
// The following key combinations have no effect on text editing on this
|
// The following key combinations have no effect on text editing on this
|
||||||
// platform:
|
// platform:
|
||||||
@ -350,6 +360,8 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||||||
// * Meta + backspace
|
// * Meta + backspace
|
||||||
static final Map<ShortcutActivator, Intent> _windowsShortcuts = <ShortcutActivator, Intent>{
|
static final Map<ShortcutActivator, Intent> _windowsShortcuts = <ShortcutActivator, Intent>{
|
||||||
..._commonShortcuts,
|
..._commonShortcuts,
|
||||||
|
const SingleActivator(LogicalKeyboardKey.pageUp): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: false, collapseSelection: true),
|
||||||
|
const SingleActivator(LogicalKeyboardKey.pageDown): const ExtendSelectionVerticallyToAdjacentPageIntent(forward: true, collapseSelection: true),
|
||||||
const SingleActivator(LogicalKeyboardKey.home): const ExtendSelectionToLineBreakIntent(forward: false, collapseSelection: true, continuesAtWrap: true),
|
const SingleActivator(LogicalKeyboardKey.home): const ExtendSelectionToLineBreakIntent(forward: false, collapseSelection: true, continuesAtWrap: true),
|
||||||
const SingleActivator(LogicalKeyboardKey.end): const ExtendSelectionToLineBreakIntent(forward: true, collapseSelection: true, continuesAtWrap: true),
|
const SingleActivator(LogicalKeyboardKey.end): const ExtendSelectionToLineBreakIntent(forward: true, collapseSelection: true, continuesAtWrap: true),
|
||||||
const SingleActivator(LogicalKeyboardKey.home, shift: true): const ExtendSelectionToLineBreakIntent(forward: false, collapseSelection: false, continuesAtWrap: true),
|
const SingleActivator(LogicalKeyboardKey.home, shift: true): const ExtendSelectionToLineBreakIntent(forward: false, collapseSelection: false, continuesAtWrap: true),
|
||||||
@ -385,7 +397,6 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||||||
const SingleActivator(LogicalKeyboardKey.keyA, meta: true): const DoNothingAndStopPropagationTextIntent(),
|
const SingleActivator(LogicalKeyboardKey.keyA, meta: true): const DoNothingAndStopPropagationTextIntent(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
static const Map<ShortcutActivator, Intent> _commonDisablingTextShortcuts = <ShortcutActivator, Intent>{
|
static const Map<ShortcutActivator, Intent> _commonDisablingTextShortcuts = <ShortcutActivator, Intent>{
|
||||||
SingleActivator(LogicalKeyboardKey.arrowDown, alt: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.arrowDown, alt: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.arrowLeft, alt: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.arrowLeft, alt: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
@ -407,6 +418,8 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||||||
SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.arrowRight, shift: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.arrowUp, shift: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
|
SingleActivator(LogicalKeyboardKey.pageUp, shift: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
|
SingleActivator(LogicalKeyboardKey.pageDown, shift: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.end, shift: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.end, shift: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.home, shift: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.home, shift: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.arrowDown): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.arrowDown): DoNothingAndStopPropagationTextIntent(),
|
||||||
@ -417,6 +430,8 @@ class DefaultTextEditingShortcuts extends StatelessWidget {
|
|||||||
SingleActivator(LogicalKeyboardKey.arrowRight, control: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.arrowRight, control: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true, control: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.arrowLeft, shift: true, control: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.arrowRight, shift: true, control: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.arrowRight, shift: true, control: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
|
SingleActivator(LogicalKeyboardKey.pageUp): DoNothingAndStopPropagationTextIntent(),
|
||||||
|
SingleActivator(LogicalKeyboardKey.pageDown): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.end): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.end): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.home): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.home): DoNothingAndStopPropagationTextIntent(),
|
||||||
SingleActivator(LogicalKeyboardKey.end, control: true): DoNothingAndStopPropagationTextIntent(),
|
SingleActivator(LogicalKeyboardKey.end, control: true): DoNothingAndStopPropagationTextIntent(),
|
||||||
@ -545,8 +560,8 @@ Intent? intentForMacOSSelector(String selectorName) {
|
|||||||
// TODO(knopp): Page Up/Down intents are missing (https://github.com/flutter/flutter/pull/105497)
|
// TODO(knopp): Page Up/Down intents are missing (https://github.com/flutter/flutter/pull/105497)
|
||||||
'scrollPageUp:': ScrollToDocumentBoundaryIntent(forward: false),
|
'scrollPageUp:': ScrollToDocumentBoundaryIntent(forward: false),
|
||||||
'scrollPageDown:': ScrollToDocumentBoundaryIntent(forward: true),
|
'scrollPageDown:': ScrollToDocumentBoundaryIntent(forward: true),
|
||||||
'pageUpAndModifySelection': ExpandSelectionToDocumentBoundaryIntent(forward: false),
|
'pageUpAndModifySelection:': ExtendSelectionVerticallyToAdjacentPageIntent(forward: false, collapseSelection: false),
|
||||||
'pageDownAndModifySelection:': ExpandSelectionToDocumentBoundaryIntent(forward: true),
|
'pageDownAndModifySelection:': ExtendSelectionVerticallyToAdjacentPageIntent(forward: true, collapseSelection: false),
|
||||||
|
|
||||||
// Escape key when there's no IME selection popup.
|
// Escape key when there's no IME selection popup.
|
||||||
'cancelOperation:': DismissIntent(),
|
'cancelOperation:': DismissIntent(),
|
||||||
|
@ -479,6 +479,7 @@ class _DiscreteKeyFrameSimulation extends Simulation {
|
|||||||
/// | [ExtendSelectionToNextWordBoundaryOrCaretLocationIntent](`collapseSelection: true`) | Collapses the selection to the word boundary before/after the selection's [TextSelection.extent] position, or [TextSelection.base], whichever is closest in the given direction | Moves the caret to the previous/next word boundary. |
|
/// | [ExtendSelectionToNextWordBoundaryOrCaretLocationIntent](`collapseSelection: true`) | Collapses the selection to the word boundary before/after the selection's [TextSelection.extent] position, or [TextSelection.base], whichever is closest in the given direction | Moves the caret to the previous/next word boundary. |
|
||||||
/// | [ExtendSelectionToLineBreakIntent](`collapseSelection: true`) | Collapses the selection to the start/end of the line at the selection's [TextSelection.extent] position | Moves the caret to the start/end of the current line .|
|
/// | [ExtendSelectionToLineBreakIntent](`collapseSelection: true`) | Collapses the selection to the start/end of the line at the selection's [TextSelection.extent] position | Moves the caret to the start/end of the current line .|
|
||||||
/// | [ExtendSelectionVerticallyToAdjacentLineIntent](`collapseSelection: true`) | Collapses the selection to the position closest to the selection's [TextSelection.extent], on the previous/next adjacent line | Moves the caret to the closest position on the previous/next adjacent line. |
|
/// | [ExtendSelectionVerticallyToAdjacentLineIntent](`collapseSelection: true`) | Collapses the selection to the position closest to the selection's [TextSelection.extent], on the previous/next adjacent line | Moves the caret to the closest position on the previous/next adjacent line. |
|
||||||
|
/// | [ExtendSelectionVerticallyToAdjacentPageIntent](`collapseSelection: true`) | Collapses the selection to the position closest to the selection's [TextSelection.extent], on the previous/next adjacent page | Moves the caret to the closest position on the previous/next adjacent page. |
|
||||||
/// | [ExtendSelectionToDocumentBoundaryIntent](`collapseSelection: true`) | Collapses the selection to the start/end of the document | Moves the caret to the start/end of the document. |
|
/// | [ExtendSelectionToDocumentBoundaryIntent](`collapseSelection: true`) | Collapses the selection to the start/end of the document | Moves the caret to the start/end of the document. |
|
||||||
///
|
///
|
||||||
/// #### Intents for Extending the Selection
|
/// #### Intents for Extending the Selection
|
||||||
@ -490,6 +491,7 @@ class _DiscreteKeyFrameSimulation extends Simulation {
|
|||||||
/// | [ExtendSelectionToNextWordBoundaryOrCaretLocationIntent](`collapseSelection: false`) | Moves the selection's [TextSelection.extent] to the previous/next word boundary, or [TextSelection.base] whichever is closest in the given direction | Moves the selection's [TextSelection.extent] to the previous/next word boundary. |
|
/// | [ExtendSelectionToNextWordBoundaryOrCaretLocationIntent](`collapseSelection: false`) | Moves the selection's [TextSelection.extent] to the previous/next word boundary, or [TextSelection.base] whichever is closest in the given direction | Moves the selection's [TextSelection.extent] to the previous/next word boundary. |
|
||||||
/// | [ExtendSelectionToLineBreakIntent](`collapseSelection: false`) | Moves the selection's [TextSelection.extent] to the start/end of the line |
|
/// | [ExtendSelectionToLineBreakIntent](`collapseSelection: false`) | Moves the selection's [TextSelection.extent] to the start/end of the line |
|
||||||
/// | [ExtendSelectionVerticallyToAdjacentLineIntent](`collapseSelection: false`) | Moves the selection's [TextSelection.extent] to the closest position on the previous/next adjacent line |
|
/// | [ExtendSelectionVerticallyToAdjacentLineIntent](`collapseSelection: false`) | Moves the selection's [TextSelection.extent] to the closest position on the previous/next adjacent line |
|
||||||
|
/// | [ExtendSelectionVerticallyToAdjacentPageIntent](`collapseSelection: false`) | Moves the selection's [TextSelection.extent] to the closest position on the previous/next adjacent page |
|
||||||
/// | [ExtendSelectionToDocumentBoundaryIntent](`collapseSelection: false`) | Moves the selection's [TextSelection.extent] to the start/end of the document |
|
/// | [ExtendSelectionToDocumentBoundaryIntent](`collapseSelection: false`) | Moves the selection's [TextSelection.extent] to the start/end of the document |
|
||||||
/// | [SelectAllTextIntent] | Selects the entire document |
|
/// | [SelectAllTextIntent] | Selects the entire document |
|
||||||
///
|
///
|
||||||
@ -3106,7 +3108,7 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||||||
// TODO(abarth): Teach RenderEditable about ValueNotifier<TextEditingValue>
|
// TODO(abarth): Teach RenderEditable about ValueNotifier<TextEditingValue>
|
||||||
// to avoid this setState().
|
// to avoid this setState().
|
||||||
setState(() { /* We use widget.controller.value in build(). */ });
|
setState(() { /* We use widget.controller.value in build(). */ });
|
||||||
_adjacentLineAction.stopCurrentVerticalRunIfSelectionChanges();
|
_verticalSelectionUpdateAction.stopCurrentVerticalRunIfSelectionChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleFocusChanged() {
|
void _handleFocusChanged() {
|
||||||
@ -3589,7 +3591,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||||||
}
|
}
|
||||||
late final Action<UpdateSelectionIntent> _updateSelectionAction = CallbackAction<UpdateSelectionIntent>(onInvoke: _updateSelection);
|
late final Action<UpdateSelectionIntent> _updateSelectionAction = CallbackAction<UpdateSelectionIntent>(onInvoke: _updateSelection);
|
||||||
|
|
||||||
late final _UpdateTextSelectionToAdjacentLineAction<ExtendSelectionVerticallyToAdjacentLineIntent> _adjacentLineAction = _UpdateTextSelectionToAdjacentLineAction<ExtendSelectionVerticallyToAdjacentLineIntent>(this);
|
late final _UpdateTextSelectionVerticallyAction<DirectionalCaretMovementIntent> _verticalSelectionUpdateAction =
|
||||||
|
_UpdateTextSelectionVerticallyAction<DirectionalCaretMovementIntent>(this);
|
||||||
|
|
||||||
void _expandSelectionToDocumentBoundary(ExpandSelectionToDocumentBoundaryIntent intent) {
|
void _expandSelectionToDocumentBoundary(ExpandSelectionToDocumentBoundaryIntent intent) {
|
||||||
final TextBoundary textBoundary = _documentBoundary(intent);
|
final TextBoundary textBoundary = _documentBoundary(intent);
|
||||||
@ -3717,7 +3720,8 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
|
|||||||
ExtendSelectionToLineBreakIntent: _makeOverridable(_UpdateTextSelectionAction<ExtendSelectionToLineBreakIntent>(this, true, _linebreak)),
|
ExtendSelectionToLineBreakIntent: _makeOverridable(_UpdateTextSelectionAction<ExtendSelectionToLineBreakIntent>(this, true, _linebreak)),
|
||||||
ExpandSelectionToLineBreakIntent: _makeOverridable(CallbackAction<ExpandSelectionToLineBreakIntent>(onInvoke: _expandSelectionToLinebreak)),
|
ExpandSelectionToLineBreakIntent: _makeOverridable(CallbackAction<ExpandSelectionToLineBreakIntent>(onInvoke: _expandSelectionToLinebreak)),
|
||||||
ExpandSelectionToDocumentBoundaryIntent: _makeOverridable(CallbackAction<ExpandSelectionToDocumentBoundaryIntent>(onInvoke: _expandSelectionToDocumentBoundary)),
|
ExpandSelectionToDocumentBoundaryIntent: _makeOverridable(CallbackAction<ExpandSelectionToDocumentBoundaryIntent>(onInvoke: _expandSelectionToDocumentBoundary)),
|
||||||
ExtendSelectionVerticallyToAdjacentLineIntent: _makeOverridable(_adjacentLineAction),
|
ExtendSelectionVerticallyToAdjacentLineIntent: _makeOverridable(_verticalSelectionUpdateAction),
|
||||||
|
ExtendSelectionVerticallyToAdjacentPageIntent: _makeOverridable(_verticalSelectionUpdateAction),
|
||||||
ExtendSelectionToDocumentBoundaryIntent: _makeOverridable(_UpdateTextSelectionAction<ExtendSelectionToDocumentBoundaryIntent>(this, true, _documentBoundary)),
|
ExtendSelectionToDocumentBoundaryIntent: _makeOverridable(_UpdateTextSelectionAction<ExtendSelectionToDocumentBoundaryIntent>(this, true, _documentBoundary)),
|
||||||
ExtendSelectionToNextWordBoundaryOrCaretLocationIntent: _makeOverridable(_ExtendSelectionOrCaretPositionAction(this, _nextWordBoundary)),
|
ExtendSelectionToNextWordBoundaryOrCaretLocationIntent: _makeOverridable(_ExtendSelectionOrCaretPositionAction(this, _nextWordBoundary)),
|
||||||
ScrollToDocumentBoundaryIntent: _makeOverridable(CallbackAction<ScrollToDocumentBoundaryIntent>(onInvoke: _scrollToDocumentBoundary)),
|
ScrollToDocumentBoundaryIntent: _makeOverridable(CallbackAction<ScrollToDocumentBoundaryIntent>(onInvoke: _scrollToDocumentBoundary)),
|
||||||
@ -4604,8 +4608,8 @@ class _ExtendSelectionOrCaretPositionAction extends ContextAction<ExtendSelectio
|
|||||||
bool get isActionEnabled => state.widget.selectionEnabled && state._value.selection.isValid;
|
bool get isActionEnabled => state.widget.selectionEnabled && state._value.selection.isValid;
|
||||||
}
|
}
|
||||||
|
|
||||||
class _UpdateTextSelectionToAdjacentLineAction<T extends DirectionalCaretMovementIntent> extends ContextAction<T> {
|
class _UpdateTextSelectionVerticallyAction<T extends DirectionalCaretMovementIntent> extends ContextAction<T> {
|
||||||
_UpdateTextSelectionToAdjacentLineAction(this.state);
|
_UpdateTextSelectionVerticallyAction(this.state);
|
||||||
|
|
||||||
final EditableTextState state;
|
final EditableTextState state;
|
||||||
|
|
||||||
@ -4647,10 +4651,12 @@ class _UpdateTextSelectionToAdjacentLineAction<T extends DirectionalCaretMovemen
|
|||||||
final VerticalCaretMovementRun currentRun = _verticalMovementRun
|
final VerticalCaretMovementRun currentRun = _verticalMovementRun
|
||||||
?? state.renderEditable.startVerticalCaretMovement(state.renderEditable.selection!.extent);
|
?? state.renderEditable.startVerticalCaretMovement(state.renderEditable.selection!.extent);
|
||||||
|
|
||||||
final bool shouldMove = intent.forward ? currentRun.moveNext() : currentRun.movePrevious();
|
final bool shouldMove = intent is ExtendSelectionVerticallyToAdjacentPageIntent
|
||||||
|
? currentRun.moveByOffset((intent.forward ? 1.0 : -1.0) * state.renderEditable.size.height)
|
||||||
|
: intent.forward ? currentRun.moveNext() : currentRun.movePrevious();
|
||||||
final TextPosition newExtent = shouldMove
|
final TextPosition newExtent = shouldMove
|
||||||
? currentRun.current
|
? currentRun.current
|
||||||
: (intent.forward ? TextPosition(offset: state._value.text.length) : const TextPosition(offset: 0));
|
: intent.forward ? TextPosition(offset: state._value.text.length) : const TextPosition(offset: 0);
|
||||||
final TextSelection newSelection = collapseSelection
|
final TextSelection newSelection = collapseSelection
|
||||||
? TextSelection.fromPosition(newExtent)
|
? TextSelection.fromPosition(newExtent)
|
||||||
: value.selection.extendTo(newExtent);
|
: value.selection.extendTo(newExtent);
|
||||||
|
@ -210,6 +210,17 @@ class ExtendSelectionVerticallyToAdjacentLineIntent extends DirectionalCaretMove
|
|||||||
}) : super(forward, collapseSelection);
|
}) : super(forward, collapseSelection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Expands, or moves the current selection from the current
|
||||||
|
/// [TextSelection.extent] position to the closest position on the adjacent
|
||||||
|
/// page.
|
||||||
|
class ExtendSelectionVerticallyToAdjacentPageIntent extends DirectionalCaretMovementIntent {
|
||||||
|
/// Creates an [ExtendSelectionVerticallyToAdjacentPageIntent].
|
||||||
|
const ExtendSelectionVerticallyToAdjacentPageIntent({
|
||||||
|
required bool forward,
|
||||||
|
required bool collapseSelection,
|
||||||
|
}) : super(forward, collapseSelection);
|
||||||
|
}
|
||||||
|
|
||||||
/// Extends, or moves the current selection from the current
|
/// Extends, or moves the current selection from the current
|
||||||
/// [TextSelection.extent] position to the start or the end of the document.
|
/// [TextSelection.extent] position to the start or the end of the document.
|
||||||
///
|
///
|
||||||
|
@ -121,6 +121,8 @@ void main() {
|
|||||||
LogicalKeyboardKey.arrowRight,
|
LogicalKeyboardKey.arrowRight,
|
||||||
LogicalKeyboardKey.arrowUp,
|
LogicalKeyboardKey.arrowUp,
|
||||||
LogicalKeyboardKey.arrowDown,
|
LogicalKeyboardKey.arrowDown,
|
||||||
|
LogicalKeyboardKey.pageUp,
|
||||||
|
LogicalKeyboardKey.pageDown,
|
||||||
LogicalKeyboardKey.home,
|
LogicalKeyboardKey.home,
|
||||||
LogicalKeyboardKey.end,
|
LogicalKeyboardKey.end,
|
||||||
];
|
];
|
||||||
@ -1447,6 +1449,18 @@ void main() {
|
|||||||
reason: activator.toString(),
|
reason: activator.toString(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (final SingleActivator activator in allModifierVariants(LogicalKeyboardKey.pageUp)) {
|
||||||
|
await sendKeyCombination(tester, activator);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(controller.text, testText);
|
||||||
|
expect(
|
||||||
|
controller.selection,
|
||||||
|
const TextSelection.collapsed(offset: 0),
|
||||||
|
reason: activator.toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}, variant: TargetPlatformVariant.all());
|
}, variant: TargetPlatformVariant.all());
|
||||||
|
|
||||||
testWidgets('at end', (WidgetTester tester) async {
|
testWidgets('at end', (WidgetTester tester) async {
|
||||||
@ -1465,6 +1479,15 @@ void main() {
|
|||||||
expect(controller.selection.baseOffset, 72, reason: activator.toString());
|
expect(controller.selection.baseOffset, 72, reason: activator.toString());
|
||||||
expect(controller.selection.extentOffset, 72, reason: activator.toString());
|
expect(controller.selection.extentOffset, 72, reason: activator.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (final SingleActivator activator in allModifierVariants(LogicalKeyboardKey.pageDown)) {
|
||||||
|
await sendKeyCombination(tester, activator);
|
||||||
|
await tester.pump();
|
||||||
|
|
||||||
|
expect(controller.text, testText);
|
||||||
|
expect(controller.selection.baseOffset, 72, reason: activator.toString());
|
||||||
|
expect(controller.selection.extentOffset, 72, reason: activator.toString());
|
||||||
|
}
|
||||||
}, variant: TargetPlatformVariant.all());
|
}, variant: TargetPlatformVariant.all());
|
||||||
|
|
||||||
testWidgets('run', (WidgetTester tester) async {
|
testWidgets('run', (WidgetTester tester) async {
|
||||||
@ -1554,6 +1577,41 @@ void main() {
|
|||||||
));
|
));
|
||||||
}, variant: TargetPlatformVariant.all());
|
}, variant: TargetPlatformVariant.all());
|
||||||
|
|
||||||
|
testWidgets('run with page down/up', (WidgetTester tester) async {
|
||||||
|
controller.text =
|
||||||
|
'aa\n' // 3
|
||||||
|
'a\n' // 3 + 2 = 5
|
||||||
|
'aa\n' // 5 + 3 = 8
|
||||||
|
'aaa\n' // 8 + 4 = 12
|
||||||
|
'${"aaa\n" * 50}'
|
||||||
|
'aaaa';
|
||||||
|
|
||||||
|
controller.selection = const TextSelection.collapsed(offset: 2);
|
||||||
|
await tester.pumpWidget(buildEditableText());
|
||||||
|
|
||||||
|
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.arrowDown));
|
||||||
|
await tester.pump();
|
||||||
|
expect(controller.selection, const TextSelection.collapsed(
|
||||||
|
offset: 4,
|
||||||
|
affinity: TextAffinity.upstream,
|
||||||
|
));
|
||||||
|
|
||||||
|
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.pageDown));
|
||||||
|
await tester.pump();
|
||||||
|
expect(controller.selection, const TextSelection.collapsed(offset: 82));
|
||||||
|
|
||||||
|
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.arrowUp));
|
||||||
|
await tester.pump();
|
||||||
|
expect(controller.selection, const TextSelection.collapsed(offset: 78));
|
||||||
|
|
||||||
|
await sendKeyCombination(tester, const SingleActivator(LogicalKeyboardKey.pageUp));
|
||||||
|
await tester.pump();
|
||||||
|
expect(controller.selection, const TextSelection.collapsed(
|
||||||
|
offset: 2,
|
||||||
|
affinity: TextAffinity.upstream,
|
||||||
|
));
|
||||||
|
}, variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{TargetPlatform.iOS, TargetPlatform.macOS})); // intended: on macOS Page Up/Down only scrolls
|
||||||
|
|
||||||
testWidgets('run can be interrupted by layout changes', (WidgetTester tester) async {
|
testWidgets('run can be interrupted by layout changes', (WidgetTester tester) async {
|
||||||
controller.text =
|
controller.text =
|
||||||
'aa\n' // 3
|
'aa\n' // 3
|
||||||
|
@ -6164,6 +6164,88 @@ void main() {
|
|||||||
reason: 'on $platform',
|
reason: 'on $platform',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Move down by page.
|
||||||
|
await sendKeys(
|
||||||
|
tester,
|
||||||
|
<LogicalKeyboardKey>[
|
||||||
|
LogicalKeyboardKey.pageDown,
|
||||||
|
],
|
||||||
|
targetPlatform: defaultTargetPlatform,
|
||||||
|
);
|
||||||
|
|
||||||
|
// On macOS, pageDown/Up don't change selection.
|
||||||
|
expect(
|
||||||
|
selection,
|
||||||
|
equals(
|
||||||
|
defaultTargetPlatform == TargetPlatform.macOS
|
||||||
|
|| defaultTargetPlatform == TargetPlatform.iOS
|
||||||
|
? const TextSelection.collapsed(offset: 0)
|
||||||
|
: const TextSelection.collapsed(offset: 55),
|
||||||
|
),
|
||||||
|
reason: 'on $platform',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Move up by page (to start).
|
||||||
|
await sendKeys(
|
||||||
|
tester,
|
||||||
|
<LogicalKeyboardKey>[
|
||||||
|
LogicalKeyboardKey.pageUp,
|
||||||
|
],
|
||||||
|
targetPlatform: defaultTargetPlatform,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
selection,
|
||||||
|
equals(
|
||||||
|
const TextSelection.collapsed(
|
||||||
|
offset: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
reason: 'on $platform',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Select towards end by page.
|
||||||
|
await sendKeys(
|
||||||
|
tester,
|
||||||
|
<LogicalKeyboardKey>[
|
||||||
|
LogicalKeyboardKey.pageDown,
|
||||||
|
],
|
||||||
|
shift: true,
|
||||||
|
targetPlatform: defaultTargetPlatform,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
selection,
|
||||||
|
equals(
|
||||||
|
const TextSelection(
|
||||||
|
baseOffset: 0,
|
||||||
|
extentOffset: 55,
|
||||||
|
affinity: TextAffinity.upstream,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
reason: 'on $platform',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Change selection extent towards start by page.
|
||||||
|
await sendKeys(
|
||||||
|
tester,
|
||||||
|
<LogicalKeyboardKey>[
|
||||||
|
LogicalKeyboardKey.pageUp,
|
||||||
|
],
|
||||||
|
shift: true,
|
||||||
|
targetPlatform: defaultTargetPlatform,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
selection,
|
||||||
|
equals(
|
||||||
|
const TextSelection.collapsed(
|
||||||
|
offset: 0,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
reason: 'on $platform',
|
||||||
|
);
|
||||||
|
|
||||||
// Jump forward three words.
|
// Jump forward three words.
|
||||||
await sendKeys(
|
await sendKeys(
|
||||||
tester,
|
tester,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user