blob: c31f076cf9f0898008c1e42e778a7b7ca3acea24 [file] [log] [blame]
// Copyright 2016 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_view.h"
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
#import "ios/chrome/browser/ui/commands/ios_command_ids.h"
#import "ios/chrome/browser/ui/find_bar/find_bar_touch_forwarding_view.h"
#import "ios/chrome/browser/ui/uikit_ui_util.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
#include "ui/base/l10n/l10n_util_mac.h"
#import "ui/gfx/ios/NSString+CrStringDrawing.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
NSString* const kFindInPageInputFieldId = @"kFindInPageInputFieldId";
NSString* const kFindInPageNextButtonId = @"kFindInPageNextButtonId";
NSString* const kFindInPagePreviousButtonId = @"kFindInPagePreviousButtonId";
NSString* const kFindInPageCloseButtonId = @"kFindInPageCloseButtonId";
@interface FindBarView ()
// The overlay that shows number of results in format "1 of 13".
@property(nonatomic, weak) UILabel* resultsLabel;
@property(nonatomic, weak) UIView* separator;
// Initializes all subviews.
- (void)setupSubviews;
// Sets up appearance of subviews, such as fonts, background colors.
- (void)configureApperance:(BOOL)isDark;
// Convenience method that returns images for light and dark appearances.
- (UIImage*)imageWithName:(NSString*)imageName isDark:(BOOL)isDark;
@end
@implementation FindBarView
@synthesize inputField = _inputField;
@synthesize resultsLabel = _resultsLabel;
@synthesize previousButton = _previousButton;
@synthesize nextButton = _nextButton;
@synthesize closeButton = _closeButton;
@synthesize separator = _separator;
- (instancetype)initWithDarkAppearance:(BOOL)darkAppearance {
self = [super initWithFrame:CGRectZero];
if (self) {
[self setupSubviews];
[self configureApperance:darkAppearance];
}
return self;
}
#pragma mark - Public methods
- (void)updateResultsLabelWithText:(NSString*)text {
self.resultsLabel.hidden = (text.length == 0);
self.resultsLabel.text = text;
}
#pragma mark - Internal
- (void)setupSubviews {
[self setBackgroundColor:[UIColor clearColor]];
// Input field.
UITextField* inputFieldScoped =
[[UITextField alloc] initWithFrame:CGRectZero];
self.inputField = inputFieldScoped;
self.inputField.backgroundColor = [UIColor clearColor];
self.inputField.tag = IDC_FIND_UPDATE;
self.inputField.translatesAutoresizingMaskIntoConstraints = NO;
self.inputField.placeholder =
l10n_util::GetNSString(IDS_IOS_PLACEHOLDER_FIND_IN_PAGE);
// Label containing number of found results.
UILabel* resultsLabelScoped = [[UILabel alloc] initWithFrame:CGRectZero];
self.resultsLabel = resultsLabelScoped;
self.resultsLabel.textColor = [UIColor lightGrayColor];
self.resultsLabel.font = [UIFont systemFontOfSize:14];
[self.resultsLabel
setContentCompressionResistancePriority:UILayoutPriorityRequired
forAxis:UILayoutConstraintAxisHorizontal];
[self.resultsLabel
setContentHuggingPriority:UILayoutPriorityRequired
forAxis:UILayoutConstraintAxisHorizontal];
// Stack view that holds |inputField| and |resultsLabel|.
UIStackView* inputStackView = [[UIStackView alloc]
initWithArrangedSubviews:@[ inputFieldScoped, resultsLabelScoped ]];
[inputStackView setLayoutMargins:UIEdgeInsetsMake(0, 12, 0, 12)];
[inputStackView setLayoutMarginsRelativeArrangement:YES];
[inputStackView setSpacing:12];
[inputStackView setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addSubview:inputStackView];
NSMutableArray* constraints = [[NSMutableArray alloc] init];
[constraints addObjectsFromArray:@[
[[inputStackView leadingAnchor] constraintEqualToAnchor:self.leadingAnchor],
[[inputStackView topAnchor] constraintEqualToAnchor:self.topAnchor],
[[inputStackView bottomAnchor] constraintEqualToAnchor:self.bottomAnchor],
]];
// Touch-forwarding view is put on top of |inputStackView| to forward touches
// to |inputField|.
// Unlike a gesture recognizer, forwarding all touch events allows for using
// long press, pinch and other manipulatiosn on the target textfield.
FindBarTouchForwardingView* forwarder =
[[FindBarTouchForwardingView alloc] init];
[forwarder setTargetView:self.inputField];
[self addSubview:forwarder];
[constraints addObjectsFromArray:@[
[[forwarder leadingAnchor]
constraintEqualToAnchor:[inputStackView leadingAnchor]],
[[forwarder topAnchor] constraintEqualToAnchor:[inputStackView topAnchor]],
[[forwarder bottomAnchor]
constraintEqualToAnchor:[inputStackView bottomAnchor]],
[[forwarder trailingAnchor]
constraintEqualToAnchor:[inputStackView trailingAnchor]],
]];
[forwarder setTranslatesAutoresizingMaskIntoConstraints:NO];
// Thin line separator between buttons and input.
UIView* separatorScoped = [[UIView alloc] initWithFrame:CGRectZero];
UIView* separator = separatorScoped;
separator.backgroundColor = [UIColor colorWithWhite:0.83 alpha:1];
[self addSubview:separator];
[constraints addObjectsFromArray:@[
[separator.widthAnchor constraintEqualToConstant:1],
[separator.bottomAnchor constraintEqualToAnchor:self.bottomAnchor
constant:-8],
[separator.topAnchor constraintEqualToAnchor:self.topAnchor constant:8],
[separator.leadingAnchor
constraintEqualToAnchor:inputStackView.trailingAnchor],
]];
separator.translatesAutoresizingMaskIntoConstraints = NO;
self.separator = separator;
// Previous button with an arrow.
UIButton* previousButtonScoped = [[UIButton alloc] initWithFrame:CGRectZero];
self.previousButton = previousButtonScoped;
[self addSubview:self.previousButton];
[constraints addObjectsFromArray:@[
[self.previousButton.centerYAnchor
constraintEqualToAnchor:self.centerYAnchor],
[self.previousButton.widthAnchor constraintEqualToConstant:48],
[self.previousButton.heightAnchor constraintEqualToConstant:56],
[self.previousButton.leadingAnchor
constraintEqualToAnchor:separator.trailingAnchor],
]];
self.previousButton.isAccessibilityElement = YES;
self.previousButton.accessibilityTraits = UIAccessibilityTraitButton;
self.previousButton.tag = IDC_FIND_PREVIOUS;
self.previousButton.translatesAutoresizingMaskIntoConstraints = NO;
// Next button with an arrow.
UIButton* nextButtonScoped = [[UIButton alloc] initWithFrame:CGRectZero];
self.nextButton = nextButtonScoped;
[self addSubview:self.nextButton];
[constraints addObjectsFromArray:@[
[self.nextButton.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[self.nextButton.widthAnchor constraintEqualToConstant:48],
[self.nextButton.heightAnchor constraintEqualToConstant:56],
[self.nextButton.leadingAnchor
constraintEqualToAnchor:self.previousButton.trailingAnchor],
]];
self.nextButton.tag = IDC_FIND_NEXT;
self.nextButton.translatesAutoresizingMaskIntoConstraints = NO;
// Close button with a cross.
UIButton* closeButtonScoped = [[UIButton alloc] initWithFrame:CGRectZero];
self.closeButton = closeButtonScoped;
[self addSubview:self.closeButton];
[constraints addObjectsFromArray:@[
[self.closeButton.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[self.closeButton.trailingAnchor constraintEqualToAnchor:self.trailingAnchor
constant:-4],
[self.closeButton.widthAnchor constraintEqualToConstant:48],
[self.closeButton.heightAnchor constraintEqualToConstant:56],
[self.closeButton.leadingAnchor
constraintEqualToAnchor:self.nextButton.trailingAnchor],
]];
self.closeButton.tag = IDC_FIND_CLOSE;
self.closeButton.translatesAutoresizingMaskIntoConstraints = NO;
// Connect outlets.
[self.nextButton addTarget:self
action:@selector(chromeExecuteCommand:)
forControlEvents:UIControlEventTouchUpInside];
[self.previousButton addTarget:self
action:@selector(chromeExecuteCommand:)
forControlEvents:UIControlEventTouchUpInside];
[self.closeButton addTarget:self
action:@selector(chromeExecuteCommand:)
forControlEvents:UIControlEventTouchUpInside];
// A11y labels.
SetA11yLabelAndUiAutomationName(self.closeButton,
IDS_FIND_IN_PAGE_CLOSE_TOOLTIP,
kFindInPageCloseButtonId);
SetA11yLabelAndUiAutomationName(self.previousButton,
IDS_FIND_IN_PAGE_PREVIOUS_TOOLTIP,
kFindInPagePreviousButtonId);
SetA11yLabelAndUiAutomationName(
self.nextButton, IDS_FIND_IN_PAGE_NEXT_TOOLTIP, kFindInPageNextButtonId);
self.inputField.accessibilityIdentifier = kFindInPageInputFieldId;
// Configure fonts.
[self.inputField setFont:[MDCTypography body1Font]];
[self.resultsLabel setFont:[MDCTypography body1Font]];
[NSLayoutConstraint activateConstraints:constraints];
}
- (void)configureApperance:(BOOL)isDark {
[self.closeButton setImage:[self imageWithName:@"find_close" isDark:isDark]
forState:UIControlStateNormal];
[self.closeButton
setImage:[self imageWithName:@"find_close_pressed" isDark:isDark]
forState:UIControlStateHighlighted];
[self.previousButton setImage:[self imageWithName:@"find_prev" isDark:isDark]
forState:UIControlStateNormal];
[self.previousButton
setImage:[self imageWithName:@"find_prev_pressed" isDark:isDark]
forState:UIControlStateHighlighted];
[self.previousButton
setImage:[self imageWithName:@"find_prev_disabled" isDark:isDark]
forState:UIControlStateDisabled];
[self.nextButton setImage:[self imageWithName:@"find_next" isDark:isDark]
forState:UIControlStateNormal];
[self.nextButton
setImage:[self imageWithName:@"find_next_pressed" isDark:isDark]
forState:UIControlStateHighlighted];
[self.nextButton
setImage:[self imageWithName:@"find_next_disabled" isDark:isDark]
forState:UIControlStateDisabled];
if (!isDark) {
return;
}
// Setup dark appearance.
[self.inputField setTextColor:[UIColor whiteColor]];
NSString* placeholder = [self.inputField placeholder];
UIColor* inputTextColor = [UIColor colorWithWhite:1 alpha:0.7];
NSDictionary* attributes = @{NSForegroundColorAttributeName : inputTextColor};
[self.inputField setAttributedPlaceholder:[[NSAttributedString alloc]
initWithString:placeholder
attributes:attributes]];
UIColor* resultTextColor = [UIColor colorWithWhite:1 alpha:0.3];
[self.resultsLabel setTextColor:resultTextColor];
UIColor* separatorColor = [UIColor colorWithWhite:0 alpha:0.1];
[self.separator setBackgroundColor:separatorColor];
}
- (UIImage*)imageWithName:(NSString*)imageName isDark:(BOOL)isDark {
NSString* name =
isDark ? [imageName stringByAppendingString:@"_incognito"] : imageName;
return [UIImage imageNamed:name];
}
@end