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

Side by Side Diff: ios/chrome/browser/ui/tab_switcher/tab_switcher_header_view.mm

Issue 2588733002: Upstream Chrome on iOS source code [9/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 2015 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/tab_switcher/tab_switcher_header_view.h"
6
7 #import "base/ios/weak_nsobject.h"
8 #include "base/logging.h"
9 #include "base/mac/scoped_nsobject.h"
10 #include "base/metrics/user_metrics_action.h"
11 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
12 #include "ios/chrome/browser/ui/rtl_geometry.h"
13 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_header_cell.h"
14 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_session_cell_data.h"
15 #import "ios/chrome/browser/ui/uikit_ui_util.h"
16 #include "ios/chrome/grit/ios_strings.h"
17 #import "ios/third_party/material_components_ios/src/components/Palettes/src/Mat erialPalettes.h"
18 #include "ui/base/l10n/l10n_util.h"
19
20 namespace {
21 const CGFloat kCollectionViewTopMargin = 39.0;
22 const CGFloat kCollectionViewHeight = 56.0;
23 const CGFloat kDismissButtonWidth = 46.0;
24 const CGFloat kDismissButtonHeight = 39.0;
25 const CGFloat kCollectionViewCellWidth = 238;
26 const CGFloat kActiveSpaceIndicatorHeight = 2;
27 enum PanelSelectionChangeDirection { RIGHT, LEFT };
28 }
29
30 @protocol AccessiblePanelSelectorDelegate
31 // Scrolls to the panel in the direction |direction|, if possible.
32 - (void)moveToPanelInDirection:(PanelSelectionChangeDirection)direction;
33 @end
34
35 // An invisible view that offers VoiceOver control of the panel selection
36 // UICollectionView.
37 // Notes:
38 // Directly subclassing UICollectionView resulted in a tons of unwanted
39 // interactions with the cells.
40 // Subclassing UIAccessibilityElement instead of UIView is not possible if
41 // we want the accessibilityFrame to resize itself using autoresizing masks.
42 @interface AccessiblePanelSelectorView : UIView {
43 // The delegate which receives actions.
44 base::WeakNSProtocol<id<AccessiblePanelSelectorDelegate>> _delegate;
45 }
46 - (void)setDelegate:(id<AccessiblePanelSelectorDelegate>)delegate;
47 @end
48
49 @implementation AccessiblePanelSelectorView
50
51 - (void)setDelegate:(id<AccessiblePanelSelectorDelegate>)delegate {
52 _delegate.reset(delegate);
53 }
54
55 - (UIAccessibilityTraits)accessibilityTraits {
56 return [super accessibilityTraits] | UIAccessibilityTraitAdjustable |
57 UIAccessibilityTraitCausesPageTurn;
58 }
59
60 - (BOOL)isAccessibilityElement {
61 return YES;
62 }
63
64 - (void)accessibilityIncrement {
65 [_delegate moveToPanelInDirection:RIGHT];
66 }
67
68 - (void)accessibilityDecrement {
69 [_delegate moveToPanelInDirection:LEFT];
70 }
71
72 @end
73
74 @interface TabSwitcherHeaderView ()<UICollectionViewDataSource,
75 UICollectionViewDelegate,
76 AccessiblePanelSelectorDelegate> {
77 base::scoped_nsobject<UICollectionViewFlowLayout> _flowLayout;
78 base::scoped_nsobject<UICollectionView> _collectionView;
79 base::scoped_nsobject<AccessiblePanelSelectorView> _accessibilityView;
80 base::scoped_nsobject<UIButton> _dismissButton;
81 base::scoped_nsobject<UIView> _activeSpaceIndicatorView;
82
83 BOOL _performingUpdate;
84 }
85
86 // Loads and initializes subviews.
87 - (void)loadSubviews;
88 // Performs layout of the collection view.
89 - (void)layoutCollectionView;
90
91 @end
92
93 @implementation TabSwitcherHeaderView
94
95 @synthesize delegate = _delegate;
96 @synthesize dataSource = _dataSource;
97
98 - (instancetype)initWithFrame:(CGRect)frame {
99 self = [super initWithFrame:frame];
100 if (self) {
101 self.backgroundColor = [[MDCPalette greyPalette] tint900];
102 [self loadSubviews];
103 }
104 return self;
105 }
106
107 - (void)layoutSubviews {
108 [super layoutSubviews];
109 [self layoutCollectionView];
110 }
111
112 - (void)selectItemAtIndex:(NSInteger)index {
113 NSInteger selectedIndex = [self selectedIndex];
114 if (selectedIndex != index) {
115 [_collectionView
116 selectItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]
117 animated:NO
118 scrollPosition:UICollectionViewScrollPositionNone];
119 [self updateSelectionAtIndex:index animated:YES];
120 }
121 }
122
123 - (void)reloadData {
124 [_collectionView reloadData];
125 [_collectionView layoutIfNeeded];
126 }
127
128 - (void)performUpdate:(void (^)(TabSwitcherHeaderView* headerView))updateBlock {
129 [self performUpdate:updateBlock completion:nil];
130 }
131
132 - (void)performUpdate:(void (^)(TabSwitcherHeaderView* headerView))updateBlock
133 completion:(ProceduralBlock)completion {
134 DCHECK(updateBlock);
135
136 __block TabSwitcherHeaderView* weakSelf = self;
137 [_collectionView performBatchUpdates:^{
138 if (!weakSelf)
139 return;
140 weakSelf->_performingUpdate = YES;
141 updateBlock(weakSelf);
142 weakSelf->_performingUpdate = NO;
143 }
144 completion:^(BOOL finished) {
145 // Reestablish selection after the update.
146 const NSInteger selectedPanelIndex =
147 [[weakSelf delegate] tabSwitcherHeaderViewSelectedPanelIndex];
148 if (selectedPanelIndex != NSNotFound)
149 [weakSelf selectItemAtIndex:selectedPanelIndex];
150 if (completion)
151 completion();
152 }];
153 }
154
155 - (void)insertSessionsAtIndexes:(NSArray*)indexes {
156 DCHECK(_performingUpdate);
157 [_collectionView
158 insertItemsAtIndexPaths:[self indexPathArrayWithIndexes:indexes]];
159 }
160
161 - (void)removeSessionsAtIndexes:(NSArray*)indexes {
162 DCHECK(_performingUpdate);
163 [_collectionView
164 deleteItemsAtIndexPaths:[self indexPathArrayWithIndexes:indexes]];
165 }
166
167 - (UIView*)dismissButton {
168 return _dismissButton.get();
169 }
170
171 #pragma mark - Private
172
173 - (NSInteger)selectedIndex {
174 NSInteger selectedIndex = NSNotFound;
175 NSArray* selectedIndexPaths = [_collectionView indexPathsForSelectedItems];
176 if (selectedIndexPaths.count) {
177 NSIndexPath* selectedIndexPath = selectedIndexPaths[0];
178 selectedIndex = selectedIndexPath.item;
179 }
180 return selectedIndex;
181 }
182
183 - (NSInteger)itemCount {
184 return [_collectionView numberOfItemsInSection:0];
185 }
186
187 // The UICollectionViewFlowLayout enumerate indexes from right to left when the
188 // UI is configured in RTL mode. This method always returns the index from value
189 // for a left to right enumeration order.
190 - (NSInteger)leftToRightIndexForFlowLayoutIndex:(NSInteger)index {
191 return UseRTLLayout() ? ([self itemCount] - 1) - index : index;
192 }
193
194 - (void)updateSelectionAtIndex:(NSInteger)index animated:(BOOL)animated {
195 const CGRect cellRect = CGRectMake(
196 [self leftToRightIndexForFlowLayoutIndex:index] *
197 kCollectionViewCellWidth,
198 0, kCollectionViewCellWidth, [_collectionView bounds].size.height);
199 [_collectionView scrollRectToVisible:cellRect animated:animated];
200 [self layoutActiveSpaceIndicatorAnimated:animated];
201 }
202
203 - (NSArray*)indexPathArrayWithIndexes:(NSArray*)indexes {
204 NSMutableArray* array =
205 [[[NSMutableArray alloc] initWithCapacity:indexes.count] autorelease];
206 for (NSNumber* index in indexes) {
207 [array
208 addObject:[NSIndexPath indexPathForItem:[index intValue] inSection:0]];
209 }
210 return array;
211 }
212
213 - (void)loadSubviews {
214 base::scoped_nsobject<UICollectionViewFlowLayout> flowLayout(
215 [[UICollectionViewFlowLayout alloc] init]);
216 [flowLayout setMinimumLineSpacing:0];
217 [flowLayout setMinimumInteritemSpacing:0];
218 const CGSize cellSize =
219 CGSizeMake(kCollectionViewCellWidth, kCollectionViewHeight);
220 [flowLayout setItemSize:cellSize];
221 [flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
222 _flowLayout = flowLayout;
223
224 _collectionView.reset([[UICollectionView alloc]
225 initWithFrame:[self collectionViewFrame]
226 collectionViewLayout:flowLayout]);
227 [_collectionView setDelegate:self];
228 [_collectionView setDataSource:self];
229 [_collectionView registerClass:[TabSwitcherHeaderCell class]
230 forCellWithReuseIdentifier:[TabSwitcherHeaderCell identifier]];
231 [_collectionView setShowsVerticalScrollIndicator:NO];
232 [_collectionView setShowsHorizontalScrollIndicator:NO];
233 [_collectionView setBackgroundColor:[[MDCPalette greyPalette] tint900]];
234 [_collectionView setAllowsMultipleSelection:NO];
235 [_collectionView setAllowsSelection:YES];
236 [_collectionView setIsAccessibilityElement:NO];
237 [_collectionView setAccessibilityElementsHidden:YES];
238 [self addSubview:_collectionView];
239
240 _accessibilityView.reset([[AccessiblePanelSelectorView alloc]
241 initWithFrame:[self collectionViewFrame]]);
242 [_accessibilityView
243 setAutoresizingMask:UIViewAutoresizingFlexibleBottomMargin |
244 UIViewAutoresizingFlexibleWidth];
245 [_accessibilityView setDelegate:self];
246 [_accessibilityView setUserInteractionEnabled:NO];
247 [self addSubview:_accessibilityView];
248
249 _dismissButton.reset([[UIButton alloc] initWithFrame:CGRectZero]);
250 UIImage* dismissImage =
251 [UIImage imageNamed:@"tabswitcher_tab_switcher_button"];
252 dismissImage =
253 [dismissImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
254 [_dismissButton setContentMode:UIViewContentModeCenter];
255 [_dismissButton setBackgroundColor:[UIColor clearColor]];
256 [_dismissButton setTintColor:[UIColor whiteColor]];
257 [_dismissButton setImage:dismissImage forState:UIControlStateNormal];
258 [_dismissButton
259 setAccessibilityLabel:l10n_util::GetNSString(
260 IDS_IOS_TAB_STRIP_LEAVE_TAB_SWITCHER)];
261
262 [_dismissButton addTarget:self
263 action:@selector(dismissButtonTouchUpInside:)
264 forControlEvents:UIControlEventTouchUpInside];
265 [_dismissButton setTranslatesAutoresizingMaskIntoConstraints:NO];
266 [self addSubview:_dismissButton];
267
268 NSArray* constraints = @[
269 @"V:|-0-[dismissButton(==buttonHeight)]",
270 @"H:[dismissButton(==buttonWidth)]-0-|",
271 ];
272 NSDictionary* viewsDictionary = @{
273 @"dismissButton" : _dismissButton.get(),
274 };
275 NSDictionary* metrics = @{
276 @"buttonHeight" : @(kDismissButtonHeight),
277 @"buttonWidth" : @(kDismissButtonWidth),
278 };
279 ApplyVisualConstraintsWithMetricsAndOptions(
280 constraints, viewsDictionary, metrics, LayoutOptionForRTLSupport(), self);
281
282 base::scoped_nsobject<UIView> activeSpaceIndicatorView(
283 [[UIView alloc] initWithFrame:CGRectZero]);
284 [activeSpaceIndicatorView
285 setBackgroundColor:[[MDCPalette cr_bluePalette] tint500]];
286 [activeSpaceIndicatorView
287 setFrame:CGRectMake(
288 0, self.bounds.size.height - kActiveSpaceIndicatorHeight,
289 kCollectionViewCellWidth, kActiveSpaceIndicatorHeight)];
290 [self addSubview:activeSpaceIndicatorView];
291 _activeSpaceIndicatorView = activeSpaceIndicatorView;
292 }
293
294 - (void)layoutCollectionView {
295 NSInteger selectedIndex = [self selectedIndex];
296 [_collectionView setFrame:[self collectionViewFrame]];
297 if (selectedIndex != NSNotFound)
298 [self updateSelectionAtIndex:selectedIndex animated:YES];
299 }
300
301 - (CGRect)collectionViewFrame {
302 return CGRectMake(0, kCollectionViewTopMargin, self.bounds.size.width,
303 kCollectionViewHeight);
304 }
305
306 - (void)layoutActiveSpaceIndicatorAnimated:(BOOL)animated {
307 [self setPanelSelectorAccessibility];
308 NSInteger selectedIndex = [self selectedIndex];
309 if (selectedIndex == NSNotFound)
310 return;
311 CGRect indicatorFrame = [_activeSpaceIndicatorView bounds];
312 indicatorFrame.origin.y =
313 self.bounds.size.height - kActiveSpaceIndicatorHeight;
314 indicatorFrame.origin.x =
315 kCollectionViewCellWidth *
316 [self leftToRightIndexForFlowLayoutIndex:selectedIndex] -
317 [_collectionView contentOffset].x;
318 if (animated)
319 [UIView beginAnimations:nil context:NULL];
320 [_activeSpaceIndicatorView setFrame:indicatorFrame];
321 if (animated)
322 [UIView commitAnimations];
323 }
324
325 - (void)dismissButtonTouchUpInside:(UIButton*)button {
326 [self.delegate tabSwitcherHeaderViewDismiss:self];
327 }
328
329 - (void)setPanelSelectorAccessibility {
330 NSInteger index = [self selectedIndex];
331 if (index != NSNotFound)
332 [_accessibilityView setAccessibilityLabel:[self panelTitleAtIndex:index]];
333 }
334
335 - (NSString*)panelTitleAtIndex:(NSInteger)index {
336 NSIndexPath* indexPath = [NSIndexPath indexPathForItem:index inSection:0];
337 SessionCellData* sessionCellData =
338 [[self dataSource] sessionCellDataAtIndex:indexPath.row];
339 return sessionCellData.title;
340 }
341
342 #pragma mark - AccessiblePanelSelectorDelegate
343
344 - (void)moveToPanelInDirection:(PanelSelectionChangeDirection)direction {
345 NSInteger indexDelta = direction == RIGHT ? 1 : -1;
346 NSInteger newIndex = [self selectedIndex] + indexDelta;
347 newIndex = std::max<NSInteger>(newIndex, 0);
348 newIndex = std::min<NSInteger>(
349 newIndex,
350 [self collectionView:_collectionView numberOfItemsInSection:0] - 1);
351 NSIndexPath* newIndexPath =
352 [NSIndexPath indexPathForItem:newIndex inSection:0];
353 [_collectionView
354 selectItemAtIndexPath:newIndexPath
355 animated:NO
356 scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
357 [self updateSelectionAtIndex:newIndexPath.item animated:NO];
358 [[self delegate]
359 tabSwitcherHeaderViewDidSelectSessionAtIndex:newIndexPath.item];
360 }
361
362 #pragma mark - UICollectionViewDataSource
363
364 - (NSInteger)collectionView:(UICollectionView*)collectionView
365 numberOfItemsInSection:(NSInteger)section {
366 DCHECK([self dataSource]);
367 DCHECK(section == 0);
368 return [[self dataSource] tabSwitcherHeaderViewSessionCount];
369 }
370
371 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
372 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
373 TabSwitcherHeaderCell* headerCell = [collectionView
374 dequeueReusableCellWithReuseIdentifier:[TabSwitcherHeaderCell identifier]
375 forIndexPath:indexPath];
376 SessionCellData* sessionCellData =
377 [[self dataSource] sessionCellDataAtIndex:indexPath.row];
378 [headerCell loadSessionCellData:sessionCellData];
379 return headerCell;
380 }
381
382 #pragma mark - UICollectionViewDelegate
383
384 - (void)collectionView:(UICollectionView*)collectionView
385 didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
386 [self updateSelectionAtIndex:indexPath.item animated:YES];
387 [[self delegate] tabSwitcherHeaderViewDidSelectSessionAtIndex:indexPath.item];
388 }
389
390 #pragma mark - UIScrollViewDelegate
391
392 - (void)scrollViewDidScroll:(UIScrollView*)scrollView {
393 [self layoutActiveSpaceIndicatorAnimated:NO];
394 }
395
396 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/ui/tab_switcher/tab_switcher_header_view.h ('k') | ios/chrome/browser/ui/tab_switcher/tab_switcher_model.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698