summaryrefslogtreecommitdiffstats
path: root/chrome/browser
diff options
context:
space:
mode:
authorshess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-15 23:05:07 +0000
committershess@chromium.org <shess@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-10-15 23:05:07 +0000
commite9ccfe39326c10720e3b284c16e8c0bf96c965b5 (patch)
tree7438f7e470a0400417554b83681217bb04b17467 /chrome/browser
parent9c1981c591996af8fdbce5dcb0d2d473e7383e5e (diff)
downloadchromium_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')
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit_view_mac.h1
-rw-r--r--chrome/browser/autocomplete/autocomplete_edit_view_mac.mm11
-rw-r--r--chrome/browser/autocomplete/autocomplete_popup_view_mac.mm25
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field.h8
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field.mm14
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell.h24
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell.mm144
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_cell_unittest.mm183
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_unittest.mm82
-rw-r--r--chrome/browser/cocoa/autocomplete_text_field_unittest_helper.h3
-rw-r--r--chrome/browser/cocoa/location_bar_view_mac.mm47
-rw-r--r--chrome/browser/cocoa/location_bar_view_mac_unittest.mm17
-rw-r--r--chrome/browser/cocoa/toolbar_controller.mm13
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));