Index: ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm |
diff --git a/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6b9072a3a96d42f683c4b431bef560c9af884802 |
--- /dev/null |
+++ b/ios/chrome/browser/ui/tab_switcher/tab_switcher_panel_cell.mm |
@@ -0,0 +1,456 @@ |
+// Copyright 2015 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/tab_switcher/tab_switcher_panel_cell.h" |
+ |
+#include "base/mac/scoped_nsobject.h" |
+#import "ios/chrome/browser/tabs/tab.h" |
+#import "ios/chrome/browser/ui/fade_truncated_label.h" |
+#import "ios/chrome/browser/ui/image_util.h" |
+#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_button.h" |
+#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_cache.h" |
+#import "ios/chrome/browser/ui/tab_switcher/tab_switcher_utils.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/Palettes/src/MaterialPalettes.h" |
+#import "ios/third_party/material_roboto_font_loader_ios/src/src/MaterialRobotoFontLoader.h" |
+#import "ios/third_party/material_text_accessibility_ios/src/src/MDFTextAccessibility.h" |
+#include "ui/base/l10n/l10n_util.h" |
+#include "ui/base/resource/resource_bundle.h" |
+#include "ui/gfx/image/image.h" |
+#include "url/gurl.h" |
+ |
+namespace gfx { |
+class ImageSkia; |
+} |
+ |
+namespace { |
+const CGFloat kFontSize = 16; |
+const CGFloat kCellCornerRadius = 2; |
+const CGFloat kBarHeight = 44; |
+const CGFloat kTitleLabelTextAlpha = .54; |
+const CGFloat kNewTabIconAlpha = .87; |
+} // Anonymous namespace |
+ |
+CGFloat tabSwitcherLocalSessionCellTopBarHeight() { |
+ return kBarHeight; |
+} |
+ |
+@interface TabSwitcherSessionCell () |
+ |
+// Returns the container view with rounded corners to which all cell subviews |
+// should be added. |
+- (UIView*)containerView; |
+ |
+@end |
+ |
+@implementation TabSwitcherSessionCell { |
+ base::scoped_nsobject<UIView> _containerView; |
+ CGSize _cachedShadowSize; |
+} |
+ |
+// Returns the cell's identifier used for the cell's re-use. |
++ (NSString*)identifier { |
+ return NSStringFromClass([self class]); |
+} |
+ |
+- (instancetype)initWithFrame:(CGRect)frame { |
+ self = [super initWithFrame:frame]; |
+ if (self) { |
+ [self contentView].isAccessibilityElement = YES; |
+ _containerView.reset([[UIView alloc] initWithFrame:self.bounds]); |
+ [_containerView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | |
+ UIViewAutoresizingFlexibleWidth]; |
+ [[_containerView layer] setCornerRadius:kCellCornerRadius]; |
+ [[_containerView layer] setMasksToBounds:YES]; |
+ [[self contentView] addSubview:_containerView]; |
+ [self updateShadow]; |
+ [[[self contentView] layer] setShouldRasterize:YES]; |
+ [[[self contentView] layer] |
+ setRasterizationScale:[[UIScreen mainScreen] scale]]; |
+ } |
+ return self; |
+} |
+ |
+- (void)layoutSubviews { |
+ [super layoutSubviews]; |
+ [self updateShadow]; |
+} |
+ |
+- (void)updateShadow { |
+ if (!CGSizeEqualToSize(_cachedShadowSize, self.bounds.size)) { |
+ CGRect offsetedRectangle = CGRectOffset(self.bounds, 0, 6); |
+ UIBezierPath* shadowPath = |
+ [UIBezierPath bezierPathWithRoundedRect:offsetedRectangle |
+ cornerRadius:kCellCornerRadius]; |
+ [[self contentView].layer setShadowPath:shadowPath.CGPath]; |
+ [[self contentView].layer setShadowColor:[UIColor blackColor].CGColor]; |
+ [[self contentView].layer setShadowOpacity:0.5]; |
+ _cachedShadowSize = self.bounds.size; |
+ } |
+} |
+ |
+- (UIView*)containerView { |
+ return _containerView.get(); |
+} |
+ |
+@end |
+ |
+@implementation TabSwitcherLocalSessionCell { |
+ base::scoped_nsobject<UIView> _topBar; |
+ base::scoped_nsobject<UILabel> _titleLabel; |
+ base::scoped_nsobject<UIImageView> _favicon; |
+ base::scoped_nsobject<UIButton> _closeButton; |
+ base::scoped_nsobject<UIImageView> _shadow; |
+ base::scoped_nsobject<UIImageView> _snapshot; |
+ base::scoped_nsobject<TabSwitcherButton> _snapshotButton; |
+ PendingSnapshotRequest _currentPendingSnapshotRequest; |
+ id<SessionCellDelegate> _delegate; // weak |
+} |
+ |
+- (instancetype)initWithFrame:(CGRect)frame { |
+ self = [super initWithFrame:frame]; |
+ if (self) { |
+ // Top bar. |
+ _topBar.reset([[UIView alloc] initWithFrame:CGRectZero]); |
+ [_topBar setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [[self containerView] addSubview:_topBar]; |
+ |
+ // Snapshot view. |
+ _snapshot.reset([[UIImageView alloc] initWithFrame:CGRectZero]); |
+ [_snapshot setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_snapshot setContentMode:UIViewContentModeScaleAspectFill]; |
+ [_snapshot setClipsToBounds:YES]; |
+ [[self containerView] addSubview:_snapshot]; |
+ |
+ // Cell button. |
+ _snapshotButton.reset([[TabSwitcherButton alloc] initWithFrame:CGRectZero]); |
+ [_snapshotButton setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_snapshotButton addTarget:self |
+ action:@selector(snapshotPressed) |
+ forControlEvents:UIControlEventTouchUpInside]; |
+ [[self containerView] addSubview:_snapshotButton]; |
+ |
+ // Shadow view. |
+ _shadow.reset([[UIImageView alloc] initWithFrame:CGRectZero]); |
+ [_shadow setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
+ gfx::Image shadow = rb.GetNativeImageNamed(IDR_IOS_TOOLBAR_SHADOW); |
+ [_shadow setImage:shadow.ToUIImage()]; |
+ [[self containerView] addSubview:_shadow]; |
+ |
+ // Constraints on the Top bar, snapshot view, and shadow view. |
+ NSDictionary* viewsDictionary = @{ |
+ @"bar" : _topBar.get(), |
+ @"shadow" : _shadow.get(), |
+ @"snapshot" : _snapshot.get(), |
+ @"snapshotButton" : _snapshotButton.get(), |
+ }; |
+ NSArray* constraints = @[ |
+ @"H:|-0-[bar]-0-|", |
+ @"H:|-0-[shadow]-0-|", |
+ @"H:|-0-[snapshot]-0-|", |
+ @"H:|-0-[snapshotButton]-0-|", |
+ @"V:|-0-[bar(==barHeight)]-0-[snapshot]-0-|", |
+ @"V:[bar]-0-[snapshotButton]-0-|", |
+ @"V:[bar]-0-[shadow]", |
+ ]; |
+ NSDictionary* metrics = |
+ @{ @"barHeight" : @(tabSwitcherLocalSessionCellTopBarHeight()) }; |
+ ApplyVisualConstraintsWithMetrics(constraints, viewsDictionary, metrics, |
+ [self containerView]); |
+ |
+ // Create and add subviews to the cell bar. |
+ // Title label. |
+ _titleLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]); |
+ [_titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_titleLabel setFont:[[MDFRobotoFontLoader sharedInstance] |
+ regularFontOfSize:kFontSize]]; |
+ [_topBar addSubview:_titleLabel]; |
+ |
+ // Favicon. |
+ _favicon.reset([[UIImageView alloc] initWithFrame:CGRectZero]); |
+ [_favicon setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_topBar addSubview:_favicon]; |
+ |
+ // Close button. |
+ _closeButton.reset([[UIButton alloc] initWithFrame:CGRectZero]); |
+ [_closeButton |
+ setImage:[[UIImage imageNamed:@"card_close_button"] |
+ imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] |
+ forState:UIControlStateNormal]; |
+ [_closeButton setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_closeButton addTarget:self |
+ action:@selector(closeButtonPressed) |
+ forControlEvents:UIControlEventTouchUpInside]; |
+ [_closeButton setExclusiveTouch:YES]; |
+ [_topBar addSubview:_closeButton]; |
+ |
+ // Constraints on the title label, favicon, and close button. |
+ NSDictionary* barViewsDictionary = @{ |
+ @"favicon" : _favicon.get(), |
+ @"title" : _titleLabel.get(), |
+ @"closeButton" : _closeButton.get() |
+ }; |
+ NSArray* barConstraints = @[ |
+ @"H:|-16-[favicon(==24)]-8-[title]-0-[closeButton(==32)]-8-|", |
+ @"V:[favicon(==24)]", |
+ @"V:[closeButton(==32)]", |
+ ]; |
+ ApplyVisualConstraints(barConstraints, barViewsDictionary, _topBar); |
+ AddSameCenterYConstraint(_topBar, _favicon); |
+ AddSameCenterYConstraint(_topBar, _titleLabel); |
+ AddSameCenterYConstraint(_topBar, _closeButton); |
+ } |
+ return self; |
+} |
+ |
+- (UIView*)topBar { |
+ return _topBar.get(); |
+} |
+ |
+- (UIImage*)screenshot { |
+ return [_snapshot image]; |
+} |
+ |
+- (void)setSnapshot:(UIImage*)image { |
+ DCHECK(!ImageHasAlphaChannel(image)); |
+ [_snapshot setImage:image]; |
+} |
+ |
+- (void)setAppearanceForTab:(Tab*)tab cellSize:(CGSize)cellSize { |
+ [_titleLabel setText:tab.title]; |
+ [self contentView].accessibilityLabel = tab.title; |
+ if (tab.favicon) { |
+ [_favicon setImage:tab.favicon]; |
+ } else { |
+ // No favicon is available, use placeholder instead. |
+ ResourceBundle& rb = ResourceBundle::GetSharedInstance(); |
+ [_favicon |
+ setImage:rb.GetNativeImageNamed(IDR_IOS_OMNIBOX_HTTP).ToUIImage()]; |
+ } |
+ |
+ CGSize snapshotSize = cellSize; |
+ snapshotSize.height -= tabSwitcherLocalSessionCellTopBarHeight(); |
+ base::WeakNSObject<TabSwitcherLocalSessionCell> weakCell(self); |
+ _currentPendingSnapshotRequest = |
+ [[self cache] requestSnapshotForTab:tab |
+ withSize:snapshotSize |
+ completionBlock:^(UIImage* image) { |
+ DCHECK([NSThread isMainThread]); |
+ [weakCell setSnapshot:image]; |
+ _currentPendingSnapshotRequest = {}; |
+ }]; |
+} |
+ |
+- (void)setSessionType:(ios_internal::SessionType)type { |
+ UIColor* topBarBackgroundColor; |
+ UIColor* closeButtonTintColor; |
+ UIColor* textColor; |
+ UIColor* snapshotBackgroundColor; |
+ if (type == ios_internal::SessionType::OFF_THE_RECORD_SESSION) { |
+ topBarBackgroundColor = [[MDCPalette greyPalette] tint700]; |
+ closeButtonTintColor = [[MDCPalette greyPalette] tint100]; |
+ textColor = [[MDCPalette greyPalette] tint100]; |
+ snapshotBackgroundColor = [[MDCPalette greyPalette] tint900]; |
+ } else { |
+ topBarBackgroundColor = [[MDCPalette greyPalette] tint100]; |
+ closeButtonTintColor = [[MDCPalette greyPalette] tint700]; |
+ textColor = [[MDCPalette greyPalette] tint700]; |
+ snapshotBackgroundColor = [UIColor whiteColor]; |
+ } |
+ [_topBar setBackgroundColor:topBarBackgroundColor]; |
+ [[_closeButton imageView] setTintColor:closeButtonTintColor]; |
+ [_titleLabel setTextColor:textColor]; |
+ [_titleLabel setBackgroundColor:topBarBackgroundColor]; |
+ [_snapshot setBackgroundColor:snapshotBackgroundColor]; |
+} |
+ |
+- (void)setDelegate:(id<SessionCellDelegate>)delegate { |
+ _delegate = delegate; |
+} |
+ |
+- (void)snapshotPressed { |
+ [_delegate cellPressed:self]; |
+} |
+ |
+- (void)closeButtonPressed { |
+ [_delegate deleteButtonPressedForCell:self]; |
+} |
+ |
+- (void)prepareForReuse { |
+ [[self cache] cancelPendingSnapshotRequest:_currentPendingSnapshotRequest]; |
+ _currentPendingSnapshotRequest.clear(); |
+ [_snapshot setImage:nil]; |
+ [_snapshotButton resetState]; |
+ [super prepareForReuse]; |
+} |
+ |
+- (TabSwitcherCache*)cache { |
+ return [_delegate tabSwitcherCache]; |
+} |
+ |
+#pragma mark - UIAccessibilityAction |
+ |
+- (NSArray*)accessibilityCustomActions { |
+ base::scoped_nsobject<NSMutableArray> customActions( |
+ [[NSMutableArray alloc] init]); |
+ base::scoped_nsobject<UIAccessibilityCustomAction> customAction( |
+ [[UIAccessibilityCustomAction alloc] |
+ initWithName:l10n_util::GetNSString(IDS_IOS_TAB_SWITCHER_CLOSE_TAB) |
+ target:self |
+ selector:@selector(closeButtonPressed)]); |
+ [customActions addObject:customAction.autorelease()]; |
+ return customActions.autorelease(); |
+} |
+ |
+@end |
+ |
+@implementation TabSwitcherDistantSessionCell { |
+ base::scoped_nsobject<UILabel> _titleLabel; |
+ base::scoped_nsobject<UIImageView> _favicon; |
+ base::scoped_nsobject<UIImageView> _newTabIcon; |
+ base::scoped_nsobject<UIView> _verticallyCenteredView; |
+ base::scoped_nsobject<TabSwitcherButton> _raisedButton; |
+ base::scoped_nsobject<NSOperation> _faviconObtainer; |
+ id<SessionCellDelegate> _delegate; // weak |
+} |
+ |
+- (instancetype)initWithFrame:(CGRect)frame { |
+ self = [super initWithFrame:frame]; |
+ if (self) { |
+ // Create and add the button that contains all other subviews. |
+ _raisedButton.reset([[TabSwitcherButton alloc] initWithFrame:CGRectZero]); |
+ [_raisedButton setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_raisedButton addTarget:self |
+ action:@selector(cellPressed) |
+ forControlEvents:UIControlEventTouchUpInside]; |
+ [[self containerView] addSubview:_raisedButton]; |
+ ApplyVisualConstraints(@[ @"H:|-0-[button]-0-|", @"V:|-0-[button]-0-|" ], |
+ @{ @"button" : _raisedButton.get() }, |
+ [self containerView]); |
+ |
+ // Create and add view that will be vertically centered in the space over |
+ // the favicon. |
+ _verticallyCenteredView.reset([[UIView alloc] initWithFrame:CGRectZero]); |
+ [_verticallyCenteredView setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_verticallyCenteredView setUserInteractionEnabled:NO]; |
+ [_raisedButton addSubview:_verticallyCenteredView]; |
+ |
+ // Create and add title label to |_verticallyCenteredContent|. |
+ _titleLabel.reset([[UILabel alloc] initWithFrame:CGRectZero]); |
+ [_titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_titleLabel setNumberOfLines:5]; |
+ [_titleLabel setTextAlignment:NSTextAlignmentCenter]; |
+ [_titleLabel setFont:[[MDFRobotoFontLoader sharedInstance] |
+ regularFontOfSize:kFontSize]]; |
+ [_verticallyCenteredView addSubview:_titleLabel]; |
+ |
+ // Create and add new tab icon to |_verticallyCenteredContent|. |
+ UIImage* newTabIcon = [[UIImage imageNamed:@"tabswitcher_new_tab"] |
+ imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; |
+ _newTabIcon.reset([[UIImageView alloc] initWithImage:newTabIcon]); |
+ [_newTabIcon setAlpha:0]; |
+ [_newTabIcon setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_verticallyCenteredView addSubview:_newTabIcon]; |
+ |
+ // Create and add favicon image container. |
+ _favicon.reset([[UIImageView alloc] initWithFrame:CGRectZero]); |
+ [_favicon setTranslatesAutoresizingMaskIntoConstraints:NO]; |
+ [_raisedButton addSubview:_favicon]; |
+ |
+ // Add constraints to the button's subviews. |
+ NSDictionary* viewsDictionary = @{ |
+ @"newTabIcon" : _newTabIcon.get(), |
+ @"title" : _titleLabel.get(), |
+ @"favicon" : _favicon.get(), |
+ @"centeredView" : _verticallyCenteredView.get(), |
+ }; |
+ NSArray* constraintsInButton = @[ |
+ @"H:|-0-[centeredView]-0-|", |
+ @"H:[favicon(==16)]", |
+ @"V:|-(>=16)-[centeredView]-(>=16)-[favicon(==16)]-16-|", |
+ ]; |
+ ApplyVisualConstraints(constraintsInButton, viewsDictionary, _raisedButton); |
+ AddSameCenterXConstraint(_raisedButton, _favicon.get()); |
+ [_raisedButton |
+ addConstraint:[NSLayoutConstraint |
+ constraintWithItem:_verticallyCenteredView.get() |
+ attribute:NSLayoutAttributeCenterY |
+ relatedBy:NSLayoutRelationEqual |
+ toItem:_favicon.get() |
+ attribute:NSLayoutAttributeCenterY |
+ multiplier:0.5 |
+ constant:0]]; |
+ |
+ // Add constraints to the subviews of the vertically centered view. |
+ NSArray* constraintsInVerticallyCenteredView = @[ |
+ @"H:|-16-[title]-16-|", |
+ @"V:|-0-[newTabIcon(==24)]-16-[title(>=16)]-0-|", |
+ ]; |
+ ApplyVisualConstraints(constraintsInVerticallyCenteredView, viewsDictionary, |
+ _verticallyCenteredView); |
+ AddSameCenterXConstraint(_verticallyCenteredView, _newTabIcon.get()); |
+ } |
+ return self; |
+} |
+ |
+- (void)setTitle:(NSString*)titleString { |
+ [_titleLabel setText:titleString]; |
+ [self contentView].accessibilityLabel = titleString; |
+} |
+ |
+- (void)setSessionGURL:(GURL const&)gurl |
+ withBrowserState:(ios::ChromeBrowserState*)browserState { |
+ ios_internal::FaviconGetterCompletionBlock block = ^(UIImage* favicon) { |
+ UIColor* imageDominantColor = |
+ DominantColorForImage(gfx::Image(favicon), 1.0); |
+ MDCPalette* dominantPalette = |
+ [MDCPalette paletteGeneratedFromColor:imageDominantColor]; |
+ UIColor* backgroundColor = dominantPalette.tint300; |
+ UIColor* textColor = |
+ [MDFTextAccessibility textColorOnBackgroundColor:backgroundColor |
+ targetTextAlpha:kTitleLabelTextAlpha |
+ font:[_titleLabel font]]; |
+ UIColor* iconColor = |
+ [MDFTextAccessibility textColorOnBackgroundColor:backgroundColor |
+ targetTextAlpha:kNewTabIconAlpha |
+ font:[_titleLabel font]]; |
+ [_raisedButton setBackgroundColor:backgroundColor]; |
+ [_titleLabel setTextColor:textColor]; |
+ [_newTabIcon setTintColor:iconColor]; |
+ [_newTabIcon setAlpha:1.0]; |
+ [_favicon setImage:favicon]; |
+ [UIView animateWithDuration:0.2 |
+ animations:^{ |
+ [_raisedButton setAlpha:1.0]; |
+ }]; |
+ }; |
+ GURL gurlCopy = gurl; |
+ _faviconObtainer.reset([[NSBlockOperation blockOperationWithBlock:^{ |
+ ios_internal::GetFavicon(gurlCopy, browserState, block); |
+ }] retain]); |
+ NSOperationQueue* operationQueue = [NSOperationQueue mainQueue]; |
+ [operationQueue addOperation:_faviconObtainer]; |
+} |
+ |
+- (void)setDelegate:(id<SessionCellDelegate>)delegate { |
+ _delegate = delegate; |
+} |
+ |
+- (void)cellPressed { |
+ [_delegate cellPressed:self]; |
+} |
+ |
+- (void)prepareForReuse { |
+ [_newTabIcon setAlpha:0]; |
+ [_faviconObtainer cancel]; |
+ _faviconObtainer.reset(); |
+ [_raisedButton setAlpha:0]; |
+ [_raisedButton resetState]; |
+ [super prepareForReuse]; |
+} |
+ |
+@end |