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"
17 #include "ui/views/test/views_test_base.h"
18 #include "ui/views/widget/widget.h"
20 using base::ASCIIToUTF16
;
24 class StyledLabelTest
: public ViewsTestBase
, public StyledLabelListener
{
27 ~StyledLabelTest() override
{}
29 // StyledLabelListener implementation.
30 void StyledLabelLinkClicked(const gfx::Range
& range
,
31 int event_flags
) override
{}
34 StyledLabel
* styled() { return styled_
.get(); }
36 void InitStyledLabel(const std::string
& ascii_text
) {
37 styled_
.reset(new StyledLabel(ASCIIToUTF16(ascii_text
), this));
38 styled_
->set_owned_by_client();
41 int StyledLabelContentHeightForWidth(int w
) {
42 return styled_
->GetHeightForWidth(w
) - styled_
->GetInsets().height();
46 scoped_ptr
<StyledLabel
> styled_
;
48 DISALLOW_COPY_AND_ASSIGN(StyledLabelTest
);
51 TEST_F(StyledLabelTest
, NoWrapping
) {
52 const std::string
text("This is a test block of text");
53 InitStyledLabel(text
);
54 Label
label(ASCIIToUTF16(text
));
55 const gfx::Size label_preferred_size
= label
.GetPreferredSize();
56 EXPECT_EQ(label_preferred_size
.height(),
57 StyledLabelContentHeightForWidth(label_preferred_size
.width() * 2));
60 TEST_F(StyledLabelTest
, TrailingWhitespaceiIgnored
) {
61 const std::string
text("This is a test block of text ");
62 InitStyledLabel(text
);
64 styled()->SetBounds(0, 0, 1000, 1000);
67 ASSERT_EQ(1, styled()->child_count());
68 ASSERT_EQ(std::string(Label::kViewClassName
),
69 styled()->child_at(0)->GetClassName());
70 EXPECT_EQ(ASCIIToUTF16("This is a test block of text"),
71 static_cast<Label
*>(styled()->child_at(0))->text());
74 TEST_F(StyledLabelTest
, RespectLeadingWhitespace
) {
75 const std::string
text(" This is a test block of text");
76 InitStyledLabel(text
);
78 styled()->SetBounds(0, 0, 1000, 1000);
81 ASSERT_EQ(1, styled()->child_count());
82 ASSERT_EQ(std::string(Label::kViewClassName
),
83 styled()->child_at(0)->GetClassName());
84 EXPECT_EQ(ASCIIToUTF16(" This is a test block of text"),
85 static_cast<Label
*>(styled()->child_at(0))->text());
88 TEST_F(StyledLabelTest
, FirstLineNotEmptyWhenLeadingWhitespaceTooLong
) {
89 const std::string
text(" a");
90 InitStyledLabel(text
);
92 Label
label(ASCIIToUTF16(text
));
93 gfx::Size label_preferred_size
= label
.GetPreferredSize();
95 styled()->SetBounds(0, 0, label_preferred_size
.width() / 2, 1000);
98 ASSERT_EQ(1, styled()->child_count());
99 ASSERT_EQ(std::string(Label::kViewClassName
),
100 styled()->child_at(0)->GetClassName());
101 EXPECT_EQ(ASCIIToUTF16("a"),
102 static_cast<Label
*>(styled()->child_at(0))->text());
105 TEST_F(StyledLabelTest
, BasicWrapping
) {
106 const std::string
text("This is a test block of text");
107 InitStyledLabel(text
);
108 Label
label(ASCIIToUTF16(text
.substr(0, text
.size() * 2 / 3)));
109 gfx::Size label_preferred_size
= label
.GetPreferredSize();
110 EXPECT_EQ(label_preferred_size
.height() * 2,
111 StyledLabelContentHeightForWidth(label_preferred_size
.width()));
113 // Also respect the border.
114 styled()->SetBorder(Border::CreateEmptyBorder(3, 3, 3, 3));
118 styled()->GetInsets().width() + label_preferred_size
.width(),
119 styled()->GetInsets().height() + 2 * label_preferred_size
.height());
121 ASSERT_EQ(2, styled()->child_count());
122 EXPECT_EQ(3, styled()->child_at(0)->x());
123 EXPECT_EQ(3, styled()->child_at(0)->y());
124 EXPECT_EQ(styled()->height() - 3, styled()->child_at(1)->bounds().bottom());
127 TEST_F(StyledLabelTest
, CreateLinks
) {
128 const std::string
text("This is a test block of text.");
129 InitStyledLabel(text
);
131 // Without links, there should be no focus border.
132 EXPECT_TRUE(styled()->GetInsets().empty());
134 // Now let's add some links.
135 styled()->AddStyleRange(gfx::Range(0, 1),
136 StyledLabel::RangeStyleInfo::CreateForLink());
137 styled()->AddStyleRange(gfx::Range(1, 2),
138 StyledLabel::RangeStyleInfo::CreateForLink());
139 styled()->AddStyleRange(gfx::Range(10, 11),
140 StyledLabel::RangeStyleInfo::CreateForLink());
141 styled()->AddStyleRange(gfx::Range(12, 13),
142 StyledLabel::RangeStyleInfo::CreateForLink());
144 // Now there should be a focus border because there are non-empty Links.
145 EXPECT_FALSE(styled()->GetInsets().empty());
147 // Verify layout creates the right number of children.
148 styled()->SetBounds(0, 0, 1000, 1000);
150 EXPECT_EQ(7, styled()->child_count());
153 TEST_F(StyledLabelTest
, DontBreakLinks
) {
154 const std::string
text("This is a test block of text, ");
155 const std::string
link_text("and this should be a link");
156 InitStyledLabel(text
+ link_text
);
157 styled()->AddStyleRange(
158 gfx::Range(text
.size(), text
.size() + link_text
.size()),
159 StyledLabel::RangeStyleInfo::CreateForLink());
161 Label
label(ASCIIToUTF16(text
+ link_text
.substr(0, link_text
.size() / 2)));
162 gfx::Size label_preferred_size
= label
.GetPreferredSize();
163 int pref_height
= styled()->GetHeightForWidth(label_preferred_size
.width());
164 EXPECT_EQ(label_preferred_size
.height() * 2,
165 pref_height
- styled()->GetInsets().height());
167 styled()->SetBounds(0, 0, label_preferred_size
.width(), pref_height
);
169 ASSERT_EQ(2, styled()->child_count());
170 // The label has no focus border while the link (and thus overall styled
171 // label) does, so the label should be inset by the width of the focus border.
172 EXPECT_EQ(Label::kFocusBorderPadding
, styled()->child_at(0)->x());
173 EXPECT_EQ(0, styled()->child_at(1)->x());
176 TEST_F(StyledLabelTest
, StyledRangeWithDisabledLineWrapping
) {
177 const std::string
text("This is a test block of text, ");
178 const std::string
unbreakable_text("and this should not be broken");
179 InitStyledLabel(text
+ unbreakable_text
);
180 StyledLabel::RangeStyleInfo style_info
;
181 style_info
.disable_line_wrapping
= true;
182 styled()->AddStyleRange(
183 gfx::Range(text
.size(), text
.size() + unbreakable_text
.size()),
186 Label
label(ASCIIToUTF16(
187 text
+ unbreakable_text
.substr(0, unbreakable_text
.size() / 2)));
188 gfx::Size label_preferred_size
= label
.GetPreferredSize();
189 int pref_height
= styled()->GetHeightForWidth(label_preferred_size
.width());
190 EXPECT_EQ(label_preferred_size
.height() * 2,
191 pref_height
- styled()->GetInsets().height());
193 styled()->SetBounds(0, 0, label_preferred_size
.width(), pref_height
);
195 ASSERT_EQ(2, styled()->child_count());
196 EXPECT_EQ(0, styled()->child_at(0)->x());
197 EXPECT_EQ(0, styled()->child_at(1)->x());
200 TEST_F(StyledLabelTest
, StyledRangeUnderlined
) {
201 const std::string
text("This is a test block of text, ");
202 const std::string
underlined_text("and this should be undelined");
203 InitStyledLabel(text
+ underlined_text
);
204 StyledLabel::RangeStyleInfo style_info
;
205 style_info
.font_style
= gfx::Font::UNDERLINE
;
206 styled()->AddStyleRange(
207 gfx::Range(text
.size(), text
.size() + underlined_text
.size()),
210 styled()->SetBounds(0, 0, 1000, 1000);
213 ASSERT_EQ(2, styled()->child_count());
214 ASSERT_EQ(std::string(Label::kViewClassName
),
215 styled()->child_at(1)->GetClassName());
217 gfx::Font::UNDERLINE
,
218 static_cast<Label
*>(styled()->child_at(1))->font_list().GetFontStyle());
221 TEST_F(StyledLabelTest
, StyledRangeBold
) {
222 const std::string
bold_text(
223 "This is a block of text whose style will be set to BOLD in the test");
224 const std::string
text(" normal text");
225 InitStyledLabel(bold_text
+ text
);
227 StyledLabel::RangeStyleInfo style_info
;
228 style_info
.font_style
= gfx::Font::BOLD
;
229 styled()->AddStyleRange(gfx::Range(0, bold_text
.size()), style_info
);
231 // Calculate the bold text width if it were a pure label view, both with bold
233 Label
label(ASCIIToUTF16(bold_text
));
234 const gfx::Size normal_label_size
= label
.GetPreferredSize();
235 label
.SetFontList(label
.font_list().DeriveWithStyle(gfx::Font::BOLD
));
236 const gfx::Size bold_label_size
= label
.GetPreferredSize();
238 ASSERT_GE(bold_label_size
.width(), normal_label_size
.width());
240 // Set the width so |bold_text| doesn't fit on a single line with bold style,
241 // but does with normal font style.
242 int styled_width
= (normal_label_size
.width() + bold_label_size
.width()) / 2;
243 int pref_height
= styled()->GetHeightForWidth(styled_width
);
245 // Sanity check that |bold_text| with normal font style would fit on a single
246 // line in a styled label with width |styled_width|.
247 StyledLabel
unstyled(ASCIIToUTF16(bold_text
), this);
248 unstyled
.SetBounds(0, 0, styled_width
, pref_height
);
250 EXPECT_EQ(1, unstyled
.child_count());
252 styled()->SetBounds(0, 0, styled_width
, pref_height
);
255 ASSERT_EQ(3, styled()->child_count());
257 // The bold text should be broken up into two parts.
258 ASSERT_EQ(std::string(Label::kViewClassName
),
259 styled()->child_at(0)->GetClassName());
262 static_cast<Label
*>(styled()->child_at(0))->font_list().GetFontStyle());
263 ASSERT_EQ(std::string(Label::kViewClassName
),
264 styled()->child_at(1)->GetClassName());
267 static_cast<Label
*>(styled()->child_at(1))->font_list().GetFontStyle());
268 ASSERT_EQ(std::string(Label::kViewClassName
),
269 styled()->child_at(2)->GetClassName());
272 static_cast<Label
*>(styled()->child_at(2))->font_list().GetFontStyle());
274 // The second bold part should start on a new line.
275 EXPECT_EQ(0, styled()->child_at(0)->x());
276 EXPECT_EQ(0, styled()->child_at(1)->x());
277 EXPECT_EQ(styled()->child_at(1)->bounds().right(),
278 styled()->child_at(2)->x());
281 TEST_F(StyledLabelTest
, Color
) {
282 const std::string
text_red("RED");
283 const std::string
text_link("link");
284 const std::string
text("word");
285 InitStyledLabel(text_red
+ text_link
+ text
);
287 StyledLabel::RangeStyleInfo style_info_red
;
288 style_info_red
.color
= SK_ColorRED
;
289 styled()->AddStyleRange(gfx::Range(0, text_red
.size()), style_info_red
);
291 StyledLabel::RangeStyleInfo style_info_link
=
292 StyledLabel::RangeStyleInfo::CreateForLink();
293 styled()->AddStyleRange(gfx::Range(text_red
.size(),
294 text_red
.size() + text_link
.size()),
297 styled()->SetBounds(0, 0, 1000, 1000);
300 Widget
* widget
= new Widget();
301 Widget::InitParams params
= CreateParams(Widget::InitParams::TYPE_POPUP
);
302 widget
->Init(params
);
303 View
* container
= new View();
304 widget
->SetContentsView(container
);
305 container
->AddChildView(styled());
307 // Obtain the default text color for a label.
308 Label
* label
= new Label(ASCIIToUTF16(text
));
309 container
->AddChildView(label
);
310 const SkColor kDefaultTextColor
= label
->enabled_color();
312 // Obtain the default text color for a link;
313 Link
* link
= new Link(ASCIIToUTF16(text_link
));
314 container
->AddChildView(link
);
315 const SkColor kDefaultLinkColor
= link
->enabled_color();
317 EXPECT_EQ(SK_ColorRED
,
318 static_cast<Label
*>(styled()->child_at(0))->enabled_color());
319 EXPECT_EQ(kDefaultLinkColor
,
320 static_cast<Label
*>(styled()->child_at(1))->enabled_color());
321 EXPECT_EQ(kDefaultTextColor
,
322 static_cast<Label
*>(styled()->child_at(2))->enabled_color());
324 // Test adjusted color readability.
325 styled()->SetDisplayedOnBackgroundColor(SK_ColorBLACK
);
327 label
->SetBackgroundColor(SK_ColorBLACK
);
329 const SkColor kAdjustedTextColor
= label
->enabled_color();
330 EXPECT_NE(kAdjustedTextColor
, kDefaultTextColor
);
331 EXPECT_EQ(kAdjustedTextColor
,
332 static_cast<Label
*>(styled()->child_at(2))->enabled_color());
337 TEST_F(StyledLabelTest
, StyledRangeWithTooltip
) {
338 const std::string
text("This is a test block of text, ");
339 const std::string
tooltip_text("this should have a tooltip,");
340 const std::string
normal_text(" this should not have a tooltip, ");
341 const std::string
link_text("and this should be a link");
343 const size_t tooltip_start
= text
.size();
344 const size_t link_start
=
345 text
.size() + tooltip_text
.size() + normal_text
.size();
347 InitStyledLabel(text
+ tooltip_text
+ normal_text
+ link_text
);
348 StyledLabel::RangeStyleInfo tooltip_style
;
349 tooltip_style
.tooltip
= ASCIIToUTF16("tooltip");
350 styled()->AddStyleRange(
351 gfx::Range(tooltip_start
, tooltip_start
+ tooltip_text
.size()),
353 styled()->AddStyleRange(gfx::Range(link_start
, link_start
+ link_text
.size()),
354 StyledLabel::RangeStyleInfo::CreateForLink());
356 // Break line inside the range with the tooltip.
357 Label
label(ASCIIToUTF16(
358 text
+ tooltip_text
.substr(0, tooltip_text
.size() - 3)));
359 gfx::Size label_preferred_size
= label
.GetPreferredSize();
360 int pref_height
= styled()->GetHeightForWidth(label_preferred_size
.width());
361 EXPECT_EQ(label_preferred_size
.height() * 3,
362 pref_height
- styled()->GetInsets().height());
364 styled()->SetBounds(0, 0, label_preferred_size
.width(), pref_height
);
367 EXPECT_EQ(label_preferred_size
.width(), styled()->width());
369 ASSERT_EQ(5, styled()->child_count());
370 // The labels have no focus border while the link (and thus overall styled
371 // label) does, so the labels should be inset by the width of the focus
373 EXPECT_EQ(Label::kFocusBorderPadding
, styled()->child_at(0)->x());
374 EXPECT_EQ(styled()->child_at(0)->bounds().right(),
375 styled()->child_at(1)->x());
376 EXPECT_EQ(Label::kFocusBorderPadding
, styled()->child_at(2)->x());
377 EXPECT_EQ(styled()->child_at(2)->bounds().right(),
378 styled()->child_at(3)->x());
379 EXPECT_EQ(0, styled()->child_at(4)->x());
381 base::string16 tooltip
;
383 styled()->child_at(1)->GetTooltipText(gfx::Point(1, 1), &tooltip
));
384 EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip
);
386 styled()->child_at(2)->GetTooltipText(gfx::Point(1, 1), &tooltip
));
387 EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip
);
390 TEST_F(StyledLabelTest
, SetBaseFontList
) {
391 const std::string
text("This is a test block of text.");
392 InitStyledLabel(text
);
393 std::string
font_name("arial");
394 gfx::Font
font(font_name
, 30);
395 styled()->SetBaseFontList(gfx::FontList(font
));
396 Label
label(ASCIIToUTF16(text
), gfx::FontList(font
));
398 styled()->SetBounds(0,
400 label
.GetPreferredSize().width(),
401 label
.GetPreferredSize().height());
403 // Make sure we have the same sizing as a label.
404 EXPECT_EQ(label
.GetPreferredSize().height(), styled()->height());
405 EXPECT_EQ(label
.GetPreferredSize().width(), styled()->width());
408 TEST_F(StyledLabelTest
, LineHeight
) {
409 const std::string
text("one");
410 InitStyledLabel(text
);
411 int default_height
= styled()->GetHeightForWidth(100);
412 const std::string
newline_text("one\ntwo\nthree");
413 InitStyledLabel(newline_text
);
414 styled()->SetLineHeight(18);
415 EXPECT_EQ(18 * 2 + default_height
, styled()->GetHeightForWidth(100));
418 TEST_F(StyledLabelTest
, HandleEmptyLayout
) {
419 const std::string
text("This is a test block of text.");
420 InitStyledLabel(text
);
422 EXPECT_EQ(0, styled()->child_count());
425 TEST_F(StyledLabelTest
, CacheSize
) {
426 const int preferred_height
= 50;
427 const int preferred_width
= 100;
428 const std::string
text("This is a test block of text.");
429 const base::string16
another_text(base::ASCIIToUTF16(
430 "This is a test block of text. This text is much longer than previous"));
432 InitStyledLabel(text
);
434 // we should be able to calculate height without any problem
435 // no controls should be created
436 int precalculated_height
= styled()->GetHeightForWidth(preferred_width
);
437 EXPECT_LT(0, precalculated_height
);
438 EXPECT_EQ(0, styled()->child_count());
440 styled()->SetBounds(0, 0, preferred_width
, preferred_height
);
443 // controls should be created after layout
444 // height should be the same as precalculated
445 int real_height
= styled()->GetHeightForWidth(styled()->width());
446 View
* first_child_after_layout
= styled()->has_children() ?
447 styled()->child_at(0) : nullptr;
448 EXPECT_LT(0, styled()->child_count());
449 EXPECT_LT(0, real_height
);
450 EXPECT_EQ(real_height
, precalculated_height
);
452 // another call to Layout should not kill and recreate all controls
454 View
* first_child_after_second_layout
= styled()->has_children() ?
455 styled()->child_at(0) : nullptr;
456 EXPECT_EQ(first_child_after_layout
, first_child_after_second_layout
);
458 // if text is changed:
459 // layout should be recalculated
460 // all controls should be recreated
461 styled()->SetText(another_text
);
462 int updated_height
= styled()->GetHeightForWidth(styled()->width());
463 EXPECT_NE(updated_height
, real_height
);
464 View
* first_child_after_text_update
= styled()->has_children() ?
465 styled()->child_at(0) : nullptr;
466 EXPECT_NE(first_child_after_text_update
, first_child_after_layout
);