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

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

Powered by Google App Engine
This is Rietveld 408576698