OLD | NEW |
| (Empty) |
1 // Copyright 2014 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. | |
4 | |
5 #include "chrome/browser/ui/views/profiles/profile_reset_bubble_view.h" | |
6 | |
7 #include "chrome/app/chrome_command_ids.h" | |
8 #include "chrome/browser/profile_resetter/profile_reset_global_error.h" | |
9 #include "chrome/browser/profile_resetter/resettable_settings_snapshot.h" | |
10 #include "chrome/browser/ui/global_error/global_error_service.h" | |
11 #include "chrome/browser/ui/global_error/global_error_service_factory.h" | |
12 #include "chrome/browser/ui/profile_reset_bubble.h" | |
13 #include "chrome/browser/ui/views/frame/browser_view.h" | |
14 #include "chrome/browser/ui/views/toolbar/toolbar_view.h" | |
15 #include "chrome/common/url_constants.h" | |
16 #include "components/google/core/browser/google_util.h" | |
17 #include "content/public/browser/page_navigator.h" | |
18 #include "content/public/browser/user_metrics.h" | |
19 #include "grit/chromium_strings.h" | |
20 #include "grit/generated_resources.h" | |
21 #include "grit/theme_resources.h" | |
22 #include "ui/base/l10n/l10n_util.h" | |
23 #include "ui/base/resource/resource_bundle.h" | |
24 #include "ui/gfx/image/image_skia_operations.h" | |
25 #include "ui/views/background.h" | |
26 #include "ui/views/controls/button/checkbox.h" | |
27 #include "ui/views/controls/button/image_button.h" | |
28 #include "ui/views/controls/button/label_button.h" | |
29 #include "ui/views/controls/label.h" | |
30 #include "ui/views/controls/link.h" | |
31 #include "ui/views/controls/scroll_view.h" | |
32 #include "ui/views/controls/separator.h" | |
33 #include "ui/views/layout/grid_layout.h" | |
34 #include "ui/views/layout/layout_constants.h" | |
35 | |
36 using views::GridLayout; | |
37 | |
38 namespace { | |
39 | |
40 // Fixed width of the column holding the description label of the bubble. | |
41 const int kWidthOfDescriptionText = 370; | |
42 | |
43 // Margins width for the top rows to compensate for the bottom panel for which | |
44 // we don't want any margin. | |
45 const int kMarginWidth = 12; | |
46 const int kMarginHeight = kMarginWidth; | |
47 | |
48 // Width of a colum in the FeedbackView. | |
49 const int kFeedbackViewColumnWidth = kWidthOfDescriptionText / 2 + kMarginWidth; | |
50 | |
51 // Width of the column used to disaplay the help button. | |
52 const int kHelpButtonColumnWidth = 30; | |
53 | |
54 // Width of the reporting checkbox column. | |
55 const int kReportingCheckboxColumnWidth = | |
56 kWidthOfDescriptionText + 2 * kMarginWidth; | |
57 | |
58 // Full width including all columns. | |
59 const int kAllColumnsWidth = | |
60 kReportingCheckboxColumnWidth + kHelpButtonColumnWidth; | |
61 | |
62 // Maximum height of the scrollable feedback view. | |
63 const int kMaxFeedbackViewHeight = 450; | |
64 | |
65 // The vertical padding between two values in the feedback view. | |
66 const int kInterFeedbackValuePadding = 4; | |
67 | |
68 // We subtract 2 to account for the natural button padding, and | |
69 // to bring the separation visually in line with the row separation | |
70 // height. | |
71 const int kButtonPadding = views::kRelatedButtonHSpacing - 2; | |
72 | |
73 // The color of the background of the sub panel to report current settings. | |
74 const SkColor kLightGrayBackgroundColor = 0xFFF5F5F5; | |
75 | |
76 // This view is used to contain the scrollable contents that are shown the user | |
77 // to expose what feedback will be sent back to Google. | |
78 class FeedbackView : public views::View { | |
79 public: | |
80 FeedbackView() {} | |
81 | |
82 // Setup the layout manager of the Feedback view using the content of the | |
83 // |feedback| ListValue which contains a list of key/value pairs stored in | |
84 // DictionaryValues. The key is to be displayed right aligned on the left, and | |
85 // the value as a left aligned multiline text on the right. | |
86 void SetupLayoutManager(const base::ListValue& feedback) { | |
87 RemoveAllChildViews(true); | |
88 set_background(views::Background::CreateSolidBackground( | |
89 kLightGrayBackgroundColor)); | |
90 | |
91 GridLayout* layout = new GridLayout(this); | |
92 SetLayoutManager(layout); | |
93 | |
94 // We only need a single column set for left/right text and middle margin. | |
95 views::ColumnSet* cs = layout->AddColumnSet(0); | |
96 cs->AddColumn(GridLayout::FILL, GridLayout::LEADING, 1, | |
97 GridLayout::FIXED, kFeedbackViewColumnWidth, 0); | |
98 cs->AddPaddingColumn(0, kMarginWidth); | |
99 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, | |
100 GridLayout::FIXED, kFeedbackViewColumnWidth, 0); | |
101 for (size_t i = 0; i < feedback.GetSize(); ++i) { | |
102 const base::DictionaryValue* dictionary = NULL; | |
103 if (!feedback.GetDictionary(i, &dictionary) || !dictionary) | |
104 continue; | |
105 | |
106 base::string16 key; | |
107 if (!dictionary->GetString("key", &key)) | |
108 continue; | |
109 | |
110 base::string16 value; | |
111 if (!dictionary->GetString("value", &value)) | |
112 continue; | |
113 | |
114 // The key is shown on the left, multi-line (required to allow wrapping in | |
115 // case the key name does not fit), and right-aligned. | |
116 views::Label* left_text_label = new views::Label(key); | |
117 left_text_label->SetMultiLine(true); | |
118 left_text_label->SetEnabledColor(SK_ColorGRAY); | |
119 left_text_label->SetHorizontalAlignment(gfx::ALIGN_RIGHT); | |
120 | |
121 // The value is shown on the right, multi-line, left-aligned. | |
122 views::Label* right_text_label = new views::Label(value); | |
123 right_text_label->SetMultiLine(true); | |
124 right_text_label->SetEnabledColor(SK_ColorDKGRAY); | |
125 right_text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
126 | |
127 layout->StartRow(0, 0); | |
128 layout->AddView(left_text_label); | |
129 layout->AddView(right_text_label); | |
130 layout->AddPaddingRow(0, kInterFeedbackValuePadding); | |
131 } | |
132 | |
133 // We need to set our size to our preferred size because our parent is a | |
134 // scroll view and doesn't know which size to set us to. Also since our | |
135 // parent scrolls, we are not bound to its size. So our size is based on the | |
136 // size computed by the our layout manager, which is what | |
137 // SizeToPreferredSize() does. | |
138 SizeToPreferredSize(); | |
139 } | |
140 | |
141 private: | |
142 DISALLOW_COPY_AND_ASSIGN(FeedbackView); | |
143 }; | |
144 | |
145 } // namespace | |
146 | |
147 // ProfileResetBubbleView --------------------------------------------------- | |
148 | |
149 // static | |
150 ProfileResetBubbleView* ProfileResetBubbleView::ShowBubble( | |
151 const base::WeakPtr<ProfileResetGlobalError>& global_error, | |
152 Browser* browser) { | |
153 views::View* anchor_view = | |
154 BrowserView::GetBrowserViewForBrowser(browser)->toolbar()->app_menu(); | |
155 ProfileResetBubbleView* reset_bubble = new ProfileResetBubbleView( | |
156 global_error, anchor_view, browser, browser->profile()); | |
157 views::BubbleDelegateView::CreateBubble(reset_bubble)->Show(); | |
158 content::RecordAction(base::UserMetricsAction("SettingsResetBubble.Show")); | |
159 return reset_bubble; | |
160 } | |
161 | |
162 ProfileResetBubbleView::~ProfileResetBubbleView() {} | |
163 | |
164 views::View* ProfileResetBubbleView::GetInitiallyFocusedView() { | |
165 return controls_.reset_button; | |
166 } | |
167 | |
168 void ProfileResetBubbleView::WindowClosing() { | |
169 if (global_error_) | |
170 global_error_->OnBubbleViewDidClose(); | |
171 } | |
172 | |
173 ProfileResetBubbleView::ProfileResetBubbleView( | |
174 const base::WeakPtr<ProfileResetGlobalError>& global_error, | |
175 views::View* anchor_view, | |
176 content::PageNavigator* navigator, | |
177 Profile* profile) | |
178 : BubbleDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), | |
179 navigator_(navigator), | |
180 profile_(profile), | |
181 global_error_(global_error), | |
182 resetting_(false), | |
183 chose_to_reset_(false), | |
184 show_help_pane_(false), | |
185 weak_factory_(this) { | |
186 } | |
187 | |
188 void ProfileResetBubbleView::ResetAllChildren() { | |
189 controls_.Reset(); | |
190 SetLayoutManager(NULL); | |
191 RemoveAllChildViews(true); | |
192 } | |
193 | |
194 void ProfileResetBubbleView::Init() { | |
195 set_margins(gfx::Insets(kMarginHeight, 0, 0, 0)); | |
196 // Start requesting the feedback data. | |
197 snapshot_.reset(new ResettableSettingsSnapshot(profile_)); | |
198 snapshot_->RequestShortcuts( | |
199 base::Bind(&ProfileResetBubbleView::UpdateFeedbackDetails, | |
200 weak_factory_.GetWeakPtr())); | |
201 SetupLayoutManager(true); | |
202 } | |
203 | |
204 void ProfileResetBubbleView::SetupLayoutManager(bool report_checked) { | |
205 ResetAllChildren(); | |
206 | |
207 base::string16 product_name( | |
208 l10n_util::GetStringUTF16(IDS_SHORT_PRODUCT_NAME)); | |
209 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
210 | |
211 // Bubble title label. | |
212 views::Label* title_label = new views::Label( | |
213 l10n_util::GetStringFUTF16(IDS_RESET_BUBBLE_TITLE, product_name), | |
214 rb.GetFontList(ui::ResourceBundle::BoldFont)); | |
215 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
216 | |
217 // Description text label. | |
218 views::Label* text_label = new views::Label( | |
219 l10n_util::GetStringFUTF16(IDS_RESET_BUBBLE_TEXT, product_name)); | |
220 text_label->SetMultiLine(true); | |
221 text_label->SetLineHeight(20); | |
222 text_label->SetEnabledColor(SK_ColorDKGRAY); | |
223 text_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
224 | |
225 // Learn more link. | |
226 views::Link* learn_more_link = new views::Link( | |
227 l10n_util::GetStringUTF16(IDS_LEARN_MORE)); | |
228 learn_more_link->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
229 learn_more_link->set_listener(this); | |
230 learn_more_link->SetUnderline(false); | |
231 | |
232 // Reset button's name is based on |resetting_| state. | |
233 int reset_button_string_id = IDS_RESET_PROFILE_SETTINGS_COMMIT_BUTTON; | |
234 if (resetting_) | |
235 reset_button_string_id = IDS_RESETTING; | |
236 controls_.reset_button = new views::LabelButton( | |
237 this, l10n_util::GetStringUTF16(reset_button_string_id)); | |
238 controls_.reset_button->SetStyle(views::Button::STYLE_BUTTON); | |
239 controls_.reset_button->SetIsDefault(true); | |
240 controls_.reset_button->SetFontList( | |
241 rb.GetFontList(ui::ResourceBundle::BoldFont)); | |
242 controls_.reset_button->SetEnabled(!resetting_); | |
243 // For the Resetting... text to fit. | |
244 gfx::Size reset_button_size = controls_.reset_button->GetPreferredSize(); | |
245 reset_button_size.set_width(100); | |
246 controls_.reset_button->SetMinSize(reset_button_size); | |
247 | |
248 // No thanks button. | |
249 controls_.no_thanks_button = new views::LabelButton( | |
250 this, l10n_util::GetStringUTF16(IDS_NO_THANKS)); | |
251 controls_.no_thanks_button->SetStyle(views::Button::STYLE_BUTTON); | |
252 controls_.no_thanks_button->SetEnabled(!resetting_); | |
253 | |
254 // Checkbox for reporting settings or not. | |
255 controls_.report_settings_checkbox = new views::Checkbox( | |
256 l10n_util::GetStringUTF16(IDS_REPORT_BUBBLE_TEXT)); | |
257 controls_.report_settings_checkbox->SetTextColor( | |
258 views::Button::STATE_NORMAL, SK_ColorGRAY); | |
259 controls_.report_settings_checkbox->SetChecked(report_checked); | |
260 controls_.report_settings_checkbox->SetTextMultiLine(true); | |
261 controls_.report_settings_checkbox->set_background( | |
262 views::Background::CreateSolidBackground(kLightGrayBackgroundColor)); | |
263 // Have a smaller margin on the right, to have the |controls_.help_button| | |
264 // closer to the edge. | |
265 controls_.report_settings_checkbox->SetBorder( | |
266 views::Border::CreateSolidSidedBorder(kMarginWidth, | |
267 kMarginWidth, | |
268 kMarginWidth, | |
269 kMarginWidth / 2, | |
270 kLightGrayBackgroundColor)); | |
271 | |
272 // Help button to toggle the bottom panel on or off. | |
273 controls_.help_button = new views::ImageButton(this); | |
274 const gfx::ImageSkia* help_image = rb.GetImageSkiaNamed(IDR_QUESTION_MARK); | |
275 color_utils::HSL hsl_shift = { -1, 0, 0.8 }; | |
276 brighter_help_image_ = gfx::ImageSkiaOperations::CreateHSLShiftedImage( | |
277 *help_image, hsl_shift); | |
278 controls_.help_button->SetImage( | |
279 views::Button::STATE_NORMAL, &brighter_help_image_); | |
280 controls_.help_button->SetImage(views::Button::STATE_HOVERED, help_image); | |
281 controls_.help_button->SetImage(views::Button::STATE_PRESSED, help_image); | |
282 controls_.help_button->set_background( | |
283 views::Background::CreateSolidBackground(kLightGrayBackgroundColor)); | |
284 controls_.help_button->SetImageAlignment(views::ImageButton::ALIGN_CENTER, | |
285 views::ImageButton::ALIGN_MIDDLE); | |
286 | |
287 GridLayout* layout = new GridLayout(this); | |
288 SetLayoutManager(layout); | |
289 | |
290 // Title row. | |
291 const int kTitleColumnSetId = 0; | |
292 views::ColumnSet* cs = layout->AddColumnSet(kTitleColumnSetId); | |
293 cs->AddPaddingColumn(0, kMarginWidth); | |
294 cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, | |
295 GridLayout::USE_PREF, 0, 0); | |
296 cs->AddPaddingColumn(0, kMarginWidth); | |
297 | |
298 // Text row. | |
299 const int kTextColumnSetId = 1; | |
300 cs = layout->AddColumnSet(kTextColumnSetId); | |
301 cs->AddPaddingColumn(0, kMarginWidth); | |
302 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, | |
303 GridLayout::FIXED, kWidthOfDescriptionText, 0); | |
304 cs->AddPaddingColumn(0, kMarginWidth); | |
305 | |
306 // Learn more link & buttons row. | |
307 const int kButtonsColumnSetId = 2; | |
308 cs = layout->AddColumnSet(kButtonsColumnSetId); | |
309 cs->AddPaddingColumn(0, kMarginWidth); | |
310 cs->AddColumn(GridLayout::LEADING, GridLayout::CENTER, 0, | |
311 GridLayout::USE_PREF, 0, 0); | |
312 cs->AddPaddingColumn(1, views::kRelatedControlHorizontalSpacing); | |
313 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0, | |
314 GridLayout::USE_PREF, 0, 0); | |
315 cs->AddPaddingColumn(0, kButtonPadding); | |
316 cs->AddColumn(GridLayout::LEADING, GridLayout::TRAILING, 0, | |
317 GridLayout::USE_PREF, 0, 0); | |
318 cs->AddPaddingColumn(0, kMarginWidth); | |
319 | |
320 // Separator. | |
321 const int kSeparatorColumnSetId = 3; | |
322 cs = layout->AddColumnSet(kSeparatorColumnSetId); | |
323 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, | |
324 GridLayout::FIXED, kAllColumnsWidth, 0); | |
325 | |
326 // Reporting row. | |
327 const int kReportColumnSetId = 4; | |
328 cs = layout->AddColumnSet(kReportColumnSetId); | |
329 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, | |
330 GridLayout::FIXED, kReportingCheckboxColumnWidth, 0); | |
331 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0, | |
332 GridLayout::FIXED, kHelpButtonColumnWidth, 0); | |
333 | |
334 layout->StartRow(0, kTitleColumnSetId); | |
335 layout->AddView(title_label); | |
336 layout->AddPaddingRow(0, kMarginHeight); | |
337 | |
338 layout->StartRow(0, kTextColumnSetId); | |
339 layout->AddView(text_label); | |
340 layout->AddPaddingRow(0, kMarginHeight); | |
341 | |
342 layout->StartRow(0, kButtonsColumnSetId); | |
343 layout->AddView(learn_more_link); | |
344 layout->AddView(controls_.reset_button); | |
345 layout->AddView(controls_.no_thanks_button); | |
346 layout->AddPaddingRow(0, kMarginHeight); | |
347 | |
348 layout->StartRow(0, kSeparatorColumnSetId); | |
349 layout->AddView(new views::Separator(views::Separator::HORIZONTAL)); | |
350 | |
351 layout->StartRow(0, kReportColumnSetId); | |
352 layout->AddView(controls_.report_settings_checkbox); | |
353 layout->AddView(controls_.help_button); | |
354 | |
355 if (show_help_pane_ && snapshot_) { | |
356 // We need a single row to add the scroll view containing the feedback. | |
357 const int kReportDetailsColumnSetId = 5; | |
358 cs = layout->AddColumnSet(kReportDetailsColumnSetId); | |
359 cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 1, | |
360 GridLayout::USE_PREF, 0, 0); | |
361 | |
362 FeedbackView* feedback_view = new FeedbackView(); | |
363 scoped_ptr<base::ListValue> feedback_data = | |
364 GetReadableFeedbackForSnapshot(profile_, *snapshot_); | |
365 feedback_view->SetupLayoutManager(*feedback_data); | |
366 | |
367 views::ScrollView* scroll_view = new views::ScrollView(); | |
368 scroll_view->set_background(views::Background::CreateSolidBackground( | |
369 kLightGrayBackgroundColor)); | |
370 scroll_view->SetContents(feedback_view); | |
371 | |
372 layout->StartRow(1, kReportDetailsColumnSetId); | |
373 layout->AddView(scroll_view, 1, 1, GridLayout::FILL, | |
374 GridLayout::FILL, kAllColumnsWidth, | |
375 std::min(feedback_view->height() + kMarginHeight, | |
376 kMaxFeedbackViewHeight)); | |
377 } | |
378 | |
379 Layout(); | |
380 AddAccelerator(ui::Accelerator(ui::VKEY_RETURN, ui::EF_NONE)); | |
381 } | |
382 | |
383 void ProfileResetBubbleView::ButtonPressed(views::Button* sender, | |
384 const ui::Event& event) { | |
385 if (sender == controls_.reset_button) { | |
386 DCHECK(!resetting_); | |
387 content::RecordAction( | |
388 base::UserMetricsAction("SettingsResetBubble.Reset")); | |
389 | |
390 // Remember that the user chose to reset, and that resetting is underway. | |
391 chose_to_reset_ = true; | |
392 resetting_ = true; | |
393 | |
394 controls_.reset_button->SetText(l10n_util::GetStringUTF16(IDS_RESETTING)); | |
395 controls_.reset_button->SetEnabled(false); | |
396 controls_.no_thanks_button->SetEnabled(false); | |
397 SchedulePaint(); | |
398 | |
399 if (global_error_) { | |
400 global_error_->OnBubbleViewResetButtonPressed( | |
401 controls_.report_settings_checkbox->checked()); | |
402 } | |
403 } else if (sender == controls_.no_thanks_button) { | |
404 DCHECK(!resetting_); | |
405 content::RecordAction( | |
406 base::UserMetricsAction("SettingsResetBubble.NoThanks")); | |
407 | |
408 if (global_error_) | |
409 global_error_->OnBubbleViewNoThanksButtonPressed(); | |
410 GetWidget()->Close(); | |
411 return; | |
412 } else if (sender == controls_.help_button) { | |
413 show_help_pane_ = !show_help_pane_; | |
414 | |
415 SetupLayoutManager(controls_.report_settings_checkbox->checked()); | |
416 SizeToContents(); | |
417 } | |
418 } | |
419 | |
420 void ProfileResetBubbleView::LinkClicked(views::Link* source, int flags) { | |
421 content::RecordAction( | |
422 base::UserMetricsAction("SettingsResetBubble.LearnMore")); | |
423 navigator_->OpenURL(content::OpenURLParams( | |
424 GURL(chrome::kResetProfileSettingsLearnMoreURL), content::Referrer(), | |
425 NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK, false)); | |
426 } | |
427 | |
428 void ProfileResetBubbleView::CloseBubbleView() { | |
429 resetting_ = false; | |
430 GetWidget()->Close(); | |
431 } | |
432 | |
433 void ProfileResetBubbleView::UpdateFeedbackDetails() { | |
434 if (show_help_pane_) | |
435 SetupLayoutManager(controls_.report_settings_checkbox->checked()); | |
436 } | |
437 | |
438 bool IsProfileResetBubbleSupported() { | |
439 return true; | |
440 } | |
441 | |
442 GlobalErrorBubbleViewBase* ShowProfileResetBubble( | |
443 const base::WeakPtr<ProfileResetGlobalError>& global_error, | |
444 Browser* browser) { | |
445 return ProfileResetBubbleView::ShowBubble(global_error, browser); | |
446 } | |
OLD | NEW |