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

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: Fixed build issue on windows and updated description. Created 7 years, 4 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..218ef5225895ef874838fd442365b0785d545b09 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,239 @@
#include "chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.h"
-ScrollEndEffectController* ScrollEndEffectController::Create() {
- return new ScrollEndEffectControllerAsh();
+#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/compositor/layer_type.h"
+#include "ui/gfx/rect.h"
+#include "ui/gfx/safe_integer_conversions.h"
+#include "ui/gfx/transform.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.10;
+}
+
+// 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 is
+// 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.
+//
+// Layout of layer tree when effect is not active:
+// +----------------------------------+
+// | browser_frame_layer |
+// +-+--------------+---------------+-+
+// | | |
+// | v v
+// v +-------------------+ +---------------------+
+// ... | non_client_layer_ | | web_contents_layer_ |
+// +-------------------+ +---------------------+
+//
+//
+// Layout of the layer tree when effect is active:
+// +---------------------+
+// | browser_frame_layer |
+// +-+---------------+---+
+// | |
+// | v
+// v +-----------------------+
+// ... | frame_clipping_layer_ |
+// +-----+--------------+--+
+// | |
+// v v
+// +-------------------+ +---------------------+
+// | non_client_layer_ | | web_clipping_layer_ |
+// +-------------------+ +---------+-----------+
+// |
+// v
+// +---------------------+
+// | web_contents_layer_ |
+// +---------------------+
+
+ScrollEndEffectController* ScrollEndEffectController::Create(
+ BrowserView* view) {
+ return new ScrollEndEffectControllerAsh(view);
}
-ScrollEndEffectControllerAsh::ScrollEndEffectControllerAsh() {
+ScrollEndEffectControllerAsh::ScrollEndEffectControllerAsh(BrowserView* view)
+ : browser_view_(view),
+ is_effect_active_(false),
+ web_contents_layer_(NULL),
+ non_client_layer_(NULL),
+ web_contents_parent_(NULL),
+ non_client_view_(NULL) {
}
ScrollEndEffectControllerAsh::~ScrollEndEffectControllerAsh() {
}
void ScrollEndEffectControllerAsh::OverscrollUpdate(int delta_y) {
- // TODO(rharrison): Implement initial version of scroll end effect
+ if (browser_view_ == NULL || browser_view_->frame() == NULL) {
+ return;
+ }
+
+ ui::Layer* browser_frame_layer = browser_view_->frame()->GetLayer();
+ if (!browser_frame_layer)
+ return;
+
+ if (delta_y == 0 && is_effect_active_) {
+ ApplyDelta(browser_frame_layer, browser_frame_layer->bounds(), 0);
+ DeactivateEffect();
+ 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(browser_frame_layer, bounds, capped_delta_y);
}
+
+void ScrollEndEffectControllerAsh::ActivateEffect() {
+ ui::Layer* browser_frame_layer = browser_view_->frame()->GetLayer();
+ if (!browser_frame_layer)
+ return;
+
+ is_effect_active_ = true;
+
+ // Get layers for all of the parts to be manipulated
+ web_contents_layer_ = browser_view_-> GetActiveWebContents()->GetView()->
+ GetNativeView()->layer();
+ non_client_view_ = browser_view_->frame()->GetContentsView();
+ non_client_view_->SetPaintToLayer(true);
+ non_client_layer_ = non_client_view_->layer();
+ non_client_layer_->SetFillsBoundsOpaquely(false);
+ // non_client_layer_->SetOpacity(0.5);
+
+ CHECK(web_contents_layer_);
+ CHECK(non_client_layer_);
+
+ // Setup clipping layers
+ frame_clipping_layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN));
+ frame_clipping_layer_->set_name("OverscrollFrameClippingLayer");
+ frame_clipping_layer_->SetMasksToBounds(true);
+ gfx::Rect frame_clipping_bounds = browser_frame_layer->bounds();
+ frame_clipping_bounds.set_origin(gfx::Point());
+ frame_clipping_layer_->SetBounds(frame_clipping_bounds);
+
+ web_clipping_layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN));
+ web_clipping_layer_->set_name("OverscrollWebClippingLayer");
+ web_clipping_layer_->SetMasksToBounds(true);
+ gfx::Rect web_clipping_bounds = browser_frame_layer->bounds();
+ non_content_height_ = browser_frame_layer->bounds().height() -
+ web_contents_layer_->bounds().height();
+ web_clipping_bounds.set_height(browser_frame_layer->bounds().height() -
+ non_content_height_);
+ web_clipping_bounds.set_origin(gfx::Point(0, non_content_height_));
+ web_clipping_layer_->SetBounds(web_clipping_bounds);
+
+ // Move the web contents since bounds are relative to the parent layer
+ gfx::Rect web_contents_bounds = web_clipping_bounds;
+ web_contents_parent_ = web_contents_layer_->parent();
+ web_contents_bounds.set_origin(gfx::Point(0, 0));
+ web_contents_bounds_ = web_contents_layer_->bounds();
+ web_contents_layer_->SetBounds(web_contents_bounds);
+
+ // Adjust the toplogy of the layer tree to add clipping layers
+ browser_frame_layer->Add(frame_clipping_layer_.get());
+ frame_clipping_layer_->Add(non_client_layer_);
+ frame_clipping_layer_->Add(web_clipping_layer_.get());
+ web_clipping_layer_->Add(web_contents_layer_);
+}
+
+void ScrollEndEffectControllerAsh::DeactivateEffect() {
+ ui::Layer* browser_frame_layer = browser_view_->frame()->GetLayer();
+ if (!browser_frame_layer)
+ return;
+
+ is_effect_active_ = false;
+
+ 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(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(ui::Layer* frame,
+ gfx::Rect bounds,
+ int delta_y) {
+ 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);
+ }
+ gfx::Transform counter_transform;
+ CHECK(frame_transform.GetInverse(&counter_transform));
+ frame->SetTransform(frame_transform);
+
+ // Ensure that we apply the counter translation about origin of the web
+ // contents
+ gfx::Transform web_to_frame_transform;
+ web_to_frame_transform.Translate(0, -non_content_height_);
+ gfx::Transform frame_to_web_transform;
+ frame_to_web_transform.Translate(0, non_content_height_);
+ gfx::Transform web_clipping_transform = counter_transform;
+ web_clipping_transform.ConcatTransform(web_to_frame_transform);
+ web_clipping_transform.PreconcatTransform(frame_to_web_transform);
+
+ 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);
+}
+

Powered by Google App Engine
This is Rietveld 408576698