Revert "Fix broken channel icon in chrome://help on CrOS" and try again
[chromium-blink-merge.git] / ios / chrome / browser / autofill / form_input_accessory_view.mm
bloba9fee6d8eff26b7fea5101d3ccd14cc08ba9b7db
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_resources.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(selectPreviousElement)
262                                enabled:NO
263                                originX:currentX
264                                originY:firstRow
265                                 height:CGRectGetHeight(frame)];
266   [previousButton
267       setAccessibilityLabel:l10n_util::GetNSString(
268                                 IDS_AUTOFILL_ACCNAME_PREVIOUS_FIELD)];
269   [navView addSubview:previousButton];
270   currentX += kNavigationButtonWidth;
272   // Add internal separator.
273   [[self class] addImageViewWithImageName:@"autofill_middle_sep"
274                                   originX:currentX
275                                   originY:firstRow
276                                     width:kNavigationButtonSeparatorWidth
277                                    inView:navView];
278   currentX += kNavigationButtonSeparatorWidth;
280   UIButton* nextButton = [self
281       keyboardNavButtonWithNormalImage:ButtonImage(@"autofill_next")
282                           pressedImage:ButtonImage(@"autofill_next_pressed")
283                          disabledImage:ButtonImage(@"autofill_next_inactive")
284                                 target:_delegate
285                                 action:@selector(selectNextElement)
286                                enabled:NO
287                                originX:currentX
288                                originY:firstRow
289                                 height:CGRectGetHeight(frame)];
290   [nextButton setAccessibilityLabel:l10n_util::GetNSString(
291                                         IDS_AUTOFILL_ACCNAME_NEXT_FIELD)];
292   [navView addSubview:nextButton];
293   currentX += kNavigationButtonWidth;
295   [_delegate fetchPreviousAndNextElementsPresenceWithCompletionHandler:
296                  ^(BOOL hasPreviousElement, BOOL hasNextElement) {
297                    previousButton.enabled = hasPreviousElement;
298                    nextButton.enabled = hasNextElement;
299                  }];
301   if (ShouldShowCloseButton()) {
302     // Add internal separator.
303     [[self class] addImageViewWithImageName:@"autofill_middle_sep"
304                                     originX:currentX
305                                     originY:firstRow
306                                       width:kNavigationButtonSeparatorWidth
307                                      inView:navView];
308     currentX += kNavigationButtonSeparatorWidth;
310     UIButton* closeButton = [self
311         keyboardNavButtonWithNormalImage:ButtonImage(@"autofill_close")
312                             pressedImage:ButtonImage(@"autofill_close_pressed")
313                            disabledImage:nil
314                                   target:_delegate
315                                   action:@selector(closeKeyboard)
316                                  enabled:YES
317                                  originX:currentX
318                                  originY:firstRow
319                                   height:CGRectGetHeight(frame)];
320     [closeButton setAccessibilityLabel:l10n_util::GetNSString(
321                                            IDS_AUTOFILL_ACCNAME_HIDE_KEYBOARD)];
322     [navView addSubview:closeButton];
323     currentX += kNavigationButtonWidth;
324   }
326   // Navigation view is at the left side for RTL. Add a right separator in
327   // this case.
328   if (isRTL) {
329     [[self class] addImageViewWithImageName:@"autofill_right_sep"
330                                     originX:currentX
331                                     originY:firstRow
332                                       width:kNavigationAreaSeparatorWidth
333                                      inView:navView];
334   }
336   return navView;
339 - (UIButton*)keyboardNavButtonWithNormalImage:(UIImage*)normalImage
340                                  pressedImage:(UIImage*)pressedImage
341                                 disabledImage:(UIImage*)disabledImage
342                                        target:(id)target
343                                        action:(SEL)action
344                                       enabled:(BOOL)enabled
345                                       originX:(CGFloat)originX
346                                       originY:(CGFloat)originY
347                                        height:(CGFloat)height {
348   UIButton* button = [UIButton buttonWithType:UIButtonTypeCustom];
350   button.frame =
351       CGRectMake(originX, originY, kNavigationButtonWidth, height - originY);
353   [button setBackgroundImage:normalImage forState:UIControlStateNormal];
354   [button setBackgroundImage:pressedImage forState:UIControlStateSelected];
355   [button setBackgroundImage:pressedImage forState:UIControlStateHighlighted];
356   if (disabledImage)
357     [button setBackgroundImage:disabledImage forState:UIControlStateDisabled];
359   CALayer* layer = [button layer];
360   layer.borderWidth = 0;
361   layer.borderColor = [[UIColor blackColor] CGColor];
362   button.enabled = enabled;
363   [button addTarget:target
364                 action:action
365       forControlEvents:UIControlEventTouchUpInside];
366   return button;
369 + (void)addBackgroundImageInView:(UIView*)view
370                    withImageName:(NSString*)imageName {
371   UIImage* backgroundImage = StretchableImageNamed(imageName);
373   UIImageView* backgroundImageView =
374       [[[UIImageView alloc] initWithFrame:view.bounds] autorelease];
375   [backgroundImageView setImage:backgroundImage];
376   [backgroundImageView setAlpha:kBackgroundColorAlpha];
377   [view addSubview:backgroundImageView];
378   [view sendSubviewToBack:backgroundImageView];
381 + (void)addImageViewWithImageName:(NSString*)imageName
382                           originX:(CGFloat)originX
383                           originY:(CGFloat)originY
384                             width:(CGFloat)width
385                            inView:(UIView*)view {
386   UIImage* image =
387       StretchableImageFromUIImage([UIImage imageNamed:imageName], 0, 0);
388   base::scoped_nsobject<UIImageView> imageView(
389       [[UIImageView alloc] initWithImage:image]);
390   [imageView setFrame:CGRectMake(originX, originY, width,
391                                  CGRectGetHeight(view.bounds) - originY)];
392   [view addSubview:imageView];
395 @end