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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/tab_switcher/tab_switcher_header_view.mm
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_header_view.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_header_view.mm
new file mode 100644
index 0000000000000000000000000000000000000000..bcaec6550fe76a7c3f34b49a1e41bb3215fff010
--- /dev/null
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_header_view.mm
@@ -0,0 +1,396 @@
+// Copyright 2015 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/tab_switcher/tab_switcher_header_view.h"
+
+#import "base/ios/weak_nsobject.h"
+#include "base/logging.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/metrics/user_metrics_action.h"
+#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
+#include "ios/chrome/browser/ui/rtl_geometry.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_header_cell.h"
+#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_session_cell_data.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/third_party/material_components_ios/src/components/Palettes/src/MaterialPalettes.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+const CGFloat kCollectionViewTopMargin = 39.0;
+const CGFloat kCollectionViewHeight = 56.0;
+const CGFloat kDismissButtonWidth = 46.0;
+const CGFloat kDismissButtonHeight = 39.0;
+const CGFloat kCollectionViewCellWidth = 238;
+const CGFloat kActiveSpaceIndicatorHeight = 2;
+enum PanelSelectionChangeDirection { RIGHT, LEFT };
+}
+
+@protocol AccessiblePanelSelectorDelegate
+// Scrolls to the panel in the direction |direction|, if possible.
+- (void)moveToPanelInDirection:(PanelSelectionChangeDirection)direction;
+@end
+
+// An invisible view that offers VoiceOver control of the panel selection
+// UICollectionView.
+// Notes:
+// Directly subclassing UICollectionView resulted in a tons of unwanted
+// interactions with the cells.
+// Subclassing UIAccessibilityElement instead of UIView is not possible if
+// we want the accessibilityFrame to resize itself using autoresizing masks.
+@interface AccessiblePanelSelectorView : UIView {
+ // The delegate which receives actions.
+ base::WeakNSProtocol<id<AccessiblePanelSelectorDelegate>> _delegate;
+}
+- (void)setDelegate:(id<AccessiblePanelSelectorDelegate>)delegate;
+@end
+
+@implementation AccessiblePanelSelectorView
+
+- (void)setDelegate:(id<AccessiblePanelSelectorDelegate>)delegate {
+ _delegate.reset(delegate);
+}
+
+- (UIAccessibilityTraits)accessibilityTraits {
+ return [super accessibilityTraits] | UIAccessibilityTraitAdjustable |
+ UIAccessibilityTraitCausesPageTurn;
+}
+
+- (BOOL)isAccessibilityElement {
+ return YES;
+}
+
+- (void)accessibilityIncrement {
+ [_delegate moveToPanelInDirection:RIGHT];
+}
+
+- (void)accessibilityDecrement {
+ [_delegate moveToPanelInDirection:LEFT];
+}
+
+@end
+
+@interface TabSwitcherHeaderView ()<UICollectionViewDataSource,
+ UICollectionViewDelegate,
+ AccessiblePanelSelectorDelegate> {
+ base::scoped_nsobject<UICollectionViewFlowLayout> _flowLayout;
+ base::scoped_nsobject<UICollectionView> _collectionView;
+ base::scoped_nsobject<AccessiblePanelSelectorView> _accessibilityView;
+ base::scoped_nsobject<UIButton> _dismissButton;
+ base::scoped_nsobject<UIView> _activeSpaceIndicatorView;
+
+ BOOL _performingUpdate;
+}
+
+// Loads and initializes subviews.
+- (void)loadSubviews;
+// Performs layout of the collection view.
+- (void)layoutCollectionView;
+
+@end
+
+@implementation TabSwitcherHeaderView
+
+@synthesize delegate = _delegate;
+@synthesize dataSource = _dataSource;
+
+- (instancetype)initWithFrame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ self.backgroundColor = [[MDCPalette greyPalette] tint900];
+ [self loadSubviews];
+ }
+ return self;
+}
+
+- (void)layoutSubviews {
+ [super layoutSubviews];
+ [self layoutCollectionView];
+}
+
+- (void)selectItemAtIndex:(NSInteger)index {
+ NSInteger selectedIndex = [self selectedIndex];
+ if (selectedIndex != index) {
+ [_collectionView
+ selectItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]
+ animated:NO
+ scrollPosition:UICollectionViewScrollPositionNone];
+ [self updateSelectionAtIndex:index animated:YES];
+ }
+}
+
+- (void)reloadData {
+ [_collectionView reloadData];
+ [_collectionView layoutIfNeeded];
+}
+
+- (void)performUpdate:(void (^)(TabSwitcherHeaderView* headerView))updateBlock {
+ [self performUpdate:updateBlock completion:nil];
+}
+
+- (void)performUpdate:(void (^)(TabSwitcherHeaderView* headerView))updateBlock
+ completion:(ProceduralBlock)completion {
+ DCHECK(updateBlock);
+
+ __block TabSwitcherHeaderView* weakSelf = self;
+ [_collectionView performBatchUpdates:^{
+ if (!weakSelf)
+ return;
+ weakSelf->_performingUpdate = YES;
+ updateBlock(weakSelf);
+ weakSelf->_performingUpdate = NO;
+ }
+ completion:^(BOOL finished) {
+ // Reestablish selection after the update.
+ const NSInteger selectedPanelIndex =
+ [[weakSelf delegate] tabSwitcherHeaderViewSelectedPanelIndex];
+ if (selectedPanelIndex != NSNotFound)
+ [weakSelf selectItemAtIndex:selectedPanelIndex];
+ if (completion)
+ completion();
+ }];
+}
+
+- (void)insertSessionsAtIndexes:(NSArray*)indexes {
+ DCHECK(_performingUpdate);
+ [_collectionView
+ insertItemsAtIndexPaths:[self indexPathArrayWithIndexes:indexes]];
+}
+
+- (void)removeSessionsAtIndexes:(NSArray*)indexes {
+ DCHECK(_performingUpdate);
+ [_collectionView
+ deleteItemsAtIndexPaths:[self indexPathArrayWithIndexes:indexes]];
+}
+
+- (UIView*)dismissButton {
+ return _dismissButton.get();
+}
+
+#pragma mark - Private
+
+- (NSInteger)selectedIndex {
+ NSInteger selectedIndex = NSNotFound;
+ NSArray* selectedIndexPaths = [_collectionView indexPathsForSelectedItems];
+ if (selectedIndexPaths.count) {
+ NSIndexPath* selectedIndexPath = selectedIndexPaths[0];
+ selectedIndex = selectedIndexPath.item;
+ }
+ return selectedIndex;
+}
+
+- (NSInteger)itemCount {
+ return [_collectionView numberOfItemsInSection:0];
+}
+
+// The UICollectionViewFlowLayout enumerate indexes from right to left when the
+// UI is configured in RTL mode. This method always returns the index from value
+// for a left to right enumeration order.
+- (NSInteger)leftToRightIndexForFlowLayoutIndex:(NSInteger)index {
+ return UseRTLLayout() ? ([self itemCount] - 1) - index : index;
+}
+
+- (void)updateSelectionAtIndex:(NSInteger)index animated:(BOOL)animated {
+ const CGRect cellRect = CGRectMake(
+ [self leftToRightIndexForFlowLayoutIndex:index] *
+ kCollectionViewCellWidth,
+ 0, kCollectionViewCellWidth, [_collectionView bounds].size.height);
+ [_collectionView scrollRectToVisible:cellRect animated:animated];
+ [self layoutActiveSpaceIndicatorAnimated:animated];
+}
+
+- (NSArray*)indexPathArrayWithIndexes:(NSArray*)indexes {
+ NSMutableArray* array =
+ [[[NSMutableArray alloc] initWithCapacity:indexes.count] autorelease];
+ for (NSNumber* index in indexes) {
+ [array
+ addObject:[NSIndexPath indexPathForItem:[index intValue] inSection:0]];
+ }
+ return array;
+}
+
+- (void)loadSubviews {
+ base::scoped_nsobject<UICollectionViewFlowLayout> flowLayout(
+ [[UICollectionViewFlowLayout alloc] init]);
+ [flowLayout setMinimumLineSpacing:0];
+ [flowLayout setMinimumInteritemSpacing:0];
+ const CGSize cellSize =
+ CGSizeMake(kCollectionViewCellWidth, kCollectionViewHeight);
+ [flowLayout setItemSize:cellSize];
+ [flowLayout setScrollDirection:UICollectionViewScrollDirectionHorizontal];
+ _flowLayout = flowLayout;
+
+ _collectionView.reset([[UICollectionView alloc]
+ initWithFrame:[self collectionViewFrame]
+ collectionViewLayout:flowLayout]);
+ [_collectionView setDelegate:self];
+ [_collectionView setDataSource:self];
+ [_collectionView registerClass:[TabSwitcherHeaderCell class]
+ forCellWithReuseIdentifier:[TabSwitcherHeaderCell identifier]];
+ [_collectionView setShowsVerticalScrollIndicator:NO];
+ [_collectionView setShowsHorizontalScrollIndicator:NO];
+ [_collectionView setBackgroundColor:[[MDCPalette greyPalette] tint900]];
+ [_collectionView setAllowsMultipleSelection:NO];
+ [_collectionView setAllowsSelection:YES];
+ [_collectionView setIsAccessibilityElement:NO];
+ [_collectionView setAccessibilityElementsHidden:YES];
+ [self addSubview:_collectionView];
+
+ _accessibilityView.reset([[AccessiblePanelSelectorView alloc]
+ initWithFrame:[self collectionViewFrame]]);
+ [_accessibilityView
+ setAutoresizingMask:UIViewAutoresizingFlexibleBottomMargin |
+ UIViewAutoresizingFlexibleWidth];
+ [_accessibilityView setDelegate:self];
+ [_accessibilityView setUserInteractionEnabled:NO];
+ [self addSubview:_accessibilityView];
+
+ _dismissButton.reset([[UIButton alloc] initWithFrame:CGRectZero]);
+ UIImage* dismissImage =
+ [UIImage imageNamed:@"tabswitcher_tab_switcher_button"];
+ dismissImage =
+ [dismissImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
+ [_dismissButton setContentMode:UIViewContentModeCenter];
+ [_dismissButton setBackgroundColor:[UIColor clearColor]];
+ [_dismissButton setTintColor:[UIColor whiteColor]];
+ [_dismissButton setImage:dismissImage forState:UIControlStateNormal];
+ [_dismissButton
+ setAccessibilityLabel:l10n_util::GetNSString(
+ IDS_IOS_TAB_STRIP_LEAVE_TAB_SWITCHER)];
+
+ [_dismissButton addTarget:self
+ action:@selector(dismissButtonTouchUpInside:)
+ forControlEvents:UIControlEventTouchUpInside];
+ [_dismissButton setTranslatesAutoresizingMaskIntoConstraints:NO];
+ [self addSubview:_dismissButton];
+
+ NSArray* constraints = @[
+ @"V:|-0-[dismissButton(==buttonHeight)]",
+ @"H:[dismissButton(==buttonWidth)]-0-|",
+ ];
+ NSDictionary* viewsDictionary = @{
+ @"dismissButton" : _dismissButton.get(),
+ };
+ NSDictionary* metrics = @{
+ @"buttonHeight" : @(kDismissButtonHeight),
+ @"buttonWidth" : @(kDismissButtonWidth),
+ };
+ ApplyVisualConstraintsWithMetricsAndOptions(
+ constraints, viewsDictionary, metrics, LayoutOptionForRTLSupport(), self);
+
+ base::scoped_nsobject<UIView> activeSpaceIndicatorView(
+ [[UIView alloc] initWithFrame:CGRectZero]);
+ [activeSpaceIndicatorView
+ setBackgroundColor:[[MDCPalette cr_bluePalette] tint500]];
+ [activeSpaceIndicatorView
+ setFrame:CGRectMake(
+ 0, self.bounds.size.height - kActiveSpaceIndicatorHeight,
+ kCollectionViewCellWidth, kActiveSpaceIndicatorHeight)];
+ [self addSubview:activeSpaceIndicatorView];
+ _activeSpaceIndicatorView = activeSpaceIndicatorView;
+}
+
+- (void)layoutCollectionView {
+ NSInteger selectedIndex = [self selectedIndex];
+ [_collectionView setFrame:[self collectionViewFrame]];
+ if (selectedIndex != NSNotFound)
+ [self updateSelectionAtIndex:selectedIndex animated:YES];
+}
+
+- (CGRect)collectionViewFrame {
+ return CGRectMake(0, kCollectionViewTopMargin, self.bounds.size.width,
+ kCollectionViewHeight);
+}
+
+- (void)layoutActiveSpaceIndicatorAnimated:(BOOL)animated {
+ [self setPanelSelectorAccessibility];
+ NSInteger selectedIndex = [self selectedIndex];
+ if (selectedIndex == NSNotFound)
+ return;
+ CGRect indicatorFrame = [_activeSpaceIndicatorView bounds];
+ indicatorFrame.origin.y =
+ self.bounds.size.height - kActiveSpaceIndicatorHeight;
+ indicatorFrame.origin.x =
+ kCollectionViewCellWidth *
+ [self leftToRightIndexForFlowLayoutIndex:selectedIndex] -
+ [_collectionView contentOffset].x;
+ if (animated)
+ [UIView beginAnimations:nil context:NULL];
+ [_activeSpaceIndicatorView setFrame:indicatorFrame];
+ if (animated)
+ [UIView commitAnimations];
+}
+
+- (void)dismissButtonTouchUpInside:(UIButton*)button {
+ [self.delegate tabSwitcherHeaderViewDismiss:self];
+}
+
+- (void)setPanelSelectorAccessibility {
+ NSInteger index = [self selectedIndex];
+ if (index != NSNotFound)
+ [_accessibilityView setAccessibilityLabel:[self panelTitleAtIndex:index]];
+}
+
+- (NSString*)panelTitleAtIndex:(NSInteger)index {
+ NSIndexPath* indexPath = [NSIndexPath indexPathForItem:index inSection:0];
+ SessionCellData* sessionCellData =
+ [[self dataSource] sessionCellDataAtIndex:indexPath.row];
+ return sessionCellData.title;
+}
+
+#pragma mark - AccessiblePanelSelectorDelegate
+
+- (void)moveToPanelInDirection:(PanelSelectionChangeDirection)direction {
+ NSInteger indexDelta = direction == RIGHT ? 1 : -1;
+ NSInteger newIndex = [self selectedIndex] + indexDelta;
+ newIndex = std::max<NSInteger>(newIndex, 0);
+ newIndex = std::min<NSInteger>(
+ newIndex,
+ [self collectionView:_collectionView numberOfItemsInSection:0] - 1);
+ NSIndexPath* newIndexPath =
+ [NSIndexPath indexPathForItem:newIndex inSection:0];
+ [_collectionView
+ selectItemAtIndexPath:newIndexPath
+ animated:NO
+ scrollPosition:UICollectionViewScrollPositionCenteredHorizontally];
+ [self updateSelectionAtIndex:newIndexPath.item animated:NO];
+ [[self delegate]
+ tabSwitcherHeaderViewDidSelectSessionAtIndex:newIndexPath.item];
+}
+
+#pragma mark - UICollectionViewDataSource
+
+- (NSInteger)collectionView:(UICollectionView*)collectionView
+ numberOfItemsInSection:(NSInteger)section {
+ DCHECK([self dataSource]);
+ DCHECK(section == 0);
+ return [[self dataSource] tabSwitcherHeaderViewSessionCount];
+}
+
+- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
+ cellForItemAtIndexPath:(NSIndexPath*)indexPath {
+ TabSwitcherHeaderCell* headerCell = [collectionView
+ dequeueReusableCellWithReuseIdentifier:[TabSwitcherHeaderCell identifier]
+ forIndexPath:indexPath];
+ SessionCellData* sessionCellData =
+ [[self dataSource] sessionCellDataAtIndex:indexPath.row];
+ [headerCell loadSessionCellData:sessionCellData];
+ return headerCell;
+}
+
+#pragma mark - UICollectionViewDelegate
+
+- (void)collectionView:(UICollectionView*)collectionView
+ didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
+ [self updateSelectionAtIndex:indexPath.item animated:YES];
+ [[self delegate] tabSwitcherHeaderViewDidSelectSessionAtIndex:indexPath.item];
+}
+
+#pragma mark - UIScrollViewDelegate
+
+- (void)scrollViewDidScroll:(UIScrollView*)scrollView {
+ [self layoutActiveSpaceIndicatorAnimated:NO];
+}
+
+@end
« 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