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

Side by Side Diff: content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.mm

Issue 314393003: mac: Add overscroll animator slider (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Respond to comments from avi. Created 6 years, 6 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
OLDNEW
(Empty)
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 #import <QuartzCore/QuartzCore.h>
6
7 #include "content/browser/web_contents/web_contents_view_overscroll_animator_sli der_mac.h"
8
9 #include "content/browser/web_contents/web_contents_impl.h"
10 #include "content/public/browser/web_contents_observer.h"
11
12 namespace {
13 // The minimum possible progress of an overscroll animation.
14 CGFloat kMinProgress = 0;
15 // The maximum possible progress of an overscroll animation.
16 CGFloat kMaxProgress = 2.0;
17 // The maximum duration of the completion or cancellation animations. The
18 // effective maximum is half of this value, since the longest animation is from
19 // progress = 1.0 to progress = 2.0;
20 CGFloat kMaxAnimationDuration = 0.2;
21 } // namespace
22
23 // OverscrollAnimatorSliderView Private Category -------------------------------
24
25 @interface OverscrollAnimatorSliderView ()
26 // Callback from WebContentsPaintObserver.
27 - (void)webContentsFinishedNonEmptyPaint;
28
29 // Resets overscroll animation state.
30 - (void)reset;
31
32 // Given a |progress| from 0 to 2, the expected frame origin of the -movingView.
33 - (NSPoint)frameOriginWithProgress:(CGFloat)progress;
34
35 // The NSView that is moving during the overscroll animation.
36 - (NSView*)movingView;
37
38 // The expected duration of an animation from progress_ to |progress|
39 - (CGFloat)animationDurationForProgress:(CGFloat)progress;
40
41 // NSView override. During an overscroll animation, the cursor may no longer
42 // rest on the RenderWidgetHost's NativeView, which prevents wheel events from
43 // reaching the NativeView. The overscroll animation is driven by wheel events
44 // so they must be explicitly forwarded to the NativeView.
45 - (void)scrollWheel:(NSEvent*)event;
46 @end
47
48 // Helper Class (ResizingView) -------------------------------------------------
49
50 // This NSView subclass is intended to be the RenderWidgetHost's NativeView's
51 // parent NSView. It is possible for the RenderWidgetHost's NativeView's size to
52 // become out of sync with its parent NSView. The override of
53 // -resizeSubviewsWithOldSize: ensures that the sizes will eventually become
54 // consistent.
55 // http://crbug.com/264207
56 @interface ResizingView : NSView
57 @end
58
59 @implementation ResizingView
60 - (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
61 for (NSView* subview in self.subviews)
62 [subview setFrame:self.bounds];
63 }
64 @end
65
66 // Helper Class (WebContentsPaintObserver) -------------------------------------
67
68 namespace overscroll_animator {
69 class WebContentsPaintObserver : public content::WebContentsObserver {
70 public:
71 WebContentsPaintObserver(content::WebContents* web_contents,
72 OverscrollAnimatorSliderView* slider_view)
73 : WebContentsObserver(web_contents), slider_view_(slider_view) {}
74
75 virtual void DidFirstVisuallyNonEmptyPaint() OVERRIDE {
76 [slider_view_ webContentsFinishedNonEmptyPaint];
77 }
78
79 private:
80 OverscrollAnimatorSliderView* slider_view_; // Weak reference.
81 };
82 } // namespace overscroll_animator
83
84 // OverscrollAnimatorSliderView Implementation ---------------------------------
85
86 @implementation OverscrollAnimatorSliderView
87
88 - (instancetype)initWithFrame:(NSRect)frame {
89 self = [super initWithFrame:frame];
90 if (self) {
91 bottomView_.reset([[NSImageView alloc] initWithFrame:self.bounds]);
92 bottomView_.get().imageScaling = NSImageScaleNone;
93 bottomView_.get().autoresizingMask =
94 NSViewWidthSizable | NSViewHeightSizable;
95 bottomView_.get().imageAlignment = NSImageAlignTop;
96 [self addSubview:bottomView_];
97 middleView_.reset([[ResizingView alloc] initWithFrame:self.bounds]);
98 middleView_.get().autoresizingMask =
99 NSViewWidthSizable | NSViewHeightSizable;
100 [self addSubview:middleView_];
101 topView_.reset([[NSImageView alloc] initWithFrame:self.bounds]);
102 topView_.get().autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
103 topView_.get().imageScaling = NSImageScaleNone;
104 topView_.get().imageAlignment = NSImageAlignTop;
105 [self addSubview:topView_];
106
107 [self reset];
108 }
109 return self;
110 }
111
112 - (void)webContentsFinishedNonEmptyPaint {
113 observer_.reset();
114 [self reset];
115 }
116
117 - (void)reset {
118 DCHECK(!animating_);
119 inOverscroll_ = NO;
120 progress_ = kMinProgress;
121
122 [CATransaction begin];
123 [CATransaction setDisableActions:YES];
124 bottomView_.get().hidden = YES;
125 middleView_.get().hidden = NO;
126 topView_.get().hidden = YES;
127
128 [bottomView_ setFrameOrigin:NSMakePoint(0, 0)];
129 [middleView_ setFrameOrigin:NSMakePoint(0, 0)];
130 [topView_ setFrameOrigin:NSMakePoint(0, 0)];
131 [CATransaction commit];
132 }
133
134 - (NSPoint)frameOriginWithProgress:(CGFloat)progress {
135 if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS)
136 return NSMakePoint(progress / kMaxProgress * self.bounds.size.width, 0);
137 return NSMakePoint((1 - progress / kMaxProgress) * self.bounds.size.width, 0);
138 }
139
140 - (NSView*)movingView {
141 if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS)
142 return middleView_;
143 return topView_;
144 }
145
146 - (CGFloat)animationDurationForProgress:(CGFloat)progress {
147 CGFloat progressPercentage =
148 fabs(progress_ - progress) / (kMaxProgress - kMinProgress);
149 return progressPercentage * kMaxAnimationDuration;
150 }
151
152 - (void)scrollWheel:(NSEvent*)event {
153 NSView* latestRenderWidgetHostView = [[middleView_ subviews] lastObject];
154 [latestRenderWidgetHostView scrollWheel:event];
155 }
156
157 // WebContentsOverscrollAnimator Implementation --------------------------------
158
159 - (BOOL)needsNavigationSnapshot {
160 return YES;
161 }
162
163 - (void)beginOverscrollInDirection:
164 (content::OverscrollAnimatorDirection)direction
165 navigationSnapshot:(NSImage*)snapshot {
166 // TODO(erikchen): If snapshot is nil, need a placeholder.
167 if (animating_ || inOverscroll_)
168 return;
169
170 inOverscroll_ = YES;
171 direction_ = direction;
172 if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS) {
173 // The middleView_ will slide to the right, revealing bottomView_.
174 bottomView_.get().hidden = NO;
175 [bottomView_ setImage:snapshot];
176 } else {
177 // The topView_ will slide in from the right, concealing middleView_.
178 topView_.get().hidden = NO;
179 [topView_ setFrameOrigin:NSMakePoint(self.bounds.size.width, 0)];
180 [topView_ setImage:snapshot];
181 }
182
183 [self updateOverscrollProgress:kMinProgress];
184 }
185
186 - (void)addRenderWidgetHostNativeView:(NSView*)view {
187 [middleView_ addSubview:view];
188 }
189
190 - (void)updateOverscrollProgress:(CGFloat)progress {
191 if (animating_)
192 return;
193 DCHECK_LE(progress, kMaxProgress + 0.00001);
194 DCHECK_GE(progress, kMinProgress - 0.00001);
Avi (use Gerrit) 2014/06/09 15:29:00 Why do you insist on that .00001? Please remove it
erikchen 2014/06/09 21:01:25 I haven't had a chance to update this CL since you
195 progress_ = progress;
196 [[self movingView] setFrameOrigin:[self frameOriginWithProgress:progress]];
197 }
198
199 - (void)completeOverscroll:(content::WebContentsImpl*)webContents {
200 if (animating_ || !inOverscroll_)
201 return;
202
203 animating_ = YES;
204
205 NSView* view = [self movingView];
206 [NSAnimationContext beginGrouping];
207 [NSAnimationContext currentContext].duration =
208 [self animationDurationForProgress:kMaxProgress];
209 [[NSAnimationContext currentContext] setCompletionHandler:^{
210 animating_ = NO;
211
212 // Animation is complete. Now perform page load.
213 if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS)
214 webContents->GetController().GoBack();
215 else
216 webContents->GetController().GoForward();
217
218 // Reset the position of the middleView_, but wait for the page to paint
219 // before showing it.
220 middleView_.get().hidden = YES;
221 [middleView_ setFrameOrigin:NSMakePoint(0, 0)];
222 observer_.reset(
223 new overscroll_animator::WebContentsPaintObserver(webContents, self));
224 }];
225
226 // Animate the moving view to its final position.
227 [[view animator] setFrameOrigin:[self frameOriginWithProgress:kMaxProgress]];
228
229 [NSAnimationContext endGrouping];
230 }
231
232 - (void)cancelOverscroll {
233 if (animating_)
234 return;
235
236 if (!inOverscroll_) {
237 [self reset];
238 return;
239 }
240
241 animating_ = YES;
242
243 NSView* view = [self movingView];
244 [NSAnimationContext beginGrouping];
245 [NSAnimationContext currentContext].duration =
246 [self animationDurationForProgress:kMinProgress];
247 [[NSAnimationContext currentContext] setCompletionHandler:^{
248 // Animation is complete. Reset the state.
249 animating_ = NO;
250 [self reset];
251 }];
252
253 // Animate the moving view to its initial position.
254 [[view animator] setFrameOrigin:[self frameOriginWithProgress:kMinProgress]];
255
256 [NSAnimationContext endGrouping];
257 }
258
259 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698