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.
5 #include "ui/views/style/mac/dialog_button_border_mac.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "third_party/skia/include/core/SkCanvas.h"
9 #include "ui/compositor/canvas_painter.h"
10 #include "ui/gfx/canvas.h"
11 #include "ui/views/border.h"
12 #include "ui/views/controls/button/label_button.h"
13 #include "ui/views/test/views_test_base.h"
18 // LabelButton that can optionally provide a custom border.
19 class TestLabelButton
: public LabelButton
{
21 explicit TestLabelButton(const char* text
)
22 : LabelButton(nullptr, base::ASCIIToUTF16(text
)) {}
24 void SimulateAddToWidget() { OnNativeThemeChanged(nullptr); }
25 void set_provide_custom_border(bool value
) { provide_custom_border_
= value
; }
28 scoped_ptr
<LabelButtonBorder
> CreateDefaultBorder() const override
{
29 if (!provide_custom_border_
)
30 return LabelButton::CreateDefaultBorder();
32 return make_scoped_ptr(new LabelButtonAssetBorder(style()));
36 bool provide_custom_border_
= false;
38 DISALLOW_COPY_AND_ASSIGN(TestLabelButton
);
41 gfx::Size
DialogButtonBorderMacSize() {
42 const DialogButtonBorderMac template_border
;
43 return template_border
.GetMinimumSize();
46 // A heuristic that tries to determine whether the border on |view| is a
47 // DialogButtonBorderMac by checking its minimum size.
48 bool BorderIsDialogButton(const View
& view
) {
49 const Border
* border
= view
.border();
50 return border
&& DialogButtonBorderMacSize() == border
->GetMinimumSize();
53 SkColor
TestPaint(View
* view
) {
54 EXPECT_TRUE(view
->visible());
55 EXPECT_FALSE(view
->bounds().IsEmpty());
56 const gfx::Point center
= view
->bounds().CenterPoint();
57 gfx::Canvas
canvas(view
->bounds().size(), 1.0, false /* is_opaque */);
58 SkCanvas
* sk_canvas
= canvas
.sk_canvas();
60 // Read a pixel - it should be blank.
61 SkColor initial_pixel
;
63 bitmap
.allocN32Pixels(1, 1);
64 EXPECT_TRUE(sk_canvas
->readPixels(&bitmap
, center
.x(), center
.y()));
65 initial_pixel
= bitmap
.getColor(0, 0);
66 EXPECT_EQ(static_cast<SkColor
>(SK_ColorTRANSPARENT
), initial_pixel
);
68 view
->Paint(ui::CanvasPainter(&canvas
, 1.f
).context());
70 // Ensure save()/restore() calls are balanced.
71 EXPECT_EQ(1, sk_canvas
->getSaveCount());
73 // Ensure "something" happened. This assumes the border is a
74 // DialogButtonBorderMac, which always modifies the center pixel.
75 EXPECT_TRUE(sk_canvas
->readPixels(&bitmap
, center
.x(), center
.y()));
76 return bitmap
.getColor(0, 0);
79 void TestPaintAllStates(CustomButton
* button
, bool verify
) {
80 for (int i
= 0; i
< Button::STATE_COUNT
; ++i
) {
81 Button::ButtonState state
= static_cast<Button::ButtonState
>(i
);
82 SCOPED_TRACE(testing::Message() << "Button::ButtonState: " << state
);
83 button
->SetState(state
);
84 SkColor color
= TestPaint(button
);
86 EXPECT_NE(static_cast<SkColor
>(SK_ColorTRANSPARENT
), color
);
92 using DialogButtonBorderMacTest
= ViewsTestBase
;
94 // Verify that the DialogButtonBorderMac insets are consistent with the
95 // minimum size, and they're correctly carried across to the View's preferred
97 TEST_F(DialogButtonBorderMacTest
, DrawMinimumSize
) {
98 TestLabelButton
button("");
99 button
.SetStyle(Button::STYLE_BUTTON
);
100 button
.SimulateAddToWidget();
102 EXPECT_TRUE(BorderIsDialogButton(button
));
104 // The border minimum size should be at least the size of the insets.
105 const gfx::Size border_min_size
= DialogButtonBorderMacSize();
106 const gfx::Insets insets
= button
.GetInsets();
107 EXPECT_LE(insets
.width(), border_min_size
.width());
108 EXPECT_LE(insets
.height(), border_min_size
.height());
110 // The view preferred size should be at least as big as the border minimum.
111 gfx::Size view_preferred_size
= button
.GetPreferredSize();
112 EXPECT_LE(border_min_size
.width(), view_preferred_size
.width());
113 EXPECT_LE(border_min_size
.height(), view_preferred_size
.height());
115 // Calling SetStyle(STYLE_BUTTON) sets a default minimum height that is larger
116 // than the border minimum height. Override that to match the border.
117 button
.SetMinSize(gfx::Size());
118 view_preferred_size
= button
.GetPreferredSize();
119 EXPECT_EQ(border_min_size
.width(), view_preferred_size
.width());
120 EXPECT_EQ(border_min_size
.height(), view_preferred_size
.height());
122 button
.SizeToPreferredSize();
123 EXPECT_EQ(view_preferred_size
.width(), button
.width());
124 EXPECT_EQ(view_preferred_size
.height(), button
.height());
127 SCOPED_TRACE("Preferred Size");
128 TestPaintAllStates(&button
, true);
131 // The View can ignore the border minimum size. To account for shadows, the
132 // border will paint something as small as 4x4.
134 SCOPED_TRACE("Minimum Paint Size");
135 button
.SetSize(gfx::Size(4, 4));
136 TestPaintAllStates(&button
, true);
139 // Smaller than that, nothing gets painted, but the paint code should be sane.
141 SCOPED_TRACE("Size 1x1");
142 button
.SetSize(gfx::Size(1, 1));
143 TestPaintAllStates(&button
, false);
147 // Test drawing with some text. The usual case.
148 TEST_F(DialogButtonBorderMacTest
, DrawWithLabel
) {
149 TestLabelButton
button("Label Text That Exceeds the Minimum Button Size");
150 button
.SetStyle(Button::STYLE_BUTTON
);
151 button
.SimulateAddToWidget();
153 EXPECT_TRUE(BorderIsDialogButton(button
));
155 button
.SetMinSize(gfx::Size());
156 button
.SizeToPreferredSize();
158 // Long label, so the button width should be greater than in DrawMinimumSize.
159 const gfx::Size border_min_size
= DialogButtonBorderMacSize();
160 EXPECT_LT(border_min_size
.width(), button
.width());
161 EXPECT_EQ(border_min_size
.height(), button
.height());
163 TestPaintAllStates(&button
, true);
166 // Test that the themed style is not used for STYLE_TEXTBUTTON (the default), or
167 // when a custom Border is set, or when a LabelButton subclass provides its own
169 TEST_F(DialogButtonBorderMacTest
, ChecksButtonStyle
) {
170 TestLabelButton
button("");
171 button
.SimulateAddToWidget();
173 // Default style is STYLE_TEXTBUTTON, which doesn't use the themed border.
174 EXPECT_FALSE(BorderIsDialogButton(button
));
176 button
.SetStyle(Button::STYLE_BUTTON
);
177 button
.SimulateAddToWidget();
178 EXPECT_TRUE(BorderIsDialogButton(button
));
180 button
.set_provide_custom_border(true);
181 button
.SimulateAddToWidget();
182 EXPECT_FALSE(BorderIsDialogButton(button
));
184 button
.set_provide_custom_border(false);
185 button
.SimulateAddToWidget();
186 EXPECT_TRUE(BorderIsDialogButton(button
));
188 // Any call to SetBorder() will immediately prevent themed buttons and adding
189 // to a Widget (to pick up a NativeTheme) shouldn't restore them.
190 button
.SetBorder(Border::NullBorder());
191 EXPECT_FALSE(BorderIsDialogButton(button
));
192 button
.SimulateAddToWidget();
193 EXPECT_FALSE(BorderIsDialogButton(button
));