| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "ui/views/cocoa/bridged_content_view.h" | 5 #import "ui/views/cocoa/bridged_content_view.h" |
| 6 | 6 |
| 7 #include "base/logging.h" | 7 #include "base/logging.h" |
| 8 #import "base/mac/mac_util.h" | 8 #import "base/mac/mac_util.h" |
| 9 #import "base/mac/scoped_nsobject.h" | 9 #import "base/mac/scoped_nsobject.h" |
| 10 #include "base/strings/sys_string_conversions.h" | 10 #include "base/strings/sys_string_conversions.h" |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 65 NSPoint point_in_window = | 65 NSPoint point_in_window = |
| 66 ui::ConvertPointFromScreenToWindow(target_window, point_in_screen); | 66 ui::ConvertPointFromScreenToWindow(target_window, point_in_screen); |
| 67 NSRect content_rect = | 67 NSRect content_rect = |
| 68 [target_window contentRectForFrameRect:[target_window frame]]; | 68 [target_window contentRectForFrameRect:[target_window frame]]; |
| 69 return gfx::Point(point_in_window.x, | 69 return gfx::Point(point_in_window.x, |
| 70 NSHeight(content_rect) - point_in_window.y); | 70 NSHeight(content_rect) - point_in_window.y); |
| 71 } | 71 } |
| 72 | 72 |
| 73 // Checks if there's an active MenuController during key event dispatch. If | 73 // Checks if there's an active MenuController during key event dispatch. If |
| 74 // there is one, it gets preference, and it will likely swallow the event. | 74 // there is one, it gets preference, and it will likely swallow the event. |
| 75 bool DispatchEventToMenu(views::Widget* widget, ui::KeyboardCode key_code) { | 75 bool DispatchEventToMenu(views::Widget* widget, ui::KeyEvent* event) { |
| 76 MenuController* menuController = MenuController::GetActiveInstance(); | 76 MenuController* menuController = MenuController::GetActiveInstance(); |
| 77 if (menuController && menuController->owner() == widget) { | 77 if (menuController && menuController->owner() == widget) { |
| 78 if (menuController->OnWillDispatchKeyEvent(0, key_code) == | 78 if (menuController->OnWillDispatchKeyEvent(event) == ui::POST_DISPATCH_NONE) |
| 79 ui::POST_DISPATCH_NONE) | |
| 80 return true; | 79 return true; |
| 81 } | 80 } |
| 82 return false; | 81 return false; |
| 83 } | 82 } |
| 84 | 83 |
| 85 // Returns true if |client| has RTL text. | 84 // Returns true if |client| has RTL text. |
| 86 bool IsTextRTL(const ui::TextInputClient* client) { | 85 bool IsTextRTL(const ui::TextInputClient* client) { |
| 87 gfx::Range text_range; | 86 gfx::Range text_range; |
| 88 base::string16 text; | 87 base::string16 text; |
| 89 return client && client->GetTextRange(&text_range) && | 88 return client && client->GetTextRange(&text_range) && |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 195 // This is a special case for which the complete string should should be | 194 // This is a special case for which the complete string should should be |
| 196 // returned. NSTextView also follows this, though the same is not mentioned in | 195 // returned. NSTextView also follows this, though the same is not mentioned in |
| 197 // NSTextInputClient documentation. | 196 // NSTextInputClient documentation. |
| 198 if (!requested_range.IsValid()) | 197 if (!requested_range.IsValid()) |
| 199 *actual_range = text_range; | 198 *actual_range = text_range; |
| 200 | 199 |
| 201 client->GetTextFromRange(*actual_range, &substring); | 200 client->GetTextFromRange(*actual_range, &substring); |
| 202 return substring; | 201 return substring; |
| 203 } | 202 } |
| 204 | 203 |
| 204 // Returns a character event corresponding to |event|. |event| must be a |
| 205 // character event itself. |
| 206 ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { |
| 207 DCHECK([event type] == NSKeyDown || [event type] == NSKeyUp); |
| 208 DCHECK_EQ(1u, [[event characters] length]); |
| 209 |
| 210 // [NSEvent characters] already considers the pressed key modifiers. Hence |
| 211 // send ui::EF_NONE as the key modifier to the KeyEvent constructor. |
| 212 // E.g. For Alt+S, [NSEvent characters] is 'ß' and not 'S'. |
| 213 return ui::KeyEvent([[event characters] characterAtIndex:0], |
| 214 ui::KeyboardCodeFromNSEvent(event), ui::EF_NONE); |
| 215 } |
| 216 |
| 205 } // namespace | 217 } // namespace |
| 206 | 218 |
| 207 @interface BridgedContentView () | 219 @interface BridgedContentView () |
| 208 | 220 |
| 209 // Translates keycodes and modifiers on |theEvent| to ui::KeyEvents and passes | 221 // Passes |event| to the InputMethod for dispatch. |
| 210 // the event to the InputMethod for dispatch. | 222 - (void)handleKeyEvent:(ui::KeyEvent*)event; |
| 211 - (void)handleKeyEvent:(NSEvent*)theEvent; | |
| 212 | 223 |
| 213 // Handles an NSResponder Action Message by mapping it to a corresponding text | 224 // Handles an NSResponder Action Message by mapping it to a corresponding text |
| 214 // editing command from ui_strings.grd and, when not being sent to a | 225 // editing command from ui_strings.grd and, when not being sent to a |
| 215 // TextInputClient, the keyCode that toolkit-views expects internally. | 226 // TextInputClient, the keyCode that toolkit-views expects internally. |
| 216 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales | 227 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales |
| 217 // even though the Home key on Mac defaults to moveToBeginningOfDocument:. | 228 // even though the Home key on Mac defaults to moveToBeginningOfDocument:. |
| 218 // This approach also allows action messages a user | 229 // This approach also allows action messages a user |
| 219 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be | 230 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be |
| 220 // catered for. | 231 // catered for. |
| 221 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict | 232 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict |
| (...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 360 return; | 371 return; |
| 361 | 372 |
| 362 views::FocusManager* focusManager = | 373 views::FocusManager* focusManager = |
| 363 hostedView_->GetWidget()->GetFocusManager(); | 374 hostedView_->GetWidget()->GetFocusManager(); |
| 364 if (focusManager) | 375 if (focusManager) |
| 365 focusManager->SetKeyboardAccessible([NSApp isFullKeyboardAccessEnabled]); | 376 focusManager->SetKeyboardAccessible([NSApp isFullKeyboardAccessEnabled]); |
| 366 } | 377 } |
| 367 | 378 |
| 368 // BridgedContentView private implementation. | 379 // BridgedContentView private implementation. |
| 369 | 380 |
| 370 - (void)handleKeyEvent:(NSEvent*)theEvent { | 381 - (void)handleKeyEvent:(ui::KeyEvent*)event { |
| 371 if (!hostedView_) | 382 if (!hostedView_) |
| 372 return; | 383 return; |
| 373 | 384 |
| 374 DCHECK(theEvent); | 385 DCHECK(event); |
| 375 ui::KeyEvent event(theEvent); | 386 if (DispatchEventToMenu(hostedView_->GetWidget(), event)) |
| 376 if (DispatchEventToMenu(hostedView_->GetWidget(), event.key_code())) | |
| 377 return; | 387 return; |
| 378 | 388 |
| 379 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event); | 389 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event); |
| 380 } | 390 } |
| 381 | 391 |
| 382 - (void)handleAction:(int)commandId | 392 - (void)handleAction:(int)commandId |
| 383 keyCode:(ui::KeyboardCode)keyCode | 393 keyCode:(ui::KeyboardCode)keyCode |
| 384 domCode:(ui::DomCode)domCode | 394 domCode:(ui::DomCode)domCode |
| 385 eventFlags:(int)eventFlags { | 395 eventFlags:(int)eventFlags { |
| 386 if (!hostedView_) | 396 if (!hostedView_) |
| 387 return; | 397 return; |
| 388 | 398 |
| 389 if (DispatchEventToMenu(hostedView_->GetWidget(), keyCode)) | 399 // Generate a synthetic event with the keycode toolkit-views expects. |
| 400 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags); |
| 401 |
| 402 if (DispatchEventToMenu(hostedView_->GetWidget(), &event)) |
| 390 return; | 403 return; |
| 391 | 404 |
| 392 // If there's an active TextInputClient, schedule the editing command to be | 405 // If there's an active TextInputClient, schedule the editing command to be |
| 393 // performed. | 406 // performed. |
| 394 if (commandId && textInputClient_ && | 407 if (commandId && textInputClient_ && |
| 395 textInputClient_->IsEditCommandEnabled(commandId)) | 408 textInputClient_->IsEditCommandEnabled(commandId)) |
| 396 textInputClient_->SetEditCommandForNextKeyEvent(commandId); | 409 textInputClient_->SetEditCommandForNextKeyEvent(commandId); |
| 397 | 410 |
| 398 // Generate a synthetic event with the keycode toolkit-views expects. | |
| 399 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags); | |
| 400 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event); | 411 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event); |
| 401 } | 412 } |
| 402 | 413 |
| 403 - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification { | 414 - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification { |
| 404 DCHECK([[notification name] | 415 DCHECK([[notification name] |
| 405 isEqualToString:kFullKeyboardAccessChangedNotification]); | 416 isEqualToString:kFullKeyboardAccessChangedNotification]); |
| 406 [self updateFullKeyboardAccess]; | 417 [self updateFullKeyboardAccess]; |
| 407 } | 418 } |
| 408 | 419 |
| 409 - (views::DragDropClientMac*)dragDropClient { | 420 - (views::DragDropClientMac*)dragDropClient { |
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 645 | 656 |
| 646 // NSResponder implementation. | 657 // NSResponder implementation. |
| 647 | 658 |
| 648 - (void)keyDown:(NSEvent*)theEvent { | 659 - (void)keyDown:(NSEvent*)theEvent { |
| 649 // Convert the event into an action message, according to OSX key mappings. | 660 // Convert the event into an action message, according to OSX key mappings. |
| 650 inKeyDown_ = YES; | 661 inKeyDown_ = YES; |
| 651 [self interpretKeyEvents:@[ theEvent ]]; | 662 [self interpretKeyEvents:@[ theEvent ]]; |
| 652 inKeyDown_ = NO; | 663 inKeyDown_ = NO; |
| 653 } | 664 } |
| 654 | 665 |
| 666 - (void)keyUp:(NSEvent*)theEvent { |
| 667 ui::KeyEvent event(theEvent); |
| 668 [self handleKeyEvent:&event]; |
| 669 } |
| 670 |
| 655 - (void)scrollWheel:(NSEvent*)theEvent { | 671 - (void)scrollWheel:(NSEvent*)theEvent { |
| 656 if (!hostedView_) | 672 if (!hostedView_) |
| 657 return; | 673 return; |
| 658 | 674 |
| 659 ui::MouseWheelEvent event(theEvent); | 675 ui::MouseWheelEvent event(theEvent); |
| 660 hostedView_->GetWidget()->OnMouseEvent(&event); | 676 hostedView_->GetWidget()->OnMouseEvent(&event); |
| 661 } | 677 } |
| 662 | 678 |
| 663 //////////////////////////////////////////////////////////////////////////////// | 679 //////////////////////////////////////////////////////////////////////////////// |
| 664 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the | 680 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the |
| 665 // 10.9 SDK). The list should eventually be complete. Anything not defined will | 681 // 10.9 SDK). The list should eventually be complete. Anything not defined will |
| 666 // beep when interpretKeyEvents: would otherwise call it. | 682 // beep when interpretKeyEvents: would otherwise call it. |
| 667 // TODO(tapted): Make this list complete, except for insert* methods which are | 683 // TODO(tapted): Make this list complete, except for insert* methods which are |
| 668 // dispatched as regular key events in doCommandBySelector:. | 684 // dispatched as regular key events in doCommandBySelector:. |
| 669 | 685 |
| 670 // views::Textfields are single-line only, map Paragraph and Document commands | 686 // views::Textfields are single-line only, map Paragraph and Document commands |
| 671 // to Line. Also, Up/Down commands correspond to beginning/end of line. | 687 // to Line. Also, Up/Down commands correspond to beginning/end of line. |
| 672 | 688 |
| 673 // The insertText action message forwards to the TextInputClient unless a menu | 689 // The insertText action message forwards to the TextInputClient unless a menu |
| 674 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't | 690 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't |
| 675 // direct insertText: through doCommandBySelector:, so this is still needed to | 691 // direct insertText: through doCommandBySelector:, so this is still needed to |
| 676 // handle the case when inputContext: is nil. When inputContext: returns non-nil | 692 // handle the case when inputContext: is nil. When inputContext: returns non-nil |
| 677 // text goes directly to insertText:replacementRange:. | 693 // text goes directly to insertText:replacementRange:. |
| 678 - (void)insertText:(id)text { | 694 - (void)insertText:(id)text { |
| 679 [self insertText:text replacementRange:NSMakeRange(NSNotFound, 0)]; | 695 DCHECK_EQ(nil, [self inputContext]); |
| 696 |
| 697 // Only handle the case where no. of characters is 1. Cases not handled (not |
| 698 // an exhaustive list): |
| 699 // - |text| contains a unicode surrogate pair, i.e. a single grapheme which |
| 700 // requires two 16 bit characters. Currently Views menu only supports |
| 701 // mnemonics using a single 16 bit character, so it is ok to ignore this |
| 702 // case. |
| 703 // - Programmatically created events. |
| 704 // - Input from IME. But this case should not occur since inputContext is nil. |
| 705 if ([text length] == 1) { |
| 706 ui::KeyEvent charEvent = GetCharacterEventFromNSEvent([NSApp currentEvent]); |
| 707 [self handleKeyEvent:&charEvent]; |
| 708 } |
| 680 } | 709 } |
| 681 | 710 |
| 682 // Selection movement and scrolling. | 711 // Selection movement and scrolling. |
| 683 | 712 |
| 684 - (void)moveForward:(id)sender { | 713 - (void)moveForward:(id)sender { |
| 685 IsTextRTL(textInputClient_) ? [self moveLeft:sender] | 714 IsTextRTL(textInputClient_) ? [self moveLeft:sender] |
| 686 : [self moveRight:sender]; | 715 : [self moveRight:sender]; |
| 687 } | 716 } |
| 688 | 717 |
| 689 - (void)moveRight:(id)sender { | 718 - (void)moveRight:(id)sender { |
| (...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1064 | 1093 |
| 1065 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { | 1094 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { |
| 1066 NOTIMPLEMENTED(); | 1095 NOTIMPLEMENTED(); |
| 1067 return 0; | 1096 return 0; |
| 1068 } | 1097 } |
| 1069 | 1098 |
| 1070 - (void)doCommandBySelector:(SEL)selector { | 1099 - (void)doCommandBySelector:(SEL)selector { |
| 1071 // Like the renderer, handle insert action messages as a regular key dispatch. | 1100 // Like the renderer, handle insert action messages as a regular key dispatch. |
| 1072 // This ensures, e.g., insertTab correctly changes focus between fields. | 1101 // This ensures, e.g., insertTab correctly changes focus between fields. |
| 1073 if (inKeyDown_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) { | 1102 if (inKeyDown_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) { |
| 1074 [self handleKeyEvent:[NSApp currentEvent]]; | 1103 ui::KeyEvent event([NSApp currentEvent]); |
| 1104 [self handleKeyEvent:&event]; |
| 1075 return; | 1105 return; |
| 1076 } | 1106 } |
| 1077 | 1107 |
| 1078 if ([self respondsToSelector:selector]) | 1108 if ([self respondsToSelector:selector]) |
| 1079 [self performSelector:selector withObject:nil]; | 1109 [self performSelector:selector withObject:nil]; |
| 1080 else | 1110 else |
| 1081 [[self nextResponder] doCommandBySelector:selector]; | 1111 [[self nextResponder] doCommandBySelector:selector]; |
| 1082 } | 1112 } |
| 1083 | 1113 |
| 1084 - (NSRect)firstRectForCharacterRange:(NSRange)range | 1114 - (NSRect)firstRectForCharacterRange:(NSRange)range |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1095 return textInputClient_ && textInputClient_->HasCompositionText(); | 1125 return textInputClient_ && textInputClient_->HasCompositionText(); |
| 1096 } | 1126 } |
| 1097 | 1127 |
| 1098 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange { | 1128 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange { |
| 1099 if (!hostedView_) | 1129 if (!hostedView_) |
| 1100 return; | 1130 return; |
| 1101 | 1131 |
| 1102 if ([text isKindOfClass:[NSAttributedString class]]) | 1132 if ([text isKindOfClass:[NSAttributedString class]]) |
| 1103 text = [text string]; | 1133 text = [text string]; |
| 1104 | 1134 |
| 1105 MenuController* menuController = MenuController::GetActiveInstance(); | 1135 // Verify inputContext is not nil, i.e. |textInputClient_| is valid and no |
| 1106 if (menuController && menuController->owner() == hostedView_->GetWidget()) { | 1136 // menu is active. |
| 1107 // Handle menu mnemonics (e.g. "sav" jumps to "Save"). Handles both single- | 1137 DCHECK([self inputContext]); |
| 1108 // characters and input from IME. For IME, swallow the entire string unless | |
| 1109 // the very first character gives ui::POST_DISPATCH_PERFORM_DEFAULT. | |
| 1110 bool swallowedAny = false; | |
| 1111 for (NSUInteger i = 0; i < [text length]; ++i) { | |
| 1112 if (!menuController || | |
| 1113 menuController->OnWillDispatchKeyEvent([text characterAtIndex:i], | |
| 1114 ui::VKEY_UNKNOWN) == | |
| 1115 ui::POST_DISPATCH_PERFORM_DEFAULT) { | |
| 1116 if (swallowedAny) | |
| 1117 return; // Swallow remainder. | |
| 1118 break; | |
| 1119 } | |
| 1120 swallowedAny = true; | |
| 1121 // Ensure the menu remains active. | |
| 1122 menuController = MenuController::GetActiveInstance(); | |
| 1123 } | |
| 1124 } | |
| 1125 | |
| 1126 if (!textInputClient_) | |
| 1127 return; | |
| 1128 | 1138 |
| 1129 textInputClient_->DeleteRange(gfx::Range(replacementRange)); | 1139 textInputClient_->DeleteRange(gfx::Range(replacementRange)); |
| 1130 | 1140 |
| 1131 // If a single character is inserted by keyDown's call to interpretKeyEvents: | 1141 // If a single character is inserted by keyDown's call to interpretKeyEvents: |
| 1132 // then use InsertChar() to allow editing events to be merged. The second | 1142 // then use InsertChar() to allow editing events to be merged. |
| 1133 // argument is the key modifier, which interpretKeyEvents: will have already | |
| 1134 // processed, so don't send it to InsertChar() as well. E.g. Alt+S puts 'ß' in | |
| 1135 // |text| but sending 'Alt' to InsertChar would filter it out since it thinks | |
| 1136 // it's a command. Actual commands (e.g. Cmd+S) won't go through insertText:. | |
| 1137 if (inKeyDown_ && [text length] == 1) { | 1143 if (inKeyDown_ && [text length] == 1) { |
| 1138 ui::KeyEvent char_event( | 1144 textInputClient_->InsertChar( |
| 1139 [text characterAtIndex:0], | 1145 GetCharacterEventFromNSEvent([NSApp currentEvent])); |
| 1140 static_cast<ui::KeyboardCode>([text characterAtIndex:0]), ui::EF_NONE); | |
| 1141 textInputClient_->InsertChar(char_event); | |
| 1142 } else { | 1146 } else { |
| 1143 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); | 1147 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); |
| 1144 } | 1148 } |
| 1145 } | 1149 } |
| 1146 | 1150 |
| 1147 - (NSRange)markedRange { | 1151 - (NSRange)markedRange { |
| 1148 if (!textInputClient_) | 1152 if (!textInputClient_) |
| 1149 return NSMakeRange(NSNotFound, 0); | 1153 return NSMakeRange(NSNotFound, 0); |
| 1150 | 1154 |
| 1151 gfx::Range range; | 1155 gfx::Range range; |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1232 } | 1236 } |
| 1233 | 1237 |
| 1234 return [super accessibilityAttributeValue:attribute]; | 1238 return [super accessibilityAttributeValue:attribute]; |
| 1235 } | 1239 } |
| 1236 | 1240 |
| 1237 - (id)accessibilityHitTest:(NSPoint)point { | 1241 - (id)accessibilityHitTest:(NSPoint)point { |
| 1238 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; | 1242 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; |
| 1239 } | 1243 } |
| 1240 | 1244 |
| 1241 @end | 1245 @end |
| OLD | NEW |