chromium/ios/chrome/browser/shared/ui/table_view/cells/table_view_text_button_item.mm

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/shared/ui/table_view/cells/table_view_text_button_item.h"

#import "base/apple/foundation_util.h"
#import "base/ios/ios_util.h"
#import "ios/chrome/browser/shared/public/features/features.h"
#import "ios/chrome/browser/shared/ui/symbols/symbols.h"
#import "ios/chrome/browser/shared/ui/table_view/legacy_chrome_table_view_styler.h"
#import "ios/chrome/browser/shared/ui/util/uikit_ui_util.h"
#import "ios/chrome/common/ui/colors/semantic_color_names.h"
#import "ios/chrome/common/ui/table_view/table_view_cells_constants.h"
#import "ios/chrome/common/ui/util/pointer_interaction_util.h"

namespace {
// Alpha value for the disabled action button.
const CGFloat kDisabledButtonAlpha = 0.5;
// Vertical spacing between stackView and cell contentView.
const CGFloat kStackViewVerticalSpacing = 9.0;
// Horizontal spacing between stackView and cell contentView.
const CGFloat kStackViewHorizontalSpacing = 16.0;
// SubView spacing within stackView.
const CGFloat kStackViewSubViewSpacing = 13.0;
// Horizontal Inset between button contents and edge.
const CGFloat kButtonTitleHorizontalContentInset = 40.0;
// Vertical Inset between button contents and edge.
const CGFloat kButtonTitleVerticalContentInset = 8.0;
// Button corner radius.
const CGFloat kButtonCornerRadius = 8;
// The size of the checkmark symbol in the confirmation state on the
// item's button.
const CGFloat kSymbolConfirmationCheckmarkPointSize = 22;
// Default Text alignment.
const NSTextAlignment kDefaultTextAlignment = NSTextAlignmentCenter;
}  // namespace

@implementation TableViewTextButtonItem
@synthesize buttonAccessibilityIdentifier = _buttonAccessibilityIdentifier;
@synthesize buttonBackgroundColor = _buttonBackgroundColor;
@synthesize buttonText = _buttonText;
@synthesize text = _text;

- (instancetype)initWithType:(NSInteger)type {
  self = [super initWithType:type];
  if (self) {
    self.cellClass = [TableViewTextButtonCell class];
    _enabled = YES;
    _textAlignment = kDefaultTextAlignment;
    _boldButtonText = YES;
    _dimBackgroundWhenDisabled = YES;
    _showsActivityIndicator = NO;
    _showsCheckmark = NO;
  }
  return self;
}

- (void)configureCell:(TableViewCell*)tableCell
           withStyler:(ChromeTableViewStyler*)styler {
  [super configureCell:tableCell withStyler:styler];
  TableViewTextButtonCell* cell =
      base::apple::ObjCCastStrict<TableViewTextButtonCell>(tableCell);
  [cell setSelectionStyle:UITableViewCellSelectionStyleNone];

  cell.textLabel.text = self.text;
  // Decide cell.textLabel.textColor in order:
  //   1. styler.cellTitleColor
  //   2. [UIColor colorNamed:kTextSecondaryColor]
  if (styler.cellTitleColor) {
    cell.textLabel.textColor = styler.cellTitleColor;
  } else {
    cell.textLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];
  }
  [cell enableItemSpacing:[self.text length]];
  [cell disableButtonIntrinsicWidth:self.disableButtonIntrinsicWidth];
  cell.textLabel.textAlignment = self.textAlignment;

  UIButtonConfiguration* buttonConfiguration = cell.button.configuration;
  UIFont* font = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
  NSDictionary* attributes = @{NSFontAttributeName : font};
  NSMutableAttributedString* attributedString =
      [[NSMutableAttributedString alloc] initWithString:self.buttonText
                                             attributes:attributes];
  buttonConfiguration.attributedTitle = attributedString;

  // Decide cell.button titleColor in order:
  //   1. self.buttonTextColor;
  //   2. [UIColor colorNamed:kSolidButtonTextColor]
  if (self.buttonTextColor) {
    buttonConfiguration.baseForegroundColor = self.buttonTextColor;
  } else {
    buttonConfiguration.baseForegroundColor =
        [UIColor colorNamed:kSolidButtonTextColor];
  }

  // Decide cell.button.backgroundColor in order:
  //   1. self.buttonBackgroundColor
  //   2. [UIColor colorNamed:kBlueColor]
  if (self.buttonBackgroundColor) {
    buttonConfiguration.background.backgroundColor = self.buttonBackgroundColor;
  } else {
    buttonConfiguration.background.backgroundColor =
        [UIColor colorNamed:kBlueColor];
  }

  if (!self.enabled && self.dimBackgroundWhenDisabled) {
    buttonConfiguration.background.backgroundColor =
        [buttonConfiguration.background.backgroundColor
            colorWithAlphaComponent:kDisabledButtonAlpha];
  }

  buttonConfiguration.showsActivityIndicator = self.showsActivityIndicator;
  if (self.showsActivityIndicator) {
    __weak __typeof(self) weakSelf = self;
    buttonConfiguration.activityIndicatorColorTransformer =
        ^UIColor*(UIColor* color) {
          return weakSelf.activityIndicatorColor
                     ? weakSelf.activityIndicatorColor
                     : [UIColor colorNamed:kSolidWhiteColor];
        };
  }

  if (self.showsCheckmark) {
    buttonConfiguration.image = DefaultSymbolWithPointSize(
        kCheckmarkCircleFillSymbol, kSymbolConfirmationCheckmarkPointSize);

    __weak __typeof(self) weakSelf = self;
    buttonConfiguration.imageColorTransformer = ^UIColor*(UIColor* color) {
      return weakSelf.checkmarkColor ? weakSelf.checkmarkColor
                                     : [UIColor colorNamed:kBlue700Color];
    };
  }

  if (self.buttonAccessibilityLabel) {
    cell.button.accessibilityLabel = self.buttonAccessibilityLabel;
  } else {
    cell.button.accessibilityLabel = nil;
  }

  cell.button.configuration = buttonConfiguration;

  [cell disableButtonIntrinsicWidth:self.disableButtonIntrinsicWidth];
  cell.button.accessibilityIdentifier = self.buttonAccessibilityIdentifier;
  cell.button.enabled = self.enabled;
}

