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

Side by Side 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 unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.h" 5 #include "chrome/browser/ui/views/frame/scroll_end_effect_controller_ash.h"
6 6
7 ScrollEndEffectController* ScrollEndEffectController::Create() { 7 #include "chrome/browser/ui/views/download/download_shelf_view.h"
8 return new ScrollEndEffectControllerAsh(); 8 #include "chrome/browser/ui/views/frame/browser_frame.h"
9 } 9 #include "chrome/browser/ui/views/frame/browser_view.h"
10 10 #include "content/public/browser/web_contents.h"
11 ScrollEndEffectControllerAsh::ScrollEndEffectControllerAsh() { 11 #include "content/public/browser/web_contents_view.h"
12 #include "ui/aura/window.h"
13 #include "ui/base/animation/tween.h"
14 #include "ui/compositor/layer_type.h"
15 #include "ui/gfx/safe_integer_conversions.h"
16 #include "ui/views/controls/single_split_view.h"
17 #include "ui/views/controls/webview/webview.h"
18
19 namespace {
20 // This factor is used to control how much translation is applied relative to
21 // the size of the window. Specifically translation is limited to a max of
22 // window_size * factor.
23 const float kScrollEndEffectFactor = 0.05;
24 }
25
26 // To achieve the desired effect to indicate vertical overscroll is occuring the
27 // layer tree needs to be manipulated. Specifically two clipping layers are
28 // inserted and the parenting of the web contents and non-client layers are
29 // modified. These modifications are removed when the effect is deactivated.
30 //
31 // The |frame_clipping_layer_| is used to make sure that the bits of the layers
32 // below do not peak out since the |browser_frame_layer| is going to be scaled
33 // down in size, but some of the sub-layers are going to be counter scaled. This
34 // clipping cannot be done in the |browser_frame_layer| since the shadows on the
35 // window are outside the clipping bounds, but also children of that layer.
36 //
37 // The |web_clipping_layer_| is used to trim the web contents so that is does
38 // not occlude the non-client elements. This is done to give the visual effect
39 // of the contents slide under the top elements without having to break these
40 // elements out into their own layer.
41 //
42 // Additionally the layer associated with the dev tools is promoted, so that it
43 // isn't occuluded by the web contents layer.
44 //
45 // Layout of layer tree when effect is not active:
46 // +----------------------------------------------------------+
47 // | browser_frame_layer |
48 // +-+--------------+---------------+-----------------------+-+
49 // | | | |
50 // | v v v
51 // v +-------------------+ +---------------------+ +-----------------+
52 // ... | non_client_layer_ | | web_contents_layer_ | | devtools_layer_ |
53 // +-------------------+ +---------------------+ +-----------------+
54 //
55 //
56 // Layout of the layer tree when effect is active:
57 // +---------------------+
58 // | browser_frame_layer |
59 // +-+---------------+---+
60 // | |
61 // | v
62 // v +-------------------------------------------------+
63 // ... | frame_clipping_layer_ |
64 // +-----+--------------+---------------------+------+
65 // | | |
66 // v v V
67 // +-------------------+ +---------------------+ +-----------------+
68 // | non_client_layer_ | | web_clipping_layer_ | | devtools_layer_ |
69 // +-------------------+ +---------+-----------+ +-----------------+
70 // |
71 // v
72 // +---------------------+
73 // | web_contents_layer_ |
74 // +---------------------+
75
76 ScrollEndEffectController* ScrollEndEffectController::Create(
77 ScrollEndEffectControllerDelegate* delegate) {
78 return new ScrollEndEffectControllerAsh(delegate);
79 }
80
81 ScrollEndEffectControllerAsh::ScrollEndEffectControllerAsh(
82 ScrollEndEffectControllerDelegate* delegate)
83 : delegate_(delegate),
84 is_effect_active_(false),
85 non_client_layer_(NULL),
86 web_contents_layer_(NULL),
87 devtools_layer_(NULL),
88 web_contents_parent_(NULL),
89 devtools_parent_(NULL),
90 non_client_view_(NULL),
91 start_delta_y_(0),
92 end_delta_y_(0) {
93 animation_.reset(new ui::SlideAnimation(this));
94 animation_->SetSlideDuration(250);
95 animation_->SetTweenType(ui::Tween::EASE_OUT);
12 } 96 }
13 97
14 ScrollEndEffectControllerAsh::~ScrollEndEffectControllerAsh() { 98 ScrollEndEffectControllerAsh::~ScrollEndEffectControllerAsh() {
15 } 99 }
16 100
17 void ScrollEndEffectControllerAsh::OverscrollUpdate(int delta_y) { 101 void ScrollEndEffectControllerAsh::OverscrollUpdate(int delta_y) {
18 // TODO(rharrison): Implement initial version of scroll end effect 102 if (delegate_ == NULL)
19 } 103 return;
104
105 if (!is_effect_active_ && delta_y == 0)
106 return;
107
108 ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
109 if (!browser_frame_layer)
110 return;
111
112 if (!is_effect_active_)
113 ActivateEffect();
114
115 int capped_delta_y = delta_y;
116 gfx::Rect bounds = browser_frame_layer->bounds();
117 // Limiting the delta size being a proportion of the frame bounds size.
118 if (capped_delta_y > 0) {
119 capped_delta_y = std::min(gfx::ToRoundedInt((bounds.height() *
120 kScrollEndEffectFactor)),
121 capped_delta_y);
122 } else if (capped_delta_y < 0) {
123 capped_delta_y = -std::min(gfx::ToRoundedInt((bounds.height() *
124 kScrollEndEffectFactor)),
125 -capped_delta_y);
126 }
127
128 ApplyDelta(end_delta_y_);
129 start_delta_y_ = end_delta_y_;
130 end_delta_y_ = capped_delta_y;
131 animation_->Reset();
132 animation_->Show();
133 }
134
135 void ScrollEndEffectControllerAsh::AnimationEnded(
136 const ui::Animation* animation) {
137 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
138 return;
139
140 if (end_delta_y_ == 0)
141 DeactivateEffect();
142 }
143
144 void ScrollEndEffectControllerAsh::AnimationProgressed(
145 const ui::Animation* animation) {
146 if (animation != animation_.get())
147 return;
148
149 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.
150 gfx::ToRoundedInt(animation_->GetCurrentValue() *
151 (end_delta_y_ - start_delta_y_));
152 ApplyDelta(current_delta);
153 }
154
155 void ScrollEndEffectControllerAsh::ActivateEffect() {
156 ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
157 if (!browser_frame_layer)
158 return;
159
160 is_effect_active_ = true;
161
162 // Get layers for all of the parts to be manipulated
163 non_client_view_ = delegate_->GetNonClientView();
164 non_client_layer_ = CreateViewLayer(non_client_view_);
165 web_contents_layer_ = delegate_->GetWebContentsLayer();
166 devtools_layer_ = delegate_->GetDevToolsLayer();
167 download_view_ = delegate_->GetDownloadView();
168 download_layer_ = CreateViewLayer(download_view_);
169
170 CHECK(non_client_layer_);
171 CHECK(web_contents_layer_);
172 CHECK(devtools_layer_);
173 CHECK(download_layer_);
174
175 frame_clipping_layer_.reset(CreateClippingLayer(
176 "OverscrollFrameClippingLayer"));
177 web_clipping_layer_.reset(CreateClippingLayer("OverscrollWebClippingLayer"));
178
179 // Save bounds for restoring later
180 devtools_bounds_ = devtools_layer_->bounds();
181 web_contents_bounds_ = web_contents_layer_->bounds();
182
183 // Adjust the toplogy of the layer tree to add clipping layers
184 devtools_parent_ = devtools_layer_->parent();
185 web_contents_parent_ = web_contents_layer_->parent();
186 browser_frame_layer->Add(frame_clipping_layer_.get());
187 frame_clipping_layer_->Add(non_client_layer_);
188 frame_clipping_layer_->Add(web_clipping_layer_.get());
189 frame_clipping_layer_->Add(devtools_layer_);
190 frame_clipping_layer_->Add(download_layer_);
191 web_clipping_layer_->Add(web_contents_layer_);
192 }
193
194 ui::Layer* ScrollEndEffectControllerAsh::CreateClippingLayer(std::string name) {
195 ui::Layer* layer = new ui::Layer(ui::LAYER_NOT_DRAWN);
196 layer->set_name(name);
197 layer->SetMasksToBounds(true);
198 layer->SetFillsBoundsOpaquely(false);
199 return layer;
200 }
201
202 ui::Layer* ScrollEndEffectControllerAsh::CreateViewLayer(views::View* view) {
203 view->SetPaintToLayer(true);
204 ui::Layer* layer = view->layer();
205 layer->SetFillsBoundsOpaquely(false);
206 view->parent()->SchedulePaint();
207 return layer;
208 }
209
210 void ScrollEndEffectControllerAsh::DeactivateEffect() {
211 ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
212 if (!browser_frame_layer)
213 return;
214
215 is_effect_active_ = false;
216
217 devtools_layer_->SetBounds(devtools_bounds_);
218 devtools_parent_->Add(devtools_layer_);
219 devtools_parent_ = NULL;
220 devtools_layer_ = NULL;
221
222 web_contents_layer_->SetBounds(web_contents_bounds_);
223 web_contents_parent_->Add(web_contents_layer_);
224 web_contents_parent_ = NULL;
225 web_contents_layer_ = NULL;
226
227 frame_clipping_layer_->Remove(download_layer_);
228 download_view_->SetPaintToLayer(false);
229 download_layer_ = NULL;
230 download_view_ = NULL;
231
232 frame_clipping_layer_->Remove(non_client_layer_);
233 non_client_view_->SetPaintToLayer(false);
234 non_client_layer_ = NULL;
235 non_client_view_ = NULL;
236
237 frame_clipping_layer_->Remove(web_clipping_layer_.get());
238 web_clipping_layer_.reset();
239 browser_frame_layer->Remove(frame_clipping_layer_.get());
240 frame_clipping_layer_.reset();
241 browser_frame_layer = NULL;
242 }
243
244 void ScrollEndEffectControllerAsh::ApplyDelta(int delta_y) {
245 SetBoundsForEffect();
246
247 ui::Layer* frame = delegate_->GetBrowserFrameLayer();
248 gfx::Rect bounds = frame->bounds();
249
250 float scale_factor = std::abs(delta_y);
251 scale_factor /= bounds.height();
252 scale_factor = 1 - scale_factor;
253
254 gfx::Transform frame_transform;
255 if (delta_y > 0) {
256 frame_transform.Translate(0, delta_y);
257 frame_transform.Scale(1, scale_factor);
258 } else {
259 frame_transform.Scale(1, scale_factor);
260 }
261 CHECK(frame_transform.GetInverse(&counter_transform_));
262 frame->SetTransform(frame_transform);
263
264 gfx::Transform download_transform =
265 CounterTransfromAboutPoint(0,
266 download_layer_->bounds().origin().y());
267 if (delta_y < 0)
268 download_transform.Translate(0, delta_y);
269 download_layer_->SetTransform(download_transform);
270
271 gfx::Transform devtools_transform =
272 CounterTransfromAboutPoint(0,
273 devtools_layer_->bounds().origin().y());
274 if (delta_y < 0)
275 devtools_transform.Translate(0, delta_y);
276 devtools_layer_->SetTransform(devtools_transform);
277
278 gfx::Transform web_clipping_transform =
279 CounterTransfromAboutPoint(0,
280 web_clipping_layer_->bounds().origin().y());
281 if (delta_y >= 0)
282 web_clipping_transform.Translate(0, delta_y);
283 web_clipping_layer_->SetTransform(web_clipping_transform);
284
285 if (delta_y <= 0) {
286 gfx::Transform web_contents_transform;
287 web_contents_transform.Translate(0, delta_y);
288 web_contents_layer_->SetTransform(web_contents_transform);
289 }
290
291 gfx::Transform non_client_transform = counter_transform_;
292 if (delta_y >= 0)
293 non_client_transform.Translate(0, delta_y);
294 non_client_layer_->SetTransform(non_client_transform);
295 }
296
297 void ScrollEndEffectControllerAsh::SetBoundsForEffect() {
298 ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
299 if (!browser_frame_layer)
300 return;
301
302 gfx::Rect frame_clipping_bounds = browser_frame_layer->bounds();
303 frame_clipping_bounds.set_origin(gfx::Point());
304 frame_clipping_layer_->SetBounds(frame_clipping_bounds);
305
306 gfx::Rect web_clipping_bounds = browser_frame_layer->bounds();
307 int download_height = download_view_->visible() ?
308 download_view_->bounds().height() : 0;
309 int non_client_top_height = browser_frame_layer->bounds().height() -
310 web_contents_layer_->bounds().height() -
311 delegate_->GetDevToolsHeight() -
312 delegate_->GetDividerHeight() -
313 download_height;
314 int web_clipping_height = browser_frame_layer->bounds().height() -
315 non_client_top_height -
316 delegate_->GetDevToolsHeight() -
317 delegate_->GetDividerHeight() -
318 download_height;
319
320 web_clipping_bounds.set_height(web_clipping_height);
321 web_clipping_bounds.set_origin(gfx::Point(0, non_client_top_height));
322 web_clipping_layer_->SetBounds(web_clipping_bounds);
323
324 gfx::Rect devtools_bounds = browser_frame_layer->bounds();
325 devtools_bounds.set_origin(gfx::Point(0,
326 browser_frame_layer->bounds().height() -
327 delegate_->GetDevToolsHeight() -
328 download_height));
329 devtools_bounds.set_height(delegate_->GetDevToolsHeight());
330 devtools_layer_->SetBounds(devtools_bounds);
331
332 // Move the web contents since bounds are relative to the parent layer
333 gfx::Rect web_contents_bounds = web_clipping_bounds;
334 web_contents_bounds.set_origin(gfx::Point(0, 0));
335 web_contents_layer_->SetBounds(web_contents_bounds);
336 }
337
338 gfx::Transform ScrollEndEffectControllerAsh::CounterTransfromAboutPoint(int x,
339 int y) {
340 gfx::Transform to_frame_transform;
341 to_frame_transform.Translate(-x, -y);
342 gfx::Transform from_frame_transform;
343 from_frame_transform.Translate(x, y);
344 gfx::Transform transform = counter_transform_;
345 transform.ConcatTransform(to_frame_transform);
346 transform.PreconcatTransform(from_frame_transform);
347 return transform;
348 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698