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

Side by Side Diff: ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.mm

Issue 2588733002: Upstream Chrome on iOS source code [9/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
5 #import "ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h"
6
7 #include <stdint.h>
8
9 #include "base/ios/ios_util.h"
10 #import "base/ios/weak_nsobject.h"
11 #include "base/logging.h"
12 #include "base/mac/objc_property_releaser.h"
13 #include "base/mac/scoped_nsobject.h"
14 #include "base/metrics/field_trial.h"
15 #include "components/reading_list/core/reading_list_switches.h"
16 #include "components/strings/grit/components_strings.h"
17 #include "ios/chrome/browser/experimental_flags.h"
18 #import "ios/chrome/browser/ui/animation_util.h"
19 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
20 #include "ios/chrome/browser/ui/commands/ios_command_ids.h"
21 #import "ios/chrome/browser/ui/reading_list/reading_list_menu_notification_deleg ate.h"
22 #import "ios/chrome/browser/ui/reading_list/reading_list_menu_notifier.h"
23 #include "ios/chrome/browser/ui/rtl_geometry.h"
24 #include "ios/chrome/browser/ui/toolbar/toolbar_resource_macros.h"
25 #import "ios/chrome/browser/ui/tools_menu/reading_list_menu_view_item.h"
26 #import "ios/chrome/browser/ui/tools_menu/tools_menu_context.h"
27 #import "ios/chrome/browser/ui/tools_menu/tools_menu_view_item.h"
28 #import "ios/chrome/browser/ui/tools_menu/tools_popup_controller.h"
29 #include "ios/chrome/browser/ui/ui_util.h"
30 #import "ios/chrome/browser/ui/uikit_ui_util.h"
31 #import "ios/chrome/common/material_timing.h"
32 #include "ios/chrome/grit/ios_strings.h"
33 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
34 #import "ios/public/provider/chrome/browser/user_feedback/user_feedback_provider .h"
35 #import "ios/third_party/material_components_ios/src/components/Ink/src/Material Ink.h"
36 #include "ui/base/l10n/l10n_util.h"
37 #include "ui/base/l10n/l10n_util_mac.h"
38 #include "ui/base/resource/resource_bundle.h"
39
40 using ios::material::TimingFunction;
41
42 NSString* const kToolsMenuNewTabId = @"kToolsMenuNewTabId";
43 NSString* const kToolsMenuNewIncognitoTabId = @"kToolsMenuNewIncognitoTabId";
44 NSString* const kToolsMenuCloseAllTabsId = @"kToolsMenuCloseAllTabsId";
45 NSString* const kToolsMenuCloseAllIncognitoTabsId =
46 @"kToolsMenuCloseAllIncognitoTabsId";
47 NSString* const kToolsMenuBookmarksId = @"kToolsMenuBookmarksId";
48 NSString* const kToolsMenuReadingListId = @"kToolsMenuReadingListId";
49 NSString* const kToolsMenuOtherDevicesId = @"kToolsMenuOtherDevicesId";
50 NSString* const kToolsMenuHistoryId = @"kToolsMenuHistoryId";
51 NSString* const kToolsMenuReportAnIssueId = @"kToolsMenuReportAnIssueId";
52 NSString* const kToolsMenuFindInPageId = @"kToolsMenuFindInPageId";
53 NSString* const kToolsMenuReaderMode = @"kToolsMenuReaderMode";
54 NSString* const kToolsMenuRequestDesktopId = @"kToolsMenuRequestDesktopId";
55 NSString* const kToolsMenuSettingsId = @"kToolsMenuSettingsId";
56 NSString* const kToolsMenuHelpId = @"kToolsMenuHelpId";
57
58 namespace {
59
60 // Time for ink to fade into view.
61 static const NSTimeInterval kMDCInkTouchDelayInterval = 0.15;
62
63 static const CGFloat kMenuItemHeight = 48;
64
65 static NSString* const kToolsItemCellID = @"ToolsItemCellID";
66
67 // Menu items can be marked as visible or not when Incognito is enabled.
68 // The following bits are used for |visibility| field in |MenuItemInfo|.
69 const NSInteger kVisibleIncognitoOnly = 1 << 0;
70 const NSInteger kVisibleNotIncognitoOnly = 1 << 1;
71
72 // Initialization table for all possible commands to initialize the
73 // tools menu at run time. Data initialized into this structure is not mutable.
74 struct MenuItemInfo {
75 int title_id;
76 NSString* accessibility_id;
77 int command_id;
78 int toolbar_types;
79 // |visibility| is applied if a menu item is included for a given
80 // |toolbar_types|. A value of 0 means the menu item is always visible for
81 // the given |toolbar_types|.
82 int visibility;
83 // Custom class, if any, for the menu item, or |nil|.
84 Class item_class;
85 };
86
87 // Flags for different toolbar types
88 typedef NS_OPTIONS(NSUInteger, kToolbarType) {
89 // clang-format off
90 kToolbarTypeNone = 0,
91 kToolbarTypeWebiPhone = 1 << 0,
92 kToolbarTypeWebiPad = 1 << 1,
93 kToolbarTypeNoTabsiPad = 1 << 2,
94 kToolbarTypeSwitcheriPhone = 1 << 3,
95 kToolbarTypeWebAll = kToolbarTypeWebiPhone | kToolbarTypeWebiPad,
96 kToolbarTypeAll = kToolbarTypeWebAll |
97 kToolbarTypeSwitcheriPhone |
98 kToolbarTypeNoTabsiPad,
99 // clang-format on
100 };
101
102 // Declare all the possible items.
103 static MenuItemInfo itemInfoList[] = {
104 // clang-format off
105 { IDS_IOS_TOOLS_MENU_NEW_TAB, kToolsMenuNewTabId,
106 IDC_NEW_TAB, kToolbarTypeAll,
107 0, nil },
108 { IDS_IOS_TOOLS_MENU_NEW_INCOGNITO_TAB, kToolsMenuNewIncognitoTabId,
109 IDC_NEW_INCOGNITO_TAB, kToolbarTypeAll,
110 0, nil },
111 { IDS_IOS_TOOLS_MENU_CLOSE_ALL_TABS, kToolsMenuCloseAllTabsId,
112 IDC_CLOSE_ALL_TABS, kToolbarTypeSwitcheriPhone,
113 kVisibleNotIncognitoOnly, nil },
114 { IDS_IOS_TOOLS_MENU_CLOSE_ALL_INCOGNITO_TABS,
115 kToolsMenuCloseAllIncognitoTabsId,
116 IDC_CLOSE_ALL_INCOGNITO_TABS, kToolbarTypeSwitcheriPhone,
117 kVisibleIncognitoOnly, nil },
118 { IDS_IOS_TOOLS_MENU_BOOKMARKS, kToolsMenuBookmarksId,
119 IDC_SHOW_BOOKMARK_MANAGER, kToolbarTypeWebAll,
120 0, nil },
121 { IDS_IOS_TOOLS_MENU_READING_LIST, kToolsMenuReadingListId,
122 IDC_SHOW_READING_LIST, kToolbarTypeWebAll,
123 0, [ReadingListMenuViewItem class] },
124 { IDS_IOS_TOOLS_MENU_RECENT_TABS, kToolsMenuOtherDevicesId,
125 IDC_SHOW_OTHER_DEVICES, kToolbarTypeWebAll,
126 kVisibleNotIncognitoOnly, nil },
127 { IDS_HISTORY_SHOW_HISTORY, kToolsMenuHistoryId,
128 IDC_SHOW_HISTORY, kToolbarTypeWebAll,
129 0, nil },
130 { IDS_IOS_OPTIONS_REPORT_AN_ISSUE, kToolsMenuReportAnIssueId,
131 IDC_REPORT_AN_ISSUE, kToolbarTypeAll,
132 0, nil },
133 { IDS_IOS_TOOLS_MENU_FIND_IN_PAGE, kToolsMenuFindInPageId,
134 IDC_FIND, kToolbarTypeWebAll,
135 0, nil },
136 { IDS_IOS_TOOLS_MENU_REQUEST_DESKTOP_SITE, kToolsMenuRequestDesktopId,
137 IDC_REQUEST_DESKTOP_SITE, kToolbarTypeWebAll,
138 0, nil },
139 { IDS_IOS_TOOLS_MENU_READER_MODE, kToolsMenuReaderMode,
140 IDC_READER_MODE, kToolbarTypeWebAll,
141 0, nil },
142 { IDS_IOS_TOOLS_MENU_SETTINGS, kToolsMenuSettingsId,
143 IDC_OPTIONS, kToolbarTypeAll,
144 0, nil },
145 { IDS_IOS_TOOLS_MENU_HELP_MOBILE, kToolsMenuHelpId,
146 IDC_HELP_PAGE_VIA_MENU, kToolbarTypeWebAll,
147 0, nil },
148 // clang-format on
149 };
150
151 NS_INLINE BOOL ItemShouldBeVisible(const MenuItemInfo& item,
152 BOOL incognito,
153 kToolbarType toolbarType) {
154 if (!(item.toolbar_types & toolbarType))
155 return NO;
156
157 if (incognito && (item.visibility & kVisibleNotIncognitoOnly))
158 return NO;
159
160 if (!incognito && (item.visibility & kVisibleIncognitoOnly))
161 return NO;
162
163 if (item.title_id == IDS_IOS_TOOLBAR_SHOW_TABS) {
164 if (!IsIPadIdiom() || !experimental_flags::IsTabSwitcherEnabled()) {
165 return NO;
166 }
167 }
168
169 if (item.title_id == IDS_IOS_TOOLS_MENU_READER_MODE) {
170 if (!experimental_flags::IsReaderModeEnabled()) {
171 return NO;
172 }
173 }
174
175 if (item.title_id == IDS_IOS_TOOLS_MENU_READING_LIST) {
176 if (!reading_list::switches::IsReadingListEnabled()) {
177 return NO;
178 }
179 }
180
181 if (item.title_id == IDS_IOS_OPTIONS_REPORT_AN_ISSUE) {
182 if (!ios::GetChromeBrowserProvider()
183 ->GetUserFeedbackProvider()
184 ->IsUserFeedbackEnabled()) {
185 return NO;
186 }
187 }
188
189 return YES;
190 }
191
192 NS_INLINE void AnimateInViews(NSArray* views,
193 CGFloat initialX,
194 CGFloat initialY) {
195 [views enumerateObjectsUsingBlock:^(UIView* view, NSUInteger index,
196 BOOL* stop) {
197 CGFloat beginTime = index * .035;
198 CABasicAnimation* transformAnimation =
199 [CABasicAnimation animationWithKeyPath:@"transform"];
200 [transformAnimation
201 setFromValue:[NSValue
202 valueWithCATransform3D:CATransform3DMakeTranslation(
203 initialX, initialY, 0)]];
204 [transformAnimation
205 setToValue:[NSValue valueWithCATransform3D:CATransform3DIdentity]];
206 [transformAnimation setDuration:ios::material::kDuration1];
207 [transformAnimation setBeginTime:beginTime];
208
209 CAAnimation* fadeAnimation = OpacityAnimationMake(0, 1);
210 [fadeAnimation setDuration:ios::material::kDuration1];
211 [fadeAnimation
212 setTimingFunction:TimingFunction(ios::material::CurveEaseOut)];
213 [fadeAnimation setBeginTime:beginTime];
214
215 [[view layer]
216 addAnimation:AnimationGroupMake(@[ transformAnimation, fadeAnimation ])
217 forKey:@"animateIn"];
218 }];
219 }
220 } // anonymous namespace
221
222 @interface ToolsMenuButton : UIButton
223 @end
224
225 @implementation ToolsMenuButton
226
227 @end
228
229 @interface ToolsMenuCollectionView : UICollectionView
230 @property(nonatomic, assign) CGPoint touchBeginPoint;
231 @property(nonatomic, assign) CGPoint touchEndPoint;
232 @end
233
234 @implementation ToolsMenuCollectionView
235
236 @synthesize touchBeginPoint = _touchBeginPoint;
237 @synthesize touchEndPoint = _touchEndPoint;
238
239 - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
240 _touchBeginPoint = [[touches anyObject] locationInView:self];
241 [super touchesBegan:touches withEvent:event];
242 }
243
244 - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
245 _touchEndPoint = [[touches anyObject] locationInView:self];
246 [super touchesEnded:touches withEvent:event];
247 }
248
249 @end
250
251 @interface ToolsMenuViewToolsCell : UICollectionViewCell
252 @property(nonatomic, retain) ToolsMenuButton* reloadButton;
253 @property(nonatomic, retain) ToolsMenuButton* shareButton;
254 @property(nonatomic, retain) ToolsMenuButton* starButton;
255 @property(nonatomic, retain) ToolsMenuButton* starredButton;
256 @property(nonatomic, retain) ToolsMenuButton* stopButton;
257 @property(nonatomic, retain) ToolsMenuButton* toolsButton;
258 @end
259
260 @implementation ToolsMenuViewToolsCell {
261 base::mac::ObjCPropertyReleaser _propertyReleaser_ToolsMenuViewToolsCell;
262 }
263
264 @synthesize reloadButton = _reloadButton;
265 @synthesize shareButton = _shareButton;
266 @synthesize starButton = _starButton;
267 @synthesize starredButton = _starredButton;
268 @synthesize stopButton = _stopButton;
269 @synthesize toolsButton = _toolsButton;
270
271 - (instancetype)initWithCoder:(NSCoder*)aDecoder {
272 self = [super initWithCoder:aDecoder];
273 if (self)
274 [self commonInitialization];
275
276 return self;
277 }
278
279 - (instancetype)initWithFrame:(CGRect)frame {
280 self = [super initWithFrame:frame];
281 if (self)
282 [self commonInitialization];
283
284 return self;
285 }
286
287 - (void)commonInitialization {
288 _propertyReleaser_ToolsMenuViewToolsCell.Init(self,
289 [ToolsMenuViewToolsCell class]);
290
291 [self setBackgroundColor:[UIColor whiteColor]];
292 [self setOpaque:YES];
293
294 int star[2][3] = TOOLBAR_IDR_TWO_STATE(STAR);
295 _starButton = [self newButtonForImageIds:star
296 commandID:IDC_BOOKMARK_PAGE
297 accessibilityLabelID:IDS_BOOKMARK_ADD_EDITOR_TITLE
298 automationName:@"Add Bookmark"];
299
300 int star_pressed[2][3] = TOOLBAR_IDR_ONE_STATE(STAR_PRESSED);
301 _starredButton = [self newButtonForImageIds:star_pressed
302 commandID:IDC_TEMP_EDIT_BOOKMARK
303 accessibilityLabelID:IDS_IOS_TOOLS_MENU_EDIT_BOOKMARK
304 automationName:@"Edit Bookmark"];
305
306 int reload[2][3] = TOOLBAR_IDR_TWO_STATE(RELOAD);
307 _reloadButton = [self newButtonForImageIds:reload
308 commandID:IDC_RELOAD
309 accessibilityLabelID:IDS_IOS_ACCNAME_RELOAD
310 automationName:@"Reload"
311 reverseForRTL:YES];
312
313 int stop[2][3] = TOOLBAR_IDR_TWO_STATE(STOP);
314 _stopButton = [self newButtonForImageIds:stop
315 commandID:IDC_STOP
316 accessibilityLabelID:IDS_IOS_ACCNAME_STOP
317 automationName:@"Stop"];
318
319 int share[2][3] = TOOLBAR_IDR_THREE_STATE(SHARE);
320 _shareButton = [self newButtonForImageIds:share
321 commandID:IDC_SHARE_PAGE
322 accessibilityLabelID:IDS_IOS_TOOLS_MENU_SHARE
323 automationName:@"Stop"];
324 int tools[2][3] = TOOLBAR_IDR_ONE_STATE(TOOLS_PRESSED);
325 _toolsButton =
326 [self newButtonForImageIds:tools
327 commandID:IDC_SHOW_TOOLS_MENU
328 accessibilityLabelID:IDS_IOS_TOOLBAR_CLOSE_MENU
329 automationName:@"kToolbarToolsMenuButtonIdentifier"];
330
331 UIView* contentView = [self contentView];
332 [contentView setBackgroundColor:[self backgroundColor]];
333 [contentView setOpaque:YES];
334
335 [contentView addSubview:_starredButton];
336 [contentView addSubview:_starButton];
337 [contentView addSubview:_stopButton];
338 [contentView addSubview:_reloadButton];
339 [contentView addSubview:_shareButton];
340
341 [self addConstraints];
342 }
343
344 - (UIImage*)imageForImageId:(int)imageId reversed:(BOOL)reversed {
345 if (imageId == 0) {
346 return nil;
347 }
348 ResourceBundle& rb = ResourceBundle::GetSharedInstance();
349 gfx::Image image = rb.GetNativeImageNamed(imageId);
350 if (reversed) {
351 return [image.ToUIImage() imageFlippedForRightToLeftLayoutDirection];
352 } else {
353 return image.ToUIImage();
354 }
355 }
356
357 - (ToolsMenuButton*)newButtonForImageIds:(int[2][3])imageIds
358 commandID:(int)commandID
359 accessibilityLabelID:(int)labelID
360 automationName:(NSString*)name {
361 return [self newButtonForImageIds:imageIds
362 commandID:commandID
363 accessibilityLabelID:labelID
364 automationName:name
365 reverseForRTL:NO];
366 }
367
368 - (ToolsMenuButton*)newButtonForImageIds:(int[2][3])imageIds
369 commandID:(int)commandID
370 accessibilityLabelID:(int)labelID
371 automationName:(NSString*)name
372 reverseForRTL:(BOOL)reverseForRTL {
373 ToolsMenuButton* button = [[ToolsMenuButton alloc] initWithFrame:CGRectZero];
374 [button setTranslatesAutoresizingMaskIntoConstraints:NO];
375 BOOL reverseImage = reverseForRTL && UseRTLLayout();
376
377 [button setImage:[self imageForImageId:imageIds[0][0] reversed:reverseImage]
378 forState:UIControlStateNormal];
379 [[button imageView] setContentMode:UIViewContentModeCenter];
380 [button setBackgroundColor:[self backgroundColor]];
381 [button setTag:commandID];
382 [button setOpaque:YES];
383
384 SetA11yLabelAndUiAutomationName(button, labelID, name);
385
386 UIImage* pressedImage =
387 [self imageForImageId:imageIds[0][1] reversed:reverseImage];
388 if (pressedImage) {
389 [button setImage:pressedImage forState:UIControlStateHighlighted];
390 }
391
392 UIImage* disabledImage =
393 [self imageForImageId:imageIds[0][2] reversed:reverseImage];
394 if (disabledImage) {
395 [button setImage:disabledImage forState:UIControlStateDisabled];
396 }
397
398 return button;
399 }
400
401 - (void)addConstraints {
402 UIView* contentView = [self contentView];
403
404 for (UIButton* button in [self allButtons]) {
405 NSDictionary* view = @{ @"button" : button };
406 NSArray* constraints = @[ @"V:|-(0)-[button]-(0)-|", @"H:[button(==48)]" ];
407 ApplyVisualConstraints(constraints, view, self);
408 }
409
410 NSDictionary* views = @{
411 @"share" : _shareButton,
412 @"star" : _starButton,
413 @"reload" : _reloadButton,
414 @"starred" : _starredButton,
415 @"stop" : _stopButton
416 };
417 // Leading offset is 16, minus the button image inset of 12.
418 NSDictionary* metrics = @{ @"offset" : @4, @"space" : @24 };
419 // clang-format off
420 NSArray* constraints = @[
421 @"H:|-(offset)-[share]-(space)-[star]-(space)-[reload]",
422 @"H:[share]-(space)-[starred]",
423 @"H:[star]-(space)-[stop]"
424 ];
425 // clang-format on
426 ApplyVisualConstraintsWithMetricsAndOptions(
427 constraints, views, metrics, LayoutOptionForRTLSupport(), contentView);
428 }
429
430 // These should be added in display order, so they are animated in display
431 // order.
432 - (NSArray*)allButtons {
433 NSMutableArray* allButtons = [NSMutableArray array];
434 if (_shareButton)
435 [allButtons addObject:_shareButton];
436
437 if (_starButton)
438 [allButtons addObject:_starButton];
439
440 if (_starredButton)
441 [allButtons addObject:_starredButton];
442
443 if (_reloadButton)
444 [allButtons addObject:_reloadButton];
445
446 if (_stopButton)
447 [allButtons addObject:_stopButton];
448
449 return allButtons;
450 }
451
452 @end
453
454 // Class Extension for private methods.
455 @interface ToolsMenuViewController ()<UICollectionViewDelegateFlowLayout,
456 UICollectionViewDataSource,
457 ReadingListMenuNotificationDelegate> {
458 base::mac::ObjCPropertyReleaser _propertyReleaser_ToolsMenuViewController;
459 BOOL _waitForInk;
460 // Weak pointer to ReadingListMenuNotifier, used to set the starting values
461 // for the reading list badge.
462 base::WeakNSObject<ReadingListMenuNotifier> _readingListMenuNotifier;
463 }
464 @property(nonatomic, retain) ToolsMenuCollectionView* menuView;
465 @property(nonatomic, retain) MDCInkView* touchFeedbackView;
466 @property(nonatomic, retain) NSMutableArray* menuItems;
467 @property(nonatomic, assign) kToolbarType toolbarType;
468
469 // Get the reading list cell.
470 - (ReadingListMenuViewCell*)readingListCell;
471 @end
472
473 @implementation ToolsMenuViewController
474
475 @synthesize menuView = _menuView;
476 @synthesize isCurrentPageBookmarked = _isCurrentPageBookmarked;
477 @synthesize touchFeedbackView = _touchFeedbackView;
478 @synthesize isTabLoading = _isTabLoading;
479 @synthesize toolbarType = _toolbarType;
480 @synthesize menuItems = _menuItems;
481 @synthesize delegate = _delegate;
482
483 #pragma mark Public methods
484
485 - (CGFloat)optimalHeight:(CGFloat)suggestedHeight {
486 NSInteger numberOfItems = [self.menuItems count];
487 if (_toolbarType == kToolbarTypeWebiPhone) {
488 // Account for the height of the first row, not included in |menuItems|.
489 numberOfItems++;
490 }
491 CGFloat maxItems = suggestedHeight / kMenuItemHeight;
492 if (maxItems >= numberOfItems) {
493 return numberOfItems * kMenuItemHeight;
494 } else {
495 const CGFloat halfHeight = kMenuItemHeight / 2;
496 return round(maxItems) * kMenuItemHeight - halfHeight;
497 }
498 }
499
500 - (void)setItemEnabled:(BOOL)enabled withTag:(NSInteger)tag {
501 for (ToolsMenuViewItem* item in _menuItems) {
502 if ([item tag] == tag) {
503 [item setActive:enabled];
504 break;
505 }
506 }
507
508 for (ToolsMenuViewCell* cell in [_menuView visibleCells]) {
509 if (![cell isKindOfClass:[ToolsMenuViewCell class]])
510 continue;
511
512 if ([cell tag] != tag)
513 continue;
514
515 NSIndexPath* path = [_menuView indexPathForCell:cell];
516 NSInteger itemIndex = [self dataIndexForIndexPath:path];
517 [cell configureForMenuItem:[_menuItems objectAtIndex:itemIndex]];
518 }
519 }
520
521 - (void)setIsCurrentPageBookmarked:(BOOL)value {
522 _isCurrentPageBookmarked = value;
523
524 ToolsMenuViewToolsCell* toolsCell = [self toolsCell];
525 [[toolsCell starButton] setHidden:_isCurrentPageBookmarked];
526 [[toolsCell starredButton] setHidden:!_isCurrentPageBookmarked];
527 }
528
529 - (void)setCanUseReaderMode:(BOOL)enabled {
530 [self setItemEnabled:enabled withTag:IDC_READER_MODE];
531 }
532
533 - (void)setCanUseDesktopUserAgent:(BOOL)enabled {
534 [self setItemEnabled:enabled withTag:IDC_REQUEST_DESKTOP_SITE];
535 }
536
537 - (void)setCanShowFindBar:(BOOL)enabled {
538 [self setItemEnabled:enabled withTag:IDC_FIND];
539 }
540
541 - (void)setCanShowShareMenu:(BOOL)enabled {
542 ToolsMenuViewToolsCell* toolsCell = [self toolsCell];
543 [[toolsCell shareButton] setEnabled:enabled];
544 [self setItemEnabled:enabled withTag:IDC_SHARE_PAGE];
545 }
546
547 - (UIButton*)toolsButton {
548 UIButton* toolsButton = [[self toolsCell] toolsButton];
549 [toolsButton addTarget:self
550 action:@selector(buttonPressed:)
551 forControlEvents:UIControlEventTouchUpInside];
552 [toolsButton setTranslatesAutoresizingMaskIntoConstraints:YES];
553 [toolsButton setOpaque:NO];
554 [toolsButton setBackgroundColor:[UIColor clearColor]];
555 return toolsButton;
556 }
557
558 - (void)setIsTabLoading:(BOOL)isTabLoading {
559 _isTabLoading = isTabLoading;
560
561 ToolsMenuViewToolsCell* toolsCell = [self toolsCell];
562 [[toolsCell stopButton] setHidden:!isTabLoading];
563 [[toolsCell reloadButton] setHidden:isTabLoading];
564 }
565
566 - (void)initializeMenu:(ToolsMenuContext*)context {
567 if (context.readingListMenuNotifier) {
568 _readingListMenuNotifier.reset(context.readingListMenuNotifier);
569 [context.readingListMenuNotifier setDelegate:self];
570 }
571
572 if (IsIPadIdiom()) {
573 _toolbarType = context.hasNoOpenedTabs
574 ? kToolbarTypeNoTabsiPad
575 : (!IsCompactTablet() ? kToolbarTypeWebiPad
576 : kToolbarTypeWebiPhone);
577 } else {
578 // kOptionInTabSwitcher option must be enabled on iPhone with
579 // no opened tabs.
580 DCHECK(!context.hasNoOpenedTabs || context.isInTabSwitcher);
581 _toolbarType = context.isInTabSwitcher ? kToolbarTypeSwitcheriPhone
582 : kToolbarTypeWebiPhone;
583 }
584
585 // Build the menu, adding all relevant items.
586 NSMutableArray* menu = [NSMutableArray array];
587
588 for (size_t i = 0; i < arraysize(itemInfoList); ++i) {
589 const MenuItemInfo& item = itemInfoList[i];
590 if (!ItemShouldBeVisible(item, context.isInIncognito, _toolbarType))
591 continue;
592
593 NSString* title = l10n_util::GetNSStringWithFixup(item.title_id);
594 Class itemClass =
595 item.item_class ? item.item_class : [ToolsMenuViewItem class];
596 // Sanity check that the class is a useful one.
597 DCHECK([itemClass respondsToSelector:@selector(menuItemWithTitle:
598 accessibilityIdentifier:
599 command:)]);
600 [menu addObject:[itemClass menuItemWithTitle:title
601 accessibilityIdentifier:item.accessibility_id
602 command:item.command_id]];
603 }
604
605 #if !defined(NDEBUG)
606 NSUserDefaults* standardDefaults = [NSUserDefaults standardUserDefaults];
607 if ((_toolbarType & kToolbarTypeWebAll) &&
608 [standardDefaults boolForKey:@"DevViewSource"]) {
609 // Debug menu, not localized, only visible if turned on by a default.
610 [menu addObject:[self createViewSourceItem]];
611 }
612 #endif // !defined(NDEBUG)
613
614 [self setMenuItems:menu];
615
616 // Disable IDC_CLOSE_ALL_TABS menu item if on phone with no tabs.
617 if (!IsIPadIdiom()) {
618 [self setItemEnabled:!context.hasNoOpenedTabs withTag:IDC_CLOSE_ALL_TABS];
619 }
620 }
621
622 #if !defined(NDEBUG)
623 - (ToolsMenuViewItem*)createViewSourceItem {
624 return [ToolsMenuViewItem menuItemWithTitle:@"View Source"
625 accessibilityIdentifier:@"View Source"
626 command:IDC_VIEW_SOURCE];
627 }
628 #endif // !defined(NDEBUG)
629
630 #pragma mark - Data handling utilities
631
632 - (ToolsMenuViewToolsCell*)toolsCell {
633 for (ToolsMenuViewToolsCell* visibleCell in [_menuView visibleCells]) {
634 if ([visibleCell isKindOfClass:[ToolsMenuViewToolsCell class]])
635 return visibleCell;
636 }
637
638 return nil;
639 }
640
641 - (ReadingListMenuViewCell*)readingListCell {
642 for (ReadingListMenuViewCell* visibleCell in [_menuView visibleCells]) {
643 if ([visibleCell isKindOfClass:[ReadingListMenuViewCell class]])
644 return visibleCell;
645 }
646
647 return nil;
648 }
649
650 - (NSInteger)dataIndexForIndexPath:(NSIndexPath*)path {
651 NSInteger item = [path item];
652
653 if (_toolbarType == kToolbarTypeWebiPhone)
654 --item;
655
656 return item;
657 }
658
659 #pragma mark - UIViewController Overrides
660
661 - (instancetype)initWithNibName:(NSString*)nibNameOrNil
662 bundle:(NSBundle*)nibBundleOrNil {
663 self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
664 if (self)
665 [self commonInitialization];
666
667 return self;
668 }
669
670 - (instancetype)initWithCoder:(NSCoder*)aDecoder {
671 self = [super initWithCoder:aDecoder];
672 if (self)
673 [self commonInitialization];
674
675 return self;
676 }
677
678 - (void)commonInitialization {
679 _propertyReleaser_ToolsMenuViewController.Init(
680 self, [ToolsMenuViewController class]);
681 _readingListMenuNotifier.reset();
682 }
683
684 - (void)loadView {
685 [super loadView];
686
687 UIView* rootView = [self view];
688 [rootView setAutoresizingMask:UIViewAutoresizingFlexibleHeight |
689 UIViewAutoresizingFlexibleWidth];
690 [rootView setBackgroundColor:[UIColor whiteColor]];
691
692 _touchFeedbackView = [[MDCInkView alloc] initWithFrame:CGRectZero];
693
694 base::scoped_nsobject<UICollectionViewFlowLayout> menuItemsLayout(
695 [[UICollectionViewFlowLayout alloc] init]);
696
697 _menuView = [[ToolsMenuCollectionView alloc] initWithFrame:[rootView bounds]
698 collectionViewLayout:menuItemsLayout];
699 [_menuView setAccessibilityLabel:l10n_util::GetNSString(IDS_IOS_TOOLS_MENU)];
700 [_menuView setAccessibilityIdentifier:kToolsMenuTableViewId];
701 [_menuView setTranslatesAutoresizingMaskIntoConstraints:NO];
702 [_menuView setBackgroundColor:[UIColor whiteColor]];
703 [_menuView setDataSource:self];
704 [_menuView setDelegate:self];
705 [_menuView setOpaque:YES];
706
707 [rootView addSubview:_menuView];
708 [self updateViewConstraints];
709 }
710
711 - (void)viewDidLoad {
712 [super viewDidLoad];
713 [_menuView registerClass:[ToolsMenuViewToolsCell class]
714 forCellWithReuseIdentifier:kToolsItemCellID];
715
716 [_menuView registerClass:[ToolsMenuViewItem cellClass]
717 forCellWithReuseIdentifier:[ToolsMenuViewItem cellID]];
718
719 // Register each custom class.
720 NSMutableSet* registeredClasses = [NSMutableSet set];
721 for (size_t i = 0; i < arraysize(itemInfoList); ++i) {
722 const MenuItemInfo& item = itemInfoList[i];
723 if (!item.item_class || [registeredClasses containsObject:item.item_class])
724 continue;
725 [_menuView registerClass:[item.item_class cellClass]
726 forCellWithReuseIdentifier:[item.item_class cellID]];
727 [registeredClasses addObject:item.item_class];
728 }
729 }
730
731 - (void)updateViewConstraints {
732 [super updateViewConstraints];
733
734 UIView* rootView = [self view];
735 NSDictionary* view = @{ @"menu" : _menuView };
736 NSArray* constraints = @[ @"V:|-(0)-[menu]-(0)-|", @"H:|-(0)-[menu]-(0)-|" ];
737 ApplyVisualConstraints(constraints, view, rootView);
738 }
739
740 #pragma mark - Content Animation Stuff
741
742 - (void)animateContentIn {
743 // Make sure that the collection view has laid out before trying to animate
744 // the contents.
745 [_menuView layoutIfNeeded];
746
747 NSArray* visibleCells =
748 [[_menuView visibleCells] sortedArrayUsingComparator:^NSComparisonResult(
749 UIView* view1, UIView* view2) {
750 CGPoint center1 = [view1 center];
751 CGPoint center2 = [view2 center];
752
753 if (center1.y < center2.y)
754 return NSOrderedAscending;
755
756 if (center1.y > center2.y)
757 return NSOrderedDescending;
758
759 return NSOrderedSame;
760 }];
761
762 ToolsMenuViewToolsCell* toolsCell = nil;
763 if (_toolbarType == kToolbarTypeWebiPhone) {
764 toolsCell = [visibleCells firstObject];
765 if ([toolsCell isKindOfClass:[ToolsMenuViewToolsCell class]]) {
766 visibleCells = [visibleCells
767 subarrayWithRange:NSMakeRange(1, [visibleCells count] - 1)];
768 } else {
769 toolsCell = nil;
770 }
771 }
772
773 [CATransaction begin];
774 [CATransaction
775 setAnimationTimingFunction:TimingFunction(ios::material::CurveEaseInOut)];
776 [CATransaction setAnimationDuration:ios::material::kDuration5];
777 AnimateInViews([toolsCell allButtons], 10, 0);
778 AnimateInViews(visibleCells, 0, -10);
779 [CATransaction commit];
780
781 [[self readingListCell]
782 updateBadgeCount:_readingListMenuNotifier.get().readingListUnreadCount
783 animated:YES];
784 [[self readingListCell]
785 updateSeenState:_readingListMenuNotifier.get().readingListUnseenItemsExist
786 animated:YES];
787 }
788
789 - (void)hideContent {
790 _menuView.alpha = 0;
791
792 // Remove the target/action for touching the buttons. VoiceOver may hold on
793 // to these buttons after |self| has been released, causing a crash if a
794 // button is activated (see http://crbug.com/480974 ).
795 UIButton* toolsButton = [[self toolsCell] toolsButton];
796 [toolsButton removeTarget:self
797 action:@selector(buttonPressed:)
798 forControlEvents:UIControlEventTouchUpInside];
799 for (UIButton* button in [[self toolsCell] allButtons]) {
800 [button removeTarget:self
801 action:@selector(buttonPressed:)
802 forControlEvents:UIControlEventTouchUpInside];
803 }
804 }
805
806 #pragma mark - Button event handling
807
808 - (IBAction)buttonPressed:(id)sender {
809 int commandId = [sender tag];
810 DCHECK(commandId);
811 // The bookmark command workaround is only needed for metrics; remap it
812 // to the real command for the dispatch. This is very hacky, but it will go
813 // away soon. See crbug/228521
814 DCHECK([sender respondsToSelector:@selector(setTag:)]);
815 if (commandId == IDC_TEMP_EDIT_BOOKMARK)
816 [sender setTag:IDC_BOOKMARK_PAGE];
817 // Do nothing when tapping the tools menu a second time.
818 if (commandId != IDC_SHOW_TOOLS_MENU) {
819 [self chromeExecuteCommand:sender];
820 }
821 if (commandId == IDC_TEMP_EDIT_BOOKMARK)
822 [sender setTag:IDC_TEMP_EDIT_BOOKMARK];
823
824 [_delegate commandWasSelected:commandId];
825 }
826
827 #pragma mark - UICollectionViewDelegate Implementation
828
829 - (BOOL)collectionView:(ToolsMenuCollectionView*)view
830 shouldHighlightItemAtIndexPath:(NSIndexPath*)path {
831 if (view.tracking)
832 return NO;
833 NSInteger item = [self dataIndexForIndexPath:path];
834 return (item >= 0);
835 }
836
837 - (void)collectionView:(ToolsMenuCollectionView*)view
838 didHighlightItemAtIndexPath:(NSIndexPath*)path {
839 ToolsMenuViewCell* cell =
840 (ToolsMenuViewCell*)[view cellForItemAtIndexPath:path];
841
842 NSInteger item = [self dataIndexForIndexPath:path];
843 DCHECK_GE(item, 0);
844 ToolsMenuViewItem* menuItem = [_menuItems objectAtIndex:item];
845 DCHECK(menuItem);
846 if ([menuItem active]) {
847 [_touchFeedbackView setFrame:cell.bounds];
848 [cell addSubview:_touchFeedbackView];
849
850 CGPoint touchPoint = [view touchBeginPoint];
851 touchPoint = [view convertPoint:touchPoint toView:_touchFeedbackView];
852 [_touchFeedbackView startTouchBeganAnimationAtPoint:touchPoint
853 completion:nil];
854 }
855 }
856
857 - (void)collectionView:(ToolsMenuCollectionView*)view
858 didUnhighlightItemAtIndexPath:(NSIndexPath*)path {
859 CGPoint touchPoint = [view touchEndPoint];
860 touchPoint = [view convertPoint:touchPoint toView:_touchFeedbackView];
861 base::WeakNSObject<MDCInkView> inkView(_touchFeedbackView);
862 _waitForInk = YES;
863 [_touchFeedbackView startTouchEndedAnimationAtPoint:touchPoint
864 completion:^{
865 _waitForInk = NO;
866 [inkView removeFromSuperview];
867 }];
868 }
869
870 - (BOOL)collectionView:(UICollectionView*)view
871 shouldSelectItemAtIndexPath:(NSIndexPath*)path {
872 NSInteger item = [self dataIndexForIndexPath:path];
873 if (item < 0)
874 return NO;
875
876 return [[_menuItems objectAtIndex:item] active];
877 }
878
879 - (void)collectionView:(UICollectionView*)view
880 didSelectItemAtIndexPath:(NSIndexPath*)path {
881 [view deselectItemAtIndexPath:path animated:YES];
882
883 NSInteger item = [self dataIndexForIndexPath:path];
884 if (item < 0)
885 return;
886
887 dispatch_time_t delayTime = dispatch_time(
888 DISPATCH_TIME_NOW, (int64_t)(NSEC_PER_SEC * kMDCInkTouchDelayInterval));
889 dispatch_after(
890 _waitForInk ? delayTime : 0, dispatch_get_main_queue(), ^(void) {
891 ToolsMenuViewItem* menuItem = [_menuItems objectAtIndex:item];
892 DCHECK([menuItem tag]);
893 [_delegate commandWasSelected:[menuItem tag]];
894 [self chromeExecuteCommand:menuItem];
895 });
896 }
897
898 #pragma mark - UICollectionViewDataSource Implementation
899
900 - (NSInteger)collectionView:(UICollectionView*)view
901 numberOfItemsInSection:(NSInteger)section {
902 NSInteger numberOfItems = [_menuItems count];
903 if (_toolbarType == kToolbarTypeWebiPhone)
904 ++numberOfItems;
905
906 return numberOfItems;
907 }
908
909 - (UICollectionViewCell*)collectionView:(UICollectionView*)view
910 cellForItemAtIndexPath:(NSIndexPath*)path {
911 NSInteger item = [self dataIndexForIndexPath:path];
912 if (item < 0) {
913 ToolsMenuViewToolsCell* cell =
914 [view dequeueReusableCellWithReuseIdentifier:kToolsItemCellID
915 forIndexPath:path];
916 for (UIButton* button in [cell allButtons]) {
917 [button addTarget:self
918 action:@selector(buttonPressed:)
919 forControlEvents:UIControlEventTouchUpInside];
920 }
921 return cell;
922 }
923
924 ToolsMenuViewItem* menuItem = [_menuItems objectAtIndex:item];
925 ToolsMenuViewCell* menuItemCell =
926 [view dequeueReusableCellWithReuseIdentifier:[[menuItem class] cellID]
927 forIndexPath:path];
928 [menuItemCell configureForMenuItem:menuItem];
929
930 return menuItemCell;
931 }
932
933 #pragma mark - UICollectionViewDelegateFlowLayout Implementation
934
935 - (CGSize)collectionView:(UICollectionView*)view
936 layout:(UICollectionViewLayout*)collectionViewLayout
937 sizeForItemAtIndexPath:(NSIndexPath*)path {
938 return CGSizeMake(CGRectGetWidth([_menuView bounds]), kMenuItemHeight);
939 }
940
941 - (CGFloat)collectionView:(UICollectionView*)collectionView
942 layout:(UICollectionViewLayout*)
943 collectionViewLayout
944 minimumLineSpacingForSectionAtIndex:(NSInteger)section {
945 return 0;
946 }
947
948 #pragma mark - ReadingListMenuNotificationDelegate Implementation
949
950 - (void)unreadCountChanged:(NSInteger)unreadCount {
951 [[self readingListCell] updateBadgeCount:unreadCount animated:YES];
952 }
953
954 - (void)unseenStateChanged:(BOOL)unseenItemsExist {
955 [[self readingListCell] updateSeenState:unseenItemsExist animated:YES];
956 }
957
958 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/ui/tools_menu/tools_menu_view_controller.h ('k') | ios/chrome/browser/ui/tools_menu/tools_menu_view_item.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698