Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / content / browser / web_contents / aura / gesture_nav_simple.cc
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
3 // found in the LICENSE file.
4
5 #include "content/browser/web_contents/aura/gesture_nav_simple.h"
6
7 #include "cc/layers/layer.h"
8 #include "content/browser/frame_host/navigation_controller_impl.h"
9 #include "content/browser/renderer_host/overscroll_controller.h"
10 #include "content/browser/web_contents/web_contents_impl.h"
11 #include "content/browser/web_contents/web_contents_view.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/overscroll_configuration.h"
14 #include "content/public/common/content_client.h"
15 #include "grit/ui_resources.h"
16 #include "ui/aura/window.h"
17 #include "ui/compositor/layer.h"
18 #include "ui/compositor/layer_animation_observer.h"
19 #include "ui/compositor/layer_delegate.h"
20 #include "ui/compositor/scoped_layer_animation_settings.h"
21 #include "ui/gfx/animation/tween.h"
22 #include "ui/gfx/canvas.h"
23 #include "ui/gfx/image/image.h"
24
25 namespace content {
26
27 namespace {
28
29 const int kArrowHeight = 280;
30 const int kArrowWidth = 140;
31 const float kMinOpacity = 0.25f;
32
33 bool ShouldNavigateForward(const NavigationController& controller,
34                            OverscrollMode mode) {
35   return mode == (base::i18n::IsRTL() ? OVERSCROLL_EAST : OVERSCROLL_WEST) &&
36          controller.CanGoForward();
37 }
38
39 bool ShouldNavigateBack(const NavigationController& controller,
40                         OverscrollMode mode) {
41   return mode == (base::i18n::IsRTL() ? OVERSCROLL_WEST : OVERSCROLL_EAST) &&
42          controller.CanGoBack();
43 }
44
45 // An animation observers that deletes itself and a pointer after the end of the
46 // animation.
47 template <class T>
48 class DeleteAfterAnimation : public ui::ImplicitAnimationObserver {
49  public:
50   explicit DeleteAfterAnimation(scoped_ptr<T> object)
51       : object_(object.Pass()) {}
52
53  private:
54   friend class base::DeleteHelper<DeleteAfterAnimation<T> >;
55
56   virtual ~DeleteAfterAnimation() {}
57
58   // ui::ImplicitAnimationObserver:
59   virtual void OnImplicitAnimationsCompleted() OVERRIDE {
60     // Deleting an observer when a ScopedLayerAnimationSettings is iterating
61     // over them can cause a crash (which can happen during tests). So instead,
62     // schedule this observer to be deleted soon.
63     BrowserThread::DeleteSoon(BrowserThread::UI, FROM_HERE, this);
64   }
65
66   scoped_ptr<T> object_;
67   DISALLOW_COPY_AND_ASSIGN(DeleteAfterAnimation);
68 };
69
70 }  // namespace
71
72 // A layer delegate that paints the shield with the arrow in it.
73 class ArrowLayerDelegate : public ui::LayerDelegate {
74  public:
75   explicit ArrowLayerDelegate(int resource_id)
76       : image_(GetContentClient()->GetNativeImageNamed(resource_id)),
77         left_arrow_(resource_id == IDR_BACK_ARROW) {
78     CHECK(!image_.IsEmpty());
79   }
80
81   virtual ~ArrowLayerDelegate() {}
82
83   bool left() const { return left_arrow_; }
84
85  private:
86   // ui::LayerDelegate:
87   virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE {
88     SkPaint paint;
89     paint.setColor(SkColorSetARGB(0xa0, 0, 0, 0));
90     paint.setStyle(SkPaint::kFill_Style);
91     paint.setAntiAlias(true);
92
93     canvas->DrawCircle(
94         gfx::Point(left_arrow_ ? 0 : kArrowWidth, kArrowHeight / 2),
95         kArrowWidth,
96         paint);
97     canvas->DrawImageInt(*image_.ToImageSkia(),
98                          left_arrow_ ? 0 : kArrowWidth - image_.Width(),
99                          (kArrowHeight - image_.Height()) / 2);
100   }
101
102   virtual void OnDeviceScaleFactorChanged(float device_scale_factor) OVERRIDE {}
103
104   virtual base::Closure PrepareForLayerBoundsChange() OVERRIDE {
105     return base::Closure();
106   }
107
108   const gfx::Image& image_;
109   const bool left_arrow_;
110
111   DISALLOW_COPY_AND_ASSIGN(ArrowLayerDelegate);
112 };
113
114 GestureNavSimple::GestureNavSimple(WebContentsImpl* web_contents)
115     : web_contents_(web_contents),
116       completion_threshold_(0.f) {}
117
118 GestureNavSimple::~GestureNavSimple() {}
119
120 void GestureNavSimple::ApplyEffectsAndDestroy(const gfx::Transform& transform,
121                                               float opacity) {
122   ui::Layer* layer = arrow_.get();
123   ui::ScopedLayerAnimationSettings settings(arrow_->GetAnimator());
124   settings.AddObserver(
125       new DeleteAfterAnimation<ArrowLayerDelegate>(arrow_delegate_.Pass()));
126   settings.AddObserver(new DeleteAfterAnimation<ui::Layer>(arrow_.Pass()));
127   settings.AddObserver(new DeleteAfterAnimation<ui::Layer>(clip_layer_.Pass()));
128   layer->SetTransform(transform);
129   layer->SetOpacity(opacity);
130 }
131
132 void GestureNavSimple::AbortGestureAnimation() {
133   if (!arrow_)
134     return;
135   gfx::Transform transform;
136   transform.Translate(arrow_delegate_->left() ? -kArrowWidth : kArrowWidth, 0);
137   ApplyEffectsAndDestroy(transform, kMinOpacity);
138 }
139
140 void GestureNavSimple::CompleteGestureAnimation() {
141   if (!arrow_)
142     return;
143   // Make sure the fade-out starts from the complete state.
144   ApplyEffectsForDelta(completion_threshold_);
145   ApplyEffectsAndDestroy(arrow_->transform(), 0.f);
146 }
147
148 bool GestureNavSimple::ApplyEffectsForDelta(float delta_x) {
149   if (!arrow_)
150     return false;
151   CHECK_GT(completion_threshold_, 0.f);
152   CHECK_GE(delta_x, 0.f);
153   double complete = std::min(1.f, delta_x / completion_threshold_);
154   float translate_x = gfx::Tween::FloatValueBetween(complete, -kArrowWidth, 0);
155   gfx::Transform transform;
156   transform.Translate(arrow_delegate_->left() ? translate_x : -translate_x,
157                       0.f);
158   arrow_->SetTransform(transform);
159   arrow_->SetOpacity(gfx::Tween::FloatValueBetween(complete, kMinOpacity, 1.f));
160   return true;
161 }
162
163 gfx::Rect GestureNavSimple::GetVisibleBounds() const {
164   return web_contents_->GetNativeView()->bounds();
165 }
166
167 bool GestureNavSimple::OnOverscrollUpdate(float delta_x, float delta_y) {
168   return ApplyEffectsForDelta(std::abs(delta_x) + 50.f);
169 }
170
171 void GestureNavSimple::OnOverscrollComplete(OverscrollMode overscroll_mode) {
172   CompleteGestureAnimation();
173
174   NavigationControllerImpl& controller = web_contents_->GetController();
175   if (ShouldNavigateForward(controller, overscroll_mode))
176     controller.GoForward();
177   else if (ShouldNavigateBack(controller, overscroll_mode))
178     controller.GoBack();
179 }
180
181 void GestureNavSimple::OnOverscrollModeChange(OverscrollMode old_mode,
182                                               OverscrollMode new_mode) {
183   NavigationControllerImpl& controller = web_contents_->GetController();
184   if (!ShouldNavigateForward(controller, new_mode) &&
185       !ShouldNavigateBack(controller, new_mode)) {
186     AbortGestureAnimation();
187     return;
188   }
189
190   arrow_.reset(new ui::Layer(ui::LAYER_TEXTURED));
191   // Note that RTL doesn't affect the arrow that should be displayed.
192   int resource_id = 0;
193   if (new_mode == OVERSCROLL_WEST)
194     resource_id = IDR_FORWARD_ARROW;
195   else if (new_mode == OVERSCROLL_EAST)
196     resource_id = IDR_BACK_ARROW;
197   else
198     NOTREACHED();
199
200   arrow_delegate_.reset(new ArrowLayerDelegate(resource_id));
201   arrow_->set_delegate(arrow_delegate_.get());
202   arrow_->SetFillsBoundsOpaquely(false);
203
204   aura::Window* window = web_contents_->GetNativeView();
205   const gfx::Rect& window_bounds = window->bounds();
206   completion_threshold_ = window_bounds.width() *
207       GetOverscrollConfig(OVERSCROLL_CONFIG_HORIZ_THRESHOLD_COMPLETE);
208
209   // Align on the left or right edge.
210   int x = (resource_id == IDR_BACK_ARROW) ? 0 :
211       (window_bounds.width() - kArrowWidth);
212   // Align in the center vertically.
213   int y = std::max(0, (window_bounds.height() - kArrowHeight) / 2);
214   arrow_->SetBounds(gfx::Rect(x, y, kArrowWidth, kArrowHeight));
215   ApplyEffectsForDelta(0.f);
216
217   // Adding the arrow as a child of the content window is not sufficient,
218   // because it is possible for a new layer to be parented on top of the arrow
219   // layer (e.g. when the navigated-to page is displayed while the completion
220   // animation is in progress). So instead, a clip layer (that doesn't paint) is
221   // installed on top of the content window as its sibling, and the arrow layer
222   // is added to that clip layer.
223   clip_layer_.reset(new ui::Layer(ui::LAYER_NOT_DRAWN));
224   clip_layer_->SetBounds(window->layer()->bounds());
225   clip_layer_->SetMasksToBounds(true);
226   clip_layer_->Add(arrow_.get());
227
228   ui::Layer* parent = window->layer()->parent();
229   parent->Add(clip_layer_.get());
230   parent->StackAtTop(clip_layer_.get());
231 }
232
233 }  // namespace content