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

Unified Diff: ios/chrome/browser/ui/history/tab_history_view_controller.mm

Issue 2590473002: Upstream Chrome on iOS source code [5/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/history/tab_history_view_controller.mm
diff --git a/ios/chrome/browser/ui/history/tab_history_view_controller.mm b/ios/chrome/browser/ui/history/tab_history_view_controller.mm
new file mode 100644
index 0000000000000000000000000000000000000000..fcd1f851ac8422802ef7391d424df8895552f743
--- /dev/null
+++ b/ios/chrome/browser/ui/history/tab_history_view_controller.mm
@@ -0,0 +1,433 @@
+// 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/history/tab_history_view_controller.h"
+
+#import "base/ios/weak_nsobject.h"
+#include "base/logging.h"
+#include "base/mac/foundation_util.h"
+#include "base/mac/objc_property_releaser.h"
+#include "base/mac/scoped_nsobject.h"
+#include "base/strings/sys_string_conversions.h"
+#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
+#include "ios/chrome/browser/ui/commands/ios_command_ids.h"
+#import "ios/chrome/browser/ui/history/tab_history_cell.h"
+#include "ios/chrome/browser/ui/rtl_geometry.h"
+#import "ios/third_party/material_components_ios/src/components/Ink/src/MaterialInk.h"
+#import "ios/web/navigation/crw_session_entry.h"
+#include "ios/web/public/favicon_status.h"
+#include "ios/web/public/navigation_item.h"
+#include "ui/gfx/image/image.h"
+
+namespace {
+
+// Visible percentage of the last visible row on the Tools menu if the
+// Tools menu is scrollable.
+const CGFloat kLastRowVisiblePercentage = 0.6;
+// Reuse identifier for cells.
+NSString* cellIdentifier = @"TabHistoryCell";
+NSString* footerIdentifier = @"Footer";
+NSString* headerIdentifier = @"Header";
+// Height of rows.
+const CGFloat kCellHeight = 48.0;
+// Fraction height for partially visible row.
+const CGFloat kCellHeightLastRow = kCellHeight * kLastRowVisiblePercentage;
+// Width and leading for the header view.
+const CGFloat kHeaderLeadingInset = 0;
+const CGFloat kHeaderWidth = 48;
+// Leading and trailing insets for cell items.
+const CGFloat kCellLeadingInset = kHeaderLeadingInset + kHeaderWidth;
+const CGFloat kCellTrailingInset = 16;
+
+typedef std::vector<CGFloat> CGFloatVector;
+typedef std::vector<CGFloatVector> ItemOffsetVector;
+
+NS_INLINE CGFloat OffsetForPath(const ItemOffsetVector& offsets,
+ NSInteger section,
+ NSInteger item) {
+ DCHECK(section < (NSInteger)offsets.size());
+ DCHECK(item < (NSInteger)offsets.at(section).size());
+
+ return offsets.at(section).at(item);
+}
+
+NS_INLINE CGFloat OffsetForPath(const ItemOffsetVector& offsets,
+ NSIndexPath* path) {
+ return OffsetForPath(offsets, [path section], [path item]);
+}
+
+NS_INLINE CGFloat OffsetForSection(const ItemOffsetVector& offsets,
+ NSIndexPath* path) {
+ DCHECK([path section] < (NSInteger)offsets.size());
+ DCHECK(offsets.at([path section]).size());
+
+ return offsets.at([path section]).at(0);
+}
+
+// Height for the footer view.
+NS_INLINE CGFloat FooterHeight() {
+ return 1.0 / [[UIScreen mainScreen] scale];
+}
+
+} // namespace
+
+@interface TabHistoryViewControllerLayout : UICollectionViewLayout
+@end
+
+@implementation TabHistoryViewControllerLayout {
+ ItemOffsetVector _itemYOffsets;
+ CGFloat _containerCalculatedHeight;
+ CGFloat _containerWidth;
+ CGFloat _cellItemWidth;
+ CGFloat _footerWidth;
+}
+
+- (void)prepareLayout {
+ [super prepareLayout];
+
+ UICollectionView* collectionView = [self collectionView];
+ CGFloat yOffset = 0;
+
+ NSInteger numberOfSections = [collectionView numberOfSections];
+ _itemYOffsets.reserve(numberOfSections);
+
+ for (NSInteger section = 0; section < numberOfSections; ++section) {
+ NSInteger numberOfItems = [collectionView numberOfItemsInSection:section];
+
+ CGFloatVector dummy;
+ _itemYOffsets.push_back(dummy);
+
+ CGFloatVector& sectionYOffsets = _itemYOffsets.at(section);
+ sectionYOffsets.reserve(numberOfItems);
+
+ for (NSInteger item = 0; item < numberOfItems; ++item) {
+ sectionYOffsets.push_back(yOffset);
+ yOffset += kCellHeight;
+ }
+
+ // The last section should not have a footer.
+ if (numberOfItems && (section + 1) < numberOfSections) {
+ yOffset += FooterHeight();
+ }
+ }
+
+ CGRect containerBounds = [collectionView bounds];
+ _containerWidth = CGRectGetWidth(containerBounds);
+ _cellItemWidth = _containerWidth - kCellLeadingInset - kCellTrailingInset;
+ _footerWidth = _containerWidth - kCellLeadingInset;
+ _containerCalculatedHeight = yOffset - kCellHeight / 2.0;
+}
+
+- (void)invalidateLayout {
+ [super invalidateLayout];
+ _itemYOffsets.clear();
+ _containerCalculatedHeight = 0;
+ _cellItemWidth = 0;
+ _footerWidth = 0;
+}
+
+- (CGSize)collectionViewContentSize {
+ return CGSizeMake(_containerWidth, _containerCalculatedHeight);
+}
+
+- (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
+ UICollectionView* collectionView = [self collectionView];
+ NSMutableArray* array = [NSMutableArray array];
+
+ NSInteger numberOfSections = [collectionView numberOfSections];
+ for (NSInteger section = 0; section < numberOfSections; ++section) {
+ NSInteger numberOfItems = [collectionView numberOfItemsInSection:section];
+ if (numberOfItems) {
+ NSIndexPath* path =
+ [NSIndexPath indexPathForItem:numberOfItems - 1 inSection:section];
+ [array addObject:[self layoutAttributesForSupplementaryViewOfKind:
+ UICollectionElementKindSectionHeader
+ atIndexPath:path]];
+ }
+
+ for (NSInteger item = 0; item < numberOfItems; ++item) {
+ NSIndexPath* path = [NSIndexPath indexPathForItem:item inSection:section];
+ [array addObject:[self layoutAttributesForItemAtIndexPath:path]];
+ }
+
+ // The last section should not have a footer.
+ if (numberOfItems && (section + 1) < numberOfSections) {
+ NSIndexPath* path =
+ [NSIndexPath indexPathForItem:numberOfItems - 1 inSection:section];
+ [array addObject:[self layoutAttributesForSupplementaryViewOfKind:
+ UICollectionElementKindSectionFooter
+ atIndexPath:path]];
+ }
+ }
+
+ return array;
+}
+
+- (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:
+ (NSIndexPath*)indexPath {
+ CGFloat yOffset = OffsetForPath(_itemYOffsets, indexPath);
+
+ UICollectionViewLayoutAttributes* attributes =
+ [UICollectionViewLayoutAttributes
+ layoutAttributesForCellWithIndexPath:indexPath];
+ LayoutRect cellLayout = LayoutRectMake(kCellLeadingInset, _containerWidth,
+ yOffset, _cellItemWidth, kCellHeight);
+ [attributes setFrame:LayoutRectGetRect(cellLayout)];
+ [attributes setZIndex:1];
+
+ return attributes;
+}
+
+- (UICollectionViewLayoutAttributes*)
+layoutAttributesForSupplementaryViewOfKind:(NSString*)kind
+ atIndexPath:(NSIndexPath*)indexPath {
+ CGFloat yOffset = OffsetForPath(_itemYOffsets, indexPath);
+
+ UICollectionViewLayoutAttributes* attributes =
+ [UICollectionViewLayoutAttributes
+ layoutAttributesForSupplementaryViewOfKind:kind
+ withIndexPath:indexPath];
+
+ if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
+ // The height is the yOffset of the first section minus the yOffset of the
+ // last item. Additionally, the height of a cell needs to be added back in
+ // since the _itemYOffsets stores Mid Y.
+ CGFloat headerYOffset = OffsetForSection(_itemYOffsets, indexPath);
+ CGFloat height = yOffset - headerYOffset + kCellHeight;
+
+ if ([indexPath section])
+ headerYOffset += FooterHeight();
+
+ LayoutRect cellLayout = LayoutRectMake(kHeaderLeadingInset, _containerWidth,
+ headerYOffset, kHeaderWidth, height);
+ [attributes setFrame:LayoutRectGetRect(cellLayout)];
+ [attributes setZIndex:0];
+ } else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
+ LayoutRect cellLayout =
+ LayoutRectMake(kCellLeadingInset, _containerWidth, yOffset,
+ _footerWidth, FooterHeight());
+ [attributes setFrame:LayoutRectGetRect(cellLayout)];
+ [attributes setZIndex:0];
+ }
+
+ return attributes;
+}
+
+@end
+
+@interface TabHistoryViewController ()<MDCInkTouchControllerDelegate> {
+ base::scoped_nsobject<MDCInkTouchController> _inkTouchController;
+ base::scoped_nsobject<NSArray> _partitionedEntries;
+ base::scoped_nsobject<NSArray> _sessionEntries;
+}
+@end
+
+@implementation TabHistoryViewController
+
+- (NSArray*)sessionEntries {
+ return [[_sessionEntries retain] autorelease];
+}
+
+#pragma mark Public Methods
+
+- (CGFloat)optimalHeight:(CGFloat)suggestedHeight {
+ DCHECK(suggestedHeight >= kCellHeight);
+ CGFloat optimalHeight = 0;
+
+ for (NSArray* sectionArray in _partitionedEntries.get()) {
+ NSUInteger sectionItemCount = [sectionArray count];
+ for (NSUInteger i = 0; i < sectionItemCount; ++i) {
+ CGFloat proposedHeight = optimalHeight + kCellHeight;
+
+ if (proposedHeight > suggestedHeight) {
+ CGFloat difference = proposedHeight - suggestedHeight;
+ if (difference > kCellHeightLastRow) {
+ return optimalHeight + kCellHeightLastRow;
+ } else {
+ return optimalHeight - kCellHeight + kCellHeightLastRow;
+ }
+ }
+
+ optimalHeight = proposedHeight;
+ }
+
+ optimalHeight += FooterHeight();
+ }
+
+ // If this point is reached, it means the entire content fits and this last
+ // section should not include the footer.
+ optimalHeight -= FooterHeight();
+
+ return optimalHeight;
+}
+
+- (instancetype)init {
+ base::scoped_nsobject<TabHistoryViewControllerLayout> layout(
+ [[TabHistoryViewControllerLayout alloc] init]);
+
+ return [self initWithCollectionViewLayout:layout];
+}
+
+- (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout*)layout {
+ self = [super initWithCollectionViewLayout:layout];
+ if (self) {
+ UICollectionView* collectionView = [self collectionView];
+ [collectionView setBackgroundColor:[UIColor whiteColor]];
+
+ [collectionView registerClass:[TabHistoryCell class]
+ forCellWithReuseIdentifier:cellIdentifier];
+
+ [collectionView registerClass:[TabHistorySectionHeader class]
+ forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
+ withReuseIdentifier:headerIdentifier];
+
+ [collectionView registerClass:[TabHistorySectionFooter class]
+ forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
+ withReuseIdentifier:footerIdentifier];
+
+ _inkTouchController.reset(
+ [[MDCInkTouchController alloc] initWithView:collectionView]);
+ [_inkTouchController setDelegate:self];
+ [_inkTouchController addInkView];
+ }
+
+ return self;
+}
+
+#pragma mark UICollectionViewDelegate
+
+- (void)collectionView:(UICollectionView*)collectionView
+ didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
+ UICollectionViewCell* cell =
+ [collectionView cellForItemAtIndexPath:indexPath];
+ [collectionView chromeExecuteCommand:cell];
+}
+
+#pragma mark UICollectionViewDataSource
+
+- (CRWSessionEntry*)entryForIndexPath:(NSIndexPath*)indexPath {
+ NSInteger section = [indexPath section];
+ NSInteger item = [indexPath item];
+
+ DCHECK(section < (NSInteger)[_partitionedEntries count]);
+ DCHECK(item < (NSInteger)[[_partitionedEntries objectAtIndex:section] count]);
+ NSArray* sectionedArray = [_partitionedEntries objectAtIndex:section];
+
+ return [sectionedArray objectAtIndex:item];
+}
+
+- (NSInteger)collectionView:(UICollectionView*)collectionView
+ numberOfItemsInSection:(NSInteger)section {
+ DCHECK(section < (NSInteger)[_partitionedEntries count]);
+ return [[_partitionedEntries objectAtIndex:section] count];
+}
+
+- (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
+ cellForItemAtIndexPath:(NSIndexPath*)indexPath {
+ TabHistoryCell* cell =
+ [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier
+ forIndexPath:indexPath];
+
+ [cell setEntry:[self entryForIndexPath:indexPath]];
+ [cell setTag:IDC_BACK_FORWARD_IN_TAB_HISTORY];
+
+ return cell;
+}
+
+- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)view {
+ return [_partitionedEntries count];
+}
+
+- (UICollectionReusableView*)collectionView:(UICollectionView*)view
+ viewForSupplementaryElementOfKind:(NSString*)kind
+ atIndexPath:(NSIndexPath*)indexPath {
+ if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
+ return [view dequeueReusableSupplementaryViewOfKind:kind
+ withReuseIdentifier:footerIdentifier
+ forIndexPath:indexPath];
+ }
+
+ DCHECK([kind isEqualToString:UICollectionElementKindSectionHeader]);
+ CRWSessionEntry* sessionEntry = [self entryForIndexPath:indexPath];
+ web::NavigationItem* navigationItem = [sessionEntry navigationItem];
+
+ TabHistorySectionHeader* header =
+ [view dequeueReusableSupplementaryViewOfKind:kind
+ withReuseIdentifier:headerIdentifier
+ forIndexPath:indexPath];
+
+ UIImage* iconImage = nil;
+ const gfx::Image& image = navigationItem->GetFavicon().image;
+ if (!image.IsEmpty())
+ iconImage = image.ToUIImage();
+ else
+ iconImage = [UIImage imageNamed:@"default_favicon"];
+
+ [[header iconView] setImage:iconImage];
+
+ return header;
+}
+
+- (void)setSessionEntries:(NSArray*)sessionEntries {
+ _sessionEntries.reset([sessionEntries retain]);
+
+ std::string previousHost;
+
+ NSMutableArray* sectionArray = [NSMutableArray array];
+ NSMutableArray* partitionedEntries = [NSMutableArray array];
+
+ NSInteger numberOfEntries = [_sessionEntries count];
+ for (NSInteger index = 0; index < numberOfEntries; ++index) {
+ CRWSessionEntry* sessionEntry = [_sessionEntries objectAtIndex:index];
+ web::NavigationItem* navigationItem = [sessionEntry navigationItem];
+
+ std::string currentHost;
+ if (navigationItem)
+ currentHost = navigationItem->GetURL().host();
+
+ if (previousHost.empty())
+ previousHost = currentHost;
+
+ // TODO: This should use some sort of Top Level Domain matching instead of
+ // explicit host match so that images.googe.com matches shopping.google.com.
+ if (previousHost == currentHost) {
+ [sectionArray addObject:sessionEntry];
+ } else {
+ [partitionedEntries addObject:sectionArray];
+ sectionArray = [NSMutableArray arrayWithObject:sessionEntry];
+ previousHost = currentHost;
+ }
+ }
+
+ if ([sectionArray count])
+ [partitionedEntries addObject:sectionArray];
+
+ if (![partitionedEntries count])
+ partitionedEntries = nil;
+
+ _partitionedEntries.reset([partitionedEntries retain]);
+}
+
+#pragma mark MDCInkTouchControllerDelegate
+
+- (BOOL)inkTouchController:(MDCInkTouchController*)inkTouchController
+ shouldProcessInkTouchesAtTouchLocation:(CGPoint)location {
+ NSIndexPath* indexPath =
+ [self.collectionView indexPathForItemAtPoint:location];
+ TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>(
+ [self.collectionView cellForItemAtIndexPath:indexPath]);
+ if (!cell) {
+ return NO;
+ }
+
+ // Increase the size of the ink view to cover the collection view from edge
+ // to edge.
+ CGRect inkViewFrame = [cell frame];
+ inkViewFrame.origin.x = 0;
+ inkViewFrame.size.width = CGRectGetWidth([self.collectionView bounds]);
+ [[inkTouchController defaultInkView] setFrame:inkViewFrame];
+ return YES;
+}
+
+@end
« no previous file with comments | « ios/chrome/browser/ui/history/tab_history_view_controller.h ('k') | ios/chrome/browser/ui/icons/chrome_icon.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698