Index: ios/chrome/browser/autofill/form_input_accessory_view.mm |
diff --git a/ios/chrome/browser/autofill/form_input_accessory_view.mm b/ios/chrome/browser/autofill/form_input_accessory_view.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a794ab43e4d2b5ab64b61d4012cc65aff8c783be |
--- /dev/null |
+++ b/ios/chrome/browser/autofill/form_input_accessory_view.mm |
@@ -0,0 +1,371 @@ |
+// 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/autofill/form_input_accessory_view.h" |
+ |
+#import <QuartzCore/QuartzCore.h> |
+ |
+#include "base/i18n/rtl.h" |
+#include "base/ios/weak_nsobject.h" |
+#include "base/mac/scoped_nsobject.h" |
+#import "ios/chrome/browser/autofill/form_input_accessory_view_delegate.h" |
+#import "ios/chrome/browser/ui/image_util.h" |
+#include "ios/chrome/browser/ui/ui_util.h" |
+ |
+namespace { |
+ |
+// The alpha value of the background color. |
+const CGFloat kBackgroundColorAlpha = 1.0; |
+ |
+// Horizontal margin around the custom view. |
+const CGFloat kCustomViewHorizontalMargin = 2; |
+ |
+// The width of the previous and next buttons. |
+const CGFloat kNavigationButtonWidth = 44; |
+ |
+// The width of the separators of the previous and next buttons. |
+const CGFloat kNavigationButtonSeparatorWidth = 1; |
+ |
+// The width of the shadow part of the navigation area separator. |
+const CGFloat kNavigationAreaSeparatorShadowWidth = 2; |
+ |
+// The width of the navigation area / custom view separator asset. |
+const CGFloat kNavigationAreaSeparatorWidth = 1; |
+ |
+// Returns YES if the keyboard close button should be shown on the accessory. |
+BOOL ShouldShowCloseButton() { |
+ return !IsIPadIdiom(); |
+} |
+ |
+// Returns the width of navigation view. |
+CGFloat GetNavigationViewWidth() { |
+ // The number of naviation buttons (includes close button if shown). |
+ NSUInteger numberNavigationButtons = 2; |
+ if (ShouldShowCloseButton()) |
+ numberNavigationButtons++; |
+ return numberNavigationButtons * kNavigationButtonWidth + |
+ (numberNavigationButtons - 1) * kNavigationButtonSeparatorWidth + |
+ kNavigationAreaSeparatorWidth; |
+} |
+ |
+} // namespace |
+ |
+@interface FormInputAccessoryView () |
+ |
+// Initializes the view with the given |customView|. |
+// If the size of |rightFrame| is non-zero, the view will be split into two |
+// parts with |leftFrame| and |rightFrame|. Otherwise the Autofill view will |
+// be shown in |leftFrame|. |
+- (void)initializeViewWithCustomView:(UIView*)customView |
+ leftFrame:(CGRect)leftFrame |
+ rightFrame:(CGRect)rightFrame; |
+ |
+// Returns a view that shows navigation buttons in the |frame|. |
+- (UIView*)viewForNavigationButtonsInFrame:(CGRect)frame; |
+ |
+// Returns a navigation button for Autofill that has |normalImage| for state |
+// UIControlStateNormal, a |pressedImage| for states UIControlStateSelected and |
+// UIControlStateHighlighted, and an optional |disabledImage| for |
+// UIControlStateDisabled. |
+- (UIButton*)keyboardNavButtonWithNormalImage:(UIImage*)normalImage |
+ pressedImage:(UIImage*)pressedImage |
+ disabledImage:(UIImage*)disabledImage |
+ target:(id)target |
+ action:(SEL)action |
+ enabled:(BOOL)enabled |
+ originX:(CGFloat)originX |
+ originY:(CGFloat)originY |
+ height:(CGFloat)height; |
+ |
+// Adds a background image to |view|. The supplied image is stretched to fit the |
+// space by stretching the content its horizontal and vertical centers. |
++ (void)addBackgroundImageInView:(UIView*)view |
+ withImageName:(NSString*)imageName; |
+ |
+// Adds an image view in |view| with an image named |imageName| at |
+// (|originX|, 0). The width is |width| and the height is the height of |view|. |
++ (void)addImageViewWithImageName:(NSString*)imageName |
+ originX:(CGFloat)originX |
+ originY:(CGFloat)originY |
+ width:(CGFloat)width |
+ inView:(UIView*)view; |
+ |
+@end |
+ |
+@implementation FormInputAccessoryView { |
+ // The custom view that is displayed in the input accessory view. |
+ base::scoped_nsobject<UIView> _customView; |
+ |
+ // Delegate of this view. |
+ base::WeakNSProtocol<id<FormInputAccessoryViewDelegate>> _delegate; |
+} |
+ |
+- (instancetype)initWithFrame:(CGRect)frame |
+ delegate:(id<FormInputAccessoryViewDelegate>)delegate |
+ customView:(UIView*)customView |
+ leftFrame:(CGRect)leftFrame |
+ rightFrame:(CGRect)rightFrame { |
+ DCHECK(delegate); |
+ self = [super initWithFrame:frame]; |
+ if (self) { |
+ _delegate.reset(delegate); |
+ _customView.reset([customView retain]); |
+ [self initializeViewWithCustomView:_customView |
+ leftFrame:leftFrame |
+ rightFrame:rightFrame]; |
+ } |
+ return self; |
+} |
+ |
+#pragma mark - |
+#pragma mark UIInputViewAudioFeedback |
+ |
+- (BOOL)enableInputClicksWhenVisible { |
+ return YES; |
+} |
+ |
+#pragma mark - |
+#pragma mark Private Methods |
+ |
+- (void)initializeViewWithCustomView:(UIView*)customView |
+ leftFrame:(CGRect)leftFrame |
+ rightFrame:(CGRect)rightFrame { |
+ UIView* customViewContainer = [[[UIView alloc] init] autorelease]; |
+ [self addSubview:customViewContainer]; |
+ UIView* navView = [[[UIView alloc] init] autorelease]; |
+ [self addSubview:navView]; |
+ |
+ bool splitKeyboard = CGRectGetWidth(rightFrame) != 0; |
+ BOOL isRTL = base::i18n::IsRTL(); |
+ |
+ // The computed frame for |customView|. |
+ CGRect customViewFrame; |
+ // Frame of a subview of |navView| in which navigation buttons will be shown. |
+ CGRect navFrame = CGRectZero; |
+ if (splitKeyboard) { |
+ NSString* navViewBackgroundImageName = nil; |
+ NSString* customViewContainerBackgroundImageName = nil; |
+ NSUInteger navFrameOriginX = 0; |
+ if (isRTL) { |
+ navView.frame = leftFrame; |
+ navViewBackgroundImageName = @"autofill_keyboard_background_left"; |
+ customViewContainer.frame = rightFrame; |
+ customViewContainerBackgroundImageName = |
+ @"autofill_keyboard_background_right"; |
+ // Navigation buttons will be shown on the left side. |
+ navFrameOriginX = 0; |
+ } else { |
+ customViewContainer.frame = leftFrame; |
+ customViewContainerBackgroundImageName = |
+ @"autofill_keyboard_background_left"; |
+ navView.frame = rightFrame; |
+ navViewBackgroundImageName = @"autofill_keyboard_background_right"; |
+ // Navigation buttons will be shown on the right side. |
+ navFrameOriginX = |
+ CGRectGetWidth(navView.frame) - GetNavigationViewWidth(); |
+ } |
+ |
+ [[self class] |
+ addBackgroundImageInView:customViewContainer |
+ withImageName:customViewContainerBackgroundImageName]; |
+ [[self class] addBackgroundImageInView:navView |
+ withImageName:navViewBackgroundImageName]; |
+ |
+ // For RTL, the custom view is the right view; the padding should be at the |
+ // left side of this view. Otherwise, the custom view is the left view |
+ // and the space is at the right side. |
+ customViewFrame = CGRectMake(isRTL ? kCustomViewHorizontalMargin : 0, 0, |
+ CGRectGetWidth(customViewContainer.bounds) - |
+ kCustomViewHorizontalMargin, |
+ CGRectGetHeight(customViewContainer.bounds)); |
+ navFrame = CGRectMake(navFrameOriginX, 0, GetNavigationViewWidth(), |
+ CGRectGetHeight(navView.frame)); |
+ } else { |
+ NSUInteger navViewFrameOriginX = 0; |
+ NSUInteger customViewContainerFrameOrginX = 0; |
+ if (isRTL) { |
+ navViewFrameOriginX = kNavigationAreaSeparatorShadowWidth; |
+ customViewContainerFrameOrginX = GetNavigationViewWidth(); |
+ } else { |
+ navViewFrameOriginX = |
+ CGRectGetWidth(leftFrame) - GetNavigationViewWidth(); |
+ } |
+ |
+ customViewContainer.frame = |
+ CGRectMake(customViewContainerFrameOrginX, 0, |
+ CGRectGetWidth(leftFrame) - GetNavigationViewWidth() + |
+ kNavigationAreaSeparatorShadowWidth, |
+ CGRectGetHeight(leftFrame)); |
+ navView.frame = CGRectMake(navViewFrameOriginX, 0, GetNavigationViewWidth(), |
+ CGRectGetHeight(leftFrame)); |
+ |
+ customViewFrame = customViewContainer.bounds; |
+ navFrame = navView.bounds; |
+ [[self class] addBackgroundImageInView:self |
+ withImageName:@"autofill_keyboard_background"]; |
+ } |
+ |
+ [customView setFrame:customViewFrame]; |
+ [customViewContainer addSubview:customView]; |
+ [navView addSubview:[self viewForNavigationButtonsInFrame:navFrame]]; |
+} |
+ |
+UIImage* ButtonImage(NSString* name) { |
+ UIImage* rawImage = [UIImage imageNamed:name]; |
+ return StretchableImageFromUIImage(rawImage, 1, 0); |
+} |
+ |
+- (UIView*)viewForNavigationButtonsInFrame:(CGRect)frame { |
+ UIView* navView = [[[UIView alloc] initWithFrame:frame] autorelease]; |
+ |
+ BOOL isRTL = base::i18n::IsRTL(); |
+ |
+ // Vertical space is left for a dividing line. |
+ CGFloat firstRow = 1; |
+ |
+ CGFloat currentX = 0; |
+ |
+ // Navigation view is at the right side if not RTL. Add a left separator in |
+ // this case. |
+ if (!isRTL) { |
+ [[self class] addImageViewWithImageName:@"autofill_left_sep" |
+ originX:currentX |
+ originY:firstRow |
+ width:kNavigationAreaSeparatorWidth |
+ inView:navView]; |
+ currentX = kNavigationAreaSeparatorWidth; |
+ } |
+ |
+ UIButton* previousButton = [self |
+ keyboardNavButtonWithNormalImage:ButtonImage(@"autofill_prev") |
+ pressedImage:ButtonImage(@"autofill_prev_pressed") |
+ disabledImage:ButtonImage(@"autofill_prev_inactive") |
+ target:_delegate |
+ action:@selector(selectPreviousElement) |
+ enabled:NO |
+ originX:currentX |
+ originY:firstRow |
+ height:CGRectGetHeight(frame)]; |
+ [navView addSubview:previousButton]; |
+ currentX += kNavigationButtonWidth; |
+ |
+ // Add internal separator. |
+ [[self class] addImageViewWithImageName:@"autofill_middle_sep" |
+ originX:currentX |
+ originY:firstRow |
+ width:kNavigationButtonSeparatorWidth |
+ inView:navView]; |
+ currentX += kNavigationButtonSeparatorWidth; |
+ |
+ UIButton* nextButton = [self |
+ keyboardNavButtonWithNormalImage:ButtonImage(@"autofill_next") |
+ pressedImage:ButtonImage(@"autofill_next_pressed") |
+ disabledImage:ButtonImage(@"autofill_next_inactive") |
+ target:_delegate |
+ action:@selector(selectNextElement) |
+ enabled:NO |
+ originX:currentX |
+ originY:firstRow |
+ height:CGRectGetHeight(frame)]; |
+ [navView addSubview:nextButton]; |
+ currentX += kNavigationButtonWidth; |
+ |
+ [_delegate fetchPreviousAndNextElementsPresenceWithCompletionHandler: |
+ ^(BOOL hasPreviousElement, BOOL hasNextElement) { |
+ previousButton.enabled = hasPreviousElement; |
+ nextButton.enabled = hasNextElement; |
+ }]; |
+ |
+ if (ShouldShowCloseButton()) { |
+ // Add internal separator. |
+ [[self class] addImageViewWithImageName:@"autofill_middle_sep" |
+ originX:currentX |
+ originY:firstRow |
+ width:kNavigationButtonSeparatorWidth |
+ inView:navView]; |
+ currentX += kNavigationButtonSeparatorWidth; |
+ |
+ [navView addSubview:[self |
+ keyboardNavButtonWithNormalImage:ButtonImage(@"autofill_close") |
+ pressedImage:ButtonImage(@"autofill_close_pressed") |
+ disabledImage:nil |
+ target:_delegate |
+ action:@selector(closeKeyboard) |
+ enabled:YES |
+ originX:currentX |
+ originY:firstRow |
+ height:CGRectGetHeight(frame)]]; |
+ currentX += kNavigationButtonWidth; |
+ } |
+ |
+ // Navigation view is at the left side for RTL. Add a right separator in |
+ // this case. |
+ if (isRTL) { |
+ [[self class] addImageViewWithImageName:@"autofill_right_sep" |
+ originX:currentX |
+ originY:firstRow |
+ width:kNavigationAreaSeparatorWidth |
+ inView:navView]; |
+ } |
+ |
+ return navView; |
+} |
+ |
+- (UIButton*)keyboardNavButtonWithNormalImage:(UIImage*)normalImage |
+ pressedImage:(UIImage*)pressedImage |
+ disabledImage:(UIImage*)disabledImage |
+ target:(id)target |
+ action:(SEL)action |
+ enabled:(BOOL)enabled |
+ originX:(CGFloat)originX |
+ originY:(CGFloat)originY |
+ height:(CGFloat)height { |
+ UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom]; |
+ |
+ button.frame = |
+ CGRectMake(originX, originY, kNavigationButtonWidth, height - originY); |
+ |
+ [button setBackgroundImage:normalImage forState:UIControlStateNormal]; |
+ [button setBackgroundImage:pressedImage forState:UIControlStateSelected]; |
+ [button setBackgroundImage:pressedImage forState:UIControlStateHighlighted]; |
+ if (disabledImage) |
+ [button setBackgroundImage:disabledImage forState:UIControlStateDisabled]; |
+ |
+ CALayer* layer = [button layer]; |
+ layer.borderWidth = 0; |
+ layer.borderColor = [[UIColor blackColor] CGColor]; |
+ button.enabled = enabled; |
+ [button addTarget:target |
+ action:action |
+ forControlEvents:UIControlEventTouchUpInside]; |
+ return button; |
+} |
+ |
++ (void)addBackgroundImageInView:(UIView*)view |
+ withImageName:(NSString*)imageName { |
+ UIImage* backgroundImage = StretchableImageNamed(imageName); |
+ |
+ UIImageView* backgroundImageView = |
+ [[[UIImageView alloc] initWithFrame:view.bounds] autorelease]; |
+ [backgroundImageView setImage:backgroundImage]; |
+ [backgroundImageView setAlpha:kBackgroundColorAlpha]; |
+ [view addSubview:backgroundImageView]; |
+ [view sendSubviewToBack:backgroundImageView]; |
+} |
+ |
++ (void)addImageViewWithImageName:(NSString*)imageName |
+ originX:(CGFloat)originX |
+ originY:(CGFloat)originY |
+ width:(CGFloat)width |
+ inView:(UIView*)view { |
+ UIImage* image = |
+ StretchableImageFromUIImage([UIImage imageNamed:imageName], 0, 0); |
+ base::scoped_nsobject<UIImageView> imageView( |
+ [[UIImageView alloc] initWithImage:image]); |
+ [imageView setFrame:CGRectMake(originX, originY, width, |
+ CGRectGetHeight(view.bounds) - originY)]; |
+ [view addSubview:imageView]; |
+} |
+ |
+@end |