| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #import "ios/chrome/browser/ui/menu/action_factory.h" |
| |
| #import "base/check.h" |
| #import "base/metrics/histogram_functions.h" |
| #import "base/metrics/user_metrics.h" |
| #import "base/strings/sys_string_conversions.h" |
| #import "ios/chrome/browser/net/model/crurl.h" |
| #import "ios/chrome/browser/shared/model/web_state_list/tab_group.h" |
| #import "ios/chrome/browser/shared/public/commands/application_commands.h" |
| #import "ios/chrome/browser/shared/public/commands/command_dispatcher.h" |
| #import "ios/chrome/browser/shared/public/features/features.h" |
| #import "ios/chrome/browser/shared/ui/symbols/symbols.h" |
| #import "ios/chrome/browser/shared/ui/util/pasteboard_util.h" |
| #import "ios/chrome/browser/signin/model/system_identity.h" |
| #import "ios/chrome/browser/ui/menu/menu_action_type.h" |
| #import "ios/chrome/grit/ios_strings.h" |
| #import "ios/public/provider/chrome/browser/context_menu/context_menu_api.h" |
| #import "ui/base/l10n/l10n_util_mac.h" |
| #import "url/gurl.h" |
| |
| @interface ActionFactory () |
| |
| // Histogram to record executed actions. |
| @property(nonatomic, assign) const char* histogram; |
| |
| @end |
| |
| @implementation ActionFactory |
| |
| - (instancetype)initWithScenario:(MenuScenarioHistogram)scenario { |
| if ((self = [super init])) { |
| _histogram = GetActionsHistogramName(scenario); |
| } |
| return self; |
| } |
| |
| - (UIAction*)actionWithTitle:(NSString*)title |
| image:(UIImage*)image |
| type:(MenuActionType)type |
| block:(ProceduralBlock)block { |
| // Capture only the histogram name's pointer to be copied by the block. |
| const char* histogram = self.histogram; |
| return [UIAction actionWithTitle:title |
| image:image |
| identifier:nil |
| handler:^(UIAction* action) { |
| base::UmaHistogramEnumeration(histogram, type); |
| if (block) { |
| block(); |
| } |
| }]; |
| } |
| |
| - (UIAction*)actionToCopyURL:(CrURL*)URL { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kLinkActionSymbol, kSymbolActionPointSize); |
| return [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_COPY_LINK_ACTION_TITLE) |
| image:image |
| type:MenuActionType::CopyURL |
| block:^{ |
| StoreURLInPasteboard(URL.gurl); |
| }]; |
| } |
| |
| - (UIAction*)actionToShowFullURL:(NSString*)URLString |
| block:(ProceduralBlock)block { |
| UIAction* action = [self actionWithTitle:nil |
| image:nil |
| type:MenuActionType::ShowFullURL |
| block:block]; |
| action.accessibilityLabel = |
| l10n_util::GetNSString(IDS_IOS_SHARE_FULL_URL_BUTTON_ACCESSIBILITY_LABEL); |
| action.attributes = UIMenuElementAttributesKeepsMenuPresented; |
| action.subtitle = ios::provider::StyledContextMenuStringForString(URLString); |
| return action; |
| } |
| |
| - (UIAction*)actionToShareWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kShareSymbol, kSymbolActionPointSize); |
| return |
| [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_SHARE_BUTTON_LABEL) |
| image:image |
| type:MenuActionType::Share |
| block:block]; |
| } |
| |
| - (UIAction*)actionToPinTabWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kPinSymbol, kSymbolActionPointSize); |
| return [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_PINTAB) |
| image:image |
| type:MenuActionType::PinTab |
| block:block]; |
| } |
| |
| - (UIAction*)actionToUnpinTabWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kPinSlashSymbol, kSymbolActionPointSize); |
| return [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_UNPINTAB) |
| image:image |
| type:MenuActionType::UnpinTab |
| block:block]; |
| } |
| |
| - (UIAction*)actionToDeleteWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kDeleteActionSymbol, kSymbolActionPointSize); |
| UIAction* action = |
| [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_DELETE_ACTION_TITLE) |
| image:image |
| type:MenuActionType::Delete |
| block:block]; |
| action.attributes = UIMenuElementAttributesDestructive; |
| return action; |
| } |
| |
| - (UIAction*)actionToOpenInNewTabWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kNewTabActionSymbol, kSymbolActionPointSize); |
| ProceduralBlock completionBlock = |
| [self recordMobileWebContextMenuOpenTabActionWithBlock:block]; |
| |
| return [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_OPENLINKNEWTAB) |
| image:image |
| type:MenuActionType::OpenInNewTab |
| block:completionBlock]; |
| } |
| |
| - (UIAction*)actionToOpenAllTabsWithBlock:(ProceduralBlock)block { |
| return [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_OPEN_ALL_LINKS) |
| image:DefaultSymbolWithPointSize( |
| kPlusSymbol, kSymbolActionPointSize) |
| type:MenuActionType::OpenAllInNewTabs |
| block:block]; |
| } |
| |
| - (UIAction*)actionToRemoveWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kHideActionSymbol, kSymbolActionPointSize); |
| UIAction* action = |
| [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_REMOVE_ACTION_TITLE) |
| image:image |
| type:MenuActionType::Remove |
| block:block]; |
| action.attributes = UIMenuElementAttributesDestructive; |
| return action; |
| } |
| |
| - (UIAction*)actionToEditWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kEditActionSymbol, kSymbolActionPointSize); |
| return [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_EDIT_ACTION_TITLE) |
| image:image |
| type:MenuActionType::Edit |
| block:block]; |
| } |
| |
| - (UIAction*)actionToHideWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kHideActionSymbol, kSymbolActionPointSize); |
| UIAction* action = |
| [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_RECENT_TABS_HIDE_MENU_OPTION) |
| image:image |
| type:MenuActionType::Hide |
| block:block]; |
| action.attributes = UIMenuElementAttributesDestructive; |
| return action; |
| } |
| |
| - (UIAction*)actionToMoveFolderWithBlock:(ProceduralBlock)block { |
| // Use multi color to make sure the arrow is visible. |
| UIImage* image = MakeSymbolMulticolor( |
| CustomSymbolWithPointSize(kMoveFolderSymbol, kSymbolActionPointSize)); |
| return [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_CONTEXT_MENU_MOVE) |
| image:image |
| type:MenuActionType::Move |
| block:block]; |
| } |
| |
| - (UIAction*)actionToMarkAsReadWithBlock:(ProceduralBlock)block { |
| UIImage* image = DefaultSymbolWithPointSize(kMarkAsReadActionSymbol, |
| kSymbolActionPointSize); |
| return [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_READING_LIST_MARK_AS_READ_ACTION) |
| image:image |
| type:MenuActionType::Read |
| block:block]; |
| } |
| |
| - (UIAction*)actionToMarkAsUnreadWithBlock:(ProceduralBlock)block { |
| UIImage* image = DefaultSymbolWithPointSize(kMarkAsUnreadActionSymbol, |
| kSymbolActionPointSize); |
| return [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_READING_LIST_MARK_AS_UNREAD_ACTION) |
| image:image |
| type:MenuActionType::Unread |
| block:block]; |
| } |
| |
| - (UIAction*)actionToOpenOfflineVersionInNewTabWithBlock: |
| (ProceduralBlock)block { |
| UIImage* image = DefaultSymbolWithPointSize(kCheckmarkCircleSymbol, |
| kSymbolActionPointSize); |
| ProceduralBlock completionBlock = |
| [self recordMobileWebContextMenuOpenTabActionWithBlock:block]; |
| |
| return [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_READING_LIST_OPEN_OFFLINE_BUTTON) |
| image:image |
| type:MenuActionType::ViewOffline |
| block:completionBlock]; |
| } |
| |
| - (UIAction*)actionToAddToReadingListWithBlock:(ProceduralBlock)block { |
| UIImage* image = DefaultSymbolWithPointSize(kReadLaterActionSymbol, |
| kSymbolActionPointSize); |
| return [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_ADDTOREADINGLIST) |
| image:image |
| type:MenuActionType::AddToReadingList |
| block:block]; |
| } |
| |
| - (UIAction*)actionToBookmarkWithBlock:(ProceduralBlock)block { |
| UIImage* image = DefaultSymbolWithPointSize(kAddBookmarkActionSymbol, |
| kSymbolActionPointSize); |
| return [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_ADDTOBOOKMARKS) |
| image:image |
| type:MenuActionType::AddToBookmarks |
| block:block]; |
| } |
| |
| - (UIAction*)actionToEditBookmarkWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kEditActionSymbol, kSymbolActionPointSize); |
| return [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_CONTEXT_MENU_EDIT) |
| image:image |
| type:MenuActionType::EditBookmark |
| block:block]; |
| } |
| |
| - (UIAction*)actionToCloseRegularTabWithBlock:(ProceduralBlock)block { |
| NSString* title = l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_CLOSETAB); |
| return [self actionToCloseTabWithTitle:title block:block]; |
| } |
| |
| - (UIAction*)actionToClosePinnedTabWithBlock:(ProceduralBlock)block { |
| NSString* title = |
| l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_CLOSEPINNEDTAB); |
| return [self actionToCloseTabWithTitle:title block:block]; |
| } |
| |
| - (UIAction*)actionToCloseAllOtherTabsWithBlock:(ProceduralBlock)block { |
| NSString* title = |
| l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_CLOSEOTHERTABS); |
| UIImage* image = |
| DefaultSymbolWithPointSize(kXMarkSymbol, kSymbolActionPointSize); |
| UIAction* action = [self actionWithTitle:title |
| image:image |
| type:MenuActionType::CloseAllOtherTabs |
| block:block]; |
| action.attributes = UIMenuElementAttributesDestructive; |
| return action; |
| } |
| |
| - (UIAction*)actionSaveImageWithBlock:(ProceduralBlock)block { |
| UIImage* image = DefaultSymbolWithPointSize(kSaveImageActionSymbol, |
| kSymbolActionPointSize); |
| UIAction* action = [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_SAVEIMAGE) |
| image:image |
| type:MenuActionType::SaveImage |
| block:block]; |
| return action; |
| } |
| |
| - (UIAction*)actionCopyImageWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kCopyActionSymbol, kSymbolActionPointSize); |
| UIAction* action = [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_COPYIMAGE) |
| image:image |
| type:MenuActionType::CopyImage |
| block:block]; |
| return action; |
| } |
| |
| - (UIAction*)actionSearchImageWithTitle:(NSString*)title |
| Block:(ProceduralBlock)block { |
| UIImage* image = CustomSymbolWithPointSize(kPhotoBadgeMagnifyingglassSymbol, |
| kSymbolActionPointSize); |
| UIAction* action = [self actionWithTitle:title |
| image:image |
| type:MenuActionType::SearchImage |
| block:block]; |
| return action; |
| } |
| |
| - (UIAction*)actionToCloseAllTabsWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kXMarkSymbol, kSymbolActionPointSize); |
| int titleID = IsTabGroupSyncEnabled() |
| ? IDS_IOS_CONTENT_CONTEXT_CLOSEALLTABSANDGROUPS |
| : IDS_IOS_CONTENT_CONTEXT_CLOSEALLTABS; |
| UIAction* action = [self actionWithTitle:l10n_util::GetNSString(titleID) |
| image:image |
| type:MenuActionType::CloseAllTabs |
| block:block]; |
| action.attributes = UIMenuElementAttributesDestructive; |
| return action; |
| } |
| |
| - (UIAction*)actionToSelectTabsWithBlock:(ProceduralBlock)block { |
| UIImage* image = DefaultSymbolWithPointSize(kCheckmarkCircleSymbol, |
| kSymbolActionPointSize); |
| UIAction* action = [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_SELECTTABS) |
| image:image |
| type:MenuActionType::SelectTabs |
| block:block]; |
| return action; |
| } |
| |
| - (UIAction*)actionToSearchImageUsingLensWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| CustomSymbolWithPointSize(kCameraLensSymbol, kSymbolActionPointSize); |
| UIAction* action = |
| [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_CONTEXT_MENU_SEARCHIMAGEWITHGOOGLE) |
| image:image |
| type:MenuActionType::SearchImageWithLens |
| block:block]; |
| return action; |
| } |
| |
| - (ProceduralBlock)recordMobileWebContextMenuOpenTabActionWithBlock: |
| (ProceduralBlock)block { |
| return ^{ |
| base::RecordAction(base::UserMetricsAction("MobileWebContextMenuOpenTab")); |
| if (block) { |
| block(); |
| } |
| }; |
| } |
| |
| - (UIAction*)actionToAddTabsToNewGroupWithTabsNumber:(int)tabsNumber |
| inSubmenu:(BOOL)inSubmenu |
| block:(ProceduralBlock)block { |
| CHECK(IsTabGroupInGridEnabled()) |
| << "You should not be able to create a tab group context menu action " |
| "outside the Tab Groups experiment."; |
| UIImage* image = DefaultSymbolWithPointSize(kNewTabGroupActionSymbol, |
| kSymbolActionPointSize); |
| NSString* title = |
| inSubmenu ? l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_ADDTABTONEWTABGROUP_SUBMENU) |
| : l10n_util::GetPluralNSStringF( |
| IDS_IOS_CONTENT_CONTEXT_ADDTABTONEWTABGROUP, tabsNumber); |
| UIAction* action = [self actionWithTitle:title |
| image:image |
| type:MenuActionType::AddTabToNewGroup |
| block:block]; |
| return action; |
| } |
| |
| - (UIAction*)actionToOpenLinkInNewGroupWithBlock:(ProceduralBlock)block |
| inSubmenu:(BOOL)inSubmenu { |
| UIImage* image = DefaultSymbolWithPointSize(kNewTabGroupActionSymbol, |
| kSymbolActionPointSize); |
| NSString* title = |
| inSubmenu ? l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_ADDTABTONEWTABGROUP_SUBMENU) |
| : l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_OPENLINKINNEWTABGROUP); |
| UIAction* action = [self actionWithTitle:title |
| image:image |
| type:MenuActionType::OpenLinkInNewGroup |
| block:block]; |
| return action; |
| } |
| |
| - (UIMenuElement*) |
| menuToAddTabToGroupWithGroups:(const std::set<const TabGroup*>&)groups |
| numberOfTabs:(int)tabsNumber |
| block:(void (^)(const TabGroup*))block { |
| CHECK(IsTabGroupInGridEnabled()) |
| << "You should not be able to create a tab group context menu action " |
| "outside the Tab Groups experiment."; |
| |
| if (groups.size() == 0) { |
| ProceduralBlock addTabToNewGroupBlock = ^{ |
| if (block) { |
| block(nil); |
| } |
| }; |
| return [self actionToAddTabsToNewGroupWithTabsNumber:tabsNumber |
| inSubmenu:NO |
| block:addTabToNewGroupBlock]; |
| } |
| |
| NSArray<UIMenuElement*>* groupsMenu = [self groupsMenuForGroups:groups |
| currentGroup:nil |
| block:block]; |
| UIMenu* menu = [UIMenu menuWithTitle:@"" |
| image:nil |
| identifier:nil |
| options:UIMenuOptionsDisplayInline |
| children:groupsMenu]; |
| ProceduralBlock addTabToNewGroupBlock = ^{ |
| if (block) { |
| block(nil); |
| } |
| }; |
| NSArray<UIMenuElement*>* addToGroupMenuElements = @[ |
| [self actionToAddTabsToNewGroupWithTabsNumber:tabsNumber |
| inSubmenu:YES |
| block:addTabToNewGroupBlock], |
| menu |
| ]; |
| |
| UIImage* image = DefaultSymbolWithPointSize(kMoveTabToGroupActionSymbol, |
| kSymbolActionPointSize); |
| |
| return [UIMenu |
| menuWithTitle:l10n_util::GetPluralNSStringF( |
| IDS_IOS_CONTENT_CONTEXT_ADDTABTOTABGROUP, tabsNumber) |
| image:image |
| identifier:nil |
| options:UIMenuOptionsSingleSelection |
| children:addToGroupMenuElements]; |
| } |
| |
| - (UIMenuElement*) |
| menuToMoveTabToGroupWithGroups:(const std::set<const TabGroup*>&)groups |
| currentGroup:(const TabGroup*)currentGroup |
| moveBlock:(void (^)(const TabGroup*))moveBlock |
| removeBlock:(ProceduralBlock)removeBlock { |
| CHECK(IsTabGroupInGridEnabled()) |
| << "You should not be able to create a tab group context menu action " |
| "outside the Tab Groups experiment."; |
| |
| if (groups.size() == 0) { |
| NOTREACHED() << "Groups cannot be empty."; |
| } |
| |
| NSArray<UIMenuElement*>* groupsMenu = [self groupsMenuForGroups:groups |
| currentGroup:currentGroup |
| block:moveBlock]; |
| UIMenu* menu = [UIMenu menuWithTitle:@"" |
| image:nil |
| identifier:nil |
| options:UIMenuOptionsDisplayInline |
| children:groupsMenu]; |
| NSArray<UIMenuElement*>* moveTabFromGroupMenuElements = |
| @[ [self actionToRemoveTabFromGroup:removeBlock], menu ]; |
| |
| UIImage* image = DefaultSymbolWithPointSize(kMoveTabToGroupActionSymbol, |
| kSymbolActionPointSize); |
| return [UIMenu menuWithTitle:l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_MOVETABTOGROUP) |
| image:image |
| identifier:nil |
| options:UIMenuOptionsSingleSelection |
| children:moveTabFromGroupMenuElements]; |
| } |
| |
| - (UIMenuElement*) |
| menuToOpenLinkInGroupWithGroups:(const std::set<const TabGroup*>&)groups |
| block:(void (^)(const TabGroup*))block { |
| CHECK(IsTabGroupInGridEnabled()) |
| << "You should not be able to create a tab group context menu action " |
| "outside the Tab Groups experiment."; |
| |
| if (groups.size() == 0) { |
| ProceduralBlock openInNewGroupBlock = ^{ |
| if (block) { |
| block(nil); |
| } |
| }; |
| return [self actionToOpenLinkInNewGroupWithBlock:openInNewGroupBlock |
| inSubmenu:NO]; |
| } |
| |
| NSArray<UIMenuElement*>* groupsMenu = [self groupsMenuForGroups:groups |
| currentGroup:nil |
| block:block]; |
| UIMenu* menu = [UIMenu menuWithTitle:@"" |
| image:nil |
| identifier:nil |
| options:UIMenuOptionsDisplayInline |
| children:groupsMenu]; |
| ProceduralBlock openInNewGroupBlock = ^{ |
| if (block) { |
| block(nil); |
| } |
| }; |
| NSArray<UIMenuElement*>* openInGroupMenuElements = @[ |
| [self actionToOpenLinkInNewGroupWithBlock:openInNewGroupBlock |
| inSubmenu:YES], |
| menu |
| ]; |
| |
| UIImage* image = DefaultSymbolWithPointSize(kMoveTabToGroupActionSymbol, |
| kSymbolActionPointSize); |
| |
| return [UIMenu menuWithTitle:l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_OPENLINKINTABGROUP) |
| image:image |
| identifier:nil |
| options:UIMenuOptionsSingleSelection |
| children:openInGroupMenuElements]; |
| } |
| |
| - (UIAction*)actionToRenameTabGroupWithBlock:(ProceduralBlock)block { |
| CHECK(IsTabGroupInGridEnabled()); |
| UIImage* image = |
| DefaultSymbolWithPointSize(kEditActionSymbol, kSymbolActionPointSize); |
| UIAction* action = |
| [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_RENAMEGROUP) |
| image:image |
| type:MenuActionType::RenameTabGroup |
| block:block]; |
| return action; |
| } |
| |
| - (UIAction*)actionToAddNewTabInGroupWithBlock:(ProceduralBlock)block { |
| CHECK(IsTabGroupInGridEnabled()); |
| UIImage* image = DefaultSymbolWithPointSize(kNewTabGroupActionSymbol, |
| kSymbolActionPointSize); |
| UIAction* action = |
| [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_NEWTABINGROUP) |
| image:image |
| type:MenuActionType::NewTabInGroup |
| block:block]; |
| return action; |
| } |
| |
| - (UIAction*)actionToUngroupTabGroupWithBlock:(ProceduralBlock)block { |
| CHECK(IsTabGroupInGridEnabled()); |
| UIImage* image = DefaultSymbolWithPointSize(kUngroupTabGroupSymbol, |
| kSymbolActionPointSize); |
| UIAction* action = [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_UNGROUP) |
| image:image |
| type:MenuActionType::UngroupTabGroup |
| block:block]; |
| return action; |
| } |
| |
| - (UIAction*)actionToDeleteTabGroupWithBlock:(ProceduralBlock)block { |
| CHECK(IsTabGroupInGridEnabled()); |
| UIImage* image = |
| DefaultSymbolWithPointSize(kDeleteActionSymbol, kSymbolActionPointSize); |
| UIAction* action = |
| [self actionWithTitle:l10n_util::GetNSString( |
| IDS_IOS_CONTENT_CONTEXT_DELETEGROUP) |
| image:image |
| type:MenuActionType::DeleteTabGroup |
| block:block]; |
| action.attributes = UIMenuElementAttributesDestructive; |
| return action; |
| } |
| |
| - (UIAction*)actionToCloseTabGroupWithBlock:(ProceduralBlock)block { |
| CHECK(IsTabGroupInGridEnabled()); |
| CHECK(IsTabGroupSyncEnabled()); |
| |
| UIImage* image = |
| DefaultSymbolWithPointSize(kXMarkSymbol, kSymbolActionPointSize); |
| UIAction* action = [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_CLOSEGROUP) |
| image:image |
| type:MenuActionType::CloseTabGroup |
| block:block]; |
| action.attributes = UIMenuElementAttributesDestructive; |
| return action; |
| } |
| |
| #pragma mark - Private |
| |
| // Creates a UIAction instance for closing a tab with a provided `title`. |
| - (UIAction*)actionToCloseTabWithTitle:(NSString*)title |
| block:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kXMarkSymbol, kSymbolActionPointSize); |
| UIAction* action = [self actionWithTitle:title |
| image:image |
| type:MenuActionType::CloseTab |
| block:block]; |
| action.attributes = UIMenuElementAttributesDestructive; |
| return action; |
| } |
| |
| // Creates a UIAction instance for removing a tab from a group. |
| - (UIAction*)actionToRemoveTabFromGroup:(ProceduralBlock)block { |
| CHECK(IsTabGroupInGridEnabled()) |
| << "You should not be able to create a tab group context menu action " |
| "outside the Tab Groups experiment."; |
| UIImage* image = DefaultSymbolWithPointSize(kRemoveTabFromGroupActionSymbol, |
| kSymbolActionPointSize); |
| NSString* title = |
| l10n_util::GetNSString(IDS_IOS_CONTENT_CONTEXT_REMOVEFROMGROUP); |
| UIAction* action = [self actionWithTitle:title |
| image:image |
| type:MenuActionType::RemoveTabFromGroup |
| block:block]; |
| return action; |
| } |
| |
| - (UIAction*)actionToShowDetailsWithBlock:(ProceduralBlock)block { |
| UIImage* image = |
| DefaultSymbolWithPointSize(kInfoCircleSymbol, kSymbolActionPointSize); |
| return [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_SHOW_DETAILS_ACTION_TITLE) |
| image:image |
| type:MenuActionType::ShowDetails |
| block:block]; |
| } |
| |
| // Returns an array of group actions for a given set of groups. If |
| // `currentGroup` is specified and is present in the set, it is selected. |
| - (NSArray<UIMenuElement*>*) |
| groupsMenuForGroups:(const std::set<const TabGroup*>&)groups |
| currentGroup:(const TabGroup*)currentGroup |
| block:(void (^)(const TabGroup*))block { |
| NSMutableArray<UIMenuElement*>* groupsMenu = [[NSMutableArray alloc] init]; |
| |
| UIImage* circleImage = |
| DefaultSymbolWithPointSize(kCircleFillSymbol, kSymbolActionPointSize); |
| circleImage = |
| [circleImage imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal]; |
| for (const TabGroup* group : groups) { |
| NSString* title = group->GetTitle(); |
| base::WeakPtr<const TabGroup> weakGroup = group->GetWeakPtr(); |
| ProceduralBlock actionBlock = ^{ |
| if (block) { |
| block(weakGroup.get()); |
| } |
| }; |
| |
| UIAction* groupAction = |
| [self actionWithTitle:title |
| image:[circleImage imageWithTintColor:group->GetColor()] |
| type:MenuActionType::MoveTabToExistingGroup |
| block:actionBlock]; |
| |
| if (group == currentGroup) { |
| groupAction.state = UIMenuElementStateOn; |
| } |
| [groupsMenu addObject:groupAction]; |
| } |
| return groupsMenu; |
| } |
| |
| - (UIAction*)actionToSortDriveItemsByNameWithBlock:(ProceduralBlock)block { |
| return |
| [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_DRIVE_SORT_BY_NAME) |
| image:nil |
| type:MenuActionType::SortDriveItemsByName |
| block:block]; |
| } |
| |
| - (UIAction*)actionToSortDriveItemsByModificationTimeWithBlock: |
| (ProceduralBlock)block { |
| return [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_DRIVE_SORT_BY_MODIFICATION) |
| image:nil |
| type:MenuActionType::SortDriveItemsByModificationTime |
| block:block]; |
| } |
| |
| - (UIAction*)actionToSortDriveItemsByOpeningTimeWithBlock: |
| (ProceduralBlock)block { |
| return [self |
| actionWithTitle:l10n_util::GetNSString(IDS_IOS_DRIVE_SORT_BY_OPENING) |
| image:nil |
| type:MenuActionType::SortDriveItemsByOpeningTime |
| block:block]; |
| } |
| |
| - (UIMenuElement*) |
| menuToSelectDriveIdentityWithIdentities: |
| (NSArray<id<SystemIdentity>>*)identities |
| currentIdentity:(id<SystemIdentity>)currentIdentity |
| block:(void (^)(const id<SystemIdentity>)) |
| block { |
| NSMutableArray<UIMenuElement*>* identitiesMenuElements = |
| [[NSMutableArray alloc] init]; |
| for (id<SystemIdentity> identity in identities) { |
| NSString* email = identity.userEmail; |
| ProceduralBlock actionBlock = ^{ |
| if (block) { |
| block(identity); |
| } |
| }; |
| |
| UIAction* identityAction = |
| [self actionWithTitle:email |
| image:nil |
| type:MenuActionType::SelectDriveIdentity |
| block:actionBlock]; |
| if (identity == currentIdentity) { |
| identityAction.state = UIMenuElementStateOn; |
| } |
| [identitiesMenuElements addObject:identityAction]; |
| } |
| |
| return [UIMenu menuWithTitle:@"" |
| image:nil |
| identifier:nil |
| options:UIMenuOptionsDisplayInline |
| children:identitiesMenuElements]; |
| } |
| |
| - (UIAction*)actionToAddAccountForDriveWithBlock:(ProceduralBlock)block { |
| return [self actionWithTitle:l10n_util::GetNSString(IDS_IOS_DRIVE_ADD_ACCOUNT) |
| image:nil |
| type:MenuActionType::AddDriveAccount |
| block:block]; |
| } |
| |
| @end |