Secure paste milestone 2 (#159013)
Implements the framework side of secure paste milestone 2, where the iOS system context menu items can be customized. Depends on PR https://github.com/flutter/flutter/pull/161103. Currently I've merged that PR into this one for testing, but I think that PR should merge separately first. ### Widget API (most users) ```dart TextField( contextMenuBuilder: (BuildContext context, EditableTextState editableTextState) { return SystemContextMenu.editableText( editableTextState: editableTextState, // items is optional items: <IOSSystemContextMenuItem>[ const IOSSystemContextMenuItemCut(), constIOS SystemContextMenuItemCopy(), const IOSSystemContextMenuItemPaste(), const IOSSystemContextMenuItemSelectAll(), const IOSSystemContextMenuItemSearchWeb( title: 'Search!', // title is optional for this button, defaults to localized string ), // Milestone 3: IOSSystemContextMenuItemCustom( // title and onPressed are required title: 'custom button', onPressed: () { print('pressed the custom button.'); } ), ], ); }, ), ``` ### Raw Controller API ```dart _systemContextMenuController.show( widget.anchor, <IOSSystemContextMenuItemData>[ // Notice these are different classes than those used for the widget. That's // mainly because I can't provide localized defaults here, so the titles are // required in the classes that have titles. const IOSSystemContextMenuItemDataCut(), const IOSSystemContextMenuItemDataCopy(), const IOSSystemContextMenuItemDataPaste(), const IOSSystemContextMenuItemDataSelectAll(), const IOSSystemContextMenuItemDataSearchWeb( title: 'Search!', // title is required. ), // Milestone 3: IOSSystemContextMenuItemDataCustom( // title and onPressed are required as before. title: 'custom button', onPressed: () { print('pressed the custom button.'); } ), ], ); ``` <details> <summary>Json format</summary> ```dart return _channel.invokeMethod<Map<String, dynamic>>( 'ContextMenu.showSystemContextMenu', <String, dynamic>{ 'targetRect': <String, double>{ 'x': targetRect.left, 'y': targetRect.top, 'width': targetRect.width, 'height': targetRect.height, }, 'items': <dynamic>[ <String, dynamic>{ 'type': 'default', 'action': 'paste', }, <String, dynamic>{ 'type': 'default', 'action': 'copy', }, <String, dynamic>{ 'type': 'default', 'title': 'Crazy Title', 'action': 'share', }, ], }, ); ``` </summary> </details> ### Localization changes This change requires the SystemContextMenu widget in the widgets library to be able to look up the default localized label for several context menu buttons like "Copy", etc. Those strings previously resided in MaterialLocalizations and CupertinoLocalizations, but not in WidgetsLocalizations, so I have copied the necessary strings into WidgetsLocalizations. --------- Co-authored-by: Huan Lin <hellohuanlin@gmail.com>
This commit is contained in:
parent
043b71954c
commit
df65e46f94
@ -413,9 +413,15 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
|
||||
// the user taps outside the menu. Not called when Flutter shows a new
|
||||
// system context menu while an old one is still visible.
|
||||
case 'ContextMenu.onDismissSystemContextMenu':
|
||||
for (final SystemContextMenuClient client in _systemContextMenuClients) {
|
||||
client.handleSystemHide();
|
||||
if (_systemContextMenuClient == null) {
|
||||
assert(
|
||||
false,
|
||||
'Platform sent onDismissSystemContextMenu when no SystemContextMenuClient was registered.',
|
||||
);
|
||||
return;
|
||||
}
|
||||
_systemContextMenuClient!.handleSystemHide();
|
||||
_systemContextMenuClient = null;
|
||||
case 'SystemChrome.systemUIChange':
|
||||
final List<dynamic> args = methodCall.arguments as List<dynamic>;
|
||||
if (_systemUiChangeCallback != null) {
|
||||
@ -571,17 +577,14 @@ mixin ServicesBinding on BindingBase, SchedulerBinding {
|
||||
await SystemChannels.platform.invokeMethod('System.initializationComplete');
|
||||
}
|
||||
|
||||
final Set<SystemContextMenuClient> _systemContextMenuClients = <SystemContextMenuClient>{};
|
||||
SystemContextMenuClient? _systemContextMenuClient;
|
||||
|
||||
/// Registers a [SystemContextMenuClient] that will receive system context
|
||||
/// menu calls from the engine.
|
||||
static void registerSystemContextMenuClient(SystemContextMenuClient client) {
|
||||
instance._systemContextMenuClients.add(client);
|
||||
}
|
||||
|
||||
/// Unregisters a [SystemContextMenuClient] so that it is no longer called.
|
||||
static void unregisterSystemContextMenuClient(SystemContextMenuClient client) {
|
||||
instance._systemContextMenuClients.remove(client);
|
||||
///
|
||||
/// To unregister, set to null.
|
||||
static set systemContextMenuClient(SystemContextMenuClient? client) {
|
||||
instance._systemContextMenuClient = client;
|
||||
}
|
||||
}
|
||||
|
||||
@ -673,6 +676,9 @@ class _DefaultBinaryMessenger extends BinaryMessenger {
|
||||
/// See also:
|
||||
/// * [SystemContextMenuController], which uses this to provide a fully
|
||||
/// featured way to control the system context menu.
|
||||
/// * [ServicesBinding.systemContextMenuClient], which can be set to a
|
||||
/// [SystemContextMenuClient] to register it to receive events, or null to
|
||||
/// unregister.
|
||||
/// * [MediaQuery.maybeSupportsShowingSystemContextMenu], which indicates
|
||||
/// whether the system context menu is supported.
|
||||
/// * [SystemContextMenu], which provides a widget interface for displaying the
|
||||
@ -680,7 +686,7 @@ class _DefaultBinaryMessenger extends BinaryMessenger {
|
||||
mixin SystemContextMenuClient {
|
||||
/// Handles the system hiding a context menu.
|
||||
///
|
||||
/// This is called for all instances of [SystemContextMenuController], so it's
|
||||
/// not guaranteed that this instance was the one that was hidden.
|
||||
/// Called only on the single active instance registered with
|
||||
/// [ServicesBinding.systemContextMenuClient].
|
||||
void handleSystemHide();
|
||||
}
|
||||
|
@ -2587,7 +2587,7 @@ class SystemContextMenuController with SystemContextMenuClient {
|
||||
///
|
||||
/// Not shown until [show] is called.
|
||||
SystemContextMenuController({this.onSystemHide}) {
|
||||
ServicesBinding.registerSystemContextMenuClient(this);
|
||||
ServicesBinding.systemContextMenuClient = this;
|
||||
}
|
||||
|
||||
/// Called when the system has hidden the context menu.
|
||||
@ -2609,11 +2609,19 @@ class SystemContextMenuController with SystemContextMenuClient {
|
||||
/// Null if [show] has not been called.
|
||||
Rect? _lastTargetRect;
|
||||
|
||||
/// The [IOSSystemContextMenuItem]s that were last given to [show].
|
||||
///
|
||||
/// Null if [show] has not been called.
|
||||
List<IOSSystemContextMenuItemData>? _lastItems;
|
||||
|
||||
/// True when the instance most recently [show]n has been hidden by the
|
||||
/// system.
|
||||
bool _hiddenBySystem = false;
|
||||
|
||||
bool get _isVisible => this == _lastShown && !_hiddenBySystem;
|
||||
/// Indicates whether the system context menu managed by this controller is
|
||||
/// currently being displayed to the user.
|
||||
@visibleForTesting
|
||||
bool get isVisible => this == _lastShown && !_hiddenBySystem;
|
||||
|
||||
/// After calling [dispose], this instance can no longer be used.
|
||||
bool _isDisposed = false;
|
||||
@ -2623,9 +2631,8 @@ class SystemContextMenuController with SystemContextMenuClient {
|
||||
@override
|
||||
void handleSystemHide() {
|
||||
assert(!_isDisposed);
|
||||
// If this instance wasn't being shown, then it wasn't the instance that was
|
||||
// hidden.
|
||||
if (!_isVisible) {
|
||||
assert(isVisible);
|
||||
if (_isDisposed || !isVisible) {
|
||||
return;
|
||||
}
|
||||
if (_lastShown == this) {
|
||||
@ -2639,25 +2646,28 @@ class SystemContextMenuController with SystemContextMenuClient {
|
||||
|
||||
/// Shows the system context menu anchored on the given [Rect].
|
||||
///
|
||||
/// Currently only supported on iOS 16.0 and later. Check
|
||||
/// [MediaQuery.maybeSupportsShowingSystemContextMenu] before calling this.
|
||||
///
|
||||
/// The [Rect] represents what the context menu is pointing to. For example,
|
||||
/// for some text selection, this would be the selection [Rect].
|
||||
///
|
||||
/// Currently this system context menu is bound to text input. Using this
|
||||
/// without an active [TextInputConnection] will be a noop.
|
||||
///
|
||||
/// There can only be one system context menu visible at a time. Calling this
|
||||
/// while another system context menu is already visible will remove the old
|
||||
/// menu before showing the new menu.
|
||||
///
|
||||
/// Currently this system context menu is bound to text input. The buttons
|
||||
/// that are shown and the actions they perform are dependent on the
|
||||
/// currently active [TextInputConnection]. Using this without an active
|
||||
/// [TextInputConnection] will be a noop.
|
||||
///
|
||||
/// This is only supported on iOS 16.0 and later.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [hide], which hides the menu shown by this method.
|
||||
/// * [MediaQuery.supportsShowingSystemContextMenu], which indicates whether
|
||||
/// this method is supported on the current platform.
|
||||
@Deprecated(
|
||||
'Use showWithItems instead. '
|
||||
'This feature was deprecated after v3.29.0-0.3.pre.',
|
||||
)
|
||||
Future<void> show(Rect targetRect) {
|
||||
assert(!_isDisposed);
|
||||
assert(
|
||||
@ -2666,29 +2676,95 @@ class SystemContextMenuController with SystemContextMenuClient {
|
||||
);
|
||||
|
||||
// Don't show the same thing that's already being shown.
|
||||
if (_lastShown != null && _lastShown!._isVisible && _lastShown!._lastTargetRect == targetRect) {
|
||||
if (_lastShown != null && _lastShown!.isVisible && _lastShown!._lastTargetRect == targetRect) {
|
||||
return Future<void>.value();
|
||||
}
|
||||
|
||||
assert(
|
||||
_lastShown == null || _lastShown == this || !_lastShown!._isVisible,
|
||||
_lastShown == null || _lastShown == this || !_lastShown!.isVisible,
|
||||
'Attempted to show while another instance was still visible.',
|
||||
);
|
||||
|
||||
ServicesBinding.systemContextMenuClient = this;
|
||||
|
||||
_lastTargetRect = targetRect;
|
||||
_lastShown = this;
|
||||
_hiddenBySystem = false;
|
||||
return _channel.invokeMethod<Map<String, dynamic>>(
|
||||
'ContextMenu.showSystemContextMenu',
|
||||
<String, dynamic>{
|
||||
'targetRect': <String, double>{
|
||||
'x': targetRect.left,
|
||||
'y': targetRect.top,
|
||||
'width': targetRect.width,
|
||||
'height': targetRect.height,
|
||||
},
|
||||
return _channel.invokeMethod('ContextMenu.showSystemContextMenu', <String, dynamic>{
|
||||
'targetRect': <String, double>{
|
||||
'x': targetRect.left,
|
||||
'y': targetRect.top,
|
||||
'width': targetRect.width,
|
||||
'height': targetRect.height,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/// Shows the system context menu anchored on the given [Rect] with the given
|
||||
/// buttons.
|
||||
///
|
||||
/// Currently only supported on iOS 16.0 and later. Check
|
||||
/// [MediaQuery.maybeSupportsShowingSystemContextMenu] before calling this.
|
||||
///
|
||||
/// The [Rect] represents what the context menu is pointing to. For example,
|
||||
/// for some text selection, this would be the selection [Rect].
|
||||
///
|
||||
/// `items` specifies the buttons that appear in the menu. The buttons that
|
||||
/// appear in the menu will be exactly as given and will not automatically
|
||||
/// udpate based on the state of the input field. See
|
||||
/// [SystemContextMenu.getDefaultItems] for the default items for a given
|
||||
/// [EditableTextState].
|
||||
///
|
||||
/// Currently this system context menu is bound to text input. Using this
|
||||
/// without an active [TextInputConnection] will be a noop.
|
||||
///
|
||||
/// There can only be one system context menu visible at a time. Calling this
|
||||
/// while another system context menu is already visible will remove the old
|
||||
/// menu before showing the new menu.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [hide], which hides the menu shown by this method.
|
||||
/// * [MediaQuery.supportsShowingSystemContextMenu], which indicates whether
|
||||
/// this method is supported on the current platform.
|
||||
Future<void> showWithItems(Rect targetRect, List<IOSSystemContextMenuItemData> items) {
|
||||
assert(!_isDisposed);
|
||||
assert(items.isNotEmpty);
|
||||
assert(
|
||||
TextInput._instance._currentConnection != null,
|
||||
'Currently, the system context menu can only be shown for an active text input connection',
|
||||
);
|
||||
|
||||
// Don't show the same thing that's already being shown.
|
||||
if (_lastShown != null &&
|
||||
_lastShown!.isVisible &&
|
||||
_lastShown!._lastTargetRect == targetRect &&
|
||||
listEquals(_lastShown!._lastItems, items)) {
|
||||
return Future<void>.value();
|
||||
}
|
||||
|
||||
assert(
|
||||
_lastShown == null || _lastShown == this || !_lastShown!.isVisible,
|
||||
'Attempted to show while another instance was still visible.',
|
||||
);
|
||||
|
||||
ServicesBinding.systemContextMenuClient = this;
|
||||
|
||||
final List<Map<String, dynamic>> itemsJson =
|
||||
items.map<Map<String, dynamic>>((IOSSystemContextMenuItemData item) => item._json).toList();
|
||||
_lastTargetRect = targetRect;
|
||||
_lastItems = items;
|
||||
_lastShown = this;
|
||||
_hiddenBySystem = false;
|
||||
return _channel.invokeMethod('ContextMenu.showSystemContextMenu', <String, dynamic>{
|
||||
'targetRect': <String, double>{
|
||||
'x': targetRect.left,
|
||||
'y': targetRect.top,
|
||||
'width': targetRect.width,
|
||||
'height': targetRect.height,
|
||||
},
|
||||
'items': itemsJson,
|
||||
});
|
||||
}
|
||||
|
||||
/// Hides this system context menu.
|
||||
@ -2705,12 +2781,13 @@ class SystemContextMenuController with SystemContextMenuClient {
|
||||
/// the system context menu is supported on the current platform.
|
||||
Future<void> hide() async {
|
||||
assert(!_isDisposed);
|
||||
// This check prevents a given instance from accidentally hiding some other
|
||||
// This check prevents the instance from accidentally hiding some other
|
||||
// instance, since only one can be visible at a time.
|
||||
if (this != _lastShown) {
|
||||
return;
|
||||
}
|
||||
_lastShown = null;
|
||||
ServicesBinding.systemContextMenuClient = null;
|
||||
// This may be called unnecessarily in the case where the user has already
|
||||
// hidden the menu (for example by tapping the screen).
|
||||
return _channel.invokeMethod<void>('ContextMenu.hideSystemContextMenu');
|
||||
@ -2718,14 +2795,229 @@ class SystemContextMenuController with SystemContextMenuClient {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SystemContextMenuController(onSystemHide=$onSystemHide, _hiddenBySystem=$_hiddenBySystem, _isVisible=$_isVisible, _isDisposed=$_isDisposed)';
|
||||
return 'SystemContextMenuController(onSystemHide=$onSystemHide, _hiddenBySystem=$_hiddenBySystem, _isVisible=$isVisible, _isDisposed=$_isDisposed)';
|
||||
}
|
||||
|
||||
/// Used to release resources when this instance will never be used again.
|
||||
void dispose() {
|
||||
assert(!_isDisposed);
|
||||
hide();
|
||||
ServicesBinding.unregisterSystemContextMenuClient(this);
|
||||
_isDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a context menu button that will be rendered in the system context
|
||||
/// menu.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenuController], which is used to show the system context
|
||||
/// menu.
|
||||
/// * [IOSSystemContextMenuItem], which performs a similar role but at the widget
|
||||
/// level, where the titles can be replaced with default localized values.
|
||||
/// * [ContextMenuButtonItem], which performs a similar role for Flutter-drawn
|
||||
/// context menus.
|
||||
@immutable
|
||||
sealed class IOSSystemContextMenuItemData {
|
||||
const IOSSystemContextMenuItemData();
|
||||
|
||||
/// The text to display to the user.
|
||||
///
|
||||
/// Not exposed for some built-in menu items whose title is always set by the
|
||||
/// platform.
|
||||
String? get title => null;
|
||||
|
||||
/// The type string used when serialized to json, recognized by the engine.
|
||||
String get _jsonType;
|
||||
|
||||
/// Returns json for use in method channel calls, specifically
|
||||
/// `ContextMenu.showSystemContextMenu`.
|
||||
Map<String, dynamic> get _json {
|
||||
return <String, dynamic>{
|
||||
'callbackId': hashCode,
|
||||
if (title != null) 'title': title,
|
||||
'type': _jsonType,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => title.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other.runtimeType != runtimeType) {
|
||||
return false;
|
||||
}
|
||||
return other is IOSSystemContextMenuItemData && other.title == title;
|
||||
}
|
||||
}
|
||||
|
||||
/// A [IOSSystemContextMenuItemData] for the system's built-in copy button.
|
||||
///
|
||||
/// The title and action are both handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenuController], which is used to show the system context
|
||||
/// menu.
|
||||
/// * [IOSSystemContextMenuItemCopy], which performs a similar role but at the
|
||||
/// widget level.
|
||||
final class IOSSystemContextMenuItemDataCopy extends IOSSystemContextMenuItemData {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemDataCopy].
|
||||
const IOSSystemContextMenuItemDataCopy();
|
||||
|
||||
@override
|
||||
String get _jsonType => 'copy';
|
||||
}
|
||||
|
||||
/// A [IOSSystemContextMenuItemData] for the system's built-in cut button.
|
||||
///
|
||||
/// The title and action are both handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenuController], which is used to show the system context
|
||||
/// menu.
|
||||
/// * [IOSSystemContextMenuItemCut], which performs a similar role but at the
|
||||
/// widget level.
|
||||
final class IOSSystemContextMenuItemDataCut extends IOSSystemContextMenuItemData {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemDataCut].
|
||||
const IOSSystemContextMenuItemDataCut();
|
||||
|
||||
@override
|
||||
String get _jsonType => 'cut';
|
||||
}
|
||||
|
||||
/// A [IOSSystemContextMenuItemData] for the system's built-in paste button.
|
||||
///
|
||||
/// The title and action are both handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenuController], which is used to show the system context
|
||||
/// menu.
|
||||
/// * [IOSSystemContextMenuItemPaste], which performs a similar role but at the
|
||||
/// widget level.
|
||||
final class IOSSystemContextMenuItemDataPaste extends IOSSystemContextMenuItemData {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemDataPaste].
|
||||
const IOSSystemContextMenuItemDataPaste();
|
||||
|
||||
@override
|
||||
String get _jsonType => 'paste';
|
||||
}
|
||||
|
||||
/// A [IOSSystemContextMenuItemData] for the system's built-in select all
|
||||
/// button.
|
||||
///
|
||||
/// The title and action are both handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenuController], which is used to show the system context
|
||||
/// menu.
|
||||
/// * [IOSSystemContextMenuItemSelectAll], which performs a similar role but at
|
||||
/// the widget level.
|
||||
final class IOSSystemContextMenuItemDataSelectAll extends IOSSystemContextMenuItemData {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemDataSelectAll].
|
||||
const IOSSystemContextMenuItemDataSelectAll();
|
||||
|
||||
@override
|
||||
String get _jsonType => 'selectAll';
|
||||
}
|
||||
|
||||
/// A [IOSSystemContextMenuItemData] for the system's built-in look up
|
||||
/// button.
|
||||
///
|
||||
/// Must specify a [title], typically [WidgetsLocalizations.lookUpButtonLabel].
|
||||
///
|
||||
/// The action is handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenuController], which is used to show the system context
|
||||
/// menu.
|
||||
/// * [IOSSystemContextMenuItemLookUp], which performs a similar role but at the
|
||||
/// widget level, where the title can be replaced with a default localized
|
||||
/// value.
|
||||
final class IOSSystemContextMenuItemDataLookUp extends IOSSystemContextMenuItemData {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemDataLookUp].
|
||||
const IOSSystemContextMenuItemDataLookUp({required this.title});
|
||||
|
||||
@override
|
||||
final String title;
|
||||
|
||||
@override
|
||||
String get _jsonType => 'lookUp';
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'IOSSystemContextMenuItemDataLookUp(title: $title)';
|
||||
}
|
||||
}
|
||||
|
||||
/// A [IOSSystemContextMenuItemData] for the system's built-in search web
|
||||
/// button.
|
||||
///
|
||||
/// Must specify a [title], typically
|
||||
/// [WidgetsLocalizations.searchWebButtonLabel].
|
||||
///
|
||||
/// The action is handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenuController], which is used to show the system context
|
||||
/// menu.
|
||||
/// * [IOSSystemContextMenuItemSearchWeb], which performs a similar role but at
|
||||
/// the widget level, where the title can be replaced with a default localized
|
||||
/// value.
|
||||
final class IOSSystemContextMenuItemDataSearchWeb extends IOSSystemContextMenuItemData {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemDataSearchWeb].
|
||||
const IOSSystemContextMenuItemDataSearchWeb({required this.title});
|
||||
|
||||
@override
|
||||
final String title;
|
||||
|
||||
@override
|
||||
String get _jsonType => 'searchWeb';
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'IOSSystemContextMenuItemDataSearchWeb(title: $title)';
|
||||
}
|
||||
}
|
||||
|
||||
/// A [IOSSystemContextMenuItemData] for the system's built-in share button.
|
||||
///
|
||||
/// Must specify a [title], typically
|
||||
/// [WidgetsLocalizations.shareButtonLabel].
|
||||
///
|
||||
/// The action is handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenuController], which is used to show the system context
|
||||
/// menu.
|
||||
/// * [IOSSystemContextMenuItemShare], which performs a similar role but at
|
||||
/// the widget level, where the title can be replaced with a default
|
||||
/// localized value.
|
||||
final class IOSSystemContextMenuItemDataShare extends IOSSystemContextMenuItemData {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemDataShare].
|
||||
const IOSSystemContextMenuItemDataShare({required this.title});
|
||||
|
||||
@override
|
||||
final String title;
|
||||
|
||||
@override
|
||||
String get _jsonType => 'share';
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'IOSSystemContextMenuItemDataShare(title: $title)';
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(justinmc): Support the "custom" type.
|
||||
// https://github.com/flutter/flutter/issues/103163
|
||||
|
@ -59,6 +59,8 @@ enum ContextMenuButtonType {
|
||||
/// * [AdaptiveTextSelectionToolbar], which can take a list of
|
||||
/// ContextMenuButtonItems and create a platform-specific context menu with
|
||||
/// the indicated buttons.
|
||||
/// * [IOSSystemContextMenuItem], which serves a similar role but for
|
||||
/// system-drawn context menu items on iOS.
|
||||
@immutable
|
||||
class ContextMenuButtonItem {
|
||||
/// Creates a const instance of [ContextMenuButtonItem].
|
||||
|
@ -3025,6 +3025,8 @@ class EditableTextState extends State<EditableText>
|
||||
///
|
||||
/// * [EditableText.getEditableButtonItems], which performs a similar role,
|
||||
/// but for any editable field, not just specifically EditableText.
|
||||
/// * [SystemContextMenu.getDefaultItems], which performs a similar role, but
|
||||
/// for the system-rendered context menu.
|
||||
/// * [SelectableRegionState.contextMenuButtonItems], which performs a similar
|
||||
/// role but for content that is selectable but not editable.
|
||||
/// * [contextMenuAnchors], which provides the anchor points for the default
|
||||
|
@ -192,6 +192,27 @@ abstract class WidgetsLocalizations {
|
||||
/// list one space right in the list.
|
||||
String get reorderItemRight;
|
||||
|
||||
/// Label for "copy" edit buttons and menu items.
|
||||
String get copyButtonLabel;
|
||||
|
||||
/// Label for "cut" edit buttons and menu items.
|
||||
String get cutButtonLabel;
|
||||
|
||||
/// Label for "paste" edit buttons and menu items.
|
||||
String get pasteButtonLabel;
|
||||
|
||||
/// Label for "select all" edit buttons and menu items.
|
||||
String get selectAllButtonLabel;
|
||||
|
||||
/// Label for "look up" edit buttons and menu items.
|
||||
String get lookUpButtonLabel;
|
||||
|
||||
/// Label for "search web" edit buttons and menu items.
|
||||
String get searchWebButtonLabel;
|
||||
|
||||
/// Label for "share" edit buttons and menu items.
|
||||
String get shareButtonLabel;
|
||||
|
||||
/// The `WidgetsLocalizations` from the closest [Localizations] instance
|
||||
/// that encloses the given context.
|
||||
///
|
||||
@ -261,6 +282,27 @@ class DefaultWidgetsLocalizations implements WidgetsLocalizations {
|
||||
@override
|
||||
String get reorderItemToStart => 'Move to the start';
|
||||
|
||||
@override
|
||||
String get copyButtonLabel => 'Copy';
|
||||
|
||||
@override
|
||||
String get cutButtonLabel => 'Cut';
|
||||
|
||||
@override
|
||||
String get pasteButtonLabel => 'Paste';
|
||||
|
||||
@override
|
||||
String get selectAllButtonLabel => 'Select all';
|
||||
|
||||
@override
|
||||
String get lookUpButtonLabel => 'Look Up';
|
||||
|
||||
@override
|
||||
String get searchWebButtonLabel => 'Search Web';
|
||||
|
||||
@override
|
||||
String get shareButtonLabel => 'Share';
|
||||
|
||||
@override
|
||||
TextDirection get textDirection => TextDirection.ltr;
|
||||
|
||||
|
@ -11,6 +11,7 @@ import 'package:flutter/services.dart';
|
||||
import 'basic.dart';
|
||||
import 'editable_text.dart';
|
||||
import 'framework.dart';
|
||||
import 'localizations.dart';
|
||||
import 'media_query.dart';
|
||||
import 'text_selection_toolbar_anchors.dart';
|
||||
|
||||
@ -28,6 +29,14 @@ import 'text_selection_toolbar_anchors.dart';
|
||||
/// and display this one. A system context menu that is hidden is informed via
|
||||
/// [onSystemHide].
|
||||
///
|
||||
/// Pass [items] to specify the buttons that will appear in the menu. Any items
|
||||
/// without a title will be given a default title from [WidgetsLocalizations].
|
||||
///
|
||||
/// By default, [items] will be set to the result of [getDefaultItems]. This
|
||||
/// method considers the state of the [EditableTextState] so that, for example,
|
||||
/// it will only include [IOSSystemContextMenuItemCopy] if there is currently a
|
||||
/// selection to copy.
|
||||
///
|
||||
/// To check if the current device supports showing the system context menu,
|
||||
/// call [isSupported].
|
||||
///
|
||||
@ -46,13 +55,23 @@ import 'text_selection_toolbar_anchors.dart';
|
||||
class SystemContextMenu extends StatefulWidget {
|
||||
/// Creates an instance of [SystemContextMenu] that points to the given
|
||||
/// [anchor].
|
||||
const SystemContextMenu._({super.key, required this.anchor, this.onSystemHide});
|
||||
const SystemContextMenu._({
|
||||
super.key,
|
||||
required this.anchor,
|
||||
required this.items,
|
||||
this.onSystemHide,
|
||||
});
|
||||
|
||||
/// Creates an instance of [SystemContextMenu] for the field indicated by the
|
||||
/// given [EditableTextState].
|
||||
factory SystemContextMenu.editableText({Key? key, required EditableTextState editableTextState}) {
|
||||
factory SystemContextMenu.editableText({
|
||||
Key? key,
|
||||
required EditableTextState editableTextState,
|
||||
List<IOSSystemContextMenuItem>? items,
|
||||
}) {
|
||||
final (startGlyphHeight: double startGlyphHeight, endGlyphHeight: double endGlyphHeight) =
|
||||
editableTextState.getGlyphHeights();
|
||||
|
||||
return SystemContextMenu._(
|
||||
key: key,
|
||||
anchor: TextSelectionToolbarAnchors.getSelectionRect(
|
||||
@ -63,15 +82,25 @@ class SystemContextMenu extends StatefulWidget {
|
||||
editableTextState.textEditingValue.selection,
|
||||
),
|
||||
),
|
||||
onSystemHide: () {
|
||||
editableTextState.hideToolbar();
|
||||
},
|
||||
items: items ?? getDefaultItems(editableTextState),
|
||||
onSystemHide: editableTextState.hideToolbar,
|
||||
);
|
||||
}
|
||||
|
||||
/// The [Rect] that the context menu should point to.
|
||||
final Rect anchor;
|
||||
|
||||
/// A list of the items to be displayed in the system context menu.
|
||||
///
|
||||
/// When passed, items will be shown regardless of the state of text input.
|
||||
/// For example, [IOSSystemContextMenuItemCopy] will produce a copy button
|
||||
/// even when there is no selection to copy. Use [EditableTextState] and/or
|
||||
/// the result of [getDefaultItems] to add and remove items based on the state
|
||||
/// of the input.
|
||||
///
|
||||
/// Defaults to the result of [getDefaultItems].
|
||||
final List<IOSSystemContextMenuItem> items;
|
||||
|
||||
/// Called when the system hides this context menu.
|
||||
///
|
||||
/// For example, tapping outside of the context menu typically causes the
|
||||
@ -88,6 +117,26 @@ class SystemContextMenu extends StatefulWidget {
|
||||
return MediaQuery.maybeSupportsShowingSystemContextMenu(context) ?? false;
|
||||
}
|
||||
|
||||
/// The default [items] for the given [EditableTextState].
|
||||
///
|
||||
/// For example, [IOSSystemContextMenuItemCopy] will only be included when the
|
||||
/// field represented by the [EditableTextState] has a selection.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [EditableTextState.contextMenuButtonItems], which provides the default
|
||||
/// [ContextMenuButtonItem]s for the Flutter-rendered context menu.
|
||||
static List<IOSSystemContextMenuItem> getDefaultItems(EditableTextState editableTextState) {
|
||||
return <IOSSystemContextMenuItem>[
|
||||
if (editableTextState.copyEnabled) const IOSSystemContextMenuItemCopy(),
|
||||
if (editableTextState.cutEnabled) const IOSSystemContextMenuItemCut(),
|
||||
if (editableTextState.pasteEnabled) const IOSSystemContextMenuItemPaste(),
|
||||
if (editableTextState.selectAllEnabled) const IOSSystemContextMenuItemSelectAll(),
|
||||
if (editableTextState.lookUpEnabled) const IOSSystemContextMenuItemLookUp(),
|
||||
if (editableTextState.searchWebEnabled) const IOSSystemContextMenuItemSearchWeb(),
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
State<SystemContextMenu> createState() => _SystemContextMenuState();
|
||||
}
|
||||
@ -99,15 +148,6 @@ class _SystemContextMenuState extends State<SystemContextMenu> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
_systemContextMenuController = SystemContextMenuController(onSystemHide: widget.onSystemHide);
|
||||
_systemContextMenuController.show(widget.anchor);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(SystemContextMenu oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.anchor != oldWidget.anchor) {
|
||||
_systemContextMenuController.show(widget.anchor);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
@ -119,6 +159,256 @@ class _SystemContextMenuState extends State<SystemContextMenu> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
assert(SystemContextMenu.isSupported(context));
|
||||
|
||||
if (widget.items.isNotEmpty) {
|
||||
final WidgetsLocalizations localizations = WidgetsLocalizations.of(context);
|
||||
final List<IOSSystemContextMenuItemData> itemDatas =
|
||||
widget.items.map((IOSSystemContextMenuItem item) => item.getData(localizations)).toList();
|
||||
_systemContextMenuController.showWithItems(widget.anchor, itemDatas);
|
||||
}
|
||||
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a context menu button that will be rendered in the iOS system
|
||||
/// context menu and not by Flutter itself.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenu], a widget that can be used to display the system
|
||||
/// context menu.
|
||||
/// * [IOSSystemContextMenuItemData], which performs a similar role but at the
|
||||
/// method channel level and mirrors the requirements of the method channel
|
||||
/// API.
|
||||
/// * [ContextMenuButtonItem], which performs a similar role for Flutter-drawn
|
||||
/// context menus.
|
||||
@immutable
|
||||
sealed class IOSSystemContextMenuItem {
|
||||
const IOSSystemContextMenuItem();
|
||||
|
||||
/// The text to display to the user.
|
||||
///
|
||||
/// Not exposed for some built-in menu items whose title is always set by the
|
||||
/// platform.
|
||||
String? get title => null;
|
||||
|
||||
/// Returns the representation of this class used by method channels.
|
||||
IOSSystemContextMenuItemData getData(WidgetsLocalizations localizations);
|
||||
|
||||
@override
|
||||
int get hashCode => title.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) {
|
||||
return true;
|
||||
}
|
||||
if (other.runtimeType != runtimeType) {
|
||||
return false;
|
||||
}
|
||||
return other is IOSSystemContextMenuItem && other.title == title;
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an instance of [IOSSystemContextMenuItem] for the system's built-in
|
||||
/// copy button.
|
||||
///
|
||||
/// Should only appear when there is a selection that can be copied.
|
||||
///
|
||||
/// The title and action are both handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenu], a widget that can be used to display the system
|
||||
/// context menu.
|
||||
/// * [IOSSystemContextMenuItemDataCopy], which specifies the data to be sent to
|
||||
/// the platform for this same button.
|
||||
final class IOSSystemContextMenuItemCopy extends IOSSystemContextMenuItem {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemCopy].
|
||||
const IOSSystemContextMenuItemCopy();
|
||||
|
||||
@override
|
||||
IOSSystemContextMenuItemDataCopy getData(WidgetsLocalizations localizations) {
|
||||
return const IOSSystemContextMenuItemDataCopy();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an instance of [IOSSystemContextMenuItem] for the system's built-in
|
||||
/// cut button.
|
||||
///
|
||||
/// Should only appear when there is a selection that can be cut.
|
||||
///
|
||||
/// The title and action are both handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenu], a widget that can be used to display the system
|
||||
/// context menu.
|
||||
/// * [IOSSystemContextMenuItemDataCut], which specifies the data to be sent to
|
||||
/// the platform for this same button.
|
||||
final class IOSSystemContextMenuItemCut extends IOSSystemContextMenuItem {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemCut].
|
||||
const IOSSystemContextMenuItemCut();
|
||||
|
||||
@override
|
||||
IOSSystemContextMenuItemDataCut getData(WidgetsLocalizations localizations) {
|
||||
return const IOSSystemContextMenuItemDataCut();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an instance of [IOSSystemContextMenuItem] for the system's built-in
|
||||
/// paste button.
|
||||
///
|
||||
/// Should only appear when the field can receive pasted content.
|
||||
///
|
||||
/// The title and action are both handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenu], a widget that can be used to display the system
|
||||
/// context menu.
|
||||
/// * [IOSSystemContextMenuItemDataPaste], which specifies the data to be sent
|
||||
/// to the platform for this same button.
|
||||
final class IOSSystemContextMenuItemPaste extends IOSSystemContextMenuItem {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemPaste].
|
||||
const IOSSystemContextMenuItemPaste();
|
||||
|
||||
@override
|
||||
IOSSystemContextMenuItemDataPaste getData(WidgetsLocalizations localizations) {
|
||||
return const IOSSystemContextMenuItemDataPaste();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an instance of [IOSSystemContextMenuItem] for the system's built-in
|
||||
/// select all button.
|
||||
///
|
||||
/// Should only appear when the field can have its selection changed.
|
||||
///
|
||||
/// The title and action are both handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenu], a widget that can be used to display the system
|
||||
/// context menu.
|
||||
/// * [IOSSystemContextMenuItemDataSelectAll], which specifies the data to be
|
||||
/// sent to the platform for this same button.
|
||||
final class IOSSystemContextMenuItemSelectAll extends IOSSystemContextMenuItem {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemSelectAll].
|
||||
const IOSSystemContextMenuItemSelectAll();
|
||||
|
||||
@override
|
||||
IOSSystemContextMenuItemDataSelectAll getData(WidgetsLocalizations localizations) {
|
||||
return const IOSSystemContextMenuItemDataSelectAll();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an instance of [IOSSystemContextMenuItem] for the
|
||||
/// system's built-in look up button.
|
||||
///
|
||||
/// Should only appear when content is selected.
|
||||
///
|
||||
/// The [title] is optional, but it must be specified before being sent to the
|
||||
/// platform. Typically it should be set to
|
||||
/// [WidgetsLocalizations.lookUpButtonLabel].
|
||||
///
|
||||
/// The action is handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenu], a widget that can be used to display the system
|
||||
/// context menu.
|
||||
/// * [IOSSystemContextMenuItemDataLookUp], which specifies the data to be sent
|
||||
/// to the platform for this same button.
|
||||
final class IOSSystemContextMenuItemLookUp extends IOSSystemContextMenuItem {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemLookUp].
|
||||
const IOSSystemContextMenuItemLookUp({this.title});
|
||||
|
||||
@override
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
IOSSystemContextMenuItemDataLookUp getData(WidgetsLocalizations localizations) {
|
||||
return IOSSystemContextMenuItemDataLookUp(title: title ?? localizations.lookUpButtonLabel);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'IOSSystemContextMenuItemLookUp(title: $title)';
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an instance of [IOSSystemContextMenuItem] for the
|
||||
/// system's built-in search web button.
|
||||
///
|
||||
/// Should only appear when content is selected.
|
||||
///
|
||||
/// The [title] is optional, but it must be specified before being sent to the
|
||||
/// platform. Typically it should be set to
|
||||
/// [WidgetsLocalizations.searchWebButtonLabel].
|
||||
///
|
||||
/// The action is handled by the platform.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenu], a widget that can be used to display the system
|
||||
/// context menu.
|
||||
/// * [IOSSystemContextMenuItemDataSearchWeb], which specifies the data to be
|
||||
/// sent to the platform for this same button.
|
||||
final class IOSSystemContextMenuItemSearchWeb extends IOSSystemContextMenuItem {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemSearchWeb].
|
||||
const IOSSystemContextMenuItemSearchWeb({this.title});
|
||||
|
||||
@override
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
IOSSystemContextMenuItemDataSearchWeb getData(WidgetsLocalizations localizations) {
|
||||
return IOSSystemContextMenuItemDataSearchWeb(
|
||||
title: title ?? localizations.searchWebButtonLabel,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'IOSSystemContextMenuItemSearchWeb(title: $title)';
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an instance of [IOSSystemContextMenuItem] for the
|
||||
/// system's built-in share button.
|
||||
///
|
||||
/// Opens the system share dialog.
|
||||
///
|
||||
/// Should only appear when shareable content is selected.
|
||||
///
|
||||
/// The [title] is optional, but it must be specified before being sent to the
|
||||
/// platform. Typically it should be set to
|
||||
/// [WidgetsLocalizations.shareButtonLabel].
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [SystemContextMenu], a widget that can be used to display the system
|
||||
/// context menu.
|
||||
/// * [IOSSystemContextMenuItemDataShare], which specifies the data to be sent
|
||||
/// to the platform for this same button.
|
||||
final class IOSSystemContextMenuItemShare extends IOSSystemContextMenuItem {
|
||||
/// Creates an instance of [IOSSystemContextMenuItemShare].
|
||||
const IOSSystemContextMenuItemShare({this.title});
|
||||
|
||||
@override
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
IOSSystemContextMenuItemDataShare getData(WidgetsLocalizations localizations) {
|
||||
return IOSSystemContextMenuItemDataShare(title: title ?? localizations.shareButtonLabel);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'IOSSystemContextMenuItemShare(title: $title)';
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(justinmc): Support the "custom" type.
|
||||
// https://github.com/flutter/flutter/issues/103163
|
||||
|
@ -2,14 +2,35 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import './text_input_utils.dart';
|
||||
import '../system_context_menu_utils.dart';
|
||||
import '../widgets/clipboard_utils.dart';
|
||||
import 'text_input_utils.dart';
|
||||
|
||||
void main() {
|
||||
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
setUp(() async {
|
||||
final MockClipboard mockClipboard = MockClipboard();
|
||||
TestWidgetsFlutterBinding.ensureInitialized().defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
mockClipboard.handleMethodCall,
|
||||
);
|
||||
// Fill the clipboard so that the Paste option is available in the text
|
||||
// selection menu.
|
||||
await Clipboard.setData(const ClipboardData(text: 'Clipboard data'));
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized().defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
test('showing and hiding one controller', () {
|
||||
// Create an active connection, which is required to show the system menu.
|
||||
final FakeTextInputClient client = FakeTextInputClient(const TextEditingValue(text: 'test1'));
|
||||
@ -55,10 +76,14 @@ void main() {
|
||||
|
||||
expect(targetRects, isEmpty);
|
||||
expect(hideCount, 0);
|
||||
expect(controller.isVisible, isFalse);
|
||||
|
||||
// Showing calls the platform.
|
||||
const Rect rect1 = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
|
||||
controller.show(rect1);
|
||||
final List<IOSSystemContextMenuItemData> items = <IOSSystemContextMenuItemData>[
|
||||
const IOSSystemContextMenuItemDataCopy(),
|
||||
];
|
||||
controller.showWithItems(rect1, items);
|
||||
expect(targetRects, hasLength(1));
|
||||
expect(targetRects.last['x'], rect1.left);
|
||||
expect(targetRects.last['y'], rect1.top);
|
||||
@ -66,12 +91,13 @@ void main() {
|
||||
expect(targetRects.last['height'], rect1.height);
|
||||
|
||||
// Showing the same thing again does nothing.
|
||||
controller.show(rect1);
|
||||
controller.showWithItems(rect1, items);
|
||||
expect(controller.isVisible, isTrue);
|
||||
expect(targetRects, hasLength(1));
|
||||
|
||||
// Showing a new rect calls the platform.
|
||||
const Rect rect2 = Rect.fromLTWH(1.0, 1.0, 200.0, 200.0);
|
||||
controller.show(rect2);
|
||||
controller.showWithItems(rect2, items);
|
||||
expect(targetRects, hasLength(2));
|
||||
expect(targetRects.last['x'], rect2.left);
|
||||
expect(targetRects.last['y'], rect2.top);
|
||||
@ -80,14 +106,17 @@ void main() {
|
||||
|
||||
// Hiding calls the platform.
|
||||
controller.hide();
|
||||
expect(controller.isVisible, isFalse);
|
||||
expect(hideCount, 1);
|
||||
|
||||
// Hiding again does nothing.
|
||||
controller.hide();
|
||||
expect(controller.isVisible, isFalse);
|
||||
expect(hideCount, 1);
|
||||
|
||||
// Showing the last shown rect calls the platform.
|
||||
controller.show(rect2);
|
||||
controller.showWithItems(rect2, items);
|
||||
expect(controller.isVisible, isTrue);
|
||||
expect(targetRects, hasLength(3));
|
||||
expect(targetRects.last['x'], rect2.left);
|
||||
expect(targetRects.last['y'], rect2.top);
|
||||
@ -95,6 +124,7 @@ void main() {
|
||||
expect(targetRects.last['height'], rect2.height);
|
||||
|
||||
controller.hide();
|
||||
expect(controller.isVisible, isFalse);
|
||||
expect(hideCount, 2);
|
||||
});
|
||||
|
||||
@ -146,13 +176,18 @@ void main() {
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
expect(controller.isVisible, isFalse);
|
||||
expect(targetRects, isEmpty);
|
||||
expect(hideCount, 0);
|
||||
expect(systemHideCount, 0);
|
||||
|
||||
// Showing calls the platform.
|
||||
const Rect rect1 = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
|
||||
controller.show(rect1);
|
||||
final List<IOSSystemContextMenuItemData> items = <IOSSystemContextMenuItemData>[
|
||||
const IOSSystemContextMenuItemDataCopy(),
|
||||
];
|
||||
controller.showWithItems(rect1, items);
|
||||
expect(controller.isVisible, isTrue);
|
||||
expect(targetRects, hasLength(1));
|
||||
expect(targetRects.last['x'], rect1.left);
|
||||
expect(targetRects.last['y'], rect1.top);
|
||||
@ -168,11 +203,13 @@ void main() {
|
||||
messageBytes,
|
||||
(ByteData? data) {},
|
||||
);
|
||||
expect(controller.isVisible, isFalse);
|
||||
expect(hideCount, 0);
|
||||
expect(systemHideCount, 1);
|
||||
|
||||
// Hiding does not call the platform, since the menu was already hidden.
|
||||
controller.hide();
|
||||
expect(controller.isVisible, isFalse);
|
||||
expect(hideCount, 0);
|
||||
});
|
||||
|
||||
@ -188,21 +225,32 @@ void main() {
|
||||
addTearDown(() {
|
||||
controller1.dispose();
|
||||
});
|
||||
expect(controller1.isVisible, isFalse);
|
||||
const Rect rect1 = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
|
||||
final List<IOSSystemContextMenuItemData> items = <IOSSystemContextMenuItemData>[
|
||||
const IOSSystemContextMenuItemDataCopy(),
|
||||
];
|
||||
expect(() {
|
||||
controller1.show(rect1);
|
||||
controller1.showWithItems(rect1, items);
|
||||
}, isNot(throwsAssertionError));
|
||||
expect(controller1.isVisible, isTrue);
|
||||
|
||||
final SystemContextMenuController controller2 = SystemContextMenuController();
|
||||
addTearDown(() {
|
||||
controller2.dispose();
|
||||
});
|
||||
expect(controller1.isVisible, isTrue);
|
||||
expect(controller2.isVisible, isFalse);
|
||||
const Rect rect2 = Rect.fromLTWH(1.0, 1.0, 200.0, 200.0);
|
||||
expect(() {
|
||||
controller2.show(rect2);
|
||||
controller2.showWithItems(rect2, items);
|
||||
}, throwsAssertionError);
|
||||
expect(controller1.isVisible, isTrue);
|
||||
expect(controller2.isVisible, isFalse);
|
||||
|
||||
controller1.hide();
|
||||
expect(controller1.isVisible, isFalse);
|
||||
expect(controller2.isVisible, isFalse);
|
||||
});
|
||||
|
||||
test('showing and hiding two controllers', () {
|
||||
@ -248,17 +296,23 @@ void main() {
|
||||
controller1.dispose();
|
||||
});
|
||||
|
||||
expect(controller1.isVisible, isFalse);
|
||||
expect(targetRects, isEmpty);
|
||||
expect(hideCount, 0);
|
||||
|
||||
// Showing calls the platform.
|
||||
const Rect rect1 = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
|
||||
controller1.show(rect1);
|
||||
final List<IOSSystemContextMenuItemData> items = <IOSSystemContextMenuItemData>[
|
||||
const IOSSystemContextMenuItemDataCopy(),
|
||||
];
|
||||
controller1.showWithItems(rect1, items);
|
||||
expect(controller1.isVisible, isTrue);
|
||||
expect(targetRects, hasLength(1));
|
||||
expect(targetRects.last['x'], rect1.left);
|
||||
|
||||
// Hiding calls the platform.
|
||||
controller1.hide();
|
||||
expect(controller1.isVisible, isFalse);
|
||||
expect(hideCount, 1);
|
||||
|
||||
// Showing a new controller calls the platform.
|
||||
@ -266,8 +320,11 @@ void main() {
|
||||
addTearDown(() {
|
||||
controller2.dispose();
|
||||
});
|
||||
expect(controller2.isVisible, isFalse);
|
||||
const Rect rect2 = Rect.fromLTWH(1.0, 1.0, 200.0, 200.0);
|
||||
controller2.show(rect2);
|
||||
controller2.showWithItems(rect2, items);
|
||||
expect(controller1.isVisible, isFalse);
|
||||
expect(controller2.isVisible, isTrue);
|
||||
expect(targetRects, hasLength(2));
|
||||
expect(targetRects.last['x'], rect2.left);
|
||||
expect(targetRects.last['y'], rect2.top);
|
||||
@ -276,10 +333,218 @@ void main() {
|
||||
|
||||
// Hiding the old controller does nothing.
|
||||
controller1.hide();
|
||||
expect(controller1.isVisible, isFalse);
|
||||
expect(controller2.isVisible, isTrue);
|
||||
expect(hideCount, 1);
|
||||
|
||||
// Hiding the new controller calls the platform.
|
||||
controller2.hide();
|
||||
expect(controller1.isVisible, isFalse);
|
||||
expect(controller2.isVisible, isFalse);
|
||||
expect(hideCount, 2);
|
||||
});
|
||||
|
||||
test('showing a controller with custom items', () {
|
||||
// Create an active connection, which is required to show the system menu.
|
||||
final FakeTextInputClient client = FakeTextInputClient(const TextEditingValue(text: 'test1'));
|
||||
final TextInputConnection connection = TextInput.attach(client, client.configuration);
|
||||
addTearDown(() {
|
||||
connection.close();
|
||||
});
|
||||
|
||||
final List<List<IOSSystemContextMenuItemData>> itemsReceived =
|
||||
<List<IOSSystemContextMenuItemData>>[];
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
(MethodCall methodCall) async {
|
||||
switch (methodCall.method) {
|
||||
case 'ContextMenu.showSystemContextMenu':
|
||||
final Map<String, dynamic> arguments = methodCall.arguments as Map<String, dynamic>;
|
||||
final List<dynamic> untypedItems = arguments['items'] as List<dynamic>;
|
||||
final List<IOSSystemContextMenuItemData> lastItems =
|
||||
untypedItems.map((dynamic value) {
|
||||
final Map<String, dynamic> itemJson = value as Map<String, dynamic>;
|
||||
return systemContextMenuItemDataFromJson(itemJson);
|
||||
}).toList();
|
||||
itemsReceived.add(lastItems);
|
||||
}
|
||||
return;
|
||||
},
|
||||
);
|
||||
addTearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
final SystemContextMenuController controller = SystemContextMenuController();
|
||||
addTearDown(() {
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
expect(controller.isVisible, isFalse);
|
||||
|
||||
// Showing calls the platform.
|
||||
const Rect rect = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
|
||||
final List<IOSSystemContextMenuItemData> items1 = <IOSSystemContextMenuItemData>[
|
||||
const IOSSystemContextMenuItemDataCut(),
|
||||
const IOSSystemContextMenuItemDataCopy(),
|
||||
const IOSSystemContextMenuItemDataPaste(),
|
||||
const IOSSystemContextMenuItemDataSelectAll(),
|
||||
const IOSSystemContextMenuItemDataSearchWeb(title: 'Special Search'),
|
||||
// TODO(justinmc): Support the "custom" item type.
|
||||
// https://github.com/flutter/flutter/issues/103163
|
||||
];
|
||||
|
||||
controller.showWithItems(rect, items1);
|
||||
expect(controller.isVisible, isTrue);
|
||||
expect(itemsReceived, hasLength(1));
|
||||
expect(itemsReceived.last, hasLength(items1.length));
|
||||
expect(itemsReceived.last, equals(items1));
|
||||
|
||||
// Showing the same thing again does nothing.
|
||||
controller.showWithItems(rect, items1);
|
||||
expect(controller.isVisible, isTrue);
|
||||
expect(itemsReceived, hasLength(1));
|
||||
|
||||
// Showing new items calls the platform.
|
||||
final List<IOSSystemContextMenuItemData> items2 = <IOSSystemContextMenuItemData>[
|
||||
const IOSSystemContextMenuItemDataCut(),
|
||||
];
|
||||
controller.showWithItems(rect, items2);
|
||||
expect(controller.isVisible, isTrue);
|
||||
expect(itemsReceived, hasLength(2));
|
||||
expect(itemsReceived.last, hasLength(items2.length));
|
||||
expect(itemsReceived.last, equals(items2));
|
||||
|
||||
controller.hide();
|
||||
expect(controller.isVisible, isFalse);
|
||||
});
|
||||
|
||||
test('showing a controller with empty items', () {
|
||||
// Create an active connection, which is required to show the system menu.
|
||||
final FakeTextInputClient client = FakeTextInputClient(const TextEditingValue(text: 'test1'));
|
||||
final TextInputConnection connection = TextInput.attach(client, client.configuration);
|
||||
addTearDown(() {
|
||||
connection.close();
|
||||
});
|
||||
|
||||
final List<List<IOSSystemContextMenuItemData>> itemsReceived =
|
||||
<List<IOSSystemContextMenuItemData>>[];
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
(MethodCall methodCall) async {
|
||||
switch (methodCall.method) {
|
||||
case 'ContextMenu.showSystemContextMenu':
|
||||
final Map<String, dynamic> arguments = methodCall.arguments as Map<String, dynamic>;
|
||||
final List<dynamic> untypedItems = arguments['items'] as List<dynamic>;
|
||||
final List<IOSSystemContextMenuItemData> lastItems =
|
||||
untypedItems.map((dynamic value) {
|
||||
final Map<String, dynamic> itemJson = value as Map<String, dynamic>;
|
||||
return systemContextMenuItemDataFromJson(itemJson);
|
||||
}).toList();
|
||||
itemsReceived.add(lastItems);
|
||||
}
|
||||
return;
|
||||
},
|
||||
);
|
||||
addTearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
final SystemContextMenuController controller = SystemContextMenuController();
|
||||
addTearDown(() {
|
||||
controller.dispose();
|
||||
});
|
||||
|
||||
expect(controller.isVisible, isFalse);
|
||||
|
||||
const Rect rect = Rect.fromLTWH(0.0, 0.0, 100.0, 100.0);
|
||||
final List<IOSSystemContextMenuItemData> items = <IOSSystemContextMenuItemData>[];
|
||||
|
||||
expect(() {
|
||||
controller.showWithItems(rect, items);
|
||||
}, throwsAssertionError);
|
||||
expect(controller.isVisible, isFalse);
|
||||
expect(itemsReceived, hasLength(0));
|
||||
});
|
||||
|
||||
testWidgets('showing a controller for an EditableText', (WidgetTester tester) async {
|
||||
final TextEditingController textEditingController = TextEditingController(text: 'test');
|
||||
final FocusNode focusNode = FocusNode();
|
||||
final GlobalKey<EditableTextState> key = GlobalKey<EditableTextState>();
|
||||
late final WidgetsLocalizations localizations;
|
||||
addTearDown(() {
|
||||
textEditingController.dispose();
|
||||
focusNode.dispose();
|
||||
});
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: SizedBox(
|
||||
width: 400,
|
||||
child: Builder(
|
||||
builder: (BuildContext context) {
|
||||
localizations = WidgetsLocalizations.of(context);
|
||||
return EditableText(
|
||||
key: key,
|
||||
maxLines: 10,
|
||||
controller: textEditingController,
|
||||
showSelectionHandles: true,
|
||||
autofocus: true,
|
||||
focusNode: focusNode,
|
||||
style: Typography.material2018().black.titleMedium!,
|
||||
cursorColor: Colors.blue,
|
||||
backgroundCursorColor: Colors.grey,
|
||||
keyboardType: TextInputType.text,
|
||||
textAlign: TextAlign.right,
|
||||
selectionControls: materialTextSelectionHandleControls,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final EditableTextState editableTextState = tester.state<EditableTextState>(
|
||||
find.byType(EditableText),
|
||||
);
|
||||
final List<IOSSystemContextMenuItem> defaultItems = SystemContextMenu.getDefaultItems(
|
||||
editableTextState,
|
||||
);
|
||||
expect(defaultItems, hasLength(2));
|
||||
expect(defaultItems[1], const IOSSystemContextMenuItemSelectAll());
|
||||
expect(defaultItems.first, const IOSSystemContextMenuItemPaste());
|
||||
|
||||
final (startGlyphHeight: double startGlyphHeight, endGlyphHeight: double endGlyphHeight) =
|
||||
editableTextState.getGlyphHeights();
|
||||
final Rect anchor = TextSelectionToolbarAnchors.getSelectionRect(
|
||||
editableTextState.renderEditable,
|
||||
startGlyphHeight,
|
||||
endGlyphHeight,
|
||||
editableTextState.renderEditable.getEndpointsForSelection(
|
||||
editableTextState.textEditingValue.selection,
|
||||
),
|
||||
);
|
||||
final List<IOSSystemContextMenuItemData> defaultItemDatas =
|
||||
defaultItems.map((IOSSystemContextMenuItem item) => item.getData(localizations)).toList();
|
||||
|
||||
expect(defaultItemDatas, isNotEmpty);
|
||||
|
||||
final SystemContextMenuController systemContextMenuController = SystemContextMenuController();
|
||||
addTearDown(() {
|
||||
systemContextMenuController.dispose();
|
||||
});
|
||||
|
||||
expect(systemContextMenuController.isVisible, isFalse);
|
||||
systemContextMenuController.showWithItems(anchor, defaultItemDatas);
|
||||
expect(systemContextMenuController.isVisible, isTrue);
|
||||
});
|
||||
}
|
||||
|
23
packages/flutter/test/system_context_menu_utils.dart
Normal file
23
packages/flutter/test/system_context_menu_utils.dart
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart' show FlutterError;
|
||||
|
||||
/// Returns a [IOSSystemContextMenuItem] of the correct subclass given its
|
||||
/// json data.
|
||||
IOSSystemContextMenuItemData systemContextMenuItemDataFromJson(Map<String, dynamic> json) {
|
||||
final String? type = json['type'] as String?;
|
||||
final String? title = json['title'] as String?;
|
||||
return switch (type) {
|
||||
'copy' => const IOSSystemContextMenuItemDataCopy(),
|
||||
'cut' => const IOSSystemContextMenuItemDataCut(),
|
||||
'paste' => const IOSSystemContextMenuItemDataPaste(),
|
||||
'selectAll' => const IOSSystemContextMenuItemDataSelectAll(),
|
||||
'searchWeb' => IOSSystemContextMenuItemDataSearchWeb(title: title!),
|
||||
'share' => IOSSystemContextMenuItemDataShare(title: title!),
|
||||
'lookUp' => IOSSystemContextMenuItemDataLookUp(title: title!),
|
||||
_ => throw FlutterError('Invalid json for IOSSystemContextMenuItem.type $type.'),
|
||||
};
|
||||
}
|
@ -10,6 +10,26 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
void main() {
|
||||
final TestAutomatedTestWidgetsFlutterBinding binding = TestAutomatedTestWidgetsFlutterBinding();
|
||||
|
||||
testWidgets('English translations exist for all WidgetsLocalizations properties', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
|
||||
|
||||
expect(localizations.reorderItemUp, isNotNull);
|
||||
expect(localizations.reorderItemDown, isNotNull);
|
||||
expect(localizations.reorderItemLeft, isNotNull);
|
||||
expect(localizations.reorderItemRight, isNotNull);
|
||||
expect(localizations.reorderItemToEnd, isNotNull);
|
||||
expect(localizations.reorderItemToStart, isNotNull);
|
||||
expect(localizations.copyButtonLabel, isNotNull);
|
||||
expect(localizations.cutButtonLabel, isNotNull);
|
||||
expect(localizations.pasteButtonLabel, isNotNull);
|
||||
expect(localizations.selectAllButtonLabel, isNotNull);
|
||||
expect(localizations.lookUpButtonLabel, isNotNull);
|
||||
expect(localizations.searchWebButtonLabel, isNotNull);
|
||||
expect(localizations.shareButtonLabel, isNotNull);
|
||||
});
|
||||
|
||||
testWidgets('Locale is available when Localizations widget stops deferring frames', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
|
@ -6,7 +6,8 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:leak_tracker_flutter_testing/leak_tracker_flutter_testing.dart';
|
||||
|
||||
import '../system_context_menu_utils.dart';
|
||||
|
||||
void main() {
|
||||
final TestWidgetsFlutterBinding binding = TestWidgetsFlutterBinding.ensureInitialized();
|
||||
@ -140,6 +141,267 @@ void main() {
|
||||
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'can customize the menu items',
|
||||
(WidgetTester tester) async {
|
||||
final List<List<IOSSystemContextMenuItemData>> itemsReceived =
|
||||
<List<IOSSystemContextMenuItemData>>[];
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
(MethodCall methodCall) async {
|
||||
switch (methodCall.method) {
|
||||
case 'ContextMenu.showSystemContextMenu':
|
||||
final Map<String, dynamic> arguments = methodCall.arguments as Map<String, dynamic>;
|
||||
final List<dynamic> untypedItems = arguments['items'] as List<dynamic>;
|
||||
final List<IOSSystemContextMenuItemData> lastItems =
|
||||
untypedItems.map((dynamic value) {
|
||||
final Map<String, dynamic> itemJson = value as Map<String, dynamic>;
|
||||
return systemContextMenuItemDataFromJson(itemJson);
|
||||
}).toList();
|
||||
itemsReceived.add(lastItems);
|
||||
}
|
||||
return;
|
||||
},
|
||||
);
|
||||
addTearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
const List<IOSSystemContextMenuItem> items1 = <IOSSystemContextMenuItem>[
|
||||
IOSSystemContextMenuItemCopy(),
|
||||
IOSSystemContextMenuItemShare(title: 'My Share Title'),
|
||||
];
|
||||
final TextEditingController controller = TextEditingController(text: 'one two three');
|
||||
addTearDown(controller.dispose);
|
||||
await tester.pumpWidget(
|
||||
Builder(
|
||||
builder: (BuildContext context) {
|
||||
final MediaQueryData mediaQueryData = MediaQuery.of(context);
|
||||
return MediaQuery(
|
||||
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
|
||||
child: MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
contextMenuBuilder: (
|
||||
BuildContext context,
|
||||
EditableTextState editableTextState,
|
||||
) {
|
||||
return SystemContextMenu.editableText(
|
||||
editableTextState: editableTextState,
|
||||
items: items1,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(SystemContextMenu), findsNothing);
|
||||
expect(itemsReceived, hasLength(0));
|
||||
|
||||
await tester.tap(find.byType(TextField));
|
||||
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
|
||||
expect(state.showToolbar(), true);
|
||||
await tester.pump();
|
||||
expect(find.byType(SystemContextMenu), findsOneWidget);
|
||||
|
||||
expect(itemsReceived, hasLength(1));
|
||||
expect(itemsReceived.last, hasLength(items1.length));
|
||||
expect(itemsReceived.last[0], equals(const IOSSystemContextMenuItemDataCopy()));
|
||||
expect(
|
||||
itemsReceived.last[1],
|
||||
equals(const IOSSystemContextMenuItemDataShare(title: 'My Share Title')),
|
||||
);
|
||||
|
||||
state.hideToolbar();
|
||||
await tester.pump();
|
||||
expect(find.byType(SystemContextMenu), findsNothing);
|
||||
},
|
||||
skip: kIsWeb, // [intended]
|
||||
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
"passing empty items builds the widget but doesn't show the system context menu",
|
||||
(WidgetTester tester) async {
|
||||
final List<List<IOSSystemContextMenuItemData>> itemsReceived =
|
||||
<List<IOSSystemContextMenuItemData>>[];
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
(MethodCall methodCall) async {
|
||||
switch (methodCall.method) {
|
||||
case 'ContextMenu.showSystemContextMenu':
|
||||
final Map<String, dynamic> arguments = methodCall.arguments as Map<String, dynamic>;
|
||||
final List<dynamic> untypedItems = arguments['items'] as List<dynamic>;
|
||||
final List<IOSSystemContextMenuItemData> lastItems =
|
||||
untypedItems.map((dynamic value) {
|
||||
final Map<String, dynamic> itemJson = value as Map<String, dynamic>;
|
||||
return systemContextMenuItemDataFromJson(itemJson);
|
||||
}).toList();
|
||||
itemsReceived.add(lastItems);
|
||||
}
|
||||
return;
|
||||
},
|
||||
);
|
||||
addTearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
const List<IOSSystemContextMenuItem> items1 = <IOSSystemContextMenuItem>[];
|
||||
final TextEditingController controller = TextEditingController(text: 'one two three');
|
||||
addTearDown(controller.dispose);
|
||||
await tester.pumpWidget(
|
||||
Builder(
|
||||
builder: (BuildContext context) {
|
||||
final MediaQueryData mediaQueryData = MediaQuery.of(context);
|
||||
return MediaQuery(
|
||||
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
|
||||
child: MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
contextMenuBuilder: (
|
||||
BuildContext context,
|
||||
EditableTextState editableTextState,
|
||||
) {
|
||||
return SystemContextMenu.editableText(
|
||||
editableTextState: editableTextState,
|
||||
items: items1,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(tester.takeException(), isNull);
|
||||
|
||||
expect(find.byType(SystemContextMenu), findsNothing);
|
||||
expect(itemsReceived, hasLength(0));
|
||||
|
||||
await tester.tap(find.byType(TextField));
|
||||
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
|
||||
expect(state.showToolbar(), true);
|
||||
expect(tester.takeException(), isNull);
|
||||
|
||||
await tester.pump();
|
||||
expect(tester.takeException(), isNull);
|
||||
expect(find.byType(SystemContextMenu), findsOneWidget);
|
||||
expect(itemsReceived, hasLength(0));
|
||||
},
|
||||
skip: kIsWeb, // [intended]
|
||||
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'items receive a default title',
|
||||
(WidgetTester tester) async {
|
||||
final List<List<IOSSystemContextMenuItemData>> itemsReceived =
|
||||
<List<IOSSystemContextMenuItemData>>[];
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
(MethodCall methodCall) async {
|
||||
switch (methodCall.method) {
|
||||
case 'ContextMenu.showSystemContextMenu':
|
||||
final Map<String, dynamic> arguments = methodCall.arguments as Map<String, dynamic>;
|
||||
final List<dynamic> untypedItems = arguments['items'] as List<dynamic>;
|
||||
final List<IOSSystemContextMenuItemData> lastItems =
|
||||
untypedItems.map((dynamic value) {
|
||||
final Map<String, dynamic> itemJson = value as Map<String, dynamic>;
|
||||
return systemContextMenuItemDataFromJson(itemJson);
|
||||
}).toList();
|
||||
itemsReceived.add(lastItems);
|
||||
}
|
||||
return;
|
||||
},
|
||||
);
|
||||
addTearDown(() {
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
null,
|
||||
);
|
||||
});
|
||||
|
||||
const List<IOSSystemContextMenuItem> items1 = <IOSSystemContextMenuItem>[
|
||||
// Copy gets no title, it's set by the platform.
|
||||
IOSSystemContextMenuItemCopy(),
|
||||
// Share could take a title, but if not, it gets a localized default.
|
||||
IOSSystemContextMenuItemShare(),
|
||||
];
|
||||
final TextEditingController controller = TextEditingController(text: 'one two three');
|
||||
addTearDown(controller.dispose);
|
||||
await tester.pumpWidget(
|
||||
Builder(
|
||||
builder: (BuildContext context) {
|
||||
final MediaQueryData mediaQueryData = MediaQuery.of(context);
|
||||
return MediaQuery(
|
||||
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
|
||||
child: MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: TextField(
|
||||
controller: controller,
|
||||
contextMenuBuilder: (
|
||||
BuildContext context,
|
||||
EditableTextState editableTextState,
|
||||
) {
|
||||
return SystemContextMenu.editableText(
|
||||
editableTextState: editableTextState,
|
||||
items: items1,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(SystemContextMenu), findsNothing);
|
||||
expect(itemsReceived, hasLength(0));
|
||||
|
||||
await tester.tap(find.byType(TextField));
|
||||
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
|
||||
expect(state.showToolbar(), true);
|
||||
await tester.pump();
|
||||
expect(find.byType(SystemContextMenu), findsOneWidget);
|
||||
|
||||
expect(itemsReceived, hasLength(1));
|
||||
expect(itemsReceived.last, hasLength(items1.length));
|
||||
expect(itemsReceived.last[0], equals(const IOSSystemContextMenuItemDataCopy()));
|
||||
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
|
||||
expect(
|
||||
itemsReceived.last[1],
|
||||
equals(IOSSystemContextMenuItemDataShare(title: localizations.shareButtonLabel)),
|
||||
);
|
||||
|
||||
state.hideToolbar();
|
||||
await tester.pump();
|
||||
expect(find.byType(SystemContextMenu), findsNothing);
|
||||
},
|
||||
skip: kIsWeb, // [intended]
|
||||
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'can be updated.',
|
||||
(WidgetTester tester) async {
|
||||
@ -365,123 +627,75 @@ void main() {
|
||||
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'asserts when built with no text input connection',
|
||||
experimentalLeakTesting:
|
||||
LeakTesting.settings.withIgnoredAll(), // leaking by design because of exception
|
||||
(WidgetTester tester) async {
|
||||
SystemContextMenu? systemContextMenu;
|
||||
late StateSetter setState;
|
||||
await tester.pumpWidget(
|
||||
Builder(
|
||||
builder: (BuildContext context) {
|
||||
final MediaQueryData mediaQueryData = MediaQuery.of(context);
|
||||
return MediaQuery(
|
||||
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
|
||||
child: MaterialApp(
|
||||
home: Scaffold(
|
||||
body: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter localSetState) {
|
||||
setState = localSetState;
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
const TextField(),
|
||||
if (systemContextMenu != null) systemContextMenu!,
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// No SystemContextMenu yet, so no assertion error.
|
||||
expect(tester.takeException(), isNull);
|
||||
|
||||
// Add the SystemContextMenu and receive an assertion since there is no
|
||||
// active text input connection.
|
||||
setState(() {
|
||||
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
|
||||
systemContextMenu = SystemContextMenu.editableText(editableTextState: state);
|
||||
});
|
||||
|
||||
final FlutterExceptionHandler? oldHandler = FlutterError.onError;
|
||||
dynamic exception;
|
||||
FlutterError.onError = (FlutterErrorDetails details) {
|
||||
exception ??= details.exception;
|
||||
};
|
||||
addTearDown(() {
|
||||
FlutterError.onError = oldHandler;
|
||||
});
|
||||
|
||||
await tester.pump();
|
||||
expect(exception, isAssertionError);
|
||||
expect(exception.toString(), contains('only be shown for an active text input connection'));
|
||||
test(
|
||||
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemCopy',
|
||||
() {
|
||||
const IOSSystemContextMenuItemCopy item = IOSSystemContextMenuItemCopy();
|
||||
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
|
||||
expect(item.getData(localizations), const IOSSystemContextMenuItemDataCopy());
|
||||
},
|
||||
skip: kIsWeb, // [intended]
|
||||
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
|
||||
);
|
||||
|
||||
testWidgets(
|
||||
'does not assert when built with an active text input connection',
|
||||
(WidgetTester tester) async {
|
||||
SystemContextMenu? systemContextMenu;
|
||||
late StateSetter setState;
|
||||
await tester.pumpWidget(
|
||||
Builder(
|
||||
builder: (BuildContext context) {
|
||||
final MediaQueryData mediaQueryData = MediaQuery.of(context);
|
||||
return MediaQuery(
|
||||
data: mediaQueryData.copyWith(supportsShowingSystemContextMenu: true),
|
||||
child: MaterialApp(
|
||||
home: Scaffold(
|
||||
body: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter localSetState) {
|
||||
setState = localSetState;
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
const TextField(),
|
||||
if (systemContextMenu != null) systemContextMenu!,
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
// No SystemContextMenu yet, so no assertion error.
|
||||
expect(tester.takeException(), isNull);
|
||||
|
||||
// Tap the field to open a text input connection.
|
||||
await tester.tap(find.byType(TextField));
|
||||
await tester.pump();
|
||||
|
||||
// Add the SystemContextMenu and expect no error.
|
||||
setState(() {
|
||||
final EditableTextState state = tester.state<EditableTextState>(find.byType(EditableText));
|
||||
systemContextMenu = SystemContextMenu.editableText(editableTextState: state);
|
||||
});
|
||||
|
||||
final FlutterExceptionHandler? oldHandler = FlutterError.onError;
|
||||
dynamic exception;
|
||||
FlutterError.onError = (FlutterErrorDetails details) {
|
||||
exception ??= details.exception;
|
||||
};
|
||||
addTearDown(() {
|
||||
FlutterError.onError = oldHandler;
|
||||
});
|
||||
|
||||
await tester.pump();
|
||||
expect(exception, isNull);
|
||||
test(
|
||||
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemCut',
|
||||
() {
|
||||
const IOSSystemContextMenuItemCut item = IOSSystemContextMenuItemCut();
|
||||
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
|
||||
expect(item.getData(localizations), const IOSSystemContextMenuItemDataCut());
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemPaste',
|
||||
() {
|
||||
const IOSSystemContextMenuItemPaste item = IOSSystemContextMenuItemPaste();
|
||||
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
|
||||
expect(item.getData(localizations), const IOSSystemContextMenuItemDataPaste());
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemSelectAll',
|
||||
() {
|
||||
const IOSSystemContextMenuItemSelectAll item = IOSSystemContextMenuItemSelectAll();
|
||||
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
|
||||
expect(item.getData(localizations), const IOSSystemContextMenuItemDataSelectAll());
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemLookUp',
|
||||
() {
|
||||
const IOSSystemContextMenuItemLookUp item = IOSSystemContextMenuItemLookUp();
|
||||
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
|
||||
expect(
|
||||
item.getData(localizations),
|
||||
IOSSystemContextMenuItemDataLookUp(title: localizations.lookUpButtonLabel),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemSearchWeb',
|
||||
() {
|
||||
const IOSSystemContextMenuItemSearchWeb item = IOSSystemContextMenuItemSearchWeb();
|
||||
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
|
||||
expect(
|
||||
item.getData(localizations),
|
||||
IOSSystemContextMenuItemDataSearchWeb(title: localizations.searchWebButtonLabel),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'can get the IOSSystemContextMenuItemData representation of an IOSSystemContextMenuItemShare',
|
||||
() {
|
||||
const IOSSystemContextMenuItemShare item = IOSSystemContextMenuItemShare();
|
||||
const WidgetsLocalizations localizations = DefaultWidgetsLocalizations();
|
||||
expect(
|
||||
item.getData(localizations),
|
||||
IOSSystemContextMenuItemDataShare(title: localizations.shareButtonLabel),
|
||||
);
|
||||
},
|
||||
skip: kIsWeb, // [intended]
|
||||
variant: TargetPlatformVariant.only(TargetPlatform.iOS),
|
||||
);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Skuif op",
|
||||
"reorderItemDown": "Skuif af",
|
||||
"reorderItemLeft": "Skuif na links",
|
||||
"reorderItemRight": "Skuif na regs"
|
||||
"reorderItemRight": "Skuif na regs",
|
||||
"copyButtonLabel": "Kopieer",
|
||||
"cutButtonLabel": "Knip",
|
||||
"lookUpButtonLabel": "Kyk op",
|
||||
"searchWebButtonLabel": "Deursoek web",
|
||||
"shareButtonLabel": "Deel",
|
||||
"pasteButtonLabel": "Plak",
|
||||
"selectAllButtonLabel": "Kies alles"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "ወደ ላይ ውሰድ",
|
||||
"reorderItemDown": "ወደ ታች ውሰድ",
|
||||
"reorderItemLeft": "ወደ ግራ ውሰድ",
|
||||
"reorderItemRight": "ወደ ቀኝ ውሰድ"
|
||||
"reorderItemRight": "ወደ ቀኝ ውሰድ",
|
||||
"copyButtonLabel": "ቅዳ",
|
||||
"cutButtonLabel": "ቁረጥ",
|
||||
"lookUpButtonLabel": "ይመልከቱ",
|
||||
"searchWebButtonLabel": "ድርን ፈልግ",
|
||||
"shareButtonLabel": "አጋራ",
|
||||
"pasteButtonLabel": "ለጥፍ",
|
||||
"selectAllButtonLabel": "ሁሉንም ምረጥ"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "نقل لأعلى",
|
||||
"reorderItemDown": "نقل لأسفل",
|
||||
"reorderItemLeft": "نقل لليمين",
|
||||
"reorderItemRight": "نقل لليسار"
|
||||
"reorderItemRight": "نقل لليسار",
|
||||
"copyButtonLabel": "نسخ",
|
||||
"cutButtonLabel": "قص",
|
||||
"lookUpButtonLabel": "بحث عام",
|
||||
"searchWebButtonLabel": "البحث على الويب",
|
||||
"shareButtonLabel": "مشاركة",
|
||||
"pasteButtonLabel": "لصق",
|
||||
"selectAllButtonLabel": "اختيار الكل"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "ওপৰলৈ নিয়ক",
|
||||
"reorderItemDown": "তললৈ স্থানান্তৰ কৰক",
|
||||
"reorderItemLeft": "বাওঁফাললৈ স্থানান্তৰ কৰক",
|
||||
"reorderItemRight": "সোঁফাললৈ স্থানান্তৰ কৰক"
|
||||
"reorderItemRight": "সোঁফাললৈ স্থানান্তৰ কৰক",
|
||||
"copyButtonLabel": "প্ৰতিলিপি কৰক",
|
||||
"cutButtonLabel": "কাট কৰক",
|
||||
"lookUpButtonLabel": "ওপৰলৈ চাওক",
|
||||
"searchWebButtonLabel": "ৱেবত সন্ধান কৰক",
|
||||
"shareButtonLabel": "শ্বেয়াৰ কৰক",
|
||||
"pasteButtonLabel": "পে'ষ্ট কৰক",
|
||||
"selectAllButtonLabel": "সকলো বাছনি কৰক"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Yuxarı köçürün",
|
||||
"reorderItemDown": "Aşağı köçürün",
|
||||
"reorderItemLeft": "Sola köçürün",
|
||||
"reorderItemRight": "Sağa köçürün"
|
||||
"reorderItemRight": "Sağa köçürün",
|
||||
"copyButtonLabel": "Kopyalayın",
|
||||
"cutButtonLabel": "Kəsin",
|
||||
"lookUpButtonLabel": "Axtarın",
|
||||
"searchWebButtonLabel": "Vebdə axtarın",
|
||||
"shareButtonLabel": "Paylaşın",
|
||||
"pasteButtonLabel": "Yerləşdirin",
|
||||
"selectAllButtonLabel": "Hamısını seçin"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Перамясціць уверх",
|
||||
"reorderItemDown": "Перамясціць уніз",
|
||||
"reorderItemLeft": "Перамясціць улева",
|
||||
"reorderItemRight": "Перамясціць управа"
|
||||
"reorderItemRight": "Перамясціць управа",
|
||||
"copyButtonLabel": "Капіраваць",
|
||||
"cutButtonLabel": "Выразаць",
|
||||
"lookUpButtonLabel": "Знайсці",
|
||||
"searchWebButtonLabel": "Пошук у сетцы",
|
||||
"shareButtonLabel": "Абагуліць",
|
||||
"pasteButtonLabel": "Уставіць",
|
||||
"selectAllButtonLabel": "Выбраць усе"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Преместване нагоре",
|
||||
"reorderItemDown": "Преместване надолу",
|
||||
"reorderItemLeft": "Преместване наляво",
|
||||
"reorderItemRight": "Преместване надясно"
|
||||
"reorderItemRight": "Преместване надясно",
|
||||
"copyButtonLabel": "Копиране",
|
||||
"cutButtonLabel": "Изрязване",
|
||||
"lookUpButtonLabel": "Look Up",
|
||||
"searchWebButtonLabel": "Търсене в мрежата",
|
||||
"shareButtonLabel": "Споделяне",
|
||||
"pasteButtonLabel": "Поставяне",
|
||||
"selectAllButtonLabel": "Избиране на всички"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "উপরের দিকে সরান",
|
||||
"reorderItemDown": "নিচের দিকে সরান",
|
||||
"reorderItemLeft": "বাঁদিকে সরান",
|
||||
"reorderItemRight": "ডানদিকে সরান"
|
||||
"reorderItemRight": "ডানদিকে সরান",
|
||||
"copyButtonLabel": "কপি করুন",
|
||||
"cutButtonLabel": "কাট করুন",
|
||||
"lookUpButtonLabel": "লুক-আপ",
|
||||
"searchWebButtonLabel": "ওয়েবে সার্চ করুন",
|
||||
"shareButtonLabel": "শেয়ার করুন",
|
||||
"pasteButtonLabel": "পেস্ট করুন",
|
||||
"selectAllButtonLabel": "সব বেছে নিন"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Pomjeri nagore",
|
||||
"reorderItemDown": "Pomjeri nadolje",
|
||||
"reorderItemLeft": "Pomjeri lijevo",
|
||||
"reorderItemRight": "Pomjeri desno"
|
||||
"reorderItemRight": "Pomjeri desno",
|
||||
"copyButtonLabel": "Kopiraj",
|
||||
"cutButtonLabel": "Izreži",
|
||||
"lookUpButtonLabel": "Pogled nagore",
|
||||
"searchWebButtonLabel": "Pretraži Web",
|
||||
"shareButtonLabel": "Dijeli",
|
||||
"pasteButtonLabel": "Zalijepi",
|
||||
"selectAllButtonLabel": "Odaberi sve"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Mou amunt",
|
||||
"reorderItemDown": "Mou avall",
|
||||
"reorderItemLeft": "Mou cap a l'esquerra",
|
||||
"reorderItemRight": "Mou cap a la dreta"
|
||||
"reorderItemRight": "Mou cap a la dreta",
|
||||
"copyButtonLabel": "Copia",
|
||||
"cutButtonLabel": "Retalla",
|
||||
"lookUpButtonLabel": "Mira amunt",
|
||||
"searchWebButtonLabel": "Cerca al web",
|
||||
"shareButtonLabel": "Comparteix",
|
||||
"pasteButtonLabel": "Enganxa",
|
||||
"selectAllButtonLabel": "Selecciona-ho tot"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Přesunout nahoru",
|
||||
"reorderItemDown": "Přesunout dolů",
|
||||
"reorderItemLeft": "Přesunout doleva",
|
||||
"reorderItemRight": "Přesunout doprava"
|
||||
"reorderItemRight": "Přesunout doprava",
|
||||
"copyButtonLabel": "Kopírovat",
|
||||
"cutButtonLabel": "Vyjmout",
|
||||
"lookUpButtonLabel": "Vyhledat",
|
||||
"searchWebButtonLabel": "Vyhledávat na webu",
|
||||
"shareButtonLabel": "Sdílet",
|
||||
"pasteButtonLabel": "Vložit",
|
||||
"selectAllButtonLabel": "Vybrat vše"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Symud i fyny",
|
||||
"reorderItemDown": "Symud i lawr",
|
||||
"reorderItemLeft": "Symud i'r chwith",
|
||||
"reorderItemRight": "Symud i'r dde"
|
||||
"reorderItemRight": "Symud i'r dde",
|
||||
"copyButtonLabel": "Copïo",
|
||||
"cutButtonLabel": "Torri",
|
||||
"lookUpButtonLabel": "Chwilio",
|
||||
"searchWebButtonLabel": "Chwilio'r We",
|
||||
"shareButtonLabel": "Rhannu",
|
||||
"pasteButtonLabel": "Gludo",
|
||||
"selectAllButtonLabel": "Dewis y Cyfan"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Flyt op",
|
||||
"reorderItemDown": "Flyt ned",
|
||||
"reorderItemLeft": "Flyt til venstre",
|
||||
"reorderItemRight": "Flyt til højre"
|
||||
"reorderItemRight": "Flyt til højre",
|
||||
"copyButtonLabel": "Kopiér",
|
||||
"cutButtonLabel": "Klip",
|
||||
"lookUpButtonLabel": "Slå op",
|
||||
"searchWebButtonLabel": "Søg på nettet",
|
||||
"shareButtonLabel": "Del",
|
||||
"pasteButtonLabel": "Indsæt",
|
||||
"selectAllButtonLabel": "Markér alt"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Nach oben verschieben",
|
||||
"reorderItemDown": "Nach unten verschieben",
|
||||
"reorderItemLeft": "Nach links verschieben",
|
||||
"reorderItemRight": "Nach rechts verschieben"
|
||||
"reorderItemRight": "Nach rechts verschieben",
|
||||
"copyButtonLabel": "Kopieren",
|
||||
"cutButtonLabel": "Ausschneiden",
|
||||
"lookUpButtonLabel": "Nachschlagen",
|
||||
"searchWebButtonLabel": "Im Web suchen",
|
||||
"shareButtonLabel": "Teilen",
|
||||
"pasteButtonLabel": "Einsetzen",
|
||||
"selectAllButtonLabel": "Alle auswählen"
|
||||
}
|
||||
|
@ -4,5 +4,9 @@
|
||||
"reorderItemUp": "Nach oben verschieben",
|
||||
"reorderItemDown": "Nach unten verschieben",
|
||||
"reorderItemLeft": "Nach links verschieben",
|
||||
"reorderItemRight": "Nach rechts verschieben"
|
||||
"reorderItemRight": "Nach rechts verschieben",
|
||||
"copyButtonLabel": "Kopieren",
|
||||
"cutButtonLabel": "Ausschneiden",
|
||||
"pasteButtonLabel": "Einsetzen",
|
||||
"selectAllButtonLabel": "Alle auswählen"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Μετακίνηση προς τα πάνω",
|
||||
"reorderItemDown": "Μετακίνηση προς τα κάτω",
|
||||
"reorderItemLeft": "Μετακίνηση αριστερά",
|
||||
"reorderItemRight": "Μετακίνηση δεξιά"
|
||||
"reorderItemRight": "Μετακίνηση δεξιά",
|
||||
"copyButtonLabel": "Αντιγραφή",
|
||||
"cutButtonLabel": "Αποκοπή",
|
||||
"lookUpButtonLabel": "Look Up",
|
||||
"searchWebButtonLabel": "Αναζήτηση στον ιστό",
|
||||
"shareButtonLabel": "Κοινή χρήση",
|
||||
"pasteButtonLabel": "Επικόλληση",
|
||||
"selectAllButtonLabel": "Επιλογή όλων"
|
||||
}
|
||||
|
@ -1,31 +1,28 @@
|
||||
{
|
||||
"reorderItemToStart": "Move to the start",
|
||||
"@reorderItemToStart": {
|
||||
"description": "The audio announcement to move an item in a Reorderable List to the start of the list."
|
||||
},
|
||||
|
||||
"@reorderItemToStart": {"description":"The audio announcement to move an item in a Reorderable List to the start of the list."},
|
||||
"reorderItemToEnd": "Move to the end",
|
||||
"@reorderItemToEnd": {
|
||||
"description": "The audio announcement to move an item in a Reorderable List to the end of the list."
|
||||
},
|
||||
|
||||
"@reorderItemToEnd": {"description":"The audio announcement to move an item in a Reorderable List to the end of the list."},
|
||||
"reorderItemUp": "Move up",
|
||||
"@reorderItemUp": {
|
||||
"description": "The audio announcement to move an item in a Reorderable List up in the list when it is oriented vertically."
|
||||
},
|
||||
|
||||
"@reorderItemUp": {"description":"The audio announcement to move an item in a Reorderable List up in the list when it is oriented vertically."},
|
||||
"reorderItemDown": "Move down",
|
||||
"@reorderItemDown": {
|
||||
"description": "The audio announcement to move an item in a Reorderable List down in the list when it is oriented vertically."
|
||||
},
|
||||
|
||||
"@reorderItemDown": {"description":"The audio announcement to move an item in a Reorderable List down in the list when it is oriented vertically."},
|
||||
"reorderItemLeft": "Move left",
|
||||
"@reorderItemLeft": {
|
||||
"description": "The audio announcement to move an item in a Reorderable List left in the list when it is oriented horizontally."
|
||||
},
|
||||
|
||||
"@reorderItemLeft": {"description":"The audio announcement to move an item in a Reorderable List left in the list when it is oriented horizontally."},
|
||||
"reorderItemRight": "Move right",
|
||||
"@reorderItemRight": {
|
||||
"description": "The audio announcement to move an item in a Reorderable List right in the list when it is oriented horizontally."
|
||||
}
|
||||
"@reorderItemRight": {"description":"The audio announcement to move an item in a Reorderable List right in the list when it is oriented horizontally."},
|
||||
"copyButtonLabel": "Copy",
|
||||
"@copyButtonLabel": {"description":"The label for copy buttons and menu items."},
|
||||
"cutButtonLabel": "Cut",
|
||||
"@cutButtonLabel": {"description":"The label for cut buttons and menu items."},
|
||||
"lookUpButtonLabel": "Look Up",
|
||||
"@lookUpButtonLabel": {"description":"The label for the Look Up button and menu items."},
|
||||
"searchWebButtonLabel": "Search Web",
|
||||
"@searchWebButtonLabel": {"description":"The label for the Search Web button and menu items."},
|
||||
"shareButtonLabel": "Share",
|
||||
"@shareButtonLabel": {"description":"The label for the Share button and menu items."},
|
||||
"pasteButtonLabel": "Paste",
|
||||
"@pasteButtonLabel": {"description":"The label for paste buttons and menu items."},
|
||||
"selectAllButtonLabel": "Select all",
|
||||
"@selectAllButtonLabel": {"description":"The label for select-all buttons and menu items."}
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Move up",
|
||||
"reorderItemDown": "Move down",
|
||||
"reorderItemLeft": "Move to the left",
|
||||
"reorderItemRight": "Move to the right"
|
||||
"reorderItemRight": "Move to the right",
|
||||
"copyButtonLabel": "Copy",
|
||||
"cutButtonLabel": "Cut",
|
||||
"lookUpButtonLabel": "Look up",
|
||||
"searchWebButtonLabel": "Search Web",
|
||||
"shareButtonLabel": "Share",
|
||||
"pasteButtonLabel": "Paste",
|
||||
"selectAllButtonLabel": "Select all"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Move up",
|
||||
"reorderItemDown": "Move down",
|
||||
"reorderItemLeft": "Move to the left",
|
||||
"reorderItemRight": "Move to the right"
|
||||
"reorderItemRight": "Move to the right",
|
||||
"copyButtonLabel": "Copy",
|
||||
"cutButtonLabel": "Cut",
|
||||
"lookUpButtonLabel": "Look Up",
|
||||
"searchWebButtonLabel": "Search Web",
|
||||
"shareButtonLabel": "Share",
|
||||
"pasteButtonLabel": "Paste",
|
||||
"selectAllButtonLabel": "Select all"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Move up",
|
||||
"reorderItemDown": "Move down",
|
||||
"reorderItemLeft": "Move to the left",
|
||||
"reorderItemRight": "Move to the right"
|
||||
"reorderItemRight": "Move to the right",
|
||||
"copyButtonLabel": "Copy",
|
||||
"cutButtonLabel": "Cut",
|
||||
"lookUpButtonLabel": "Look up",
|
||||
"searchWebButtonLabel": "Search Web",
|
||||
"shareButtonLabel": "Share",
|
||||
"pasteButtonLabel": "Paste",
|
||||
"selectAllButtonLabel": "Select all"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Move up",
|
||||
"reorderItemDown": "Move down",
|
||||
"reorderItemLeft": "Move to the left",
|
||||
"reorderItemRight": "Move to the right"
|
||||
"reorderItemRight": "Move to the right",
|
||||
"copyButtonLabel": "Copy",
|
||||
"cutButtonLabel": "Cut",
|
||||
"lookUpButtonLabel": "Look up",
|
||||
"searchWebButtonLabel": "Search Web",
|
||||
"shareButtonLabel": "Share",
|
||||
"pasteButtonLabel": "Paste",
|
||||
"selectAllButtonLabel": "Select all"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Move up",
|
||||
"reorderItemDown": "Move down",
|
||||
"reorderItemLeft": "Move to the left",
|
||||
"reorderItemRight": "Move to the right"
|
||||
"reorderItemRight": "Move to the right",
|
||||
"copyButtonLabel": "Copy",
|
||||
"cutButtonLabel": "Cut",
|
||||
"lookUpButtonLabel": "Look up",
|
||||
"searchWebButtonLabel": "Search Web",
|
||||
"shareButtonLabel": "Share",
|
||||
"pasteButtonLabel": "Paste",
|
||||
"selectAllButtonLabel": "Select all"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Move up",
|
||||
"reorderItemDown": "Move down",
|
||||
"reorderItemLeft": "Move to the left",
|
||||
"reorderItemRight": "Move to the right"
|
||||
"reorderItemRight": "Move to the right",
|
||||
"copyButtonLabel": "Copy",
|
||||
"cutButtonLabel": "Cut",
|
||||
"lookUpButtonLabel": "Look up",
|
||||
"searchWebButtonLabel": "Search Web",
|
||||
"shareButtonLabel": "Share",
|
||||
"pasteButtonLabel": "Paste",
|
||||
"selectAllButtonLabel": "Select all"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Move up",
|
||||
"reorderItemDown": "Move down",
|
||||
"reorderItemLeft": "Move to the left",
|
||||
"reorderItemRight": "Move to the right"
|
||||
"reorderItemRight": "Move to the right",
|
||||
"copyButtonLabel": "Copy",
|
||||
"cutButtonLabel": "Cut",
|
||||
"lookUpButtonLabel": "Look up",
|
||||
"searchWebButtonLabel": "Search Web",
|
||||
"shareButtonLabel": "Share",
|
||||
"pasteButtonLabel": "Paste",
|
||||
"selectAllButtonLabel": "Select all"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Move up",
|
||||
"reorderItemDown": "Move down",
|
||||
"reorderItemLeft": "Move to the left",
|
||||
"reorderItemRight": "Move to the right"
|
||||
"reorderItemRight": "Move to the right",
|
||||
"copyButtonLabel": "Copy",
|
||||
"cutButtonLabel": "Cut",
|
||||
"lookUpButtonLabel": "Look up",
|
||||
"searchWebButtonLabel": "Search Web",
|
||||
"shareButtonLabel": "Share",
|
||||
"pasteButtonLabel": "Paste",
|
||||
"selectAllButtonLabel": "Select all"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemDown": "Mover hacia abajo",
|
||||
"reorderItemLeft": "Mover hacia la izquierda",
|
||||
"reorderItemRight": "Mover hacia la derecha"
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Buscador visual",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemDown": "Mover hacia abajo",
|
||||
"reorderItemLeft": "Mover hacia la izquierda",
|
||||
"reorderItemRight": "Mover hacia la derecha"
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemDown": "Mover hacia abajo",
|
||||
"reorderItemLeft": "Mover hacia la izquierda",
|
||||
"reorderItemRight": "Mover hacia la derecha"
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemToEnd": "Mover al final",
|
||||
"reorderItemRight": "Mover hacia la derecha",
|
||||
"reorderItemUp": "Mover hacia arriba",
|
||||
"reorderItemToStart": "Mover al inicio"
|
||||
"reorderItemToStart": "Mover al inicio",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar hacia arriba",
|
||||
"searchWebButtonLabel": "Buscar en la Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Teisalda üles",
|
||||
"reorderItemDown": "Teisalda alla",
|
||||
"reorderItemLeft": "Teisalda vasakule",
|
||||
"reorderItemRight": "Teisalda paremale"
|
||||
"reorderItemRight": "Teisalda paremale",
|
||||
"copyButtonLabel": "Kopeeri",
|
||||
"cutButtonLabel": "Lõika",
|
||||
"lookUpButtonLabel": "Look Up",
|
||||
"searchWebButtonLabel": "Otsi veebist",
|
||||
"shareButtonLabel": "Jagamine",
|
||||
"pasteButtonLabel": "Kleebi",
|
||||
"selectAllButtonLabel": "Vali kõik"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Eraman gora",
|
||||
"reorderItemDown": "Eraman behera",
|
||||
"reorderItemLeft": "Eraman ezkerrera",
|
||||
"reorderItemRight": "Eraman eskuinera"
|
||||
"reorderItemRight": "Eraman eskuinera",
|
||||
"copyButtonLabel": "Kopiatu",
|
||||
"cutButtonLabel": "Ebaki",
|
||||
"lookUpButtonLabel": "Bilatu",
|
||||
"searchWebButtonLabel": "Bilatu sarean",
|
||||
"shareButtonLabel": "Partekatu",
|
||||
"pasteButtonLabel": "Itsatsi",
|
||||
"selectAllButtonLabel": "Hautatu guztiak"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "انتقال به بالا",
|
||||
"reorderItemDown": "انتقال به پایین",
|
||||
"reorderItemLeft": "انتقال به راست",
|
||||
"reorderItemRight": "انتقال به چپ"
|
||||
"reorderItemRight": "انتقال به چپ",
|
||||
"copyButtonLabel": "کپی",
|
||||
"cutButtonLabel": "برش",
|
||||
"lookUpButtonLabel": "جستجو",
|
||||
"searchWebButtonLabel": "جستجو در وب",
|
||||
"shareButtonLabel": "همرسانی کردن",
|
||||
"pasteButtonLabel": "جایگذاری",
|
||||
"selectAllButtonLabel": "انتخاب همه"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Siirrä ylös",
|
||||
"reorderItemDown": "Siirrä alas",
|
||||
"reorderItemLeft": "Siirrä vasemmalle",
|
||||
"reorderItemRight": "Siirrä oikealle"
|
||||
"reorderItemRight": "Siirrä oikealle",
|
||||
"copyButtonLabel": "Kopioi",
|
||||
"cutButtonLabel": "Leikkaa",
|
||||
"lookUpButtonLabel": "Hae",
|
||||
"searchWebButtonLabel": "Hae verkosta",
|
||||
"shareButtonLabel": "Jaa",
|
||||
"pasteButtonLabel": "Liitä",
|
||||
"selectAllButtonLabel": "Valitse kaikki"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Ilipat pataas",
|
||||
"reorderItemDown": "Ilipat pababa",
|
||||
"reorderItemLeft": "Ilipat pakaliwa",
|
||||
"reorderItemRight": "Ilipat pakanan"
|
||||
"reorderItemRight": "Ilipat pakanan",
|
||||
"copyButtonLabel": "Kopyahin",
|
||||
"cutButtonLabel": "I-cut",
|
||||
"lookUpButtonLabel": "Tumingin sa Itaas",
|
||||
"searchWebButtonLabel": "Maghanap sa Web",
|
||||
"shareButtonLabel": "I-share",
|
||||
"pasteButtonLabel": "I-paste",
|
||||
"selectAllButtonLabel": "Piliin lahat"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Déplacer vers le haut",
|
||||
"reorderItemDown": "Déplacer vers le bas",
|
||||
"reorderItemLeft": "Déplacer vers la gauche",
|
||||
"reorderItemRight": "Déplacer vers la droite"
|
||||
"reorderItemRight": "Déplacer vers la droite",
|
||||
"copyButtonLabel": "Copier",
|
||||
"cutButtonLabel": "Couper",
|
||||
"lookUpButtonLabel": "Recherche visuelle",
|
||||
"searchWebButtonLabel": "Rechercher sur le Web",
|
||||
"shareButtonLabel": "Partager",
|
||||
"pasteButtonLabel": "Coller",
|
||||
"selectAllButtonLabel": "Tout sélectionner"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Déplacer vers le haut",
|
||||
"reorderItemDown": "Déplacer vers le bas",
|
||||
"reorderItemLeft": "Déplacer vers la gauche",
|
||||
"reorderItemRight": "Déplacer vers la droite"
|
||||
"reorderItemRight": "Déplacer vers la droite",
|
||||
"copyButtonLabel": "Copier",
|
||||
"cutButtonLabel": "Couper",
|
||||
"lookUpButtonLabel": "Regarder en haut",
|
||||
"searchWebButtonLabel": "Rechercher sur le Web",
|
||||
"shareButtonLabel": "Partager",
|
||||
"pasteButtonLabel": "Coller",
|
||||
"selectAllButtonLabel": "Tout sélectionner"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Mover cara arriba",
|
||||
"reorderItemDown": "Mover cara abaixo",
|
||||
"reorderItemLeft": "Mover cara á esquerda",
|
||||
"reorderItemRight": "Mover cara á dereita"
|
||||
"reorderItemRight": "Mover cara á dereita",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Mirar cara arriba",
|
||||
"searchWebButtonLabel": "Buscar na Web",
|
||||
"shareButtonLabel": "Compartir",
|
||||
"pasteButtonLabel": "Pegar",
|
||||
"selectAllButtonLabel": "Seleccionar todo"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Nach oben verschieben",
|
||||
"reorderItemDown": "Nach unten verschieben",
|
||||
"reorderItemLeft": "Nach links verschieben",
|
||||
"reorderItemRight": "Nach rechts verschieben"
|
||||
"reorderItemRight": "Nach rechts verschieben",
|
||||
"copyButtonLabel": "Kopieren",
|
||||
"cutButtonLabel": "Ausschneiden",
|
||||
"lookUpButtonLabel": "Nachschlagen",
|
||||
"searchWebButtonLabel": "Im Web suchen",
|
||||
"shareButtonLabel": "Teilen",
|
||||
"pasteButtonLabel": "Einsetzen",
|
||||
"selectAllButtonLabel": "Alle auswählen"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "ઉપર ખસેડો",
|
||||
"reorderItemDown": "નીચે ખસેડો",
|
||||
"reorderItemLeft": "ડાબે ખસેડો",
|
||||
"reorderItemRight": "જમણે ખસેડો"
|
||||
"reorderItemRight": "જમણે ખસેડો",
|
||||
"copyButtonLabel": "કૉપિ કરો",
|
||||
"cutButtonLabel": "કાપો",
|
||||
"lookUpButtonLabel": "શોધો",
|
||||
"searchWebButtonLabel": "વેબ પર શોધો",
|
||||
"shareButtonLabel": "શેર કરો",
|
||||
"pasteButtonLabel": "પેસ્ટ કરો",
|
||||
"selectAllButtonLabel": "બધા પસંદ કરો"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "העברה למעלה",
|
||||
"reorderItemDown": "העברה למטה",
|
||||
"reorderItemLeft": "העברה שמאלה",
|
||||
"reorderItemRight": "העברה ימינה"
|
||||
"reorderItemRight": "העברה ימינה",
|
||||
"copyButtonLabel": "העתקה",
|
||||
"cutButtonLabel": "גזירה",
|
||||
"lookUpButtonLabel": "חיפוש",
|
||||
"searchWebButtonLabel": "חיפוש באינטרנט",
|
||||
"shareButtonLabel": "שיתוף",
|
||||
"pasteButtonLabel": "הדבקה",
|
||||
"selectAllButtonLabel": "בחירת הכול"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "ऊपर ले जाएं",
|
||||
"reorderItemDown": "नीचे ले जाएं",
|
||||
"reorderItemLeft": "बाएं ले जाएं",
|
||||
"reorderItemRight": "दाएं ले जाएं"
|
||||
"reorderItemRight": "दाएं ले जाएं",
|
||||
"copyButtonLabel": "कॉपी करें",
|
||||
"cutButtonLabel": "काटें",
|
||||
"lookUpButtonLabel": "लुक अप बटन",
|
||||
"searchWebButtonLabel": "वेब पर खोजें",
|
||||
"shareButtonLabel": "शेयर करें",
|
||||
"pasteButtonLabel": "चिपकाएं",
|
||||
"selectAllButtonLabel": "सभी को चुनें"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Pomakni prema gore",
|
||||
"reorderItemDown": "Pomakni prema dolje",
|
||||
"reorderItemLeft": "Pomakni ulijevo",
|
||||
"reorderItemRight": "Pomakni udesno"
|
||||
"reorderItemRight": "Pomakni udesno",
|
||||
"copyButtonLabel": "Kopiraj",
|
||||
"cutButtonLabel": "Izreži",
|
||||
"lookUpButtonLabel": "Pogled prema gore",
|
||||
"searchWebButtonLabel": "Pretraži web",
|
||||
"shareButtonLabel": "Dijeli",
|
||||
"pasteButtonLabel": "Zalijepi",
|
||||
"selectAllButtonLabel": "Odaberi sve"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Áthelyezés felfelé",
|
||||
"reorderItemDown": "Áthelyezés lefelé",
|
||||
"reorderItemLeft": "Áthelyezés balra",
|
||||
"reorderItemRight": "Áthelyezés jobbra"
|
||||
"reorderItemRight": "Áthelyezés jobbra",
|
||||
"copyButtonLabel": "Másolás",
|
||||
"cutButtonLabel": "Kivágás",
|
||||
"lookUpButtonLabel": "Felfelé nézés",
|
||||
"searchWebButtonLabel": "Keresés az interneten",
|
||||
"shareButtonLabel": "Megosztás",
|
||||
"pasteButtonLabel": "Beillesztés",
|
||||
"selectAllButtonLabel": "Összes kijelölése"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Տեղափոխել վերև",
|
||||
"reorderItemDown": "Տեղափոխել ներքև",
|
||||
"reorderItemLeft": "Տեղափոխել ձախ",
|
||||
"reorderItemRight": "Տեղափոխել աջ"
|
||||
"reorderItemRight": "Տեղափոխել աջ",
|
||||
"copyButtonLabel": "Պատճենել",
|
||||
"cutButtonLabel": "Կտրել",
|
||||
"lookUpButtonLabel": "Փնտրել",
|
||||
"searchWebButtonLabel": "Որոնել համացանցում",
|
||||
"shareButtonLabel": "Կիսվել",
|
||||
"pasteButtonLabel": "Տեղադրել",
|
||||
"selectAllButtonLabel": "Նշել բոլորը"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Naikkan",
|
||||
"reorderItemDown": "Turunkan",
|
||||
"reorderItemLeft": "Pindahkan ke kiri",
|
||||
"reorderItemRight": "Pindahkan ke kanan"
|
||||
"reorderItemRight": "Pindahkan ke kanan",
|
||||
"copyButtonLabel": "Salin",
|
||||
"cutButtonLabel": "Potong",
|
||||
"lookUpButtonLabel": "Cari",
|
||||
"searchWebButtonLabel": "Telusuri di Web",
|
||||
"shareButtonLabel": "Bagikan",
|
||||
"pasteButtonLabel": "Tempel",
|
||||
"selectAllButtonLabel": "Pilih semua"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Færa upp",
|
||||
"reorderItemDown": "Færa niður",
|
||||
"reorderItemLeft": "Færa til vinstri",
|
||||
"reorderItemRight": "Færa til hægri"
|
||||
"reorderItemRight": "Færa til hægri",
|
||||
"copyButtonLabel": "Afrita",
|
||||
"cutButtonLabel": "Klippa",
|
||||
"lookUpButtonLabel": "Look Up",
|
||||
"searchWebButtonLabel": "Leita á vefnum",
|
||||
"shareButtonLabel": "Deila",
|
||||
"pasteButtonLabel": "Líma",
|
||||
"selectAllButtonLabel": "Velja allt"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Sposta su",
|
||||
"reorderItemDown": "Sposta giù",
|
||||
"reorderItemLeft": "Sposta a sinistra",
|
||||
"reorderItemRight": "Sposta a destra"
|
||||
"reorderItemRight": "Sposta a destra",
|
||||
"copyButtonLabel": "Copia",
|
||||
"cutButtonLabel": "Taglia",
|
||||
"lookUpButtonLabel": "Cerca",
|
||||
"searchWebButtonLabel": "Cerca sul web",
|
||||
"shareButtonLabel": "Condividi",
|
||||
"pasteButtonLabel": "Incolla",
|
||||
"selectAllButtonLabel": "Seleziona tutto"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "上に移動",
|
||||
"reorderItemDown": "下に移動",
|
||||
"reorderItemLeft": "左に移動",
|
||||
"reorderItemRight": "右に移動"
|
||||
"reorderItemRight": "右に移動",
|
||||
"copyButtonLabel": "コピー",
|
||||
"cutButtonLabel": "切り取り",
|
||||
"lookUpButtonLabel": "調べる",
|
||||
"searchWebButtonLabel": "ウェブを検索",
|
||||
"shareButtonLabel": "共有",
|
||||
"pasteButtonLabel": "貼り付け",
|
||||
"selectAllButtonLabel": "すべてを選択"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "ზემოთ გადატანა",
|
||||
"reorderItemDown": "ქვემოთ გადატანა",
|
||||
"reorderItemLeft": "მარცხნივ გადატანა",
|
||||
"reorderItemRight": "მარჯვნივ გადატანა"
|
||||
"reorderItemRight": "მარჯვნივ გადატანა",
|
||||
"copyButtonLabel": "კოპირება",
|
||||
"cutButtonLabel": "ამოჭრა",
|
||||
"lookUpButtonLabel": "აიხედეთ ზემოთ",
|
||||
"searchWebButtonLabel": "ვებში ძიება",
|
||||
"shareButtonLabel": "გაზიარება",
|
||||
"pasteButtonLabel": "ჩასმა",
|
||||
"selectAllButtonLabel": "ყველას არჩევა"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Жоғарыға жылжыту",
|
||||
"reorderItemDown": "Төменге жылжыту",
|
||||
"reorderItemLeft": "Солға жылжыту",
|
||||
"reorderItemRight": "Оңға жылжыту"
|
||||
"reorderItemRight": "Оңға жылжыту",
|
||||
"copyButtonLabel": "Көшіру",
|
||||
"cutButtonLabel": "Қию",
|
||||
"lookUpButtonLabel": "Іздеу",
|
||||
"searchWebButtonLabel": "Интернеттен іздеу",
|
||||
"shareButtonLabel": "Бөлісу",
|
||||
"pasteButtonLabel": "Қою",
|
||||
"selectAllButtonLabel": "Барлығын таңдау"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "ផ្លាស់ទីឡើងលើ",
|
||||
"reorderItemDown": "ផ្លាស់ទីចុះក្រោម",
|
||||
"reorderItemLeft": "ផ្លាស់ទីទៅឆ្វេង",
|
||||
"reorderItemRight": "ផ្លាស់ទីទៅស្តាំ"
|
||||
"reorderItemRight": "ផ្លាស់ទីទៅស្តាំ",
|
||||
"copyButtonLabel": "ចម្លង",
|
||||
"cutButtonLabel": "កាត់",
|
||||
"lookUpButtonLabel": "រកមើល",
|
||||
"searchWebButtonLabel": "ស្វែងរកលើបណ្ដាញ",
|
||||
"shareButtonLabel": "ចែករំលែក",
|
||||
"pasteButtonLabel": "ដាក់ចូល",
|
||||
"selectAllButtonLabel": "ជ្រើសរើសទាំងអស់"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "\u0cae\u0cc7\u0cb2\u0cc6\u0020\u0cb8\u0cb0\u0cbf\u0cb8\u0cbf",
|
||||
"reorderItemDown": "\u0c95\u0cc6\u0cb3\u0c97\u0cc6\u0020\u0cb8\u0cb0\u0cbf\u0cb8\u0cbf",
|
||||
"reorderItemLeft": "\u0c8e\u0ca1\u0c95\u0ccd\u0c95\u0cc6\u0020\u0cb8\u0cb0\u0cbf\u0cb8\u0cbf",
|
||||
"reorderItemRight": "\u0cac\u0cb2\u0c95\u0ccd\u0c95\u0cc6\u0020\u0cb8\u0cb0\u0cbf\u0cb8\u0cbf"
|
||||
"reorderItemRight": "\u0cac\u0cb2\u0c95\u0ccd\u0c95\u0cc6\u0020\u0cb8\u0cb0\u0cbf\u0cb8\u0cbf",
|
||||
"copyButtonLabel": "\u0ca8\u0c95\u0cb2\u0cbf\u0cb8\u0cbf",
|
||||
"cutButtonLabel": "\u0c95\u0ca4\u0ccd\u0ca4\u0cb0\u0cbf\u0cb8\u0cbf",
|
||||
"lookUpButtonLabel": "\u0cae\u0cc7\u0cb2\u0cc6\u0020\u0ca8\u0ccb\u0ca1\u0cbf",
|
||||
"searchWebButtonLabel": "\u0cb5\u0cc6\u0cac\u0ccd\u200c\u0ca8\u0cb2\u0ccd\u0cb2\u0cbf\u0020\u0cb9\u0cc1\u0ca1\u0cc1\u0c95\u0cbf",
|
||||
"shareButtonLabel": "\u0cb9\u0c82\u0c9a\u0cbf\u0c95\u0cca\u0cb3\u0ccd\u0cb3\u0cbf",
|
||||
"pasteButtonLabel": "\u0c85\u0c82\u0c9f\u0cbf\u0cb8\u0cbf",
|
||||
"selectAllButtonLabel": "\u0c8e\u0cb2\u0ccd\u0cb2\u0cb5\u0ca8\u0ccd\u0ca8\u0cc2\u0020\u0c86\u0caf\u0ccd\u0c95\u0cc6\u0020\u0cae\u0cbe\u0ca1\u0cbf"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "위로 이동",
|
||||
"reorderItemDown": "아래로 이동",
|
||||
"reorderItemLeft": "왼쪽으로 이동",
|
||||
"reorderItemRight": "오른쪽으로 이동"
|
||||
"reorderItemRight": "오른쪽으로 이동",
|
||||
"copyButtonLabel": "복사",
|
||||
"cutButtonLabel": "잘라내기",
|
||||
"lookUpButtonLabel": "찾기",
|
||||
"searchWebButtonLabel": "웹 검색",
|
||||
"shareButtonLabel": "공유",
|
||||
"pasteButtonLabel": "붙여넣기",
|
||||
"selectAllButtonLabel": "전체 선택"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Жогору жылдыруу",
|
||||
"reorderItemDown": "Төмөн жылдыруу",
|
||||
"reorderItemLeft": "Солго жылдыруу",
|
||||
"reorderItemRight": "Оңго жылдыруу"
|
||||
"reorderItemRight": "Оңго жылдыруу",
|
||||
"copyButtonLabel": "Көчүрүү",
|
||||
"cutButtonLabel": "Кесүү",
|
||||
"lookUpButtonLabel": "Издөө",
|
||||
"searchWebButtonLabel": "Интернеттен издөө",
|
||||
"shareButtonLabel": "Бөлүшүү",
|
||||
"pasteButtonLabel": "Чаптоо",
|
||||
"selectAllButtonLabel": "Баарын тандоо"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "ຍ້າຍຂຶ້ນ",
|
||||
"reorderItemDown": "ຍ້າຍລົງ",
|
||||
"reorderItemLeft": "ຍ້າຍໄປຊ້າຍ",
|
||||
"reorderItemRight": "ຍ້າຍໄປຂວາ"
|
||||
"reorderItemRight": "ຍ້າຍໄປຂວາ",
|
||||
"copyButtonLabel": "ສຳເນົາ",
|
||||
"cutButtonLabel": "ຕັດ",
|
||||
"lookUpButtonLabel": "ຊອກຫາຂໍ້ມູນ",
|
||||
"searchWebButtonLabel": "ຊອກຫາຢູ່ອິນເຕີເນັດ",
|
||||
"shareButtonLabel": "ແບ່ງປັນ",
|
||||
"pasteButtonLabel": "ວາງ",
|
||||
"selectAllButtonLabel": "ເລືອກທັງໝົດ"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Perkelti aukštyn",
|
||||
"reorderItemDown": "Perkelti žemyn",
|
||||
"reorderItemLeft": "Perkelti kairėn",
|
||||
"reorderItemRight": "Perkelti dešinėn"
|
||||
"reorderItemRight": "Perkelti dešinėn",
|
||||
"copyButtonLabel": "Kopijuoti",
|
||||
"cutButtonLabel": "Iškirpti",
|
||||
"lookUpButtonLabel": "Ieškoti",
|
||||
"searchWebButtonLabel": "Ieškoti žiniatinklyje",
|
||||
"shareButtonLabel": "Bendrinti",
|
||||
"pasteButtonLabel": "Įklijuoti",
|
||||
"selectAllButtonLabel": "Pasirinkti viską"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Pārvietot uz augšu",
|
||||
"reorderItemDown": "Pārvietot uz leju",
|
||||
"reorderItemLeft": "Pārvietot pa kreisi",
|
||||
"reorderItemRight": "Pārvietot pa labi"
|
||||
"reorderItemRight": "Pārvietot pa labi",
|
||||
"copyButtonLabel": "Kopēt",
|
||||
"cutButtonLabel": "Izgriezt",
|
||||
"lookUpButtonLabel": "Meklēt",
|
||||
"searchWebButtonLabel": "Meklēt tīmeklī",
|
||||
"shareButtonLabel": "Kopīgot",
|
||||
"pasteButtonLabel": "Ielīmēt",
|
||||
"selectAllButtonLabel": "Atlasīt visu"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Преместете нагоре",
|
||||
"reorderItemDown": "Преместете надолу",
|
||||
"reorderItemLeft": "Преместете налево",
|
||||
"reorderItemRight": "Преместете надесно"
|
||||
"reorderItemRight": "Преместете надесно",
|
||||
"copyButtonLabel": "Копирај",
|
||||
"cutButtonLabel": "Исечи",
|
||||
"lookUpButtonLabel": "Погледнете нагоре",
|
||||
"searchWebButtonLabel": "Пребарајте на интернет",
|
||||
"shareButtonLabel": "Сподели",
|
||||
"pasteButtonLabel": "Залепи",
|
||||
"selectAllButtonLabel": "Избери ги сите"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "മുകളിലോട്ട് നീക്കുക",
|
||||
"reorderItemDown": "താഴോട്ട് നീക്കുക",
|
||||
"reorderItemLeft": "ഇടത്തോട്ട് നീക്കുക",
|
||||
"reorderItemRight": "വലത്തോട്ട് നീക്കുക"
|
||||
"reorderItemRight": "വലത്തോട്ട് നീക്കുക",
|
||||
"copyButtonLabel": "പകർത്തുക",
|
||||
"cutButtonLabel": "മുറിക്കുക",
|
||||
"lookUpButtonLabel": "മുകളിലേക്ക് നോക്കുക",
|
||||
"searchWebButtonLabel": "വെബിൽ തിരയുക",
|
||||
"shareButtonLabel": "പങ്കിടുക",
|
||||
"pasteButtonLabel": "ഒട്ടിക്കുക",
|
||||
"selectAllButtonLabel": "എല്ലാം തിരഞ്ഞെടുക്കുക"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Дээш зөөх",
|
||||
"reorderItemDown": "Доош зөөх",
|
||||
"reorderItemLeft": "Зүүн тийш зөөх",
|
||||
"reorderItemRight": "Баруун тийш зөөх"
|
||||
"reorderItemRight": "Баруун тийш зөөх",
|
||||
"copyButtonLabel": "Хуулах",
|
||||
"cutButtonLabel": "Таслах",
|
||||
"lookUpButtonLabel": "Дээшээ харах",
|
||||
"searchWebButtonLabel": "Вебээс хайх",
|
||||
"shareButtonLabel": "Хуваалцах",
|
||||
"pasteButtonLabel": "Буулгах",
|
||||
"selectAllButtonLabel": "Бүгдийг сонгох"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "वर हलवा",
|
||||
"reorderItemDown": "खाली हलवा",
|
||||
"reorderItemLeft": "डावीकडे हलवा",
|
||||
"reorderItemRight": "उजवीकडे हलवा"
|
||||
"reorderItemRight": "उजवीकडे हलवा",
|
||||
"copyButtonLabel": "कॉपी करा",
|
||||
"cutButtonLabel": "कट करा",
|
||||
"lookUpButtonLabel": "शोध घ्या",
|
||||
"searchWebButtonLabel": "वेबवर शोधा",
|
||||
"shareButtonLabel": "शेअर करा",
|
||||
"pasteButtonLabel": "पेस्ट करा",
|
||||
"selectAllButtonLabel": "सर्व निवडा"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Alih ke atas",
|
||||
"reorderItemDown": "Alih ke bawah",
|
||||
"reorderItemLeft": "Alih ke kiri",
|
||||
"reorderItemRight": "Alih ke kanan"
|
||||
"reorderItemRight": "Alih ke kanan",
|
||||
"copyButtonLabel": "Salin",
|
||||
"cutButtonLabel": "Potong",
|
||||
"lookUpButtonLabel": "Lihat ke Atas",
|
||||
"searchWebButtonLabel": "Buat carian pada Web",
|
||||
"shareButtonLabel": "Kongsi",
|
||||
"pasteButtonLabel": "Tampal",
|
||||
"selectAllButtonLabel": "Pilih semua"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "အပေါ်သို့ ရွှေ့ရန်",
|
||||
"reorderItemDown": "အောက်သို့ရွှေ့ရန်",
|
||||
"reorderItemLeft": "ဘယ်ဘက်သို့ရွှေ့ရန်",
|
||||
"reorderItemRight": "ညာဘက်သို့ရွှေ့ရန်"
|
||||
"reorderItemRight": "ညာဘက်သို့ရွှေ့ရန်",
|
||||
"copyButtonLabel": "မိတ္တူကူးရန်",
|
||||
"cutButtonLabel": "ဖြတ်ယူရန်",
|
||||
"lookUpButtonLabel": "အပေါ်ကြည့်ရန်",
|
||||
"searchWebButtonLabel": "ဝဘ်တွင်ရှာရန်",
|
||||
"shareButtonLabel": "မျှဝေရန်",
|
||||
"pasteButtonLabel": "ကူးထည့်ရန်",
|
||||
"selectAllButtonLabel": "အားလုံး ရွေးရန်"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Flytt opp",
|
||||
"reorderItemDown": "Flytt ned",
|
||||
"reorderItemLeft": "Flytt til venstre",
|
||||
"reorderItemRight": "Flytt til høyre"
|
||||
"reorderItemRight": "Flytt til høyre",
|
||||
"copyButtonLabel": "Kopiér",
|
||||
"cutButtonLabel": "Klipp ut",
|
||||
"lookUpButtonLabel": "Slå opp",
|
||||
"searchWebButtonLabel": "Søk på nettet",
|
||||
"shareButtonLabel": "Del",
|
||||
"pasteButtonLabel": "Lim inn",
|
||||
"selectAllButtonLabel": "Velg alle"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "माथि सार्नुहोस्",
|
||||
"reorderItemDown": "तल सार्नुहोस्",
|
||||
"reorderItemLeft": "बायाँ सार्नुहोस्",
|
||||
"reorderItemRight": "दायाँ सार्नुहोस्"
|
||||
"reorderItemRight": "दायाँ सार्नुहोस्",
|
||||
"copyButtonLabel": "कपी गर्नुहोस्",
|
||||
"cutButtonLabel": "काट्नुहोस्",
|
||||
"lookUpButtonLabel": "माथितिर हेर्नुहोस्",
|
||||
"searchWebButtonLabel": "वेबमा खोज्नुहोस्",
|
||||
"shareButtonLabel": "सेयर गर्नुहोस्",
|
||||
"pasteButtonLabel": "टाँस्नुहोस्",
|
||||
"selectAllButtonLabel": "सबै बटनहरू चयन गर्नुहोस्"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Omhoog verplaatsen",
|
||||
"reorderItemDown": "Omlaag verplaatsen",
|
||||
"reorderItemLeft": "Naar links verplaatsen",
|
||||
"reorderItemRight": "Naar rechts verplaatsen"
|
||||
"reorderItemRight": "Naar rechts verplaatsen",
|
||||
"copyButtonLabel": "Kopiëren",
|
||||
"cutButtonLabel": "Knippen",
|
||||
"lookUpButtonLabel": "Opzoeken",
|
||||
"searchWebButtonLabel": "Op internet zoeken",
|
||||
"shareButtonLabel": "Delen",
|
||||
"pasteButtonLabel": "Plakken",
|
||||
"selectAllButtonLabel": "Alles selecteren"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Flytt opp",
|
||||
"reorderItemDown": "Flytt ned",
|
||||
"reorderItemLeft": "Flytt til venstre",
|
||||
"reorderItemRight": "Flytt til høyre"
|
||||
"reorderItemRight": "Flytt til høyre",
|
||||
"copyButtonLabel": "Kopiér",
|
||||
"cutButtonLabel": "Klipp ut",
|
||||
"lookUpButtonLabel": "Slå opp",
|
||||
"searchWebButtonLabel": "Søk på nettet",
|
||||
"shareButtonLabel": "Del",
|
||||
"pasteButtonLabel": "Lim inn",
|
||||
"selectAllButtonLabel": "Velg alle"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "ଉପରକୁ ନିଅନ୍ତୁ",
|
||||
"reorderItemDown": "ତଳକୁ ଯାଆନ୍ତୁ",
|
||||
"reorderItemLeft": "ବାମକୁ ଯାଆନ୍ତୁ",
|
||||
"reorderItemRight": "ଡାହାଣକୁ ଯାଆନ୍ତୁ"
|
||||
"reorderItemRight": "ଡାହାଣକୁ ଯାଆନ୍ତୁ",
|
||||
"copyButtonLabel": "କପି କରନ୍ତୁ",
|
||||
"cutButtonLabel": "କଟ୍ କରନ୍ତୁ",
|
||||
"lookUpButtonLabel": "ଉପରକୁ ଦେଖନ୍ତୁ",
|
||||
"searchWebButtonLabel": "ୱେବ ସର୍ଚ୍ଚ କରନ୍ତୁ",
|
||||
"shareButtonLabel": "ସେୟାର କରନ୍ତୁ",
|
||||
"pasteButtonLabel": "ପେଷ୍ଟ କରନ୍ତୁ",
|
||||
"selectAllButtonLabel": "ସବୁ ଚୟନ କରନ୍ତୁ"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "ਉੱਪਰ ਲਿਜਾਓ",
|
||||
"reorderItemDown": "ਹੇਠਾਂ ਲਿਜਾਓ",
|
||||
"reorderItemLeft": "ਖੱਬੇ ਲਿਜਾਓ",
|
||||
"reorderItemRight": "ਸੱਜੇ ਲਿਜਾਓ"
|
||||
"reorderItemRight": "ਸੱਜੇ ਲਿਜਾਓ",
|
||||
"copyButtonLabel": "ਕਾਪੀ ਕਰੋ",
|
||||
"cutButtonLabel": "ਕੱਟ ਕਰੋ",
|
||||
"lookUpButtonLabel": "ਖੋਜੋ",
|
||||
"searchWebButtonLabel": "ਵੈੱਬ 'ਤੇ ਖੋਜੋ",
|
||||
"shareButtonLabel": "ਸਾਂਝਾ ਕਰੋ",
|
||||
"pasteButtonLabel": "ਪੇਸਟ ਕਰੋ",
|
||||
"selectAllButtonLabel": "ਸਭ ਚੁਣੋ"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Przenieś w górę",
|
||||
"reorderItemDown": "Przenieś w dół",
|
||||
"reorderItemLeft": "Przenieś w lewo",
|
||||
"reorderItemRight": "Przenieś w prawo"
|
||||
"reorderItemRight": "Przenieś w prawo",
|
||||
"copyButtonLabel": "Kopiuj",
|
||||
"cutButtonLabel": "Wytnij",
|
||||
"lookUpButtonLabel": "Sprawdź",
|
||||
"searchWebButtonLabel": "Szukaj w internecie",
|
||||
"shareButtonLabel": "Udostępnij",
|
||||
"pasteButtonLabel": "Wklej",
|
||||
"selectAllButtonLabel": "Zaznacz wszystko"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Move up",
|
||||
"reorderItemDown": "Move down",
|
||||
"reorderItemLeft": "Move left",
|
||||
"reorderItemRight": "Move right"
|
||||
"reorderItemRight": "Move right",
|
||||
"copyButtonLabel": "کاپی",
|
||||
"cutButtonLabel": "کم کړئ",
|
||||
"lookUpButtonLabel": "Look Up",
|
||||
"searchWebButtonLabel": "Search Web",
|
||||
"shareButtonLabel": "Share...",
|
||||
"pasteButtonLabel": "پیټ کړئ",
|
||||
"selectAllButtonLabel": "غوره کړئ"
|
||||
}
|
||||
|
@ -4,5 +4,12 @@
|
||||
"reorderItemUp": "Mover para cima",
|
||||
"reorderItemDown": "Mover para baixo",
|
||||
"reorderItemLeft": "Mover para a esquerda",
|
||||
"reorderItemRight": "Mover para a direita"
|
||||
"reorderItemRight": "Mover para a direita",
|
||||
"copyButtonLabel": "Copiar",
|
||||
"cutButtonLabel": "Cortar",
|
||||
"lookUpButtonLabel": "Pesquisar",
|
||||
"searchWebButtonLabel": "Pesquisar na Web",
|
||||
"shareButtonLabel": "Compartilhar",
|
||||
"pasteButtonLabel": "Colar",
|
||||
"selectAllButtonLabel": "Selecionar tudo"
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user