Commit 70e5db65 authored by Moe Ahmadi's avatar Moe Ahmadi Committed by Commit Bot

[iOS][Translate] Compact translate infobar view

video: https://drive.google.com/file/d/1rRJgtGsgFPsFgcoUCwqcYT-dHDKguuhk/view?usp=sharing

Starting from the leading edge, it features an icon followed by the source
and the target languages. Toggling between the languages results in the
page to be translated or the translation to be reverted. At the trailing
edge, the infobar features an options button that opens a popup menu that
allows changing translate preferences followed by a close button to dismiss
the infobar. The view hierarchy is as follows:
TranslateInfobarView ->
TranslateInfobarTabStripView ->
TranslateInfobarTabView

Bug: 910994
Change-Id: Icc9d06f43a4a91ba0ec967ce3e9b372b5e9b9bfb
Reviewed-on: https://chromium-review.googlesource.com/c/1426014
Commit-Queue: Moe Ahmadi <mahmadi@chromium.org>
Reviewed-by: default avatarSergio Collazos <sczs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#625681}
parent 2e96eaa6
...@@ -41,6 +41,15 @@ source_set("translate_ui") { ...@@ -41,6 +41,15 @@ source_set("translate_ui") {
"language_selection_provider.h", "language_selection_provider.h",
"language_selection_view_controller.h", "language_selection_view_controller.h",
"language_selection_view_controller.mm", "language_selection_view_controller.mm",
"translate_infobar_language_tab_strip_view.h",
"translate_infobar_language_tab_strip_view.mm",
"translate_infobar_language_tab_strip_view_delegate.h",
"translate_infobar_language_tab_view.h",
"translate_infobar_language_tab_view.mm",
"translate_infobar_language_tab_view_delegate.h",
"translate_infobar_view.h",
"translate_infobar_view.mm",
"translate_infobar_view_delegate.h",
"translate_notification_delegate.h", "translate_notification_delegate.h",
"translate_notification_handler.h", "translate_notification_handler.h",
"translate_notification_presenter.h", "translate_notification_presenter.h",
...@@ -50,7 +59,14 @@ source_set("translate_ui") { ...@@ -50,7 +59,14 @@ source_set("translate_ui") {
"//base", "//base",
"//components/strings:components_strings", "//components/strings:components_strings",
"//ios/chrome/browser", "//ios/chrome/browser",
"//ios/chrome/browser/ui/colors",
"//ios/chrome/browser/ui/infobars:infobars_ui",
"//ios/chrome/browser/ui/toolbar/buttons",
"//ios/chrome/browser/ui/translate/resources:translate_dismiss",
"//ios/chrome/browser/ui/translate/resources:translate_icon",
"//ios/chrome/browser/ui/translate/resources:translate_options",
"//ios/chrome/browser/ui/util", "//ios/chrome/browser/ui/util",
"//ios/chrome/common/ui_util",
"//ios/third_party/material_components_ios", "//ios/third_party/material_components_ios",
"//ui/base", "//ui/base",
] ]
......
# Copyright 2019 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("//build/config/ios/asset_catalog.gni")
imageset("translate_icon") {
sources = [
"translate_icon.imageset/Contents.json",
"translate_icon.imageset/translate_icon.png",
"translate_icon.imageset/translate_icon@2x.png",
"translate_icon.imageset/translate_icon@3x.png",
]
}
imageset("translate_options") {
sources = [
"translate_options.imageset/Contents.json",
"translate_options.imageset/translate_options.png",
"translate_options.imageset/translate_options@2x.png",
"translate_options.imageset/translate_options@3x.png",
]
}
imageset("translate_dismiss") {
sources = [
"translate_dismiss.imageset/Contents.json",
"translate_dismiss.imageset/translate_dismiss.png",
"translate_dismiss.imageset/translate_dismiss@2x.png",
"translate_dismiss.imageset/translate_dismiss@3x.png",
]
}
{
"images": [
{
"idiom": "universal",
"scale": "1x",
"filename": "translate_dismiss.png"
},
{
"idiom": "universal",
"scale": "2x",
"filename": "translate_dismiss@2x.png"
},
{
"idiom": "universal",
"scale": "3x",
"filename": "translate_dismiss@3x.png"
}
],
"info": {
"version": 1,
"author": "xcode"
}
}
{
"images": [
{
"idiom": "universal",
"scale": "1x",
"filename": "translate_icon.png"
},
{
"idiom": "universal",
"scale": "2x",
"filename": "translate_icon@2x.png"
},
{
"idiom": "universal",
"scale": "3x",
"filename": "translate_icon@3x.png"
}
],
"info": {
"version": 1,
"author": "xcode"
}
}
{
"images": [
{
"idiom": "universal",
"scale": "1x",
"filename": "translate_options.png"
},
{
"idiom": "universal",
"scale": "2x",
"filename": "translate_options@2x.png"
},
{
"idiom": "universal",
"scale": "3x",
"filename": "translate_options@3x.png"
}
],
"info": {
"version": 1,
"author": "xcode"
}
}
// Copyright 2019 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.
#ifndef IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_STRIP_VIEW_H_
#define IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_STRIP_VIEW_H_
#import <UIKit/UIKit.h>
#import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_view.h"
@protocol TranslateInfobarLanguageTabStripViewDelegate;
// A view containing a scrollable tab strip featuring the source and the target
// language tabs. When a language tab is tapped, it is scrolled into view, if
// necessary.
@interface TranslateInfobarLanguageTabStripView : UIView
// Source language name.
@property(nonatomic, copy) NSString* sourceLanguage;
// Target language name.
@property(nonatomic, copy) NSString* targetLanguage;
// State of the source language tab.
@property(nonatomic)
TranslateInfobarLanguageTabViewState sourceLanguageTabState;
// State of the target language tab.
@property(nonatomic)
TranslateInfobarLanguageTabViewState targetLanguageTabState;
// Delegate object that gets notified if user taps the source or the target
// language tabs.
@property(nonatomic, weak) id<TranslateInfobarLanguageTabStripViewDelegate>
delegate;
@end
#endif // IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_STRIP_VIEW_H_
// Copyright 2019 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/translate/translate_infobar_language_tab_strip_view.h"
#import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view_delegate.h"
#import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_view_delegate.h"
#import "ios/chrome/browser/ui/translate/translate_infobar_view.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// The point where the gradient on the leading edge of the scroll view stops.
CGFloat kScrollViewLeadingGradientStop = 0.025;
// The point where the gradient on the trailing edge of the scroll view starts.
CGFloat kScrollViewTrailingGradientStart = 0.975;
} // namespace
@interface TranslateInfobarLanguageTabStripView () <
TranslateInfobarLanguageTabViewDelegate,
UIScrollViewDelegate>
// Scroll view holding the source and the target language tab views.
@property(nonatomic, weak) UIScrollView* languagesScrollView;
// Used for the fading effect on the edges of the scroll view.
@property(nonatomic, weak) CAGradientLayer* gradientLayer;
// Source language tab view.
@property(nonatomic, weak) TranslateInfobarLanguageTabView* sourceLanguageTab;
// Target language tab view.
@property(nonatomic, weak) TranslateInfobarLanguageTabView* targetLanguageTab;
@end
@implementation TranslateInfobarLanguageTabStripView
#pragma mark - UIView
- (void)willMoveToSuperview:(UIView*)newSuperview {
// Create and add subviews the first time this moves to a superview.
if (newSuperview && !self.subviews.count) {
[self setupSubviews];
}
[super willMoveToSuperview:newSuperview];
}
- (void)layoutSubviews {
[super layoutSubviews];
// Scroll the scroll view so that the selected language tab is visible.
if (self.sourceLanguageTabState ==
TranslateInfobarLanguageTabViewStateSelected) {
[self.languagesScrollView scrollRectToVisible:self.sourceLanguageTab.frame
animated:YES];
} else if (self.targetLanguageTabState ==
TranslateInfobarLanguageTabViewStateSelected) {
[self.languagesScrollView scrollRectToVisible:self.targetLanguageTab.frame
animated:YES];
}
[self updateLanguageScrollViewGradient];
}
#pragma mark - Properties
- (void)setSourceLanguage:(NSString*)sourceLanguage {
_sourceLanguage = sourceLanguage;
self.sourceLanguageTab.title = sourceLanguage;
[self updateLanguageScrollViewGradient];
}
- (void)setTargetLanguage:(NSString*)targetLanguage {
_targetLanguage = targetLanguage;
self.targetLanguageTab.title = targetLanguage;
[self updateLanguageScrollViewGradient];
}
- (void)setSourceLanguageTabState:
(TranslateInfobarLanguageTabViewState)sourceLanguageTabState {
_sourceLanguageTabState = sourceLanguageTabState;
self.sourceLanguageTab.state = sourceLanguageTabState;
}
- (void)setTargetLanguageTabState:
(TranslateInfobarLanguageTabViewState)targetLanguageTabState {
_targetLanguageTabState = targetLanguageTabState;
self.targetLanguageTab.state = targetLanguageTabState;
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView*)scrollView {
[self updateLanguageScrollViewGradient];
}
#pragma mark - TranslateInfobarLanguageTabViewDelegate
- (void)translateInfobarTabViewDidTap:(TranslateInfobarLanguageTabView*)sender {
[self.languagesScrollView scrollRectToVisible:sender.frame animated:YES];
if (sender == self.targetLanguageTab) {
[self.delegate translateInfobarTabStripViewDidTapTargetLangugage:self];
} else {
[self.delegate translateInfobarTabStripViewDidTapSourceLangugage:self];
}
}
#pragma mark - Private
- (void)setupSubviews {
UIScrollView* languagesScrollView = [[UIScrollView alloc] init];
self.languagesScrollView = languagesScrollView;
self.languagesScrollView.translatesAutoresizingMaskIntoConstraints = NO;
self.languagesScrollView.showsVerticalScrollIndicator = NO;
self.languagesScrollView.showsHorizontalScrollIndicator = NO;
self.languagesScrollView.canCancelContentTouches = YES;
self.languagesScrollView.bounces = NO;
self.languagesScrollView.delegate = self;
[self addSubview:self.languagesScrollView];
self.gradientLayer = [CAGradientLayer layer];
self.gradientLayer.colors =
[NSArray arrayWithObjects:(id)[[UIColor clearColor] CGColor],
(id)[[UIColor whiteColor] CGColor],
(id)[[UIColor whiteColor] CGColor],
(id)[[UIColor clearColor] CGColor], nil];
// The following two lines make the gradient horizontal.
self.gradientLayer.startPoint = CGPointMake(0.0, 0.5);
self.gradientLayer.endPoint = CGPointMake(1.0, 0.5);
self.languagesScrollView.layer.mask = self.gradientLayer;
TranslateInfobarLanguageTabView* sourceLanguageTab =
[[TranslateInfobarLanguageTabView alloc] init];
self.sourceLanguageTab = sourceLanguageTab;
self.sourceLanguageTab.translatesAutoresizingMaskIntoConstraints = NO;
self.sourceLanguageTab.title = self.sourceLanguage;
self.sourceLanguageTab.delegate = self;
[self.languagesScrollView addSubview:self.sourceLanguageTab];
TranslateInfobarLanguageTabView* targetLanguageTab =
[[TranslateInfobarLanguageTabView alloc] init];
self.targetLanguageTab = targetLanguageTab;
self.targetLanguageTab.translatesAutoresizingMaskIntoConstraints = NO;
self.targetLanguageTab.title = self.targetLanguage;
self.targetLanguageTab.delegate = self;
[self.languagesScrollView addSubview:self.targetLanguageTab];
ApplyVisualConstraintsWithMetrics(
@[
@"H:|[scrollView]|",
@"H:|[sourceLanguage][targetLanguage]|",
@"V:|[scrollView(infobarHeight)]|",
@"V:|[sourceLanguage(infobarHeight)]|",
@"V:|[targetLanguage(infobarHeight)]|",
],
@{
@"scrollView" : self.languagesScrollView,
@"sourceLanguage" : self.sourceLanguageTab,
@"targetLanguage" : self.targetLanguageTab,
},
@{
@"infobarHeight" : @(kInfobarHeight),
});
}
// Updates the scroll view's gradient locations based on whether or not the
// scroll view has content under the leading fold, trailing fold, or both.
- (void)updateLanguageScrollViewGradient {
// Lay out the scroll view's subviews if needed.
[self.languagesScrollView layoutIfNeeded];
CGFloat scrollViewWidth = CGRectGetWidth(self.languagesScrollView.frame);
CGFloat contentWidth = self.languagesScrollView.contentSize.width;
CGFloat scrollOffset = self.languagesScrollView.contentOffset.x;
BOOL hasContentUnderLeadingFold = (scrollOffset > 0);
BOOL hasContentUnderTrailingFold =
(scrollOffset + scrollViewWidth < contentWidth);
self.gradientLayer.locations = @[
@0, hasContentUnderLeadingFold ? @(kScrollViewLeadingGradientStop) : @0,
hasContentUnderTrailingFold ? @(kScrollViewTrailingGradientStart) : @1, @1
];
// Disable animation; or updating the gradient's frame will be animated.
[CATransaction begin];
[CATransaction setDisableActions:YES];
// Update the gradient's frame.
self.gradientLayer.frame = CGRectMake(
scrollOffset, 0, CGRectGetWidth(self.languagesScrollView.bounds),
CGRectGetHeight(self.languagesScrollView.bounds));
// Reenable animation.
[CATransaction commit];
}
@end
// Copyright 2019 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.
#ifndef IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_STRIP_VIEW_DELEGATE_H_
#define IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_STRIP_VIEW_DELEGATE_H_
#import <Foundation/Foundation.h>
// A protocol implemented by a delegate of TranslateInfobarLanguageTabStripView.
@protocol TranslateInfobarLanguageTabStripViewDelegate
// Notifies the delegate that user tapped the source language tab.
- (void)translateInfobarTabStripViewDidTapSourceLangugage:
(TranslateInfobarLanguageTabStripView*)sender;
// Notifies the delegate that user tapped the target language tab.
- (void)translateInfobarTabStripViewDidTapTargetLangugage:
(TranslateInfobarLanguageTabStripView*)sender;
@end
#endif // IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_STRIP_VIEW_DELEGATE_H_
// Copyright 2019 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.
#ifndef IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_VIEW_H_
#define IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_VIEW_H_
#import <UIKit/UIKit.h>
// States in which the language tab can be.
typedef NS_ENUM(NSInteger, TranslateInfobarLanguageTabViewState) {
TranslateInfobarLanguageTabViewStateDefault,
TranslateInfobarLanguageTabViewStateSelected,
TranslateInfobarLanguageTabViewStateLoading,
};
@protocol TranslateInfobarLanguageTabViewDelegate;
// The language tab view featuring a label. It can be in a default, selected, or
// loading state. In the selected state the label is highlighted and in the
// loading state the label is replaced by an activity indicator.
@interface TranslateInfobarLanguageTabView : UIView
// Title of the language tab.
@property(nonatomic, copy) NSString* title;
// State of the language tab.
@property(nonatomic) TranslateInfobarLanguageTabViewState state;
// Delegate object that gets notified if user taps the view.
@property(nonatomic, weak) id<TranslateInfobarLanguageTabViewDelegate> delegate;
@end
#endif // IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_VIEW_H_
// Copyright 2019 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/translate/translate_infobar_language_tab_view.h"
#include "base/logging.h"
#import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
#import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.h"
#import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_view_delegate.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#import "ios/third_party/material_components_ios/src/components/ActivityIndicator/src/MaterialActivityIndicator.h"
#import "ios/third_party/material_components_ios/src/components/Buttons/src/MaterialButtons.h"
#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Size of the activity indicator.
const CGFloat kActivityIndicatorSize = 24;
// Radius of the activity indicator.
const CGFloat kActivityIndicatorRadius = 10;
// Duration of the animation to make the activity indicator visible.
const NSTimeInterval kActivityIndicatorVisbilityAnimationDuration = 0.4;
// Title color of the action buttons in RGB.
const int kButtonTitleColor = 0x4285f4;
// Padding for contents of the button.
const CGFloat kButtonPadding = 12;
} // namespace
@interface TranslateInfobarLanguageTabView ()
// Button subview providing the tappable area.
@property(nonatomic, weak) MDCFlatButton* button;
// Activity indicator replacing the button when in loading state.
@property(nonatomic, weak) MDCActivityIndicator* activityIndicator;
@end
@implementation TranslateInfobarLanguageTabView
#pragma mark - UIView
- (void)willMoveToSuperview:(UIView*)newSuperview {
// Create and add subviews the first time this moves to a superview.
if (newSuperview && !self.subviews.count) {
[self setupSubviews];
}
[super willMoveToSuperview:newSuperview];
}
#pragma mark - Properties
- (void)setTitle:(NSString*)title {
_title = title;
[self.button setTitle:title forState:UIControlStateNormal];
}
- (void)setState:(TranslateInfobarLanguageTabViewState)state {
_state = state;
if (state == TranslateInfobarLanguageTabViewStateLoading) {
[self.activityIndicator startAnimating];
// Animate showing the activity indicator and hiding the button. Otherwise
// the ripple effect on the button won't be seen.
[UIView animateWithDuration:kActivityIndicatorVisbilityAnimationDuration
animations:^{
self.activityIndicator.hidden = NO;
self.button.hidden = YES;
}];
} else {
self.button.hidden = NO;
self.activityIndicator.hidden = YES;
[self.activityIndicator stopAnimating];
[self.button setTitleColor:[self titleColor] forState:UIControlStateNormal];
}
}
#pragma mark - Private
- (void)setupSubviews {
MDCActivityIndicator* activityIndicator = [[MDCActivityIndicator alloc] init];
self.activityIndicator = activityIndicator;
self.activityIndicator.translatesAutoresizingMaskIntoConstraints = NO;
self.activityIndicator.cycleColors =
@[ [[MDCPalette cr_bluePalette] tint500] ];
[self.activityIndicator setRadius:kActivityIndicatorRadius];
self.activityIndicator.hidden = YES; // Initially hidden.
[self addSubview:self.activityIndicator];
[NSLayoutConstraint activateConstraints:@[
[self.activityIndicator.heightAnchor
constraintEqualToConstant:kActivityIndicatorSize],
]];
AddSameCenterConstraints(self, self.activityIndicator);
MDCFlatButton* button = [[MDCFlatButton alloc] init];
self.button = button;
self.button.translatesAutoresizingMaskIntoConstraints = NO;
[self.button setUnderlyingColorHint:[UIColor blackColor]];
self.button.contentEdgeInsets = UIEdgeInsetsMake(
kButtonPadding, kButtonPadding, kButtonPadding, kButtonPadding);
self.button.titleLabel.adjustsFontSizeToFitWidth = YES;
self.button.titleLabel.minimumScaleFactor = 0.6f;
[self.button setTitle:self.title forState:UIControlStateNormal];
self.button.uppercaseTitle = NO;
[self.button
setTitleFont:[UIFont preferredFontForTextStyle:UIFontTextStyleHeadline]
forState:UIControlStateNormal];
[self.button setTitleColor:[self titleColor] forState:UIControlStateNormal];
self.button.inkColor = [[MDCPalette greyPalette] tint300];
[self.button addTarget:self
action:@selector(buttonWasTapped)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:self.button];
AddSameConstraints(self, self.button);
}
// Returns the button's title color depending on the state.
- (UIColor*)titleColor {
return self.state == TranslateInfobarLanguageTabViewStateSelected
? UIColorFromRGB(kButtonTitleColor)
: [[MDCPalette greyPalette] tint600];
}
- (void)buttonWasTapped {
[self.delegate translateInfobarTabViewDidTap:self];
}
@end
// Copyright 2019 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.
#ifndef IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_VIEW_DELEGATE_H_
#define IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_VIEW_DELEGATE_H_
#import <Foundation/Foundation.h>
// A protocol implemented by a delegate of TranslateInfobarLanguageTabView.
@protocol TranslateInfobarLanguageTabViewDelegate
// Notifies the delegate that user tapped the view.
- (void)translateInfobarTabViewDidTap:(TranslateInfobarLanguageTabView*)sender;
@end
#endif // IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_LANGUAGE_TAB_VIEW_DELEGATE_H_
// Copyright 2019 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.
#ifndef IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_VIEW_H_
#define IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_VIEW_H_
#import <UIKit/UIKit.h>
#include <vector>
// States in which the infobar can be.
typedef NS_ENUM(NSInteger, TranslateInfobarViewState) {
TranslateInfobarViewStateBeforeTranslate,
TranslateInfobarViewStateTranslating,
TranslateInfobarViewStateAfterTranslate,
};
// Height of the infobar.
extern const CGFloat kInfobarHeight;
@protocol TranslateInfobarViewDelegate;
// An infobar for translating the page. Starting from the leading edge, it
// features an icon followed by the source and the target languages. Toggling
// between the languages results in the page to be translated or the translation
// to be reverted. At the trailing edge, the infobar features an options button
// that opens a popup menu that allows changing translate preferences followed
// by a dismiss button to close the infobar.
@interface TranslateInfobarView : UIView
// Source language name.
@property(nonatomic, copy) NSString* sourceLanguage;
// Target language name.
@property(nonatomic, copy) NSString* targetLanguage;
// Infobar's state.
@property(nonatomic) TranslateInfobarViewState state;
// Delegate object that gets notified if user taps the source language tab, the
// target language tab, the options button, or the dismiss button.
@property(nonatomic, weak) id<TranslateInfobarViewDelegate> delegate;
// Updates the infobar UI when a popup menu is displayed or dismissed.
- (void)updateUIForPopUpMenuDisplayed:(BOOL)displayed;
@end
#endif // IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_VIEW_H_
// Copyright 2019 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/translate/translate_infobar_view.h"
#include "base/logging.h"
#include "components/strings/grit/components_strings.h"
#import "ios/chrome/browser/procedural_block_types.h"
#import "ios/chrome/browser/ui/infobars/infobar_constants.h"
#import "ios/chrome/browser/ui/toolbar/buttons/toolbar_button.h"
#import "ios/chrome/browser/ui/toolbar/buttons/toolbar_configuration.h"
#import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view.h"
#import "ios/chrome/browser/ui/translate/translate_infobar_language_tab_strip_view_delegate.h"
#import "ios/chrome/browser/ui/translate/translate_infobar_view_delegate.h"
#import "ios/chrome/browser/ui/util/label_link_controller.h"
#import "ios/chrome/browser/ui/util/layout_guide_names.h"
#import "ios/chrome/browser/ui/util/named_guide.h"
#include "ios/chrome/browser/ui/util/ui_util.h"
#import "ios/chrome/browser/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/ui_util/constraints_ui_util.h"
#include "ui/base/l10n/l10n_util.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
const CGFloat kInfobarHeight = 54;
namespace {
// Size of the infobar buttons.
const CGFloat kButtonSize = 44;
// Size of the infobar icons.
const CGFloat kIconSize = 24;
// Leading margin for the translate icon.
const CGFloat kIconLeadingMargin = 16;
// Trailing margin for the translate icon.
const CGFloat kIconTrailingMargin = 12;
} // namespace
@interface TranslateInfobarView () <
TranslateInfobarLanguageTabStripViewDelegate>
// Translate icon view.
@property(nonatomic, weak) UIImageView* iconView;
// Scrollable tab strip holding the source and the target language tabs.
@property(nonatomic, weak) TranslateInfobarLanguageTabStripView* languagesView;
// Options button. Presents the options popup menu when tapped.
@property(nonatomic, weak) ToolbarButton* optionsButton;
// Dismiss button.
@property(nonatomic, weak) ToolbarButton* dismissButton;
// Toolbar configuration object used for |optionsButton| and |dismissButton|.
@property(nonatomic, strong) ToolbarConfiguration* toolbarConfiguration;
// Constraint used to add bottom margin to the view.
@property(nonatomic, weak) NSLayoutConstraint* bottomAnchorConstraint;
@end
@implementation TranslateInfobarView
#pragma mark - UIView
- (void)willMoveToSuperview:(UIView*)newSuperview {
// Create and add subviews the first time this moves to a superview.
if (newSuperview && !self.subviews.count) {
[self setupSubviews];
// Lower constraint's priority to avoid breaking other constraints while
// |newSuperview| is animating.
// TODO(crbug.com/904521): Investigate why this is needed.
self.bottomAnchorConstraint.priority = UILayoutPriorityDefaultLow;
}
[super willMoveToSuperview:newSuperview];
}
- (void)didMoveToSuperview {
[super didMoveToSuperview];
if (!self.superview)
return;
self.state = TranslateInfobarViewStateBeforeTranslate;
[NamedGuide guideWithName:kTranslateInfobarOptionsGuide
view:self.optionsButton]
.constrainedView = self.optionsButton;
// Increase constraint's priority after the view was added to its superview.
// TODO(crbug.com/904521): Investigate why this is needed.
self.bottomAnchorConstraint.priority = UILayoutPriorityDefaultHigh;
}
- (CGSize)sizeThatFits:(CGSize)size {
// Calculate the safe area and current Toolbar height. Set the
// bottomAnchorConstraint constant to this height to create the bottom
// padding.
CGFloat bottomSafeAreaInset = self.safeAreaInsets.bottom;
CGFloat toolbarHeight = 0;
UILayoutGuide* guide = [NamedGuide guideWithName:kSecondaryToolbarGuide
view:self];
UILayoutGuide* guideNoFullscreen =
[NamedGuide guideWithName:kSecondaryToolbarNoFullscreenGuide view:self];
if (guide && guideNoFullscreen) {
CGFloat toolbarHeightCurrent = guide.layoutFrame.size.height;
CGFloat toolbarHeightMax = guideNoFullscreen.layoutFrame.size.height;
if (toolbarHeightMax > 0) {
CGFloat fullscreenProgress = toolbarHeightCurrent / toolbarHeightMax;
CGFloat toolbarHeightInSafeArea = toolbarHeightMax - bottomSafeAreaInset;
toolbarHeight += fullscreenProgress * toolbarHeightInSafeArea;
}
}
self.bottomAnchorConstraint.constant = toolbarHeight + bottomSafeAreaInset;
// Now that the constraint constant has been set calculate the fitting size.
CGSize computedSize = [self systemLayoutSizeFittingSize:size];
return CGSizeMake(size.width, computedSize.height);
}
#pragma mark - Properties
- (void)setSourceLanguage:(NSString*)sourceLanguage {
_sourceLanguage = sourceLanguage;
self.languagesView.sourceLanguage = sourceLanguage;
}
- (void)setTargetLanguage:(NSString*)targetLanguage {
_targetLanguage = targetLanguage;
self.languagesView.targetLanguage = targetLanguage;
}
- (void)setState:(TranslateInfobarViewState)state {
_state = state;
switch (state) {
case TranslateInfobarViewStateBeforeTranslate:
self.languagesView.sourceLanguageTabState =
TranslateInfobarLanguageTabViewStateSelected;
self.languagesView.targetLanguageTabState =
TranslateInfobarLanguageTabViewStateDefault;
break;
case TranslateInfobarViewStateTranslating:
self.languagesView.sourceLanguageTabState =
TranslateInfobarLanguageTabViewStateDefault;
self.languagesView.targetLanguageTabState =
TranslateInfobarLanguageTabViewStateLoading;
break;
case TranslateInfobarViewStateAfterTranslate:
self.languagesView.sourceLanguageTabState =
TranslateInfobarLanguageTabViewStateDefault;
self.languagesView.targetLanguageTabState =
TranslateInfobarLanguageTabViewStateSelected;
break;
}
}
#pragma mark - Public
- (void)updateUIForPopUpMenuDisplayed:(BOOL)displayed {
self.optionsButton.spotlighted = displayed;
self.optionsButton.dimmed = displayed;
self.dismissButton.dimmed = displayed;
}
#pragma mark - TranslateInfobarLanguageTabStripViewDelegate
- (void)translateInfobarTabStripViewDidTapSourceLangugage:
(TranslateInfobarLanguageTabStripView*)sender {
[self.delegate translateInfobarViewDidTapSourceLangugage:self];
}
- (void)translateInfobarTabStripViewDidTapTargetLangugage:
(TranslateInfobarLanguageTabStripView*)sender {
[self.delegate translateInfobarViewDidTapTargetLangugage:self];
}
#pragma mark - Private
- (void)setupSubviews {
[self setAccessibilityViewIsModal:YES];
if (IsUIRefreshPhase1Enabled()) {
self.backgroundColor = UIColorFromRGB(kInfobarBackgroundColor);
} else {
self.backgroundColor = [UIColor whiteColor];
}
id<LayoutGuideProvider> safeAreaLayoutGuide = self.safeAreaLayoutGuide;
// The Content view. Holds all the other subviews.
UIView* contentView = [[UIView alloc] init];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:contentView];
self.bottomAnchorConstraint =
[self.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor];
[NSLayoutConstraint activateConstraints:@[
[safeAreaLayoutGuide.leadingAnchor
constraintEqualToAnchor:contentView.leadingAnchor],
[safeAreaLayoutGuide.trailingAnchor
constraintEqualToAnchor:contentView.trailingAnchor],
[self.topAnchor constraintEqualToAnchor:contentView.topAnchor],
self.bottomAnchorConstraint
]];
UIImage* icon = [[UIImage imageNamed:@"translate_icon"]
resizableImageWithCapInsets:UIEdgeInsetsZero
resizingMode:UIImageResizingModeStretch];
UIImageView* iconView = [[UIImageView alloc] initWithImage:icon];
self.iconView = iconView;
self.iconView.translatesAutoresizingMaskIntoConstraints = NO;
[contentView addSubview:self.iconView];
TranslateInfobarLanguageTabStripView* languagesView =
[[TranslateInfobarLanguageTabStripView alloc] init];
self.languagesView = languagesView;
self.languagesView.translatesAutoresizingMaskIntoConstraints = NO;
self.languagesView.sourceLanguage = self.sourceLanguage;
self.languagesView.targetLanguage = self.targetLanguage;
self.languagesView.delegate = self;
[contentView addSubview:self.languagesView];
self.toolbarConfiguration =
[[ToolbarConfiguration alloc] initWithStyle:NORMAL];
self.optionsButton =
[self toolbarButtonWithImageNamed:@"translate_options"
target:self
action:@selector(showOptions)];
[contentView addSubview:self.optionsButton];
self.dismissButton =
[self toolbarButtonWithImageNamed:@"translate_dismiss"
target:self
action:@selector(dismiss)];
[contentView addSubview:self.dismissButton];
ApplyVisualConstraintsWithMetrics(
@[
@"H:|-(iconLeadingMargin)-[icon(iconSize)]-(iconTrailingMargin)-[languages][options(buttonSize)][dismiss(buttonSize)]|",
@"V:|[languages(infobarHeight)]|",
@"V:[icon(iconSize)]",
@"V:[options(buttonSize)]",
@"V:[dismiss(buttonSize)]",
],
@{
@"icon" : self.iconView,
@"languages" : self.languagesView,
@"options" : self.optionsButton,
@"dismiss" : self.dismissButton,
},
@{
@"iconSize" : @(kIconSize),
@"iconLeadingMargin" : @(kIconLeadingMargin),
@"iconTrailingMargin" : @(kIconTrailingMargin),
@"infobarHeight" : @(kInfobarHeight),
@"buttonSize" : @(kButtonSize),
});
AddSameCenterYConstraint(contentView, self.iconView);
AddSameCenterYConstraint(contentView, self.optionsButton);
AddSameCenterYConstraint(contentView, self.dismissButton);
}
- (ToolbarButton*)toolbarButtonWithImageNamed:(NSString*)name
target:(id)target
action:(SEL)action {
UIImage* image = [[UIImage imageNamed:name]
imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
ToolbarButton* button = [ToolbarButton toolbarButtonWithImage:image];
[button addTarget:target
action:action
forControlEvents:UIControlEventTouchUpInside];
const CGFloat kButtonPadding = (kButtonSize - kIconSize) / 2;
button.contentEdgeInsets = UIEdgeInsetsMake(kButtonPadding, kButtonPadding,
kButtonPadding, kButtonPadding);
button.configuration = self.toolbarConfiguration;
return button;
}
- (void)showOptions {
[self.delegate translateInfobarViewDidTapOptions:self];
}
- (void)dismiss {
[self.delegate translateInfobarViewDidTapDismiss:self];
}
@end
// Copyright 2019 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.
#ifndef IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_VIEW_DELEGATE_H_
#define IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_VIEW_DELEGATE_H_
#import <Foundation/Foundation.h>
// A protocol implemented by a delegate of TranslateInfobarView.
@protocol TranslateInfobarViewDelegate
// Notifies the delegate that user tapped the source language button.
- (void)translateInfobarViewDidTapSourceLangugage:(TranslateInfobarView*)sender;
// Notifies the delegate that user tapped the target language button.
- (void)translateInfobarViewDidTapTargetLangugage:(TranslateInfobarView*)sender;
// Notifies the delegate that user tapped the options button.
- (void)translateInfobarViewDidTapOptions:(TranslateInfobarView*)sender;
// Notifies the delegate that user tapped the dismiss button.
- (void)translateInfobarViewDidTapDismiss:(TranslateInfobarView*)sender;
@end
#endif // IOS_CHROME_BROWSER_UI_TRANSLATE_TRANSLATE_INFOBAR_VIEW_DELEGATE_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment