Allow ExtensionInstallPrompt be created with a parent native view.
[chromium-blink-merge.git] / chrome / browser / ui / views / extensions / extension_install_dialog_view.cc
blob5624a7e3b436b0b721f418f1d6d71eb7749d0664
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 <vector>
7 #include "base/basictypes.h"
8 #include "base/command_line.h"
9 #include "base/compiler_specific.h"
10 #include "base/i18n/rtl.h"
11 #include "base/string_util.h"
12 #include "base/utf_string_conversions.h"
13 #include "chrome/browser/extensions/bundle_installer.h"
14 #include "chrome/browser/extensions/extension_install_prompt.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/common/chrome_switches.h"
17 #include "chrome/common/extensions/extension.h"
18 #include "chrome/installer/util/browser_distribution.h"
19 #include "content/public/browser/page_navigator.h"
20 #include "content/public/browser/web_contents.h"
21 #include "grit/chromium_strings.h"
22 #include "grit/generated_resources.h"
23 #include "grit/google_chrome_strings.h"
24 #include "grit/theme_resources.h"
25 #include "ui/base/animation/animation_delegate.h"
26 #include "ui/base/animation/slide_animation.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/base/resource/resource_bundle.h"
29 #include "ui/gfx/transform.h"
30 #include "ui/views/border.h"
31 #include "ui/views/controls/button/checkbox.h"
32 #include "ui/views/controls/image_view.h"
33 #include "ui/views/controls/label.h"
34 #include "ui/views/controls/link.h"
35 #include "ui/views/controls/link_listener.h"
36 #include "ui/views/controls/separator.h"
37 #include "ui/views/layout/box_layout.h"
38 #include "ui/views/layout/grid_layout.h"
39 #include "ui/views/layout/layout_constants.h"
40 #include "ui/views/view.h"
41 #include "ui/views/widget/widget.h"
42 #include "ui/views/window/dialog_delegate.h"
44 #if defined(OS_WIN)
45 #include "chrome/browser/extensions/app_host_installer_win.h"
46 #include "chrome/installer/launcher_support/chrome_launcher_support.h"
47 #endif
49 using content::OpenURLParams;
50 using content::Referrer;
51 using extensions::BundleInstaller;
53 namespace {
55 // Size of extension icon in top left of dialog.
56 const int kIconSize = 69;
58 // Width of the left column of the dialog when the extension requests
59 // permissions.
60 const int kPermissionsLeftColumnWidth = 250;
62 // Width of the left column of the dialog when the extension requests no
63 // permissions.
64 const int kNoPermissionsLeftColumnWidth = 200;
66 // Width of the left column for bundle install prompts. There's only one column
67 // in this case, so make it wider than normal.
68 const int kBundleLeftColumnWidth = 300;
70 // Width of the left column for external install prompts. The text is long in
71 // this case, so make it wider than normal.
72 const int kExternalInstallLeftColumnWidth = 350;
74 // Heading font size correction.
75 const int kHeadingFontSizeDelta = 1;
77 const int kRatingFontSizeDelta = -1;
79 void AddResourceIcon(const gfx::ImageSkia* skia_image, void* data) {
80 views::View* parent = static_cast<views::View*>(data);
81 views::ImageView* image_view = new views::ImageView();
82 image_view->SetImage(*skia_image);
83 parent->AddChildView(image_view);
86 // Creates a string for displaying |message| to the user. If it has to look
87 // like a entry in a bullet point list, one is added.
88 string16 PrepareForDisplay(const string16& message, bool bullet_point) {
89 return bullet_point ? l10n_util::GetStringFUTF16(
90 IDS_EXTENSION_PERMISSION_LINE,
91 message) : message;
94 // Implements the extension installation dialog for TOOLKIT_VIEWS.
95 class ExtensionInstallDialogView : public views::DialogDelegateView,
96 public views::LinkListener {
97 public:
98 ExtensionInstallDialogView(content::PageNavigator* navigator,
99 ExtensionInstallPrompt::Delegate* delegate,
100 const ExtensionInstallPrompt::Prompt& prompt,
101 bool show_launcher_opt_in);
102 virtual ~ExtensionInstallDialogView();
104 // Changes the size of the containing widget to match the preferred size
105 // of this dialog.
106 void SizeToContents();
108 private:
109 // views::DialogDelegateView:
110 virtual string16 GetDialogButtonLabel(ui::DialogButton button) const OVERRIDE;
111 virtual int GetDefaultDialogButton() const OVERRIDE;
112 virtual bool Cancel() OVERRIDE;
113 virtual bool Accept() OVERRIDE;
115 // views::WidgetDelegate:
116 virtual ui::ModalType GetModalType() const OVERRIDE;
117 virtual string16 GetWindowTitle() const OVERRIDE;
119 // views::LinkListener:
120 virtual void LinkClicked(views::Link* source, int event_flags) OVERRIDE;
122 bool is_inline_install() const {
123 return prompt_.type() == ExtensionInstallPrompt::INLINE_INSTALL_PROMPT;
126 bool is_bundle_install() const {
127 return prompt_.type() == ExtensionInstallPrompt::BUNDLE_INSTALL_PROMPT;
130 bool is_external_install() const {
131 return prompt_.type() == ExtensionInstallPrompt::EXTERNAL_INSTALL_PROMPT;
134 content::PageNavigator* navigator_;
135 ExtensionInstallPrompt::Delegate* delegate_;
136 ExtensionInstallPrompt::Prompt prompt_;
137 bool show_launcher_opt_in_;
139 // The lifetime of |app_launcher_opt_in_checkbox_| is managed by the views
140 // system.
141 views::Checkbox* app_launcher_opt_in_checkbox_;
143 DISALLOW_COPY_AND_ASSIGN(ExtensionInstallDialogView);
146 // A view to display a single IssueAdviceInfoEntry.
147 class IssueAdviceView : public views::View,
148 public ui::AnimationDelegate {
149 public:
150 IssueAdviceView(ExtensionInstallDialogView* owner,
151 const IssueAdviceInfoEntry& issue_advice,
152 int horizontal_space);
153 virtual ~IssueAdviceView() {}
155 // Implementation of views::View:
156 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE;
157 virtual void OnMouseReleased(const ui::MouseEvent& event) OVERRIDE;
158 virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE;
160 // Implementation of ui::AnimationDelegate:
161 virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
163 private:
164 // A view which displays all the details of an IssueAdviceInfoEntry.
165 class DetailsView : public views::View {
166 public:
167 explicit DetailsView(int horizontal_space);
168 virtual ~DetailsView() {}
170 // Implementation of views::View:
171 virtual gfx::Size GetPreferredSize() OVERRIDE;
173 void AddDetail(const string16& detail);
175 // Animates this to be a height proportional to |state|.
176 void AnimateToState(double state);
178 private:
179 views::GridLayout* layout_;
180 double state_;
182 DISALLOW_COPY_AND_ASSIGN(DetailsView);
185 // The dialog that owns |this|. It's also an ancestor in the View hierarchy.
186 ExtensionInstallDialogView* owner_;
188 // A view for showing |issue_advice.details|.
189 DetailsView* details_view_;
191 // The '>' zippy control.
192 views::ImageView* arrow_view_;
194 ui::SlideAnimation slide_animation_;
196 DISALLOW_COPY_AND_ASSIGN(IssueAdviceView);
199 void DoShowDialog(const ExtensionInstallPrompt::ShowParams& show_params,
200 ExtensionInstallPrompt::Delegate* delegate,
201 const ExtensionInstallPrompt::Prompt& prompt,
202 bool show_launcher_opt_in) {
203 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
204 views::Widget::CreateWindowWithParent(
205 new ExtensionInstallDialogView(show_params.navigator, delegate, prompt,
206 show_launcher_opt_in),
207 show_params.parent_window)->Show();
210 // Runs on the FILE thread. Check if the launcher is present and then show
211 // the install dialog with an appropriate |show_launcher_opt_in|.
212 void CheckLauncherAndShowDialog(
213 const ExtensionInstallPrompt::ShowParams& show_params,
214 ExtensionInstallPrompt::Delegate* delegate,
215 const ExtensionInstallPrompt::Prompt& prompt) {
216 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::FILE));
217 #if defined(OS_WIN)
218 bool present = chrome_launcher_support::IsAppLauncherPresent();
219 #else
220 NOTREACHED();
221 bool present = false;
222 #endif
223 content::BrowserThread::PostTask(
224 content::BrowserThread::UI,
225 FROM_HERE,
226 base::Bind(&DoShowDialog, show_params, delegate, prompt, !present));
229 void ShowExtensionInstallDialogImpl(
230 const ExtensionInstallPrompt::ShowParams& show_params,
231 ExtensionInstallPrompt::Delegate* delegate,
232 const ExtensionInstallPrompt::Prompt& prompt) {
233 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
234 #if defined(OS_WIN)
235 CommandLine* command_line = CommandLine::ForCurrentProcess();
236 if (command_line->HasSwitch(switches::kEnableAppListOptIn) &&
237 BrowserDistribution::GetDistribution()->AppHostIsSupported() &&
238 prompt.extension()->is_platform_app()) {
239 content::BrowserThread::PostTask(
240 content::BrowserThread::FILE,
241 FROM_HERE,
242 base::Bind(&CheckLauncherAndShowDialog, show_params, delegate, prompt));
243 return;
245 #endif
246 DoShowDialog(show_params, delegate, prompt, false);
249 } // namespace
251 ExtensionInstallDialogView::ExtensionInstallDialogView(
252 content::PageNavigator* navigator,
253 ExtensionInstallPrompt::Delegate* delegate,
254 const ExtensionInstallPrompt::Prompt& prompt,
255 bool show_launcher_opt_in)
256 : navigator_(navigator),
257 delegate_(delegate),
258 prompt_(prompt),
259 show_launcher_opt_in_(show_launcher_opt_in),
260 app_launcher_opt_in_checkbox_(NULL) {
261 // Possible grid layouts:
262 // Inline install
263 // w/ permissions no permissions
264 // +--------------------+------+ +--------------+------+
265 // | heading | icon | | heading | icon |
266 // +--------------------| | +--------------| |
267 // | rating | | | rating | |
268 // +--------------------| | +--------------+ |
269 // | user_count | | | user_count | |
270 // +--------------------| | +--------------| |
271 // | store_link | | | store_link | |
272 // +--------------------+------+ +--------------+------+
273 // | separator |
274 // +--------------------+------+
275 // | permissions_header | |
276 // +--------------------+------+
277 // | permission1 | |
278 // +--------------------+------+
279 // | permission2 | |
280 // +--------------------+------+
282 // Regular install
283 // w/ permissions XOR oauth issues no permissions
284 // +--------------------+------+ +--------------+------+
285 // | heading | icon | | heading | icon |
286 // +--------------------| | +--------------+------+
287 // | permissions_header | |
288 // +--------------------| |
289 // | permission1 | |
290 // +--------------------| |
291 // | permission2 | |
292 // +--------------------+------+
294 // w/ permissions AND oauth issues
295 // +--------------------+------+
296 // | heading | icon |
297 // +--------------------| |
298 // | permissions_header | |
299 // +--------------------| |
300 // | permission1 | |
301 // +--------------------| |
302 // | permission2 | |
303 // +--------------------+------+
304 // | oauth header |
305 // +---------------------------+
306 // | oauth issue 1 |
307 // +---------------------------+
308 // | oauth issue 2 |
309 // +---------------------------+
311 // w/ launcher opt in
312 // +--------------------+------+
313 // | heading | icon |
314 // +--------------------| |
315 // | permissions_header | |
316 // | |
317 // | ......................... |
318 // | |
319 // +--------------------+------+
320 // | launcher opt in |
321 // +---------------------------+
323 views::GridLayout* layout = views::GridLayout::CreatePanel(this);
324 SetLayoutManager(layout);
326 int column_set_id = 0;
327 views::ColumnSet* column_set = layout->AddColumnSet(column_set_id);
328 int left_column_width =
329 prompt.GetPermissionCount() + prompt.GetOAuthIssueCount() > 0 ?
330 kPermissionsLeftColumnWidth : kNoPermissionsLeftColumnWidth;
331 if (is_bundle_install())
332 left_column_width = kBundleLeftColumnWidth;
333 if (is_external_install())
334 left_column_width = kExternalInstallLeftColumnWidth;
336 column_set->AddColumn(views::GridLayout::LEADING,
337 views::GridLayout::FILL,
338 0, // no resizing
339 views::GridLayout::USE_PREF,
340 0, // no fixed width
341 left_column_width);
342 if (!is_bundle_install()) {
343 column_set->AddPaddingColumn(0, views::kPanelHorizMargin);
344 column_set->AddColumn(views::GridLayout::LEADING,
345 views::GridLayout::LEADING,
346 0, // no resizing
347 views::GridLayout::USE_PREF,
348 0, // no fixed width
349 kIconSize);
352 layout->StartRow(0, column_set_id);
354 views::Label* heading = new views::Label(prompt.GetHeading());
355 heading->SetFont(heading->font().DeriveFont(kHeadingFontSizeDelta,
356 gfx::Font::BOLD));
357 heading->SetMultiLine(true);
358 heading->SetHorizontalAlignment(gfx::ALIGN_LEFT);
359 heading->SizeToFit(left_column_width);
360 layout->AddView(heading);
362 if (!is_bundle_install()) {
363 // Scale down to icon size, but allow smaller icons (don't scale up).
364 const gfx::ImageSkia* image = prompt.icon().ToImageSkia();
365 gfx::Size size(image->width(), image->height());
366 if (size.width() > kIconSize || size.height() > kIconSize)
367 size = gfx::Size(kIconSize, kIconSize);
368 views::ImageView* icon = new views::ImageView();
369 icon->SetImageSize(size);
370 icon->SetImage(*image);
371 icon->SetHorizontalAlignment(views::ImageView::CENTER);
372 icon->SetVerticalAlignment(views::ImageView::CENTER);
373 int icon_row_span = 1;
374 if (is_inline_install()) {
375 // Also span the rating, user_count and store_link rows.
376 icon_row_span = 4;
377 } else if (prompt.GetPermissionCount()) {
378 // Also span the permission header and each of the permission rows (all
379 // have a padding row above it).
380 icon_row_span = 3 + prompt.GetPermissionCount() * 2;
381 } else if (prompt.GetOAuthIssueCount()) {
382 // Also span the permission header and each of the permission rows (all
383 // have a padding row above it).
384 icon_row_span = 3 + prompt.GetOAuthIssueCount() * 2;
386 layout->AddView(icon, 1, icon_row_span);
389 if (is_inline_install()) {
390 layout->StartRow(0, column_set_id);
391 views::View* rating = new views::View();
392 rating->SetLayoutManager(new views::BoxLayout(
393 views::BoxLayout::kHorizontal, 0, 0, 0));
394 layout->AddView(rating);
395 prompt.AppendRatingStars(AddResourceIcon, rating);
397 views::Label* rating_count = new views::Label(prompt.GetRatingCount());
398 rating_count->SetFont(
399 rating_count->font().DeriveFont(kRatingFontSizeDelta));
400 // Add some space between the stars and the rating count.
401 rating_count->set_border(views::Border::CreateEmptyBorder(0, 2, 0, 0));
402 rating->AddChildView(rating_count);
404 layout->StartRow(0, column_set_id);
405 views::Label* user_count = new views::Label(prompt.GetUserCount());
406 user_count->SetAutoColorReadabilityEnabled(false);
407 user_count->SetEnabledColor(SK_ColorGRAY);
408 user_count->SetFont(user_count->font().DeriveFont(kRatingFontSizeDelta));
409 layout->AddView(user_count);
411 layout->StartRow(0, column_set_id);
412 views::Link* store_link = new views::Link(
413 l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_STORE_LINK));
414 store_link->SetFont(store_link->font().DeriveFont(kRatingFontSizeDelta));
415 store_link->set_listener(this);
416 layout->AddView(store_link);
419 if (is_bundle_install()) {
420 BundleInstaller::ItemList items = prompt_.bundle()->GetItemsWithState(
421 BundleInstaller::Item::STATE_PENDING);
422 for (size_t i = 0; i < items.size(); ++i) {
423 string16 extension_name = UTF8ToUTF16(items[i].localized_name);
424 base::i18n::AdjustStringForLocaleDirection(&extension_name);
425 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
426 layout->StartRow(0, column_set_id);
427 views::Label* extension_label = new views::Label(
428 PrepareForDisplay(extension_name, true));
429 extension_label->SetMultiLine(true);
430 extension_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
431 extension_label->SizeToFit(left_column_width);
432 layout->AddView(extension_label);
436 if (prompt.GetPermissionCount()) {
437 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
439 if (is_inline_install()) {
440 layout->StartRow(0, column_set_id);
441 layout->AddView(new views::Separator(), 3, 1, views::GridLayout::FILL,
442 views::GridLayout::FILL);
443 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
446 layout->StartRow(0, column_set_id);
447 views::Label* permissions_header = NULL;
448 if (is_bundle_install()) {
449 // We need to make the font bold like this, rather than using SetFont,
450 // because otherwise SizeToFit mis-judges the width of the line.
451 gfx::Font bold_font = ui::ResourceBundle::GetSharedInstance().GetFont(
452 ui::ResourceBundle::BaseFont).DeriveFont(
453 kHeadingFontSizeDelta, gfx::Font::BOLD);
454 permissions_header = new views::Label(
455 prompt.GetPermissionsHeading(), bold_font);
456 } else {
457 permissions_header = new views::Label(prompt.GetPermissionsHeading());
459 permissions_header->SetMultiLine(true);
460 permissions_header->SetHorizontalAlignment(gfx::ALIGN_LEFT);
461 permissions_header->SizeToFit(left_column_width);
462 layout->AddView(permissions_header);
464 for (size_t i = 0; i < prompt.GetPermissionCount(); ++i) {
465 layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
466 layout->StartRow(0, column_set_id);
467 views::Label* permission_label = new views::Label(PrepareForDisplay(
468 prompt.GetPermission(i), true));
469 permission_label->SetMultiLine(true);
470 permission_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
471 permission_label->SizeToFit(left_column_width);
472 layout->AddView(permission_label);
476 if (prompt.GetOAuthIssueCount()) {
477 // Slide in under the permissions, if there are any. If there are
478 // permissions, the OAuth prompt stretches all the way to the right of the
479 // dialog. If there are no permissions, the OAuth prompt just takes up the
480 // left column.
481 int space_for_oauth = left_column_width;
482 if (prompt.GetPermissionCount()) {
483 space_for_oauth += kIconSize;
484 column_set = layout->AddColumnSet(++column_set_id);
485 column_set->AddColumn(views::GridLayout::FILL,
486 views::GridLayout::FILL,
488 views::GridLayout::USE_PREF,
489 0, // no fixed width
490 space_for_oauth);
493 layout->StartRowWithPadding(0, column_set_id,
494 0, views::kRelatedControlVerticalSpacing);
495 views::Label* oauth_header = new views::Label(prompt.GetOAuthHeading());
496 oauth_header->SetMultiLine(true);
497 oauth_header->SetHorizontalAlignment(gfx::ALIGN_LEFT);
498 oauth_header->SizeToFit(left_column_width);
499 layout->AddView(oauth_header);
501 for (size_t i = 0; i < prompt.GetOAuthIssueCount(); ++i) {
502 layout->StartRowWithPadding(
503 0, column_set_id,
504 0, views::kRelatedControlVerticalSpacing);
506 IssueAdviceView* issue_advice_view =
507 new IssueAdviceView(this, prompt.GetOAuthIssue(i), space_for_oauth);
508 layout->AddView(issue_advice_view);
512 if (show_launcher_opt_in) {
513 // Put the launcher opt-in prompt at the bottom. It should always stretch
514 // to take up the whole width of the dialog.
515 column_set = layout->AddColumnSet(++column_set_id);
516 column_set->AddColumn(views::GridLayout::FILL,
517 views::GridLayout::FILL,
519 views::GridLayout::USE_PREF,
520 0, // no fixed width
521 left_column_width + kIconSize);
522 layout->StartRowWithPadding(0, column_set_id,
523 0, views::kRelatedControlVerticalSpacing);
524 app_launcher_opt_in_checkbox_ = new views::Checkbox(
525 l10n_util::GetStringUTF16(IDS_APP_LIST_OPT_IN_TEXT));
526 app_launcher_opt_in_checkbox_->SetFont(
527 app_launcher_opt_in_checkbox_->font().DeriveFont(0, gfx::Font::BOLD));
528 layout->AddView(app_launcher_opt_in_checkbox_);
532 ExtensionInstallDialogView::~ExtensionInstallDialogView() {}
534 void ExtensionInstallDialogView::SizeToContents() {
535 GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize());
538 string16 ExtensionInstallDialogView::GetDialogButtonLabel(
539 ui::DialogButton button) const {
540 switch (button) {
541 case ui::DIALOG_BUTTON_OK:
542 return prompt_.GetAcceptButtonLabel();
543 case ui::DIALOG_BUTTON_CANCEL:
544 return prompt_.HasAbortButtonLabel() ?
545 prompt_.GetAbortButtonLabel() :
546 l10n_util::GetStringUTF16(IDS_CANCEL);
547 default:
548 NOTREACHED();
549 return string16();
553 int ExtensionInstallDialogView::GetDefaultDialogButton() const {
554 return ui::DIALOG_BUTTON_CANCEL;
557 bool ExtensionInstallDialogView::Cancel() {
558 delegate_->InstallUIAbort(true);
559 return true;
562 bool ExtensionInstallDialogView::Accept() {
563 #if defined(OS_WIN)
564 extensions::AppHostInstaller::SetInstallWithLauncher(
565 app_launcher_opt_in_checkbox_ &&
566 app_launcher_opt_in_checkbox_->checked());
567 #endif
568 delegate_->InstallUIProceed();
569 return true;
572 ui::ModalType ExtensionInstallDialogView::GetModalType() const {
573 return ui::MODAL_TYPE_WINDOW;
576 string16 ExtensionInstallDialogView::GetWindowTitle() const {
577 return prompt_.GetDialogTitle();
580 void ExtensionInstallDialogView::LinkClicked(views::Link* source,
581 int event_flags) {
582 GURL store_url(extension_urls::GetWebstoreItemDetailURLPrefix() +
583 prompt_.extension()->id());
584 OpenURLParams params(
585 store_url, Referrer(), NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_LINK,
586 false);
587 navigator_->OpenURL(params);
588 GetWidget()->Close();
591 // static
592 ExtensionInstallPrompt::ShowDialogCallback
593 ExtensionInstallPrompt::GetDefaultShowDialogCallback() {
594 return base::Bind(&ShowExtensionInstallDialogImpl);
597 // IssueAdviceView::DetailsView ------------------------------------------------
599 IssueAdviceView::DetailsView::DetailsView(int horizontal_space)
600 : layout_(new views::GridLayout(this)),
601 state_(0) {
602 SetLayoutManager(layout_);
603 views::ColumnSet* column_set = layout_->AddColumnSet(0);
604 column_set->AddColumn(views::GridLayout::LEADING,
605 views::GridLayout::LEADING,
607 views::GridLayout::FIXED,
608 horizontal_space,
612 void IssueAdviceView::DetailsView::AddDetail(const string16& detail) {
613 layout_->StartRowWithPadding(0, 0,
614 0, views::kRelatedControlSmallVerticalSpacing);
615 views::Label* detail_label =
616 new views::Label(PrepareForDisplay(detail, true));
617 detail_label->SetMultiLine(true);
618 detail_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
619 layout_->AddView(detail_label);
622 gfx::Size IssueAdviceView::DetailsView::GetPreferredSize() {
623 gfx::Size size = views::View::GetPreferredSize();
624 return gfx::Size(size.width(), size.height() * state_);
627 void IssueAdviceView::DetailsView::AnimateToState(double state) {
628 state_ = state;
629 PreferredSizeChanged();
630 SchedulePaint();
633 // IssueAdviceView -------------------------------------------------------------
635 IssueAdviceView::IssueAdviceView(ExtensionInstallDialogView* owner,
636 const IssueAdviceInfoEntry& issue_advice,
637 int horizontal_space)
638 : owner_(owner),
639 details_view_(NULL),
640 arrow_view_(NULL),
641 slide_animation_(this) {
642 // TODO(estade): replace this with a more appropriate image.
643 const gfx::ImageSkia& image = *ui::ResourceBundle::GetSharedInstance().
644 GetImageSkiaNamed(IDR_OMNIBOX_TTS);
646 views::GridLayout* layout = new views::GridLayout(this);
647 SetLayoutManager(layout);
648 int column_set_id = 0;
649 views::ColumnSet* column_set = layout->AddColumnSet(column_set_id);
650 if (!issue_advice.details.empty()) {
651 column_set->AddColumn(views::GridLayout::LEADING,
652 views::GridLayout::LEADING,
654 views::GridLayout::FIXED,
655 image.width(),
657 horizontal_space -= image.width();
658 details_view_ = new DetailsView(horizontal_space);
660 column_set->AddColumn(views::GridLayout::LEADING,
661 views::GridLayout::FILL,
663 views::GridLayout::FIXED,
664 horizontal_space,
666 layout->StartRow(0, column_set_id);
668 if (details_view_) {
669 arrow_view_ = new views::ImageView();
670 arrow_view_->SetImage(image);
671 arrow_view_->SetVerticalAlignment(views::ImageView::CENTER);
672 layout->AddView(arrow_view_);
675 views::Label* description_label =
676 new views::Label(PrepareForDisplay(issue_advice.description,
677 !details_view_));
678 description_label->SetMultiLine(true);
679 description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
680 description_label->SizeToFit(horizontal_space);
681 layout->AddView(description_label);
683 if (!details_view_)
684 return;
686 layout->StartRow(0, column_set_id);
687 layout->SkipColumns(1);
688 layout->AddView(details_view_);
690 for (size_t i = 0; i < issue_advice.details.size(); ++i)
691 details_view_->AddDetail(issue_advice.details[i]);
694 bool IssueAdviceView::OnMousePressed(const ui::MouseEvent& event) {
695 return details_view_ && event.IsLeftMouseButton();
698 void IssueAdviceView::OnMouseReleased(const ui::MouseEvent& event) {
699 if (slide_animation_.IsShowing())
700 slide_animation_.Hide();
701 else
702 slide_animation_.Show();
705 void IssueAdviceView::AnimationProgressed(const ui::Animation* animation) {
706 DCHECK_EQ(animation, &slide_animation_);
708 if (details_view_)
709 details_view_->AnimateToState(animation->GetCurrentValue());
711 if (arrow_view_) {
712 gfx::Transform rotate;
713 if (animation->GetCurrentValue() != 0.0) {
714 rotate.Translate(arrow_view_->width() / 2.0,
715 arrow_view_->height() / 2.0);
716 // TODO(estade): for some reason there are rendering errors at 90 degrees.
717 // Figure out why.
718 rotate.Rotate(animation->GetCurrentValue() * 89);
719 rotate.Translate(-arrow_view_->width() / 2.0,
720 -arrow_view_->height() / 2.0);
722 arrow_view_->SetTransform(rotate);
726 void IssueAdviceView::ChildPreferredSizeChanged(views::View* child) {
727 owner_->SizeToContents();