Index: ios/chrome/browser/ui/omnibox/truncating_attributed_label.mm |
diff --git a/ios/chrome/browser/ui/omnibox/truncating_attributed_label.mm b/ios/chrome/browser/ui/omnibox/truncating_attributed_label.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c4a13e49895e5c9e4f4fb2d622700fc8b1bbc867 |
--- /dev/null |
+++ b/ios/chrome/browser/ui/omnibox/truncating_attributed_label.mm |
@@ -0,0 +1,150 @@ |
+// Copyright (c) 2012 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/omnibox/truncating_attributed_label.h" |
+ |
+#include <algorithm> |
+ |
+#include "base/mac/objc_property_releaser.h" |
+#include "base/mac/scoped_cftyperef.h" |
+ |
+@interface OmniboxPopupTruncatingLabel () |
+- (void)setup; |
+- (UIImage*)getLinearGradient:(CGRect)rect; |
+@end |
+ |
+@implementation OmniboxPopupTruncatingLabel { |
+ // Attributed text. |
+ base::scoped_nsobject<CATextLayer> textLayer_; |
+ // Gradient used to create fade effect. Changes based on view.frame size. |
+ base::scoped_nsobject<UIImage> gradient_; |
+ |
+ base::mac::ObjCPropertyReleaser propertyReleaser_OmniboxPopupTruncatingLabel_; |
+} |
+ |
+@synthesize truncateMode = truncateMode_; |
+@synthesize attributedText = attributedText_; |
+@synthesize highlighted = highlighted_; |
+@synthesize highlightedText = highlightedText_; |
+@synthesize textAlignment = textAlignment_; |
+ |
+- (void)setup { |
+ self.backgroundColor = [UIColor clearColor]; |
+ self.contentMode = UIViewContentModeRedraw; |
+ truncateMode_ = OmniboxPopupTruncatingTail; |
+ |
+ // Disable animations in CATextLayer. |
+ textLayer_.reset([[CATextLayer layer] retain]); |
+ base::scoped_nsobject<NSDictionary> actions([[NSDictionary alloc] |
+ initWithObjectsAndKeys:[NSNull null], @"contents", nil]); |
+ [textLayer_ setActions:actions]; |
+ [textLayer_ setFrame:self.bounds]; |
+ [textLayer_ setContentsScale:[[UIScreen mainScreen] scale]]; |
+} |
+ |
+- (id)initWithFrame:(CGRect)frame { |
+ self = [super initWithFrame:frame]; |
+ if (self) { |
+ propertyReleaser_OmniboxPopupTruncatingLabel_.Init( |
+ self, [OmniboxPopupTruncatingLabel class]); |
+ [self setup]; |
+ } |
+ return self; |
+} |
+ |
+- (void)awakeFromNib { |
+ [super awakeFromNib]; |
+ [self setup]; |
+} |
+ |
+- (void)setFrame:(CGRect)frame { |
+ [super setFrame:frame]; |
+ [textLayer_ setFrame:self.bounds]; |
+ |
+ // Cache the fade gradient when the frame changes. |
+ if (!CGRectIsEmpty(frame) && |
+ (!gradient_.get() || !CGSizeEqualToSize([gradient_ size], frame.size))) { |
+ CGRect rect = CGRectMake(0, 0, frame.size.width, frame.size.height); |
+ gradient_.reset([[self getLinearGradient:rect] retain]); |
+ } |
+} |
+ |
+- (void)drawRect:(CGRect)rect { |
+ if ([attributedText_ length] == 0) |
+ return; |
+ |
+ CGContextRef context = UIGraphicsGetCurrentContext(); |
+ CGContextSaveGState(context); |
+ [textLayer_ setString:highlighted_ ? highlightedText_ : attributedText_]; |
+ CGContextClipToMask(context, self.bounds, [gradient_ CGImage]); |
+ [textLayer_ renderInContext:context]; |
+ |
+ CGContextRestoreGState(context); |
+} |
+ |
+- (void)setTextAlignment:(NSTextAlignment)textAlignment { |
+ if (textAlignment == NSTextAlignmentLeft) { |
+ [textLayer_ setAlignmentMode:kCAAlignmentLeft]; |
+ self.truncateMode = OmniboxPopupTruncatingTail; |
+ } else if (textAlignment == NSTextAlignmentRight) { |
+ [textLayer_ setAlignmentMode:kCAAlignmentRight]; |
+ self.truncateMode = OmniboxPopupTruncatingHead; |
+ } else if (textAlignment == NSTextAlignmentNatural) { |
+ [textLayer_ setAlignmentMode:kCAAlignmentNatural]; |
+ self.truncateMode = OmniboxPopupTruncatingTail; |
+ } else { |
+ NOTREACHED(); |
+ } |
+ if (textAlignment != textAlignment_) |
+ gradient_.reset(); |
+ textAlignment_ = textAlignment; |
+} |
+ |
+// Create gradient opacity mask based on direction. |
+- (UIImage*)getLinearGradient:(CGRect)rect { |
+ // Create an opaque context. |
+ CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); |
+ CGContextRef context = |
+ CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, 8, |
+ 4 * rect.size.width, colorSpace, kCGImageAlphaNone); |
+ |
+ // White background will mask opaque, black gradient will mask transparent. |
+ CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); |
+ CGContextFillRect(context, rect); |
+ |
+ // Create gradient from white to black. |
+ CGFloat locs[2] = {0.0f, 1.0f}; |
+ CGFloat components[4] = {1.0f, 1.0f, 0.0f, 1.0f}; |
+ CGGradientRef gradient = |
+ CGGradientCreateWithColorComponents(colorSpace, components, locs, 2); |
+ CGColorSpaceRelease(colorSpace); |
+ |
+ // Draw head and/or tail gradient. |
+ CGFloat fadeWidth = |
+ std::min(rect.size.height * 2, (CGFloat)floor(rect.size.width / 4)); |
+ CGFloat minX = CGRectGetMinX(rect); |
+ CGFloat maxX = CGRectGetMaxX(rect); |
+ if (self.truncateMode & OmniboxPopupTruncatingTail) { |
+ CGFloat startX = maxX - fadeWidth; |
+ CGPoint startPoint = CGPointMake(startX, CGRectGetMidY(rect)); |
+ CGPoint endPoint = CGPointMake(maxX, CGRectGetMidY(rect)); |
+ CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0); |
+ } |
+ if (self.truncateMode & OmniboxPopupTruncatingHead) { |
+ CGFloat startX = minX + fadeWidth; |
+ CGPoint startPoint = CGPointMake(startX, CGRectGetMidY(rect)); |
+ CGPoint endPoint = CGPointMake(minX, CGRectGetMidY(rect)); |
+ CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0); |
+ } |
+ CGGradientRelease(gradient); |
+ |
+ // Clean up, return image. |
+ CGImageRef ref = CGBitmapContextCreateImage(context); |
+ UIImage* image = [UIImage imageWithCGImage:ref]; |
+ CGImageRelease(ref); |
+ CGContextRelease(context); |
+ return image; |
+} |
+ |
+@end |