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
3 // found in the LICENSE file.
5 #include "base/strings/utf_string_conversions.h"
6 #include "ui/base/hit_test.h"
7 #include "ui/views/bubble/bubble_border.h"
8 #include "ui/views/bubble/bubble_frame_view.h"
9 #include "ui/views/controls/button/checkbox.h"
10 #include "ui/views/controls/button/label_button.h"
11 #include "ui/views/test/views_test_base.h"
12 #include "ui/views/widget/widget.h"
13 #include "ui/views/window/dialog_client_view.h"
14 #include "ui/views/window/dialog_delegate.h"
20 class TestDialog
: public DialogDelegateView
, public ButtonListener
{
26 last_pressed_button_(NULL
) {}
27 ~TestDialog() override
{}
29 // WidgetDelegate overrides:
30 bool ShouldShowWindowTitle() const override
{
31 return !title_
.empty();
34 // DialogDelegateView overrides:
35 bool Cancel() override
{
39 bool Accept() override
{
44 // DialogDelegateView overrides:
45 gfx::Size
GetPreferredSize() const override
{ return gfx::Size(200, 200); }
46 base::string16
GetWindowTitle() const override
{ return title_
; }
47 bool UseNewStyleForThisDialog() const override
{ return true; }
49 // ButtonListener override:
50 void ButtonPressed(Button
* sender
, const ui::Event
& event
) override
{
51 last_pressed_button_
= sender
;
54 Button
* last_pressed_button() const { return last_pressed_button_
; }
56 void PressEnterAndCheckStates(Button
* button
) {
57 ui::KeyEvent
key_event(ui::ET_KEY_PRESSED
, ui::VKEY_RETURN
, ui::EF_NONE
);
58 GetFocusManager()->OnKeyEvent(key_event
);
59 const DialogClientView
* client_view
= GetDialogClientView();
60 EXPECT_EQ(canceled_
, client_view
->cancel_button()->is_default());
61 EXPECT_EQ(accepted_
, client_view
->ok_button()->is_default());
62 // This view does not listen for ok or cancel clicks, DialogClientView does.
63 CheckAndResetStates(button
== client_view
->cancel_button(),
64 button
== client_view
->ok_button(),
65 (canceled_
|| accepted_
) ? NULL
: button
);
68 void CheckAndResetStates(bool canceled
, bool accepted
, Button
* last_pressed
) {
69 EXPECT_EQ(canceled
, canceled_
);
71 EXPECT_EQ(accepted
, accepted_
);
73 EXPECT_EQ(last_pressed
, last_pressed_button_
);
74 last_pressed_button_
= NULL
;
82 void set_title(const base::string16
& title
) { title_
= title
; }
87 // Prevent the dialog from closing, for repeated ok and cancel button clicks.
89 Button
* last_pressed_button_
;
90 base::string16 title_
;
92 DISALLOW_COPY_AND_ASSIGN(TestDialog
);
95 class DialogTest
: public ViewsTestBase
{
97 DialogTest() : dialog_(NULL
) {}
98 ~DialogTest() override
{}
100 void SetUp() override
{
101 ViewsTestBase::SetUp();
102 dialog_
= new TestDialog();
103 DialogDelegate::CreateDialogWidget(dialog_
, GetContext(), NULL
)->Show();
106 void TearDown() override
{
108 ViewsTestBase::TearDown();
111 TestDialog
* dialog() const { return dialog_
; }
116 DISALLOW_COPY_AND_ASSIGN(DialogTest
);
121 TEST_F(DialogTest
, DefaultButtons
) {
122 DialogClientView
* client_view
= dialog()->GetDialogClientView();
123 LabelButton
* ok_button
= client_view
->ok_button();
125 // DialogDelegate's default button (ok) should be default (and handle enter).
126 EXPECT_EQ(ui::DIALOG_BUTTON_OK
, dialog()->GetDefaultDialogButton());
127 dialog()->PressEnterAndCheckStates(ok_button
);
129 // Focus another button in the dialog, it should become the default.
130 LabelButton
* button_1
= new LabelButton(dialog(), base::string16());
131 client_view
->AddChildView(button_1
);
132 client_view
->OnWillChangeFocus(ok_button
, button_1
);
133 EXPECT_TRUE(button_1
->is_default());
134 dialog()->PressEnterAndCheckStates(button_1
);
136 // Focus a Checkbox (not a push button), OK should become the default again.
137 Checkbox
* checkbox
= new Checkbox(base::string16());
138 client_view
->AddChildView(checkbox
);
139 client_view
->OnWillChangeFocus(button_1
, checkbox
);
140 EXPECT_FALSE(button_1
->is_default());
141 dialog()->PressEnterAndCheckStates(ok_button
);
143 // Focus yet another button in the dialog, it should become the default.
144 LabelButton
* button_2
= new LabelButton(dialog(), base::string16());
145 client_view
->AddChildView(button_2
);
146 client_view
->OnWillChangeFocus(checkbox
, button_2
);
147 EXPECT_FALSE(button_1
->is_default());
148 EXPECT_TRUE(button_2
->is_default());
149 dialog()->PressEnterAndCheckStates(button_2
);
151 // Focus nothing, OK should become the default again.
152 client_view
->OnWillChangeFocus(button_2
, NULL
);
153 EXPECT_FALSE(button_1
->is_default());
154 EXPECT_FALSE(button_2
->is_default());
155 dialog()->PressEnterAndCheckStates(ok_button
);
158 TEST_F(DialogTest
, AcceptAndCancel
) {
159 DialogClientView
* client_view
= dialog()->GetDialogClientView();
160 LabelButton
* ok_button
= client_view
->ok_button();
161 LabelButton
* cancel_button
= client_view
->cancel_button();
163 // Check that return/escape accelerators accept/cancel dialogs.
164 const ui::KeyEvent
return_key(
165 ui::ET_KEY_PRESSED
, ui::VKEY_RETURN
, ui::EF_NONE
);
166 dialog()->GetFocusManager()->OnKeyEvent(return_key
);
167 dialog()->CheckAndResetStates(false, true, NULL
);
168 const ui::KeyEvent
escape_key(
169 ui::ET_KEY_PRESSED
, ui::VKEY_ESCAPE
, ui::EF_NONE
);
170 dialog()->GetFocusManager()->OnKeyEvent(escape_key
);
171 dialog()->CheckAndResetStates(true, false, NULL
);
173 // Check ok and cancel button behavior on a directed return key events.
174 ok_button
->OnKeyPressed(return_key
);
175 dialog()->CheckAndResetStates(false, true, NULL
);
176 cancel_button
->OnKeyPressed(return_key
);
177 dialog()->CheckAndResetStates(true, false, NULL
);
179 // Check that return accelerators cancel dialogs if cancel is focused.
180 cancel_button
->RequestFocus();
181 dialog()->GetFocusManager()->OnKeyEvent(return_key
);
182 dialog()->CheckAndResetStates(true, false, NULL
);
185 TEST_F(DialogTest
, RemoveDefaultButton
) {
186 // Removing buttons from the dialog here should not cause a crash on close.
187 delete dialog()->GetDialogClientView()->ok_button();
188 delete dialog()->GetDialogClientView()->cancel_button();
191 TEST_F(DialogTest
, HitTest_HiddenTitle
) {
192 // Ensure that BubbleFrameView hit-tests as expected when the title is hidden.
193 const NonClientView
* view
= dialog()->GetWidget()->non_client_view();
194 BubbleFrameView
* frame
= static_cast<BubbleFrameView
*>(view
->frame_view());
195 const int border
= frame
->bubble_border()->GetBorderThickness();
201 { border
, HTSYSMENU
},
202 { border
+ 10, HTSYSMENU
},
203 { border
+ 20, HTCLIENT
},
204 { border
+ 50, HTCLIENT
},
205 { border
+ 60, HTCLIENT
},
209 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
210 gfx::Point
point(cases
[i
].point
, cases
[i
].point
);
211 EXPECT_EQ(cases
[i
].hit
, frame
->NonClientHitTest(point
))
212 << " with border: " << border
<< ", at point " << cases
[i
].point
;
216 TEST_F(DialogTest
, HitTest_WithTitle
) {
217 // Ensure that BubbleFrameView hit-tests as expected when the title is shown.
218 const NonClientView
* view
= dialog()->GetWidget()->non_client_view();
219 dialog()->set_title(base::ASCIIToUTF16("Title"));
220 dialog()->GetWidget()->UpdateWindowTitle();
221 BubbleFrameView
* frame
= static_cast<BubbleFrameView
*>(view
->frame_view());
222 const int border
= frame
->bubble_border()->GetBorderThickness();
228 { border
, HTSYSMENU
},
229 { border
+ 10, HTSYSMENU
},
230 { border
+ 20, HTCAPTION
},
231 { border
+ 50, HTCLIENT
},
232 { border
+ 60, HTCLIENT
},
236 for (size_t i
= 0; i
< arraysize(cases
); ++i
) {
237 gfx::Point
point(cases
[i
].point
, cases
[i
].point
);
238 EXPECT_EQ(cases
[i
].hit
, frame
->NonClientHitTest(point
))
239 << " with border: " << border
<< ", at point " << cases
[i
].point
;
243 TEST_F(DialogTest
, BoundsAccommodateTitle
) {
244 TestDialog
* dialog2(new TestDialog());
245 dialog2
->set_title(base::ASCIIToUTF16("Title"));
246 DialogDelegate::CreateDialogWidget(dialog2
, GetContext(), NULL
);
248 // Titled dialogs have taller initial frame bounds than untitled dialogs.
249 View
* frame1
= dialog()->GetWidget()->non_client_view()->frame_view();
250 View
* frame2
= dialog2
->GetWidget()->non_client_view()->frame_view();
251 EXPECT_LT(frame1
->GetPreferredSize().height(),
252 frame2
->GetPreferredSize().height());
254 // Giving the default test dialog a title will yield the same bounds.
255 dialog()->set_title(base::ASCIIToUTF16("Title"));
256 dialog()->GetWidget()->UpdateWindowTitle();
257 EXPECT_EQ(frame1
->GetPreferredSize().height(),
258 frame2
->GetPreferredSize().height());