Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6002)

Unified Diff: chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.cc

Issue 22265009: Implement initial version of scroll end effect Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Added SlideAnimation to smooth out transitions Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.cc
diff --git a/chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.cc b/chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.cc
index aa0642821e61bc5cfe4d5932bf978aca44f60377..296ecc9f45d4a5e404a6b5bb017f8c60ba134a4a 100644
--- a/chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.cc
+++ b/chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.cc
@@ -4,16 +4,345 @@
#include "chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.h"
-ScrollEndEffectController* ScrollEndEffectController::Create() {
- return new ScrollEndEffectControllerAsh();
+#include "chrome/browser/ui/views/download/download_shelf_view.h"
+#include "chrome/browser/ui/views/frame/browser_frame.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "content/public/browser/web_contents.h"
+#include "content/public/browser/web_contents_view.h"
+#include "ui/aura/window.h"
+#include "ui/base/animation/tween.h"
+#include "ui/compositor/layer_type.h"
+#include "ui/gfx/safe_integer_conversions.h"
+#include "ui/views/controls/single_split_view.h"
+#include "ui/views/controls/webview/webview.h"
+
+namespace {
+// This factor is used to control how much translation is applied relative to
+// the size of the window. Specifically translation is limited to a max of
+// window_size * factor.
+const float kScrollEndEffectFactor = 0.05;
+}
+
+// To achieve the desired effect to indicate vertical overscroll is occuring the
+// layer tree needs to be manipulated. Specifically two clipping layers are
+// inserted and the parenting of the web contents and non-client layers are
+// modified. These modifications are removed when the effect is deactivated.
+//
+// The |frame_clipping_layer_| is used to make sure that the bits of the layers
+// below do not peak out since the |browser_frame_layer| is going to be scaled
+// down in size, but some of the sub-layers are going to be counter scaled. This
+// clipping cannot be done in the |browser_frame_layer| since the shadows on the
+// window are outside the clipping bounds, but also children of that layer.
+//
+// The |web_clipping_layer_| is used to trim the web contents so that is does
+// not occlude the non-client elements. This is done to give the visual effect
+// of the contents slide under the top elements without having to break these
+// elements out into their own layer.
+//
+// Additionally the layer associated with the dev tools is promoted, so that it
+// isn't occuluded by the web contents layer.
+//
+// Layout of layer tree when effect is not active:
+// +----------------------------------------------------------+
+// | browser_frame_layer |
+// +-+--------------+---------------+-----------------------+-+
+// | | | |
+// | v v v
+// v +-------------------+ +---------------------+ +-----------------+
+// ... | non_client_layer_ | | web_contents_layer_ | | devtools_layer_ |
+// +-------------------+ +---------------------+ +-----------------+
+//
+//
+// Layout of the layer tree when effect is active:
+// +---------------------+
+// | browser_frame_layer |
+// +-+---------------+---+
+// | |
+// | v
+// v +-------------------------------------------------+
+// ... | frame_clipping_layer_ |
+// +-----+--------------+---------------------+------+
+// | | |
+// v v V
+// +-------------------+ +---------------------+ +-----------------+
+// | non_client_layer_ | | web_clipping_layer_ | | devtools_layer_ |
+// +-------------------+ +---------+-----------+ +-----------------+
+// |
+// v
+// +---------------------+
+// | web_contents_layer_ |
+// +---------------------+
+
+ScrollEndEffectController* ScrollEndEffectController::Create(
+ ScrollEndEffectControllerDelegate* delegate) {
+ return new ScrollEndEffectControllerAsh(delegate);
}
-ScrollEndEffectControllerAsh::ScrollEndEffectControllerAsh() {
+ScrollEndEffectControllerAsh::ScrollEndEffectControllerAsh(
+ ScrollEndEffectControllerDelegate* delegate)
+ : delegate_(delegate),
+ is_effect_active_(false),
+ non_client_layer_(NULL),
+ web_contents_layer_(NULL),
+ devtools_layer_(NULL),
+ web_contents_parent_(NULL),
+ devtools_parent_(NULL),
+ non_client_view_(NULL),
+ start_delta_y_(0),
+ end_delta_y_(0) {
+ animation_.reset(new ui::SlideAnimation(this));
+ animation_->SetSlideDuration(250);
+ animation_->SetTweenType(ui::Tween::EASE_OUT);
}
ScrollEndEffectControllerAsh::~ScrollEndEffectControllerAsh() {
}
void ScrollEndEffectControllerAsh::OverscrollUpdate(int delta_y) {
- // TODO(rharrison): Implement initial version of scroll end effect
+ if (delegate_ == NULL)
+ return;
+
+ if (!is_effect_active_ && delta_y == 0)
+ return;
+
+ ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
+ if (!browser_frame_layer)
+ return;
+
+ if (!is_effect_active_)
+ ActivateEffect();
+
+ int capped_delta_y = delta_y;
+ gfx::Rect bounds = browser_frame_layer->bounds();
+ // Limiting the delta size being a proportion of the frame bounds size.
+ if (capped_delta_y > 0) {
+ capped_delta_y = std::min(gfx::ToRoundedInt((bounds.height() *
+ kScrollEndEffectFactor)),
+ capped_delta_y);
+ } else if (capped_delta_y < 0) {
+ capped_delta_y = -std::min(gfx::ToRoundedInt((bounds.height() *
+ kScrollEndEffectFactor)),
+ -capped_delta_y);
+ }
+
+ ApplyDelta(end_delta_y_);
+ start_delta_y_ = end_delta_y_;
+ end_delta_y_ = capped_delta_y;
+ animation_->Reset();
+ animation_->Show();
+}
+
+void ScrollEndEffectControllerAsh::AnimationEnded(
+ const ui::Animation* animation) {
+ if (animation != animation_.get())
Ian Vollick 2013/09/05 02:33:48 Could there be another animation?
rharrison 2013/09/05 15:33:47 No there shouldn't be. I was being overly defensiv
+ return;
+
+ if (end_delta_y_ == 0)
+ DeactivateEffect();
+}
+
+void ScrollEndEffectControllerAsh::AnimationProgressed(
+ const ui::Animation* animation) {
+ if (animation != animation_.get())
+ return;
+
+ int current_delta = start_delta_y_ +
Ian Vollick 2013/09/05 02:33:48 I think you want to use ui::Tween::ValueBetween he
rharrison 2013/09/05 15:33:47 Done.
+ gfx::ToRoundedInt(animation_->GetCurrentValue() *
+ (end_delta_y_ - start_delta_y_));
+ ApplyDelta(current_delta);
+}
+
+void ScrollEndEffectControllerAsh::ActivateEffect() {
+ ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
+ if (!browser_frame_layer)
+ return;
+
+ is_effect_active_ = true;
+
+ // Get layers for all of the parts to be manipulated
+ non_client_view_ = delegate_->GetNonClientView();
+ non_client_layer_ = CreateViewLayer(non_client_view_);
+ web_contents_layer_ = delegate_->GetWebContentsLayer();
+ devtools_layer_ = delegate_->GetDevToolsLayer();
+ download_view_ = delegate_->GetDownloadView();
+ download_layer_ = CreateViewLayer(download_view_);
+
+ CHECK(non_client_layer_);
+ CHECK(web_contents_layer_);
+ CHECK(devtools_layer_);
+ CHECK(download_layer_);
+
+ frame_clipping_layer_.reset(CreateClippingLayer(
+ "OverscrollFrameClippingLayer"));
+ web_clipping_layer_.reset(CreateClippingLayer("OverscrollWebClippingLayer"));
+
+ // Save bounds for restoring later
+ devtools_bounds_ = devtools_layer_->bounds();
+ web_contents_bounds_ = web_contents_layer_->bounds();
+
+ // Adjust the toplogy of the layer tree to add clipping layers
+ devtools_parent_ = devtools_layer_->parent();
+ web_contents_parent_ = web_contents_layer_->parent();
+ browser_frame_layer->Add(frame_clipping_layer_.get());
+ frame_clipping_layer_->Add(non_client_layer_);
+ frame_clipping_layer_->Add(web_clipping_layer_.get());
+ frame_clipping_layer_->Add(devtools_layer_);
+ frame_clipping_layer_->Add(download_layer_);
+ web_clipping_layer_->Add(web_contents_layer_);
+}
+
+ui::Layer* ScrollEndEffectControllerAsh::CreateClippingLayer(std::string name) {
+ ui::Layer* layer = new ui::Layer(ui::LAYER_NOT_DRAWN);
+ layer->set_name(name);
+ layer->SetMasksToBounds(true);
+ layer->SetFillsBoundsOpaquely(false);
+ return layer;
+}
+
+ui::Layer* ScrollEndEffectControllerAsh::CreateViewLayer(views::View* view) {
+ view->SetPaintToLayer(true);
+ ui::Layer* layer = view->layer();
+ layer->SetFillsBoundsOpaquely(false);
+ view->parent()->SchedulePaint();
+ return layer;
+}
+
+void ScrollEndEffectControllerAsh::DeactivateEffect() {
+ ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
+ if (!browser_frame_layer)
+ return;
+
+ is_effect_active_ = false;
+
+ devtools_layer_->SetBounds(devtools_bounds_);
+ devtools_parent_->Add(devtools_layer_);
+ devtools_parent_ = NULL;
+ devtools_layer_ = NULL;
+
+ web_contents_layer_->SetBounds(web_contents_bounds_);
+ web_contents_parent_->Add(web_contents_layer_);
+ web_contents_parent_ = NULL;
+ web_contents_layer_ = NULL;
+
+ frame_clipping_layer_->Remove(download_layer_);
+ download_view_->SetPaintToLayer(false);
+ download_layer_ = NULL;
+ download_view_ = NULL;
+
+ frame_clipping_layer_->Remove(non_client_layer_);
+ non_client_view_->SetPaintToLayer(false);
+ non_client_layer_ = NULL;
+ non_client_view_ = NULL;
+
+ frame_clipping_layer_->Remove(web_clipping_layer_.get());
+ web_clipping_layer_.reset();
+ browser_frame_layer->Remove(frame_clipping_layer_.get());
+ frame_clipping_layer_.reset();
+ browser_frame_layer = NULL;
+}
+
+void ScrollEndEffectControllerAsh::ApplyDelta(int delta_y) {
+ SetBoundsForEffect();
+
+ ui::Layer* frame = delegate_->GetBrowserFrameLayer();
+ gfx::Rect bounds = frame->bounds();
+
+ float scale_factor = std::abs(delta_y);
+ scale_factor /= bounds.height();
+ scale_factor = 1 - scale_factor;
+
+ gfx::Transform frame_transform;
+ if (delta_y > 0) {
+ frame_transform.Translate(0, delta_y);
+ frame_transform.Scale(1, scale_factor);
+ } else {
+ frame_transform.Scale(1, scale_factor);
+ }
+ CHECK(frame_transform.GetInverse(&counter_transform_));
+ frame->SetTransform(frame_transform);
+
+ gfx::Transform download_transform =
+ CounterTransfromAboutPoint(0,
+ download_layer_->bounds().origin().y());
+ if (delta_y < 0)
+ download_transform.Translate(0, delta_y);
+ download_layer_->SetTransform(download_transform);
+
+ gfx::Transform devtools_transform =
+ CounterTransfromAboutPoint(0,
+ devtools_layer_->bounds().origin().y());
+ if (delta_y < 0)
+ devtools_transform.Translate(0, delta_y);
+ devtools_layer_->SetTransform(devtools_transform);
+
+ gfx::Transform web_clipping_transform =
+ CounterTransfromAboutPoint(0,
+ web_clipping_layer_->bounds().origin().y());
+ if (delta_y >= 0)
+ web_clipping_transform.Translate(0, delta_y);
+ web_clipping_layer_->SetTransform(web_clipping_transform);
+
+ if (delta_y <= 0) {
+ gfx::Transform web_contents_transform;
+ web_contents_transform.Translate(0, delta_y);
+ web_contents_layer_->SetTransform(web_contents_transform);
+ }
+
+ gfx::Transform non_client_transform = counter_transform_;
+ if (delta_y >= 0)
+ non_client_transform.Translate(0, delta_y);
+ non_client_layer_->SetTransform(non_client_transform);
+}
+
+void ScrollEndEffectControllerAsh::SetBoundsForEffect() {
+ ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
+ if (!browser_frame_layer)
+ return;
+
+ gfx::Rect frame_clipping_bounds = browser_frame_layer->bounds();
+ frame_clipping_bounds.set_origin(gfx::Point());
+ frame_clipping_layer_->SetBounds(frame_clipping_bounds);
+
+ gfx::Rect web_clipping_bounds = browser_frame_layer->bounds();
+ int download_height = download_view_->visible() ?
+ download_view_->bounds().height() : 0;
+ int non_client_top_height = browser_frame_layer->bounds().height() -
+ web_contents_layer_->bounds().height() -
+ delegate_->GetDevToolsHeight() -
+ delegate_->GetDividerHeight() -
+ download_height;
+ int web_clipping_height = browser_frame_layer->bounds().height() -
+ non_client_top_height -
+ delegate_->GetDevToolsHeight() -
+ delegate_->GetDividerHeight() -
+ download_height;
+
+ web_clipping_bounds.set_height(web_clipping_height);
+ web_clipping_bounds.set_origin(gfx::Point(0, non_client_top_height));
+ web_clipping_layer_->SetBounds(web_clipping_bounds);
+
+ gfx::Rect devtools_bounds = browser_frame_layer->bounds();
+ devtools_bounds.set_origin(gfx::Point(0,
+ browser_frame_layer->bounds().height() -
+ delegate_->GetDevToolsHeight() -
+ download_height));
+ devtools_bounds.set_height(delegate_->GetDevToolsHeight());
+ devtools_layer_->SetBounds(devtools_bounds);
+
+ // Move the web contents since bounds are relative to the parent layer
+ gfx::Rect web_contents_bounds = web_clipping_bounds;
+ web_contents_bounds.set_origin(gfx::Point(0, 0));
+ web_contents_layer_->SetBounds(web_contents_bounds);
+}
+
+gfx::Transform ScrollEndEffectControllerAsh::CounterTransfromAboutPoint(int x,
+ int y) {
+ gfx::Transform to_frame_transform;
+ to_frame_transform.Translate(-x, -y);
+ gfx::Transform from_frame_transform;
+ from_frame_transform.Translate(x, y);
+ gfx::Transform transform = counter_transform_;
+ transform.ConcatTransform(to_frame_transform);
+ transform.PreconcatTransform(from_frame_transform);
+ return transform;
}

Powered by Google App Engine
This is Rietveld 408576698