Chromium Code Reviews| 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 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 60 NSPoint point_in_window = | 60 NSPoint point_in_window = |
| 61 ui::ConvertPointFromScreenToWindow(target_window, point_in_screen); | 61 ui::ConvertPointFromScreenToWindow(target_window, point_in_screen); |
| 62 NSRect content_rect = | 62 NSRect content_rect = |
| 63 [target_window contentRectForFrameRect:[target_window frame]]; | 63 [target_window contentRectForFrameRect:[target_window frame]]; |
| 64 return gfx::Point(point_in_window.x, | 64 return gfx::Point(point_in_window.x, |
| 65 NSHeight(content_rect) - point_in_window.y); | 65 NSHeight(content_rect) - point_in_window.y); |
| 66 } | 66 } |
| 67 | 67 |
| 68 // Checks if there's an active MenuController during key event dispatch. If | 68 // Checks if there's an active MenuController during key event dispatch. If |
| 69 // there is one, it gets preference, and it will likely swallow the event. | 69 // there is one, it gets preference, and it will likely swallow the event. |
| 70 bool DispatchEventToMenu(views::Widget* widget, ui::KeyboardCode key_code) { | 70 bool DispatchEventToMenu(views::Widget* widget, const ui::KeyEvent& event) { |
| 71 MenuController* menuController = MenuController::GetActiveInstance(); | 71 MenuController* menuController = MenuController::GetActiveInstance(); |
| 72 if (menuController && menuController->owner() == widget) { | 72 if (menuController && menuController->owner() == widget) { |
| 73 if (menuController->OnWillDispatchKeyEvent(0, key_code) == | 73 if (menuController->OnWillDispatchKeyEvent(event) == ui::POST_DISPATCH_NONE) |
| 74 ui::POST_DISPATCH_NONE) | |
| 75 return true; | 74 return true; |
| 76 } | 75 } |
| 77 return false; | 76 return false; |
| 78 } | 77 } |
| 79 | 78 |
| 80 // Returns true if |client| has RTL text. | 79 // Returns true if |client| has RTL text. |
| 81 bool IsTextRTL(const ui::TextInputClient* client) { | 80 bool IsTextRTL(const ui::TextInputClient* client) { |
| 82 gfx::Range text_range; | 81 gfx::Range text_range; |
| 83 base::string16 text; | 82 base::string16 text; |
| 84 return client && client->GetTextRange(&text_range) && | 83 return client && client->GetTextRange(&text_range) && |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 190 // This is a special case for which the complete string should should be | 189 // This is a special case for which the complete string should should be |
| 191 // returned. NSTextView also follows this, though the same is not mentioned in | 190 // returned. NSTextView also follows this, though the same is not mentioned in |
| 192 // NSTextInputClient documentation. | 191 // NSTextInputClient documentation. |
| 193 if (!requested_range.IsValid()) | 192 if (!requested_range.IsValid()) |
| 194 *actual_range = text_range; | 193 *actual_range = text_range; |
| 195 | 194 |
| 196 client->GetTextFromRange(*actual_range, &substring); | 195 client->GetTextFromRange(*actual_range, &substring); |
| 197 return substring; | 196 return substring; |
| 198 } | 197 } |
| 199 | 198 |
| 199 // Returns a character event corresponding to |event|. |event| must be a | |
| 200 // character event itself. | |
| 201 ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) { | |
| 202 DCHECK([event type] == NSKeyDown || [event type] == NSKeyUp); | |
| 203 DCHECK_EQ(1u, [[event characters] length]); | |
| 204 | |
| 205 // [NSEvent characters] already considers the pressed key modifiers. Hence | |
| 206 // send ui::EF_NONE as the key modifier to the KeyEvent constructor. | |
| 207 // E.g. For Alt+S, [NSEvent characters] is 'ß' and not 'S'. | |
| 208 return ui::KeyEvent([[event characters] characterAtIndex:0], | |
| 209 ui::KeyboardCodeFromNSEvent(event), ui::EF_NONE); | |
|
karandeepb
2016/06/06 02:04:42
Modified this. Earlier we were just casting the ch
| |
| 210 } | |
| 211 | |
| 200 } // namespace | 212 } // namespace |
| 201 | 213 |
| 202 @interface BridgedContentView () | 214 @interface BridgedContentView () |
| 203 | 215 |
| 204 // Translates keycodes and modifiers on |theEvent| to ui::KeyEvents and passes | 216 // Passes |event| to the InputMethod for dispatch. |
| 205 // the event to the InputMethod for dispatch. | 217 - (void)handleKeyDownEvent:(ui::KeyEvent*)event; |
| 206 - (void)handleKeyEvent:(NSEvent*)theEvent; | |
| 207 | 218 |
| 208 // Handles an NSResponder Action Message by mapping it to a corresponding text | 219 // Handles an NSResponder Action Message by mapping it to a corresponding text |
| 209 // editing command from ui_strings.grd and, when not being sent to a | 220 // editing command from ui_strings.grd and, when not being sent to a |
| 210 // TextInputClient, the keyCode that toolkit-views expects internally. | 221 // TextInputClient, the keyCode that toolkit-views expects internally. |
| 211 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales | 222 // For example, moveToLeftEndOfLine: would pass ui::VKEY_HOME in non-RTL locales |
| 212 // even though the Home key on Mac defaults to moveToBeginningOfDocument:. | 223 // even though the Home key on Mac defaults to moveToBeginningOfDocument:. |
| 213 // This approach also allows action messages a user | 224 // This approach also allows action messages a user |
| 214 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be | 225 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be |
| 215 // catered for. | 226 // catered for. |
| 216 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict | 227 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 350 return; | 361 return; |
| 351 | 362 |
| 352 views::FocusManager* focusManager = | 363 views::FocusManager* focusManager = |
| 353 hostedView_->GetWidget()->GetFocusManager(); | 364 hostedView_->GetWidget()->GetFocusManager(); |
| 354 if (focusManager) | 365 if (focusManager) |
| 355 focusManager->SetKeyboardAccessible([NSApp isFullKeyboardAccessEnabled]); | 366 focusManager->SetKeyboardAccessible([NSApp isFullKeyboardAccessEnabled]); |
| 356 } | 367 } |
| 357 | 368 |
| 358 // BridgedContentView private implementation. | 369 // BridgedContentView private implementation. |
| 359 | 370 |
| 360 - (void)handleKeyEvent:(NSEvent*)theEvent { | 371 - (void)handleKeyDownEvent:(ui::KeyEvent*)event { |
| 361 if (!hostedView_) | 372 if (!hostedView_) |
| 362 return; | 373 return; |
| 363 | 374 DCHECK(event); |
|
tapted
2016/06/06 04:44:05
nit: blank line before
karandeepb
2016/06/06 11:21:10
Done.
| |
| 364 DCHECK(theEvent); | 375 if (DispatchEventToMenu(hostedView_->GetWidget(), *event)) |
| 365 ui::KeyEvent event(theEvent); | |
| 366 if (DispatchEventToMenu(hostedView_->GetWidget(), event.key_code())) | |
| 367 return; | 376 return; |
| 368 | 377 |
| 369 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event); | 378 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event); |
| 370 } | 379 } |
| 371 | 380 |
| 372 - (void)handleAction:(int)commandId | 381 - (void)handleAction:(int)commandId |
| 373 keyCode:(ui::KeyboardCode)keyCode | 382 keyCode:(ui::KeyboardCode)keyCode |
| 374 domCode:(ui::DomCode)domCode | 383 domCode:(ui::DomCode)domCode |
| 375 eventFlags:(int)eventFlags { | 384 eventFlags:(int)eventFlags { |
| 376 if (!hostedView_) | 385 if (!hostedView_) |
| 377 return; | 386 return; |
| 378 | 387 |
| 379 if (DispatchEventToMenu(hostedView_->GetWidget(), keyCode)) | 388 // Generate a synthetic event with the keycode toolkit-views expects. |
| 389 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags); | |
| 390 | |
| 391 if (DispatchEventToMenu(hostedView_->GetWidget(), event)) | |
| 380 return; | 392 return; |
| 381 | 393 |
| 382 // If there's an active TextInputClient, schedule the editing command to be | 394 // If there's an active TextInputClient, schedule the editing command to be |
| 383 // performed. | 395 // performed. |
| 384 if (commandId && textInputClient_ && | 396 if (commandId && textInputClient_ && |
| 385 textInputClient_->IsEditCommandEnabled(commandId)) | 397 textInputClient_->IsEditCommandEnabled(commandId)) |
| 386 textInputClient_->SetEditCommandForNextKeyEvent(commandId); | 398 textInputClient_->SetEditCommandForNextKeyEvent(commandId); |
| 387 | 399 |
| 388 // Generate a synthetic event with the keycode toolkit-views expects. | |
| 389 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags); | |
| 390 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event); | 400 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event); |
| 391 } | 401 } |
| 392 | 402 |
| 393 - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification { | 403 - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification { |
| 394 DCHECK([[notification name] | 404 DCHECK([[notification name] |
| 395 isEqualToString:kFullKeyboardAccessChangedNotification]); | 405 isEqualToString:kFullKeyboardAccessChangedNotification]); |
| 396 [self updateFullKeyboardAccess]; | 406 [self updateFullKeyboardAccess]; |
| 397 } | 407 } |
| 398 | 408 |
| 399 - (void)undo:(id)sender { | 409 - (void)undo:(id)sender { |
| (...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 613 | 623 |
| 614 // NSResponder implementation. | 624 // NSResponder implementation. |
| 615 | 625 |
| 616 - (void)keyDown:(NSEvent*)theEvent { | 626 - (void)keyDown:(NSEvent*)theEvent { |
| 617 // Convert the event into an action message, according to OSX key mappings. | 627 // Convert the event into an action message, according to OSX key mappings. |
| 618 inKeyDown_ = YES; | 628 inKeyDown_ = YES; |
| 619 [self interpretKeyEvents:@[ theEvent ]]; | 629 [self interpretKeyEvents:@[ theEvent ]]; |
| 620 inKeyDown_ = NO; | 630 inKeyDown_ = NO; |
| 621 } | 631 } |
| 622 | 632 |
| 633 - (void)keyUp:(NSEvent*)theEvent { | |
| 634 // Generate a synthetic ui::KeyEvent for toolkit-views. | |
| 635 ui::KeyEvent event(theEvent); | |
| 636 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event); | |
|
tapted
2016/06/06 04:44:05
Should allow the menu to intercept keyUp as well,
karandeepb
2016/06/06 11:21:10
Done.
| |
| 637 } | |
| 638 | |
| 623 - (void)scrollWheel:(NSEvent*)theEvent { | 639 - (void)scrollWheel:(NSEvent*)theEvent { |
| 624 if (!hostedView_) | 640 if (!hostedView_) |
| 625 return; | 641 return; |
| 626 | 642 |
| 627 ui::MouseWheelEvent event(theEvent); | 643 ui::MouseWheelEvent event(theEvent); |
| 628 hostedView_->GetWidget()->OnMouseEvent(&event); | 644 hostedView_->GetWidget()->OnMouseEvent(&event); |
| 629 } | 645 } |
| 630 | 646 |
| 631 //////////////////////////////////////////////////////////////////////////////// | 647 //////////////////////////////////////////////////////////////////////////////// |
| 632 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the | 648 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the |
| 633 // 10.9 SDK). The list should eventually be complete. Anything not defined will | 649 // 10.9 SDK). The list should eventually be complete. Anything not defined will |
| 634 // beep when interpretKeyEvents: would otherwise call it. | 650 // beep when interpretKeyEvents: would otherwise call it. |
| 635 // TODO(tapted): Make this list complete, except for insert* methods which are | 651 // TODO(tapted): Make this list complete, except for insert* methods which are |
| 636 // dispatched as regular key events in doCommandBySelector:. | 652 // dispatched as regular key events in doCommandBySelector:. |
| 637 | 653 |
| 638 // views::Textfields are single-line only, map Paragraph and Document commands | 654 // views::Textfields are single-line only, map Paragraph and Document commands |
| 639 // to Line. Also, Up/Down commands correspond to beginning/end of line. | 655 // to Line. Also, Up/Down commands correspond to beginning/end of line. |
| 640 | 656 |
| 641 // The insertText action message forwards to the TextInputClient unless a menu | 657 // The insertText action message forwards to the TextInputClient unless a menu |
| 642 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't | 658 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't |
| 643 // direct insertText: through doCommandBySelector:, so this is still needed to | 659 // direct insertText: through doCommandBySelector:, so this is still needed to |
| 644 // handle the case when inputContext: is nil. When inputContext: returns non-nil | 660 // handle the case when inputContext: is nil. When inputContext: returns non-nil |
| 645 // text goes directly to insertText:replacementRange:. | 661 // text goes directly to insertText:replacementRange:. |
| 646 - (void)insertText:(id)text { | 662 - (void)insertText:(id)text { |
| 647 [self insertText:text replacementRange:NSMakeRange(NSNotFound, 0)]; | 663 DCHECK_EQ(nil, [self inputContext]); |
| 664 | |
| 665 // We only handle the case where no of characters is 1. Cases not handled (not | |
|
tapted
2016/06/06 04:44:05
nit: 'We only' -> 'Only'. Also this needs to say w
karandeepb
2016/06/06 11:21:10
Done.
| |
| 666 // an exhaustive list): | |
| 667 // - |text| contains a unicode surrogate pair, i.e. a single grapheme which | |
| 668 // requires two 16 bit characters. | |
| 669 // - Programmatically created events. | |
| 670 // - Input from IME. But this case should not occur since inputContext is nil. | |
| 671 if ([text length] == 1) { | |
| 672 ui::KeyEvent charEvent = GetCharacterEventFromNSEvent([NSApp currentEvent]); | |
| 673 [self handleKeyDownEvent:&charEvent]; | |
| 674 } | |
| 648 } | 675 } |
| 649 | 676 |
| 650 // Selection movement and scrolling. | 677 // Selection movement and scrolling. |
| 651 | 678 |
| 652 - (void)moveForward:(id)sender { | 679 - (void)moveForward:(id)sender { |
| 653 IsTextRTL(textInputClient_) ? [self moveLeft:sender] | 680 IsTextRTL(textInputClient_) ? [self moveLeft:sender] |
| 654 : [self moveRight:sender]; | 681 : [self moveRight:sender]; |
| 655 } | 682 } |
| 656 | 683 |
| 657 - (void)moveRight:(id)sender { | 684 - (void)moveRight:(id)sender { |
| (...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1032 | 1059 |
| 1033 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { | 1060 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { |
| 1034 NOTIMPLEMENTED(); | 1061 NOTIMPLEMENTED(); |
| 1035 return 0; | 1062 return 0; |
| 1036 } | 1063 } |
| 1037 | 1064 |
| 1038 - (void)doCommandBySelector:(SEL)selector { | 1065 - (void)doCommandBySelector:(SEL)selector { |
| 1039 // Like the renderer, handle insert action messages as a regular key dispatch. | 1066 // Like the renderer, handle insert action messages as a regular key dispatch. |
| 1040 // This ensures, e.g., insertTab correctly changes focus between fields. | 1067 // This ensures, e.g., insertTab correctly changes focus between fields. |
| 1041 if (inKeyDown_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) { | 1068 if (inKeyDown_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) { |
| 1042 [self handleKeyEvent:[NSApp currentEvent]]; | 1069 ui::KeyEvent event([NSApp currentEvent]); |
| 1070 [self handleKeyDownEvent:&event]; | |
| 1043 return; | 1071 return; |
| 1044 } | 1072 } |
| 1045 | 1073 |
| 1046 if ([self respondsToSelector:selector]) | 1074 if ([self respondsToSelector:selector]) |
| 1047 [self performSelector:selector withObject:nil]; | 1075 [self performSelector:selector withObject:nil]; |
| 1048 else | 1076 else |
| 1049 [[self nextResponder] doCommandBySelector:selector]; | 1077 [[self nextResponder] doCommandBySelector:selector]; |
| 1050 } | 1078 } |
| 1051 | 1079 |
| 1052 - (NSRect)firstRectForCharacterRange:(NSRange)range | 1080 - (NSRect)firstRectForCharacterRange:(NSRange)range |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 1063 return textInputClient_ && textInputClient_->HasCompositionText(); | 1091 return textInputClient_ && textInputClient_->HasCompositionText(); |
| 1064 } | 1092 } |
| 1065 | 1093 |
| 1066 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange { | 1094 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange { |
| 1067 if (!hostedView_) | 1095 if (!hostedView_) |
| 1068 return; | 1096 return; |
| 1069 | 1097 |
| 1070 if ([text isKindOfClass:[NSAttributedString class]]) | 1098 if ([text isKindOfClass:[NSAttributedString class]]) |
| 1071 text = [text string]; | 1099 text = [text string]; |
| 1072 | 1100 |
| 1073 MenuController* menuController = MenuController::GetActiveInstance(); | 1101 // Verify inputContext is not nil, i.e. |textInputClient_| is valid and no |
| 1074 if (menuController && menuController->owner() == hostedView_->GetWidget()) { | 1102 // menu is active. |
| 1075 // Handle menu mnemonics (e.g. "sav" jumps to "Save"). Handles both single- | 1103 DCHECK([self inputContext]); |
| 1076 // characters and input from IME. For IME, swallow the entire string unless | |
| 1077 // the very first character gives ui::POST_DISPATCH_PERFORM_DEFAULT. | |
| 1078 bool swallowedAny = false; | |
| 1079 for (NSUInteger i = 0; i < [text length]; ++i) { | |
| 1080 if (!menuController || | |
| 1081 menuController->OnWillDispatchKeyEvent([text characterAtIndex:i], | |
| 1082 ui::VKEY_UNKNOWN) == | |
| 1083 ui::POST_DISPATCH_PERFORM_DEFAULT) { | |
| 1084 if (swallowedAny) | |
| 1085 return; // Swallow remainder. | |
| 1086 break; | |
| 1087 } | |
| 1088 swallowedAny = true; | |
| 1089 // Ensure the menu remains active. | |
| 1090 menuController = MenuController::GetActiveInstance(); | |
| 1091 } | |
| 1092 } | |
| 1093 | |
| 1094 if (!textInputClient_) | |
| 1095 return; | |
| 1096 | 1104 |
| 1097 textInputClient_->DeleteRange(gfx::Range(replacementRange)); | 1105 textInputClient_->DeleteRange(gfx::Range(replacementRange)); |
| 1098 | 1106 |
| 1099 // If a single character is inserted by keyDown's call to interpretKeyEvents: | 1107 // If a single character is inserted by keyDown's call to interpretKeyEvents: |
| 1100 // then use InsertChar() to allow editing events to be merged. The second | 1108 // then use InsertChar() to allow editing events to be merged. |
| 1101 // argument is the key modifier, which interpretKeyEvents: will have already | |
| 1102 // processed, so don't send it to InsertChar() as well. E.g. Alt+S puts 'ß' in | |
| 1103 // |text| but sending 'Alt' to InsertChar would filter it out since it thinks | |
| 1104 // it's a command. Actual commands (e.g. Cmd+S) won't go through insertText:. | |
| 1105 if (inKeyDown_ && [text length] == 1) { | 1109 if (inKeyDown_ && [text length] == 1) { |
| 1106 ui::KeyEvent char_event( | 1110 textInputClient_->InsertChar( |
| 1107 [text characterAtIndex:0], | 1111 GetCharacterEventFromNSEvent([NSApp currentEvent])); |
| 1108 static_cast<ui::KeyboardCode>([text characterAtIndex:0]), ui::EF_NONE); | |
| 1109 textInputClient_->InsertChar(char_event); | |
| 1110 } else { | 1112 } else { |
| 1111 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); | 1113 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); |
| 1112 } | 1114 } |
| 1113 } | 1115 } |
| 1114 | 1116 |
| 1115 - (NSRange)markedRange { | 1117 - (NSRange)markedRange { |
| 1116 if (!textInputClient_) | 1118 if (!textInputClient_) |
| 1117 return NSMakeRange(NSNotFound, 0); | 1119 return NSMakeRange(NSNotFound, 0); |
| 1118 | 1120 |
| 1119 gfx::Range range; | 1121 gfx::Range range; |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1185 } | 1187 } |
| 1186 | 1188 |
| 1187 return [super accessibilityAttributeValue:attribute]; | 1189 return [super accessibilityAttributeValue:attribute]; |
| 1188 } | 1190 } |
| 1189 | 1191 |
| 1190 - (id)accessibilityHitTest:(NSPoint)point { | 1192 - (id)accessibilityHitTest:(NSPoint)point { |
| 1191 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; | 1193 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; |
| 1192 } | 1194 } |
| 1193 | 1195 |
| 1194 @end | 1196 @end |
| OLD | NEW |