Index: chrome/browser/ui/views/extensions/extension_install_dialog_view.cc |
diff --git a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc |
index 15216d8b9ed83b665ce6a2d90602b4b40644747b..9eb58c7bc34e3b2b05a5ba963ae79ce3dc738dd5 100644 |
--- a/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc |
+++ b/chrome/browser/ui/views/extensions/extension_install_dialog_view.cc |
@@ -15,8 +15,13 @@ |
#include "chrome/common/extensions/extension.h" |
#include "content/public/browser/page_navigator.h" |
#include "grit/generated_resources.h" |
+#include "grit/theme_resources.h" |
+#include "ui/base/animation/animation_delegate.h" |
+#include "ui/base/animation/slide_animation.h" |
#include "ui/base/l10n/l10n_util.h" |
#include "ui/base/resource/resource_bundle.h" |
+#include "ui/gfx/point3.h" |
+#include "ui/gfx/transform.h" |
#include "ui/views/border.h" |
#include "ui/views/controls/image_view.h" |
#include "ui/views/controls/label.h" |
@@ -67,7 +72,13 @@ void AddResourceIcon(const gfx::ImageSkia* skia_image, void* data) { |
parent->AddChildView(image_view); |
} |
-} // namespace |
+// Creates a string for displaying |message| to the user. If it has to look |
+// like a entry in a bullet point list, one is added. |
+string16 PrepareForDisplay(const string16& message, bool bullet_point) { |
+ return bullet_point ? l10n_util::GetStringFUTF16( |
+ IDS_EXTENSION_PERMISSION_LINE, |
+ message) : message; |
+} |
// Implements the extension installation dialog for TOOLKIT_VIEWS. |
class ExtensionInstallDialogView : public views::DialogDelegateView, |
@@ -78,6 +89,10 @@ class ExtensionInstallDialogView : public views::DialogDelegateView, |
const ExtensionInstallPrompt::Prompt& prompt); |
virtual ~ExtensionInstallDialogView(); |
+ // Changes the size of the containing widget to match the preferred size |
+ // of this dialog. |
+ void SizeToContents(); |
+ |
private: |
// views::DialogDelegateView: |
virtual string16 GetDialogButtonLabel(ui::DialogButton button) const OVERRIDE; |
@@ -108,6 +123,61 @@ class ExtensionInstallDialogView : public views::DialogDelegateView, |
DISALLOW_COPY_AND_ASSIGN(ExtensionInstallDialogView); |
}; |
+// A view to display a single IssueAdviceInfoEntry. |
+class IssueAdviceView : public views::View, |
+ public ui::AnimationDelegate { |
+ public: |
+ IssueAdviceView(ExtensionInstallDialogView* owner, |
+ const IssueAdviceInfoEntry& issue_advice, |
+ int horizontal_space); |
+ virtual ~IssueAdviceView() {} |
+ |
+ // Implementation of views::View: |
+ virtual bool OnMousePressed(const views::MouseEvent& event) OVERRIDE; |
+ virtual void OnMouseReleased(const views::MouseEvent& event) OVERRIDE; |
+ virtual void ChildPreferredSizeChanged(views::View* child) OVERRIDE; |
+ |
+ // Implementation of ui::AnimationDelegate: |
+ virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE; |
+ |
+ private: |
+ // A view which displays all the details of an IssueAdviceInfoEntry. |
+ class DetailsView : public views::View { |
+ public: |
+ explicit DetailsView(int horizontal_space); |
+ virtual ~DetailsView() {} |
+ |
+ // Implementation of views::View: |
+ virtual gfx::Size GetPreferredSize() OVERRIDE; |
+ |
+ void AddDetail(const string16& detail); |
+ |
+ // Animates this to be a height proportional to |state|. |
+ void AnimateToState(double state); |
+ |
+ private: |
+ views::GridLayout* layout_; |
+ double state_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DetailsView); |
+ }; |
+ |
+ // The dialog that owns |this|. It's also an ancestor in the View hierarchy. |
+ ExtensionInstallDialogView* owner_; |
+ |
+ // A view for showing |issue_advice.details|. |
+ DetailsView* details_view_; |
+ |
+ // The '>' zippy control. |
+ views::ImageView* arrow_view_; |
+ |
+ ui::SlideAnimation slide_animation_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(IssueAdviceView); |
+}; |
+ |
+} // namespace |
+ |
ExtensionInstallDialogView::ExtensionInstallDialogView( |
content::PageNavigator* navigator, |
ExtensionInstallPrompt::Delegate* delegate, |
@@ -137,7 +207,7 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( |
// +--------------------+------+ |
// |
// Regular install |
- // w/ permissions no permissions |
+ // w/ permissions XOR oauth issues no permissions |
// +--------------------+------+ +--------------+------+ |
// | heading | icon | | heading | icon | |
// +--------------------| | +--------------+------+ |
@@ -147,6 +217,23 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( |
// +--------------------| | |
// | permission2 | | |
// +--------------------+------+ |
+ // |
+ // w/ permissions AND oauth issues |
+ // +--------------------+------+ |
+ // | heading | icon | |
+ // +--------------------| | |
+ // | permissions_header | | |
+ // +--------------------| | |
+ // | permission1 | | |
+ // +--------------------| | |
+ // | permission2 | | |
+ // +--------------------+------+ |
+ // | oauth header | |
+ // +---------------------------+ |
+ // | oauth issue 1 | |
+ // +---------------------------+ |
+ // | oauth issue 2 | |
+ // +---------------------------+ |
views::GridLayout* layout = views::GridLayout::CreatePanel(this); |
SetLayoutManager(layout); |
@@ -162,7 +249,7 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( |
views::GridLayout::FILL, |
0, // no resizing |
views::GridLayout::USE_PREF, |
- 0, // no fixed with |
+ 0, // no fixed width |
left_column_width); |
if (!is_bundle_install()) { |
column_set->AddPaddingColumn(0, views::kPanelHorizMargin); |
@@ -203,6 +290,10 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( |
// Also span the permission header and each of the permission rows (all |
// have a padding row above it). |
icon_row_span = 3 + prompt.GetPermissionCount() * 2; |
+ } else if (prompt.GetOAuthIssueCount()) { |
+ // Also span the permission header and each of the permission rows (all |
+ // have a padding row above it). |
+ icon_row_span = 3 + prompt.GetOAuthIssueCount() * 2; |
} |
layout->AddView(icon, 1, icon_row_span); |
} |
@@ -246,8 +337,7 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( |
layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); |
layout->StartRow(0, column_set_id); |
views::Label* extension_label = new views::Label( |
- l10n_util::GetStringFUTF16( |
- IDS_EXTENSION_PERMISSION_LINE, extension_name)); |
+ PrepareForDisplay(extension_name, true)); |
extension_label->SetMultiLine(true); |
extension_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); |
extension_label->SizeToFit(left_column_width); |
@@ -294,11 +384,49 @@ ExtensionInstallDialogView::ExtensionInstallDialogView( |
layout->AddView(permission_label); |
} |
} |
+ |
+ if (prompt.GetOAuthIssueCount()) { |
+ // Slide in under the permissions; stretch all the way to the right of the |
+ // dialog. |
+ int space_for_oauth = left_column_width; |
+ if (prompt.GetPermissionCount()) { |
+ space_for_oauth += kIconSize; |
+ column_set = layout->AddColumnSet(++column_set_id); |
+ column_set->AddColumn(views::GridLayout::FILL, |
+ views::GridLayout::FILL, |
+ 1, |
+ views::GridLayout::USE_PREF, |
+ 0, // no fixed width |
+ space_for_oauth); |
+ } |
+ |
+ layout->StartRowWithPadding(0, column_set_id, |
+ 0, views::kRelatedControlVerticalSpacing); |
+ views::Label* oauth_header = new views::Label(prompt.GetOAuthHeading()); |
+ oauth_header->SetMultiLine(true); |
+ oauth_header->SetHorizontalAlignment(views::Label::ALIGN_LEFT); |
+ oauth_header->SizeToFit(left_column_width); |
+ layout->AddView(oauth_header); |
+ |
+ for (size_t i = 0; i < prompt.GetOAuthIssueCount(); ++i) { |
+ layout->StartRowWithPadding( |
+ 0, column_set_id, |
+ 0, views::kRelatedControlVerticalSpacing); |
+ |
+ IssueAdviceView* issue_advice_view = |
+ new IssueAdviceView(this, prompt.GetOAuthIssue(i), space_for_oauth); |
+ layout->AddView(issue_advice_view); |
+ } |
+ } |
} |
ExtensionInstallDialogView::~ExtensionInstallDialogView() { |
} |
+void ExtensionInstallDialogView::SizeToContents() { |
+ GetWidget()->SetSize(GetWidget()->non_client_view()->GetPreferredSize()); |
+} |
+ |
string16 ExtensionInstallDialogView::GetDialogButtonLabel( |
ui::DialogButton button) const { |
switch (button) { |
@@ -360,3 +488,136 @@ void ShowExtensionInstallDialogImpl( |
new ExtensionInstallDialogView(navigator, delegate, prompt), |
parent)->Show(); |
} |
+ |
+// IssueAdviceView::DetailsView ------------------------------------------------ |
+ |
+IssueAdviceView::DetailsView::DetailsView(int horizontal_space) |
+ : layout_(new views::GridLayout(this)), |
+ state_(0) { |
+ SetLayoutManager(layout_); |
+ views::ColumnSet* column_set = layout_->AddColumnSet(0); |
+ column_set->AddColumn(views::GridLayout::LEADING, |
+ views::GridLayout::LEADING, |
+ 0, |
+ views::GridLayout::FIXED, |
+ horizontal_space, |
+ 0); |
+} |
+ |
+void IssueAdviceView::DetailsView::AddDetail(const string16& detail) { |
+ layout_->StartRowWithPadding(0, 0, |
+ 0, views::kRelatedControlSmallVerticalSpacing); |
+ views::Label* detail_label = |
+ new views::Label(PrepareForDisplay(detail, true)); |
+ detail_label->SetMultiLine(true); |
+ detail_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); |
+ layout_->AddView(detail_label); |
+} |
+ |
+gfx::Size IssueAdviceView::DetailsView::GetPreferredSize() { |
+ gfx::Size size = views::View::GetPreferredSize(); |
+ return gfx::Size(size.width(), size.height() * state_); |
+} |
+ |
+void IssueAdviceView::DetailsView::AnimateToState(double state) { |
+ state_ = state; |
+ PreferredSizeChanged(); |
+ SchedulePaint(); |
+} |
+ |
+// IssueAdviceView ------------------------------------------------------------- |
+ |
+IssueAdviceView::IssueAdviceView(ExtensionInstallDialogView* owner, |
+ const IssueAdviceInfoEntry& issue_advice, |
+ int horizontal_space) |
+ : owner_(owner), |
+ details_view_(NULL), |
+ arrow_view_(NULL), |
+ slide_animation_(this) { |
+ // TODO(estade): replace this with a more appropriate image. |
+ const gfx::ImageSkia& image = *ui::ResourceBundle::GetSharedInstance(). |
+ GetImageSkiaNamed(IDR_OMNIBOX_TTS); |
+ |
+ views::GridLayout* layout = new views::GridLayout(this); |
+ SetLayoutManager(layout); |
+ int column_set_id = 0; |
+ views::ColumnSet* column_set = layout->AddColumnSet(column_set_id); |
+ if (!issue_advice.details.empty()) { |
+ column_set->AddColumn(views::GridLayout::LEADING, |
+ views::GridLayout::LEADING, |
+ 0, |
+ views::GridLayout::FIXED, |
+ image.width(), |
+ 0); |
+ horizontal_space -= image.width(); |
+ details_view_ = new DetailsView(horizontal_space); |
+ } |
+ column_set->AddColumn(views::GridLayout::LEADING, |
+ views::GridLayout::FILL, |
+ 0, |
+ views::GridLayout::FIXED, |
+ horizontal_space, |
+ 0); |
+ layout->StartRow(0, column_set_id); |
+ |
+ if (details_view_) { |
+ arrow_view_ = new views::ImageView(); |
+ arrow_view_->SetImage(image); |
+ arrow_view_->SetVerticalAlignment(views::ImageView::CENTER); |
+ layout->AddView(arrow_view_); |
+ } |
+ |
+ views::Label* description_label = |
+ new views::Label(PrepareForDisplay(issue_advice.description, |
+ !details_view_)); |
+ description_label->SetMultiLine(true); |
+ description_label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); |
+ description_label->SizeToFit(horizontal_space); |
+ layout->AddView(description_label); |
+ |
+ if (!details_view_) |
+ return; |
+ |
+ layout->StartRow(0, column_set_id); |
+ layout->SkipColumns(1); |
+ layout->AddView(details_view_); |
+ |
+ for (size_t i = 0; i < issue_advice.details.size(); ++i) |
+ details_view_->AddDetail(issue_advice.details[i]); |
+} |
+ |
+bool IssueAdviceView::OnMousePressed(const views::MouseEvent& event) { |
+ return details_view_ && event.IsLeftMouseButton(); |
+} |
+ |
+void IssueAdviceView::OnMouseReleased(const views::MouseEvent& event) { |
+ if (slide_animation_.IsShowing()) |
+ slide_animation_.Hide(); |
+ else |
+ slide_animation_.Show(); |
+} |
+ |
+void IssueAdviceView::AnimationProgressed(const ui::Animation* animation) { |
+ DCHECK_EQ(animation, &slide_animation_); |
+ |
+ if (details_view_) |
+ details_view_->AnimateToState(animation->GetCurrentValue()); |
+ |
+ if (arrow_view_) { |
+ ui::Transform rotate; |
+ if (animation->GetCurrentValue() != 0.0) { |
+ rotate.SetTranslate(-arrow_view_->width() / 2.0, |
+ -arrow_view_->height() / 2.0); |
+ // TODO(estade): for some reason there are rendering errors at 90 degrees. |
+ // Figure out why. |
+ rotate.ConcatRotate(animation->GetCurrentValue() * 89); |
+ rotate.ConcatTranslate(arrow_view_->width() / 2.0, |
+ arrow_view_->height() / 2.0); |
+ } |
+ arrow_view_->SetTransform(rotate); |
+ } |
+} |
+ |
+void IssueAdviceView::ChildPreferredSizeChanged(views::View* child) { |
+ owner_->SizeToContents(); |
+} |