blob: 062389f53bf708f5d36f295d2b71e8bb618a72bf [file] [log] [blame]
// Copyright 2014 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/open_in_toolbar.h"
#include <cmath>
#include "base/logging.h"
#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
#import "ios/chrome/browser/ui/util/named_guide.h"
#include "ios/chrome/browser/ui/util/rtl_geometry.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#include "ios/chrome/grit/ios_strings.h"
#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
#include "ui/base/l10n/l10n_util_mac.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// The toolbar's open button constants.
const CGFloat kOpenButtonVerticalPadding = 8.0f;
const CGFloat kOpenButtonTrailingPadding = 16.0f;
// The toolbar's border related constants.
const CGFloat kTopBorderHeight = 0.5f;
const CGFloat kTopBorderTransparency = 0.13f;
const int kTopBorderColor = 0x000000;
// The toolbar's background related constants.
const int kToolbarBackgroundColor = 0xFFFFFF;
const CGFloat kToolbarBackgroundTransparency = 0.97f;
} // anonymous namespace
@interface OpenInToolbar () {
// Backing object for |self.openButton|.
MDCButton* _openButton;
// Backing object for |self.topBorder|.
UIView* _topBorder;
}
// The "Open in..." button that's hooked up with the target and action passed
// on initialization.
@property(nonatomic, retain, readonly) MDCButton* openButton;
// The line used as the border at the top of the toolbar.
@property(nonatomic, retain, readonly) UIView* topBorder;
// View used to have a bottom margin to prevent overlapping with the bottom
// toolbar. This view is used because the the CRWWebControllerContainerView is
// not using autolayout so the toolbar cannot use autolayout either. See
// https://crbug.com/857371.
@property(nonatomic, strong) UIView* bottomMargin;
// Relayout the frame of the Open In toolbar, based on its superview and the
// bottom margin.
- (void)relayout;
@end
// TODO(crbug.com/857371): Remove this once the OpenInToolbar is positioned with
// autolayout.
// This view is used to have a view the same height as the bottom toolbar as the
// OpenInToolbar cannot be positioned using autolayout. The height of this view
// should be constrained to the height of the bottom toolbar. When this view is
// re-layouted, its is asking the toolbar to relayout its frame.
@interface OpenInBottomMargin : UIView
// The OpenInToolbar that is notified when the bottom toolbar margin is changed.
@property(nonatomic, weak) OpenInToolbar* owner;
@end
@implementation OpenInBottomMargin
@synthesize owner = _owner;
- (void)layoutSubviews {
[super layoutSubviews];
// This call also happens when the height of this view is changed. And its
// height is the same as the height of the bottom toolbar. So this allows to
// monitor the height changes in the bottom toolbar. Calling relayout on the
// OpenInToolbar ensures that it is correctly positioned.
[self.owner relayout];
}
@end
@implementation OpenInToolbar
@synthesize bottomMargin = _bottomMargin;
@synthesize bottomMarginConstraint = _bottomMarginConstraint;
- (instancetype)initWithFrame:(CGRect)aRect {
NOTREACHED();
return nil;
}
- (instancetype)initWithTarget:(id)target action:(SEL)action {
self = [super initWithFrame:CGRectZero];
if (self) {
DCHECK([target respondsToSelector:action]);
[self setBackgroundColor:UIColorFromRGB(kToolbarBackgroundColor,
kToolbarBackgroundTransparency)];
[self addSubview:self.openButton];
[self.openButton addTarget:target
action:action
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.topBorder];
self.topBorder.translatesAutoresizingMaskIntoConstraints = NO;
self.openButton.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.openButton.trailingAnchor
constraintEqualToAnchor:self.trailingAnchor
constant:-kOpenButtonTrailingPadding],
[self.openButton.leadingAnchor
constraintGreaterThanOrEqualToAnchor:self.leadingAnchor
constant:kOpenButtonTrailingPadding],
[self.openButton.topAnchor
constraintEqualToAnchor:self.topAnchor
constant:kOpenButtonVerticalPadding],
[self.topBorder.heightAnchor constraintEqualToConstant:kTopBorderHeight],
]];
AddSameConstraintsToSides(
self.topBorder, self,
LayoutSides::kTop | LayoutSides::kLeading | LayoutSides::kTrailing);
OpenInBottomMargin* bottom = [[OpenInBottomMargin alloc] init];
bottom.owner = self;
bottom.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:bottom];
self.bottomMargin = bottom;
}
return self;
}
#pragma mark Accessors
- (MDCButton*)openButton {
if (!_openButton) {
_openButton = [[MDCFlatButton alloc] init];
[_openButton setTitleColor:[[MDCPalette cr_bluePalette] tint500]
forState:UIControlStateNormal];
[_openButton setTitle:l10n_util::GetNSStringWithFixup(IDS_IOS_OPEN_IN)
forState:UIControlStateNormal];
[_openButton sizeToFit];
}
return _openButton;
}
- (UIView*)topBorder {
if (!_topBorder) {
_topBorder = [[UIView alloc] initWithFrame:CGRectZero];
[_topBorder setBackgroundColor:UIColorFromRGB(kTopBorderColor,
kTopBorderTransparency)];
}
return _topBorder;
}
#pragma mark Layout
- (CGSize)sizeThatFits:(CGSize)size {
CGSize openButtonSize = [self.openButton sizeThatFits:size];
CGFloat requiredHeight =
openButtonSize.height + 2.0 * kOpenButtonVerticalPadding;
return CGSizeMake(size.width, requiredHeight);
}
- (void)didMoveToSuperview {
if (!self.superview) {
self.bottomMarginConstraint = nil;
return;
}
self.bottomMarginConstraint.active = NO;
// If the bottom toolbar isn't present on screen, guide is nil and then
// bottomMarginConstraint is also nil.
NamedGuide* guide =
[NamedGuide guideWithName:kSecondaryToolbarGuide view:self];
self.bottomMarginConstraint = [guide.heightAnchor
constraintEqualToAnchor:self.bottomMargin.heightAnchor];
self.bottomMarginConstraint.active = YES;
[self relayout];
}
- (void)relayout {
CGRect frame = self.superview.frame;
CGSize sizeThatFits = [self sizeThatFits:frame.size];
CGFloat toolbarHeight =
sizeThatFits.height + self.bottomMargin.frame.size.height;
frame.origin.y = frame.size.height - toolbarHeight;
frame.size.height = toolbarHeight;
CGFloat xDiff = std::fabs(frame.origin.x - self.frame.origin.x);
CGFloat yDiff = std::fabs(frame.origin.y - self.frame.origin.y);
CGFloat widthDiff = std::fabs(frame.size.width - self.frame.size.width);
CGFloat heightDiff = std::fabs(frame.size.height - self.frame.size.height);
if (xDiff <= std::numeric_limits<CGFloat>::epsilon() &&
yDiff <= std::numeric_limits<CGFloat>::epsilon() &&
widthDiff <= std::numeric_limits<CGFloat>::epsilon() &&
heightDiff <= std::numeric_limits<CGFloat>::epsilon()) {
// Don't update the frame if it is close enough to prevent potential
// infinite layout cycle.
return;
}
self.frame = frame;
}
@end