| Index: chrome/browser/ui/views/tabs/tab.cc
|
| diff --git a/chrome/browser/ui/views/tabs/tab.cc b/chrome/browser/ui/views/tabs/tab.cc
|
| index c828f1f42e515968c7d11840eec806832e05f77a..b6056cde652713cdf221936659d24353faf4c875 100644
|
| --- a/chrome/browser/ui/views/tabs/tab.cc
|
| +++ b/chrome/browser/ui/views/tabs/tab.cc
|
| @@ -30,6 +30,8 @@
|
| #include "ui/base/models/list_selection_model.h"
|
| #include "ui/base/resource/resource_bundle.h"
|
| #include "ui/base/theme_provider.h"
|
| +#include "ui/compositor/layer_animator.h"
|
| +#include "ui/compositor/paint_recorder.h"
|
| #include "ui/gfx/animation/animation_container.h"
|
| #include "ui/gfx/animation/multi_animation.h"
|
| #include "ui/gfx/animation/throb_animation.h"
|
| @@ -137,65 +139,6 @@ const int kImmersiveLoadingStepCount = 32;
|
| const char kTabCloseButtonName[] = "TabCloseButton";
|
| const int kTabCloseButtonSize = 16;
|
|
|
| -// Layer-backed view for updating a waiting or loading tab spinner.
|
| -class ThrobberView : public views::View {
|
| - public:
|
| - explicit ThrobberView(Tab* owner) : owner_(owner) {
|
| - // Since the throbber animates, paint to a separate layer do reduce repaint
|
| - // overheads.
|
| - SetPaintToLayer(true);
|
| - SetFillsBoundsOpaquely(false);
|
| - }
|
| -
|
| - // views::View:
|
| - bool CanProcessEventsWithinSubtree() const override { return false; }
|
| -
|
| - void OnPaint(gfx::Canvas* canvas) override {
|
| - const TabRendererData::NetworkState state = owner_->data().network_state;
|
| - if (state == TabRendererData::NETWORK_STATE_NONE)
|
| - return;
|
| -
|
| - const gfx::Rect bounds = GetLocalBounds();
|
| -
|
| - // Paint network activity (aka throbber) animation frame.
|
| - ui::ThemeProvider* tp = GetThemeProvider();
|
| - if (state == TabRendererData::NETWORK_STATE_WAITING) {
|
| - if (waiting_start_time_ == base::TimeTicks())
|
| - waiting_start_time_ = base::TimeTicks::Now();
|
| -
|
| - waiting_state_.elapsed_time =
|
| - base::TimeTicks::Now() - waiting_start_time_;
|
| - gfx::PaintThrobberWaiting(
|
| - canvas, bounds, tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING),
|
| - waiting_state_.elapsed_time);
|
| - } else {
|
| - if (loading_start_time_ == base::TimeTicks())
|
| - loading_start_time_ = base::TimeTicks::Now();
|
| -
|
| - waiting_state_.color =
|
| - tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING);
|
| - gfx::PaintThrobberSpinningAfterWaiting(
|
| - canvas, bounds,
|
| - tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING),
|
| - base::TimeTicks::Now() - loading_start_time_, &waiting_state_);
|
| - }
|
| - }
|
| -
|
| - private:
|
| - Tab* owner_; // Weak. Owns |this|.
|
| -
|
| - // The point in time when the tab icon was first painted in the waiting state.
|
| - base::TimeTicks waiting_start_time_;
|
| -
|
| - // The point in time when the tab icon was first painted in the loading state.
|
| - base::TimeTicks loading_start_time_;
|
| -
|
| - // Paint state for the throbber after the most recent waiting paint.
|
| - gfx::ThrobberWaitingState waiting_state_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(ThrobberView);
|
| -};
|
| -
|
| void DrawIconAtLocation(gfx::Canvas* canvas,
|
| const gfx::ImageSkia& image,
|
| int image_offset,
|
| @@ -451,6 +394,170 @@ Tab::ImageCacheEntry::ImageCacheEntry()
|
| Tab::ImageCacheEntry::~ImageCacheEntry() {}
|
|
|
| ////////////////////////////////////////////////////////////////////////////////
|
| +// ThrobberView
|
| +
|
| +// Layer-backed view for updating a waiting or loading tab spinner.
|
| +class Tab::ThrobberView : public views::View {
|
| + public:
|
| + explicit ThrobberView(Tab* owner) : owner_(owner), waiting_arc_(180) {
|
| + // Since the throbber animates, paint to a separate layer do reduce repaint
|
| + // overheads.
|
| + SetPaintToLayer(true);
|
| + SetFillsBoundsOpaquely(false);
|
| +
|
| + waiting_mask_.SetFillsBoundsOpaquely(false);
|
| + waiting_mask_.SetMasksToBounds(true);
|
| + waiting_mask_.Add(waiting_arc_.layer());
|
| + }
|
| +
|
| + void SchedulePaintIfRequired() {
|
| + if (NeedsPaint())
|
| + SchedulePaint();
|
| + }
|
| +
|
| + // views::View:
|
| + bool CanProcessEventsWithinSubtree() const override { return false; }
|
| +
|
| + void OnBoundsChanged(const gfx::Rect& previous_bounds) override {
|
| + gfx::Rect bounds = GetLocalBounds();
|
| + waiting_arc_.layer()->SetBounds(
|
| + gfx::Rect(0, 0, bounds.width() * Arc::kAA, bounds.height() * Arc::kAA));
|
| +
|
| + bounds.set_width(bounds.width() / 2);
|
| + waiting_mask_.SetBounds(bounds);
|
| + }
|
| +
|
| + void OnPaint(gfx::Canvas* canvas) override {
|
| + if (state_ == TabRendererData::NETWORK_STATE_NONE)
|
| + return;
|
| +
|
| + const gfx::Rect bounds = GetLocalBounds();
|
| +
|
| + // Paint network activity (aka throbber) animation frame.
|
| + ui::ThemeProvider* tp = owner_->GetThemeProvider();
|
| + if (state_ == TabRendererData::NETWORK_STATE_WAITING) {
|
| + // Painted by Arc.
|
| + } else {
|
| + if (loading_start_time_ == base::TimeTicks())
|
| + loading_start_time_ = base::TimeTicks::Now();
|
| +
|
| + waiting_state_.color =
|
| + tp->GetColor(ThemeProperties::COLOR_THROBBER_WAITING);
|
| + gfx::PaintThrobberSpinningAfterWaiting(
|
| + canvas, bounds,
|
| + tp->GetColor(ThemeProperties::COLOR_THROBBER_SPINNING),
|
| + base::TimeTicks::Now() - loading_start_time_, &waiting_state_);
|
| + }
|
| + }
|
| +
|
| + private:
|
| + class Arc : public ui::LayerDelegate {
|
| + public:
|
| + // Since the rotation transform mis-aligns the pixel anti-aliasing done by
|
| + // Skia, perform a kind of FSAA by drawing on a larger canvas and scaling
|
| + // down as part of the transform.
|
| + static const int kAA = 1;
|
| +
|
| + explicit Arc(SkScalar sweep)
|
| + : color_(SK_ColorRED), sweep_(sweep), layer_(ui::LAYER_TEXTURED) {
|
| + layer_.set_delegate(this);
|
| + layer_.SetFillsBoundsOpaquely(false);
|
| + }
|
| +
|
| + ui::Layer* layer() { return &layer_; }
|
| +
|
| + void set_color(SkColor color) { color_ = color; }
|
| +
|
| + void SetAngle(SkScalar angle) {
|
| + const gfx::Size size = layer()->size();
|
| + gfx::Transform transform;
|
| + transform.Translate(size.width() / 2.0 / kAA, size.height() / 2.0 / kAA);
|
| + transform.Rotate(-angle);
|
| + transform.Scale(1.0 / kAA, 1.0 / kAA);
|
| + transform.Translate(-size.width() / 2, -size.height() / 2);
|
| + layer()->SetTransform(transform);
|
| + }
|
| +
|
| + // LayerDelegate:
|
| + void OnPaintLayer(const ui::PaintContext& context) override {
|
| + const gfx::Size size = layer()->size();
|
| + ui::PaintRecorder recorder(context, size);
|
| + gfx::PaintThrobberArc(recorder.canvas(), gfx::Rect(size), color_, -90,
|
| + sweep_);
|
| + }
|
| +
|
| + void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {}
|
| + void OnDeviceScaleFactorChanged(float device_scale_factor) override {}
|
| + base::Closure PrepareForLayerBoundsChange() override {
|
| + return base::Closure();
|
| + }
|
| +
|
| + private:
|
| + SkColor color_;
|
| + SkScalar sweep_;
|
| + ui::Layer layer_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(Arc);
|
| + };
|
| +
|
| + void ApplyWaitingRotation(const base::TimeDelta& elapsed_time) {
|
| + const base::TimeDelta revolution_time =
|
| + base::TimeDelta::FromMilliseconds(1320);
|
| + bool needs_mask = elapsed_time < revolution_time / 2;
|
| + waiting_mask_.SetMasksToBounds(needs_mask);
|
| + waiting_arc_.SetAngle(360 * waiting_state_.elapsed_time / revolution_time);
|
| + waiting_arc_.set_color(
|
| + GetThemeProvider()->GetColor(ThemeProperties::COLOR_THROBBER_WAITING));
|
| + }
|
| +
|
| + bool NeedsPaint() {
|
| + if (bounds().IsEmpty())
|
| + return false;
|
| +
|
| + TabRendererData::NetworkState new_state = owner_->data().network_state;
|
| + const bool changing_state = new_state != state_;
|
| +
|
| + // Waiting throbber is fully layer-backed.
|
| + if (new_state == TabRendererData::NETWORK_STATE_WAITING) {
|
| + if (waiting_start_time_ == base::TimeTicks())
|
| + waiting_start_time_ = base::TimeTicks::Now();
|
| +
|
| + state_ = new_state;
|
| + waiting_state_.elapsed_time =
|
| + base::TimeTicks::Now() - waiting_start_time_;
|
| + layer()->Add(&waiting_mask_);
|
| + ApplyWaitingRotation(waiting_state_.elapsed_time);
|
| + return changing_state;
|
| + }
|
| +
|
| + if (state_ == TabRendererData::NETWORK_STATE_WAITING)
|
| + layer()->Remove(&waiting_mask_);
|
| +
|
| + state_ = new_state;
|
| +
|
| + return true;
|
| + }
|
| +
|
| + Tab* owner_; // Weak. Owns this.
|
| +
|
| + TabRendererData::NetworkState state_ = TabRendererData::NETWORK_STATE_NONE;
|
| +
|
| + // The point in time when the tab icon was first painted in the waiting state.
|
| + base::TimeTicks waiting_start_time_;
|
| +
|
| + // The point in time when the tab icon was first painted in the loading state.
|
| + base::TimeTicks loading_start_time_;
|
| +
|
| + // Paint state for the throbber after the most recent waiting paint.
|
| + gfx::ThrobberWaitingState waiting_state_;
|
| +
|
| + ui::Layer waiting_mask_;
|
| + Arc waiting_arc_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ThrobberView);
|
| +};
|
| +
|
| +////////////////////////////////////////////////////////////////////////////////
|
| // Tab, statics:
|
|
|
| // static
|
| @@ -1460,7 +1567,7 @@ void Tab::AdvanceLoadingAnimation(TabRendererData::NetworkState state) {
|
| ScheduleIconPaint(); // Repaint the icon area to update favicon visibility.
|
| throbber_->SetVisible(needs_throbber);
|
| }
|
| - throbber_->SchedulePaint();
|
| + throbber_->SchedulePaintIfRequired();
|
| }
|
|
|
| int Tab::IconCapacity() const {
|
|
|