Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(449)

Side by Side Diff: ui/views/cocoa/bridged_content_view.mm

Issue 2033433006: MacViews: Modify insertText handlers to correctly handle space key and simplify menu event dispatch. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix failing interactive_ui_tests. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « ui/views/cocoa/bridged_content_view.h ('k') | ui/views/controls/button/custom_button_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698