| 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 936ce5751103fb1b3bf15a41aa3f1b6258b4b182..c153e4e3ec22d347b0dfb892c8ad87c91fe097e9 100644
|
| --- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
|
| +++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
|
| @@ -8,6 +8,7 @@
|
| #include <cmath>
|
|
|
| #include "base/i18n/rtl.h"
|
| +#include "base/mac/foundation_util.h"
|
| #include "base/mac/scoped_nsobject.h"
|
| #include "base/strings/string_number_conversions.h"
|
| #include "base/strings/string_util.h"
|
| @@ -24,6 +25,13 @@
|
|
|
| namespace {
|
|
|
| +// How much to adjust the cell sizing up from the default determined
|
| +// by the font.
|
| +const CGFloat kCellHeightAdjust = 6.0;
|
| +
|
| +// How large the icon should be when displayed.
|
| +const CGFloat kImageSize = 19.0;
|
| +
|
| // How far to offset image column from the left.
|
| const CGFloat kImageXOffset = 5.0;
|
|
|
| @@ -279,85 +287,105 @@ NSAttributedString* CreateClassifiedAttributedString(
|
|
|
| } // namespace
|
|
|
| -@implementation OmniboxPopupCell
|
| -
|
| -- (instancetype)init {
|
| - self = [super init];
|
| - if (self) {
|
| - [self setImagePosition:NSImageLeft];
|
| - [self setBordered:NO];
|
| - [self setButtonType:NSRadioButton];
|
| -
|
| - // Without this highlighting messes up white areas of images.
|
| - [self setHighlightsBy:NSNoCellMask];
|
| +@interface OmniboxPopupCell ()
|
| +- (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
|
| + withFrame:(NSRect)cellFrame
|
| + atOffset:(CGFloat)offset
|
| + withMaxWidth:(int)maxWidth;
|
| +- (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame
|
| + tableView:(OmniboxPopupMatrix*)tableView
|
| + withContentsMaxWidth:(int*)contentsMaxWidth;
|
| +- (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView;
|
| +@end
|
|
|
| - const base::string16& raw_separator =
|
| - l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR);
|
| - separator_.reset(
|
| - [CreateAttributedString(raw_separator, DimTextColor()) retain]);
|
| +@implementation OmniboxPopupCellData
|
| +
|
| +@synthesize contents = contents_;
|
| +@synthesize description = description_;
|
| +@synthesize prefix = prefix_;
|
| +@synthesize image = image_;
|
| +@synthesize answerImage = answerImage_;
|
| +@synthesize contentsOffset = contentsOffset_;
|
| +@synthesize isContentsRTL = isContentsRTL_;
|
| +@synthesize matchType = matchType_;
|
| +
|
| +- (instancetype)initWithMatch:(const AutocompleteMatch&)match
|
| + contentsOffset:(CGFloat)contentsOffset
|
| + image:(NSImage*)image
|
| + answerImage:(NSImage*)answerImage {
|
| + if ((self = [super init])) {
|
| + image_ = [image retain];
|
| + answerImage_ = [answerImage retain];
|
| + contentsOffset_ = contentsOffset;
|
| +
|
| + 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.
|
| + NSTextAlignment textAlignment =
|
| + isContentsRTL_ ? NSRightTextAlignment : NSLeftTextAlignment;
|
| + prefix_ =
|
| + [CreateAttributedString(base::UTF8ToUTF16(match.GetAdditionalInfo(
|
| + kACMatchPropertyContentsPrefix)),
|
| + ContentTextColor(), textAlignment) retain];
|
| +
|
| + contents_ = [CreateClassifiedAttributedString(
|
| + match.contents, ContentTextColor(), match.contents_class) retain];
|
| +
|
| + if (match.answer) {
|
| + base::scoped_nsobject<NSMutableAttributedString> answerString(
|
| + [[NSMutableAttributedString alloc] init]);
|
| + DCHECK(!match.answer->second_line().text_fields().empty());
|
| + for (const SuggestionAnswer::TextField& textField :
|
| + match.answer->second_line().text_fields()) {
|
| + [answerString
|
| + appendAttributedString:CreateAnswerString(textField.text(),
|
| + textField.type())];
|
| + }
|
| + const base::string16 space(base::ASCIIToUTF16(" "));
|
| + const SuggestionAnswer::TextField* textField =
|
| + match.answer->second_line().additional_text();
|
| + if (textField) {
|
| + [answerString
|
| + appendAttributedString:CreateAnswerString(space + textField->text(),
|
| + textField->type())];
|
| + }
|
| + textField = match.answer->second_line().status_text();
|
| + if (textField) {
|
| + [answerString
|
| + appendAttributedString:CreateAnswerString(space + textField->text(),
|
| + textField->type())];
|
| + }
|
| + description_ = answerString.release();
|
| + } else if (!match.description.empty()) {
|
| + description_ = [CreateClassifiedAttributedString(
|
| + match.description, DimTextColor(), match.description_class) retain];
|
| + }
|
| }
|
| return self;
|
| }
|
|
|
| -- (void)setAnswerImage:(NSImage*)image {
|
| - answerImage_.reset([image retain]);
|
| +- (instancetype)copyWithZone:(NSZone*)zone {
|
| + return [self retain];
|
| }
|
|
|
| -- (void)setMatch:(const AutocompleteMatch&)match {
|
| - match_ = match;
|
| - NSAttributedString *contents = CreateClassifiedAttributedString(
|
| - match_.contents, ContentTextColor(), match_.contents_class);
|
| - [self setAttributedTitle:contents];
|
| - [self setAnswerImage:nil];
|
| - if (match_.answer) {
|
| - base::scoped_nsobject<NSMutableAttributedString> answerString(
|
| - [[NSMutableAttributedString alloc] init]);
|
| - DCHECK(!match_.answer->second_line().text_fields().empty());
|
| - for (const SuggestionAnswer::TextField& textField :
|
| - match_.answer->second_line().text_fields()) {
|
| - NSAttributedString* attributedString =
|
| - CreateAnswerString(textField.text(), textField.type());
|
| - [answerString appendAttributedString:attributedString];
|
| - }
|
| - const base::char16 space(' ');
|
| - const SuggestionAnswer::TextField* textField =
|
| - match_.answer->second_line().additional_text();
|
| - if (textField) {
|
| - [answerString
|
| - appendAttributedString:CreateAnswerString(space + textField->text(),
|
| - textField->type())];
|
| - }
|
| - textField = match_.answer->second_line().status_text();
|
| - if (textField) {
|
| - [answerString
|
| - appendAttributedString:CreateAnswerString(space + textField->text(),
|
| - textField->type())];
|
| - }
|
| - description_.reset(answerString.release());
|
| - } else if (match_.description.empty()) {
|
| - description_.reset();
|
| - } else {
|
| - description_.reset([CreateClassifiedAttributedString(
|
| - match_.description, DimTextColor(), match_.description_class) retain]);
|
| - }
|
| +- (CGFloat)getMatchContentsWidth {
|
| + return [contents_ size].width;
|
| }
|
|
|
| -- (NSAttributedString*)description {
|
| - return description_;
|
| +- (CGFloat)rowHeight {
|
| + return kImageSize + kCellHeightAdjust;
|
| }
|
|
|
| -- (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth {
|
| - maxMatchContentsWidth_ = maxMatchContentsWidth;
|
| -}
|
| +@end
|
|
|
| -- (void)setContentsOffset:(CGFloat)contentsOffset {
|
| - contentsOffset_ = contentsOffset;
|
| -}
|
| +@implementation OmniboxPopupCell
|
|
|
| -// 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 {
|
| +- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
|
| if ([self state] == NSOnState || [self isHighlighted]) {
|
| if ([self state] == NSOnState)
|
| [SelectedBackgroundColor() set];
|
| @@ -370,154 +398,130 @@ NSAttributedString* CreateClassifiedAttributedString(
|
| [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];
|
| }
|
|
|
| - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
|
| - NSAttributedString* contents = [self attributedTitle];
|
| -
|
| + OmniboxPopupCellData* cellData =
|
| + base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]);
|
| + OmniboxPopupMatrix* tableView =
|
| + base::mac::ObjCCastStrict<OmniboxPopupMatrix>(controlView);
|
| CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
|
| - CGFloat contentsWidth = [self getMatchContentsWidth];
|
| - CGFloat separatorWidth = [separator_ size].width;
|
| - CGFloat descriptionWidth = description_.get() ? [description_ size].width : 0;
|
| + CGFloat contentsWidth = [cellData getMatchContentsWidth];
|
| + CGFloat separatorWidth = [[tableView separator] size].width;
|
| + CGFloat descriptionWidth =
|
| + [cellData description] ? [[cellData description] size].width : 0;
|
| int contentsMaxWidth, descriptionMaxWidth;
|
| OmniboxPopupModel::ComputeMatchMaxWidths(
|
| - ceilf(contentsWidth),
|
| - ceilf(separatorWidth),
|
| - ceilf(descriptionWidth),
|
| + ceilf(contentsWidth), ceilf(separatorWidth), ceilf(descriptionWidth),
|
| ceilf(remainingWidth),
|
| - !AutocompleteMatch::IsSearchType(match_.type),
|
| - &contentsMaxWidth,
|
| + !AutocompleteMatch::IsSearchType([cellData matchType]), &contentsMaxWidth,
|
| &descriptionMaxWidth);
|
|
|
| + // Put the image centered vertically but in a fixed column.
|
| + NSRect imageRect = cellFrame;
|
| + imageRect.size = [[cellData image] size];
|
| + imageRect.origin.y +=
|
| + std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0);
|
| + imageRect.origin.x += kImageXOffset;
|
| + [[cellData 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 ([cellData matchType] == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) {
|
| // Infinite suggestions are rendered with a prefix (usually ellipsis), which
|
| // appear vertically stacked.
|
| offset += [self drawMatchPrefixWithFrame:cellFrame
|
| - inView:controlView
|
| + tableView:tableView
|
| withContentsMaxWidth:&contentsMaxWidth];
|
| }
|
| - offset += [self drawMatchPart:contents
|
| + offset += [self drawMatchPart:[cellData contents]
|
| withFrame:cellFrame
|
| atOffset:offset
|
| - withMaxWidth:contentsMaxWidth
|
| - inView:controlView];
|
| + withMaxWidth:contentsMaxWidth];
|
|
|
| if (descriptionMaxWidth != 0) {
|
| - offset += [self drawMatchPart:separator_
|
| + offset += [self drawMatchPart:[tableView separator]
|
| withFrame:cellFrame
|
| atOffset:offset
|
| - withMaxWidth:separatorWidth
|
| - inView:controlView];
|
| - if (answerImage_) {
|
| + withMaxWidth:separatorWidth];
|
| NSRect imageRect = NSMakeRect(offset, NSMinY(cellFrame),
|
| NSHeight(cellFrame), NSHeight(cellFrame));
|
| - [answerImage_ drawInRect:FlipIfRTL(imageRect, cellFrame)
|
| - fromRect:NSZeroRect
|
| - operation:NSCompositeSourceOver
|
| - fraction:1.0
|
| - respectFlipped:YES
|
| - hints:nil];
|
| - offset += NSWidth(imageRect);
|
| - }
|
| - offset += [self drawMatchPart:description_
|
| - withFrame:cellFrame
|
| - atOffset:offset
|
| - withMaxWidth:descriptionMaxWidth
|
| - inView:controlView];
|
| + [[cellData answerImage] drawInRect:FlipIfRTL(imageRect, cellFrame)
|
| + fromRect:NSZeroRect
|
| + operation:NSCompositeSourceOver
|
| + fraction:1.0
|
| + respectFlipped:YES
|
| + hints:nil];
|
| + if ([cellData answerImage])
|
| + offset += NSWidth(imageRect);
|
| + offset += [self drawMatchPart:[cellData description]
|
| + withFrame:cellFrame
|
| + atOffset:offset
|
| + withMaxWidth:descriptionMaxWidth];
|
| }
|
| }
|
|
|
| - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame
|
| - inView:(NSView*)controlView
|
| + tableView:(OmniboxPopupMatrix*)tableView
|
| withContentsMaxWidth:(int*)contentsMaxWidth {
|
| + OmniboxPopupCellData* cellData =
|
| + base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]);
|
| CGFloat offset = 0.0f;
|
| CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
|
| - 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 prefixWidth = [[cellData prefix] size].width;
|
|
|
| CGFloat prefixOffset = 0.0f;
|
| - if (base::i18n::IsRTL() != isContentsRTL) {
|
| + if (base::i18n::IsRTL() != [cellData 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
|
| // sufficient to render the widest suggestion, we increase it to
|
| - // |maxMatchContentsWidth_|. If |remainingWidth| is not sufficient to
|
| + // |maxMatchContentsWidth|. If |remainingWidth| is not sufficient to
|
| // accommodate that, we reduce the offset so that the prefix gets rendered.
|
| prefixOffset = std::min(
|
| - remainingWidth - prefixWidth, std::max(contentsOffset_,
|
| - maxMatchContentsWidth_));
|
| + remainingWidth - prefixWidth,
|
| + std::max([cellData contentsOffset], [tableView maxMatchContentsWidth]));
|
| offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth);
|
| } else { // The direction of contents is same as UI direction.
|
| // Ideally the offset should be |contentsOffset_|. If the max total width
|
| - // (|prefixWidth| + |maxMatchContentsWidth_|) from offset will exceed the
|
| + // (|prefixWidth| + |maxMatchContentsWidth|) from offset will exceed the
|
| // |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,
|
| - std::min(remainingWidth - maxMatchContentsWidth_, contentsOffset_));
|
| + offset =
|
| + std::max(prefixWidth,
|
| + std::min(remainingWidth - [tableView maxMatchContentsWidth],
|
| + [cellData contentsOffset]));
|
| prefixOffset = offset - prefixWidth;
|
| }
|
| *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth),
|
| *contentsMaxWidth);
|
| - [self drawMatchPart:prefix_
|
| + [self drawMatchPart:[cellData prefix]
|
| withFrame:cellFrame
|
| atOffset:prefixOffset + kTextStartOffset
|
| - withMaxWidth:prefixWidth
|
| - inView:controlView];
|
| + withMaxWidth:prefixWidth];
|
| return offset;
|
| }
|
|
|
| - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
|
| withFrame:(NSRect)cellFrame
|
| atOffset:(CGFloat)offset
|
| - withMaxWidth:(int)maxWidth
|
| - inView:(NSView*)controlView {
|
| + withMaxWidth:(int)maxWidth {
|
| if (offset > NSWidth(cellFrame))
|
| return 0.0f;
|
| NSRect renderRect = ShiftRect(cellFrame, offset);
|
| renderRect.size.width =
|
| std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth));
|
| - if (renderRect.size.width != 0) {
|
| - [self drawTitle:attributedString
|
| - withFrame:FlipIfRTL(renderRect, cellFrame)
|
| - inView:controlView];
|
| - }
|
| + if (NSWidth(renderRect) > 0.0)
|
| + [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));
|
| @@ -584,4 +588,10 @@ NSAttributedString* CreateClassifiedAttributedString(
|
| return base::i18n::IsRTL() ? (inputWidth - glyphOffset) : glyphOffset;
|
| }
|
|
|
| ++ (NSAttributedString*)createSeparatorString {
|
| + base::string16 raw_separator =
|
| + l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR);
|
| + return CreateAttributedString(raw_separator, DimTextColor());
|
| +}
|
| +
|
| @end
|
|
|