OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/browser/ui/omnibox/page_info_view_controller.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/location.h" |
| 9 #include "base/mac/bundle_locations.h" |
| 10 #import "base/mac/foundation_util.h" |
| 11 #include "base/mac/objc_property_releaser.h" |
| 12 #include "base/mac/scoped_nsobject.h" |
| 13 #include "base/message_loop/message_loop.h" |
| 14 #include "base/single_thread_task_runner.h" |
| 15 #include "base/strings/sys_string_conversions.h" |
| 16 #include "base/threading/thread_task_runner_handle.h" |
| 17 #include "components/strings/grit/components_strings.h" |
| 18 #import "ios/chrome/browser/ui/animation_util.h" |
| 19 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h" |
| 20 #include "ios/chrome/browser/ui/commands/ios_command_ids.h" |
| 21 #import "ios/chrome/browser/ui/fancy_ui/bidi_container_view.h" |
| 22 #include "ios/chrome/browser/ui/omnibox/page_info_model.h" |
| 23 #import "ios/chrome/browser/ui/popup_menu/popup_menu_view.h" |
| 24 #include "ios/chrome/browser/ui/rtl_geometry.h" |
| 25 #include "ios/chrome/browser/ui/ui_util.h" |
| 26 #import "ios/chrome/browser/ui/uikit_ui_util.h" |
| 27 #import "ios/chrome/common/material_timing.h" |
| 28 #include "ios/chrome/grit/ios_strings.h" |
| 29 #import "ios/third_party/material_components_ios/src/components/Typography/src/M
aterialTypography.h" |
| 30 #import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoF
ontLoader.h" |
| 31 #include "ui/base/l10n/l10n_util.h" |
| 32 #include "ui/base/l10n/l10n_util_mac.h" |
| 33 #import "ui/gfx/ios/NSString+CrStringDrawing.h" |
| 34 #import "ui/gfx/ios/uikit_util.h" |
| 35 |
| 36 using ios::material::TimingFunction; |
| 37 |
| 38 namespace { |
| 39 |
| 40 // The width of the view. |
| 41 const CGFloat kViewWidthRegular = 600.0; |
| 42 const CGFloat kViewWidthCompact = 288.0; |
| 43 const CGFloat kViewWidthiPhoneLandscape = 400.0; |
| 44 // Spacing in between sections. |
| 45 const CGFloat kVerticalSpacing = 20.0; |
| 46 // Initial position for the left side of the frame. |
| 47 const CGFloat kInitialFramePosition = 8.0; |
| 48 // Alpha for the shield. |
| 49 const CGFloat kShieldAlpha = 0.5; |
| 50 // Scroll View inset. |
| 51 const CGFloat kScrollViewInset = 5.0; |
| 52 // The size of the footer (rounded corner and shadow) for page info view. |
| 53 const CGFloat kPageInfoViewFooterSize = 15.0; |
| 54 // Padding between the window frame and content. |
| 55 const CGFloat kFramePadding = 24; |
| 56 // Padding for the initial line of the view. |
| 57 const CGFloat kInitialLinePadding = 40; |
| 58 // Padding between the bottom of the content and the window frame. |
| 59 const CGFloat kFrameBottomPadding = 16; |
| 60 // Spacing between the optional headline and description text views. |
| 61 const CGFloat kHeadlineSpacing = 16; |
| 62 // Spacing between the image and the text. |
| 63 const CGFloat kImageSpacing = 16; |
| 64 // Square size of the image. |
| 65 const CGFloat kImageSize = 24; |
| 66 // The height of the headline label. |
| 67 const CGFloat kHeadlineHeight = 19; |
| 68 // The hex color for the help button text. |
| 69 const int kPageInfoHelpButtonRGB = 0x3b8cfe; |
| 70 // The grey scale color for the text within the page info alert. |
| 71 const CGFloat kPageInfoTextGreyComponent = 0.2; |
| 72 |
| 73 inline UIColor* PageInfoTextColor() { |
| 74 return [UIColor colorWithWhite:kPageInfoTextGreyComponent alpha:1]; |
| 75 } |
| 76 |
| 77 inline UIColor* PageInfoHelpButtonColor() { |
| 78 return UIColorFromRGB(kPageInfoHelpButtonRGB); |
| 79 } |
| 80 |
| 81 inline UIFont* PageInfoHeadlineFont() { |
| 82 return [[MDFRobotoFontLoader sharedInstance] mediumFontOfSize:16]; |
| 83 } |
| 84 |
| 85 inline CATransform3D PageInfoAnimationScale() { |
| 86 return CATransform3DMakeScale(0.03, 0.03, 1); |
| 87 } |
| 88 |
| 89 // Offset to make sure image aligns with the header line. |
| 90 inline CGFloat PageInfoImageVerticalOffset() { |
| 91 return ui::AlignValueToUpperPixel((kHeadlineHeight - kImageSize) / 2.0); |
| 92 } |
| 93 |
| 94 // The X position of the text fields. Variants for with and without an image. |
| 95 const CGFloat kTextXPositionNoImage = kFramePadding; |
| 96 const CGFloat kTextXPosition = |
| 97 kTextXPositionNoImage + kImageSize + kImageSpacing; |
| 98 |
| 99 // The X offset for the help button. |
| 100 const CGFloat kButtonXOffset = kTextXPosition; |
| 101 |
| 102 } // namespace |
| 103 |
| 104 PageInfoModelBubbleBridge::PageInfoModelBubbleBridge() |
| 105 : controller_(nil), weak_ptr_factory_(this) {} |
| 106 |
| 107 PageInfoModelBubbleBridge::~PageInfoModelBubbleBridge() {} |
| 108 |
| 109 void PageInfoModelBubbleBridge::OnPageInfoModelChanged() { |
| 110 // Check to see if a layout has already been scheduled. |
| 111 if (weak_ptr_factory_.HasWeakPtrs()) |
| 112 return; |
| 113 |
| 114 // Delay performing layout by a second so that all the animations from |
| 115 // InfoBubbleWindow and origin updates from BaseBubbleController finish, so |
| 116 // that we don't all race trying to change the frame's origin. |
| 117 // |
| 118 // Using MessageLoop is superior here to |-performSelector:| because it will |
| 119 // not retain its target; if the child outlives its parent, zombies get left |
| 120 // behind (http://crbug.com/59619). This will cancel the scheduled task if |
| 121 // the controller (and thus this bridge) get destroyed before the message |
| 122 // can be delivered. |
| 123 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| 124 FROM_HERE, base::Bind(&PageInfoModelBubbleBridge::PerformLayout, |
| 125 weak_ptr_factory_.GetWeakPtr()), |
| 126 base::TimeDelta::FromMilliseconds(1000 /* milliseconds */)); |
| 127 } |
| 128 |
| 129 @interface PageInfoViewController ()<UIGestureRecognizerDelegate> { |
| 130 // Scroll View inside the PageInfoView used to display content that exceeds |
| 131 // the available space. |
| 132 base::scoped_nsobject<UIScrollView> scrollView_; |
| 133 // Container View added inside the Scroll View. All content is added to this |
| 134 // view instead of PopupMenuController.containerView_. |
| 135 base::scoped_nsobject<BidiContainerView> innerContainerView_; |
| 136 |
| 137 // Origin of the arrow at the top of the popup window. |
| 138 CGPoint origin_; |
| 139 |
| 140 // Model for the data to display. |
| 141 std::unique_ptr<PageInfoModel> model_; |
| 142 |
| 143 // Thin bridge that pushes model-changed notifications from C++ to Cocoa. |
| 144 std::unique_ptr<PageInfoModelObserver> bridge_; |
| 145 |
| 146 // Width of the view. Depends on the device (iPad/iPhone). |
| 147 CGFloat viewWidth_; |
| 148 |
| 149 // Width of the text fields. |
| 150 CGFloat textWidth_; |
| 151 |
| 152 // YES when the popup has finished animating in. NO otherwise. |
| 153 BOOL animateInCompleted_; |
| 154 |
| 155 base::mac::ObjCPropertyReleaser propertyReleaser_PageInfoViewController_; |
| 156 } |
| 157 |
| 158 // Adds the state image at a pre-determined x position and the given y. This |
| 159 // does not affect the next Y position because the image is placed next to |
| 160 // a text field that is larger and accounts for the image's size. |
| 161 - (void)addImageViewForInfo:(const PageInfoModel::SectionInfo&)info |
| 162 toSubviews:(NSMutableArray*)subviews |
| 163 atOffset:(CGFloat)offset; |
| 164 |
| 165 // Adds the title text field at the given x,y position, and returns the y |
| 166 // position for the next element. |
| 167 - (CGFloat)addHeadlineViewForInfo:(const PageInfoModel::SectionInfo&)info |
| 168 toSubviews:(NSMutableArray*)subviews |
| 169 atPoint:(CGPoint)point; |
| 170 |
| 171 // Adds the description text field at the given x,y position, and returns the y |
| 172 // position for the next element. |
| 173 - (CGFloat)addDescriptionViewForInfo:(const PageInfoModel::SectionInfo&)info |
| 174 toSubviews:(NSMutableArray*)subviews |
| 175 atPoint:(CGPoint)point; |
| 176 |
| 177 // Returns a button with title and action configured for |buttonAction|. |
| 178 - (UIButton*)buttonForAction:(PageInfoModel::ButtonAction)buttonAction; |
| 179 |
| 180 // Adds the the button |buttonAction| that explains the icons. Returns the y |
| 181 // position delta for the next offset. |
| 182 - (CGFloat)addButton:(PageInfoModel::ButtonAction)buttonAction |
| 183 toSubviews:(NSMutableArray*)subviews |
| 184 atOffset:(CGFloat)offset; |
| 185 |
| 186 @property(nonatomic, retain) UIView* containerView; |
| 187 @property(nonatomic, retain) UIView* popupContainer; |
| 188 @end |
| 189 |
| 190 @implementation PageInfoViewController |
| 191 |
| 192 @synthesize containerView = containerView_; |
| 193 @synthesize popupContainer = popupContainer_; |
| 194 |
| 195 - (id)initWithModel:(PageInfoModel*)model |
| 196 bridge:(PageInfoModelObserver*)bridge |
| 197 sourceFrame:(CGRect)source |
| 198 parentView:(UIView*)parent { |
| 199 DCHECK(parent); |
| 200 self = [super init]; |
| 201 if (self) { |
| 202 propertyReleaser_PageInfoViewController_.Init( |
| 203 self, [PageInfoViewController class]); |
| 204 |
| 205 scrollView_.reset( |
| 206 [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 240, 128)]); |
| 207 [scrollView_ setMultipleTouchEnabled:YES]; |
| 208 [scrollView_ setClipsToBounds:YES]; |
| 209 [scrollView_ setShowsHorizontalScrollIndicator:NO]; |
| 210 [scrollView_ setIndicatorStyle:UIScrollViewIndicatorStyleBlack]; |
| 211 [scrollView_ |
| 212 setAutoresizingMask:(UIViewAutoresizingFlexibleTrailingMargin() | |
| 213 UIViewAutoresizingFlexibleTopMargin)]; |
| 214 |
| 215 innerContainerView_.reset( |
| 216 [[BidiContainerView alloc] initWithFrame:CGRectMake(0, 0, 194, 327)]); |
| 217 [innerContainerView_ setBackgroundColor:[UIColor clearColor]]; |
| 218 [innerContainerView_ |
| 219 setAccessibilityLabel:@"Page Security Info Scroll Container"]; |
| 220 [innerContainerView_ |
| 221 setAutoresizingMask:(UIViewAutoresizingFlexibleTrailingMargin() | |
| 222 UIViewAutoresizingFlexibleBottomMargin)]; |
| 223 |
| 224 model_.reset(model); |
| 225 bridge_.reset(bridge); |
| 226 origin_ = CGPointMake(CGRectGetMidX(source), CGRectGetMaxY(source)); |
| 227 |
| 228 UIInterfaceOrientation orientation = |
| 229 [[UIApplication sharedApplication] statusBarOrientation]; |
| 230 viewWidth_ = IsCompact() ? kViewWidthCompact : kViewWidthRegular; |
| 231 // Special case iPhone landscape. |
| 232 if (!IsIPadIdiom() && UIInterfaceOrientationIsLandscape(orientation)) |
| 233 viewWidth_ = kViewWidthiPhoneLandscape; |
| 234 |
| 235 textWidth_ = viewWidth_ - (kImageSize + kImageSpacing + kFramePadding * 2 + |
| 236 kScrollViewInset * 2); |
| 237 |
| 238 base::scoped_nsobject<UILongPressGestureRecognizer> touchDownRecognizer( |
| 239 [[UILongPressGestureRecognizer alloc] |
| 240 initWithTarget:self |
| 241 action:@selector(rootViewTapped:)]); |
| 242 // Setting the duration to .001 makes this similar to a control event |
| 243 // UIControlEventTouchDown. |
| 244 [touchDownRecognizer setMinimumPressDuration:.001]; |
| 245 [touchDownRecognizer setDelegate:self]; |
| 246 |
| 247 containerView_ = [[UIView alloc] initWithFrame:[parent bounds]]; |
| 248 [containerView_ addGestureRecognizer:touchDownRecognizer]; |
| 249 [containerView_ |
| 250 setBackgroundColor:[UIColor colorWithWhite:0 alpha:kShieldAlpha]]; |
| 251 [containerView_ setTag:IDC_HIDE_PAGE_INFO]; |
| 252 [containerView_ setOpaque:NO]; |
| 253 [containerView_ setAlpha:0]; |
| 254 [containerView_ setAccessibilityViewIsModal:YES]; |
| 255 |
| 256 popupContainer_ = [[UIView alloc] initWithFrame:CGRectZero]; |
| 257 [popupContainer_ setBackgroundColor:[UIColor whiteColor]]; |
| 258 [popupContainer_ setClipsToBounds:YES]; |
| 259 [containerView_ addSubview:popupContainer_]; |
| 260 |
| 261 [self.popupContainer addSubview:scrollView_]; |
| 262 [scrollView_ addSubview:innerContainerView_]; |
| 263 [scrollView_ setAccessibilityIdentifier:@"Page Security Scroll View"]; |
| 264 [parent addSubview:self.containerView]; |
| 265 [self performLayout]; |
| 266 |
| 267 [self animatePageInfoViewIn:source]; |
| 268 UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, |
| 269 containerView_); |
| 270 } |
| 271 |
| 272 return self; |
| 273 } |
| 274 |
| 275 - (void)performLayout { |
| 276 CGFloat offset = kInitialLinePadding; |
| 277 |
| 278 // Keep the new subviews in an array that gets replaced at the end. |
| 279 NSMutableArray* subviews = [NSMutableArray array]; |
| 280 |
| 281 int sectionCount = model_->GetSectionCount(); |
| 282 PageInfoModel::ButtonAction action = PageInfoModel::BUTTON_NONE; |
| 283 |
| 284 for (int i = 0; i < sectionCount; i++) { |
| 285 PageInfoModel::SectionInfo info = model_->GetSectionInfo(i); |
| 286 |
| 287 if (action == PageInfoModel::BUTTON_NONE && |
| 288 info.button != PageInfoModel::BUTTON_NONE) { |
| 289 // Show the button corresponding to the first section that requires a |
| 290 // button. |
| 291 action = info.button; |
| 292 } |
| 293 |
| 294 // Only certain sections have images. This affects the X position. |
| 295 BOOL hasImage = model_->GetIconImage(info.icon_id) != nil; |
| 296 CGFloat xPosition = (hasImage ? kTextXPosition : kTextXPositionNoImage); |
| 297 |
| 298 // Insert the image subview for sections that are appropriate. |
| 299 CGFloat imageBaseline = offset + kImageSize; |
| 300 if (hasImage) { |
| 301 [self addImageViewForInfo:info |
| 302 toSubviews:subviews |
| 303 atOffset:offset + PageInfoImageVerticalOffset()]; |
| 304 } |
| 305 |
| 306 // Add the title. |
| 307 if (!info.headline.empty()) { |
| 308 offset += [self addHeadlineViewForInfo:info |
| 309 toSubviews:subviews |
| 310 atPoint:CGPointMake(xPosition, offset)]; |
| 311 offset += kHeadlineSpacing; |
| 312 } |
| 313 |
| 314 // Create the description of the state. |
| 315 offset += [self addDescriptionViewForInfo:info |
| 316 toSubviews:subviews |
| 317 atPoint:CGPointMake(xPosition, offset)]; |
| 318 |
| 319 // If at this point the description and optional headline and button are |
| 320 // not as tall as the image, adjust the offset by the difference. |
| 321 CGFloat imageBaselineDelta = imageBaseline - offset; |
| 322 if (imageBaselineDelta > 0) |
| 323 offset += imageBaselineDelta; |
| 324 |
| 325 // Add the separators. |
| 326 int testSectionCount = sectionCount - 1; |
| 327 if (i != testSectionCount || |
| 328 (i == testSectionCount && action != PageInfoModel::BUTTON_NONE)) { |
| 329 offset += kVerticalSpacing; |
| 330 } |
| 331 } |
| 332 |
| 333 // The last item at the bottom of the window is the help center link. Do not |
| 334 // show this for the internal pages, which have one section. |
| 335 offset += [self addButton:action toSubviews:subviews atOffset:offset]; |
| 336 |
| 337 // Add the bottom padding. |
| 338 offset += kVerticalSpacing; |
| 339 CGRect frame = |
| 340 CGRectMake(kInitialFramePosition, origin_.y, viewWidth_, offset); |
| 341 |
| 342 // Increase the size of the frame by the amount used for drawing rounded |
| 343 // corners and shadow. |
| 344 frame.size.height += kPageInfoViewFooterSize; |
| 345 |
| 346 if (CGRectGetMaxY(frame) > |
| 347 CGRectGetMaxY([[self containerView] superview].bounds) - |
| 348 kFrameBottomPadding) { |
| 349 // If the frame is bigger than the parent view than change the frame to |
| 350 // fit in the superview bounds. |
| 351 frame.size.height = [[self containerView] superview].bounds.size.height - |
| 352 kFrameBottomPadding - frame.origin.y; |
| 353 |
| 354 [scrollView_ setScrollEnabled:YES]; |
| 355 [scrollView_ flashScrollIndicators]; |
| 356 } else { |
| 357 [scrollView_ setScrollEnabled:NO]; |
| 358 } |
| 359 |
| 360 CGRect containerBounds = [containerView_ bounds]; |
| 361 CGRect popupFrame = frame; |
| 362 popupFrame.origin.x = |
| 363 CGRectGetMidX(containerBounds) - CGRectGetWidth(popupFrame) / 2.0; |
| 364 popupFrame.origin.y = |
| 365 CGRectGetMidY(containerBounds) - CGRectGetHeight(popupFrame) / 2.0; |
| 366 |
| 367 popupFrame.origin = AlignPointToPixel(popupFrame.origin); |
| 368 CGRect innerFrame = CGRectMake(0, 0, popupFrame.size.width, offset); |
| 369 |
| 370 // If the initial animation has completed, animate the new frames. |
| 371 if (animateInCompleted_) { |
| 372 [UIView cr_animateWithDuration:ios::material::kDuration3 |
| 373 delay:0 |
| 374 curve:ios::material::CurveEaseInOut |
| 375 options:0 |
| 376 animations:^{ |
| 377 [popupContainer_ setFrame:popupFrame]; |
| 378 [scrollView_ setFrame:[popupContainer_ bounds]]; |
| 379 [innerContainerView_ setFrame:innerFrame]; |
| 380 } |
| 381 completion:nil]; |
| 382 } else { |
| 383 // Popup hasn't finished animating in yet. Set frames immediately. |
| 384 [popupContainer_ setFrame:popupFrame]; |
| 385 [scrollView_ setFrame:[popupContainer_ bounds]]; |
| 386 [innerContainerView_ setFrame:innerFrame]; |
| 387 } |
| 388 |
| 389 for (UIView* view in [innerContainerView_ subviews]) { |
| 390 [view removeFromSuperview]; |
| 391 } |
| 392 |
| 393 for (UIView* view in subviews) { |
| 394 [innerContainerView_ addSubview:view]; |
| 395 [innerContainerView_ setSubviewNeedsAdjustmentForRTL:view]; |
| 396 } |
| 397 |
| 398 [scrollView_ setContentSize:innerContainerView_.get().frame.size]; |
| 399 } |
| 400 |
| 401 - (void)dismiss { |
| 402 [self animatePageInfoViewOut]; |
| 403 UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, |
| 404 nil); |
| 405 } |
| 406 |
| 407 #pragma mark - Helper methods to create subviews. |
| 408 |
| 409 - (void)addImageViewForInfo:(const PageInfoModel::SectionInfo&)info |
| 410 toSubviews:(NSMutableArray*)subviews |
| 411 atOffset:(CGFloat)offset { |
| 412 CGRect frame = CGRectMake(kFramePadding, offset, kImageSize, kImageSize); |
| 413 base::scoped_nsobject<UIImageView> imageView( |
| 414 [[UIImageView alloc] initWithFrame:frame]); |
| 415 [imageView setImage:model_->GetIconImage(info.icon_id)->ToUIImage()]; |
| 416 [subviews addObject:imageView.get()]; |
| 417 } |
| 418 |
| 419 - (CGFloat)addHeadlineViewForInfo:(const PageInfoModel::SectionInfo&)info |
| 420 toSubviews:(NSMutableArray*)subviews |
| 421 atPoint:(CGPoint)point { |
| 422 CGRect frame = CGRectMake(point.x, point.y, textWidth_, kHeadlineHeight); |
| 423 base::scoped_nsobject<UILabel> label([[UILabel alloc] initWithFrame:frame]); |
| 424 [label setTextAlignment:NSTextAlignmentNatural]; |
| 425 [label setText:base::SysUTF16ToNSString(info.headline)]; |
| 426 [label setTextColor:PageInfoTextColor()]; |
| 427 [label setFont:PageInfoHeadlineFont()]; |
| 428 [label setBackgroundColor:[UIColor clearColor]]; |
| 429 [label setFrame:frame]; |
| 430 [label setLineBreakMode:NSLineBreakByTruncatingHead]; |
| 431 [subviews addObject:label.get()]; |
| 432 return CGRectGetHeight(frame); |
| 433 } |
| 434 |
| 435 - (CGFloat)addDescriptionViewForInfo:(const PageInfoModel::SectionInfo&)info |
| 436 toSubviews:(NSMutableArray*)subviews |
| 437 atPoint:(CGPoint)point { |
| 438 CGRect frame = CGRectMake(point.x, point.y, textWidth_, kImageSize); |
| 439 base::scoped_nsobject<UILabel> label([[UILabel alloc] initWithFrame:frame]); |
| 440 [label setTextAlignment:NSTextAlignmentNatural]; |
| 441 NSString* description = base::SysUTF16ToNSString(info.description); |
| 442 UIFont* font = [MDCTypography captionFont]; |
| 443 [label setTextColor:PageInfoTextColor()]; |
| 444 [label setText:description]; |
| 445 [label setFont:font]; |
| 446 [label setNumberOfLines:0]; |
| 447 [label setBackgroundColor:[UIColor clearColor]]; |
| 448 |
| 449 // If the text is oversized, resize the text field. |
| 450 CGSize constraintSize = CGSizeMake(textWidth_, CGFLOAT_MAX); |
| 451 CGSize sizeToFit = |
| 452 [description cr_boundingSizeWithSize:constraintSize font:font]; |
| 453 frame.size.height = sizeToFit.height; |
| 454 [label setFrame:frame]; |
| 455 [subviews addObject:label.get()]; |
| 456 return CGRectGetHeight(frame); |
| 457 } |
| 458 |
| 459 - (UIButton*)buttonForAction:(PageInfoModel::ButtonAction)buttonAction { |
| 460 if (buttonAction == PageInfoModel::BUTTON_NONE) { |
| 461 return nil; |
| 462 } |
| 463 UIButton* button = [[[UIButton alloc] initWithFrame:CGRectZero] autorelease]; |
| 464 int messageId = IDS_IOS_PAGE_INFO_RELOAD; |
| 465 NSInteger tag = IDC_RELOAD; |
| 466 NSString* accessibilityID = @"Reload button"; |
| 467 switch (buttonAction) { |
| 468 case PageInfoModel::BUTTON_NONE: |
| 469 NOTREACHED(); |
| 470 return nil; |
| 471 case PageInfoModel::BUTTON_SHOW_SECURITY_HELP: |
| 472 messageId = IDS_PAGE_INFO_HELP_CENTER_LINK; |
| 473 tag = IDC_SHOW_SECURITY_HELP; |
| 474 accessibilityID = @"What do these mean?"; |
| 475 break; |
| 476 case PageInfoModel::BUTTON_RELOAD: |
| 477 messageId = IDS_IOS_PAGE_INFO_RELOAD; |
| 478 tag = IDC_RELOAD; |
| 479 accessibilityID = @"Reload button"; |
| 480 [button addTarget:self |
| 481 action:@selector(dismiss) |
| 482 forControlEvents:UIControlEventTouchUpInside]; |
| 483 break; |
| 484 }; |
| 485 |
| 486 NSString* title = l10n_util::GetNSStringWithFixup(messageId); |
| 487 SetA11yLabelAndUiAutomationName(button, messageId, accessibilityID); |
| 488 [button setTitle:title forState:UIControlStateNormal]; |
| 489 [button setTag:tag]; |
| 490 [button addTarget:nil |
| 491 action:@selector(chromeExecuteCommand:) |
| 492 forControlEvents:UIControlEventTouchUpInside]; |
| 493 return button; |
| 494 } |
| 495 |
| 496 - (CGFloat)addButton:(PageInfoModel::ButtonAction)buttonAction |
| 497 toSubviews:(NSMutableArray*)subviews |
| 498 atOffset:(CGFloat)offset { |
| 499 UIButton* button = [self buttonForAction:buttonAction]; |
| 500 if (!button) { |
| 501 return 0; |
| 502 } |
| 503 // The size of the initial frame is irrelevant since it will be changed based |
| 504 // on the size for the string inside. |
| 505 CGRect frame = CGRectMake(kButtonXOffset, offset, 100, 10); |
| 506 |
| 507 UIFont* font = [MDCTypography captionFont]; |
| 508 CGSize sizeWithFont = |
| 509 [[[button titleLabel] text] cr_pixelAlignedSizeWithFont:font]; |
| 510 frame.size = sizeWithFont; |
| 511 |
| 512 [button setFrame:frame]; |
| 513 |
| 514 [button.titleLabel setFont:font]; |
| 515 [button.titleLabel setTextAlignment:NSTextAlignmentLeft]; |
| 516 [button setTitleColor:PageInfoHelpButtonColor() |
| 517 forState:UIControlStateNormal]; |
| 518 [button setTitleColor:PageInfoHelpButtonColor() |
| 519 forState:UIControlStateSelected]; |
| 520 [button setBackgroundColor:[UIColor clearColor]]; |
| 521 |
| 522 [subviews addObject:button]; |
| 523 |
| 524 return CGRectGetHeight([button frame]); |
| 525 } |
| 526 |
| 527 #pragma mark - UIGestureRecognizerDelegate Implemenation |
| 528 |
| 529 - (void)rootViewTapped:(UIGestureRecognizer*)sender { |
| 530 CGPoint pt = [sender locationInView:containerView_]; |
| 531 if (!CGRectContainsPoint([popupContainer_ frame], pt)) { |
| 532 [containerView_ chromeExecuteCommand:containerView_]; |
| 533 } |
| 534 } |
| 535 |
| 536 - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer |
| 537 shouldReceiveTouch:(UITouch*)touch { |
| 538 CGPoint pt = [touch locationInView:containerView_]; |
| 539 return !CGRectContainsPoint([popupContainer_ frame], pt); |
| 540 } |
| 541 |
| 542 - (void)animatePageInfoViewIn:(CGRect)source { |
| 543 // Animate the info card itself. |
| 544 CATransform3D scaleTransform = PageInfoAnimationScale(); |
| 545 CGPoint fromPoint = CGPointMake(CGRectGetMidX(source), CGRectGetMidY(source)); |
| 546 |
| 547 CABasicAnimation* scaleAnimation = |
| 548 [CABasicAnimation animationWithKeyPath:@"transform"]; |
| 549 [scaleAnimation setFromValue:[NSValue valueWithCATransform3D:scaleTransform]]; |
| 550 |
| 551 CABasicAnimation* positionAnimation = |
| 552 [CABasicAnimation animationWithKeyPath:@"position"]; |
| 553 [positionAnimation setFromValue:[NSValue valueWithCGPoint:fromPoint]]; |
| 554 |
| 555 CAAnimationGroup* sizeAnimation = [CAAnimationGroup animation]; |
| 556 [sizeAnimation setAnimations:@[ scaleAnimation, positionAnimation ]]; |
| 557 [sizeAnimation |
| 558 setTimingFunction:TimingFunction(ios::material::CurveEaseInOut)]; |
| 559 [sizeAnimation setDuration:ios::material::kDuration3]; |
| 560 |
| 561 CABasicAnimation* fadeAnimation = |
| 562 [CABasicAnimation animationWithKeyPath:@"opacity"]; |
| 563 [fadeAnimation setTimingFunction:TimingFunction(ios::material::CurveEaseOut)]; |
| 564 [fadeAnimation setDuration:ios::material::kDuration6]; |
| 565 [fadeAnimation setFromValue:@0]; |
| 566 [fadeAnimation setToValue:@1]; |
| 567 |
| 568 [[popupContainer_ layer] addAnimation:fadeAnimation forKey:@"fade"]; |
| 569 [[popupContainer_ layer] addAnimation:sizeAnimation forKey:@"size"]; |
| 570 |
| 571 // Animation the background grey overlay. |
| 572 CABasicAnimation* overlayAnimation = |
| 573 [CABasicAnimation animationWithKeyPath:@"opacity"]; |
| 574 [overlayAnimation |
| 575 setTimingFunction:TimingFunction(ios::material::CurveEaseOut)]; |
| 576 [overlayAnimation setDuration:ios::material::kDuration3]; |
| 577 [overlayAnimation setFromValue:@0]; |
| 578 [overlayAnimation setToValue:@1]; |
| 579 |
| 580 [[containerView_ layer] addAnimation:overlayAnimation forKey:@"fade"]; |
| 581 [containerView_ setAlpha:1]; |
| 582 |
| 583 // Animate the contents of the info card. |
| 584 CALayer* contentsLayer = [innerContainerView_ layer]; |
| 585 |
| 586 CGRect startFrame = CGRectOffset([innerContainerView_ frame], 0, -32); |
| 587 CAAnimation* contentSlideAnimation = FrameAnimationMake( |
| 588 contentsLayer, startFrame, [innerContainerView_ frame]); |
| 589 [contentSlideAnimation |
| 590 setTimingFunction:TimingFunction(ios::material::CurveEaseOut)]; |
| 591 [contentSlideAnimation setDuration:ios::material::kDuration5]; |
| 592 contentSlideAnimation = |
| 593 DelayedAnimationMake(contentSlideAnimation, ios::material::kDuration2); |
| 594 [contentsLayer addAnimation:contentSlideAnimation forKey:@"slide"]; |
| 595 |
| 596 [CATransaction begin]; |
| 597 [CATransaction setCompletionBlock:^{ |
| 598 [innerContainerView_ setAlpha:1]; |
| 599 animateInCompleted_ = YES; |
| 600 }]; |
| 601 CAAnimation* contentFadeAnimation = OpacityAnimationMake(0.0, 1.0); |
| 602 [contentFadeAnimation |
| 603 setTimingFunction:TimingFunction(ios::material::CurveEaseOut)]; |
| 604 [contentFadeAnimation setDuration:ios::material::kDuration5]; |
| 605 contentFadeAnimation = |
| 606 DelayedAnimationMake(contentFadeAnimation, ios::material::kDuration1); |
| 607 [contentsLayer addAnimation:contentFadeAnimation forKey:@"fade"]; |
| 608 [CATransaction commit]; |
| 609 |
| 610 // Since the animations have delay on them, the alpha of the content view |
| 611 // needs to be set to zero and then one after the animation starts. If these |
| 612 // steps are not taken, there will be a visible flash/jump from the initial |
| 613 // spot during the animation. |
| 614 [innerContainerView_ setAlpha:0]; |
| 615 } |
| 616 |
| 617 - (void)animatePageInfoViewOut { |
| 618 [CATransaction begin]; |
| 619 [CATransaction setCompletionBlock:^{ |
| 620 [self.containerView removeFromSuperview]; |
| 621 }]; |
| 622 |
| 623 CABasicAnimation* opacityAnimation = |
| 624 [CABasicAnimation animationWithKeyPath:@"opacity"]; |
| 625 [opacityAnimation |
| 626 setTimingFunction:TimingFunction(ios::material::CurveEaseIn)]; |
| 627 [opacityAnimation setDuration:ios::material::kDuration3]; |
| 628 [opacityAnimation setFromValue:@1]; |
| 629 [opacityAnimation setToValue:@0]; |
| 630 [[containerView_ layer] addAnimation:opacityAnimation forKey:@"animateOut"]; |
| 631 |
| 632 [popupContainer_ setAlpha:0]; |
| 633 [containerView_ setAlpha:0]; |
| 634 [CATransaction commit]; |
| 635 } |
| 636 |
| 637 @end |
OLD | NEW |