| Index: ios/chrome/browser/ui/find_bar/find_bar_controller_ios.mm
|
| diff --git a/ios/chrome/browser/ui/find_bar/find_bar_controller_ios.mm b/ios/chrome/browser/ui/find_bar/find_bar_controller_ios.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..f032bf65af2b22e3c5ecfa784488e9aebf3ac49e
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/find_bar/find_bar_controller_ios.mm
|
| @@ -0,0 +1,480 @@
|
| +// Copyright 2012 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/ui/find_bar/find_bar_controller_ios.h"
|
| +
|
| +#include "base/format_macros.h"
|
| +#include "base/i18n/rtl.h"
|
| +#include "base/ios/ios_util.h"
|
| +#include "base/mac/bundle_locations.h"
|
| +#include "base/mac/foundation_util.h"
|
| +#include "base/mac/objc_property_releaser.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "components/strings/grit/components_strings.h"
|
| +#import "ios/chrome/browser/find_in_page/find_in_page_controller.h"
|
| +#import "ios/chrome/browser/find_in_page/find_in_page_model.h"
|
| +#import "ios/chrome/browser/ui/UIView+SizeClassSupport.h"
|
| +#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
|
| +#import "ios/chrome/browser/ui/find_bar/find_bar_view.h"
|
| +#include "ios/chrome/browser/ui/ui_util.h"
|
| +#include "ui/base/l10n/l10n_util_mac.h"
|
| +#include "ui/base/resource/resource_bundle.h"
|
| +
|
| +NSString* const kFindInPageContainerViewId = @"kFindInPageContainerViewId";
|
| +
|
| +namespace {
|
| +
|
| +// Find Bar height.
|
| +// Right padding on iPad find bar.
|
| +const CGFloat kFindBarIPhoneHeight = 56;
|
| +const CGFloat kFindBarIPadHeight = 62;
|
| +
|
| +// Find Bar animation drop down duration.
|
| +const CGFloat kAnimationDuration = 0.15;
|
| +
|
| +// For the first |kSearchDelayChars| characters, delay by |kSearchLongDelay|
|
| +// For the remaining characters, delay by |kSearchShortDelay|.
|
| +const NSUInteger kSearchDelayChars = 3;
|
| +const NSTimeInterval kSearchLongDelay = 1.0;
|
| +const NSTimeInterval kSearchShortDelay = 0.100;
|
| +
|
| +} // anonymous namespace
|
| +
|
| +#pragma mark - FindBarControllerIOS
|
| +
|
| +@interface FindBarControllerIOS ()<UITextFieldDelegate> {
|
| + base::mac::ObjCPropertyReleaser _propertyReleaser_FindInPageController;
|
| +}
|
| +
|
| +// Set up iPad UI
|
| +- (void)setUpIPad;
|
| +// Set up iPhone UI
|
| +- (void)setUpIPhone;
|
| +// Animate find bar to iPad top right, or, when possible, to align find bar
|
| +// horizontally with |alignmentFrame|.
|
| +- (void)showIPadFindBarView:(BOOL)animate
|
| + intoView:(UIView*)parentView
|
| + withFrame:(CGRect)targetFrame
|
| + alignWithFrame:(CGRect)alignmentFrame
|
| + selectText:(BOOL)selectText;
|
| +// Animate find bar over iPhone toolbar.
|
| +- (void)showIPhoneFindBarView:(BOOL)animate
|
| + intoView:(UIView*)parentView
|
| + withFrame:(CGRect)targetFrame
|
| + selectText:(BOOL)selectText;
|
| +// Returns the appropriate variant of the image for |image_name| based on
|
| +// |_isIncognito| and device idiom.
|
| +- (UIImage*)imageWithName:(NSString*)image_name;
|
| +// Delay searching for the first |kSearchDelayChars| characters of a search term
|
| +// to give time for a user to type out a longer word. Short words are currently
|
| +// very inefficient and lock up the UIWebView.
|
| +- (void)editingChanged:(id)sender;
|
| +// Return the expected find bar height. This will include the status bar height
|
| +// when running iOS7 on an iPhone.
|
| +- (CGFloat)findBarHeight;
|
| +// Selects text in such way that selection menu does not appear and
|
| +// a11y label is read. When -[UITextField selectAll:] is used, iOS
|
| +// will read "Select All" instead of a11y label.
|
| +- (void)selectAllText;
|
| +
|
| +// Redefined to be readwrite. This view acts as background for |findBarView| and
|
| +// contains it as a subview.
|
| +@property(nonatomic, readwrite, strong) UIView* view;
|
| +// The view containing all the buttons and textfields that is common between
|
| +// iPhone and iPad.
|
| +@property(nonatomic, retain) FindBarView* findBarView;
|
| +// Typing delay timer.
|
| +@property(nonatomic, retain) NSTimer* delayTimer;
|
| +// Yes if incognito.
|
| +@property(nonatomic, assign) BOOL isIncognito;
|
| +@end
|
| +
|
| +@implementation FindBarControllerIOS
|
| +
|
| +@synthesize view = _view;
|
| +@synthesize findBarView = _findBarView;
|
| +@synthesize delayTimer = _delayTimer;
|
| +@synthesize isIncognito = _isIncognito;
|
| +
|
| +#pragma mark - Lifecycle
|
| +
|
| +- (instancetype)initWithIncognito:(BOOL)isIncognito {
|
| + self = [super init];
|
| + if (self) {
|
| + _propertyReleaser_FindInPageController.Init(self,
|
| + [FindBarControllerIOS class]);
|
| + _isIncognito = isIncognito;
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +#pragma mark View Setup & Teardown
|
| +
|
| +- (UIView*)newFindBarView {
|
| + BOOL isIPad = IsIPadIdiom();
|
| + UIView* findBarBackground = nil;
|
| + if (isIPad) {
|
| + // Future self.view. Contains only |contentView|. Is an image view that is
|
| + // typecast elsewhere but always is exposed as a UIView.
|
| + findBarBackground =
|
| + [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 345, 62)];
|
| + findBarBackground.backgroundColor = [UIColor clearColor];
|
| + findBarBackground.userInteractionEnabled = YES;
|
| + } else {
|
| + findBarBackground =
|
| + [[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 56)];
|
| + findBarBackground.backgroundColor = [UIColor whiteColor];
|
| + }
|
| +
|
| + self.findBarView = [[[FindBarView alloc]
|
| + initWithDarkAppearance:self.isIncognito && !IsIPadIdiom()] autorelease];
|
| + [findBarBackground addSubview:self.findBarView];
|
| + self.findBarView.translatesAutoresizingMaskIntoConstraints = NO;
|
| + base::scoped_nsobject<NSMutableArray> constraints(
|
| + [[NSMutableArray alloc] init]);
|
| + [constraints addObjectsFromArray:@[
|
| + [self.findBarView.trailingAnchor
|
| + constraintEqualToAnchor:findBarBackground.trailingAnchor],
|
| + [self.findBarView.leadingAnchor
|
| + constraintEqualToAnchor:findBarBackground.leadingAnchor],
|
| + [self.findBarView.heightAnchor constraintEqualToConstant:56.0f]
|
| + ]];
|
| +
|
| + if (isIPad) {
|
| + [constraints
|
| + addObject:[self.findBarView.centerYAnchor
|
| + constraintEqualToAnchor:findBarBackground.centerYAnchor
|
| + constant:-2]];
|
| + } else {
|
| + [constraints
|
| + addObject:[self.findBarView.bottomAnchor
|
| + constraintEqualToAnchor:findBarBackground.bottomAnchor]];
|
| + }
|
| +
|
| + [NSLayoutConstraint activateConstraints:constraints];
|
| +
|
| + self.findBarView.inputField.delegate = self;
|
| + [self.findBarView.inputField addTarget:self
|
| + action:@selector(editingChanged:)
|
| + forControlEvents:UIControlEventEditingChanged];
|
| + [self.findBarView.nextButton addTarget:self
|
| + action:@selector(hideKeyboard:)
|
| + forControlEvents:UIControlEventTouchUpInside];
|
| + [self.findBarView.previousButton addTarget:self
|
| + action:@selector(hideKeyboard:)
|
| + forControlEvents:UIControlEventTouchUpInside];
|
| +
|
| + return findBarBackground;
|
| +}
|
| +
|
| +- (void)setupViewInView:(UIView*)view {
|
| + self.view = [[self newFindBarView] autorelease];
|
| +
|
| + // Idiom specific setup.
|
| + if ([self shouldShowCompactSearchBarInView:view])
|
| + [self setUpIPhone];
|
| + else
|
| + [self setUpIPad];
|
| +
|
| + self.view.accessibilityIdentifier = kFindInPageContainerViewId;
|
| +}
|
| +
|
| +- (void)teardownView {
|
| + [self.view removeFromSuperview];
|
| + self.view = nil;
|
| +}
|
| +
|
| +- (void)setUpIPhone {
|
| + CGRect frame = self.view.frame;
|
| + frame.size.height = [self findBarHeight];
|
| + self.view.frame = frame;
|
| +
|
| + if (self.isIncognito) {
|
| + [self.view setBackgroundColor:[UIColor colorWithWhite:115 / 255.0 alpha:1]];
|
| + }
|
| +}
|
| +
|
| +- (void)setUpIPad {
|
| + self.view.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
|
| + UIEdgeInsets backgroundInsets = UIEdgeInsetsMake(6, 9, 10, 8);
|
| + UIImage* bgImage = [UIImage imageNamed:@"find_bg"];
|
| + bgImage = [bgImage resizableImageWithCapInsets:backgroundInsets];
|
| + UIImageView* bgView = (UIImageView*)self.view;
|
| + [bgView setImage:bgImage];
|
| +}
|
| +
|
| +#pragma mark - Public
|
| +
|
| +- (NSString*)searchTerm {
|
| + return [self.findBarView.inputField text];
|
| +}
|
| +
|
| +- (BOOL)isFindInPageShown {
|
| + return self.view != nil;
|
| +}
|
| +
|
| +- (BOOL)isFocused {
|
| + return [self.findBarView.inputField isFirstResponder];
|
| +}
|
| +
|
| +- (void)updateResultsCount:(FindInPageModel*)model {
|
| + [self updateWithMatchNumber:model.currentIndex
|
| + matchCount:model.matches
|
| + searchText:model.text];
|
| +}
|
| +
|
| +- (void)updateView:(FindInPageModel*)model
|
| + initialUpdate:(BOOL)initialUpdate
|
| + focusTextfield:(BOOL)focusTextfield {
|
| + [self.delayTimer invalidate];
|
| + self.delayTimer = nil;
|
| +
|
| + if (initialUpdate) {
|
| + // Set initial text and first search.
|
| + [self.findBarView.inputField setText:model.text];
|
| + [self editingChanged:self.findBarView.inputField];
|
| + }
|
| +
|
| + // Focus input field if necessary.
|
| + if (focusTextfield) {
|
| + [self.findBarView.inputField becomeFirstResponder];
|
| + } else {
|
| + [self.findBarView.inputField resignFirstResponder];
|
| + }
|
| +
|
| + [self updateWithMatchNumber:model.currentIndex
|
| + matchCount:model.matches
|
| + searchText:model.text];
|
| +}
|
| +
|
| +- (void)updateWithMatchNumber:(NSUInteger)matchNumber
|
| + matchCount:(NSUInteger)matchCount
|
| + searchText:(NSString*)searchText {
|
| + NSString* text = nil;
|
| + if (searchText.length != 0) {
|
| + NSString* indexStr = [NSString stringWithFormat:@"%" PRIdNS, matchNumber];
|
| + NSString* matchesStr = [NSString stringWithFormat:@"%" PRIdNS, matchCount];
|
| + text = l10n_util::GetNSStringF(IDS_FIND_IN_PAGE_COUNT,
|
| + base::SysNSStringToUTF16(indexStr),
|
| + base::SysNSStringToUTF16(matchesStr));
|
| + }
|
| + [self.findBarView updateResultsLabelWithText:text];
|
| +
|
| + BOOL enabled = matchCount != 0;
|
| + self.findBarView.nextButton.enabled = enabled;
|
| + self.findBarView.previousButton.enabled = enabled;
|
| +}
|
| +
|
| +- (void)addFindBarView:(BOOL)animate
|
| + intoView:(UIView*)view
|
| + withFrame:(CGRect)frame
|
| + alignWithFrame:(CGRect)omniboxFrame
|
| + selectText:(BOOL)selectText {
|
| + // If already showing find bar, nothing to do.
|
| + if (self.view) {
|
| + return;
|
| + }
|
| + if ([self shouldShowCompactSearchBarInView:view]) {
|
| + [self showIPhoneFindBarView:animate
|
| + intoView:view
|
| + withFrame:frame
|
| + selectText:selectText];
|
| + } else {
|
| + [self showIPadFindBarView:animate
|
| + intoView:view
|
| + withFrame:frame
|
| + alignWithFrame:omniboxFrame
|
| + selectText:selectText];
|
| + }
|
| +}
|
| +
|
| +- (void)hideFindBarView:(BOOL)animate {
|
| + // If view is nil, nothing to hide.
|
| + if (!self.view) {
|
| + return;
|
| + }
|
| +
|
| + self.findBarView.inputField.selectedTextRange = nil;
|
| + [self.delayTimer invalidate];
|
| + self.delayTimer = nil;
|
| +
|
| + if (animate) {
|
| + [UIView animateWithDuration:kAnimationDuration
|
| + animations:^{
|
| + CGRect frame = self.view.frame;
|
| + frame.size.height = 0;
|
| + self.view.frame = frame;
|
| + }
|
| + completion:^(BOOL finished) {
|
| + [self teardownView];
|
| + }];
|
| + } else {
|
| + [self teardownView];
|
| + }
|
| +}
|
| +
|
| +- (void)hideKeyboard:(id)sender {
|
| + [self.view endEditing:YES];
|
| +}
|
| +
|
| +- (UIImage*)imageWithName:(NSString*)imageName {
|
| + NSString* name = !IsIPadIdiom() && self.isIncognito
|
| + ? [imageName stringByAppendingString:@"_incognito"]
|
| + : imageName;
|
| + return [UIImage imageNamed:name];
|
| +}
|
| +
|
| +#pragma mark - Internal
|
| +
|
| +- (void)selectAllText {
|
| + UITextRange* wholeTextRange = [self.findBarView.inputField
|
| + textRangeFromPosition:self.findBarView.inputField.beginningOfDocument
|
| + toPosition:self.findBarView.inputField.endOfDocument];
|
| + self.findBarView.inputField.selectedTextRange = wholeTextRange;
|
| +}
|
| +
|
| +- (BOOL)shouldShowCompactSearchBarInView:(UIView*)view {
|
| + return !IsIPadIdiom();
|
| +}
|
| +
|
| +// Animate find bar to iPad top right.
|
| +- (void)showIPadFindBarView:(BOOL)animate
|
| + intoView:(UIView*)parentView
|
| + withFrame:(CGRect)targetFrame
|
| + alignWithFrame:(CGRect)omniboxFrame
|
| + selectText:(BOOL)selectText {
|
| + DCHECK(IsIPadIdiom());
|
| + [self setupViewInView:parentView];
|
| + UIView* view = self.view;
|
| + CGRect frame = view.frame;
|
| + frame.size.width =
|
| + MIN(CGRectGetWidth(parentView.bounds), CGRectGetWidth(frame));
|
| + frame.origin.y = targetFrame.origin.y;
|
| + frame.size.height = 0;
|
| +
|
| + CGFloat containerWidth = parentView.bounds.size.width;
|
| + CGFloat nibWidth = frame.size.width;
|
| +
|
| + // On iPad, there are three possible frames for the Search bar:
|
| + // 1. In Regular width size class, it is short, right-aligned to the omnibox's
|
| + // right edge.
|
| + // 2. In Compact size class, if the short bar width is less than the omnibox,
|
| + // stretch and align the search bar to the omnibox.
|
| + // 3. Finally, if the short bar width is more than the omnibox, fill the
|
| + // container view from edge to edge, ignoring the omnibox.
|
| + if (view.cr_widthSizeClass == REGULAR) {
|
| + if (base::i18n::IsRTL()) {
|
| + frame.origin.x = CGRectGetMinX(omniboxFrame);
|
| + } else {
|
| + frame.origin.x =
|
| + CGRectGetMinX(omniboxFrame) + CGRectGetWidth(omniboxFrame) - nibWidth;
|
| + }
|
| + frame.size.width = nibWidth;
|
| + } else {
|
| + // Compact size class.
|
| + if (omniboxFrame.size.width > nibWidth) {
|
| + frame.origin.x = omniboxFrame.origin.x;
|
| + frame.size.width = omniboxFrame.size.width;
|
| + } else {
|
| + frame.origin.x = 0;
|
| + frame.size.width = containerWidth;
|
| + }
|
| + }
|
| +
|
| + view.frame = frame;
|
| + [parentView addSubview:view];
|
| +
|
| + CGFloat duration = (animate) ? kAnimationDuration : 0;
|
| + [UIView animateWithDuration:duration
|
| + animations:^{
|
| + CGRect frame = view.frame;
|
| + frame.size.height = [self findBarHeight];
|
| + view.frame = frame;
|
| + }
|
| + completion:^(BOOL finished) {
|
| + if (selectText)
|
| + [self selectAllText];
|
| + }];
|
| +}
|
| +
|
| +// Animate find bar over iPhone toolbar.
|
| +- (void)showIPhoneFindBarView:(BOOL)animate
|
| + intoView:(UIView*)parentView
|
| + withFrame:(CGRect)targetFrame
|
| + selectText:(BOOL)selectText {
|
| + [self setupViewInView:parentView];
|
| + UIView* view = self.view;
|
| + CGRect frame = view.frame;
|
| + frame.size.width = targetFrame.size.width;
|
| + frame.origin.y = 0 - frame.size.height;
|
| + frame.origin.x = 0;
|
| + view.frame = frame;
|
| + [parentView addSubview:view];
|
| +
|
| + CGFloat duration = (animate) ? kAnimationDuration : 0;
|
| + [UIView animateWithDuration:duration
|
| + animations:^{
|
| + CGRect frame = view.frame;
|
| + frame.origin.y = 0;
|
| + view.frame = frame;
|
| + }
|
| + completion:^(BOOL finished) {
|
| + if (selectText)
|
| + [self selectAllText];
|
| + }];
|
| +}
|
| +
|
| +- (void)textChanged {
|
| + [self.view chromeExecuteCommand:self.findBarView.inputField];
|
| +}
|
| +
|
| +- (void)editingChanged:(id)sender {
|
| + [self.delayTimer invalidate];
|
| + NSUInteger length = [[self searchTerm] length];
|
| + if (length == 0)
|
| + return [self textChanged];
|
| +
|
| + // Delay delivery of text change event. Use a longer delay when the input
|
| + // length is short.
|
| + NSTimeInterval delay =
|
| + (length > kSearchDelayChars) ? kSearchShortDelay : kSearchLongDelay;
|
| + self.delayTimer =
|
| + [NSTimer scheduledTimerWithTimeInterval:delay
|
| + target:self
|
| + selector:@selector(textChanged)
|
| + userInfo:nil
|
| + repeats:NO];
|
| +}
|
| +
|
| +- (CGFloat)findBarHeight {
|
| + if (IsIPadIdiom())
|
| + return kFindBarIPadHeight;
|
| + return StatusBarHeight() + kFindBarIPhoneHeight;
|
| +}
|
| +
|
| +#pragma mark - UITextFieldDelegate
|
| +
|
| +- (BOOL)textFieldShouldBeginEditing:(UITextField*)textField {
|
| + DCHECK(textField == self.findBarView.inputField);
|
| + [[NSNotificationCenter defaultCenter]
|
| + postNotificationName:kFindBarTextFieldWillBecomeFirstResponderNotification
|
| + object:self];
|
| + return YES;
|
| +}
|
| +
|
| +- (void)textFieldDidEndEditing:(UITextField*)textField {
|
| + DCHECK(textField == self.findBarView.inputField);
|
| + [[NSNotificationCenter defaultCenter]
|
| + postNotificationName:kFindBarTextFieldDidResignFirstResponderNotification
|
| + object:self];
|
| +}
|
| +
|
| +- (BOOL)textFieldShouldReturn:(UITextField*)textField {
|
| + DCHECK(textField == self.findBarView.inputField);
|
| + [self.findBarView.inputField resignFirstResponder];
|
| + return YES;
|
| +}
|
| +
|
| +@end
|
|
|