| Index: ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm
|
| diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm b/ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6c0e9ce2b9bc59b20e444f5c303899b441a1bb74
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.mm
|
| @@ -0,0 +1,1345 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_home_tablet_ntp_controller.h"
|
| +
|
| +#include <memory>
|
| +
|
| +#include "base/ios/block_types.h"
|
| +#import "base/ios/weak_nsobject.h"
|
| +#include "base/logging.h"
|
| +#include "base/mac/objc_property_releaser.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#include "base/metrics/user_metrics.h"
|
| +#include "base/metrics/user_metrics_action.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "components/bookmarks/browser/bookmark_model.h"
|
| +#include "components/strings/grit/components_strings.h"
|
| +#include "google_apis/gaia/google_service_auth_error.h"
|
| +#include "ios/chrome/browser/bookmarks/bookmark_model_factory.h"
|
| +#include "ios/chrome/browser/bookmarks/bookmarks_utils.h"
|
| +#include "ios/chrome/browser/browser_state/chrome_browser_state.h"
|
| +#import "ios/chrome/browser/metrics/new_tab_page_uma.h"
|
| +#import "ios/chrome/browser/ui/alert_coordinator/action_sheet_coordinator.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bars/bookmark_editing_bar.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bars/bookmark_navigation_bar.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_all_collection_view.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_edit_view_controller.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_folder_collection_view.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_folder_view_controller.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_home_primary_view.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_home_waiting_view.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_menu_item.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_menu_view.h"
|
| +#include "ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_navigation_controller.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_panel_view.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_promo_controller.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
|
| +#import "ios/chrome/browser/ui/rtl_geometry.h"
|
| +#import "ios/chrome/browser/ui/uikit_ui_util.h"
|
| +#import "ios/chrome/browser/ui/url_loader.h"
|
| +#include "ios/chrome/grit/ios_strings.h"
|
| +#include "ios/web/public/referrer.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +#include "ui/base/l10n/l10n_util_mac.h"
|
| +#include "ui/base/page_transition_types.h"
|
| +
|
| +using bookmarks::BookmarkNode;
|
| +
|
| +namespace {
|
| +// The width of the bookmark menu, displaying the different sections.
|
| +const CGFloat kMenuWidth = 264.0;
|
| +// The margin on top to the navigation bar.
|
| +const CGFloat kNavigationBarTopMargin = 8.0;
|
| +} // namespace
|
| +
|
| +// A simple UIView subclass to pass on relayout information to its delegate.
|
| +@protocol ContentViewDelegate<NSObject>
|
| +- (void)willLayoutSubviews;
|
| +@end
|
| +
|
| +@interface ContentView : UIView
|
| +@property(nonatomic, assign) id<ContentViewDelegate> delegate;
|
| +@end
|
| +
|
| +@implementation ContentView
|
| +@synthesize delegate = _delegate;
|
| +
|
| +- (void)layoutSubviews {
|
| + [self.delegate willLayoutSubviews];
|
| + [super layoutSubviews];
|
| +}
|
| +@end
|
| +
|
| +@interface BookmarkHomeTabletNTPController ()<
|
| + BookmarkEditViewControllerDelegate,
|
| + BookmarkFolderCollectionViewDelegate,
|
| + BookmarkFolderEditorViewControllerDelegate,
|
| + BookmarkFolderViewControllerDelegate,
|
| + BookmarkMenuViewDelegate,
|
| + BookmarkModelBridgeObserver,
|
| + BookmarkPromoControllerDelegate,
|
| + ContentViewDelegate> {
|
| + // Bridge to register for bookmark changes.
|
| + std::unique_ptr<bookmarks::BookmarkModelBridge> _bridge;
|
| + ios::ChromeBrowserState* _browserState; // Weak.
|
| + id<UrlLoader> _loader; // Weak.
|
| +
|
| + // The following 2 ivars both represent the set of nodes being edited.
|
| + // The set is for fast lookup.
|
| + // The vector maintains the order that edit nodes were added.
|
| + // Use the relevant instance methods to modify these two ivars in tandem.
|
| + // DO NOT modify these two ivars directly.
|
| + std::set<const BookmarkNode*> _editNodes;
|
| + std::vector<const BookmarkNode*> _editNodesOrdered;
|
| +
|
| + base::mac::ObjCPropertyReleaser
|
| + _propertyReleaser_BookmarkHomeTabletNTPController;
|
| +}
|
| +
|
| +@property(nonatomic, retain) BookmarkPanelView* panelView;
|
| +
|
| +#pragma mark - Properties and methods akin to BookmarkHomeViewController
|
| +
|
| +// Whether the view controller is in editing mode.
|
| +@property(nonatomic, assign) BOOL editing;
|
| +// The set of edited index paths.
|
| +@property(nonatomic, retain) NSMutableArray* editIndexPaths;
|
| +// The bookmark model used.
|
| +@property(nonatomic, assign, readonly) bookmarks::BookmarkModel* bookmarks;
|
| +// The user's browser state model used.
|
| +@property(nonatomic, assign, readonly)
|
| + ios::ChromeBrowserState* browserState; // from superclass.
|
| +
|
| +// Replaces |_editNodes| and |_editNodesOrdered| with new container objects.
|
| +- (void)resetEditNodes;
|
| +// Adds |node| corresponding to a |cell| if it isn't already present.
|
| +- (void)insertEditNode:(const BookmarkNode*)node
|
| + atIndexPath:(NSIndexPath*)indexPath;
|
| +// Removes |node| corresponding to a |cell| if it's present.
|
| +- (void)removeEditNode:(const BookmarkNode*)node
|
| + atIndexPath:(NSIndexPath*)indexPath;
|
| +// This method updates the property, and resets the edit nodes.
|
| +- (void)setEditing:(BOOL)editing animated:(BOOL)animated;
|
| +
|
| +#pragma mark - Properties and methods akin to BookmarkHomeHandsetViewController
|
| +
|
| +// This views holds the primary content of this controller. At any point in
|
| +// time, it contains exactly one of the BookmarkCollectionView subclasses.
|
| +@property(nonatomic, retain) ContentView* contentView;
|
| +// The possible views that can be shown from the menu.
|
| +@property(nonatomic, retain) BookmarkAllCollectionView* allItemsView;
|
| +@property(nonatomic, retain) BookmarkFolderCollectionView* folderView;
|
| +// This view is created and used if the model is not fully loaded yet by the
|
| +// time this controller starts.
|
| +@property(nonatomic, retain) BookmarkHomeWaitingView* waitForModelView;
|
| +
|
| +// The menu with all the folders and special entries.
|
| +@property(nonatomic, retain) BookmarkMenuView* menuView;
|
| +// At any point in time, there is exactly one collection view whose view is part
|
| +// of the view hierarchy. This property determines which collection view is
|
| +// visible. Not by accident, this property also reflects the selected menu item
|
| +// in the BookmarkMenuView.
|
| +@property(nonatomic, retain) BookmarkMenuItem* primaryMenuItem;
|
| +// When the view is first shown on the screen, this property represents the
|
| +// cached value of the y of the content offset of the primary view. This
|
| +// property is set to nil after it is used.
|
| +@property(nonatomic, retain)
|
| + NSNumber* cachedContentPosition; // FIXME: INACTIVE
|
| +
|
| +// The navigation bar sits on top of the main content.
|
| +@property(nonatomic, retain) BookmarkNavigationBar* navigationBar;
|
| +// The editing bar present when items are selected.
|
| +@property(nonatomic, retain) BookmarkEditingBar* editingBar;
|
| +
|
| +// The action sheet coordinator used when trying to edit a single bookmark.
|
| +@property(nonatomic, retain) ActionSheetCoordinator* actionSheetCoordinator;
|
| +// The view controller used to view and edit a single bookmark.
|
| +@property(nonatomic, retain) BookmarkEditViewController* editViewController;
|
| +// The view controller used to pick a folder in which to move the selected
|
| +// bookmarks.
|
| +@property(nonatomic, retain) BookmarkFolderViewController* folderSelector;
|
| +// The view controller to present when editing the current folder.
|
| +@property(nonatomic, retain)
|
| + BookmarkFolderEditorViewController* folderEditor; // FIX
|
| +// The controller managing the display of the promo cell and the promo view
|
| +// controller.
|
| +@property(nonatomic, retain) BookmarkPromoController* bookmarkPromoController;
|
| +
|
| +#pragma mark Specific to this class.
|
| +
|
| +// Either the menu or the primaryView can scrollToTop.
|
| +@property(nonatomic, assign) BOOL scrollToTop;
|
| +
|
| +// Opens the url.
|
| +- (void)loadURL:(const GURL&)url;
|
| +#pragma mark View loading, laying out, and switching.
|
| +// This method is called if the view needs to be loaded and the model is not
|
| +// ready yet.
|
| +- (void)loadWaitingView;
|
| +// This method should be called at most once in the life-cycle of the class.
|
| +// It should be called at the soonest possible time after the view has been
|
| +// loaded, and the bookmark model is loaded.
|
| +- (void)loadBookmarkViews;
|
| +// If the view doesn't exist, create it.
|
| +- (void)ensureAllViewExists;
|
| +- (void)ensureFolderViewExists;
|
| +// Updates the property 'primaryMenuItem'.
|
| +// Updates the UI to reflect the new state of 'primaryMenuItem'.
|
| +- (void)updatePrimaryMenuItem:(BookmarkMenuItem*)menuItem
|
| + animated:(BOOL)animated;
|
| +// The active collection view that corresponds to primaryMenuItem.
|
| +- (UIView<BookmarkHomePrimaryView>*)primaryView;
|
| +// Returns whether the menu should be in a side panel that slides in.
|
| +- (BOOL)shouldPresentMenuInSlideInPanel;
|
| +// Returns the width of the menu.
|
| +- (CGFloat)menuWidth;
|
| +// Returns the leading margin of the primary view.
|
| +- (CGFloat)primaryViewLeadingMargin;
|
| +// Moves the menu and primary view to their correct parent views depending on
|
| +// the layout.
|
| +- (void)moveMenuAndPrimaryViewToAdequateParent;
|
| +// Updates the frame of the primary view.
|
| +- (void)refreshFrameOfPrimaryView;
|
| +// Returns the frame of the primary view.
|
| +- (CGRect)frameForPrimaryView;
|
| +
|
| +#pragma mark Editing related methods
|
| +// This method statelessly updates the editing top bar from |_editNodes| and
|
| +// |editing|.
|
| +- (void)updateEditingStateAnimated:(BOOL)animated;
|
| +// Shows or hides the editing bar.
|
| +- (void)showEditingBarAnimated:(BOOL)animated;
|
| +- (void)hideEditingBarAnimated:(BOOL)animated;
|
| +// Instaneously updates the shadow of the edit bar.
|
| +// This method should be called anytime:
|
| +// (1)|editing| property changes.
|
| +// (2)The primary view changes.
|
| +// (3)The primary view's collection view is scrolled.
|
| +// When |editing| is NO, the shadow is never shown.
|
| +- (void)updateEditBarShadow;
|
| +
|
| +#pragma mark Editing bar callbacks
|
| +// The cancel button was tapped on the editing bar.
|
| +- (void)editingBarCancel;
|
| +// The move button was tapped on the editing bar.
|
| +- (void)editingBarMove;
|
| +// The delete button was tapped on the editing bar.
|
| +- (void)editingBarDelete;
|
| +// The edit button was tapped on the editing bar.
|
| +- (void)editingBarEdit;
|
| +// The menu button is pressed on the editing bar.
|
| +- (void)toggleMenuAnimated;
|
| +
|
| +#pragma mark Action sheet callbacks
|
| +// Enters into edit mode by selecting the given node corresponding to the
|
| +// given cell.
|
| +- (void)selectFirstNode:(const BookmarkNode*)node
|
| + withCell:(UICollectionViewCell*)cell;
|
| +// Opens the editor on the given node and cell.
|
| +- (void)editNode:(const BookmarkNode*)node withCell:(UICollectionViewCell*)cell;
|
| +// Opens the folder move editor for the given node.
|
| +- (void)moveNodes:(const std::set<const BookmarkNode*>&)nodes;
|
| +// Deletes the current node.
|
| +- (void)deleteNodes:(const std::set<const BookmarkNode*>&)nodes;
|
| +
|
| +#pragma mark private utility methods
|
| +// Deletes the nodes, and presents a toast with an undo button.
|
| +- (void)deleteSelectedNodes;
|
| +
|
| +#pragma mark Navigation bar
|
| +// Updates the UI of the navigation bar with the primaryMenuItem.
|
| +// This method should be called anytime:
|
| +// (1)The primary view changes.
|
| +// (2)The primary view has type folder, and the relevant folder has changed.
|
| +// (3)The interface orientation changes.
|
| +// (4)viewWillAppear, as the interface orientation may have changed.
|
| +- (void)updateNavigationBarAnimated:(BOOL)animated
|
| + orientation:(UIInterfaceOrientation)orientation;
|
| +- (void)updateNavigationBarWithDuration:(CGFloat)duration
|
| + orientation:(UIInterfaceOrientation)orientation;
|
| +// Whether the edit button on the navigation bar should be shown.
|
| +- (BOOL)shouldShowEditButton;
|
| +// Whether the back button on the navigation bar should be shown.
|
| +- (BOOL)shouldShowBackButton;
|
| +// Called when the edit button is pressed on the navigation bar.
|
| +- (void)navigationBarWantsEditing:(id)sender;
|
| +// Called when the back button is pressed on the navigation bar.
|
| +- (void)navigationBarBack:(id)sender;
|
| +
|
| +// TODO(crbug.com/450646): This should not be needed but require refactoring of
|
| +// the BookmarkCollectionViewDelegate.
|
| +- (NSIndexPath*)indexPathForCell:(UICollectionViewCell*)cell;
|
| +
|
| +@end
|
| +
|
| +@implementation BookmarkHomeTabletNTPController
|
| +@synthesize editing = _editing;
|
| +@synthesize editIndexPaths = _editIndexPaths;
|
| +@synthesize bookmarks = _bookmarks;
|
| +
|
| +@synthesize contentView = _contentView;
|
| +@synthesize allItemsView = _allItemsView;
|
| +@synthesize folderView = _folderView;
|
| +@synthesize waitForModelView = _waitForModelView;
|
| +
|
| +@synthesize menuView = _menuView;
|
| +@synthesize primaryMenuItem = _primaryMenuItem;
|
| +@synthesize cachedContentPosition = _cachedContentPosition;
|
| +@synthesize navigationBar = _navigationBar;
|
| +@synthesize editingBar = _editingBar;
|
| +@synthesize panelView = _panelView;
|
| +
|
| +@synthesize actionSheetCoordinator = _actionSheetCoordinator;
|
| +@synthesize editViewController = _editViewController;
|
| +@synthesize folderSelector = _folderSelector;
|
| +@synthesize folderEditor = _folderEditor;
|
| +@synthesize bookmarkPromoController = _bookmarkPromoController;
|
| +
|
| +@synthesize scrollToTop = _scrollToTop;
|
| +
|
| +// Property declared in NewTabPagePanelProtocol.
|
| +@synthesize delegate = _delegate;
|
| +
|
| +- (id)initWithLoader:(id<UrlLoader>)loader
|
| + browserState:(ios::ChromeBrowserState*)browserState {
|
| + self = [super init];
|
| + if (self) {
|
| + DCHECK(browserState);
|
| + _browserState = browserState->GetOriginalChromeBrowserState();
|
| + _loader = loader;
|
| + _propertyReleaser_BookmarkHomeTabletNTPController.Init(
|
| + self, [BookmarkHomeTabletNTPController class]);
|
| +
|
| + _bookmarks = ios::BookmarkModelFactory::GetForBrowserState(_browserState);
|
| + _bridge.reset(new bookmarks::BookmarkModelBridge(self, _bookmarks));
|
| + _editIndexPaths = [[NSMutableArray alloc] init];
|
| + // It is important to initialize the promo controller with the browser state
|
| + // passed in, as it could be incognito.
|
| + _bookmarkPromoController =
|
| + [[BookmarkPromoController alloc] initWithBrowserState:browserState
|
| + delegate:self];
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)dealloc {
|
| + _contentView.delegate = nil;
|
| +
|
| + _allItemsView.delegate = nil;
|
| + _folderView.delegate = nil;
|
| +
|
| + _menuView.delegate = nil;
|
| +
|
| + _editViewController.delegate = nil;
|
| + _folderSelector.delegate = nil;
|
| +
|
| + [super dealloc];
|
| +}
|
| +
|
| +- (ios::ChromeBrowserState*)browserState {
|
| + return _browserState;
|
| +}
|
| +
|
| +#pragma mark - ContentViewDelegate method.
|
| +
|
| +- (void)willLayoutSubviews {
|
| + if (![self primaryView] && ![self primaryMenuItem] &&
|
| + self.bookmarks->loaded()) {
|
| + BookmarkMenuItem* item = nil;
|
| + CGFloat position = 0;
|
| + BOOL found =
|
| + bookmark_utils_ios::GetPositionCache(self.bookmarks, &item, &position);
|
| + if (!found)
|
| + item = [self.menuView defaultMenuItem];
|
| +
|
| + [self updatePrimaryMenuItem:item animated:NO];
|
| + }
|
| +
|
| + [self moveMenuAndPrimaryViewToAdequateParent];
|
| + CGFloat leadingMargin = [self primaryViewLeadingMargin];
|
| +
|
| + // Prevent the panelView from hijacking the gestures so that the
|
| + // NTPController's scrollview can still scroll with the gestures.
|
| + [self.panelView enableSideSwiping:NO];
|
| +
|
| + CGFloat width = self.contentView.bounds.size.width;
|
| + LayoutRect navBarLayout =
|
| + LayoutRectMake(leadingMargin, width, 0, width - leadingMargin,
|
| + CGRectGetHeight([self navigationBarFrame]));
|
| + self.navigationBar.frame = LayoutRectGetRect(navBarLayout);
|
| + [self.editingBar setFrame:[self editingBarFrame]];
|
| +
|
| + UIInterfaceOrientation orient = GetInterfaceOrientation();
|
| + [self refreshFrameOfPrimaryView];
|
| + [[self primaryView] changeOrientation:orient];
|
| + [self updateNavigationBarWithDuration:0 orientation:orient];
|
| + if (![self shouldPresentMenuInSlideInPanel])
|
| + [self updateMenuViewLayout];
|
| +}
|
| +
|
| +#pragma mark HomeViewController simili methods.
|
| +
|
| +- (void)resetEditNodes {
|
| + _editNodes = std::set<const BookmarkNode*>();
|
| + _editNodesOrdered = std::vector<const BookmarkNode*>();
|
| + [self.editIndexPaths removeAllObjects];
|
| +}
|
| +
|
| +- (void)insertEditNode:(const BookmarkNode*)node
|
| + atIndexPath:(NSIndexPath*)indexPath {
|
| + if (_editNodes.find(node) != _editNodes.end())
|
| + return;
|
| + _editNodes.insert(node);
|
| + _editNodesOrdered.push_back(node);
|
| + if (indexPath) {
|
| + [self.editIndexPaths addObject:indexPath];
|
| + } else {
|
| + // Insert null to keep the index valid.
|
| + [self.editIndexPaths addObject:[NSNull null]];
|
| + }
|
| +}
|
| +
|
| +- (void)removeEditNode:(const BookmarkNode*)node
|
| + atIndexPath:(NSIndexPath*)indexPath {
|
| + if (_editNodes.find(node) == _editNodes.end())
|
| + return;
|
| + _editNodes.erase(node);
|
| + std::vector<const BookmarkNode*>::iterator it =
|
| + std::find(_editNodesOrdered.begin(), _editNodesOrdered.end(), node);
|
| + DCHECK(it != _editNodesOrdered.end());
|
| + _editNodesOrdered.erase(it);
|
| + if (indexPath) {
|
| + [self.editIndexPaths removeObject:indexPath];
|
| + } else {
|
| + // If we don't have the cell, we remove it by using its index.
|
| + const NSUInteger index = std::distance(_editNodesOrdered.begin(), it);
|
| + if (index < self.editIndexPaths.count) {
|
| + [self.editIndexPaths removeObjectAtIndex:index];
|
| + }
|
| + }
|
| +
|
| + if (_editNodes.size() == 0)
|
| + [self setEditing:NO animated:YES];
|
| + else
|
| + [self updateEditingStateAnimated:YES];
|
| +}
|
| +
|
| +#pragma mark - private methods
|
| +
|
| +- (void)loadURL:(const GURL&)url {
|
| + if (url == GURL() || url.SchemeIs(url::kJavaScriptScheme))
|
| + return;
|
| +
|
| + new_tab_page_uma::RecordAction(self.browserState,
|
| + new_tab_page_uma::ACTION_OPENED_BOOKMARK);
|
| + base::RecordAction(base::UserMetricsAction("MobileNTPBookmark"));
|
| + [_loader loadURL:url
|
| + referrer:web::Referrer()
|
| + transition:ui::PAGE_TRANSITION_AUTO_BOOKMARK
|
| + rendererInitiated:NO];
|
| +}
|
| +
|
| +#pragma mark - Views
|
| +
|
| +- (void)loadWaitingView {
|
| + DCHECK(!self.waitForModelView);
|
| + DCHECK(self.contentView);
|
| +
|
| + // Present a waiting view.
|
| + base::scoped_nsobject<BookmarkHomeWaitingView> waitingView(
|
| + [[BookmarkHomeWaitingView alloc] initWithFrame:self.view.bounds]);
|
| + self.waitForModelView = waitingView;
|
| + [self.view addSubview:self.waitForModelView];
|
| + [self.waitForModelView startWaiting];
|
| +}
|
| +
|
| +- (void)updateMenuViewLayout {
|
| + LayoutRect menuLayout =
|
| + LayoutRectMake(0, self.view.bounds.size.width, 0, self.menuWidth,
|
| + self.view.bounds.size.height);
|
| + self.menuView.frame = LayoutRectGetRect(menuLayout);
|
| +}
|
| +
|
| +- (void)loadBookmarkViews {
|
| + DCHECK(self.bookmarks->loaded());
|
| +
|
| + // Create the menu.
|
| + LayoutRect menuLayout =
|
| + LayoutRectMake(0, self.view.bounds.size.width, 0, self.menuWidth,
|
| + self.view.bounds.size.height);
|
| + self.menuView =
|
| + base::scoped_nsobject<BookmarkMenuView>([[BookmarkMenuView alloc]
|
| + initWithBrowserState:self.browserState
|
| + frame:LayoutRectGetRect(menuLayout)]);
|
| + self.menuView.delegate = self;
|
| + self.menuView.autoresizingMask = UIViewAutoresizingFlexibleHeight;
|
| +
|
| + [self moveMenuAndPrimaryViewToAdequateParent];
|
| +
|
| + // Load the last primary menu item which the user had active.
|
| + BookmarkMenuItem* item = nil;
|
| + CGFloat position = 0;
|
| + BOOL found =
|
| + bookmark_utils_ios::GetPositionCache(self.bookmarks, &item, &position);
|
| + if (!found)
|
| + item = [self.menuView defaultMenuItem];
|
| +
|
| + [self updatePrimaryMenuItem:item animated:NO];
|
| +
|
| + [[self primaryView] applyContentPosition:position];
|
| +
|
| + if (found) {
|
| + // If the view has already been laid out, then immediately apply the content
|
| + // position.
|
| + if (self.view.window) {
|
| + [[self primaryView] applyContentPosition:position];
|
| + } else {
|
| + // Otherwise, save the position to be applied once the view has been laid
|
| + // out.
|
| + self.cachedContentPosition = [NSNumber numberWithFloat:position];
|
| + }
|
| + }
|
| +}
|
| +
|
| +- (void)ensureAllViewExists {
|
| + if (self.allItemsView)
|
| + return;
|
| +
|
| + base::scoped_nsobject<BookmarkAllCollectionView> view(
|
| + [[BookmarkAllCollectionView alloc] initWithBrowserState:self.browserState
|
| + frame:CGRectZero]);
|
| + self.allItemsView = view;
|
| + self.allItemsView.delegate = self;
|
| + [self.allItemsView setEditing:self.editing animated:NO];
|
| + self.allItemsView.autoresizingMask =
|
| + UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
| +}
|
| +
|
| +- (void)ensureFolderViewExists {
|
| + if (self.folderView)
|
| + return;
|
| +
|
| + base::scoped_nsobject<BookmarkFolderCollectionView> view(
|
| + [[BookmarkFolderCollectionView alloc]
|
| + initWithBrowserState:self.browserState
|
| + frame:CGRectZero]);
|
| + self.folderView = view;
|
| + self.folderView.delegate = self;
|
| + [self.folderView setEditing:self.editing animated:NO];
|
| + self.folderView.autoresizingMask =
|
| + UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
|
| +}
|
| +
|
| +- (void)updatePrimaryMenuItem:(BookmarkMenuItem*)menuItem
|
| + animated:(BOOL)animated {
|
| + if ([self.primaryMenuItem isEqual:menuItem])
|
| + return;
|
| +
|
| + if (![self.contentView superview])
|
| + return;
|
| +
|
| + [[self primaryView] removeFromSuperview];
|
| + self.primaryMenuItem = menuItem;
|
| +
|
| + switch (self.primaryMenuItem.type) {
|
| + case bookmarks::MenuItemAll:
|
| + [self ensureAllViewExists];
|
| + break;
|
| + case bookmarks::MenuItemFolder:
|
| + [self ensureFolderViewExists];
|
| + [self.folderView resetFolder:self.primaryMenuItem.folder];
|
| + [self.folderView promoStateChangedAnimated:NO];
|
| + break;
|
| + case bookmarks::MenuItemDivider:
|
| + case bookmarks::MenuItemSectionHeader:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| +
|
| + [[self primaryView] changeOrientation:GetInterfaceOrientation()];
|
| + [[self primaryView] setScrollsToTop:self.scrollToTop];
|
| +
|
| + [self moveMenuAndPrimaryViewToAdequateParent];
|
| +
|
| + // [self.contentView sendSubviewToBack:primaryView];
|
| + [self refreshFrameOfPrimaryView];
|
| +
|
| + self.navigationBar.hidden = NO;
|
| + [self updateNavigationBarAnimated:animated
|
| + orientation:GetInterfaceOrientation()];
|
| +
|
| + [self.menuView updatePrimaryMenuItem:self.primaryMenuItem];
|
| + [self updateEditBarShadow];
|
| +}
|
| +
|
| +- (UIView<BookmarkHomePrimaryView>*)primaryView {
|
| + if (!self.primaryMenuItem)
|
| + return nil;
|
| + DCHECK([self contentView]);
|
| +
|
| + switch (self.primaryMenuItem.type) {
|
| + case bookmarks::MenuItemAll:
|
| + return self.allItemsView;
|
| + case bookmarks::MenuItemFolder:
|
| + return self.folderView;
|
| + case bookmarks::MenuItemDivider:
|
| + case bookmarks::MenuItemSectionHeader:
|
| + NOTREACHED();
|
| + return nil;
|
| + }
|
| +}
|
| +
|
| +- (BOOL)shouldPresentMenuInSlideInPanel {
|
| + return IsCompactTablet();
|
| +}
|
| +
|
| +- (CGFloat)menuWidth {
|
| + return kMenuWidth;
|
| +}
|
| +
|
| +- (CGFloat)primaryViewLeadingMargin {
|
| + if ([self shouldPresentMenuInSlideInPanel])
|
| + return 0;
|
| + return [self menuWidth];
|
| +}
|
| +
|
| +- (void)moveMenuAndPrimaryViewToAdequateParent {
|
| + // Remove the menuView, panelView, and primaryView from the view hierarchy.
|
| + if ([self.menuView superview])
|
| + [self.menuView removeFromSuperview];
|
| + if ([self.panelView superview])
|
| + [self.panelView removeFromSuperview];
|
| + UIView* primaryView = [self primaryView];
|
| + if ([primaryView superview])
|
| + [primaryView removeFromSuperview];
|
| +
|
| + if ([self shouldPresentMenuInSlideInPanel]) {
|
| + // Create (if needed), and add the panelView to the view hierarchy.
|
| + if (!self.panelView) {
|
| + self.panelView = base::scoped_nsobject<BookmarkPanelView>(
|
| + [[BookmarkPanelView alloc] initWithFrame:CGRectZero
|
| + menuViewWidth:[self menuWidth]]);
|
| + }
|
| + [self.view addSubview:self.panelView];
|
| + CGSize size = self.contentView.bounds.size;
|
| + CGFloat navBarHeight = CGRectGetHeight([self navigationBarFrame]);
|
| + LayoutRect panelLayout = LayoutRectMake(
|
| + 0, size.width, navBarHeight, size.width, size.height - navBarHeight);
|
| +
|
| + // Initialize the panelView with the menuView and the primaryView.
|
| + [self.panelView setFrame:LayoutRectGetRect(panelLayout)];
|
| + [self.panelView.menuView addSubview:self.menuView];
|
| + self.menuView.frame = self.panelView.menuView.bounds;
|
| + [self.panelView.contentView addSubview:primaryView];
|
| + } else {
|
| + [self.view addSubview:self.menuView];
|
| + [self.view addSubview:primaryView];
|
| + }
|
| +
|
| + // Make sure the navigation bar is the frontmost subview.
|
| + [self.view bringSubviewToFront:self.navigationBar];
|
| +}
|
| +
|
| +- (void)refreshFrameOfPrimaryView {
|
| + [self primaryView].frame = [self frameForPrimaryView];
|
| +}
|
| +
|
| +- (CGRect)frameForPrimaryView {
|
| + CGFloat topInset = 0;
|
| + if (!IsCompactTablet())
|
| + topInset = CGRectGetHeight([self navigationBarFrame]);
|
| +
|
| + CGFloat leadingMargin = [self primaryViewLeadingMargin];
|
| + CGSize size = self.view.bounds.size;
|
| + LayoutRect primaryViewLayout =
|
| + LayoutRectMake(leadingMargin, size.width, topInset,
|
| + size.width - leadingMargin, size.height - topInset);
|
| + return LayoutRectGetRect(primaryViewLayout);
|
| +}
|
| +
|
| +#pragma mark - Editing bar methods.
|
| +
|
| +- (CGRect)editingBarFrame {
|
| + return CGRectInset(self.navigationBar.frame, 24.0, 0);
|
| +}
|
| +
|
| +- (void)updateEditingStateAnimated:(BOOL)animated {
|
| + if (!self.editing) {
|
| + [self hideEditingBarAnimated:animated];
|
| + [self updateEditBarShadow];
|
| + return;
|
| + }
|
| +
|
| + if (!self.editingBar) {
|
| + self.editingBar = base::scoped_nsobject<BookmarkEditingBar>(
|
| + [[BookmarkEditingBar alloc] initWithFrame:[self editingBarFrame]]);
|
| + [self.editingBar setCancelTarget:self action:@selector(editingBarCancel)];
|
| + [self.editingBar setDeleteTarget:self action:@selector(editingBarDelete)];
|
| + [self.editingBar setMoveTarget:self action:@selector(editingBarMove)];
|
| + [self.editingBar setEditTarget:self action:@selector(editingBarEdit)];
|
| +
|
| + [self.view addSubview:self.editingBar];
|
| + self.editingBar.hidden = YES;
|
| + }
|
| +
|
| + int bookmarkCount = 0;
|
| + int folderCount = 0;
|
| + for (auto node : _editNodes) {
|
| + if (node->is_url())
|
| + ++bookmarkCount;
|
| + else
|
| + ++folderCount;
|
| + }
|
| + [self.editingBar updateUIWithBookmarkCount:bookmarkCount
|
| + folderCount:folderCount];
|
| +
|
| + [self showEditingBarAnimated:animated];
|
| + [self updateEditBarShadow];
|
| +}
|
| +
|
| +- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
|
| + if (_editing == editing)
|
| + return;
|
| +
|
| + _editing = editing;
|
| +
|
| + if (editing) {
|
| + self.bookmarkPromoController.promoState = NO;
|
| + } else {
|
| + // Only reset the editing state when leaving edit mode. This allows
|
| + // subclasses to add nodes for editing before entering edit mode.
|
| + [self resetEditNodes];
|
| + [self.bookmarkPromoController updatePromoState];
|
| + }
|
| +
|
| + [self updateEditingStateAnimated:animated];
|
| + if ([[self primaryMenuItem] supportsEditing])
|
| + [[self primaryView] setEditing:editing animated:animated];
|
| +}
|
| +
|
| +- (void)showEditingBarAnimated:(BOOL)animated {
|
| + CGRect endFrame = [self editingBarFrame];
|
| + if (self.editingBar.hidden) {
|
| + CGRect startFrame = endFrame;
|
| + startFrame.origin.y = -CGRectGetHeight(startFrame);
|
| + self.editingBar.frame = startFrame;
|
| + }
|
| + self.editingBar.hidden = NO;
|
| + [UIView animateWithDuration:animated ? 0.2 : 0
|
| + delay:0
|
| + options:UIViewAnimationOptionBeginFromCurrentState
|
| + animations:^{
|
| + self.editingBar.frame = endFrame;
|
| + }
|
| + completion:^(BOOL finished) {
|
| + if (finished)
|
| + self.navigationBar.hidden = YES;
|
| + }];
|
| +}
|
| +
|
| +- (void)hideEditingBarAnimated:(BOOL)animated {
|
| + CGRect frame = [self editingBarFrame];
|
| + if (!self.editingBar.hidden) {
|
| + frame.origin.y = -CGRectGetHeight(frame);
|
| + }
|
| + self.navigationBar.hidden = NO;
|
| + [UIView animateWithDuration:animated ? 0.2 : 0
|
| + delay:0
|
| + options:UIViewAnimationOptionBeginFromCurrentState
|
| + animations:^{
|
| + self.editingBar.frame = frame;
|
| + }
|
| + completion:^(BOOL finished) {
|
| + if (finished)
|
| + self.editingBar.hidden = YES;
|
| + }];
|
| +}
|
| +
|
| +- (void)updateEditBarShadow {
|
| + [self.editingBar showShadow:self.editing];
|
| +}
|
| +
|
| +#pragma mark Editing Bar Callbacks
|
| +
|
| +- (void)editingBarCancel {
|
| + [self setEditing:NO animated:YES];
|
| +}
|
| +
|
| +- (void)editingBarMove {
|
| + [self moveNodes:_editNodes];
|
| +}
|
| +
|
| +- (void)editingBarDelete {
|
| + [self deleteSelectedNodes];
|
| + [self setEditing:NO animated:YES];
|
| +}
|
| +
|
| +- (void)editingBarEdit {
|
| + DCHECK_EQ(_editNodes.size(), 1u);
|
| + const BookmarkNode* node = *(_editNodes.begin());
|
| + BookmarkItemCell* itemCell = nil;
|
| + if (self.editIndexPaths.count > 0) {
|
| + NSIndexPath* indexPath = [self.editIndexPaths firstObject];
|
| + if (indexPath && ![indexPath isKindOfClass:[NSNull class]]) {
|
| + UICollectionViewCell* cell =
|
| + [[self primaryView].collectionView cellForItemAtIndexPath:indexPath];
|
| + if ([cell isKindOfClass:[BookmarkItemCell class]]) {
|
| + itemCell = static_cast<BookmarkItemCell*>(cell);
|
| + }
|
| + }
|
| + }
|
| + [self editNode:node withCell:itemCell];
|
| +}
|
| +
|
| +- (void)toggleMenuAnimated {
|
| + if ([self.panelView userDrivenAnimationInProgress])
|
| + return;
|
| +
|
| + if (self.panelView.showingMenu) {
|
| + [self.panelView hideMenuAnimated:YES];
|
| + } else {
|
| + [self.panelView showMenuAnimated:YES];
|
| + }
|
| +}
|
| +
|
| +#pragma mark - BookmarkMenuViewDelegate
|
| +
|
| +- (void)bookmarkMenuView:(BookmarkMenuView*)view
|
| + selectedMenuItem:(BookmarkMenuItem*)menuItem {
|
| + BOOL menuItemChanged = ![[self primaryMenuItem] isEqual:menuItem];
|
| + [self toggleMenuAnimated];
|
| + if (menuItemChanged) {
|
| + [self setEditing:NO animated:YES];
|
| + [self updatePrimaryMenuItem:menuItem animated:YES];
|
| + }
|
| +}
|
| +
|
| +#pragma mark - BookmarkCollectionViewDelegate
|
| +// This class owns multiple views that have a delegate that conforms to
|
| +// BookmarkCollectionViewDelegate, or a subprotocol of
|
| +// BookmarkCollectionViewDelegate.
|
| +- (void)bookmarkCollectionView:(BookmarkCollectionView*)view
|
| + cell:(UICollectionViewCell*)cell
|
| + addNodeForEditing:(const BookmarkNode*)node {
|
| + [self insertEditNode:node atIndexPath:[self indexPathForCell:cell]];
|
| + [self updateEditingStateAnimated:YES];
|
| +}
|
| +
|
| +- (void)bookmarkCollectionView:(BookmarkCollectionView*)view
|
| + cell:(UICollectionViewCell*)cell
|
| + removeNodeForEditing:(const BookmarkNode*)node {
|
| + [self removeEditNode:node atIndexPath:[self indexPathForCell:cell]];
|
| +}
|
| +
|
| +- (const std::set<const BookmarkNode*>&)nodesBeingEdited {
|
| + return _editNodes;
|
| +}
|
| +
|
| +- (void)bookmarkCollectionViewDidScroll:(BookmarkCollectionView*)view {
|
| + [self updateEditBarShadow];
|
| +}
|
| +
|
| +- (void)bookmarkCollectionView:(BookmarkCollectionView*)view
|
| + selectedUrlForNavigation:(const GURL&)url {
|
| + [self cachePosition];
|
| + // Before passing the URL to the block, make sure the block has a copy of the
|
| + // URL and not just a reference.
|
| + const GURL localUrl(url);
|
| + dispatch_async(dispatch_get_main_queue(), ^{
|
| + [self loadURL:localUrl];
|
| + });
|
| +}
|
| +
|
| +- (void)bookmarkCollectionView:(BookmarkCollectionView*)collectionView
|
| + wantsMenuForBookmark:(const BookmarkNode*)node
|
| + onView:(UIView*)view
|
| + forCell:(BookmarkItemCell*)cell {
|
| + DCHECK(!self.editViewController);
|
| + DCHECK(!self.actionSheetCoordinator);
|
| + self.actionSheetCoordinator = [[[ActionSheetCoordinator alloc]
|
| + initWithBaseViewController:self.view.window.rootViewController
|
| + title:nil
|
| + message:nil
|
| + rect:view.bounds
|
| + view:view] autorelease];
|
| + base::WeakNSObject<BookmarkHomeTabletNTPController> weakSelf(self);
|
| +
|
| + // Select action.
|
| + [self.actionSheetCoordinator
|
| + addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_SELECT)
|
| + action:^{
|
| + [weakSelf selectFirstNode:node withCell:cell];
|
| + weakSelf.get().actionSheetCoordinator = nil;
|
| + }
|
| + style:UIAlertActionStyleDefault];
|
| +
|
| + // Edit action.
|
| + [self.actionSheetCoordinator
|
| + addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_EDIT)
|
| + action:^{
|
| + BookmarkItemCell* itemCell = nil;
|
| + if ([cell isKindOfClass:[BookmarkItemCell class]])
|
| + itemCell = static_cast<BookmarkItemCell*>(cell);
|
| + [weakSelf editNode:node withCell:itemCell];
|
| + weakSelf.get().actionSheetCoordinator = nil;
|
| + }
|
| + style:UIAlertActionStyleDefault];
|
| +
|
| + // Move action.
|
| + [self.actionSheetCoordinator
|
| + addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_MOVE)
|
| + action:^{
|
| + std::set<const BookmarkNode*> nodes;
|
| + nodes.insert(node);
|
| + [weakSelf moveNodes:nodes];
|
| + weakSelf.get().actionSheetCoordinator = nil;
|
| + }
|
| + style:UIAlertActionStyleDefault];
|
| +
|
| + // Delete action.
|
| + [self.actionSheetCoordinator
|
| + addItemWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_ACTION_DELETE)
|
| + action:^{
|
| + std::set<const BookmarkNode*> nodes;
|
| + nodes.insert(node);
|
| + [weakSelf deleteNodes:nodes];
|
| + weakSelf.get().actionSheetCoordinator = nil;
|
| + }
|
| + style:UIAlertActionStyleDestructive];
|
| +
|
| + // Cancel action.
|
| + [self.actionSheetCoordinator
|
| + addItemWithTitle:l10n_util::GetNSString(IDS_CANCEL)
|
| + action:^{
|
| + weakSelf.get().actionSheetCoordinator = nil;
|
| + }
|
| + style:UIAlertActionStyleCancel];
|
| +
|
| + [self.actionSheetCoordinator start];
|
| +}
|
| +
|
| +- (void)bookmarkCollectionView:(BookmarkCollectionView*)view
|
| + didLongPressCell:(UICollectionViewCell*)cell
|
| + forBookmark:(const BookmarkNode*)node {
|
| + DCHECK(!self.editing);
|
| + [self selectFirstNode:node withCell:cell];
|
| +}
|
| +
|
| +- (BOOL)bookmarkCollectionViewShouldShowPromoCell:
|
| + (BookmarkCollectionView*)collectionView {
|
| + return self.bookmarkPromoController.promoState;
|
| +}
|
| +
|
| +- (void)bookmarkCollectionViewShowSignIn:(BookmarkCollectionView*)view {
|
| + [self.bookmarkPromoController showSignIn];
|
| +}
|
| +
|
| +- (void)bookmarkCollectionViewDismissPromo:(BookmarkCollectionView*)view {
|
| + [self.bookmarkPromoController hidePromoCell];
|
| +}
|
| +
|
| +#pragma mark Action Sheet Callbacks
|
| +
|
| +- (void)selectFirstNode:(const BookmarkNode*)node
|
| + withCell:(UICollectionViewCell*)cell {
|
| + DCHECK(!self.editing);
|
| + [self insertEditNode:node atIndexPath:[self indexPathForCell:cell]];
|
| + [self setEditing:YES animated:YES];
|
| +}
|
| +
|
| +- (void)editNode:(const BookmarkNode*)node withCell:(BookmarkItemCell*)cell {
|
| + DCHECK(!self.editViewController);
|
| + DCHECK(!self.folderEditor);
|
| + UIViewController* editorController = nil;
|
| + if (node->is_folder()) {
|
| + BookmarkFolderEditorViewController* folderEditor =
|
| + [BookmarkFolderEditorViewController
|
| + folderEditorWithBookmarkModel:self.bookmarks
|
| + folder:node
|
| + browserState:self.browserState];
|
| + folderEditor.delegate = self;
|
| + self.folderEditor = folderEditor;
|
| + editorController = folderEditor;
|
| + } else {
|
| + base::scoped_nsobject<BookmarkEditViewController> controller([
|
| + [BookmarkEditViewController alloc] initWithBookmark:node
|
| + browserState:self.browserState]);
|
| + self.editViewController = controller;
|
| + self.editViewController.delegate = self;
|
| + editorController = self.editViewController;
|
| + }
|
| + DCHECK(editorController);
|
| + base::scoped_nsobject<UINavigationController> navController(
|
| + [[BookmarkNavigationController alloc]
|
| + initWithRootViewController:editorController]);
|
| + navController.get().modalPresentationStyle = UIModalPresentationFormSheet;
|
| + [self.view.window.rootViewController presentViewController:navController
|
| + animated:YES
|
| + completion:NULL];
|
| +}
|
| +
|
| +- (void)moveNodes:(const std::set<const BookmarkNode*>&)nodes {
|
| + DCHECK(!self.folderSelector);
|
| + DCHECK(nodes.size() > 0);
|
| + const BookmarkNode* editedNode = *(nodes.begin());
|
| + const BookmarkNode* selectedFolder = editedNode->parent();
|
| + self.folderSelector = base::scoped_nsobject<BookmarkFolderViewController>(
|
| + [[BookmarkFolderViewController alloc]
|
| + initWithBookmarkModel:self.bookmarks
|
| + allowsNewFolders:YES
|
| + editedNodes:nodes
|
| + allowsCancel:YES
|
| + selectedFolder:selectedFolder]);
|
| + self.folderSelector.delegate = self;
|
| + base::scoped_nsobject<UINavigationController> controller(
|
| + [[BookmarkNavigationController alloc]
|
| + initWithRootViewController:self.folderSelector]);
|
| + controller.get().modalPresentationStyle = UIModalPresentationFormSheet;
|
| + [self.view.window.rootViewController presentViewController:controller
|
| + animated:YES
|
| + completion:NULL];
|
| +}
|
| +
|
| +- (void)deleteNodes:(const std::set<const BookmarkNode*>&)nodes {
|
| + DCHECK_GE(nodes.size(), 1u);
|
| + bookmark_utils_ios::DeleteBookmarksWithUndoToast(nodes, self.bookmarks,
|
| + self.browserState);
|
| +}
|
| +
|
| +#pragma mark - BookmarkFolderCollectionViewDelegate
|
| +
|
| +- (void)bookmarkFolderCollectionView:(BookmarkFolderCollectionView*)view
|
| + selectedFolderForNavigation:(const BookmarkNode*)folder {
|
| + BookmarkMenuItem* menuItem = nil;
|
| + if (view == self.folderView) {
|
| + const BookmarkNode* parent = RootLevelFolderForNode(folder, self.bookmarks);
|
| + menuItem =
|
| + [BookmarkMenuItem folderMenuItemForNode:folder rootAncestor:parent];
|
| + } else {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| + [self updatePrimaryMenuItem:menuItem animated:YES];
|
| +}
|
| +
|
| +#pragma mark - BookmarkEditViewControllerDelegate
|
| +
|
| +- (BOOL)bookmarkEditor:(BookmarkEditViewController*)controller
|
| + shoudDeleteAllOccurencesOfBookmark:(const BookmarkNode*)bookmark {
|
| + return NO;
|
| +}
|
| +
|
| +- (void)bookmarkEditorWantsDismissal:(BookmarkEditViewController*)controller {
|
| + self.editViewController.delegate = nil;
|
| + self.editViewController = nil;
|
| + [controller dismissViewControllerAnimated:YES completion:NULL];
|
| +
|
| + // The editViewController can be displayed from the menu button, or from the
|
| + // edit button in edit mode. Either way, after it's dismissed, edit mode
|
| + // should be off.
|
| + [self setEditing:NO animated:NO];
|
| +}
|
| +
|
| +#pragma mark - BookmarkFolderViewControllerDelegate
|
| +
|
| +- (void)folderPicker:(BookmarkFolderViewController*)folderPicker
|
| + didFinishWithFolder:(const BookmarkNode*)folder {
|
| + DCHECK(folder);
|
| + DCHECK(!folder->is_url());
|
| + DCHECK_GE(folderPicker.editedNodes.size(), 1u);
|
| +
|
| + bookmark_utils_ios::MoveBookmarksWithUndoToast(
|
| + folderPicker.editedNodes, self.bookmarks, folder, self.browserState);
|
| +
|
| + [self setEditing:NO animated:NO];
|
| + [[folderPicker presentingViewController] dismissViewControllerAnimated:YES
|
| + completion:nil];
|
| + self.folderSelector.delegate = nil;
|
| + self.folderSelector = nil;
|
| +}
|
| +
|
| +- (void)folderPickerDidCancel:(BookmarkFolderViewController*)folderPicker {
|
| + [self setEditing:NO animated:NO];
|
| + [[folderPicker presentingViewController] dismissViewControllerAnimated:YES
|
| + completion:nil];
|
| + self.folderSelector.delegate = nil;
|
| + self.folderSelector = nil;
|
| +}
|
| +
|
| +#pragma mark - BookmarkFolderEditorViewControllerDelegate
|
| +
|
| +- (void)bookmarkFolderEditor:(BookmarkFolderEditorViewController*)folderEditor
|
| + didFinishEditingFolder:(const BookmarkNode*)folder {
|
| + DCHECK(folder);
|
| + [[folderEditor presentingViewController] dismissViewControllerAnimated:YES
|
| + completion:nil];
|
| + self.folderEditor.delegate = nil;
|
| + self.folderEditor = nil;
|
| +}
|
| +
|
| +- (void)bookmarkFolderEditorDidDeleteEditedFolder:
|
| + (BookmarkFolderEditorViewController*)folderEditor {
|
| + [[folderEditor presentingViewController] dismissViewControllerAnimated:YES
|
| + completion:nil];
|
| + self.folderEditor.delegate = nil;
|
| + self.folderEditor = nil;
|
| +}
|
| +
|
| +- (void)bookmarkFolderEditorDidCancel:
|
| + (BookmarkFolderEditorViewController*)folderEditor {
|
| + [[folderEditor presentingViewController] dismissViewControllerAnimated:YES
|
| + completion:nil];
|
| + self.folderEditor.delegate = nil;
|
| + self.folderEditor = nil;
|
| +}
|
| +
|
| +#pragma mark - Internal Utility Methods
|
| +
|
| +- (void)deleteSelectedNodes {
|
| + [self deleteNodes:_editNodes];
|
| +}
|
| +
|
| +- (void)moveEditingNodesToFolder:(const BookmarkNode*)folder {
|
| + // The UI only supports moving nodes when there are at least one selected.
|
| + DCHECK_GE(_editNodes.size(), 1u);
|
| +
|
| + bookmark_utils_ios::MoveBookmarksWithUndoToast(_editNodes, self.bookmarks,
|
| + folder, self.browserState);
|
| +}
|
| +
|
| +- (void)cachePosition {
|
| + if ([self primaryView]) {
|
| + bookmark_utils_ios::CachePosition(
|
| + [[self primaryView] contentPositionInPortraitOrientation],
|
| + [self primaryMenuItem]);
|
| + }
|
| +}
|
| +
|
| +#pragma mark - Navigation bar
|
| +
|
| +- (CGRect)navigationBarFrame {
|
| + return CGRectMake(0, 0, CGRectGetWidth(self.view.bounds),
|
| + [BookmarkNavigationBar expectedContentViewHeight] +
|
| + kNavigationBarTopMargin);
|
| +}
|
| +
|
| +- (void)updateNavigationBarAnimated:(BOOL)animated
|
| + orientation:(UIInterfaceOrientation)orientation {
|
| + CGFloat duration = animated ? bookmark_utils_ios::menuAnimationDuration : 0;
|
| + [self updateNavigationBarWithDuration:duration orientation:orientation];
|
| +}
|
| +
|
| +- (void)updateNavigationBarWithDuration:(CGFloat)duration
|
| + orientation:(UIInterfaceOrientation)orientation {
|
| + [self.navigationBar setTitle:[self.primaryMenuItem titleForNavigationBar]];
|
| + if ([self shouldShowEditButton])
|
| + [self.navigationBar showEditButtonWithAnimationDuration:duration];
|
| + else
|
| + [self.navigationBar hideEditButtonWithAnimationDuration:duration];
|
| +
|
| + if ([self shouldShowBackButton])
|
| + [self.navigationBar showBackButtonInsteadOfMenuButton:duration];
|
| + else
|
| + [self.navigationBar showMenuButtonInsteadOfBackButton:duration];
|
| +}
|
| +
|
| +- (BOOL)shouldShowEditButton {
|
| + if (self.primaryMenuItem.type != bookmarks::MenuItemFolder)
|
| + return NO;
|
| + // The type is MenuItemFolder, so it is safe to access |folder|.
|
| + return !self.bookmarks->is_permanent_node(self.primaryMenuItem.folder);
|
| +}
|
| +
|
| +- (BOOL)shouldShowBackButton {
|
| + if (self.primaryMenuItem.type != bookmarks::MenuItemFolder)
|
| + return NO;
|
| + // The type is MenuItemFolder, so it is safe to access |folder|.
|
| + const BookmarkNode* folder = self.primaryMenuItem.folder;
|
| + // Show the back button iff the folder or its immediate parent is a permanent
|
| + // primary folder.
|
| + BOOL isTopFolder = IsPrimaryPermanentNode(folder, self.bookmarks) ||
|
| + IsPrimaryPermanentNode(folder->parent(), self.bookmarks);
|
| + return !isTopFolder;
|
| +}
|
| +
|
| +#pragma mark Navigation Bar Callbacks
|
| +
|
| +- (void)navigationBarWantsEditing:(id)sender {
|
| + DCHECK(self.primaryMenuItem.type == bookmarks::MenuItemFolder);
|
| + const BookmarkNode* folder = self.primaryMenuItem.folder;
|
| + BookmarkFolderEditorViewController* folderEditor =
|
| + [BookmarkFolderEditorViewController
|
| + folderEditorWithBookmarkModel:self.bookmarks
|
| + folder:folder
|
| + browserState:self.browserState];
|
| + folderEditor.delegate = self;
|
| + self.folderEditor = folderEditor;
|
| +
|
| + base::scoped_nsobject<BookmarkNavigationController> navController(
|
| + [[BookmarkNavigationController alloc]
|
| + initWithRootViewController:self.folderEditor]);
|
| + navController.get().modalPresentationStyle = UIModalPresentationFormSheet;
|
| + [self.view.window.rootViewController presentViewController:navController
|
| + animated:YES
|
| + completion:NULL];
|
| +}
|
| +
|
| +- (void)navigationBarBack:(id)sender {
|
| + DCHECK([self shouldShowBackButton]);
|
| +
|
| + // Go to the parent folder.
|
| + DCHECK(self.primaryMenuItem.type == bookmarks::MenuItemFolder);
|
| + const BookmarkNode* parentFolder = self.primaryMenuItem.folder->parent();
|
| + const BookmarkNode* rootAncestor =
|
| + RootLevelFolderForNode(parentFolder, self.bookmarks);
|
| + BookmarkMenuItem* menuItem =
|
| + [BookmarkMenuItem folderMenuItemForNode:parentFolder
|
| + rootAncestor:rootAncestor];
|
| + [self updatePrimaryMenuItem:menuItem animated:YES];
|
| +}
|
| +
|
| +#pragma mark - NewTabPagePanelProtocol
|
| +
|
| +- (void)reload {
|
| +}
|
| +
|
| +- (void)wasShown {
|
| +}
|
| +
|
| +- (void)wasHidden {
|
| + [self cachePosition];
|
| +}
|
| +
|
| +- (void)dismissModals {
|
| + [self.actionSheetCoordinator stop];
|
| + self.actionSheetCoordinator = nil;
|
| + [self.editViewController dismiss];
|
| +}
|
| +
|
| +- (void)dismissKeyboard {
|
| + // Uses self.contentView directly instead of going throught self.view to
|
| + // avoid creating the view hierarchy unnecessarily.
|
| + [self.contentView endEditing:YES];
|
| +}
|
| +
|
| +- (void)setScrollsToTop:(BOOL)enabled {
|
| + self.scrollToTop = enabled;
|
| + [[self primaryView] setScrollsToTop:self.scrollToTop];
|
| +}
|
| +
|
| +- (UIView*)view {
|
| + if (!self.contentView) {
|
| + _contentView = [[ContentView alloc] initWithFrame:CGRectZero];
|
| + _contentView.delegate = self;
|
| + self.contentView.backgroundColor =
|
| + bookmark_utils_ios::mainBackgroundColor();
|
| + base::scoped_nsobject<BookmarkNavigationBar> bar(
|
| + [[BookmarkNavigationBar alloc] initWithFrame:CGRectZero]);
|
| + self.navigationBar = bar;
|
| + self.navigationBar.autoresizingMask = UIViewAutoresizingFlexibleWidth;
|
| +
|
| + [self.navigationBar setEditTarget:self
|
| + action:@selector(navigationBarWantsEditing:)];
|
| + [self.navigationBar setBackTarget:self
|
| + action:@selector(navigationBarBack:)];
|
| +
|
| + [self.navigationBar setMenuTarget:self
|
| + action:@selector(toggleMenuAnimated)];
|
| +
|
| + [self.view addSubview:self.navigationBar];
|
| +
|
| + if (self.bookmarks->loaded())
|
| + [self loadBookmarkViews];
|
| + else
|
| + [self loadWaitingView];
|
| + }
|
| + return self.contentView;
|
| +}
|
| +
|
| +- (CGFloat)alphaForBottomShadow {
|
| + return 0;
|
| +}
|
| +
|
| +#pragma mark - BookmarkModelBridgeObserver
|
| +
|
| +- (void)bookmarkModelLoaded {
|
| + if (!self.contentView)
|
| + return;
|
| +
|
| + DCHECK(self.waitForModelView);
|
| + base::WeakNSObject<BookmarkHomeTabletNTPController> weakSelf(self);
|
| + [self.waitForModelView stopWaitingWithCompletion:^{
|
| + base::scoped_nsobject<BookmarkHomeTabletNTPController> strongSelf(
|
| + [weakSelf retain]);
|
| + // Early return if the controller has been deallocated.
|
| + if (!strongSelf)
|
| + return;
|
| + [UIView animateWithDuration:0.2
|
| + animations:^{
|
| + strongSelf.get().waitForModelView.alpha = 0.0;
|
| + }
|
| + completion:^(BOOL finished) {
|
| + [strongSelf.get().waitForModelView removeFromSuperview];
|
| + strongSelf.get().waitForModelView = nil;
|
| + }];
|
| + [strongSelf loadBookmarkViews];
|
| + }];
|
| +}
|
| +
|
| +- (void)bookmarkNodeChanged:(const BookmarkNode*)bookmarkNode {
|
| + // The title of the folder may have changed.
|
| + if (self.primaryMenuItem.type == bookmarks::MenuItemFolder &&
|
| + self.primaryMenuItem.folder == bookmarkNode) {
|
| + UIInterfaceOrientation orient = GetInterfaceOrientation();
|
| + [self updateNavigationBarAnimated:NO orientation:orient];
|
| + }
|
| +}
|
| +
|
| +- (void)bookmarkNodeChildrenChanged:(const BookmarkNode*)bookmarkNode {
|
| + // The node has not changed, but the ordering and existence of its children
|
| + // have changed.
|
| +}
|
| +
|
| +- (void)bookmarkNode:(const BookmarkNode*)bookmarkNode
|
| + movedFromParent:(const BookmarkNode*)oldParent
|
| + toParent:(const BookmarkNode*)newParent {
|
| + // The node has moved to a new parent folder.
|
| +}
|
| +
|
| +- (void)bookmarkNodeDeleted:(const BookmarkNode*)node
|
| + fromFolder:(const BookmarkNode*)folder {
|
| + [self removeEditNode:node atIndexPath:nil];
|
| +}
|
| +
|
| +- (void)bookmarkModelRemovedAllNodes {
|
| + // All non-permanent nodes have been removed.
|
| + [self setEditing:NO animated:YES];
|
| +}
|
| +
|
| +#pragma mark - BookmarkPromoControllerDelegate
|
| +
|
| +- (void)promoStateChanged:(BOOL)promoEnabled {
|
| + [self.allItemsView.collectionView reloadData];
|
| + // This is required to workaround a crash seen once on iOS 7.1 when
|
| + // the collection gets two reloadData without getting a call to layout
|
| + // subviews, the collection view will reuse some cached data for the perfoming
|
| + // the layout which are invalid after the second call to reloadData.
|
| + // Forcing the layout invalidation after each reloadData seems to fix the
|
| + // issue.
|
| + [self.allItemsView.collectionView.collectionViewLayout invalidateLayout];
|
| + [self.folderView
|
| + promoStateChangedAnimated:self.folderView == [self primaryView]];
|
| +}
|
| +
|
| +- (NSIndexPath*)indexPathForCell:(UICollectionViewCell*)cell {
|
| + DCHECK([self primaryView].collectionView);
|
| + NSIndexPath* indexPath =
|
| + [[self primaryView].collectionView indexPathForCell:cell];
|
| + return indexPath;
|
| +}
|
| +
|
| +@end
|
|
|