Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(206)

Side by Side Diff: ios/chrome/browser/ui/tabs/tab_view.mm

Issue 2588733002: Upstream Chrome on iOS source code [9/11]. (Closed)
Patch Set: Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ios/chrome/browser/ui/tabs/tab_view.h ('k') | ios/chrome/browser/ui/tabs/target_frame_cache.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1
2 // Copyright 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #import "ios/chrome/browser/ui/tabs/tab_view.h"
7
8 #include "base/i18n/rtl.h"
9 #include "base/ios/ios_util.h"
10 #include "base/logging.h"
11 #include "base/mac/objc_property_releaser.h"
12 #include "base/strings/sys_string_conversions.h"
13 #import "ios/chrome/browser/ui/colors/MDCPalette+CrAdditions.h"
14 #import "ios/chrome/browser/ui/commands/UIKit+ChromeExecuteCommand.h"
15 #import "ios/chrome/browser/ui/commands/generic_chrome_command.h"
16 #import "ios/chrome/browser/ui/image_util.h"
17 #include "ios/chrome/browser/ui/rtl_geometry.h"
18 #include "ios/chrome/browser/ui/tabs/tab_util.h"
19 #include "ios/chrome/browser/ui/ui_util.h"
20 #import "ios/chrome/browser/ui/uikit_ui_util.h"
21 #include "ios/chrome/grit/ios_strings.h"
22 #import "ios/third_party/material_components_ios/src/components/ActivityIndicato r/src/MaterialActivityIndicator.h"
23 #import "ios/third_party/material_components_ios/src/components/Typography/src/M aterialTypography.h"
24 #include "third_party/google_toolbox_for_mac/src/iPhone/GTMFadeTruncatingLabel.h "
25 #include "ui/base/l10n/l10n_util.h"
26 #include "ui/base/l10n/l10n_util_mac.h"
27 #include "ui/base/resource/resource_bundle.h"
28 #include "ui/gfx/image/image.h"
29 #import "ui/gfx/ios/uikit_util.h"
30
31 namespace {
32
33 // Tab close button insets.
34 const CGFloat kTabCloseTopInset = -1.0;
35 const CGFloat kTabCloseLeftInset = 0.0;
36 const CGFloat kTabCloseBottomInset = 0.0;
37 const CGFloat kTabCloseRightInset = 0.0;
38 const CGFloat kTabBackgroundLeftCapInset = 24.0;
39 const CGFloat kFaviconLeftInset = 23.5;
40 const CGFloat kFaviconVerticalOffset = 2.0;
41 const CGFloat kTabStripLineMargin = 2.5;
42 const CGFloat kTabStripLineHeight = 0.5;
43 const CGFloat kCloseButtonHorizontalShift = 15;
44 const CGFloat kCloseButtonVerticalShift = 4.0;
45 const CGFloat kTitleLeftMargin = 8.0;
46 const CGFloat kTitleRightMargin = 0.0;
47
48 const CGFloat kCloseButtonSize = 24.0;
49 const CGFloat kFaviconSize = 16.0;
50 }
51
52 @interface TabView () {
53 // Close button for this tab.
54 UIButton* _closeButton;
55
56 // View that draws the tab title.
57 GTMFadeTruncatingLabel* _titleLabel;
58
59 // Background image for this tab.
60 base::scoped_nsobject<UIImageView> _backgroundImageView;
61 // This view is used to draw a separator line at the bottom of the tab view.
62 // This view is hidden when the tab view is in a selected state.
63 base::scoped_nsobject<UIView> _lineSeparator;
64 BOOL _incognitoStyle;
65
66 // Set to YES when the layout constraints have been initialized.
67 BOOL _layoutConstraintsInitialized;
68
69 // Image view used to draw the favicon and spinner.
70 base::scoped_nsobject<UIImageView> _faviconView;
71
72 // If |YES|, this view will adjust its appearance and draw as a collapsed tab.
73 BOOL _collapsed;
74
75 base::scoped_nsobject<MDCActivityIndicator> _activityIndicator;
76
77 base::mac::ObjCPropertyReleaser _propertyReleaser_TabView;
78 }
79 @end
80
81 @interface TabView (Private)
82
83 // Creates the close button, favicon button, and title.
84 - (void)createButtonsAndLabel;
85
86 // Updates this tab's line separator color based on the current incognito style.
87 - (void)updateLineSeparator;
88
89 // Updates this tab's background image based on the value of |selected|.
90 - (void)updateBackgroundImage:(BOOL)selected;
91
92 // Updates this tab's close button image based on the current incognito style.
93 - (void)updateCloseButtonImages;
94
95 // Return the default favicon image based on the current incognito style.
96 - (UIImage*)defaultFaviconImage;
97
98 // Returns the rect in which to draw the favicon.
99 - (CGRect)faviconRectForBounds:(CGRect)bounds;
100
101 // Returns the rect in which to draw the tab title.
102 - (CGRect)titleRectForBounds:(CGRect)bounds;
103
104 // Returns the frame rect for the close button.
105 - (CGRect)closeRectForBounds:(CGRect)bounds;
106
107 @end
108
109 @implementation TabView
110
111 @synthesize closeButton = _closeButton;
112 @synthesize titleLabel = _titleLabel;
113 @synthesize collapsed = _collapsed;
114 @synthesize background = background_;
115 @synthesize incognitoStyle = _incognitoStyle;
116
117 - (id)initWithEmptyView:(BOOL)emptyView selected:(BOOL)selected {
118 if ((self = [super initWithFrame:CGRectZero])) {
119 _propertyReleaser_TabView.Init(self, [TabView class]);
120 [self setOpaque:NO];
121 [self createCommonViews];
122 // -setSelected only calls -updateBackgroundImage if the selected state
123 // changes. |isSelected| defaults to NO, so if |selected| is also NO,
124 // -updateBackgroundImage needs to be called explicitly.
125 [self setSelected:selected];
126 [self updateLineSeparator];
127 [self updateBackgroundImage:selected];
128 if (!emptyView)
129 [self createButtonsAndLabel];
130 }
131 return self;
132 }
133
134 - (void)setSelected:(BOOL)selected {
135 BOOL wasSelected = [self isSelected];
136 [super setSelected:selected];
137
138 [_lineSeparator setHidden:selected];
139
140 if (selected != wasSelected)
141 [self updateBackgroundImage:selected];
142
143 // It would make more sense to set active/inactive on tab_view itself, but
144 // tab_view is not an an accessible element, and making it one would add
145 // several complicated layers to UIA. Instead, simply set active/inactive
146 // here to be used by UIA.
147 [_closeButton setAccessibilityValue:(selected ? @"active" : @"inactive")];
148 }
149
150 - (void)setCollapsed:(BOOL)collapsed {
151 if (_collapsed != collapsed)
152 [_closeButton setHidden:collapsed];
153
154 _collapsed = collapsed;
155 }
156
157 - (void)setTitle:(NSString*)title {
158 if ([_titleLabel.text isEqualToString:title])
159 return;
160 if (base::i18n::GetStringDirection(base::SysNSStringToUTF16(title)) ==
161 base::i18n::RIGHT_TO_LEFT) {
162 [_titleLabel setTruncateMode:GTMFadeTruncatingHead];
163 } else {
164 [_titleLabel setTruncateMode:GTMFadeTruncatingTail];
165 }
166 _titleLabel.text = title;
167 }
168
169 - (UIImage*)favicon {
170 return [_faviconView image];
171 }
172
173 - (void)setFavicon:(UIImage*)favicon {
174 if (!favicon)
175 favicon = [self defaultFaviconImage];
176 [_faviconView setImage:favicon];
177 }
178
179 - (void)setIncognitoStyle:(BOOL)incognitoStyle {
180 _incognitoStyle = incognitoStyle;
181 _titleLabel.textColor =
182 incognitoStyle ? [UIColor whiteColor] : [UIColor blackColor];
183 [_faviconView setImage:[self defaultFaviconImage]];
184 [self updateLineSeparator];
185 [self updateCloseButtonImages];
186 [self updateBackgroundImage:[self isSelected]];
187 }
188
189 - (void)startProgressSpinner {
190 [_activityIndicator startAnimating];
191 [_activityIndicator setHidden:NO];
192 [_faviconView setHidden:YES];
193 }
194
195 - (void)stopProgressSpinner {
196 [_activityIndicator stopAnimating];
197 [_activityIndicator setHidden:YES];
198 [_faviconView setHidden:NO];
199 }
200
201 #pragma mark - UIView overrides
202
203 - (void)setFrame:(CGRect)frame {
204 const CGRect previousFrame = [self frame];
205 [super setFrame:frame];
206 // We are checking for a zero frame before triggering constraints updates in
207 // order to prevent computation of constraints that will never be used for the
208 // final layout. We could also initialize with a dummy frame but first this is
209 // inefficient and second it's non trivial to compute the minimum valid frame
210 // in regard to tweakable constants.
211 if (CGRectEqualToRect(CGRectZero, previousFrame) &&
212 !_layoutConstraintsInitialized) {
213 [self setNeedsUpdateConstraints];
214 }
215 }
216
217 - (void)updateConstraints {
218 [super updateConstraints];
219 if (!_layoutConstraintsInitialized &&
220 !CGRectEqualToRect(CGRectZero, self.frame)) {
221 _layoutConstraintsInitialized = YES;
222 [self addCommonConstraints];
223 // Add buttons and labels constraints if needed.
224 if (_closeButton)
225 [self addButtonsAndLabelConstraints];
226 }
227 }
228
229 - (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent*)event {
230 // Account for the trapezoidal shape of the tab. Inset the tab bounds by
231 // (y = -2.2x + 56), determined empirically from looking at the tab background
232 // images.
233 CGFloat inset = MAX(0.0, (point.y - 56) / -2.2);
234 return CGRectContainsPoint(CGRectInset([self bounds], inset, 0), point);
235 }
236
237 #pragma mark - Private
238
239 - (void)createCommonViews {
240 _backgroundImageView.reset([[UIImageView alloc] init]);
241 [_backgroundImageView setTranslatesAutoresizingMaskIntoConstraints:NO];
242 [self addSubview:_backgroundImageView];
243
244 _lineSeparator.reset([[UIView alloc] initWithFrame:CGRectZero]);
245 [_lineSeparator setTranslatesAutoresizingMaskIntoConstraints:NO];
246 [self addSubview:_lineSeparator];
247 }
248
249 - (void)addCommonConstraints {
250 NSDictionary* commonViewsDictionary = @{
251 @"backgroundImageView" : _backgroundImageView.get(),
252 @"lineSeparator" : _lineSeparator.get()
253 };
254 NSArray* commonConstraints = @[
255 @"H:|-0-[backgroundImageView]-0-|",
256 @"V:|-0-[backgroundImageView]-0-|",
257 @"H:|-tabStripLineMargin-[lineSeparator]-tabStripLineMargin-|",
258 @"V:[lineSeparator(==tabStripLineHeight)]-0-|",
259 ];
260 NSDictionary* commonMetrics = @{
261 @"tabStripLineMargin" : @(kTabStripLineMargin),
262 @"tabStripLineHeight" : @(kTabStripLineHeight)
263 };
264 ApplyVisualConstraintsWithMetrics(commonConstraints, commonViewsDictionary,
265 commonMetrics, self);
266 }
267
268 - (void)createButtonsAndLabel {
269 _closeButton = [[UIButton buttonWithType:UIButtonTypeCustom] retain];
270 [_closeButton setTranslatesAutoresizingMaskIntoConstraints:NO];
271 [_closeButton setImage:[UIImage imageNamed:@"tabstrip_tab_close"]
272 forState:UIControlStateNormal];
273 [_closeButton setImage:[UIImage imageNamed:@"tabstrip_tab_close_pressed"]
274 forState:UIControlStateHighlighted];
275 [_closeButton setContentEdgeInsets:UIEdgeInsetsMake(kTabCloseTopInset,
276 kTabCloseLeftInset,
277 kTabCloseBottomInset,
278 kTabCloseRightInset)];
279 [_closeButton setAccessibilityLabel:l10n_util::GetNSString(
280 IDS_IOS_TOOLS_MENU_CLOSE_TAB)];
281 [self addSubview:_closeButton];
282
283 // Add fade truncating label.
284 _titleLabel = [[GTMFadeTruncatingLabel alloc] initWithFrame:CGRectZero];
285 [_titleLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
286 [_titleLabel setFont:[MDCTypography body1Font]];
287 // Setting NSLineBreakByCharWrapping fixes an issue where the beginning of the
288 // text is truncated for RTL text writing direction. Anyway since the label is
289 // only one line and the end of the text is faded behind a gradient mask, it
290 // is visually almost equivalent to NSLineBreakByClipping.
291 [_titleLabel setLineBreakMode:NSLineBreakByCharWrapping];
292
293 [_titleLabel setTextAlignment:NSTextAlignmentNatural];
294 [self addSubview:_titleLabel];
295
296 CGRect faviconFrame = CGRectMake(0, 0, kFaviconSize, kFaviconSize);
297 _faviconView.reset([[UIImageView alloc] initWithFrame:faviconFrame]);
298 [_faviconView setTranslatesAutoresizingMaskIntoConstraints:NO];
299 [_faviconView setContentMode:UIViewContentModeScaleAspectFit];
300 [_faviconView setImage:[self defaultFaviconImage]];
301 [_faviconView setAccessibilityIdentifier:@"Favicon"];
302 [self addSubview:_faviconView];
303
304 _activityIndicator.reset(
305 [[MDCActivityIndicator alloc] initWithFrame:faviconFrame]);
306 [_activityIndicator setTranslatesAutoresizingMaskIntoConstraints:NO];
307 [_activityIndicator
308 setCycleColors:@[ [[MDCPalette cr_bluePalette] tint500] ]];
309 [_activityIndicator setRadius:ui::AlignValueToUpperPixel(kFaviconSize / 2)];
310 [self addSubview:_activityIndicator];
311 }
312
313 - (void)addButtonsAndLabelConstraints {
314 // Constraints on the Top bar, snapshot view, and shadow view.
315 NSDictionary* viewsDictionary = @{
316 @"close" : _closeButton,
317 @"title" : _titleLabel,
318 @"favicon" : _faviconView,
319 };
320 NSArray* constraints = @[
321 @"H:|-faviconLeftInset-[favicon(faviconSize)]",
322 @"V:|-faviconVerticalOffset-[favicon]-0-|",
323 @"H:[close(==closeButtonSize)]-closeButtonHorizontalShift-|",
324 @"V:|-closeButtonVerticalShift-[close]-0-|",
325 @"H:[favicon]-titleLeftMargin-[title]-titleRightMargin-[close]",
326 @"V:[title(==titleHeight)]",
327 ];
328 NSDictionary* metrics = @{
329 @"closeButtonSize" : @(kCloseButtonSize),
330 @"closeButtonHorizontalShift" : @(kCloseButtonHorizontalShift),
331 @"closeButtonVerticalShift" : @(kCloseButtonVerticalShift),
332 @"titleLeftMargin" : @(kTitleLeftMargin),
333 @"titleRightMargin" : @(kTitleRightMargin),
334 @"titleHeight" : @(kFaviconSize),
335 @"faviconLeftInset" : @(AlignValueToPixel(kFaviconLeftInset)),
336 @"faviconVerticalOffset" : @(kFaviconVerticalOffset),
337 @"faviconSize" : @(kFaviconSize),
338 };
339 ApplyVisualConstraintsWithMetrics(constraints, viewsDictionary, metrics,
340 self);
341 AddSameCenterXConstraint(self, _faviconView, _activityIndicator);
342 AddSameCenterYConstraint(self, _faviconView, _activityIndicator);
343 AddSameCenterYConstraint(self, _faviconView, _titleLabel);
344 }
345
346 - (void)updateLineSeparator {
347 UIColor* separatorColor =
348 _incognitoStyle ? [UIColor colorWithWhite:36 / 255.0 alpha:1.0]
349 : [UIColor colorWithWhite:185 / 255.0 alpha:1.0];
350 [_lineSeparator setBackgroundColor:separatorColor];
351 }
352
353 - (void)updateBackgroundImage:(BOOL)selected {
354 NSString* state = (selected ? @"foreground" : @"background");
355 NSString* incognito = _incognitoStyle ? @"incognito_" : @"";
356 NSString* imageName =
357 [NSString stringWithFormat:@"tabstrip_%@%@_tab", incognito, state];
358 UIImage* backgroundImage = StretchableImageFromUIImage(
359 [UIImage imageNamed:imageName], kTabBackgroundLeftCapInset, 0);
360 [_backgroundImageView setImage:backgroundImage];
361 }
362
363 - (void)updateCloseButtonImages {
364 UIImage* normalImage =
365 self.incognitoStyle ? [UIImage imageNamed:@"tabstrip_tab_close_incognito"]
366 : [UIImage imageNamed:@"tabstrip_tab_close"];
367 UIImage* pressedImage =
368 self.incognitoStyle
369 ? [UIImage imageNamed:@"tabstrip_tab_close_incognito_pressed"]
370 : [UIImage imageNamed:@"tabstrip_tab_close_pressed"];
371 [_closeButton setImage:normalImage forState:UIControlStateNormal];
372 [_closeButton setImage:pressedImage forState:UIControlStateHighlighted];
373 }
374
375 - (UIImage*)defaultFaviconImage {
376 return self.incognitoStyle ? [UIImage imageNamed:@"default_favicon_incognito"]
377 : [UIImage imageNamed:@"default_favicon"];
378 }
379
380 @end
OLDNEW
« no previous file with comments | « ios/chrome/browser/ui/tabs/tab_view.h ('k') | ios/chrome/browser/ui/tabs/target_frame_cache.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698