| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/views/controls/styled_label.h" | 5 #include "ui/views/controls/styled_label.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <memory> | 9 #include <memory> |
| 10 #include <string> | 10 #include <string> |
| 11 | 11 |
| 12 #include "base/macros.h" | 12 #include "base/macros.h" |
| 13 #include "base/strings/utf_string_conversions.h" | 13 #include "base/strings/utf_string_conversions.h" |
| 14 #include "testing/gtest/include/gtest/gtest.h" | 14 #include "testing/gtest/include/gtest/gtest.h" |
| 15 #include "third_party/skia/include/core/SkColor.h" | 15 #include "third_party/skia/include/core/SkColor.h" |
| 16 #include "ui/base/test/material_design_controller_test_api.h" |
| 16 #include "ui/gfx/font_list.h" | 17 #include "ui/gfx/font_list.h" |
| 17 #include "ui/views/border.h" | 18 #include "ui/views/border.h" |
| 18 #include "ui/views/controls/link.h" | 19 #include "ui/views/controls/link.h" |
| 19 #include "ui/views/controls/styled_label_listener.h" | 20 #include "ui/views/controls/styled_label_listener.h" |
| 20 #include "ui/views/test/views_test_base.h" | 21 #include "ui/views/test/views_test_base.h" |
| 21 #include "ui/views/widget/widget.h" | 22 #include "ui/views/widget/widget.h" |
| 22 | 23 |
| 23 using base::ASCIIToUTF16; | 24 using base::ASCIIToUTF16; |
| 24 | 25 |
| 25 namespace views { | 26 namespace views { |
| 27 namespace { |
| 28 |
| 29 enum class SecondaryUiMode { NON_MD, MD }; |
| 30 |
| 31 std::string SecondaryUiModeToString( |
| 32 const ::testing::TestParamInfo<SecondaryUiMode>& info) { |
| 33 return info.param == SecondaryUiMode::MD ? "MD" : "NonMD"; |
| 34 } |
| 35 } // namespace |
| 26 | 36 |
| 27 class StyledLabelTest : public ViewsTestBase, public StyledLabelListener { | 37 class StyledLabelTest : public ViewsTestBase, public StyledLabelListener { |
| 28 public: | 38 public: |
| 29 StyledLabelTest() {} | 39 StyledLabelTest() {} |
| 30 ~StyledLabelTest() override {} | 40 ~StyledLabelTest() override {} |
| 31 | 41 |
| 32 // StyledLabelListener implementation. | 42 // StyledLabelListener implementation. |
| 33 void StyledLabelLinkClicked(StyledLabel* label, | 43 void StyledLabelLinkClicked(StyledLabel* label, |
| 34 const gfx::Range& range, | 44 const gfx::Range& range, |
| 35 int event_flags) override {} | 45 int event_flags) override {} |
| 36 | 46 |
| 37 protected: | 47 protected: |
| 38 StyledLabel* styled() { return styled_.get(); } | 48 StyledLabel* styled() { return styled_.get(); } |
| 39 | 49 |
| 40 void InitStyledLabel(const std::string& ascii_text) { | 50 void InitStyledLabel(const std::string& ascii_text) { |
| 41 styled_.reset(new StyledLabel(ASCIIToUTF16(ascii_text), this)); | 51 styled_.reset(new StyledLabel(ASCIIToUTF16(ascii_text), this)); |
| 42 styled_->set_owned_by_client(); | 52 styled_->set_owned_by_client(); |
| 43 } | 53 } |
| 44 | 54 |
| 45 int StyledLabelContentHeightForWidth(int w) { | 55 int StyledLabelContentHeightForWidth(int w) { |
| 46 return styled_->GetHeightForWidth(w) - styled_->GetInsets().height(); | 56 return styled_->GetHeightForWidth(w) - styled_->GetInsets().height(); |
| 47 } | 57 } |
| 48 | 58 |
| 49 private: | 59 private: |
| 50 std::unique_ptr<StyledLabel> styled_; | 60 std::unique_ptr<StyledLabel> styled_; |
| 51 | 61 |
| 52 DISALLOW_COPY_AND_ASSIGN(StyledLabelTest); | 62 DISALLOW_COPY_AND_ASSIGN(StyledLabelTest); |
| 53 }; | 63 }; |
| 54 | 64 |
| 65 // StyledLabelTest harness that runs both with and without secondary UI set to |
| 66 // MD. |
| 67 class MDStyledLabelTest |
| 68 : public StyledLabelTest, |
| 69 public ::testing::WithParamInterface<SecondaryUiMode> { |
| 70 public: |
| 71 MDStyledLabelTest() |
| 72 : md_test_api_(ui::MaterialDesignController::Mode::MATERIAL_NORMAL) {} |
| 73 |
| 74 // StyledLabelTest: |
| 75 void SetUp() override { |
| 76 // This works while StyledLabelTest has no SetUp() of its own. Otherwise the |
| 77 // mode should be set after ViewsTestBase::SetUp(), but before the rest of |
| 78 // StyledLabelTest::SetUp(), so that StyledLabelTest::SetUp() obeys the MD |
| 79 // setting. |
| 80 StyledLabelTest::SetUp(); |
| 81 md_test_api_.SetSecondaryUiMaterial(GetParam() == SecondaryUiMode::MD); |
| 82 } |
| 83 |
| 84 private: |
| 85 ui::test::MaterialDesignControllerTestAPI md_test_api_; |
| 86 |
| 87 DISALLOW_COPY_AND_ASSIGN(MDStyledLabelTest); |
| 88 }; |
| 89 |
| 55 TEST_F(StyledLabelTest, NoWrapping) { | 90 TEST_F(StyledLabelTest, NoWrapping) { |
| 56 const std::string text("This is a test block of text"); | 91 const std::string text("This is a test block of text"); |
| 57 InitStyledLabel(text); | 92 InitStyledLabel(text); |
| 58 Label label(ASCIIToUTF16(text)); | 93 Label label(ASCIIToUTF16(text)); |
| 59 const gfx::Size label_preferred_size = label.GetPreferredSize(); | 94 const gfx::Size label_preferred_size = label.GetPreferredSize(); |
| 60 EXPECT_EQ(label_preferred_size.height(), | 95 EXPECT_EQ(label_preferred_size.height(), |
| 61 StyledLabelContentHeightForWidth(label_preferred_size.width() * 2)); | 96 StyledLabelContentHeightForWidth(label_preferred_size.width() * 2)); |
| 62 } | 97 } |
| 63 | 98 |
| 64 TEST_F(StyledLabelTest, TrailingWhitespaceiIgnored) { | 99 TEST_F(StyledLabelTest, TrailingWhitespaceiIgnored) { |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 197 EXPECT_EQ(gfx::Point(0, styled()->height() / 2), | 232 EXPECT_EQ(gfx::Point(0, styled()->height() / 2), |
| 198 styled()->child_at(1)->origin()); | 233 styled()->child_at(1)->origin()); |
| 199 | 234 |
| 200 EXPECT_FALSE(static_cast<Label*>(styled()->child_at(0))->text().empty()); | 235 EXPECT_FALSE(static_cast<Label*>(styled()->child_at(0))->text().empty()); |
| 201 EXPECT_FALSE(static_cast<Label*>(styled()->child_at(1))->text().empty()); | 236 EXPECT_FALSE(static_cast<Label*>(styled()->child_at(1))->text().empty()); |
| 202 EXPECT_EQ(ASCIIToUTF16(text), | 237 EXPECT_EQ(ASCIIToUTF16(text), |
| 203 static_cast<Label*>(styled()->child_at(0))->text() + | 238 static_cast<Label*>(styled()->child_at(0))->text() + |
| 204 static_cast<Label*>(styled()->child_at(1))->text()); | 239 static_cast<Label*>(styled()->child_at(1))->text()); |
| 205 } | 240 } |
| 206 | 241 |
| 207 TEST_F(StyledLabelTest, CreateLinks) { | 242 TEST_P(MDStyledLabelTest, CreateLinks) { |
| 208 const std::string text("This is a test block of text."); | 243 const std::string text("This is a test block of text."); |
| 209 InitStyledLabel(text); | 244 InitStyledLabel(text); |
| 210 | 245 |
| 211 // Without links, there should be no focus border. | 246 // Without links, there should be no focus border. |
| 212 EXPECT_TRUE(styled()->GetInsets().IsEmpty()); | 247 EXPECT_TRUE(styled()->GetInsets().IsEmpty()); |
| 213 | 248 |
| 214 // Now let's add some links. | 249 // Now let's add some links. |
| 215 styled()->AddStyleRange(gfx::Range(0, 1), | 250 styled()->AddStyleRange(gfx::Range(0, 1), |
| 216 StyledLabel::RangeStyleInfo::CreateForLink()); | 251 StyledLabel::RangeStyleInfo::CreateForLink()); |
| 217 styled()->AddStyleRange(gfx::Range(1, 2), | 252 styled()->AddStyleRange(gfx::Range(1, 2), |
| 218 StyledLabel::RangeStyleInfo::CreateForLink()); | 253 StyledLabel::RangeStyleInfo::CreateForLink()); |
| 219 styled()->AddStyleRange(gfx::Range(10, 11), | 254 styled()->AddStyleRange(gfx::Range(10, 11), |
| 220 StyledLabel::RangeStyleInfo::CreateForLink()); | 255 StyledLabel::RangeStyleInfo::CreateForLink()); |
| 221 styled()->AddStyleRange(gfx::Range(12, 13), | 256 styled()->AddStyleRange(gfx::Range(12, 13), |
| 222 StyledLabel::RangeStyleInfo::CreateForLink()); | 257 StyledLabel::RangeStyleInfo::CreateForLink()); |
| 223 | 258 |
| 224 // Now there should be a focus border because there are non-empty Links. | 259 if (GetParam() == SecondaryUiMode::MD) { |
| 225 EXPECT_FALSE(styled()->GetInsets().IsEmpty()); | 260 // Insets shouldn't change under MD when links are added, since the links |
| 261 // indicate focus by adding an underline instead. |
| 262 EXPECT_TRUE(styled()->GetInsets().IsEmpty()); |
| 263 } else { |
| 264 // Now there should be a focus border because there are non-empty Links. |
| 265 EXPECT_FALSE(styled()->GetInsets().IsEmpty()); |
| 266 } |
| 226 | 267 |
| 227 // Verify layout creates the right number of children. | 268 // Verify layout creates the right number of children. |
| 228 styled()->SetBounds(0, 0, 1000, 1000); | 269 styled()->SetBounds(0, 0, 1000, 1000); |
| 229 styled()->Layout(); | 270 styled()->Layout(); |
| 230 EXPECT_EQ(7, styled()->child_count()); | 271 EXPECT_EQ(7, styled()->child_count()); |
| 231 } | 272 } |
| 232 | 273 |
| 233 TEST_F(StyledLabelTest, DontBreakLinks) { | 274 TEST_P(MDStyledLabelTest, DontBreakLinks) { |
| 234 const std::string text("This is a test block of text, "); | 275 const std::string text("This is a test block of text, "); |
| 235 const std::string link_text("and this should be a link"); | 276 const std::string link_text("and this should be a link"); |
| 236 InitStyledLabel(text + link_text); | 277 InitStyledLabel(text + link_text); |
| 237 styled()->AddStyleRange( | 278 styled()->AddStyleRange( |
| 238 gfx::Range(static_cast<uint32_t>(text.size()), | 279 gfx::Range(static_cast<uint32_t>(text.size()), |
| 239 static_cast<uint32_t>(text.size() + link_text.size())), | 280 static_cast<uint32_t>(text.size() + link_text.size())), |
| 240 StyledLabel::RangeStyleInfo::CreateForLink()); | 281 StyledLabel::RangeStyleInfo::CreateForLink()); |
| 241 | 282 |
| 242 Label label(ASCIIToUTF16(text + link_text.substr(0, link_text.size() / 2))); | 283 Label label(ASCIIToUTF16(text + link_text.substr(0, link_text.size() / 2))); |
| 243 gfx::Size label_preferred_size = label.GetPreferredSize(); | 284 gfx::Size label_preferred_size = label.GetPreferredSize(); |
| 244 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width()); | 285 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width()); |
| 245 EXPECT_EQ(label_preferred_size.height() * 2, | 286 EXPECT_EQ(label_preferred_size.height() * 2, |
| 246 pref_height - styled()->GetInsets().height()); | 287 pref_height - styled()->GetInsets().height()); |
| 247 | 288 |
| 248 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); | 289 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); |
| 249 styled()->Layout(); | 290 styled()->Layout(); |
| 250 ASSERT_EQ(2, styled()->child_count()); | 291 ASSERT_EQ(2, styled()->child_count()); |
| 251 // The label has no focus border while the link (and thus overall styled | 292 |
| 252 // label) does, so the label should be inset by the width of the focus border. | 293 if (GetParam() == SecondaryUiMode::MD) { |
| 253 EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(0)->x()); | 294 // No additional insets should be added under MD. |
| 295 EXPECT_EQ(0, styled()->child_at(0)->x()); |
| 296 } else { |
| 297 // The label has no focus border while, when non-MD, the link (and thus |
| 298 // overall styled label) does, so the label should be inset by the width of |
| 299 // the focus border. |
| 300 EXPECT_EQ(Link::kFocusBorderPadding, styled()->child_at(0)->x()); |
| 301 } |
| 302 // The Link shouldn't be offset (it grows in size under non-MD instead). |
| 254 EXPECT_EQ(0, styled()->child_at(1)->x()); | 303 EXPECT_EQ(0, styled()->child_at(1)->x()); |
| 255 } | 304 } |
| 256 | 305 |
| 257 TEST_F(StyledLabelTest, StyledRangeWithDisabledLineWrapping) { | 306 TEST_F(StyledLabelTest, StyledRangeWithDisabledLineWrapping) { |
| 258 const std::string text("This is a test block of text, "); | 307 const std::string text("This is a test block of text, "); |
| 259 const std::string unbreakable_text("and this should not be broken"); | 308 const std::string unbreakable_text("and this should not be broken"); |
| 260 InitStyledLabel(text + unbreakable_text); | 309 InitStyledLabel(text + unbreakable_text); |
| 261 StyledLabel::RangeStyleInfo style_info; | 310 StyledLabel::RangeStyleInfo style_info; |
| 262 style_info.disable_line_wrapping = true; | 311 style_info.disable_line_wrapping = true; |
| 263 styled()->AddStyleRange( | 312 styled()->AddStyleRange( |
| (...skipping 156 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 420 label->SetBackgroundColor(SK_ColorBLACK); | 469 label->SetBackgroundColor(SK_ColorBLACK); |
| 421 | 470 |
| 422 const SkColor kAdjustedTextColor = label->enabled_color(); | 471 const SkColor kAdjustedTextColor = label->enabled_color(); |
| 423 EXPECT_NE(kAdjustedTextColor, kDefaultTextColor); | 472 EXPECT_NE(kAdjustedTextColor, kDefaultTextColor); |
| 424 EXPECT_EQ(kAdjustedTextColor, | 473 EXPECT_EQ(kAdjustedTextColor, |
| 425 static_cast<Label*>(styled()->child_at(2))->enabled_color()); | 474 static_cast<Label*>(styled()->child_at(2))->enabled_color()); |
| 426 | 475 |
| 427 widget->CloseNow(); | 476 widget->CloseNow(); |
| 428 } | 477 } |
| 429 | 478 |
| 430 TEST_F(StyledLabelTest, StyledRangeWithTooltip) { | 479 TEST_P(MDStyledLabelTest, StyledRangeWithTooltip) { |
| 431 const std::string text("This is a test block of text, "); | 480 const std::string text("This is a test block of text, "); |
| 432 const std::string tooltip_text("this should have a tooltip,"); | 481 const std::string tooltip_text("this should have a tooltip,"); |
| 433 const std::string normal_text(" this should not have a tooltip, "); | 482 const std::string normal_text(" this should not have a tooltip, "); |
| 434 const std::string link_text("and this should be a link"); | 483 const std::string link_text("and this should be a link"); |
| 435 | 484 |
| 436 const size_t tooltip_start = text.size(); | 485 const size_t tooltip_start = text.size(); |
| 437 const size_t link_start = | 486 const size_t link_start = |
| 438 text.size() + tooltip_text.size() + normal_text.size(); | 487 text.size() + tooltip_text.size() + normal_text.size(); |
| 439 | 488 |
| 440 InitStyledLabel(text + tooltip_text + normal_text + link_text); | 489 InitStyledLabel(text + tooltip_text + normal_text + link_text); |
| (...skipping 15 matching lines...) Expand all Loading... |
| 456 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width()); | 505 int pref_height = styled()->GetHeightForWidth(label_preferred_size.width()); |
| 457 EXPECT_EQ(label_preferred_size.height() * 3, | 506 EXPECT_EQ(label_preferred_size.height() * 3, |
| 458 pref_height - styled()->GetInsets().height()); | 507 pref_height - styled()->GetInsets().height()); |
| 459 | 508 |
| 460 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); | 509 styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); |
| 461 styled()->Layout(); | 510 styled()->Layout(); |
| 462 | 511 |
| 463 EXPECT_EQ(label_preferred_size.width(), styled()->width()); | 512 EXPECT_EQ(label_preferred_size.width(), styled()->width()); |
| 464 | 513 |
| 465 ASSERT_EQ(5, styled()->child_count()); | 514 ASSERT_EQ(5, styled()->child_count()); |
| 466 // The labels have no focus border while the link (and thus overall styled | 515 |
| 467 // label) does, so the labels should be inset by the width of the focus | 516 if (GetParam() == SecondaryUiMode::MD) { |
| 468 // border. | 517 // In MD, the labels shouldn't be offset to cater for focus rings. |
| 469 EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(0)->x()); | 518 EXPECT_EQ(0, styled()->child_at(0)->x()); |
| 519 EXPECT_EQ(0, styled()->child_at(2)->x()); |
| 520 } else { |
| 521 // The labels have no focus border while the link (and thus overall styled |
| 522 // label) does, so the labels should be inset by the width of the focus |
| 523 // border. |
| 524 EXPECT_EQ(Link::kFocusBorderPadding, styled()->child_at(0)->x()); |
| 525 EXPECT_EQ(Link::kFocusBorderPadding, styled()->child_at(2)->x()); |
| 526 } |
| 527 |
| 470 EXPECT_EQ(styled()->child_at(0)->bounds().right(), | 528 EXPECT_EQ(styled()->child_at(0)->bounds().right(), |
| 471 styled()->child_at(1)->x()); | 529 styled()->child_at(1)->x()); |
| 472 EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(2)->x()); | |
| 473 EXPECT_EQ(styled()->child_at(2)->bounds().right(), | 530 EXPECT_EQ(styled()->child_at(2)->bounds().right(), |
| 474 styled()->child_at(3)->x()); | 531 styled()->child_at(3)->x()); |
| 475 EXPECT_EQ(0, styled()->child_at(4)->x()); | 532 EXPECT_EQ(0, styled()->child_at(4)->x()); |
| 476 | 533 |
| 477 base::string16 tooltip; | 534 base::string16 tooltip; |
| 478 EXPECT_TRUE( | 535 EXPECT_TRUE( |
| 479 styled()->child_at(1)->GetTooltipText(gfx::Point(1, 1), &tooltip)); | 536 styled()->child_at(1)->GetTooltipText(gfx::Point(1, 1), &tooltip)); |
| 480 EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip); | 537 EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip); |
| 481 EXPECT_TRUE( | 538 EXPECT_TRUE( |
| 482 styled()->child_at(2)->GetTooltipText(gfx::Point(1, 1), &tooltip)); | 539 styled()->child_at(2)->GetTooltipText(gfx::Point(1, 1), &tooltip)); |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 572 styled()->SetBounds(0, 0, 1000, 0); | 629 styled()->SetBounds(0, 0, 1000, 0); |
| 573 styled()->Layout(); | 630 styled()->Layout(); |
| 574 EXPECT_EQ( | 631 EXPECT_EQ( |
| 575 label_preferred_size.height() + 5 /*top border*/ + 6 /*bottom border*/, | 632 label_preferred_size.height() + 5 /*top border*/ + 6 /*bottom border*/, |
| 576 styled()->GetPreferredSize().height()); | 633 styled()->GetPreferredSize().height()); |
| 577 EXPECT_EQ( | 634 EXPECT_EQ( |
| 578 label_preferred_size.width() + 10 /*left border*/ + 20 /*right border*/, | 635 label_preferred_size.width() + 10 /*left border*/ + 20 /*right border*/, |
| 579 styled()->GetPreferredSize().width()); | 636 styled()->GetPreferredSize().width()); |
| 580 } | 637 } |
| 581 | 638 |
| 639 INSTANTIATE_TEST_CASE_P(, |
| 640 MDStyledLabelTest, |
| 641 ::testing::Values(SecondaryUiMode::MD, |
| 642 SecondaryUiMode::NON_MD), |
| 643 &SecondaryUiModeToString); |
| 644 |
| 582 } // namespace views | 645 } // namespace views |
| OLD | NEW |