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

Side by Side 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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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/bookmarks/bookmark_folder_collection_view.h"
6
7 #include "base/logging.h"
8 #include "base/mac/objc_property_releaser.h"
9 #include "base/strings/sys_string_conversions.h"
10 #include "components/bookmarks/browser/bookmark_model.h"
11 #include "ios/chrome/browser/bookmarks/bookmarks_utils.h"
12 #include "ios/chrome/browser/experimental_flags.h"
13 #import "ios/chrome/browser/ui/bookmarks/bookmark_collection_cells.h"
14 #import "ios/chrome/browser/ui/bookmarks/bookmark_promo_cell.h"
15 #import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
16
17 using bookmarks::BookmarkNode;
18
19 @interface BookmarkFolderCollectionView ()<BookmarkPromoCellDelegate> {
20 base::mac::ObjCPropertyReleaser
21 _propertyReleaser_BookmarkFolderCollectionView;
22 // A vector of folders to display in the collection view.
23 std::vector<const BookmarkNode*> _subFolders;
24 // A vector of bookmark urls to display in the collection view.
25 std::vector<const BookmarkNode*> _subItems;
26
27 // True if the promo is visible.
28 BOOL _promoVisible;
29 }
30 @property(nonatomic, assign) const bookmarks::BookmarkNode* folder;
31
32 // Section indices.
33 @property(nonatomic, readonly, assign) NSInteger promoSection;
34 @property(nonatomic, readonly, assign) NSInteger folderSection;
35 @property(nonatomic, readonly, assign) NSInteger itemsSection;
36 @property(nonatomic, readonly, assign) NSInteger sectionCount;
37
38 // Keep a reference to the promo cell to deregister as delegate.
39 @property(nonatomic, retain) BookmarkPromoCell* promoCell;
40
41 @end
42
43 @implementation BookmarkFolderCollectionView
44 @synthesize delegate = _delegate;
45 @synthesize folder = _folder;
46 @synthesize promoCell = _promoCell;
47
48 - (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
49 frame:(CGRect)frame {
50 self = [super initWithBrowserState:browserState frame:frame];
51 if (self) {
52 _propertyReleaser_BookmarkFolderCollectionView.Init(
53 self, [BookmarkFolderCollectionView class]);
54
55 [self updateCollectionView];
56 }
57 return self;
58 }
59
60 - (void)dealloc {
61 _promoCell.delegate = nil;
62 [super dealloc];
63 }
64
65 - (void)setDelegate:(id<BookmarkFolderCollectionViewDelegate>)delegate {
66 _delegate = delegate;
67 [self promoStateChangedAnimated:NO];
68 }
69
70 - (NSInteger)promoSection {
71 return [self shouldShowPromoCell] ? 0 : -1;
72 }
73
74 - (NSInteger)folderSection {
75 return [self shouldShowPromoCell] ? 1 : 0;
76 }
77
78 - (NSInteger)itemsSection {
79 return [self shouldShowPromoCell] ? 2 : 1;
80 }
81
82 - (NSInteger)sectionCount {
83 return [self shouldShowPromoCell] ? 3 : 2;
84 }
85
86 - (void)updateCollectionView {
87 if (!self.bookmarkModel->loaded())
88 return;
89
90 // Regenerate the list of all bookmarks.
91 _subFolders = std::vector<const BookmarkNode*>();
92 _subItems = std::vector<const BookmarkNode*>();
93
94 if (self.folder) {
95 int childCount = self.folder->child_count();
96 for (int i = 0; i < childCount; ++i) {
97 const BookmarkNode* node = self.folder->GetChild(i);
98 if (node->is_folder())
99 _subFolders.push_back(node);
100 else
101 _subItems.push_back(node);
102 }
103
104 bookmark_utils_ios::SortFolders(&_subFolders);
105 }
106
107 [self cancelAllFaviconLoads];
108 [self.collectionView reloadData];
109 }
110
111 #pragma mark - BookmarkModelBridgeObserver Callbacks
112
113 - (void)bookmarkModelLoaded {
114 [self updateCollectionView];
115 }
116
117 - (void)bookmarkNodeChanged:(const BookmarkNode*)bookmarkNode {
118 // The base folder changed. Do nothing.
119 if (bookmarkNode == self.folder)
120 return;
121
122 // A specific cell changed. Reload that cell.
123 NSIndexPath* indexPath = [self indexPathForNode:bookmarkNode];
124
125 if (indexPath) {
126 // TODO(crbug.com/603661): Ideally, we would only reload the relevant index
127 // path. However, calling reloadItemsAtIndexPaths:(0,0) immediately after
128 // reloadData results in a exception: NSInternalInconsistencyException
129 // 'request for index path for global index 2147483645 ...'
130 // One solution would be to keep track of whether we've just called
131 // reloadData, but that requires experimentation to determine how long we
132 // have to wait before we can safely call reloadItemsAtIndexPaths.
133 [self updateCollectionView];
134 }
135 }
136
137 - (void)bookmarkNodeFaviconChanged:
138 (const bookmarks::BookmarkNode*)bookmarkNode {
139 // Only urls have favicons.
140 DCHECK(bookmarkNode->is_url());
141
142 // Update image of corresponding cell.
143 NSIndexPath* indexPath = [self indexPathForNode:bookmarkNode];
144
145 if (!indexPath)
146 return;
147
148 // Check that this cell is visible.
149 NSArray* visiblePaths = [self.collectionView indexPathsForVisibleItems];
150 if (![visiblePaths containsObject:indexPath])
151 return;
152
153 [self loadFaviconAtIndexPath:indexPath];
154 }
155
156 - (NSIndexPath*)indexPathForNode:(const bookmarks::BookmarkNode*)bookmarkNode {
157 NSIndexPath* indexPath = nil;
158 if (bookmarkNode->is_folder()) {
159 std::vector<const BookmarkNode*>::iterator it =
160 std::find(_subFolders.begin(), _subFolders.end(), bookmarkNode);
161 if (it != _subFolders.end()) {
162 ptrdiff_t index = std::distance(_subFolders.begin(), it);
163 indexPath =
164 [NSIndexPath indexPathForRow:index inSection:self.folderSection];
165 }
166 } else if (bookmarkNode->is_url()) {
167 std::vector<const BookmarkNode*>::iterator it =
168 std::find(_subItems.begin(), _subItems.end(), bookmarkNode);
169 if (it != _subItems.end()) {
170 ptrdiff_t index = std::distance(_subItems.begin(), it);
171 indexPath =
172 [NSIndexPath indexPathForRow:index inSection:self.itemsSection];
173 }
174 }
175 return indexPath;
176 }
177
178 - (void)bookmarkNodeDeleted:(const BookmarkNode*)node
179 fromFolder:(const BookmarkNode*)folder {
180 if (self.folder == node) {
181 self.folder = nil;
182 [self updateCollectionView];
183 }
184 }
185
186 - (void)bookmarkNodeChildrenChanged:(const BookmarkNode*)bookmarkNode {
187 // The base folder's children changed. Reload everything.
188 if (bookmarkNode == self.folder) {
189 [self updateCollectionView];
190 return;
191 }
192
193 // A subfolder's children changed. Reload that cell.
194 std::vector<const BookmarkNode*>::iterator it =
195 std::find(_subFolders.begin(), _subFolders.end(), bookmarkNode);
196 if (it != _subFolders.end()) {
197 // TODO(crbug.com/603661): Ideally, we would only reload the relevant index
198 // path. However, calling reloadItemsAtIndexPaths:(0,0) immediately after
199 // reloadData results in a exception: NSInternalInconsistencyException
200 // 'request for index path for global index 2147483645 ...'
201 // One solution would be to keep track of whether we've just called
202 // reloadData, but that requires experimentation to determine how long we
203 // have to wait before we can safely call reloadItemsAtIndexPaths.
204 [self updateCollectionView];
205 }
206 }
207
208 - (void)bookmarkNode:(const BookmarkNode*)bookmarkNode
209 movedFromParent:(const BookmarkNode*)oldParent
210 toParent:(const BookmarkNode*)newParent {
211 if (oldParent == self.folder || newParent == self.folder) {
212 // A folder was added or removed from the base folder.
213 [self updateCollectionView];
214 }
215 }
216
217 - (void)bookmarkModelRemovedAllNodes {
218 self.folder = nil;
219 [self updateCollectionView];
220 }
221
222 #pragma mark - Parent class overrides that affect functionality
223
224 - (void)collectionView:(UICollectionView*)collectionView
225 willDisplayCell:(UICollectionViewCell*)cell
226 forItemAtIndexPath:(NSIndexPath*)indexPath {
227 if (indexPath.section == self.itemsSection) {
228 [self loadFaviconAtIndexPath:indexPath];
229 }
230 }
231
232 - (void)didAddCellForEditingAtIndexPath:(NSIndexPath*)indexPath {
233 const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
234 UICollectionViewCell* cell =
235 [self.collectionView cellForItemAtIndexPath:indexPath];
236 [self.delegate bookmarkCollectionView:self cell:cell addNodeForEditing:node];
237 }
238
239 - (void)didRemoveCellForEditingAtIndexPath:(NSIndexPath*)indexPath {
240 const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
241 UICollectionViewCell* cell =
242 [self.collectionView cellForItemAtIndexPath:indexPath];
243 [self.delegate bookmarkCollectionView:self
244 cell:cell
245 removeNodeForEditing:node];
246 }
247
248 - (void)didTapCellAtIndexPath:(NSIndexPath*)indexPath {
249 if (indexPath.section == self.promoSection) {
250 // User tapped inside promo cell but not on one of the buttons. Ignore it.
251 return;
252 }
253
254 const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
255 DCHECK(node);
256
257 if (indexPath.section == self.folderSection) {
258 [self.delegate bookmarkFolderCollectionView:self
259 selectedFolderForNavigation:node];
260 } else {
261 RecordBookmarkLaunch(BOOKMARK_LAUNCH_LOCATION_FOLDER);
262 [self.delegate bookmarkCollectionView:self
263 selectedUrlForNavigation:node->url()];
264 }
265 }
266
267 - (void)didTapMenuButtonAtIndexPath:(NSIndexPath*)indexPath
268 onView:(UIView*)view
269 forCell:(BookmarkItemCell*)cell {
270 [self.delegate bookmarkCollectionView:self
271 wantsMenuForBookmark:[self nodeAtIndexPath:indexPath]
272 onView:view
273 forCell:cell];
274 }
275
276 - (bookmark_cell::ButtonType)buttonTypeForCellAtIndexPath:
277 (NSIndexPath*)indexPath {
278 return self.editing ? bookmark_cell::ButtonNone : bookmark_cell::ButtonMenu;
279 }
280
281 - (BOOL)allowLongPressForCellAtIndexPath:(NSIndexPath*)indexPath {
282 return !self.editing;
283 }
284
285 - (void)didLongPressCell:(UICollectionViewCell*)cell
286 atIndexPath:(NSIndexPath*)indexPath {
287 if (indexPath.section == self.promoSection) {
288 // User long-pressed inside promo cell. Ignore it.
289 return;
290 }
291
292 [self.delegate bookmarkCollectionView:self
293 didLongPressCell:cell
294 forBookmark:[self nodeAtIndexPath:indexPath]];
295 }
296
297 - (BOOL)shouldSelectCellAtIndexPath:(NSIndexPath*)indexPath {
298 return YES;
299 }
300
301 - (BOOL)cellIsSelectedForEditingAtIndexPath:(NSIndexPath*)indexPath {
302 const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
303 const std::set<const BookmarkNode*>& editingNodes =
304 [self.delegate nodesBeingEdited];
305 return editingNodes.find(node) != editingNodes.end();
306 }
307
308 - (const BookmarkNode*)nodeAtIndexPath:(NSIndexPath*)indexPath {
309 if (indexPath.section == self.folderSection)
310 return _subFolders[indexPath.row];
311 if (indexPath.section == self.itemsSection)
312 return _subItems[indexPath.row];
313
314 NOTREACHED();
315 return nullptr;
316 }
317
318 - (void)resetFolder:(const BookmarkNode*)folder {
319 DCHECK(folder->is_folder());
320 self.folder = folder;
321 [self updateCollectionView];
322 }
323
324 - (CGFloat)interitemSpacingForSectionAtIndex:(NSInteger)section {
325 CGFloat interitemSpacing = 0;
326 SEL minimumInteritemSpacingSelector = @selector(collectionView:
327 layout:
328 minimumInteritemSpacingForSectionAtIndex:);
329 if ([self.collectionView.delegate
330 respondsToSelector:minimumInteritemSpacingSelector]) {
331 id collectionViewDelegate = static_cast<id>(self.collectionView.delegate);
332 interitemSpacing =
333 [collectionViewDelegate collectionView:self.collectionView
334 layout:self.collectionView
335 .collectionViewLayout
336 minimumInteritemSpacingForSectionAtIndex:section];
337 } else if ([self.collectionView.collectionViewLayout
338 isKindOfClass:[UICollectionViewFlowLayout class]]) {
339 UICollectionViewFlowLayout* flowLayout =
340 static_cast<UICollectionViewFlowLayout*>(
341 self.collectionView.collectionViewLayout);
342 interitemSpacing = flowLayout.minimumInteritemSpacing;
343 }
344 return interitemSpacing;
345 }
346
347 // Parent class override.
348 - (UIEdgeInsets)insetForSectionAtIndex:(NSInteger)section {
349 UIEdgeInsets insets = [super insetForSectionAtIndex:section];
350 if (section == self.folderSection)
351 insets.bottom = [self interitemSpacingForSectionAtIndex:section] / 2.;
352 else if (section == self.itemsSection)
353 insets.top = [self interitemSpacingForSectionAtIndex:section] / 2.;
354 else if (section == self.promoSection)
355 (void)0; // No insets to update.
356 else
357 NOTREACHED();
358 return insets;
359 }
360
361 // Parent class override.
362 - (UICollectionViewCell*)cellAtIndexPath:(NSIndexPath*)indexPath {
363 if (indexPath.section == self.promoSection) {
364 self.promoCell = [self.collectionView
365 dequeueReusableCellWithReuseIdentifier:[BookmarkPromoCell
366 reuseIdentifier]
367 forIndexPath:indexPath];
368 self.promoCell.delegate = self;
369 return self.promoCell;
370 }
371 const BookmarkNode* node = [self nodeAtIndexPath:indexPath];
372
373 if (indexPath.section == self.folderSection)
374 return [self cellForFolder:node indexPath:indexPath];
375
376 BookmarkItemCell* cell = [self cellForBookmark:node indexPath:indexPath];
377 return cell;
378 }
379
380 - (BOOL)needsSectionHeaderForSection:(NSInteger)section {
381 // Only show header when there is at least one element in the previous
382 // section.
383 if (section == 0)
384 return NO;
385
386 if ([self numberOfItemsInSection:(section - 1)] == 0)
387 return NO;
388
389 return YES;
390 }
391
392 // Parent class override.
393 - (CGSize)headerSizeForSection:(NSInteger)section {
394 if ([self needsSectionHeaderForSection:section])
395 return CGSizeMake(self.bounds.size.width,
396 [BookmarkHeaderSeparatorView preferredHeight]);
397
398 return CGSizeZero;
399 }
400
401 // Parent class override.
402 - (UICollectionReusableView*)headerAtIndexPath:(NSIndexPath*)indexPath {
403 if (![self needsSectionHeaderForSection:indexPath.section])
404 return nil;
405
406 BookmarkHeaderSeparatorView* view = [self.collectionView
407 dequeueReusableSupplementaryViewOfKind:
408 UICollectionElementKindSectionHeader
409 withReuseIdentifier:[BookmarkHeaderSeparatorView
410 reuseIdentifier]
411 forIndexPath:indexPath];
412 view.backgroundColor = [UIColor colorWithWhite:1 alpha:1];
413 return view;
414 }
415
416 - (NSInteger)numberOfItemsInSection:(NSInteger)section {
417 if (section == self.folderSection)
418 return _subFolders.size();
419 if (section == self.itemsSection)
420 return _subItems.size();
421 if (section == self.promoSection)
422 return 1;
423
424 NOTREACHED();
425 return -1;
426 }
427
428 - (NSInteger)numberOfSections {
429 return self.sectionCount;
430 }
431
432 - (void)collectionViewScrolled {
433 [self.delegate bookmarkCollectionViewDidScroll:self];
434 }
435
436 - (void)setEditing:(BOOL)editing animated:(BOOL)animated {
437 [super setEditing:editing animated:animated];
438 [self promoStateChangedAnimated:animated];
439 }
440
441 - (void)promoStateChangedAnimated:(BOOL)animate {
442 if (experimental_flags::IsAllBookmarksEnabled())
443 return; // The promo is not shown if All Bookmarks is enabled.
444
445 BOOL newPromoState =
446 !self.editing && self.folder &&
447 self.folder->type() == BookmarkNode::MOBILE &&
448 [self.delegate bookmarkCollectionViewShouldShowPromoCell:self];
449 if (newPromoState != _promoVisible) {
450 // This is awful, but until the old code to do the refresh when switching
451 // in and out of edit mode is fixed, this is probably the cleanest thing to
452 // do.
453 _promoVisible = newPromoState;
454 [self.collectionView reloadData];
455 }
456 }
457
458 #pragma mark - BookmarkPromoCellDelegate
459
460 - (void)bookmarkPromoCellDidTapSignIn:(BookmarkPromoCell*)bookmarkPromoCell {
461 [self.delegate bookmarkCollectionViewShowSignIn:self];
462 }
463
464 - (void)bookmarkPromoCellDidTapDismiss:(BookmarkPromoCell*)bookmarkPromoCell {
465 [self.delegate bookmarkCollectionViewDismissPromo:self];
466 }
467
468 #pragma mark - Promo Cell
469
470 - (BOOL)shouldShowPromoCell {
471 return _promoVisible;
472 }
473
474 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698