OLD | NEW |
(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_home_handset_view_controller.h
" |
| 6 |
| 7 #include <memory> |
| 8 |
| 9 #import "base/ios/weak_nsobject.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/mac/objc_property_releaser.h" |
| 12 #include "base/mac/scoped_nsobject.h" |
| 13 #include "base/metrics/user_metrics.h" |
| 14 #include "base/metrics/user_metrics_action.h" |
| 15 #include "base/strings/sys_string_conversions.h" |
| 16 #include "components/bookmarks/browser/bookmark_model.h" |
| 17 #include "components/strings/grit/components_strings.h" |
| 18 #include "google_apis/gaia/google_service_auth_error.h" |
| 19 #include "ios/chrome/browser/bookmarks/bookmarks_utils.h" |
| 20 #include "ios/chrome/browser/browser_state/chrome_browser_state.h" |
| 21 #import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h" |
| 22 #import "ios/chrome/browser/ui/bookmarks/bars/bookmark_editing_bar.h" |
| 23 #import "ios/chrome/browser/ui/bookmarks/bars/bookmark_navigation_bar.h" |
| 24 #import "ios/chrome/browser/ui/bookmarks/bookmark_all_collection_view.h" |
| 25 #import "ios/chrome/browser/ui/bookmarks/bookmark_collection_cells.h" |
| 26 #import "ios/chrome/browser/ui/bookmarks/bookmark_edit_view_controller.h" |
| 27 #import "ios/chrome/browser/ui/bookmarks/bookmark_folder_collection_view.h" |
| 28 #import "ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller.
h" |
| 29 #import "ios/chrome/browser/ui/bookmarks/bookmark_folder_view_controller.h" |
| 30 #import "ios/chrome/browser/ui/bookmarks/bookmark_home_primary_view.h" |
| 31 #import "ios/chrome/browser/ui/bookmarks/bookmark_home_waiting_view.h" |
| 32 #import "ios/chrome/browser/ui/bookmarks/bookmark_menu_item.h" |
| 33 #import "ios/chrome/browser/ui/bookmarks/bookmark_menu_view.h" |
| 34 #include "ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h" |
| 35 #import "ios/chrome/browser/ui/bookmarks/bookmark_navigation_controller.h" |
| 36 #import "ios/chrome/browser/ui/bookmarks/bookmark_panel_view.h" |
| 37 #import "ios/chrome/browser/ui/bookmarks/bookmark_position_cache.h" |
| 38 #import "ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.h" |
| 39 #import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h" |
| 40 #include "ios/chrome/browser/ui/ui_util.h" |
| 41 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
| 42 #include "ios/chrome/grit/ios_strings.h" |
| 43 #include "ui/base/l10n/l10n_util.h" |
| 44 #include "ui/base/l10n/l10n_util_mac.h" |
| 45 |
| 46 using bookmarks::BookmarkNode; |
| 47 |
| 48 namespace { |
| 49 const CGFloat kBookmarkMenuWidth = 264; |
| 50 } // namespace |
| 51 |
| 52 @interface BookmarkHomeHandsetViewController ()< |
| 53 BookmarkEditViewControllerDelegate, |
| 54 BookmarkFolderCollectionViewDelegate, |
| 55 BookmarkFolderEditorViewControllerDelegate, |
| 56 BookmarkFolderViewControllerDelegate, |
| 57 BookmarkMenuViewDelegate, |
| 58 BookmarkModelBridgeObserver, |
| 59 BookmarkPanelViewDelegate, |
| 60 BookmarkPromoControllerDelegate> { |
| 61 // Bridge to register for bookmark changes. |
| 62 std::unique_ptr<bookmarks::BookmarkModelBridge> _bridge; |
| 63 base::mac::ObjCPropertyReleaser |
| 64 _propertyReleaser_BookmarkHomeHandsetViewController; |
| 65 } |
| 66 |
| 67 // This views holds the primary content of this view controller. At any point in |
| 68 // time, it contains exactly one of the BookmarkCollectionView subclasses. |
| 69 @property(nonatomic, retain) UIView* contentView; |
| 70 // The possible views that can be shown from the menu. |
| 71 @property(nonatomic, retain) BookmarkAllCollectionView* allItemsView; |
| 72 @property(nonatomic, retain) BookmarkFolderCollectionView* folderView; |
| 73 // This view is created and used if the model is not fully loaded yet by the |
| 74 // time this controller starts. |
| 75 @property(nonatomic, retain) BookmarkHomeWaitingView* waitForModelView; |
| 76 |
| 77 // The menu with all the folders and special entries. |
| 78 @property(nonatomic, retain) BookmarkMenuView* menuView; |
| 79 // At any point in time, there is exactly one collection view whose view is part |
| 80 // of the view hierarchy. This property determine which collection view is |
| 81 // visible. Not by accident, this property also reflects the selected menu item |
| 82 // in the BookmarkMenuView. |
| 83 @property(nonatomic, retain) BookmarkMenuItem* primaryMenuItem; |
| 84 // When the view is first shown on the screen, this property represents the |
| 85 // cached value of the y of the content offset of the primary view. This |
| 86 // property is set to nil after it is used. |
| 87 @property(nonatomic, retain) NSNumber* cachedContentPosition; |
| 88 |
| 89 // The navigation bar sits on top of the main content. |
| 90 @property(nonatomic, retain) BookmarkNavigationBar* navigationBar; |
| 91 // The layout code in this class relies on the assumption that the editingBar |
| 92 // has the same frame as the navigationBar. |
| 93 @property(nonatomic, retain) BookmarkEditingBar* editingBar; |
| 94 |
| 95 // The action sheet coordinator used when trying to edit a single bookmark. |
| 96 @property(nonatomic, retain) ActionSheetCoordinator* actionSheetCoordinator; |
| 97 // The view controller used to view and edit a single bookmark. |
| 98 @property(nonatomic, retain) BookmarkEditViewController* editViewController; |
| 99 // The view controller used to pick a folder in which to move the selected |
| 100 // bookmarks. |
| 101 @property(nonatomic, retain) BookmarkFolderViewController* folderSelector; |
| 102 // The view controller to present when editing the current folder. |
| 103 @property(nonatomic, retain) BookmarkFolderEditorViewController* folderEditor; |
| 104 #pragma mark Specific to this class. |
| 105 |
| 106 // The panel view slides on top of the content to display the menu. |
| 107 @property(nonatomic, retain) BookmarkPanelView* panelView; |
| 108 |
| 109 // Either the menu or the primaryView can scrollToTop. |
| 110 @property(nonatomic, assign) BOOL scrollingMenuToTop; |
| 111 // The controller managing the display of the promo cell and the promo view |
| 112 // controller. |
| 113 @property(nonatomic, retain) BookmarkPromoController* bookmarkPromoController; |
| 114 |
| 115 #pragma mark View loading and switching |
| 116 // This method is called if the view needs to be loaded and the model is not |
| 117 // ready yet. |
| 118 - (void)loadWaitingView; |
| 119 // This method should be called at most once in the life-cycle of the |
| 120 // class. It should be called at the soonest possible time after the |
| 121 // view has been loaded, and the bookmark model is loaded. |
| 122 - (void)loadBookmarkViews; |
| 123 // If the view doesn't exist, create it. |
| 124 - (void)ensureAllViewExists; |
| 125 - (void)ensureFolderViewExists; |
| 126 // Updates the property 'primaryMenuItem'. |
| 127 // Updates the UI to reflect the new state of 'primaryMenuItem'. |
| 128 - (void)updatePrimaryMenuItem:(BookmarkMenuItem*)menuItem |
| 129 animated:(BOOL)animated; |
| 130 |
| 131 #pragma mark Editing related methods |
| 132 // This method statelessly updates the editing top bar from |_editNodes| and |
| 133 // |editing|. |
| 134 - (void)updateEditingStateAnimated:(BOOL)animated; |
| 135 // Shows or hides the editing bar. |
| 136 - (void)showEditingBarAnimated:(BOOL)animated; |
| 137 - (void)hideEditingBarAnimated:(BOOL)animated; |
| 138 // Instaneously updates the shadow of the edit bar. |
| 139 // This method should be called anytime: |
| 140 // (1)|editing| property changes. |
| 141 // (2)The primary view changes. |
| 142 // (3)The primary view's collection view is scrolled. |
| 143 // (2) is not necessary right now, as it is only possible to switch primary |
| 144 // views when |editing| is NO. When |editing| is NO, the shadow is never shown. |
| 145 - (void)updateEditBarShadow; |
| 146 |
| 147 #pragma mark Editing bar callbacks |
| 148 // The cancel button was tapped on the editing bar. |
| 149 - (void)editingBarCancel; |
| 150 // The move button was tapped on the editing bar. |
| 151 - (void)editingBarMove; |
| 152 // The delete button was tapped on the editing bar. |
| 153 - (void)editingBarDelete; |
| 154 // The edit button was tapped on the editing bar. |
| 155 - (void)editingBarEdit; |
| 156 |
| 157 #pragma mark Action sheet callbacks |
| 158 // Enters into edit mode by selecting the given node and cell. |
| 159 - (void)selectFirstNode:(const BookmarkNode*)node |
| 160 withCell:(UICollectionViewCell*)cell; |
| 161 // Opens the editor on the given node with a transition from cell. |
| 162 - (void)editNode:(const BookmarkNode*)node; |
| 163 // Opens the folder move editor for the given node. |
| 164 - (void)moveNodes:(const std::set<const BookmarkNode*>&)nodes; |
| 165 // Deletes the current node. |
| 166 - (void)deleteNodes:(const std::set<const BookmarkNode*>&)nodes; |
| 167 |
| 168 #pragma mark private utility methods |
| 169 // Deletes the nodes, and presents a toast with an undo button. |
| 170 - (void)deleteSelectedNodes; |
| 171 |
| 172 #pragma mark Navigation bar |
| 173 // Updates the UI of the navigation bar with the primaryMenuItem. |
| 174 // This method should be called anytime: |
| 175 // (1)The primary view changes. |
| 176 // (2)The menu is shown or hidden. |
| 177 // (3)The primary view has type folder, and the relevant folder has changed. |
| 178 // (4)The interface orientation changes. |
| 179 // (5)viewWillAppear, as the interface orientation may have changed. |
| 180 - (void)updateNavigationBarAnimated:(BOOL)animated |
| 181 orientation:(UIInterfaceOrientation)orientation; |
| 182 - (void)updateNavigationBarWithDuration:(CGFloat)duration |
| 183 orientation:(UIInterfaceOrientation)orientation; |
| 184 // Whether the edit button on the navigation bar should be shown. |
| 185 - (BOOL)shouldShowEditButtonWithMenuVisibility:(BOOL)visible; |
| 186 // Whether the back button on the navigation bar should be preferred to the menu |
| 187 // button. |
| 188 - (BOOL)shouldShowBackButtonInsteadOfMenuButton; |
| 189 // Called when the edit button is pressed on the navigation bar. |
| 190 - (void)navigationBarWantsEditing:(id)sender; |
| 191 // Called when the cancel button is pressed on the navigation bar. |
| 192 - (void)navigationBarCancel:(id)sender; |
| 193 // Called when the menu button is pressed on the navigation bar. |
| 194 - (void)navigationBarToggledMenu:(id)sender; |
| 195 // Called when the back button is pressed on the navigation bar. |
| 196 - (void)navigationBarBack:(id)sender; |
| 197 |
| 198 #pragma mark private methods |
| 199 // The active collection view that corresponds to primaryMenuItem. |
| 200 // This must be implemented by subclass. |
| 201 - (UIView<BookmarkHomePrimaryView>*)primaryView; |
| 202 // Returns the size of the primary view. |
| 203 - (CGRect)frameForPrimaryView; |
| 204 // Updates the UI to reflect the given orientation, with an animation lasting |
| 205 // |duration|. |
| 206 - (void)updateUIForInterfaceOrientation:(UIInterfaceOrientation)orientation |
| 207 duration:(NSTimeInterval)duration; |
| 208 // Shows or hides the menu. |
| 209 - (void)showMenuAnimated:(BOOL)animated; |
| 210 - (void)hideMenuAnimated:(BOOL)animated updateNavigationBar:(BOOL)update; |
| 211 |
| 212 // Saves the current position and asks the delegate to open the url. |
| 213 - (void)delegateDismiss:(const GURL&)url; |
| 214 |
| 215 // TODO(crbug.com/450646): This should not be needed but require refactoring of |
| 216 // the BookmarkCollectionViewDelegate. |
| 217 - (NSIndexPath*)indexPathForCell:(UICollectionViewCell*)cell; |
| 218 |
| 219 @end |
| 220 |
| 221 @implementation BookmarkHomeHandsetViewController |
| 222 @synthesize allItemsView = _allItemsView; |
| 223 @synthesize contentView = _contentView; |
| 224 @synthesize folderView = _folderView; |
| 225 @synthesize waitForModelView = _waitForModelView; |
| 226 |
| 227 @synthesize menuView = _menuView; |
| 228 @synthesize primaryMenuItem = _primaryMenuItem; |
| 229 @synthesize cachedContentPosition = _cachedContentPosition; |
| 230 @synthesize navigationBar = _navigationBar; |
| 231 @synthesize editingBar = _editingBar; |
| 232 |
| 233 @synthesize actionSheetCoordinator = _actionSheetCoordinator; |
| 234 @synthesize editViewController = _editViewController; |
| 235 @synthesize folderSelector = _folderSelector; |
| 236 @synthesize folderEditor = _folderEditor; |
| 237 |
| 238 @synthesize panelView = _panelView; |
| 239 @synthesize scrollingMenuToTop = _scrollingMenuToTop; |
| 240 @synthesize bookmarkPromoController = _bookmarkPromoController; |
| 241 |
| 242 - (instancetype)initWithLoader:(id<UrlLoader>)loader |
| 243 browserState:(ios::ChromeBrowserState*)browserState { |
| 244 self = [super initWithLoader:loader browserState:browserState]; |
| 245 if (self) { |
| 246 _propertyReleaser_BookmarkHomeHandsetViewController.Init( |
| 247 self, [BookmarkHomeHandsetViewController class]); |
| 248 |
| 249 _bridge.reset(new bookmarks::BookmarkModelBridge(self, self.bookmarks)); |
| 250 // It is important to initialize the promo controller with the browser state |
| 251 // passed in, as it could be incognito. |
| 252 _bookmarkPromoController = |
| 253 [[BookmarkPromoController alloc] initWithBrowserState:browserState |
| 254 delegate:self]; |
| 255 } |
| 256 return self; |
| 257 } |
| 258 |
| 259 - (void)dealloc { |
| 260 _allItemsView.delegate = nil; |
| 261 _folderView.delegate = nil; |
| 262 |
| 263 _menuView.delegate = nil; |
| 264 |
| 265 _editViewController.delegate = nil; |
| 266 _folderSelector.delegate = nil; |
| 267 |
| 268 _panelView.delegate = nil; |
| 269 [super dealloc]; |
| 270 } |
| 271 |
| 272 - (void)removeEditNode:(const BookmarkNode*)node |
| 273 cell:(UICollectionViewCell*)cell { |
| 274 [super removeEditNode:node atIndexPath:[self indexPathForCell:cell]]; |
| 275 if (_editNodes.size() == 0) |
| 276 [self setEditing:NO animated:YES]; |
| 277 else |
| 278 [self updateEditingStateAnimated:YES]; |
| 279 } |
| 280 |
| 281 #pragma mark - UIViewController methods |
| 282 |
| 283 - (void)viewDidLoad { |
| 284 [super viewDidLoad]; |
| 285 |
| 286 base::scoped_nsobject<BookmarkNavigationBar> bar( |
| 287 [[BookmarkNavigationBar alloc] initWithFrame:[self navigationBarFrame]]); |
| 288 self.navigationBar = bar; |
| 289 [self.navigationBar setEditTarget:self |
| 290 action:@selector(navigationBarWantsEditing:)]; |
| 291 [self.navigationBar setMenuTarget:self |
| 292 action:@selector(navigationBarToggledMenu:)]; |
| 293 [self.navigationBar setBackTarget:self action:@selector(navigationBarBack:)]; |
| 294 [self.navigationBar setCancelTarget:self |
| 295 action:@selector(navigationBarCancel:)]; |
| 296 |
| 297 [self.view addSubview:self.navigationBar]; |
| 298 [self.view bringSubviewToFront:self.navigationBar]; |
| 299 |
| 300 if (self.bookmarks->loaded()) |
| 301 [self loadBookmarkViews]; |
| 302 else |
| 303 [self loadWaitingView]; |
| 304 } |
| 305 |
| 306 - (BOOL)prefersStatusBarHidden { |
| 307 return NO; |
| 308 } |
| 309 |
| 310 #pragma mark - Accessibility |
| 311 |
| 312 - (BOOL)accessibilityPerformEscape { |
| 313 [self delegateDismiss:GURL()]; |
| 314 return YES; |
| 315 } |
| 316 |
| 317 #pragma mark - Methods duplicated from BookmarkHomeTabletNTPController. |
| 318 |
| 319 - (void)loadWaitingView { |
| 320 DCHECK(!self.waitForModelView); |
| 321 DCHECK([self isViewLoaded]); |
| 322 |
| 323 // Present a waiting view. |
| 324 base::scoped_nsobject<BookmarkHomeWaitingView> waitingView( |
| 325 [[BookmarkHomeWaitingView alloc] initWithFrame:self.view.bounds]); |
| 326 self.waitForModelView = waitingView; |
| 327 [self.view addSubview:self.waitForModelView]; |
| 328 [self.waitForModelView startWaiting]; |
| 329 } |
| 330 |
| 331 - (void)loadBookmarkViews { |
| 332 DCHECK(self.bookmarks->loaded()); |
| 333 DCHECK([self isViewLoaded]); |
| 334 |
| 335 self.panelView = base::scoped_nsobject<BookmarkPanelView>( |
| 336 [[BookmarkPanelView alloc] initWithFrame:[self frameForPrimaryView] |
| 337 menuViewWidth:kBookmarkMenuWidth]); |
| 338 self.panelView.autoresizingMask = |
| 339 UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; |
| 340 self.panelView.delegate = self; |
| 341 [self.view insertSubview:self.panelView atIndex:0]; |
| 342 |
| 343 self.contentView = base::scoped_nsobject<UIView>([[UIView alloc] init]); |
| 344 self.contentView.frame = self.panelView.contentView.bounds; |
| 345 self.contentView.autoresizingMask = |
| 346 UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; |
| 347 [self.panelView.contentView addSubview:self.contentView]; |
| 348 |
| 349 // The user can swipe the BookmarkPanelView to show the menuView. |
| 350 // Therefore, it must be created here. |
| 351 self.menuView = |
| 352 base::scoped_nsobject<BookmarkMenuView>([[BookmarkMenuView alloc] |
| 353 initWithBrowserState:self.browserState |
| 354 frame:self.panelView.menuView.bounds]); |
| 355 self.menuView.delegate = self; |
| 356 [self.panelView.menuView addSubview:self.menuView]; |
| 357 self.menuView.autoresizingMask = |
| 358 UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; |
| 359 |
| 360 // Load the last primary menu item which the user had active. |
| 361 BookmarkMenuItem* item = nil; |
| 362 CGFloat position = 0; |
| 363 BOOL found = |
| 364 bookmark_utils_ios::GetPositionCache(self.bookmarks, &item, &position); |
| 365 if (!found) |
| 366 item = [self.menuView defaultMenuItem]; |
| 367 |
| 368 [self updatePrimaryMenuItem:item animated:NO]; |
| 369 |
| 370 if (found) { |
| 371 // If the view has already been laid out, then immediately apply the content |
| 372 // position. |
| 373 if (self.view.window) { |
| 374 [[self primaryView] applyContentPosition:position]; |
| 375 } else { |
| 376 // Otherwise, save the position to be applied once the view has been laid |
| 377 // out. |
| 378 self.cachedContentPosition = [NSNumber numberWithFloat:position]; |
| 379 } |
| 380 } |
| 381 } |
| 382 |
| 383 - (void)ensureAllViewExists { |
| 384 if (self.allItemsView) |
| 385 return; |
| 386 |
| 387 base::scoped_nsobject<BookmarkAllCollectionView> view( |
| 388 [[BookmarkAllCollectionView alloc] |
| 389 initWithBrowserState:self.browserState |
| 390 frame:[self frameForPrimaryView]]); |
| 391 self.allItemsView = view; |
| 392 self.allItemsView.delegate = self; |
| 393 [self.allItemsView setEditing:self.editing animated:NO]; |
| 394 self.allItemsView.autoresizingMask = |
| 395 UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; |
| 396 } |
| 397 |
| 398 - (void)ensureFolderViewExists { |
| 399 if (self.folderView) |
| 400 return; |
| 401 |
| 402 base::scoped_nsobject<BookmarkFolderCollectionView> view( |
| 403 [[BookmarkFolderCollectionView alloc] |
| 404 initWithBrowserState:self.browserState |
| 405 frame:[self frameForPrimaryView]]); |
| 406 self.folderView = view; |
| 407 self.folderView.delegate = self; |
| 408 [self.folderView setEditing:self.editing animated:NO]; |
| 409 self.folderView.autoresizingMask = |
| 410 UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; |
| 411 } |
| 412 |
| 413 - (void)updatePrimaryMenuItem:(BookmarkMenuItem*)menuItem |
| 414 animated:(BOOL)animated { |
| 415 if ([self.primaryMenuItem isEqual:menuItem]) |
| 416 return; |
| 417 |
| 418 // Disable editing on previous primary view before dismissing it. No need to |
| 419 // animate because this view is immediately removed from hierarchy. |
| 420 [self.primaryView setEditing:NO animated:NO]; |
| 421 |
| 422 [[self primaryView] removeFromSuperview]; |
| 423 self.primaryMenuItem = menuItem; |
| 424 |
| 425 switch (self.primaryMenuItem.type) { |
| 426 case bookmarks::MenuItemAll: |
| 427 [self ensureAllViewExists]; |
| 428 break; |
| 429 case bookmarks::MenuItemFolder: |
| 430 [self ensureFolderViewExists]; |
| 431 [self.folderView resetFolder:self.primaryMenuItem.folder]; |
| 432 [self.folderView promoStateChangedAnimated:NO]; |
| 433 break; |
| 434 case bookmarks::MenuItemDivider: |
| 435 case bookmarks::MenuItemSectionHeader: |
| 436 NOTREACHED(); |
| 437 break; |
| 438 } |
| 439 |
| 440 UIView* primaryView = [self primaryView]; |
| 441 [[self primaryView] changeOrientation:GetInterfaceOrientation()]; |
| 442 [[self primaryView] setScrollsToTop:!self.scrollingMenuToTop]; |
| 443 |
| 444 [self.contentView insertSubview:primaryView atIndex:0]; |
| 445 primaryView.frame = self.contentView.bounds; |
| 446 |
| 447 [self updateNavigationBarAnimated:animated |
| 448 orientation:GetInterfaceOrientation()]; |
| 449 |
| 450 [self.menuView updatePrimaryMenuItem:self.primaryMenuItem]; |
| 451 } |
| 452 |
| 453 - (UIView<BookmarkHomePrimaryView>*)primaryView { |
| 454 switch (self.primaryMenuItem.type) { |
| 455 case bookmarks::MenuItemAll: |
| 456 return self.allItemsView; |
| 457 case bookmarks::MenuItemFolder: |
| 458 return self.folderView; |
| 459 case bookmarks::MenuItemDivider: |
| 460 case bookmarks::MenuItemSectionHeader: |
| 461 NOTREACHED(); |
| 462 return nil; |
| 463 } |
| 464 } |
| 465 |
| 466 #pragma mark - Editing bar methods. |
| 467 |
| 468 - (void)updateEditingStateAnimated:(BOOL)animated { |
| 469 if (!self.editing) { |
| 470 [self hideEditingBarAnimated:animated]; |
| 471 [self updateEditBarShadow]; |
| 472 [self.panelView enableSideSwiping:YES]; |
| 473 return; |
| 474 } |
| 475 |
| 476 if (!self.editingBar) { |
| 477 self.editingBar = base::scoped_nsobject<BookmarkEditingBar>( |
| 478 [[BookmarkEditingBar alloc] initWithFrame:self.navigationBar.frame]); |
| 479 [self.editingBar setCancelTarget:self action:@selector(editingBarCancel)]; |
| 480 [self.editingBar setDeleteTarget:self action:@selector(editingBarDelete)]; |
| 481 [self.editingBar setMoveTarget:self action:@selector(editingBarMove)]; |
| 482 [self.editingBar setEditTarget:self action:@selector(editingBarEdit)]; |
| 483 |
| 484 [self.view addSubview:self.editingBar]; |
| 485 self.editingBar.alpha = 0; |
| 486 } |
| 487 |
| 488 int bookmarkCount = 0; |
| 489 int folderCount = 0; |
| 490 for (auto node : _editNodes) { |
| 491 if (node->is_url()) |
| 492 ++bookmarkCount; |
| 493 else |
| 494 ++folderCount; |
| 495 } |
| 496 [self.editingBar updateUIWithBookmarkCount:bookmarkCount |
| 497 folderCount:folderCount]; |
| 498 |
| 499 [self showEditingBarAnimated:animated]; |
| 500 [self updateEditBarShadow]; |
| 501 [self.panelView enableSideSwiping:NO]; |
| 502 } |
| 503 |
| 504 - (void)setEditing:(BOOL)editing animated:(BOOL)animated { |
| 505 [super setEditing:editing animated:animated]; |
| 506 |
| 507 [self updateEditingStateAnimated:animated]; |
| 508 if ([[self primaryMenuItem] supportsEditing]) |
| 509 [[self primaryView] setEditing:editing animated:animated]; |
| 510 |
| 511 if (editing) |
| 512 self.bookmarkPromoController.promoState = NO; |
| 513 else |
| 514 [self.bookmarkPromoController updatePromoState]; |
| 515 } |
| 516 |
| 517 - (void)showEditingBarAnimated:(BOOL)animated { |
| 518 CGRect frame = self.navigationBar.frame; |
| 519 self.editingBar.hidden = NO; |
| 520 if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) |
| 521 [self setNeedsStatusBarAppearanceUpdate]; |
| 522 [UIView animateWithDuration:animated ? 0.2 : 0 |
| 523 delay:0 |
| 524 options:UIViewAnimationOptionBeginFromCurrentState |
| 525 animations:^{ |
| 526 self.editingBar.alpha = 1; |
| 527 self.editingBar.frame = frame; |
| 528 } |
| 529 completion:^(BOOL finished) { |
| 530 if (finished) { |
| 531 self.navigationBar.hidden = YES; |
| 532 } else { |
| 533 if ([self respondsToSelector:@selector( |
| 534 setNeedsStatusBarAppearanceUpdate)]) |
| 535 [self setNeedsStatusBarAppearanceUpdate]; |
| 536 } |
| 537 }]; |
| 538 } |
| 539 |
| 540 - (void)hideEditingBarAnimated:(BOOL)animated { |
| 541 CGRect frame = self.navigationBar.frame; |
| 542 self.navigationBar.hidden = NO; |
| 543 if ([self respondsToSelector:@selector(setNeedsStatusBarAppearanceUpdate)]) |
| 544 [self setNeedsStatusBarAppearanceUpdate]; |
| 545 [UIView animateWithDuration:animated ? 0.2 : 0 |
| 546 delay:0 |
| 547 options:UIViewAnimationOptionBeginFromCurrentState |
| 548 animations:^{ |
| 549 self.editingBar.alpha = 0; |
| 550 self.editingBar.frame = frame; |
| 551 } |
| 552 completion:^(BOOL finished) { |
| 553 if (finished) { |
| 554 self.editingBar.hidden = YES; |
| 555 } else { |
| 556 if ([self respondsToSelector:@selector( |
| 557 setNeedsStatusBarAppearanceUpdate)]) |
| 558 [self setNeedsStatusBarAppearanceUpdate]; |
| 559 } |
| 560 }]; |
| 561 } |
| 562 |
| 563 - (void)updateEditBarShadow { |
| 564 [self.editingBar showShadow:self.editing]; |
| 565 } |
| 566 |
| 567 #pragma mark Editing Bar Callbacks |
| 568 |
| 569 - (void)editingBarCancel { |
| 570 [self setEditing:NO animated:YES]; |
| 571 } |
| 572 |
| 573 - (void)editingBarMove { |
| 574 [self moveNodes:_editNodes]; |
| 575 } |
| 576 |
| 577 - (void)editingBarDelete { |
| 578 [self deleteSelectedNodes]; |
| 579 [self setEditing:NO animated:YES]; |
| 580 } |
| 581 |
| 582 - (void)editingBarEdit { |
| 583 DCHECK_EQ(_editNodes.size(), 1u); |
| 584 const BookmarkNode* node = *(_editNodes.begin()); |
| 585 [self editNode:node]; |
| 586 } |
| 587 |
| 588 #pragma mark - BookmarkMenuViewDelegate |
| 589 |
| 590 - (void)bookmarkMenuView:(BookmarkMenuView*)view |
| 591 selectedMenuItem:(BookmarkMenuItem*)menuItem { |
| 592 BOOL menuItemChanged = ![[self primaryMenuItem] isEqual:menuItem]; |
| 593 // If the primary menu item has changed, then updatePrimaryMenuItem: will |
| 594 // update the navigation bar, and there's no need for hideMenuAnimated: to do |
| 595 // so. |
| 596 [self hideMenuAnimated:YES updateNavigationBar:!menuItemChanged]; |
| 597 if (menuItemChanged) |
| 598 [self updatePrimaryMenuItem:menuItem animated:YES]; |
| 599 } |
| 600 |
| 601 #pragma mark - BookmarkCollectionViewDelegate |
| 602 // This class owns multiple views that have a delegate that conforms to |
| 603 // BookmarkCollectionViewDelegate, or a subprotocol of |
| 604 // BookmarkCollectionViewDelegate. |
| 605 - (void)bookmarkCollectionView:(BookmarkCollectionView*)view |
| 606 cell:(UICollectionViewCell*)cell |
| 607 addNodeForEditing:(const BookmarkNode*)node { |
| 608 [self insertEditNode:node atIndexPath:[self indexPathForCell:cell]]; |
| 609 [self updateEditingStateAnimated:YES]; |
| 610 } |
| 611 |
| 612 - (void)bookmarkCollectionView:(BookmarkCollectionView*)view |
| 613 cell:(UICollectionViewCell*)cell |
| 614 removeNodeForEditing:(const BookmarkNode*)node { |
| 615 [self removeEditNode:node atIndexPath:[self indexPathForCell:cell]]; |
| 616 if (_editNodes.size() == 0) |
| 617 [self setEditing:NO animated:YES]; |
| 618 else |
| 619 [self updateEditingStateAnimated:YES]; |
| 620 } |
| 621 |
| 622 - (const std::set<const BookmarkNode*>&)nodesBeingEdited { |
| 623 return _editNodes; |
| 624 } |
| 625 |
| 626 - (void)bookmarkCollectionViewDidScroll:(BookmarkCollectionView*)view { |
| 627 [self updateEditBarShadow]; |
| 628 } |
| 629 |
| 630 - (void)bookmarkCollectionView:(BookmarkCollectionView*)view |
| 631 selectedUrlForNavigation:(const GURL&)url { |
| 632 [self delegateDismiss:url]; |
| 633 } |
| 634 |
| 635 - (void)bookmarkCollectionView:(BookmarkCollectionView*)collectionView |
| 636 wantsMenuForBookmark:(const BookmarkNode*)node |
| 637 onView:(UIView*)view |
| 638 forCell:(BookmarkItemCell*)cell { |
| 639 DCHECK(!self.editViewController); |
| 640 DCHECK(!self.actionSheetCoordinator); |
| 641 self.actionSheetCoordinator = [[[ActionSheetCoordinator alloc] |
| 642 initWithBaseViewController:self |
| 643 title:nil |
| 644 message:nil |
| 645 rect:CGRectZero |
| 646 view:nil] autorelease]; |
| 647 base::WeakNSObject<BookmarkHomeHandsetViewController> weakSelf(self); |
| 648 |
| 649 // Select action. |
| 650 [self.actionSheetCoordinator |
| 651 addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_SELECT) |
| 652 action:^{ |
| 653 [weakSelf selectFirstNode:node withCell:cell]; |
| 654 weakSelf.get().actionSheetCoordinator = nil; |
| 655 } |
| 656 style:UIAlertActionStyleDefault]; |
| 657 |
| 658 // Edit action. |
| 659 [self.actionSheetCoordinator |
| 660 addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_EDIT) |
| 661 action:^{ |
| 662 [weakSelf editNode:node]; |
| 663 weakSelf.get().actionSheetCoordinator = nil; |
| 664 } |
| 665 style:UIAlertActionStyleDefault]; |
| 666 |
| 667 // Move action. |
| 668 [self.actionSheetCoordinator |
| 669 addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_MOVE) |
| 670 action:^{ |
| 671 std::set<const BookmarkNode*> nodes; |
| 672 nodes.insert(node); |
| 673 [weakSelf moveNodes:nodes]; |
| 674 weakSelf.get().actionSheetCoordinator = nil; |
| 675 } |
| 676 style:UIAlertActionStyleDefault]; |
| 677 |
| 678 // Delete action. |
| 679 [self.actionSheetCoordinator |
| 680 addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_DELETE) |
| 681 action:^{ |
| 682 std::set<const BookmarkNode*> nodes; |
| 683 nodes.insert(node); |
| 684 [weakSelf deleteNodes:nodes]; |
| 685 weakSelf.get().actionSheetCoordinator = nil; |
| 686 } |
| 687 style:UIAlertActionStyleDestructive]; |
| 688 |
| 689 // Cancel action. |
| 690 [self.actionSheetCoordinator |
| 691 addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL) |
| 692 action:^{ |
| 693 weakSelf.get().actionSheetCoordinator = nil; |
| 694 } |
| 695 style:UIAlertActionStyleCancel]; |
| 696 |
| 697 [self.actionSheetCoordinator start]; |
| 698 } |
| 699 |
| 700 - (void)bookmarkCollectionView:(BookmarkCollectionView*)view |
| 701 didLongPressCell:(UICollectionViewCell*)cell |
| 702 forBookmark:(const BookmarkNode*)node { |
| 703 [self selectFirstNode:node withCell:cell]; |
| 704 } |
| 705 |
| 706 - (BOOL)bookmarkCollectionViewShouldShowPromoCell: |
| 707 (BookmarkCollectionView*)collectionView { |
| 708 return self.bookmarkPromoController.promoState; |
| 709 } |
| 710 |
| 711 - (void)bookmarkCollectionViewShowSignIn:(BookmarkCollectionView*)view { |
| 712 [self.bookmarkPromoController showSignIn]; |
| 713 } |
| 714 |
| 715 - (void)bookmarkCollectionViewDismissPromo:(BookmarkCollectionView*)view { |
| 716 [self.bookmarkPromoController hidePromoCell]; |
| 717 } |
| 718 |
| 719 #pragma mark Action Sheet Callbacks |
| 720 |
| 721 - (void)selectFirstNode:(const BookmarkNode*)node |
| 722 withCell:(UICollectionViewCell*)cell { |
| 723 DCHECK(!self.editing); |
| 724 [self insertEditNode:node atIndexPath:[self indexPathForCell:cell]]; |
| 725 [self setEditing:YES animated:YES]; |
| 726 } |
| 727 |
| 728 - (void)editNode:(const BookmarkNode*)node { |
| 729 DCHECK(!self.editViewController); |
| 730 DCHECK(!self.folderEditor); |
| 731 UIViewController* editorController = nil; |
| 732 if (node->is_folder()) { |
| 733 BookmarkFolderEditorViewController* folderEditor = |
| 734 [BookmarkFolderEditorViewController |
| 735 folderEditorWithBookmarkModel:self.bookmarks |
| 736 folder:node |
| 737 browserState:self.browserState]; |
| 738 folderEditor.delegate = self; |
| 739 self.folderEditor = folderEditor; |
| 740 editorController = folderEditor; |
| 741 } else { |
| 742 base::scoped_nsobject<BookmarkEditViewController> controller([ |
| 743 [BookmarkEditViewController alloc] initWithBookmark:node |
| 744 browserState:self.browserState]); |
| 745 self.editViewController = controller; |
| 746 self.editViewController.delegate = self; |
| 747 editorController = self.editViewController; |
| 748 } |
| 749 DCHECK(editorController); |
| 750 base::scoped_nsobject<UINavigationController> navController( |
| 751 [[BookmarkNavigationController alloc] |
| 752 initWithRootViewController:editorController]); |
| 753 [navController setModalPresentationStyle:UIModalPresentationFormSheet]; |
| 754 [self presentViewController:navController animated:YES completion:NULL]; |
| 755 } |
| 756 |
| 757 - (void)moveNodes:(const std::set<const BookmarkNode*>&)nodes { |
| 758 DCHECK(!self.folderSelector); |
| 759 DCHECK(nodes.size() > 0); |
| 760 const BookmarkNode* editedNode = *(nodes.begin()); |
| 761 const BookmarkNode* selectedFolder = editedNode->parent(); |
| 762 self.folderSelector = base::scoped_nsobject<BookmarkFolderViewController>( |
| 763 [[BookmarkFolderViewController alloc] |
| 764 initWithBookmarkModel:self.bookmarks |
| 765 allowsNewFolders:YES |
| 766 editedNodes:nodes |
| 767 allowsCancel:YES |
| 768 selectedFolder:selectedFolder]); |
| 769 self.folderSelector.delegate = self; |
| 770 base::scoped_nsobject<UINavigationController> navController( |
| 771 [[BookmarkNavigationController alloc] |
| 772 initWithRootViewController:self.folderSelector]); |
| 773 [navController setModalPresentationStyle:UIModalPresentationFormSheet]; |
| 774 [self presentViewController:navController animated:YES completion:NULL]; |
| 775 } |
| 776 |
| 777 - (void)deleteNodes:(const std::set<const BookmarkNode*>&)nodes { |
| 778 DCHECK_GE(nodes.size(), 1u); |
| 779 bookmark_utils_ios::DeleteBookmarksWithUndoToast(nodes, self.bookmarks, |
| 780 self.browserState); |
| 781 } |
| 782 |
| 783 #pragma mark - BookmarkFolderCollectionViewDelegate |
| 784 |
| 785 - (void)bookmarkFolderCollectionView:(BookmarkFolderCollectionView*)view |
| 786 selectedFolderForNavigation:(const BookmarkNode*)folder { |
| 787 BookmarkMenuItem* menuItem = nil; |
| 788 if (view == self.folderView) { |
| 789 const BookmarkNode* parent = RootLevelFolderForNode(folder, self.bookmarks); |
| 790 menuItem = |
| 791 [BookmarkMenuItem folderMenuItemForNode:folder rootAncestor:parent]; |
| 792 } else { |
| 793 NOTREACHED(); |
| 794 return; |
| 795 } |
| 796 [self updatePrimaryMenuItem:menuItem animated:YES]; |
| 797 } |
| 798 |
| 799 #pragma mark - BookmarkEditViewControllerDelegate |
| 800 |
| 801 - (BOOL)bookmarkEditor:(BookmarkEditViewController*)controller |
| 802 shoudDeleteAllOccurencesOfBookmark:(const BookmarkNode*)bookmark { |
| 803 return NO; |
| 804 } |
| 805 |
| 806 - (void)bookmarkEditorWantsDismissal:(BookmarkEditViewController*)controller { |
| 807 self.editViewController.delegate = nil; |
| 808 self.editViewController = nil; |
| 809 [self dismissViewControllerAnimated:YES completion:NULL]; |
| 810 |
| 811 // The editViewController can be displayed from the menu button, or from the |
| 812 // edit button in edit mode. Either way, after it's dismissed, edit mode |
| 813 // should be off. |
| 814 [self setEditing:NO animated:NO]; |
| 815 } |
| 816 |
| 817 #pragma mark - BookmarkFolderViewControllerDelegate |
| 818 |
| 819 - (void)folderPicker:(BookmarkFolderViewController*)folderPicker |
| 820 didFinishWithFolder:(const BookmarkNode*)folder { |
| 821 DCHECK(folder); |
| 822 DCHECK(!folder->is_url()); |
| 823 DCHECK_GE(folderPicker.editedNodes.size(), 1u); |
| 824 |
| 825 bookmark_utils_ios::MoveBookmarksWithUndoToast( |
| 826 folderPicker.editedNodes, self.bookmarks, folder, self.browserState); |
| 827 |
| 828 [self setEditing:NO animated:NO]; |
| 829 [self dismissViewControllerAnimated:YES completion:NULL]; |
| 830 self.folderSelector.delegate = nil; |
| 831 self.folderSelector = nil; |
| 832 } |
| 833 |
| 834 - (void)folderPickerDidCancel:(BookmarkFolderViewController*)folderPicker { |
| 835 [self setEditing:NO animated:NO]; |
| 836 [self dismissViewControllerAnimated:YES completion:NULL]; |
| 837 self.folderSelector.delegate = nil; |
| 838 self.folderSelector = nil; |
| 839 } |
| 840 |
| 841 #pragma mark - BookmarkFolderEditorViewControllerDelegate |
| 842 |
| 843 - (void)bookmarkFolderEditor:(BookmarkFolderEditorViewController*)folderEditor |
| 844 didFinishEditingFolder:(const BookmarkNode*)folder { |
| 845 DCHECK(folder); |
| 846 [self dismissViewControllerAnimated:YES completion:nil]; |
| 847 self.folderEditor.delegate = nil; |
| 848 self.folderEditor = nil; |
| 849 } |
| 850 |
| 851 - (void)bookmarkFolderEditorDidDeleteEditedFolder: |
| 852 (BookmarkFolderEditorViewController*)folderEditor { |
| 853 [self dismissViewControllerAnimated:YES completion:nil]; |
| 854 self.folderEditor.delegate = nil; |
| 855 self.folderEditor = nil; |
| 856 } |
| 857 |
| 858 - (void)bookmarkFolderEditorDidCancel: |
| 859 (BookmarkFolderEditorViewController*)folderEditor { |
| 860 [self dismissViewControllerAnimated:YES completion:nil]; |
| 861 self.folderEditor.delegate = nil; |
| 862 self.folderEditor = nil; |
| 863 } |
| 864 |
| 865 #pragma mark - Internal Utility Methods |
| 866 |
| 867 - (void)deleteSelectedNodes { |
| 868 [self deleteNodes:_editNodes]; |
| 869 } |
| 870 |
| 871 #pragma mark - Navigation bar |
| 872 |
| 873 - (CGRect)navigationBarFrame { |
| 874 return CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), |
| 875 [BookmarkNavigationBar expectedContentViewHeight] + |
| 876 [self.topLayoutGuide length]); |
| 877 } |
| 878 |
| 879 - (void)updateNavigationBarAnimated:(BOOL)animated |
| 880 orientation:(UIInterfaceOrientation)orientation { |
| 881 CGFloat duration = animated ? bookmark_utils_ios::menuAnimationDuration : 0; |
| 882 [self updateNavigationBarWithDuration:duration orientation:orientation]; |
| 883 } |
| 884 |
| 885 - (void)updateNavigationBarWithDuration:(CGFloat)duration |
| 886 orientation:(UIInterfaceOrientation)orientation { |
| 887 if ([self.panelView userDrivenAnimationInProgress]) |
| 888 return; |
| 889 |
| 890 [self.navigationBar setTitle:[self.primaryMenuItem titleForNavigationBar]]; |
| 891 BOOL isMenuVisible = self.panelView.showingMenu; |
| 892 if ([self shouldShowEditButtonWithMenuVisibility:isMenuVisible]) |
| 893 [self.navigationBar showEditButtonWithAnimationDuration:duration]; |
| 894 else |
| 895 [self.navigationBar hideEditButtonWithAnimationDuration:duration]; |
| 896 |
| 897 if ([self shouldShowBackButtonInsteadOfMenuButton]) |
| 898 [self.navigationBar showBackButtonInsteadOfMenuButton:duration]; |
| 899 else |
| 900 [self.navigationBar showMenuButtonInsteadOfBackButton:duration]; |
| 901 } |
| 902 |
| 903 - (BOOL)shouldShowEditButtonWithMenuVisibility:(BOOL)visible { |
| 904 if (visible) |
| 905 return NO; |
| 906 if (self.primaryMenuItem.type != bookmarks::MenuItemFolder) |
| 907 return NO; |
| 908 // The type is MenuItemFolder, so it is safe to access |folder|. |
| 909 return !self.bookmarks->is_permanent_node(self.primaryMenuItem.folder); |
| 910 } |
| 911 |
| 912 - (BOOL)shouldShowBackButtonInsteadOfMenuButton { |
| 913 if (self.primaryMenuItem.type != bookmarks::MenuItemFolder) |
| 914 return NO; |
| 915 // The type is MenuItemFolder, so it is safe to access |folder|. |
| 916 const BookmarkNode* folder = self.primaryMenuItem.folder; |
| 917 // Show the back button iff the folder or its immediate parent is a permanent |
| 918 // primary folder. |
| 919 BOOL isTopFolder = IsPrimaryPermanentNode(folder, self.bookmarks) || |
| 920 IsPrimaryPermanentNode(folder->parent(), self.bookmarks); |
| 921 return !isTopFolder; |
| 922 } |
| 923 |
| 924 #pragma mark Navigation Bar Callbacks |
| 925 |
| 926 - (void)navigationBarWantsEditing:(id)sender { |
| 927 DCHECK(self.primaryMenuItem.type == bookmarks::MenuItemFolder); |
| 928 const BookmarkNode* folder = self.primaryMenuItem.folder; |
| 929 BookmarkFolderEditorViewController* folderEditor = |
| 930 [BookmarkFolderEditorViewController |
| 931 folderEditorWithBookmarkModel:self.bookmarks |
| 932 folder:folder |
| 933 browserState:self.browserState]; |
| 934 folderEditor.delegate = self; |
| 935 self.folderEditor = folderEditor; |
| 936 |
| 937 base::scoped_nsobject<BookmarkNavigationController> navController( |
| 938 [[BookmarkNavigationController alloc] |
| 939 initWithRootViewController:self.folderEditor]); |
| 940 [navController setModalPresentationStyle:UIModalPresentationFormSheet]; |
| 941 [self presentViewController:navController animated:YES completion:NULL]; |
| 942 } |
| 943 |
| 944 - (void)navigationBarCancel:(id)sender { |
| 945 [self delegateDismiss:GURL()]; |
| 946 } |
| 947 |
| 948 - (void)navigationBarToggledMenu:(id)sender { |
| 949 if ([self.panelView userDrivenAnimationInProgress]) |
| 950 return; |
| 951 |
| 952 if (self.panelView.showingMenu) |
| 953 [self hideMenuAnimated:YES updateNavigationBar:YES]; |
| 954 else |
| 955 [self showMenuAnimated:YES]; |
| 956 } |
| 957 |
| 958 - (void)navigationBarBack:(id)sender { |
| 959 DCHECK([self shouldShowBackButtonInsteadOfMenuButton]); |
| 960 |
| 961 // Go to the parent folder. |
| 962 DCHECK(self.primaryMenuItem.type == bookmarks::MenuItemFolder); |
| 963 const BookmarkNode* parentFolder = self.primaryMenuItem.folder->parent(); |
| 964 const BookmarkNode* rootAncestor = |
| 965 RootLevelFolderForNode(parentFolder, self.bookmarks); |
| 966 BookmarkMenuItem* menuItem = |
| 967 [BookmarkMenuItem folderMenuItemForNode:parentFolder |
| 968 rootAncestor:rootAncestor]; |
| 969 [self updatePrimaryMenuItem:menuItem animated:YES]; |
| 970 } |
| 971 |
| 972 #pragma mark - Other internal methods. |
| 973 |
| 974 - (CGRect)frameForPrimaryView { |
| 975 CGFloat margin; |
| 976 if (self.editing) |
| 977 margin = CGRectGetMaxY(self.editingBar.frame); |
| 978 else |
| 979 margin = CGRectGetMaxY(self.navigationBar.frame); |
| 980 CGFloat height = CGRectGetHeight(self.view.frame) - margin; |
| 981 CGFloat width = CGRectGetWidth(self.view.frame); |
| 982 return CGRectMake(0, margin, width, height); |
| 983 } |
| 984 |
| 985 - (void)updateUIForInterfaceOrientation:(UIInterfaceOrientation)orientation |
| 986 duration:(NSTimeInterval)duration { |
| 987 [[self primaryView] changeOrientation:orientation]; |
| 988 [self updateNavigationBarWithDuration:duration orientation:orientation]; |
| 989 } |
| 990 |
| 991 - (void)showMenuAnimated:(BOOL)animated { |
| 992 [self.menuView setScrollsToTop:YES]; |
| 993 [[self primaryView] setScrollsToTop:NO]; |
| 994 self.scrollingMenuToTop = YES; |
| 995 [self.panelView showMenuAnimated:animated]; |
| 996 [self updateNavigationBarAnimated:animated |
| 997 orientation:GetInterfaceOrientation()]; |
| 998 } |
| 999 |
| 1000 - (void)hideMenuAnimated:(BOOL)animated updateNavigationBar:(BOOL)update { |
| 1001 [self.menuView setScrollsToTop:NO]; |
| 1002 [[self primaryView] setScrollsToTop:YES]; |
| 1003 self.scrollingMenuToTop = NO; |
| 1004 [self.panelView hideMenuAnimated:animated]; |
| 1005 if (update) { |
| 1006 UIInterfaceOrientation orient = GetInterfaceOrientation(); |
| 1007 [self updateNavigationBarAnimated:animated orientation:orient]; |
| 1008 } |
| 1009 } |
| 1010 |
| 1011 #pragma mark - BookmarkHomeViewController |
| 1012 |
| 1013 - (void)dismissModals:(BOOL)animated { |
| 1014 [self.actionSheetCoordinator stop]; |
| 1015 self.actionSheetCoordinator = nil; |
| 1016 } |
| 1017 |
| 1018 #pragma mark - BookmarkPanelViewDelegate |
| 1019 |
| 1020 - (void)bookmarkPanelView:(BookmarkPanelView*)view |
| 1021 willShowMenu:(BOOL)showMenu |
| 1022 withAnimationDuration:(CGFloat)duration { |
| 1023 if (showMenu) { |
| 1024 [self.menuView setScrollsToTop:YES]; |
| 1025 [[self primaryView] setScrollsToTop:NO]; |
| 1026 self.scrollingMenuToTop = YES; |
| 1027 } else { |
| 1028 [self.menuView setScrollsToTop:NO]; |
| 1029 [[self primaryView] setScrollsToTop:YES]; |
| 1030 self.scrollingMenuToTop = NO; |
| 1031 } |
| 1032 |
| 1033 if ([self shouldShowEditButtonWithMenuVisibility:showMenu]) |
| 1034 [self.navigationBar showEditButtonWithAnimationDuration:duration]; |
| 1035 else |
| 1036 [self.navigationBar hideEditButtonWithAnimationDuration:duration]; |
| 1037 } |
| 1038 |
| 1039 - (void)bookmarkPanelView:(BookmarkPanelView*)view |
| 1040 updatedMenuVisibility:(CGFloat)visibility { |
| 1041 // As the menu becomes more visible, the edit button will always fade out. |
| 1042 if ([self shouldShowEditButtonWithMenuVisibility:NO]) |
| 1043 [self.navigationBar updateEditButtonVisibility:1 - visibility]; |
| 1044 else |
| 1045 [self.navigationBar updateEditButtonVisibility:0]; |
| 1046 } |
| 1047 |
| 1048 #pragma mark - BookmarkModelBridgeObserver |
| 1049 |
| 1050 - (void)bookmarkModelLoaded { |
| 1051 if (![self isViewLoaded]) |
| 1052 return; |
| 1053 |
| 1054 DCHECK(self.waitForModelView); |
| 1055 base::WeakNSObject<BookmarkHomeHandsetViewController> weakSelf(self); |
| 1056 [self.waitForModelView stopWaitingWithCompletion:^{ |
| 1057 base::scoped_nsobject<BookmarkHomeHandsetViewController> strongSelf( |
| 1058 [weakSelf retain]); |
| 1059 // Early return if the controller has been deallocated. |
| 1060 if (!strongSelf) |
| 1061 return; |
| 1062 [UIView animateWithDuration:0.2 |
| 1063 animations:^{ |
| 1064 strongSelf.get().waitForModelView.alpha = 0.0; |
| 1065 } |
| 1066 completion:^(BOOL finished) { |
| 1067 [strongSelf.get().waitForModelView removeFromSuperview]; |
| 1068 strongSelf.get().waitForModelView = nil; |
| 1069 }]; |
| 1070 [strongSelf loadBookmarkViews]; |
| 1071 }]; |
| 1072 } |
| 1073 |
| 1074 - (void)bookmarkNodeChanged:(const BookmarkNode*)node { |
| 1075 // The title of the folder may have changed. |
| 1076 if (self.primaryMenuItem.type == bookmarks::MenuItemFolder && |
| 1077 self.primaryMenuItem.folder == node) { |
| 1078 UIInterfaceOrientation orient = GetInterfaceOrientation(); |
| 1079 [self updateNavigationBarAnimated:NO orientation:orient]; |
| 1080 } |
| 1081 } |
| 1082 |
| 1083 - (void)bookmarkNodeChildrenChanged:(const BookmarkNode*)bookmarkNode { |
| 1084 // The node has not changed, but the ordering and existence of its children |
| 1085 // have changed. |
| 1086 } |
| 1087 |
| 1088 - (void)bookmarkNode:(const BookmarkNode*)bookmarkNode |
| 1089 movedFromParent:(const BookmarkNode*)oldParent |
| 1090 toParent:(const BookmarkNode*)newParent { |
| 1091 // The node has moved to a new parent folder. |
| 1092 } |
| 1093 |
| 1094 - (void)bookmarkNodeDeleted:(const BookmarkNode*)node |
| 1095 fromFolder:(const BookmarkNode*)folder { |
| 1096 [self removeEditNode:node atIndexPath:nil]; |
| 1097 } |
| 1098 |
| 1099 - (void)bookmarkModelRemovedAllNodes { |
| 1100 // All non-permanent nodes have been removed. |
| 1101 [self setEditing:NO animated:YES]; |
| 1102 } |
| 1103 |
| 1104 - (UIStatusBarStyle)preferredStatusBarStyle { |
| 1105 return self.editing ? UIStatusBarStyleLightContent : UIStatusBarStyleDefault; |
| 1106 } |
| 1107 |
| 1108 // There are 2 UIViewController methods that could be overridden. This one and |
| 1109 // willAnimateRotationToInterfaceOrientation:duration:. The latter is called |
| 1110 // during an "animation block", and its unclear that reloading a collection view |
| 1111 // will always work duratin an "animation block", although it appears to work at |
| 1112 // first glance. |
| 1113 - (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)orientation |
| 1114 duration:(NSTimeInterval)duration { |
| 1115 [self updateUIForInterfaceOrientation:orientation duration:duration]; |
| 1116 } |
| 1117 |
| 1118 - (void)viewWillAppear:(BOOL)animated { |
| 1119 [super viewWillAppear:animated]; |
| 1120 UIInterfaceOrientation orient = GetInterfaceOrientation(); |
| 1121 [self updateUIForInterfaceOrientation:orient duration:0]; |
| 1122 if (self.cachedContentPosition) { |
| 1123 [[self primaryView] |
| 1124 applyContentPosition:[self.cachedContentPosition floatValue]]; |
| 1125 self.cachedContentPosition = nil; |
| 1126 } |
| 1127 } |
| 1128 |
| 1129 - (void)viewWillLayoutSubviews { |
| 1130 [super viewWillLayoutSubviews]; |
| 1131 // Invalidate the layout of the collection view, as its frame might have |
| 1132 // changed. Normally, this can be done automatically when the collection view |
| 1133 // layout returns YES to -shouldInvalidateLayoutForBoundsChange:. |
| 1134 // Unfortunately, it doesn't happen for all bounds changes. E.g. on iPhone 6 |
| 1135 // Plus landscape, the width of the collection is too large when created, then |
| 1136 // resized down before being presented. Yet, the bounds change doesn't yield a |
| 1137 // call to the flow layout, thus not invalidating the layout correctly. |
| 1138 [[self primaryView].collectionView.collectionViewLayout invalidateLayout]; |
| 1139 |
| 1140 self.navigationBar.frame = [self navigationBarFrame]; |
| 1141 self.editingBar.frame = [self navigationBarFrame]; |
| 1142 [self.panelView setFrame:[self frameForPrimaryView]]; |
| 1143 } |
| 1144 |
| 1145 #pragma mark - Internal non-UI Methods |
| 1146 |
| 1147 - (void)delegateDismiss:(const GURL&)url { |
| 1148 if ([self primaryView]) { |
| 1149 bookmark_utils_ios::CachePosition( |
| 1150 [[self primaryView] contentPositionInPortraitOrientation], |
| 1151 [self primaryMenuItem]); |
| 1152 } |
| 1153 [self.delegate bookmarkHomeViewControllerWantsDismissal:self |
| 1154 navigationToUrl:url]; |
| 1155 } |
| 1156 |
| 1157 #pragma mark - BookmarkPromoControllerDelegate |
| 1158 |
| 1159 - (void)promoStateChanged:(BOOL)promoEnabled { |
| 1160 [self.allItemsView.collectionView reloadData]; |
| 1161 // This is required to workaround a crash seen once on iOS 7.1 when |
| 1162 // the collection gets two reloadData without getting a call to layout |
| 1163 // subviews, the collection view will reuse some cached data for the perfoming |
| 1164 // the layout which are invalid after the second call to reloadData. |
| 1165 // Forcing the layout invalidation after each reloadData seems to fix the |
| 1166 // issue. |
| 1167 [self.allItemsView.collectionView.collectionViewLayout invalidateLayout]; |
| 1168 |
| 1169 [self.folderView |
| 1170 promoStateChangedAnimated:self.folderView == [self primaryView]]; |
| 1171 } |
| 1172 |
| 1173 - (NSIndexPath*)indexPathForCell:(UICollectionViewCell*)cell { |
| 1174 DCHECK([self primaryView].collectionView); |
| 1175 NSIndexPath* indexPath = |
| 1176 [[self primaryView].collectionView indexPathForCell:cell]; |
| 1177 return indexPath; |
| 1178 } |
| 1179 |
| 1180 @end |
OLD | NEW |