| // Copyright 2015 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/keyboard/UIKeyCommand+Chrome.h" |
| |
| #import "ios/chrome/browser/shared/ui/util/rtl_geometry.h" |
| #import "ios/chrome/browser/ui/keyboard/key_command_actions.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace { |
| |
| UIKeyModifierFlags None = 0; |
| UIKeyModifierFlags Command = UIKeyModifierCommand; |
| UIKeyModifierFlags Control = UIKeyModifierControl; |
| UIKeyModifierFlags AltCommand = UIKeyModifierAlternate | UIKeyModifierCommand; |
| UIKeyModifierFlags ShiftCommand = UIKeyModifierShift | UIKeyModifierCommand; |
| UIKeyModifierFlags AltShiftCommand = |
| UIKeyModifierAlternate | UIKeyModifierShift | UIKeyModifierCommand; |
| UIKeyModifierFlags ControlShift = UIKeyModifierControl | UIKeyModifierShift; |
| |
| // Backport UIKeyInputDelete to iOS 14 and below. |
| NSString* Delete(void) { |
| if (@available(iOS 15.0, *)) |
| return UIKeyInputDelete; |
| else |
| return @"\b"; |
| } |
| |
| } // namespace |
| |
| @implementation UIKeyCommand (Chrome) |
| |
| #pragma mark - Specific Keyboard Commands |
| |
| + (UIKeyCommand*)cr_openNewTab { |
| return [self cr_commandWithInput:@"t" |
| modifierFlags:Command |
| action:@selector(keyCommand_openNewTab) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_NEW_TAB"]; |
| } |
| |
| + (UIKeyCommand*)cr_openNewRegularTab { |
| return [self keyCommandWithInput:@"n" |
| modifierFlags:Command |
| action:@selector(keyCommand_openNewRegularTab)]; |
| } |
| |
| + (UIKeyCommand*)cr_openNewIncognitoTab { |
| return [self cr_commandWithInput:@"n" |
| modifierFlags:ShiftCommand |
| action:@selector(keyCommand_openNewIncognitoTab) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_NEW_INCOGNITO_TAB"]; |
| } |
| |
| + (UIKeyCommand*)cr_openNewWindow { |
| return [self cr_commandWithInput:@"n" |
| modifierFlags:AltCommand |
| action:@selector(keyCommand_openNewWindow) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_NEW_WINDOW"]; |
| } |
| |
| + (UIKeyCommand*)cr_openNewIncognitoWindow { |
| return [self cr_commandWithInput:@"n" |
| modifierFlags:AltShiftCommand |
| action:@selector(keyCommand_openNewIncognitoWindow) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_NEW_INCOGNITO_WINDOW"]; |
| } |
| |
| + (UIKeyCommand*)cr_reopenLastClosedTab { |
| return [self cr_commandWithInput:@"t" |
| modifierFlags:ShiftCommand |
| action:@selector(keyCommand_reopenLastClosedTab) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_REOPEN_CLOSED_TAB"]; |
| } |
| |
| + (UIKeyCommand*)cr_find { |
| return [self cr_commandWithInput:@"f" |
| modifierFlags:Command |
| action:@selector(keyCommand_find) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_FIND"]; |
| } |
| |
| + (UIKeyCommand*)cr_findNext { |
| return [self cr_commandWithInput:@"g" |
| modifierFlags:Command |
| action:@selector(keyCommand_findNext) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_FIND_NEXT"]; |
| } |
| |
| + (UIKeyCommand*)cr_findPrevious { |
| return [self cr_commandWithInput:@"g" |
| modifierFlags:ShiftCommand |
| action:@selector(keyCommand_findPrevious) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_FIND_PREVIOUS"]; |
| } |
| |
| + (UIKeyCommand*)cr_openLocation { |
| return [self cr_commandWithInput:@"l" |
| modifierFlags:Command |
| action:@selector(keyCommand_openLocation) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_OPEN_LOCATION"]; |
| } |
| |
| + (UIKeyCommand*)cr_closeTab { |
| return [self cr_commandWithInput:@"w" |
| modifierFlags:Command |
| action:@selector(keyCommand_closeTab) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_CLOSE_TAB"]; |
| } |
| |
| + (UIKeyCommand*)cr_showNextTab { |
| UIKeyCommand* keyCommand = |
| [self cr_commandWithInput:@"\t" |
| modifierFlags:Control |
| action:@selector(keyCommand_showNextTab) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_NEXT_TAB"]; |
| if (@available(iOS 15.0, *)) { |
| keyCommand.wantsPriorityOverSystemBehavior = YES; |
| } |
| return keyCommand; |
| } |
| |
| + (UIKeyCommand*)cr_showPreviousTab { |
| UIKeyCommand* keyCommand = |
| [self cr_commandWithInput:@"\t" |
| modifierFlags:ControlShift |
| action:@selector(keyCommand_showPreviousTab) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_PREVIOUS_TAB"]; |
| if (@available(iOS 15.0, *)) { |
| keyCommand.wantsPriorityOverSystemBehavior = YES; |
| } |
| return keyCommand; |
| } |
| |
| + (UIKeyCommand*)cr_showNextTab_2 { |
| // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true |
| // by default. It handles flipping the direction of braces. |
| NSString* braceNext; |
| if (@available(iOS 15.0, *)) { |
| braceNext = @"}"; |
| } else { |
| if (UseRTLLayout()) { |
| braceNext = @"{"; |
| } else { |
| braceNext = @"}"; |
| } |
| } |
| return [self keyCommandWithInput:braceNext |
| modifierFlags:Command |
| action:@selector(keyCommand_showNextTab)]; |
| } |
| |
| + (UIKeyCommand*)cr_showPreviousTab_2 { |
| // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true |
| // by default. It handles flipping the direction of braces. |
| NSString* bracePrevious; |
| if (@available(iOS 15.0, *)) { |
| bracePrevious = @"{"; |
| } else { |
| if (UseRTLLayout()) { |
| bracePrevious = @"}"; |
| } else { |
| bracePrevious = @"{"; |
| } |
| } |
| return [self keyCommandWithInput:bracePrevious |
| modifierFlags:Command |
| action:@selector(keyCommand_showPreviousTab)]; |
| } |
| |
| + (UIKeyCommand*)cr_showNextTab_3 { |
| // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true |
| // by default. It handles flipping the direction of arrows. |
| NSString* arrowNext; |
| if (@available(iOS 15.0, *)) { |
| arrowNext = UIKeyInputRightArrow; |
| } else { |
| if (UseRTLLayout()) { |
| arrowNext = UIKeyInputLeftArrow; |
| } else { |
| arrowNext = UIKeyInputRightArrow; |
| } |
| } |
| return [self keyCommandWithInput:arrowNext |
| modifierFlags:AltCommand |
| action:@selector(keyCommand_showNextTab)]; |
| } |
| |
| + (UIKeyCommand*)cr_showPreviousTab_3 { |
| // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true |
| // by default. It handles flipping the direction of arrows. |
| NSString* arrowPrevious; |
| if (@available(iOS 15.0, *)) { |
| arrowPrevious = UIKeyInputLeftArrow; |
| } else { |
| if (UseRTLLayout()) { |
| arrowPrevious = UIKeyInputRightArrow; |
| } else { |
| arrowPrevious = UIKeyInputLeftArrow; |
| } |
| } |
| return [self keyCommandWithInput:arrowPrevious |
| modifierFlags:AltCommand |
| action:@selector(keyCommand_showPreviousTab)]; |
| } |
| |
| + (UIKeyCommand*)cr_showBookmarks { |
| return [self cr_commandWithInput:@"b" |
| modifierFlags:AltCommand |
| action:@selector(keyCommand_showBookmarks) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_BOOKMARKS"]; |
| } |
| |
| + (UIKeyCommand*)cr_addToBookmarks { |
| return [self cr_commandWithInput:@"d" |
| modifierFlags:Command |
| action:@selector(keyCommand_addToBookmarks) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_ADD_TO_BOOKMARKS"]; |
| } |
| |
| + (UIKeyCommand*)cr_reload { |
| return [self cr_commandWithInput:@"r" |
| modifierFlags:Command |
| action:@selector(keyCommand_reload) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_RELOAD"]; |
| } |
| |
| + (UIKeyCommand*)cr_back { |
| // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true |
| // by default. It handles flipping the direction of brackets. |
| NSString* bracketBack; |
| if (@available(iOS 15.0, *)) { |
| bracketBack = @"["; |
| } else { |
| if (UseRTLLayout()) { |
| bracketBack = @"]"; |
| } else { |
| bracketBack = @"["; |
| } |
| } |
| return [self cr_commandWithInput:bracketBack |
| modifierFlags:Command |
| action:@selector(keyCommand_back) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_HISTORY_BACK"]; |
| } |
| |
| + (UIKeyCommand*)cr_forward { |
| // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is true |
| // by default. It handles flipping the direction of brackets. |
| NSString* bracketForward; |
| if (@available(iOS 15.0, *)) { |
| bracketForward = @"]"; |
| } else { |
| if (UseRTLLayout()) { |
| bracketForward = @"["; |
| } else { |
| bracketForward = @"]"; |
| } |
| } |
| return [self cr_commandWithInput:bracketForward |
| modifierFlags:Command |
| action:@selector(keyCommand_forward) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_HISTORY_FORWARD"]; |
| } |
| |
| + (UIKeyCommand*)cr_back_2 { |
| // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is |
| // true by default. It handles flipping the direction of arrows. |
| NSString* arrowBack; |
| if (@available(iOS 15.0, *)) { |
| arrowBack = UIKeyInputLeftArrow; |
| } else { |
| if (UseRTLLayout()) { |
| arrowBack = UIKeyInputRightArrow; |
| } else { |
| arrowBack = UIKeyInputLeftArrow; |
| } |
| } |
| return [self keyCommandWithInput:arrowBack |
| modifierFlags:Command |
| action:@selector(keyCommand_back)]; |
| } |
| |
| + (UIKeyCommand*)cr_forward_2 { |
| // iOS 15+ supports -[UIKeyCommand allowsAutomaticMirroring], which is |
| // true by default. It handles flipping the direction of arrows. |
| NSString* arrowForward; |
| if (@available(iOS 15.0, *)) { |
| arrowForward = UIKeyInputRightArrow; |
| } else { |
| if (UseRTLLayout()) { |
| arrowForward = UIKeyInputLeftArrow; |
| } else { |
| arrowForward = UIKeyInputRightArrow; |
| } |
| } |
| return [self keyCommandWithInput:arrowForward |
| modifierFlags:Command |
| action:@selector(keyCommand_forward)]; |
| } |
| |
| + (UIKeyCommand*)cr_showHistory { |
| return [self cr_commandWithInput:@"y" |
| modifierFlags:Command |
| action:@selector(keyCommand_showHistory) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_HISTORY"]; |
| } |
| |
| + (UIKeyCommand*)cr_voiceSearch { |
| return [self cr_commandWithInput:@"." |
| modifierFlags:ShiftCommand |
| action:@selector(keyCommand_voiceSearch) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_VOICE_SEARCH"]; |
| } |
| |
| + (UIKeyCommand*)cr_close { |
| return [self keyCommandWithInput:UIKeyInputEscape |
| modifierFlags:None |
| action:@selector(keyCommand_close)]; |
| } |
| |
| + (UIKeyCommand*)cr_showSettings { |
| return [self cr_commandWithInput:@"," |
| modifierFlags:Command |
| action:@selector(keyCommand_showSettings) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_SETTINGS"]; |
| } |
| |
| + (UIKeyCommand*)cr_stop { |
| return [self cr_commandWithInput:@"." |
| modifierFlags:Command |
| action:@selector(keyCommand_stop) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_STOP"]; |
| } |
| |
| + (UIKeyCommand*)cr_showHelp { |
| return [self cr_commandWithInput:@"?" |
| modifierFlags:AltCommand |
| action:@selector(keyCommand_showHelp) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_HELP"]; |
| } |
| |
| + (UIKeyCommand*)cr_showDownloads { |
| return [self cr_commandWithInput:@"l" |
| modifierFlags:AltCommand |
| action:@selector(keyCommand_showDownloads) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_DOWNLOADS"]; |
| } |
| |
| + (UIKeyCommand*)cr_showDownloads_2 { |
| return [self keyCommandWithInput:@"j" |
| modifierFlags:ShiftCommand |
| action:@selector(keyCommand_showDownloads)]; |
| } |
| |
| + (UIKeyCommand*)cr_select1 { |
| return [self keyCommandWithInput:@"1" |
| modifierFlags:Command |
| action:@selector(keyCommand_select1)]; |
| } |
| |
| + (UIKeyCommand*)cr_select2 { |
| return [self keyCommandWithInput:@"2" |
| modifierFlags:Command |
| action:@selector(keyCommand_select2)]; |
| } |
| |
| + (UIKeyCommand*)cr_select3 { |
| return [self keyCommandWithInput:@"3" |
| modifierFlags:Command |
| action:@selector(keyCommand_select3)]; |
| } |
| |
| + (UIKeyCommand*)cr_select4 { |
| return [self keyCommandWithInput:@"4" |
| modifierFlags:Command |
| action:@selector(keyCommand_select4)]; |
| } |
| |
| + (UIKeyCommand*)cr_select5 { |
| return [self keyCommandWithInput:@"5" |
| modifierFlags:Command |
| action:@selector(keyCommand_select5)]; |
| } |
| |
| + (UIKeyCommand*)cr_select6 { |
| return [self keyCommandWithInput:@"6" |
| modifierFlags:Command |
| action:@selector(keyCommand_select6)]; |
| } |
| |
| + (UIKeyCommand*)cr_select7 { |
| return [self keyCommandWithInput:@"7" |
| modifierFlags:Command |
| action:@selector(keyCommand_select7)]; |
| } |
| |
| + (UIKeyCommand*)cr_select8 { |
| return [self keyCommandWithInput:@"8" |
| modifierFlags:Command |
| action:@selector(keyCommand_select8)]; |
| } |
| |
| + (UIKeyCommand*)cr_select9 { |
| return [self cr_commandWithInput:@"9" |
| modifierFlags:Command |
| action:@selector(keyCommand_select9) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_LAST_TAB"]; |
| } |
| |
| + (UIKeyCommand*)cr_reportAnIssue { |
| return [self cr_commandWithInput:@"i" |
| modifierFlags:ShiftCommand |
| action:@selector(keyCommand_reportAnIssue) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_REPORT_AN_ISSUE"]; |
| } |
| |
| + (UIKeyCommand*)cr_reportAnIssue_2 { |
| return [self keyCommandWithInput:@"i" |
| modifierFlags:AltShiftCommand |
| action:@selector(keyCommand_reportAnIssue)]; |
| } |
| |
| + (UIKeyCommand*)cr_addToReadingList { |
| return [self cr_commandWithInput:@"d" |
| modifierFlags:ShiftCommand |
| action:@selector(keyCommand_addToReadingList) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_ADD_TO_READING_LIST"]; |
| } |
| |
| + (UIKeyCommand*)cr_showReadingList { |
| return [self cr_commandWithInput:@"r" |
| modifierFlags:AltCommand |
| action:@selector(keyCommand_showReadingList) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_SHOW_READING_LIST"]; |
| } |
| |
| + (UIKeyCommand*)cr_goToTabGrid { |
| return [self cr_commandWithInput:@"\\" |
| modifierFlags:ShiftCommand |
| action:@selector(keyCommand_goToTabGrid) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_GO_TO_TAB_GRID"]; |
| } |
| |
| + (UIKeyCommand*)cr_clearBrowsingData { |
| return [self cr_commandWithInput:Delete() |
| modifierFlags:ShiftCommand |
| action:@selector(keyCommand_clearBrowsingData) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_CLEAR_BROWSING_DATA"]; |
| } |
| |
| + (UIKeyCommand*)cr_closeAll { |
| return [self cr_commandWithInput:@"w" |
| modifierFlags:ShiftCommand |
| action:@selector(keyCommand_closeAll) |
| titleIDAsString:@"IDS_IOS_KEYBOARD_CLOSE_ALL"]; |
| } |
| |
| + (UIKeyCommand*)cr_undo { |
| UIKeyCommand* keyCommand = |
| [self keyCommandWithInput:@"z" |
| modifierFlags:Command |
| action:@selector(keyCommand_undo)]; |
| if (@available(iOS 15.0, *)) { |
| keyCommand.wantsPriorityOverSystemBehavior = YES; |
| } |
| return keyCommand; |
| } |
| |
| #pragma mark - Symbolic Description |
| |
| - (NSString*)cr_symbolicDescription { |
| NSMutableString* description = [NSMutableString string]; |
| |
| if (self.modifierFlags & UIKeyModifierNumericPad) |
| [description appendString:@"Num lock "]; |
| if (self.modifierFlags & UIKeyModifierControl) |
| [description appendString:@"⌃"]; |
| if (self.modifierFlags & UIKeyModifierAlternate) |
| [description appendString:@"⌥"]; |
| if (self.modifierFlags & UIKeyModifierShift) |
| [description appendString:@"⇧"]; |
| if (self.modifierFlags & UIKeyModifierAlphaShift) |
| [description appendString:@"⇪"]; |
| if (self.modifierFlags & UIKeyModifierCommand) |
| [description appendString:@"⌘"]; |
| |
| if ([self.input isEqualToString:Delete()]) |
| [description appendString:@"⌫"]; |
| else if ([self.input isEqualToString:@"\r"]) |
| [description appendString:@"↵"]; |
| else if ([self.input isEqualToString:@"\t"]) |
| [description appendString:@"⇥"]; |
| else if ([self.input isEqualToString:UIKeyInputUpArrow]) |
| [description appendString:@"↑"]; |
| else if ([self.input isEqualToString:UIKeyInputDownArrow]) |
| [description appendString:@"↓"]; |
| else if ([self.input isEqualToString:UIKeyInputLeftArrow]) |
| [description appendString:@"←"]; |
| else if ([self.input isEqualToString:UIKeyInputRightArrow]) |
| [description appendString:@"→"]; |
| else if ([self.input isEqualToString:UIKeyInputEscape]) |
| [description appendString:@"⎋"]; |
| else if ([self.input isEqualToString:@" "]) |
| [description appendString:@"␣"]; |
| else |
| [description appendString:[self.input uppercaseString]]; |
| return description; |
| } |
| |
| #pragma mark - Private |
| |
| // The title ID string is used as a key to NSLocalizedString because key |
| // commands can be requested by the OS very early on, before the resource bundle |
| // of the localized strings is loaded. |
| + (instancetype)cr_commandWithInput:(NSString*)input |
| modifierFlags:(UIKeyModifierFlags)modifierFlags |
| action:(SEL)action |
| titleIDAsString:(NSString*)messageID { |
| UIKeyCommand* keyCommand = |
| [self commandWithTitle:NSLocalizedString(messageID, @"") |
| image:nil |
| action:action |
| input:input |
| modifierFlags:modifierFlags |
| propertyList:nil]; |
| keyCommand.discoverabilityTitle = keyCommand.title; |
| return keyCommand; |
| } |
| |
| @end |