| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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 "ui/views/style/mac/dialog_button_border_mac.h" | |
| 6 | |
| 7 #include "base/macros.h" | |
| 8 #include "base/memory/ptr_util.h" | |
| 9 #include "base/strings/utf_string_conversions.h" | |
| 10 #include "third_party/skia/include/core/SkCanvas.h" | |
| 11 #include "ui/compositor/canvas_painter.h" | |
| 12 #include "ui/gfx/canvas.h" | |
| 13 #include "ui/views/border.h" | |
| 14 #include "ui/views/controls/button/label_button.h" | |
| 15 #include "ui/views/test/views_test_base.h" | |
| 16 | |
| 17 namespace views { | |
| 18 namespace { | |
| 19 | |
| 20 // LabelButton that can optionally provide a custom border. | |
| 21 class TestLabelButton : public LabelButton { | |
| 22 public: | |
| 23 explicit TestLabelButton(const char* text) | |
| 24 : LabelButton(nullptr, base::ASCIIToUTF16(text)) {} | |
| 25 | |
| 26 void SimulateAddToWidget() { OnNativeThemeChanged(nullptr); } | |
| 27 void set_provide_custom_border(bool value) { provide_custom_border_ = value; } | |
| 28 | |
| 29 // LabelButton: | |
| 30 std::unique_ptr<LabelButtonBorder> CreateDefaultBorder() const override { | |
| 31 if (!provide_custom_border_) | |
| 32 return LabelButton::CreateDefaultBorder(); | |
| 33 | |
| 34 return base::MakeUnique<LabelButtonAssetBorder>(style()); | |
| 35 } | |
| 36 | |
| 37 private: | |
| 38 bool provide_custom_border_ = false; | |
| 39 | |
| 40 DISALLOW_COPY_AND_ASSIGN(TestLabelButton); | |
| 41 }; | |
| 42 | |
| 43 gfx::Size DialogButtonBorderMacSize() { | |
| 44 const DialogButtonBorderMac template_border; | |
| 45 return template_border.GetMinimumSize(); | |
| 46 } | |
| 47 | |
| 48 // A heuristic that tries to determine whether the border on |view| is a | |
| 49 // DialogButtonBorderMac by checking its minimum size. | |
| 50 bool BorderIsDialogButton(const View& view) { | |
| 51 const Border* border = view.border(); | |
| 52 return border && DialogButtonBorderMacSize() == border->GetMinimumSize(); | |
| 53 } | |
| 54 | |
| 55 SkColor TestPaint(View* view) { | |
| 56 EXPECT_TRUE(view->visible()); | |
| 57 EXPECT_FALSE(view->bounds().IsEmpty()); | |
| 58 const gfx::Point center = view->bounds().CenterPoint(); | |
| 59 gfx::Canvas canvas(view->bounds().size(), 1.0, false /* is_opaque */); | |
| 60 SkCanvas* sk_canvas = canvas.sk_canvas(); | |
| 61 | |
| 62 // Read a pixel - it should be blank. | |
| 63 SkColor initial_pixel; | |
| 64 SkBitmap bitmap; | |
| 65 bitmap.allocN32Pixels(1, 1); | |
| 66 EXPECT_TRUE(sk_canvas->readPixels(&bitmap, center.x(), center.y())); | |
| 67 initial_pixel = bitmap.getColor(0, 0); | |
| 68 EXPECT_EQ(static_cast<SkColor>(SK_ColorTRANSPARENT), initial_pixel); | |
| 69 | |
| 70 view->Paint(ui::CanvasPainter(&canvas, 1.f).context()); | |
| 71 | |
| 72 // Ensure save()/restore() calls are balanced. | |
| 73 EXPECT_EQ(1, sk_canvas->getSaveCount()); | |
| 74 | |
| 75 // Ensure "something" happened. This assumes the border is a | |
| 76 // DialogButtonBorderMac, which always modifies the center pixel. | |
| 77 EXPECT_TRUE(sk_canvas->readPixels(&bitmap, center.x(), center.y())); | |
| 78 return bitmap.getColor(0, 0); | |
| 79 } | |
| 80 | |
| 81 void TestPaintAllStates(CustomButton* button, bool verify) { | |
| 82 for (int i = 0; i < Button::STATE_COUNT; ++i) { | |
| 83 Button::ButtonState state = static_cast<Button::ButtonState>(i); | |
| 84 SCOPED_TRACE(testing::Message() << "Button::ButtonState: " << state); | |
| 85 button->SetState(state); | |
| 86 SkColor color = TestPaint(button); | |
| 87 if (verify) | |
| 88 EXPECT_NE(static_cast<SkColor>(SK_ColorTRANSPARENT), color); | |
| 89 } | |
| 90 } | |
| 91 | |
| 92 } // namespace | |
| 93 | |
| 94 using DialogButtonBorderMacTest = ViewsTestBase; | |
| 95 | |
| 96 // Verify that the DialogButtonBorderMac insets are consistent with the | |
| 97 // minimum size, and they're correctly carried across to the View's preferred | |
| 98 // size. | |
| 99 TEST_F(DialogButtonBorderMacTest, DrawMinimumSize) { | |
| 100 TestLabelButton button(""); | |
| 101 button.SetStyle(Button::STYLE_BUTTON); | |
| 102 button.SimulateAddToWidget(); | |
| 103 | |
| 104 EXPECT_TRUE(BorderIsDialogButton(button)); | |
| 105 | |
| 106 // The border minimum size should be at least the size of the insets. | |
| 107 const gfx::Size border_min_size = DialogButtonBorderMacSize(); | |
| 108 const gfx::Insets insets = button.GetInsets(); | |
| 109 EXPECT_LE(insets.width(), border_min_size.width()); | |
| 110 EXPECT_LE(insets.height(), border_min_size.height()); | |
| 111 | |
| 112 // The view preferred size should be at least as big as the border minimum. | |
| 113 gfx::Size view_preferred_size = button.GetPreferredSize(); | |
| 114 EXPECT_LE(border_min_size.width(), view_preferred_size.width()); | |
| 115 EXPECT_LE(border_min_size.height(), view_preferred_size.height()); | |
| 116 | |
| 117 // Note that Mac's PlatformStyle specifies a minimum button size, but it | |
| 118 // shouldn't be larger than the size of the button's label plus border insets. | |
| 119 // If it was, a Button::SetMinSize() call would be needed here to override it. | |
| 120 | |
| 121 button.SizeToPreferredSize(); | |
| 122 EXPECT_EQ(view_preferred_size.width(), button.width()); | |
| 123 EXPECT_EQ(view_preferred_size.height(), button.height()); | |
| 124 | |
| 125 { | |
| 126 SCOPED_TRACE("Preferred Size"); | |
| 127 TestPaintAllStates(&button, true); | |
| 128 } | |
| 129 | |
| 130 // The View can ignore the border minimum size. To account for shadows, the | |
| 131 // border will paint something as small as 4x4. | |
| 132 { | |
| 133 SCOPED_TRACE("Minimum Paint Size"); | |
| 134 button.SetSize(gfx::Size(4, 4)); | |
| 135 TestPaintAllStates(&button, true); | |
| 136 } | |
| 137 | |
| 138 // Smaller than that, nothing gets painted, but the paint code should be sane. | |
| 139 { | |
| 140 SCOPED_TRACE("Size 1x1"); | |
| 141 button.SetSize(gfx::Size(1, 1)); | |
| 142 TestPaintAllStates(&button, false); | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 // Test drawing with some text. The usual case. | |
| 147 TEST_F(DialogButtonBorderMacTest, DrawWithLabel) { | |
| 148 TestLabelButton button(""); | |
| 149 button.SetStyle(Button::STYLE_BUTTON); | |
| 150 button.SimulateAddToWidget(); | |
| 151 | |
| 152 EXPECT_TRUE(BorderIsDialogButton(button)); | |
| 153 | |
| 154 button.SizeToPreferredSize(); | |
| 155 const gfx::Size no_label_size = button.size(); | |
| 156 | |
| 157 button.SetText( | |
| 158 base::ASCIIToUTF16("Label Text That Exceeds the Minimum Button Size")); | |
| 159 button.SizeToPreferredSize(); | |
| 160 | |
| 161 // Long label, so the button width should be greater than the empty button. | |
| 162 EXPECT_LT(no_label_size.width(), button.width()); | |
| 163 | |
| 164 // The height shouldn't change. | |
| 165 EXPECT_EQ(no_label_size.height(), button.height()); | |
| 166 | |
| 167 TestPaintAllStates(&button, true); | |
| 168 } | |
| 169 | |
| 170 // Test that the themed style is not used for STYLE_TEXTBUTTON (the default), or | |
| 171 // when a custom Border is set, or when a LabelButton subclass provides its own | |
| 172 // default border. | |
| 173 TEST_F(DialogButtonBorderMacTest, ChecksButtonStyle) { | |
| 174 TestLabelButton button(""); | |
| 175 button.SimulateAddToWidget(); | |
| 176 | |
| 177 // Default style is STYLE_TEXTBUTTON, which doesn't use the themed border. | |
| 178 EXPECT_FALSE(BorderIsDialogButton(button)); | |
| 179 | |
| 180 button.SetStyle(Button::STYLE_BUTTON); | |
| 181 button.SimulateAddToWidget(); | |
| 182 EXPECT_TRUE(BorderIsDialogButton(button)); | |
| 183 | |
| 184 button.set_provide_custom_border(true); | |
| 185 button.SimulateAddToWidget(); | |
| 186 EXPECT_FALSE(BorderIsDialogButton(button)); | |
| 187 | |
| 188 button.set_provide_custom_border(false); | |
| 189 button.SimulateAddToWidget(); | |
| 190 EXPECT_TRUE(BorderIsDialogButton(button)); | |
| 191 | |
| 192 // Any call to SetBorder() will immediately prevent themed buttons and adding | |
| 193 // to a Widget (to pick up a NativeTheme) shouldn't restore them. | |
| 194 button.SetBorder(Border::NullBorder()); | |
| 195 EXPECT_FALSE(BorderIsDialogButton(button)); | |
| 196 button.SimulateAddToWidget(); | |
| 197 EXPECT_FALSE(BorderIsDialogButton(button)); | |
| 198 } | |
| 199 | |
| 200 } // namespace views | |
| OLD | NEW |