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