@end

@interface TableViewTextButtonCell ()
// StackView that contains the cell's Button and Label.
@property(nonatomic, strong) UIStackView* verticalStackView;
// Constraints used to match the Button width to the StackView.
@property(nonatomic, strong) NSArray* expandedButtonWidthConstraints;
@end

@implementation TableViewTextButtonCell
@synthesize textLabel = _textLabel;
@synthesize button = _button;

- (instancetype)initWithStyle:(UITableViewCellStyle)style
              reuseIdentifier:(NSString*)reuseIdentifier {
  self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  if (self) {
    // Create informative text label.
    self.textLabel = [[UILabel alloc] init];
    self.textLabel.numberOfLines = 0;
    self.textLabel.lineBreakMode = NSLineBreakByWordWrapping;
    self.textLabel.textAlignment = NSTextAlignmentCenter;
    self.textLabel.font =
        [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote];
    self.textLabel.textColor = [UIColor colorNamed:kTextSecondaryColor];

    // Create button.
    self.button = [UIButton buttonWithType:UIButtonTypeSystem];
    self.button.translatesAutoresizingMaskIntoConstraints = NO;
    self.button.layer.cornerRadius = kButtonCornerRadius;
    self.button.clipsToBounds = YES;

    UIButtonConfiguration* buttonConfiguration =
        [UIButtonConfiguration plainButtonConfiguration];
    buttonConfiguration.contentInsets = NSDirectionalEdgeInsetsMake(
        kButtonTitleVerticalContentInset, kButtonTitleHorizontalContentInset,
        kButtonTitleVerticalContentInset, kButtonTitleHorizontalContentInset);
    buttonConfiguration.titleLineBreakMode = NSLineBreakByWordWrapping;
    buttonConfiguration.titleAlignment =
        UIButtonConfigurationTitleAlignmentCenter;
    self.button.configuration = buttonConfiguration;

    self.button.pointerInteractionEnabled = YES;
    // This button's background color is configured whenever the cell is
    // reused. The pointer style provider used here dynamically provides the
    // appropriate style based on the background color at runtime.
    self.button.pointerStyleProvider =
        CreateOpaqueOrTransparentButtonPointerStyleProvider();

    // Vertical stackView to hold label and button.
    self.verticalStackView = [[UIStackView alloc]
        initWithArrangedSubviews:@[ self.textLabel, self.button ]];
    self.verticalStackView.alignment = UIStackViewAlignmentCenter;
    self.verticalStackView.axis = UILayoutConstraintAxisVertical;
    self.verticalStackView.translatesAutoresizingMaskIntoConstraints = NO;

    [self.contentView addSubview:self.verticalStackView];

    // Add constraints for stackView
    [NSLayoutConstraint activateConstraints:@[
      [self.verticalStackView.leadingAnchor
          constraintEqualToAnchor:self.contentView.leadingAnchor
                         constant:kStackViewHorizontalSpacing],
      [self.verticalStackView.trailingAnchor
          constraintEqualToAnchor:self.contentView.trailingAnchor
                         constant:-kStackViewHorizontalSpacing],
      [self.verticalStackView.topAnchor
          constraintEqualToAnchor:self.contentView.topAnchor
                         constant:kStackViewVerticalSpacing],
      [self.verticalStackView.bottomAnchor
          constraintEqualToAnchor:self.contentView.bottomAnchor
                         constant:-kStackViewVerticalSpacing]
    ]];

    self.expandedButtonWidthConstraints = @[
      [self.button.leadingAnchor
          constraintEqualToAnchor:self.verticalStackView.leadingAnchor],
      [self.button.trailingAnchor
          constraintEqualToAnchor:self.verticalStackView.trailingAnchor],
    ];
  }
  return self;
}

#pragma mark - Public Methods

- (void)enableItemSpacing:(BOOL)enable {
  self.verticalStackView.spacing = enable ? kStackViewSubViewSpacing : 0;
}

- (void)disableButtonIntrinsicWidth:(BOOL)disable {
  if (disable) {
    [NSLayoutConstraint
        activateConstraints:self.expandedButtonWidthConstraints];
  } else {
    [NSLayoutConstraint
        deactivateConstraints:self.expandedButtonWidthConstraints];
  }
}

#pragma mark - UITableViewCell

- (void)prepareForReuse {
  [super prepareForReuse];
  UIButtonConfiguration* buttonConfiguration = self.button.configuration;
  buttonConfiguration.baseForegroundColor =
      [UIColor colorNamed:kSolidButtonTextColor];
  buttonConfiguration.titleAlignment =
      UIButtonConfigurationTitleAlignmentCenter;
  self.button.configuration = buttonConfiguration;
  [self disableButtonIntrinsicWidth:NO];
}

@end