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

Unified Diff: ios/chrome/browser/ui/bookmarks/bookmark_panel_view.mm

Issue 2586993002: Upstream Chrome on iOS source code [3/11]. (Closed)
Patch Set: Created 4 years 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
Index: ios/chrome/browser/ui/bookmarks/bookmark_panel_view.mm
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_panel_view.mm b/ios/chrome/browser/ui/bookmarks/bookmark_panel_view.mm
new file mode 100644
index 0000000000000000000000000000000000000000..35b355c09cdc516bff31b34295a66dbaf82b02b7
--- /dev/null
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_panel_view.mm
@@ -0,0 +1,398 @@
+// 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 "ios/chrome/browser/ui/bookmarks/bookmark_panel_view.h"
+
+#include "base/logging.h"
+#include "base/mac/objc_property_releaser.h"
+#include "base/mac/scoped_nsobject.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
+#import "ios/chrome/browser/ui/rtl_geometry.h"
+
+// The position of the MenuViewWrapper doesn't change, but its subview menuView
+// can slide horizontally. This UIView subclass decides whether to swallow
+// touches based on the transform of its subview, since its subview might lie
+// outsides the bounds of itself.
+@interface MenuViewWrapper : UIView {
+ base::mac::ObjCPropertyReleaser _propertyReleaser_MenuViewWrapper;
+}
+@property(nonatomic, retain) UIView* menuView;
+@end
+
+@implementation MenuViewWrapper
+@synthesize menuView = _menuView;
+
+- (id)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ _propertyReleaser_MenuViewWrapper.Init(self, [MenuViewWrapper class]);
+ }
+ return self;
+}
+
+- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
+ return CGRectContainsPoint(self.menuView.frame, point);
+}
+
+@end
+
+@interface BookmarkPanelView ()<UIGestureRecognizerDelegate> {
+ base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkPanelView;
+}
+// The content view always has the same size as this view.
+// Redefined to be read-write.
+@property(nonatomic, retain) UIView* contentView;
+// When the menu is showing, the cover partially obscures the content view.
+@property(nonatomic, retain) UIView* contentViewCover;
+// The menu view's frame never changes. Sliding it left and right is performed
+// by changing its transform property.
+// Redefined to be read-write.
+@property(nonatomic, retain) UIView* menuView;
+// The menu view's layout is adjusted by changing its transform property.
+// Changing the transform property results in a layoutSubviews call to the
+// parentView. To prevent confusion to the origin of the layoutSubview call, the
+// menu is placed inside a wrapper. The wrapper is always placed offscreen to
+// the left. It requires a UIView subclass to correctly decide whether touches
+// should make it to the menuView.
+@property(nonatomic, retain) MenuViewWrapper* menuViewWrapper;
+@property(nonatomic, assign) CGFloat menuWidth;
+@property(nonatomic, retain) UIPanGestureRecognizer* panRecognizer;
+
+// This property corresponds to whether startPoint is valid. It also reflects
+// whether this class is responding to a user-driven animation.
+@property(nonatomic, assign) BOOL hasStartPoint;
+@property(nonatomic, assign) CGPoint startPoint;
+// The most recent point of the user's pan gesture.
+@property(nonatomic, assign) CGPoint lastPoint;
+
+// When an animation that tracks the user's gesture is in progress, this
+// property reflects the state of the menu at the beginning of the animation.
+// Redefined to be read-write.
+@property(nonatomic, assign) BOOL showingMenu;
+
+// The user panned the view.
+// Invoked frequently during a pan gesture.
+- (void)panRecognized:(id)target;
+// Returns true if the last point was updated.
+// Updates the last point of the user's gesture.
+// If hasStartPoint is NO, sets the startPoint and sets hasStartPoint to YES.
+- (BOOL)updateLastPoint;
+// The width of the menu. This does not change when the screen orientation
+// changes.
+- (CGFloat)menuWidth;
+// Resets all state and UI pertaining to the user driven animation.
+- (void)resetUserDrivenAnimation;
+// Callback for when the user tapped the content view cover.
+- (void)contentViewCoverTapped;
+// Updates the layout of subviews. Similar to layoutSubviews, but intended to
+// also be called from -init.
+- (void)updateLayout;
+// Given a touch position, calculates the visible width of menu respecting menu
+// state (open/closed) and RTL.
+- (CGFloat)peekWidthWithTouchPosition:(CGFloat)position;
+// Updates menu visibility given the visible width of menu, respecting RTL.
+- (void)updateMenuPositionWithPeekWidth:(CGFloat)peekWidth;
+@end
+
+@implementation BookmarkPanelView
+@synthesize contentView = _contentView;
+@synthesize contentViewCover = _contentViewCover;
+@synthesize delegate = _delegate;
+@synthesize hasStartPoint = _hasStartPoint;
+@synthesize lastPoint = _lastPoint;
+@synthesize menuView = _menuView;
+@synthesize menuViewWrapper = _menuViewWrapper;
+@synthesize menuWidth = _menuWidth;
+@synthesize panRecognizer = _panRecognizer;
+@synthesize showingMenu = _showingMenu;
+@synthesize startPoint = _startPoint;
+
+#pragma mark Initialization
+
+- (id)init {
+ NOTREACHED();
+ return nil;
+}
+
+- (id)initWithFrame:(CGRect)frame {
+ NOTREACHED();
+ return nil;
+}
+
+- (id)initWithFrame:(CGRect)frame menuViewWidth:(CGFloat)width {
+ self = [super initWithFrame:frame];
+ if (self) {
+ _propertyReleaser_BookmarkPanelView.Init(self, [BookmarkPanelView class]);
+
+ DCHECK(width);
+ _menuWidth = width;
+
+ self.contentView = base::scoped_nsobject<UIView>([[UIView alloc] init]);
+ self.contentView.autoresizingMask =
+ UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
+ [self addSubview:self.contentView];
+
+ self.contentViewCover =
+ base::scoped_nsobject<UIView>([[UIView alloc] init]);
+ [self addSubview:self.contentViewCover];
+ self.contentViewCover.backgroundColor =
+ [UIColor colorWithWhite:0 alpha:0.8];
+ self.contentViewCover.alpha = 0;
+
+ base::scoped_nsobject<UITapGestureRecognizer> tapRecognizer(
+ [[UITapGestureRecognizer alloc]
+ initWithTarget:self
+ action:@selector(contentViewCoverTapped)]);
+ [self.contentViewCover addGestureRecognizer:tapRecognizer];
+ self.contentViewCover.autoresizingMask =
+ UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
+
+ self.menuViewWrapper =
+ base::scoped_nsobject<MenuViewWrapper>([[MenuViewWrapper alloc] init]);
+ self.menuViewWrapper.backgroundColor = [UIColor clearColor];
+ [self addSubview:self.menuViewWrapper];
+
+ self.menuView = base::scoped_nsobject<UIView>([[UIView alloc] init]);
+ [self.menuViewWrapper addSubview:self.menuView];
+ self.menuViewWrapper.menuView = self.menuView;
+
+ self.panRecognizer = base::scoped_nsobject<UIPanGestureRecognizer>(
+ [[UIPanGestureRecognizer alloc]
+ initWithTarget:self
+ action:@selector(panRecognized:)]);
+ [self addGestureRecognizer:self.panRecognizer];
+
+ [self updateLayout];
+ }
+ return self;
+}
+
+#pragma mark Gesture recognizer
+
+- (void)panRecognized:(id)target {
+ switch (self.panRecognizer.state) {
+ case UIGestureRecognizerStatePossible:
+ case UIGestureRecognizerStateBegan:
+ [self updateLastPoint];
+ break;
+
+ case UIGestureRecognizerStateChanged: {
+ BOOL hasPoint = [self updateLastPoint];
+
+ if (hasPoint) {
+ CGFloat touchPosition =
+ [self.panRecognizer locationOfTouch:0 inView:self].x;
+ CGFloat peekWidth = [self peekWidthWithTouchPosition:touchPosition];
+ [self updateMenuPositionWithPeekWidth:peekWidth];
+
+ CGFloat visibility = peekWidth / self.menuWidth;
+ self.contentViewCover.alpha = visibility;
+ [self.delegate bookmarkPanelView:self updatedMenuVisibility:visibility];
+ }
+ break;
+ }
+ case UIGestureRecognizerStateEnded:
+ case UIGestureRecognizerStateCancelled:
+ case UIGestureRecognizerStateFailed:
+ [self resetUserDrivenAnimation];
+ break;
+ }
+}
+
+- (BOOL)updateLastPoint {
+ if ([self.panRecognizer numberOfTouches] == 0)
+ return NO;
+
+ self.lastPoint = [self.panRecognizer locationOfTouch:0 inView:self];
+
+ if (!self.hasStartPoint) {
+ self.hasStartPoint = YES;
+ self.startPoint = self.lastPoint;
+ }
+
+ return YES;
+}
+
+#pragma mark Layout
+
+- (void)layoutSubviews {
+ [self resetUserDrivenAnimation];
+ [self updateLayout];
+}
+
+- (void)updateLayout {
+ self.contentView.frame = self.bounds;
+ self.contentViewCover.frame = self.bounds;
+
+ CGFloat menuLeading = self.showingMenu ? 0 : -1 * self.menuWidth;
+ LayoutRect menuWrapperLayout =
+ LayoutRectMake(menuLeading, self.bounds.size.width, 0, self.menuWidth,
+ self.bounds.size.height);
+
+ self.menuViewWrapper.frame = LayoutRectGetRect(menuWrapperLayout);
+ self.menuView.frame = self.menuViewWrapper.bounds;
+}
+
+#pragma mark - UIAccessibilityAction
+
+- (BOOL)accessibilityPerformEscape {
+ if (!self.showingMenu)
+ return NO;
+ [self hideMenuAnimated:YES];
+ return YES;
+}
+
+#pragma mark - Public Methods
+
+- (void)showMenuAnimated:(BOOL)animated {
+ if (self.hasStartPoint)
+ return;
+
+ self.showingMenu = YES;
+ self.menuViewWrapper.accessibilityViewIsModal = YES;
+
+ CGFloat animationDuration = 0;
+
+ if (animated) {
+ CGFloat baseDuration = bookmark_utils_ios::menuAnimationDuration;
+ // Reduce the time of the animation if the menu is close to its destination.
+ CGFloat closeness =
+ fabs(self.menuWidth - self.menuView.transform.tx) / self.menuWidth;
+ animationDuration = baseDuration * closeness;
+ animationDuration = MIN(baseDuration, animationDuration);
+ }
+
+ [self.delegate bookmarkPanelView:self
+ willShowMenu:YES
+ withAnimationDuration:animationDuration];
+
+ [UIView animateWithDuration:animated ? animationDuration : 0
+ delay:0
+ options:UIViewAnimationOptionBeginFromCurrentState
+ animations:^{
+ [self updateMenuPositionWithPeekWidth:self.menuWidth];
+ self.contentViewCover.alpha = 1;
+ }
+ completion:^(BOOL finished) {
+ UIAccessibilityPostNotification(
+ UIAccessibilityScreenChangedNotification, self.menuView);
+ }];
+}
+
+- (void)hideMenuAnimated:(BOOL)animated {
+ if (self.hasStartPoint)
+ return;
+
+ self.showingMenu = NO;
+ self.menuViewWrapper.accessibilityViewIsModal = NO;
+
+ CGFloat animationDuration = 0;
+
+ if (animated) {
+ CGFloat baseDuration = bookmark_utils_ios::menuAnimationDuration;
+ // Reduce the time of the animation if the menu is close to its destination.
+ CGFloat closeness = fabs(self.menuView.transform.tx) / self.menuWidth;
+ animationDuration = baseDuration * closeness;
+ animationDuration = MIN(baseDuration, animationDuration);
+ }
+
+ [self.delegate bookmarkPanelView:self
+ willShowMenu:NO
+ withAnimationDuration:animationDuration];
+
+ [UIView animateWithDuration:animated ? animationDuration : 0
+ delay:0
+ options:UIViewAnimationOptionBeginFromCurrentState
+ animations:^{
+ [self updateMenuPositionWithPeekWidth:0];
+ self.contentViewCover.alpha = 0;
+ }
+ completion:^(BOOL finished) {
+ UIAccessibilityPostNotification(
+ UIAccessibilityScreenChangedNotification, self.contentView);
+ }];
+}
+
+- (BOOL)userDrivenAnimationInProgress {
+ return self.hasStartPoint;
+}
+
+- (void)enableSideSwiping:(BOOL)enable {
+ self.panRecognizer.enabled = enable;
+}
+
+#pragma mark Private methods
+
+- (void)resetUserDrivenAnimation {
+ // If no user-driven animation is in progress, there's nothing to do.
+ if (!self.hasStartPoint)
+ return;
+
+ CGFloat width = self.menuWidth;
+ CGFloat peekWidth = [self peekWidthWithTouchPosition:self.lastPoint.x];
+
+ self.hasStartPoint = NO;
+
+ // If the menu is more than half showing when the user lets go, open it all
+ // the way. Otherwise, close it all the way.
+ if (self.showingMenu) {
+ if (peekWidth < width / 2) {
+ [self hideMenuAnimated:YES];
+ } else {
+ [self showMenuAnimated:YES];
+ }
+ } else {
+ if (peekWidth > width / 2) {
+ [self showMenuAnimated:YES];
+ } else {
+ [self hideMenuAnimated:YES];
+ }
+ }
+}
+
+- (void)contentViewCoverTapped {
+ [self hideMenuAnimated:YES];
+}
+
+- (CGFloat)peekWidthWithTouchPosition:(CGFloat)position {
+ if (!self.hasStartPoint)
+ return 0;
+
+ CGFloat delta = position - self.startPoint.x;
+ CGFloat peekWidth = 0;
+ CGFloat menuWidth = self.menuWidth;
+ if (self.showingMenu) {
+ // The menu is already open.
+ if (UseRTLLayout()) {
+ delta = MAX(0, delta);
+ peekWidth = menuWidth - delta;
+ } else {
+ delta = MIN(0, delta);
+ peekWidth = menuWidth + delta;
+ }
+ } else {
+ // The menu is not open yet.
+ if (UseRTLLayout()) {
+ delta = MIN(0, delta);
+ peekWidth = -1 * delta;
+ } else {
+ delta = MAX(0, delta);
+ peekWidth = delta;
+ }
+ }
+
+ peekWidth = MIN(peekWidth, menuWidth);
+ peekWidth = MAX(0, peekWidth);
+ return peekWidth;
+}
+
+- (void)updateMenuPositionWithPeekWidth:(CGFloat)peekWidth {
+ DCHECK(peekWidth >= 0);
+ DCHECK(peekWidth <= self.menuWidth);
+
+ self.menuView.transform = CGAffineTransformMakeTranslation(
+ UseRTLLayout() ? -peekWidth : peekWidth, 0);
+}
+
+@end
« no previous file with comments | « ios/chrome/browser/ui/bookmarks/bookmark_panel_view.h ('k') | ios/chrome/browser/ui/bookmarks/bookmark_position_cache.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698