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

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

Issue 2624093002: MacViews: Correctly handle character events when there's an active TextInputClient. (Closed)
Patch Set: Flip isKeyDownEventHandled_ -> hasUnhandledKeyDownEvent_ Created 3 years, 11 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/views/cocoa/bridged_content_view.h ('k') | ui/views/controls/combobox/combobox_unittest.cc » ('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 #import "base/mac/sdk_forward_declarations.h" 10 #import "base/mac/sdk_forward_declarations.h"
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
193 // This is a special case for which the complete string should should be 193 // This is a special case for which the complete string should should be
194 // returned. NSTextView also follows this, though the same is not mentioned in 194 // returned. NSTextView also follows this, though the same is not mentioned in
195 // NSTextInputClient documentation. 195 // NSTextInputClient documentation.
196 if (!requested_range.IsValid()) 196 if (!requested_range.IsValid())
197 *actual_range = text_range; 197 *actual_range = text_range;
198 198
199 client->GetTextFromRange(*actual_range, &substring); 199 client->GetTextFromRange(*actual_range, &substring);
200 return substring; 200 return substring;
201 } 201 }
202 202
203 // Returns a character event corresponding to |event|. |event| must be a
204 // character event itself.
205 ui::KeyEvent GetCharacterEventFromNSEvent(NSEvent* event) {
206 DCHECK([event type] == NSKeyDown || [event type] == NSKeyUp);
207 DCHECK_EQ(1u, [[event characters] length]);
208
209 // [NSEvent characters] already considers the pressed key modifiers. Hence
210 // send ui::EF_NONE as the key modifier to the KeyEvent constructor.
211 // E.g. For Alt+S, [NSEvent characters] is 'ß' and not 'S'.
212 return ui::KeyEvent([[event characters] characterAtIndex:0],
213 ui::KeyboardCodeFromNSEvent(event), ui::EF_NONE);
214 }
215
216 NSAttributedString* GetAttributedString( 203 NSAttributedString* GetAttributedString(
217 const gfx::DecoratedText& decorated_text) { 204 const gfx::DecoratedText& decorated_text) {
218 base::scoped_nsobject<NSMutableAttributedString> str( 205 base::scoped_nsobject<NSMutableAttributedString> str(
219 [[NSMutableAttributedString alloc] 206 [[NSMutableAttributedString alloc]
220 initWithString:base::SysUTF16ToNSString(decorated_text.text)]); 207 initWithString:base::SysUTF16ToNSString(decorated_text.text)]);
221 [str beginEditing]; 208 [str beginEditing];
222 209
223 NSValue* const line_style = 210 NSValue* const line_style =
224 @(NSUnderlineStyleSingle | NSUnderlinePatternSolid); 211 @(NSUnderlineStyleSingle | NSUnderlinePatternSolid);
225 212
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
455 return; 442 return;
456 443
457 DCHECK(event); 444 DCHECK(event);
458 if (DispatchEventToMenu([self activeMenuController], event)) 445 if (DispatchEventToMenu([self activeMenuController], event))
459 return; 446 return;
460 447
461 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event); 448 hostedView_->GetWidget()->GetInputMethod()->DispatchKeyEvent(event);
462 } 449 }
463 450
464 - (BOOL)handleUnhandledKeyDownAsKeyEvent { 451 - (BOOL)handleUnhandledKeyDownAsKeyEvent {
465 if (!keyDownEvent_) 452 if (!hasUnhandledKeyDownEvent_)
466 return NO; 453 return NO;
467 454
468 ui::KeyEvent event(keyDownEvent_); 455 ui::KeyEvent event(keyDownEvent_);
469 [self handleKeyEvent:&event]; 456 [self handleKeyEvent:&event];
470 keyDownEvent_ = nil; 457 hasUnhandledKeyDownEvent_ = NO;
471 return event.handled(); 458 return event.handled();
472 } 459 }
473 460
474 - (void)handleAction:(ui::TextEditCommand)command 461 - (void)handleAction:(ui::TextEditCommand)command
475 keyCode:(ui::KeyboardCode)keyCode 462 keyCode:(ui::KeyboardCode)keyCode
476 domCode:(ui::DomCode)domCode 463 domCode:(ui::DomCode)domCode
477 eventFlags:(int)eventFlags { 464 eventFlags:(int)eventFlags {
478 if (!hostedView_) 465 if (!hostedView_)
479 return; 466 return;
480 467
(...skipping 18 matching lines...) Expand all
499 } 486 }
500 487
501 - (void)insertTextInternal:(id)text { 488 - (void)insertTextInternal:(id)text {
502 if (!hostedView_) 489 if (!hostedView_)
503 return; 490 return;
504 491
505 if ([text isKindOfClass:[NSAttributedString class]]) 492 if ([text isKindOfClass:[NSAttributedString class]])
506 text = [text string]; 493 text = [text string];
507 494
508 bool isCharacterEvent = keyDownEvent_ && [text length] == 1; 495 bool isCharacterEvent = keyDownEvent_ && [text length] == 1;
496 // Pass the character event to the View hierarchy. Cases this handles (non-
497 // exhaustive)-
498 // - Space key press on controls. Unlike Tab and newline which have
499 // corresponding action messages, an insertText: message is generated for
500 // the Space key (insertText:replacementRange: when there's an active
501 // input context).
502 // - Menu mnemonic selection.
503 // Note we create a custom character ui::KeyEvent (and not use the
504 // ui::KeyEvent(NSEvent*) constructor) since we can't just rely on the event
505 // key code to get the actual characters from the ui::KeyEvent. This for
506 // example is necessary for menu mnemonic selection of non-latin text.
507
508 // Don't generate a key event when there is active composition text. These key
509 // down events should be consumed by the IME and not reach the Views layer.
510 // For example, on pressing Return to commit composition text, if we passed a
511 // synthetic key event to the View hierarchy, it will have the effect of
512 // performing the default action on the current dialog. We do not want this.
513
514 // Also note that a single key down event can cause multiple
515 // insertText:replacementRange: action messages. Example, on pressing Alt+e,
516 // the accent (´) character is composed via setMarkedText:. Now on pressing
517 // the character 'r', two insertText:replacementRange: action messages are
518 // generated with the text value of accent (´) and 'r' respectively. The key
519 // down event will have characters field of length 2. The first of these
520 // insertText messages won't generate a KeyEvent since there'll be active
521 // marked text. However, a KeyEvent will be generated corresponding to 'r'.
522
523 // Currently there seems to be no use case to pass non-character events routed
524 // from insertText: handlers to the View hierarchy.
525 if (isCharacterEvent && ![self hasMarkedText]) {
526 ui::KeyEvent charEvent([text characterAtIndex:0],
527 ui::KeyboardCodeFromNSEvent(keyDownEvent_),
528 ui::EF_NONE);
529 [self handleKeyEvent:&charEvent];
530 hasUnhandledKeyDownEvent_ = NO;
531 }
509 532
510 // Forward the |text| to |textInputClient_| if no menu is active. 533 // Forward the |text| to |textInputClient_| if no menu is active.
511 if (textInputClient_ && ![self activeMenuController]) { 534 if (textInputClient_ && ![self activeMenuController]) {
512 // If a single character is inserted by keyDown's call to 535 // If a single character is inserted by keyDown's call to
513 // interpretKeyEvents: then use InsertChar() to allow editing events to be 536 // interpretKeyEvents: then use InsertChar() to allow editing events to be
514 // merged. We use ui::VKEY_UNKNOWN as the key code since it's not feasible 537 // merged. We use ui::VKEY_UNKNOWN as the key code since it's not feasible
515 // to determine the correct key code for each unicode character. Also a 538 // to determine the correct key code for each unicode character. Also a
516 // correct keycode is not needed in the current context. Send ui::EF_NONE as 539 // correct keycode is not needed in the current context. Send ui::EF_NONE as
517 // the key modifier since |text| already accounts for the pressed key 540 // the key modifier since |text| already accounts for the pressed key
518 // modifiers. 541 // modifiers.
519 542
520 // Also, note we don't use |keyDownEvent_| to generate the synthetic 543 // Also, note we don't use |keyDownEvent_| to generate the synthetic
521 // ui::KeyEvent since for text inserted using an IME, [keyDownEvent_ 544 // ui::KeyEvent since for composed text, [keyDownEvent_ characters] might
522 // characters] might not be the same as |text|. This is because 545 // not be the same as |text|. This is because |keyDownEvent_| will
523 // |keyDownEvent_| will correspond to the event that caused the composition 546 // correspond to the event that caused the composition text to be confirmed,
524 // text to be confirmed, say, Return key press. 547 // say, Return key press.
525 if (isCharacterEvent) { 548 if (isCharacterEvent) {
526 textInputClient_->InsertChar(ui::KeyEvent([text characterAtIndex:0], 549 textInputClient_->InsertChar(ui::KeyEvent([text characterAtIndex:0],
527 ui::VKEY_UNKNOWN, ui::EF_NONE)); 550 ui::VKEY_UNKNOWN, ui::EF_NONE));
528 } else { 551 } else {
529 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); 552 textInputClient_->InsertText(base::SysNSStringToUTF16(text));
530 } 553 }
531 554 hasUnhandledKeyDownEvent_ = NO;
532 keyDownEvent_ = nil; // Handled.
533 return;
534 }
535
536 // Only handle the case where no. of characters is 1. Cases not handled (not
537 // an exhaustive list):
538 // - |text| contains a unicode surrogate pair, i.e. a single grapheme which
539 // requires two 16 bit characters. Currently Views menu only supports
540 // mnemonics using a single 16 bit character, so it is ok to ignore this
541 // case.
542 // - Programmatically created events.
543 // - Input from IME. But this case should not occur since inputContext is
544 // nil.
545 if (isCharacterEvent) {
546 ui::KeyEvent charEvent = GetCharacterEventFromNSEvent(keyDownEvent_);
547 [self handleKeyEvent:&charEvent];
548 keyDownEvent_ = nil; // Handled.
549 } 555 }
550 } 556 }
551 557
552 - (views::DragDropClientMac*)dragDropClient { 558 - (views::DragDropClientMac*)dragDropClient {
553 views::BridgedNativeWidget* bridge = 559 views::BridgedNativeWidget* bridge =
554 views::NativeWidgetMac::GetBridgeForNativeWindow([self window]); 560 views::NativeWidgetMac::GetBridgeForNativeWindow([self window]);
555 return bridge ? bridge->drag_drop_client() : nullptr; 561 return bridge ? bridge->drag_drop_client() : nullptr;
556 } 562 }
557 563
558 - (void)undo:(id)sender { 564 - (void)undo:(id)sender {
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after
797 // correctly, etc. 803 // correctly, etc.
798 // (However, there are still some keys that Cocoa swallows, e.g. the key 804 // (However, there are still some keys that Cocoa swallows, e.g. the key
799 // equivalent that Cocoa uses for toggling the input language. In this case, 805 // equivalent that Cocoa uses for toggling the input language. In this case,
800 // that's actually a good thing, though -- see http://crbug.com/26115 .) 806 // that's actually a good thing, though -- see http://crbug.com/26115 .)
801 return YES; 807 return YES;
802 } 808 }
803 809
804 - (void)keyDown:(NSEvent*)theEvent { 810 - (void)keyDown:(NSEvent*)theEvent {
805 // Convert the event into an action message, according to OSX key mappings. 811 // Convert the event into an action message, according to OSX key mappings.
806 keyDownEvent_ = theEvent; 812 keyDownEvent_ = theEvent;
813 hasUnhandledKeyDownEvent_ = YES;
807 [self interpretKeyEvents:@[ theEvent ]]; 814 [self interpretKeyEvents:@[ theEvent ]];
808 815
809 // If |keyDownEvent_| wasn't cleared during -interpretKeyEvents:, it wasn't 816 // If |keyDownEvent_| wasn't cleared during -interpretKeyEvents:, it wasn't
810 // handled. Give Widget accelerators a chance to handle it. 817 // handled. Give Widget accelerators a chance to handle it.
811 [self handleUnhandledKeyDownAsKeyEvent]; 818 [self handleUnhandledKeyDownAsKeyEvent];
812 DCHECK(!keyDownEvent_); 819 DCHECK(!hasUnhandledKeyDownEvent_);
820 keyDownEvent_ = nil;
813 } 821 }
814 822
815 - (void)keyUp:(NSEvent*)theEvent { 823 - (void)keyUp:(NSEvent*)theEvent {
816 ui::KeyEvent event(theEvent); 824 ui::KeyEvent event(theEvent);
817 [self handleKeyEvent:&event]; 825 [self handleKeyEvent:&event];
818 } 826 }
819 827
820 - (void)scrollWheel:(NSEvent*)theEvent { 828 - (void)scrollWheel:(NSEvent*)theEvent {
821 if (!hostedView_) 829 if (!hostedView_)
822 return; 830 return;
(...skipping 503 matching lines...) Expand 10 before | Expand all | Expand 10 after
1326 } 1334 }
1327 1335
1328 - (void)doCommandBySelector:(SEL)selector { 1336 - (void)doCommandBySelector:(SEL)selector {
1329 // Like the renderer, handle insert action messages as a regular key dispatch. 1337 // Like the renderer, handle insert action messages as a regular key dispatch.
1330 // This ensures, e.g., insertTab correctly changes focus between fields. 1338 // This ensures, e.g., insertTab correctly changes focus between fields.
1331 if (keyDownEvent_ && [NSStringFromSelector(selector) hasPrefix:@"insert"]) 1339 if (keyDownEvent_ && [NSStringFromSelector(selector) hasPrefix:@"insert"])
1332 return; // Handle in -keyDown:. 1340 return; // Handle in -keyDown:.
1333 1341
1334 if ([self respondsToSelector:selector]) { 1342 if ([self respondsToSelector:selector]) {
1335 [self performSelector:selector withObject:nil]; 1343 [self performSelector:selector withObject:nil];
1336 keyDownEvent_ = nil; 1344 hasUnhandledKeyDownEvent_ = NO;
1337 return; 1345 return;
1338 } 1346 }
1339 1347
1340 // For events that AppKit sends via doCommandBySelector:, first attempt to 1348 // For events that AppKit sends via doCommandBySelector:, first attempt to
1341 // handle as a Widget accelerator. Forward along the responder chain only if 1349 // handle as a Widget accelerator. Forward along the responder chain only if
1342 // the Widget doesn't handle it. 1350 // the Widget doesn't handle it.
1343 if (![self handleUnhandledKeyDownAsKeyEvent]) 1351 if (![self handleUnhandledKeyDownAsKeyEvent])
1344 [[self nextResponder] doCommandBySelector:selector]; 1352 [[self nextResponder] doCommandBySelector:selector];
1345 } 1353 }
1346 1354
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
1405 // Add a black underline with a transparent background to the composition 1413 // Add a black underline with a transparent background to the composition
1406 // text. TODO(karandeepb): On Cocoa textfields, the target clause of the 1414 // text. TODO(karandeepb): On Cocoa textfields, the target clause of the
1407 // composition has a thick underlines. The composition text also has 1415 // composition has a thick underlines. The composition text also has
1408 // discontinous underlines for different clauses. This is also supported in 1416 // discontinous underlines for different clauses. This is also supported in
1409 // the Chrome renderer. Add code to extract underlines from |text| once our 1417 // the Chrome renderer. Add code to extract underlines from |text| once our
1410 // render text implementation supports thick underlines and discontinous 1418 // render text implementation supports thick underlines and discontinous
1411 // underlines for consecutive characters. See http://crbug.com/612675. 1419 // underlines for consecutive characters. See http://crbug.com/612675.
1412 composition.underlines.push_back(ui::CompositionUnderline( 1420 composition.underlines.push_back(ui::CompositionUnderline(
1413 0, [text length], SK_ColorBLACK, false, SK_ColorTRANSPARENT)); 1421 0, [text length], SK_ColorBLACK, false, SK_ColorTRANSPARENT));
1414 textInputClient_->SetCompositionText(composition); 1422 textInputClient_->SetCompositionText(composition);
1415 keyDownEvent_ = nil; // Handled. 1423 hasUnhandledKeyDownEvent_ = NO;
1416 } 1424 }
1417 1425
1418 - (void)unmarkText { 1426 - (void)unmarkText {
1419 if (!textInputClient_) 1427 if (!textInputClient_)
1420 return; 1428 return;
1421 1429
1422 textInputClient_->ConfirmCompositionText(); 1430 textInputClient_->ConfirmCompositionText();
1423 keyDownEvent_ = nil; // Handled. 1431 hasUnhandledKeyDownEvent_ = NO;
1424 } 1432 }
1425 1433
1426 - (NSArray*)validAttributesForMarkedText { 1434 - (NSArray*)validAttributesForMarkedText {
1427 return @[]; 1435 return @[];
1428 } 1436 }
1429 1437
1430 // NSUserInterfaceValidations protocol implementation. 1438 // NSUserInterfaceValidations protocol implementation.
1431 1439
1432 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { 1440 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
1433 ui::TextEditCommand command = GetTextEditCommandForMenuAction([item action]); 1441 ui::TextEditCommand command = GetTextEditCommandForMenuAction([item action]);
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
1480 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; 1488 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point];
1481 } 1489 }
1482 1490
1483 - (id)accessibilityFocusedUIElement { 1491 - (id)accessibilityFocusedUIElement {
1484 if (!hostedView_) 1492 if (!hostedView_)
1485 return nil; 1493 return nil;
1486 return [hostedView_->GetNativeViewAccessible() accessibilityFocusedUIElement]; 1494 return [hostedView_->GetNativeViewAccessible() accessibilityFocusedUIElement];
1487 } 1495 }
1488 1496
1489 @end 1497 @end
OLDNEW
« no previous file with comments | « ui/views/cocoa/bridged_content_view.h ('k') | ui/views/controls/combobox/combobox_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698