OLD | NEW |
---|---|
(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 #include "content/browser/web_contents/web_contents_view_overscroll_animator_sli der_mac.h" | |
6 | |
7 #include "content/browser/web_contents/web_contents_impl.h" | |
8 #include "content/public/browser/web_contents_observer.h" | |
9 #import <QuartzCore/QuartzCore.h> | |
Avi (use Gerrit)
2014/06/06 23:09:45
This import gets its own section.
erikchen
2014/06/07 00:34:04
Done.
| |
10 | |
11 namespace { | |
12 // The minimum possible progress of an overscroll animation. | |
13 CGFloat kMinProgress = 0; | |
14 // The maximum possible progress of an overscroll animation. | |
15 CGFloat kMaxProgress = 2.0; | |
16 // The maximum duration of the completion or cancellation animations. The | |
17 // effective maximum is half of this value, since the longest animation is from | |
18 // progress_ = 1 to progress = 2.0; | |
Avi (use Gerrit)
2014/06/06 23:09:45
either progress_ or progress.
erikchen
2014/06/07 00:34:04
I switched to "progress" for both.
| |
19 CGFloat kMaxAnimationDuration = 0.2; | |
20 } | |
21 | |
22 // OverscrollAnimatorSliderView Private Category ------------------------------- | |
23 | |
24 @interface OverscrollAnimatorSliderView () | |
25 // Callback from WebContentsPaintObserver. | |
26 - (void)webContentsFinishedNonEmptyPaint; | |
27 | |
28 // Resets overscroll animation state. | |
29 - (void)reset; | |
30 | |
31 // Given a |progress| from 0 to 2, the expected frame origin of the -movingView. | |
32 - (NSPoint)frameOriginWithProgress:(CGFloat)progress; | |
33 | |
34 // The NSView that is moving during the overscroll animation. | |
35 - (NSView*)movingView; | |
36 | |
37 // The expected duration of an animation from progress_ to |progress| | |
38 - (CGFloat)animationDurationForProgress:(CGFloat)progress; | |
39 | |
40 // NSView override. During an overscroll animation, the cursor may no longer | |
41 // rest on the RenderWidgetHost's NativeView, which prevents wheel events from | |
42 // reaching the NativeView. The overscroll animation is driven by wheel events | |
43 // so they must be explicitly forwarded to the NativeView. | |
44 - (void)scrollWheel:(NSEvent*)event; | |
45 @end | |
46 | |
47 // Helper Class (ResizingView) ------------------------------------------------- | |
48 | |
49 // This NSView subclass is intended to be the RenderWidgetHost's NativeView's | |
50 // parent NSView. It is possible for the RenderWidgetHost's NativeView's size to | |
51 // become out of sync with its parent NSView. The override of | |
52 // -resizeSubviewsWithOldSize: ensures that the sizes will eventually become | |
53 // consistent. | |
54 // http://crbug.com/264207) | |
Avi (use Gerrit)
2014/06/06 23:09:45
unbalanced parenthesis.
erikchen
2014/06/07 00:34:04
removed.
| |
55 @interface ResizingView : NSView | |
56 @end | |
57 | |
58 @implementation ResizingView | |
59 - (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize { | |
60 for (NSView* subview in self.subviews) | |
61 [subview setFrame:self.bounds]; | |
62 } | |
63 @end | |
64 | |
65 // Helper Class (WebContentsPaintObserver) ------------------------------------- | |
66 | |
67 namespace content { | |
Avi (use Gerrit)
2014/06/06 23:09:45
put this in an anon namespace
erikchen
2014/06/07 00:34:04
that's doesn't work, as this class has a forward d
| |
68 class WebContentsPaintObserver : public WebContentsObserver { | |
69 public: | |
70 WebContentsPaintObserver(WebContents* web_contents, | |
71 OverscrollAnimatorSliderView* slider_view) | |
72 : WebContentsObserver(web_contents), slider_view_(slider_view) {} | |
73 | |
74 virtual void DidFirstVisuallyNonEmptyPaint() OVERRIDE { | |
75 [slider_view_ webContentsFinishedNonEmptyPaint]; | |
76 } | |
77 | |
78 private: | |
79 OverscrollAnimatorSliderView* slider_view_; // Weak reference. | |
80 }; | |
81 } | |
82 | |
83 // OverscrollAnimatorSliderView Implementation --------------------------------- | |
84 | |
85 @implementation OverscrollAnimatorSliderView | |
86 | |
87 - (instancetype)initWithFrame:(NSRect)frame { | |
88 self = [super initWithFrame:frame]; | |
89 if (self) { | |
90 bottomView_.reset([[NSImageView alloc] initWithFrame:self.bounds]); | |
91 bottomView_.get().imageScaling = NSImageScaleNone; | |
92 bottomView_.get().autoresizingMask = | |
93 NSViewWidthSizable | NSViewHeightSizable; | |
94 bottomView_.get().imageAlignment = NSImageAlignTop; | |
95 [self addSubview:bottomView_]; | |
96 middleView_.reset([[ResizingView alloc] initWithFrame:self.bounds]); | |
97 middleView_.get().autoresizingMask = | |
98 NSViewWidthSizable | NSViewHeightSizable; | |
99 [self addSubview:middleView_]; | |
100 topView_.reset([[NSImageView alloc] initWithFrame:self.bounds]); | |
101 topView_.get().autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; | |
102 topView_.get().imageScaling = NSImageScaleNone; | |
103 topView_.get().imageAlignment = NSImageAlignTop; | |
104 [self addSubview:topView_]; | |
105 | |
106 [self reset]; | |
107 } | |
108 return self; | |
109 } | |
110 | |
111 - (void)webContentsFinishedNonEmptyPaint { | |
112 observer_.reset(); | |
113 [self reset]; | |
114 } | |
115 | |
116 - (void)reset { | |
117 DCHECK(!animating_); | |
118 inOverscroll_ = NO; | |
119 progress_ = kMinProgress; | |
120 | |
121 [CATransaction begin]; | |
122 [CATransaction setDisableActions:YES]; | |
123 bottomView_.get().hidden = YES; | |
124 middleView_.get().hidden = NO; | |
125 topView_.get().hidden = YES; | |
126 | |
127 [bottomView_ setFrameOrigin:NSMakePoint(0, 0)]; | |
128 [middleView_ setFrameOrigin:NSMakePoint(0, 0)]; | |
129 [topView_ setFrameOrigin:NSMakePoint(0, 0)]; | |
130 [CATransaction commit]; | |
131 } | |
132 | |
133 - (NSPoint)frameOriginWithProgress:(CGFloat)progress { | |
134 if (slidingLeft_) | |
135 return NSMakePoint(progress / kMaxProgress * self.bounds.size.width, 0); | |
136 return NSMakePoint((1 - progress / kMaxProgress) * self.bounds.size.width, 0); | |
137 } | |
138 | |
139 - (NSView*)movingView { | |
140 if (slidingLeft_) | |
141 return middleView_; | |
142 return topView_; | |
143 } | |
144 | |
145 - (CGFloat)animationDurationForProgress:(CGFloat)progress { | |
146 CGFloat progressPercentage = | |
147 fabs(progress_ - progress) / (kMaxProgress - kMinProgress); | |
148 return progressPercentage * kMaxAnimationDuration; | |
149 } | |
150 | |
151 - (void)scrollWheel:(NSEvent*)event { | |
152 NSView* latestRenderWidgetHostView = [[middleView_ subviews] lastObject]; | |
153 [latestRenderWidgetHostView scrollWheel:event]; | |
154 } | |
155 | |
156 // WebContentsOverscrollAnimator Implementation -------------------------------- | |
157 | |
158 - (void)beginOverscrollLeft:(BOOL)left { | |
159 if (animating_ || inOverscroll_) | |
160 return; | |
161 | |
162 inOverscroll_ = YES; | |
163 slidingLeft_ = left; | |
164 if (left) { | |
165 // The middleView_ will slide to the right, revealing bottomView_. | |
166 bottomView_.get().hidden = NO; | |
167 } else { | |
168 // The topView_ will slide in from the right, concealing middleView_. | |
169 topView_.get().hidden = NO; | |
170 [topView_ setFrameOrigin:NSMakePoint(self.bounds.size.width, 0)]; | |
171 } | |
172 | |
173 [self updateOverscrollProgress:kMinProgress]; | |
174 } | |
175 | |
176 - (BOOL)needsNavigationSnapshot { | |
177 return YES; | |
178 } | |
179 | |
180 - (void)supplyNavigationSnapshot:(NSImage*)image { | |
181 if (slidingLeft_) | |
182 [bottomView_ setImage:image]; | |
183 else | |
184 [topView_ setImage:image]; | |
185 } | |
186 | |
187 - (void)addRenderWidgetHostNativeView:(NSView*)view { | |
188 [middleView_ addSubview:view]; | |
189 } | |
190 | |
191 - (void)updateOverscrollProgress:(CGFloat)progress { | |
192 if (animating_) | |
193 return; | |
194 DCHECK_LT(progress, kMaxProgress + 0.00001); | |
195 DCHECK_GT(progress, kMinProgress - 0.00001); | |
Avi (use Gerrit)
2014/06/06 23:09:45
Why not DCHECK_LE and DCHECK_GE?
erikchen
2014/06/07 00:34:04
Equality comparisons aren't particularly useful fo
Avi (use Gerrit)
2014/06/07 02:57:32
They are.
Your values, kMax|MinProgress are 0 and
| |
196 progress_ = progress; | |
197 [[self movingView] setFrameOrigin:[self frameOriginWithProgress:progress]]; | |
198 } | |
199 | |
200 - (void)completeOverscroll:(content::WebContentsImpl*)webContents { | |
201 if (animating_ || !inOverscroll_) | |
202 return; | |
203 | |
204 animating_ = YES; | |
205 | |
206 NSView* view = [self movingView]; | |
207 [NSAnimationContext beginGrouping]; | |
208 [NSAnimationContext currentContext].duration = | |
209 [self animationDurationForProgress:kMaxProgress]; | |
210 [[NSAnimationContext currentContext] setCompletionHandler:^{ | |
erikchen
2014/06/06 22:44:56
This API is only available in 10.7+, but the relev
Avi (use Gerrit)
2014/06/06 23:09:45
If you're guaranteed that you never will come thro
erikchen
2014/06/07 00:34:04
Done.
| |
211 animating_ = NO; | |
212 | |
213 // Animation is complete. Now perform page load. | |
214 if (slidingLeft_) | |
215 webContents->GetController().GoBack(); | |
216 else | |
217 webContents->GetController().GoForward(); | |
218 | |
219 // Reset the position of the middleView_, but wait for the page to paint | |
220 // before showing it. | |
221 middleView_.get().hidden = YES; | |
222 [middleView_ setFrameOrigin:NSMakePoint(0, 0)]; | |
223 observer_.reset(new content::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]; | |
erikchen
2014/06/06 22:44:56
Everything but the block can be shared with -compl
Avi (use Gerrit)
2014/06/06 23:09:45
Yes, there's duplication, but I don't see a ton of
| |
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 | |
OLD | NEW |