blob: 5ebb92bc5f2496e6af6a0b431d48b36c0a1a9363 [file] [log] [blame]
// 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/mac/bundle_locations.h"
#include "base/mac/foundation_util.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/commands/browser_commands.h"
#import "ios/chrome/browser/ui/find_bar/find_bar_constants.h"
#import "ios/chrome/browser/ui/find_bar/find_bar_view.h"
#import "ios/chrome/browser/ui/find_bar/find_bar_view_controller.h"
#import "ios/chrome/browser/ui/image_util/image_util.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
#include "ios/chrome/browser/ui/util/rtl_geometry.h"
#include "ios/chrome/browser/ui/util/ui_util.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/colors/dynamic_color_util.h"
#import "ios/chrome/common/colors/semantic_color_names.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#include "ui/base/l10n/l10n_util_mac.h"
#include "ui/base/resource/resource_bundle.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// 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>
// Responds to touches that make editing changes on the text field, triggering
// find-in-page searches for the field's current value.
- (void)editingChanged;
// 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
@property(nonatomic, strong, readwrite)
FindBarViewController* findBarViewController;
// Typing delay timer.
@property(nonatomic, strong) NSTimer* delayTimer;
// Yes if incognito.
@property(nonatomic, assign) BOOL isIncognito;
@end
@implementation FindBarControllerIOS
#pragma mark - Lifecycle
- (instancetype)initWithIncognito:(BOOL)isIncognito {
self = [super init];
if (self) {
_isIncognito = isIncognito;
}
return self;
}
#pragma mark - Public
- (FindBarViewController*)findBarViewController {
if (_findBarViewController) {
return _findBarViewController;
}
_findBarViewController =
[[FindBarViewController alloc] initWithDarkAppearance:self.isIncognito];
_findBarViewController.findBarView.inputField.delegate = self;
[_findBarViewController.findBarView.inputField
addTarget:self
action:@selector(editingChanged)
forControlEvents:UIControlEventEditingChanged];
[_findBarViewController.findBarView.nextButton
addTarget:self.dispatcher
action:@selector(findNextStringInPage)
forControlEvents:UIControlEventTouchUpInside];
[_findBarViewController.findBarView.nextButton
addTarget:self
action:@selector(hideKeyboard:)
forControlEvents:UIControlEventTouchUpInside];
[_findBarViewController.findBarView.previousButton
addTarget:self.dispatcher
action:@selector(findPreviousStringInPage)
forControlEvents:UIControlEventTouchUpInside];
[_findBarViewController.findBarView.previousButton
addTarget:self
action:@selector(hideKeyboard:)
forControlEvents:UIControlEventTouchUpInside];
[_findBarViewController.findBarView.closeButton
addTarget:self.dispatcher
action:@selector(closeFindInPage)
forControlEvents:UIControlEventTouchUpInside];
return _findBarViewController;
}
- (void)findBarViewWillHide {
self.findBarViewController.findBarView.inputField.selectedTextRange = nil;
[self.delayTimer invalidate];
self.delayTimer = nil;
}
- (void)findBarViewDidHide {
self.findBarViewController = nil;
}
- (NSString*)searchTerm {
return [self.findBarViewController.findBarView.inputField text];
}
- (BOOL)isFindInPageShown {
return self.findBarViewController.findBarView != nil;
}
- (BOOL)isFocused {
return [self.findBarViewController.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.findBarViewController.findBarView.inputField setText:model.text];
[self editingChanged];
}
// Focus input field if necessary.
if (focusTextfield) {
[self.findBarViewController.findBarView.inputField becomeFirstResponder];
} else {
[self.findBarViewController.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.findBarViewController.findBarView updateResultsLabelWithText:text];
BOOL enabled = matchCount != 0;
self.findBarViewController.findBarView.nextButton.enabled = enabled;
self.findBarViewController.findBarView.previousButton.enabled = enabled;
}
- (void)hideKeyboard:(id)sender {
[self.findBarViewController.findBarView endEditing:YES];
}
- (void)selectAllText {
UITextRange* wholeTextRange =
[self.findBarViewController.findBarView.inputField
textRangeFromPosition:self.findBarViewController.findBarView
.inputField.beginningOfDocument
toPosition:self.findBarViewController.findBarView
.inputField.endOfDocument];
self.findBarViewController.findBarView.inputField.selectedTextRange =
wholeTextRange;
}
#pragma mark - Internal
- (void)editingChanged {
[self.delayTimer invalidate];
NSUInteger length = [[self searchTerm] length];
if (length == 0) {
[self.dispatcher searchFindInPage];
return;
}
// Delay delivery of the search text event to give time for a user to type out
// a longer word. Use a longer delay when the input length is short, as short
// words are currently very inefficient and lock up the web view.
NSTimeInterval delay =
(length > kSearchDelayChars) ? kSearchShortDelay : kSearchLongDelay;
self.delayTimer =
[NSTimer scheduledTimerWithTimeInterval:delay
target:self.dispatcher
selector:@selector(searchFindInPage)
userInfo:nil
repeats:NO];
}
#pragma mark - UITextFieldDelegate
- (BOOL)textFieldShouldBeginEditing:(UITextField*)textField {
DCHECK(textField == self.findBarViewController.findBarView.inputField);
[[NSNotificationCenter defaultCenter]
postNotificationName:kFindBarTextFieldWillBecomeFirstResponderNotification
object:self];
return YES;
}
- (void)textFieldDidEndEditing:(UITextField*)textField {
DCHECK(textField == self.findBarViewController.findBarView.inputField);
[[NSNotificationCenter defaultCenter]
postNotificationName:kFindBarTextFieldDidResignFirstResponderNotification
object:self];
}
- (BOOL)textFieldShouldReturn:(UITextField*)textField {
DCHECK(textField == self.findBarViewController.findBarView.inputField);
[self.findBarViewController.findBarView.inputField resignFirstResponder];
return YES;
}
@end