diff options
5 files changed, 136 insertions, 54 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_mac.h b/chrome/browser/autocomplete/autocomplete_edit_view_mac.h index 740fde7..d904952 100644 --- a/chrome/browser/autocomplete/autocomplete_edit_view_mac.h +++ b/chrome/browser/autocomplete/autocomplete_edit_view_mac.h @@ -137,6 +137,11 @@ class AutocompleteEditViewMac : public AutocompleteEditView, // though here we cannot really do the in-place operation they do. void EmphasizeURLComponents(); + // Calculates text attributes according to |display_text| and applies them + // to the given |as| object. + void ApplyTextAttributes(const std::wstring& display_text, + NSMutableAttributedString* as); + scoped_ptr<AutocompleteEditModel> model_; scoped_ptr<AutocompletePopupViewMac> popup_view_; diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm index a4a54ce..0c97205 100644 --- a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm +++ b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm @@ -406,13 +406,23 @@ void AutocompleteEditViewMac::UpdatePopup() { if (!model_->has_focus()) return; - // TODO(shess): - // Shouldn't inline autocomplete when the caret/selection isn't at - // the end of the text. - // - // One option would seem to be to check for a non-nil field - // editor, and check it's selected range against its length. - model_->StartAutocomplete(false); + // Comment copied from AutocompleteEditViewWin::UpdatePopup(): + // Don't inline autocomplete when: + // * The user is deleting text + // * The caret/selection isn't at the end of the text + // * The user has just pasted in something that replaced all the text + // * The user is trying to compose something in an IME + bool prevent_inline_autocomplete = false; + NSTextView* editor = (NSTextView*)[field_ currentEditor]; + if (editor) { + if ([editor hasMarkedText]) + prevent_inline_autocomplete = true; + + if (NSMaxRange([editor selectedRange]) < [[editor textStorage] length]) + prevent_inline_autocomplete = true; + } + + model_->StartAutocomplete(prevent_inline_autocomplete); } void AutocompleteEditViewMac::ClosePopup() { @@ -426,6 +436,50 @@ void AutocompleteEditViewMac::SetText(const std::wstring& display_text) { NSString* ss = base::SysWideToNSString(display_text); NSMutableAttributedString* as = [[[NSMutableAttributedString alloc] initWithString:ss] autorelease]; + + ApplyTextAttributes(display_text, as); + + [field_ setAttributedStringValue:as]; + + // TODO(shess): This may be an appropriate place to call: + // controller_->OnChanged(); + // In the current implementation, this tells LocationBarViewMac to + // mess around with |model_| and update |field_|. Unfortunately, + // when I look at our peer implementations, it's not entirely clear + // to me if this is safe. SetText() is sort of an utility method, + // and different callers sometimes have different needs. Research + // this issue so that it can be added safely. + + // TODO(shess): Also, consider whether this code couldn't just + // manage things directly. Windows uses a series of overlaid view + // objects to accomplish the hinting stuff that OnChanged() does, so + // it makes sense to have it in the controller that lays those + // things out. Mac instead pushes the support into a custom + // text-field implementation. +} + +void AutocompleteEditViewMac::SetTextAndSelectedRange( + const std::wstring& display_text, const NSRange range) { + SetText(display_text); + SetSelectedRange(range); +} + +void AutocompleteEditViewMac::EmphasizeURLComponents() { + NSTextView* editor = (NSTextView*)[field_ currentEditor]; + // If the autocomplete text field is in editing mode, then we can just change + // its attributes through its editor. Otherwise, we simply reset its content. + if (editor) { + NSTextStorage* storage = [editor textStorage]; + [storage beginEditing]; + ApplyTextAttributes(GetText(), storage); + [storage endEditing]; + } else { + SetText(GetText()); + } +} + +void AutocompleteEditViewMac::ApplyTextAttributes( + const std::wstring& display_text, NSMutableAttributedString* as) { NSFont* font = ResourceBundle::GetSharedInstance().GetFont( ResourceBundle::BaseFont).nativeFont(); [as addAttribute:NSFontAttributeName value:font @@ -475,38 +529,6 @@ void AutocompleteEditViewMac::SetText(const std::wstring& display_text) { [as addAttribute:NSForegroundColorAttributeName value:color range:ComponentToNSRange(scheme)]; } - - [field_ setAttributedStringValue:as]; - - // TODO(shess): This may be an appropriate place to call: - // controller_->OnChanged(); - // In the current implementation, this tells LocationBarViewMac to - // mess around with |model_| and update |field_|. Unfortunately, - // when I look at our peer implementations, it's not entirely clear - // to me if this is safe. SetText() is sort of an utility method, - // and different callers sometimes have different needs. Research - // this issue so that it can be added safely. - - // TODO(shess): Also, consider whether this code couldn't just - // manage things directly. Windows uses a series of overlaid view - // objects to accomplish the hinting stuff that OnChanged() does, so - // it makes sense to have it in the controller that lays those - // things out. Mac instead pushes the support into a custom - // text-field implementation. -} - -void AutocompleteEditViewMac::SetTextAndSelectedRange( - const std::wstring& display_text, const NSRange range) { - SetText(display_text); - SetSelectedRange(range); -} - -void AutocompleteEditViewMac::EmphasizeURLComponents() { - if ([field_ currentEditor]) { - SetTextAndSelectedRange(GetText(), GetSelectedRange()); - } else { - SetText(GetText()); - } } void AutocompleteEditViewMac::OnTemporaryTextMaybeChanged( @@ -532,7 +554,8 @@ bool AutocompleteEditViewMac::OnInlineAutocompleteTextMaybeChanged( } DCHECK_LE(user_text_length, display_text.size()); - const NSRange range = NSMakeRange(user_text_length, display_text.size()); + const NSRange range = + NSMakeRange(user_text_length, display_text.size() - user_text_length); SetTextAndSelectedRange(display_text, range); controller_->OnChanged(); [field_ clearUndoChain]; diff --git a/chrome/browser/cocoa/autocomplete_text_field.mm b/chrome/browser/cocoa/autocomplete_text_field.mm index a07cac1..a4242bb 100644 --- a/chrome/browser/cocoa/autocomplete_text_field.mm +++ b/chrome/browser/cocoa/autocomplete_text_field.mm @@ -6,6 +6,7 @@ #include "base/logging.h" #import "chrome/browser/cocoa/autocomplete_text_field_cell.h" +#import "chrome/browser/cocoa/autocomplete_text_field_editor.h" #import "chrome/browser/cocoa/browser_window_controller.h" #import "chrome/browser/cocoa/toolbar_controller.h" #import "chrome/browser/cocoa/url_drop_target.h" @@ -169,17 +170,17 @@ } - (void)setAttributedStringValue:(NSAttributedString*)aString { - NSTextView* editor = static_cast<NSTextView*>([self currentEditor]); + AutocompleteTextFieldEditor* editor = + static_cast<AutocompleteTextFieldEditor*>([self currentEditor]); + if (!editor) { [super setAttributedStringValue:aString]; } else { - // -currentEditor is defined to return NSText*, make sure our - // assumptions still hold, here. - DCHECK([editor isKindOfClass:[NSTextView class]]); + // The type of the field editor must be AutocompleteTextFieldEditor, + // otherwise things won't work. + DCHECK([editor isKindOfClass:[AutocompleteTextFieldEditor class]]); - NSTextStorage* textStorage = [editor textStorage]; - DCHECK(textStorage); - [textStorage setAttributedString:aString]; + [editor setAttributedString:aString]; } } @@ -253,13 +254,6 @@ } } -- (void)textDidChange:(NSNotification *)aNotification { - [super textDidChange:aNotification]; - if (observer_) { - observer_->OnDidChange(); - } -} - - (void)textDidEndEditing:(NSNotification *)aNotification { [super textDidEndEditing:aNotification]; if (observer_) { diff --git a/chrome/browser/cocoa/autocomplete_text_field_editor.h b/chrome/browser/cocoa/autocomplete_text_field_editor.h index 1c3ce5a..8f3c18c 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_editor.h +++ b/chrome/browser/cocoa/autocomplete_text_field_editor.h @@ -30,6 +30,17 @@ class Profile; Profile* profile_; scoped_nsobject<NSCharacterSet> forbiddenCharacters_; + + // Indicates if the field editor's interpretKeyEvents: method is being called. + // If it's YES, then we should postpone the call to the observer's + // OnDidChange() method after the field editor's interpretKeyEvents: method + // is finished, rather than calling it in textDidChange: method. Because the + // input method may update the marked text after inserting some text, but we + // need the observer be aware of the marked text as well. + BOOL interpretingKeyEvents_; + + // Indicates if the text has been changed by key events. + BOOL textChangedByKeyEvents_; } @property(nonatomic) Profile* profile; @@ -39,6 +50,10 @@ class Profile; - (AutocompleteTextField*)delegate; - (void)setDelegate:(AutocompleteTextField*)delegate; +// Sets attributed string programatically through the field editor's text +// storage object. +- (void)setAttributedString:(NSAttributedString*)aString; + @end @interface AutocompleteTextFieldEditor(PrivateTestMethods) diff --git a/chrome/browser/cocoa/autocomplete_text_field_editor.mm b/chrome/browser/cocoa/autocomplete_text_field_editor.mm index 974b346..936b013 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_editor.mm +++ b/chrome/browser/cocoa/autocomplete_text_field_editor.mm @@ -266,4 +266,49 @@ class Extension; [super insertText:aString]; } +- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange { + [super setMarkedText:aString selectedRange:selRange]; + + // Because the AutocompleteEditViewMac class treats marked text as content, + // we need to treat the change to marked text as content change as well. + [self didChangeText]; +} + +- (void)interpretKeyEvents:(NSArray *)eventArray { + DCHECK(!interpretingKeyEvents_); + interpretingKeyEvents_ = YES; + textChangedByKeyEvents_ = NO; + [super interpretKeyEvents:eventArray]; + + AutocompleteTextFieldObserver* observer = [self observer]; + if (textChangedByKeyEvents_ && observer) + observer->OnDidChange(); + + DCHECK(interpretingKeyEvents_); + interpretingKeyEvents_ = NO; +} + +- (void)didChangeText { + [super didChangeText]; + + AutocompleteTextFieldObserver* observer = [self observer]; + if (observer) { + if (!interpretingKeyEvents_) + observer->OnDidChange(); + else + textChangedByKeyEvents_ = YES; + } +} + +- (void)setAttributedString:(NSAttributedString*)aString { + NSTextStorage* textStorage = [self textStorage]; + DCHECK(textStorage); + [textStorage setAttributedString:aString]; + + // The text has been changed programmatically. The observer should know + // this change, so setting |textChangedByKeyEvents_| to NO to + // prevent its OnDidChange() method from being called unnecessarily. + textChangedByKeyEvents_ = NO; +} + @end |