styled_label_unittest.cc revision 5c02ac1a9c1b504631c0a3d2b6e737b5d738bae1
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.
4
5#include <string>
6
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"
19
20using base::ASCIIToUTF16;
21
22namespace views {
23
24class StyledLabelTest : public ViewsTestBase, public StyledLabelListener {
25 public:
26  StyledLabelTest() {}
27  virtual ~StyledLabelTest() {}
28
29  // StyledLabelListener implementation.
30  virtual void StyledLabelLinkClicked(const gfx::Range& range,
31                                      int event_flags) OVERRIDE {}
32
33 protected:
34  StyledLabel* styled() { return styled_.get(); }
35
36  void InitStyledLabel(const std::string& ascii_text) {
37    styled_.reset(new StyledLabel(ASCIIToUTF16(ascii_text), this));
38    styled_->set_owned_by_client();
39  }
40
41  int StyledLabelContentHeightForWidth(int w) {
42    return styled_->GetHeightForWidth(w) - styled_->GetInsets().height();
43  }
44
45 private:
46  scoped_ptr<StyledLabel> styled_;
47
48  DISALLOW_COPY_AND_ASSIGN(StyledLabelTest);
49};
50
51TEST_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));
58}
59
60TEST_F(StyledLabelTest, TrailingWhitespaceiIgnored) {
61  const std::string text("This is a test block of text   ");
62  InitStyledLabel(text);
63
64  styled()->SetBounds(0, 0, 1000, 1000);
65  styled()->Layout();
66
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());
72}
73
74TEST_F(StyledLabelTest, RespectLeadingWhitespace) {
75  const std::string text("   This is a test block of text");
76  InitStyledLabel(text);
77
78  styled()->SetBounds(0, 0, 1000, 1000);
79  styled()->Layout();
80
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());
86}
87
88TEST_F(StyledLabelTest, FirstLineNotEmptyWhenLeadingWhitespaceTooLong) {
89  const std::string text("                                     a");
90  InitStyledLabel(text);
91
92  Label label(ASCIIToUTF16(text));
93  gfx::Size label_preferred_size = label.GetPreferredSize();
94
95  styled()->SetBounds(0, 0, label_preferred_size.width() / 2, 1000);
96  styled()->Layout();
97
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());
103}
104
105TEST_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()));
112
113  // Also respect the border.
114  styled()->SetBorder(Border::CreateEmptyBorder(3, 3, 3, 3));
115  styled()->SetBounds(
116      0,
117      0,
118      styled()->GetInsets().width() + label_preferred_size.width(),
119      styled()->GetInsets().height() + 2 * label_preferred_size.height());
120  styled()->Layout();
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());
125}
126
127TEST_F(StyledLabelTest, CreateLinks) {
128  const std::string text("This is a test block of text.");
129  InitStyledLabel(text);
130
131  // Without links, there should be no focus border.
132  EXPECT_TRUE(styled()->GetInsets().empty());
133
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());
143
144  // Now there should be a focus border because there are non-empty Links.
145  EXPECT_FALSE(styled()->GetInsets().empty());
146
147  // Verify layout creates the right number of children.
148  styled()->SetBounds(0, 0, 1000, 1000);
149  styled()->Layout();
150  EXPECT_EQ(7, styled()->child_count());
151}
152
153TEST_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());
160
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());
166
167  styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height);
168  styled()->Layout();
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());
174}
175
176TEST_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()),
184      style_info);
185
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());
192
193  styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height);
194  styled()->Layout();
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());
198}
199
200TEST_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()),
208      style_info);
209
210  styled()->SetBounds(0, 0, 1000, 1000);
211  styled()->Layout();
212
213  ASSERT_EQ(2, styled()->child_count());
214  ASSERT_EQ(std::string(Label::kViewClassName),
215            styled()->child_at(1)->GetClassName());
216  EXPECT_EQ(
217      gfx::Font::UNDERLINE,
218      static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle());
219}
220
221TEST_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);
226
227  StyledLabel::RangeStyleInfo style_info;
228  style_info.font_style = gfx::Font::BOLD;
229  styled()->AddStyleRange(gfx::Range(0, bold_text.size()), style_info);
230
231  // Calculate the bold text width if it were a pure label view, both with bold
232  // and normal style.
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();
237
238  ASSERT_GE(bold_label_size.width(), normal_label_size.width());
239
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);
244
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);
249  unstyled.Layout();
250  EXPECT_EQ(1, unstyled.child_count());
251
252  styled()->SetBounds(0, 0, styled_width, pref_height);
253  styled()->Layout();
254
255  ASSERT_EQ(3, styled()->child_count());
256
257  // The bold text should be broken up into two parts.
258  ASSERT_EQ(std::string(Label::kViewClassName),
259            styled()->child_at(0)->GetClassName());
260  EXPECT_EQ(
261      gfx::Font::BOLD,
262      static_cast<Label*>(styled()->child_at(0))->font_list().GetFontStyle());
263  ASSERT_EQ(std::string(Label::kViewClassName),
264            styled()->child_at(1)->GetClassName());
265  EXPECT_EQ(
266      gfx::Font::BOLD,
267      static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle());
268  ASSERT_EQ(std::string(Label::kViewClassName),
269            styled()->child_at(2)->GetClassName());
270  EXPECT_EQ(
271      gfx::Font::NORMAL,
272      static_cast<Label*>(styled()->child_at(2))->font_list().GetFontStyle());
273
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());
279}
280
281TEST_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);
286
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);
290
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()),
295                          style_info_link);
296
297  styled()->SetBounds(0, 0, 1000, 1000);
298  styled()->Layout();
299
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());
306
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();
311
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();
316
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());
323
324  // Test adjusted color readability.
325  styled()->SetDisplayedOnBackgroundColor(SK_ColorBLACK);
326  styled()->Layout();
327  label->SetBackgroundColor(SK_ColorBLACK);
328
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());
333
334  widget->CloseNow();
335}
336
337TEST_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");
342
343  const size_t tooltip_start = text.size();
344  const size_t link_start =
345      text.size() + tooltip_text.size() + normal_text.size();
346
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()),
352      tooltip_style);
353  styled()->AddStyleRange(gfx::Range(link_start, link_start + link_text.size()),
354                          StyledLabel::RangeStyleInfo::CreateForLink());
355
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());
363
364  styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height);
365  styled()->Layout();
366
367  EXPECT_EQ(label_preferred_size.width(), styled()->width());
368
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
372  // border.
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());
380
381  base::string16 tooltip;
382  EXPECT_TRUE(
383      styled()->child_at(1)->GetTooltipText(gfx::Point(1, 1), &tooltip));
384  EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip);
385  EXPECT_TRUE(
386      styled()->child_at(2)->GetTooltipText(gfx::Point(1, 1), &tooltip));
387  EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip);
388}
389
390TEST_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));
397
398  styled()->SetBounds(0,
399                      0,
400                      label.GetPreferredSize().width(),
401                      label.GetPreferredSize().height());
402
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());
406}
407
408TEST_F(StyledLabelTest, HandleEmptyLayout) {
409  const std::string text("This is a test block of text.");
410  InitStyledLabel(text);
411  styled()->Layout();
412  EXPECT_EQ(0, styled()->child_count());
413}
414
415}  // namespace views
416