Commit 2fd5efd2 authored by Roberto Moura's avatar Roberto Moura Committed by Commit Bot

Add Tab Switcher Layout and Horizontal Layout classes.

As specified in the "Thumb Strip Layout" subsection of the Thumb Strip
design doc (go/bling-thumb-strip-design):
- Create a new Horizontal Layout class.
- Create a Tab Switcher Layout class that will contain the intersection
of the new Horizontal Layout and the existing Grid Layout.
- Make the Grid Layout and Horizontal Layout subclasses of the Tab
Switcher Layout.

Bug: 1127604
Change-Id: I8da3e43563e6a18abb3bf0234db26aeccefc8e06
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2367092
Commit-Queue: Roberto Moura <mouraroberto@google.com>
Reviewed-by: default avatarGauthier Ambard <gambard@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806573}
parent 2812c5fa
......@@ -28,6 +28,10 @@ source_set("grid_ui") {
"grid_theme.h",
"grid_view_controller.h",
"grid_view_controller.mm",
"horizontal_layout.h",
"horizontal_layout.mm",
"tab_switcher_layout.h",
"tab_switcher_layout.mm",
]
configs += [ "//build/config/compiler:enable_arc" ]
......
......@@ -5,21 +5,10 @@
#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_LAYOUT_H_
#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_LAYOUT_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/tab_grid/grid/tab_switcher_layout.h"
// Collection view flow layout that displays items in a grid. Items are
// square-ish. Item sizes adapt to the size classes they are shown in. Item
// deletions are animated.
@interface GridLayout : UICollectionViewFlowLayout
// Whether to animate item insertions and deletions.
@property(nonatomic, assign) BOOL animatesItemUpdates;
@end
// A specialization of GridLayout that shows the UI in its "reordering" state,
// with the moving cell enlarged and the non-moving cells transparent.
@interface GridReorderingLayout : GridLayout
// A specialization of TabSwitcherLayout that displays items in a grid.
@interface GridLayout : TabSwitcherLayout
@end
#endif // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_GRID_LAYOUT_H_
......@@ -10,20 +10,8 @@
#error "This file requires ARC support."
#endif
@interface GridLayout ()
@property(nonatomic, strong) NSArray<NSIndexPath*>* indexPathsOfDeletingItems;
@property(nonatomic, strong) NSArray<NSIndexPath*>* indexPathsOfInsertingItems;
@end
@implementation GridLayout
- (instancetype)init {
if (self = [super init]) {
_animatesItemUpdates = YES;
}
return self;
}
#pragma mark - UICollectionViewLayout
// This is called whenever the layout is invalidated, including during rotation.
......@@ -76,140 +64,5 @@
}
}
- (void)prepareForCollectionViewUpdates:
(NSArray<UICollectionViewUpdateItem*>*)updateItems {
[super prepareForCollectionViewUpdates:updateItems];
// Track which items in this update are explicitly being deleted or inserted.
NSMutableArray<NSIndexPath*>* deletingItems =
[NSMutableArray arrayWithCapacity:updateItems.count];
NSMutableArray<NSIndexPath*>* insertingItems =
[NSMutableArray arrayWithCapacity:updateItems.count];
for (UICollectionViewUpdateItem* item in updateItems) {
switch (item.updateAction) {
case UICollectionUpdateActionDelete:
[deletingItems addObject:item.indexPathBeforeUpdate];
break;
case UICollectionUpdateActionInsert:
[insertingItems addObject:item.indexPathAfterUpdate];
break;
default:
break;
}
}
self.indexPathsOfDeletingItems = [deletingItems copy];
self.indexPathsOfInsertingItems = [insertingItems copy];
}
- (UICollectionViewLayoutAttributes*)
finalLayoutAttributesForDisappearingItemAtIndexPath:
(NSIndexPath*)itemIndexPath {
// Return initial layout if animations are disabled.
if (!self.animatesItemUpdates) {
return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
}
// Note that this method is called for any item whose index path changing from
// |itemIndexPath|, which includes any items that were in the layout and whose
// index path is changing. For an item whose index path is changing, this
// method is called before
// -initialLayoutAttributesForAppearingItemAtIndexPath:
UICollectionViewLayoutAttributes* attributes = [[super
finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath] copy];
// Disappearing items that aren't being deleted just use the default
// attributes.
if (![self.indexPathsOfDeletingItems containsObject:itemIndexPath]) {
return attributes;
}
// Cells being deleted scale to 0, and are z-positioned behind all others.
// (Note that setting the zIndex here actually has no effect, despite what is
// implied in the UIKit documentation).
attributes.zIndex = -10;
// Scaled down to 0% (or near enough).
CGAffineTransform transform =
CGAffineTransformScale(attributes.transform, /*sx=*/0.01, /*sy=*/0.01);
attributes.transform = transform;
// Fade out.
attributes.alpha = 0.0;
return attributes;
}
- (UICollectionViewLayoutAttributes*)
initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath*)itemIndexPath {
// Return final layout if animations are disabled.
if (!self.animatesItemUpdates) {
return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
}
// Note that this method is called for any item whose index path is becoming
// |itemIndexPath|, which includes any items that were in the layout but whose
// index path is changing. For an item whose index path is changing, this
// method is called after
// -finalLayoutAttributesForDisappearingItemAtIndexPath:
UICollectionViewLayoutAttributes* attributes = [[super
initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath] copy];
// Appearing items that aren't being inserted just use the default
// attributes.
if (![self.indexPathsOfInsertingItems containsObject:itemIndexPath]) {
return attributes;
}
// TODO(crbug.com/820410) : Polish the animation, and put constants where they
// belong.
// Cells being inserted start faded out, scaled down, and drop downwards
// slightly.
attributes.alpha = 0.0;
CGAffineTransform transform =
CGAffineTransformScale(attributes.transform, /*sx=*/0.9, /*sy=*/0.9);
transform = CGAffineTransformTranslate(transform, /*tx=*/0,
/*ty=*/attributes.size.height * 0.1);
attributes.transform = transform;
return attributes;
}
- (void)finalizeCollectionViewUpdates {
self.indexPathsOfDeletingItems = @[];
self.indexPathsOfInsertingItems = @[];
}
@end
@implementation GridReorderingLayout
#pragma mark - UICollectionViewLayout
// Both -layoutAttributesForElementsInRect: and
// -layoutAttributesForItemAtIndexPath: need to be overridden to change the
// default layout attributes.
- (NSArray<__kindof UICollectionViewLayoutAttributes*>*)
layoutAttributesForElementsInRect:(CGRect)rect {
NSArray* baseAttributes = [super layoutAttributesForElementsInRect:rect];
NSMutableArray<__kindof UICollectionViewLayoutAttributes*>* attributes =
[NSMutableArray array];
for (UICollectionViewLayoutAttributes* attribute in baseAttributes) {
UICollectionViewLayoutAttributes* newAttribute = [attribute copy];
newAttribute.alpha = kReorderingInactiveCellOpacity;
[attributes addObject:newAttribute];
}
return [attributes copy];
}
- (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:
(NSIndexPath*)indexPath {
UICollectionViewLayoutAttributes* attributes =
[[super layoutAttributesForItemAtIndexPath:indexPath] copy];
attributes.alpha = kReorderingInactiveCellOpacity;
return attributes;
}
- (UICollectionViewLayoutAttributes*)
layoutAttributesForInteractivelyMovingItemAtIndexPath:(NSIndexPath*)indexPath
withTargetPosition:(CGPoint)position {
UICollectionViewLayoutAttributes* attributes = [[super
layoutAttributesForInteractivelyMovingItemAtIndexPath:indexPath
withTargetPosition:position] copy];
// The moving item has regular opacity, but is scaled.
attributes.alpha = 1.0;
attributes.transform =
CGAffineTransformScale(attributes.transform, kReorderingActiveCellScale,
kReorderingActiveCellScale);
return attributes;
}
@end
......@@ -21,6 +21,7 @@
#import "ios/chrome/browser/ui/tab_grid/grid/grid_image_data_source.h"
#import "ios/chrome/browser/ui/tab_grid/grid/grid_item.h"
#import "ios/chrome/browser/ui/tab_grid/grid/grid_layout.h"
#import "ios/chrome/browser/ui/tab_grid/grid/tab_switcher_layout.h"
#import "ios/chrome/browser/ui/tab_grid/transitions/grid_transition_layout.h"
#include "ios/chrome/browser/ui/ui_feature_flags.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
......@@ -76,8 +77,8 @@ NSIndexPath* CreateIndexPath(NSInteger index) {
@property(nonatomic, assign) CGPoint itemReorderTouchPoint;
// Animator to show or hide the empty state.
@property(nonatomic, strong) UIViewPropertyAnimator* emptyStateAnimator;
// The default layout for the tab grid.
@property(nonatomic, strong) GridLayout* defaultLayout;
// The default layout for the tab switcher.
@property(nonatomic, strong) TabSwitcherLayout* defaultLayout;
// The layout used while the grid is being reordered.
@property(nonatomic, strong) UICollectionViewLayout* reorderingLayout;
// YES if, when reordering is enabled, the order of the cells has changed.
......@@ -137,7 +138,7 @@ NSIndexPath* CreateIndexPath(NSInteger index) {
collectionView.dropDelegate = self;
collectionView.dragInteractionEnabled = YES;
} else {
self.reorderingLayout = [[GridReorderingLayout alloc] init];
self.reorderingLayout = [[TabSwitcherReorderingLayout alloc] init];
self.itemReorderRecognizer = [[UILongPressGestureRecognizer alloc]
initWithTarget:self
action:@selector(handleItemReorderingWithGesture:)];
......
// Copyright 2020 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.
#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_HORIZONTAL_LAYOUT_H_
#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_HORIZONTAL_LAYOUT_H_
#import "ios/chrome/browser/ui/tab_grid/grid/tab_switcher_layout.h"
// A specialization of TabSwitcherLayout that displays items horizontally.
@interface HorizontalLayout : TabSwitcherLayout
@end
#endif // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_HORIZONTAL_LAYOUT_H_
// Copyright 2020 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_grid/grid/horizontal_layout.h"
#import "ios/chrome/browser/ui/tab_grid/grid/grid_constants.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@implementation HorizontalLayout
- (instancetype)init {
if (self = [super init]) {
self.scrollDirection = UICollectionViewScrollDirectionHorizontal;
}
return self;
}
#pragma mark - UICollectionViewLayout
// This is called whenever the layout is invalidated, including during rotation.
// Resizes item, margins, and spacing to fit new size classes and width.
- (void)prepareLayout {
[super prepareLayout];
self.itemSize = kGridCellSizeSmall;
CGFloat height = CGRectGetHeight(self.collectionView.bounds);
CGFloat spacing = kGridLayoutLineSpacingCompactCompactLimitedWidth;
self.sectionInset = UIEdgeInsets{
spacing, spacing, height - self.itemSize.height - 2 * spacing, spacing};
self.minimumLineSpacing = kGridLayoutLineSpacingRegularRegular;
}
@end
// Copyright 2020 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.
#ifndef IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_TAB_SWITCHER_LAYOUT_H_
#define IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_TAB_SWITCHER_LAYOUT_H_
#import <UIKit/UIKit.h>
// Collection view flow layout that displays items in a grid or horizontally.
// Items are square-ish. Item sizes adapt to the size classes they are shown in.
// Item deletions are animated.
@interface TabSwitcherLayout : UICollectionViewFlowLayout
// Whether to animate item insertions and deletions.
@property(nonatomic, assign) BOOL animatesItemUpdates;
@end
// A specialization of TabSwitcherLayout that shows the UI in its "reordering"
// state, with the moving cell enlarged and the non-moving cells transparent.
@interface TabSwitcherReorderingLayout : TabSwitcherLayout
@end
#endif // IOS_CHROME_BROWSER_UI_TAB_GRID_GRID_TAB_SWITCHER_LAYOUT_H_
// Copyright 2020 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_grid/grid/tab_switcher_layout.h"
#import "ios/chrome/browser/ui/tab_grid/grid/grid_constants.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
@interface TabSwitcherLayout ()
@property(nonatomic, strong) NSArray<NSIndexPath*>* indexPathsOfDeletingItems;
@property(nonatomic, strong) NSArray<NSIndexPath*>* indexPathsOfInsertingItems;
@end
@implementation TabSwitcherLayout
- (instancetype)init {
if (self = [super init]) {
_animatesItemUpdates = YES;
}
return self;
}
#pragma mark - UICollectionViewLayout
- (void)prepareForCollectionViewUpdates:
(NSArray<UICollectionViewUpdateItem*>*)updateItems {
[super prepareForCollectionViewUpdates:updateItems];
// Track which items in this update are explicitly being deleted or inserted.
NSMutableArray<NSIndexPath*>* deletingItems =
[NSMutableArray arrayWithCapacity:updateItems.count];
NSMutableArray<NSIndexPath*>* insertingItems =
[NSMutableArray arrayWithCapacity:updateItems.count];
for (UICollectionViewUpdateItem* item in updateItems) {
switch (item.updateAction) {
case UICollectionUpdateActionDelete:
[deletingItems addObject:item.indexPathBeforeUpdate];
break;
case UICollectionUpdateActionInsert:
[insertingItems addObject:item.indexPathAfterUpdate];
break;
default:
break;
}
}
self.indexPathsOfDeletingItems = [deletingItems copy];
self.indexPathsOfInsertingItems = [insertingItems copy];
}
- (UICollectionViewLayoutAttributes*)
finalLayoutAttributesForDisappearingItemAtIndexPath:
(NSIndexPath*)itemIndexPath {
// Return initial layout if animations are disabled.
if (!self.animatesItemUpdates) {
return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
}
// Note that this method is called for any item whose index path changing from
// |itemIndexPath|, which includes any items that were in the layout and whose
// index path is changing. For an item whose index path is changing, this
// method is called before
// -initialLayoutAttributesForAppearingItemAtIndexPath:
UICollectionViewLayoutAttributes* attributes = [[super
finalLayoutAttributesForDisappearingItemAtIndexPath:itemIndexPath] copy];
// Disappearing items that aren't being deleted just use the default
// attributes.
if (![self.indexPathsOfDeletingItems containsObject:itemIndexPath]) {
return attributes;
}
// Cells being deleted scale to 0, and are z-positioned behind all others.
// (Note that setting the zIndex here actually has no effect, despite what is
// implied in the UIKit documentation).
attributes.zIndex = -10;
// Scaled down to 0% (or near enough).
CGAffineTransform transform =
CGAffineTransformScale(attributes.transform, /*sx=*/0.01, /*sy=*/0.01);
attributes.transform = transform;
// Fade out.
attributes.alpha = 0.0;
return attributes;
}
- (UICollectionViewLayoutAttributes*)
initialLayoutAttributesForAppearingItemAtIndexPath:
(NSIndexPath*)itemIndexPath {
// Return final layout if animations are disabled.
if (!self.animatesItemUpdates) {
return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
}
// Note that this method is called for any item whose index path is becoming
// |itemIndexPath|, which includes any items that were in the layout but whose
// index path is changing. For an item whose index path is changing, this
// method is called after
// -finalLayoutAttributesForDisappearingItemAtIndexPath:
UICollectionViewLayoutAttributes* attributes = [[super
initialLayoutAttributesForAppearingItemAtIndexPath:itemIndexPath] copy];
// Appearing items that aren't being inserted just use the default
// attributes.
if (![self.indexPathsOfInsertingItems containsObject:itemIndexPath]) {
return attributes;
}
// TODO(crbug.com/820410) : Polish the animation, and put constants where they
// belong.
// Cells being inserted start faded out, scaled down, and drop downwards
// slightly.
attributes.alpha = 0.0;
CGAffineTransform transform =
CGAffineTransformScale(attributes.transform, /*sx=*/0.9, /*sy=*/0.9);
transform = CGAffineTransformTranslate(transform, /*tx=*/0,
/*ty=*/attributes.size.height * 0.1);
attributes.transform = transform;
return attributes;
}
- (void)finalizeCollectionViewUpdates {
self.indexPathsOfDeletingItems = @[];
self.indexPathsOfInsertingItems = @[];
}
@end
@implementation TabSwitcherReorderingLayout
#pragma mark - UICollectionViewLayout
// Both -layoutAttributesForElementsInRect: and
// -layoutAttributesForItemAtIndexPath: need to be overridden to change the
// default layout attributes.
- (NSArray<__kindof UICollectionViewLayoutAttributes*>*)
layoutAttributesForElementsInRect:(CGRect)rect {
NSArray* baseAttributes = [super layoutAttributesForElementsInRect:rect];
NSMutableArray<__kindof UICollectionViewLayoutAttributes*>* attributes =
[NSMutableArray array];
for (UICollectionViewLayoutAttributes* attribute in baseAttributes) {
UICollectionViewLayoutAttributes* newAttribute = [attribute copy];
newAttribute.alpha = kReorderingInactiveCellOpacity;
[attributes addObject:newAttribute];
}
return [attributes copy];
}
- (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:
(NSIndexPath*)indexPath {
UICollectionViewLayoutAttributes* attributes =
[[super layoutAttributesForItemAtIndexPath:indexPath] copy];
attributes.alpha = kReorderingInactiveCellOpacity;
return attributes;
}
- (UICollectionViewLayoutAttributes*)
layoutAttributesForInteractivelyMovingItemAtIndexPath:
(NSIndexPath*)indexPath
withTargetPosition:(CGPoint)position {
UICollectionViewLayoutAttributes* attributes = [[super
layoutAttributesForInteractivelyMovingItemAtIndexPath:indexPath
withTargetPosition:position] copy];
// The moving item has regular opacity, but is scaled.
attributes.alpha = 1.0;
attributes.transform =
CGAffineTransformScale(attributes.transform, kReorderingActiveCellScale,
kReorderingActiveCellScale);
return attributes;
}
@end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment