Index: chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm |
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm |
index 0a295320c06236866fae25cdaf9c62ee1dd5c5d6..cab3074bac0612543c20aaf7ff68c706e421a79e 100644 |
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm |
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm |
@@ -23,6 +23,10 @@ |
namespace { |
+// How much to adjust the cell sizing up from the default determined |
+// by the font. |
+const CGFloat kCellHeightAdjust = 6.0; |
+ |
// How far to offset image column from the left. |
const CGFloat kImageXOffset = 5.0; |
@@ -155,54 +159,76 @@ NSAttributedString* CreateClassifiedAttributedString( |
} // namespace |
-@implementation OmniboxPopupCell |
+@interface OmniboxPopupCell () |
+- (CGFloat)drawMatchPart:(NSAttributedString*)attributedString |
+ withFrame:(NSRect)cellFrame |
+ atOffset:(CGFloat)offset |
+ withMaxWidth:(int)maxWidth |
+ inView:(NSView*)controlView; |
+@end |
-- (id)init { |
- self = [super init]; |
- if (self) { |
- [self setImagePosition:NSImageLeft]; |
- [self setBordered:NO]; |
- [self setButtonType:NSRadioButton]; |
+@implementation OmniboxPopupCellData |
- // Without this highlighting messes up white areas of images. |
- [self setHighlightsBy:NSNoCellMask]; |
+- (id)initWithMatch:(const AutocompleteMatch&)match image:(NSImage*)image { |
+ if ((self = [super init])) { |
+ image_.reset([image retain]); |
const base::string16& raw_separator = |
l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); |
separator_.reset( |
[CreateAttributedString(raw_separator, DimTextColor()) retain]); |
+ |
+ isContentsRTL_ = |
+ (base::i18n::RIGHT_TO_LEFT == |
+ base::i18n::GetFirstStrongCharacterDirection(match.contents)); |
+ matchType_ = match.type; |
+ |
+ // Prefix may not have any characters with strong directionality, and may |
+ // take |
+ // the UI directionality. But prefix needs to appear in continuation of the |
+ // contents so we force the directionality. |
Scott Hess - ex-Googler
2015/05/21 20:40:26
Reformat comment.
dschuyler
2015/05/26 18:40:20
Done.
|
+ NSTextAlignment textAlignment = |
+ isContentsRTL_ ? NSRightTextAlignment : NSLeftTextAlignment; |
+ prefix_.reset( |
+ [CreateAttributedString(base::UTF8ToUTF16(match.GetAdditionalInfo( |
+ kACMatchPropertyContentsPrefix)), |
+ ContentTextColor(), textAlignment) retain]); |
+ |
+ contents_.reset([CreateClassifiedAttributedString( |
+ match.contents, ContentTextColor(), match.contents_class) retain]); |
+ |
+ if (match.answer) { |
+ base::string16 answerString; |
+ DCHECK(!match.answer->second_line().text_fields().empty()); |
+ for (const SuggestionAnswer::TextField& textField : |
+ match.answer->second_line().text_fields()) |
+ answerString.append(textField.text()); |
+ const base::string16 space(base::ASCIIToUTF16(" ")); |
+ const SuggestionAnswer::TextField* textField = |
+ match.answer->second_line().additional_text(); |
+ if (textField) |
+ answerString.append(space).append(textField->text()); |
+ textField = match.answer->second_line().status_text(); |
+ if (textField) |
+ answerString.append(space).append(textField->text()); |
+ description_.reset([CreateClassifiedAttributedString( |
+ answerString, DimTextColor(), match.description_class) retain]); |
+ } else if (match.description.empty()) { |
+ description_.reset(); |
+ } else { |
+ description_.reset([CreateClassifiedAttributedString( |
+ match.description, DimTextColor(), match.description_class) retain]); |
+ } |
} |
return self; |
} |
-- (void)setMatch:(const AutocompleteMatch&)match { |
- match_ = match; |
- NSAttributedString *contents = CreateClassifiedAttributedString( |
- match_.contents, ContentTextColor(), match_.contents_class); |
- [self setAttributedTitle:contents]; |
- |
- if (match_.answer) { |
- base::string16 answerString; |
- DCHECK(!match_.answer->second_line().text_fields().empty()); |
- for (const SuggestionAnswer::TextField& textField : |
- match_.answer->second_line().text_fields()) |
- answerString += textField.text(); |
- const base::char16 space(' '); |
- const SuggestionAnswer::TextField* textField = |
- match_.answer->second_line().additional_text(); |
- if (textField) |
- answerString += space + textField->text(); |
- textField = match_.answer->second_line().status_text(); |
- if (textField) |
- answerString += space + textField->text(); |
- description_.reset([CreateClassifiedAttributedString( |
- answerString, DimTextColor(), match_.description_class) retain]); |
- } else if (match_.description.empty()) { |
- description_.reset(); |
- } else { |
- description_.reset([CreateClassifiedAttributedString( |
- match_.description, DimTextColor(), match_.description_class) retain]); |
- } |
+- (void)setContents:(NSAttributedString*)contents { |
+ contents_.reset([contents retain]); |
+} |
+ |
+- (void)setImage:(NSImage*)image { |
+ image_.reset([image retain]); |
} |
- (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth { |
@@ -213,79 +239,64 @@ NSAttributedString* CreateClassifiedAttributedString( |
contentsOffset_ = contentsOffset; |
} |
-// The default NSButtonCell drawing leaves the image flush left and |
-// the title next to the image. This spaces things out to line up |
-// with the star button and autocomplete field. |
-- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { |
- if ([self state] == NSOnState || [self isHighlighted]) { |
- if ([self state] == NSOnState) |
- [SelectedBackgroundColor() set]; |
- else |
- [HoveredBackgroundColor() set]; |
- NSBezierPath* path = |
- [NSBezierPath bezierPathWithRoundedRect:cellFrame |
- xRadius:kCellRoundingRadius |
- yRadius:kCellRoundingRadius]; |
- [path fill]; |
- } |
- |
- // Put the image centered vertically but in a fixed column. |
- NSImage* image = [self image]; |
- if (image) { |
- NSRect imageRect = cellFrame; |
- imageRect.size = [image size]; |
- imageRect.origin.y += |
- std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0); |
- imageRect.origin.x += kImageXOffset; |
- [image drawInRect:FlipIfRTL(imageRect, cellFrame) |
- fromRect:NSZeroRect // Entire image |
- operation:NSCompositeSourceOver |
- fraction:1.0 |
- respectFlipped:YES |
- hints:nil]; |
- } |
- |
- [self drawMatchWithFrame:cellFrame inView:controlView]; |
+- (CGFloat)getMatchContentsWidth { |
+ return [contents_ size].width; |
} |
-- (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
- NSAttributedString* contents = [self attributedTitle]; |
+- (CGFloat)rowHeight { |
+ return [image_ size].height + kCellHeightAdjust; |
Scott Hess - ex-Googler
2015/05/21 20:40:26
I realize that the current version doesn't have th
dschuyler
2015/05/26 18:40:20
Done.
|
+} |
+- (void)drawMatchWithFrame:(NSRect)cellFrame |
+ inCell:(OmniboxPopupCell*)cell |
+ inView:(NSView*)controlView { |
CGFloat remainingWidth = GetContentAreaWidth(cellFrame); |
CGFloat contentsWidth = [self getMatchContentsWidth]; |
CGFloat separatorWidth = [separator_ size].width; |
- CGFloat descriptionWidth = description_.get() ? [description_ size].width : 0; |
+ CGFloat descriptionWidth = [description_ size].width; |
int contentsMaxWidth, descriptionMaxWidth; |
OmniboxPopupModel::ComputeMatchMaxWidths( |
- ceilf(contentsWidth), |
- ceilf(separatorWidth), |
- ceilf(descriptionWidth), |
- ceilf(remainingWidth), |
- !AutocompleteMatch::IsSearchType(match_.type), |
- &contentsMaxWidth, |
- &descriptionMaxWidth); |
+ ceilf(contentsWidth), ceilf(separatorWidth), ceilf(descriptionWidth), |
+ ceilf(remainingWidth), !AutocompleteMatch::IsSearchType(matchType_), |
+ &contentsMaxWidth, &descriptionMaxWidth); |
+ |
+ // Put the image centered vertically but in a fixed column. |
+ if (image_) { |
+ NSRect imageRect = cellFrame; |
+ imageRect.size = [image_ size]; |
+ imageRect.origin.y += |
+ std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0); |
+ imageRect.origin.x += kImageXOffset; |
+ [image_ drawInRect:FlipIfRTL(imageRect, cellFrame) |
+ fromRect:NSZeroRect |
+ operation:NSCompositeSourceOver |
+ fraction:1.0 |
+ respectFlipped:YES |
+ hints:nil]; |
+ } |
CGFloat offset = kTextStartOffset; |
- if (match_.type == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { |
+ if (matchType_ == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { |
// Infinite suggestions are rendered with a prefix (usually ellipsis), which |
// appear vertically stacked. |
offset += [self drawMatchPrefixWithFrame:cellFrame |
+ inCell:cell |
inView:controlView |
withContentsMaxWidth:&contentsMaxWidth]; |
} |
- offset += [self drawMatchPart:contents |
+ offset += [cell drawMatchPart:contents_ |
withFrame:cellFrame |
atOffset:offset |
withMaxWidth:contentsMaxWidth |
inView:controlView]; |
if (descriptionMaxWidth != 0) { |
- offset += [self drawMatchPart:separator_ |
+ offset += [cell drawMatchPart:separator_ |
withFrame:cellFrame |
atOffset:offset |
withMaxWidth:separatorWidth |
inView:controlView]; |
- offset += [self drawMatchPart:description_ |
+ offset += [cell drawMatchPart:description_ |
withFrame:cellFrame |
atOffset:offset |
withMaxWidth:descriptionMaxWidth |
@@ -294,25 +305,15 @@ NSAttributedString* CreateClassifiedAttributedString( |
} |
- (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame |
+ inCell:(OmniboxPopupCell*)cell |
inView:(NSView*)controlView |
withContentsMaxWidth:(int*)contentsMaxWidth { |
CGFloat offset = 0.0f; |
CGFloat remainingWidth = GetContentAreaWidth(cellFrame); |
- bool isRTL = base::i18n::IsRTL(); |
- bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT == |
- base::i18n::GetFirstStrongCharacterDirection(match_.contents)); |
- // Prefix may not have any characters with strong directionality, and may take |
- // the UI directionality. But prefix needs to appear in continuation of the |
- // contents so we force the directionality. |
- NSTextAlignment textAlignment = isContentsRTL ? |
- NSRightTextAlignment : NSLeftTextAlignment; |
- prefix_.reset([CreateAttributedString(base::UTF8ToUTF16( |
- match_.GetAdditionalInfo(kACMatchPropertyContentsPrefix)), |
- ContentTextColor(), textAlignment) retain]); |
CGFloat prefixWidth = [prefix_ size].width; |
CGFloat prefixOffset = 0.0f; |
- if (isRTL != isContentsRTL) { |
+ if (base::i18n::IsRTL() != isContentsRTL_) { |
// The contents is rendered between the contents offset extending towards |
// the start edge, while prefix is rendered in opposite direction. Ideally |
// the prefix should be rendered at |contentsOffset_|. If that is not |
@@ -329,13 +330,14 @@ NSAttributedString* CreateClassifiedAttributedString( |
// |remainingWidth|, then we shift the offset to the left , so that all |
// postfix suggestions are visible. |
// We have to render the prefix, so offset has to be at least |prefixWidth|. |
- offset = std::max(prefixWidth, |
+ offset = std::max( |
+ prefixWidth, |
std::min(remainingWidth - maxMatchContentsWidth_, contentsOffset_)); |
prefixOffset = offset - prefixWidth; |
} |
*contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth), |
*contentsMaxWidth); |
- [self drawMatchPart:prefix_ |
+ [cell drawMatchPart:prefix_ |
withFrame:cellFrame |
atOffset:prefixOffset + kTextStartOffset |
withMaxWidth:prefixWidth |
@@ -343,7 +345,39 @@ NSAttributedString* CreateClassifiedAttributedString( |
return offset; |
} |
-- (CGFloat)drawMatchPart:(NSAttributedString*)as |
+@end |
+ |
+@implementation OmniboxPopupCell |
+ |
+- (id)copyWithZone:(NSZone*)zone { |
+ OmniboxPopupCell* copy = [super copyWithZone:zone]; |
+ // The representedObject is set to nil in the copy. |
Scott Hess - ex-Googler
2015/05/21 20:40:27
What did they do, put _all_ of the special edge ca
groby-ooo-7-16
2015/05/22 05:03:07
Hey, at least this one is documented. (Under repre
dschuyler
2015/05/26 18:40:19
Acknowledged.
dschuyler
2015/05/26 18:40:19
I'm looking into the objectValue vs representedObj
|
+ [copy setRepresentedObject:[self representedObject]]; |
+ return copy; |
+} |
+ |
+// The default NSButtonCell drawing leaves the image flush left and |
groby-ooo-7-16
2015/05/21 03:01:48
Comment doesn't apply any more - it's just an NSCe
dschuyler
2015/05/26 18:40:19
Done.
|
+// the title next to the image. This spaces things out to line up |
+// with the star button and autocomplete field. |
+- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
+ if ([self state] == NSOnState || [self isHighlighted]) { |
+ if ([self state] == NSOnState) |
+ [SelectedBackgroundColor() set]; |
+ else |
+ [HoveredBackgroundColor() set]; |
+ NSBezierPath* path = |
+ [NSBezierPath bezierPathWithRoundedRect:cellFrame |
+ xRadius:kCellRoundingRadius |
+ yRadius:kCellRoundingRadius]; |
+ [path fill]; |
+ } |
+ |
+ base::scoped_nsobject<OmniboxPopupCellData> cellData( |
+ [self representedObject]); |
groby-ooo-7-16
2015/05/21 03:01:48
No need to scope - representedObject (and objectVa
Scott Hess - ex-Googler
2015/05/21 20:40:26
Scoping is wrong, because -representedObject is no
dschuyler
2015/05/26 18:40:20
Done.
dschuyler
2015/05/26 18:40:20
Acknowledged.
|
+ [cellData drawMatchWithFrame:cellFrame inCell:self inView:controlView]; |
+} |
+ |
+- (CGFloat)drawMatchPart:(NSAttributedString*)attributedString |
withFrame:(NSRect)cellFrame |
atOffset:(CGFloat)offset |
withMaxWidth:(int)maxWidth |
@@ -353,20 +387,15 @@ NSAttributedString* CreateClassifiedAttributedString( |
NSRect renderRect = ShiftRect(cellFrame, offset); |
renderRect.size.width = |
std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth)); |
- if (renderRect.size.width != 0) { |
- [self drawTitle:as |
- withFrame:FlipIfRTL(renderRect, cellFrame) |
- inView:controlView]; |
- } |
+ NSRect textRect = |
+ [attributedString boundingRectWithSize:renderRect.size options:nil]; |
+ renderRect.origin.y += |
+ std::floor((NSHeight(cellFrame) - NSHeight(textRect)) / 2.0); |
+ if (NSWidth(renderRect) != 0) |
Scott Hess - ex-Googler
2015/05/21 20:40:26
I'd say > 0.0. Just because I'm that paranoid.
groby-ooo-7-16
2015/05/22 05:03:07
Let's be properly paranoid and say !NSIsEmptyRect(
dschuyler
2015/05/26 18:40:20
Done.
|
+ [attributedString drawInRect:FlipIfRTL(renderRect, cellFrame)]; |
return NSWidth(renderRect); |
} |
-- (CGFloat)getMatchContentsWidth { |
- NSAttributedString* contents = [self attributedTitle]; |
- return contents ? [contents size].width : 0; |
-} |
- |
- |
+ (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match { |
const base::string16& inputText = base::UTF8ToUTF16( |
match.GetAdditionalInfo(kACMatchPropertyInputText)); |
@@ -385,9 +414,10 @@ NSAttributedString* CreateClassifiedAttributedString( |
base::i18n::GetFirstStrongCharacterDirection(match.contents)); |
// Color does not matter. |
- NSAttributedString* as = CreateAttributedString(inputText, DimTextColor()); |
- base::scoped_nsobject<NSTextStorage> textStorage([[NSTextStorage alloc] |
- initWithAttributedString:as]); |
+ NSAttributedString* attributedString = |
+ CreateAttributedString(inputText, DimTextColor()); |
+ base::scoped_nsobject<NSTextStorage> textStorage( |
+ [[NSTextStorage alloc] initWithAttributedString:attributedString]); |
base::scoped_nsobject<NSLayoutManager> layoutManager( |
[[NSLayoutManager alloc] init]); |
base::scoped_nsobject<NSTextContainer> textContainer( |
@@ -403,7 +433,7 @@ NSAttributedString* CreateClassifiedAttributedString( |
// left edge of the string, irrespective of the directionality of UI or text. |
CGFloat glyphOffset = [layoutManager locationForGlyphAtIndex:glyphIndex].x; |
- CGFloat inputWidth = [as size].width; |
+ CGFloat inputWidth = [attributedString size].width; |
// The offset obtained above may need to be corrected because the left-most |
// glyph may not have 0 offset. So we find the offset of left-most glyph, and |
@@ -417,7 +447,7 @@ NSAttributedString* CreateClassifiedAttributedString( |
// we are looking for. |
CGFloat glyphWidth = inputWidth; |
- for (NSUInteger i = 0; i < [as length]; i++) { |
+ for (NSUInteger i = 0; i < [attributedString length]; i++) { |
if (i == charIndex) continue; |
glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:i]; |
CGFloat offset = [layoutManager locationForGlyphAtIndex:glyphIndex].x; |