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

Unified Diff: ios/chrome/browser/ui/bookmarks/bookmark_menu_view.mm

Issue 2586993002: Upstream Chrome on iOS source code [3/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ios/chrome/browser/ui/bookmarks/bookmark_menu_view.mm
diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_menu_view.mm b/ios/chrome/browser/ui/bookmarks/bookmark_menu_view.mm
new file mode 100644
index 0000000000000000000000000000000000000000..bee854aeec5f5c32898e07f220afba19e70e08e4
--- /dev/null
+++ b/ios/chrome/browser/ui/bookmarks/bookmark_menu_view.mm
@@ -0,0 +1,429 @@
+// 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_menu_view.h"
+
+#include <memory>
+
+#include "base/mac/foundation_util.h"
+#include "base/mac/objc_property_releaser.h"
+#include "base/mac/scoped_nsobject.h"
+#include "components/bookmarks/browser/bookmark_model.h"
+#include "components/bookmarks/browser/bookmark_model_observer.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"
+#include "ios/chrome/browser/experimental_flags.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_menu_cell.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_menu_item.h"
+#include "ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h"
+#import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
+#include "ios/chrome/browser/ui/ui_util.h"
+#import "ios/chrome/browser/ui/uikit_ui_util.h"
+#include "ios/chrome/grit/ios_strings.h"
+#import "ios/third_party/material_components_ios/src/components/Ink/src/MaterialInk.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/base/models/tree_node_iterator.h"
+
+using bookmarks::BookmarkNode;
+
+@interface BookmarkMenuView ()<BookmarkModelBridgeObserver,
+ MDCInkTouchControllerDelegate,
+ UITableViewDataSource,
+ UITableViewDelegate> {
+ // A bridge to receive bookmark model observer callbacks.
+ std::unique_ptr<bookmarks::BookmarkModelBridge> _modelBridge;
+
+ base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkMenuView;
+}
+@property(nonatomic, assign) bookmarks::BookmarkModel* bookmarkModel;
+// This array directly represents the rows that show up in the table.
+@property(nonatomic, retain) NSMutableArray* menuItems;
+// The primary menu item is blue instead of gray.
+@property(nonatomic, retain) BookmarkMenuItem* primaryMenuItem;
+@property(nonatomic, assign) ios::ChromeBrowserState* browserState;
+@property(nonatomic, retain) UITableView* tableView;
+@property(nonatomic, retain) MDCInkTouchController* inkTouchController;
+
+// Updates the data model, and the UI.
+- (void)reloadData;
+
+// Creates the views for this class.
+- (void)createViews;
+
+@end
+
+@implementation BookmarkMenuView
+@synthesize bookmarkModel = _bookmarkModel;
+@synthesize delegate = _delegate;
+@synthesize menuItems = _menuItems;
+@synthesize primaryMenuItem = _primaryMenuItem;
+@synthesize browserState = _browserState;
+@synthesize tableView = _tableView;
+@synthesize inkTouchController = _inkTouchController;
+
+- (id)initWithFrame:(CGRect)frame {
+ NOTREACHED();
+ return nil;
+}
+
+- (instancetype)initWithBrowserState:(ios::ChromeBrowserState*)browserState
+ frame:(CGRect)frame {
+ self = [super initWithFrame:frame];
+ if (self) {
+ _propertyReleaser_BookmarkMenuView.Init(self, [BookmarkMenuView class]);
+
+ _browserState = browserState;
+
+ // Set up connection to the BookmarkModel.
+ _bookmarkModel =
+ ios::BookmarkModelFactory::GetForBrowserState(_browserState);
+ // Set up observers.
+ _modelBridge.reset(
+ new bookmarks::BookmarkModelBridge(self, _bookmarkModel));
+
+ self.menuItems = [NSMutableArray array];
+
+ [self createViews];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ self.tableView.delegate = nil;
+ self.tableView.dataSource = nil;
+ [super dealloc];
+}
+
+- (void)createViews {
+ // Make the table view.
+ self.tableView = base::scoped_nsobject<UITableView>(
+ [[UITableView alloc] initWithFrame:self.bounds]);
+ [self addSubview:self.tableView];
+ self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
+ self.tableView.delegate = self;
+ self.tableView.dataSource = self;
+ self.tableView.scrollsToTop = NO;
+ [self reloadData];
+
+ // Set up ink touch controller.
+ base::scoped_nsobject<MDCInkTouchController> inkTouchController(
+ [[MDCInkTouchController alloc] initWithView:self.tableView]);
+ self.inkTouchController = inkTouchController;
+ self.inkTouchController.delegate = self;
+ self.inkTouchController.delaysInkSpread = YES;
+}
+
+- (void)layoutSubviews {
+ [super layoutSubviews];
+ self.tableView.frame = self.bounds;
+}
+
+- (void)reloadData {
+ if (!self.bookmarkModel->loaded())
+ return;
+
+ BookmarkMenuItem* primaryItem = [self.primaryMenuItem parentItem];
+
+ [self.menuItems removeAllObjects];
+
+ const BookmarkNode* mobileBookmarks = self.bookmarkModel->mobile_node();
+ const BookmarkNode* bookmarkBar = self.bookmarkModel->bookmark_bar_node();
+ const BookmarkNode* otherBookmarks = self.bookmarkModel->other_node();
+
+ // The first section is always visible.
+ base::scoped_nsobject<NSMutableArray> topSection(
+ [[NSMutableArray alloc] init]);
+ [self.menuItems addObject:topSection];
+
+ if (experimental_flags::IsAllBookmarksEnabled()) {
+ // All Items is always visible.
+ [topSection addObject:[BookmarkMenuItem allMenuItem]];
+ }
+ // Bookmarks Bar, Mobile Bookmarks and Other Bookmarks are special folders and
+ // are shown at the top if they contain anything.
+ if (!mobileBookmarks->empty() ||
+ !experimental_flags::IsAllBookmarksEnabled()) {
+ [topSection
+ addObject:[BookmarkMenuItem folderMenuItemForNode:mobileBookmarks
+ rootAncestor:mobileBookmarks]];
+ }
+ if (!bookmarkBar->empty()) {
+ [topSection addObject:[BookmarkMenuItem folderMenuItemForNode:bookmarkBar
+ rootAncestor:bookmarkBar]];
+ }
+ if (!otherBookmarks->empty()) {
+ [topSection
+ addObject:[BookmarkMenuItem folderMenuItemForNode:otherBookmarks
+ rootAncestor:otherBookmarks]];
+ }
+
+ // The second section contains all the top level folders (except for the
+ // permanent nodes).
+ base::scoped_nsobject<NSMutableArray> folderSection(
+ [[NSMutableArray alloc] init]);
+ std::vector<const BookmarkNode*> rootLevelFolders =
+ RootLevelFolders(self.bookmarkModel);
+ bookmark_utils_ios::SortFolders(&rootLevelFolders);
+ for (auto node : rootLevelFolders) {
+ [folderSection addObject:[BookmarkMenuItem folderMenuItemForNode:node
+ rootAncestor:node]];
+ }
+ if ([folderSection count]) {
+ // Add the title and the divider at the top of the section.
+ [folderSection
+ insertObject:[BookmarkMenuItem sectionMenuItemWithTitle:
+ l10n_util::GetNSString(
+ IDS_IOS_BOOKMARK_FOLDERS_LABEL)]
+ atIndex:0];
+ [folderSection insertObject:[BookmarkMenuItem dividerMenuItem] atIndex:0];
+ [self.menuItems addObject:folderSection];
+ }
+
+ // If the currently selected menuitem is no longer present in the menu, then
+ // select the first item in the top section instead.
+ if (![topSection containsObject:primaryItem] &&
+ ![folderSection containsObject:primaryItem]) {
+ self.primaryMenuItem = [topSection firstObject];
+ [self.delegate bookmarkMenuView:self selectedMenuItem:self.primaryMenuItem];
+ }
+
+ [self.tableView reloadData];
+}
+
+- (BookmarkMenuItem*)defaultMenuItem {
+ // The first item in the first section.
+ DCHECK([[self.menuItems firstObject] firstObject]);
+ return [[self.menuItems firstObject] firstObject];
+}
+
+- (BookmarkMenuItem*)menuItemAtIndexPath:(NSIndexPath*)indexPath {
+ return self.menuItems[indexPath.section][indexPath.row];
+}
+
+#pragma mark UIView method
+
+- (void)didMoveToSuperview {
+ [super didMoveToSuperview];
+ // The background color depends on where in the view hierachy the menu is.
+ // For example, the menu may be moved to a slide over panel if the
+ // horizontal size class changes from regular to compact.
+ self.tableView.backgroundColor = bookmark_utils_ios::menuBackgroundColor();
+}
+
+#pragma mark BookmarkModelBridgeObserver
+
+- (void)bookmarkModelLoaded {
+ [self reloadData];
+}
+
+- (void)bookmarkNodeChanged:(const BookmarkNode*)bookmarkNode {
+ [self reloadData];
+}
+
+- (void)bookmarkNodeChildrenChanged:(const BookmarkNode*)bookmarkNode {
+ [self reloadData];
+}
+
+- (void)bookmarkNode:(const BookmarkNode*)bookmarkNode
+ movedFromParent:(const BookmarkNode*)oldParent
+ toParent:(const BookmarkNode*)newParent {
+ if (self.primaryMenuItem.type == bookmarks::MenuItemFolder &&
+ bookmarkNode->is_folder()) {
+ // Checking which folder moved and if the current folder was implicated is
+ // complicated and not worth the effort. Just rebuild a new primaryMenu item
+ // unconditionally, this is simpler.
+ const BookmarkNode* currentFolder = self.primaryMenuItem.folder;
+ BookmarkMenuItem* menuItem = [BookmarkMenuItem
+ folderMenuItemForNode:currentFolder
+ rootAncestor:RootLevelFolderForNode(currentFolder,
+ self.bookmarkModel)];
+ if (menuItem != self.primaryMenuItem) {
+ self.primaryMenuItem = menuItem;
+ [self.delegate bookmarkMenuView:self
+ selectedMenuItem:self.primaryMenuItem];
+ }
+ }
+ [self reloadData];
+}
+
+- (void)bookmarkNodeDeleted:(const BookmarkNode*)node
+ fromFolder:(const BookmarkNode*)parentFolder {
+ // If the current folder or one of its ancestor has been deleted, the
+ // selection needs to move up to a non deleted ancestor. This check is made
+ // more complex as by the time this method is called |node| is no longer in
+ // the hierarchy : its parent is already set to null.
+
+ if (self.primaryMenuItem.type != bookmarks::MenuItemFolder) {
+ // If the object currently selected is not a folder, just reload.
+ [self reloadData];
+ return;
+ }
+
+ if (parentFolder == self.primaryMenuItem.folder || !node->is_folder()) {
+ // A child of the selected folder has been deleted or a url not visible in
+ // the UI right now has been deleted. Nothing to do as the menu itself needs
+ // no change.
+ return;
+ }
+
+ if (node == self.primaryMenuItem.rootAncestor) {
+ // The deleted node is the root node of the current selected folder. Move to
+ // all items.
+ self.primaryMenuItem = [BookmarkMenuItem allMenuItem];
+ [self.delegate bookmarkMenuView:self selectedMenuItem:self.primaryMenuItem];
+ [self reloadData];
+ return;
+ }
+
+ const BookmarkNode* root =
+ RootLevelFolderForNode(parentFolder, self.bookmarkModel);
+
+ if (root != self.primaryMenuItem.rootAncestor) {
+ // The deleted folder is not in the same hierarchy as the current selected
+ // folder, there is nothing to reload unless the deleted folder is a root
+ // node.
+ if (!root)
+ [self reloadData];
+ return;
+ }
+
+ if (node == self.primaryMenuItem.folder) {
+ // The simple case where the deleted folder is the one currently in the UI.
+ // At this point the deleted folder is known to not be a root node:
+ DCHECK_NE(self.primaryMenuItem.folder, self.primaryMenuItem.rootAncestor);
+ // Simply move to the parent.
+ self.primaryMenuItem =
+ [BookmarkMenuItem folderMenuItemForNode:parentFolder rootAncestor:root];
+ [self.delegate bookmarkMenuView:self selectedMenuItem:self.primaryMenuItem];
+ [self reloadData];
+ }
+
+ // The only case left is when the deleted folder used to be an ancestor of the
+ // selected folder. This is easy to infer, if the selected folder is no longer
+ // present in the common root hierarchy, this means it was deleted as well.
+ ui::TreeNodeIterator<const BookmarkNode> iterator(root);
+ while (iterator.has_next()) {
+ if (self.primaryMenuItem.folder == iterator.Next())
+ return; // Nothing to do.
+ }
+ // The current folder was not found, relocate to the first non deleted
+ // ancestor.
+ self.primaryMenuItem =
+ [BookmarkMenuItem folderMenuItemForNode:parentFolder rootAncestor:root];
+ [self.delegate bookmarkMenuView:self selectedMenuItem:self.primaryMenuItem];
+ [self reloadData];
+}
+
+- (void)bookmarkModelRemovedAllNodes {
+ [self reloadData];
+}
+
+#pragma mark UITableViewDataSource
+
+- (UITableViewCell*)tableView:(UITableView*)tableView
+ cellForRowAtIndexPath:(NSIndexPath*)indexPath {
+ BookmarkMenuCell* cell = [tableView
+ dequeueReusableCellWithIdentifier:[BookmarkMenuCell reuseIdentifier]];
+ if (!cell) {
+ cell = [[[BookmarkMenuCell alloc]
+ initWithStyle:UITableViewCellStyleDefault
+ reuseIdentifier:[BookmarkMenuCell reuseIdentifier]] autorelease];
+ }
+ cell.selectionStyle = UITableViewCellSelectionStyleNone;
+ BookmarkMenuItem* menuItem = [self menuItemAtIndexPath:indexPath];
+ BOOL primary =
+ [[self.primaryMenuItem parentItem] isEqual:[menuItem parentItem]];
+ [cell updateWithBookmarkMenuItem:menuItem primary:primary];
+ if (primary && bookmark_utils_ios::bookmarkMenuIsInSlideInPanel()) {
+ [tableView selectRowAtIndexPath:indexPath
+ animated:NO
+ scrollPosition:UITableViewScrollPositionNone];
+ }
+ return cell;
+}
+
+- (NSInteger)tableView:(UITableView*)tableView
+ numberOfRowsInSection:(NSInteger)section {
+ return [self.menuItems[section] count];
+}
+
+- (NSInteger)numberOfSectionsInTableView:(UITableView*)tableView {
+ return [self.menuItems count];
+}
+
+#pragma mark UITableViewDelegate
+
+- (CGFloat)tableView:(UITableView*)tableView
+ heightForRowAtIndexPath:(NSIndexPath*)indexPath {
+ BookmarkMenuItem* menuItem = [self menuItemAtIndexPath:indexPath];
+ return [menuItem height];
+}
+
+- (NSIndexPath*)tableView:(UITableView*)tableView
+ willSelectRowAtIndexPath:(NSIndexPath*)indexPath {
+ BookmarkMenuItem* menuItem = [self menuItemAtIndexPath:indexPath];
+ return [menuItem canBeSelected] ? indexPath : nil;
+}
+
+- (void)tableView:(UITableView*)tableView
+ didSelectRowAtIndexPath:(NSIndexPath*)indexPath {
+ BookmarkMenuItem* menuItem = [self menuItemAtIndexPath:indexPath];
+ [self.delegate bookmarkMenuView:self selectedMenuItem:menuItem];
+}
+
+- (CGFloat)tableView:(UITableView*)tableView
+ heightForHeaderInSection:(NSInteger)section {
+ return 8.0;
+}
+
+- (CGFloat)tableView:(UITableView*)tableView
+ heightForFooterInSection:(NSInteger)section {
+ BOOL isLastSection = [tableView numberOfSections] == (section + 1);
+ return isLastSection ? 8.0 : 0.0;
+}
+
+- (UIView*)tableView:(UITableView*)tableView
+ viewForHeaderInSection:(NSInteger)section {
+ return [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
+}
+
+- (UIView*)tableView:(UITableView*)tableView
+ viewForFooterInSection:(NSInteger)section {
+ return [[[UIView alloc] initWithFrame:CGRectZero] autorelease];
+}
+
+#pragma mark MDCInkTouchControllerDelegate
+
+- (BOOL)inkTouchController:(MDCInkTouchController*)inkTouchController
+ shouldProcessInkTouchesAtTouchLocation:(CGPoint)location {
+ NSIndexPath* indexPath = [self.tableView indexPathForRowAtPoint:location];
+ BookmarkMenuItem* menuItem = [self menuItemAtIndexPath:indexPath];
+ return menuItem.type == bookmarks::MenuItemAll ||
+ menuItem.type == bookmarks::MenuItemFolder;
+}
+
+- (MDCInkView*)inkTouchController:(MDCInkTouchController*)inkTouchController
+ inkViewAtTouchLocation:(CGPoint)location {
+ NSIndexPath* indexPath = [self.tableView indexPathForRowAtPoint:location];
+ BookmarkMenuCell* cell = base::mac::ObjCCastStrict<BookmarkMenuCell>(
+ [self.tableView cellForRowAtIndexPath:indexPath]);
+ return cell.inkView;
+}
+
+#pragma mark Public Methods
+
+- (void)updatePrimaryMenuItem:(BookmarkMenuItem*)menuItem {
+ if ([self.primaryMenuItem isEqual:menuItem])
+ return;
+
+ self.primaryMenuItem = menuItem;
+ [self.tableView reloadData];
+}
+
+- (void)setScrollsToTop:(BOOL)scrollsToTop {
+ self.tableView.scrollsToTop = scrollsToTop;
+}
+
+@end
« no previous file with comments | « ios/chrome/browser/ui/bookmarks/bookmark_menu_view.h ('k') | ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698