OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/chrome/browser/ui/omnibox/truncating_attributed_label.h" | 5 #import "ios/chrome/browser/ui/omnibox/truncating_attributed_label.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/mac/objc_property_releaser.h" | 9 #include "base/mac/objc_property_releaser.h" |
10 #include "base/mac/scoped_cftyperef.h" | 10 #include "base/mac/scoped_cftyperef.h" |
11 | 11 |
12 @interface OmniboxPopupTruncatingLabel () | 12 @interface OmniboxPopupTruncatingLabel () |
13 - (void)setup; | 13 - (void)setup; |
14 - (UIImage*)getLinearGradient:(CGRect)rect; | 14 - (UIImage*)getLinearGradient:(CGRect)rect; |
15 @end | 15 @end |
16 | 16 |
17 @implementation OmniboxPopupTruncatingLabel { | 17 @implementation OmniboxPopupTruncatingLabel { |
18 // Attributed text. | |
19 base::scoped_nsobject<CATextLayer> textLayer_; | |
20 // Gradient used to create fade effect. Changes based on view.frame size. | |
21 base::scoped_nsobject<UIImage> gradient_; | |
22 | |
23 base::mac::ObjCPropertyReleaser propertyReleaser_OmniboxPopupTruncatingLabel_; | 18 base::mac::ObjCPropertyReleaser propertyReleaser_OmniboxPopupTruncatingLabel_; |
24 } | 19 } |
25 | 20 |
26 @synthesize truncateMode = truncateMode_; | 21 @synthesize truncateMode = truncateMode_; |
27 @synthesize attributedText = attributedText_; | |
28 @synthesize highlighted = highlighted_; | |
29 @synthesize highlightedText = highlightedText_; | |
30 @synthesize textAlignment = textAlignment_; | |
31 | 22 |
32 - (void)setup { | 23 - (void)setup { |
33 self.backgroundColor = [UIColor clearColor]; | 24 self.backgroundColor = [UIColor clearColor]; |
34 self.contentMode = UIViewContentModeRedraw; | |
35 truncateMode_ = OmniboxPopupTruncatingTail; | 25 truncateMode_ = OmniboxPopupTruncatingTail; |
36 | |
37 // Disable animations in CATextLayer. | |
38 textLayer_.reset([[CATextLayer layer] retain]); | |
39 base::scoped_nsobject<NSDictionary> actions([[NSDictionary alloc] | |
40 initWithObjectsAndKeys:[NSNull null], @"contents", nil]); | |
41 [textLayer_ setActions:actions]; | |
42 [textLayer_ setFrame:self.bounds]; | |
43 [textLayer_ setContentsScale:[[UIScreen mainScreen] scale]]; | |
44 } | 26 } |
45 | 27 |
46 - (id)initWithFrame:(CGRect)frame { | 28 - (id)initWithFrame:(CGRect)frame { |
47 self = [super initWithFrame:frame]; | 29 self = [super initWithFrame:frame]; |
48 if (self) { | 30 if (self) { |
49 propertyReleaser_OmniboxPopupTruncatingLabel_.Init( | 31 propertyReleaser_OmniboxPopupTruncatingLabel_.Init( |
50 self, [OmniboxPopupTruncatingLabel class]); | 32 self, [OmniboxPopupTruncatingLabel class]); |
33 self.lineBreakMode = NSLineBreakByClipping; | |
51 [self setup]; | 34 [self setup]; |
52 } | 35 } |
53 return self; | 36 return self; |
54 } | 37 } |
55 | 38 |
56 - (void)awakeFromNib { | 39 - (void)awakeFromNib { |
57 [super awakeFromNib]; | 40 [super awakeFromNib]; |
58 [self setup]; | 41 [self setup]; |
59 } | 42 } |
60 | 43 |
61 - (void)setFrame:(CGRect)frame { | 44 // Draw fade gradient mask if text is wider than rect. |
62 [super setFrame:frame]; | 45 - (void)drawTextInRect:(CGRect)requestedRect { |
63 [textLayer_ setFrame:self.bounds]; | |
64 | |
65 // Cache the fade gradient when the frame changes. | |
66 if (!CGRectIsEmpty(frame) && | |
67 (!gradient_.get() || !CGSizeEqualToSize([gradient_ size], frame.size))) { | |
68 CGRect rect = CGRectMake(0, 0, frame.size.width, frame.size.height); | |
69 gradient_.reset([[self getLinearGradient:rect] retain]); | |
70 } | |
71 } | |
72 | |
73 - (void)drawRect:(CGRect)rect { | |
74 if ([attributedText_ length] == 0) | |
75 return; | |
76 | |
77 CGContextRef context = UIGraphicsGetCurrentContext(); | 46 CGContextRef context = UIGraphicsGetCurrentContext(); |
78 CGContextSaveGState(context); | 47 CGContextSaveGState(context); |
79 [textLayer_ setString:highlighted_ ? highlightedText_ : attributedText_]; | |
80 CGContextClipToMask(context, self.bounds, [gradient_ CGImage]); | |
81 [textLayer_ renderInContext:context]; | |
82 | 48 |
49 CGSize size = CGSizeZero; | |
50 if (self.attributedText) { | |
51 size = [self.attributedText size]; | |
52 } else if (self.font) { | |
53 size = [self.text sizeWithAttributes:@{NSFontAttributeName : self.font}]; | |
54 // sizeWithAttributes: may return fractional values, so ceil the width and | |
55 // height to preserve the behavior of sizeWithFont:. | |
56 size = CGSizeMake(ceil(size.width), ceil(size.height)); | |
57 } | |
58 | |
59 if (size.width > requestedRect.size.width) { | |
60 UIImage* image = [self getLinearGradient:requestedRect]; | |
justincohen
2017/02/20 20:07:24
Does it matter this isn't cached like it used to b
Justin Donnelly
2017/02/21 18:16:50
I don't think so. GTMFadeTruncatingLabel doesn't c
justincohen
2017/02/22 03:52:29
I had to go back to 2012 ( https://b.corp.google.c
Justin Donnelly
2017/02/22 17:17:42
I don't honestly know. Without the caching, there'
| |
61 CGContextClipToMask(context, self.bounds, image.CGImage); | |
62 } | |
63 | |
64 if (self.attributedText) { | |
65 NSMutableAttributedString* attributedString = | |
66 [[self.attributedText mutableCopy] autorelease]; | |
67 | |
68 NSMutableParagraphStyle* textStyle = | |
69 [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease]; | |
70 textStyle.lineBreakMode = self.lineBreakMode; | |
71 textStyle.alignment = self.textAlignment; | |
72 [attributedString addAttribute:NSParagraphStyleAttributeName | |
73 value:textStyle | |
74 range:NSMakeRange(0, [self.text length])]; | |
75 | |
76 [attributedString drawInRect:requestedRect]; | |
justincohen
2017/02/20 20:07:24
Is lineBreakMode and textAlignment different in se
Justin Donnelly
2017/02/21 18:16:50
No, they behave the same. Maybe it would be better
justincohen
2017/02/22 03:52:29
What's the default linebreakmode? If the client s
Justin Donnelly
2017/02/22 17:17:42
The default is NSLineBreakByTruncatingTail (traili
| |
77 } else if (self.font) { | |
78 // The UILabel docs say the default textColor is black and experimentation | |
79 // shows that calling -textColor will return the cached [UIColor blackColor] | |
80 // when called on a freshly alloc/init-ed UILabel, or a UILabel whose | |
81 // textColor has been set to nil. | |
82 // | |
83 // @see | |
84 // https://developer.apple.com/Library/ios/documentation/UIKit/Reference/UIL abel_Class/Reference/UILabel.html#//apple_ref/occ/instp/UILabel/textColor | |
85 // (NOTE(bgoodwin): interesting side-note. These docs also say setting | |
86 // textColor to nil will result in an exception. In my testing, that did not | |
87 // happen.) | |
88 NSMutableParagraphStyle* textStyle = | |
89 [[[NSParagraphStyle defaultParagraphStyle] mutableCopy] autorelease]; | |
90 textStyle.lineBreakMode = self.lineBreakMode; | |
91 textStyle.alignment = self.textAlignment; | |
92 NSDictionary* attributes = @{ | |
93 NSFontAttributeName : self.font, | |
94 NSParagraphStyleAttributeName : textStyle, | |
95 NSForegroundColorAttributeName : self.textColor | |
96 }; | |
97 [self.text drawInRect:requestedRect withAttributes:attributes]; | |
justincohen
2017/02/20 20:07:24
Why not just create an attributed string from text
Justin Donnelly
2017/02/21 18:16:50
It seemed simpler and safer to me to just copy the
| |
98 } | |
83 CGContextRestoreGState(context); | 99 CGContextRestoreGState(context); |
84 } | 100 } |
85 | 101 |
86 - (void)setTextAlignment:(NSTextAlignment)textAlignment { | |
87 if (textAlignment == NSTextAlignmentLeft) { | |
88 [textLayer_ setAlignmentMode:kCAAlignmentLeft]; | |
89 self.truncateMode = OmniboxPopupTruncatingTail; | |
90 } else if (textAlignment == NSTextAlignmentRight) { | |
91 [textLayer_ setAlignmentMode:kCAAlignmentRight]; | |
92 self.truncateMode = OmniboxPopupTruncatingHead; | |
93 } else if (textAlignment == NSTextAlignmentNatural) { | |
94 [textLayer_ setAlignmentMode:kCAAlignmentNatural]; | |
95 self.truncateMode = OmniboxPopupTruncatingTail; | |
96 } else { | |
97 NOTREACHED(); | |
98 } | |
99 if (textAlignment != textAlignment_) | |
100 gradient_.reset(); | |
101 textAlignment_ = textAlignment; | |
102 } | |
103 | |
104 // Create gradient opacity mask based on direction. | 102 // Create gradient opacity mask based on direction. |
105 - (UIImage*)getLinearGradient:(CGRect)rect { | 103 - (UIImage*)getLinearGradient:(CGRect)rect { |
106 // Create an opaque context. | 104 // Create an opaque context. |
107 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); | 105 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); |
108 CGContextRef context = | 106 CGContextRef context = |
109 CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, 8, | 107 CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, 8, |
110 4 * rect.size.width, colorSpace, kCGImageAlphaNone); | 108 4 * rect.size.width, colorSpace, kCGImageAlphaNone); |
111 | 109 |
112 // White background will mask opaque, black gradient will mask transparent. | 110 // White background will mask opaque, black gradient will mask transparent. |
113 CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); | 111 CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); |
(...skipping 27 matching lines...) Expand all Loading... | |
141 | 139 |
142 // Clean up, return image. | 140 // Clean up, return image. |
143 CGImageRef ref = CGBitmapContextCreateImage(context); | 141 CGImageRef ref = CGBitmapContextCreateImage(context); |
144 UIImage* image = [UIImage imageWithCGImage:ref]; | 142 UIImage* image = [UIImage imageWithCGImage:ref]; |
145 CGImageRelease(ref); | 143 CGImageRelease(ref); |
146 CGContextRelease(context); | 144 CGContextRelease(context); |
147 return image; | 145 return image; |
148 } | 146 } |
149 | 147 |
150 @end | 148 @end |
OLD | NEW |