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

Side by Side Diff: ui/base/ios/cru_context_menu_controller.mm

Issue 1707603002: Removes UIActionSheet implementation of CRUActionSheetController (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 10 months 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
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 The Chromium Authors. All rights reserved. 1 // Copyright 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #import "ui/base/ios/cru_context_menu_controller.h" 5 #import "ui/base/ios/cru_context_menu_controller.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/ios/ios_util.h"
10 #include "base/ios/weak_nsobject.h" 9 #include "base/ios/weak_nsobject.h"
11 #include "base/logging.h" 10 #include "base/logging.h"
12 #import "base/mac/scoped_nsobject.h" 11 #import "base/mac/scoped_nsobject.h"
13 #include "ui/base/device_form_factor.h" 12 #include "ui/base/device_form_factor.h"
14 #import "ui/base/ios/cru_context_menu_holder.h" 13 #import "ui/base/ios/cru_context_menu_holder.h"
15 #include "ui/base/l10n/l10n_util.h" 14 #include "ui/base/l10n/l10n_util.h"
16 #import "ui/gfx/ios/NSString+CrStringDrawing.h"
17 #include "ui/strings/grit/ui_strings.h" 15 #include "ui/strings/grit/ui_strings.h"
18 16
19 namespace {
20
21 // Returns the screen's height in points.
22 CGFloat GetScreenHeight() {
23 DCHECK(!base::ios::IsRunningOnIOS8OrLater());
24 switch ([[UIApplication sharedApplication] statusBarOrientation]) {
25 case UIInterfaceOrientationLandscapeLeft:
26 case UIInterfaceOrientationLandscapeRight:
27 return CGRectGetWidth([[UIScreen mainScreen] bounds]);
28 case UIInterfaceOrientationPortraitUpsideDown:
29 case UIInterfaceOrientationPortrait:
30 case UIInterfaceOrientationUnknown:
31 return CGRectGetHeight([[UIScreen mainScreen] bounds]);
32 }
33 }
34
35 } // namespace
36
37 // Abstracts system implementation of popovers and action sheets. 17 // Abstracts system implementation of popovers and action sheets.
38 @protocol CRUContextMenuControllerImpl<NSObject> 18 @protocol CRUContextMenuControllerImpl<NSObject>
39 19
40 // Whether the context menu is visible. 20 // Whether the context menu is visible.
41 @property(nonatomic, readonly, getter=isVisible) BOOL visible; 21 @property(nonatomic, readonly, getter=isVisible) BOOL visible;
42 22
43 // Displays a context menu. 23 // Displays a context menu.
44 - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder 24 - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder
45 atPoint:(CGPoint)localPoint 25 atPoint:(CGPoint)localPoint
46 inView:(UIView*)view; 26 inView:(UIView*)view;
47 @end 27 @end
48 28
49 // Backs up CRUContextMenuController on iOS 7 by using UIActionSheet. 29 // Backs up CRUContextMenuController by using UIAlertController.
50 @interface CRUActionSheetController
51 : NSObject<CRUContextMenuControllerImpl, UIActionSheetDelegate> {
52 // The action sheet used to display the UI.
53 base::scoped_nsobject<UIActionSheet> _sheet;
54 // Holds all the titles and actions for the menu.
55 base::scoped_nsobject<CRUContextMenuHolder> _menuHolder;
56 }
57 @end
58
59 // Backs up CRUContextMenuController on iOS 8 and higher by using
60 // UIAlertController.
61 @interface CRUAlertController : NSObject<CRUContextMenuControllerImpl> 30 @interface CRUAlertController : NSObject<CRUContextMenuControllerImpl>
Eugene But (OOO till 7-30) 2016/02/17 15:06:24 There is no need for using a bridge pattern now an
62 // Redefined to readwrite. 31 // Redefined to readwrite.
63 @property(nonatomic, readwrite, getter=isVisible) BOOL visible; 32 @property(nonatomic, readwrite, getter=isVisible) BOOL visible;
64 @end 33 @end
65 34
66 // Displays a context menu. Implements Bridge pattern. 35 // Displays a context menu. Implements Bridge pattern.
67 @implementation CRUContextMenuController { 36 @implementation CRUContextMenuController {
68 // Implementation specific for iOS version. 37 // Implementation specific for iOS version.
69 base::scoped_nsprotocol<id<CRUContextMenuControllerImpl>> _impl; 38 base::scoped_nsprotocol<id<CRUContextMenuControllerImpl>> _impl;
70 } 39 }
71 40
72 - (BOOL)isVisible { 41 - (BOOL)isVisible {
73 return [_impl isVisible]; 42 return [_impl isVisible];
74 } 43 }
75 44
76 - (instancetype)init { 45 - (instancetype)init {
77 self = [super init]; 46 self = [super init];
78 if (self) { 47 if (self) {
79 if (base::ios::IsRunningOnIOS8OrLater()) { 48 _impl.reset([[CRUAlertController alloc] init]);
80 _impl.reset([[CRUAlertController alloc] init]);
81 } else {
82 _impl.reset([[CRUActionSheetController alloc] init]);
83 }
84 } 49 }
85 return self; 50 return self;
86 } 51 }
87 52
88 - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder 53 - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder
89 atPoint:(CGPoint)point 54 atPoint:(CGPoint)point
90 inView:(UIView*)view { 55 inView:(UIView*)view {
91 DCHECK(menuHolder.itemCount); 56 DCHECK(menuHolder.itemCount);
92 // Check that the view is still visible on screen, otherwise just return and 57 // Check that the view is still visible on screen, otherwise just return and
93 // don't show the context menu. 58 // don't show the context menu.
94 if (![view window] && ![view isKindOfClass:[UIWindow class]]) 59 if (![view window] && ![view isKindOfClass:[UIWindow class]])
95 return; 60 return;
96 [_impl showWithHolder:menuHolder atPoint:point inView:view]; 61 [_impl showWithHolder:menuHolder atPoint:point inView:view];
97 } 62 }
98 63
99 @end 64 @end
100 65
101 #pragma mark - iOS 7
102
103 @implementation CRUActionSheetController
104 @synthesize visible = _visible;
105
106 - (void)dealloc {
107 if (_visible) {
108 // Context menu must be dismissed explicitly if it is still visible.
109 NSUInteger cancelButtonIndex = [_menuHolder itemCount];
110 [_sheet dismissWithClickedButtonIndex:cancelButtonIndex animated:NO];
111 }
112 [super dealloc];
113 }
114
115 - (void)showWithHolder:(CRUContextMenuHolder*)menuHolder
116 atPoint:(CGPoint)point
117 inView:(UIView*)view {
118 // If the content of UIActionSheet does not fit the screen then scrollbars
119 // are added to the menu items area. If that's the case, elide the title to
120 // avoid having scrollbars for menu items.
121 CGSize spaceAvailableForTitle =
122 [self sizeForTitleThatFitsMenuWithHolder:menuHolder
123 atPoint:point
124 inView:view];
125 NSString* menuTitle = menuHolder.menuTitle;
126 if (menuTitle) {
127 // Show at least one line of text, even if that means the action sheet's
128 // items will need to scroll.
129 const CGFloat kMinimumVerticalSpace = 21;
130 spaceAvailableForTitle.height =
131 std::max(kMinimumVerticalSpace, spaceAvailableForTitle.height);
132 menuTitle = [menuTitle cr_stringByElidingToFitSize:spaceAvailableForTitle];
133 }
134
135 // Present UIActionSheet.
136 _sheet.reset(
137 [self newActionSheetWithHolder:menuHolder title:menuTitle delegate:self]);
138 [_sheet setCancelButtonIndex:menuHolder.itemCount];
139 [_sheet showFromRect:CGRectMake(point.x, point.y, 1.0, 1.0)
140 inView:view
141 animated:YES];
142
143 _menuHolder.reset([menuHolder retain]);
144 _visible = YES;
145 }
146
147 #pragma mark Implementation
148
149 // Returns an approximation of the free space available for the title of an
150 // actionSheet filled with |menu| shown in |view| at |point|.
151 - (CGSize)sizeForTitleThatFitsMenuWithHolder:(CRUContextMenuHolder*)menuHolder
152 atPoint:(CGPoint)point
153 inView:(UIView*)view {
154 // Create a dummy UIActionSheet.
155 base::scoped_nsobject<UIActionSheet> dummySheet(
156 [self newActionSheetWithHolder:menuHolder title:nil delegate:nil]);
157 // Temporarily add the dummy UIActionSheet to |view|.
158 [dummySheet showFromRect:CGRectMake(point.x, point.y, 1.0, 1.0)
159 inView:view
160 animated:NO];
161 // On iPad the actionsheet is positioned under or over |point| (as opposed
162 // to next to it) when the user clicks within approximately 200 points of
163 // respectively the top or bottom edge. This reduces the amount of vertical
164 // space available for the title, hence the large padding on ipad.
165 const CGFloat kPaddingiPad = 200;
166 const CGFloat kPaddingiPhone = 20;
167 BOOL isIPad = ui::GetDeviceFormFactor() == ui::DEVICE_FORM_FACTOR_TABLET;
168 const CGFloat padding = isIPad ? kPaddingiPad : kPaddingiPhone;
169 // A title uses the full width of the actionsheet and all the vertical
170 // space on the screen.
171 CGSize result = CGSizeMake(
172 CGRectGetWidth([dummySheet frame]),
173 GetScreenHeight() - CGRectGetHeight([dummySheet frame]) - padding);
174 [dummySheet dismissWithClickedButtonIndex:0 animated:NO];
175 return result;
176 }
177
178 // Returns an UIActionSheet. Callers responsible for releasing returned object.
179 - (UIActionSheet*)newActionSheetWithHolder:(CRUContextMenuHolder*)menuHolder
180 title:(NSString*)title
181 delegate:(id<UIActionSheetDelegate>)delegate {
182 UIActionSheet* sheet = [[UIActionSheet alloc] initWithTitle:title
183 delegate:delegate
184 cancelButtonTitle:nil
185 destructiveButtonTitle:nil
186 otherButtonTitles:nil];
187
188 for (NSString* itemTitle in menuHolder.itemTitles) {
189 [sheet addButtonWithTitle:itemTitle];
190 }
191 [sheet addButtonWithTitle:l10n_util::GetNSString(IDS_APP_CANCEL)];
192 return sheet;
193 }
194
195 #pragma mark UIActionSheetDelegate
196
197 // Called when the action sheet is dismissed in the modal context menu sheet.
198 // There is no way to dismiss the sheet without going through this method. Note
199 // that on iPad this method is called with the index of an nonexistent cancel
200 // button when the user taps outside the sheet.
201 - (void)actionSheet:(UIActionSheet*)actionSheet
202 didDismissWithButtonIndex:(NSInteger)buttonIndex {
203 NSUInteger unsignedButtonIndex = buttonIndex;
204 // Assumes "cancel" button is last in order.
205 if (unsignedButtonIndex < [_menuHolder itemCount])
206 [_menuHolder performActionAtIndex:unsignedButtonIndex];
207 _menuHolder.reset();
208 _visible = NO;
209 }
210
211 // Called when the user chooses a button in the modal context menu sheet. Note
212 // that on iPad this method is called with the index of an nonexistent cancel
213 // button when the user taps outside the sheet.
214 - (void)actionSheet:(UIActionSheet*)actionSheet
215 clickedButtonAtIndex:(NSInteger)buttonIndex {
216 // Some use cases (e.g. opening a new tab on handset) should not wait for the
217 // action sheet to animate away before executing the action.
218 if ([_menuHolder shouldDismissImmediatelyOnClickedAtIndex:buttonIndex]) {
219 [_sheet dismissWithClickedButtonIndex:buttonIndex animated:NO];
220 }
221 }
222
223 @end
224
225 #pragma mark - iOS8 and higher
226
227 @implementation CRUAlertController 66 @implementation CRUAlertController
228 @synthesize visible = _visible; 67 @synthesize visible = _visible;
229 68
230 - (CGSize)sizeForTitleThatFitsMenuWithHolder:(CRUContextMenuHolder*)menuHolder 69 - (CGSize)sizeForTitleThatFitsMenuWithHolder:(CRUContextMenuHolder*)menuHolder
231 atPoint:(CGPoint)point 70 atPoint:(CGPoint)point
232 inView:(UIView*)view { 71 inView:(UIView*)view {
233 // Presenting and dismissing a dummy UIAlertController flushes a screen. 72 // Presenting and dismissing a dummy UIAlertController flushes a screen.
234 // As a workaround return an estimation of the space available depending 73 // As a workaround return an estimation of the space available depending
235 // on the device's type. 74 // on the device's type.
236 const CGFloat kAvailableWidth = 320; 75 const CGFloat kAvailableWidth = 320;
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
279 118
280 // Present sheet/popover using controller that is added to view hierarchy. 119 // Present sheet/popover using controller that is added to view hierarchy.
281 UIViewController* topController = view.window.rootViewController; 120 UIViewController* topController = view.window.rootViewController;
282 while (topController.presentedViewController) 121 while (topController.presentedViewController)
283 topController = topController.presentedViewController; 122 topController = topController.presentedViewController;
284 [topController presentViewController:alert animated:YES completion:nil]; 123 [topController presentViewController:alert animated:YES completion:nil];
285 self.visible = YES; 124 self.visible = YES;
286 } 125 }
287 126
288 @end 127 @end
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698