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