Skip to content

Commit f019789

Browse files
authored
[framework] Add Look Up to selection controls for iOS (flutter#131798)
This PR adds framework support for the Look Up feature in iOS. https://github.com/flutter/flutter/assets/36148254/d301df79-4e23-454f-8742-2f8e39c2960c The corresponding merged engine PR can be found [here](flutter/engine#43308). This PR addresses flutter#82907 More details are available in this [design doc.](flutter.dev/go/add-missing-features-to-selection-controls) This is the same PR as flutter#130532, this is an attempt to fix the Google Testing issue
1 parent 00a8323 commit f019789

File tree

175 files changed

+1094
-246
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

175 files changed

+1094
-246
lines changed

packages/flutter/lib/src/cupertino/adaptive_text_selection_toolbar.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ class CupertinoAdaptiveTextSelectionToolbar extends StatelessWidget {
9494
required VoidCallback? onCut,
9595
required VoidCallback? onPaste,
9696
required VoidCallback? onSelectAll,
97+
required VoidCallback? onLookUp,
9798
required VoidCallback? onLiveTextInput,
9899
required this.anchors,
99100
}) : children = null,
@@ -103,6 +104,7 @@ class CupertinoAdaptiveTextSelectionToolbar extends StatelessWidget {
103104
onCut: onCut,
104105
onPaste: onPaste,
105106
onSelectAll: onSelectAll,
107+
onLookUp: onLookUp,
106108
onLiveTextInput: onLiveTextInput
107109
);
108110

