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

Side by Side 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 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/history/tab_history_view_controller.h"
6
7 #import "base/ios/weak_nsobject.h"
8 #include "base/logging.h"
9 #include "base/mac/foundation_util.h"
10 #include "base/mac/objc_property_releaser.h"
11 #include "base/mac/scoped_nsobject.h"
12 #include "base/strings/sys_string_conversions.h"
13 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
14 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
15 #import "ios/chrome/browser/ui/history/tab_history_cell.h"
16 #include "ios/chrome/browser/ui/rtl_geometry.h"
17 #import "ios/third_party/material_components_ios/src/components/Ink/src/Material Ink.h"
18 #import "ios/web/navigation/crw_session_entry.h"
19 #include "ios/web/public/favicon_status.h"
20 #include "ios/web/public/navigation_item.h"
21 #include "ui/gfx/image/image.h"
22
23 namespace {
24
25 // Visible percentage of the last visible row on the Tools menu if the
26 // Tools menu is scrollable.
27 const CGFloat kLastRowVisiblePercentage = 0.6;
28 // Reuse identifier for cells.
29 NSString* cellIdentifier = @"TabHistoryCell";
30 NSString* footerIdentifier = @"Footer";
31 NSString* headerIdentifier = @"Header";
32 // Height of rows.
33 const CGFloat kCellHeight = 48.0;
34 // Fraction height for partially visible row.
35 const CGFloat kCellHeightLastRow = kCellHeight * kLastRowVisiblePercentage;
36 // Width and leading for the header view.
37 const CGFloat kHeaderLeadingInset = 0;
38 const CGFloat kHeaderWidth = 48;
39 // Leading and trailing insets for cell items.
40 const CGFloat kCellLeadingInset = kHeaderLeadingInset + kHeaderWidth;
41 const CGFloat kCellTrailingInset = 16;
42
43 typedef std::vector<CGFloat> CGFloatVector;
44 typedef std::vector<CGFloatVector> ItemOffsetVector;
45
46 NS_INLINE CGFloat OffsetForPath(const ItemOffsetVector& offsets,
47 NSInteger section,
48 NSInteger item) {
49 DCHECK(section < (NSInteger)offsets.size());
50 DCHECK(item < (NSInteger)offsets.at(section).size());
51
52 return offsets.at(section).at(item);
53 }
54
55 NS_INLINE CGFloat OffsetForPath(const ItemOffsetVector& offsets,
56 NSIndexPath* path) {
57 return OffsetForPath(offsets, [path section], [path item]);
58 }
59
60 NS_INLINE CGFloat OffsetForSection(const ItemOffsetVector& offsets,
61 NSIndexPath* path) {
62 DCHECK([path section] < (NSInteger)offsets.size());
63 DCHECK(offsets.at([path section]).size());
64
65 return offsets.at([path section]).at(0);
66 }
67
68 // Height for the footer view.
69 NS_INLINE CGFloat FooterHeight() {
70 return 1.0 / [[UIScreen mainScreen] scale];
71 }
72
73 } // namespace
74
75 @interface TabHistoryViewControllerLayout : UICollectionViewLayout
76 @end
77
78 @implementation TabHistoryViewControllerLayout {
79 ItemOffsetVector _itemYOffsets;
80 CGFloat _containerCalculatedHeight;
81 CGFloat _containerWidth;
82 CGFloat _cellItemWidth;
83 CGFloat _footerWidth;
84 }
85
86 - (void)prepareLayout {
87 [super prepareLayout];
88
89 UICollectionView* collectionView = [self collectionView];
90 CGFloat yOffset = 0;
91
92 NSInteger numberOfSections = [collectionView numberOfSections];
93 _itemYOffsets.reserve(numberOfSections);
94
95 for (NSInteger section = 0; section < numberOfSections; ++section) {
96 NSInteger numberOfItems = [collectionView numberOfItemsInSection:section];
97
98 CGFloatVector dummy;
99 _itemYOffsets.push_back(dummy);
100
101 CGFloatVector& sectionYOffsets = _itemYOffsets.at(section);
102 sectionYOffsets.reserve(numberOfItems);
103
104 for (NSInteger item = 0; item < numberOfItems; ++item) {
105 sectionYOffsets.push_back(yOffset);
106 yOffset += kCellHeight;
107 }
108
109 // The last section should not have a footer.
110 if (numberOfItems && (section + 1) < numberOfSections) {
111 yOffset += FooterHeight();
112 }
113 }
114
115 CGRect containerBounds = [collectionView bounds];
116 _containerWidth = CGRectGetWidth(containerBounds);
117 _cellItemWidth = _containerWidth - kCellLeadingInset - kCellTrailingInset;
118 _footerWidth = _containerWidth - kCellLeadingInset;
119 _containerCalculatedHeight = yOffset - kCellHeight / 2.0;
120 }
121
122 - (void)invalidateLayout {
123 [super invalidateLayout];
124 _itemYOffsets.clear();
125 _containerCalculatedHeight = 0;
126 _cellItemWidth = 0;
127 _footerWidth = 0;
128 }
129
130 - (CGSize)collectionViewContentSize {
131 return CGSizeMake(_containerWidth, _containerCalculatedHeight);
132 }
133
134 - (NSArray*)layoutAttributesForElementsInRect:(CGRect)rect {
135 UICollectionView* collectionView = [self collectionView];
136 NSMutableArray* array = [NSMutableArray array];
137
138 NSInteger numberOfSections = [collectionView numberOfSections];
139 for (NSInteger section = 0; section < numberOfSections; ++section) {
140 NSInteger numberOfItems = [collectionView numberOfItemsInSection:section];
141 if (numberOfItems) {
142 NSIndexPath* path =
143 [NSIndexPath indexPathForItem:numberOfItems - 1 inSection:section];
144 [array addObject:[self layoutAttributesForSupplementaryViewOfKind:
145 UICollectionElementKindSectionHeader
146 atIndexPath:path]];
147 }
148
149 for (NSInteger item = 0; item < numberOfItems; ++item) {
150 NSIndexPath* path = [NSIndexPath indexPathForItem:item inSection:section];
151 [array addObject:[self layoutAttributesForItemAtIndexPath:path]];
152 }
153
154 // The last section should not have a footer.
155 if (numberOfItems && (section + 1) < numberOfSections) {
156 NSIndexPath* path =
157 [NSIndexPath indexPathForItem:numberOfItems - 1 inSection:section];
158 [array addObject:[self layoutAttributesForSupplementaryViewOfKind:
159 UICollectionElementKindSectionFooter
160 atIndexPath:path]];
161 }
162 }
163
164 return array;
165 }
166
167 - (UICollectionViewLayoutAttributes*)layoutAttributesForItemAtIndexPath:
168 (NSIndexPath*)indexPath {
169 CGFloat yOffset = OffsetForPath(_itemYOffsets, indexPath);
170
171 UICollectionViewLayoutAttributes* attributes =
172 [UICollectionViewLayoutAttributes
173 layoutAttributesForCellWithIndexPath:indexPath];
174 LayoutRect cellLayout = LayoutRectMake(kCellLeadingInset, _containerWidth,
175 yOffset, _cellItemWidth, kCellHeight);
176 [attributes setFrame:LayoutRectGetRect(cellLayout)];
177 [attributes setZIndex:1];
178
179 return attributes;
180 }
181
182 - (UICollectionViewLayoutAttributes*)
183 layoutAttributesForSupplementaryViewOfKind:(NSString*)kind
184 atIndexPath:(NSIndexPath*)indexPath {
185 CGFloat yOffset = OffsetForPath(_itemYOffsets, indexPath);
186
187 UICollectionViewLayoutAttributes* attributes =
188 [UICollectionViewLayoutAttributes
189 layoutAttributesForSupplementaryViewOfKind:kind
190 withIndexPath:indexPath];
191
192 if ([kind isEqualToString:UICollectionElementKindSectionHeader]) {
193 // The height is the yOffset of the first section minus the yOffset of the
194 // last item. Additionally, the height of a cell needs to be added back in
195 // since the _itemYOffsets stores Mid Y.
196 CGFloat headerYOffset = OffsetForSection(_itemYOffsets, indexPath);
197 CGFloat height = yOffset - headerYOffset + kCellHeight;
198
199 if ([indexPath section])
200 headerYOffset += FooterHeight();
201
202 LayoutRect cellLayout = LayoutRectMake(kHeaderLeadingInset, _containerWidth,
203 headerYOffset, kHeaderWidth, height);
204 [attributes setFrame:LayoutRectGetRect(cellLayout)];
205 [attributes setZIndex:0];
206 } else if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
207 LayoutRect cellLayout =
208 LayoutRectMake(kCellLeadingInset, _containerWidth, yOffset,
209 _footerWidth, FooterHeight());
210 [attributes setFrame:LayoutRectGetRect(cellLayout)];
211 [attributes setZIndex:0];
212 }
213
214 return attributes;
215 }
216
217 @end
218
219 @interface TabHistoryViewController ()<MDCInkTouchControllerDelegate> {
220 base::scoped_nsobject<MDCInkTouchController> _inkTouchController;
221 base::scoped_nsobject<NSArray> _partitionedEntries;
222 base::scoped_nsobject<NSArray> _sessionEntries;
223 }
224 @end
225
226 @implementation TabHistoryViewController
227
228 - (NSArray*)sessionEntries {
229 return [[_sessionEntries retain] autorelease];
230 }
231
232 #pragma mark Public Methods
233
234 - (CGFloat)optimalHeight:(CGFloat)suggestedHeight {
235 DCHECK(suggestedHeight >= kCellHeight);
236 CGFloat optimalHeight = 0;
237
238 for (NSArray* sectionArray in _partitionedEntries.get()) {
239 NSUInteger sectionItemCount = [sectionArray count];
240 for (NSUInteger i = 0; i < sectionItemCount; ++i) {
241 CGFloat proposedHeight = optimalHeight + kCellHeight;
242
243 if (proposedHeight > suggestedHeight) {
244 CGFloat difference = proposedHeight - suggestedHeight;
245 if (difference > kCellHeightLastRow) {
246 return optimalHeight + kCellHeightLastRow;
247 } else {
248 return optimalHeight - kCellHeight + kCellHeightLastRow;
249 }
250 }
251
252 optimalHeight = proposedHeight;
253 }
254
255 optimalHeight += FooterHeight();
256 }
257
258 // If this point is reached, it means the entire content fits and this last
259 // section should not include the footer.
260 optimalHeight -= FooterHeight();
261
262 return optimalHeight;
263 }
264
265 - (instancetype)init {
266 base::scoped_nsobject<TabHistoryViewControllerLayout> layout(
267 [[TabHistoryViewControllerLayout alloc] init]);
268
269 return [self initWithCollectionViewLayout:layout];
270 }
271
272 - (instancetype)initWithCollectionViewLayout:(UICollectionViewLayout*)layout {
273 self = [super initWithCollectionViewLayout:layout];
274 if (self) {
275 UICollectionView* collectionView = [self collectionView];
276 [collectionView setBackgroundColor:[UIColor whiteColor]];
277
278 [collectionView registerClass:[TabHistoryCell class]
279 forCellWithReuseIdentifier:cellIdentifier];
280
281 [collectionView registerClass:[TabHistorySectionHeader class]
282 forSupplementaryViewOfKind:UICollectionElementKindSectionHeader
283 withReuseIdentifier:headerIdentifier];
284
285 [collectionView registerClass:[TabHistorySectionFooter class]
286 forSupplementaryViewOfKind:UICollectionElementKindSectionFooter
287 withReuseIdentifier:footerIdentifier];
288
289 _inkTouchController.reset(
290 [[MDCInkTouchController alloc] initWithView:collectionView]);
291 [_inkTouchController setDelegate:self];
292 [_inkTouchController addInkView];
293 }
294
295 return self;
296 }
297
298 #pragma mark UICollectionViewDelegate
299
300 - (void)collectionView:(UICollectionView*)collectionView
301 didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
302 UICollectionViewCell* cell =
303 [collectionView cellForItemAtIndexPath:indexPath];
304 [collectionView chromeExecuteCommand:cell];
305 }
306
307 #pragma mark UICollectionViewDataSource
308
309 - (CRWSessionEntry*)entryForIndexPath:(NSIndexPath*)indexPath {
310 NSInteger section = [indexPath section];
311 NSInteger item = [indexPath item];
312
313 DCHECK(section < (NSInteger)[_partitionedEntries count]);
314 DCHECK(item < (NSInteger)[[_partitionedEntries objectAtIndex:section] count]);
315 NSArray* sectionedArray = [_partitionedEntries objectAtIndex:section];
316
317 return [sectionedArray objectAtIndex:item];
318 }
319
320 - (NSInteger)collectionView:(UICollectionView*)collectionView
321 numberOfItemsInSection:(NSInteger)section {
322 DCHECK(section < (NSInteger)[_partitionedEntries count]);
323 return [[_partitionedEntries objectAtIndex:section] count];
324 }
325
326 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView
327 cellForItemAtIndexPath:(NSIndexPath*)indexPath {
328 TabHistoryCell* cell =
329 [collectionView dequeueReusableCellWithReuseIdentifier:cellIdentifier
330 forIndexPath:indexPath];
331
332 [cell setEntry:[self entryForIndexPath:indexPath]];
333 [cell setTag:IDC_BACK_FORWARD_IN_TAB_HISTORY];
334
335 return cell;
336 }
337
338 - (NSInteger)numberOfSectionsInCollectionView:(UICollectionView*)view {
339 return [_partitionedEntries count];
340 }
341
342 - (UICollectionReusableView*)collectionView:(UICollectionView*)view
343 viewForSupplementaryElementOfKind:(NSString*)kind
344 atIndexPath:(NSIndexPath*)indexPath {
345 if ([kind isEqualToString:UICollectionElementKindSectionFooter]) {
346 return [view dequeueReusableSupplementaryViewOfKind:kind
347 withReuseIdentifier:footerIdentifier
348 forIndexPath:indexPath];
349 }
350
351 DCHECK([kind isEqualToString:UICollectionElementKindSectionHeader]);
352 CRWSessionEntry* sessionEntry = [self entryForIndexPath:indexPath];
353 web::NavigationItem* navigationItem = [sessionEntry navigationItem];
354
355 TabHistorySectionHeader* header =
356 [view dequeueReusableSupplementaryViewOfKind:kind
357 withReuseIdentifier:headerIdentifier
358 forIndexPath:indexPath];
359
360 UIImage* iconImage = nil;
361 const gfx::Image& image = navigationItem->GetFavicon().image;
362 if (!image.IsEmpty())
363 iconImage = image.ToUIImage();
364 else
365 iconImage = [UIImage imageNamed:@"default_favicon"];
366
367 [[header iconView] setImage:iconImage];
368
369 return header;
370 }
371
372 - (void)setSessionEntries:(NSArray*)sessionEntries {
373 _sessionEntries.reset([sessionEntries retain]);
374
375 std::string previousHost;
376
377 NSMutableArray* sectionArray = [NSMutableArray array];
378 NSMutableArray* partitionedEntries = [NSMutableArray array];
379
380 NSInteger numberOfEntries = [_sessionEntries count];
381 for (NSInteger index = 0; index < numberOfEntries; ++index) {
382 CRWSessionEntry* sessionEntry = [_sessionEntries objectAtIndex:index];
383 web::NavigationItem* navigationItem = [sessionEntry navigationItem];
384
385 std::string currentHost;
386 if (navigationItem)
387 currentHost = navigationItem->GetURL().host();
388
389 if (previousHost.empty())
390 previousHost = currentHost;
391
392 // TODO: This should use some sort of Top Level Domain matching instead of
393 // explicit host match so that images.googe.com matches shopping.google.com.
394 if (previousHost == currentHost) {
395 [sectionArray addObject:sessionEntry];
396 } else {
397 [partitionedEntries addObject:sectionArray];
398 sectionArray = [NSMutableArray arrayWithObject:sessionEntry];
399 previousHost = currentHost;
400 }
401 }
402
403 if ([sectionArray count])
404 [partitionedEntries addObject:sectionArray];
405
406 if (![partitionedEntries count])
407 partitionedEntries = nil;
408
409 _partitionedEntries.reset([partitionedEntries retain]);
410 }
411
412 #pragma mark MDCInkTouchControllerDelegate
413
414 - (BOOL)inkTouchController:(MDCInkTouchController*)inkTouchController
415 shouldProcessInkTouchesAtTouchLocation:(CGPoint)location {
416 NSIndexPath* indexPath =
417 [self.collectionView indexPathForItemAtPoint:location];
418 TabHistoryCell* cell = base::mac::ObjCCastStrict<TabHistoryCell>(
419 [self.collectionView cellForItemAtIndexPath:indexPath]);
420 if (!cell) {
421 return NO;
422 }
423
424 // Increase the size of the ink view to cover the collection view from edge
425 // to edge.
426 CGRect inkViewFrame = [cell frame];
427 inkViewFrame.origin.x = 0;
428 inkViewFrame.size.width = CGRectGetWidth([self.collectionView bounds]);
429 [[inkTouchController defaultInkView] setFrame:inkViewFrame];
430 return YES;
431 }
432
433 @end
OLDNEW
« 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