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/scoped_nsobject.h" | 8 #import "base/mac/scoped_nsobject.h" |
9 #include "base/strings/sys_string_conversions.h" | 9 #include "base/strings/sys_string_conversions.h" |
10 #include "ui/base/ime/text_input_client.h" | 10 #include "ui/base/ime/text_input_client.h" |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
58 // catered for. | 58 // catered for. |
59 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict | 59 // Note: default key bindings in Mac can be read from StandardKeyBinding.dict |
60 // which lives in /System/Library/Frameworks/AppKit.framework/Resources. Do | 60 // which lives in /System/Library/Frameworks/AppKit.framework/Resources. Do |
61 // `plutil -convert xml1 -o StandardKeyBinding.xml StandardKeyBinding.dict` to | 61 // `plutil -convert xml1 -o StandardKeyBinding.xml StandardKeyBinding.dict` to |
62 // get something readable. | 62 // get something readable. |
63 - (void)handleAction:(int)commandId | 63 - (void)handleAction:(int)commandId |
64 keyCode:(ui::KeyboardCode)keyCode | 64 keyCode:(ui::KeyboardCode)keyCode |
65 domCode:(ui::DomCode)domCode | 65 domCode:(ui::DomCode)domCode |
66 eventFlags:(int)eventFlags; | 66 eventFlags:(int)eventFlags; |
67 | 67 |
| 68 // Menu action handlers. |
| 69 - (void)undo:(id)sender; |
| 70 - (void)redo:(id)sender; |
| 71 - (void)cut:(id)sender; |
| 72 - (void)copy:(id)sender; |
| 73 - (void)paste:(id)sender; |
| 74 - (void)selectAll:(id)sender; |
| 75 |
68 @end | 76 @end |
69 | 77 |
70 @implementation BridgedContentView | 78 @implementation BridgedContentView |
71 | 79 |
72 @synthesize hostedView = hostedView_; | 80 @synthesize hostedView = hostedView_; |
73 @synthesize textInputClient = textInputClient_; | 81 @synthesize textInputClient = textInputClient_; |
74 | 82 |
75 - (id)initWithView:(views::View*)viewToHost { | 83 - (id)initWithView:(views::View*)viewToHost { |
76 DCHECK(viewToHost); | 84 DCHECK(viewToHost); |
77 gfx::Rect bounds = viewToHost->bounds(); | 85 gfx::Rect bounds = viewToHost->bounds(); |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
140 | 148 |
141 // If there's an active MenuController it gets preference, and it will likely | 149 // If there's an active MenuController it gets preference, and it will likely |
142 // swallow the event. | 150 // swallow the event. |
143 MenuController* menuController = MenuController::GetActiveInstance(); | 151 MenuController* menuController = MenuController::GetActiveInstance(); |
144 if (menuController && menuController->owner() == hostedView_->GetWidget()) { | 152 if (menuController && menuController->owner() == hostedView_->GetWidget()) { |
145 if (menuController->OnWillDispatchKeyEvent(0, keyCode) == | 153 if (menuController->OnWillDispatchKeyEvent(0, keyCode) == |
146 ui::POST_DISPATCH_NONE) | 154 ui::POST_DISPATCH_NONE) |
147 return; | 155 return; |
148 } | 156 } |
149 | 157 |
| 158 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags); |
| 159 // If the text input's controller soaks up the event, bail. |
| 160 if (textInputClient_ && textInputClient_->HandleAsKeyEventOnly(event)) |
| 161 return; |
| 162 |
150 // If there's an active TextInputClient, it ignores the key and processes the | 163 // If there's an active TextInputClient, it ignores the key and processes the |
151 // logical editing action. | 164 // logical editing action. Ignores |event|. |
152 if (commandId && textInputClient_ && | 165 if (commandId && textInputClient_ && |
153 textInputClient_->IsEditingCommandEnabled(commandId)) { | 166 textInputClient_->IsEditingCommandEnabled(commandId)) { |
154 textInputClient_->ExecuteEditingCommand(commandId); | 167 textInputClient_->ExecuteEditingCommand(commandId); |
155 return; | 168 return; |
156 } | 169 } |
157 | 170 |
158 // Otherwise, process the action as a regular key event. | 171 // Otherwise, process the action as a regular key event. |
159 ui::KeyEvent event(ui::ET_KEY_PRESSED, keyCode, domCode, eventFlags); | |
160 hostedView_->GetWidget()->OnKeyEvent(&event); | 172 hostedView_->GetWidget()->OnKeyEvent(&event); |
161 } | 173 } |
162 | 174 |
| 175 - (void)undo:(id)sender { |
| 176 // This DCHECK is more strict than a similar check in handleAction:. It can be |
| 177 // done here because the actors sending these actions should be calling |
| 178 // validateUserInterfaceItem: before enabling UI that allows these messages to |
| 179 // be sent. Checking it here would be too late to provide correct UI feedback |
| 180 // (e.g. there will be no "beep"). |
| 181 DCHECK(textInputClient_->IsEditingCommandEnabled(IDS_APP_UNDO)); |
| 182 [self handleAction:IDS_APP_UNDO |
| 183 keyCode:ui::VKEY_Z |
| 184 domCode:ui::DomCode::KEY_Z |
| 185 eventFlags:ui::EF_CONTROL_DOWN]; |
| 186 } |
| 187 |
| 188 - (void)redo:(id)sender { |
| 189 DCHECK(textInputClient_->IsEditingCommandEnabled(IDS_APP_REDO)); |
| 190 [self handleAction:IDS_APP_REDO |
| 191 keyCode:ui::VKEY_Y |
| 192 domCode:ui::DomCode::KEY_Y |
| 193 eventFlags:ui::EF_CONTROL_DOWN]; |
| 194 } |
| 195 |
| 196 - (void)cut:(id)sender { |
| 197 DCHECK(textInputClient_->IsEditingCommandEnabled(IDS_APP_CUT)); |
| 198 [self handleAction:IDS_APP_CUT |
| 199 keyCode:ui::VKEY_X |
| 200 domCode:ui::DomCode::KEY_X |
| 201 eventFlags:ui::EF_CONTROL_DOWN]; |
| 202 } |
| 203 |
| 204 - (void)copy:(id)sender { |
| 205 DCHECK(textInputClient_->IsEditingCommandEnabled(IDS_APP_COPY)); |
| 206 [self handleAction:IDS_APP_COPY |
| 207 keyCode:ui::VKEY_C |
| 208 domCode:ui::DomCode::KEY_C |
| 209 eventFlags:ui::EF_CONTROL_DOWN]; |
| 210 } |
| 211 |
| 212 - (void)paste:(id)sender { |
| 213 DCHECK(textInputClient_->IsEditingCommandEnabled(IDS_APP_PASTE)); |
| 214 [self handleAction:IDS_APP_PASTE |
| 215 keyCode:ui::VKEY_V |
| 216 domCode:ui::DomCode::KEY_V |
| 217 eventFlags:ui::EF_CONTROL_DOWN]; |
| 218 } |
| 219 |
| 220 - (void)selectAll:(id)sender { |
| 221 DCHECK(textInputClient_->IsEditingCommandEnabled(IDS_APP_SELECT_ALL)); |
| 222 [self handleAction:IDS_APP_SELECT_ALL |
| 223 keyCode:ui::VKEY_A |
| 224 domCode:ui::DomCode::KEY_A |
| 225 eventFlags:ui::EF_CONTROL_DOWN]; |
| 226 } |
| 227 |
163 // NSView implementation. | 228 // NSView implementation. |
164 | 229 |
165 - (BOOL)acceptsFirstResponder { | 230 - (BOOL)acceptsFirstResponder { |
166 return YES; | 231 return YES; |
167 } | 232 } |
168 | 233 |
169 - (void)setFrameSize:(NSSize)newSize { | 234 - (void)setFrameSize:(NSSize)newSize { |
170 [super setFrameSize:newSize]; | 235 [super setFrameSize:newSize]; |
171 if (!hostedView_) | 236 if (!hostedView_) |
172 return; | 237 return; |
(...skipping 26 matching lines...) Expand all Loading... |
199 if (menuController && menuController->owner() == hostedView_->GetWidget()) | 264 if (menuController && menuController->owner() == hostedView_->GetWidget()) |
200 return nil; | 265 return nil; |
201 | 266 |
202 return [super inputContext]; | 267 return [super inputContext]; |
203 } | 268 } |
204 | 269 |
205 // NSResponder implementation. | 270 // NSResponder implementation. |
206 | 271 |
207 - (void)keyDown:(NSEvent*)theEvent { | 272 - (void)keyDown:(NSEvent*)theEvent { |
208 // Convert the event into an action message, according to OSX key mappings. | 273 // Convert the event into an action message, according to OSX key mappings. |
| 274 inKeyDown_ = YES; |
209 [self interpretKeyEvents:@[ theEvent ]]; | 275 [self interpretKeyEvents:@[ theEvent ]]; |
| 276 inKeyDown_= NO; |
210 } | 277 } |
211 | 278 |
212 - (void)mouseDown:(NSEvent*)theEvent { | 279 - (void)mouseDown:(NSEvent*)theEvent { |
213 [self handleMouseEvent:theEvent]; | 280 [self handleMouseEvent:theEvent]; |
214 } | 281 } |
215 | 282 |
216 - (void)rightMouseDown:(NSEvent*)theEvent { | 283 - (void)rightMouseDown:(NSEvent*)theEvent { |
217 [self handleMouseEvent:theEvent]; | 284 [self handleMouseEvent:theEvent]; |
218 } | 285 } |
219 | 286 |
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
478 } | 545 } |
479 | 546 |
480 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { | 547 - (NSUInteger)characterIndexForPoint:(NSPoint)aPoint { |
481 NOTIMPLEMENTED(); | 548 NOTIMPLEMENTED(); |
482 return 0; | 549 return 0; |
483 } | 550 } |
484 | 551 |
485 - (void)doCommandBySelector:(SEL)selector { | 552 - (void)doCommandBySelector:(SEL)selector { |
486 if ([self respondsToSelector:selector]) | 553 if ([self respondsToSelector:selector]) |
487 [self performSelector:selector withObject:nil]; | 554 [self performSelector:selector withObject:nil]; |
488 else | 555 else { |
| 556 NSLog(@"Unhandled: %@\n", NSStringFromSelector(selector)); |
489 [[self nextResponder] doCommandBySelector:selector]; | 557 [[self nextResponder] doCommandBySelector:selector]; |
| 558 } |
490 } | 559 } |
491 | 560 |
492 - (NSRect)firstRectForCharacterRange:(NSRange)range | 561 - (NSRect)firstRectForCharacterRange:(NSRange)range |
493 actualRange:(NSRangePointer)actualRange { | 562 actualRange:(NSRangePointer)actualRange { |
494 NOTIMPLEMENTED(); | 563 NOTIMPLEMENTED(); |
495 return NSZeroRect; | 564 return NSZeroRect; |
496 } | 565 } |
497 | 566 |
498 - (BOOL)hasMarkedText { | 567 - (BOOL)hasMarkedText { |
499 return textInputClient_ && textInputClient_->HasCompositionText(); | 568 return textInputClient_ && textInputClient_->HasCompositionText(); |
(...skipping 24 matching lines...) Expand all Loading... |
524 swallowedAny = true; | 593 swallowedAny = true; |
525 // Ensure the menu remains active. | 594 // Ensure the menu remains active. |
526 menuController = MenuController::GetActiveInstance(); | 595 menuController = MenuController::GetActiveInstance(); |
527 } | 596 } |
528 } | 597 } |
529 | 598 |
530 if (!textInputClient_) | 599 if (!textInputClient_) |
531 return; | 600 return; |
532 | 601 |
533 textInputClient_->DeleteRange(gfx::Range(replacementRange)); | 602 textInputClient_->DeleteRange(gfx::Range(replacementRange)); |
534 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); | 603 |
| 604 // If a single character is inserted by keyDown's call to interpretKeyEvents: |
| 605 // then use InsertChar() to allow editing events to be merged. Never send the |
| 606 // key modifier flags to InsertChar since interpretKeyEvents: will filter out |
| 607 // things that are actually commands, and 'Alt' on Mac actually inserts |
| 608 // alternate characters (e.g. Alt+S is ß), so shouldn't be ignored. |
| 609 if (inKeyDown_ && [text length] == 1) |
| 610 textInputClient_->InsertChar([text characterAtIndex:0], 0); |
| 611 else |
| 612 textInputClient_->InsertText(base::SysNSStringToUTF16(text)); |
535 } | 613 } |
536 | 614 |
537 - (NSRange)markedRange { | 615 - (NSRange)markedRange { |
538 if (!textInputClient_) | 616 if (!textInputClient_) |
539 return NSMakeRange(NSNotFound, 0); | 617 return NSMakeRange(NSNotFound, 0); |
540 | 618 |
541 gfx::Range range; | 619 gfx::Range range; |
542 textInputClient_->GetCompositionTextRange(&range); | 620 textInputClient_->GetCompositionTextRange(&range); |
543 return range.ToNSRange(); | 621 return range.ToNSRange(); |
544 } | 622 } |
(...skipping 23 matching lines...) Expand all Loading... |
568 | 646 |
569 - (void)unmarkText { | 647 - (void)unmarkText { |
570 if (textInputClient_) | 648 if (textInputClient_) |
571 textInputClient_->ConfirmCompositionText(); | 649 textInputClient_->ConfirmCompositionText(); |
572 } | 650 } |
573 | 651 |
574 - (NSArray*)validAttributesForMarkedText { | 652 - (NSArray*)validAttributesForMarkedText { |
575 return @[]; | 653 return @[]; |
576 } | 654 } |
577 | 655 |
| 656 // NSUserInterfaceValidations protocol implementation. |
| 657 |
| 658 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { |
| 659 // TODO(tapted): A read-only views::Textfield currently returns null from |
| 660 // GetTextInputClient(), which means this incorrectly (in)validates menus for |
| 661 // read-only textfields (e.g. Copy gets disabled). |
| 662 if (!textInputClient_) |
| 663 return NO; |
| 664 |
| 665 SEL action = [item action]; |
| 666 |
| 667 if (action == @selector(undo:)) |
| 668 return textInputClient_->IsEditingCommandEnabled(IDS_APP_UNDO); |
| 669 if (action == @selector(redo:)) |
| 670 return textInputClient_->IsEditingCommandEnabled(IDS_APP_REDO); |
| 671 if (action == @selector(cut:)) |
| 672 return textInputClient_->IsEditingCommandEnabled(IDS_APP_CUT); |
| 673 if (action == @selector(copy:)) |
| 674 return textInputClient_->IsEditingCommandEnabled(IDS_APP_COPY); |
| 675 if (action == @selector(paste:)) |
| 676 return textInputClient_->IsEditingCommandEnabled(IDS_APP_PASTE); |
| 677 if (action == @selector(selectAll:)) |
| 678 return textInputClient_->IsEditingCommandEnabled(IDS_APP_SELECT_ALL); |
| 679 |
| 680 return NO; |
| 681 } |
| 682 |
578 // NSAccessibility informal protocol implementation. | 683 // NSAccessibility informal protocol implementation. |
579 | 684 |
580 - (id)accessibilityAttributeValue:(NSString*)attribute { | 685 - (id)accessibilityAttributeValue:(NSString*)attribute { |
581 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { | 686 if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) { |
582 return @[ hostedView_->GetNativeViewAccessible() ]; | 687 return @[ hostedView_->GetNativeViewAccessible() ]; |
583 } | 688 } |
584 | 689 |
585 return [super accessibilityAttributeValue:attribute]; | 690 return [super accessibilityAttributeValue:attribute]; |
586 } | 691 } |
587 | 692 |
588 - (id)accessibilityHitTest:(NSPoint)point { | 693 - (id)accessibilityHitTest:(NSPoint)point { |
589 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; | 694 return [hostedView_->GetNativeViewAccessible() accessibilityHitTest:point]; |
590 } | 695 } |
591 | 696 |
592 @end | 697 @end |
OLD | NEW |