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"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/ui/global_error/global_error.h"
9 #include "chrome/browser/ui/global_error/global_error_service.h"
10 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
11 #include "chrome/browser/ui/views/elevation_icon_setter.h"
12 #include "chrome/browser/ui/views/frame/browser_view.h"
13 #include "chrome/browser/ui/views/toolbar/toolbar_view.h"
14 #include "chrome/browser/ui/views/toolbar/wrench_toolbar_button.h"
15 #include "ui/base/resource/resource_bundle.h"
16 #include "ui/gfx/image/image.h"
17 #include "ui/views/controls/button/label_button.h"
18 #include "ui/views/controls/image_view.h"
19 #include "ui/views/controls/label.h"
20 #include "ui/views/layout/grid_layout.h"
21 #include "ui/views/layout/layout_constants.h"
26 TAG_ACCEPT_BUTTON
= 1,
30 const int kMaxBubbleViewWidth
= 262;
32 // The horizontal padding between the title and the icon.
33 const int kTitleHorizontalPadding
= 5;
35 // The vertical inset of the wrench bubble anchor from the wrench menu button.
36 const int kAnchorVerticalInset
= 5;
38 const int kBubblePadding
= 6;
42 // GlobalErrorBubbleViewBase ---------------------------------------------------
45 GlobalErrorBubbleViewBase
* GlobalErrorBubbleViewBase::ShowStandardBubbleView(
47 const base::WeakPtr
<GlobalErrorWithStandardBubble
>& error
) {
48 BrowserView
* browser_view
= BrowserView::GetBrowserViewForBrowser(browser
);
49 views::View
* wrench_button
= browser_view
->toolbar()->app_menu();
50 GlobalErrorBubbleView
* bubble_view
=
51 new GlobalErrorBubbleView(wrench_button
,
52 views::BubbleBorder::TOP_RIGHT
,
55 views::BubbleDelegateView::CreateBubble(bubble_view
);
56 bubble_view
->GetWidget()->Show();
60 // GlobalErrorBubbleView -------------------------------------------------------
62 GlobalErrorBubbleView::GlobalErrorBubbleView(
63 views::View
* anchor_view
,
64 views::BubbleBorder::Arrow arrow
,
66 const base::WeakPtr
<GlobalErrorWithStandardBubble
>& error
)
67 : BubbleDelegateView(anchor_view
, arrow
),
70 // Compensate for built-in vertical padding in the anchor view's image.
71 set_anchor_view_insets(
72 gfx::Insets(kAnchorVerticalInset
, 0, kAnchorVerticalInset
, 0));
74 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
75 gfx::Image image
= error_
->GetBubbleViewIcon();
76 CHECK(!image
.IsEmpty());
77 scoped_ptr
<views::ImageView
> image_view(new views::ImageView());
78 image_view
->SetImage(image
.ToImageSkia());
80 base::string16
title_string(error_
->GetBubbleViewTitle());
81 scoped_ptr
<views::Label
> title_label(new views::Label(title_string
));
82 title_label
->SetMultiLine(true);
83 title_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
84 title_label
->SetFontList(rb
.GetFontList(ui::ResourceBundle::MediumFont
));
86 std::vector
<base::string16
> message_strings(error_
->GetBubbleViewMessages());
87 std::vector
<views::Label
*> message_labels
;
88 for (size_t i
= 0; i
< message_strings
.size(); ++i
) {
89 views::Label
* message_label
= new views::Label(message_strings
[i
]);
90 message_label
->SetMultiLine(true);
91 message_label
->SizeToFit(kMaxBubbleViewWidth
);
92 message_label
->SetHorizontalAlignment(gfx::ALIGN_LEFT
);
93 message_labels
.push_back(message_label
);
96 base::string16
accept_string(error_
->GetBubbleViewAcceptButtonLabel());
97 scoped_ptr
<views::LabelButton
> accept_button(
98 new views::LabelButton(this, accept_string
));
99 accept_button
->SetStyle(views::Button::STYLE_BUTTON
);
100 accept_button
->SetIsDefault(true);
101 accept_button
->set_tag(TAG_ACCEPT_BUTTON
);
102 if (error_
->ShouldAddElevationIconToAcceptButton())
103 elevation_icon_setter_
.reset(new ElevationIconSetter(accept_button
.get()));
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
);
115 layout
->SetInsets(kBubblePadding
, kBubblePadding
,
116 kBubblePadding
, kBubblePadding
);
118 // Top row, icon and title.
119 views::ColumnSet
* cs
= layout
->AddColumnSet(0);
120 cs
->AddColumn(views::GridLayout::LEADING
, views::GridLayout::LEADING
,
121 0, views::GridLayout::USE_PREF
, 0, 0);
122 cs
->AddPaddingColumn(0, kTitleHorizontalPadding
);
123 cs
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
,
124 1, views::GridLayout::USE_PREF
, 0, 0);
126 // Middle rows, message labels.
127 cs
= layout
->AddColumnSet(1);
128 cs
->AddColumn(views::GridLayout::FILL
, views::GridLayout::FILL
,
129 1, views::GridLayout::USE_PREF
, 0, 0);
131 // Bottom row, accept and cancel button.
132 cs
= layout
->AddColumnSet(2);
133 cs
->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing
);
134 cs
->AddColumn(views::GridLayout::TRAILING
, views::GridLayout::LEADING
,
135 0, views::GridLayout::USE_PREF
, 0, 0);
136 if (cancel_button
.get()) {
137 cs
->AddPaddingColumn(0, views::kRelatedButtonHSpacing
);
138 cs
->AddColumn(views::GridLayout::TRAILING
, views::GridLayout::LEADING
,
139 0, views::GridLayout::USE_PREF
, 0, 0);
142 layout
->StartRow(1, 0);
143 layout
->AddView(image_view
.release());
144 layout
->AddView(title_label
.release());
145 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
147 for (size_t i
= 0; i
< message_labels
.size(); ++i
) {
148 layout
->StartRow(1, 1);
149 layout
->AddView(message_labels
[i
]);
150 if (i
< message_labels
.size() - 1)
151 layout
->AddPaddingRow(0, views::kRelatedControlVerticalSpacing
);
153 layout
->AddPaddingRow(0, views::kLabelToControlVerticalSpacing
);
155 layout
->StartRow(0, 2);
156 layout
->AddView(accept_button
.release());
157 if (cancel_button
.get())
158 layout
->AddView(cancel_button
.release());
160 // Adjust the message label size in case buttons are too long.
161 for (size_t i
= 0; i
< message_labels
.size(); ++i
)
162 message_labels
[i
]->SizeToFit(layout
->GetPreferredSize(this).width());
164 // These bubbles show at times where activation is sporadic (like at startup,
165 // or a new window opening). Make sure the bubble doesn't disappear before the
166 // user sees it, if the bubble needs to be acknowledged.
167 set_close_on_deactivate(error_
->ShouldCloseOnDeactivate());
170 GlobalErrorBubbleView::~GlobalErrorBubbleView() {
171 // |elevation_icon_setter_| references |accept_button_|, so make sure it is
172 // destroyed before |accept_button_|.
173 elevation_icon_setter_
.reset();
176 void GlobalErrorBubbleView::ButtonPressed(views::Button
* sender
,
177 const ui::Event
& event
) {
179 if (sender
->tag() == TAG_ACCEPT_BUTTON
)
180 error_
->BubbleViewAcceptButtonPressed(browser_
);
181 else if (sender
->tag() == TAG_CANCEL_BUTTON
)
182 error_
->BubbleViewCancelButtonPressed(browser_
);
186 GetWidget()->Close();
189 void GlobalErrorBubbleView::WindowClosing() {
191 error_
->BubbleViewDidClose(browser_
);
194 void GlobalErrorBubbleView::CloseBubbleView() {
195 GetWidget()->Close();