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 |