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

Unified Diff: ios/chrome/browser/ui/bookmarks/bookmark_folder_collection_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 side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/bookmarks/bookmark_folder_collection_view.mm
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_folder_collection_view.mm b/ios/chrome/browser/ui/bookmarks/bookmark_folder_collection_view.mm
new file mode 100644
index 0000000000000000000000000000000000000000..7f68a85e91e340eb4a49d2ddf5d073ca9b62a86a
--- /dev/null
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_folder_collection_view.mm
@@ -0,0 +1,474 @@
+// 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/bookmarks/bookmark_folder_collection_view.h"
+
+#include "base/logging.h"
+#include "base/mac/objc_property_releaser.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "ios/chrome/browser/bookmarks/bookmarks_utils.h"
+#include "ios/chrome/browser/experimental_flags.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_collection_cells.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_promo_cell.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
+
+using bookmarks::BookmarkNode;
+
+@interface BookmarkFolderCollectionView ()<BookmarkPromoCellDelegate> {
+ base::mac::ObjCPropertyReleaser
+ _propertyReleaser_BookmarkFolderCollectionView;
+ // A vector of folders to display in the collection view.
+ std::vector<const BookmarkNode*> _subFolders;
+ // A vector of bookmark urls to display in the collection view.
+ std::vector<const BookmarkNode*> _subItems;
+
+ // True if the promo is visible.
+ BOOL _promoVisible;
+}
+@property(nonatomic, assign) const bookmarks::BookmarkNode* folder;
+
+// Section indices.
+@property(nonatomic, readonly, assign) NSInteger promoSection;
+@property(nonatomic, readonly, assign) NSInteger folderSection;
+@property(nonatomic, readonly, assign) NSInteger itemsSection;
+@property(nonatomic, readonly, assign) NSInteger sectionCount;
+
+// Keep a reference to the promo cell to deregister as delegate.
+@property(nonatomic, retain) BookmarkPromoCell* promoCell;
+
+@end
+
+@implementation BookmarkFolderCollectionView
+@synthesize delegate = _delegate;
+@synthesize folder = _folder;
+@synthesize promoCell = _promoCell;
+
+- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
+ frame:(CGRect)frame {
+ self = [super initWithBrowserState:browserState frame:frame];
+ if (self) {
+ _propertyReleaser_BookmarkFolderCollectionView.Init(
+ self, [BookmarkFolderCollectionView class]);
+
+ [self updateCollectionView];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ _promoCell.delegate = nil;
+ [super dealloc];
+}
+
+- (void)setDelegate:(id<BookmarkFolderCollectionViewDelegate>)delegate {
+ _delegate = delegate;
+ [self promoStateChangedAnimated:NO];
+}
+
+- (NSInteger)promoSection {
+ return [self shouldShowPromoCell] ? 0 : -1;
+}
+
+- (NSInteger)folderSection {
+ return [self shouldShowPromoCell] ? 1 : 0;
+}
+
+- (NSInteger)itemsSection {
+ return [self shouldShowPromoCell] ? 2 : 1;
+}
+
+- (NSInteger)sectionCount {
+ return [self shouldShowPromoCell] ? 3 : 2;
+}
+
+- (void)updateCollectionView {
+ if (!self.bookmarkModel->loaded())
+ return;
+
+ // Regenerate the list of all bookmarks.
+ _subFolders = std::vector<const BookmarkNode*>();
+ _subItems = std::vector<const BookmarkNode*>();
+
+ if (self.folder) {
+ int childCount = self.folder->child_count();
+ for (int i = 0; i < childCount; ++i) {
+ const BookmarkNode* node = self.folder->GetChild(i);
+ if (node->is_folder())
+ _subFolders.push_back(node);
+ else
+ _subItems.push_back(node);
+ }
+
+ bookmark_utils_ios::SortFolders(&_subFolders);
+ }
+
+ [self cancelAllFaviconLoads];
+ [self.collectionView reloadData];
+}
+
+#pragma mark - BookmarkModelBridgeObserver Callbacks
+
+- (void)bookmarkModelLoaded {
+ [self updateCollectionView];
+}
+
+- (void)bookmarkNodeChanged:(const BookmarkNode*)bookmarkNode {
+ // The base folder changed. Do nothing.
+ if (bookmarkNode == self.folder)
+ return;
+
+ // A specific cell changed. Reload that cell.
+ NSIndexPath* indexPath = [self indexPathForNode:bookmarkNode];
+
+ if (indexPath) {
+ // TODO(crbug.com/603661): Ideally, we would only reload the relevant index
+ // path. However, calling reloadItemsAtIndexPaths:(0,0) immediately after
+ // reloadData results in a exception: NSInternalInconsistencyException
+ // 'request for index path for global index 2147483645 ...'
+ // One solution would be to keep track of whether we've just called
+ // reloadData, but that requires experimentation to determine how long we
+ // have to wait before we can safely call reloadItemsAtIndexPaths.
+ [self updateCollectionView];
+ }
+}
+
+- (void)bookmarkNodeFaviconChanged:
+ (const bookmarks::BookmarkNode*)bookmarkNode {
+ // Only urls have favicons.
+ DCHECK(bookmarkNode->is_url());
+
+ // Update image of corresponding cell.
+ NSIndexPath* indexPath = [self indexPathForNode:bookmarkNode];
+
+ if (!indexPath)
+ return;
+
+ // Check that this cell is visible.
+ NSArray* visiblePaths = [self.collectionView indexPathsForVisibleItems];
+ if (![visiblePaths containsObject:indexPath])
+ return;
+
+ [self loadFaviconAtIndexPath:indexPath];
+}
+
+- (NSIndexPath*)indexPathForNode:(const bookmarks::BookmarkNode*)bookmarkNode {
+ NSIndexPath* indexPath = nil;
+ if (bookmarkNode->is_folder()) {
+ std::vector<const BookmarkNode*>::iterator it =
+ std::find(_subFolders.begin(), _subFolders.end(), bookmarkNode);
+ if (it != _subFolders.end()) {
+ ptrdiff_t index = std::distance(_subFolders.begin(), it);
+ indexPath =
+ [NSIndexPath indexPathForRow:index inSection:self.folderSection];
+ }
+ } else if (bookmarkNode->is_url()) {
+ std::vector<const BookmarkNode*>::iterator it =
+ std::find(_subItems.begin(), _subItems.end(), bookmarkNode);
+ if (it != _subItems.end()) {
+ ptrdiff_t index = std::distance(_subItems.begin(), it);
+ indexPath =
+ [NSIndexPath indexPathForRow:index inSection:self.itemsSection];
+ }
+ }
+ return indexPath;
+}
+
+- (void)bookmarkNodeDeleted:(const BookmarkNode*)node
+ fromFolder:(const BookmarkNode*)folder {
+ if (self.folder == node) {
+ self.folder = nil;
+ [self updateCollectionView];
+ }
+}
+
+- (void)bookmarkNodeChildrenChanged:(const BookmarkNode*)bookmarkNode {
+ // The base folder's children changed. Reload everything.
+ if (bookmarkNode == self.folder) {
+ [self updateCollectionView];
+ return;
+ }
+
+ // A subfolder's children changed. Reload that cell.
+ std::vector<const BookmarkNode*>::iterator it =
+ std::find(_subFolders.begin(), _subFolders.end(), bookmarkNode);
+ if (it != _subFolders.end()) {
+ // TODO(crbug.com/603661): Ideally, we would only reload the relevant index
+ // path. However, calling reloadItemsAtIndexPaths:(0,0) immediately after
+ // reloadData results in a exception: NSInternalInconsistencyException
+ // 'request for index path for global index 2147483645 ...'
+ // One solution would be to keep track of whether we've just called
+ // reloadData, but that requires experimentation to determine how long we
+ // have to wait before we can safely call reloadItemsAtIndexPaths.
+ [self updateCollectionView];
+ }
+}
+
+- (void)bookmarkNode:(const BookmarkNode*)bookmarkNode
+ movedFromParent:(const BookmarkNode*)oldParent
+ toParent:(const BookmarkNode*)newParent {
+ if (oldParent == self.folder || newParent == self.folder) {
+ // A folder was added or removed from the base folder.
+ [self updateCollectionView];
+ }
+}
+
+- (void)bookmarkModelRemovedAllNodes {
+ self.folder = nil;
+ [self updateCollectionView];
+}
+
+#pragma mark - Parent class overrides that affect functionality
+
+- (void)collectionView:(UICollectionView*)collectionView
+ willDisplayCell:(UICollectionViewCell*)cell
+ forItemAtIndexPath:(NSIndexPath*)indexPath {
+ if (indexPath.section == self.itemsSection) {
+ [self loadFaviconAtIndexPath:indexPath];
+ }
+}
+
+- (void)didAddCellForEditingAtIndexPath:(NSIndexPath*)indexPath {
+ const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
+ UICollectionViewCell* cell =
+ [self.collectionView cellForItemAtIndexPath:indexPath];
+ [self.delegate bookmarkCollectionView:self cell:cell addNodeForEditing:node];
+}
+
+- (void)didRemoveCellForEditingAtIndexPath:(NSIndexPath*)indexPath {
+ const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
+ UICollectionViewCell* cell =
+ [self.collectionView cellForItemAtIndexPath:indexPath];
+ [self.delegate bookmarkCollectionView:self
+ cell:cell
+ removeNodeForEditing:node];
+}
+
+- (void)didTapCellAtIndexPath:(NSIndexPath*)indexPath {
+ if (indexPath.section == self.promoSection) {
+ // User tapped inside promo cell but not on one of the buttons. Ignore it.
+ return;
+ }
+
+ const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
+ DCHECK(node);
+
+ if (indexPath.section == self.folderSection) {
+ [self.delegate bookmarkFolderCollectionView:self
+ selectedFolderForNavigation:node];
+ } else {
+ RecordBookmarkLaunch(BOOKMARK_LAUNCH_LOCATION_FOLDER);
+ [self.delegate bookmarkCollectionView:self
+ selectedUrlForNavigation:node->url()];
+ }
+}
+
+- (void)didTapMenuButtonAtIndexPath:(NSIndexPath*)indexPath
+ onView:(UIView*)view
+ forCell:(BookmarkItemCell*)cell {
+ [self.delegate bookmarkCollectionView:self
+ wantsMenuForBookmark:[self nodeAtIndexPath:indexPath]
+ onView:view
+ forCell:cell];
+}
+
+- (bookmark_cell::ButtonType)buttonTypeForCellAtIndexPath:
+ (NSIndexPath*)indexPath {
+ return self.editing ? bookmark_cell::ButtonNone : bookmark_cell::ButtonMenu;
+}
+
+- (BOOL)allowLongPressForCellAtIndexPath:(NSIndexPath*)indexPath {
+ return !self.editing;
+}
+
+- (void)didLongPressCell:(UICollectionViewCell*)cell
+ atIndexPath:(NSIndexPath*)indexPath {
+ if (indexPath.section == self.promoSection) {
+ // User long-pressed inside promo cell. Ignore it.
+ return;
+ }
+
+ [self.delegate bookmarkCollectionView:self
+ didLongPressCell:cell
+ forBookmark:[self nodeAtIndexPath:indexPath]];
+}
+
+- (BOOL)shouldSelectCellAtIndexPath:(NSIndexPath*)indexPath {
+ return YES;
+}
+
+- (BOOL)cellIsSelectedForEditingAtIndexPath:(NSIndexPath*)indexPath {
+ const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
+ const std::set<const BookmarkNode*>& editingNodes =
+ [self.delegate nodesBeingEdited];
+ return editingNodes.find(node) != editingNodes.end();
+}
+
+- (const BookmarkNode*)nodeAtIndexPath:(NSIndexPath*)indexPath {
+ if (indexPath.section == self.folderSection)
+ return _subFolders[indexPath.row];
+ if (indexPath.section == self.itemsSection)
+ return _subItems[indexPath.row];
+
+ NOTREACHED();
+ return nullptr;
+}
+
+- (void)resetFolder:(const BookmarkNode*)folder {
+ DCHECK(folder->is_folder());
+ self.folder = folder;
+ [self updateCollectionView];
+}
+
+- (CGFloat)interitemSpacingForSectionAtIndex:(NSInteger)section {
+ CGFloat interitemSpacing = 0;
+ SEL minimumInteritemSpacingSelector = @selector(collectionView:
+ layout:
+ minimumInteritemSpacingForSectionAtIndex:);
+ if ([self.collectionView.delegate
+ respondsToSelector:minimumInteritemSpacingSelector]) {
+ id collectionViewDelegate = static_cast<id>(self.collectionView.delegate);
+ interitemSpacing =
+ [collectionViewDelegate collectionView:self.collectionView
+ layout:self.collectionView
+ .collectionViewLayout
+ minimumInteritemSpacingForSectionAtIndex:section];
+ } else if ([self.collectionView.collectionViewLayout
+ isKindOfClass:[UICollectionViewFlowLayout class]]) {
+ UICollectionViewFlowLayout* flowLayout =
+ static_cast<UICollectionViewFlowLayout*>(
+ self.collectionView.collectionViewLayout);
+ interitemSpacing = flowLayout.minimumInteritemSpacing;
+ }
+ return interitemSpacing;
+}
+
+// Parent class override.
+- (UIEdgeInsets)insetForSectionAtIndex:(NSInteger)section {
+ UIEdgeInsets insets = [super insetForSectionAtIndex:section];
+ if (section == self.folderSection)
+ insets.bottom = [self interitemSpacingForSectionAtIndex:section] / 2.;
+ else if (section == self.itemsSection)
+ insets.top = [self interitemSpacingForSectionAtIndex:section] / 2.;
+ else if (section == self.promoSection)
+ (void)0; // No insets to update.
+ else
+ NOTREACHED();
+ return insets;
+}
+
+// Parent class override.
+- (UICollectionViewCell*)cellAtIndexPath:(NSIndexPath*)indexPath {
+ if (indexPath.section == self.promoSection) {
+ self.promoCell = [self.collectionView
+ dequeueReusableCellWithReuseIdentifier:[BookmarkPromoCell
+ reuseIdentifier]
+ forIndexPath:indexPath];
+ self.promoCell.delegate = self;
+ return self.promoCell;
+ }
+ const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
+
+ if (indexPath.section == self.folderSection)
+ return [self cellForFolder:node indexPath:indexPath];
+
+ BookmarkItemCell* cell = [self cellForBookmark:node indexPath:indexPath];
+ return cell;
+}
+
+- (BOOL)needsSectionHeaderForSection:(NSInteger)section {
+ // Only show header when there is at least one element in the previous
+ // section.
+ if (section == 0)
+ return NO;
+
+ if ([self numberOfItemsInSection:(section - 1)] == 0)
+ return NO;
+
+ return YES;
+}
+
+// Parent class override.
+- (CGSize)headerSizeForSection:(NSInteger)section {
+ if ([self needsSectionHeaderForSection:section])
+ return CGSizeMake(self.bounds.size.width,
+ [BookmarkHeaderSeparatorView preferredHeight]);
+
+ return CGSizeZero;
+}
+
+// Parent class override.
+- (UICollectionReusableView*)headerAtIndexPath:(NSIndexPath*)indexPath {
+ if (![self needsSectionHeaderForSection:indexPath.section])
+ return nil;
+
+ BookmarkHeaderSeparatorView* view = [self.collectionView
+ dequeueReusableSupplementaryViewOfKind:
+ UICollectionElementKindSectionHeader
+ withReuseIdentifier:[BookmarkHeaderSeparatorView
+ reuseIdentifier]
+ forIndexPath:indexPath];
+ view.backgroundColor = [UIColor colorWithWhite:1 alpha:1];
+ return view;
+}
+
+- (NSInteger)numberOfItemsInSection:(NSInteger)section {
+ if (section == self.folderSection)
+ return _subFolders.size();
+ if (section == self.itemsSection)
+ return _subItems.size();
+ if (section == self.promoSection)
+ return 1;
+
+ NOTREACHED();
+ return -1;
+}
+
+- (NSInteger)numberOfSections {
+ return self.sectionCount;
+}
+
+- (void)collectionViewScrolled {
+ [self.delegate bookmarkCollectionViewDidScroll:self];
+}
+
+- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
+ [super setEditing:editing animated:animated];
+ [self promoStateChangedAnimated:animated];
+}
+
+- (void)promoStateChangedAnimated:(BOOL)animate {
+ if (experimental_flags::IsAllBookmarksEnabled())
+ return; // The promo is not shown if All Bookmarks is enabled.
+
+ BOOL newPromoState =
+ !self.editing && self.folder &&
+ self.folder->type() == BookmarkNode::MOBILE &&
+ [self.delegate bookmarkCollectionViewShouldShowPromoCell:self];
+ if (newPromoState != _promoVisible) {
+ // This is awful, but until the old code to do the refresh when switching
+ // in and out of edit mode is fixed, this is probably the cleanest thing to
+ // do.
+ _promoVisible = newPromoState;
+ [self.collectionView reloadData];
+ }
+}
+
+#pragma mark - BookmarkPromoCellDelegate
+
+- (void)bookmarkPromoCellDidTapSignIn:(BookmarkPromoCell*)bookmarkPromoCell {
+ [self.delegate bookmarkCollectionViewShowSignIn:self];
+}
+
+- (void)bookmarkPromoCellDidTapDismiss:(BookmarkPromoCell*)bookmarkPromoCell {
+ [self.delegate bookmarkCollectionViewDismissPromo:self];
+}
+
+#pragma mark - Promo Cell
+
+- (BOOL)shouldShowPromoCell {
+ return _promoVisible;
+}
+
+@end

Powered by Google App Engine
This is Rietveld 408576698