| Index: ios/chrome/browser/ui/bookmarks/bookmark_collection_cells.mm
|
| diff --git a/ios/chrome/browser/ui/bookmarks/bookmark_collection_cells.mm b/ios/chrome/browser/ui/bookmarks/bookmark_collection_cells.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9d7be019d491fa4bba3181fba799d738a9284362
|
| --- /dev/null
|
| +++ b/ios/chrome/browser/ui/bookmarks/bookmark_collection_cells.mm
|
| @@ -0,0 +1,550 @@
|
| +// Copyright 2014 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/bookmarks/bookmark_collection_cells.h"
|
| +
|
| +#import <QuartzCore/QuartzCore.h>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/mac/objc_property_releaser.h"
|
| +#include "base/mac/scoped_nsobject.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_extended_button.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_home_view_controller.h"
|
| +#import "ios/chrome/browser/ui/bookmarks/bookmark_utils_ios.h"
|
| +#import "ios/chrome/browser/ui/rtl_geometry.h"
|
| +#import "ios/chrome/browser/ui/uikit_ui_util.h"
|
| +#include "ios/chrome/grit/ios_strings.h"
|
| +#include "ios/chrome/grit/ios_theme_resources.h"
|
| +#import "ios/third_party/material_components_ios/src/components/Ink/src/MaterialInk.h"
|
| +#import "ios/third_party/material_components_ios/src/components/Typography/src/MaterialTypography.h"
|
| +#include "ui/base/l10n/l10n_util_mac.h"
|
| +#include "ui/base/resource/resource_bundle.h"
|
| +#import "ui/gfx/ios/NSString+CrStringDrawing.h"
|
| +#include "url/gurl.h"
|
| +
|
| +namespace {
|
| +const CGFloat kBookmarkItemCellDefaultImageSize = 40.0;
|
| +const CGFloat kBookmarkFolderCellDefaultImageSize = 24.0;
|
| +} // namespace
|
| +
|
| +@interface BookmarkCell () {
|
| + @protected
|
| + // Subclasses should set these in the constructor with the wanted values.
|
| + CGFloat _imageSize;
|
| +
|
| + @private
|
| + base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkCell;
|
| +}
|
| +// Redefined to be read-write.
|
| +@property(nonatomic, retain) UILabel* titleLabel;
|
| +// Redefined to readwrite.
|
| +@property(nonatomic, retain) UIImageView* imageView;
|
| +// Label to show placeholder text when there is no image displayed.
|
| +@property(nonatomic, retain) UILabel* placeholderLabel;
|
| +// When the cell is selected for editing, a cover is shown with a blue color.
|
| +// Subclasses should insert new views below this view.
|
| +@property(nonatomic, retain) UIView* highlightCover;
|
| +// Lists the accessibility elements that are to be seen by UIAccessibility.
|
| +@property(nonatomic, readonly) NSMutableArray* accessibilityElements;
|
| +// Location of the last touch on the cell.
|
| +@property(nonatomic, assign) CGPoint touchLocation;
|
| +// The view doing the highlight animation. Only set while the cell is
|
| +// highlighted.
|
| +@property(nonatomic, retain) MDCInkView* touchFeedbackView;
|
| +@property(nonatomic, retain) BookmarkExtendedButton* button;
|
| +@property(nonatomic, assign) SEL buttonAction;
|
| +@property(nonatomic, assign) id buttonTarget;
|
| +@end
|
| +
|
| +@implementation BookmarkCell
|
| +@synthesize accessibilityElements = _accessibilityElements;
|
| +@synthesize titleLabel = _titleLabel;
|
| +@synthesize imageView = _imageView;
|
| +@synthesize highlightCover = _highlightCover;
|
| +@synthesize shouldAnimateImageChanges = _shouldAnimateImageChanges;
|
| +@synthesize touchLocation = _touchLocation;
|
| +@synthesize touchFeedbackView = _touchFeedbackView;
|
| +@synthesize button = _button;
|
| +@synthesize buttonAction = _buttonAction;
|
| +@synthesize buttonTarget = _buttonTarget;
|
| +@synthesize placeholderLabel = _placeholderLabel;
|
| +
|
| ++ (NSString*)reuseIdentifier {
|
| + NOTREACHED();
|
| + return nil;
|
| +}
|
| +
|
| +- (instancetype)initWithFrame:(CGRect)frame {
|
| + self = [super initWithFrame:frame];
|
| + if (self) {
|
| + _propertyReleaser_BookmarkCell.Init(self, [BookmarkCell class]);
|
| + self.exclusiveTouch = YES;
|
| + self.backgroundColor = [UIColor whiteColor];
|
| +
|
| + _titleLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
| + _titleLabel.textColor = bookmark_utils_ios::darkTextColor();
|
| + self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [self.contentView addSubview:_titleLabel];
|
| +
|
| + _imageView = [[UIImageView alloc] initWithFrame:CGRectZero];
|
| + _imageView.clipsToBounds = YES;
|
| + self.imageView.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [self.contentView addSubview:_imageView];
|
| +
|
| + _placeholderLabel = [[UILabel alloc] init];
|
| + _placeholderLabel.textAlignment = NSTextAlignmentCenter;
|
| + [self.contentView addSubview:_placeholderLabel];
|
| +
|
| + _highlightCover = [[UIView alloc] initWithFrame:CGRectZero];
|
| + _highlightCover.backgroundColor =
|
| + [bookmark_utils_ios::blueColor() colorWithAlphaComponent:0.7];
|
| + _highlightCover.hidden = YES;
|
| + _highlightCover.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [self.contentView addSubview:_highlightCover];
|
| +
|
| + self.button = base::scoped_nsobject<BookmarkExtendedButton>(
|
| + [[BookmarkExtendedButton alloc] init]);
|
| + self.button.contentMode = UIViewContentModeCenter;
|
| + self.button.backgroundColor = [UIColor clearColor];
|
| + [self.button addTarget:self
|
| + action:@selector(buttonTapped:)
|
| + forControlEvents:UIControlEventTouchUpInside];
|
| + self.button.translatesAutoresizingMaskIntoConstraints = NO;
|
| + [self.contentView insertSubview:self.button
|
| + belowSubview:self.highlightCover];
|
| +
|
| + _accessibilityElements = [[NSMutableArray alloc] init];
|
| + self.contentView.isAccessibilityElement = YES;
|
| + self.contentView.accessibilityTraits |= UIAccessibilityTraitButton;
|
| + [_accessibilityElements addObject:self.contentView];
|
| + [self.accessibilityElements addObject:self.button];
|
| + }
|
| + return self;
|
| +}
|
| +
|
| ++ (BOOL)requiresConstraintBasedLayout {
|
| + return YES;
|
| +}
|
| +
|
| +- (void)updateConstraints {
|
| + if (_imageSize) {
|
| + // Create constraints.
|
| +
|
| + // Align all the views on the same horizontal line.
|
| + AddSameCenterYConstraint(self.contentView, self.titleLabel);
|
| + AddSameCenterYConstraint(self.contentView, self.imageView);
|
| + AddSameCenterYConstraint(self.contentView, self.button);
|
| +
|
| + // clang-format off
|
| + ApplyVisualConstraintsWithMetrics(
|
| + @[
|
| + @"H:|-leadingImageMargin-[image(imageSize)]",
|
| + @"H:|-leadingMargin-[title]-[button(buttonSize)]|",
|
| + @"V:[image(imageSize)]",
|
| + @"V:[button(buttonSize)]",
|
| + @"H:|[highlight]|",
|
| + @"V:|[highlight]|"
|
| + ],
|
| + @{
|
| + @"title" : self.titleLabel,
|
| + @"image" : self.imageView,
|
| + @"button" : self.button,
|
| + @"highlight" : self.highlightCover
|
| + },
|
| + @{
|
| + @"buttonSize" : [NSNumber numberWithFloat:32.0],
|
| + @"leadingImageMargin" : [NSNumber numberWithFloat:16.0],
|
| + @"leadingMargin" : [NSNumber numberWithFloat:64.0],
|
| + @"imageSize" : [NSNumber numberWithFloat:_imageSize],
|
| + },
|
| + self.contentView);
|
| + // clang-format on
|
| + }
|
| +
|
| + [NSLayoutConstraint activateConstraints:@[
|
| + [self.placeholderLabel.leadingAnchor
|
| + constraintEqualToAnchor:self.imageView.leadingAnchor],
|
| + [self.placeholderLabel.trailingAnchor
|
| + constraintEqualToAnchor:self.imageView.trailingAnchor],
|
| + [self.placeholderLabel.topAnchor
|
| + constraintEqualToAnchor:self.imageView.topAnchor],
|
| + [self.placeholderLabel.bottomAnchor
|
| + constraintEqualToAnchor:self.imageView.bottomAnchor]
|
| + ]];
|
| + self.placeholderLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
| +
|
| + [super updateConstraints];
|
| +}
|
| +
|
| +- (void)setSelectedForEditing:(BOOL)selected animated:(BOOL)animated {
|
| + self.highlightCover.hidden = !selected;
|
| + if (selected)
|
| + self.contentView.accessibilityTraits |= UIAccessibilityTraitSelected;
|
| + else
|
| + self.contentView.accessibilityTraits &= ~UIAccessibilityTraitSelected;
|
| +}
|
| +
|
| +- (void)setImage:(UIImage*)image {
|
| + self.imageView.image = image;
|
| + self.placeholderLabel.hidden = YES;
|
| + self.imageView.hidden = NO;
|
| +
|
| + if (!self.shouldAnimateImageChanges)
|
| + return;
|
| +
|
| + CATransition* transition = [CATransition animation];
|
| + transition.duration = 0.2f;
|
| + transition.timingFunction = [CAMediaTimingFunction
|
| + functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
|
| + transition.type = kCATransitionFade;
|
| + [self.imageView.layer addAnimation:transition forKey:nil];
|
| +}
|
| +
|
| +- (void)setPlaceholderText:(NSString*)text
|
| + textColor:(UIColor*)textColor
|
| + backgroundColor:(UIColor*)backgroundColor {
|
| + self.placeholderLabel.hidden = NO;
|
| + self.imageView.hidden = YES;
|
| +
|
| + self.placeholderLabel.backgroundColor = backgroundColor;
|
| + self.placeholderLabel.textColor = textColor;
|
| + self.placeholderLabel.text = text;
|
| +}
|
| +
|
| +- (void)prepareForReuse {
|
| + self.imageView.image = nil;
|
| + self.imageView.backgroundColor = [UIColor whiteColor];
|
| + self.highlightCover.hidden = YES;
|
| + self.shouldAnimateImageChanges = NO;
|
| + [self.touchFeedbackView cancelAllAnimationsAnimated:NO];
|
| + [self.touchFeedbackView removeFromSuperview];
|
| + self.touchFeedbackView = nil;
|
| + [self updateWithTitle:nil];
|
| + self.placeholderLabel.hidden = YES;
|
| + self.imageView.hidden = NO;
|
| +
|
| + [super prepareForReuse];
|
| +}
|
| +
|
| +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
|
| + UITouch* touch = [touches anyObject];
|
| + self.touchLocation = [touch locationInView:self];
|
| + [super touchesBegan:touches withEvent:event];
|
| +}
|
| +
|
| +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
|
| + UITouch* touch = [touches anyObject];
|
| + self.touchLocation = [touch locationInView:self];
|
| + [super touchesEnded:touches withEvent:event];
|
| +}
|
| +
|
| +- (void)setHighlighted:(BOOL)highlighted {
|
| + [super setHighlighted:highlighted];
|
| +
|
| + // To prevent selection messing up with ink, immediately remove ink.
|
| + [self.touchFeedbackView removeFromSuperview];
|
| + self.touchFeedbackView = nil;
|
| +
|
| + if (highlighted) {
|
| + // Creates an ink feedback and animates it.
|
| + base::scoped_nsobject<MDCInkView> touchFeedbackView(
|
| + [[MDCInkView alloc] initWithFrame:self.bounds]);
|
| + [self addSubview:touchFeedbackView];
|
| + self.touchFeedbackView = touchFeedbackView;
|
| + [self.touchFeedbackView startTouchBeganAnimationAtPoint:self.touchLocation
|
| + completion:nil];
|
| + } else {
|
| + [self.touchFeedbackView startTouchEndedAnimationAtPoint:self.touchLocation
|
| + completion:nil];
|
| + }
|
| +}
|
| +
|
| +- (void)updateWithTitle:(NSString*)title {
|
| + self.titleLabel.text = title;
|
| + [self setNeedsLayout];
|
| + [self updateAccessibilityValues];
|
| +}
|
| +
|
| +#pragma mark - Button
|
| +- (void)setButtonTarget:(id)target action:(SEL)action {
|
| + self.buttonTarget = target;
|
| + self.buttonAction = action;
|
| +}
|
| +
|
| +- (void)buttonTapped:(id)target {
|
| + [self.buttonTarget performSelector:self.buttonAction
|
| + withObject:self
|
| + withObject:target];
|
| +}
|
| +
|
| +- (void)showButtonOfType:(bookmark_cell::ButtonType)buttonType
|
| + animated:(BOOL)animated {
|
| + switch (buttonType) {
|
| + case bookmark_cell::ButtonNone:
|
| + [self removeButtonAnimated:animated];
|
| + break;
|
| +
|
| + case bookmark_cell::ButtonMenu: {
|
| + ResourceBundle& rb = ResourceBundle::GetSharedInstance();
|
| + UIImage* bgImage =
|
| + rb.GetNativeImageNamed(IDR_IOS_TOOLBAR_LIGHT_TOOLS).ToUIImage();
|
| + [self addButtonWithImage:bgImage animated:animated];
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| +- (void)addButtonWithImage:(UIImage*)image animated:(BOOL)animated {
|
| + // Set the image.
|
| + [self.button setImage:image forState:UIControlStateNormal];
|
| +
|
| + // Animates the button in.
|
| + [UIView animateWithDuration:animated ? 0.15 : 0
|
| + delay:0
|
| + options:UIViewAnimationOptionBeginFromCurrentState
|
| + animations:^{
|
| + self.button.alpha = 1.0;
|
| + }
|
| + completion:^(BOOL finished) {
|
| + if (finished) {
|
| + // If this method is called twice in succession, the button should
|
| + // only be added once to |self.accessibilityElements|.
|
| + if (![self.accessibilityElements containsObject:self.button])
|
| + [self.accessibilityElements addObject:self.button];
|
| + }
|
| + }];
|
| +}
|
| +
|
| +- (void)removeButtonAnimated:(BOOL)animated {
|
| + if (!self.button)
|
| + return;
|
| +
|
| + [UIView animateWithDuration:animated ? 0.15 : 0
|
| + delay:0
|
| + options:UIViewAnimationOptionBeginFromCurrentState
|
| + animations:^{
|
| + self.button.alpha = 0.0;
|
| + }
|
| + completion:^(BOOL finished) {
|
| + if (finished) {
|
| + [self.accessibilityElements removeObject:self.button];
|
| + }
|
| + }];
|
| +}
|
| +
|
| +#pragma mark - Accessibility
|
| +
|
| +- (void)updateAccessibilityValues {
|
| + self.button.accessibilityIdentifier =
|
| + [NSString stringWithFormat:@"%@ %@", self.titleLabel.text, @"Info"];
|
| + self.button.accessibilityLabel = l10n_util::GetNSString(
|
| + IDS_IOS_BOOKMARK_NEW_MORE_BUTTON_ACCESSIBILITY_LABEL);
|
| +}
|
| +
|
| +- (NSInteger)accessibilityElementCount {
|
| + return [self.accessibilityElements count];
|
| +}
|
| +
|
| +- (id)accessibilityElementAtIndex:(NSInteger)index {
|
| + return [self.accessibilityElements objectAtIndex:index];
|
| +}
|
| +
|
| +- (NSInteger)indexOfAccessibilityElement:(id)element {
|
| + return [self.accessibilityElements indexOfObject:element];
|
| +}
|
| +
|
| +@end
|
| +
|
| +#pragma mark - BookmarkItemCell
|
| +
|
| +@interface BookmarkItemCell () {
|
| + base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkItemCell;
|
| +}
|
| +@end
|
| +
|
| +@implementation BookmarkItemCell
|
| +
|
| ++ (NSString*)reuseIdentifier {
|
| + return @"BookmarkItemCell";
|
| +}
|
| +
|
| ++ (CGFloat)preferredImageSize {
|
| + return kBookmarkItemCellDefaultImageSize;
|
| +}
|
| +
|
| +- (instancetype)initWithFrame:(CGRect)frame {
|
| + self = [super initWithFrame:frame];
|
| + if (self) {
|
| + _propertyReleaser_BookmarkItemCell.Init(self, [BookmarkItemCell class]);
|
| +
|
| + // Set the non-layout properties of the titles.
|
| + UIFont* font = [MDCTypography subheadFont];
|
| + self.titleLabel.font = font;
|
| + self.titleLabel.backgroundColor = [UIColor clearColor];
|
| + self.titleLabel.numberOfLines = 1;
|
| +
|
| + _imageSize = kBookmarkItemCellDefaultImageSize;
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)updateWithTitle:(NSString*)title {
|
| + [super updateWithTitle:title];
|
| + [self updateAccessibilityValues];
|
| +}
|
| +
|
| +- (void)updateAccessibilityValues {
|
| + [super updateAccessibilityValues];
|
| + self.contentView.accessibilityLabel =
|
| + [NSString stringWithFormat:@"%@", self.titleLabel.text];
|
| + self.contentView.accessibilityIdentifier = self.titleLabel.text;
|
| +}
|
| +
|
| +- (void)prepareForReuse {
|
| + [super prepareForReuse];
|
| + [self updateAccessibilityValues];
|
| +}
|
| +
|
| +@end
|
| +
|
| +#pragma mark - BookmarkFolderCell
|
| +
|
| +@interface BookmarkFolderCell () {
|
| + base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkFolderCell;
|
| +}
|
| +
|
| +@end
|
| +
|
| +@implementation BookmarkFolderCell
|
| +
|
| ++ (NSString*)reuseIdentifier {
|
| + return @"BookmarkFolderCell";
|
| +}
|
| +
|
| +- (instancetype)initWithFrame:(CGRect)frame {
|
| + self = [super initWithFrame:frame];
|
| + if (self) {
|
| + _propertyReleaser_BookmarkFolderCell.Init(self, [BookmarkFolderCell class]);
|
| +
|
| + self.imageView.image = [UIImage imageNamed:@"bookmark_gray_folder"];
|
| + self.titleLabel.font = [MDCTypography subheadFont];
|
| +
|
| + _imageSize = kBookmarkFolderCellDefaultImageSize;
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)prepareForReuse {
|
| + [super prepareForReuse];
|
| + [self setImage:[UIImage imageNamed:@"bookmark_gray_folder"]];
|
| +}
|
| +
|
| +- (void)updateWithTitle:(NSString*)title {
|
| + [super updateWithTitle:title];
|
| +
|
| + self.contentView.accessibilityLabel = title;
|
| + self.contentView.accessibilityIdentifier = title;
|
| +}
|
| +
|
| +@end
|
| +
|
| +#pragma mark - BookmarkHeaderView
|
| +
|
| +@interface BookmarkHeaderView () {
|
| + base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkHeaderView;
|
| +}
|
| +@property(nonatomic, retain) UILabel* titleLabel;
|
| +@end
|
| +
|
| +@implementation BookmarkHeaderView
|
| +@synthesize titleLabel = _titleLabel;
|
| ++ (NSString*)reuseIdentifier {
|
| + return @"BookmarkHeaderView";
|
| +}
|
| +
|
| ++ (CGFloat)handsetHeight {
|
| + return 47.0;
|
| +}
|
| +
|
| +- (instancetype)initWithFrame:(CGRect)frame {
|
| + self = [super initWithFrame:frame];
|
| + if (self) {
|
| + _propertyReleaser_BookmarkHeaderView.Init(self, [BookmarkHeaderView class]);
|
| + base::scoped_nsobject<UILabel> titleLabel(
|
| + [[UILabel alloc] initWithFrame:CGRectZero]);
|
| + self.titleLabel = titleLabel;
|
| + UIFont* font = [MDCTypography body2Font];
|
| + self.titleLabel.font = font;
|
| + self.titleLabel.textColor = bookmark_utils_ios::lightTextColor();
|
| + self.titleLabel.backgroundColor = [UIColor clearColor];
|
| + self.titleLabel.accessibilityTraits |= UIAccessibilityTraitHeader;
|
| + [self addSubview:self.titleLabel];
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)layoutSubviews {
|
| + [super layoutSubviews];
|
| +
|
| + // On handsets the title is aligned with the icon in the navigation bar.
|
| + CGFloat margin = 16.0;
|
| + if (IsIPadIdiom())
|
| + margin = 24.0; // On tablets the alignment is the same as the tiles.
|
| + self.titleLabel.frame =
|
| + CGRectMake(margin, 0, self.bounds.size.width - margin * 2.0,
|
| + self.bounds.size.height);
|
| +}
|
| +
|
| +- (void)setTitle:(NSString*)title {
|
| + self.titleLabel.text = title;
|
| +}
|
| +
|
| +- (void)updateLayoutWithLeftMargin:(CGFloat)leftMargin {
|
| + self.titleLabel.frame =
|
| + CGRectMake(leftMargin, 0, self.bounds.size.width - leftMargin,
|
| + self.bounds.size.height);
|
| +}
|
| +
|
| +@end
|
| +
|
| +#pragma mark - BookmarkHeaderSeparatorView
|
| +
|
| +@interface BookmarkHeaderSeparatorView () {
|
| + base::mac::ObjCPropertyReleaser _propertyReleaser_BookmarkHeaderSeparatorView;
|
| +}
|
| +// The bottom separator line.
|
| +@property(nonatomic, retain) UIView* lineView;
|
| +@end
|
| +
|
| +@implementation BookmarkHeaderSeparatorView
|
| +
|
| +@synthesize lineView = _lineView;
|
| +
|
| ++ (NSString*)reuseIdentifier {
|
| + return NSStringFromClass(self);
|
| +}
|
| +
|
| ++ (CGFloat)preferredHeight {
|
| + return 16;
|
| +}
|
| +
|
| +- (instancetype)initWithFrame:(CGRect)frame {
|
| + self = [super initWithFrame:frame];
|
| + if (self) {
|
| + _propertyReleaser_BookmarkHeaderSeparatorView.Init(
|
| + self, [BookmarkHeaderSeparatorView class]);
|
| + _lineView = [[UIView alloc] init];
|
| + _lineView.backgroundColor = bookmark_utils_ios::separatorColor();
|
| + [self addSubview:_lineView];
|
| + }
|
| + return self;
|
| +}
|
| +
|
| +- (void)layoutSubviews {
|
| + CGFloat lineHeight = 1;
|
| + CGFloat lineY = self.bounds.size.height - lineHeight - 8;
|
| + _lineView.frame = CGRectMake(0, lineY, self.bounds.size.width, lineHeight);
|
| +}
|
| +
|
| +@end
|
|
|