| Index: ios/chrome/browser/ui/collection_view/collection_view_model.mm
|
| diff --git a/ios/chrome/browser/ui/collection_view/collection_view_model.mm b/ios/chrome/browser/ui/collection_view/collection_view_model.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d7e388db372a767db11a49c018e409934f766a41
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/collection_view/collection_view_model.mm
|
| @@ -0,0 +1,325 @@
|
| +// Copyright 2016 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/collection_view/collection_view_model.h"
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
|
| +
|
| +namespace {
|
| +typedef NSMutableArray<CollectionViewItem*> SectionItems;
|
| +}
|
| +
|
| +@implementation CollectionViewModel {
|
| + // Ordered list of section identifiers, one per section in the model.
|
| + base::scoped_nsobject<NSMutableArray<NSNumber*>> _sectionIdentifiers;
|
| +
|
| + // The lists of section items, one per section.
|
| + base::scoped_nsobject<NSMutableArray<SectionItems*>> _sections;
|
| +
|
| + // Maps from section identifier to header and footer.
|
| + base::scoped_nsobject<NSMutableDictionary<NSNumber*, CollectionViewItem*>>
|
| + _headers;
|
| + base::scoped_nsobject<NSMutableDictionary<NSNumber*, CollectionViewItem*>>
|
| + _footers;
|
| +}
|
| +
|
| +- (instancetype)init {
|
| + if ((self = [super init])) {
|
| + _sectionIdentifiers.reset([[NSMutableArray alloc] init]);
|
| + _sections.reset([[NSMutableArray alloc] init]);
|
| + _headers.reset([[NSMutableDictionary alloc] init]);
|
| + _footers.reset([[NSMutableDictionary alloc] init]);
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +#pragma mark Modification methods
|
| +
|
| +- (void)addSectionWithIdentifier:(NSInteger)sectionIdentifier {
|
| + DCHECK_GE(sectionIdentifier, kSectionIdentifierEnumZero);
|
| + DCHECK_EQ(static_cast<NSUInteger>(NSNotFound),
|
| + [self internalSectionForIdentifier:sectionIdentifier]);
|
| + [_sectionIdentifiers addObject:@(sectionIdentifier)];
|
| +
|
| + base::scoped_nsobject<SectionItems> section([[SectionItems alloc] init]);
|
| + [_sections addObject:section];
|
| +}
|
| +
|
| +- (void)insertSectionWithIdentifier:(NSInteger)sectionIdentifier
|
| + atIndex:(NSUInteger)index {
|
| + DCHECK_GE(sectionIdentifier, kSectionIdentifierEnumZero);
|
| + DCHECK_EQ(static_cast<NSUInteger>(NSNotFound),
|
| + [self internalSectionForIdentifier:sectionIdentifier]);
|
| + DCHECK_LE(index, [_sections count]);
|
| +
|
| + [_sectionIdentifiers insertObject:@(sectionIdentifier) atIndex:index];
|
| +
|
| + base::scoped_nsobject<SectionItems> section([[SectionItems alloc] init]);
|
| + [_sections insertObject:section atIndex:index];
|
| +}
|
| +
|
| +- (void)addItem:(CollectionViewItem*)item
|
| + toSectionWithIdentifier:(NSInteger)sectionIdentifier {
|
| + DCHECK_GE(item.type, kItemTypeEnumZero);
|
| + NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier];
|
| + SectionItems* items = [_sections objectAtIndex:section];
|
| + [items addObject:item];
|
| +}
|
| +
|
| +- (void)insertItem:(CollectionViewItem*)item
|
| + inSectionWithIdentifier:(NSInteger)sectionIdentifier
|
| + atIndex:(NSUInteger)index {
|
| + DCHECK_GE(item.type, kItemTypeEnumZero);
|
| + NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier];
|
| + SectionItems* items = [_sections objectAtIndex:section];
|
| + DCHECK(index <= [items count]);
|
| + [items insertObject:item atIndex:index];
|
| +}
|
| +
|
| +- (void)removeItemWithType:(NSInteger)itemType
|
| + fromSectionWithIdentifier:(NSInteger)sectionIdentifier {
|
| + [self removeItemWithType:itemType
|
| + fromSectionWithIdentifier:sectionIdentifier
|
| + atIndex:0];
|
| +}
|
| +
|
| +- (void)removeItemWithType:(NSInteger)itemType
|
| + fromSectionWithIdentifier:(NSInteger)sectionIdentifier
|
| + atIndex:(NSUInteger)index {
|
| + NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier];
|
| + SectionItems* items = [_sections objectAtIndex:section];
|
| + NSInteger item =
|
| + [self itemForItemType:itemType inSectionItems:items atIndex:index];
|
| + DCHECK_NE(NSNotFound, item);
|
| + [items removeObjectAtIndex:item];
|
| +}
|
| +
|
| +- (void)removeSectionWithIdentifier:(NSInteger)sectionIdentifier {
|
| + NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier];
|
| + [_sectionIdentifiers removeObjectAtIndex:section];
|
| + [_sections removeObjectAtIndex:section];
|
| +}
|
| +
|
| +- (void)setHeader:(CollectionViewItem*)header
|
| + forSectionWithIdentifier:(NSInteger)sectionIdentifier {
|
| + NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier];
|
| + if (header) {
|
| + [_headers setObject:header forKey:key];
|
| + } else {
|
| + [_headers removeObjectForKey:key];
|
| + }
|
| +}
|
| +
|
| +- (void)setFooter:(CollectionViewItem*)footer
|
| + forSectionWithIdentifier:(NSInteger)sectionIdentifier {
|
| + NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier];
|
| + if (footer) {
|
| + [_footers setObject:footer forKey:key];
|
| + } else {
|
| + [_footers removeObjectForKey:key];
|
| + }
|
| +}
|
| +
|
| +#pragma mark Query model coordinates from index paths
|
| +
|
| +- (NSInteger)sectionIdentifierForSection:(NSInteger)section {
|
| + DCHECK_LT(static_cast<NSUInteger>(section), [_sectionIdentifiers count]);
|
| + return [[_sectionIdentifiers objectAtIndex:section] integerValue];
|
| +}
|
| +
|
| +- (NSInteger)itemTypeForIndexPath:(NSIndexPath*)indexPath {
|
| + return [self itemAtIndexPath:indexPath].type;
|
| +}
|
| +
|
| +- (NSUInteger)indexInItemTypeForIndexPath:(NSIndexPath*)indexPath {
|
| + DCHECK_LT(static_cast<NSUInteger>(indexPath.section), [_sections count]);
|
| + SectionItems* items = [_sections objectAtIndex:indexPath.section];
|
| +
|
| + CollectionViewItem* item = [self itemAtIndexPath:indexPath];
|
| + NSUInteger indexInItemType =
|
| + [self indexInItemTypeForItem:item inSectionItems:items];
|
| + return indexInItemType;
|
| +}
|
| +
|
| +#pragma mark Query items from index paths
|
| +
|
| +- (BOOL)hasItemAtIndexPath:(NSIndexPath*)indexPath {
|
| + if (static_cast<NSUInteger>(indexPath.section) < [_sections count]) {
|
| + SectionItems* items = [_sections objectAtIndex:indexPath.section];
|
| + return static_cast<NSUInteger>(indexPath.item) < [items count];
|
| + }
|
| + return NO;
|
| +}
|
| +
|
| +- (CollectionViewItem*)itemAtIndexPath:(NSIndexPath*)indexPath {
|
| + DCHECK_LT(static_cast<NSUInteger>(indexPath.section), [_sections count]);
|
| + SectionItems* items = [_sections objectAtIndex:indexPath.section];
|
| +
|
| + DCHECK_LT(static_cast<NSUInteger>(indexPath.item), [items count]);
|
| + return [items objectAtIndex:indexPath.item];
|
| +}
|
| +
|
| +- (CollectionViewItem*)headerForSection:(NSInteger)section {
|
| + NSInteger sectionIdentifier = [self sectionIdentifierForSection:section];
|
| + NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier];
|
| + return [_headers objectForKey:key];
|
| +}
|
| +
|
| +- (CollectionViewItem*)footerForSection:(NSInteger)section {
|
| + NSInteger sectionIdentifier = [self sectionIdentifierForSection:section];
|
| + NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier];
|
| + return [_footers objectForKey:key];
|
| +}
|
| +
|
| +- (NSArray*)itemsInSectionWithIdentifier:(NSInteger)sectionIdentifier {
|
| + NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier];
|
| + DCHECK_LT(static_cast<NSUInteger>(section), [_sections count]);
|
| + return [_sections objectAtIndex:section];
|
| +}
|
| +
|
| +- (CollectionViewItem*)headerForSectionWithIdentifier:
|
| + (NSInteger)sectionIdentifier {
|
| + NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier];
|
| + return [_headers objectForKey:key];
|
| +}
|
| +
|
| +- (CollectionViewItem*)footerForSectionWithIdentifier:
|
| + (NSInteger)sectionIdentifier {
|
| + NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier];
|
| + return [_footers objectForKey:key];
|
| +}
|
| +
|
| +#pragma mark Query index paths from model coordinates
|
| +
|
| +- (BOOL)hasSectionForSectionIdentifier:(NSInteger)sectionIdentifier {
|
| + NSUInteger section = [self internalSectionForIdentifier:sectionIdentifier];
|
| + return section != static_cast<NSUInteger>(NSNotFound);
|
| +}
|
| +
|
| +- (NSInteger)sectionForSectionIdentifier:(NSInteger)sectionIdentifier {
|
| + NSUInteger section = [self internalSectionForIdentifier:sectionIdentifier];
|
| + DCHECK_NE(static_cast<NSUInteger>(NSNotFound), section);
|
| + return section;
|
| +}
|
| +
|
| +- (BOOL)hasItemForItemType:(NSInteger)itemType
|
| + sectionIdentifier:(NSInteger)sectionIdentifier {
|
| + return [self hasItemForItemType:itemType
|
| + sectionIdentifier:sectionIdentifier
|
| + atIndex:0];
|
| +}
|
| +
|
| +- (NSIndexPath*)indexPathForItemType:(NSInteger)itemType
|
| + sectionIdentifier:(NSInteger)sectionIdentifier {
|
| + return [self indexPathForItemType:itemType
|
| + sectionIdentifier:sectionIdentifier
|
| + atIndex:0];
|
| +}
|
| +
|
| +- (BOOL)hasItemForItemType:(NSInteger)itemType
|
| + sectionIdentifier:(NSInteger)sectionIdentifier
|
| + atIndex:(NSUInteger)index {
|
| + if (![self hasSectionForSectionIdentifier:sectionIdentifier]) {
|
| + return NO;
|
| + }
|
| + NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier];
|
| + SectionItems* items = [_sections objectAtIndex:section];
|
| + NSInteger item =
|
| + [self itemForItemType:itemType inSectionItems:items atIndex:index];
|
| + return item != NSNotFound;
|
| +}
|
| +
|
| +- (NSIndexPath*)indexPathForItemType:(NSInteger)itemType
|
| + sectionIdentifier:(NSInteger)sectionIdentifier
|
| + atIndex:(NSUInteger)index {
|
| + NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier];
|
| + SectionItems* items = [_sections objectAtIndex:section];
|
| + NSInteger item =
|
| + [self itemForItemType:itemType inSectionItems:items atIndex:index];
|
| + return [NSIndexPath indexPathForItem:item inSection:section];
|
| +}
|
| +
|
| +#pragma mark Query index paths from items
|
| +
|
| +- (BOOL)hasItem:(CollectionViewItem*)item
|
| + inSectionWithIdentifier:(NSInteger)sectionIdentifier {
|
| + return [[self itemsInSectionWithIdentifier:sectionIdentifier]
|
| + indexOfObject:item] != NSNotFound;
|
| +}
|
| +
|
| +- (NSIndexPath*)indexPathForItem:(CollectionViewItem*)item
|
| + inSectionWithIdentifier:(NSInteger)sectionIdentifier {
|
| + NSArray* itemsInSection =
|
| + [self itemsInSectionWithIdentifier:sectionIdentifier];
|
| +
|
| + NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier];
|
| + NSInteger itemIndex = [itemsInSection indexOfObject:item];
|
| + DCHECK_NE(NSNotFound, itemIndex);
|
| + return [NSIndexPath indexPathForItem:itemIndex inSection:section];
|
| +}
|
| +
|
| +#pragma mark UICollectionView data sourcing
|
| +
|
| +- (NSInteger)numberOfSections {
|
| + return [_sections count];
|
| +}
|
| +
|
| +- (NSInteger)numberOfItemsInSection:(NSInteger)section {
|
| + DCHECK_LT(static_cast<NSUInteger>(section), [_sections count]);
|
| + SectionItems* items = [_sections objectAtIndex:section];
|
| + return items.count;
|
| +}
|
| +
|
| +#pragma mark Private methods
|
| +
|
| +// Returns the section for the given section identifier. If the section
|
| +// identifier is not found, NSNotFound is returned.
|
| +- (NSUInteger)internalSectionForIdentifier:(NSInteger)sectionIdentifier {
|
| + return [_sectionIdentifiers indexOfObject:@(sectionIdentifier)];
|
| +}
|
| +
|
| +// Returns the item for the given item type in the list of items, at the
|
| +// given index. If no item is found with the given type, NSNotFound is returned.
|
| +- (NSUInteger)itemForItemType:(NSInteger)itemType
|
| + inSectionItems:(SectionItems*)sectionItems
|
| + atIndex:(NSUInteger)index {
|
| + __block NSUInteger item = NSNotFound;
|
| + __block NSUInteger indexInItemType = 0;
|
| + [sectionItems enumerateObjectsUsingBlock:^(CollectionViewItem* obj,
|
| + NSUInteger idx, BOOL* stop) {
|
| + if (obj.type == itemType) {
|
| + if (indexInItemType == index) {
|
| + item = idx;
|
| + *stop = YES;
|
| + } else {
|
| + indexInItemType++;
|
| + }
|
| + }
|
| + }];
|
| + return item;
|
| +}
|
| +
|
| +// Returns |item|'s index among all the items of the same type in the given
|
| +// section items. |item| must belong to |sectionItems|.
|
| +- (NSUInteger)indexInItemTypeForItem:(CollectionViewItem*)item
|
| + inSectionItems:(SectionItems*)sectionItems {
|
| + DCHECK([sectionItems containsObject:item]);
|
| + BOOL found = NO;
|
| + NSUInteger indexInItemType = 0;
|
| + for (CollectionViewItem* sectionItem in sectionItems) {
|
| + if (sectionItem == item) {
|
| + found = YES;
|
| + break;
|
| + }
|
| + if (sectionItem.type == item.type) {
|
| + indexInItemType++;
|
| + }
|
| + }
|
| + DCHECK(found);
|
| + return indexInItemType;
|
| +}
|
| +
|
| +@end
|
|
|