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 |