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 "ui/views/window/dialog_client_view.h"
9 #include "ui/events/keycodes/keyboard_codes.h"
10 #include "ui/views/background.h"
11 #include "ui/views/controls/button/blue_button.h"
12 #include "ui/views/controls/button/label_button.h"
13 #include "ui/views/layout/layout_constants.h"
14 #include "ui/views/widget/widget.h"
15 #include "ui/views/window/dialog_delegate.h"
21 // The group used by the buttons. This name is chosen voluntarily big not to
22 // conflict with other groups that could be in the dialog content.
23 const int kButtonGroup
= 6666;
25 #if defined(OS_WIN) || defined(OS_CHROMEOS)
26 const bool kIsOkButtonOnLeftSide
= true;
28 const bool kIsOkButtonOnLeftSide
= false;
31 // Returns true if the given view should be shown (i.e. exists and is
33 bool ShouldShow(View
* view
) {
34 return view
&& view
->visible();
37 // Do the layout for a button.
38 void LayoutButton(LabelButton
* button
, gfx::Rect
* row_bounds
) {
42 const gfx::Size size
= button
->GetPreferredSize();
43 row_bounds
->set_width(row_bounds
->width() - size
.width());
44 button
->SetBounds(row_bounds
->right(), row_bounds
->y(),
45 size
.width(), row_bounds
->height());
46 row_bounds
->set_width(row_bounds
->width() - kRelatedButtonHSpacing
);
51 ///////////////////////////////////////////////////////////////////////////////
52 // DialogClientView, public:
54 DialogClientView::DialogClientView(Widget
* owner
, View
* contents_view
)
55 : ClientView(owner
, contents_view
),
58 default_button_(NULL
),
62 notified_delegate_(false) {
65 DialogClientView::~DialogClientView() {
68 void DialogClientView::AcceptWindow() {
69 // Only notify the delegate once. See |notified_delegate_|'s comment.
70 if (!notified_delegate_
&& GetDialogDelegate()->Accept(false)) {
71 notified_delegate_
= true;
76 void DialogClientView::CancelWindow() {
77 // Only notify the delegate once. See |notified_delegate_|'s comment.
78 if (!notified_delegate_
&& GetDialogDelegate()->Cancel()) {
79 notified_delegate_
= true;
84 void DialogClientView::UpdateDialogButtons() {
85 const int buttons
= GetDialogDelegate()->GetDialogButtons();
86 ui::Accelerator
escape(ui::VKEY_ESCAPE
, ui::EF_NONE
);
88 default_button_
->SetIsDefault(false);
89 default_button_
= NULL
;
91 if (buttons
& ui::DIALOG_BUTTON_OK
) {
93 ok_button_
= CreateDialogButton(ui::DIALOG_BUTTON_OK
);
94 if (!(buttons
& ui::DIALOG_BUTTON_CANCEL
))
95 ok_button_
->AddAccelerator(escape
);
96 AddChildView(ok_button_
);
99 UpdateButton(ok_button_
, ui::DIALOG_BUTTON_OK
);
100 } else if (ok_button_
) {
105 if (buttons
& ui::DIALOG_BUTTON_CANCEL
) {
106 if (!cancel_button_
) {
107 cancel_button_
= CreateDialogButton(ui::DIALOG_BUTTON_CANCEL
);
108 cancel_button_
->AddAccelerator(escape
);
109 AddChildView(cancel_button_
);
112 UpdateButton(cancel_button_
, ui::DIALOG_BUTTON_CANCEL
);
113 } else if (cancel_button_
) {
114 delete cancel_button_
;
115 cancel_button_
= NULL
;
118 // Use the escape key to close the window if there are no dialog buttons.
119 if (!has_dialog_buttons())
120 AddAccelerator(escape
);
125 ///////////////////////////////////////////////////////////////////////////////
126 // DialogClientView, ClientView overrides:
128 bool DialogClientView::CanClose() {
129 if (notified_delegate_
)
132 // The dialog is closing but no Accept or Cancel action has been performed
133 // before: it's a Close action.
134 if (GetDialogDelegate()->Close()) {
135 notified_delegate_
= true;
136 GetDialogDelegate()->OnClosed();
142 DialogClientView
* DialogClientView::AsDialogClientView() {
146 const DialogClientView
* DialogClientView::AsDialogClientView() const {
150 void DialogClientView::OnWillChangeFocus(View
* focused_before
,
152 // Make the newly focused button default or restore the dialog's default.
153 const int default_button
= GetDialogDelegate()->GetDefaultDialogButton();
154 LabelButton
* new_default_button
= NULL
;
156 !strcmp(focused_now
->GetClassName(), LabelButton::kViewClassName
)) {
157 new_default_button
= static_cast<LabelButton
*>(focused_now
);
158 } else if (default_button
== ui::DIALOG_BUTTON_OK
&& ok_button_
) {
159 new_default_button
= ok_button_
;
160 } else if (default_button
== ui::DIALOG_BUTTON_CANCEL
&& cancel_button_
) {
161 new_default_button
= cancel_button_
;
164 if (default_button_
&& default_button_
!= new_default_button
)
165 default_button_
->SetIsDefault(false);
166 default_button_
= new_default_button
;
167 if (default_button_
&& !default_button_
->is_default())
168 default_button_
->SetIsDefault(true);
171 void DialogClientView::OnDidChangeFocus(View
* focused_before
,
175 ////////////////////////////////////////////////////////////////////////////////
176 // DialogClientView, View overrides:
178 gfx::Size
DialogClientView::GetPreferredSize() const {
179 // Initialize the size to fit the buttons and extra view row.
180 int extra_view_padding
= 0;
181 if (!GetDialogDelegate()->GetExtraViewPadding(&extra_view_padding
))
182 extra_view_padding
= kRelatedButtonHSpacing
;
184 (ok_button_
? ok_button_
->GetPreferredSize().width() : 0) +
185 (cancel_button_
? cancel_button_
->GetPreferredSize().width() : 0) +
186 (cancel_button_
&& ok_button_
? kRelatedButtonHSpacing
: 0) +
187 (ShouldShow(extra_view_
) ? extra_view_
->GetPreferredSize().width() : 0) +
188 (ShouldShow(extra_view_
) && has_dialog_buttons() ?
189 extra_view_padding
: 0),
192 int buttons_height
= GetButtonsAndExtraViewRowHeight();
193 if (buttons_height
!= 0) {
194 size
.Enlarge(0, buttons_height
+ kRelatedControlVerticalSpacing
);
195 // Inset the buttons and extra view.
196 const gfx::Insets insets
= GetButtonRowInsets();
197 size
.Enlarge(insets
.width(), insets
.height());
200 // Increase the size as needed to fit the contents view.
201 // NOTE: The contents view is not inset on the top or side client view edges.
202 gfx::Size contents_size
= contents_view()->GetPreferredSize();
203 size
.Enlarge(0, contents_size
.height());
204 size
.set_width(std::max(size
.width(), contents_size
.width()));
206 // Increase the size as needed to fit the footnote view.
207 if (ShouldShow(footnote_view_
)) {
208 gfx::Size footnote_size
= footnote_view_
->GetPreferredSize();
209 if (!footnote_size
.IsEmpty())
210 size
.set_width(std::max(size
.width(), footnote_size
.width()));
212 int footnote_height
= footnote_view_
->GetHeightForWidth(size
.width());
213 size
.Enlarge(0, footnote_height
);
219 void DialogClientView::Layout() {
220 gfx::Rect bounds
= GetContentsBounds();
222 // Layout the footnote view.
223 if (ShouldShow(footnote_view_
)) {
224 const int height
= footnote_view_
->GetHeightForWidth(bounds
.width());
225 footnote_view_
->SetBounds(bounds
.x(), bounds
.bottom() - height
,
226 bounds
.width(), height
);
228 bounds
.Inset(0, 0, 0, height
);
231 // Layout the row containing the buttons and the extra view.
232 if (has_dialog_buttons() || ShouldShow(extra_view_
)) {
233 bounds
.Inset(GetButtonRowInsets());
234 const int height
= GetButtonsAndExtraViewRowHeight();
235 gfx::Rect
row_bounds(bounds
.x(), bounds
.bottom() - height
,
236 bounds
.width(), height
);
237 if (kIsOkButtonOnLeftSide
) {
238 LayoutButton(cancel_button_
, &row_bounds
);
239 LayoutButton(ok_button_
, &row_bounds
);
241 LayoutButton(ok_button_
, &row_bounds
);
242 LayoutButton(cancel_button_
, &row_bounds
);
245 int custom_padding
= 0;
246 if (has_dialog_buttons() &&
247 GetDialogDelegate()->GetExtraViewPadding(&custom_padding
)) {
248 // The call to LayoutButton() will already have accounted for some of
250 custom_padding
-= kRelatedButtonHSpacing
;
251 row_bounds
.set_width(row_bounds
.width() - custom_padding
);
253 row_bounds
.set_width(std::min(row_bounds
.width(),
254 extra_view_
->GetPreferredSize().width()));
255 extra_view_
->SetBoundsRect(row_bounds
);
259 bounds
.Inset(0, 0, 0, height
+ kRelatedControlVerticalSpacing
);
262 // Layout the contents view to the top and side edges of the contents bounds.
263 // NOTE: The local insets do not apply to the contents view sides or top.
264 const gfx::Rect contents_bounds
= GetContentsBounds();
265 contents_view()->SetBounds(contents_bounds
.x(), contents_bounds
.y(),
266 contents_bounds
.width(), bounds
.bottom() - contents_bounds
.y());
269 bool DialogClientView::AcceleratorPressed(const ui::Accelerator
& accelerator
) {
270 DCHECK_EQ(accelerator
.key_code(), ui::VKEY_ESCAPE
);
275 void DialogClientView::ViewHierarchyChanged(
276 const ViewHierarchyChangedDetails
& details
) {
277 ClientView::ViewHierarchyChanged(details
);
278 if (details
.is_add
&& details
.child
== this) {
279 focus_manager_
= GetFocusManager();
281 GetFocusManager()->AddFocusChangeListener(this);
283 UpdateDialogButtons();
285 CreateFootnoteView();
286 } else if (!details
.is_add
&& details
.child
== this) {
288 focus_manager_
->RemoveFocusChangeListener(this);
289 focus_manager_
= NULL
;
290 } else if (!details
.is_add
) {
291 if (details
.child
== default_button_
)
292 default_button_
= NULL
;
293 if (details
.child
== ok_button_
)
295 if (details
.child
== cancel_button_
)
296 cancel_button_
= NULL
;
300 void DialogClientView::NativeViewHierarchyChanged() {
301 FocusManager
* focus_manager
= GetFocusManager();
302 if (focus_manager_
!= focus_manager
) {
304 focus_manager_
->RemoveFocusChangeListener(this);
305 focus_manager_
= focus_manager
;
307 focus_manager_
->AddFocusChangeListener(this);
311 void DialogClientView::OnNativeThemeChanged(const ui::NativeTheme
* theme
) {
312 // The old dialog style needs an explicit background color, while the new
313 // dialog style simply inherits the bubble's frame view color.
314 const DialogDelegate
* dialog
= GetDialogDelegate();
316 if (dialog
&& !dialog
->UseNewStyleForThisDialog()) {
317 set_background(views::Background::CreateSolidBackground(GetNativeTheme()->
318 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground
)));
322 ////////////////////////////////////////////////////////////////////////////////
323 // DialogClientView, ButtonListener implementation:
325 void DialogClientView::ButtonPressed(Button
* sender
, const ui::Event
& event
) {
326 // Check for a valid delegate to avoid handling events after destruction.
327 if (!GetDialogDelegate())
330 if (sender
== ok_button_
)
332 else if (sender
== cancel_button_
)
338 ////////////////////////////////////////////////////////////////////////////////
339 // DialogClientView, protected:
341 DialogClientView::DialogClientView(View
* contents_view
)
342 : ClientView(NULL
, contents_view
),
344 cancel_button_(NULL
),
345 default_button_(NULL
),
346 focus_manager_(NULL
),
348 footnote_view_(NULL
),
349 notified_delegate_(false) {}
351 DialogDelegate
* DialogClientView::GetDialogDelegate() const {
352 return GetWidget()->widget_delegate()->AsDialogDelegate();
355 void DialogClientView::CreateExtraView() {
359 extra_view_
= GetDialogDelegate()->CreateExtraView();
361 extra_view_
->SetGroup(kButtonGroup
);
362 AddChildView(extra_view_
);
366 void DialogClientView::CreateFootnoteView() {
370 footnote_view_
= GetDialogDelegate()->CreateFootnoteView();
372 AddChildView(footnote_view_
);
375 void DialogClientView::ChildPreferredSizeChanged(View
* child
) {
376 if (child
== footnote_view_
|| child
== extra_view_
)
380 void DialogClientView::ChildVisibilityChanged(View
* child
) {
381 ChildPreferredSizeChanged(child
);
384 ////////////////////////////////////////////////////////////////////////////////
385 // DialogClientView, private:
387 LabelButton
* DialogClientView::CreateDialogButton(ui::DialogButton type
) {
388 const base::string16 title
= GetDialogDelegate()->GetDialogButtonLabel(type
);
389 LabelButton
* button
= NULL
;
390 if (GetDialogDelegate()->UseNewStyleForThisDialog() &&
391 GetDialogDelegate()->GetDefaultDialogButton() == type
&&
392 GetDialogDelegate()->ShouldDefaultButtonBeBlue()) {
393 button
= new BlueButton(this, title
);
395 button
= new LabelButton(this, title
);
396 button
->SetStyle(Button::STYLE_BUTTON
);
398 button
->SetFocusable(true);
400 const int kDialogMinButtonWidth
= 75;
401 button
->SetMinSize(gfx::Size(kDialogMinButtonWidth
, 0));
402 button
->SetGroup(kButtonGroup
);
406 void DialogClientView::UpdateButton(LabelButton
* button
,
407 ui::DialogButton type
) {
408 DialogDelegate
* dialog
= GetDialogDelegate();
409 button
->SetText(dialog
->GetDialogButtonLabel(type
));
410 button
->SetEnabled(dialog
->IsDialogButtonEnabled(type
));
412 if (type
== dialog
->GetDefaultDialogButton()) {
413 default_button_
= button
;
414 button
->SetIsDefault(true);
418 int DialogClientView::GetButtonsAndExtraViewRowHeight() const {
419 int extra_view_height
= ShouldShow(extra_view_
) ?
420 extra_view_
->GetPreferredSize().height() : 0;
421 int buttons_height
= std::max(
422 ok_button_
? ok_button_
->GetPreferredSize().height() : 0,
423 cancel_button_
? cancel_button_
->GetPreferredSize().height() : 0);
424 return std::max(extra_view_height
, buttons_height
);
427 gfx::Insets
DialogClientView::GetButtonRowInsets() const {
428 // NOTE: The insets only apply to the buttons, extra view, and footnote view.
429 return GetButtonsAndExtraViewRowHeight() == 0 ? gfx::Insets() :
430 gfx::Insets(0, kButtonHEdgeMarginNew
,
431 kButtonVEdgeMarginNew
, kButtonHEdgeMarginNew
);
434 void DialogClientView::Close() {
435 GetWidget()->Close();
436 GetDialogDelegate()->OnClosed();