OLD | NEW |
(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/autofill/form_input_accessory_view.h" |
| 6 |
| 7 #import <QuartzCore/QuartzCore.h> |
| 8 |
| 9 #include "base/i18n/rtl.h" |
| 10 #include "base/ios/weak_nsobject.h" |
| 11 #include "base/mac/scoped_nsobject.h" |
| 12 #import "ios/chrome/browser/autofill/form_input_accessory_view_delegate.h" |
| 13 #import "ios/chrome/browser/ui/image_util.h" |
| 14 #include "ios/chrome/browser/ui/ui_util.h" |
| 15 |
| 16 namespace { |
| 17 |
| 18 // The alpha value of the background color. |
| 19 const CGFloat kBackgroundColorAlpha = 1.0; |
| 20 |
| 21 // Horizontal margin around the custom view. |
| 22 const CGFloat kCustomViewHorizontalMargin = 2; |
| 23 |
| 24 // The width of the previous and next buttons. |
| 25 const CGFloat kNavigationButtonWidth = 44; |
| 26 |
| 27 // The width of the separators of the previous and next buttons. |
| 28 const CGFloat kNavigationButtonSeparatorWidth = 1; |
| 29 |
| 30 // The width of the shadow part of the navigation area separator. |
| 31 const CGFloat kNavigationAreaSeparatorShadowWidth = 2; |
| 32 |
| 33 // The width of the navigation area / custom view separator asset. |
| 34 const CGFloat kNavigationAreaSeparatorWidth = 1; |
| 35 |
| 36 // Returns YES if the keyboard close button should be shown on the accessory. |
| 37 BOOL ShouldShowCloseButton() { |
| 38 return !IsIPadIdiom(); |
| 39 } |
| 40 |
| 41 // Returns the width of navigation view. |
| 42 CGFloat GetNavigationViewWidth() { |
| 43 // The number of naviation buttons (includes close button if shown). |
| 44 NSUInteger numberNavigationButtons = 2; |
| 45 if (ShouldShowCloseButton()) |
| 46 numberNavigationButtons++; |
| 47 return numberNavigationButtons * kNavigationButtonWidth + |
| 48 (numberNavigationButtons - 1) * kNavigationButtonSeparatorWidth + |
| 49 kNavigationAreaSeparatorWidth; |
| 50 } |
| 51 |
| 52 } // namespace |
| 53 |
| 54 @interface FormInputAccessoryView () |
| 55 |
| 56 // Initializes the view with the given |customView|. |
| 57 // If the size of |rightFrame| is non-zero, the view will be split into two |
| 58 // parts with |leftFrame| and |rightFrame|. Otherwise the Autofill view will |
| 59 // be shown in |leftFrame|. |
| 60 - (void)initializeViewWithCustomView:(UIView*)customView |
| 61 leftFrame:(CGRect)leftFrame |
| 62 rightFrame:(CGRect)rightFrame; |
| 63 |
| 64 // Returns a view that shows navigation buttons in the |frame|. |
| 65 - (UIView*)viewForNavigationButtonsInFrame:(CGRect)frame; |
| 66 |
| 67 // Returns a navigation button for Autofill that has |normalImage| for state |
| 68 // UIControlStateNormal, a |pressedImage| for states UIControlStateSelected and |
| 69 // UIControlStateHighlighted, and an optional |disabledImage| for |
| 70 // UIControlStateDisabled. |
| 71 - (UIButton*)keyboardNavButtonWithNormalImage:(UIImage*)normalImage |
| 72 pressedImage:(UIImage*)pressedImage |
| 73 disabledImage:(UIImage*)disabledImage |
| 74 target:(id)target |
| 75 action:(SEL)action |
| 76 enabled:(BOOL)enabled |
| 77 originX:(CGFloat)originX |
| 78 originY:(CGFloat)originY |
| 79 height:(CGFloat)height; |
| 80 |
| 81 // Adds a background image to |view|. The supplied image is stretched to fit the |
| 82 // space by stretching the content its horizontal and vertical centers. |
| 83 + (void)addBackgroundImageInView:(UIView*)view |
| 84 withImageName:(NSString*)imageName; |
| 85 |
| 86 // Adds an image view in |view| with an image named |imageName| at |
| 87 // (|originX|, 0). The width is |width| and the height is the height of |view|. |
| 88 + (void)addImageViewWithImageName:(NSString*)imageName |
| 89 originX:(CGFloat)originX |
| 90 originY:(CGFloat)originY |
| 91 width:(CGFloat)width |
| 92 inView:(UIView*)view; |
| 93 |
| 94 @end |
| 95 |
| 96 @implementation FormInputAccessoryView { |
| 97 // The custom view that is displayed in the input accessory view. |
| 98 base::scoped_nsobject<UIView> _customView; |
| 99 |
| 100 // Delegate of this view. |
| 101 base::WeakNSProtocol<id<FormInputAccessoryViewDelegate>> _delegate; |
| 102 } |
| 103 |
| 104 - (instancetype)initWithFrame:(CGRect)frame |
| 105 delegate:(id<FormInputAccessoryViewDelegate>)delegate |
| 106 customView:(UIView*)customView |
| 107 leftFrame:(CGRect)leftFrame |
| 108 rightFrame:(CGRect)rightFrame { |
| 109 DCHECK(delegate); |
| 110 self = [super initWithFrame:frame]; |
| 111 if (self) { |
| 112 _delegate.reset(delegate); |
| 113 _customView.reset([customView retain]); |
| 114 [self initializeViewWithCustomView:_customView |
| 115 leftFrame:leftFrame |
| 116 rightFrame:rightFrame]; |
| 117 } |
| 118 return self; |
| 119 } |
| 120 |
| 121 #pragma mark - |
| 122 #pragma mark UIInputViewAudioFeedback |
| 123 |
| 124 - (BOOL)enableInputClicksWhenVisible { |
| 125 return YES; |
| 126 } |
| 127 |
| 128 #pragma mark - |
| 129 #pragma mark Private Methods |
| 130 |
| 131 - (void)initializeViewWithCustomView:(UIView*)customView |
| 132 leftFrame:(CGRect)leftFrame |
| 133 rightFrame:(CGRect)rightFrame { |
| 134 UIView* customViewContainer = [[[UIView alloc] init] autorelease]; |
| 135 [self addSubview:customViewContainer]; |
| 136 UIView* navView = [[[UIView alloc] init] autorelease]; |
| 137 [self addSubview:navView]; |
| 138 |
| 139 bool splitKeyboard = CGRectGetWidth(rightFrame) != 0; |
| 140 BOOL isRTL = base::i18n::IsRTL(); |
| 141 |
| 142 // The computed frame for |customView|. |
| 143 CGRect customViewFrame; |
| 144 // Frame of a subview of |navView| in which navigation buttons will be shown. |
| 145 CGRect navFrame = CGRectZero; |
| 146 if (splitKeyboard) { |
| 147 NSString* navViewBackgroundImageName = nil; |
| 148 NSString* customViewContainerBackgroundImageName = nil; |
| 149 NSUInteger navFrameOriginX = 0; |
| 150 if (isRTL) { |
| 151 navView.frame = leftFrame; |
| 152 navViewBackgroundImageName = @"autofill_keyboard_background_left"; |
| 153 customViewContainer.frame = rightFrame; |
| 154 customViewContainerBackgroundImageName = |
| 155 @"autofill_keyboard_background_right"; |
| 156 // Navigation buttons will be shown on the left side. |
| 157 navFrameOriginX = 0; |
| 158 } else { |
| 159 customViewContainer.frame = leftFrame; |
| 160 customViewContainerBackgroundImageName = |
| 161 @"autofill_keyboard_background_left"; |
| 162 navView.frame = rightFrame; |
| 163 navViewBackgroundImageName = @"autofill_keyboard_background_right"; |
| 164 // Navigation buttons will be shown on the right side. |
| 165 navFrameOriginX = |
| 166 CGRectGetWidth(navView.frame) - GetNavigationViewWidth(); |
| 167 } |
| 168 |
| 169 [[self class] |
| 170 addBackgroundImageInView:customViewContainer |
| 171 withImageName:customViewContainerBackgroundImageName]; |
| 172 [[self class] addBackgroundImageInView:navView |
| 173 withImageName:navViewBackgroundImageName]; |
| 174 |
| 175 // For RTL, the custom view is the right view; the padding should be at the |
| 176 // left side of this view. Otherwise, the custom view is the left view |
| 177 // and the space is at the right side. |
| 178 customViewFrame = CGRectMake(isRTL ? kCustomViewHorizontalMargin : 0, 0, |
| 179 CGRectGetWidth(customViewContainer.bounds) - |
| 180 kCustomViewHorizontalMargin, |
| 181 CGRectGetHeight(customViewContainer.bounds)); |
| 182 navFrame = CGRectMake(navFrameOriginX, 0, GetNavigationViewWidth(), |
| 183 CGRectGetHeight(navView.frame)); |
| 184 } else { |
| 185 NSUInteger navViewFrameOriginX = 0; |
| 186 NSUInteger customViewContainerFrameOrginX = 0; |
| 187 if (isRTL) { |
| 188 navViewFrameOriginX = kNavigationAreaSeparatorShadowWidth; |
| 189 customViewContainerFrameOrginX = GetNavigationViewWidth(); |
| 190 } else { |
| 191 navViewFrameOriginX = |
| 192 CGRectGetWidth(leftFrame) - GetNavigationViewWidth(); |
| 193 } |
| 194 |
| 195 customViewContainer.frame = |
| 196 CGRectMake(customViewContainerFrameOrginX, 0, |
| 197 CGRectGetWidth(leftFrame) - GetNavigationViewWidth() + |
| 198 kNavigationAreaSeparatorShadowWidth, |
| 199 CGRectGetHeight(leftFrame)); |
| 200 navView.frame = CGRectMake(navViewFrameOriginX, 0, GetNavigationViewWidth(), |
| 201 CGRectGetHeight(leftFrame)); |
| 202 |
| 203 customViewFrame = customViewContainer.bounds; |
| 204 navFrame = navView.bounds; |
| 205 [[self class] addBackgroundImageInView:self |
| 206 withImageName:@"autofill_keyboard_background"]; |
| 207 } |
| 208 |
| 209 [customView setFrame:customViewFrame]; |
| 210 [customViewContainer addSubview:customView]; |
| 211 [navView addSubview:[self viewForNavigationButtonsInFrame:navFrame]]; |
| 212 } |
| 213 |
| 214 UIImage* ButtonImage(NSString* name) { |
| 215 UIImage* rawImage = [UIImage imageNamed:name]; |
| 216 return StretchableImageFromUIImage(rawImage, 1, 0); |
| 217 } |
| 218 |
| 219 - (UIView*)viewForNavigationButtonsInFrame:(CGRect)frame { |
| 220 UIView* navView = [[[UIView alloc] initWithFrame:frame] autorelease]; |
| 221 |
| 222 BOOL isRTL = base::i18n::IsRTL(); |
| 223 |
| 224 // Vertical space is left for a dividing line. |
| 225 CGFloat firstRow = 1; |
| 226 |
| 227 CGFloat currentX = 0; |
| 228 |
| 229 // Navigation view is at the right side if not RTL. Add a left separator in |
| 230 // this case. |
| 231 if (!isRTL) { |
| 232 [[self class] addImageViewWithImageName:@"autofill_left_sep" |
| 233 originX:currentX |
| 234 originY:firstRow |
| 235 width:kNavigationAreaSeparatorWidth |
| 236 inView:navView]; |
| 237 currentX = kNavigationAreaSeparatorWidth; |
| 238 } |
| 239 |
| 240 UIButton* previousButton = [self |
| 241 keyboardNavButtonWithNormalImage:ButtonImage(@"autofill_prev") |
| 242 pressedImage:ButtonImage(@"autofill_prev_pressed") |
| 243 disabledImage:ButtonImage(@"autofill_prev_inactive") |
| 244 target:_delegate |
| 245 action:@selector(selectPreviousElement) |
| 246 enabled:NO |
| 247 originX:currentX |
| 248 originY:firstRow |
| 249 height:CGRectGetHeight(frame)]; |
| 250 [navView addSubview:previousButton]; |
| 251 currentX += kNavigationButtonWidth; |
| 252 |
| 253 // Add internal separator. |
| 254 [[self class] addImageViewWithImageName:@"autofill_middle_sep" |
| 255 originX:currentX |
| 256 originY:firstRow |
| 257 width:kNavigationButtonSeparatorWidth |
| 258 inView:navView]; |
| 259 currentX += kNavigationButtonSeparatorWidth; |
| 260 |
| 261 UIButton* nextButton = [self |
| 262 keyboardNavButtonWithNormalImage:ButtonImage(@"autofill_next") |
| 263 pressedImage:ButtonImage(@"autofill_next_pressed") |
| 264 disabledImage:ButtonImage(@"autofill_next_inactive") |
| 265 target:_delegate |
| 266 action:@selector(selectNextElement) |
| 267 enabled:NO |
| 268 originX:currentX |
| 269 originY:firstRow |
| 270 height:CGRectGetHeight(frame)]; |
| 271 [navView addSubview:nextButton]; |
| 272 currentX += kNavigationButtonWidth; |
| 273 |
| 274 [_delegate fetchPreviousAndNextElementsPresenceWithCompletionHandler: |
| 275 ^(BOOL hasPreviousElement, BOOL hasNextElement) { |
| 276 previousButton.enabled = hasPreviousElement; |
| 277 nextButton.enabled = hasNextElement; |
| 278 }]; |
| 279 |
| 280 if (ShouldShowCloseButton()) { |
| 281 // Add internal separator. |
| 282 [[self class] addImageViewWithImageName:@"autofill_middle_sep" |
| 283 originX:currentX |
| 284 originY:firstRow |
| 285 width:kNavigationButtonSeparatorWidth |
| 286 inView:navView]; |
| 287 currentX += kNavigationButtonSeparatorWidth; |
| 288 |
| 289 [navView addSubview:[self |
| 290 keyboardNavButtonWithNormalImage:ButtonImage(@"autofill_close") |
| 291 pressedImage:ButtonImage(@"autofill_close_pressed") |
| 292 disabledImage:nil |
| 293 target:_delegate |
| 294 action:@selector(closeKeyboard) |
| 295 enabled:YES |
| 296 originX:currentX |
| 297 originY:firstRow |
| 298 height:CGRectGetHeight(frame)]]; |
| 299 currentX += kNavigationButtonWidth; |
| 300 } |
| 301 |
| 302 // Navigation view is at the left side for RTL. Add a right separator in |
| 303 // this case. |
| 304 if (isRTL) { |
| 305 [[self class] addImageViewWithImageName:@"autofill_right_sep" |
| 306 originX:currentX |
| 307 originY:firstRow |
| 308 width:kNavigationAreaSeparatorWidth |
| 309 inView:navView]; |
| 310 } |
| 311 |
| 312 return navView; |
| 313 } |
| 314 |
| 315 - (UIButton*)keyboardNavButtonWithNormalImage:(UIImage*)normalImage |
| 316 pressedImage:(UIImage*)pressedImage |
| 317 disabledImage:(UIImage*)disabledImage |
| 318 target:(id)target |
| 319 action:(SEL)action |
| 320 enabled:(BOOL)enabled |
| 321 originX:(CGFloat)originX |
| 322 originY:(CGFloat)originY |
| 323 height:(CGFloat)height { |
| 324 UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom]; |
| 325 |
| 326 button.frame = |
| 327 CGRectMake(originX, originY, kNavigationButtonWidth, height - originY); |
| 328 |
| 329 [button setBackgroundImage:normalImage forState:UIControlStateNormal]; |
| 330 [button setBackgroundImage:pressedImage forState:UIControlStateSelected]; |
| 331 [button setBackgroundImage:pressedImage forState:UIControlStateHighlighted]; |
| 332 if (disabledImage) |
| 333 [button setBackgroundImage:disabledImage forState:UIControlStateDisabled]; |
| 334 |
| 335 CALayer* layer = [button layer]; |
| 336 layer.borderWidth = 0; |
| 337 layer.borderColor = [[UIColor blackColor] CGColor]; |
| 338 button.enabled = enabled; |
| 339 [button addTarget:target |
| 340 action:action |
| 341 forControlEvents:UIControlEventTouchUpInside]; |
| 342 return button; |
| 343 } |
| 344 |
| 345 + (void)addBackgroundImageInView:(UIView*)view |
| 346 withImageName:(NSString*)imageName { |
| 347 UIImage* backgroundImage = StretchableImageNamed(imageName); |
| 348 |
| 349 UIImageView* backgroundImageView = |
| 350 [[[UIImageView alloc] initWithFrame:view.bounds] autorelease]; |
| 351 [backgroundImageView setImage:backgroundImage]; |
| 352 [backgroundImageView setAlpha:kBackgroundColorAlpha]; |
| 353 [view addSubview:backgroundImageView]; |
| 354 [view sendSubviewToBack:backgroundImageView]; |
| 355 } |
| 356 |
| 357 + (void)addImageViewWithImageName:(NSString*)imageName |
| 358 originX:(CGFloat)originX |
| 359 originY:(CGFloat)originY |
| 360 width:(CGFloat)width |
| 361 inView:(UIView*)view { |
| 362 UIImage* image = |
| 363 StretchableImageFromUIImage([UIImage imageNamed:imageName], 0, 0); |
| 364 base::scoped_nsobject<UIImageView> imageView( |
| 365 [[UIImageView alloc] initWithImage:image]); |
| 366 [imageView setFrame:CGRectMake(originX, originY, width, |
| 367 CGRectGetHeight(view.bounds) - originY)]; |
| 368 [view addSubview:imageView]; |
| 369 } |
| 370 |
| 371 @end |
OLD | NEW |