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

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: Code cleanup for sadrul@ 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..b118c8db8dba9b5636897abca4a6d8e1dc378ce0 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,352 @@
#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;
+
+// Utility methods used to create needed layers.
+ui::Layer* CreateClippingLayer(const 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;
}
-ScrollEndEffectControllerAsh::ScrollEndEffectControllerAsh() {
+ui::Layer* CreateViewLayer(views::View* view) {
+ view->SetPaintToLayer(true);
+ ui::Layer* layer = view->layer();
+ layer->SetFillsBoundsOpaquely(false);
+ view->parent()->SchedulePaint();
sky 2013/09/05 21:12:57 Why do you need the SchedulePaint here?
rharrison 2013/09/09 20:01:17 Without it there is a stale version of the parent
+ return layer;
+}
+
+} // namespace
+
+// 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 |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 |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:
+// +----------------------------------------------------------+
+// | frame_layer_ |
+// +-+--------------+---------------+-----------------------+-+
+// | | | |
+// | v v v
+// v +-------------------+ +---------------------+ +-----------------+
+// ... | non_client_layer_ | | web_contents_layer_ | | devtools_layer_ |
+// +-------------------+ +---------------------+ +-----------------+
+//
+//
+// Layout of the layer tree when effect is active:
+// +---------------------+
+// | 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(
+ ScrollEndEffectControllerDelegate* delegate)
+ : delegate_(delegate),
+ is_effect_active_(false),
+ frame_layer_(NULL),
+ non_client_layer_(NULL),
+ web_contents_layer_(NULL),
+ devtools_layer_(NULL),
+ web_contents_parent_(NULL),
sky 2013/09/05 21:12:57 Make sure you initialize everything (looks like yo
rharrison 2013/09/09 20:01:17 Done.
+ devtools_parent_(NULL),
+ non_client_view_(NULL),
+ start_delta_y_(0),
+ current_delta_y_(0),
+ end_delta_y_(0) {
+ animation_.reset(new ui::SlideAnimation(this));
+ animation_->SetSlideDuration(100);
+ animation_->SetTweenType(ui::Tween::EASE_OUT);
}
ScrollEndEffectControllerAsh::~ScrollEndEffectControllerAsh() {
+ if (is_effect_active_)
+ DeactivateEffect();
sky 2013/09/05 21:12:57 Did you verify you don't crash if you end up here?
rharrison 2013/09/09 20:01:17 It does crash, removed the code, since I don't thi
}
void ScrollEndEffectControllerAsh::OverscrollUpdate(int delta_y) {
- // TODO(rharrison): Implement initial version of scroll end effect
+ if (delegate_ == NULL)
sky 2013/09/05 21:12:57 You never delegate_ to NULL, so why this check?
rharrison 2013/09/09 20:01:17 Done.
+ return;
+
+ if (!is_effect_active_ && delta_y == 0)
+ return;
+
+ frame_layer_ = delegate_->GetBrowserFrameLayer();
sky 2013/09/05 21:12:57 Seems like you should only set frame_layer_ if you
rharrison 2013/09/09 20:01:17 Done.
+ if (!frame_layer_)
+ return;
+
+ if (!is_effect_active_)
+ ActivateEffect();
+
+ int capped_delta_y = delta_y;
+ gfx::Rect bounds = 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);
+ }
+
+ start_delta_y_ = current_delta_y_;
+ end_delta_y_ = capped_delta_y;
+ if (end_delta_y_ == 0) {
+ animation_->Reset();
+ animation_->Show();
+ } else {
+ animation_->Reset();
+ ApplyDelta(end_delta_y_);
+ }
+}
+
+void ScrollEndEffectControllerAsh::AnimationEnded(
+ const ui::Animation* animation) {
+ if (current_delta_y_ == 0)
+ DeactivateEffect();
+}
+
+void ScrollEndEffectControllerAsh::AnimationProgressed(
+ const ui::Animation* animation) {
+ ApplyDelta(ui::Tween::ValueBetween(animation_->GetCurrentValue(),
+ start_delta_y_,
+ end_delta_y_));
+}
+
+void ScrollEndEffectControllerAsh::ActivateEffect() {
+ 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_ = download_view_ ? CreateViewLayer(download_view_) :
+ NULL;
+
+ CHECK(non_client_layer_);
sky 2013/09/05 21:12:57 DCHECK on these
rharrison 2013/09/09 20:01:17 Done.
+ CHECK(web_contents_layer_);
+ CHECK(devtools_layer_);
+
+ frame_clipping_layer_.reset(CreateClippingLayer(
+ "OverscrollFrameClippingLayer"));
+ web_clipping_layer_.reset(CreateClippingLayer("OverscrollWebClippingLayer"));
+
+ // Save bounds for restoring later
+ devtools_bounds_ = devtools_layer_->bounds();
sky 2013/09/05 21:12:57 How do you know these bounds are going to be right
rharrison 2013/09/09 20:01:17 The size of the devtools area should not change du
+ web_contents_bounds_ = web_contents_layer_->bounds();
+
+ // Adjust the toplogy of the layer tree to add clipping layers
sky 2013/09/05 21:12:57 toplogy->hierarchy
rharrison 2013/09/09 20:01:17 Done.
+ devtools_parent_ = devtools_layer_->parent();
+ web_contents_parent_ = web_contents_layer_->parent();
+ 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_);
+ if (download_layer_)
+ frame_clipping_layer_->Add(download_layer_);
+ web_clipping_layer_->Add(web_contents_layer_);
sky 2013/09/05 21:12:57 This all strikes me as very fragile. In particular
rharrison 2013/09/09 20:01:17 I agree this is relatively brittle. If I recall co
sky 2013/09/11 00:13:39 I don't think you need to abandon this CL entirely
+}
+
+void ScrollEndEffectControllerAsh::DeactivateEffect() {
+ 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;
+
+ if (download_layer_) {
+ 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();
+ frame_layer_->Remove(frame_clipping_layer_.get());
+ frame_clipping_layer_.reset();
+}
+
+void ScrollEndEffectControllerAsh::ApplyDelta(int delta_y) {
+ current_delta_y_ = delta_y;
+ SetBoundsForEffect();
+
+ ui::Layer* frame = delegate_->GetBrowserFrameLayer();
sky 2013/09/05 21:12:57 Isn't this frame_layer_?
rharrison 2013/09/09 20:01:17 Done.
+ gfx::Rect bounds = frame->bounds();
+
+ float scale_factor = std::abs(delta_y);
+ scale_factor /= bounds.height();
+ scale_factor = 1 - scale_factor;
+
+ bool scrolling_down = delta_y > 0;
+ gfx::Transform frame_transform;
+ if (scrolling_down) {
+ 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);
+
+ if (!download_layer_) {
+ download_view_ = delegate_->GetDownloadView();
+ download_layer_ = download_view_ ? CreateViewLayer(download_view_) :
+ NULL;
+ }
+ if (download_layer_) {
+ gfx::Transform download_transform =
+ CounterTransfromAboutPoint(0,
+ download_layer_->bounds().origin().y());
+ if (!scrolling_down)
+ download_transform.Translate(0, delta_y);
+ download_layer_->SetTransform(download_transform);
+ }
+
+ gfx::Transform devtools_transform =
+ CounterTransfromAboutPoint(0,
+ devtools_layer_->bounds().origin().y());
+ if (!scrolling_down)
+ 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 (scrolling_down)
+ web_clipping_transform.Translate(0, delta_y);
+ web_clipping_layer_->SetTransform(web_clipping_transform);
+
+ if (!scrolling_down) {
+ 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 (scrolling_down)
+ non_client_transform.Translate(0, delta_y);
+ non_client_layer_->SetTransform(non_client_transform);
+}
+
+void ScrollEndEffectControllerAsh::SetBoundsForEffect() {
+ gfx::Rect frame_clipping_bounds = frame_layer_->bounds();
+ frame_clipping_bounds.set_origin(gfx::Point());
+ frame_clipping_layer_->SetBounds(frame_clipping_bounds);
+
+ gfx::Rect web_clipping_bounds = frame_layer_->bounds();
+ int download_height = GetDownloadHeight();
+ int non_client_top_height = frame_layer_->bounds().height() -
+ web_contents_layer_->bounds().height() -
+ delegate_->GetDevToolsHeight() -
+ delegate_->GetDividerHeight() -
+ download_height;
+ int web_clipping_height = 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 = frame_layer_->bounds();
+ devtools_bounds.set_origin(gfx::Point(0,
+ 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());
+ web_contents_layer_->SetBounds(web_contents_bounds);
+}
+
+int ScrollEndEffectControllerAsh::GetDownloadHeight() const {
+ return !download_view_ ? 0 :
sky 2013/09/05 21:12:57 nit: return download_view_ && download_view_->v
rharrison 2013/09/09 20:01:17 Done.
+ !download_view_->visible() ? 0 :
+ download_view_->bounds().height();
+}
+
+gfx::Transform ScrollEndEffectControllerAsh::CounterTransfromAboutPoint(int x,
sky 2013/09/05 21:12:57 transform and const
rharrison 2013/09/09 20:01:17 Done.
+ 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