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/views/controls/link.h" 13 #include "ui/views/controls/styled_label.h" 14 #include "ui/views/controls/styled_label_listener.h" 15 16 namespace views { 17 18 class StyledLabelTest : public testing::Test, public StyledLabelListener { 19 public: 20 StyledLabelTest() {} 21 virtual ~StyledLabelTest() {} 22 23 // StyledLabelListener implementation. 24 virtual void StyledLabelLinkClicked(const ui::Range& range, 25 int event_flags) OVERRIDE {} 26 27 protected: 28 StyledLabel* styled() { return styled_.get(); } 29 30 void InitStyledLabel(const std::string& ascii_text) { 31 styled_.reset(new StyledLabel(ASCIIToUTF16(ascii_text), this)); 32 } 33 34 int StyledLabelContentHeightForWidth(int w) { 35 return styled_->GetHeightForWidth(w) - styled_->GetInsets().height(); 36 } 37 38 private: 39 scoped_ptr<StyledLabel> styled_; 40 41 DISALLOW_COPY_AND_ASSIGN(StyledLabelTest); 42 }; 43 44 TEST_F(StyledLabelTest, NoWrapping) { 45 const std::string text("This is a test block of text"); 46 InitStyledLabel(text); 47 Label label(ASCIIToUTF16(text)); 48 const gfx::Size label_preferred_size = label.GetPreferredSize(); 49 EXPECT_EQ(label_preferred_size.height(), 50 StyledLabelContentHeightForWidth(label_preferred_size.width() * 2)); 51 } 52 53 TEST_F(StyledLabelTest, TrailingWhitespaceiIgnored) { 54 const std::string text("This is a test block of text "); 55 InitStyledLabel(text); 56 57 styled()->SetBounds(0, 0, 1000, 1000); 58 styled()->Layout(); 59 60 ASSERT_EQ(1, styled()->child_count()); 61 ASSERT_EQ(std::string(Label::kViewClassName), 62 styled()->child_at(0)->GetClassName()); 63 EXPECT_EQ(ASCIIToUTF16("This is a test block of text"), 64 static_cast<Label*>(styled()->child_at(0))->text()); 65 } 66 67 TEST_F(StyledLabelTest, RespectLeadingWhitespace) { 68 const std::string text(" This is a test block of text"); 69 InitStyledLabel(text); 70 71 styled()->SetBounds(0, 0, 1000, 1000); 72 styled()->Layout(); 73 74 ASSERT_EQ(1, styled()->child_count()); 75 ASSERT_EQ(std::string(Label::kViewClassName), 76 styled()->child_at(0)->GetClassName()); 77 EXPECT_EQ(ASCIIToUTF16(" This is a test block of text"), 78 static_cast<Label*>(styled()->child_at(0))->text()); 79 } 80 81 TEST_F(StyledLabelTest, FirstLineNotEmptyWhenLeadingWhitespaceTooLong) { 82 const std::string text(" a"); 83 InitStyledLabel(text); 84 85 Label label(ASCIIToUTF16(text)); 86 gfx::Size label_preferred_size = label.GetPreferredSize(); 87 88 styled()->SetBounds(0, 0, label_preferred_size.width() / 2, 1000); 89 styled()->Layout(); 90 91 ASSERT_EQ(1, styled()->child_count()); 92 ASSERT_EQ(std::string(Label::kViewClassName), 93 styled()->child_at(0)->GetClassName()); 94 EXPECT_EQ(ASCIIToUTF16("a"), 95 static_cast<Label*>(styled()->child_at(0))->text()); 96 } 97 98 TEST_F(StyledLabelTest, BasicWrapping) { 99 const std::string text("This is a test block of text"); 100 InitStyledLabel(text); 101 Label label(ASCIIToUTF16(text.substr(0, text.size() * 2 / 3))); 102 gfx::Size label_preferred_size = label.GetPreferredSize(); 103 EXPECT_EQ(label_preferred_size.height() * 2, 104 StyledLabelContentHeightForWidth(label_preferred_size.width())); 105 106 // Also respect the border. 107 styled()->set_border(Border::CreateEmptyBorder(3, 3, 3, 3)); 108 styled()->SetBounds( 109 0, 110 0, 111 styled()->GetInsets().width() + label_preferred_size.width(), 112 styled()->GetInsets().height() + 2 * label_preferred_size.height()); 113 styled()->Layout(); 114 ASSERT_EQ(2, styled()->child_count()); 115 EXPECT_EQ(3, styled()->child_at(0)->bounds().x()); 116 EXPECT_EQ(3, styled()->child_at(0)->bounds().y()); 117 EXPECT_EQ(styled()->bounds().height() - 3, 118 styled()->child_at(1)->bounds().bottom()); 119 } 120 121 TEST_F(StyledLabelTest, CreateLinks) { 122 const std::string text("This is a test block of text."); 123 InitStyledLabel(text); 124 styled()->AddStyleRange(ui::Range(0, 1), 125 StyledLabel::RangeStyleInfo::CreateForLink()); 126 styled()->AddStyleRange(ui::Range(1, 2), 127 StyledLabel::RangeStyleInfo::CreateForLink()); 128 styled()->AddStyleRange(ui::Range(10, 11), 129 StyledLabel::RangeStyleInfo::CreateForLink()); 130 styled()->AddStyleRange(ui::Range(12, 13), 131 StyledLabel::RangeStyleInfo::CreateForLink()); 132 133 styled()->SetBounds(0, 0, 1000, 1000); 134 styled()->Layout(); 135 ASSERT_EQ(7, styled()->child_count()); 136 } 137 138 TEST_F(StyledLabelTest, DontBreakLinks) { 139 const std::string text("This is a test block of text, "); 140 const std::string link_text("and this should be a link"); 141 InitStyledLabel(text + link_text); 142 styled()->AddStyleRange( 143 ui::Range(text.size(), text.size() + link_text.size()), 144 StyledLabel::RangeStyleInfo::CreateForLink()); 145 146 Label label(ASCIIToUTF16(text + link_text.substr(0, link_text.size() / 2))); 147 gfx::Size label_preferred_size = label.GetPreferredSize(); 148 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width()); 149 EXPECT_EQ(label_preferred_size.height() * 2, 150 pref_height - styled()->GetInsets().height()); 151 152 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); 153 styled()->Layout(); 154 ASSERT_EQ(2, styled()->child_count()); 155 EXPECT_EQ(0, styled()->child_at(0)->bounds().x()); 156 EXPECT_EQ(0, styled()->child_at(1)->bounds().x()); 157 } 158 159 TEST_F(StyledLabelTest, StyledRangeWithDisabledLineWrapping) { 160 const std::string text("This is a test block of text, "); 161 const std::string unbreakable_text("and this should not be breaked"); 162 InitStyledLabel(text + unbreakable_text); 163 StyledLabel::RangeStyleInfo style_info; 164 style_info.disable_line_wrapping = true; 165 styled()->AddStyleRange( 166 ui::Range(text.size(), text.size() + unbreakable_text.size()), 167 style_info); 168 169 Label label(ASCIIToUTF16( 170 text + unbreakable_text.substr(0, unbreakable_text.size() / 2))); 171 gfx::Size label_preferred_size = label.GetPreferredSize(); 172 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width()); 173 EXPECT_EQ(label_preferred_size.height() * 2, 174 pref_height - styled()->GetInsets().height()); 175 176 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); 177 styled()->Layout(); 178 ASSERT_EQ(2, styled()->child_count()); 179 EXPECT_EQ(0, styled()->child_at(0)->bounds().x()); 180 EXPECT_EQ(0, styled()->child_at(1)->bounds().x()); 181 } 182 183 TEST_F(StyledLabelTest, StyledRangeUnderlined) { 184 const std::string text("This is a test block of text, "); 185 const std::string underlined_text("and this should be undelined"); 186 InitStyledLabel(text + underlined_text); 187 StyledLabel::RangeStyleInfo style_info; 188 style_info.font_style = gfx::Font::UNDERLINE; 189 styled()->AddStyleRange( 190 ui::Range(text.size(), text.size() + underlined_text.size()), 191 style_info); 192 193 styled()->SetBounds(0, 0, 1000, 1000); 194 styled()->Layout(); 195 196 ASSERT_EQ(2, styled()->child_count()); 197 ASSERT_EQ(std::string(Label::kViewClassName), 198 styled()->child_at(1)->GetClassName()); 199 EXPECT_EQ(gfx::Font::UNDERLINE, 200 static_cast<Label*>(styled()->child_at(1))->font().GetStyle()); 201 } 202 203 TEST_F(StyledLabelTest, StyledRangeBold) { 204 const std::string bold_text( 205 "This is a block of text whose style will be set to BOLD in the test"); 206 const std::string text(" normal text"); 207 InitStyledLabel(bold_text + text); 208 209 StyledLabel::RangeStyleInfo style_info; 210 style_info.font_style = gfx::Font::BOLD; 211 styled()->AddStyleRange(ui::Range(0, bold_text.size()), style_info); 212 213 // Calculate the bold text width if it were a pure label view, both with bold 214 // and normal style. 215 Label label(ASCIIToUTF16(bold_text)); 216 const gfx::Size normal_label_size = label.GetPreferredSize(); 217 label.SetFont(label.font().DeriveFont(0, gfx::Font::BOLD)); 218 const gfx::Size bold_label_size = label.GetPreferredSize(); 219 220 ASSERT_GE(bold_label_size.width(), normal_label_size.width()); 221 222 // Set the width so |bold_text| doesn't fit on a single line with bold style, 223 // but does with normal font style. 224 int styled_width = (normal_label_size.width() + bold_label_size.width()) / 2; 225 int pref_height = styled()->GetHeightForWidth(styled_width); 226 227 // Sanity check that |bold_text| with normal font style would fit on a single 228 // line in a styled label with width |styled_width|. 229 StyledLabel unstyled(ASCIIToUTF16(bold_text), this); 230 unstyled.SetBounds(0, 0, styled_width, pref_height); 231 unstyled.Layout(); 232 ASSERT_EQ(1, unstyled.child_count()); 233 234 styled()->SetBounds(0, 0, styled_width, pref_height); 235 styled()->Layout(); 236 237 ASSERT_EQ(3, styled()->child_count()); 238 239 // The bold text should be broken up into two parts. 240 ASSERT_EQ(std::string(Label::kViewClassName), 241 styled()->child_at(0)->GetClassName()); 242 EXPECT_EQ(gfx::Font::BOLD, 243 static_cast<Label*>(styled()->child_at(0))->font().GetStyle()); 244 ASSERT_EQ(std::string(Label::kViewClassName), 245 styled()->child_at(1)->GetClassName()); 246 EXPECT_EQ(gfx::Font::BOLD, 247 static_cast<Label*>(styled()->child_at(1))->font().GetStyle()); 248 ASSERT_EQ(std::string(Label::kViewClassName), 249 styled()->child_at(2)->GetClassName()); 250 EXPECT_EQ(gfx::Font::NORMAL, 251 static_cast<Label*>(styled()->child_at(2))->font().GetStyle()); 252 253 // The second bold part should start on a new line. 254 EXPECT_EQ(0, styled()->child_at(0)->bounds().x()); 255 EXPECT_EQ(0, styled()->child_at(1)->bounds().x()); 256 EXPECT_EQ(styled()->child_at(1)->bounds().right() - 2, 257 styled()->child_at(2)->bounds().x()); 258 } 259 260 TEST_F(StyledLabelTest, Color) { 261 const std::string text_red("RED"); 262 const std::string text_link("link"); 263 const std::string text("word"); 264 InitStyledLabel(text_red + text_link + text); 265 266 StyledLabel::RangeStyleInfo style_info_red; 267 style_info_red.color = SK_ColorRED; 268 styled()->AddStyleRange(ui::Range(0, text_red.size()), style_info_red); 269 270 StyledLabel::RangeStyleInfo style_info_link = 271 StyledLabel::RangeStyleInfo::CreateForLink(); 272 styled()->AddStyleRange(ui::Range(text_red.size(), 273 text_red.size() + text_link.size()), 274 style_info_link); 275 276 // Obtain the default text color for a label. 277 Label label(ASCIIToUTF16(text)); 278 const SkColor kDefaultTextColor = label.enabled_color(); 279 280 // Obtain the default text color for a link; 281 Link link(ASCIIToUTF16(text_link)); 282 const SkColor kDefaultLinkColor = link.enabled_color(); 283 284 styled()->SetBounds(0, 0, 1000, 1000); 285 styled()->Layout(); 286 287 EXPECT_EQ(SK_ColorRED, 288 static_cast<Label*>(styled()->child_at(0))->enabled_color()); 289 EXPECT_EQ(kDefaultLinkColor, 290 static_cast<Label*>(styled()->child_at(1))->enabled_color()); 291 EXPECT_EQ(kDefaultTextColor, 292 static_cast<Label*>(styled()->child_at(2))->enabled_color()); 293 } 294 295 TEST_F(StyledLabelTest, ColorReadability) { 296 const std::string text( 297 "This is a block of text that needs color adjustment."); 298 InitStyledLabel(text); 299 styled()->SetDisplayedOnBackgroundColor(SK_ColorBLACK); 300 301 // Obtain the text color if it were a pure label. 302 Label label(ASCIIToUTF16(text)); 303 label.SetBackgroundColor(SK_ColorBLACK); 304 305 styled()->SetBounds(0, 0, 1000, 1000); 306 styled()->Layout(); 307 308 EXPECT_EQ(label.enabled_color(), 309 static_cast<Label*>(styled()->child_at(0))->enabled_color()); 310 } 311 312 TEST_F(StyledLabelTest, StyledRangeWithTooltip) { 313 const std::string text("This is a test block of text, "); 314 const std::string tooltip_text("this should have a tooltip,"); 315 const std::string normal_text(" this should not have a tooltip, "); 316 const std::string link_text("and this should be a link"); 317 318 const size_t tooltip_start = text.size(); 319 const size_t link_start = 320 text.size() + tooltip_text.size() + normal_text.size(); 321 322 InitStyledLabel(text + tooltip_text + normal_text + link_text); 323 StyledLabel::RangeStyleInfo tooltip_style; 324 tooltip_style.tooltip = ASCIIToUTF16("tooltip"); 325 styled()->AddStyleRange( 326 ui::Range(tooltip_start, tooltip_start + tooltip_text.size()), 327 tooltip_style); 328 styled()->AddStyleRange(ui::Range(link_start, link_start + link_text.size()), 329 StyledLabel::RangeStyleInfo::CreateForLink()); 330 331 // Break line inside the range with the tooltip. 332 Label label(ASCIIToUTF16( 333 text + tooltip_text.substr(0, tooltip_text.size() - 3))); 334 gfx::Size label_preferred_size = label.GetPreferredSize(); 335 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width()); 336 EXPECT_EQ(label_preferred_size.height() * 3, 337 pref_height - styled()->GetInsets().height()); 338 339 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); 340 styled()->Layout(); 341 342 EXPECT_EQ(label_preferred_size.width(), styled()->width()); 343 344 ASSERT_EQ(5, styled()->child_count()); 345 EXPECT_EQ(0, styled()->child_at(0)->bounds().x()); 346 EXPECT_EQ(styled()->child_at(0)->bounds().right() - 2, 347 styled()->child_at(1)->bounds().x()); 348 EXPECT_EQ(0, styled()->child_at(2)->bounds().x()); 349 EXPECT_EQ(styled()->child_at(2)->bounds().right() - 2, 350 styled()->child_at(3)->bounds().x()); 351 EXPECT_EQ(0, styled()->child_at(4)->bounds().x()); 352 353 string16 tooltip; 354 EXPECT_TRUE( 355 styled()->child_at(1)->GetTooltipText(gfx::Point(1, 1), &tooltip)); 356 EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip); 357 EXPECT_TRUE( 358 styled()->child_at(2)->GetTooltipText(gfx::Point(1, 1), &tooltip)); 359 EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip); 360 } 361 362 TEST_F(StyledLabelTest, HandleEmptyLayout) { 363 const std::string text("This is a test block of text, "); 364 InitStyledLabel(text); 365 styled()->Layout(); 366 ASSERT_EQ(0, styled()->child_count()); 367 } 368 369 } // namespace 370