| Index: chrome/browser/ui/views/web_intent_picker_views.cc
|
| diff --git a/chrome/browser/ui/views/web_intent_picker_views.cc b/chrome/browser/ui/views/web_intent_picker_views.cc
|
| index f71d2f68ef491d43d89d396e37b0d363d6cf0b9c..75caf14f069b3ad1d1f0aa05676be7533e5c58e0 100644
|
| --- a/chrome/browser/ui/views/web_intent_picker_views.cc
|
| +++ b/chrome/browser/ui/views/web_intent_picker_views.cc
|
| @@ -5,6 +5,7 @@
|
| #include <algorithm>
|
| #include <vector>
|
|
|
| +#include "base/time.h"
|
| #include "base/memory/scoped_vector.h"
|
| #include "chrome/browser/ui/browser.h"
|
| #include "chrome/browser/ui/browser_navigator.h"
|
| @@ -27,6 +28,7 @@
|
| #include "grit/generated_resources.h"
|
| #include "grit/google_chrome_strings.h"
|
| #include "grit/theme_resources.h"
|
| +#include "grit/ui_resources.h"
|
| #include "grit/ui_resources_standard.h"
|
| #include "third_party/skia/include/core/SkColor.h"
|
| #include "ui/base/l10n/l10n_util.h"
|
| @@ -76,6 +78,9 @@ const SkColor kHalfOpacityWhite = SkColorSetARGB(128, 255, 255, 255);
|
| // The color used to display a disabled link.
|
| const SkColor kDisabledLinkColor = SkColorSetRGB(128, 128, 128);
|
|
|
| +// The time between successive throbber frames in milliseconds.
|
| +const int kThrobberFrameTimeMs = 50;
|
| +
|
| // Enables or disables all child views of |view|.
|
| void EnableChildViews(views::View* view, bool enabled) {
|
| for (int i = 0; i < view->child_count(); ++i) {
|
| @@ -125,6 +130,117 @@ StarsView::StarsView(double rating)
|
| StarsView::~StarsView() {
|
| }
|
|
|
| +// ThrobberNativeTextButton ----------------------------------------------------
|
| +
|
| +// A native text button that can display a throbber in place of its icon. Much
|
| +// of the logic of this class is copied from ui/views/controls/throbber.h.
|
| +class ThrobberNativeTextButton : public views::NativeTextButton {
|
| + public:
|
| + ThrobberNativeTextButton(views::ButtonListener* listener,
|
| + const string16& text);
|
| + virtual ~ThrobberNativeTextButton();
|
| +
|
| + // Start or stop the throbber.
|
| + void StartThrobber();
|
| + void StopThrobber();
|
| +
|
| + // Set the throbber bitmap to use. IDR_THROBBER is used by default.
|
| + void SetFrames(const SkBitmap* frames);
|
| +
|
| + protected:
|
| + virtual const SkBitmap& GetImageToPaint() const OVERRIDE;
|
| +
|
| + private:
|
| + // The timer callback to schedule painting this view.
|
| + void Run();
|
| +
|
| + // Bitmap that contains the throbber frames.
|
| + const SkBitmap* frames_;
|
| +
|
| + // The currently displayed frame, given to GetImageToPaint.
|
| + mutable SkBitmap this_frame_;
|
| +
|
| + // How long one frame is displayed.
|
| + base::TimeDelta frame_time_;
|
| +
|
| + // Used to schedule Run calls.
|
| + base::RepeatingTimer<ThrobberNativeTextButton> timer_;
|
| +
|
| + // How many frames we have.
|
| + int frame_count_;
|
| +
|
| + // Time when StartThrobber was called.
|
| + base::TimeTicks start_time_;
|
| +
|
| + // Whether the throbber is shown an animating.
|
| + bool running_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ThrobberNativeTextButton);
|
| +};
|
| +
|
| +ThrobberNativeTextButton::ThrobberNativeTextButton(
|
| + views::ButtonListener* listener, const string16& text)
|
| + : NativeTextButton(listener, text),
|
| + frame_time_(base::TimeDelta::FromMilliseconds(kThrobberFrameTimeMs)),
|
| + frame_count_(0),
|
| + running_(false) {
|
| + SetFrames(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
|
| + IDR_THROBBER).ToSkBitmap());
|
| +}
|
| +
|
| +ThrobberNativeTextButton::~ThrobberNativeTextButton() {
|
| + StopThrobber();
|
| +}
|
| +
|
| +void ThrobberNativeTextButton::StartThrobber() {
|
| + if (running_)
|
| + return;
|
| +
|
| + start_time_ = base::TimeTicks::Now();
|
| + timer_.Start(FROM_HERE, frame_time_, this, &ThrobberNativeTextButton::Run);
|
| + running_ = true;
|
| +
|
| + SchedulePaint();
|
| +}
|
| +
|
| +void ThrobberNativeTextButton::StopThrobber() {
|
| + if (!running_)
|
| + return;
|
| +
|
| + timer_.Stop();
|
| + running_ = false;
|
| +}
|
| +
|
| +void ThrobberNativeTextButton::SetFrames(const SkBitmap* frames) {
|
| + frames_ = frames;
|
| + DCHECK(frames_->width() > 0 && frames_->height() > 0);
|
| + DCHECK(frames_->width() % frames_->height() == 0);
|
| + frame_count_ = frames_->width() / frames_->height();
|
| + PreferredSizeChanged();
|
| +}
|
| +
|
| +const SkBitmap& ThrobberNativeTextButton::GetImageToPaint() const {
|
| + if (!running_)
|
| + return NativeTextButton::GetImageToPaint();
|
| +
|
| + const base::TimeDelta elapsed_time = base::TimeTicks::Now() - start_time_;
|
| + const int current_frame =
|
| + static_cast<int>(elapsed_time / frame_time_) % frame_count_;
|
| + const int image_size = frames_->height();
|
| + const int image_offset = current_frame * image_size;
|
| +
|
| + SkIRect subset_rect = SkIRect::MakeXYWH(image_offset, 0,
|
| + image_size, image_size);
|
| + frames_->extractSubset(&this_frame_, subset_rect);
|
| + return this_frame_;
|
| +}
|
| +
|
| +void ThrobberNativeTextButton::Run() {
|
| + DCHECK(running_);
|
| +
|
| + SchedulePaint();
|
| +}
|
| +
|
| // ServiceButtonsView ----------------------------------------------------------
|
|
|
| // A view that contains all service buttons (i.e. the installed services).
|
| @@ -148,6 +264,10 @@ class ServiceButtonsView : public views::View,
|
| // Updates the service button view with new model data.
|
| void Update();
|
|
|
| + // Start a throbber on the service button that will launch the service at
|
| + // |url|.
|
| + void StartThrobber(const GURL& url);
|
| +
|
| // views::ButtonListener implementation.
|
| virtual void ButtonPressed(views::Button* sender,
|
| const views::Event& event) OVERRIDE;
|
| @@ -194,8 +314,8 @@ void ServiceButtonsView::Update() {
|
|
|
| grid_layout->StartRow(0, 0);
|
|
|
| - views::NativeTextButton* button =
|
| - new views::NativeTextButton(this, service.title);
|
| + ThrobberNativeTextButton* button =
|
| + new ThrobberNativeTextButton(this, service.title);
|
| button->set_alignment(views::TextButton::ALIGN_LEFT);
|
| button->SetTooltipText(UTF8ToUTF16(service.url.spec().c_str()));
|
| button->SetIcon(*service.favicon.ToSkBitmap());
|
| @@ -208,6 +328,20 @@ void ServiceButtonsView::Update() {
|
| grid_layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing);
|
| }
|
|
|
| +void ServiceButtonsView::StartThrobber(const GURL& url) {
|
| + for (size_t i = 0; i < model_->GetInstalledServiceCount(); ++i) {
|
| + const WebIntentPickerModel::InstalledService& service =
|
| + model_->GetInstalledServiceAt(i);
|
| + if (service.url != url)
|
| + continue;
|
| +
|
| + ThrobberNativeTextButton* button =
|
| + static_cast<ThrobberNativeTextButton*>(child_at(i));
|
| + button->StartThrobber();
|
| + return;
|
| + }
|
| +}
|
| +
|
| void ServiceButtonsView::ButtonPressed(views::Button* sender,
|
| const views::Event& event) {
|
| size_t index = static_cast<size_t>(sender->tag());
|
| @@ -351,14 +485,11 @@ class SuggestedExtensionsRowView : public views::View,
|
| // this extension.
|
| views::Link* title_link_;
|
|
|
| - // A throbber to display when the extension is being installed.
|
| - views::Throbber* throbber_;
|
| -
|
| // The star rating of this extension.
|
| StarsView* stars_;
|
|
|
| // A button to install the extension.
|
| - views::NativeTextButton* install_button_;
|
| + ThrobberNativeTextButton* install_button_;
|
|
|
| DISALLOW_COPY_AND_ASSIGN(SuggestedExtensionsRowView);
|
| };
|
| @@ -380,14 +511,10 @@ SuggestedExtensionsRowView::SuggestedExtensionsRowView(
|
| title_link_->set_listener(this);
|
| AddChildView(title_link_);
|
|
|
| - throbber_ = new views::Throbber(60, true);
|
| - throbber_->SetVisible(false);
|
| - AddChildView(throbber_);
|
| -
|
| stars_ = new StarsView(extension_->average_rating);
|
| AddChildView(stars_);
|
|
|
| - install_button_= new views::NativeTextButton(
|
| + install_button_= new ThrobberNativeTextButton(
|
| this, l10n_util::GetStringUTF16(IDS_INTENT_PICKER_INSTALL_EXTENSION));
|
| AddChildView(install_button_);
|
| }
|
| @@ -406,25 +533,20 @@ void SuggestedExtensionsRowView::LinkClicked(views::Link* source,
|
| }
|
|
|
| void SuggestedExtensionsRowView::StartThrobber() {
|
| - stars_->SetVisible(false);
|
| - install_button_->SetVisible(false);
|
| - throbber_->SetVisible(true);
|
| - throbber_->Start();
|
| - Layout();
|
| + install_button_->StartThrobber();
|
| + install_button_->SetText(string16());
|
| }
|
|
|
| void SuggestedExtensionsRowView::StopThrobber() {
|
| - stars_->SetVisible(true);
|
| - install_button_->SetVisible(true);
|
| - throbber_->SetVisible(false);
|
| - throbber_->Stop();
|
| - Layout();
|
| + install_button_->StopThrobber();
|
| + install_button_->SetText(
|
| + l10n_util::GetStringUTF16(IDS_INTENT_PICKER_INSTALL_EXTENSION));
|
| }
|
|
|
| void SuggestedExtensionsRowView::OnEnabledChanged() {
|
| title_link_->SetEnabled(enabled());
|
| - stars_->SetVisible(enabled());
|
| - install_button_->SetVisible(enabled());
|
| + stars_->SetEnabled(enabled());
|
| + install_button_->SetEnabled(enabled());
|
| View::OnEnabledChanged();
|
| Layout();
|
| }
|
| @@ -439,8 +561,6 @@ void SuggestedExtensionsRowView::PaintChildren(gfx::Canvas* canvas) {
|
|
|
| // A view that contains suggested extensions from the Chrome Web Store that
|
| // provide an intent service matching the action/type pair.
|
| -// This view also displays the "More suggestions" link which searches the
|
| -// Chrome Web Store for more extensions.
|
| class SuggestedExtensionsView : public views::View {
|
| public:
|
| SuggestedExtensionsView(const WebIntentPickerModel* model,
|
| @@ -460,6 +580,9 @@ class SuggestedExtensionsView : public views::View {
|
| // Hide the install throbber. This function re-enables all buttons and links.
|
| void StopThrobber();
|
|
|
| + protected:
|
| + virtual void OnEnabledChanged() OVERRIDE;
|
| +
|
| private:
|
| const WebIntentPickerModel* model_;
|
| SuggestedExtensionsRowView::Delegate* delegate_;
|
| @@ -518,6 +641,11 @@ void SuggestedExtensionsView::StopThrobber() {
|
| }
|
| }
|
|
|
| +void SuggestedExtensionsView::OnEnabledChanged() {
|
| + EnableChildViews(this, enabled());
|
| + View::OnEnabledChanged();
|
| +}
|
| +
|
| } // namespace
|
|
|
| // WebIntentPickerViews --------------------------------------------------------
|
| @@ -557,6 +685,8 @@ class WebIntentPickerViews : public views::ButtonListener,
|
| virtual void SetActionString(const string16& action) OVERRIDE;
|
| virtual void OnExtensionInstallSuccess(const std::string& id) OVERRIDE;
|
| virtual void OnExtensionInstallFailure(const std::string& id) OVERRIDE;
|
| + virtual void OnInlineDispositionWebContentsLoaded(
|
| + content::WebContents* web_contents) OVERRIDE;
|
|
|
| // WebIntentPickerModelObserver implementation.
|
| virtual void OnModelChanged(WebIntentPickerModel* model) OVERRIDE;
|
| @@ -625,6 +755,10 @@ class WebIntentPickerViews : public views::ButtonListener,
|
| // A weak pointer to the choose another service link.
|
| views::Link* choose_another_service_link_;
|
|
|
| + // Set to true when displaying the inline disposition web contents. Used to
|
| + // prevent laying out the inline disposition widgets twice.
|
| + bool displaying_web_contents_;
|
| +
|
| DISALLOW_COPY_AND_ASSIGN(WebIntentPickerViews);
|
| };
|
|
|
| @@ -653,7 +787,8 @@ WebIntentPickerViews::WebIntentPickerViews(Browser* browser,
|
| contents_(NULL),
|
| window_(NULL),
|
| more_suggestions_link_(NULL),
|
| - choose_another_service_link_(NULL) {
|
| + choose_another_service_link_(NULL),
|
| + displaying_web_contents_(false) {
|
| model_->set_observer(this);
|
| InitContents();
|
|
|
| @@ -733,58 +868,10 @@ void WebIntentPickerViews::OnExtensionInstallFailure(const std::string& id) {
|
| // TODO(binji): What to display to user on failure?
|
| }
|
|
|
| -void WebIntentPickerViews::OnModelChanged(WebIntentPickerModel* model) {
|
| - if (model->GetInstalledServiceCount() == 0) {
|
| - suggestions_label_->SetText(l10n_util::GetStringUTF16(
|
| - IDS_INTENT_PICKER_GET_MORE_SERVICES_NONE_INSTALLED));
|
| - } else {
|
| - suggestions_label_->SetText(
|
| - l10n_util::GetStringUTF16(IDS_INTENT_PICKER_GET_MORE_SERVICES));
|
| - }
|
| -
|
| - service_buttons_->Update();
|
| - extensions_->Update();
|
| - contents_->Layout();
|
| - SizeToContents();
|
| -}
|
| -
|
| -void WebIntentPickerViews::OnFaviconChanged(
|
| - WebIntentPickerModel* model, size_t index) {
|
| - service_buttons_->Update();
|
| - contents_->Layout();
|
| - SizeToContents();
|
| -}
|
| -
|
| -void WebIntentPickerViews::OnExtensionIconChanged(
|
| - WebIntentPickerModel* model,
|
| - const string16& extension_id) {
|
| - extensions_->Update();
|
| - contents_->Layout();
|
| - SizeToContents();
|
| -}
|
| -
|
| -void WebIntentPickerViews::OnInlineDisposition(
|
| - WebIntentPickerModel* model, const GURL& url) {
|
| - WebContents* web_contents = WebContents::Create(
|
| - browser_->profile(), NULL, MSG_ROUTING_NONE, NULL, NULL);
|
| - inline_disposition_delegate_.reset(new WebIntentInlineDispositionDelegate);
|
| - web_contents->SetDelegate(inline_disposition_delegate_.get());
|
| -
|
| - const WebIntentPickerModel::InstalledService* service =
|
| - model->GetInstalledServiceWithURL(url);
|
| - DCHECK(service);
|
| -
|
| - // Must call this immediately after WebContents creation to avoid race
|
| - // with load.
|
| - delegate_->OnInlineDispositionWebContentsCreated(web_contents);
|
| -
|
| - TabContentsContainer* tab_contents_container = new TabContentsContainer;
|
| -
|
| - web_contents->GetController().LoadURL(
|
| - url,
|
| - content::Referrer(),
|
| - content::PAGE_TRANSITION_START_PAGE,
|
| - std::string());
|
| +void WebIntentPickerViews::OnInlineDispositionWebContentsLoaded(
|
| + content::WebContents* web_contents) {
|
| + if (displaying_web_contents_)
|
| + return;
|
|
|
| // Replace the picker with the inline disposition.
|
| contents_->RemoveAllChildViews(true);
|
| @@ -813,6 +900,9 @@ void WebIntentPickerViews::OnInlineDisposition(
|
| full_cs->AddColumn(GridLayout::FILL, GridLayout::FILL, 0,
|
| GridLayout::USE_PREF, 0, 0);
|
|
|
| + const WebIntentPickerModel::InstalledService* service =
|
| + model_->GetInstalledServiceWithURL(model_->inline_disposition_url());
|
| +
|
| // Header row.
|
| grid_layout->StartRow(0, 0);
|
| views::ImageView* icon = new views::ImageView();
|
| @@ -834,17 +924,77 @@ void WebIntentPickerViews::OnInlineDisposition(
|
|
|
| // Inline web contents row.
|
| grid_layout->StartRow(0, 1);
|
| + TabContentsContainer* tab_contents_container = new TabContentsContainer;
|
| grid_layout->AddView(tab_contents_container, 1, 1, GridLayout::CENTER,
|
| GridLayout::CENTER, kDialogMinWidth, 140);
|
|
|
| // The contents can only be changed after the child is added to view
|
| // hierarchy.
|
| tab_contents_container->ChangeWebContents(web_contents);
|
| + contents_->Layout();
|
| + SizeToContents();
|
| + displaying_web_contents_ = true;
|
| +}
|
| +
|
| +void WebIntentPickerViews::OnModelChanged(WebIntentPickerModel* model) {
|
| + if (model->GetInstalledServiceCount() == 0) {
|
| + suggestions_label_->SetText(l10n_util::GetStringUTF16(
|
| + IDS_INTENT_PICKER_GET_MORE_SERVICES_NONE_INSTALLED));
|
| + } else {
|
| + suggestions_label_->SetText(
|
| + l10n_util::GetStringUTF16(IDS_INTENT_PICKER_GET_MORE_SERVICES));
|
| + }
|
| +
|
| + service_buttons_->Update();
|
| + extensions_->Update();
|
| + contents_->Layout();
|
| + SizeToContents();
|
| +}
|
| +
|
| +void WebIntentPickerViews::OnFaviconChanged(
|
| + WebIntentPickerModel* model, size_t index) {
|
| + service_buttons_->Update();
|
| + contents_->Layout();
|
| + SizeToContents();
|
| +}
|
|
|
| +void WebIntentPickerViews::OnExtensionIconChanged(
|
| + WebIntentPickerModel* model,
|
| + const string16& extension_id) {
|
| + extensions_->Update();
|
| contents_->Layout();
|
| SizeToContents();
|
| }
|
|
|
| +void WebIntentPickerViews::OnInlineDisposition(
|
| + WebIntentPickerModel* model, const GURL& url) {
|
| + WebContents* web_contents = WebContents::Create(
|
| + browser_->profile(), NULL, MSG_ROUTING_NONE, NULL, NULL);
|
| + inline_disposition_delegate_.reset(
|
| + new WebIntentInlineDispositionDelegate(this));
|
| + web_contents->SetDelegate(inline_disposition_delegate_.get());
|
| +
|
| + const WebIntentPickerModel::InstalledService* service =
|
| + model->GetInstalledServiceWithURL(url);
|
| + DCHECK(service);
|
| +
|
| + // Must call this immediately after WebContents creation to avoid race
|
| + // with load.
|
| + delegate_->OnInlineDispositionWebContentsCreated(web_contents);
|
| + web_contents->GetController().LoadURL(
|
| + url,
|
| + content::Referrer(),
|
| + content::PAGE_TRANSITION_START_PAGE,
|
| + std::string());
|
| +
|
| + // Disable all buttons and show throbber.
|
| + service_buttons_->SetEnabled(false);
|
| + service_buttons_->StartThrobber(url);
|
| + extensions_->SetEnabled(false);
|
| + more_suggestions_link_->SetEnabled(false);
|
| + contents_->Layout();
|
| +}
|
| +
|
| void WebIntentPickerViews::OnServiceButtonClicked(
|
| const WebIntentPickerModel::InstalledService& service) {
|
| delegate_->OnServiceChosen(service.url, service.disposition);
|
|
|