OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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/collection_view/collection_view_model.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #include "base/mac/scoped_nsobject.h" |
| 9 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h" |
| 10 |
| 11 namespace { |
| 12 typedef NSMutableArray<CollectionViewItem*> SectionItems; |
| 13 } |
| 14 |
| 15 @implementation CollectionViewModel { |
| 16 // Ordered list of section identifiers, one per section in the model. |
| 17 base::scoped_nsobject<NSMutableArray<NSNumber*>> _sectionIdentifiers; |
| 18 |
| 19 // The lists of section items, one per section. |
| 20 base::scoped_nsobject<NSMutableArray<SectionItems*>> _sections; |
| 21 |
| 22 // Maps from section identifier to header and footer. |
| 23 base::scoped_nsobject<NSMutableDictionary<NSNumber*, CollectionViewItem*>> |
| 24 _headers; |
| 25 base::scoped_nsobject<NSMutableDictionary<NSNumber*, CollectionViewItem*>> |
| 26 _footers; |
| 27 } |
| 28 |
| 29 - (instancetype)init { |
| 30 if ((self = [super init])) { |
| 31 _sectionIdentifiers.reset([[NSMutableArray alloc] init]); |
| 32 _sections.reset([[NSMutableArray alloc] init]); |
| 33 _headers.reset([[NSMutableDictionary alloc] init]); |
| 34 _footers.reset([[NSMutableDictionary alloc] init]); |
| 35 } |
| 36 return self; |
| 37 } |
| 38 |
| 39 #pragma mark Modification methods |
| 40 |
| 41 - (void)addSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 42 DCHECK_GE(sectionIdentifier, kSectionIdentifierEnumZero); |
| 43 DCHECK_EQ(static_cast<NSUInteger>(NSNotFound), |
| 44 [self internalSectionForIdentifier:sectionIdentifier]); |
| 45 [_sectionIdentifiers addObject:@(sectionIdentifier)]; |
| 46 |
| 47 base::scoped_nsobject<SectionItems> section([[SectionItems alloc] init]); |
| 48 [_sections addObject:section]; |
| 49 } |
| 50 |
| 51 - (void)insertSectionWithIdentifier:(NSInteger)sectionIdentifier |
| 52 atIndex:(NSUInteger)index { |
| 53 DCHECK_GE(sectionIdentifier, kSectionIdentifierEnumZero); |
| 54 DCHECK_EQ(static_cast<NSUInteger>(NSNotFound), |
| 55 [self internalSectionForIdentifier:sectionIdentifier]); |
| 56 DCHECK_LE(index, [_sections count]); |
| 57 |
| 58 [_sectionIdentifiers insertObject:@(sectionIdentifier) atIndex:index]; |
| 59 |
| 60 base::scoped_nsobject<SectionItems> section([[SectionItems alloc] init]); |
| 61 [_sections insertObject:section atIndex:index]; |
| 62 } |
| 63 |
| 64 - (void)addItem:(CollectionViewItem*)item |
| 65 toSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 66 DCHECK_GE(item.type, kItemTypeEnumZero); |
| 67 NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier]; |
| 68 SectionItems* items = [_sections objectAtIndex:section]; |
| 69 [items addObject:item]; |
| 70 } |
| 71 |
| 72 - (void)insertItem:(CollectionViewItem*)item |
| 73 inSectionWithIdentifier:(NSInteger)sectionIdentifier |
| 74 atIndex:(NSUInteger)index { |
| 75 DCHECK_GE(item.type, kItemTypeEnumZero); |
| 76 NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier]; |
| 77 SectionItems* items = [_sections objectAtIndex:section]; |
| 78 DCHECK(index <= [items count]); |
| 79 [items insertObject:item atIndex:index]; |
| 80 } |
| 81 |
| 82 - (void)removeItemWithType:(NSInteger)itemType |
| 83 fromSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 84 [self removeItemWithType:itemType |
| 85 fromSectionWithIdentifier:sectionIdentifier |
| 86 atIndex:0]; |
| 87 } |
| 88 |
| 89 - (void)removeItemWithType:(NSInteger)itemType |
| 90 fromSectionWithIdentifier:(NSInteger)sectionIdentifier |
| 91 atIndex:(NSUInteger)index { |
| 92 NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier]; |
| 93 SectionItems* items = [_sections objectAtIndex:section]; |
| 94 NSInteger item = |
| 95 [self itemForItemType:itemType inSectionItems:items atIndex:index]; |
| 96 DCHECK_NE(NSNotFound, item); |
| 97 [items removeObjectAtIndex:item]; |
| 98 } |
| 99 |
| 100 - (void)removeSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 101 NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier]; |
| 102 [_sectionIdentifiers removeObjectAtIndex:section]; |
| 103 [_sections removeObjectAtIndex:section]; |
| 104 } |
| 105 |
| 106 - (void)setHeader:(CollectionViewItem*)header |
| 107 forSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 108 NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier]; |
| 109 if (header) { |
| 110 [_headers setObject:header forKey:key]; |
| 111 } else { |
| 112 [_headers removeObjectForKey:key]; |
| 113 } |
| 114 } |
| 115 |
| 116 - (void)setFooter:(CollectionViewItem*)footer |
| 117 forSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 118 NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier]; |
| 119 if (footer) { |
| 120 [_footers setObject:footer forKey:key]; |
| 121 } else { |
| 122 [_footers removeObjectForKey:key]; |
| 123 } |
| 124 } |
| 125 |
| 126 #pragma mark Query model coordinates from index paths |
| 127 |
| 128 - (NSInteger)sectionIdentifierForSection:(NSInteger)section { |
| 129 DCHECK_LT(static_cast<NSUInteger>(section), [_sectionIdentifiers count]); |
| 130 return [[_sectionIdentifiers objectAtIndex:section] integerValue]; |
| 131 } |
| 132 |
| 133 - (NSInteger)itemTypeForIndexPath:(NSIndexPath*)indexPath { |
| 134 return [self itemAtIndexPath:indexPath].type; |
| 135 } |
| 136 |
| 137 - (NSUInteger)indexInItemTypeForIndexPath:(NSIndexPath*)indexPath { |
| 138 DCHECK_LT(static_cast<NSUInteger>(indexPath.section), [_sections count]); |
| 139 SectionItems* items = [_sections objectAtIndex:indexPath.section]; |
| 140 |
| 141 CollectionViewItem* item = [self itemAtIndexPath:indexPath]; |
| 142 NSUInteger indexInItemType = |
| 143 [self indexInItemTypeForItem:item inSectionItems:items]; |
| 144 return indexInItemType; |
| 145 } |
| 146 |
| 147 #pragma mark Query items from index paths |
| 148 |
| 149 - (BOOL)hasItemAtIndexPath:(NSIndexPath*)indexPath { |
| 150 if (static_cast<NSUInteger>(indexPath.section) < [_sections count]) { |
| 151 SectionItems* items = [_sections objectAtIndex:indexPath.section]; |
| 152 return static_cast<NSUInteger>(indexPath.item) < [items count]; |
| 153 } |
| 154 return NO; |
| 155 } |
| 156 |
| 157 - (CollectionViewItem*)itemAtIndexPath:(NSIndexPath*)indexPath { |
| 158 DCHECK_LT(static_cast<NSUInteger>(indexPath.section), [_sections count]); |
| 159 SectionItems* items = [_sections objectAtIndex:indexPath.section]; |
| 160 |
| 161 DCHECK_LT(static_cast<NSUInteger>(indexPath.item), [items count]); |
| 162 return [items objectAtIndex:indexPath.item]; |
| 163 } |
| 164 |
| 165 - (CollectionViewItem*)headerForSection:(NSInteger)section { |
| 166 NSInteger sectionIdentifier = [self sectionIdentifierForSection:section]; |
| 167 NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier]; |
| 168 return [_headers objectForKey:key]; |
| 169 } |
| 170 |
| 171 - (CollectionViewItem*)footerForSection:(NSInteger)section { |
| 172 NSInteger sectionIdentifier = [self sectionIdentifierForSection:section]; |
| 173 NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier]; |
| 174 return [_footers objectForKey:key]; |
| 175 } |
| 176 |
| 177 - (NSArray*)itemsInSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 178 NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier]; |
| 179 DCHECK_LT(static_cast<NSUInteger>(section), [_sections count]); |
| 180 return [_sections objectAtIndex:section]; |
| 181 } |
| 182 |
| 183 - (CollectionViewItem*)headerForSectionWithIdentifier: |
| 184 (NSInteger)sectionIdentifier { |
| 185 NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier]; |
| 186 return [_headers objectForKey:key]; |
| 187 } |
| 188 |
| 189 - (CollectionViewItem*)footerForSectionWithIdentifier: |
| 190 (NSInteger)sectionIdentifier { |
| 191 NSNumber* key = [NSNumber numberWithInteger:sectionIdentifier]; |
| 192 return [_footers objectForKey:key]; |
| 193 } |
| 194 |
| 195 #pragma mark Query index paths from model coordinates |
| 196 |
| 197 - (BOOL)hasSectionForSectionIdentifier:(NSInteger)sectionIdentifier { |
| 198 NSUInteger section = [self internalSectionForIdentifier:sectionIdentifier]; |
| 199 return section != static_cast<NSUInteger>(NSNotFound); |
| 200 } |
| 201 |
| 202 - (NSInteger)sectionForSectionIdentifier:(NSInteger)sectionIdentifier { |
| 203 NSUInteger section = [self internalSectionForIdentifier:sectionIdentifier]; |
| 204 DCHECK_NE(static_cast<NSUInteger>(NSNotFound), section); |
| 205 return section; |
| 206 } |
| 207 |
| 208 - (BOOL)hasItemForItemType:(NSInteger)itemType |
| 209 sectionIdentifier:(NSInteger)sectionIdentifier { |
| 210 return [self hasItemForItemType:itemType |
| 211 sectionIdentifier:sectionIdentifier |
| 212 atIndex:0]; |
| 213 } |
| 214 |
| 215 - (NSIndexPath*)indexPathForItemType:(NSInteger)itemType |
| 216 sectionIdentifier:(NSInteger)sectionIdentifier { |
| 217 return [self indexPathForItemType:itemType |
| 218 sectionIdentifier:sectionIdentifier |
| 219 atIndex:0]; |
| 220 } |
| 221 |
| 222 - (BOOL)hasItemForItemType:(NSInteger)itemType |
| 223 sectionIdentifier:(NSInteger)sectionIdentifier |
| 224 atIndex:(NSUInteger)index { |
| 225 if (![self hasSectionForSectionIdentifier:sectionIdentifier]) { |
| 226 return NO; |
| 227 } |
| 228 NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier]; |
| 229 SectionItems* items = [_sections objectAtIndex:section]; |
| 230 NSInteger item = |
| 231 [self itemForItemType:itemType inSectionItems:items atIndex:index]; |
| 232 return item != NSNotFound; |
| 233 } |
| 234 |
| 235 - (NSIndexPath*)indexPathForItemType:(NSInteger)itemType |
| 236 sectionIdentifier:(NSInteger)sectionIdentifier |
| 237 atIndex:(NSUInteger)index { |
| 238 NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier]; |
| 239 SectionItems* items = [_sections objectAtIndex:section]; |
| 240 NSInteger item = |
| 241 [self itemForItemType:itemType inSectionItems:items atIndex:index]; |
| 242 return [NSIndexPath indexPathForItem:item inSection:section]; |
| 243 } |
| 244 |
| 245 #pragma mark Query index paths from items |
| 246 |
| 247 - (BOOL)hasItem:(CollectionViewItem*)item |
| 248 inSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 249 return [[self itemsInSectionWithIdentifier:sectionIdentifier] |
| 250 indexOfObject:item] != NSNotFound; |
| 251 } |
| 252 |
| 253 - (NSIndexPath*)indexPathForItem:(CollectionViewItem*)item |
| 254 inSectionWithIdentifier:(NSInteger)sectionIdentifier { |
| 255 NSArray* itemsInSection = |
| 256 [self itemsInSectionWithIdentifier:sectionIdentifier]; |
| 257 |
| 258 NSInteger section = [self sectionForSectionIdentifier:sectionIdentifier]; |
| 259 NSInteger itemIndex = [itemsInSection indexOfObject:item]; |
| 260 DCHECK_NE(NSNotFound, itemIndex); |
| 261 return [NSIndexPath indexPathForItem:itemIndex inSection:section]; |
| 262 } |
| 263 |
| 264 #pragma mark UICollectionView data sourcing |
| 265 |
| 266 - (NSInteger)numberOfSections { |
| 267 return [_sections count]; |
| 268 } |
| 269 |
| 270 - (NSInteger)numberOfItemsInSection:(NSInteger)section { |
| 271 DCHECK_LT(static_cast<NSUInteger>(section), [_sections count]); |
| 272 SectionItems* items = [_sections objectAtIndex:section]; |
| 273 return items.count; |
| 274 } |
| 275 |
| 276 #pragma mark Private methods |
| 277 |
| 278 // Returns the section for the given section identifier. If the section |
| 279 // identifier is not found, NSNotFound is returned. |
| 280 - (NSUInteger)internalSectionForIdentifier:(NSInteger)sectionIdentifier { |
| 281 return [_sectionIdentifiers indexOfObject:@(sectionIdentifier)]; |
| 282 } |
| 283 |
| 284 // Returns the item for the given item type in the list of items, at the |
| 285 // given index. If no item is found with the given type, NSNotFound is returned. |
| 286 - (NSUInteger)itemForItemType:(NSInteger)itemType |
| 287 inSectionItems:(SectionItems*)sectionItems |
| 288 atIndex:(NSUInteger)index { |
| 289 __block NSUInteger item = NSNotFound; |
| 290 __block NSUInteger indexInItemType = 0; |
| 291 [sectionItems enumerateObjectsUsingBlock:^(CollectionViewItem* obj, |
| 292 NSUInteger idx, BOOL* stop) { |
| 293 if (obj.type == itemType) { |
| 294 if (indexInItemType == index) { |
| 295 item = idx; |
| 296 *stop = YES; |
| 297 } else { |
| 298 indexInItemType++; |
| 299 } |
| 300 } |
| 301 }]; |
| 302 return item; |
| 303 } |
| 304 |
| 305 // Returns |item|'s index among all the items of the same type in the given |
| 306 // section items. |item| must belong to |sectionItems|. |
| 307 - (NSUInteger)indexInItemTypeForItem:(CollectionViewItem*)item |
| 308 inSectionItems:(SectionItems*)sectionItems { |
| 309 DCHECK([sectionItems containsObject:item]); |
| 310 BOOL found = NO; |
| 311 NSUInteger indexInItemType = 0; |
| 312 for (CollectionViewItem* sectionItem in sectionItems) { |
| 313 if (sectionItem == item) { |
| 314 found = YES; |
| 315 break; |
| 316 } |
| 317 if (sectionItem.type == item.type) { |
| 318 indexInItemType++; |
| 319 } |
| 320 } |
| 321 DCHECK(found); |
| 322 return indexInItemType; |
| 323 } |
| 324 |
| 325 @end |
OLD | NEW |