1 // Copyright (c) 2012 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/basictypes.h"
6 #include "base/strings/utf_string_conversions.h"
7 #include "ui/base/ui_base_types.h"
8 #include "ui/views/controls/button/label_button.h"
9 #include "ui/views/test/test_views.h"
10 #include "ui/views/test/views_test_base.h"
11 #include "ui/views/widget/widget.h"
12 #include "ui/views/window/dialog_client_view.h"
13 #include "ui/views/window/dialog_delegate.h"
17 class TestDialogClientView
: public DialogClientView
{
19 TestDialogClientView(View
* contents_view
,
20 DialogDelegate
* dialog_delegate
)
21 : DialogClientView(contents_view
),
22 dialog_(dialog_delegate
) {}
23 ~TestDialogClientView() override
{}
25 // DialogClientView implementation.
26 DialogDelegate
* GetDialogDelegate() const override
{ return dialog_
; }
28 View
* GetContentsView() { return contents_view(); }
30 void CreateExtraViews() {
36 DialogDelegate
* dialog_
;
38 DISALLOW_COPY_AND_ASSIGN(TestDialogClientView
);
41 class DialogClientViewTest
: public ViewsTestBase
,
42 public DialogDelegateView
{
44 DialogClientViewTest()
45 : dialog_buttons_(ui::DIALOG_BUTTON_NONE
),
47 footnote_view_(NULL
) {}
48 ~DialogClientViewTest() override
{}
50 // testing::Test implementation.
51 void SetUp() override
{
52 dialog_buttons_
= ui::DIALOG_BUTTON_NONE
;
53 contents_
.reset(new StaticSizedView(gfx::Size(100, 200)));
54 client_view_
.reset(new TestDialogClientView(contents_
.get(), this));
56 ViewsTestBase::SetUp();
59 // DialogDelegateView implementation.
60 View
* GetContentsView() override
{ return contents_
.get(); }
61 View
* CreateExtraView() override
{ return extra_view_
; }
62 bool GetExtraViewPadding(int* padding
) override
{
63 if (extra_view_padding_
)
64 *padding
= *extra_view_padding_
;
65 return extra_view_padding_
.get() != nullptr;
67 View
* CreateFootnoteView() override
{ return footnote_view_
; }
68 int GetDialogButtons() const override
{ return dialog_buttons_
; }
71 gfx::Rect
GetUpdatedClientBounds() {
72 client_view_
->SizeToPreferredSize();
73 client_view_
->Layout();
74 return client_view_
->bounds();
77 // Makes sure that the content view is sized correctly. Width must be at least
78 // the requested amount, but height should always match exactly.
79 void CheckContentsIsSetToPreferredSize() {
80 const gfx::Rect client_bounds
= GetUpdatedClientBounds();
81 const gfx::Size preferred_size
= contents_
->GetPreferredSize();
82 EXPECT_EQ(preferred_size
.height(), contents_
->bounds().height());
83 EXPECT_LE(preferred_size
.width(), contents_
->bounds().width());
84 EXPECT_EQ(contents_
->bounds().origin(), client_bounds
.origin());
85 EXPECT_EQ(contents_
->bounds().right(), client_bounds
.right());
88 // Sets the buttons to show in the dialog and refreshes the dialog.
89 void SetDialogButtons(int dialog_buttons
) {
90 dialog_buttons_
= dialog_buttons
;
91 client_view_
->UpdateDialogButtons();
94 // Sets the extra view.
95 void SetExtraView(View
* view
) {
98 client_view_
->CreateExtraViews();
101 // Sets the extra view padding.
102 void SetExtraViewPadding(int padding
) {
103 DCHECK(!extra_view_padding_
);
104 extra_view_padding_
.reset(new int(padding
));
105 client_view_
->Layout();
108 // Sets the footnote view.
109 void SetFootnoteView(View
* view
) {
110 DCHECK(!footnote_view_
);
111 footnote_view_
= view
;
112 client_view_
->CreateExtraViews();
115 TestDialogClientView
* client_view() { return client_view_
.get(); }
118 // The contents of the dialog.
119 scoped_ptr
<View
> contents_
;
120 // The DialogClientView that's being tested.
121 scoped_ptr
<TestDialogClientView
> client_view_
;
122 // The bitmask of buttons to show in the dialog.
124 View
* extra_view_
; // weak
125 scoped_ptr
<int> extra_view_padding_
; // Null by default.
126 View
* footnote_view_
; // weak
128 DISALLOW_COPY_AND_ASSIGN(DialogClientViewTest
);
131 TEST_F(DialogClientViewTest
, UpdateButtons
) {
132 // This dialog should start with no buttons.
133 EXPECT_EQ(GetDialogButtons(), ui::DIALOG_BUTTON_NONE
);
134 EXPECT_EQ(NULL
, client_view()->ok_button());
135 EXPECT_EQ(NULL
, client_view()->cancel_button());
136 const int height_without_buttons
= GetUpdatedClientBounds().height();
138 // Update to use both buttons.
139 SetDialogButtons(ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
);
140 EXPECT_TRUE(client_view()->ok_button()->is_default());
141 EXPECT_FALSE(client_view()->cancel_button()->is_default());
142 const int height_with_buttons
= GetUpdatedClientBounds().height();
143 EXPECT_GT(height_with_buttons
, height_without_buttons
);
145 // Remove the dialog buttons.
146 SetDialogButtons(ui::DIALOG_BUTTON_NONE
);
147 EXPECT_EQ(NULL
, client_view()->ok_button());
148 EXPECT_EQ(NULL
, client_view()->cancel_button());
149 EXPECT_EQ(GetUpdatedClientBounds().height(), height_without_buttons
);
151 // Reset with just an ok button.
152 SetDialogButtons(ui::DIALOG_BUTTON_OK
);
153 EXPECT_TRUE(client_view()->ok_button()->is_default());
154 EXPECT_EQ(NULL
, client_view()->cancel_button());
155 EXPECT_EQ(GetUpdatedClientBounds().height(), height_with_buttons
);
157 // Reset with just a cancel button.
158 SetDialogButtons(ui::DIALOG_BUTTON_CANCEL
);
159 EXPECT_EQ(NULL
, client_view()->ok_button());
160 EXPECT_TRUE(client_view()->cancel_button()->is_default());
161 EXPECT_EQ(GetUpdatedClientBounds().height(), height_with_buttons
);
164 TEST_F(DialogClientViewTest
, RemoveAndUpdateButtons
) {
165 // Removing buttons from another context should clear the local pointer.
166 SetDialogButtons(ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
);
167 delete client_view()->ok_button();
168 EXPECT_EQ(NULL
, client_view()->ok_button());
169 delete client_view()->cancel_button();
170 EXPECT_EQ(NULL
, client_view()->cancel_button());
172 // Updating should restore the requested buttons properly.
173 SetDialogButtons(ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
);
174 EXPECT_TRUE(client_view()->ok_button()->is_default());
175 EXPECT_FALSE(client_view()->cancel_button()->is_default());
178 // Test that the contents view gets its preferred size in the basic dialog
180 TEST_F(DialogClientViewTest
, ContentsSize
) {
181 CheckContentsIsSetToPreferredSize();
182 EXPECT_EQ(GetContentsView()->bounds().bottom(),
183 client_view()->bounds().bottom());
186 // Test the effect of the button strip on layout.
187 TEST_F(DialogClientViewTest
, LayoutWithButtons
) {
188 SetDialogButtons(ui::DIALOG_BUTTON_OK
| ui::DIALOG_BUTTON_CANCEL
);
189 CheckContentsIsSetToPreferredSize();
190 EXPECT_LT(GetContentsView()->bounds().bottom(),
191 client_view()->bounds().bottom());
192 gfx::Size no_extra_view_size
= client_view()->bounds().size();
194 View
* extra_view
= new StaticSizedView(gfx::Size(200, 200));
195 SetExtraView(extra_view
);
196 CheckContentsIsSetToPreferredSize();
197 EXPECT_GT(client_view()->bounds().height(), no_extra_view_size
.height());
198 int width_of_dialog
= client_view()->bounds().width();
199 int width_of_extra_view
= extra_view
->bounds().width();
201 // Try with an adjusted padding for the extra view.
202 SetExtraViewPadding(250);
203 CheckContentsIsSetToPreferredSize();
204 EXPECT_GT(client_view()->bounds().width(), width_of_dialog
);
206 // Visibility of extra view is respected.
207 extra_view
->SetVisible(false);
208 CheckContentsIsSetToPreferredSize();
209 EXPECT_EQ(no_extra_view_size
.height(), client_view()->bounds().height());
210 EXPECT_EQ(no_extra_view_size
.width(), client_view()->bounds().width());
212 // Try with a reduced-size dialog.
213 extra_view
->SetVisible(true);
214 client_view()->SetBoundsRect(gfx::Rect(gfx::Point(0, 0), no_extra_view_size
));
215 client_view()->Layout();
216 EXPECT_GT(width_of_extra_view
, extra_view
->bounds().width());
219 // Test the effect of the footnote view on layout.
220 TEST_F(DialogClientViewTest
, LayoutWithFootnote
) {
221 CheckContentsIsSetToPreferredSize();
222 gfx::Size no_footnote_size
= client_view()->bounds().size();
224 View
* footnote_view
= new StaticSizedView(gfx::Size(200, 200));
225 SetFootnoteView(footnote_view
);
226 CheckContentsIsSetToPreferredSize();
227 EXPECT_GT(client_view()->bounds().height(), no_footnote_size
.height());
228 EXPECT_EQ(200, footnote_view
->bounds().height());
229 gfx::Size with_footnote_size
= client_view()->bounds().size();
230 EXPECT_EQ(with_footnote_size
.width(), footnote_view
->bounds().width());
232 SetDialogButtons(ui::DIALOG_BUTTON_CANCEL
);
233 CheckContentsIsSetToPreferredSize();
234 EXPECT_LE(with_footnote_size
.height(), client_view()->bounds().height());
235 EXPECT_LE(with_footnote_size
.width(), client_view()->bounds().width());
236 gfx::Size with_footnote_and_button_size
= client_view()->bounds().size();
238 SetDialogButtons(ui::DIALOG_BUTTON_NONE
);
239 footnote_view
->SetVisible(false);
240 CheckContentsIsSetToPreferredSize();
241 EXPECT_EQ(no_footnote_size
.height(), client_view()->bounds().height());
242 EXPECT_EQ(no_footnote_size
.width(), client_view()->bounds().width());
245 // Test that GetHeightForWidth is respected for the footnote view.
246 TEST_F(DialogClientViewTest
, LayoutWithFootnoteHeightForWidth
) {
247 CheckContentsIsSetToPreferredSize();
248 gfx::Size no_footnote_size
= client_view()->bounds().size();
250 View
* footnote_view
= new ProportionallySizedView(3);
251 SetFootnoteView(footnote_view
);
252 CheckContentsIsSetToPreferredSize();
253 EXPECT_GT(client_view()->bounds().height(), no_footnote_size
.height());
254 EXPECT_EQ(footnote_view
->bounds().width() * 3,
255 footnote_view
->bounds().height());
258 // Test that the DialogClientView's FocusManager is properly updated when the
259 // DialogClientView belongs to a non top level widget and the widget is
260 // reparented. The DialogClientView belongs to a non top level widget in the
261 // case of constrained windows. The constrained window's widget is reparented
262 // when a browser tab is dragged to a different browser window.
263 TEST_F(DialogClientViewTest
, FocusManager
) {
264 scoped_ptr
<Widget
> toplevel1(new Widget
);
265 Widget::InitParams toplevel1_params
=
266 CreateParams(Widget::InitParams::TYPE_WINDOW
);
267 toplevel1_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
268 toplevel1
->Init(toplevel1_params
);
270 scoped_ptr
<Widget
> toplevel2(new Widget
);
271 Widget::InitParams toplevel2_params
=
272 CreateParams(Widget::InitParams::TYPE_WINDOW
);
273 toplevel2_params
.ownership
= Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET
;
274 toplevel2
->Init(toplevel2_params
);
276 Widget
* dialog
= new Widget
;
277 Widget::InitParams dialog_params
=
278 CreateParams(Widget::InitParams::TYPE_WINDOW
);
279 dialog_params
.child
= true;
280 dialog_params
.delegate
= new DialogDelegateView();
281 dialog_params
.parent
= toplevel1
->GetNativeView();
282 dialog
->Init(dialog_params
);
284 // Test that the FocusManager has been properly set when the DialogClientView
285 // was parented to |dialog|.
286 DialogClientView
* client_view
=
287 static_cast<DialogClientView
*>(dialog
->client_view());
288 EXPECT_EQ(toplevel1
->GetFocusManager(), client_view
->focus_manager_
);
290 // Test that the FocusManager is properly updated when the DialogClientView's
291 // top level widget is changed.
292 Widget::ReparentNativeView(dialog
->GetNativeView(), NULL
);
293 EXPECT_EQ(NULL
, client_view
->focus_manager_
);
294 Widget::ReparentNativeView(dialog
->GetNativeView(),
295 toplevel2
->GetNativeView());
296 EXPECT_EQ(toplevel2
->GetFocusManager(), client_view
->focus_manager_
);
297 Widget::ReparentNativeView(dialog
->GetNativeView(),
298 toplevel1
->GetNativeView());
299 EXPECT_NE(toplevel1
->GetFocusManager(), toplevel2
->GetFocusManager());
300 EXPECT_EQ(toplevel1
->GetFocusManager(), client_view
->focus_manager_
);
302 // Test that the FocusManager is properly cleared when the DialogClientView is
303 // removed from |dialog| during the widget's destruction.
304 client_view
->set_owned_by_client();
305 scoped_ptr
<DialogClientView
> owned_client_view(client_view
);
306 toplevel1
->CloseNow();
307 toplevel2
->CloseNow();
308 EXPECT_EQ(NULL
, owned_client_view
->focus_manager_
);