1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
7 #include "base/basictypes.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/skia/include/core/SkColor.h"
12 #include "ui/gfx/font_list.h"
13 #include "ui/views/border.h"
14 #include "ui/views/controls/link.h"
15 #include "ui/views/controls/styled_label.h"
16 #include "ui/views/controls/styled_label_listener.h"
18 using base::ASCIIToUTF16;
22 class StyledLabelTest : public testing::Test, public StyledLabelListener {
25 virtual ~StyledLabelTest() {}
27 // StyledLabelListener implementation.
28 virtual void StyledLabelLinkClicked(const gfx::Range& range,
29 int event_flags) OVERRIDE {}
32 StyledLabel* styled() { return styled_.get(); }
34 void InitStyledLabel(const std::string& ascii_text) {
35 styled_.reset(new StyledLabel(ASCIIToUTF16(ascii_text), this));
36 styled_->set_owned_by_client();
39 int StyledLabelContentHeightForWidth(int w) {
40 return styled_->GetHeightForWidth(w) - styled_->GetInsets().height();
44 scoped_ptr<StyledLabel> styled_;
46 DISALLOW_COPY_AND_ASSIGN(StyledLabelTest);
49 TEST_F(StyledLabelTest, NoWrapping) {
50 const std::string text("This is a test block of text");
51 InitStyledLabel(text);
52 Label label(ASCIIToUTF16(text));
53 const gfx::Size label_preferred_size = label.GetPreferredSize();
54 EXPECT_EQ(label_preferred_size.height(),
55 StyledLabelContentHeightForWidth(label_preferred_size.width() * 2));
58 TEST_F(StyledLabelTest, TrailingWhitespaceiIgnored) {
59 const std::string text("This is a test block of text ");
60 InitStyledLabel(text);
62 styled()->SetBounds(0, 0, 1000, 1000);
65 ASSERT_EQ(1, styled()->child_count());
66 ASSERT_EQ(std::string(Label::kViewClassName),
67 styled()->child_at(0)->GetClassName());
68 EXPECT_EQ(ASCIIToUTF16("This is a test block of text"),
69 static_cast<Label*>(styled()->child_at(0))->text());
72 TEST_F(StyledLabelTest, RespectLeadingWhitespace) {
73 const std::string text(" This is a test block of text");
74 InitStyledLabel(text);
76 styled()->SetBounds(0, 0, 1000, 1000);
79 ASSERT_EQ(1, styled()->child_count());
80 ASSERT_EQ(std::string(Label::kViewClassName),
81 styled()->child_at(0)->GetClassName());
82 EXPECT_EQ(ASCIIToUTF16(" This is a test block of text"),
83 static_cast<Label*>(styled()->child_at(0))->text());
86 TEST_F(StyledLabelTest, FirstLineNotEmptyWhenLeadingWhitespaceTooLong) {
87 const std::string text(" a");
88 InitStyledLabel(text);
90 Label label(ASCIIToUTF16(text));
91 gfx::Size label_preferred_size = label.GetPreferredSize();
93 styled()->SetBounds(0, 0, label_preferred_size.width() / 2, 1000);
96 ASSERT_EQ(1, styled()->child_count());
97 ASSERT_EQ(std::string(Label::kViewClassName),
98 styled()->child_at(0)->GetClassName());
99 EXPECT_EQ(ASCIIToUTF16("a"),
100 static_cast<Label*>(styled()->child_at(0))->text());
103 TEST_F(StyledLabelTest, BasicWrapping) {
104 const std::string text("This is a test block of text");
105 InitStyledLabel(text);
106 Label label(ASCIIToUTF16(text.substr(0, text.size() * 2 / 3)));
107 gfx::Size label_preferred_size = label.GetPreferredSize();
108 EXPECT_EQ(label_preferred_size.height() * 2,
109 StyledLabelContentHeightForWidth(label_preferred_size.width()));
111 // Also respect the border.
112 styled()->SetBorder(Border::CreateEmptyBorder(3, 3, 3, 3));
116 styled()->GetInsets().width() + label_preferred_size.width(),
117 styled()->GetInsets().height() + 2 * label_preferred_size.height());
119 ASSERT_EQ(2, styled()->child_count());
120 EXPECT_EQ(3, styled()->child_at(0)->x());
121 EXPECT_EQ(3, styled()->child_at(0)->y());
122 EXPECT_EQ(styled()->height() - 3, styled()->child_at(1)->bounds().bottom());
125 TEST_F(StyledLabelTest, CreateLinks) {
126 const std::string text("This is a test block of text.");
127 InitStyledLabel(text);
129 // Without links, there should be no focus border.
130 EXPECT_TRUE(styled()->GetInsets().empty());
132 // Now let's add some links.
133 styled()->AddStyleRange(gfx::Range(0, 1),
134 StyledLabel::RangeStyleInfo::CreateForLink());
135 styled()->AddStyleRange(gfx::Range(1, 2),
136 StyledLabel::RangeStyleInfo::CreateForLink());
137 styled()->AddStyleRange(gfx::Range(10, 11),
138 StyledLabel::RangeStyleInfo::CreateForLink());
139 styled()->AddStyleRange(gfx::Range(12, 13),
140 StyledLabel::RangeStyleInfo::CreateForLink());
142 // Now there should be a focus border because there are non-empty Links.
143 EXPECT_FALSE(styled()->GetInsets().empty());
145 // Verify layout creates the right number of children.
146 styled()->SetBounds(0, 0, 1000, 1000);
148 EXPECT_EQ(7, styled()->child_count());
151 TEST_F(StyledLabelTest, DontBreakLinks) {
152 const std::string text("This is a test block of text, ");
153 const std::string link_text("and this should be a link");
154 InitStyledLabel(text + link_text);
155 styled()->AddStyleRange(
156 gfx::Range(text.size(), text.size() + link_text.size()),
157 StyledLabel::RangeStyleInfo::CreateForLink());
159 Label label(ASCIIToUTF16(text + link_text.substr(0, link_text.size() / 2)));
160 gfx::Size label_preferred_size = label.GetPreferredSize();
161 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width());
162 EXPECT_EQ(label_preferred_size.height() * 2,
163 pref_height - styled()->GetInsets().height());
165 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height);
167 ASSERT_EQ(2, styled()->child_count());
168 // The label has no focus border while the link (and thus overall styled
169 // label) does, so the label should be inset by the width of the focus border.
170 EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(0)->x());
171 EXPECT_EQ(0, styled()->child_at(1)->x());
174 TEST_F(StyledLabelTest, StyledRangeWithDisabledLineWrapping) {
175 const std::string text("This is a test block of text, ");
176 const std::string unbreakable_text("and this should not be broken");
177 InitStyledLabel(text + unbreakable_text);
178 StyledLabel::RangeStyleInfo style_info;
179 style_info.disable_line_wrapping = true;
180 styled()->AddStyleRange(
181 gfx::Range(text.size(), text.size() + unbreakable_text.size()),
184 Label label(ASCIIToUTF16(
185 text + unbreakable_text.substr(0, unbreakable_text.size() / 2)));
186 gfx::Size label_preferred_size = label.GetPreferredSize();
187 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width());
188 EXPECT_EQ(label_preferred_size.height() * 2,
189 pref_height - styled()->GetInsets().height());
191 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height);
193 ASSERT_EQ(2, styled()->child_count());
194 EXPECT_EQ(0, styled()->child_at(0)->x());
195 EXPECT_EQ(0, styled()->child_at(1)->x());
198 TEST_F(StyledLabelTest, StyledRangeUnderlined) {
199 const std::string text("This is a test block of text, ");
200 const std::string underlined_text("and this should be undelined");
201 InitStyledLabel(text + underlined_text);
202 StyledLabel::RangeStyleInfo style_info;
203 style_info.font_style = gfx::Font::UNDERLINE;
204 styled()->AddStyleRange(
205 gfx::Range(text.size(), text.size() + underlined_text.size()),
208 styled()->SetBounds(0, 0, 1000, 1000);
211 ASSERT_EQ(2, styled()->child_count());
212 ASSERT_EQ(std::string(Label::kViewClassName),
213 styled()->child_at(1)->GetClassName());
215 gfx::Font::UNDERLINE,
216 static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle());
219 TEST_F(StyledLabelTest, StyledRangeBold) {
220 const std::string bold_text(
221 "This is a block of text whose style will be set to BOLD in the test");
222 const std::string text(" normal text");
223 InitStyledLabel(bold_text + text);
225 StyledLabel::RangeStyleInfo style_info;
226 style_info.font_style = gfx::Font::BOLD;
227 styled()->AddStyleRange(gfx::Range(0, bold_text.size()), style_info);
229 // Calculate the bold text width if it were a pure label view, both with bold
231 Label label(ASCIIToUTF16(bold_text));
232 const gfx::Size normal_label_size = label.GetPreferredSize();
233 label.SetFontList(label.font_list().DeriveFontListWithSizeDeltaAndStyle(
234 0, gfx::Font::BOLD));
235 const gfx::Size bold_label_size = label.GetPreferredSize();
237 ASSERT_GE(bold_label_size.width(), normal_label_size.width());
239 // Set the width so |bold_text| doesn't fit on a single line with bold style,
240 // but does with normal font style.
241 int styled_width = (normal_label_size.width() + bold_label_size.width()) / 2;
242 int pref_height = styled()->GetHeightForWidth(styled_width);
244 // Sanity check that |bold_text| with normal font style would fit on a single
245 // line in a styled label with width |styled_width|.
246 StyledLabel unstyled(ASCIIToUTF16(bold_text), this);
247 unstyled.SetBounds(0, 0, styled_width, pref_height);
249 EXPECT_EQ(1, unstyled.child_count());
251 styled()->SetBounds(0, 0, styled_width, pref_height);
254 ASSERT_EQ(3, styled()->child_count());
256 // The bold text should be broken up into two parts.
257 ASSERT_EQ(std::string(Label::kViewClassName),
258 styled()->child_at(0)->GetClassName());
261 static_cast<Label*>(styled()->child_at(0))->font_list().GetFontStyle());
262 ASSERT_EQ(std::string(Label::kViewClassName),
263 styled()->child_at(1)->GetClassName());
266 static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle());
267 ASSERT_EQ(std::string(Label::kViewClassName),
268 styled()->child_at(2)->GetClassName());
271 static_cast<Label*>(styled()->child_at(2))->font_list().GetFontStyle());
273 // The second bold part should start on a new line.
274 EXPECT_EQ(0, styled()->child_at(0)->x());
275 EXPECT_EQ(0, styled()->child_at(1)->x());
276 EXPECT_EQ(styled()->child_at(1)->bounds().right(),
277 styled()->child_at(2)->x());
280 TEST_F(StyledLabelTest, Color) {
281 const std::string text_red("RED");
282 const std::string text_link("link");
283 const std::string text("word");
284 InitStyledLabel(text_red + text_link + text);
286 StyledLabel::RangeStyleInfo style_info_red;
287 style_info_red.color = SK_ColorRED;
288 styled()->AddStyleRange(gfx::Range(0, text_red.size()), style_info_red);
290 StyledLabel::RangeStyleInfo style_info_link =
291 StyledLabel::RangeStyleInfo::CreateForLink();
292 styled()->AddStyleRange(gfx::Range(text_red.size(),
293 text_red.size() + text_link.size()),
296 // Obtain the default text color for a label.
297 Label label(ASCIIToUTF16(text));
298 const SkColor kDefaultTextColor = label.enabled_color();
300 // Obtain the default text color for a link;
301 Link link(ASCIIToUTF16(text_link));
302 const SkColor kDefaultLinkColor = link.enabled_color();
304 styled()->SetBounds(0, 0, 1000, 1000);
307 EXPECT_EQ(SK_ColorRED,
308 static_cast<Label*>(styled()->child_at(0))->enabled_color());
309 EXPECT_EQ(kDefaultLinkColor,
310 static_cast<Label*>(styled()->child_at(1))->enabled_color());
311 EXPECT_EQ(kDefaultTextColor,
312 static_cast<Label*>(styled()->child_at(2))->enabled_color());
315 TEST_F(StyledLabelTest, ColorReadability) {
316 const std::string text(
317 "This is a block of text that needs color adjustment.");
318 InitStyledLabel(text);
319 styled()->SetDisplayedOnBackgroundColor(SK_ColorBLACK);
321 // Obtain the text color if it were a pure label.
322 Label label(ASCIIToUTF16(text));
323 label.SetBackgroundColor(SK_ColorBLACK);
325 styled()->SetBounds(0, 0, 1000, 1000);
328 EXPECT_EQ(label.enabled_color(),
329 static_cast<Label*>(styled()->child_at(0))->enabled_color());
332 TEST_F(StyledLabelTest, StyledRangeWithTooltip) {
333 const std::string text("This is a test block of text, ");
334 const std::string tooltip_text("this should have a tooltip,");
335 const std::string normal_text(" this should not have a tooltip, ");
336 const std::string link_text("and this should be a link");
338 const size_t tooltip_start = text.size();
339 const size_t link_start =
340 text.size() + tooltip_text.size() + normal_text.size();
342 InitStyledLabel(text + tooltip_text + normal_text + link_text);
343 StyledLabel::RangeStyleInfo tooltip_style;
344 tooltip_style.tooltip = ASCIIToUTF16("tooltip");
345 styled()->AddStyleRange(
346 gfx::Range(tooltip_start, tooltip_start + tooltip_text.size()),
348 styled()->AddStyleRange(gfx::Range(link_start, link_start + link_text.size()),
349 StyledLabel::RangeStyleInfo::CreateForLink());
351 // Break line inside the range with the tooltip.
352 Label label(ASCIIToUTF16(
353 text + tooltip_text.substr(0, tooltip_text.size() - 3)));
354 gfx::Size label_preferred_size = label.GetPreferredSize();
355 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width());
356 EXPECT_EQ(label_preferred_size.height() * 3,
357 pref_height - styled()->GetInsets().height());
359 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height);
362 EXPECT_EQ(label_preferred_size.width(), styled()->width());
364 ASSERT_EQ(5, styled()->child_count());
365 // The labels have no focus border while the link (and thus overall styled
366 // label) does, so the labels should be inset by the width of the focus
368 EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(0)->x());
369 EXPECT_EQ(styled()->child_at(0)->bounds().right(),
370 styled()->child_at(1)->x());
371 EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(2)->x());
372 EXPECT_EQ(styled()->child_at(2)->bounds().right(),
373 styled()->child_at(3)->x());
374 EXPECT_EQ(0, styled()->child_at(4)->x());
376 base::string16 tooltip;
378 styled()->child_at(1)->GetTooltipText(gfx::Point(1, 1), &tooltip));
379 EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip);
381 styled()->child_at(2)->GetTooltipText(gfx::Point(1, 1), &tooltip));
382 EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip);
385 TEST_F(StyledLabelTest, HandleEmptyLayout) {
386 const std::string text("This is a test block of text.");
387 InitStyledLabel(text);
389 EXPECT_EQ(0, styled()->child_count());