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

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: 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
« no previous file with comments | « ui/base/test/ui_controls_mac.mm ('k') | ui/views/controls/menu/menu_controller.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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 base::char16 character = [[event characters] characterAtIndex:0];
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(character, static_cast<ui::KeyboardCode>(character),
210 ui::EF_NONE);
211 }
212
200 } // namespace 213 } // namespace
201 214
202 @interface BridgedContentView () 215 @interface BridgedContentView ()
203 216
204 // Translates keycodes and modifiers on |theEvent| to ui::KeyEvents and passes 217 // Passes |event| to the InputMethod for dispatch.
205 // the event to the InputMethod for dispatch. 218 - (void)handleKeyDownEvent:(ui::KeyEvent*)event;
206 - (void)handleKeyEvent:(NSEvent*)theEvent;
207 219
208 // 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
209 // 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
210 // TextInputClient, the keyCode that toolkit-views expects internally. 222 // TextInputClient, the keyCode that toolkit-views expects internally.
211 // 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
212 // even though the Home key on Mac defaults to moveToBeginningOfDocument:. 224 // even though the Home key on Mac defaults to moveToBeginningOfDocument:.
213 // This approach also allows action messages a user 225 // This approach also allows action messages a user
214 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be 226 // may have remapped in ~/Library/KeyBindings/DefaultKeyBinding.dict to be
215 // catered for. 227 // catered for.
216 // 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 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
350 return; 362 return;
351 363
352 views::FocusManager* focusManager = 364 views::FocusManager* focusManager =
353 hostedView_->GetWidget()->GetFocusManager(); 365 hostedView_->GetWidget()->GetFocusManager();
354 if (focusManager) 366 if (focusManager)
355 focusManager->SetKeyboardAccessible([NSApp isFullKeyboardAccessEnabled]); 367 focusManager->SetKeyboardAccessible([NSApp isFullKeyboardAccessEnabled]);
356 } 368 }
357 369
358 // BridgedContentView private implementation. 370 // BridgedContentView private implementation.
359 371
360 - (void)handleKeyEvent:(NSEvent*)theEvent { 372 - (void)handleKeyDownEvent:(ui::KeyEvent*)event {
361 if (!hostedView_) 373 if (!hostedView_)
362 return; 374 return;
363 375 DCHECK(event);
364 DCHECK(theEvent); 376 if (DispatchEventToMenu(hostedView_->GetWidget(), *event))
365 ui::KeyEvent event(theEvent);
366 if (DispatchEventToMenu(hostedView_->GetWidget(), event.key_code()))
367 return; 377 return;
368 378
369 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event); 379 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event);
370 } 380 }
371 381
372 - (void)handleAction:(int)commandId 382 - (void)handleAction:(int)commandId
373 keyCode:(ui::KeyboardCode)keyCode 383 keyCode:(ui::KeyboardCode)keyCode
374 domCode:(ui::DomCode)domCode 384 domCode:(ui::DomCode)domCode
375 eventFlags:(int)eventFlags { 385 eventFlags:(int)eventFlags {
376 if (!hostedView_) 386 if (!hostedView_)
377 return; 387 return;
378 388
379 if (DispatchEventToMenu(hostedView_->GetWidget(), keyCode)) 389 // Generate a synthetic event with the keycode toolkit-views expects.
390 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags);
391
392 if (DispatchEventToMenu(hostedView_->GetWidget(), event))
380 return; 393 return;
381 394
382 // If there's an active TextInputClient, schedule the editing command to be 395 // If there's an active TextInputClient, schedule the editing command to be
383 // performed. 396 // performed.
384 if (commandId && textInputClient_ && 397 if (commandId && textInputClient_ &&
385 textInputClient_->IsEditCommandEnabled(commandId)) 398 textInputClient_->IsEditCommandEnabled(commandId))
386 textInputClient_->SetEditCommandForNextKeyEvent(commandId); 399 textInputClient_->SetEditCommandForNextKeyEvent(commandId);
387 400
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); 401 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event);
391 } 402 }
392 403
393 - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification { 404 - (void)onFullKeyboardAccessModeChanged:(NSNotification*)notification {
394 DCHECK([[notification name] 405 DCHECK([[notification name]
395 isEqualToString:kFullKeyboardAccessChangedNotification]); 406 isEqualToString:kFullKeyboardAccessChangedNotification]);
396 [self updateFullKeyboardAccess]; 407 [self updateFullKeyboardAccess];
397 } 408 }
398 409
399 - (void)undo:(id)sender { 410 - (void)undo:(id)sender {
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
613 624
614 // NSResponder implementation. 625 // NSResponder implementation.
615 626
616 - (void)keyDown:(NSEvent*)theEvent { 627 - (void)keyDown:(NSEvent*)theEvent {
617 // Convert the event into an action message, according to OSX key mappings. 628 // Convert the event into an action message, according to OSX key mappings.
618 inKeyDown_ = YES; 629 inKeyDown_ = YES;
619 [self interpretKeyEvents:@[ theEvent ]]; 630 [self interpretKeyEvents:@[ theEvent ]];
620 inKeyDown_ = NO; 631 inKeyDown_ = NO;
621 } 632 }
622 633
634 - (void)keyUp:(NSEvent*)theEvent {
635 // Generate a synthetic ui::KeyEvent for toolkit-views.
karandeepb 2016/06/06 02:04:42 Since keyUp wasn't implemented till now, Views::On
636 ui::KeyEvent event(theEvent);
637 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(&event);
638 }
639
623 - (void)scrollWheel:(NSEvent*)theEvent { 640 - (void)scrollWheel:(NSEvent*)theEvent {
624 if (!hostedView_) 641 if (!hostedView_)
625 return; 642 return;
626 643
627 ui::MouseWheelEvent event(theEvent); 644 ui::MouseWheelEvent event(theEvent);
628 hostedView_->GetWidget()->OnMouseEvent(&event); 645 hostedView_->GetWidget()->OnMouseEvent(&event);
629 } 646 }
630 647
631 //////////////////////////////////////////////////////////////////////////////// 648 ////////////////////////////////////////////////////////////////////////////////
632 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the 649 // NSResponder Action Messages. Keep sorted according NSResponder.h (from the
633 // 10.9 SDK). The list should eventually be complete. Anything not defined will 650 // 10.9 SDK). The list should eventually be complete. Anything not defined will
634 // beep when interpretKeyEvents: would otherwise call it. 651 // beep when interpretKeyEvents: would otherwise call it.
635 // TODO(tapted): Make this list complete, except for insert* methods which are 652 // TODO(tapted): Make this list complete, except for insert* methods which are
636 // dispatched as regular key events in doCommandBySelector:. 653 // dispatched as regular key events in doCommandBySelector:.
637 654
638 // views::Textfields are single-line only, map Paragraph and Document commands 655 // views::Textfields are single-line only, map Paragraph and Document commands
639 // to Line. Also, Up/Down commands correspond to beginning/end of line. 656 // to Line. Also, Up/Down commands correspond to beginning/end of line.
640 657
641 // The insertText action message forwards to the TextInputClient unless a menu 658 // The insertText action message forwards to the TextInputClient unless a menu
642 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't 659 // is active. Note that NSResponder's interpretKeyEvents: implementation doesn't
643 // direct insertText: through doCommandBySelector:, so this is still needed to 660 // direct insertText: through doCommandBySelector:, so this is still needed to
644 // handle the case when inputContext: is nil. When inputContext: returns non-nil 661 // handle the case when inputContext: is nil. When inputContext: returns non-nil
645 // text goes directly to insertText:replacementRange:. 662 // text goes directly to insertText:replacementRange:.
646 - (void)insertText:(id)text { 663 - (void)insertText:(id)text {
647 [self insertText:text replacementRange:NSMakeRange(NSNotFound, 0)]; 664 // This is only to prevent crash in case of tests.
665 if(!text)
666 return;
667
668 // For a string of length 1, invoke handleKeyDownEvent, so that a synthetic
669 // ui::KeyEvent is generated.
670 // TODO(karandeepb): Is there a case where [text length] != 1, since an IME
671 // should not be active when inputContext is nil.
672 DCHECK_EQ(nil, [self inputContext]);
673 if ([text length] == 1) {
674 ui::KeyEvent charEvent = GetCharacterEventFromNSEvent([NSApp currentEvent]);
675 [self handleKeyDownEvent:&charEvent];
676 }
677 else {
678 NOTREACHED();
679 }
648 } 680 }
649 681
650 // Selection movement and scrolling. 682 // Selection movement and scrolling.
651 683
652 - (void)moveForward:(id)sender { 684 - (void)moveForward:(id)sender {
653 IsTextRTL(textInputClient_) ? [self moveLeft:sender] 685 IsTextRTL(textInputClient_) ? [self moveLeft:sender]
654 : [self moveRight:sender]; 686 : [self moveRight:sender];
655 } 687 }
656 688
657 - (void)moveRight:(id)sender { 689 - (void)moveRight:(id)sender {
(...skipping 374 matching lines...) Expand 10 before | Expand all | Expand 10 after
1032 1064
1033 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { 1065 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint {
1034 NOTIMPLEMENTED(); 1066 NOTIMPLEMENTED();
1035 return 0; 1067 return 0;
1036 } 1068 }
1037 1069
1038 - (void)doCommandBySelector:(SEL)selector { 1070 - (void)doCommandBySelector:(SEL)selector {
1039 // Like the renderer, handle insert action messages as a regular key dispatch. 1071 // Like the renderer, handle insert action messages as a regular key dispatch.
1040 // This ensures, e.g., insertTab correctly changes focus between fields. 1072 // This ensures, e.g., insertTab correctly changes focus between fields.
1041 if (inKeyDown_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) { 1073 if (inKeyDown_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) {
1042 [self handleKeyEvent:[NSApp currentEvent]]; 1074 ui::KeyEvent event([NSApp currentEvent]);
1075 [self handleKeyDownEvent:&event];
1043 return; 1076 return;
1044 } 1077 }
1045 1078
1046 if ([self respondsToSelector:selector]) 1079 if ([self respondsToSelector:selector])
1047 [self performSelector:selector withObject:nil]; 1080 [self performSelector:selector withObject:nil];
1048 else 1081 else
1049 [[self nextResponder] doCommandBySelector:selector]; 1082 [[self nextResponder] doCommandBySelector:selector];
1050 } 1083 }
1051 1084
1052 - (NSRect)firstRectForCharacterRange:(NSRange)range 1085 - (NSRect)firstRectForCharacterRange:(NSRange)range
(...skipping 10 matching lines...) Expand all
1063 return textInputClient_ && textInputClient_->HasCompositionText(); 1096 return textInputClient_ && textInputClient_->HasCompositionText();
1064 } 1097 }
1065 1098
1066 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange { 1099 - (void)insertText:(id)text replacementRange:(NSRange)replacementRange {
1067 if (!hostedView_) 1100 if (!hostedView_)
1068 return; 1101 return;
1069 1102
1070 if ([text isKindOfClass:[NSAttributedString class]]) 1103 if ([text isKindOfClass:[NSAttributedString class]])
1071 text = [text string]; 1104 text = [text string];
1072 1105
1073 MenuController* menuController = MenuController::GetActiveInstance(); 1106 // Verify inputContext is not nil, i.e. |textInputClient_| is valid and no
1074 if (menuController && menuController->owner() == hostedView_->GetWidget()) { 1107 // menu is active.
1075 // Handle menu mnemonics (e.g. "sav" jumps to "Save"). Handles both single- 1108 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) ==
karandeepb 2016/06/06 02:04:42 Can IME's be active in case of an active menu, as
tapted 2016/06/06 04:44:05 That does kinda seem like something that should be
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 1109
1097 textInputClient_->DeleteRange(gfx::Range(replacementRange)); 1110 textInputClient_->DeleteRange(gfx::Range(replacementRange));
1098 1111
1099 // If a single character is inserted by keyDown's call to interpretKeyEvents: 1112 // 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 1113 // 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) { 1114 if (inKeyDown_ && [text length] == 1) {
1106 ui::KeyEvent char_event( 1115 textInputClient_->InsertChar(
1107 [text characterAtIndex:0], 1116 GetCharacterEventFromNSEvent([NSApp currentEvent]));
1108 static_cast<ui::KeyboardCode>([text characterAtIndex:0]), ui::EF_NONE);
1109 textInputClient_->InsertChar(char_event);
1110 } else { 1117 } else {
1111 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); 1118 textInputClient_->InsertText(base::SysNSStringToUTF16(text));
1112 } 1119 }
1113 } 1120 }
1114 1121
1115 - (NSRange)markedRange { 1122 - (NSRange)markedRange {
1116 if (!textInputClient_) 1123 if (!textInputClient_)
1117 return NSMakeRange(NSNotFound, 0); 1124 return NSMakeRange(NSNotFound, 0);
1118 1125
1119 gfx::Range range; 1126 gfx::Range range;
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
1185 } 1192 }
1186 1193
1187 return [super accessibilityAttributeValue:attribute]; 1194 return [super accessibilityAttributeValue:attribute];
1188 } 1195 }
1189 1196
1190 - (id)accessibilityHitTest:(NSPoint)point { 1197 - (id)accessibilityHitTest:(NSPoint)point {
1191 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; 1198 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];
1192 } 1199 }
1193 1200
1194 @end 1201 @end
OLDNEW
« no previous file with comments | « ui/base/test/ui_controls_mac.mm ('k') | ui/views/controls/menu/menu_controller.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698