packages/flutter/lib/src/cupertino/localizations.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,10 @@ abstract class CupertinoLocalizations {
245245
// The global version uses the translated string from the arb file.
246246
String get selectAllButtonLabel;
247247

248+
/// The term used for looking up a selection.
249+
// The global version uses the translated string from the arb file.
250+
String get lookUpButtonLabel;
251+
248252
/// The default placeholder used in [CupertinoSearchTextField].
249253
// The global version uses the translated string from the arb file.
250254
String get searchTextFieldPlaceholderLabel;
@@ -455,6 +459,9 @@ class DefaultCupertinoLocalizations implements CupertinoLocalizations {
455459
@override
456460
String get selectAllButtonLabel => 'Select All';
457461

462+
@override
463+
String get lookUpButtonLabel => 'Look Up';
464+
458465
@override
459466
String get searchTextFieldPlaceholderLabel => 'Search';
460467

packages/flutter/lib/src/cupertino/text_selection_toolbar_button.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ class CupertinoTextSelectionToolbarButton extends StatefulWidget {
105105
return localizations.pasteButtonLabel;
106106
case ContextMenuButtonType.selectAll:
107107
return localizations.selectAllButtonLabel;
108+
case ContextMenuButtonType.lookUp:
109+
return localizations.lookUpButtonLabel;
108110
case ContextMenuButtonType.liveTextInput:
109111
case ContextMenuButtonType.delete:
110112
case ContextMenuButtonType.custom:
@@ -189,6 +191,7 @@ class _CupertinoTextSelectionToolbarButtonState extends State<CupertinoTextSelec
189191
case ContextMenuButtonType.paste:
190192
case ContextMenuButtonType.selectAll:
191193
case ContextMenuButtonType.delete:
194+
case ContextMenuButtonType.lookUp:
192195
case ContextMenuButtonType.custom:
193196
return textWidget;
194197
case ContextMenuButtonType.liveTextInput:

packages/flutter/lib/src/material/adaptive_text_selection_toolbar.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ class AdaptiveTextSelectionToolbar extends StatelessWidget {
103103
required VoidCallback? onCut,
104104
required VoidCallback? onPaste,
105105
required VoidCallback? onSelectAll,
106+
required VoidCallback? onLookUp,
106107
required VoidCallback? onLiveTextInput,
107108
required this.anchors,
108109
}) : children = null,
@@ -112,6 +113,7 @@ class AdaptiveTextSelectionToolbar extends StatelessWidget {
112113
onCut: onCut,
113114
onPaste: onPaste,
114115
onSelectAll: onSelectAll,
116+
onLookUp: onLookUp,
115117
onLiveTextInput: onLiveTextInput
116118
);
117119

@@ -215,6 +217,8 @@ class AdaptiveTextSelectionToolbar extends StatelessWidget {
215217
return localizations.selectAllButtonLabel;
216218
case ContextMenuButtonType.delete:
217219
return localizations.deleteButtonTooltip.toUpperCase();
220+
case ContextMenuButtonType.lookUp:
221+
return localizations.lookUpButtonLabel;
218222
case ContextMenuButtonType.liveTextInput:
219223
return localizations.scanTextButtonLabel;
220224
case ContextMenuButtonType.custom:

packages/flutter/lib/src/material/material_localizations.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,9 @@ abstract class MaterialLocalizations {
115115
/// Label for "select all" edit buttons and menu items.
116116
String get selectAllButtonLabel;
117117

118+
/// Label for "look up" edit buttons and menu items.
119+
String get lookUpButtonLabel;
120+
118121
/// Label for the [AboutDialog] button that shows the [LicensePage].
119122
String get viewLicensesButtonLabel;
120123

@@ -1178,6 +1181,9 @@ class DefaultMaterialLocalizations implements MaterialLocalizations {
11781181
@override
11791182
String get selectAllButtonLabel => 'Select all';
11801183

1184+
@override
1185+
String get lookUpButtonLabel => 'Look Up';
1186+
11811187
@override
11821188
String get viewLicensesButtonLabel => 'View licenses';
11831189

packages/flutter/lib/src/services/text_input.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,9 @@ mixin TextSelectionDelegate {
10501050
/// Whether select all is enabled, must not be null.
10511051
bool get selectAllEnabled => true;
10521052

1053+
/// Whether look up is enabled, must not be null.
1054+
bool get lookUpEnabled => true;
1055+
10531056
/// Whether Live Text input is enabled.
10541057
///
10551058
/// See also:

packages/flutter/lib/src/widgets/context_menu_button_item.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ enum ContextMenuButtonType {
2626
/// A button that deletes the current text selection.
2727
delete,
2828

29+
/// A button that looks up the current text selection.
30+
lookUp,
31+
2932
/// A button for starting Live Text input.
3033
///
3134
/// See also:

packages/flutter/lib/src/widgets/editable_text.dart

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1852,6 +1852,7 @@ class EditableText extends StatefulWidget {
18521852
required final VoidCallback? onCut,
18531853
required final VoidCallback? onPaste,
18541854
required final VoidCallback? onSelectAll,
1855+
required final VoidCallback? onLookUp,
18551856
required final VoidCallback? onLiveTextInput,
18561857
}) {
18571858
final List<ContextMenuButtonItem> resultButtonItem = <ContextMenuButtonItem>[];
@@ -1882,6 +1883,11 @@ class EditableText extends StatefulWidget {
18821883
onPressed: onSelectAll,
18831884
type: ContextMenuButtonType.selectAll,
18841885
),
1886+
if (onLookUp != null)
1887+
ContextMenuButtonItem(
1888+
onPressed: onLookUp,
1889+
type: ContextMenuButtonType.lookUp,
1890+
),
18851891
]);
18861892
}
18871893

@@ -2232,6 +2238,15 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
22322238
}
22332239
}
22342240

2241+
@override
2242+
bool get lookUpEnabled {
2243+
if (defaultTargetPlatform != TargetPlatform.iOS) {
2244+
return false;
2245+
}
2246+
return !widget.obscureText
2247+
&& !textEditingValue.selection.isCollapsed;
2248+
}
2249+
22352250
@override
22362251
bool get liveTextInputEnabled {
22372252
return _liveTextInputStatus?.value == LiveTextInputStatus.enabled &&
@@ -2397,6 +2412,22 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
23972412
}
23982413
}
23992414

2415+
/// Look up the current selection, as in the "Look Up" edit menu button on iOS.
2416+
/// Currently this is only implemented for iOS.
2417+
/// Throws an error if the selection is empty or collapsed.
2418+
Future<void> lookUpSelection(SelectionChangedCause cause) async {
2419+
assert(!widget.obscureText);
2420+
2421+
final String text = textEditingValue.selection.textInside(textEditingValue.text);
2422+
if (widget.obscureText || text.isEmpty) {
2423+
return;
2424+
}
2425+
await SystemChannels.platform.invokeMethod(
2426+
'LookUp.invoke',
2427+
text,
2428+
);
2429+
}
2430+
24002431
void _startLiveTextInput(SelectionChangedCause cause) {
24012432
if (!liveTextInputEnabled) {
24022433
return;
@@ -2623,6 +2654,9 @@ class EditableTextState extends State<EditableText> with AutomaticKeepAliveClien
26232654
onSelectAll: selectAllEnabled
26242655
? () => selectAll(SelectionChangedCause.toolbar)
26252656
: null,
2657+
onLookUp: lookUpEnabled
2658+
? () => lookUpSelection(SelectionChangedCause.toolbar)
2659+
: null,
26262660
onLiveTextInput: liveTextInputEnabled
26272661
? () => _startLiveTextInput(SelectionChangedCause.toolbar)
26282662
: null,

packages/flutter/test/cupertino/adaptive_text_selection_toolbar_test.dart

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ void main() {
168168
onPaste: () {},
169169
onSelectAll: () {},
170170
onLiveTextInput: () {},
171+
onLookUp: () {},
171172
),
172173
),
173174
));
@@ -180,16 +181,16 @@ void main() {
180181

181182
switch (defaultTargetPlatform) {
182183
case TargetPlatform.android:
183-
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(5));
184+
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6));
184185
case TargetPlatform.fuchsia:
185-
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(5));
186+
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6));
186187
case TargetPlatform.iOS:
187-
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(5));
188+
expect(find.byType(CupertinoTextSelectionToolbarButton), findsNWidgets(6));
188189
expect(findLiveTextButton(), findsOneWidget);
189190
case TargetPlatform.macOS:
190191
case TargetPlatform.linux:
191192
case TargetPlatform.windows:
192-
expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNWidgets(5));
193+
expect(find.byType(CupertinoDesktopTextSelectionToolbarButton), findsNWidgets(6));
193194
}
194195
},
195196
skip: kIsWeb, // [intended] on web the browser handles the context menu.

0 commit comments

Comments
 (0)