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

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: Cleaned up animation code 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_);
sadrul 2013/09/05 16:09:02 This seems a bit odd? I would've thought we would
rharrison 2013/09/05 20:18:34 Just applying the capped value would cause the eff
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 (end_delta_y_ == 0)
138 DeactivateEffect();
139 }
140
141 void ScrollEndEffectControllerAsh::AnimationProgressed(
142 const ui::Animation* animation) {
143 int current_delta = ui::Tween::ValueBetween(animation_->GetCurrentValue(),
144 start_delta_y_,
145 end_delta_y_);
146 ApplyDelta(current_delta);
147 }
148
149 void ScrollEndEffectControllerAsh::ActivateEffect() {
150 ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
151 if (!browser_frame_layer)
152 return;
153
154 is_effect_active_ = true;
155
156 // Get layers for all of the parts to be manipulated
157 non_client_view_ = delegate_->GetNonClientView();
158 non_client_layer_ = CreateViewLayer(non_client_view_);
159 web_contents_layer_ = delegate_->GetWebContentsLayer();
160 devtools_layer_ = delegate_->GetDevToolsLayer();
161 download_view_ = delegate_->GetDownloadView();
162 download_layer_ = CreateViewLayer(download_view_);
163
164 CHECK(non_client_layer_);
165 CHECK(web_contents_layer_);
166 CHECK(devtools_layer_);
167 CHECK(download_layer_);
168
169 frame_clipping_layer_.reset(CreateClippingLayer(
170 "OverscrollFrameClippingLayer"));
171 web_clipping_layer_.reset(CreateClippingLayer("OverscrollWebClippingLayer"));
172
173 // Save bounds for restoring later
174 devtools_bounds_ = devtools_layer_->bounds();
175 web_contents_bounds_ = web_contents_layer_->bounds();
176
177 // Adjust the toplogy of the layer tree to add clipping layers
178 devtools_parent_ = devtools_layer_->parent();
179 web_contents_parent_ = web_contents_layer_->parent();
180 browser_frame_layer->Add(frame_clipping_layer_.get());
181 frame_clipping_layer_->Add(non_client_layer_);
182 frame_clipping_layer_->Add(web_clipping_layer_.get());
183 frame_clipping_layer_->Add(devtools_layer_);
184 frame_clipping_layer_->Add(download_layer_);
185 web_clipping_layer_->Add(web_contents_layer_);
186 }
187
188 ui::Layer* ScrollEndEffectControllerAsh::CreateClippingLayer(std::string name) {
189 ui::Layer* layer = new ui::Layer(ui::LAYER_NOT_DRAWN);
190 layer->set_name(name);
191 layer->SetMasksToBounds(true);
192 layer->SetFillsBoundsOpaquely(false);
193 return layer;
194 }
195
196 ui::Layer* ScrollEndEffectControllerAsh::CreateViewLayer(views::View* view) {
sadrul 2013/09/05 16:09:02 CreateViewLayer and CreateClippingLayer could be s
rharrison 2013/09/05 20:18:34 Done.
197 view->SetPaintToLayer(true);
198 ui::Layer* layer = view->layer();
199 layer->SetFillsBoundsOpaquely(false);
200 view->parent()->SchedulePaint();
201 return layer;
202 }
203
204 void ScrollEndEffectControllerAsh::DeactivateEffect() {
205 ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
206 if (!browser_frame_layer)
207 return;
208
209 is_effect_active_ = false;
210
211 devtools_layer_->SetBounds(devtools_bounds_);
212 devtools_parent_->Add(devtools_layer_);
213 devtools_parent_ = NULL;
214 devtools_layer_ = NULL;
215
216 web_contents_layer_->SetBounds(web_contents_bounds_);
217 web_contents_parent_->Add(web_contents_layer_);
218 web_contents_parent_ = NULL;
219 web_contents_layer_ = NULL;
220
221 frame_clipping_layer_->Remove(download_layer_);
222 download_view_->SetPaintToLayer(false);
223 download_layer_ = NULL;
224 download_view_ = NULL;
225
226 frame_clipping_layer_->Remove(non_client_layer_);
227 non_client_view_->SetPaintToLayer(false);
228 non_client_layer_ = NULL;
229 non_client_view_ = NULL;
230
231 frame_clipping_layer_->Remove(web_clipping_layer_.get());
232 web_clipping_layer_.reset();
233 browser_frame_layer->Remove(frame_clipping_layer_.get());
234 frame_clipping_layer_.reset();
235 browser_frame_layer = NULL;
236 }
237
238 void ScrollEndEffectControllerAsh::ApplyDelta(int delta_y) {
239 SetBoundsForEffect();
sadrul 2013/09/05 16:09:02 Hm. I assume you call this for each ApplyDelta() c
rharrison 2013/09/05 20:18:34 yup
240
241 ui::Layer* frame = delegate_->GetBrowserFrameLayer();
sadrul 2013/09/05 16:09:02 Is there a reason GetBrowserFrameLayer() isn't cac
rharrison 2013/09/05 20:18:34 It is an artifact from how I refactored the code.
242 gfx::Rect bounds = frame->bounds();
243
244 float scale_factor = std::abs(delta_y);
245 scale_factor /= bounds.height();
246 scale_factor = 1 - scale_factor;
247
248 gfx::Transform frame_transform;
249 if (delta_y > 0) {
sadrul 2013/09/05 16:09:02 Can you set a 'bool scrolling_down = delta_y > 0;'
rharrison 2013/09/05 20:18:34 Done.
250 frame_transform.Translate(0, delta_y);
251 frame_transform.Scale(1, scale_factor);
252 } else {
253 frame_transform.Scale(1, scale_factor);
254 }
255 CHECK(frame_transform.GetInverse(&counter_transform_));
256 frame->SetTransform(frame_transform);
257
258 gfx::Transform download_transform =
259 CounterTransfromAboutPoint(0,
260 download_layer_->bounds().origin().y());
261 if (delta_y < 0)
262 download_transform.Translate(0, delta_y);
263 download_layer_->SetTransform(download_transform);
264
265 gfx::Transform devtools_transform =
266 CounterTransfromAboutPoint(0,
267 devtools_layer_->bounds().origin().y());
268 if (delta_y < 0)
269 devtools_transform.Translate(0, delta_y);
270 devtools_layer_->SetTransform(devtools_transform);
271
272 gfx::Transform web_clipping_transform =
273 CounterTransfromAboutPoint(0,
274 web_clipping_layer_->bounds().origin().y());
275 if (delta_y >= 0)
276 web_clipping_transform.Translate(0, delta_y);
277 web_clipping_layer_->SetTransform(web_clipping_transform);
278
279 if (delta_y <= 0) {
280 gfx::Transform web_contents_transform;
281 web_contents_transform.Translate(0, delta_y);
282 web_contents_layer_->SetTransform(web_contents_transform);
283 }
284
285 gfx::Transform non_client_transform = counter_transform_;
286 if (delta_y >= 0)
287 non_client_transform.Translate(0, delta_y);
288 non_client_layer_->SetTransform(non_client_transform);
289 }
290
291 void ScrollEndEffectControllerAsh::SetBoundsForEffect() {
292 ui::Layer* browser_frame_layer = delegate_->GetBrowserFrameLayer();
293 if (!browser_frame_layer)
294 return;
295
296 gfx::Rect frame_clipping_bounds = browser_frame_layer->bounds();
297 frame_clipping_bounds.set_origin(gfx::Point());
298 frame_clipping_layer_->SetBounds(frame_clipping_bounds);
299
300 gfx::Rect web_clipping_bounds = browser_frame_layer->bounds();
301 int download_height = download_view_->visible() ?
302 download_view_->bounds().height() : 0;
303 int non_client_top_height = browser_frame_layer->bounds().height() -
304 web_contents_layer_->bounds().height() -
305 delegate_->GetDevToolsHeight() -
306 delegate_->GetDividerHeight() -
307 download_height;
308 int web_clipping_height = browser_frame_layer->bounds().height() -
309 non_client_top_height -
310 delegate_->GetDevToolsHeight() -
311 delegate_->GetDividerHeight() -
312 download_height;
313
314 web_clipping_bounds.set_height(web_clipping_height);
315 web_clipping_bounds.set_origin(gfx::Point(0, non_client_top_height));
316 web_clipping_layer_->SetBounds(web_clipping_bounds);
317
318 gfx::Rect devtools_bounds = browser_frame_layer->bounds();
319 devtools_bounds.set_origin(gfx::Point(0,
320 browser_frame_layer->bounds().height() -
321 delegate_->GetDevToolsHeight() -
322 download_height));
323 devtools_bounds.set_height(delegate_->GetDevToolsHeight());
324 devtools_layer_->SetBounds(devtools_bounds);
325
326 // Move the web contents since bounds are relative to the parent layer
327 gfx::Rect web_contents_bounds = web_clipping_bounds;
328 web_contents_bounds.set_origin(gfx::Point(0, 0));
sadrul 2013/09/05 16:09:02 gfx::Point() like above
rharrison 2013/09/05 20:18:34 Done.
329 web_contents_layer_->SetBounds(web_contents_bounds);
330 }
331
332 gfx::Transform ScrollEndEffectControllerAsh::CounterTransfromAboutPoint(int x,
333 int y) {
334 gfx::Transform to_frame_transform;
335 to_frame_transform.Translate(-x, -y);
336 gfx::Transform from_frame_transform;
337 from_frame_transform.Translate(x, y);
338 gfx::Transform transform = counter_transform_;
339 transform.ConcatTransform(to_frame_transform);
340 transform.PreconcatTransform(from_frame_transform);
341 return transform;
342 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698