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

Side by Side Diff: ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller.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 unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 #import "ios/chrome/browser/ui/bookmarks/bookmark_folder_editor_view_controller. h"
5
6 #include <memory>
7 #include <set>
8
9 #include "base/auto_reset.h"
10 #include "base/i18n/rtl.h"
11 #import "base/ios/weak_nsobject.h"
12 #include "base/logging.h"
13 #include "base/mac/foundation_util.h"
14 #include "base/mac/objc_property_releaser.h"
15 #include "base/mac/scoped_nsobject.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "components/bookmarks/browser/bookmark_model.h"
18 #include "components/bookmarks/browser/bookmark_node.h"
19 #import "ios/chrome/browser/ui/bookmarks/bookmark_elevated_toolbar.h"
20 #import "ios/chrome/browser/ui/bookmarks/bookmark_folder_view_controller.h"
21 #import "ios/chrome/browser/ui/bookmarks/bookmark_model_bridge_observer.h"
22 #import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
23 #import "ios/chrome/browser/ui/bookmarks/cells/bookmark_parent_folder_item.h"
24 #import "ios/chrome/browser/ui/bookmarks/cells/bookmark_text_field_item.h"
25 #import "ios/chrome/browser/ui/collection_view/cells/collection_view_item.h"
26 #import "ios/chrome/browser/ui/collection_view/collection_view_model.h"
27 #import "ios/chrome/browser/ui/icons/chrome_icon.h"
28 #import "ios/chrome/browser/ui/material_components/utils.h"
29 #include "ios/chrome/browser/ui/rtl_geometry.h"
30 #include "ios/chrome/grit/ios_strings.h"
31 #import "ios/third_party/material_components_ios/src/components/NavigationBar/sr c/MaterialNavigationBar.h"
32 #import "ios/third_party/material_components_ios/src/components/Palettes/src/Mat erialPalettes.h"
33 #import "ios/third_party/material_components_ios/src/components/ShadowElevations /src/MaterialShadowElevations.h"
34 #import "ios/third_party/material_components_ios/src/components/ShadowLayer/src/ MaterialShadowLayer.h"
35 #import "ios/third_party/material_components_ios/src/components/Typography/src/M aterialTypography.h"
36 #include "ui/base/l10n/l10n_util.h"
37
38 using bookmarks::BookmarkNode;
39
40 namespace {
41
42 typedef NS_ENUM(NSInteger, SectionIdentifier) {
43 SectionIdentifierInfo = kSectionIdentifierEnumZero,
44 };
45
46 typedef NS_ENUM(NSInteger, ItemType) {
47 ItemTypeFolderTitle = kItemTypeEnumZero,
48 ItemTypeParentFolder,
49 };
50
51 } // namespace
52
53 @interface BookmarkFolderEditorViewController ()<
54 BookmarkFolderViewControllerDelegate,
55 BookmarkModelBridgeObserver,
56 BookmarkTextFieldItemDelegate> {
57 std::unique_ptr<bookmarks::BookmarkModelBridge> _modelBridge;
58 base::mac::ObjCPropertyReleaser
59 _propertyReleaser_BookmarkFolderEditorViewController;
60 // Flag to ignore bookmark model Move notifications when the move is performed
61 // by this class.
62 BOOL _ignoresOwnMove;
63 }
64 @property(nonatomic, assign) BOOL editingExistingFolder;
65 @property(nonatomic, assign) bookmarks::BookmarkModel* bookmarkModel;
66 @property(nonatomic, assign) ios::ChromeBrowserState* browserState;
67 @property(nonatomic, assign) const BookmarkNode* folder;
68 @property(nonatomic, retain) BookmarkFolderViewController* folderViewController;
69 @property(nonatomic, assign) const BookmarkNode* parentFolder;
70 @property(nonatomic, assign) UIBarButtonItem* doneItem;
71 @property(nonatomic, retain) BookmarkTextFieldItem* titleItem;
72 @property(nonatomic, retain) BookmarkParentFolderItem* parentFolderItem;
73 // Bottom toolbar with DELETE button that only appears when the edited folder
74 // allows deletion.
75 @property(nonatomic, assign) BookmarksElevatedToolbar* toolbar;
76
77 // |bookmarkModel| must not be NULL and must be loaded.
78 - (instancetype)initWithBookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
79 NS_DESIGNATED_INITIALIZER;
80 - (instancetype)initWithStyle:(CollectionViewControllerStyle)style
81 NS_UNAVAILABLE;
82
83 // Enables or disables the save button depending on the state of the form.
84 - (void)updateSaveButtonState;
85
86 // Configures collection view model.
87 - (void)setupCollectionViewModel;
88
89 // Adds toolbar with DELETE button.
90 - (void)addToolbar;
91
92 // Removes toolbar.
93 - (void)removeToolbar;
94
95 @end
96
97 @implementation BookmarkFolderEditorViewController
98
99 @synthesize bookmarkModel = _bookmarkModel;
100 @synthesize delegate = _delegate;
101 @synthesize editingExistingFolder = _editingExistingFolder;
102 @synthesize folder = _folder;
103 @synthesize folderViewController = _folderViewController;
104 @synthesize parentFolder = _parentFolder;
105 @synthesize browserState = _browserState;
106 @synthesize doneItem = _doneItem;
107 @synthesize titleItem = _titleItem;
108 @synthesize parentFolderItem = _parentFolderItem;
109 @synthesize toolbar = _toolbar;
110
111 #pragma mark - Class methods
112
113 + (instancetype)
114 folderCreatorWithBookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
115 parentFolder:(const BookmarkNode*)parentFolder {
116 base::scoped_nsobject<BookmarkFolderEditorViewController> folderCreator(
117 [[self alloc] initWithBookmarkModel:bookmarkModel]);
118 folderCreator.get().parentFolder = parentFolder;
119 folderCreator.get().folder = NULL;
120 folderCreator.get().editingExistingFolder = NO;
121 return folderCreator.autorelease();
122 }
123
124 + (instancetype)
125 folderEditorWithBookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel
126 folder:(const BookmarkNode*)folder
127 browserState:(ios::ChromeBrowserState*)browserState {
128 DCHECK(folder);
129 DCHECK(!bookmarkModel->is_permanent_node(folder));
130 DCHECK(browserState);
131 base::scoped_nsobject<BookmarkFolderEditorViewController> folderEditor(
132 [[self alloc] initWithBookmarkModel:bookmarkModel]);
133 folderEditor.get().parentFolder = folder->parent();
134 folderEditor.get().folder = folder;
135 folderEditor.get().browserState = browserState;
136 folderEditor.get().editingExistingFolder = YES;
137 return folderEditor.autorelease();
138 }
139
140 #pragma mark - Initialization
141
142 - (instancetype)initWithBookmarkModel:(bookmarks::BookmarkModel*)bookmarkModel {
143 DCHECK(bookmarkModel);
144 DCHECK(bookmarkModel->loaded());
145 self = [super initWithStyle:CollectionViewControllerStyleAppBar];
146 if (self) {
147 _propertyReleaser_BookmarkFolderEditorViewController.Init(
148 self, [BookmarkFolderEditorViewController class]);
149 _bookmarkModel = bookmarkModel;
150
151 // Set up the bookmark model oberver.
152 _modelBridge.reset(
153 new bookmarks::BookmarkModelBridge(self, _bookmarkModel));
154 }
155 return self;
156 }
157
158 - (instancetype)initWithStyle:(CollectionViewControllerStyle)style {
159 NOTREACHED();
160 return nil;
161 }
162
163 - (void)dealloc {
164 _titleItem.delegate = nil;
165 _folderViewController.delegate = nil;
166 [super dealloc];
167 }
168
169 #pragma mark - UIViewController
170
171 - (void)viewDidLoad {
172 [super viewDidLoad];
173 self.collectionView.backgroundColor = [UIColor whiteColor];
174
175 // Add Done button.
176 base::scoped_nsobject<UIBarButtonItem> doneItem([[UIBarButtonItem alloc]
177 initWithTitle:l10n_util::GetNSString(
178 IDS_IOS_BOOKMARK_EDIT_MODE_EXIT_MOBILE)
179 style:UIBarButtonItemStylePlain
180 target:self
181 action:@selector(saveFolder)]);
182 doneItem.get().accessibilityIdentifier = @"Save";
183 self.navigationItem.rightBarButtonItem = doneItem;
184 self.doneItem = doneItem;
185
186 if (self.editingExistingFolder) {
187 // Add Cancel Button.
188 UIBarButtonItem* cancelItem =
189 [ChromeIcon templateBarButtonItemWithImage:[ChromeIcon closeIcon]
190 target:self
191 action:@selector(cancel)];
192 cancelItem.accessibilityLabel =
193 l10n_util::GetNSString(IDS_IOS_BOOKMARK_NEW_CANCEL_BUTTON_LABEL);
194 cancelItem.accessibilityIdentifier = @"Cancel";
195 self.navigationItem.leftBarButtonItem = cancelItem;
196
197 [self addToolbar];
198 } else {
199 // Add Back button.
200 UIBarButtonItem* backItem =
201 [ChromeIcon templateBarButtonItemWithImage:[ChromeIcon backIcon]
202 target:self
203 action:@selector(back)];
204 backItem.accessibilityLabel =
205 l10n_util::GetNSString(IDS_IOS_BOOKMARK_NEW_BACK_LABEL);
206 backItem.accessibilityIdentifier = @"Back";
207 self.navigationItem.leftBarButtonItem = backItem;
208 }
209
210 [self updateEditingState];
211 [self setupCollectionViewModel];
212 }
213
214 - (void)viewWillAppear:(BOOL)animated {
215 [super viewWillAppear:animated];
216 [self updateSaveButtonState];
217 }
218
219 #pragma mark - Accessibility
220
221 - (BOOL)accessibilityPerformEscape {
222 [self.delegate bookmarkFolderEditorDidCancel:self];
223 return YES;
224 }
225
226 #pragma mark - Actions
227
228 - (void)back {
229 [self.delegate bookmarkFolderEditorDidCancel:self];
230 }
231
232 - (void)cancel {
233 [self.delegate bookmarkFolderEditorDidCancel:self];
234 }
235
236 - (void)deleteFolder {
237 DCHECK(self.editingExistingFolder);
238 DCHECK(self.folder);
239 std::set<const BookmarkNode*> editedNodes;
240 editedNodes.insert(self.folder);
241 bookmark_utils_ios::DeleteBookmarksWithUndoToast(
242 editedNodes, self.bookmarkModel, self.browserState);
243 [self.delegate bookmarkFolderEditorDidDeleteEditedFolder:self];
244 }
245
246 - (void)saveFolder {
247 DCHECK(self.parentFolder);
248
249 NSString* folderString = self.titleItem.text;
250 DCHECK(folderString.length > 0);
251 base::string16 folderTitle = base::SysNSStringToUTF16(folderString);
252
253 if (self.editingExistingFolder) {
254 DCHECK(self.folder);
255 self.bookmarkModel->SetTitle(self.folder, folderTitle);
256 if (self.folder->parent() != self.parentFolder) {
257 base::AutoReset<BOOL> autoReset(&_ignoresOwnMove, YES);
258 std::set<const BookmarkNode*> editedNodes;
259 editedNodes.insert(self.folder);
260 bookmark_utils_ios::MoveBookmarksWithUndoToast(
261 editedNodes, self.bookmarkModel, self.parentFolder,
262 self.browserState);
263 }
264 } else {
265 DCHECK(!self.folder);
266 self.folder = self.bookmarkModel->AddFolder(
267 self.parentFolder, self.parentFolder->child_count(), folderTitle);
268 }
269 [self.delegate bookmarkFolderEditor:self didFinishEditingFolder:self.folder];
270 }
271
272 - (void)changeParentFolder {
273 std::set<const BookmarkNode*> editedNodes;
274 if (self.folder)
275 editedNodes.insert(self.folder);
276 base::scoped_nsobject<BookmarkFolderViewController> folderViewController(
277 [[BookmarkFolderViewController alloc]
278 initWithBookmarkModel:self.bookmarkModel
279 allowsNewFolders:NO
280 editedNodes:editedNodes
281 allowsCancel:NO
282 selectedFolder:self.parentFolder]);
283 folderViewController.get().delegate = self;
284 self.folderViewController = folderViewController;
285
286 [self.navigationController pushViewController:folderViewController
287 animated:YES];
288 }
289
290 #pragma mark - BookmarkFolderViewControllerDelegate
291
292 - (void)folderPicker:(BookmarkFolderViewController*)folderPicker
293 didFinishWithFolder:(const BookmarkNode*)folder {
294 self.parentFolder = folder;
295 [self updateParentFolderState];
296 [self.navigationController popViewControllerAnimated:YES];
297 self.folderViewController.delegate = nil;
298 self.folderViewController = nil;
299 }
300
301 - (void)folderPickerDidCancel:(BookmarkFolderViewController*)folderPicker {
302 [self.navigationController popViewControllerAnimated:YES];
303 self.folderViewController.delegate = nil;
304 self.folderViewController = nil;
305 }
306
307 #pragma mark - BookmarkModelBridgeObserver
308
309 - (void)bookmarkModelLoaded {
310 // The bookmark model is assumed to be loaded when this controller is created.
311 NOTREACHED();
312 }
313
314 - (void)bookmarkNodeChanged:(const BookmarkNode*)bookmarkNode {
315 if (bookmarkNode == self.parentFolder) {
316 [self updateParentFolderState];
317 }
318 }
319
320 - (void)bookmarkNodeChildrenChanged:(const BookmarkNode*)bookmarkNode {
321 // No-op.
322 }
323
324 - (void)bookmarkNode:(const BookmarkNode*)bookmarkNode
325 movedFromParent:(const BookmarkNode*)oldParent
326 toParent:(const BookmarkNode*)newParent {
327 if (_ignoresOwnMove)
328 return;
329 if (bookmarkNode == self.folder) {
330 DCHECK(oldParent == self.parentFolder);
331 self.parentFolder = newParent;
332 [self updateParentFolderState];
333 }
334 }
335
336 - (void)bookmarkNodeDeleted:(const BookmarkNode*)bookmarkNode
337 fromFolder:(const BookmarkNode*)folder {
338 if (bookmarkNode == self.parentFolder) {
339 self.parentFolder = NULL;
340 [self updateParentFolderState];
341 return;
342 }
343 if (bookmarkNode == self.folder) {
344 self.folder = NULL;
345 self.editingExistingFolder = NO;
346 [self updateEditingState];
347 }
348 }
349
350 - (void)bookmarkModelRemovedAllNodes {
351 if (self.bookmarkModel->is_permanent_node(self.parentFolder))
352 return; // The current parent folder is still valid.
353
354 self.parentFolder = NULL;
355 [self updateParentFolderState];
356 }
357
358 #pragma mark - BookmarkTextFieldItemDelegate
359
360 - (void)textDidChangeForItem:(BookmarkTextFieldItem*)item {
361 [self updateSaveButtonState];
362 }
363
364 - (BOOL)textFieldShouldReturn:(UITextField*)textField {
365 [textField resignFirstResponder];
366 return YES;
367 }
368
369 #pragma mark - UICollectionViewDelegate
370
371 - (void)collectionView:(UICollectionView*)collectionView
372 didSelectItemAtIndexPath:(NSIndexPath*)indexPath {
373 [super collectionView:collectionView didSelectItemAtIndexPath:indexPath];
374 if ([self.collectionViewModel itemTypeForIndexPath:indexPath] ==
375 ItemTypeParentFolder) {
376 [self changeParentFolder];
377 }
378 }
379
380 #pragma mark - UICollectionViewFlowLayout
381
382 - (CGSize)collectionView:(UICollectionView*)collectionView
383 layout:(UICollectionViewLayout*)collectionViewLayout
384 sizeForItemAtIndexPath:(NSIndexPath*)indexPath {
385 switch ([self.collectionViewModel itemTypeForIndexPath:indexPath]) {
386 case ItemTypeFolderTitle: {
387 const CGFloat kTitleCellHeight = 96;
388 return CGSizeMake(CGRectGetWidth(collectionView.bounds),
389 kTitleCellHeight);
390 }
391 case ItemTypeParentFolder: {
392 const CGFloat kParentFolderCellHeight = 50;
393 return CGSizeMake(CGRectGetWidth(collectionView.bounds),
394 kParentFolderCellHeight);
395 }
396 default:
397 NOTREACHED();
398 return CGSizeZero;
399 }
400 }
401
402 #pragma mark - Private
403
404 - (void)setParentFolder:(const BookmarkNode*)parentFolder {
405 if (!parentFolder) {
406 parentFolder = self.bookmarkModel->mobile_node();
407 }
408 _parentFolder = parentFolder;
409 }
410
411 - (void)updateEditingState {
412 if (![self isViewLoaded])
413 return;
414
415 self.view.accessibilityIdentifier =
416 (self.folder) ? @"Folder Editor" : @"Folder Creator";
417
418 [self setTitle:(self.folder)
419 ? l10n_util::GetNSString(
420 IDS_IOS_BOOKMARK_NEW_GROUP_EDITOR_EDIT_TITLE)
421 : l10n_util::GetNSString(
422 IDS_IOS_BOOKMARK_NEW_GROUP_EDITOR_CREATE_TITLE)];
423 }
424
425 - (void)updateParentFolderState {
426 NSIndexPath* folderSelectionIndexPath =
427 [self.collectionViewModel indexPathForItemType:ItemTypeParentFolder
428 sectionIdentifier:SectionIdentifierInfo];
429 self.parentFolderItem.title =
430 bookmark_utils_ios::TitleForBookmarkNode(self.parentFolder);
431 [self.collectionView reloadItemsAtIndexPaths:@[ folderSelectionIndexPath ]];
432
433 if (self.editingExistingFolder && !self.toolbar)
434 [self addToolbar];
435
436 if (!self.editingExistingFolder && self.toolbar)
437 [self removeToolbar];
438 }
439
440 - (void)setupCollectionViewModel {
441 [self loadModel];
442
443 [self.collectionViewModel addSectionWithIdentifier:SectionIdentifierInfo];
444
445 base::scoped_nsobject<BookmarkTextFieldItem> titleItem(
446 [[BookmarkTextFieldItem alloc] initWithType:ItemTypeFolderTitle]);
447 titleItem.get().text =
448 (self.folder)
449 ? bookmark_utils_ios::TitleForBookmarkNode(self.folder)
450 : l10n_util::GetNSString(IDS_IOS_BOOKMARK_NEW_GROUP_DEFAULT_NAME);
451 titleItem.get().placeholder =
452 l10n_util::GetNSString(IDS_IOS_BOOKMARK_NEW_EDITOR_NAME_LABEL);
453 titleItem.get().accessibilityIdentifier = @"Title";
454 [self.collectionViewModel addItem:titleItem
455 toSectionWithIdentifier:SectionIdentifierInfo];
456 titleItem.get().delegate = self;
457 self.titleItem = titleItem;
458
459 base::scoped_nsobject<BookmarkParentFolderItem> parentFolderItem(
460 [[BookmarkParentFolderItem alloc] initWithType:ItemTypeParentFolder]);
461 parentFolderItem.get().title =
462 bookmark_utils_ios::TitleForBookmarkNode(self.parentFolder);
463 [self.collectionViewModel addItem:parentFolderItem
464 toSectionWithIdentifier:SectionIdentifierInfo];
465 self.parentFolderItem = parentFolderItem;
466 }
467
468 - (void)addToolbar {
469 // Add bottom toolbar with Delete button.
470 base::scoped_nsobject<BookmarksElevatedToolbar> buttonBar(
471 [[BookmarksElevatedToolbar alloc] init]);
472 base::scoped_nsobject<UIBarButtonItem> deleteItem([[UIBarButtonItem alloc]
473 initWithTitle:l10n_util::GetNSString(IDS_IOS_BOOKMARK_GROUP_DELETE)
474 style:UIBarButtonItemStylePlain
475 target:self
476 action:@selector(deleteFolder)]);
477 deleteItem.get().accessibilityIdentifier = @"Delete Folder";
478 [deleteItem setTitleTextAttributes:@{
479 NSForegroundColorAttributeName : [UIColor blackColor]
480 }
481 forState:UIControlStateNormal];
482 [buttonBar.get().layer
483 addSublayer:[[[MDCShadowLayer alloc] init] autorelease]];
484 buttonBar.get().shadowElevation = MDCShadowElevationSearchBarResting;
485 buttonBar.get().items = @[ deleteItem ];
486 [self.view addSubview:buttonBar];
487
488 // Constraint |buttonBar| to be in bottom.
489 buttonBar.get().translatesAutoresizingMaskIntoConstraints = NO;
490 [self.view addConstraints:
491 [NSLayoutConstraint
492 constraintsWithVisualFormat:@"H:|[buttonBar]|"
493 options:0
494 metrics:nil
495 views:NSDictionaryOfVariableBindings(
496 buttonBar)]];
497 [self.view addConstraint:[NSLayoutConstraint
498 constraintWithItem:buttonBar
499 attribute:NSLayoutAttributeBottom
500 relatedBy:NSLayoutRelationEqual
501 toItem:self.view
502 attribute:NSLayoutAttributeBottom
503 multiplier:1.0
504 constant:0.0]];
505 [self.view
506 addConstraint:[NSLayoutConstraint
507 constraintWithItem:buttonBar
508 attribute:NSLayoutAttributeHeight
509 relatedBy:NSLayoutRelationEqual
510 toItem:nil
511 attribute:NSLayoutAttributeNotAnAttribute
512 multiplier:1.0
513 constant:48.0]];
514 self.toolbar = buttonBar;
515 }
516
517 - (void)removeToolbar {
518 [self.toolbar removeFromSuperview];
519 self.toolbar = nil;
520 }
521
522 - (void)updateSaveButtonState {
523 self.doneItem.enabled = (self.titleItem.text.length > 0);
524 }
525
526 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698