Index: ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm |
diff --git a/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm b/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ba66ba0a4da365c6ecde2cc658e21ce04eb0717e |
--- /dev/null |
+++ b/ios/chrome/browser/ui/alert_coordinator/loading_alert_coordinator.mm |
@@ -0,0 +1,237 @@ |
+// Copyright 2016 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/alert_coordinator/loading_alert_coordinator.h" |
+ |
+#import <UIKit/UIKit.h> |
+ |
+#include "base/ios/block_types.h" |
+#import "base/ios/weak_nsobject.h" |
+#import "base/mac/scoped_block.h" |
+#import "base/mac/scoped_nsobject.h" |
+#include "components/strings/grit/components_strings.h" |
+#import "ios/chrome/browser/ui/material_components/activity_indicator.h" |
+#import "ios/chrome/browser/ui/uikit_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/Dialogs/src/MaterialDialogs.h" |
+#import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h" |
+#include "ui/base/l10n/l10n_util_mac.h" |
+ |
+namespace { |
+ |
+// MDC constraints. |
+const CGFloat kMDCPadding = 24.0; |
+const CGFloat kMDCMargin = 40.0; |
+const CGFloat kButtonPadding = 8.0; |
+ |
+// Size of the activity indicator. |
+const CGFloat kActivityIndicatorPadding = 50.0; |
+ |
+// Constant for the title |
+const int kTitleLabelFontColor = 0x333333; |
+const int kTitleLabelFontSize = 16; |
+ |
+// Maximum size of the dialog |
+const CGFloat kPrefWidth = 330; |
+const CGFloat kPrefHeight = 300; |
+ |
+} // namespace |
+ |
+@interface LoadingAlertCoordinator () { |
+ // Title of the alert. |
+ base::scoped_nsobject<NSString> _title; |
+ // Callback for the cancel button. |
+ base::mac::ScopedBlock<ProceduralBlock> _cancelHandler; |
+ // View Controller which will be displayed on |baseViewController|. |
+ base::scoped_nsobject<UIViewController> _presentedViewController; |
+} |
+ |
+// Callback called when the cancel button is pressed. |
+- (void)cancelCallback; |
+ |
+@end |
+ |
+// View Controller handling the layout of the dialog. |
+@interface LoadingViewController : UIViewController { |
+ // Title of the dialog. |
+ base::scoped_nsobject<NSString> _title; |
+ // View containing the elements of the dialog. |
+ base::scoped_nsobject<UIView> _contentView; |
+ // Coordinator used for the cancel callback. |
+ base::WeakNSObject<LoadingAlertCoordinator> _coordinator; |
+ // Transitioning delegate for this ViewController. |
+ base::scoped_nsobject<MDCDialogTransitionController> _transitionDelegate; |
+} |
+ |
+// Initializes with the |title| of the dialog and the |coordinator| which will |
+// be notified if the cancel callback occurs. |
+- (instancetype)initWithTitle:(NSString*)title |
+ coordinator:(LoadingAlertCoordinator*)coordinator; |
+ |
+// Returns the maximum possible width for the dialog, taking into account the |
+// MDC constraints. |
+- (CGFloat)maxDialogWidth; |
+// Callback for the cancel button, calling the coordinator's callback. |
+- (void)cancelCallback; |
+ |
+@end |
+ |
+@implementation LoadingViewController |
+ |
+- (instancetype)initWithTitle:(NSString*)title |
+ coordinator:(LoadingAlertCoordinator*)coordinator { |
+ self = [super initWithNibName:nil bundle:nil]; |
+ if (self) { |
+ _title.reset([title copy]); |
+ _coordinator.reset(coordinator); |
+ self.modalPresentationStyle = UIModalPresentationCustom; |
+ _transitionDelegate.reset([[MDCDialogTransitionController alloc] init]); |
+ self.transitioningDelegate = _transitionDelegate; |
+ } |
+ return self; |
+} |
+ |
+- (void)viewDidLoad { |
+ [super viewDidLoad]; |
+ |
+ self.preferredContentSize = CGSizeMake([self maxDialogWidth], kPrefHeight); |
+ self.view.backgroundColor = [UIColor whiteColor]; |
+ |
+ // Cancel button. |
+ NSString* cancelTitle = l10n_util::GetNSString(IDS_CANCEL); |
+ MDCFlatButton* cancelButton = [[[MDCFlatButton alloc] init] autorelease]; |
+ [cancelButton sizeToFit]; |
+ [cancelButton setCustomTitleColor:[UIColor blackColor]]; |
+ [cancelButton setTitle:cancelTitle forState:UIControlStateNormal]; |
+ [cancelButton addTarget:self |
+ action:@selector(cancelCallback) |
+ forControlEvents:UIControlEventTouchUpInside]; |
+ |
+ // Activity indicator. |
+ base::scoped_nsobject<MDCActivityIndicator> activityIndicator( |
+ [[MDCActivityIndicator alloc] initWithFrame:CGRectZero]); |
+ [activityIndicator setCycleColors:ActivityIndicatorBrandedCycleColors()]; |
+ [activityIndicator startAnimating]; |
+ |
+ // Title. |
+ NSMutableDictionary* attrsDictionary = [NSMutableDictionary |
+ dictionaryWithObject:[[MDFRobotoFontLoader sharedInstance] |
+ mediumFontOfSize:kTitleLabelFontSize] |
+ forKey:NSFontAttributeName]; |
+ [attrsDictionary setObject:UIColorFromRGB(kTitleLabelFontColor) |
+ forKey:NSForegroundColorAttributeName]; |
+ |
+ NSMutableAttributedString* string = [[[NSMutableAttributedString alloc] |
+ initWithString:_title |
+ attributes:attrsDictionary] autorelease]; |
+ |
+ UILabel* title = [[[UILabel alloc] initWithFrame:CGRectZero] autorelease]; |
+ title.attributedText = string; |
+ |
+ // Content view. |
+ _contentView.reset([[UIView alloc] initWithFrame:CGRectZero]); |
+ |
+ // Constraints. |
+ [activityIndicator setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [cancelButton setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [title setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_contentView setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ |
+ [_contentView addSubview:title]; |
+ [_contentView addSubview:activityIndicator]; |
+ [_contentView addSubview:cancelButton]; |
+ |
+ [[self view] addSubview:_contentView]; |
+ |
+ NSDictionary* viewsDictionary = @{ |
+ @"spinner" : activityIndicator, |
+ @"cancel" : cancelButton, |
+ @"title" : title, |
+ @"contentView" : _contentView.get() |
+ }; |
+ NSDictionary* metrics = @{ |
+ @"padding" : @(kMDCPadding), |
+ @"buttonPadding" : @(kButtonPadding), |
+ @"spinnerButtonPadding" : @(kButtonPadding + kMDCPadding), |
+ @"spinnerPadding" : @(kActivityIndicatorPadding) |
+ }; |
+ |
+ NSString* verticalConstaint = @"V:|-(padding)-[title]-(spinnerPadding)-[" |
+ @"spinner]-(spinnerButtonPadding)-[cancel]-(" |
+ @"buttonPadding)-|"; |
+ |
+ NSArray* constraints = @[ |
+ verticalConstaint, @"H:|-(>=padding)-[title]-(>=padding)-|", |
+ @"H:|-[spinner]-|", @"H:[cancel]-(buttonPadding)-|", @"V:|[contentView]", |
+ @"H:|[contentView]|" |
+ ]; |
+ ApplyVisualConstraintsWithMetricsAndOptions(constraints, viewsDictionary, |
+ metrics, 0); |
+ |
+ [_contentView addConstraint:[NSLayoutConstraint |
+ constraintWithItem:title |
+ attribute:NSLayoutAttributeCenterX |
+ relatedBy:NSLayoutRelationEqual |
+ toItem:_contentView |
+ attribute:NSLayoutAttributeCenterX |
+ multiplier:1.f |
+ constant:0.f]]; |
+} |
+ |
+- (CGFloat)maxDialogWidth { |
+ CGFloat screenWidth = [[UIScreen mainScreen] bounds].size.width; |
+ return MIN(kPrefWidth, screenWidth - 2 * kMDCMargin); |
+} |
+ |
+- (void)viewDidLayoutSubviews { |
+ CGSize currentSize = |
+ CGSizeMake([self maxDialogWidth], [_contentView frame].size.height); |
+ self.preferredContentSize = currentSize; |
+} |
+ |
+- (void)cancelCallback { |
+ [_coordinator cancelCallback]; |
+} |
+@end |
+ |
+@implementation LoadingAlertCoordinator |
+ |
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController { |
+ NOTREACHED(); |
+ return nil; |
+} |
+ |
+- (instancetype)initWithBaseViewController:(UIViewController*)viewController |
+ title:(NSString*)title |
+ cancelHandler:(ProceduralBlock)cancelHandler { |
+ self = [super initWithBaseViewController:viewController]; |
+ if (self) { |
+ _title.reset([title copy]); |
+ _cancelHandler.reset(cancelHandler, base::scoped_policy::RETAIN); |
+ } |
+ return self; |
+} |
+ |
+- (void)start { |
+ _presentedViewController.reset( |
+ [[LoadingViewController alloc] initWithTitle:_title coordinator:self]); |
+ [self.baseViewController presentViewController:_presentedViewController |
+ animated:YES |
+ completion:nil]; |
+} |
+ |
+- (void)stop { |
+ [_presentedViewController dismissViewControllerAnimated:NO completion:nil]; |
+ _presentedViewController.reset(); |
+ _cancelHandler.reset(); |
+} |
+ |
+- (void)cancelCallback { |
+ if (_cancelHandler) |
+ _cancelHandler.get()(); |
+ [self stop]; |
+} |
+ |
+@end |