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

Unified 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: Rebase against top of tree. 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.mm
diff --git a/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.mm b/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.mm
new file mode 100644
index 0000000000000000000000000000000000000000..29c84c1cdf57a1e629dd403a9daad0e47cf8ed12
--- /dev/null
+++ b/content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.mm
@@ -0,0 +1,259 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import <QuartzCore/QuartzCore.h>
+
+#include "content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.h"
+
+#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/public/browser/web_contents_observer.h"
+
+namespace {
+// The minimum possible progress of an overscroll animation.
+CGFloat kMinProgress = 0;
+// The maximum possible progress of an overscroll animation.
+CGFloat kMaxProgress = 2.0;
+// The maximum duration of the completion or cancellation animations. The
+// effective maximum is half of this value, since the longest animation is from
+// progress = 1.0 to progress = 2.0;
+CGFloat kMaxAnimationDuration = 0.2;
+} // namespace
+
+// OverscrollAnimatorSliderView Private Category -------------------------------
+
+@interface OverscrollAnimatorSliderView ()
+// Callback from WebContentsPaintObserver.
+- (void)webContentsFinishedNonEmptyPaint;
+
+// Resets overscroll animation state.
+- (void)reset;
+
+// Given a |progress| from 0 to 2, the expected frame origin of the -movingView.
+- (NSPoint)frameOriginWithProgress:(CGFloat)progress;
+
+// The NSView that is moving during the overscroll animation.
+- (NSView*)movingView;
+
+// The expected duration of an animation from progress_ to |progress|
+- (CGFloat)animationDurationForProgress:(CGFloat)progress;
+
+// NSView override. During an overscroll animation, the cursor may no longer
+// rest on the RenderWidgetHost's NativeView, which prevents wheel events from
+// reaching the NativeView. The overscroll animation is driven by wheel events
+// so they must be explicitly forwarded to the NativeView.
+- (void)scrollWheel:(NSEvent*)event;
+@end
+
+// Helper Class (ResizingView) -------------------------------------------------
+
+// This NSView subclass is intended to be the RenderWidgetHost's NativeView's
+// parent NSView. It is possible for the RenderWidgetHost's NativeView's size to
+// become out of sync with its parent NSView. The override of
+// -resizeSubviewsWithOldSize: ensures that the sizes will eventually become
+// consistent.
+// http://crbug.com/264207
+@interface ResizingView : NSView
+@end
+
+@implementation ResizingView
+- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
+ for (NSView* subview in self.subviews)
+ [subview setFrame:self.bounds];
+}
+@end
+
+// Helper Class (WebContentsPaintObserver) -------------------------------------
+
+namespace overscroll_animator {
+class WebContentsPaintObserver : public content::WebContentsObserver {
+ public:
+ WebContentsPaintObserver(content::WebContents* web_contents,
+ OverscrollAnimatorSliderView* slider_view)
+ : WebContentsObserver(web_contents), slider_view_(slider_view) {}
+
+ virtual void DidFirstVisuallyNonEmptyPaint() OVERRIDE {
+ [slider_view_ webContentsFinishedNonEmptyPaint];
+ }
+
+ private:
+ OverscrollAnimatorSliderView* slider_view_; // Weak reference.
+};
+} // namespace overscroll_animator
+
+// OverscrollAnimatorSliderView Implementation ---------------------------------
+
+@implementation OverscrollAnimatorSliderView
+
+- (instancetype)initWithFrame:(NSRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ bottomView_.reset([[NSImageView alloc] initWithFrame:self.bounds]);
+ bottomView_.get().imageScaling = NSImageScaleNone;
+ bottomView_.get().autoresizingMask =
+ NSViewWidthSizable | NSViewHeightSizable;
+ bottomView_.get().imageAlignment = NSImageAlignTop;
+ [self addSubview:bottomView_];
+ middleView_.reset([[ResizingView alloc] initWithFrame:self.bounds]);
+ middleView_.get().autoresizingMask =
+ NSViewWidthSizable | NSViewHeightSizable;
+ [self addSubview:middleView_];
+ topView_.reset([[NSImageView alloc] initWithFrame:self.bounds]);
+ topView_.get().autoresizingMask = NSViewWidthSizable | NSViewHeightSizable;
+ topView_.get().imageScaling = NSImageScaleNone;
+ topView_.get().imageAlignment = NSImageAlignTop;
+ [self addSubview:topView_];
+
+ [self reset];
+ }
+ return self;
+}
+
+- (void)webContentsFinishedNonEmptyPaint {
+ observer_.reset();
+ [self reset];
+}
+
+- (void)reset {
+ DCHECK(!animating_);
+ inOverscroll_ = NO;
+ progress_ = kMinProgress;
+
+ [CATransaction begin];
+ [CATransaction setDisableActions:YES];
+ bottomView_.get().hidden = YES;
+ middleView_.get().hidden = NO;
+ topView_.get().hidden = YES;
+
+ [bottomView_ setFrameOrigin:NSMakePoint(0, 0)];
+ [middleView_ setFrameOrigin:NSMakePoint(0, 0)];
+ [topView_ setFrameOrigin:NSMakePoint(0, 0)];
+ [CATransaction commit];
+}
+
+- (NSPoint)frameOriginWithProgress:(CGFloat)progress {
+ if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS)
+ return NSMakePoint(progress / kMaxProgress * self.bounds.size.width, 0);
+ return NSMakePoint((1 - progress / kMaxProgress) * self.bounds.size.width, 0);
+}
+
+- (NSView*)movingView {
+ if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS)
+ return middleView_;
+ return topView_;
+}
+
+- (CGFloat)animationDurationForProgress:(CGFloat)progress {
+ CGFloat progressPercentage =
+ fabs(progress_ - progress) / (kMaxProgress - kMinProgress);
+ return progressPercentage * kMaxAnimationDuration;
+}
+
+- (void)scrollWheel:(NSEvent*)event {
+ NSView* latestRenderWidgetHostView = [[middleView_ subviews] lastObject];
+ [latestRenderWidgetHostView scrollWheel:event];
+}
+
+// WebContentsOverscrollAnimator Implementation --------------------------------
+
+- (BOOL)needsNavigationSnapshot {
+ return YES;
+}
+
+- (void)beginOverscrollInDirection:
+ (content::OverscrollAnimatorDirection)direction
+ navigationSnapshot:(NSImage*)snapshot {
+ // TODO(erikchen): If snapshot is nil, need a placeholder.
+ if (animating_ || inOverscroll_)
+ return;
+
+ inOverscroll_ = YES;
+ direction_ = direction;
+ if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS) {
+ // The middleView_ will slide to the right, revealing bottomView_.
+ bottomView_.get().hidden = NO;
+ [bottomView_ setImage:snapshot];
+ } else {
+ // The topView_ will slide in from the right, concealing middleView_.
+ topView_.get().hidden = NO;
+ [topView_ setFrameOrigin:NSMakePoint(self.bounds.size.width, 0)];
+ [topView_ setImage:snapshot];
+ }
+
+ [self updateOverscrollProgress:kMinProgress];
+}
+
+- (void)addRenderWidgetHostNativeView:(NSView*)view {
+ [middleView_ addSubview:view];
+}
+
+- (void)updateOverscrollProgress:(CGFloat)progress {
+ if (animating_)
+ return;
+ DCHECK_LE(progress, kMaxProgress);
+ DCHECK_GE(progress, kMinProgress);
+ progress_ = progress;
+ [[self movingView] setFrameOrigin:[self frameOriginWithProgress:progress]];
+}
+
+- (void)completeOverscroll:(content::WebContentsImpl*)webContents {
+ if (animating_ || !inOverscroll_)
+ return;
+
+ animating_ = YES;
+
+ NSView* view = [self movingView];
+ [NSAnimationContext beginGrouping];
+ [NSAnimationContext currentContext].duration =
+ [self animationDurationForProgress:kMaxProgress];
+ [[NSAnimationContext currentContext] setCompletionHandler:^{
+ animating_ = NO;
+
+ // Animation is complete. Now perform page load.
+ if (direction_ == content::OVERSCROLL_ANIMATOR_DIRECTION_BACKWARDS)
+ webContents->GetController().GoBack();
+ else
+ webContents->GetController().GoForward();
+
+ // Reset the position of the middleView_, but wait for the page to paint
+ // before showing it.
+ middleView_.get().hidden = YES;
+ [middleView_ setFrameOrigin:NSMakePoint(0, 0)];
+ observer_.reset(
+ new overscroll_animator::WebContentsPaintObserver(webContents, self));
+ }];
+
+ // Animate the moving view to its final position.
+ [[view animator] setFrameOrigin:[self frameOriginWithProgress:kMaxProgress]];
+
+ [NSAnimationContext endGrouping];
+}
+
+- (void)cancelOverscroll {
+ if (animating_)
+ return;
+
+ if (!inOverscroll_) {
+ [self reset];
+ return;
+ }
+
+ animating_ = YES;
+
+ NSView* view = [self movingView];
+ [NSAnimationContext beginGrouping];
+ [NSAnimationContext currentContext].duration =
+ [self animationDurationForProgress:kMinProgress];
+ [[NSAnimationContext currentContext] setCompletionHandler:^{
+ // Animation is complete. Reset the state.
+ animating_ = NO;
+ [self reset];
+ }];
+
+ // Animate the moving view to its initial position.
+ [[view animator] setFrameOrigin:[self frameOriginWithProgress:kMinProgress]];
+
+ [NSAnimationContext endGrouping];
+}
+
+@end
« no previous file with comments | « content/browser/web_contents/web_contents_view_overscroll_animator_slider_mac.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698