Chromium Code Reviews| 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; |
| } |