// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include <string> #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "base/strings/utf_string_conversions.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/skia/include/core/SkColor.h" #include "ui/gfx/font_list.h" #include "ui/views/border.h" #include "ui/views/controls/link.h" #include "ui/views/controls/styled_label.h" #include "ui/views/controls/styled_label_listener.h" #include "ui/views/test/views_test_base.h" #include "ui/views/widget/widget.h" using base::ASCIIToUTF16; namespace views { class StyledLabelTest : public ViewsTestBase, public StyledLabelListener { public: StyledLabelTest() {} virtual ~StyledLabelTest() {} // StyledLabelListener implementation. virtual void StyledLabelLinkClicked(const gfx::Range& range, int event_flags) OVERRIDE {} protected: StyledLabel* styled() { return styled_.get(); } void InitStyledLabel(const std::string& ascii_text) { styled_.reset(new StyledLabel(ASCIIToUTF16(ascii_text), this)); styled_->set_owned_by_client(); } int StyledLabelContentHeightForWidth(int w) { return styled_->GetHeightForWidth(w) - styled_->GetInsets().height(); } private: scoped_ptr<StyledLabel> styled_; DISALLOW_COPY_AND_ASSIGN(StyledLabelTest); }; TEST_F(StyledLabelTest, NoWrapping) { const std::string text("This is a test block of text"); InitStyledLabel(text); Label label(ASCIIToUTF16(text)); const gfx::Size label_preferred_size = label.GetPreferredSize(); EXPECT_EQ(label_preferred_size.height(), StyledLabelContentHeightForWidth(label_preferred_size.width() * 2)); } TEST_F(StyledLabelTest, TrailingWhitespaceiIgnored) { const std::string text("This is a test block of text "); InitStyledLabel(text); styled()->SetBounds(0, 0, 1000, 1000); styled()->Layout(); ASSERT_EQ(1, styled()->child_count()); ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(0)->GetClassName()); EXPECT_EQ(ASCIIToUTF16("This is a test block of text"), static_cast<Label*>(styled()->child_at(0))->text()); } TEST_F(StyledLabelTest, RespectLeadingWhitespace) { const std::string text(" This is a test block of text"); InitStyledLabel(text); styled()->SetBounds(0, 0, 1000, 1000); styled()->Layout(); ASSERT_EQ(1, styled()->child_count()); ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(0)->GetClassName()); EXPECT_EQ(ASCIIToUTF16(" This is a test block of text"), static_cast<Label*>(styled()->child_at(0))->text()); } TEST_F(StyledLabelTest, FirstLineNotEmptyWhenLeadingWhitespaceTooLong) { const std::string text(" a"); InitStyledLabel(text); Label label(ASCIIToUTF16(text)); gfx::Size label_preferred_size = label.GetPreferredSize(); styled()->SetBounds(0, 0, label_preferred_size.width() / 2, 1000); styled()->Layout(); ASSERT_EQ(1, styled()->child_count()); ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(0)->GetClassName()); EXPECT_EQ(ASCIIToUTF16("a"), static_cast<Label*>(styled()->child_at(0))->text()); } TEST_F(StyledLabelTest, BasicWrapping) { const std::string text("This is a test block of text"); InitStyledLabel(text); Label label(ASCIIToUTF16(text.substr(0, text.size() * 2 / 3))); gfx::Size label_preferred_size = label.GetPreferredSize(); EXPECT_EQ(label_preferred_size.height() * 2, StyledLabelContentHeightForWidth(label_preferred_size.width())); // Also respect the border. styled()->SetBorder(Border::CreateEmptyBorder(3, 3, 3, 3)); styled()->SetBounds( 0, 0, styled()->GetInsets().width() + label_preferred_size.width(), styled()->GetInsets().height() + 2 * label_preferred_size.height()); styled()->Layout(); ASSERT_EQ(2, styled()->child_count()); EXPECT_EQ(3, styled()->child_at(0)->x()); EXPECT_EQ(3, styled()->child_at(0)->y()); EXPECT_EQ(styled()->height() - 3, styled()->child_at(1)->bounds().bottom()); } TEST_F(StyledLabelTest, CreateLinks) { const std::string text("This is a test block of text."); InitStyledLabel(text); // Without links, there should be no focus border. EXPECT_TRUE(styled()->GetInsets().empty()); // Now let's add some links. styled()->AddStyleRange(gfx::Range(0, 1), StyledLabel::RangeStyleInfo::CreateForLink()); styled()->AddStyleRange(gfx::Range(1, 2), StyledLabel::RangeStyleInfo::CreateForLink()); styled()->AddStyleRange(gfx::Range(10, 11), StyledLabel::RangeStyleInfo::CreateForLink()); styled()->AddStyleRange(gfx::Range(12, 13), StyledLabel::RangeStyleInfo::CreateForLink()); // Now there should be a focus border because there are non-empty Links. EXPECT_FALSE(styled()->GetInsets().empty()); // Verify layout creates the right number of children. styled()->SetBounds(0, 0, 1000, 1000); styled()->Layout(); EXPECT_EQ(7, styled()->child_count()); } TEST_F(StyledLabelTest, DontBreakLinks) { const std::string text("This is a test block of text, "); const std::string link_text("and this should be a link"); InitStyledLabel(text + link_text); styled()->AddStyleRange( gfx::Range(text.size(), text.size() + link_text.size()), StyledLabel::RangeStyleInfo::CreateForLink()); Label label(ASCIIToUTF16(text + link_text.substr(0, link_text.size() / 2))); gfx::Size label_preferred_size = label.GetPreferredSize(); int pref_height = styled()->GetHeightForWidth(label_preferred_size.width()); EXPECT_EQ(label_preferred_size.height() * 2, pref_height - styled()->GetInsets().height()); styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); styled()->Layout(); ASSERT_EQ(2, styled()->child_count()); // The label has no focus border while the link (and thus overall styled // label) does, so the label should be inset by the width of the focus border. EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(0)->x()); EXPECT_EQ(0, styled()->child_at(1)->x()); } TEST_F(StyledLabelTest, StyledRangeWithDisabledLineWrapping) { const std::string text("This is a test block of text, "); const std::string unbreakable_text("and this should not be broken"); InitStyledLabel(text + unbreakable_text); StyledLabel::RangeStyleInfo style_info; style_info.disable_line_wrapping = true; styled()->AddStyleRange( gfx::Range(text.size(), text.size() + unbreakable_text.size()), style_info); Label label(ASCIIToUTF16( text + unbreakable_text.substr(0, unbreakable_text.size() / 2))); gfx::Size label_preferred_size = label.GetPreferredSize(); int pref_height = styled()->GetHeightForWidth(label_preferred_size.width()); EXPECT_EQ(label_preferred_size.height() * 2, pref_height - styled()->GetInsets().height()); styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); styled()->Layout(); ASSERT_EQ(2, styled()->child_count()); EXPECT_EQ(0, styled()->child_at(0)->x()); EXPECT_EQ(0, styled()->child_at(1)->x()); } TEST_F(StyledLabelTest, StyledRangeUnderlined) { const std::string text("This is a test block of text, "); const std::string underlined_text("and this should be undelined"); InitStyledLabel(text + underlined_text); StyledLabel::RangeStyleInfo style_info; style_info.font_style = gfx::Font::UNDERLINE; styled()->AddStyleRange( gfx::Range(text.size(), text.size() + underlined_text.size()), style_info); styled()->SetBounds(0, 0, 1000, 1000); styled()->Layout(); ASSERT_EQ(2, styled()->child_count()); ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(1)->GetClassName()); EXPECT_EQ( gfx::Font::UNDERLINE, static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle()); } TEST_F(StyledLabelTest, StyledRangeBold) { const std::string bold_text( "This is a block of text whose style will be set to BOLD in the test"); const std::string text(" normal text"); InitStyledLabel(bold_text + text); StyledLabel::RangeStyleInfo style_info; style_info.font_style = gfx::Font::BOLD; styled()->AddStyleRange(gfx::Range(0, bold_text.size()), style_info); // Calculate the bold text width if it were a pure label view, both with bold // and normal style. Label label(ASCIIToUTF16(bold_text)); const gfx::Size normal_label_size = label.GetPreferredSize(); label.SetFontList(label.font_list().DeriveWithStyle(gfx::Font::BOLD)); const gfx::Size bold_label_size = label.GetPreferredSize(); ASSERT_GE(bold_label_size.width(), normal_label_size.width()); // Set the width so |bold_text| doesn't fit on a single line with bold style, // but does with normal font style. int styled_width = (normal_label_size.width() + bold_label_size.width()) / 2; int pref_height = styled()->GetHeightForWidth(styled_width); // Sanity check that |bold_text| with normal font style would fit on a single // line in a styled label with width |styled_width|. StyledLabel unstyled(ASCIIToUTF16(bold_text), this); unstyled.SetBounds(0, 0, styled_width, pref_height); unstyled.Layout(); EXPECT_EQ(1, unstyled.child_count()); styled()->SetBounds(0, 0, styled_width, pref_height); styled()->Layout(); ASSERT_EQ(3, styled()->child_count()); // The bold text should be broken up into two parts. ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(0)->GetClassName()); EXPECT_EQ( gfx::Font::BOLD, static_cast<Label*>(styled()->child_at(0))->font_list().GetFontStyle()); ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(1)->GetClassName()); EXPECT_EQ( gfx::Font::BOLD, static_cast<Label*>(styled()->child_at(1))->font_list().GetFontStyle()); ASSERT_EQ(std::string(Label::kViewClassName), styled()->child_at(2)->GetClassName()); EXPECT_EQ( gfx::Font::NORMAL, static_cast<Label*>(styled()->child_at(2))->font_list().GetFontStyle()); // The second bold part should start on a new line. EXPECT_EQ(0, styled()->child_at(0)->x()); EXPECT_EQ(0, styled()->child_at(1)->x()); EXPECT_EQ(styled()->child_at(1)->bounds().right(), styled()->child_at(2)->x()); } TEST_F(StyledLabelTest, Color) { const std::string text_red("RED"); const std::string text_link("link"); const std::string text("word"); InitStyledLabel(text_red + text_link + text); StyledLabel::RangeStyleInfo style_info_red; style_info_red.color = SK_ColorRED; styled()->AddStyleRange(gfx::Range(0, text_red.size()), style_info_red); StyledLabel::RangeStyleInfo style_info_link = StyledLabel::RangeStyleInfo::CreateForLink(); styled()->AddStyleRange(gfx::Range(text_red.size(), text_red.size() + text_link.size()), style_info_link); styled()->SetBounds(0, 0, 1000, 1000); styled()->Layout(); Widget* widget = new Widget(); Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_POPUP); widget->Init(params); View* container = new View(); widget->SetContentsView(container); container->AddChildView(styled()); // Obtain the default text color for a label. Label* label = new Label(ASCIIToUTF16(text)); container->AddChildView(label); const SkColor kDefaultTextColor = label->enabled_color(); // Obtain the default text color for a link; Link* link = new Link(ASCIIToUTF16(text_link)); container->AddChildView(link); const SkColor kDefaultLinkColor = link->enabled_color(); EXPECT_EQ(SK_ColorRED, static_cast<Label*>(styled()->child_at(0))->enabled_color()); EXPECT_EQ(kDefaultLinkColor, static_cast<Label*>(styled()->child_at(1))->enabled_color()); EXPECT_EQ(kDefaultTextColor, static_cast<Label*>(styled()->child_at(2))->enabled_color()); // Test adjusted color readability. styled()->SetDisplayedOnBackgroundColor(SK_ColorBLACK); styled()->Layout(); label->SetBackgroundColor(SK_ColorBLACK); const SkColor kAdjustedTextColor = label->enabled_color(); EXPECT_NE(kAdjustedTextColor, kDefaultTextColor); EXPECT_EQ(kAdjustedTextColor, static_cast<Label*>(styled()->child_at(2))->enabled_color()); widget->CloseNow(); } TEST_F(StyledLabelTest, StyledRangeWithTooltip) { const std::string text("This is a test block of text, "); const std::string tooltip_text("this should have a tooltip,"); const std::string normal_text(" this should not have a tooltip, "); const std::string link_text("and this should be a link"); const size_t tooltip_start = text.size(); const size_t link_start = text.size() + tooltip_text.size() + normal_text.size(); InitStyledLabel(text + tooltip_text + normal_text + link_text); StyledLabel::RangeStyleInfo tooltip_style; tooltip_style.tooltip = ASCIIToUTF16("tooltip"); styled()->AddStyleRange( gfx::Range(tooltip_start, tooltip_start + tooltip_text.size()), tooltip_style); styled()->AddStyleRange(gfx::Range(link_start, link_start + link_text.size()), StyledLabel::RangeStyleInfo::CreateForLink()); // Break line inside the range with the tooltip. Label label(ASCIIToUTF16( text + tooltip_text.substr(0, tooltip_text.size() - 3))); gfx::Size label_preferred_size = label.GetPreferredSize(); int pref_height = styled()->GetHeightForWidth(label_preferred_size.width()); EXPECT_EQ(label_preferred_size.height() * 3, pref_height - styled()->GetInsets().height()); styled()->SetBounds(0, 0, label_preferred_size.width(), pref_height); styled()->Layout(); EXPECT_EQ(label_preferred_size.width(), styled()->width()); ASSERT_EQ(5, styled()->child_count()); // The labels have no focus border while the link (and thus overall styled // label) does, so the labels should be inset by the width of the focus // border. EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(0)->x()); EXPECT_EQ(styled()->child_at(0)->bounds().right(), styled()->child_at(1)->x()); EXPECT_EQ(Label::kFocusBorderPadding, styled()->child_at(2)->x()); EXPECT_EQ(styled()->child_at(2)->bounds().right(), styled()->child_at(3)->x()); EXPECT_EQ(0, styled()->child_at(4)->x()); base::string16 tooltip; EXPECT_TRUE( styled()->child_at(1)->GetTooltipText(gfx::Point(1, 1), &tooltip)); EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip); EXPECT_TRUE( styled()->child_at(2)->GetTooltipText(gfx::Point(1, 1), &tooltip)); EXPECT_EQ(ASCIIToUTF16("tooltip"), tooltip); } TEST_F(StyledLabelTest, SetBaseFontList) { const std::string text("This is a test block of text."); InitStyledLabel(text); std::string font_name("arial"); gfx::Font font(font_name, 30); styled()->SetBaseFontList(gfx::FontList(font)); Label label(ASCIIToUTF16(text), gfx::FontList(font)); styled()->SetBounds(0, 0, label.GetPreferredSize().width(), label.GetPreferredSize().height()); // Make sure we have the same sizing as a label. EXPECT_EQ(label.GetPreferredSize().height(), styled()->height()); EXPECT_EQ(label.GetPreferredSize().width(), styled()->width()); } TEST_F(StyledLabelTest, HandleEmptyLayout) { const std::string text("This is a test block of text."); InitStyledLabel(text); styled()->Layout(); EXPECT_EQ(0, styled()->child_count()); } } // namespace views