@@ -802,6 +802,7 @@ @interface FlutterTextInputView ()
802
802
// etc)
803
803
@property (nonatomic , copy ) NSString * temporarilyDeletedComposedCharacter;
804
804
@property (nonatomic , assign ) CGRect editMenuTargetRect;
805
+ @property (nonatomic , strong ) NSArray <NSDictionary*>* editMenuItems;
805
806
806
807
- (void )setEditableTransform : (NSArray *)matrix ;
807
808
@end
@@ -875,10 +876,123 @@ - (instancetype)initWithOwner:(FlutterTextInputPlugin*)textInputPlugin {
875
876
return self;
876
877
}
877
878
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
+
878
942
- (UIMenu*)editMenuInteraction : (UIEditMenuInteraction*)interaction
879
943
menuForConfiguration : (UIEditMenuConfiguration*)configuration
880
944
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];
882
996
}
883
997
884
998
- (void )editMenuInteraction : (UIEditMenuInteraction*)interaction
@@ -894,8 +1008,10 @@ - (CGRect)editMenuInteraction:(UIEditMenuInteraction*)interaction
894
1008
return _editMenuTargetRect;
895
1009
}
896
1010
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 )) {
898
1013
_editMenuTargetRect = targetRect;
1014
+ _editMenuItems = items;
899
1015
UIEditMenuConfiguration* config =
900
1016
[UIEditMenuConfiguration configurationWithIdentifier: nil sourcePoint: CGPointZero];
901
1017
[self .editMenuInteraction presentEditMenuWithConfiguration: config];
@@ -2574,7 +2690,7 @@ - (BOOL)showEditMenu:(NSDictionary*)args API_AVAILABLE(ios(16.0)) {
2574
2690
[encodedTargetRect[@" x" ] doubleValue ], [encodedTargetRect[@" y" ] doubleValue ],
2575
2691
[encodedTargetRect[@" width" ] doubleValue ], [encodedTargetRect[@" height" ] doubleValue ]);
2576
2692
CGRect localTargetRect = [self .hostView convertRect: globalTargetRect toView: self .activeView];
2577
- [self .activeView showEditMenuWithTargetRect: localTargetRect];
2693
+ [self .activeView showEditMenuWithTargetRect: localTargetRect items: args[ @" items " ] ];
2578
2694
return YES ;
2579
2695
}
2580
2696
0 commit comments