blob: efd9e1dc52c6e985a83e7be4d024ebc09769cf09 [file] [log] [blame]
// Copyright 2018 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/location_bar/location_bar_steady_view.h"
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/ui/elements/extended_touch_target_button.h"
#import "ios/chrome/browser/ui/infobars/infobar_feature.h"
#import "ios/chrome/browser/ui/omnibox/omnibox_constants.h"
#import "ios/chrome/browser/ui/toolbar/public/toolbar_constants.h"
#import "ios/chrome/browser/ui/util/dynamic_type_util.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/colors/semantic_color_names.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#include "ios/chrome/grit/ios_strings.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Length of the trailing button side.
const CGFloat kButtonSize = 24;
// Space between the location icon and the location label.
const CGFloat kLocationImageToLabelSpacing = -4.0;
// Minimal horizontal padding between the leading edge of the location bar and
// the content of the location bar.
const CGFloat kLocationBarLeadingPadding = 5.0;
// Trailing space between the trailing button and the trailing edge of the
// location bar.
const CGFloat kButtonTrailingSpacing = 10;
// Duration of display and hide animation of the badge view, in seconds.
const CGFloat kbadgeViewAnimationDuration = 0.2;
} // namespace
@interface LocationBarSteadyView ()
// The image view displaying the current location icon (i.e. http[s] status).
@property(nonatomic, strong) UIImageView* locationIconImageView;
// The view containing the location label, and (sometimes) the location image
// view.
@property(nonatomic, strong) UIView* locationContainerView;
// Leading constraint for locationContainerView when there is no BadgeView to
// its left.
@property(nonatomic, strong)
NSLayoutConstraint* locationContainerViewLeadingAnchorConstraint;
// Constraints to pin the badge view to the right next to the
// |locationContainerView|.
@property(nonatomic, strong)
NSArray<NSLayoutConstraint*>* badgeViewFullScreenEnabledConstraints;
// Constraints to pin the badge view to the left side of the LocationBar.
@property(nonatomic, strong)
NSArray<NSLayoutConstraint*>* badgeViewFullScreenDisabledConstraints;
// Constraints to hide the location image view.
@property(nonatomic, strong)
NSArray<NSLayoutConstraint*>* hideLocationImageConstraints;
// Constraints to show the location image view.
@property(nonatomic, strong)
NSArray<NSLayoutConstraint*>* showLocationImageConstraints;
// Elements to surface in accessibility.
@property(nonatomic, strong) NSMutableArray* accessibleElements;
@end
#pragma mark - LocationBarSteadyViewColorScheme
@implementation LocationBarSteadyViewColorScheme
+ (instancetype)standardScheme {
LocationBarSteadyViewColorScheme* scheme =
[[LocationBarSteadyViewColorScheme alloc] init];
scheme.fontColor = [UIColor colorNamed:kTextPrimaryColor];
scheme.placeholderColor = [UIColor colorNamed:kTextfieldPlaceholderColor];
scheme.trailingButtonColor = [UIColor colorNamed:kToolbarButtonColor];
return scheme;
}
+ (instancetype)incognitoScheme {
LocationBarSteadyViewColorScheme* scheme =
[[LocationBarSteadyViewColorScheme alloc] init];
// In iOS 12, the overridePreferredInterfaceStyle API is unavailable, so
// incognito colors need to be set specifically.
// TODO(crbug.com/981889): Clean up after iOS 12 support is dropped.
scheme.fontColor = [UIColor colorNamed:kTextPrimaryDarkColor];
scheme.placeholderColor = [UIColor colorNamed:kTextfieldPlaceholderDarkColor];
scheme.trailingButtonColor = [UIColor colorNamed:kToolbarButtonDarkColor];
return scheme;
}
@end
#pragma mark - LocationBarSteadyButton
// Buttons with a darker background in highlighted state.
@interface LocationBarSteadyButton : UIButton
@end
@implementation LocationBarSteadyButton
- (void)layoutSubviews {
[super layoutSubviews];
self.layer.cornerRadius = self.bounds.size.height / 2.0;
}
- (void)setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
CGFloat duration = highlighted ? 0.1 : 0.2;
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
CGFloat alpha = highlighted ? 0.07 : 0;
self.backgroundColor =
[UIColor colorWithWhite:0 alpha:alpha];
}
completion:nil];
}
@end
#pragma mark - LocationBarSteadyView
@implementation LocationBarSteadyView
- (instancetype)init {
self = [super initWithFrame:CGRectZero];
if (self) {
_locationLabel = [[UILabel alloc] init];
_locationIconImageView = [[UIImageView alloc] init];
_locationIconImageView.translatesAutoresizingMaskIntoConstraints = NO;
[_locationIconImageView
setContentCompressionResistancePriority:UILayoutPriorityRequired
forAxis:
UILayoutConstraintAxisHorizontal];
SetA11yLabelAndUiAutomationName(
_locationIconImageView,
IDS_IOS_PAGE_INFO_SECURITY_BUTTON_ACCESSIBILITY_LABEL,
@"Page Security Info");
_locationIconImageView.isAccessibilityElement = YES;
// Setup trailing button.
_trailingButton =
[ExtendedTouchTargetButton buttonWithType:UIButtonTypeSystem];
_trailingButton.translatesAutoresizingMaskIntoConstraints = NO;
// Setup label.
_locationLabel.lineBreakMode = NSLineBreakByTruncatingHead;
_locationLabel.translatesAutoresizingMaskIntoConstraints = NO;
[_locationLabel
setContentCompressionResistancePriority:UILayoutPriorityDefaultLow
forAxis:UILayoutConstraintAxisVertical];
_locationLabel.font = [self locationLabelFont];
// Container for location label and icon.
_locationContainerView = [[UIView alloc] init];
_locationContainerView.translatesAutoresizingMaskIntoConstraints = NO;
_locationContainerView.userInteractionEnabled = NO;
[_locationContainerView addSubview:_locationIconImageView];
[_locationContainerView addSubview:_locationLabel];
_showLocationImageConstraints = @[
[_locationContainerView.leadingAnchor
constraintEqualToAnchor:_locationIconImageView.leadingAnchor],
[_locationIconImageView.trailingAnchor
constraintEqualToAnchor:_locationLabel.leadingAnchor
constant:kLocationImageToLabelSpacing],
[_locationLabel.trailingAnchor
constraintEqualToAnchor:_locationContainerView.trailingAnchor],
[_locationIconImageView.centerYAnchor
constraintEqualToAnchor:_locationContainerView.centerYAnchor],
];
_hideLocationImageConstraints = @[
[_locationContainerView.leadingAnchor
constraintEqualToAnchor:_locationLabel.leadingAnchor],
[_locationLabel.trailingAnchor
constraintEqualToAnchor:_locationContainerView.trailingAnchor],
];
[NSLayoutConstraint activateConstraints:_showLocationImageConstraints];
_locationButton = [[LocationBarSteadyButton alloc] init];
_locationButton.translatesAutoresizingMaskIntoConstraints = NO;
[_locationButton addSubview:_trailingButton];
[_locationButton addSubview:_locationContainerView];
[self addSubview:_locationButton];
ApplyVisualConstraints(
@[ @"V:|[label]|", @"V:|[container]|" ],
@{@"label" : _locationLabel, @"container" : _locationContainerView});
AddSameConstraints(self, _locationButton);
// Make the label gravitate towards the center of the view.
NSLayoutConstraint* centerX = [_locationContainerView.centerXAnchor
constraintEqualToAnchor:self.centerXAnchor];
centerX.priority = UILayoutPriorityDefaultHigh;
_locationContainerViewLeadingAnchorConstraint =
[_locationContainerView.leadingAnchor
constraintGreaterThanOrEqualToAnchor:self.leadingAnchor
constant:kLocationBarLeadingPadding];
// Setup and activate constraints.
[NSLayoutConstraint activateConstraints:@[
[_trailingButton.centerYAnchor
constraintEqualToAnchor:self.centerYAnchor],
[_locationContainerView.centerYAnchor
constraintEqualToAnchor:self.centerYAnchor],
[_trailingButton.leadingAnchor
constraintGreaterThanOrEqualToAnchor:_locationContainerView
.trailingAnchor],
[_trailingButton.widthAnchor constraintEqualToConstant:kButtonSize],
[_trailingButton.heightAnchor constraintEqualToConstant:kButtonSize],
[self.trailingButton.trailingAnchor
constraintEqualToAnchor:self.trailingAnchor
constant:-kButtonTrailingSpacing],
centerX,
_locationContainerViewLeadingAnchorConstraint,
]];
}
// Setup accessibility.
_trailingButton.isAccessibilityElement = YES;
if (self.badgeView) {
self.badgeView.isAccessibilityElement = YES;
// TODO(crbug.com/989233): This needs to reflect the currently displayed
// badge.
self.badgeView.accessibilityLabel =
l10n_util::GetNSString(IDS_IOS_INFOBAR_BADGES_PASSWORD_HINT);
}
_locationButton.isAccessibilityElement = YES;
_locationButton.accessibilityLabel =
l10n_util::GetNSString(IDS_ACCNAME_LOCATION);
_accessibleElements = [[NSMutableArray alloc] init];
[_accessibleElements addObject:_locationButton];
[_accessibleElements addObject:_trailingButton];
// These two elements must remain accessible for egtests, but will not be
// included in accessibility navigation as they are not added to the
// accessibleElements array.
_locationIconImageView.isAccessibilityElement = YES;
_locationLabel.isAccessibilityElement = YES;
[self updateAccessibility];
return self;
}
- (void)setColorScheme:(LocationBarSteadyViewColorScheme*)colorScheme {
_colorScheme = colorScheme;
self.trailingButton.tintColor = self.colorScheme.trailingButtonColor;
// The text color is set in -setLocationLabelText: and
// -setLocationLabelPlaceholderText: because the two text styles have
// different colors. The icon should be the same color as the text, but it
// only appears with the regular label, so its color can be set here.
self.locationIconImageView.tintColor = self.colorScheme.fontColor;
}
- (void)setLocationImage:(UIImage*)locationImage {
BOOL hadImage = self.locationIconImageView.image != nil;
BOOL hasImage = locationImage != nil;
self.locationIconImageView.image = locationImage;
if (hadImage == hasImage) {
return;
}
if (hasImage) {
[self.locationContainerView addSubview:self.locationIconImageView];
[NSLayoutConstraint
deactivateConstraints:self.hideLocationImageConstraints];
[NSLayoutConstraint activateConstraints:self.showLocationImageConstraints];
} else {
[NSLayoutConstraint
deactivateConstraints:self.showLocationImageConstraints];
[NSLayoutConstraint activateConstraints:self.hideLocationImageConstraints];
[self.locationIconImageView removeFromSuperview];
}
}
- (void)setLocationLabelText:(NSString*)string {
if ([self.locationLabel.text isEqualToString:string]) {
return;
}
self.locationLabel.textColor = self.colorScheme.fontColor;
self.locationLabel.text = string;
[self updateAccessibility];
}
- (void)setLocationLabelPlaceholderText:(NSString*)string {
self.locationLabel.textColor = self.colorScheme.placeholderColor;
self.locationLabel.text = string;
}
- (void)setSecurityLevelAccessibilityString:(NSString*)string {
if ([_securityLevelAccessibilityString isEqualToString:string]) {
return;
}
_securityLevelAccessibilityString = [string copy];
[self updateAccessibility];
}
- (void)setBadgeView:(UIView*)badgeView {
BOOL hadBadgeView = _badgeView != nil;
_badgeView = badgeView;
if (!hadBadgeView && badgeView) {
_badgeView.translatesAutoresizingMaskIntoConstraints = NO;
[self.locationButton addSubview:_badgeView];
// Lazy init.
self.badgeViewFullScreenEnabledConstraints = @[
[self.badgeView.leadingAnchor
constraintGreaterThanOrEqualToAnchor:self.leadingAnchor],
[self.badgeView.trailingAnchor
constraintEqualToAnchor:self.locationContainerView.leadingAnchor],
];
self.badgeViewFullScreenDisabledConstraints = @[
[self.badgeView.leadingAnchor constraintEqualToAnchor:self.leadingAnchor],
[self.badgeView.trailingAnchor
constraintLessThanOrEqualToAnchor:self.locationContainerView
.leadingAnchor],
];
[NSLayoutConstraint deactivateConstraints:@[
self.locationContainerViewLeadingAnchorConstraint
]];
[NSLayoutConstraint
activateConstraints:
[self.badgeViewFullScreenDisabledConstraints
arrayByAddingObjectsFromArray:@[
[self.badgeView.topAnchor
constraintEqualToAnchor:self.topAnchor],
[self.badgeView.bottomAnchor
constraintEqualToAnchor:self.bottomAnchor],
]]];
}
}
- (void)setFullScreenCollapsedMode:(BOOL)isFullScreenCollapsed {
if (!self.badgeView) {
return;
}
if (isFullScreenCollapsed) {
[NSLayoutConstraint
activateConstraints:self.badgeViewFullScreenEnabledConstraints];
[NSLayoutConstraint
deactivateConstraints:self.badgeViewFullScreenDisabledConstraints];
} else {
[NSLayoutConstraint
deactivateConstraints:self.badgeViewFullScreenEnabledConstraints];
[NSLayoutConstraint
activateConstraints:self.badgeViewFullScreenDisabledConstraints];
}
}
- (void)displayBadgeView:(BOOL)display animated:(BOOL)animated {
if (display) {
// Adding InfobarBadge button as an accessibility element behind location
// label. Thus, there should be at least one object alreading in
// |accessibleElements|.
DCHECK([self.accessibleElements count] > 0);
[self.accessibleElements insertObject:self.badgeView atIndex:1];
} else {
[self.accessibleElements removeObject:self.badgeView];
}
void (^changeHiddenState)() = ^{
self.badgeView.hidden = !display;
};
if (animated) {
[UIView animateWithDuration:kbadgeViewAnimationDuration
animations:changeHiddenState];
} else {
changeHiddenState();
}
}
- (void)enableTrailingButton:(BOOL)enabled {
self.trailingButton.enabled = enabled;
[self updateAccessibility];
}
#pragma mark - UIResponder
// This is needed for UIMenu
- (BOOL)canBecomeFirstResponder {
return true;
}
#pragma mark - UIView
- (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
if (previousTraitCollection.preferredContentSizeCategory !=
self.traitCollection.preferredContentSizeCategory) {
self.locationLabel.font = [self locationLabelFont];
}
}
#pragma mark - UIAccessibilityContainer
- (NSArray*)accessibilityElements {
return self.accessibleElements;
}
- (NSInteger)accessibilityElementCount {
return self.accessibleElements.count;
}
- (id)accessibilityElementAtIndex:(NSInteger)index {
return self.accessibleElements[index];
}
- (NSInteger)indexOfAccessibilityElement:(id)element {
return [self.accessibleElements indexOfObject:element];
}
#pragma mark - private
- (void)updateAccessibility {
if (self.securityLevelAccessibilityString.length > 0) {
self.locationButton.accessibilityValue =
[NSString stringWithFormat:@"%@ %@", self.locationLabel.text,
self.securityLevelAccessibilityString];
} else {
self.locationButton.accessibilityValue =
[NSString stringWithFormat:@"%@", self.locationLabel.text];
}
if (self.trailingButton.enabled) {
[self.accessibleElements addObject:self.trailingButton];
} else {
[self.accessibleElements removeObject:self.trailingButton];
}
}
// Returns the font size for the location label.
- (UIFont*)locationLabelFont {
return PreferredFontForTextStyleWithMaxCategory(
UIFontTextStyleBody, self.traitCollection.preferredContentSizeCategory,
UIContentSizeCategoryAccessibilityExtraLarge);
}
@end