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.
181 (ok_button_
? ok_button_
->GetPreferredSize().width() : 0) +
182 (cancel_button_
? cancel_button_
->GetPreferredSize().width() : 0) +
183 (cancel_button_
&& ok_button_
? kRelatedButtonHSpacing
: 0) +
184 (ShouldShow(extra_view_
) ? extra_view_
->GetPreferredSize().width() : 0) +
185 (ShouldShow(extra_view_
) && has_dialog_buttons() ?
186 kRelatedButtonHSpacing
: 0),
189 int buttons_height
= GetButtonsAndExtraViewRowHeight();
190 if (buttons_height
!= 0) {
191 size
.Enlarge(0, buttons_height
+ kRelatedControlVerticalSpacing
);
192 // Inset the buttons and extra view.
193 const gfx::Insets insets
= GetButtonRowInsets();
194 size
.Enlarge(insets
.width(), insets
.height());
197 // Increase the size as needed to fit the contents view.
198 // NOTE: The contents view is not inset on the top or side client view edges.
199 gfx::Size contents_size
= contents_view()->GetPreferredSize();
200 size
.Enlarge(0, contents_size
.height());
201 size
.set_width(std::max(size
.width(), contents_size
.width()));
203 // Increase the size as needed to fit the footnote view.
204 if (ShouldShow(footnote_view_
)) {
205 gfx::Size footnote_size
= footnote_view_
->GetPreferredSize();
206 if (!footnote_size
.IsEmpty())
207 size
.set_width(std::max(size
.width(), footnote_size
.width()));
209 int footnote_height
= footnote_view_
->GetHeightForWidth(size
.width());
210 size
.Enlarge(0, footnote_height
);
216 void DialogClientView::Layout() {
217 gfx::Rect bounds
= GetContentsBounds();
219 // Layout the footnote view.
220 if (ShouldShow(footnote_view_
)) {
221 const int height
= footnote_view_
->GetHeightForWidth(bounds
.width());
222 footnote_view_
->SetBounds(bounds
.x(), bounds
.bottom() - height
,
223 bounds
.width(), height
);
225 bounds
.Inset(0, 0, 0, height
);
228 // Layout the row containing the buttons and the extra view.
229 if (has_dialog_buttons() || ShouldShow(extra_view_
)) {
230 bounds
.Inset(GetButtonRowInsets());
231 const int height
= GetButtonsAndExtraViewRowHeight();
232 gfx::Rect
row_bounds(bounds
.x(), bounds
.bottom() - height
,
233 bounds
.width(), height
);
234 if (kIsOkButtonOnLeftSide
) {
235 LayoutButton(cancel_button_
, &row_bounds
);
236 LayoutButton(ok_button_
, &row_bounds
);
238 LayoutButton(ok_button_
, &row_bounds
);
239 LayoutButton(cancel_button_
, &row_bounds
);
242 row_bounds
.set_width(std::min(row_bounds
.width(),
243 extra_view_
->GetPreferredSize().width()));
244 extra_view_
->SetBoundsRect(row_bounds
);
248 bounds
.Inset(0, 0, 0, height
+ kRelatedControlVerticalSpacing
);
251 // Layout the contents view to the top and side edges of the contents bounds.
252 // NOTE: The local insets do not apply to the contents view sides or top.
253 const gfx::Rect contents_bounds
= GetContentsBounds();
254 contents_view()->SetBounds(contents_bounds
.x(), contents_bounds
.y(),
255 contents_bounds
.width(), bounds
.bottom() - contents_bounds
.y());
258 bool DialogClientView::AcceleratorPressed(const ui::Accelerator
& accelerator
) {
259 DCHECK_EQ(accelerator
.key_code(), ui::VKEY_ESCAPE
);
264 void DialogClientView::ViewHierarchyChanged(
265 const ViewHierarchyChangedDetails
& details
) {
266 ClientView::ViewHierarchyChanged(details
);
267 if (details
.is_add
&& details
.child
== this) {
268 focus_manager_
= GetFocusManager();
270 GetFocusManager()->AddFocusChangeListener(this);
272 UpdateDialogButtons();
274 CreateFootnoteView();
275 } else if (!details
.is_add
&& details
.child
== this) {
277 focus_manager_
->RemoveFocusChangeListener(this);
278 focus_manager_
= NULL
;
279 } else if (!details
.is_add
) {
280 if (details
.child
== default_button_
)
281 default_button_
= NULL
;
282 if (details
.child
== ok_button_
)
284 if (details
.child
== cancel_button_
)
285 cancel_button_
= NULL
;
289 void DialogClientView::NativeViewHierarchyChanged() {
290 FocusManager
* focus_manager
= GetFocusManager();
291 if (focus_manager_
!= focus_manager
) {
293 focus_manager_
->RemoveFocusChangeListener(this);
294 focus_manager_
= focus_manager
;
296 focus_manager_
->AddFocusChangeListener(this);
300 void DialogClientView::OnNativeThemeChanged(const ui::NativeTheme
* theme
) {
301 // The old dialog style needs an explicit background color, while the new
302 // dialog style simply inherits the bubble's frame view color.
303 const DialogDelegate
* dialog
= GetDialogDelegate();
305 if (dialog
&& !dialog
->UseNewStyleForThisDialog()) {
306 set_background(views::Background::CreateSolidBackground(GetNativeTheme()->
307 GetSystemColor(ui::NativeTheme::kColorId_DialogBackground
)));
311 ////////////////////////////////////////////////////////////////////////////////
312 // DialogClientView, ButtonListener implementation:
314 void DialogClientView::ButtonPressed(Button
* sender
, const ui::Event
& event
) {
315 // Check for a valid delegate to avoid handling events after destruction.
316 if (!GetDialogDelegate())
319 if (sender
== ok_button_
)
321 else if (sender
== cancel_button_
)
327 ////////////////////////////////////////////////////////////////////////////////
328 // DialogClientView, protected:
330 DialogClientView::DialogClientView(View
* contents_view
)
331 : ClientView(NULL
, contents_view
),
333 cancel_button_(NULL
),
334 default_button_(NULL
),
335 focus_manager_(NULL
),
337 footnote_view_(NULL
),
338 notified_delegate_(false) {}
340 DialogDelegate
* DialogClientView::GetDialogDelegate() const {
341 return GetWidget()->widget_delegate()->AsDialogDelegate();
344 void DialogClientView::CreateExtraView() {
348 extra_view_
= GetDialogDelegate()->CreateExtraView();
350 extra_view_
->SetGroup(kButtonGroup
);
351 AddChildView(extra_view_
);
355 void DialogClientView::CreateFootnoteView() {
359 footnote_view_
= GetDialogDelegate()->CreateFootnoteView();
361 AddChildView(footnote_view_
);
364 void DialogClientView::ChildPreferredSizeChanged(View
* child
) {
365 if (child
== footnote_view_
|| child
== extra_view_
)
369 void DialogClientView::ChildVisibilityChanged(View
* child
) {
370 ChildPreferredSizeChanged(child
);
373 ////////////////////////////////////////////////////////////////////////////////
374 // DialogClientView, private:
376 LabelButton
* DialogClientView::CreateDialogButton(ui::DialogButton type
) {
377 const base::string16 title
= GetDialogDelegate()->GetDialogButtonLabel(type
);
378 LabelButton
* button
= NULL
;
379 if (GetDialogDelegate()->UseNewStyleForThisDialog() &&
380 GetDialogDelegate()->GetDefaultDialogButton() == type
&&
381 GetDialogDelegate()->ShouldDefaultButtonBeBlue()) {
382 button
= new BlueButton(this, title
);
384 button
= new LabelButton(this, title
);
385 button
->SetStyle(Button::STYLE_BUTTON
);
387 button
->SetFocusable(true);
389 const int kDialogMinButtonWidth
= 75;
390 button
->SetMinSize(gfx::Size(kDialogMinButtonWidth
, 0));
391 button
->SetGroup(kButtonGroup
);
395 void DialogClientView::UpdateButton(LabelButton
* button
,
396 ui::DialogButton type
) {
397 DialogDelegate
* dialog
= GetDialogDelegate();
398 button
->SetText(dialog
->GetDialogButtonLabel(type
));
399 button
->SetEnabled(dialog
->IsDialogButtonEnabled(type
));
401 if (type
== dialog
->GetDefaultDialogButton()) {
402 default_button_
= button
;
403 button
->SetIsDefault(true);
407 int DialogClientView::GetButtonsAndExtraViewRowHeight() const {
408 int extra_view_height
= ShouldShow(extra_view_
) ?
409 extra_view_
->GetPreferredSize().height() : 0;
410 int buttons_height
= std::max(
411 ok_button_
? ok_button_
->GetPreferredSize().height() : 0,
412 cancel_button_
? cancel_button_
->GetPreferredSize().height() : 0);
413 return std::max(extra_view_height
, buttons_height
);
416 gfx::Insets
DialogClientView::GetButtonRowInsets() const {
417 // NOTE: The insets only apply to the buttons, extra view, and footnote view.
418 return GetButtonsAndExtraViewRowHeight() == 0 ? gfx::Insets() :
419 gfx::Insets(0, kButtonHEdgeMarginNew
,
420 kButtonVEdgeMarginNew
, kButtonHEdgeMarginNew
);
423 void DialogClientView::Close() {
424 GetWidget()->Close();
425 GetDialogDelegate()->OnClosed();