| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 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 #include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" | 5 #include "chrome/browser/autocomplete/autocomplete_edit_view_mac.h" |
| 6 | 6 |
| 7 #include "base/sys_string_conversions.h" | 7 #include "base/sys_string_conversions.h" |
| 8 #include "chrome/browser/autocomplete/autocomplete_edit.h" | 8 #include "chrome/browser/autocomplete/autocomplete_edit.h" |
| 9 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" | 9 #include "chrome/browser/autocomplete/autocomplete_popup_model.h" |
| 10 #include "chrome/browser/autocomplete/autocomplete_popup_view_mac.h" | 10 #include "chrome/browser/autocomplete/autocomplete_popup_view_mac.h" |
| 11 #include "chrome/browser/tab_contents/tab_contents.h" | 11 #include "chrome/browser/tab_contents/tab_contents.h" |
| 12 | 12 |
| 13 // Focus-handling between |field_| and |model_| is a bit subtle. |
| 14 // Other platforms detect change of focus, which is inconvenient |
| 15 // without subclassing NSTextField (even with a subclass, the use of a |
| 16 // field editor may complicate things). |
| 17 // |
| 18 // |model_| doesn't actually do anything when it gains focus, it just |
| 19 // initializes. Visible activity happens only after the user edits. |
| 20 // NSTextField delegate receives messages around starting and ending |
| 21 // edits, so that sufcices to catch focus changes. Since all calls |
| 22 // into |model_| start from AutocompleteEditViewMac, in the worst case |
| 23 // we can add code to sync up the sense of focus as needed. |
| 24 // |
| 25 // I've added DCHECK(IsFirstResponder()) in the places which I believe |
| 26 // should only be reachable when |field_| is being edited. If these |
| 27 // fire, it probably means someone unexpected is calling into |
| 28 // |model_|. |
| 29 // |
| 30 // Other platforms don't appear to have the sense of "key window" that |
| 31 // Mac does (I believe their fields lose focus when the window loses |
| 32 // focus). Rather than modifying focus outside the control's edit |
| 33 // scope, when the window resigns key the autocomplete popup is |
| 34 // closed. |model_| still believes it has focus, and the popup will |
| 35 // be regenerated on the user's next edit. That seems to match how |
| 36 // things work on other platforms. |
| 37 |
| 13 namespace { | 38 namespace { |
| 14 | 39 |
| 15 // TODO(shess): This is ugly, find a better way. Using it right now | 40 // TODO(shess): This is ugly, find a better way. Using it right now |
| 16 // so that I can crib from gtk and still be able to see that I'm using | 41 // so that I can crib from gtk and still be able to see that I'm using |
| 17 // the same values easily. | 42 // the same values easily. |
| 18 const NSColor* ColorWithRGBBytes(int rr, int gg, int bb) { | 43 const NSColor* ColorWithRGBBytes(int rr, int gg, int bb) { |
| 19 DCHECK_LE(rr, 255); | 44 DCHECK_LE(rr, 255); |
| 20 DCHECK_LE(bb, 255); | 45 DCHECK_LE(bb, 255); |
| 21 DCHECK_LE(gg, 255); | 46 DCHECK_LE(gg, 255); |
| 22 return [NSColor colorWithCalibratedRed:static_cast<float>(rr)/255.0 | 47 return [NSColor colorWithCalibratedRed:static_cast<float>(rr)/255.0 |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 91 // Thin Obj-C bridge class that is the delegate of the omnibox field. | 116 // Thin Obj-C bridge class that is the delegate of the omnibox field. |
| 92 // It intercepts various control delegate methods and vectors them to | 117 // It intercepts various control delegate methods and vectors them to |
| 93 // the edit view. | 118 // the edit view. |
| 94 | 119 |
| 95 @interface AutocompleteFieldDelegate : NSObject { | 120 @interface AutocompleteFieldDelegate : NSObject { |
| 96 @private | 121 @private |
| 97 AutocompleteEditViewMac* edit_view_; // weak, owns us. | 122 AutocompleteEditViewMac* edit_view_; // weak, owns us. |
| 98 } | 123 } |
| 99 - initWithEditView:(AutocompleteEditViewMac*)view; | 124 - initWithEditView:(AutocompleteEditViewMac*)view; |
| 100 - (void)windowDidResignKey:(NSNotification*)notification; | 125 - (void)windowDidResignKey:(NSNotification*)notification; |
| 101 - (void)windowDidBecomeKey:(NSNotification*)notification; | |
| 102 @end | 126 @end |
| 103 | 127 |
| 104 AutocompleteEditViewMac::AutocompleteEditViewMac( | 128 AutocompleteEditViewMac::AutocompleteEditViewMac( |
| 105 AutocompleteEditController* controller, | 129 AutocompleteEditController* controller, |
| 106 ToolbarModel* toolbar_model, | 130 ToolbarModel* toolbar_model, |
| 107 Profile* profile, | 131 Profile* profile, |
| 108 CommandUpdater* command_updater, | 132 CommandUpdater* command_updater, |
| 109 NSTextField* field) | 133 NSTextField* field) |
| 110 : model_(new AutocompleteEditModel(this, controller, profile)), | 134 : model_(new AutocompleteEditModel(this, controller, profile)), |
| 111 popup_view_(new AutocompletePopupViewMac(this, model_.get(), profile, | 135 popup_view_(new AutocompletePopupViewMac(this, model_.get(), profile, |
| (...skipping 13 matching lines...) Expand all Loading... |
| 125 // Needed so that editing doesn't lose the styling. | 149 // Needed so that editing doesn't lose the styling. |
| 126 [field_ setAllowsEditingTextAttributes:YES]; | 150 [field_ setAllowsEditingTextAttributes:YES]; |
| 127 | 151 |
| 128 // Track the window's key status for signalling focus changes to | 152 // Track the window's key status for signalling focus changes to |
| 129 // |model_|. | 153 // |model_|. |
| 130 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; | 154 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; |
| 131 [nc addObserver:edit_helper_ | 155 [nc addObserver:edit_helper_ |
| 132 selector:@selector(windowDidResignKey:) | 156 selector:@selector(windowDidResignKey:) |
| 133 name:NSWindowDidResignKeyNotification | 157 name:NSWindowDidResignKeyNotification |
| 134 object:[field_ window]]; | 158 object:[field_ window]]; |
| 135 [nc addObserver:edit_helper_ | |
| 136 selector:@selector(windowDidBecomeKey:) | |
| 137 name:NSWindowDidBecomeKeyNotification | |
| 138 object:[field_ window]]; | |
| 139 } | 159 } |
| 140 | 160 |
| 141 AutocompleteEditViewMac::~AutocompleteEditViewMac() { | 161 AutocompleteEditViewMac::~AutocompleteEditViewMac() { |
| 142 // TODO(shess): Having to be aware of destructor ordering in this | 162 // TODO(shess): Having to be aware of destructor ordering in this |
| 143 // way seems brittle. There must be a better way. | 163 // way seems brittle. There must be a better way. |
| 144 | 164 |
| 145 // Destroy popup view before this object in case it tries to call us | 165 // Destroy popup view before this object in case it tries to call us |
| 146 // back in the destructor. Likewise for destroying the model before | 166 // back in the destructor. Likewise for destroying the model before |
| 147 // this object. | 167 // this object. |
| 148 popup_view_.reset(); | 168 popup_view_.reset(); |
| (...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 392 SetSelectedRange(NSMakeRange(user_text_length, display_text.size())); | 412 SetSelectedRange(NSMakeRange(user_text_length, display_text.size())); |
| 393 return true; | 413 return true; |
| 394 } | 414 } |
| 395 | 415 |
| 396 void AutocompleteEditViewMac::OnRevertTemporaryText() { | 416 void AutocompleteEditViewMac::OnRevertTemporaryText() { |
| 397 UpdateAndStyleText(saved_temporary_text_); | 417 UpdateAndStyleText(saved_temporary_text_); |
| 398 saved_temporary_text_.clear(); | 418 saved_temporary_text_.clear(); |
| 399 SetSelectedRange(saved_temporary_selection_); | 419 SetSelectedRange(saved_temporary_selection_); |
| 400 } | 420 } |
| 401 | 421 |
| 422 bool AutocompleteEditViewMac::IsFirstResponder() const { |
| 423 return [field_ currentEditor] != nil ? true : false; |
| 424 } |
| 425 |
| 402 void AutocompleteEditViewMac::OnBeforePossibleChange() { | 426 void AutocompleteEditViewMac::OnBeforePossibleChange() { |
| 427 // We should only arrive here when the field is focussed. |
| 428 DCHECK(IsFirstResponder()); |
| 429 |
| 403 selection_before_change_ = GetSelectedRange(); | 430 selection_before_change_ = GetSelectedRange(); |
| 404 text_before_change_ = GetText(); | 431 text_before_change_ = GetText(); |
| 405 } | 432 } |
| 406 | 433 |
| 407 bool AutocompleteEditViewMac::OnAfterPossibleChange() { | 434 bool AutocompleteEditViewMac::OnAfterPossibleChange() { |
| 435 // We should only arrive here when the field is focussed. |
| 436 DCHECK(IsFirstResponder()); |
| 437 |
| 408 const NSRange new_selection(GetSelectedRange()); | 438 const NSRange new_selection(GetSelectedRange()); |
| 409 const std::wstring new_text(GetText()); | 439 const std::wstring new_text(GetText()); |
| 410 const size_t length = new_text.length(); | 440 const size_t length = new_text.length(); |
| 411 | 441 |
| 412 const bool selection_differs = !NSEqualRanges(new_selection, | 442 const bool selection_differs = !NSEqualRanges(new_selection, |
| 413 selection_before_change_); | 443 selection_before_change_); |
| 414 const bool at_end_of_edit = (length == new_selection.location); | 444 const bool at_end_of_edit = (length == new_selection.location); |
| 415 const bool text_differs = (new_text != text_before_change_); | 445 const bool text_differs = (new_text != text_before_change_); |
| 416 | 446 |
| 417 // When the user has deleted text, we don't allow inline | 447 // When the user has deleted text, we don't allow inline |
| (...skipping 25 matching lines...) Expand all Loading... |
| 443 // is then re-created with the new autocomplete results. | 473 // is then re-created with the new autocomplete results. |
| 444 if (something_changed) { | 474 if (something_changed) { |
| 445 UpdateAndStyleText(new_text); | 475 UpdateAndStyleText(new_text); |
| 446 SetSelectedRange(new_selection); | 476 SetSelectedRange(new_selection); |
| 447 } | 477 } |
| 448 | 478 |
| 449 return something_changed; | 479 return something_changed; |
| 450 } | 480 } |
| 451 | 481 |
| 452 void AutocompleteEditViewMac::OnUpOrDownKeyPressed(bool up, bool by_page) { | 482 void AutocompleteEditViewMac::OnUpOrDownKeyPressed(bool up, bool by_page) { |
| 483 // We should only arrive here when the field is focussed. |
| 484 DCHECK(IsFirstResponder()); |
| 485 |
| 453 const int count = by_page ? model_->result().size() : 1; | 486 const int count = by_page ? model_->result().size() : 1; |
| 454 model_->OnUpOrDownKeyPressed(up ? -count : count); | 487 model_->OnUpOrDownKeyPressed(up ? -count : count); |
| 455 } | 488 } |
| 489 |
| 456 void AutocompleteEditViewMac::OnEscapeKeyPressed() { | 490 void AutocompleteEditViewMac::OnEscapeKeyPressed() { |
| 491 // We should only arrive here when the field is focussed. |
| 492 DCHECK(IsFirstResponder()); |
| 493 |
| 457 model_->OnEscapeKeyPressed(); | 494 model_->OnEscapeKeyPressed(); |
| 458 } | 495 } |
| 459 void AutocompleteEditViewMac::OnSetFocus(bool f) { | 496 |
| 460 // Only forward if we actually do have the focus. | 497 void AutocompleteEditViewMac::OnWillBeginEditing() { |
| 461 if ([field_ currentEditor]) { | 498 // We should only arrive here when the field is focussed. |
| 462 model_->OnSetFocus(f); | 499 DCHECK([field_ currentEditor]); |
| 463 } | 500 |
| 501 // TODO(shess): Having the control key depressed changes the desired |
| 502 // TLD for autocomplete, which changes the results. Not sure if we |
| 503 // can detect that without subclassing NSTextField. |
| 504 const bool controlDown = false; |
| 505 model_->OnSetFocus(controlDown); |
| 506 |
| 507 // Capture the current state. |
| 508 OnBeforePossibleChange(); |
| 464 } | 509 } |
| 465 void AutocompleteEditViewMac::OnKillFocus() { | 510 |
| 466 // TODO(shess): This would seem to be a job for |model_|. | 511 void AutocompleteEditViewMac::OnDidEndEditing() { |
| 467 ClosePopup(); | 512 ClosePopup(); |
| 468 | 513 |
| 469 // Tell the model to reset itself. | 514 // Tell the model to reset itself. |
| 470 model_->OnKillFocus(); | 515 model_->OnKillFocus(); |
| 471 } | 516 } |
| 517 |
| 518 void AutocompleteEditViewMac::OnDidResignKey() { |
| 519 ClosePopup(); |
| 520 } |
| 521 |
| 472 void AutocompleteEditViewMac::AcceptInput( | 522 void AutocompleteEditViewMac::AcceptInput( |
| 473 WindowOpenDisposition disposition, bool for_drop) { | 523 WindowOpenDisposition disposition, bool for_drop) { |
| 524 // We should only arrive here when the field is focussed. |
| 525 DCHECK([field_ currentEditor]); |
| 526 |
| 474 model_->AcceptInput(disposition, for_drop); | 527 model_->AcceptInput(disposition, for_drop); |
| 475 } | 528 } |
| 476 | 529 |
| 477 void AutocompleteEditViewMac::FocusLocation() { | 530 void AutocompleteEditViewMac::FocusLocation() { |
| 478 // -makeFirstResponder: will select the entire field_. If we're | 531 // -makeFirstResponder: will select the entire field_. If we're |
| 479 // already firstResponder, it's likely that we want to retain the | 532 // already firstResponder, it's likely that we want to retain the |
| 480 // current selection. | 533 // current selection. |
| 481 if (![field_ currentEditor]) { | 534 if (![field_ currentEditor]) { |
| 482 [[field_ window] makeFirstResponder:field_]; | 535 [[field_ window] makeFirstResponder:field_]; |
| 483 DCHECK_EQ([field_ currentEditor], [[field_ window] firstResponder]); | 536 DCHECK_EQ([field_ currentEditor], [[field_ window] firstResponder]); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 527 } | 580 } |
| 528 | 581 |
| 529 // Capture the state before the operation changes the content. | 582 // Capture the state before the operation changes the content. |
| 530 // TODO(shess): Determine if this is always redundent WRT the call | 583 // TODO(shess): Determine if this is always redundent WRT the call |
| 531 // in -controlTextDidChange:. | 584 // in -controlTextDidChange:. |
| 532 edit_view_->OnBeforePossibleChange(); | 585 edit_view_->OnBeforePossibleChange(); |
| 533 return NO; | 586 return NO; |
| 534 } | 587 } |
| 535 | 588 |
| 536 - (void)controlTextDidBeginEditing:(NSNotification*)aNotification { | 589 - (void)controlTextDidBeginEditing:(NSNotification*)aNotification { |
| 537 edit_view_->OnSetFocus(false); | 590 edit_view_->OnWillBeginEditing(); |
| 538 | |
| 539 // Capture the current state. | |
| 540 edit_view_->OnBeforePossibleChange(); | |
| 541 } | 591 } |
| 542 | 592 |
| 543 - (void)controlTextDidChange:(NSNotification*)aNotification { | 593 - (void)controlTextDidChange:(NSNotification*)aNotification { |
| 544 // Figure out what changed and notify the model_. | 594 // Figure out what changed and notify the model_. |
| 545 edit_view_->OnAfterPossibleChange(); | 595 edit_view_->OnAfterPossibleChange(); |
| 546 | 596 |
| 547 // Then capture the new state. | 597 // Then capture the new state. |
| 548 edit_view_->OnBeforePossibleChange(); | 598 edit_view_->OnBeforePossibleChange(); |
| 549 } | 599 } |
| 550 | 600 |
| 551 - (BOOL)control:(NSControl*)control textShouldEndEditing:(NSText*)fieldEditor { | 601 - (BOOL)control:(NSControl*)control textShouldEndEditing:(NSText*)fieldEditor { |
| 552 edit_view_->OnKillFocus(); | 602 edit_view_->OnDidEndEditing(); |
| 553 | 603 |
| 554 return YES; | 604 return YES; |
| 555 | 605 |
| 556 // TODO(shess): Figure out where the selection belongs. On GTK, | 606 // TODO(shess): Figure out where the selection belongs. On GTK, |
| 557 // it's set to the start of the text. | 607 // it's set to the start of the text. |
| 558 } | 608 } |
| 559 | 609 |
| 560 // Signal that we've lost focus when the window resigns key. | 610 // Signal that we've lost focus when the window resigns key. |
| 561 - (void)windowDidResignKey:(NSNotification*)notification { | 611 - (void)windowDidResignKey:(NSNotification*)notification { |
| 562 edit_view_->OnKillFocus(); | 612 edit_view_->OnDidResignKey(); |
| 563 } | |
| 564 | |
| 565 // Signal that we may have regained focus. | |
| 566 - (void)windowDidBecomeKey:(NSNotification*)notification { | |
| 567 edit_view_->OnSetFocus(false); | |
| 568 } | 613 } |
| 569 | 614 |
| 570 @end | 615 @end |
| OLD | NEW |