| Index: ios/chrome/browser/ui/omnibox/page_info_view_controller.mm
|
| diff --git a/ios/chrome/browser/ui/omnibox/page_info_view_controller.mm b/ios/chrome/browser/ui/omnibox/page_info_view_controller.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..78fb2a5a7b825d9bb004acf85b47c11bf6fdea45
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/omnibox/page_info_view_controller.mm
|
| @@ -0,0 +1,637 @@
|
| +// Copyright (c) 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/omnibox/page_info_view_controller.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/location.h"
|
| +#include "base/mac/bundle_locations.h"
|
| +#import "base/mac/foundation_util.h"
|
| +#include "base/mac/objc_property_releaser.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/single_thread_task_runner.h"
|
| +#include "base/strings/sys_string_conversions.h"
|
| +#include "base/threading/thread_task_runner_handle.h"
|
| +#include "components/strings/grit/components_strings.h"
|
| +#import "ios/chrome/browser/ui/animation_util.h"
|
| +#import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
|
| +#include "ios/chrome/browser/ui/commands/ios_command_ids.h"
|
| +#import "ios/chrome/browser/ui/fancy_ui/bidi_container_view.h"
|
| +#include "ios/chrome/browser/ui/omnibox/page_info_model.h"
|
| +#import "ios/chrome/browser/ui/popup_menu/popup_menu_view.h"
|
| +#include "ios/chrome/browser/ui/rtl_geometry.h"
|
| +#include "ios/chrome/browser/ui/ui_util.h"
|
| +#import "ios/chrome/browser/ui/uikit_ui_util.h"
|
| +#import "ios/chrome/common/material_timing.h"
|
| +#include "ios/chrome/grit/ios_strings.h"
|
| +#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
|
| +#import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h"
|
| +#include "ui/base/l10n/l10n_util.h"
|
| +#include "ui/base/l10n/l10n_util_mac.h"
|
| +#import "ui/gfx/ios/NSString+CrStringDrawing.h"
|
| +#import "ui/gfx/ios/uikit_util.h"
|
| +
|
| +using ios::material::TimingFunction;
|
| +
|
| +namespace {
|
| +
|
| +// The width of the view.
|
| +const CGFloat kViewWidthRegular = 600.0;
|
| +const CGFloat kViewWidthCompact = 288.0;
|
| +const CGFloat kViewWidthiPhoneLandscape = 400.0;
|
| +// Spacing in between sections.
|
| +const CGFloat kVerticalSpacing = 20.0;
|
| +// Initial position for the left side of the frame.
|
| +const CGFloat kInitialFramePosition = 8.0;
|
| +// Alpha for the shield.
|
| +const CGFloat kShieldAlpha = 0.5;
|
| +// Scroll View inset.
|
| +const CGFloat kScrollViewInset = 5.0;
|
| +// The size of the footer (rounded corner and shadow) for page info view.
|
| +const CGFloat kPageInfoViewFooterSize = 15.0;
|
| +// Padding between the window frame and content.
|
| +const CGFloat kFramePadding = 24;
|
| +// Padding for the initial line of the view.
|
| +const CGFloat kInitialLinePadding = 40;
|
| +// Padding between the bottom of the content and the window frame.
|
| +const CGFloat kFrameBottomPadding = 16;
|
| +// Spacing between the optional headline and description text views.
|
| +const CGFloat kHeadlineSpacing = 16;
|
| +// Spacing between the image and the text.
|
| +const CGFloat kImageSpacing = 16;
|
| +// Square size of the image.
|
| +const CGFloat kImageSize = 24;
|
| +// The height of the headline label.
|
| +const CGFloat kHeadlineHeight = 19;
|
| +// The hex color for the help button text.
|
| +const int kPageInfoHelpButtonRGB = 0x3b8cfe;
|
| +// The grey scale color for the text within the page info alert.
|
| +const CGFloat kPageInfoTextGreyComponent = 0.2;
|
| +
|
| +inline UIColor* PageInfoTextColor() {
|
| + return [UIColor colorWithWhite:kPageInfoTextGreyComponent alpha:1];
|
| +}
|
| +
|
| +inline UIColor* PageInfoHelpButtonColor() {
|
| + return UIColorFromRGB(kPageInfoHelpButtonRGB);
|
| +}
|
| +
|
| +inline UIFont* PageInfoHeadlineFont() {
|
| + return [[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:16];
|
| +}
|
| +
|
| +inline CATransform3D PageInfoAnimationScale() {
|
| + return CATransform3DMakeScale(0.03, 0.03, 1);
|
| +}
|
| +
|
| +// Offset to make sure image aligns with the header line.
|
| +inline CGFloat PageInfoImageVerticalOffset() {
|
| + return ui::AlignValueToUpperPixel((kHeadlineHeight - kImageSize) / 2.0);
|
| +}
|
| +
|
| +// The X position of the text fields. Variants for with and without an image.
|
| +const CGFloat kTextXPositionNoImage = kFramePadding;
|
| +const CGFloat kTextXPosition =
|
| + kTextXPositionNoImage + kImageSize + kImageSpacing;
|
| +
|
| +// The X offset for the help button.
|
| +const CGFloat kButtonXOffset = kTextXPosition;
|
| +
|
| +} // namespace
|
| +
|
| +PageInfoModelBubbleBridge::PageInfoModelBubbleBridge()
|
| + : controller_(nil), weak_ptr_factory_(this) {}
|
| +
|
| +PageInfoModelBubbleBridge::~PageInfoModelBubbleBridge() {}
|
| +
|
| +void PageInfoModelBubbleBridge::OnPageInfoModelChanged() {
|
| + // Check to see if a layout has already been scheduled.
|
| + if (weak_ptr_factory_.HasWeakPtrs())
|
| + return;
|
| +
|
| + // Delay performing layout by a second so that all the animations from
|
| + // InfoBubbleWindow and origin updates from BaseBubbleController finish, so
|
| + // that we don't all race trying to change the frame's origin.
|
| + //
|
| + // Using MessageLoop is superior here to |-performSelector:| because it will
|
| + // not retain its target; if the child outlives its parent, zombies get left
|
| + // behind (http://crbug.com/59619). This will cancel the scheduled task if
|
| + // the controller (and thus this bridge) get destroyed before the message
|
| + // can be delivered.
|
| + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
|
| + FROM_HERE, base::Bind(&PageInfoModelBubbleBridge::PerformLayout,
|
| + weak_ptr_factory_.GetWeakPtr()),
|
| + base::TimeDelta::FromMilliseconds(1000 /* milliseconds */));
|
| +}
|
| +
|
| +@interface PageInfoViewController ()<UIGestureRecognizerDelegate> {
|
| + // Scroll View inside the PageInfoView used to display content that exceeds
|
| + // the available space.
|
| + base::scoped_nsobject<UIScrollView> scrollView_;
|
| + // Container View added inside the Scroll View. All content is added to this
|
| + // view instead of PopupMenuController.containerView_.
|
| + base::scoped_nsobject<BidiContainerView> innerContainerView_;
|
| +
|
| + // Origin of the arrow at the top of the popup window.
|
| + CGPoint origin_;
|
| +
|
| + // Model for the data to display.
|
| + std::unique_ptr<PageInfoModel> model_;
|
| +
|
| + // Thin bridge that pushes model-changed notifications from C++ to Cocoa.
|
| + std::unique_ptr<PageInfoModelObserver> bridge_;
|
| +
|
| + // Width of the view. Depends on the device (iPad/iPhone).
|
| + CGFloat viewWidth_;
|
| +
|
| + // Width of the text fields.
|
| + CGFloat textWidth_;
|
| +
|
| + // YES when the popup has finished animating in. NO otherwise.
|
| + BOOL animateInCompleted_;
|
| +
|
| + base::mac::ObjCPropertyReleaser propertyReleaser_PageInfoViewController_;
|
| +}
|
| +
|
| +// Adds the state image at a pre-determined x position and the given y. This
|
| +// does not affect the next Y position because the image is placed next to
|
| +// a text field that is larger and accounts for the image's size.
|
| +- (void)addImageViewForInfo:(const PageInfoModel::SectionInfo&)info
|
| + toSubviews:(NSMutableArray*)subviews
|
| + atOffset:(CGFloat)offset;
|
| +
|
| +// Adds the title text field at the given x,y position, and returns the y
|
| +// position for the next element.
|
| +- (CGFloat)addHeadlineViewForInfo:(const PageInfoModel::SectionInfo&)info
|
| + toSubviews:(NSMutableArray*)subviews
|
| + atPoint:(CGPoint)point;
|
| +
|
| +// Adds the description text field at the given x,y position, and returns the y
|
| +// position for the next element.
|
| +- (CGFloat)addDescriptionViewForInfo:(const PageInfoModel::SectionInfo&)info
|
| + toSubviews:(NSMutableArray*)subviews
|
| + atPoint:(CGPoint)point;
|
| +
|
| +// Returns a button with title and action configured for |buttonAction|.
|
| +- (UIButton*)buttonForAction:(PageInfoModel::ButtonAction)buttonAction;
|
| +
|
| +// Adds the the button |buttonAction| that explains the icons. Returns the y
|
| +// position delta for the next offset.
|
| +- (CGFloat)addButton:(PageInfoModel::ButtonAction)buttonAction
|
| + toSubviews:(NSMutableArray*)subviews
|
| + atOffset:(CGFloat)offset;
|
| +
|
| +@property(nonatomic, retain) UIView* containerView;
|
| +@property(nonatomic, retain) UIView* popupContainer;
|
| +@end
|
| +
|
| +@implementation PageInfoViewController
|
| +
|
| +@synthesize containerView = containerView_;
|
| +@synthesize popupContainer = popupContainer_;
|
| +
|
| +- (id)initWithModel:(PageInfoModel*)model
|
| + bridge:(PageInfoModelObserver*)bridge
|
| + sourceFrame:(CGRect)source
|
| + parentView:(UIView*)parent {
|
| + DCHECK(parent);
|
| + self = [super init];
|
| + if (self) {
|
| + propertyReleaser_PageInfoViewController_.Init(
|
| + self, [PageInfoViewController class]);
|
| +
|
| + scrollView_.reset(
|
| + [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 240, 128)]);
|
| + [scrollView_ setMultipleTouchEnabled:YES];
|
| + [scrollView_ setClipsToBounds:YES];
|
| + [scrollView_ setShowsHorizontalScrollIndicator:NO];
|
| + [scrollView_ setIndicatorStyle:UIScrollViewIndicatorStyleBlack];
|
| + [scrollView_
|
| + setAutoresizingMask:(UIViewAutoresizingFlexibleTrailingMargin() |
|
| + UIViewAutoresizingFlexibleTopMargin)];
|
| +
|
| + innerContainerView_.reset(
|
| + [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 194, 327)]);
|
| + [innerContainerView_ setBackgroundColor:[UIColor clearColor]];
|
| + [innerContainerView_
|
| + setAccessibilityLabel:@"Page Security Info Scroll Container"];
|
| + [innerContainerView_
|
| + setAutoresizingMask:(UIViewAutoresizingFlexibleTrailingMargin() |
|
| + UIViewAutoresizingFlexibleBottomMargin)];
|
| +
|
| + model_.reset(model);
|
| + bridge_.reset(bridge);
|
| + origin_ = CGPointMake(CGRectGetMidX(source), CGRectGetMaxY(source));
|
| +
|
| + UIInterfaceOrientation orientation =
|
| + [[UIApplication sharedApplication] statusBarOrientation];
|
| + viewWidth_ = IsCompact() ? kViewWidthCompact : kViewWidthRegular;
|
| + // Special case iPhone landscape.
|
| + if (!IsIPadIdiom() && UIInterfaceOrientationIsLandscape(orientation))
|
| + viewWidth_ = kViewWidthiPhoneLandscape;
|
| +
|
| + textWidth_ = viewWidth_ - (kImageSize + kImageSpacing + kFramePadding * 2 +
|
| + kScrollViewInset * 2);
|
| +
|
| + base::scoped_nsobject<UILongPressGestureRecognizer> touchDownRecognizer(
|
| + [[UILongPressGestureRecognizer alloc]
|
| + initWithTarget:self
|
| + action:@selector(rootViewTapped:)]);
|
| + // Setting the duration to .001 makes this similar to a control event
|
| + // UIControlEventTouchDown.
|
| + [touchDownRecognizer setMinimumPressDuration:.001];
|
| + [touchDownRecognizer setDelegate:self];
|
| +
|
| + containerView_ = [[UIView alloc] initWithFrame:[parent bounds]];
|
| + [containerView_ addGestureRecognizer:touchDownRecognizer];
|
| + [containerView_
|
| + setBackgroundColor:[UIColor colorWithWhite:0 alpha:kShieldAlpha]];
|
| + [containerView_ setTag:IDC_HIDE_PAGE_INFO];
|
| + [containerView_ setOpaque:NO];
|
| + [containerView_ setAlpha:0];
|
| + [containerView_ setAccessibilityViewIsModal:YES];
|
| +
|
| + popupContainer_ = [[UIView alloc] initWithFrame:CGRectZero];
|
| + [popupContainer_ setBackgroundColor:[UIColor whiteColor]];
|
| + [popupContainer_ setClipsToBounds:YES];
|
| + [containerView_ addSubview:popupContainer_];
|
| +
|
| + [self.popupContainer addSubview:scrollView_];
|
| + [scrollView_ addSubview:innerContainerView_];
|
| + [scrollView_ setAccessibilityIdentifier:@"Page Security Scroll View"];
|
| + [parent addSubview:self.containerView];
|
| + [self performLayout];
|
| +
|
| + [self animatePageInfoViewIn:source];
|
| + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification,
|
| + containerView_);
|
| + }
|
| +
|
| + return self;
|
| +}
|
| +
|
| +- (void)performLayout {
|
| + CGFloat offset = kInitialLinePadding;
|
| +
|
| + // Keep the new subviews in an array that gets replaced at the end.
|
| + NSMutableArray* subviews = [NSMutableArray array];
|
| +
|
| + int sectionCount = model_->GetSectionCount();
|
| + PageInfoModel::ButtonAction action = PageInfoModel::BUTTON_NONE;
|
| +
|
| + for (int i = 0; i < sectionCount; i++) {
|
| + PageInfoModel::SectionInfo info = model_->GetSectionInfo(i);
|
| +
|
| + if (action == PageInfoModel::BUTTON_NONE &&
|
| + info.button != PageInfoModel::BUTTON_NONE) {
|
| + // Show the button corresponding to the first section that requires a
|
| + // button.
|
| + action = info.button;
|
| + }
|
| +
|
| + // Only certain sections have images. This affects the X position.
|
| + BOOL hasImage = model_->GetIconImage(info.icon_id) != nil;
|
| + CGFloat xPosition = (hasImage ? kTextXPosition : kTextXPositionNoImage);
|
| +
|
| + // Insert the image subview for sections that are appropriate.
|
| + CGFloat imageBaseline = offset + kImageSize;
|
| + if (hasImage) {
|
| + [self addImageViewForInfo:info
|
| + toSubviews:subviews
|
| + atOffset:offset + PageInfoImageVerticalOffset()];
|
| + }
|
| +
|
| + // Add the title.
|
| + if (!info.headline.empty()) {
|
| + offset += [self addHeadlineViewForInfo:info
|
| + toSubviews:subviews
|
| + atPoint:CGPointMake(xPosition, offset)];
|
| + offset += kHeadlineSpacing;
|
| + }
|
| +
|
| + // Create the description of the state.
|
| + offset += [self addDescriptionViewForInfo:info
|
| + toSubviews:subviews
|
| + atPoint:CGPointMake(xPosition, offset)];
|
| +
|
| + // If at this point the description and optional headline and button are
|
| + // not as tall as the image, adjust the offset by the difference.
|
| + CGFloat imageBaselineDelta = imageBaseline - offset;
|
| + if (imageBaselineDelta > 0)
|
| + offset += imageBaselineDelta;
|
| +
|
| + // Add the separators.
|
| + int testSectionCount = sectionCount - 1;
|
| + if (i != testSectionCount ||
|
| + (i == testSectionCount && action != PageInfoModel::BUTTON_NONE)) {
|
| + offset += kVerticalSpacing;
|
| + }
|
| + }
|
| +
|
| + // The last item at the bottom of the window is the help center link. Do not
|
| + // show this for the internal pages, which have one section.
|
| + offset += [self addButton:action toSubviews:subviews atOffset:offset];
|
| +
|
| + // Add the bottom padding.
|
| + offset += kVerticalSpacing;
|
| + CGRect frame =
|
| + CGRectMake(kInitialFramePosition, origin_.y, viewWidth_, offset);
|
| +
|
| + // Increase the size of the frame by the amount used for drawing rounded
|
| + // corners and shadow.
|
| + frame.size.height += kPageInfoViewFooterSize;
|
| +
|
| + if (CGRectGetMaxY(frame) >
|
| + CGRectGetMaxY([[self containerView] superview].bounds) -
|
| + kFrameBottomPadding) {
|
| + // If the frame is bigger than the parent view than change the frame to
|
| + // fit in the superview bounds.
|
| + frame.size.height = [[self containerView] superview].bounds.size.height -
|
| + kFrameBottomPadding - frame.origin.y;
|
| +
|
| + [scrollView_ setScrollEnabled:YES];
|
| + [scrollView_ flashScrollIndicators];
|
| + } else {
|
| + [scrollView_ setScrollEnabled:NO];
|
| + }
|
| +
|
| + CGRect containerBounds = [containerView_ bounds];
|
| + CGRect popupFrame = frame;
|
| + popupFrame.origin.x =
|
| + CGRectGetMidX(containerBounds) - CGRectGetWidth(popupFrame) / 2.0;
|
| + popupFrame.origin.y =
|
| + CGRectGetMidY(containerBounds) - CGRectGetHeight(popupFrame) / 2.0;
|
| +
|
| + popupFrame.origin = AlignPointToPixel(popupFrame.origin);
|
| + CGRect innerFrame = CGRectMake(0, 0, popupFrame.size.width, offset);
|
| +
|
| + // If the initial animation has completed, animate the new frames.
|
| + if (animateInCompleted_) {
|
| + [UIView cr_animateWithDuration:ios::material::kDuration3
|
| + delay:0
|
| + curve:ios::material::CurveEaseInOut
|
| + options:0
|
| + animations:^{
|
| + [popupContainer_ setFrame:popupFrame];
|
| + [scrollView_ setFrame:[popupContainer_ bounds]];
|
| + [innerContainerView_ setFrame:innerFrame];
|
| + }
|
| + completion:nil];
|
| + } else {
|
| + // Popup hasn't finished animating in yet. Set frames immediately.
|
| + [popupContainer_ setFrame:popupFrame];
|
| + [scrollView_ setFrame:[popupContainer_ bounds]];
|
| + [innerContainerView_ setFrame:innerFrame];
|
| + }
|
| +
|
| + for (UIView* view in [innerContainerView_ subviews]) {
|
| + [view removeFromSuperview];
|
| + }
|
| +
|
| + for (UIView* view in subviews) {
|
| + [innerContainerView_ addSubview:view];
|
| + [innerContainerView_ setSubviewNeedsAdjustmentForRTL:view];
|
| + }
|
| +
|
| + [scrollView_ setContentSize:innerContainerView_.get().frame.size];
|
| +}
|
| +
|
| +- (void)dismiss {
|
| + [self animatePageInfoViewOut];
|
| + UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification,
|
| + nil);
|
| +}
|
| +
|
| +#pragma mark - Helper methods to create subviews.
|
| +
|
| +- (void)addImageViewForInfo:(const PageInfoModel::SectionInfo&)info
|
| + toSubviews:(NSMutableArray*)subviews
|
| + atOffset:(CGFloat)offset {
|
| + CGRect frame = CGRectMake(kFramePadding, offset, kImageSize, kImageSize);
|
| + base::scoped_nsobject<UIImageView> imageView(
|
| + [[UIImageView alloc] initWithFrame:frame]);
|
| + [imageView setImage:model_->GetIconImage(info.icon_id)->ToUIImage()];
|
| + [subviews addObject:imageView.get()];
|
| +}
|
| +
|
| +- (CGFloat)addHeadlineViewForInfo:(const PageInfoModel::SectionInfo&)info
|
| + toSubviews:(NSMutableArray*)subviews
|
| + atPoint:(CGPoint)point {
|
| + CGRect frame = CGRectMake(point.x, point.y, textWidth_, kHeadlineHeight);
|
| + base::scoped_nsobject<UILabel> label([[UILabel alloc] initWithFrame:frame]);
|
| + [label setTextAlignment:NSTextAlignmentNatural];
|
| + [label setText:base::SysUTF16ToNSString(info.headline)];
|
| + [label setTextColor:PageInfoTextColor()];
|
| + [label setFont:PageInfoHeadlineFont()];
|
| + [label setBackgroundColor:[UIColor clearColor]];
|
| + [label setFrame:frame];
|
| + [label setLineBreakMode:NSLineBreakByTruncatingHead];
|
| + [subviews addObject:label.get()];
|
| + return CGRectGetHeight(frame);
|
| +}
|
| +
|
| +- (CGFloat)addDescriptionViewForInfo:(const PageInfoModel::SectionInfo&)info
|
| + toSubviews:(NSMutableArray*)subviews
|
| + atPoint:(CGPoint)point {
|
| + CGRect frame = CGRectMake(point.x, point.y, textWidth_, kImageSize);
|
| + base::scoped_nsobject<UILabel> label([[UILabel alloc] initWithFrame:frame]);
|
| + [label setTextAlignment:NSTextAlignmentNatural];
|
| + NSString* description = base::SysUTF16ToNSString(info.description);
|
| + UIFont* font = [MDCTypography captionFont];
|
| + [label setTextColor:PageInfoTextColor()];
|
| + [label setText:description];
|
| + [label setFont:font];
|
| + [label setNumberOfLines:0];
|
| + [label setBackgroundColor:[UIColor clearColor]];
|
| +
|
| + // If the text is oversized, resize the text field.
|
| + CGSize constraintSize = CGSizeMake(textWidth_, CGFLOAT_MAX);
|
| + CGSize sizeToFit =
|
| + [description cr_boundingSizeWithSize:constraintSize font:font];
|
| + frame.size.height = sizeToFit.height;
|
| + [label setFrame:frame];
|
| + [subviews addObject:label.get()];
|
| + return CGRectGetHeight(frame);
|
| +}
|
| +
|
| +- (UIButton*)buttonForAction:(PageInfoModel::ButtonAction)buttonAction {
|
| + if (buttonAction == PageInfoModel::BUTTON_NONE) {
|
| + return nil;
|
| + }
|
| + UIButton* button = [[[UIButton alloc] initWithFrame:CGRectZero] autorelease];
|
| + int messageId = IDS_IOS_PAGE_INFO_RELOAD;
|
| + NSInteger tag = IDC_RELOAD;
|
| + NSString* accessibilityID = @"Reload button";
|
| + switch (buttonAction) {
|
| + case PageInfoModel::BUTTON_NONE:
|
| + NOTREACHED();
|
| + return nil;
|
| + case PageInfoModel::BUTTON_SHOW_SECURITY_HELP:
|
| + messageId = IDS_PAGE_INFO_HELP_CENTER_LINK;
|
| + tag = IDC_SHOW_SECURITY_HELP;
|
| + accessibilityID = @"What do these mean?";
|
| + break;
|
| + case PageInfoModel::BUTTON_RELOAD:
|
| + messageId = IDS_IOS_PAGE_INFO_RELOAD;
|
| + tag = IDC_RELOAD;
|
| + accessibilityID = @"Reload button";
|
| + [button addTarget:self
|
| + action:@selector(dismiss)
|
| + forControlEvents:UIControlEventTouchUpInside];
|
| + break;
|
| + };
|
| +
|
| + NSString* title = l10n_util::GetNSStringWithFixup(messageId);
|
| + SetA11yLabelAndUiAutomationName(button, messageId, accessibilityID);
|
| + [button setTitle:title forState:UIControlStateNormal];
|
| + [button setTag:tag];
|
| + [button addTarget:nil
|
| + action:@selector(chromeExecuteCommand:)
|
| + forControlEvents:UIControlEventTouchUpInside];
|
| + return button;
|
| +}
|
| +
|
| +- (CGFloat)addButton:(PageInfoModel::ButtonAction)buttonAction
|
| + toSubviews:(NSMutableArray*)subviews
|
| + atOffset:(CGFloat)offset {
|
| + UIButton* button = [self buttonForAction:buttonAction];
|
| + if (!button) {
|
| + return 0;
|
| + }
|
| + // The size of the initial frame is irrelevant since it will be changed based
|
| + // on the size for the string inside.
|
| + CGRect frame = CGRectMake(kButtonXOffset, offset, 100, 10);
|
| +
|
| + UIFont* font = [MDCTypography captionFont];
|
| + CGSize sizeWithFont =
|
| + [[[button titleLabel] text] cr_pixelAlignedSizeWithFont:font];
|
| + frame.size = sizeWithFont;
|
| +
|
| + [button setFrame:frame];
|
| +
|
| + [button.titleLabel setFont:font];
|
| + [button.titleLabel setTextAlignment:NSTextAlignmentLeft];
|
| + [button setTitleColor:PageInfoHelpButtonColor()
|
| + forState:UIControlStateNormal];
|
| + [button setTitleColor:PageInfoHelpButtonColor()
|
| + forState:UIControlStateSelected];
|
| + [button setBackgroundColor:[UIColor clearColor]];
|
| +
|
| + [subviews addObject:button];
|
| +
|
| + return CGRectGetHeight([button frame]);
|
| +}
|
| +
|
| +#pragma mark - UIGestureRecognizerDelegate Implemenation
|
| +
|
| +- (void)rootViewTapped:(UIGestureRecognizer*)sender {
|
| + CGPoint pt = [sender locationInView:containerView_];
|
| + if (!CGRectContainsPoint([popupContainer_ frame], pt)) {
|
| + [containerView_ chromeExecuteCommand:containerView_];
|
| + }
|
| +}
|
| +
|
| +- (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer
|
| + shouldReceiveTouch:(UITouch*)touch {
|
| + CGPoint pt = [touch locationInView:containerView_];
|
| + return !CGRectContainsPoint([popupContainer_ frame], pt);
|
| +}
|
| +
|
| +- (void)animatePageInfoViewIn:(CGRect)source {
|
| + // Animate the info card itself.
|
| + CATransform3D scaleTransform = PageInfoAnimationScale();
|
| + CGPoint fromPoint = CGPointMake(CGRectGetMidX(source), CGRectGetMidY(source));
|
| +
|
| + CABasicAnimation* scaleAnimation =
|
| + [CABasicAnimation animationWithKeyPath:@"transform"];
|
| + [scaleAnimation setFromValue:[NSValue valueWithCATransform3D:scaleTransform]];
|
| +
|
| + CABasicAnimation* positionAnimation =
|
| + [CABasicAnimation animationWithKeyPath:@"position"];
|
| + [positionAnimation setFromValue:[NSValue valueWithCGPoint:fromPoint]];
|
| +
|
| + CAAnimationGroup* sizeAnimation = [CAAnimationGroup animation];
|
| + [sizeAnimation setAnimations:@[ scaleAnimation, positionAnimation ]];
|
| + [sizeAnimation
|
| + setTimingFunction:TimingFunction(ios::material::CurveEaseInOut)];
|
| + [sizeAnimation setDuration:ios::material::kDuration3];
|
| +
|
| + CABasicAnimation* fadeAnimation =
|
| + [CABasicAnimation animationWithKeyPath:@"opacity"];
|
| + [fadeAnimation setTimingFunction:TimingFunction(ios::material::CurveEaseOut)];
|
| + [fadeAnimation setDuration:ios::material::kDuration6];
|
| + [fadeAnimation setFromValue:@0];
|
| + [fadeAnimation setToValue:@1];
|
| +
|
| + [[popupContainer_ layer] addAnimation:fadeAnimation forKey:@"fade"];
|
| + [[popupContainer_ layer] addAnimation:sizeAnimation forKey:@"size"];
|
| +
|
| + // Animation the background grey overlay.
|
| + CABasicAnimation* overlayAnimation =
|
| + [CABasicAnimation animationWithKeyPath:@"opacity"];
|
| + [overlayAnimation
|
| + setTimingFunction:TimingFunction(ios::material::CurveEaseOut)];
|
| + [overlayAnimation setDuration:ios::material::kDuration3];
|
| + [overlayAnimation setFromValue:@0];
|
| + [overlayAnimation setToValue:@1];
|
| +
|
| + [[containerView_ layer] addAnimation:overlayAnimation forKey:@"fade"];
|
| + [containerView_ setAlpha:1];
|
| +
|
| + // Animate the contents of the info card.
|
| + CALayer* contentsLayer = [innerContainerView_ layer];
|
| +
|
| + CGRect startFrame = CGRectOffset([innerContainerView_ frame], 0, -32);
|
| + CAAnimation* contentSlideAnimation = FrameAnimationMake(
|
| + contentsLayer, startFrame, [innerContainerView_ frame]);
|
| + [contentSlideAnimation
|
| + setTimingFunction:TimingFunction(ios::material::CurveEaseOut)];
|
| + [contentSlideAnimation setDuration:ios::material::kDuration5];
|
| + contentSlideAnimation =
|
| + DelayedAnimationMake(contentSlideAnimation, ios::material::kDuration2);
|
| + [contentsLayer addAnimation:contentSlideAnimation forKey:@"slide"];
|
| +
|
| + [CATransaction begin];
|
| + [CATransaction setCompletionBlock:^{
|
| + [innerContainerView_ setAlpha:1];
|
| + animateInCompleted_ = YES;
|
| + }];
|
| + CAAnimation* contentFadeAnimation = OpacityAnimationMake(0.0, 1.0);
|
| + [contentFadeAnimation
|
| + setTimingFunction:TimingFunction(ios::material::CurveEaseOut)];
|
| + [contentFadeAnimation setDuration:ios::material::kDuration5];
|
| + contentFadeAnimation =
|
| + DelayedAnimationMake(contentFadeAnimation, ios::material::kDuration1);
|
| + [contentsLayer addAnimation:contentFadeAnimation forKey:@"fade"];
|
| + [CATransaction commit];
|
| +
|
| + // Since the animations have delay on them, the alpha of the content view
|
| + // needs to be set to zero and then one after the animation starts. If these
|
| + // steps are not taken, there will be a visible flash/jump from the initial
|
| + // spot during the animation.
|
| + [innerContainerView_ setAlpha:0];
|
| +}
|
| +
|
| +- (void)animatePageInfoViewOut {
|
| + [CATransaction begin];
|
| + [CATransaction setCompletionBlock:^{
|
| + [self.containerView removeFromSuperview];
|
| + }];
|
| +
|
| + CABasicAnimation* opacityAnimation =
|
| + [CABasicAnimation animationWithKeyPath:@"opacity"];
|
| + [opacityAnimation
|
| + setTimingFunction:TimingFunction(ios::material::CurveEaseIn)];
|
| + [opacityAnimation setDuration:ios::material::kDuration3];
|
| + [opacityAnimation setFromValue:@1];
|
| + [opacityAnimation setToValue:@0];
|
| + [[containerView_ layer] addAnimation:opacityAnimation forKey:@"animateOut"];
|
| +
|
| + [popupContainer_ setAlpha:0];
|
| + [containerView_ setAlpha:0];
|
| + [CATransaction commit];
|
| +}
|
| +
|
| +@end
|
|
|