OLD | NEW |
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/frame/browser_frame.h" |
8 return new ScrollEndEffectControllerAsh(); | 8 #include "chrome/browser/ui/views/frame/browser_view.h" |
9 } | 9 #include "content/public/browser/web_contents.h" |
10 | 10 #include "content/public/browser/web_contents_view.h" |
11 ScrollEndEffectControllerAsh::ScrollEndEffectControllerAsh() { | 11 #include "ui/aura/window.h" |
| 12 #include "ui/compositor/layer_type.h" |
| 13 #include "ui/gfx/rect.h" |
| 14 #include "ui/gfx/safe_integer_conversions.h" |
| 15 #include "ui/gfx/transform.h" |
| 16 |
| 17 namespace { |
| 18 // This factor is used to control how much translation is applied relative to |
| 19 // the size of the window. Specifically translation is limited to a max of |
| 20 // window_size * factor. |
| 21 const float kScrollEndEffectFactor = 0.10; |
| 22 } |
| 23 |
| 24 // To achieve the desired effect to indicate vertical overscroll is occuring the |
| 25 // layer tree needs to be manipulated. Specifically two clipping layers are |
| 26 // inserted and the parenting of the web contents and non-client layers is |
| 27 // modified. These modifications are removed when the effect is deactivated. |
| 28 // |
| 29 // The |frame_clipping_layer_| is used to make sure that the bits of the layers |
| 30 // below do not peak out since the |browser_frame_layer| is going to be scaled |
| 31 // down in size, but some of the sub-layers are going to be counter scaled. This |
| 32 // clipping cannot be done in the |browser_frame_layer| since the shadows on the |
| 33 // window are outside the clipping bounds, but also children of that layer. |
| 34 // |
| 35 // The |web_clipping_layer_| is used to trim the web contents so that is does |
| 36 // not occlude the non-client elements. This is done to give the visual effect |
| 37 // of the contents slide under the top elements without having to break these |
| 38 // elements out into their own layer. |
| 39 // |
| 40 // Layout of layer tree when effect is not active: |
| 41 // +----------------------------------+ |
| 42 // | browser_frame_layer | |
| 43 // +-+--------------+---------------+-+ |
| 44 // | | | |
| 45 // | v v |
| 46 // v +-------------------+ +---------------------+ |
| 47 // ... | non_client_layer_ | | web_contents_layer_ | |
| 48 // +-------------------+ +---------------------+ |
| 49 // |
| 50 // |
| 51 // Layout of the layer tree when effect is active: |
| 52 // +---------------------+ |
| 53 // | browser_frame_layer | |
| 54 // +-+---------------+---+ |
| 55 // | | |
| 56 // | v |
| 57 // v +-----------------------+ |
| 58 // ... | frame_clipping_layer_ | |
| 59 // +-----+--------------+--+ |
| 60 // | | |
| 61 // v v |
| 62 // +-------------------+ +---------------------+ |
| 63 // | non_client_layer_ | | web_clipping_layer_ | |
| 64 // +-------------------+ +---------+-----------+ |
| 65 // | |
| 66 // v |
| 67 // +---------------------+ |
| 68 // | web_contents_layer_ | |
| 69 // +---------------------+ |
| 70 |
| 71 ScrollEndEffectController* ScrollEndEffectController::Create( |
| 72 BrowserView* view) { |
| 73 return new ScrollEndEffectControllerAsh(view); |
| 74 } |
| 75 |
| 76 ScrollEndEffectControllerAsh::ScrollEndEffectControllerAsh(BrowserView* view) |
| 77 : browser_view_(view), |
| 78 is_effect_active_(false), |
| 79 web_contents_layer_(NULL), |
| 80 non_client_layer_(NULL), |
| 81 web_contents_parent_(NULL), |
| 82 non_client_view_(NULL) { |
12 } | 83 } |
13 | 84 |
14 ScrollEndEffectControllerAsh::~ScrollEndEffectControllerAsh() { | 85 ScrollEndEffectControllerAsh::~ScrollEndEffectControllerAsh() { |
15 } | 86 } |
16 | 87 |
17 void ScrollEndEffectControllerAsh::OverscrollUpdate(int delta_y) { | 88 void ScrollEndEffectControllerAsh::OverscrollUpdate(int delta_y) { |
18 // TODO(rharrison): Implement initial version of scroll end effect | 89 if (browser_view_ == NULL || browser_view_->frame() == NULL) { |
19 } | 90 return; |
| 91 } |
| 92 |
| 93 ui::Layer* browser_frame_layer = browser_view_->frame()->GetLayer(); |
| 94 if (!browser_frame_layer) |
| 95 return; |
| 96 |
| 97 if (delta_y == 0 && is_effect_active_) { |
| 98 ApplyDelta(browser_frame_layer, browser_frame_layer->bounds(), 0); |
| 99 DeactivateEffect(); |
| 100 return; |
| 101 } |
| 102 |
| 103 if (!is_effect_active_) |
| 104 ActivateEffect(); |
| 105 |
| 106 int capped_delta_y = delta_y; |
| 107 gfx::Rect bounds = browser_frame_layer->bounds(); |
| 108 // Limiting the delta size being a proportion of the frame bounds size. |
| 109 if (capped_delta_y > 0) { |
| 110 capped_delta_y = std::min(gfx::ToRoundedInt((bounds.height() * |
| 111 kScrollEndEffectFactor)), |
| 112 capped_delta_y); |
| 113 } else if (capped_delta_y < 0) { |
| 114 capped_delta_y = -std::min(gfx::ToRoundedInt((bounds.height() * |
| 115 kScrollEndEffectFactor)), |
| 116 -capped_delta_y); |
| 117 } |
| 118 ApplyDelta(browser_frame_layer, bounds, capped_delta_y); |
| 119 } |
| 120 |
| 121 void ScrollEndEffectControllerAsh::ActivateEffect() { |
| 122 ui::Layer* browser_frame_layer = browser_view_->frame()->GetLayer(); |
| 123 if (!browser_frame_layer) |
| 124 return; |
| 125 |
| 126 is_effect_active_ = true; |
| 127 |
| 128 // Get layers for all of the parts to be manipulated |
| 129 web_contents_layer_ = browser_view_-> GetActiveWebContents()->GetView()-> |
| 130 GetNativeView()->layer(); |
| 131 non_client_view_ = browser_view_->frame()->GetContentsView(); |
| 132 non_client_view_->SetPaintToLayer(true); |
| 133 non_client_layer_ = non_client_view_->layer(); |
| 134 non_client_layer_->SetFillsBoundsOpaquely(false); |
| 135 // non_client_layer_->SetOpacity(0.5); |
| 136 |
| 137 CHECK(web_contents_layer_); |
| 138 CHECK(non_client_layer_); |
| 139 |
| 140 // Setup clipping layers |
| 141 frame_clipping_layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN)); |
| 142 frame_clipping_layer_->set_name("OverscrollFrameClippingLayer"); |
| 143 frame_clipping_layer_->SetMasksToBounds(true); |
| 144 gfx::Rect frame_clipping_bounds = browser_frame_layer->bounds(); |
| 145 frame_clipping_bounds.set_origin(gfx::Point()); |
| 146 frame_clipping_layer_->SetBounds(frame_clipping_bounds); |
| 147 |
| 148 web_clipping_layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN)); |
| 149 web_clipping_layer_->set_name("OverscrollWebClippingLayer"); |
| 150 web_clipping_layer_->SetMasksToBounds(true); |
| 151 gfx::Rect web_clipping_bounds = browser_frame_layer->bounds(); |
| 152 non_content_height_ = browser_frame_layer->bounds().height() - |
| 153 web_contents_layer_->bounds().height(); |
| 154 web_clipping_bounds.set_height(browser_frame_layer->bounds().height() - |
| 155 non_content_height_); |
| 156 web_clipping_bounds.set_origin(gfx::Point(0, non_content_height_)); |
| 157 web_clipping_layer_->SetBounds(web_clipping_bounds); |
| 158 |
| 159 // Move the web contents since bounds are relative to the parent layer |
| 160 gfx::Rect web_contents_bounds = web_clipping_bounds; |
| 161 web_contents_parent_ = web_contents_layer_->parent(); |
| 162 web_contents_bounds.set_origin(gfx::Point(0, 0)); |
| 163 web_contents_bounds_ = web_contents_layer_->bounds(); |
| 164 web_contents_layer_->SetBounds(web_contents_bounds); |
| 165 |
| 166 // Adjust the toplogy of the layer tree to add clipping layers |
| 167 browser_frame_layer->Add(frame_clipping_layer_.get()); |
| 168 frame_clipping_layer_->Add(non_client_layer_); |
| 169 frame_clipping_layer_->Add(web_clipping_layer_.get()); |
| 170 web_clipping_layer_->Add(web_contents_layer_); |
| 171 } |
| 172 |
| 173 void ScrollEndEffectControllerAsh::DeactivateEffect() { |
| 174 ui::Layer* browser_frame_layer = browser_view_->frame()->GetLayer(); |
| 175 if (!browser_frame_layer) |
| 176 return; |
| 177 |
| 178 is_effect_active_ = false; |
| 179 |
| 180 web_contents_layer_->SetBounds(web_contents_bounds_); |
| 181 web_contents_parent_->Add(web_contents_layer_); |
| 182 web_contents_parent_ = NULL; |
| 183 web_contents_layer_ = NULL; |
| 184 |
| 185 frame_clipping_layer_->Remove(non_client_layer_); |
| 186 non_client_view_->SetPaintToLayer(false); |
| 187 non_client_layer_ = NULL; |
| 188 non_client_view_ = NULL; |
| 189 |
| 190 frame_clipping_layer_->Remove(web_clipping_layer_.get()); |
| 191 web_clipping_layer_.reset(); |
| 192 browser_frame_layer->Remove(frame_clipping_layer_.get()); |
| 193 frame_clipping_layer_.reset(); |
| 194 browser_frame_layer = NULL; |
| 195 } |
| 196 |
| 197 void ScrollEndEffectControllerAsh::ApplyDelta(ui::Layer* frame, |
| 198 gfx::Rect bounds, |
| 199 int delta_y) { |
| 200 float scale_factor = std::abs(delta_y); |
| 201 scale_factor /= bounds.height(); |
| 202 scale_factor = 1 - scale_factor; |
| 203 |
| 204 gfx::Transform frame_transform; |
| 205 if (delta_y > 0) { |
| 206 frame_transform.Translate(0, delta_y); |
| 207 frame_transform.Scale(1, scale_factor); |
| 208 } else { |
| 209 frame_transform.Scale(1, scale_factor); |
| 210 } |
| 211 gfx::Transform counter_transform; |
| 212 CHECK(frame_transform.GetInverse(&counter_transform)); |
| 213 frame->SetTransform(frame_transform); |
| 214 |
| 215 // Ensure that we apply the counter translation about origin of the web |
| 216 // contents |
| 217 gfx::Transform web_to_frame_transform; |
| 218 web_to_frame_transform.Translate(0, -non_content_height_); |
| 219 gfx::Transform frame_to_web_transform; |
| 220 frame_to_web_transform.Translate(0, non_content_height_); |
| 221 gfx::Transform web_clipping_transform = counter_transform; |
| 222 web_clipping_transform.ConcatTransform(web_to_frame_transform); |
| 223 web_clipping_transform.PreconcatTransform(frame_to_web_transform); |
| 224 |
| 225 if (delta_y >= 0) { |
| 226 web_clipping_transform.Translate(0, delta_y); |
| 227 } |
| 228 web_clipping_layer_->SetTransform(web_clipping_transform); |
| 229 |
| 230 if (delta_y <= 0) { |
| 231 gfx::Transform web_contents_transform; |
| 232 web_contents_transform.Translate(0, delta_y); |
| 233 web_contents_layer_->SetTransform(web_contents_transform); |
| 234 } |
| 235 |
| 236 gfx::Transform non_client_transform = counter_transform; |
| 237 if (delta_y >= 0) { |
| 238 non_client_transform.Translate(0, delta_y); |
| 239 } |
| 240 non_client_layer_->SetTransform(non_client_transform); |
| 241 } |
| 242 |
OLD | NEW |