Unverified Commit d04c906e authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

Add arguments to SemanticsActions; implement extend selection for a11y (#13490)

**This PR contains a breaking API change:**
`SemanticsConfiguration.addAction()` has been removed and replaces by action-specific setters (`onTap`, `onLongPress`, etc.) to take care of the fact that some action handlers (those, who take arguments) have different signatures.
parent 1dd68d5d
2bdb21985c5eb9250a7b744b9b4d606c5ba30945
e07eafae1d14b454c33b1ae68d14b7a5694c22b6
......@@ -383,8 +383,8 @@ class _RenderCupertinoSlider extends RenderConstrainedBox {
config.isSemanticBoundary = isInteractive;
if (isInteractive) {
config.addAction(SemanticsAction.increase, _increaseAction);
config.addAction(SemanticsAction.decrease, _decreaseAction);
config.onIncrease = _increaseAction;
config.onDecrease = _decreaseAction;
}
}
......
......@@ -380,7 +380,7 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
config.isSemanticBoundary = isInteractive;
if (isInteractive)
config.addAction(SemanticsAction.tap, _handleTap);
config.onTap = _handleTap;
config.isChecked = _value;
}
......
......@@ -713,8 +713,8 @@ class _RenderSlider extends RenderBox {
config.isSemanticBoundary = isInteractive;
if (isInteractive) {
config.addAction(SemanticsAction.increase, _increaseAction);
config.addAction(SemanticsAction.decrease, _decreaseAction);
config.onIncrease = _increaseAction;
config.onDecrease = _decreaseAction;
}
}
......
......@@ -288,7 +288,7 @@ abstract class RenderToggleable extends RenderConstrainedBox {
config.isSemanticBoundary = isInteractive;
if (isInteractive)
config.addAction(SemanticsAction.tap, _handleTap);
config.onTap = _handleTap;
config.isChecked = _value;
}
......
......@@ -4,6 +4,7 @@
import 'dart:async';
import 'dart:developer';
import 'dart:typed_data';
import 'dart:ui' as ui show window;
import 'package:flutter/foundation.dart';
......@@ -187,8 +188,12 @@ abstract class RendererBinding extends BindingBase with ServicesBinding, Schedul
}
}
void _handleSemanticsAction(int id, SemanticsAction action) {
_pipelineOwner.semanticsOwner?.performAction(id, action);
void _handleSemanticsAction(int id, SemanticsAction action, ByteData args) {
_pipelineOwner.semanticsOwner?.performAction(
id,
action,
args != null ? const StandardMessageCodec().decodeMessage(args) : null,
);
}
void _handleSemanticsOwnerCreated() {
......
......@@ -823,34 +823,34 @@ class RenderCustomPaint extends RenderProxyBox {
config.textDirection = properties.textDirection;
}
if (properties.onTap != null) {
config.addAction(SemanticsAction.tap, properties.onTap);
config.onTap = properties.onTap;
}
if (properties.onLongPress != null) {
config.addAction(SemanticsAction.longPress, properties.onLongPress);
config.onLongPress = properties.onLongPress;
}
if (properties.onScrollLeft != null) {
config.addAction(SemanticsAction.scrollLeft, properties.onScrollLeft);
config.onScrollLeft = properties.onScrollLeft;
}
if (properties.onScrollRight != null) {
config.addAction(SemanticsAction.scrollRight, properties.onScrollRight);
config.onScrollRight = properties.onScrollRight;
}
if (properties.onScrollUp != null) {
config.addAction(SemanticsAction.scrollUp, properties.onScrollUp);
config.onScrollUp = properties.onScrollUp;
}
if (properties.onScrollDown != null) {
config.addAction(SemanticsAction.scrollDown, properties.onScrollDown);
config.onScrollDown = properties.onScrollDown;
}
if (properties.onIncrease != null) {
config.addAction(SemanticsAction.increase, properties.onIncrease);
config.onIncrease = properties.onIncrease;
}
if (properties.onDecrease != null) {
config.addAction(SemanticsAction.decrease, properties.onDecrease);
config.onDecrease = properties.onDecrease;
}
if (properties.onMoveCursorForwardByCharacter != null) {
config.addAction(SemanticsAction.moveCursorForwardByCharacter, properties.onMoveCursorForwardByCharacter);
config.onMoveCursorForwardByCharacter = properties.onMoveCursorForwardByCharacter;
}
if (properties.onMoveCursorBackwardByCharacter != null) {
config.addAction(SemanticsAction.moveCursorBackwardByCharacter, properties.onMoveCursorBackwardByCharacter);
config.onMoveCursorBackwardByCharacter = properties.onMoveCursorBackwardByCharacter;
}
newChild.updateWith(
......
......@@ -349,30 +349,33 @@ class RenderEditable extends RenderBox {
..isTextField = true;
if (_selection?.isValid == true) {
if (_textPainter.getOffsetBefore(_selection.extentOffset) != null) {
config.addAction(SemanticsAction.moveCursorBackwardByCharacter, () {
final int offset = _textPainter.getOffsetBefore(_selection.extentOffset);
if (offset == null)
return;
onSelectionChanged(
new TextSelection.collapsed(offset: offset), this, SelectionChangedCause.keyboard,
);
});
}
if (_textPainter.getOffsetAfter(_selection.extentOffset) != null) {
config.addAction(SemanticsAction.moveCursorForwardByCharacter, () {
final int offset = _textPainter.getOffsetAfter(_selection.extentOffset);
if (offset == null)
return;
onSelectionChanged(
new TextSelection.collapsed(offset: offset), this, SelectionChangedCause.keyboard,
);
});
}
if (_textPainter.getOffsetBefore(_selection.extentOffset) != null)
config.onMoveCursorBackwardByCharacter = _handleMoveCursorBackwardByCharacter;
if (_textPainter.getOffsetAfter(_selection.extentOffset) != null)
config.onMoveCursorForwardByCharacter = _handleMoveCursorForwardByCharacter;
}
}
void _handleMoveCursorForwardByCharacter(bool extentSelection) {
final int extentOffset = _textPainter.getOffsetAfter(_selection.extentOffset);
if (extentOffset == null)
return;
final int baseOffset = !extentSelection ? extentOffset : _selection.baseOffset;
onSelectionChanged(
new TextSelection(baseOffset: baseOffset, extentOffset: extentOffset), this, SelectionChangedCause.keyboard,
);
}
void _handleMoveCursorBackwardByCharacter(bool extentSelection) {
final int extentOffset = _textPainter.getOffsetBefore(_selection.extentOffset);
if (extentOffset == null)
return;
final int baseOffset = !extentSelection ? extentOffset : _selection.baseOffset;
onSelectionChanged(
new TextSelection(baseOffset: baseOffset, extentOffset: extentOffset), this, SelectionChangedCause.keyboard,
);
}
@override
void attach(PipelineOwner owner) {
super.attach(owner);
......
......@@ -2140,7 +2140,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im
/// void describeSemanticsConfiguration(SemanticsConfiguration config) {
/// super.describeSemanticsConfiguration(config);
/// config
/// ..addAction(SemanticsAction.tap, _handleTap)
/// ..onTap = _handleTap
/// ..label = 'I am a button'
/// ..isButton = true;
/// }
......
......@@ -2685,27 +2685,26 @@ class RenderSemanticsGestureHandler extends RenderProxyBox {
config.explicitChildNodes = onHorizontalDragUpdate != null
|| onVerticalDragUpdate != null;
final Map<SemanticsAction, VoidCallback> actions = <SemanticsAction, VoidCallback>{};
if (onTap != null)
actions[SemanticsAction.tap] = onTap;
if (onLongPress != null)
actions[SemanticsAction.longPress] = onLongPress;
if (onTap != null && _isValidAction(SemanticsAction.tap))
config.onTap = onTap;
if (onLongPress != null && _isValidAction(SemanticsAction.longPress))
config.onLongPress = onLongPress;
if (onHorizontalDragUpdate != null) {
actions[SemanticsAction.scrollRight] = _performSemanticScrollRight;
actions[SemanticsAction.scrollLeft] = _performSemanticScrollLeft;
if (_isValidAction(SemanticsAction.scrollRight))
config.onScrollRight = _performSemanticScrollRight;
if (_isValidAction(SemanticsAction.scrollLeft))
config.onScrollLeft = _performSemanticScrollLeft;
}
if (onVerticalDragUpdate != null) {
actions[SemanticsAction.scrollUp] = _performSemanticScrollUp;
actions[SemanticsAction.scrollDown] = _performSemanticScrollDown;
if (_isValidAction(SemanticsAction.scrollUp))
config.onScrollUp = _performSemanticScrollUp;
if (_isValidAction(SemanticsAction.scrollDown))
config.onScrollDown = _performSemanticScrollDown;
}
}
final Iterable<SemanticsAction> actionsToAdd = validActions ?? actions.keys;
for (SemanticsAction action in actionsToAdd) {
final VoidCallback handler = actions[action];
if (handler != null)
config.addAction(action, handler);
}
bool _isValidAction(SemanticsAction action) {
return validActions == null || validActions.contains(action);
}
SemanticsNode _innerNode;
......@@ -2830,8 +2829,8 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
VoidCallback onScrollDown,
VoidCallback onIncrease,
VoidCallback onDecrease,
VoidCallback onMoveCursorForwardByCharacter,
VoidCallback onMoveCursorBackwardByCharacter,
MoveCursorHandler onMoveCursorForwardByCharacter,
MoveCursorHandler onMoveCursorBackwardByCharacter,
}) : assert(container != null),
_container = container,
_explicitChildNodes = explicitChildNodes,
......@@ -3173,9 +3172,9 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
///
/// TalkBack users can trigger this by pressing the volume up key while the
/// input focus is in a text field.
VoidCallback get onMoveCursorForwardByCharacter => _onMoveCursorForwardByCharacter;
VoidCallback _onMoveCursorForwardByCharacter;
set onMoveCursorForwardByCharacter(VoidCallback handler) {
MoveCursorHandler get onMoveCursorForwardByCharacter => _onMoveCursorForwardByCharacter;
MoveCursorHandler _onMoveCursorForwardByCharacter;
set onMoveCursorForwardByCharacter(MoveCursorHandler handler) {
if (_onMoveCursorForwardByCharacter == handler)
return;
final bool hadValue = _onMoveCursorForwardByCharacter != null;
......@@ -3191,9 +3190,9 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
///
/// TalkBack users can trigger this by pressing the volume down key while the
/// input focus is in a text field.
VoidCallback get onMoveCursorBackwardByCharacter => _onMoveCursorBackwardByCharacter;
VoidCallback _onMoveCursorBackwardByCharacter;
set onMoveCursorBackwardByCharacter(VoidCallback handler) {
MoveCursorHandler get onMoveCursorBackwardByCharacter => _onMoveCursorBackwardByCharacter;
MoveCursorHandler _onMoveCursorBackwardByCharacter;
set onMoveCursorBackwardByCharacter(MoveCursorHandler handler) {
if (_onMoveCursorBackwardByCharacter == handler)
return;
final bool hadValue = _onMoveCursorBackwardByCharacter != null;
......@@ -3230,25 +3229,25 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
// ones to ensure that changing a user provided handler from a non-null to
// another non-null value doesn't require a semantics update.
if (onTap != null)
config.addAction(SemanticsAction.tap, _performTap);
config.onTap = _performTap;
if (onLongPress != null)
config.addAction(SemanticsAction.longPress, _performLongPress);
config.onLongPress = _performLongPress;
if (onScrollLeft != null)
config.addAction(SemanticsAction.scrollLeft, _performScrollLeft);
config.onScrollLeft = _performScrollLeft;
if (onScrollRight != null)
config.addAction(SemanticsAction.scrollRight, _performScrollRight);
config.onScrollRight = _performScrollRight;
if (onScrollUp != null)
config.addAction(SemanticsAction.scrollUp, _performScrollUp);
config.onScrollUp = _performScrollUp;
if (onScrollDown != null)
config.addAction(SemanticsAction.scrollDown, _performScrollDown);
config.onScrollDown = _performScrollDown;
if (onIncrease != null)
config.addAction(SemanticsAction.increase, _performIncrease);
config.onIncrease = _performIncrease;
if (onDecrease != null)
config.addAction(SemanticsAction.decrease, _performDecrease);
config.onDecrease = _performDecrease;
if (onMoveCursorForwardByCharacter != null)
config.addAction(SemanticsAction.moveCursorForwardByCharacter, _performMoveCursorForwardByCharacter);
config.onMoveCursorForwardByCharacter = _performMoveCursorForwardByCharacter;
if (onMoveCursorBackwardByCharacter != null)
config.addAction(SemanticsAction.moveCursorBackwardByCharacter, _performMoveCursorBackwardByCharacter);
config.onMoveCursorBackwardByCharacter = _performMoveCursorBackwardByCharacter;
}
void _performTap() {
......@@ -3291,14 +3290,14 @@ class RenderSemanticsAnnotations extends RenderProxyBox {
onDecrease();
}
void _performMoveCursorForwardByCharacter() {
void _performMoveCursorForwardByCharacter(bool extendSelection) {
if (onMoveCursorForwardByCharacter != null)
onMoveCursorForwardByCharacter();
onMoveCursorForwardByCharacter(extendSelection);
}
void _performMoveCursorBackwardByCharacter() {
void _performMoveCursorBackwardByCharacter(bool extendSelection) {
if (onMoveCursorBackwardByCharacter != null)
onMoveCursorBackwardByCharacter();
onMoveCursorBackwardByCharacter(extendSelection);
}
}
......
......@@ -265,10 +265,17 @@ TextEditingValue _selectionAwareTextManipulation(
value.text.substring(selectionEndIndex)
);
manipulatedText = beforeSelection + inSelection + afterSelection;
manipulatedSelection = value.selection.copyWith(
baseOffset: beforeSelection.length,
extentOffset: beforeSelection.length + inSelection.length,
);
if (value.selection.baseOffset > value.selection.extentOffset) {
manipulatedSelection = value.selection.copyWith(
baseOffset: beforeSelection.length + inSelection.length,
extentOffset: beforeSelection.length,
);
} else {
manipulatedSelection = value.selection.copyWith(
baseOffset: beforeSelection.length,
extentOffset: beforeSelection.length + inSelection.length,
);
}
}
return new TextEditingValue(
text: manipulatedText,
......
......@@ -4726,8 +4726,8 @@ class Semantics extends SingleChildRenderObjectWidget {
VoidCallback onScrollDown,
VoidCallback onIncrease,
VoidCallback onDecrease,
VoidCallback onMoveCursorForwardByCharacter,
VoidCallback onMoveCursorBackwardByCharacter,
MoveCursorHandler onMoveCursorForwardByCharacter,
MoveCursorHandler onMoveCursorBackwardByCharacter,
}) : this.fromProperties(
key: key,
child: child,
......
......@@ -65,19 +65,19 @@ void main() {
TestRender middle;
final TestRender root = new TestRender(
action: SemanticsAction.tap,
hasTapAction: true,
isSemanticBoundary: true,
child: new TestRender(
action: SemanticsAction.longPress,
hasLongPressAction: true,
isSemanticBoundary: false,
child: middle = new TestRender(
action: SemanticsAction.scrollLeft,
hasScrollLeftAction: true,
isSemanticBoundary: false,
child: new TestRender(
action: SemanticsAction.scrollRight,
hasScrollRightAction: true,
isSemanticBoundary: false,
child: new TestRender(
action: SemanticsAction.scrollUp,
hasScrollUpAction: true,
isSemanticBoundary: true,
)
)
......@@ -91,7 +91,9 @@ void main() {
int expectedActions = SemanticsAction.tap.index | SemanticsAction.longPress.index | SemanticsAction.scrollLeft.index | SemanticsAction.scrollRight.index;
expect(root.debugSemantics.getSemanticsData().actions, expectedActions);
middle.action = SemanticsAction.scrollDown;
middle
..hasScrollLeftAction = false
..hasScrollDownAction = true;
middle.markNeedsSemanticsUpdate();
pumpFrame(phase: EnginePhase.flushSemantics);
......@@ -204,9 +206,9 @@ void main() {
final SemanticsConfiguration config = new SemanticsConfiguration()
..isMergingSemanticsOfDescendants = true
..addAction(SemanticsAction.scrollUp, () { })
..addAction(SemanticsAction.longPress, () { })
..addAction(SemanticsAction.showOnScreen, () { })
..onScrollUp = () { }
..onLongPress = () { }
..onShowOnScreen = () { }
..isChecked = false
..isSelected = true
..isButton = true
......@@ -237,22 +239,128 @@ void main() {
'SemanticsData(Rect.fromLTRB(50.0, 10.0, 70.0, 40.0), [10.0,0.0,0.0,0.0; 0.0,10.0,0.0,0.0; 0.0,0.0,1.0,0.0; 0.0,0.0,0.0,1.0])',
);
});
test('SemanticsConfiguration getter/setter', () {
final SemanticsConfiguration config = new SemanticsConfiguration();
expect(config.isSemanticBoundary, isFalse);
expect(config.isButton, isFalse);
expect(config.isMergingSemanticsOfDescendants, isFalse);
expect(config.isChecked, isFalse);
expect(config.isSelected, isFalse);
expect(config.isBlockingSemanticsOfPreviouslyPaintedNodes, isFalse);
expect(config.isFocused, isFalse);
expect(config.isMergingDescendantsIntoOneNode, isFalse);
expect(config.isTextField, isFalse);
expect(config.onShowOnScreen, isNull);
expect(config.onScrollDown, isNull);
expect(config.onScrollUp, isNull);
expect(config.onScrollLeft, isNull);
expect(config.onScrollRight, isNull);
expect(config.onLongPress, isNull);
expect(config.onDecrease, isNull);
expect(config.onIncrease, isNull);
expect(config.onMoveCursorForwardByCharacter, isNull);
expect(config.onMoveCursorBackwardByCharacter, isNull);
expect(config.onTap, isNull);
config.isSemanticBoundary = true;
config.isButton = true;
config.isMergingSemanticsOfDescendants = true;
config.isChecked = true;
config.isSelected = true;
config.isBlockingSemanticsOfPreviouslyPaintedNodes = true;
config.isFocused = true;
config.isMergingDescendantsIntoOneNode = true;
config.isTextField = true;
final VoidCallback onShowOnScreen = () { };
final VoidCallback onScrollDown = () { };
final VoidCallback onScrollUp = () { };
final VoidCallback onScrollLeft = () { };
final VoidCallback onScrollRight = () { };
final VoidCallback onLongPress = () { };
final VoidCallback onDecrease = () { };
final VoidCallback onIncrease = () { };
final MoveCursorHandler onMoveCursorForwardByCharacter = (bool _) { };
final MoveCursorHandler onMoveCursorBackwardByCharacter = (bool _) { };
final VoidCallback onTap = () { };
config.onShowOnScreen = onShowOnScreen;
config.onScrollDown = onScrollDown;
config.onScrollUp = onScrollUp;
config.onScrollLeft = onScrollLeft;
config.onScrollRight = onScrollRight;
config.onLongPress = onLongPress;
config.onDecrease = onDecrease;
config.onIncrease = onIncrease;
config.onMoveCursorForwardByCharacter = onMoveCursorForwardByCharacter;
config.onMoveCursorBackwardByCharacter = onMoveCursorBackwardByCharacter;
config.onTap = onTap;
expect(config.isSemanticBoundary, isTrue);
expect(config.isButton, isTrue);
expect(config.isMergingSemanticsOfDescendants, isTrue);
expect(config.isChecked, isTrue);
expect(config.isSelected, isTrue);
expect(config.isBlockingSemanticsOfPreviouslyPaintedNodes, isTrue);
expect(config.isFocused, isTrue);
expect(config.isMergingDescendantsIntoOneNode, isTrue);
expect(config.isTextField, isTrue);
expect(config.onShowOnScreen, same(onShowOnScreen));
expect(config.onScrollDown, same(onScrollDown));
expect(config.onScrollUp, same(onScrollUp));
expect(config.onScrollLeft, same(onScrollLeft));
expect(config.onScrollRight, same(onScrollRight));
expect(config.onLongPress, same(onLongPress));
expect(config.onDecrease, same(onDecrease));
expect(config.onIncrease, same(onIncrease));
expect(config.onMoveCursorForwardByCharacter, same(onMoveCursorForwardByCharacter));
expect(config.onMoveCursorBackwardByCharacter, same(onMoveCursorBackwardByCharacter));
expect(config.onTap, same(onTap));
});
}
class TestRender extends RenderProxyBox {
TestRender({ this.action, this.isSemanticBoundary, RenderObject child }) : super(child);
final bool isSemanticBoundary;
TestRender({
this.hasTapAction: false,
this.hasLongPressAction: false,
this.hasScrollLeftAction: false,
this.hasScrollRightAction: false,
this.hasScrollUpAction: false,
this.hasScrollDownAction: false,
this.isSemanticBoundary,
RenderObject child
}) : super(child);
bool hasTapAction;
bool hasLongPressAction;
bool hasScrollLeftAction;
bool hasScrollRightAction;
bool hasScrollUpAction;
bool hasScrollDownAction;
bool isSemanticBoundary;
SemanticsAction action;
@override
void describeSemanticsConfiguration(SemanticsConfiguration config) {
super.describeSemanticsConfiguration(config);
config
..isSemanticBoundary = isSemanticBoundary
..addAction(action, () { });
config.isSemanticBoundary = isSemanticBoundary;
if (hasTapAction)
config.onTap = () { };
if (hasLongPressAction)
config.onLongPress = () { };
if (hasScrollLeftAction)
config.onScrollLeft = () { };
if (hasScrollRightAction)
config.onScrollRight = () { };
if (hasScrollUpAction)
config.onScrollUp = () { };
if (hasScrollDownAction)
config.onScrollDown = () { };
}
}
......@@ -414,6 +414,7 @@ void main() {
testWidgets('can move cursor with a11y means', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
const bool doNotExtendSelection = false;
controller.text = 'test';
controller.selection = new TextSelection.collapsed(offset: controller.text.length);
......@@ -440,7 +441,7 @@ void main() {
expect(controller.selection.baseOffset, 4);
expect(controller.selection.extentOffset, 4);
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter);
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 3);
......@@ -454,11 +455,11 @@ void main() {
],
));
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter);
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection);
await tester.pumpAndSettle();
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter);
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection);
await tester.pumpAndSettle();
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter);
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, doNotExtendSelection);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 0);
......@@ -472,6 +473,89 @@ void main() {
],
));
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorForwardByCharacter, doNotExtendSelection);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 1);
expect(controller.selection.extentOffset, 1);
semantics.dispose();
});
testWidgets('can extend selection with a11y means', (WidgetTester tester) async {
final SemanticsTester semantics = new SemanticsTester(tester);
const bool extendSelection = true;
const bool doNotExtendSelection = false;
controller.text = 'test';
controller.selection = new TextSelection.collapsed(offset: controller.text.length);
await tester.pumpWidget(new MaterialApp(
home: new EditableText(
controller: controller,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
));
expect(semantics, includesNodeWith(
value: 'test',
actions: <SemanticsAction>[
SemanticsAction.moveCursorBackwardByCharacter,
],
));
final RenderEditable render = tester.allRenderObjects.firstWhere((RenderObject o) => o.runtimeType == RenderEditable);
final int semanticsId = render.debugSemantics.id;
expect(controller.selection.baseOffset, 4);
expect(controller.selection.extentOffset, 4);
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, extendSelection);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 4);
expect(controller.selection.extentOffset, 3);
expect(semantics, includesNodeWith(
value: 'test',
actions: <SemanticsAction>[
SemanticsAction.moveCursorBackwardByCharacter,
SemanticsAction.moveCursorForwardByCharacter,
],
));
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, extendSelection);
await tester.pumpAndSettle();
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, extendSelection);
await tester.pumpAndSettle();
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorBackwardByCharacter, extendSelection);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 4);
expect(controller.selection.extentOffset, 0);
await tester.pumpAndSettle();
expect(semantics, includesNodeWith(
value: 'test',
actions: <SemanticsAction>[
SemanticsAction.moveCursorForwardByCharacter,
],
));
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorForwardByCharacter, doNotExtendSelection);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 1);
expect(controller.selection.extentOffset, 1);
tester.binding.pipelineOwner.semanticsOwner.performAction(semanticsId, SemanticsAction.moveCursorForwardByCharacter, extendSelection);
await tester.pumpAndSettle();
expect(controller.selection.baseOffset, 1);
expect(controller.selection.extentOffset, 2);
semantics.dispose();
});
}
......@@ -388,8 +388,8 @@ void main() {
onScrollDown: () => performedActions.add(SemanticsAction.scrollDown),
onIncrease: () => performedActions.add(SemanticsAction.increase),
onDecrease: () => performedActions.add(SemanticsAction.decrease),
onMoveCursorForwardByCharacter: () => performedActions.add(SemanticsAction.moveCursorForwardByCharacter),
onMoveCursorBackwardByCharacter: () => performedActions.add(SemanticsAction.moveCursorBackwardByCharacter),
onMoveCursorForwardByCharacter: (bool _) => performedActions.add(SemanticsAction.moveCursorForwardByCharacter),
onMoveCursorBackwardByCharacter: (bool _) => performedActions.add(SemanticsAction.moveCursorBackwardByCharacter),
)
);
......@@ -412,7 +412,14 @@ void main() {
final SemanticsOwner semanticsOwner = tester.binding.pipelineOwner.semanticsOwner;
int expectedLength = 1;
for (SemanticsAction action in allActions) {
semanticsOwner.performAction(expectedId, action);
switch (action) {
case SemanticsAction.moveCursorBackwardByCharacter:
case SemanticsAction.moveCursorForwardByCharacter:
semanticsOwner.performAction(expectedId, action, true);
break;
default:
semanticsOwner.performAction(expectedId, action);
}
expect(performedActions.length, expectedLength);
expect(performedActions.last, action);
expectedLength += 1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment