@@ -802,6 +802,7 @@ @interface FlutterTextInputView ()
802802// etc)
803803@property (nonatomic , copy ) NSString * temporarilyDeletedComposedCharacter;
804804@property (nonatomic , assign ) CGRect editMenuTargetRect;
805+ @property (nonatomic , strong ) NSArray <NSDictionary*>* editMenuItems;
805806
806807- (void )setEditableTransform : (NSArray *)matrix ;
807808@end
@@ -875,10 +876,123 @@ - (instancetype)initWithOwner:(FlutterTextInputPlugin*)textInputPlugin {
875876 return self;
876877}
877878
879+ - (void )handleSearchWebAction {
880+ [self .textInputDelegate flutterTextInputView: self
881+ searchWebWithSelectedText: [self textInRange: _selectedTextRange]];
882+ }
883+
884+ - (void )handleLookUpAction {
885+ [self .textInputDelegate flutterTextInputView: self
886+ lookUpSelectedText: [self textInRange: _selectedTextRange]];
887+ }
888+
889+ - (void )handleShareAction {
890+ [self .textInputDelegate flutterTextInputView: self
891+ shareSelectedText: [self textInRange: _selectedTextRange]];
892+ }
893+
894+ // DFS algorithm to search a UICommand from the menu tree.
895+ - (UICommand*)searchCommandWithSelector : (SEL )selector
896+ element : (UIMenuElement*)element API_AVAILABLE(ios(16.0 )) {
897+ if ([element isKindOfClass: UICommand.class ]) {
898+ UICommand* command = (UICommand*)element;
899+ return command.action == selector ? command : nil ;
900+ } else if ([element isKindOfClass: UIMenu.class ]) {
901+ NSArray <UIMenuElement*>* children = ((UIMenu*)element).children ;
902+ for (UIMenuElement* child in children) {
903+ UICommand* result = [self searchCommandWithSelector: selector element: child];
904+ if (result) {
905+ return result;
906+ }
907+ }
908+ return nil ;
909+ } else {
910+ return nil ;
911+ }
912+ }
913+
914+ - (void )addBasicEditingCommandToItems : (NSMutableArray *)items
915+ type : (NSString *)type
916+ selector : (SEL )selector
917+ suggestedMenu : (UIMenu*)suggestedMenu {
918+ UICommand* command = [self searchCommandWithSelector: selector element: suggestedMenu];
919+ if (command) {
920+ [items addObject: command];
921+ } else {
922+ FML_LOG (ERROR) << " Cannot find context menu item of type \" " << type.UTF8String << " \" ." ;
923+ }
924+ }
925+
926+ - (void )addAdditionalBasicCommandToItems : (NSMutableArray *)items
927+ type : (NSString *)type
928+ selector : (SEL )selector
929+ encodedItem : (NSDictionary <NSString*, id>*)encodedItem {
930+ NSString * title = encodedItem[@" title" ];
931+ if (title) {
932+ UICommand* command = [UICommand commandWithTitle: title
933+ image: nil
934+ action: selector
935+ propertyList: nil ];
936+ [items addObject: command];
937+ } else {
938+ FML_LOG (ERROR) << " Missing title for context menu item of type \" " << type.UTF8String << " \" ." ;
939+ }
940+ }
941+
878942- (UIMenu*)editMenuInteraction : (UIEditMenuInteraction*)interaction
879943 menuForConfiguration : (UIEditMenuConfiguration*)configuration
880944 suggestedActions : (NSArray <UIMenuElement*>*)suggestedActions API_AVAILABLE(ios(16.0 )) {
881- return [UIMenu menuWithChildren: suggestedActions];
945+ UIMenu* suggestedMenu = [UIMenu menuWithChildren: suggestedActions];
946+ if (!_editMenuItems) {
947+ return suggestedMenu;
948+ }
949+
950+ NSMutableArray * items = [NSMutableArray array ];
951+ for (NSDictionary <NSString *, id >* encodedItem in _editMenuItems) {
952+ NSString * type = encodedItem[@" type" ];
953+ if ([type isEqualToString: @" copy" ]) {
954+ [self addBasicEditingCommandToItems: items
955+ type: type
956+ selector: @selector (copy: )
957+ suggestedMenu: suggestedMenu];
958+ } else if ([type isEqualToString: @" paste" ]) {
959+ [self addBasicEditingCommandToItems: items
960+ type: type
961+ selector: @selector (paste: )
962+ suggestedMenu: suggestedMenu];
963+ } else if ([type isEqualToString: @" cut" ]) {
964+ [self addBasicEditingCommandToItems: items
965+ type: type
966+ selector: @selector (cut: )
967+ suggestedMenu: suggestedMenu];
968+ } else if ([type isEqualToString: @" delete" ]) {
969+ [self addBasicEditingCommandToItems: items
970+ type: type
971+ selector: @selector (delete: )
972+ suggestedMenu: suggestedMenu];
973+ } else if ([type isEqualToString: @" selectAll" ]) {
974+ [self addBasicEditingCommandToItems: items
975+ type: type
976+ selector: @selector (selectAll: )
977+ suggestedMenu: suggestedMenu];
978+ } else if ([type isEqualToString: @" searchWeb" ]) {
979+ [self addAdditionalBasicCommandToItems: items
980+ type: type
981+ selector: @selector (handleSearchWebAction )
982+ encodedItem: encodedItem];
983+ } else if ([type isEqualToString: @" share" ]) {
984+ [self addAdditionalBasicCommandToItems: items
985+ type: type
986+ selector: @selector (handleShareAction )
987+ encodedItem: encodedItem];
988+ } else if ([type isEqualToString: @" lookUp" ]) {
989+ [self addAdditionalBasicCommandToItems: items
990+ type: type
991+ selector: @selector (handleLookUpAction )
992+ encodedItem: encodedItem];
993+ }
994+ }
995+ return [UIMenu menuWithChildren: items];
882996}
883997
884998- (void )editMenuInteraction : (UIEditMenuInteraction*)interaction
@@ -894,8 +1008,10 @@ - (CGRect)editMenuInteraction:(UIEditMenuInteraction*)interaction
8941008 return _editMenuTargetRect;
8951009}
8961010
897- - (void )showEditMenuWithTargetRect : (CGRect)targetRect API_AVAILABLE(ios(16.0 )) {
1011+ - (void )showEditMenuWithTargetRect : (CGRect)targetRect
1012+ items : (NSArray <NSDictionary*>*)items API_AVAILABLE(ios(16.0 )) {
8981013 _editMenuTargetRect = targetRect;
1014+ _editMenuItems = items;
8991015 UIEditMenuConfiguration* config =
9001016 [UIEditMenuConfiguration configurationWithIdentifier: nil sourcePoint: CGPointZero];
9011017 [self .editMenuInteraction presentEditMenuWithConfiguration: config];
@@ -2574,7 +2690,7 @@ - (BOOL)showEditMenu:(NSDictionary*)args API_AVAILABLE(ios(16.0)) {
25742690 [encodedTargetRect[@" x" ] doubleValue ], [encodedTargetRect[@" y" ] doubleValue ],
25752691 [encodedTargetRect[@" width" ] doubleValue ], [encodedTargetRect[@" height" ] doubleValue ]);
25762692 CGRect localTargetRect = [self .hostView convertRect: globalTargetRect toView: self .activeView];
2577- [self .activeView showEditMenuWithTargetRect: localTargetRect];
2693+ [self .activeView showEditMenuWithTargetRect: localTargetRect items: args[ @" items " ] ];
25782694 return YES ;
25792695}
25802696
0 commit comments