Fix: A selectable's selection under the active selection should not be cleared on right-click (#151851)

Fixes #150268

The issue was related to the check for selection geometry here: 22a5c6cb0a/packages/flutter/lib/src/widgets/selectable_region.dart (L2469-L2476) . Since `otherList == myList` is a reference check this would fail even if the selection rects inside the list contained in SelectionGeometry where the same causing the selectables inside the selection but outside the selectable containing the tapped position to have their selection cleared, use `listEquals` instead.
This commit is contained in:
Renzo Olivares 2024-07-24 15:46:22 -07:00 committed by GitHub
parent e7f39c1516
commit 00eeabf584
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 77 additions and 1 deletions

View File

@ -713,7 +713,7 @@ class SelectionGeometry {
return other is SelectionGeometry
&& other.startSelectionPoint == startSelectionPoint
&& other.endSelectionPoint == endSelectionPoint
&& other.selectionRects == selectionRects
&& listEquals(other.selectionRects, selectionRects)
&& other.status == status
&& other.hasContent == hasContent;
}

View File

@ -2358,6 +2358,82 @@ void main() {
skip: kIsWeb, // [intended] Web uses its native context menu.
);
testWidgets(
'right-click mouse on an active selection does not clear the selection in other selectables on Apple platforms',
(WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/issues/150268.
Set<ContextMenuButtonType> buttonTypes = <ContextMenuButtonType>{};
final UniqueKey toolbarKey = UniqueKey();
final FocusNode focusNode = FocusNode();
addTearDown(focusNode.dispose);
await tester.pumpWidget(
MaterialApp(
home: SelectableRegion(
focusNode: focusNode,
selectionControls: materialTextSelectionHandleControls,
contextMenuBuilder: (
BuildContext context,
SelectableRegionState selectableRegionState,
) {
buttonTypes = selectableRegionState.contextMenuButtonItems
.map((ContextMenuButtonItem buttonItem) => buttonItem.type)
.toSet();
return SizedBox.shrink(key: toolbarKey);
},
child: const Column(
children: <Widget>[
Text('How are you?'),
Text('Good, and you?'),
Text('Fine, thank you.'),
],
),
),
),
);
expect(buttonTypes.isEmpty, true);
expect(find.byKey(toolbarKey), findsNothing);
final RenderParagraph paragraph = tester.renderObject<RenderParagraph>(find.descendant(of: find.text('How are you?'), matching: find.byType(RichText)));
final RenderParagraph paragraph2 = tester.renderObject<RenderParagraph>(find.descendant(of: find.text('Good, and you?'), matching: find.byType(RichText)));
final RenderParagraph paragraph3 = tester.renderObject<RenderParagraph>(find.descendant(of: find.text('Fine, thank you.'), matching: find.byType(RichText)));
final TestGesture gesture = await tester.startGesture(textOffsetToPosition(paragraph, 2), kind: PointerDeviceKind.mouse);
final TestGesture secondaryMouseButtonGesture = await tester.createGesture(kind: PointerDeviceKind.mouse, buttons: kSecondaryMouseButton);
addTearDown(secondaryMouseButtonGesture.removePointer);
addTearDown(gesture.removePointer);
await tester.pump();
await gesture.moveTo(textOffsetToPosition(paragraph3, 5));
await tester.pump();
await gesture.up();
await tester.pumpAndSettle();
expect(paragraph.selections, isNotEmpty);
expect(paragraph2.selections, isNotEmpty);
expect(paragraph3.selections, isNotEmpty);
expect(paragraph.selections[0], const TextSelection(baseOffset: 2, extentOffset: 12));
expect(paragraph2.selections[0], const TextSelection(baseOffset: 0, extentOffset: 14));
expect(paragraph3.selections[0], const TextSelection(baseOffset: 0, extentOffset: 5));
// Right-clicking on the active selection should retain the selection.
await secondaryMouseButtonGesture.down(textOffsetToPosition(paragraph2, 7));
await tester.pump();
await secondaryMouseButtonGesture.up();
await tester.pumpAndSettle();
expect(paragraph.selections, isNotEmpty);
expect(paragraph2.selections, isNotEmpty);
expect(paragraph3.selections, isNotEmpty);
expect(paragraph.selections[0], const TextSelection(baseOffset: 2, extentOffset: 12));
expect(paragraph2.selections[0], const TextSelection(baseOffset: 0, extentOffset: 14));
expect(paragraph3.selections[0], const TextSelection(baseOffset: 0, extentOffset: 5));
expect(buttonTypes, contains(ContextMenuButtonType.copy));
expect(buttonTypes, contains(ContextMenuButtonType.selectAll));
expect(find.byKey(toolbarKey), findsOneWidget);
},
variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }),
skip: kIsWeb, // [intended] Web uses its native context menu.
);
testWidgets(
'right-click mouse at the same position as previous right-click toggles the context menu on macOS',
(WidgetTester tester) async {