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

Side by Side 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 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 "ios/chrome/browser/ui/bookmarks/bookmark_panel_view.h"
6
7 #include "base/logging.h"
8 #include "base/mac/objc_property_releaser.h"
9 #include "base/mac/scoped_nsobject.h"
10 #import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
11 #import "ios/chrome/browser/ui/rtl_geometry.h"
12
13 // The position of the MenuViewWrapper doesn't change, but its subview menuView
14 // can slide horizontally. This UIView subclass decides whether to swallow
15 // touches based on the transform of its subview, since its subview might lie
16 // outsides the bounds of itself.
17 @interface MenuViewWrapper : UIView {
18 base::mac::ObjCPropertyReleaser _propertyReleaser_MenuViewWrapper;
19 }
20 @property(nonatomic, retain) UIView* menuView;
21 @end
22
23 @implementation MenuViewWrapper
24 @synthesize menuView = _menuView;
25
26 - (id)initWithFrame:(CGRect)frame {
27 self = [super initWithFrame:frame];
28 if (self) {
29 _propertyReleaser_MenuViewWrapper.Init(self, [MenuViewWrapper class]);
30 }
31 return self;
32 }
33
34 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
35 return CGRectContainsPoint(self.menuView.frame, point);
36 }
37
38 @end
39
40 @interface BookmarkPanelView ()<UIGestureRecognizerDelegate> {
41 base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkPanelView;
42 }
43 // The content view always has the same size as this view.
44 // Redefined to be read-write.
45 @property(nonatomic, retain) UIView* contentView;
46 // When the menu is showing, the cover partially obscures the content view.
47 @property(nonatomic, retain) UIView* contentViewCover;
48 // The menu view's frame never changes. Sliding it left and right is performed
49 // by changing its transform property.
50 // Redefined to be read-write.
51 @property(nonatomic, retain) UIView* menuView;
52 // The menu view's layout is adjusted by changing its transform property.
53 // Changing the transform property results in a layoutSubviews call to the
54 // parentView. To prevent confusion to the origin of the layoutSubview call, the
55 // menu is placed inside a wrapper. The wrapper is always placed offscreen to
56 // the left. It requires a UIView subclass to correctly decide whether touches
57 // should make it to the menuView.
58 @property(nonatomic, retain) MenuViewWrapper* menuViewWrapper;
59 @property(nonatomic, assign) CGFloat menuWidth;
60 @property(nonatomic, retain) UIPanGestureRecognizer* panRecognizer;
61
62 // This property corresponds to whether startPoint is valid. It also reflects
63 // whether this class is responding to a user-driven animation.
64 @property(nonatomic, assign) BOOL hasStartPoint;
65 @property(nonatomic, assign) CGPoint startPoint;
66 // The most recent point of the user's pan gesture.
67 @property(nonatomic, assign) CGPoint lastPoint;
68
69 // When an animation that tracks the user's gesture is in progress, this
70 // property reflects the state of the menu at the beginning of the animation.
71 // Redefined to be read-write.
72 @property(nonatomic, assign) BOOL showingMenu;
73
74 // The user panned the view.
75 // Invoked frequently during a pan gesture.
76 - (void)panRecognized:(id)target;
77 // Returns true if the last point was updated.
78 // Updates the last point of the user's gesture.
79 // If hasStartPoint is NO, sets the startPoint and sets hasStartPoint to YES.
80 - (BOOL)updateLastPoint;
81 // The width of the menu. This does not change when the screen orientation
82 // changes.
83 - (CGFloat)menuWidth;
84 // Resets all state and UI pertaining to the user driven animation.
85 - (void)resetUserDrivenAnimation;
86 // Callback for when the user tapped the content view cover.
87 - (void)contentViewCoverTapped;
88 // Updates the layout of subviews. Similar to layoutSubviews, but intended to
89 // also be called from -init.
90 - (void)updateLayout;
91 // Given a touch position, calculates the visible width of menu respecting menu
92 // state (open/closed) and RTL.
93 - (CGFloat)peekWidthWithTouchPosition:(CGFloat)position;
94 // Updates menu visibility given the visible width of menu, respecting RTL.
95 - (void)updateMenuPositionWithPeekWidth:(CGFloat)peekWidth;
96 @end
97
98 @implementation BookmarkPanelView
99 @synthesize contentView = _contentView;
100 @synthesize contentViewCover = _contentViewCover;
101 @synthesize delegate = _delegate;
102 @synthesize hasStartPoint = _hasStartPoint;
103 @synthesize lastPoint = _lastPoint;
104 @synthesize menuView = _menuView;
105 @synthesize menuViewWrapper = _menuViewWrapper;
106 @synthesize menuWidth = _menuWidth;
107 @synthesize panRecognizer = _panRecognizer;
108 @synthesize showingMenu = _showingMenu;
109 @synthesize startPoint = _startPoint;
110
111 #pragma mark Initialization
112
113 - (id)init {
114 NOTREACHED();
115 return nil;
116 }
117
118 - (id)initWithFrame:(CGRect)frame {
119 NOTREACHED();
120 return nil;
121 }
122
123 - (id)initWithFrame:(CGRect)frame menuViewWidth:(CGFloat)width {
124 self = [super initWithFrame:frame];
125 if (self) {
126 _propertyReleaser_BookmarkPanelView.Init(self, [BookmarkPanelView class]);
127
128 DCHECK(width);
129 _menuWidth = width;
130
131 self.contentView = base::scoped_nsobject<UIView>([[UIView alloc] init]);
132 self.contentView.autoresizingMask =
133 UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
134 [self addSubview:self.contentView];
135
136 self.contentViewCover =
137 base::scoped_nsobject<UIView>([[UIView alloc] init]);
138 [self addSubview:self.contentViewCover];
139 self.contentViewCover.backgroundColor =
140 [UIColor colorWithWhite:0 alpha:0.8];
141 self.contentViewCover.alpha = 0;
142
143 base::scoped_nsobject<UITapGestureRecognizer> tapRecognizer(
144 [[UITapGestureRecognizer alloc]
145 initWithTarget:self
146 action:@selector(contentViewCoverTapped)]);
147 [self.contentViewCover addGestureRecognizer:tapRecognizer];
148 self.contentViewCover.autoresizingMask =
149 UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
150
151 self.menuViewWrapper =
152 base::scoped_nsobject<MenuViewWrapper>([[MenuViewWrapper alloc] init]);
153 self.menuViewWrapper.backgroundColor = [UIColor clearColor];
154 [self addSubview:self.menuViewWrapper];
155
156 self.menuView = base::scoped_nsobject<UIView>([[UIView alloc] init]);
157 [self.menuViewWrapper addSubview:self.menuView];
158 self.menuViewWrapper.menuView = self.menuView;
159
160 self.panRecognizer = base::scoped_nsobject<UIPanGestureRecognizer>(
161 [[UIPanGestureRecognizer alloc]
162 initWithTarget:self
163 action:@selector(panRecognized:)]);
164 [self addGestureRecognizer:self.panRecognizer];
165
166 [self updateLayout];
167 }
168 return self;
169 }
170
171 #pragma mark Gesture recognizer
172
173 - (void)panRecognized:(id)target {
174 switch (self.panRecognizer.state) {
175 case UIGestureRecognizerStatePossible:
176 case UIGestureRecognizerStateBegan:
177 [self updateLastPoint];
178 break;
179
180 case UIGestureRecognizerStateChanged: {
181 BOOL hasPoint = [self updateLastPoint];
182
183 if (hasPoint) {
184 CGFloat touchPosition =
185 [self.panRecognizer locationOfTouch:0 inView:self].x;
186 CGFloat peekWidth = [self peekWidthWithTouchPosition:touchPosition];
187 [self updateMenuPositionWithPeekWidth:peekWidth];
188
189 CGFloat visibility = peekWidth / self.menuWidth;
190 self.contentViewCover.alpha = visibility;
191 [self.delegate bookmarkPanelView:self updatedMenuVisibility:visibility];
192 }
193 break;
194 }
195 case UIGestureRecognizerStateEnded:
196 case UIGestureRecognizerStateCancelled:
197 case UIGestureRecognizerStateFailed:
198 [self resetUserDrivenAnimation];
199 break;
200 }
201 }
202
203 - (BOOL)updateLastPoint {
204 if ([self.panRecognizer numberOfTouches] == 0)
205 return NO;
206
207 self.lastPoint = [self.panRecognizer locationOfTouch:0 inView:self];
208
209 if (!self.hasStartPoint) {
210 self.hasStartPoint = YES;
211 self.startPoint = self.lastPoint;
212 }
213
214 return YES;
215 }
216
217 #pragma mark Layout
218
219 - (void)layoutSubviews {
220 [self resetUserDrivenAnimation];
221 [self updateLayout];
222 }
223
224 - (void)updateLayout {
225 self.contentView.frame = self.bounds;
226 self.contentViewCover.frame = self.bounds;
227
228 CGFloat menuLeading = self.showingMenu ? 0 : -1 * self.menuWidth;
229 LayoutRect menuWrapperLayout =
230 LayoutRectMake(menuLeading, self.bounds.size.width, 0, self.menuWidth,
231 self.bounds.size.height);
232
233 self.menuViewWrapper.frame = LayoutRectGetRect(menuWrapperLayout);
234 self.menuView.frame = self.menuViewWrapper.bounds;
235 }
236
237 #pragma mark - UIAccessibilityAction
238
239 - (BOOL)accessibilityPerformEscape {
240 if (!self.showingMenu)
241 return NO;
242 [self hideMenuAnimated:YES];
243 return YES;
244 }
245
246 #pragma mark - Public Methods
247
248 - (void)showMenuAnimated:(BOOL)animated {
249 if (self.hasStartPoint)
250 return;
251
252 self.showingMenu = YES;
253 self.menuViewWrapper.accessibilityViewIsModal = YES;
254
255 CGFloat animationDuration = 0;
256
257 if (animated) {
258 CGFloat baseDuration = bookmark_utils_ios::menuAnimationDuration;
259 // Reduce the time of the animation if the menu is close to its destination.
260 CGFloat closeness =
261 fabs(self.menuWidth - self.menuView.transform.tx) / self.menuWidth;
262 animationDuration = baseDuration * closeness;
263 animationDuration = MIN(baseDuration, animationDuration);
264 }
265
266 [self.delegate bookmarkPanelView:self
267 willShowMenu:YES
268 withAnimationDuration:animationDuration];
269
270 [UIView animateWithDuration:animated ? animationDuration : 0
271 delay:0
272 options:UIViewAnimationOptionBeginFromCurrentState
273 animations:^{
274 [self updateMenuPositionWithPeekWidth:self.menuWidth];
275 self.contentViewCover.alpha = 1;
276 }
277 completion:^(BOOL finished) {
278 UIAccessibilityPostNotification(
279 UIAccessibilityScreenChangedNotification, self.menuView);
280 }];
281 }
282
283 - (void)hideMenuAnimated:(BOOL)animated {
284 if (self.hasStartPoint)
285 return;
286
287 self.showingMenu = NO;
288 self.menuViewWrapper.accessibilityViewIsModal = NO;
289
290 CGFloat animationDuration = 0;
291
292 if (animated) {
293 CGFloat baseDuration = bookmark_utils_ios::menuAnimationDuration;
294 // Reduce the time of the animation if the menu is close to its destination.
295 CGFloat closeness = fabs(self.menuView.transform.tx) / self.menuWidth;
296 animationDuration = baseDuration * closeness;
297 animationDuration = MIN(baseDuration, animationDuration);
298 }
299
300 [self.delegate bookmarkPanelView:self
301 willShowMenu:NO
302 withAnimationDuration:animationDuration];
303
304 [UIView animateWithDuration:animated ? animationDuration : 0
305 delay:0
306 options:UIViewAnimationOptionBeginFromCurrentState
307 animations:^{
308 [self updateMenuPositionWithPeekWidth:0];
309 self.contentViewCover.alpha = 0;
310 }
311 completion:^(BOOL finished) {
312 UIAccessibilityPostNotification(
313 UIAccessibilityScreenChangedNotification, self.contentView);
314 }];
315 }
316
317 - (BOOL)userDrivenAnimationInProgress {
318 return self.hasStartPoint;
319 }
320
321 - (void)enableSideSwiping:(BOOL)enable {
322 self.panRecognizer.enabled = enable;
323 }
324
325 #pragma mark Private methods
326
327 - (void)resetUserDrivenAnimation {
328 // If no user-driven animation is in progress, there's nothing to do.
329 if (!self.hasStartPoint)
330 return;
331
332 CGFloat width = self.menuWidth;
333 CGFloat peekWidth = [self peekWidthWithTouchPosition:self.lastPoint.x];
334
335 self.hasStartPoint = NO;
336
337 // If the menu is more than half showing when the user lets go, open it all
338 // the way. Otherwise, close it all the way.
339 if (self.showingMenu) {
340 if (peekWidth < width / 2) {
341 [self hideMenuAnimated:YES];
342 } else {
343 [self showMenuAnimated:YES];
344 }
345 } else {
346 if (peekWidth > width / 2) {
347 [self showMenuAnimated:YES];
348 } else {
349 [self hideMenuAnimated:YES];
350 }
351 }
352 }
353
354 - (void)contentViewCoverTapped {
355 [self hideMenuAnimated:YES];
356 }
357
358 - (CGFloat)peekWidthWithTouchPosition:(CGFloat)position {
359 if (!self.hasStartPoint)
360 return 0;
361
362 CGFloat delta = position - self.startPoint.x;
363 CGFloat peekWidth = 0;
364 CGFloat menuWidth = self.menuWidth;
365 if (self.showingMenu) {
366 // The menu is already open.
367 if (UseRTLLayout()) {
368 delta = MAX(0, delta);
369 peekWidth = menuWidth - delta;
370 } else {
371 delta = MIN(0, delta);
372 peekWidth = menuWidth + delta;
373 }
374 } else {
375 // The menu is not open yet.
376 if (UseRTLLayout()) {
377 delta = MIN(0, delta);
378 peekWidth = -1 * delta;
379 } else {
380 delta = MAX(0, delta);
381 peekWidth = delta;
382 }
383 }
384
385 peekWidth = MIN(peekWidth, menuWidth);
386 peekWidth = MAX(0, peekWidth);
387 return peekWidth;
388 }
389
390 - (void)updateMenuPositionWithPeekWidth:(CGFloat)peekWidth {
391 DCHECK(peekWidth >= 0);
392 DCHECK(peekWidth <= self.menuWidth);
393
394 self.menuView.transform = CGAffineTransformMakeTranslation(
395 UseRTLLayout() ? -peekWidth : peekWidth, 0);
396 }
397
398 @end
OLDNEW
« 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