Chromium Code Reviews| 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; |