summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h2
-rw-r--r--chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm193
-rw-r--r--chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm39
-rw-r--r--chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h2
-rw-r--r--chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm16
-rw-r--r--components/omnibox/suggestion_answer.h18
6 files changed, 242 insertions, 28 deletions
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
index d2f3736..7e60da9 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h
@@ -46,6 +46,8 @@ class OmniboxPopupViewMac;
- (void)setMatch:(const AutocompleteMatch&)match;
+- (NSAttributedString*)description;
+
- (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth;
- (void)setContentsOffset:(CGFloat)contentsOffset;
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
index 5100d12..67d7c22 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm
@@ -18,6 +18,7 @@
#include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
#include "chrome/grit/generated_resources.h"
#include "components/omnibox/suggestion_answer.h"
+#include "skia/ext/skia_utils_mac.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/font.h"
@@ -66,6 +67,12 @@ NSColor* ContentTextColor() {
NSColor* DimTextColor() {
return [NSColor darkGrayColor];
}
+NSColor* PositiveTextColor() {
+ return gfx::SkColorToCalibratedNSColor(SkColorSetRGB(0x0b, 0x80, 0x43));
+}
+NSColor* NegativeTextColor() {
+ return gfx::SkColorToCalibratedNSColor(SkColorSetRGB(0xc5, 0x39, 0x29));
+}
NSColor* URLTextColor() {
return [NSColor colorWithCalibratedRed:0.0 green:0.55 blue:0.0 alpha:1.0];
}
@@ -76,11 +83,126 @@ NSFont* FieldFont() {
NSFont* BoldFieldFont() {
return OmniboxViewMac::GetFieldFont(gfx::Font::BOLD);
}
+NSFont* LargeFont() {
+ return OmniboxViewMac::GetLargeFont(gfx::Font::NORMAL);
+}
+NSFont* LargeSuperscriptFont() {
+ NSFont* font = OmniboxViewMac::GetLargeFont(gfx::Font::NORMAL);
+ // Calculate a slightly smaller font. The ratio here is somewhat arbitrary.
+ // Proportions from 5/9 to 5/7 all look pretty good.
+ CGFloat size = [font pointSize] * 5.0 / 9.0;
+ NSFontDescriptor* descriptor = [font fontDescriptor];
+ return [NSFont fontWithDescriptor:descriptor size:size];
+}
+NSFont* SmallFont() {
+ return OmniboxViewMac::GetSmallFont(gfx::Font::NORMAL);
+}
CGFloat GetContentAreaWidth(NSRect cellFrame) {
return NSWidth(cellFrame) - kTextStartOffset;
}
+NSAttributedString* CreateAnswerString(const base::string16& text,
+ NSInteger style_type) {
+ NSDictionary* answer_style = nil;
+ switch (style_type) {
+ case SuggestionAnswer::ANSWER:
+ answer_style = @{
+ NSForegroundColorAttributeName : ContentTextColor(),
+ NSFontAttributeName : LargeFont()
+ };
+ break;
+ case SuggestionAnswer::HEADLINE:
+ answer_style = @{
+ NSForegroundColorAttributeName : DimTextColor(),
+ NSFontAttributeName : LargeFont()
+ };
+ break;
+ case SuggestionAnswer::TOP_ALIGNED:
+ answer_style = @{
+ NSForegroundColorAttributeName : DimTextColor(),
+ NSFontAttributeName : LargeSuperscriptFont(),
+ NSSuperscriptAttributeName : @1
+ };
+ break;
+ case SuggestionAnswer::DESCRIPTION:
+ answer_style = @{
+ NSForegroundColorAttributeName : DimTextColor(),
+ NSFontAttributeName : FieldFont()
+ };
+ break;
+ case SuggestionAnswer::DESCRIPTION_NEGATIVE:
+ answer_style = @{
+ NSForegroundColorAttributeName : NegativeTextColor(),
+ NSFontAttributeName : LargeSuperscriptFont()
+ };
+ break;
+ case SuggestionAnswer::DESCRIPTION_POSITIVE:
+ answer_style = @{
+ NSForegroundColorAttributeName : PositiveTextColor(),
+ NSFontAttributeName : LargeSuperscriptFont()
+ };
+ break;
+ case SuggestionAnswer::MORE_INFO:
+ answer_style = @{
+ NSForegroundColorAttributeName : DimTextColor(),
+ NSFontAttributeName : SmallFont()
+ };
+ break;
+ case SuggestionAnswer::SUGGESTION:
+ answer_style = @{
+ NSForegroundColorAttributeName : ContentTextColor(),
+ NSFontAttributeName : FieldFont()
+ };
+ break;
+ case SuggestionAnswer::SUGGESTION_POSITIVE:
+ answer_style = @{
+ NSForegroundColorAttributeName : PositiveTextColor(),
+ NSFontAttributeName : FieldFont()
+ };
+ break;
+ case SuggestionAnswer::SUGGESTION_NEGATIVE:
+ answer_style = @{
+ NSForegroundColorAttributeName : NegativeTextColor(),
+ NSFontAttributeName : FieldFont()
+ };
+ break;
+ case SuggestionAnswer::SUGGESTION_LINK:
+ answer_style = @{
+ NSForegroundColorAttributeName : URLTextColor(),
+ NSFontAttributeName : FieldFont()
+ };
+ break;
+ case SuggestionAnswer::STATUS:
+ answer_style = @{
+ NSForegroundColorAttributeName : DimTextColor(),
+ NSFontAttributeName : LargeSuperscriptFont()
+ };
+ break;
+ case SuggestionAnswer::PERSONALIZED_SUGGESTION:
+ answer_style = @{
+ NSForegroundColorAttributeName : ContentTextColor(),
+ NSFontAttributeName : FieldFont()
+ };
+ break;
+ }
+
+ // Start out with a string using the default style info.
+ base::scoped_nsobject<NSMutableAttributedString> attributed_string(
+ [[NSMutableAttributedString alloc]
+ initWithString:base::SysUTF16ToNSString(text)
+ attributes:answer_style]);
+
+ base::scoped_nsobject<NSMutableParagraphStyle> style(
+ [[NSMutableParagraphStyle alloc] init]);
+ [style setLineBreakMode:NSLineBreakByTruncatingTail];
+ [style setTighteningFactorForTruncation:0.0];
+ [attributed_string addAttribute:NSParagraphStyleAttributeName
+ value:style
+ range:NSMakeRange(0, [attributed_string length])];
+ return attributed_string.autorelease();
+}
+
NSMutableAttributedString* CreateAttributedString(
const base::string16& text,
NSColor* text_color,
@@ -91,21 +213,20 @@ NSMutableAttributedString* CreateAttributedString(
NSFontAttributeName : FieldFont(),
NSForegroundColorAttributeName : text_color
};
- NSMutableAttributedString* as =
- [[[NSMutableAttributedString alloc] initWithString:s
- attributes:attributes]
- autorelease];
+ NSMutableAttributedString* attributedString = [[
+ [NSMutableAttributedString alloc] initWithString:s
+ attributes:attributes] autorelease];
NSMutableParagraphStyle* style =
[[[NSMutableParagraphStyle alloc] init] autorelease];
[style setLineBreakMode:NSLineBreakByTruncatingTail];
[style setTighteningFactorForTruncation:0.0];
[style setAlignment:textAlignment];
- [as addAttribute:NSParagraphStyleAttributeName
- value:style
- range:NSMakeRange(0, [as length])];
+ [attributedString addAttribute:NSParagraphStyleAttributeName
+ value:style
+ range:NSMakeRange(0, [attributedString length])];
- return as;
+ return attributedString;
}
NSMutableAttributedString* CreateAttributedString(
@@ -118,8 +239,9 @@ NSAttributedString* CreateClassifiedAttributedString(
const base::string16& text,
NSColor* text_color,
const ACMatchClassifications& classifications) {
- NSMutableAttributedString* as = CreateAttributedString(text, text_color);
- NSUInteger match_length = [as length];
+ NSMutableAttributedString* attributedString =
+ CreateAttributedString(text, text_color);
+ NSUInteger match_length = [attributedString length];
// Mark up the runs which differ from the default.
for (ACMatchClassifications::const_iterator i = classifications.begin();
@@ -136,21 +258,23 @@ NSAttributedString* CreateClassifiedAttributedString(
NSMakeRange(location, std::min(length, match_length - location));
if (0 != (i->style & ACMatchClassification::MATCH)) {
- [as addAttribute:NSFontAttributeName value:BoldFieldFont() range:range];
+ [attributedString addAttribute:NSFontAttributeName
+ value:BoldFieldFont()
+ range:range];
}
if (0 != (i->style & ACMatchClassification::URL)) {
- [as addAttribute:NSForegroundColorAttributeName
- value:URLTextColor()
- range:range];
+ [attributedString addAttribute:NSForegroundColorAttributeName
+ value:URLTextColor()
+ range:range];
} else if (0 != (i->style & ACMatchClassification::DIM)) {
- [as addAttribute:NSForegroundColorAttributeName
- value:DimTextColor()
- range:range];
+ [attributedString addAttribute:NSForegroundColorAttributeName
+ value:DimTextColor()
+ range:range];
}
}
- return as;
+ return attributedString;
}
} // namespace
@@ -184,23 +308,32 @@ NSAttributedString* CreateClassifiedAttributedString(
NSAttributedString *contents = CreateClassifiedAttributedString(
match_.contents, ContentTextColor(), match_.contents_class);
[self setAttributedTitle:contents];
-
[self setAnswerImage:nil];
if (match_.answer) {
- base::string16 answerString;
+ 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 += textField.text();
+ match_.answer->second_line().text_fields()) {
+ NSAttributedString* as =
+ CreateAnswerString(textField.text(), textField.type());
+ [answerString appendAttributedString:as];
+ }
const base::char16 space(' ');
const SuggestionAnswer::TextField* textField =
match_.answer->second_line().additional_text();
- if (textField)
- answerString += space + textField->text();
+ if (textField) {
+ [answerString
+ appendAttributedString:CreateAnswerString(space + textField->text(),
+ textField->type())];
+ }
textField = match_.answer->second_line().status_text();
- if (textField)
- answerString += space + textField->text();
- description_.reset([CreateClassifiedAttributedString(
- answerString, DimTextColor(), match_.description_class) retain]);
+ if (textField) {
+ [answerString
+ appendAttributedString:CreateAnswerString(space + textField->text(),
+ textField->type())];
+ }
+ description_.reset(answerString.release());
} else if (match_.description.empty()) {
description_.reset();
} else {
@@ -209,6 +342,10 @@ NSAttributedString* CreateClassifiedAttributedString(
}
}
+- (NSAttributedString*)description {
+ return description_;
+}
+
- (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth {
maxMatchContentsWidth_ = maxMatchContentsWidth;
}
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm
index e4bb64b..0367a84 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell_unittest.mm
@@ -4,8 +4,12 @@
#import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h"
+#include "base/json/json_reader.h"
#include "base/mac/scoped_nsobject.h"
+#include "base/values.h"
#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
+#include "components/omnibox/suggestion_answer.h"
+#import "testing/gtest_mac.h"
namespace {
@@ -44,4 +48,39 @@ TEST_F(OmniboxPopupCellTest, Title) {
[button_ display];
}
+TEST_F(OmniboxPopupCellTest, AnswerStyle) {
+ const char* weatherJson =
+ "{\"l\": [ {\"il\": {\"t\": [ {"
+ "\"t\": \"weather in pari&lt;b&gt;s&lt;/b&gt;\", \"tt\": 8} ]}}, {"
+ "\"il\": {\"at\": {\"t\": \"Thu\",\"tt\": 12}, "
+ "\"i\": {\"d\": \"//ssl.gstatic.com/onebox/weather/64/cloudy.png\","
+ "\"t\": 3}, \"t\": [ {\"t\": \"46\",\"tt\": 1}, {"
+ "\"t\": \"°F\",\"tt\": 3} ]}} ]}";
+ NSString* finalString = @"46°F Thu";
+
+ scoped_ptr<base::Value> root(base::JSONReader::Read(weatherJson));
+ ASSERT_NE(root, nullptr);
+ base::DictionaryValue* dictionary;
+ root->GetAsDictionary(&dictionary);
+ ASSERT_NE(dictionary, nullptr);
+ AutocompleteMatch match;
+ match.answer = SuggestionAnswer::ParseAnswer(dictionary);
+ EXPECT_TRUE(match.answer);
+ [cell_ setMatch:match];
+ EXPECT_NSEQ([[cell_ description] string], finalString);
+ size_t length = [[cell_ description] length];
+ const NSRange checkValues[] = {{0, 2}, {2, 2}, {4, 4}};
+ EXPECT_EQ(length, 8UL);
+ NSDictionary* lastAttributes = nil;
+ for (const NSRange& value : checkValues) {
+ NSRange range;
+ NSDictionary* currentAttributes =
+ [[cell_ description] attributesAtIndex:value.location
+ effectiveRange:&range];
+ EXPECT_TRUE(NSEqualRanges(value, range));
+ EXPECT_FALSE([currentAttributes isEqualToDictionary:lastAttributes]);
+ lastAttributes = currentAttributes;
+ }
+}
+
} // namespace
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h
index ec0d2c13..385b79ed 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h
@@ -102,6 +102,8 @@ class OmniboxViewMac : public OmniboxView,
// The style parameter specifies the new style for the font, and is a
// bitmask of the values: BOLD, ITALIC and UNDERLINE (see ui/gfx/font.h).
static NSFont* GetFieldFont(int style);
+ static NSFont* GetLargeFont(int style);
+ static NSFont* GetSmallFont(int style);
// If |resource_id| has a PDF image which can be used, return it.
// Otherwise return the PNG image from the resource bundle.
diff --git a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
index efa97fe..0e8f701 100644
--- a/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
+++ b/chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.mm
@@ -1021,6 +1021,22 @@ NSFont* OmniboxViewMac::GetFieldFont(int style) {
.GetPrimaryFont().GetNativeFont();
}
+NSFont* OmniboxViewMac::GetLargeFont(int style) {
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ return rb.GetFontList(ui::ResourceBundle::LargeFont)
+ .Derive(1, style)
+ .GetPrimaryFont()
+ .GetNativeFont();
+}
+
+NSFont* OmniboxViewMac::GetSmallFont(int style) {
+ ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
+ return rb.GetFontList(ui::ResourceBundle::SmallFont)
+ .Derive(1, style)
+ .GetPrimaryFont()
+ .GetNativeFont();
+}
+
int OmniboxViewMac::GetOmniboxTextLength() const {
return static_cast<int>(GetTextLength());
}
diff --git a/components/omnibox/suggestion_answer.h b/components/omnibox/suggestion_answer.h
index a2df1bc..98e3969 100644
--- a/components/omnibox/suggestion_answer.h
+++ b/components/omnibox/suggestion_answer.h
@@ -35,6 +35,24 @@ class SuggestionAnswer {
typedef std::vector<TextField> TextFields;
typedef std::vector<GURL> URLs;
+ // These values are named and numbered to match a specification at go/ais_api.
+ // The values are only used for answer results.
+ enum TextType {
+ ANSWER = 1,
+ HEADLINE = 2,
+ TOP_ALIGNED = 3,
+ DESCRIPTION = 4,
+ DESCRIPTION_NEGATIVE = 5,
+ DESCRIPTION_POSITIVE = 6,
+ MORE_INFO = 7,
+ SUGGESTION = 8,
+ SUGGESTION_POSITIVE = 9,
+ SUGGESTION_NEGATIVE = 10,
+ SUGGESTION_LINK = 11,
+ STATUS = 12,
+ PERSONALIZED_SUGGESTION = 13,
+ };
+
class TextField {
public:
TextField();