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/label_button.h"
21 #include "ui/views/controls/image_view.h"
22 #include "ui/views/controls/label.h"
23 #include "ui/views/layout/grid_layout.h"
24 #include "ui/views/layout/layout_constants.h"
29 TAG_ACCEPT_BUTTON
= 1,
33 const int kMaxBubbleViewWidth
= 262;
35 // The vertical inset of the wrench bubble anchor from the wrench menu button.
36 const int kAnchorVerticalInset
= 5;
38 const int kBubblePadding
= 19;
40 // Spacing between bubble text and buttons.
41 const int kLabelToButtonVerticalSpacing
= 14;
45 // GlobalErrorBubbleViewBase ---------------------------------------------------
48 GlobalErrorBubbleViewBase
* GlobalErrorBubbleViewBase::ShowStandardBubbleView(
50 const base::WeakPtr
<GlobalErrorWithStandardBubble
>& error
) {
51 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
52 views::View
* wrench_button
= browser_view
->toolbar()->app_menu();
53 GlobalErrorBubbleView
* bubble_view
=
54 new GlobalErrorBubbleView(wrench_button
,
55 views::BubbleBorder::TOP_RIGHT
,
58 views::BubbleDelegateView::CreateBubble(bubble_view
);
59 bubble_view
->GetWidget()->Show();
63 // GlobalErrorBubbleView -------------------------------------------------------
65 GlobalErrorBubbleView::GlobalErrorBubbleView(
66 views::View
* anchor_view
,
67 views::BubbleBorder::Arrow arrow
,
69 const base::WeakPtr
<GlobalErrorWithStandardBubble
>& error
)
70 : BubbleDelegateView(anchor_view
, arrow
),
73 // Set content margins to left-align the bubble text with the title.
74 // BubbleFrameView adds enough padding below title, no top padding needed.
75 set_margins(gfx::Insets(0, kBubblePadding
, kBubblePadding
, kBubblePadding
));
77 // Compensate for built-in vertical padding in the anchor view's image.
78 set_anchor_view_insets(
79 gfx::Insets(kAnchorVerticalInset
, 0, kAnchorVerticalInset
, 0));
81 std::vector
<base::string16
> message_strings(error_
->GetBubbleViewMessages());
82 std::vector
<views::Label
*> message_labels
;
83 for (size_t i
= 0; i
< message_strings
.size(); ++i
) {
84 views::Label
* message_label
= new views::Label(message_strings
[i
]);
85 message_label
->SetMultiLine(true);
86 message_label
->SizeToFit(kMaxBubbleViewWidth
);
87 message_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
88 message_labels
.push_back(message_label
);
91 base::string16
accept_string(error_
->GetBubbleViewAcceptButtonLabel());
92 scoped_ptr
<views::LabelButton
> accept_button(
93 new views::LabelButton(this, accept_string
));
94 accept_button
->SetStyle(views::Button::STYLE_BUTTON
);
95 accept_button
->SetIsDefault(true);
96 accept_button
->set_tag(TAG_ACCEPT_BUTTON
);
97 if (error_
->ShouldAddElevationIconToAcceptButton())
98 elevation_icon_setter_
.reset(
99 new ElevationIconSetter(
101 base::Bind(&GlobalErrorBubbleView::SizeToContents
,
102 base::Unretained(this))));
104 base::string16
cancel_string(error_
->GetBubbleViewCancelButtonLabel());
105 scoped_ptr
<views::LabelButton
> cancel_button
;
106 if (!cancel_string
.empty()) {
107 cancel_button
.reset(new views::LabelButton(this, cancel_string
));
108 cancel_button
->SetStyle(views::Button::STYLE_BUTTON
);
109 cancel_button
->set_tag(TAG_CANCEL_BUTTON
);
112 views::GridLayout
* layout
= new views::GridLayout(this);
113 SetLayoutManager(layout
);
115 // First row, message labels.
116 views::ColumnSet
* cs
= layout
->AddColumnSet(0);
117 cs
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
,
118 1, views::GridLayout::USE_PREF
, 0, 0);
120 // Second row, accept and cancel button.
121 cs
= layout
->AddColumnSet(1);
122 cs
->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing
);
123 cs
->AddColumn(views::GridLayout::TRAILING
, views::GridLayout::LEADING
,
124 0, views::GridLayout::USE_PREF
, 0, 0);
125 if (cancel_button
.get()) {
126 cs
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
127 cs
->AddColumn(views::GridLayout::TRAILING
, views::GridLayout::LEADING
,
128 0, views::GridLayout::USE_PREF
, 0, 0);
131 for (size_t i
= 0; i
< message_labels
.size(); ++i
) {
132 layout
->StartRow(1, 0);
133 layout
->AddView(message_labels
[i
]);
134 if (i
< message_labels
.size() - 1)
135 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
137 layout
->AddPaddingRow(0, kLabelToButtonVerticalSpacing
);
139 layout
->StartRow(0, 1);
140 layout
->AddView(accept_button
.release());
141 if (cancel_button
.get())
142 layout
->AddView(cancel_button
.release());
144 // Adjust the message label size in case buttons are too long.
145 for (size_t i
= 0; i
< message_labels
.size(); ++i
)
146 message_labels
[i
]->SizeToFit(layout
->GetPreferredSize(this).width());
148 // These bubbles show at times where activation is sporadic (like at startup,
149 // or a new window opening). Make sure the bubble doesn't disappear before the
150 // user sees it, if the bubble needs to be acknowledged.
151 set_close_on_deactivate(error_
->ShouldCloseOnDeactivate());
154 GlobalErrorBubbleView::~GlobalErrorBubbleView() {
155 // |elevation_icon_setter_| references |accept_button_|, so make sure it is
156 // destroyed before |accept_button_|.
157 elevation_icon_setter_
.reset();
160 void GlobalErrorBubbleView::ButtonPressed(views::Button
* sender
,
161 const ui::Event
& event
) {
163 if (sender
->tag() == TAG_ACCEPT_BUTTON
)
164 error_
->BubbleViewAcceptButtonPressed(browser_
);
165 else if (sender
->tag() == TAG_CANCEL_BUTTON
)
166 error_
->BubbleViewCancelButtonPressed(browser_
);
170 GetWidget()->Close();
173 base::string16
GlobalErrorBubbleView::GetWindowTitle() const {
174 return error_
->GetBubbleViewTitle();
177 gfx::ImageSkia
GlobalErrorBubbleView::GetWindowIcon() {
178 gfx::Image image
= error_
->GetBubbleViewIcon();
179 DCHECK(!image
.IsEmpty());
180 return *image
.ToImageSkia();
183 bool GlobalErrorBubbleView::ShouldShowWindowIcon() const {
187 void GlobalErrorBubbleView::WindowClosing() {
189 error_
->BubbleViewDidClose(browser_
);
192 bool GlobalErrorBubbleView::ShouldShowCloseButton() const {
193 return error_
&& error_
->ShouldShowCloseButton();
196 void GlobalErrorBubbleView::CloseBubbleView() {
197 GetWidget()->Close();