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 "chrome/browser/ui/views/global_error_bubble_view.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/ui/global_error/global_error.h"
11 #include "chrome/browser/ui/global_error/global_error_service.h"
12 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
13 #include "chrome/browser/ui/views/elevation_icon_setter.h"
14 #include "chrome/browser/ui/views/frame/browser_view.h"
15 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
16 #include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h"
17 #include "ui/base/resource/resource_bundle.h"
18 #include "ui/gfx/image/image.h"
19 #include "ui/views/bubble/bubble_frame_view.h"
20 #include "ui/views/controls/button/blue_button.h"
21 #include "ui/views/controls/button/label_button.h"
22 #include "ui/views/controls/image_view.h"
23 #include "ui/views/controls/label.h"
24 #include "ui/views/layout/grid_layout.h"
25 #include "ui/views/layout/layout_constants.h"
30 TAG_ACCEPT_BUTTON
= 1,
34 const int kMaxBubbleViewWidth
= 262;
36 // The vertical inset of the wrench bubble anchor from the wrench menu button.
37 const int kAnchorVerticalInset
= 5;
39 const int kBubblePadding
= 19;
41 // Spacing between bubble text and buttons.
42 const int kLabelToButtonVerticalSpacing
= 14;
46 // GlobalErrorBubbleViewBase ---------------------------------------------------
49 GlobalErrorBubbleViewBase
* GlobalErrorBubbleViewBase::ShowStandardBubbleView(
51 const base::WeakPtr
<GlobalErrorWithStandardBubble
>& error
) {
52 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
53 views::View
* wrench_button
= browser_view
->toolbar()->app_menu();
54 GlobalErrorBubbleView
* bubble_view
=
55 new GlobalErrorBubbleView(wrench_button
,
56 views::BubbleBorder::TOP_RIGHT
,
59 views::BubbleDelegateView::CreateBubble(bubble_view
);
60 bubble_view
->GetWidget()->Show();
64 // GlobalErrorBubbleView -------------------------------------------------------
66 GlobalErrorBubbleView::GlobalErrorBubbleView(
67 views::View
* anchor_view
,
68 views::BubbleBorder::Arrow arrow
,
70 const base::WeakPtr
<GlobalErrorWithStandardBubble
>& error
)
71 : BubbleDelegateView(anchor_view
, arrow
),
74 // Set content margins to left-align the bubble text with the title.
75 // BubbleFrameView adds enough padding below title, no top padding needed.
76 set_margins(gfx::Insets(0, kBubblePadding
, kBubblePadding
, kBubblePadding
));
78 // Compensate for built-in vertical padding in the anchor view's image.
79 set_anchor_view_insets(
80 gfx::Insets(kAnchorVerticalInset
, 0, kAnchorVerticalInset
, 0));
82 std::vector
<base::string16
> message_strings(error_
->GetBubbleViewMessages());
83 std::vector
<views::Label
*> message_labels
;
84 for (size_t i
= 0; i
< message_strings
.size(); ++i
) {
85 views::Label
* message_label
= new views::Label(message_strings
[i
]);
86 message_label
->SetMultiLine(true);
87 message_label
->SizeToFit(kMaxBubbleViewWidth
);
88 message_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
89 message_labels
.push_back(message_label
);
92 base::string16
accept_string(error_
->GetBubbleViewAcceptButtonLabel());
93 scoped_ptr
<views::BlueButton
> accept_button(
94 new views::BlueButton(this, accept_string
));
95 accept_button
->SetIsDefault(true);
96 accept_button
->set_tag(TAG_ACCEPT_BUTTON
);
98 if (error_
->ShouldAddElevationIconToAcceptButton())
99 elevation_icon_setter_
.reset(
100 new ElevationIconSetter(
102 base::Bind(&GlobalErrorBubbleView::SizeToContents
,
103 base::Unretained(this))));
105 base::string16
cancel_string(error_
->GetBubbleViewCancelButtonLabel());
106 scoped_ptr
<views::LabelButton
> cancel_button
;
107 if (!cancel_string
.empty()) {
108 cancel_button
.reset(new views::LabelButton(this, cancel_string
));
109 cancel_button
->SetStyle(views::Button::STYLE_BUTTON
);
110 cancel_button
->set_tag(TAG_CANCEL_BUTTON
);
113 views::GridLayout
* layout
= new views::GridLayout(this);
114 SetLayoutManager(layout
);
116 // First row, message labels.
117 views::ColumnSet
* cs
= layout
->AddColumnSet(0);
118 cs
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
,
119 1, views::GridLayout::USE_PREF
, 0, 0);
121 // Second row, accept and cancel button.
122 cs
= layout
->AddColumnSet(1);
123 cs
->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing
);
124 cs
->AddColumn(views::GridLayout::TRAILING
, views::GridLayout::LEADING
,
125 0, views::GridLayout::USE_PREF
, 0, 0);
126 if (cancel_button
.get()) {
127 cs
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
128 cs
->AddColumn(views::GridLayout::TRAILING
, views::GridLayout::LEADING
,
129 0, views::GridLayout::USE_PREF
, 0, 0);
132 for (size_t i
= 0; i
< message_labels
.size(); ++i
) {
133 layout
->StartRow(1, 0);
134 layout
->AddView(message_labels
[i
]);
135 if (i
< message_labels
.size() - 1)
136 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
138 layout
->AddPaddingRow(0, kLabelToButtonVerticalSpacing
);
140 layout
->StartRow(0, 1);
141 layout
->AddView(accept_button
.release());
142 if (cancel_button
.get())
143 layout
->AddView(cancel_button
.release());
145 // Adjust the message label size in case buttons are too long.
146 for (size_t i
= 0; i
< message_labels
.size(); ++i
)
147 message_labels
[i
]->SizeToFit(layout
->GetPreferredSize(this).width());
149 // These bubbles show at times where activation is sporadic (like at startup,
150 // or a new window opening). Make sure the bubble doesn't disappear before the
151 // user sees it, if the bubble needs to be acknowledged.
152 set_close_on_deactivate(error_
->ShouldCloseOnDeactivate());
155 GlobalErrorBubbleView::~GlobalErrorBubbleView() {
156 // |elevation_icon_setter_| references |accept_button_|, so make sure it is
157 // destroyed before |accept_button_|.
158 elevation_icon_setter_
.reset();
161 void GlobalErrorBubbleView::ButtonPressed(views::Button
* sender
,
162 const ui::Event
& event
) {
164 if (sender
->tag() == TAG_ACCEPT_BUTTON
)
165 error_
->BubbleViewAcceptButtonPressed(browser_
);
166 else if (sender
->tag() == TAG_CANCEL_BUTTON
)
167 error_
->BubbleViewCancelButtonPressed(browser_
);
171 GetWidget()->Close();
174 base::string16
GlobalErrorBubbleView::GetWindowTitle() const {
175 return error_
->GetBubbleViewTitle();
178 gfx::ImageSkia
GlobalErrorBubbleView::GetWindowIcon() {
179 gfx::Image image
= error_
->GetBubbleViewIcon();
180 DCHECK(!image
.IsEmpty());
181 return *image
.ToImageSkia();
184 bool GlobalErrorBubbleView::ShouldShowWindowIcon() const {
188 void GlobalErrorBubbleView::WindowClosing() {
190 error_
->BubbleViewDidClose(browser_
);
193 bool GlobalErrorBubbleView::ShouldShowCloseButton() const {
194 return error_
&& error_
->ShouldShowCloseButton();
197 void GlobalErrorBubbleView::CloseBubbleView() {
198 GetWidget()->Close();