| 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
|
|
|