diff options
author | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-15 23:05:07 +0000 |
---|---|---|
committer | shess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-15 23:05:07 +0000 |
commit | e9ccfe39326c10720e3b284c16e8c0bf96c965b5 (patch) | |
tree | 7438f7e470a0400417554b83681217bb04b17467 /chrome/browser | |
parent | 9c1981c591996af8fdbce5dcb0d2d473e7383e5e (diff) | |
download | chromium_src-e9ccfe39326c10720e3b284c16e8c0bf96c965b5.zip chromium_src-e9ccfe39326c10720e3b284c16e8c0bf96c965b5.tar.gz chromium_src-e9ccfe39326c10720e3b284c16e8c0bf96c965b5.tar.bz2 |
[Mac] Trim Omnibox hints as field shrinks.
When user text doesn't fit with the full hint:
"Type to search" hint goes away
"Tab to search" condenses to "Tab"
"Search Engine:" is truncated
http://crbug.com/20285
TEST=Type a sentence. Resize window to see if type-hint goes away.
TEST=Type "google". Resize window to see if tab-hint goes away.
TEST=Type "google" then TAB, then sentence. Resize window to see if "Search Google:" is truncated.
TEST=In all cases, text should not scroll before truncation (though it might move as part of truncation).
Review URL: http://codereview.chromium.org/262034
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@29202 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
13 files changed, 434 insertions, 138 deletions
diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_mac.h b/chrome/browser/autocomplete/autocomplete_edit_view_mac.h index c1ff0c0..51bb40e 100644 --- a/chrome/browser/autocomplete/autocomplete_edit_view_mac.h +++ b/chrome/browser/autocomplete/autocomplete_edit_view_mac.h @@ -97,6 +97,7 @@ class AutocompleteEditViewMac : public AutocompleteEditView, virtual int GetPasteActionStringId(); virtual void OnPasteAndGo(); virtual void OnSecurityIconClicked(); + virtual void OnFrameChanged(); // Helper functions for use from AutocompleteEditHelper Objective-C // class. diff --git a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm index c736c8f..79e9369 100644 --- a/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm +++ b/chrome/browser/autocomplete/autocomplete_edit_view_mac.mm @@ -658,6 +658,17 @@ void AutocompleteEditViewMac::OnPasteAndGo() { model_->PasteAndGo(); } +void AutocompleteEditViewMac::OnFrameChanged() { + // TODO(shess): UpdatePopupAppearance() is called frequently, so it + // should be really cheap, but in this case we could probably make + // things even cheaper by refactoring between the popup-placement + // code and the matrix-population code. + popup_view_->UpdatePopupAppearance(); + + // Give controller a chance to rearrange decorations. + controller_->OnChanged(); +} + bool AutocompleteEditViewMac::OnTabPressed() { if (model_->is_keyword_hint() && !model_->keyword().empty()) { model_->AcceptKeyword(); diff --git a/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm b/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm index 0a553a5..97670b8 100644 --- a/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm +++ b/chrome/browser/autocomplete/autocomplete_popup_view_mac.mm @@ -302,9 +302,6 @@ NSAttributedString* AutocompletePopupViewMac::MatchText( // Call |popup_view_| OnMiddleClick(). - (void)middleSelect:(id)sender; -// Resize the popup when the field's window resizes. -- (void)windowDidResize:(NSNotification*)notification; - @end AutocompletePopupViewMac::AutocompletePopupViewMac( @@ -363,13 +360,6 @@ void AutocompletePopupViewMac::CreatePopupIfNeeded() { [matrix setAction:@selector(select:)]; [matrix setMiddleClickAction:@selector(middleSelect:)]; [popup_ setContentView:matrix]; - - // We need the popup to follow window resize. - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc addObserver:matrix_target_ - selector:@selector(windowDidResize:) - name:NSWindowDidResizeNotification - object:[field_ window]]; } } @@ -383,11 +373,6 @@ void AutocompletePopupViewMac::UpdatePopupAppearance() { NSMatrix* matrix = [popup_ contentView]; [matrix setTarget:nil]; - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc removeObserver:matrix_target_ - name:NSWindowDidResizeNotification - object:[field_ window]]; - popup_.reset(nil); return; @@ -736,14 +721,4 @@ void AutocompletePopupViewMac::OnMiddleClick() { popup_view_->OnMiddleClick(); } -- (void)windowDidResize:(NSNotification*)notification { - DCHECK(popup_view_); - - // TODO(shess): UpdatePopupAppearance() is called frequently, so it - // should be really cheap, but in this case we could probably make - // things even cheaper by refactoring between the popup-placement - // code and the matrix-population code. - popup_view_->UpdatePopupAppearance(); -} - @end diff --git a/chrome/browser/cocoa/autocomplete_text_field.h b/chrome/browser/cocoa/autocomplete_text_field.h index be88561..58e514c 100644 --- a/chrome/browser/cocoa/autocomplete_text_field.h +++ b/chrome/browser/cocoa/autocomplete_text_field.h @@ -53,6 +53,9 @@ class AutocompleteTextFieldObserver { // Called when the user clicks the hint icon (i.e. the security icon) in the // location bar. virtual void OnSecurityIconClicked() = 0; + + // Called when the field's frame changes. + virtual void OnFrameChanged() = 0; }; @interface AutocompleteTextField : NSTextField { @@ -69,6 +72,11 @@ class AutocompleteTextFieldObserver { // field editor may need to be repositioned. - (void)resetFieldEditorFrameIfNeeded; +// Returns the amount of the field's width which is not being taken up +// by the text contents. May be negative if the contents are large +// enough to scroll. +- (CGFloat)availableDecorationWidth; + @end #endif // CHROME_BROWSER_COCOA_AUTOCOMPLETE_TEXT_FIELD_H_ diff --git a/chrome/browser/cocoa/autocomplete_text_field.mm b/chrome/browser/cocoa/autocomplete_text_field.mm index 60a7a56..d135854 100644 --- a/chrome/browser/cocoa/autocomplete_text_field.mm +++ b/chrome/browser/cocoa/autocomplete_text_field.mm @@ -164,4 +164,18 @@ [editor mouseDown:theEvent]; } +- (CGFloat)availableDecorationWidth { + NSAttributedString* as = [self attributedStringValue]; + const NSSize size([as size]); + const NSRect bounds([self bounds]); + return NSWidth(bounds) - size.width; +} + +- (void)setFrame:(NSRect)frameRect { + [super setFrame:frameRect]; + if (observer_) { + observer_->OnFrameChanged(); + } +} + @end diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell.h b/chrome/browser/cocoa/autocomplete_text_field_cell.h index 7715b4f..fdf4e1e 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_cell.h +++ b/chrome/browser/cocoa/autocomplete_text_field_cell.h @@ -49,19 +49,27 @@ @property BOOL fieldEditorNeedsReset; -// TODO(shess): There should be two alternatives for -// -setKeywordString:, the normal string and the min string. Min can -// be used when the text field's contents gets too wide to fit both it -// and this. - // The following setup |keywordString_| or |hintString_| based on the // input, and set |fieldEditorNeedsReset_| if the layout of the field // changed. -- (void)setKeywordString:(NSString*)aString; + +// Chooses |partialString| if |width| won't fit |fullString|. Strings +// must be non-nil. +- (void)setKeywordString:(NSString*)fullString + partialString:(NSString*)partialString + availableWidth:(CGFloat)width; + +// Chooses |anImage| only if all pieces won't fit w/in |width|. +// Inputs must be non-nil. - (void)setKeywordHintPrefix:(NSString*)prefixString image:(NSImage*)anImage - suffix:(NSString*)suffixString; -- (void)setSearchHintString:(NSString*)aString; + suffix:(NSString*)suffixString + availableWidth:(CGFloat)width; + +// Suppresses hint entirely if |aString| won't fit w/in |width|. +// String must be non-nil. +- (void)setSearchHintString:(NSString*)aString + availableWidth:(CGFloat)width; - (void)clearKeywordAndHint; // Sets the hint icon and optional icon label. If |icon| is nil, the current diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell.mm b/chrome/browser/cocoa/autocomplete_text_field_cell.mm index edaf16d..fb8042f 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_cell.mm +++ b/chrome/browser/cocoa/autocomplete_text_field_cell.mm @@ -47,6 +47,19 @@ const NSInteger kHintIconHorizontalPad = 3; // How far to shift bounding box of hint icon label down from top of field. const NSInteger kHintIconLabelYOffset = 7; +// How far the editor insets itself, for purposes of determining if +// decorations need to be trimmed. +const CGFloat kEditorHorizontalInset = 3.0; + +// Conveniences to centralize width+offset calculations. +CGFloat WidthForHint(NSAttributedString* hintString) { + return kHintXOffset + ceil([hintString size].width); +} +CGFloat WidthForKeyword(NSAttributedString* keywordString) { + return kKeywordXOffset + ceil([keywordString size].width) + + 2 * kKeywordTokenInset; +} + } // namespace @implementation AutocompleteTextFieldCell @@ -61,76 +74,101 @@ const NSInteger kHintIconLabelYOffset = 7; return hintString_.get(); } -- (void)setKeywordString:(NSString*)aString { - DCHECK(aString != nil); - if (hintString_ || ![[keywordString_ string] isEqualToString:aString]) { - NSDictionary* attributes = - [NSDictionary dictionaryWithObjectsAndKeys: - [self font], NSFontAttributeName, - nil]; +- (void)setKeywordString:(NSString*)fullString + partialString:(NSString*)partialString + availableWidth:(CGFloat)width { + DCHECK(fullString != nil); - keywordString_.reset( - [[NSAttributedString alloc] initWithString:aString - attributes:attributes]); + // Overriding |hintString_| always requires a reset. + if (hintString_) { hintString_.reset(); - fieldEditorNeedsReset_ = YES; } -} -- (void)setHintString:(NSAttributedString*)aString { - keywordString_.reset(); - hintString_.reset([aString copy]); + // Adjust for space between editor and decorations. + width -= 2 * kEditorHorizontalInset; + + // If |fullString| won't fit, choose |partialString|. + NSDictionary* attributes = + [NSDictionary dictionaryWithObject:[self font] + forKey:NSFontAttributeName]; + NSString* s = fullString; + if ([s sizeWithAttributes:attributes].width > width) { + if (partialString) { + s = partialString; + } + } + scoped_nsobject<NSAttributedString> as( + [[NSAttributedString alloc] initWithString:s attributes:attributes]); - fieldEditorNeedsReset_ = YES; + // If the value has changed, require a reset. + if (![keywordString_ isEqualToAttributedString:as]) { + keywordString_.reset([as retain]); + fieldEditorNeedsReset_ = YES; + } } // Convenience for the attributes used in the right-justified info // cells. - (NSDictionary*)hintAttributes { - NSMutableParagraphStyle* style = - [[[NSMutableParagraphStyle alloc] init] autorelease]; + scoped_nsobject<NSMutableParagraphStyle> style( + [[NSMutableParagraphStyle alloc] init]); [style setAlignment:NSRightTextAlignment]; return [NSDictionary dictionaryWithObjectsAndKeys: [self font], NSFontAttributeName, [NSColor lightGrayColor], NSForegroundColorAttributeName, - style, NSParagraphStyleAttributeName, + style.get(), NSParagraphStyleAttributeName, nil]; } - (void)setKeywordHintPrefix:(NSString*)prefixString image:(NSImage*)anImage - suffix:(NSString*)suffixString { + suffix:(NSString*)suffixString + availableWidth:(CGFloat)width { DCHECK(prefixString != nil); DCHECK(anImage != nil); DCHECK(suffixString != nil); + // Adjust for space between editor and decorations. + width -= 2 * kEditorHorizontalInset; + + // Overriding |keywordString_| always requires a reset. + if (keywordString_) { + keywordString_.reset(); + fieldEditorNeedsReset_ = YES; + } + + // If |hintString_| is now too wide, clear it so that we don't pass + // the equality tests. + if (hintString_ && WidthForHint(hintString_) > width) { + [self clearKeywordAndHint]; + } + // TODO(shess): Also check the length? - if (keywordString_ || - ![[hintString_ string] hasPrefix:prefixString] || + if (![[hintString_ string] hasPrefix:prefixString] || ![[hintString_ string] hasSuffix:suffixString]) { // Build an attributed string with the concatenation of the prefix // and suffix. NSString* s = [prefixString stringByAppendingString:suffixString]; - NSMutableAttributedString* as = - [[[NSMutableAttributedString alloc] - initWithString:s attributes:[self hintAttributes]] autorelease]; + scoped_nsobject<NSMutableAttributedString> as( + [[NSMutableAttributedString alloc] + initWithString:s attributes:[self hintAttributes]]); // Build an attachment containing the hint image. - NSTextAttachmentCell* attachmentCell = - [[[NSTextAttachmentCell alloc] initImageCell:anImage] autorelease]; - NSTextAttachment* attachment = - [[[NSTextAttachment alloc] init] autorelease]; + scoped_nsobject<NSTextAttachmentCell> attachmentCell( + [[NSTextAttachmentCell alloc] initImageCell:anImage]); + scoped_nsobject<NSTextAttachment> attachment( + [[NSTextAttachment alloc] init]); [attachment setAttachmentCell:attachmentCell]; // The attachment's baseline needs to be adjusted so the image // doesn't sit on the same baseline as the text and make // everything too tall. - NSMutableAttributedString* is = - [[[NSAttributedString attributedStringWithAttachment:attachment] - mutableCopy] autorelease]; + scoped_nsobject<NSMutableAttributedString> is( + [[NSAttributedString attributedStringWithAttachment:attachment] + mutableCopy]); [is addAttribute:NSBaselineOffsetAttributeName value:[NSNumber numberWithFloat:kKeywordHintImageBaseline] range:NSMakeRange(0, [is length])]; @@ -138,20 +176,41 @@ const NSInteger kHintIconLabelYOffset = 7; // Stuff the image attachment between the prefix and suffix. [as insertAttributedString:is atIndex:[prefixString length]]; - [self setHintString:as]; + // If too wide, just show the image. + hintString_.reset(WidthForHint(as) > width ? [is copy] : [as copy]); + + fieldEditorNeedsReset_ = YES; } } -- (void)setSearchHintString:(NSString*)aString { +- (void)setSearchHintString:(NSString*)aString + availableWidth:(CGFloat)width { DCHECK(aString != nil); - if (keywordString_ || ![[hintString_ string] isEqualToString:aString]) { - NSAttributedString* as = - [[[NSAttributedString alloc] initWithString:aString - attributes:[self hintAttributes]] - autorelease]; + // Adjust for space between editor and decorations. + width -= 2 * kEditorHorizontalInset; - [self setHintString:as]; + // Overriding |keywordString_| always requires a reset. + if (keywordString_) { + keywordString_.reset(); + fieldEditorNeedsReset_ = YES; + } + + // If |hintString_| is now too wide, clear it so that we don't pass + // the equality tests. + if (hintString_ && WidthForHint(hintString_) > width) { + [self clearKeywordAndHint]; + } + + if (![[hintString_ string] isEqualToString:aString]) { + scoped_nsobject<NSAttributedString> as( + [[NSAttributedString alloc] initWithString:aString + attributes:[self hintAttributes]]); + + // If too wide, don't keep the hint. + hintString_.reset(WidthForHint(as) > width ? nil : [as copy]); + + fieldEditorNeedsReset_ = YES; } } @@ -246,7 +305,7 @@ const NSInteger kHintIconLabelYOffset = 7; if (hintString_) { DCHECK(!keywordString_); - const CGFloat hintWidth = kHintXOffset + ceil([hintString_ size].width); + const CGFloat hintWidth(WidthForHint(hintString_)); // TODO(shess): This could be better. Show the hint until the // non-hint text bumps against it? @@ -255,8 +314,7 @@ const NSInteger kHintIconLabelYOffset = 7; } } else if (keywordString_) { DCHECK(!hintString_); - const CGFloat keywordWidth = kKeywordXOffset + - ceil([keywordString_ size].width) + 2 * kKeywordTokenInset; + const CGFloat keywordWidth(WidthForKeyword(keywordString_)); // TODO(shess): This could be better. There's support for a // "short" version of the keyword string, work that in in a diff --git a/chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm b/chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm index e0d5cbb..7908094 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm +++ b/chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm @@ -12,12 +12,19 @@ namespace { +// Width of the field so that we don't have to ask |field_| for it all +// the time. +const CGFloat kWidth(300.0); + +// A narrow width for tests which test things that don't fit. +const CGFloat kNarrowWidth(5.0); + class AutocompleteTextFieldCellTest : public PlatformTest { public: AutocompleteTextFieldCellTest() { // Make sure this is wide enough to play games with the cell // decorations. - const NSRect frame = NSMakeRect(0, 0, 300, 30); + const NSRect frame = NSMakeRect(0, 0, kWidth, 30); view_.reset([[NSTextField alloc] initWithFrame:frame]); scoped_nsobject<AutocompleteTextFieldCell> cell( [[AutocompleteTextFieldCell alloc] initTextCell:@"Testing"]); @@ -52,14 +59,17 @@ TEST_F(AutocompleteTextFieldCellTest, Display) { AutocompleteTextFieldCell* cell = static_cast<AutocompleteTextFieldCell*>([view_ cell]); - [cell setSearchHintString:@"Type to search"]; + [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; [view_ display]; NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; - [cell setKeywordHintPrefix:@"prefix" image:image suffix:@"suffix"]; + [cell setKeywordHintPrefix:@"prefix" image:image suffix:@"suffix" + availableWidth:kWidth]; [view_ display]; - [cell setKeywordString:@"Search Engine:"]; + [cell setKeywordString:@"Search Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; [view_ display]; } @@ -71,19 +81,19 @@ TEST_F(AutocompleteTextFieldCellTest, SearchHint) { EXPECT_FALSE([cell fieldEditorNeedsReset]); // Setting a search hint will need a reset. - [cell setSearchHintString:@"Type to search"]; + [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); // Changing the search hint needs a reset. - [cell setSearchHintString:@"Type to find"]; + [cell setSearchHintString:@"Type to find" availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); // Changing to an identical string doesn't need a reset. - [cell setSearchHintString:@"Type to find"]; + [cell setSearchHintString:@"Type to find" availableWidth:kWidth]; EXPECT_FALSE([cell fieldEditorNeedsReset]); } @@ -96,19 +106,22 @@ TEST_F(AutocompleteTextFieldCellTest, KeywordHint) { EXPECT_FALSE([cell fieldEditorNeedsReset]); // Setting a keyword hint will need a reset. - [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine"]; + [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine" + availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); // Changing the keyword hint needs a reset. - [cell setKeywordHintPrefix:@"Type " image:image suffix:@" to search Engine"]; + [cell setKeywordHintPrefix:@"Type " image:image suffix:@" to search Engine" + availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); // Changing to identical strings doesn't need a reset. - [cell setKeywordHintPrefix:@"Type " image:image suffix:@" to search Engine"]; + [cell setKeywordHintPrefix:@"Type " image:image suffix:@" to search Engine" + availableWidth:kWidth]; EXPECT_FALSE([cell fieldEditorNeedsReset]); } @@ -120,19 +133,25 @@ TEST_F(AutocompleteTextFieldCellTest, KeywordString) { EXPECT_FALSE([cell fieldEditorNeedsReset]); // Setting a keyword string will need a reset. - [cell setKeywordString:@"Search Engine:"]; + [cell setKeywordString:@"Search Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); // Changing the keyword string needs a reset. - [cell setKeywordString:@"Search on Engine:"]; + [cell setKeywordString:@"Search on Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); // Changing to an identical string doesn't need a reset. - [cell setKeywordString:@"Search on Engine:"]; + [cell setKeywordString:@"Search on Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; EXPECT_FALSE([cell fieldEditorNeedsReset]); } @@ -203,19 +222,22 @@ TEST_F(AutocompleteTextFieldCellTest, Transitions) { // Transitions from hint to keyword string, keyword hint, and // cleared. - [cell setSearchHintString:@"Type to search"]; + [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); - [cell setKeywordString:@"Search Engine:"]; + [cell setKeywordString:@"Search Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); - [cell setSearchHintString:@"Type to search"]; + [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); - [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine"]; + [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine" + availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); - [cell setSearchHintString:@"Type to search"]; + [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); [cell clearKeywordAndHint]; @@ -223,19 +245,24 @@ TEST_F(AutocompleteTextFieldCellTest, Transitions) { // Transitions from keyword hint to keyword string, simple hint, and // cleared. - [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine"]; + [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine" + availableWidth:kWidth]; [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); - [cell setSearchHintString:@"Type to search"]; + [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); - [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine"]; + [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine" + availableWidth:kWidth]; [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); - [cell setKeywordString:@"Search Engine:"]; + [cell setKeywordString:@"Search Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); - [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine"]; + [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine" + availableWidth:kWidth]; [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); [cell clearKeywordAndHint]; @@ -243,19 +270,26 @@ TEST_F(AutocompleteTextFieldCellTest, Transitions) { // Transitions from keyword string to either type of hint, or // cleared. - [cell setKeywordString:@"Search on Engine:"]; + [cell setKeywordString:@"Search on Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); - [cell setSearchHintString:@"Type to search"]; + [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); - [cell setKeywordString:@"Search on Engine:"]; + [cell setKeywordString:@"Search on Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); - [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine"]; + [cell setKeywordHintPrefix:@"Press " image:image suffix:@" to search Engine" + availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); - [cell setKeywordString:@"Search on Engine:"]; + [cell setKeywordString:@"Search on Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); [cell clearKeywordAndHint]; @@ -280,7 +314,7 @@ TEST_F(AutocompleteTextFieldCellTest, TextFrame) { EXPECT_TRUE(NSEqualRects(cursorFrame, textFrame)); // Small search hint leaves text frame to left. - [cell setSearchHintString:@"Search hint"]; + [cell setSearchHintString:@"Search hint" availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(textFrame)); EXPECT_TRUE(NSContainsRect(bounds, textFrame)); @@ -295,7 +329,8 @@ TEST_F(AutocompleteTextFieldCellTest, TextFrame) { // changed (keyword hint overrode search hint), and that they aren't // handled identically w/out reference to the actual button cell. NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; - [cell setKeywordHintPrefix:@"Keyword " image:image suffix:@" hint"]; + [cell setKeywordHintPrefix:@"Keyword " image:image suffix:@" hint" + availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(textFrame)); EXPECT_TRUE(NSContainsRect(bounds, textFrame)); @@ -304,7 +339,9 @@ TEST_F(AutocompleteTextFieldCellTest, TextFrame) { EXPECT_TRUE(NSContainsRect(cursorFrame, textFrame)); // Keyword search leaves text area to right. - [cell setKeywordString:@"Search Engine:"]; + [cell setKeywordString:@"Search Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; EXPECT_FALSE(NSIsEmptyRect(textFrame)); EXPECT_TRUE(NSContainsRect(bounds, textFrame)); @@ -333,7 +370,7 @@ TEST_F(AutocompleteTextFieldCellTest, TextFrame) { // Search hint text takes precedence over the hint icon; the text frame // should be smaller in order to accomodate the text that is wider than // the icon. - [cell setSearchHintString:@"Search hint"]; + [cell setSearchHintString:@"Search hint" availableWidth:kWidth]; NSRect textFrameWithHintText = [cell textFrameForFrame:bounds]; EXPECT_TRUE(NSContainsRect(textFrame, textFrameWithHintText)); EXPECT_LT(NSWidth(textFrameWithHintText), NSWidth(textFrame)); @@ -356,20 +393,23 @@ TEST_F(AutocompleteTextFieldCellTest, DrawingRectForBounds) { // TODO(shess): Do we really need to test every combination? - [cell setSearchHintString:@"Search hint"]; + [cell setSearchHintString:@"Search hint" availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_TRUE(NSContainsRect(textFrame, NSInsetRect(drawingRect, 1, 1))); NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; - [cell setKeywordHintPrefix:@"Keyword " image:image suffix:@" hint"]; + [cell setKeywordHintPrefix:@"Keyword " image:image suffix:@" hint" + availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); EXPECT_TRUE(NSContainsRect(NSInsetRect(textFrame, 1, 1), drawingRect)); - [cell setKeywordString:@"Search Engine:"]; + [cell setKeywordString:@"Search Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; textFrame = [cell textFrameForFrame:bounds]; drawingRect = [cell drawingRectForBounds:bounds]; EXPECT_FALSE(NSIsEmptyRect(drawingRect)); @@ -423,4 +463,77 @@ TEST_F(AutocompleteTextFieldCellTest, HintImageFrame) { EXPECT_TRUE(NSIsEmptyRect(iconRect)); } +// Test that the cell correctly chooses the partial keyword if there's +// no enough room. +TEST_F(AutocompleteTextFieldCellTest, UsesPartialKeywordIfNarrow) { + AutocompleteTextFieldCell* cell = + static_cast<AutocompleteTextFieldCell*>([view_ cell]); + + const NSString* kFullString = @"Search Engine:"; + const NSString* kPartialString = @"Search Eng:"; + + // Wide width chooses the full string. + [cell setKeywordString:kFullString + partialString:kPartialString + availableWidth:kWidth]; + EXPECT_TRUE([cell keywordString]); + EXPECT_TRUE([[[cell keywordString] string] isEqualToString:kFullString]); + + // Narrow width chooses the partial string. + [cell setKeywordString:kFullString + partialString:kPartialString + availableWidth:kNarrowWidth]; + EXPECT_TRUE([cell keywordString]); + EXPECT_TRUE([[[cell keywordString] string] isEqualToString:kPartialString]); + + // But not if there isn't one! + [cell setKeywordString:kFullString + partialString:nil + availableWidth:kNarrowWidth]; + EXPECT_TRUE([cell keywordString]); + EXPECT_TRUE([[[cell keywordString] string] isEqualToString:kFullString]); +} + +// Test that the cell drops the search hint if there is no room for +// it. +TEST_F(AutocompleteTextFieldCellTest, OmitsSearchHintIfNarrow) { + AutocompleteTextFieldCell* cell = + static_cast<AutocompleteTextFieldCell*>([view_ cell]); + + const NSString* kSearchHint = @"Type to search"; + + // Wide width chooses the full string. + [cell setSearchHintString:kSearchHint availableWidth:kWidth]; + EXPECT_TRUE([cell hintString]); + EXPECT_TRUE([[[cell hintString] string] isEqualToString:kSearchHint]); + + // Narrow width suppresses entirely. + [cell setSearchHintString:kSearchHint availableWidth:kNarrowWidth]; + EXPECT_FALSE([cell hintString]); +} + +// Test that the cell drops all but the image if there is no room for +// the entire keyword hint. +TEST_F(AutocompleteTextFieldCellTest, TrimsKeywordHintIfNarrow) { + AutocompleteTextFieldCell* cell = + static_cast<AutocompleteTextFieldCell*>([view_ cell]); + scoped_nsobject<NSImage> image( + [[NSImage alloc] initWithSize:NSMakeSize(20, 20)]); + + const NSString* kHintPrefix = @"Press "; + const NSString* kHintSuffix = @" to search Engine"; + + // Wide width chooses the full string. + [cell setKeywordHintPrefix:kHintPrefix image:image suffix:kHintSuffix + availableWidth:kWidth]; + EXPECT_TRUE([cell hintString]); + EXPECT_TRUE([[[cell hintString] string] hasPrefix:kHintPrefix]); + EXPECT_TRUE([[[cell hintString] string] hasSuffix:kHintSuffix]); + + // Narrow width suppresses everything but the image. + [cell setKeywordHintPrefix:kHintPrefix image:image suffix:kHintSuffix + availableWidth:kNarrowWidth]; + EXPECT_EQ([[cell hintString] length], 1U); +} + } // namespace diff --git a/chrome/browser/cocoa/autocomplete_text_field_unittest.mm b/chrome/browser/cocoa/autocomplete_text_field_unittest.mm index b6e5be5..a78a159 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_unittest.mm +++ b/chrome/browser/cocoa/autocomplete_text_field_unittest.mm @@ -55,12 +55,16 @@ NSEvent* Event(NSView* view, const NSPoint point, const NSEventType type) { return Event(view, point, type, 1); } +// Width of the field so that we don't have to ask |field_| for it all +// the time. +static const CGFloat kWidth(300.0); + class AutocompleteTextFieldTest : public PlatformTest { public: AutocompleteTextFieldTest() { // Make sure this is wide enough to play games with the cell // decorations. - NSRect frame = NSMakeRect(0, 0, 300, 30); + NSRect frame = NSMakeRect(0, 0, kWidth, 30); field_.reset([[AutocompleteTextField alloc] initWithFrame:frame]); [field_ setStringValue:@"Test test"]; [field_ setObserver:&field_observer_]; @@ -147,6 +151,40 @@ TEST_F(AutocompleteTextFieldTest, FirstResponder) { EXPECT_TRUE([[field_ currentEditor] isKindOfClass:c]); } +TEST_F(AutocompleteTextFieldTest, AvailableDecorationWidth) { + // A fudge factor to account for how much space the border takes up. + // The test shouldn't be too dependent on the field's internals, but + // it also shouldn't let deranged cases fall through the cracks + // (like nothing available with no text, or everything available + // with some text). + const CGFloat kBorderWidth = 20.0; + + // With no contents, almost the entire width is available for + // decorations. + [field_ setStringValue:@""]; + CGFloat availableWidth = [field_ availableDecorationWidth]; + EXPECT_LE(availableWidth, kWidth); + EXPECT_GT(availableWidth, kWidth - kBorderWidth); + + // With minor contents, most of the remaining width is available for + // decorations. + NSDictionary* attributes = + [NSDictionary dictionaryWithObject:[field_ font] + forKey:NSFontAttributeName]; + NSString* string = @"Hello world"; + const NSSize size([string sizeWithAttributes:attributes]); + [field_ setStringValue:string]; + availableWidth = [field_ availableDecorationWidth]; + EXPECT_LE(availableWidth, kWidth - size.width); + EXPECT_GT(availableWidth, kWidth - size.width - kBorderWidth); + + // With huge contents, nothing at all is left for decorations. + string = @"A long string which is surely wider than field_ can hold."; + [field_ setStringValue:string]; + availableWidth = [field_ availableDecorationWidth]; + EXPECT_LT(availableWidth, 0.0); +} + // Test drawing, mostly to ensure nothing leaks or crashes. TEST_F(AutocompleteTextFieldTest, Display) { [field_ display]; @@ -159,14 +197,17 @@ TEST_F(AutocompleteTextFieldTest, Display) { // Test display of various cell configurations. AutocompleteTextFieldCell* cell = [field_ autocompleteTextFieldCell]; - [cell setSearchHintString:@"Type to search"]; + [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; [field_ display]; NSImage* image = [NSImage imageNamed:@"NSApplicationIcon"]; - [cell setKeywordHintPrefix:@"prefix" image:image suffix:@"suffix"]; + [cell setKeywordHintPrefix:@"prefix" image:image suffix:@"suffix" + availableWidth:kWidth]; [field_ display]; - [cell setKeywordString:@"Search Engine:"]; + [cell setKeywordString:@"Search Engine:" + partialString:@"Search Eng:" + availableWidth:kWidth]; [field_ display]; [cell clearKeywordAndHint]; @@ -204,6 +245,14 @@ TEST_F(AutocompleteTextFieldTest, FieldEditorFlagsChanged) { [firstResponder flagsChanged:KeyDownEventWithFlags(NSControlKeyMask)]; } +// Frame size changes are propagated to |observer_|. +TEST_F(AutocompleteTextFieldTest, FrameChanged) { + EXPECT_CALL(field_observer_, OnFrameChanged()); + NSRect frame = [field_ frame]; + frame.size.width += 10.0; + [field_ setFrame:frame]; +} + // Test that the field editor gets the same bounds when focus is // delivered by the standard focusing machinery, or by // -resetFieldEditorFrameIfNeeded. @@ -217,7 +266,7 @@ TEST_F(AutocompleteTextFieldTest, ResetFieldEditorBase) { const NSRect baseEditorFrame(EditorFrame()); // Setting a hint should result in a strictly smaller editor frame. - [cell setSearchHintString:@"search hint"]; + [cell setSearchHintString:@"search hint" availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [field_ resetFieldEditorFrameIfNeeded]; EXPECT_FALSE([cell fieldEditorNeedsReset]); @@ -244,7 +293,7 @@ TEST_F(AutocompleteTextFieldTest, ResetFieldEditorSearchHint) { // Capture the editor frame resulting from the standard focus // machinery. - [cell setSearchHintString:kHintString]; + [cell setSearchHintString:kHintString availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); @@ -263,7 +312,7 @@ TEST_F(AutocompleteTextFieldTest, ResetFieldEditorSearchHint) { // Setting the same hint string and using // -resetFieldEditorFrameIfNeeded should result in the same frame as // the standard focus machinery. - [cell setSearchHintString:kHintString]; + [cell setSearchHintString:kHintString availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [field_ resetFieldEditorFrameIfNeeded]; EXPECT_FALSE([cell fieldEditorNeedsReset]); @@ -277,11 +326,14 @@ TEST_F(AutocompleteTextFieldTest, ResetFieldEditorKeywordHint) { AutocompleteTextFieldCell* cell = [field_ autocompleteTextFieldCell]; EXPECT_FALSE([cell fieldEditorNeedsReset]); - const NSString* kHintString(@"Search Engine:"); + const NSString* kFullString(@"Search Engine:"); + const NSString* kPartialString(@"Search Eng:"); // Capture the editor frame resulting from the standard focus // machinery. - [cell setKeywordString:kHintString]; + [cell setKeywordString:kFullString + partialString:kPartialString + availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [cell setFieldEditorNeedsReset:NO]; EXPECT_FALSE([cell fieldEditorNeedsReset]); @@ -300,7 +352,9 @@ TEST_F(AutocompleteTextFieldTest, ResetFieldEditorKeywordHint) { // Setting the same hint string and using // -resetFieldEditorFrameIfNeeded should result in the same frame as // the standard focus machinery. - [cell setKeywordString:kHintString]; + [cell setKeywordString:kFullString + partialString:kPartialString + availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [field_ resetFieldEditorFrameIfNeeded]; EXPECT_FALSE([cell fieldEditorNeedsReset]); @@ -357,7 +411,7 @@ TEST_F(AutocompleteTextFieldTest, ResetFieldEditorBlocksEndEditing) { // No more messages to mockDelegate. AutocompleteTextFieldCell* cell = [field_ autocompleteTextFieldCell]; EXPECT_FALSE([cell fieldEditorNeedsReset]); - [cell setSearchHintString:@"Type to search"]; + [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; EXPECT_TRUE([cell fieldEditorNeedsReset]); [field_ resetFieldEditorFrameIfNeeded]; [mockDelegate verify]; @@ -372,7 +426,7 @@ TEST_F(AutocompleteTextFieldTest, ClickSearchHintPutsCaretRightmost) { // Set the decoration before becoming responder. EXPECT_FALSE([field_ currentEditor]); AutocompleteTextFieldCell* cell = [field_ autocompleteTextFieldCell]; - [cell setSearchHintString:@"Type to search"]; + [cell setSearchHintString:@"Type to search" availableWidth:kWidth]; // Can't rely on the window machinery to make us first responder, // here. @@ -395,7 +449,9 @@ TEST_F(AutocompleteTextFieldTest, ClickKeywordPutsCaretLeftmost) { // Set the decoration before becoming responder. EXPECT_FALSE([field_ currentEditor]); AutocompleteTextFieldCell* cell = [field_ autocompleteTextFieldCell]; - [cell setKeywordString:@"Search Engine:"]; + [cell setKeywordString:@"Search Engine:" + partialString:@"Search:" + availableWidth:kWidth]; // Can't rely on the window machinery to make us first responder, // here. diff --git a/chrome/browser/cocoa/autocomplete_text_field_unittest_helper.h b/chrome/browser/cocoa/autocomplete_text_field_unittest_helper.h index c3a617e..cfe3443 100644 --- a/chrome/browser/cocoa/autocomplete_text_field_unittest_helper.h +++ b/chrome/browser/cocoa/autocomplete_text_field_unittest_helper.h @@ -30,6 +30,8 @@ namespace { // is here so the mock interface doesn't have to change in multiple // places. +// Any method you add here needs a unit test. You knew that. + class MockAutocompleteTextFieldObserver : public AutocompleteTextFieldObserver { public: MOCK_METHOD1(OnControlKeyChanged, void(bool pressed)); @@ -38,6 +40,7 @@ class MockAutocompleteTextFieldObserver : public AutocompleteTextFieldObserver { MOCK_METHOD0(GetPasteActionStringId, int()); MOCK_METHOD0(OnPasteAndGo, void()); MOCK_METHOD0(OnSecurityIconClicked, void()); + MOCK_METHOD0(OnFrameChanged, void()); }; } // namespace diff --git a/chrome/browser/cocoa/location_bar_view_mac.mm b/chrome/browser/cocoa/location_bar_view_mac.mm index f59d90d..a4d2e64 100644 --- a/chrome/browser/cocoa/location_bar_view_mac.mm +++ b/chrome/browser/cocoa/location_bar_view_mac.mm @@ -4,7 +4,7 @@ #import "chrome/browser/cocoa/location_bar_view_mac.h" -#include "app/l10n_util.h" +#include "app/l10n_util_mac.h" #include "app/resource_bundle.h" #include "base/string_util.h" #include "base/sys_string_conversions.h" @@ -49,6 +49,25 @@ static const CGFloat kEvTextColorRedComponent = 0.0; static const CGFloat kEvTextColorGreenComponent = 0.59; static const CGFloat kEvTextColorBlueComponent = 0.08; +// Build a short string to use in keyword-search when the field isn't +// very big. +// TODO(shess): Copied from views/location_bar_view.cc. Try to share. +std::wstring CalculateMinString(const std::wstring& description) { + // Chop at the first '.' or whitespace. + const size_t dot_index = description.find(L'.'); + const size_t ws_index = description.find_first_of(kWhitespaceWide); + size_t chop_index = std::min(dot_index, ws_index); + std::wstring min_string; + if (chop_index == std::wstring::npos) { + // No dot or whitespace, truncate to at most 3 chars. + min_string = l10n_util::TruncateString(description, 3); + } else { + min_string = description.substr(0, chop_index); + } + l10n_util::AdjustStringForLocaleDirection(min_string, &min_string); + return min_string; +} + } // namespace LocationBarViewMac::LocationBarViewMac( @@ -162,17 +181,27 @@ void LocationBarViewMac::OnChangedImpl(AutocompleteTextField* field, const bool show_search_hint, NSImage* image) { AutocompleteTextFieldCell* cell = [field autocompleteTextFieldCell]; + const CGFloat availableWidth([field availableDecorationWidth]); if (!keyword.empty() && !is_keyword_hint) { // Keyword search mode. The text will be like "Search Engine:". // "Engine" is a parameter to be replaced by text based on the // keyword. - // TODO(shess): This needs to additionally support a minimized - // version, to be used when the string below is too long. - const std::wstring keyword_text( - l10n_util::GetStringF(IDS_OMNIBOX_KEYWORD_TEXT, short_name)); - [cell setKeywordString:base::SysWideToNSString(keyword_text)]; + const std::wstring min_name(CalculateMinString(short_name)); + NSString* partial_string = nil; + if (!min_name.empty()) { + partial_string = + l10n_util::GetNSStringF(IDS_OMNIBOX_KEYWORD_TEXT, + WideToUTF16(min_name)); + } + + NSString* keyword_string = + l10n_util::GetNSStringF(IDS_OMNIBOX_KEYWORD_TEXT, + WideToUTF16(short_name)); + [cell setKeywordString:keyword_string + partialString:partial_string + availableWidth:availableWidth]; } else if (!keyword.empty() && is_keyword_hint) { // Keyword is a hint, like "Press [Tab] to search Engine". [Tab] // is a parameter to be replaced by an image. "Engine" is a @@ -193,12 +222,14 @@ void LocationBarViewMac::OnChangedImpl(AutocompleteTextField* field, NSString* prefix = base::SysWideToNSString(keyword_hint.substr(0, split)); NSString* suffix = base::SysWideToNSString(keyword_hint.substr(split)); - [cell setKeywordHintPrefix:prefix image:image suffix:suffix]; + [cell setKeywordHintPrefix:prefix image:image suffix:suffix + availableWidth:availableWidth]; } else if (show_search_hint) { // Show a search hint right-justified in the field if there is no // keyword. const std::wstring hint(l10n_util::GetString(IDS_OMNIBOX_EMPTY_TEXT)); - [cell setSearchHintString:base::SysWideToNSString(hint)]; + [cell setSearchHintString:base::SysWideToNSString(hint) + availableWidth:availableWidth]; } else { // Nothing interesting to show, plain old text field. [cell clearKeywordAndHint]; diff --git a/chrome/browser/cocoa/location_bar_view_mac_unittest.mm b/chrome/browser/cocoa/location_bar_view_mac_unittest.mm index 072f96e..8cbb3fb 100644 --- a/chrome/browser/cocoa/location_bar_view_mac_unittest.mm +++ b/chrome/browser/cocoa/location_bar_view_mac_unittest.mm @@ -64,7 +64,7 @@ class LocationBarViewMacTest : public PlatformTest { LocationBarViewMacTest() { // Make sure this is wide enough to play games with the cell // decorations. - NSRect frame = NSMakeRect(0, 0, 300, 30); + NSRect frame = NSMakeRect(0, 0, 400.0, 30); field_.reset([[AutocompleteTextField alloc] initWithFrame:frame]); [field_ setStringValue:@"Testing"]; [cocoa_helper_.contentView() addSubview:field_.get()]; @@ -84,6 +84,9 @@ TEST_F(LocationBarViewMacTest, OnChangedImpl) { const NSString* kKeywordPrefix = @"Press "; const NSString* kKeywordSuffix = @" to search Google"; const NSString* kKeywordString = @"Search Google:"; + // 0x2026 is Unicode ellipses. + const NSString* kPartialString = + [NSString stringWithFormat:@"Search Go%C:", 0x2026]; // With no special hints requested, none set. LocationBarViewMac::OnChangedImpl( @@ -121,6 +124,18 @@ TEST_F(LocationBarViewMacTest, OnChangedImpl) { EXPECT_TRUE([[[cell keywordString] string] isEqualToString:kKeywordString]); EXPECT_FALSE([cell hintString]); + // Check that a partial keyword-search string is passed down in case + // the view is narrow. + // TODO(shess): Is this test a good argument for re-writing using a + // mock field? + NSRect frame([field_ frame]); + frame.size.width = 10.0; + [field_ setFrame:frame]; + LocationBarViewMac::OnChangedImpl( + field_.get(), kKeyword, kKeyword, false, true, image); + EXPECT_TRUE([[[cell keywordString] string] isEqualToString:kPartialString]); + EXPECT_FALSE([cell hintString]); + // Transition back to baseline. LocationBarViewMac::OnChangedImpl( field_.get(), std::wstring(), std::wstring(), false, false, image); diff --git a/chrome/browser/cocoa/toolbar_controller.mm b/chrome/browser/cocoa/toolbar_controller.mm index cc69542..9f588a5 100644 --- a/chrome/browser/cocoa/toolbar_controller.mm +++ b/chrome/browser/cocoa/toolbar_controller.mm @@ -510,11 +510,14 @@ class PrefObserverBridge : public NotificationObserver { // up with the visible borders on the location stack. const int kLocationStackEdgeWidth = 2; - NSRect locationFrame = [locationBar_ frame]; - int minX = NSMinX([starButton_ frame]); - int maxX = NSMaxX([goButton_ frame]); - DCHECK(minX < NSMinX(locationFrame)); - DCHECK(maxX > NSMaxX(locationFrame)); + const NSRect locationFrame = [locationBar_ frame]; + + // Expand to include star and go buttons. Including the widths + // rather that calculating from their current placement because this + // method can be called while the resize is still rearranging the + // views involved. + const CGFloat minX = NSMinX(locationFrame) - NSWidth([starButton_ frame]); + const CGFloat maxX = NSMaxX(locationFrame) + NSWidth([goButton_ frame]); NSRect r = NSMakeRect(minX, NSMinY(locationFrame), maxX - minX, NSHeight(locationFrame)); |