| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 "content/browser/web_contents/aura/gesture_nav_simple.h" | 5 #include "content/browser/web_contents/aura/gesture_nav_simple.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/macros.h" | 9 #include "base/macros.h" |
| 10 #include "cc/layers/layer.h" | |
| 11 #include "cc/paint/paint_flags.h" | 10 #include "cc/paint/paint_flags.h" |
| 12 #include "content/browser/frame_host/navigation_controller_impl.h" | 11 #include "content/browser/frame_host/navigation_controller_impl.h" |
| 13 #include "content/browser/renderer_host/overscroll_controller.h" | 12 #include "content/browser/renderer_host/overscroll_controller.h" |
| 14 #include "content/browser/web_contents/web_contents_impl.h" | 13 #include "content/browser/web_contents/web_contents_impl.h" |
| 15 #include "content/browser/web_contents/web_contents_view.h" | |
| 16 #include "content/public/browser/browser_thread.h" | |
| 17 #include "content/public/browser/overscroll_configuration.h" | 14 #include "content/public/browser/overscroll_configuration.h" |
| 18 #include "content/public/common/content_client.h" | 15 #include "third_party/skia/include/core/SkDrawLooper.h" |
| 19 #include "ui/aura/window.h" | 16 #include "ui/aura/window.h" |
| 20 #include "ui/compositor/layer.h" | 17 #include "ui/compositor/layer.h" |
| 21 #include "ui/compositor/layer_animation_observer.h" | |
| 22 #include "ui/compositor/layer_delegate.h" | 18 #include "ui/compositor/layer_delegate.h" |
| 23 #include "ui/compositor/paint_recorder.h" | 19 #include "ui/compositor/paint_recorder.h" |
| 24 #include "ui/compositor/scoped_layer_animation_settings.h" | 20 #include "ui/gfx/animation/animation_delegate.h" |
| 21 #include "ui/gfx/animation/linear_animation.h" |
| 25 #include "ui/gfx/animation/tween.h" | 22 #include "ui/gfx/animation/tween.h" |
| 26 #include "ui/gfx/canvas.h" | 23 #include "ui/gfx/canvas.h" |
| 27 #include "ui/gfx/image/image.h" | 24 #include "ui/gfx/color_palette.h" |
| 28 #include "ui/resources/grit/ui_resources.h" | 25 #include "ui/gfx/paint_vector_icon.h" |
| 26 #include "ui/gfx/shadow_value.h" |
| 27 #include "ui/gfx/skia_paint_util.h" |
| 28 #include "ui/vector_icons/vector_icons.h" |
| 29 | 29 |
| 30 namespace content { | 30 namespace content { |
| 31 | 31 |
| 32 namespace { | 32 namespace { |
| 33 | 33 |
| 34 const int kArrowHeight = 280; | 34 // Parameters defining the arrow icon inside the affordance. |
| 35 const int kArrowWidth = 140; | 35 const int kArrowSize = 16; |
| 36 const float kMinOpacity = 0.25f; | 36 const SkColor kArrowColor = gfx::kGoogleBlue500; |
| 37 |
| 38 // Parameters defining the background circle of the affordance. |
| 39 const int kBackgroundRadius = 18; |
| 40 const SkColor kBackgroundColor = SK_ColorWHITE; |
| 41 const int kBgShadowOffsetY = 2; |
| 42 const int kBgShadowBlurRadius = 8; |
| 43 const SkColor kBgShadowColor = SkColorSetA(SK_ColorBLACK, 0x4D); |
| 44 |
| 45 // Parameters defining the affordance ripple. The ripple fades in and grows as |
| 46 // the user drags the affordance until it reaches |kMaxRippleRadius|. If the |
| 47 // overscroll is successful, the ripple will burst by fading out and growing to |
| 48 // |kMaxRippleBurstRadius|. |
| 49 const int kMaxRippleRadius = 54; |
| 50 const SkColor kRippleColor = SkColorSetA(gfx::kGoogleBlue500, 0x33); |
| 51 const int kMaxRippleBurstRadius = 72; |
| 52 const gfx::Tween::Type kBurstAnimationTweenType = gfx::Tween::EASE_IN; |
| 53 const int kRippleBurstAnimationDuration = 160; |
| 54 |
| 55 // Offset of the affordance when it is at the maximum distance with content |
| 56 // border. Since the affordance is initially out of content bounds, this is the |
| 57 // offset of the farther side of the affordance (which equals 128 + 18). |
| 58 const int kMaxAffordanceOffset = 146; |
| 59 |
| 60 // Parameters defining animation when the affordance is aborted. |
| 61 const gfx::Tween::Type kAbortAnimationTweenType = gfx::Tween::EASE_IN; |
| 62 const int kAbortAnimationDuration = 300; |
| 37 | 63 |
| 38 bool ShouldNavigateForward(const NavigationController& controller, | 64 bool ShouldNavigateForward(const NavigationController& controller, |
| 39 OverscrollMode mode) { | 65 OverscrollMode mode) { |
| 40 return mode == (base::i18n::IsRTL() ? OVERSCROLL_EAST : OVERSCROLL_WEST) && | 66 return mode == (base::i18n::IsRTL() ? OVERSCROLL_EAST : OVERSCROLL_WEST) && |
| 41 controller.CanGoForward(); | 67 controller.CanGoForward(); |
| 42 } | 68 } |
| 43 | 69 |
| 44 bool ShouldNavigateBack(const NavigationController& controller, | 70 bool ShouldNavigateBack(const NavigationController& controller, |
| 45 OverscrollMode mode) { | 71 OverscrollMode mode) { |
| 46 return mode == (base::i18n::IsRTL() ? OVERSCROLL_WEST : OVERSCROLL_EAST) && | 72 return mode == (base::i18n::IsRTL() ? OVERSCROLL_WEST : OVERSCROLL_EAST) && |
| 47 controller.CanGoBack(); | 73 controller.CanGoBack(); |
| 48 } | 74 } |
| 49 | 75 |
| 50 // An animation observers that deletes itself and a pointer after the end of the | 76 } // namespace |
| 51 // animation. | 77 |
| 52 template <class T> | 78 // This class is responsible for creating, painting, and positioning the layer |
| 53 class DeleteAfterAnimation : public ui::ImplicitAnimationObserver { | 79 // for the gesture nav affordance. |
| 80 class GestureNavSimple::Affordance : public ui::LayerDelegate, |
| 81 public gfx::AnimationDelegate { |
| 54 public: | 82 public: |
| 55 explicit DeleteAfterAnimation(std::unique_ptr<T> object) | 83 Affordance(OverscrollMode mode, const gfx::Rect& content_bounds); |
| 56 : object_(std::move(object)) {} | 84 ~Affordance() override; |
| 85 |
| 86 // Sets progress of affordance drag as a value between 0 and 1. |
| 87 void SetDragProgress(float progress); |
| 88 |
| 89 // Aborts the affordance and animates it back. This will delete |this| |
| 90 // instance after the animation. |
| 91 void Abort(); |
| 92 |
| 93 // Completes the affordance by doing a ripple burst animation. This will |
| 94 // delete |this| instance after the animation. |
| 95 void Complete(); |
| 96 |
| 97 // Returns the root layer of the affordance. |
| 98 ui::Layer* root_layer() const { return root_layer_.get(); } |
| 57 | 99 |
| 58 private: | 100 private: |
| 59 friend class base::DeleteHelper<DeleteAfterAnimation<T> >; | 101 enum class State { DRAGGING, ABORTING, COMPLETING }; |
| 60 | 102 |
| 61 ~DeleteAfterAnimation() override {} | 103 void UpdateTransform(); |
| 62 | 104 void SchedulePaint(); |
| 63 // ui::ImplicitAnimationObserver: | 105 void SetAbortProgress(float progress); |
| 64 void OnImplicitAnimationsCompleted() override { | 106 void SetCompleteProgress(float progress); |
| 65 // Deleting an observer when a ScopedLayerAnimationSettings is iterating | 107 |
| 66 // over them can cause a crash (which can happen during tests). So instead, | 108 // ui::LayerDelegate: |
| 67 // schedule this observer to be deleted soon. | 109 void OnPaintLayer(const ui::PaintContext& context) override; |
| 68 BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this); | 110 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override; |
| 111 void OnDeviceScaleFactorChanged(float device_scale_factor) override; |
| 112 |
| 113 // gfx::AnimationDelegate: |
| 114 void AnimationEnded(const gfx::Animation* animation) override; |
| 115 void AnimationProgressed(const gfx::Animation* animation) override; |
| 116 void AnimationCanceled(const gfx::Animation* animation) override; |
| 117 |
| 118 const OverscrollMode mode_; |
| 119 |
| 120 // Root layer of the affordance. This is used to clip the affordance to the |
| 121 // content bounds. |
| 122 std::unique_ptr<ui::Layer> root_layer_; |
| 123 |
| 124 // Layer that actually paints the affordance. |
| 125 std::unique_ptr<ui::Layer> painted_layer_; |
| 126 |
| 127 // Arrow image to be used for the affordance. |
| 128 const gfx::Image image_; |
| 129 |
| 130 // Values that determine current state of the affordance. |
| 131 State state_ = State::DRAGGING; |
| 132 float drag_progress_ = 0.f; |
| 133 float abort_progress_ = 0.f; |
| 134 float complete_progress_ = 0.f; |
| 135 |
| 136 std::unique_ptr<gfx::LinearAnimation> animation_; |
| 137 |
| 138 DISALLOW_COPY_AND_ASSIGN(Affordance); |
| 139 }; |
| 140 |
| 141 GestureNavSimple::Affordance::Affordance(OverscrollMode mode, |
| 142 const gfx::Rect& content_bounds) |
| 143 : mode_(mode), |
| 144 root_layer_(base::MakeUnique<ui::Layer>(ui::LAYER_NOT_DRAWN)), |
| 145 painted_layer_(base::MakeUnique<ui::Layer>(ui::LAYER_TEXTURED)), |
| 146 image_(gfx::CreateVectorIcon( |
| 147 mode == OVERSCROLL_EAST ? ui::kBackArrowIcon : ui::kForwardArrowIcon, |
| 148 kArrowSize, |
| 149 kArrowColor)) { |
| 150 DCHECK(mode == OVERSCROLL_EAST || mode == OVERSCROLL_WEST); |
| 151 DCHECK(!image_.IsEmpty()); |
| 152 |
| 153 root_layer_->SetBounds(content_bounds); |
| 154 root_layer_->SetMasksToBounds(true); |
| 155 |
| 156 painted_layer_->SetFillsBoundsOpaquely(false); |
| 157 int x = |
| 158 mode_ == OVERSCROLL_EAST |
| 159 ? -kMaxRippleBurstRadius - kBackgroundRadius |
| 160 : content_bounds.width() - kMaxRippleBurstRadius + kBackgroundRadius; |
| 161 int y = std::max(0, content_bounds.height() / 2 - kMaxRippleBurstRadius); |
| 162 painted_layer_->SetBounds( |
| 163 gfx::Rect(x, y, 2 * kMaxRippleBurstRadius, 2 * kMaxRippleBurstRadius)); |
| 164 painted_layer_->set_delegate(this); |
| 165 |
| 166 root_layer_->Add(painted_layer_.get()); |
| 167 } |
| 168 |
| 169 GestureNavSimple::Affordance::~Affordance() {} |
| 170 |
| 171 void GestureNavSimple::Affordance::SetDragProgress(float progress) { |
| 172 DCHECK_EQ(State::DRAGGING, state_); |
| 173 DCHECK_LE(0.f, progress); |
| 174 DCHECK_GE(1.f, progress); |
| 175 |
| 176 if (drag_progress_ == progress) |
| 177 return; |
| 178 drag_progress_ = progress; |
| 179 |
| 180 UpdateTransform(); |
| 181 SchedulePaint(); |
| 182 } |
| 183 |
| 184 void GestureNavSimple::Affordance::Abort() { |
| 185 DCHECK_EQ(State::DRAGGING, state_); |
| 186 |
| 187 state_ = State::ABORTING; |
| 188 |
| 189 animation_.reset( |
| 190 new gfx::LinearAnimation(drag_progress_ * kAbortAnimationDuration, |
| 191 gfx::LinearAnimation::kDefaultFrameRate, this)); |
| 192 animation_->Start(); |
| 193 } |
| 194 |
| 195 void GestureNavSimple::Affordance::Complete() { |
| 196 DCHECK_EQ(State::DRAGGING, state_); |
| 197 DCHECK_EQ(1.f, drag_progress_); |
| 198 |
| 199 state_ = State::COMPLETING; |
| 200 |
| 201 animation_.reset( |
| 202 new gfx::LinearAnimation(kRippleBurstAnimationDuration, |
| 203 gfx::LinearAnimation::kDefaultFrameRate, this)); |
| 204 animation_->Start(); |
| 205 } |
| 206 |
| 207 void GestureNavSimple::Affordance::UpdateTransform() { |
| 208 float offset = (1 - abort_progress_) * drag_progress_ * kMaxAffordanceOffset; |
| 209 gfx::Transform transform; |
| 210 transform.Translate(mode_ == OVERSCROLL_EAST ? offset : -offset, 0); |
| 211 painted_layer_->SetTransform(transform); |
| 212 } |
| 213 |
| 214 void GestureNavSimple::Affordance::SchedulePaint() { |
| 215 painted_layer_->SchedulePaint(gfx::Rect(painted_layer_->size())); |
| 216 } |
| 217 |
| 218 void GestureNavSimple::Affordance::SetAbortProgress(float progress) { |
| 219 DCHECK_EQ(State::ABORTING, state_); |
| 220 DCHECK_LE(0.f, progress); |
| 221 DCHECK_GE(1.f, progress); |
| 222 |
| 223 if (abort_progress_ == progress) |
| 224 return; |
| 225 abort_progress_ = progress; |
| 226 |
| 227 UpdateTransform(); |
| 228 SchedulePaint(); |
| 229 } |
| 230 |
| 231 void GestureNavSimple::Affordance::SetCompleteProgress(float progress) { |
| 232 DCHECK_EQ(State::COMPLETING, state_); |
| 233 DCHECK_LE(0.f, progress); |
| 234 DCHECK_GE(1.f, progress); |
| 235 |
| 236 if (complete_progress_ == progress) |
| 237 return; |
| 238 complete_progress_ = progress; |
| 239 |
| 240 painted_layer_->SetOpacity(1 - complete_progress_); |
| 241 SchedulePaint(); |
| 242 } |
| 243 |
| 244 void GestureNavSimple::Affordance::OnPaintLayer( |
| 245 const ui::PaintContext& context) { |
| 246 DCHECK(drag_progress_ == 1.f || state_ != State::COMPLETING); |
| 247 DCHECK(abort_progress_ == 0.f || state_ == State::ABORTING); |
| 248 DCHECK(complete_progress_ == 0.f || state_ == State::COMPLETING); |
| 249 |
| 250 ui::PaintRecorder recorder(context, painted_layer_->size()); |
| 251 gfx::Canvas* canvas = recorder.canvas(); |
| 252 |
| 253 gfx::PointF center_point(kMaxRippleBurstRadius, kMaxRippleBurstRadius); |
| 254 float progress = (1 - abort_progress_) * drag_progress_; |
| 255 |
| 256 // Draw the ripple. |
| 257 cc::PaintFlags ripple_paint; |
| 258 ripple_paint.setAntiAlias(true); |
| 259 ripple_paint.setStyle(cc::PaintFlags::kFill_Style); |
| 260 ripple_paint.setColor(kRippleColor); |
| 261 float ripple_radius; |
| 262 if (state_ == State::COMPLETING) { |
| 263 ripple_radius = |
| 264 kMaxRippleRadius + |
| 265 complete_progress_ * (kMaxRippleBurstRadius - kMaxRippleRadius); |
| 266 } else { |
| 267 ripple_radius = |
| 268 kBackgroundRadius + progress * (kMaxRippleRadius - kBackgroundRadius); |
| 69 } | 269 } |
| 70 | 270 canvas->DrawCircle(center_point, ripple_radius, ripple_paint); |
| 71 std::unique_ptr<T> object_; | 271 |
| 72 DISALLOW_COPY_AND_ASSIGN(DeleteAfterAnimation); | 272 // Draw the arrow background circle with the shadow. |
| 73 }; | 273 cc::PaintFlags bg_paint; |
| 74 | 274 bg_paint.setAntiAlias(true); |
| 75 } // namespace | 275 bg_paint.setStyle(cc::PaintFlags::kFill_Style); |
| 76 | 276 bg_paint.setColor(kBackgroundColor); |
| 77 // A layer delegate that paints the shield with the arrow in it. | 277 gfx::ShadowValues shadow; |
| 78 class ArrowLayerDelegate : public ui::LayerDelegate { | 278 shadow.emplace_back(gfx::Vector2d(0, kBgShadowOffsetY), kBgShadowBlurRadius, |
| 79 public: | 279 kBgShadowColor); |
| 80 explicit ArrowLayerDelegate(int resource_id) | 280 bg_paint.setLooper(gfx::CreateShadowDrawLooperCorrectBlur(shadow)); |
| 81 : image_(GetContentClient()->GetNativeImageNamed(resource_id)), | 281 canvas->DrawCircle(center_point, kBackgroundRadius, bg_paint); |
| 82 left_arrow_(resource_id == IDR_BACK_ARROW) { | 282 |
| 83 CHECK(!image_.IsEmpty()); | 283 // Draw the arrow. |
| 284 float arrow_x = center_point.x() - kArrowSize / 2.f; |
| 285 float arrow_y = center_point.y() - kArrowSize / 2.f; |
| 286 // Calculate the offset for the arrow relative to its circular background. |
| 287 float arrow_x_offset = |
| 288 (1 - progress) * (-kBackgroundRadius + kArrowSize / 2.f); |
| 289 arrow_x += mode_ == OVERSCROLL_EAST ? arrow_x_offset : -arrow_x_offset; |
| 290 uint8_t arrow_alpha = |
| 291 static_cast<uint8_t>(std::min(0xFF, static_cast<int>(progress * 0xFF))); |
| 292 canvas->DrawImageInt(*image_.ToImageSkia(), static_cast<int>(arrow_x), |
| 293 static_cast<int>(arrow_y), arrow_alpha); |
| 294 } |
| 295 |
| 296 void GestureNavSimple::Affordance::OnDelegatedFrameDamage( |
| 297 const gfx::Rect& damage_rect_in_dip) {} |
| 298 |
| 299 void GestureNavSimple::Affordance::OnDeviceScaleFactorChanged( |
| 300 float device_scale_factor) {} |
| 301 |
| 302 void GestureNavSimple::Affordance::AnimationEnded( |
| 303 const gfx::Animation* animation) { |
| 304 delete this; |
| 305 } |
| 306 |
| 307 void GestureNavSimple::Affordance::AnimationProgressed( |
| 308 const gfx::Animation* animation) { |
| 309 switch (state_) { |
| 310 case State::DRAGGING: |
| 311 NOTREACHED(); |
| 312 break; |
| 313 case State::ABORTING: |
| 314 SetAbortProgress(gfx::Tween::CalculateValue( |
| 315 kAbortAnimationTweenType, animation->GetCurrentValue())); |
| 316 break; |
| 317 case State::COMPLETING: |
| 318 SetCompleteProgress(gfx::Tween::CalculateValue( |
| 319 kBurstAnimationTweenType, animation->GetCurrentValue())); |
| 320 break; |
| 84 } | 321 } |
| 85 | 322 } |
| 86 ~ArrowLayerDelegate() override {} | 323 |
| 87 | 324 void GestureNavSimple::Affordance::AnimationCanceled( |
| 88 bool left() const { return left_arrow_; } | 325 const gfx::Animation* animation) { |
| 89 | 326 NOTREACHED(); |
| 90 private: | 327 } |
| 91 // ui::LayerDelegate: | 328 |
| 92 void OnPaintLayer(const ui::PaintContext& context) override { | |
| 93 cc::PaintFlags paint; | |
| 94 paint.setColor(SkColorSetARGB(0xa0, 0, 0, 0)); | |
| 95 paint.setStyle(cc::PaintFlags::kFill_Style); | |
| 96 paint.setAntiAlias(true); | |
| 97 | |
| 98 // Set the recording size to be the size of the |arrow_| layer, and draw a | |
| 99 // half circle (the other half will be clipped), then an arrow image inside | |
| 100 // it. | |
| 101 ui::PaintRecorder recorder(context, gfx::Size(kArrowWidth, kArrowHeight)); | |
| 102 recorder.canvas()->DrawCircle( | |
| 103 gfx::Point(left_arrow_ ? 0 : kArrowWidth, kArrowHeight / 2), | |
| 104 kArrowWidth, paint); | |
| 105 recorder.canvas()->DrawImageInt( | |
| 106 *image_.ToImageSkia(), left_arrow_ ? 0 : kArrowWidth - image_.Width(), | |
| 107 (kArrowHeight - image_.Height()) / 2); | |
| 108 } | |
| 109 | |
| 110 void OnDelegatedFrameDamage(const gfx::Rect& damage_rect_in_dip) override {} | |
| 111 | |
| 112 void OnDeviceScaleFactorChanged(float device_scale_factor) override {} | |
| 113 | |
| 114 const gfx::Image& image_; | |
| 115 const bool left_arrow_; | |
| 116 | |
| 117 DISALLOW_COPY_AND_ASSIGN(ArrowLayerDelegate); | |
| 118 }; | |
| 119 | 329 |
| 120 GestureNavSimple::GestureNavSimple(WebContentsImpl* web_contents) | 330 GestureNavSimple::GestureNavSimple(WebContentsImpl* web_contents) |
| 121 : web_contents_(web_contents), | 331 : web_contents_(web_contents), |
| 122 completion_threshold_(0.f) {} | 332 completion_threshold_(0.f) {} |
| 123 | 333 |
| 124 GestureNavSimple::~GestureNavSimple() {} | 334 GestureNavSimple::~GestureNavSimple() {} |
| 125 | 335 |
| 126 void GestureNavSimple::ApplyEffectsAndDestroy(const gfx::Transform& transform, | |
| 127 float opacity) { | |
| 128 ui::Layer* layer = arrow_.get(); | |
| 129 ui::ScopedLayerAnimationSettings settings(arrow_->GetAnimator()); | |
| 130 settings.AddObserver( | |
| 131 new DeleteAfterAnimation<ArrowLayerDelegate>(std::move(arrow_delegate_))); | |
| 132 settings.AddObserver(new DeleteAfterAnimation<ui::Layer>(std::move(arrow_))); | |
| 133 settings.AddObserver( | |
| 134 new DeleteAfterAnimation<ui::Layer>(std::move(clip_layer_))); | |
| 135 layer->SetTransform(transform); | |
| 136 layer->SetOpacity(opacity); | |
| 137 } | |
| 138 | |
| 139 void GestureNavSimple::AbortGestureAnimation() { | 336 void GestureNavSimple::AbortGestureAnimation() { |
| 140 if (!arrow_) | 337 if (!affordance_) |
| 141 return; | 338 return; |
| 142 gfx::Transform transform; | 339 // Release the unique pointer. The affordance will delete itself upon |
| 143 transform.Translate(arrow_delegate_->left() ? -kArrowWidth : kArrowWidth, 0); | 340 // completion of animation. |
| 144 ApplyEffectsAndDestroy(transform, kMinOpacity); | 341 Affordance* affordance = affordance_.release(); |
| 342 affordance->Abort(); |
| 145 } | 343 } |
| 146 | 344 |
| 147 void GestureNavSimple::CompleteGestureAnimation() { | 345 void GestureNavSimple::CompleteGestureAnimation() { |
| 148 if (!arrow_) | 346 if (!affordance_) |
| 149 return; | 347 return; |
| 150 // Make sure the fade-out starts from the complete state. | 348 // Release the unique pointer. The affordance will delete itself upon |
| 151 ApplyEffectsForDelta(completion_threshold_); | 349 // completion of animation. |
| 152 ApplyEffectsAndDestroy(arrow_->transform(), 0.f); | 350 Affordance* affordance = affordance_.release(); |
| 153 } | 351 affordance->Complete(); |
| 154 | |
| 155 bool GestureNavSimple::ApplyEffectsForDelta(float delta_x) { | |
| 156 if (!arrow_) | |
| 157 return false; | |
| 158 CHECK_GT(completion_threshold_, 0.f); | |
| 159 CHECK_GE(delta_x, 0.f); | |
| 160 double complete = std::min(1.f, delta_x / completion_threshold_); | |
| 161 float translate_x = gfx::Tween::FloatValueBetween(complete, -kArrowWidth, 0); | |
| 162 gfx::Transform transform; | |
| 163 transform.Translate(arrow_delegate_->left() ? translate_x : -translate_x, | |
| 164 0.f); | |
| 165 arrow_->SetTransform(transform); | |
| 166 arrow_->SetOpacity(gfx::Tween::FloatValueBetween(complete, kMinOpacity, 1.f)); | |
| 167 return true; | |
| 168 } | 352 } |
| 169 | 353 |
| 170 gfx::Rect GestureNavSimple::GetVisibleBounds() const { | 354 gfx::Rect GestureNavSimple::GetVisibleBounds() const { |
| 171 return web_contents_->GetNativeView()->bounds(); | 355 return web_contents_->GetNativeView()->bounds(); |
| 172 } | 356 } |
| 173 | 357 |
| 174 bool GestureNavSimple::OnOverscrollUpdate(float delta_x, float delta_y) { | 358 bool GestureNavSimple::OnOverscrollUpdate(float delta_x, float delta_y) { |
| 175 return ApplyEffectsForDelta(std::abs(delta_x) + 50.f); | 359 if (!affordance_) |
| 360 return false; |
| 361 affordance_->SetDragProgress( |
| 362 std::min(1.f, std::abs(delta_x) / completion_threshold_)); |
| 363 return true; |
| 176 } | 364 } |
| 177 | 365 |
| 178 void GestureNavSimple::OnOverscrollComplete(OverscrollMode overscroll_mode) { | 366 void GestureNavSimple::OnOverscrollComplete(OverscrollMode overscroll_mode) { |
| 179 CompleteGestureAnimation(); | 367 CompleteGestureAnimation(); |
| 180 | 368 |
| 181 NavigationControllerImpl& controller = web_contents_->GetController(); | 369 NavigationControllerImpl& controller = web_contents_->GetController(); |
| 182 if (ShouldNavigateForward(controller, overscroll_mode)) | 370 if (ShouldNavigateForward(controller, overscroll_mode)) |
| 183 controller.GoForward(); | 371 controller.GoForward(); |
| 184 else if (ShouldNavigateBack(controller, overscroll_mode)) | 372 else if (ShouldNavigateBack(controller, overscroll_mode)) |
| 185 controller.GoBack(); | 373 controller.GoBack(); |
| 186 } | 374 } |
| 187 | 375 |
| 188 void GestureNavSimple::OnOverscrollModeChange(OverscrollMode old_mode, | 376 void GestureNavSimple::OnOverscrollModeChange(OverscrollMode old_mode, |
| 189 OverscrollMode new_mode) { | 377 OverscrollMode new_mode) { |
| 190 NavigationControllerImpl& controller = web_contents_->GetController(); | 378 NavigationControllerImpl& controller = web_contents_->GetController(); |
| 191 if (!ShouldNavigateForward(controller, new_mode) && | 379 if (!ShouldNavigateForward(controller, new_mode) && |
| 192 !ShouldNavigateBack(controller, new_mode)) { | 380 !ShouldNavigateBack(controller, new_mode)) { |
| 193 AbortGestureAnimation(); | 381 AbortGestureAnimation(); |
| 194 return; | 382 return; |
| 195 } | 383 } |
| 196 | 384 |
| 197 arrow_.reset(new ui::Layer(ui::LAYER_TEXTURED)); | |
| 198 // Note that RTL doesn't affect the arrow that should be displayed. | |
| 199 int resource_id = 0; | |
| 200 if (new_mode == OVERSCROLL_WEST) | |
| 201 resource_id = IDR_FORWARD_ARROW; | |
| 202 else if (new_mode == OVERSCROLL_EAST) | |
| 203 resource_id = IDR_BACK_ARROW; | |
| 204 else | |
| 205 NOTREACHED(); | |
| 206 | |
| 207 arrow_delegate_.reset(new ArrowLayerDelegate(resource_id)); | |
| 208 arrow_->set_delegate(arrow_delegate_.get()); | |
| 209 arrow_->SetFillsBoundsOpaquely(false); | |
| 210 | |
| 211 aura::Window* window = web_contents_->GetNativeView(); | 385 aura::Window* window = web_contents_->GetNativeView(); |
| 212 const gfx::Rect& window_bounds = window->bounds(); | 386 const gfx::Rect& window_bounds = window->bounds(); |
| 213 completion_threshold_ = window_bounds.width() * | 387 completion_threshold_ = |
| 214 GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE); | 388 window_bounds.width() * |
| 389 GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE) - |
| 390 GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_START_TOUCHSCREEN); |
| 215 | 391 |
| 216 // Align on the left or right edge. | 392 affordance_.reset(new Affordance(new_mode, window_bounds)); |
| 217 int x = (resource_id == IDR_BACK_ARROW) ? 0 : | |
| 218 (window_bounds.width() - kArrowWidth); | |
| 219 // Align in the center vertically. | |
| 220 int y = std::max(0, (window_bounds.height() - kArrowHeight) / 2); | |
| 221 arrow_->SetBounds(gfx::Rect(x, y, kArrowWidth, kArrowHeight)); | |
| 222 ApplyEffectsForDelta(0.f); | |
| 223 | 393 |
| 224 // Adding the arrow as a child of the content window is not sufficient, | 394 // Adding the affordance as a child of the content window is not sufficient, |
| 225 // because it is possible for a new layer to be parented on top of the arrow | 395 // because it is possible for a new layer to be parented on top of the |
| 226 // layer (e.g. when the navigated-to page is displayed while the completion | 396 // affordance layer (e.g. when the navigated-to page is displayed while the |
| 227 // animation is in progress). So instead, a clip layer (that doesn't paint) is | 397 // completion animation is in progress). So instead, it is installed on top of |
| 228 // installed on top of the content window as its sibling, and the arrow layer | 398 // the content window as its sibling. Note that the affordance itself makes |
| 229 // is added to that clip layer. | 399 // sure that its contents are clipped to the bounds given to it. |
| 230 clip_layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN)); | |
| 231 clip_layer_->SetBounds(window->layer()->bounds()); | |
| 232 clip_layer_->SetMasksToBounds(true); | |
| 233 clip_layer_->Add(arrow_.get()); | |
| 234 | |
| 235 ui::Layer* parent = window->layer()->parent(); | 400 ui::Layer* parent = window->layer()->parent(); |
| 236 parent->Add(clip_layer_.get()); | 401 parent->Add(affordance_->root_layer()); |
| 237 parent->StackAtTop(clip_layer_.get()); | 402 parent->StackAtTop(affordance_->root_layer()); |
| 238 } | 403 } |
| 239 | 404 |
| 240 } // namespace content | 405 } // namespace content |
| OLD | NEW |