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

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: Changes to comments. 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 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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | ui/views/controls/menu/menu_controller.h » ('j') | ui/views/controls/menu/menu_controller.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698