OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 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/tab_switcher/tab_switcher_panel_controller.h" |
| 6 |
| 7 #include "base/logging.h" |
| 8 #import "base/mac/scoped_nsobject.h" |
| 9 #include "base/strings/sys_string_conversions.h" |
| 10 #import "ios/chrome/browser/tabs/tab.h" |
| 11 #include "ios/chrome/browser/ui/ntp/recent_tabs/synced_sessions.h" |
| 12 #import "ios/chrome/browser/ui/tab_switcher/session_changes.h" |
| 13 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.h" |
| 14 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.h" |
| 15 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_overlay_view.h" |
| 16 #import "ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_view.h" |
| 17 |
| 18 namespace { |
| 19 |
| 20 void FillVectorWithHashesUsingDistantSession( |
| 21 synced_sessions::DistantSession const& session, |
| 22 std::vector<size_t>* hashes) { |
| 23 DCHECK(hashes); |
| 24 DCHECK(hashes->empty()); |
| 25 for (size_t i = 0; i < session.tabs.size(); ++i) { |
| 26 hashes->push_back(session.tabs[i]->hashOfUserVisibleProperties()); |
| 27 } |
| 28 } |
| 29 |
| 30 } // namespace |
| 31 |
| 32 @interface TabSwitcherPanelController ()<UICollectionViewDataSource, |
| 33 SessionCellDelegate> { |
| 34 ios::ChromeBrowserState* _browserState; // Weak. |
| 35 base::scoped_nsobject<TabSwitcherPanelView> _panelView; |
| 36 base::scoped_nsobject<TabSwitcherModel> _model; |
| 37 std::string _sessionTag; |
| 38 ios_internal::SessionType _sessionType; |
| 39 base::scoped_nsobject<TabSwitcherCache> _cache; |
| 40 base::scoped_nsobject<TabSwitcherPanelOverlayView> _overlayView; |
| 41 std::unique_ptr<const synced_sessions::DistantSession> _distantSession; |
| 42 std::unique_ptr<const TabModelSnapshot> _localSession; |
| 43 } |
| 44 |
| 45 // Changes the visibility of the zero tab state overlay view. |
| 46 - (void)setZeroTabStateOverlayVisible:(BOOL)show; |
| 47 |
| 48 @end |
| 49 |
| 50 @implementation TabSwitcherPanelController |
| 51 |
| 52 @synthesize delegate = _delegate; |
| 53 @synthesize sessionType = _sessionType; |
| 54 |
| 55 - (instancetype)initWithModel:(TabSwitcherModel*)model |
| 56 forDistantSessionWithTag:(std::string const&)sessionTag |
| 57 browserState:(ios::ChromeBrowserState*)browserState { |
| 58 self = [super init]; |
| 59 if (self) { |
| 60 DCHECK(model); |
| 61 _sessionType = ios_internal::SessionType::DISTANT_SESSION; |
| 62 _model.reset([model retain]); |
| 63 _distantSession = [model distantSessionForTag:sessionTag]; |
| 64 _sessionTag = sessionTag; |
| 65 _browserState = browserState; |
| 66 [self loadView]; |
| 67 } |
| 68 return self; |
| 69 } |
| 70 |
| 71 - (instancetype)initWithModel:(TabSwitcherModel*)model |
| 72 forLocalSessionOfType:(ios_internal::SessionType)sessionType |
| 73 withCache:(TabSwitcherCache*)cache |
| 74 browserState:(ios::ChromeBrowserState*)browserState { |
| 75 self = [super init]; |
| 76 if (self) { |
| 77 DCHECK(model); |
| 78 _sessionType = sessionType; |
| 79 _model.reset([model retain]); |
| 80 _localSession = [model tabModelSnapshotForLocalSession:sessionType]; |
| 81 _cache.reset([cache retain]); |
| 82 _browserState = browserState; |
| 83 [self loadView]; |
| 84 } |
| 85 return self; |
| 86 } |
| 87 |
| 88 - (TabSwitcherPanelView*)view { |
| 89 return _panelView; |
| 90 } |
| 91 |
| 92 - (std::string)sessionTag { |
| 93 return _sessionTag; |
| 94 } |
| 95 |
| 96 - (void)setDelegate:(id<TabSwitcherPanelControllerDelegate>)delegate { |
| 97 _delegate = delegate; |
| 98 [[_panelView collectionView] performBatchUpdates:nil completion:nil]; |
| 99 } |
| 100 |
| 101 - (BOOL)shouldShowNewTabButton { |
| 102 if (_sessionType == ios_internal::SessionType::DISTANT_SESSION) { |
| 103 return NO; |
| 104 } else { |
| 105 return ![self isOverlayVisible]; |
| 106 } |
| 107 } |
| 108 |
| 109 - (void)updateCollectionViewIfNeeded { |
| 110 if (_sessionType == ios_internal::SessionType::DISTANT_SESSION) { |
| 111 UICollectionView* collectionView = [_panelView collectionView]; |
| 112 // TODO(crbug.com/633928) Compute SessionChanges outside of the |
| 113 // updateBlock. |
| 114 auto updateBlock = ^{ |
| 115 std::unique_ptr<const synced_sessions::DistantSession> newDistantSession = |
| 116 [_model distantSessionForTag:_sessionTag]; |
| 117 std::vector<size_t> oldTabsHashes; |
| 118 std::vector<size_t> newTabsHashes; |
| 119 FillVectorWithHashesUsingDistantSession(*_distantSession.get(), |
| 120 &oldTabsHashes); |
| 121 FillVectorWithHashesUsingDistantSession(*newDistantSession.get(), |
| 122 &newTabsHashes); |
| 123 ios_internal::SessionChanges changes(oldTabsHashes, newTabsHashes); |
| 124 if (changes.hasChanges()) { |
| 125 _distantSession = std::move(newDistantSession); |
| 126 [self applyChanges:changes toCollectionView:collectionView]; |
| 127 } |
| 128 }; |
| 129 [collectionView performBatchUpdates:updateBlock completion:nil]; |
| 130 } else { |
| 131 UICollectionView* collectionView = [_panelView collectionView]; |
| 132 auto updateBlock = ^{ |
| 133 std::unique_ptr<const TabModelSnapshot> newLocalSession = |
| 134 [_model tabModelSnapshotForLocalSession:_sessionType]; |
| 135 ios_internal::SessionChanges changes(_localSession->hashes(), |
| 136 newLocalSession->hashes()); |
| 137 if (changes.hasChanges()) { |
| 138 _localSession = std::move(newLocalSession); |
| 139 [self applyChanges:changes toCollectionView:collectionView]; |
| 140 } |
| 141 }; |
| 142 [collectionView performBatchUpdates:updateBlock completion:nil]; |
| 143 } |
| 144 } |
| 145 |
| 146 - (void)applyChanges:(ios_internal::SessionChanges&)changes |
| 147 toCollectionView:(UICollectionView*)collectionView { |
| 148 NSMutableArray* deletedIndexes = [NSMutableArray array]; |
| 149 NSMutableArray* insertedIndexes = [NSMutableArray array]; |
| 150 NSMutableArray* updatedIndexes = [NSMutableArray array]; |
| 151 for (size_t i : changes.deletions()) { |
| 152 NSInteger deletedTabIndex = static_cast<NSInteger>(i); |
| 153 [deletedIndexes |
| 154 addObject:[NSIndexPath indexPathForItem:deletedTabIndex inSection:0]]; |
| 155 } |
| 156 for (size_t i : changes.insertions()) { |
| 157 NSInteger insertedTabIndex = static_cast<NSInteger>(i); |
| 158 [insertedIndexes |
| 159 addObject:[NSIndexPath indexPathForItem:insertedTabIndex inSection:0]]; |
| 160 } |
| 161 for (size_t i : changes.updates()) { |
| 162 NSInteger updatedTabIndex = static_cast<NSInteger>(i); |
| 163 [updatedIndexes |
| 164 addObject:[NSIndexPath indexPathForItem:updatedTabIndex inSection:0]]; |
| 165 } |
| 166 [collectionView deleteItemsAtIndexPaths:deletedIndexes]; |
| 167 [collectionView insertItemsAtIndexPaths:insertedIndexes]; |
| 168 [collectionView reloadItemsAtIndexPaths:updatedIndexes]; |
| 169 } |
| 170 |
| 171 - (void)scrollTabIndexToVisible:(NSInteger)index triggerLayout:(BOOL)layout { |
| 172 [[_panelView collectionView] |
| 173 scrollToItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0] |
| 174 atScrollPosition:UICollectionViewScrollPositionTop |
| 175 animated:NO]; |
| 176 if (layout) |
| 177 [[_panelView collectionView] layoutIfNeeded]; |
| 178 } |
| 179 |
| 180 - (TabSwitcherLocalSessionCell*)localSessionCellForTabAtIndex:(NSInteger)index { |
| 181 return (TabSwitcherLocalSessionCell*)[[_panelView collectionView] |
| 182 cellForItemAtIndexPath:[NSIndexPath indexPathForItem:index inSection:0]]; |
| 183 } |
| 184 |
| 185 - (CGRect)localSessionCellFrameForTabAtIndex:(NSInteger)index { |
| 186 UICollectionViewLayoutAttributes* attributes = [[_panelView collectionView] |
| 187 layoutAttributesForItemAtIndexPath:[NSIndexPath indexPathForItem:index |
| 188 inSection:0]]; |
| 189 return attributes.frame; |
| 190 } |
| 191 |
| 192 - (void)reload { |
| 193 [[_panelView collectionView] reloadSections:[NSIndexSet indexSetWithIndex:0]]; |
| 194 } |
| 195 |
| 196 - (UICollectionView*)collectionView { |
| 197 return [_panelView collectionView]; |
| 198 } |
| 199 |
| 200 #pragma mark - UICollectionViewDataSource |
| 201 |
| 202 - (NSInteger)collectionView:(UICollectionView*)collectionView |
| 203 numberOfItemsInSection:(NSInteger)section { |
| 204 DCHECK_EQ(section, 0); |
| 205 if (_sessionType == ios_internal::SessionType::DISTANT_SESSION) { |
| 206 CHECK(_distantSession); |
| 207 return _distantSession->tabs.size(); |
| 208 } else { |
| 209 CHECK(_localSession); |
| 210 NSInteger numberOfTabs = _localSession->tabs().size(); |
| 211 [self setZeroTabStateOverlayVisible:numberOfTabs == 0]; |
| 212 return numberOfTabs; |
| 213 } |
| 214 } |
| 215 |
| 216 - (UICollectionViewCell*)collectionView:(UICollectionView*)collectionView |
| 217 cellForItemAtIndexPath:(NSIndexPath*)indexPath { |
| 218 UICollectionViewCell* cell = nil; |
| 219 NSUInteger tabIndex = indexPath.item; |
| 220 if (_sessionType == ios_internal::SessionType::DISTANT_SESSION) { |
| 221 cell = [collectionView |
| 222 dequeueReusableCellWithReuseIdentifier:[TabSwitcherDistantSessionCell |
| 223 identifier] |
| 224 forIndexPath:indexPath]; |
| 225 DCHECK([cell isKindOfClass:[TabSwitcherDistantSessionCell class]]); |
| 226 TabSwitcherDistantSessionCell* panelCell = |
| 227 static_cast<TabSwitcherDistantSessionCell*>(cell); |
| 228 CHECK(_distantSession); |
| 229 const std::size_t distantSessionTabCount = _distantSession->tabs.size(); |
| 230 LOG_ASSERT(tabIndex < distantSessionTabCount) |
| 231 << "tabIndex == " << tabIndex |
| 232 << " _distantSession->tabs.size() == " << distantSessionTabCount; |
| 233 synced_sessions::DistantTab* tab = _distantSession->tabs[tabIndex].get(); |
| 234 CHECK(tab); |
| 235 [panelCell setTitle:SysUTF16ToNSString(tab->title)]; |
| 236 [panelCell setSessionGURL:tab->virtual_url |
| 237 withBrowserState:[_model browserState]]; |
| 238 [panelCell setDelegate:self]; |
| 239 } else { |
| 240 cell = [collectionView |
| 241 dequeueReusableCellWithReuseIdentifier:[TabSwitcherLocalSessionCell |
| 242 identifier] |
| 243 forIndexPath:indexPath]; |
| 244 DCHECK([cell isKindOfClass:[TabSwitcherLocalSessionCell class]]); |
| 245 TabSwitcherLocalSessionCell* panelCell = |
| 246 static_cast<TabSwitcherLocalSessionCell*>(cell); |
| 247 Tab* tab = _localSession->tabs()[tabIndex]; |
| 248 [panelCell setDelegate:self]; |
| 249 [panelCell setSessionType:_sessionType]; |
| 250 [panelCell setAppearanceForTab:tab cellSize:[_panelView cellSize]]; |
| 251 } |
| 252 return cell; |
| 253 } |
| 254 |
| 255 #pragma mark - SessionCellDelegate |
| 256 |
| 257 - (TabSwitcherCache*)tabSwitcherCache { |
| 258 return _cache; |
| 259 } |
| 260 |
| 261 - (void)cellPressed:(UICollectionViewCell*)cell { |
| 262 const NSInteger tabIndex = |
| 263 [[_panelView collectionView] indexPathForCell:cell].item; |
| 264 |
| 265 if (_sessionType == ios_internal::SessionType::DISTANT_SESSION) { |
| 266 synced_sessions::DistantTab* tab = _distantSession->tabs[tabIndex].get(); |
| 267 if (tab) |
| 268 [self.delegate tabSwitcherPanelController:self didSelectDistantTab:tab]; |
| 269 } else { |
| 270 Tab* tab = _localSession->tabs()[tabIndex]; |
| 271 if (tab) |
| 272 [self.delegate tabSwitcherPanelController:self didSelectLocalTab:tab]; |
| 273 } |
| 274 } |
| 275 |
| 276 - (void)deleteButtonPressedForCell:(UICollectionViewCell*)cell { |
| 277 DCHECK(_sessionType != ios_internal::SessionType::DISTANT_SESSION); |
| 278 const NSInteger tabIndex = |
| 279 [[_panelView collectionView] indexPathForCell:cell].item; |
| 280 Tab* tab = _localSession->tabs()[tabIndex]; |
| 281 if (tab) |
| 282 [self.delegate tabSwitcherPanelController:self didCloseLocalTab:tab]; |
| 283 } |
| 284 |
| 285 #pragma mark - Private |
| 286 |
| 287 - (BOOL)isOverlayVisible { |
| 288 return _overlayView && [_overlayView alpha] != 0.0; |
| 289 } |
| 290 |
| 291 - (void)setZeroTabStateOverlayVisible:(BOOL)show { |
| 292 if (show == [self isOverlayVisible]) |
| 293 return; |
| 294 |
| 295 DCHECK(ios_internal::IsLocalSession(_sessionType)); |
| 296 |
| 297 if (!_overlayView) { |
| 298 _overlayView.reset([[TabSwitcherPanelOverlayView alloc] |
| 299 initWithFrame:[_panelView bounds] |
| 300 browserState:_browserState]); |
| 301 [_overlayView |
| 302 setOverlayType: |
| 303 (_sessionType == ios_internal::SessionType::OFF_THE_RECORD_SESSION) |
| 304 ? TabSwitcherPanelOverlayType:: |
| 305 OVERLAY_PANEL_USER_NO_INCOGNITO_TABS |
| 306 : TabSwitcherPanelOverlayType::OVERLAY_PANEL_USER_NO_OPEN_TABS]; |
| 307 |
| 308 [_overlayView setAlpha:0]; |
| 309 [_overlayView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | |
| 310 UIViewAutoresizingFlexibleWidth]; |
| 311 [_panelView addSubview:_overlayView]; |
| 312 [_overlayView setNeedsLayout]; |
| 313 } |
| 314 |
| 315 [UIView |
| 316 animateWithDuration:0.25 |
| 317 animations:^{ |
| 318 [_overlayView setAlpha:show ? 1.0 : 0.0]; |
| 319 [self.delegate |
| 320 tabSwitcherPanelControllerDidUpdateOverlayViewVisibility: |
| 321 self]; |
| 322 }]; |
| 323 } |
| 324 |
| 325 - (void)loadView { |
| 326 _panelView.reset( |
| 327 [[TabSwitcherPanelView alloc] initWithSessionType:_sessionType]); |
| 328 _panelView.get().collectionView.dataSource = self; |
| 329 } |
| 330 |
| 331 - (synced_sessions::DistantSession const*)distantSession { |
| 332 return _distantSession.get(); |
| 333 } |
| 334 |
| 335 @end |
OLD | NEW |