OLD | NEW |
(Empty) | |
| 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 |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #import "ios/chrome/browser/ui/omnibox/truncating_attributed_label.h" |
| 6 |
| 7 #include <algorithm> |
| 8 |
| 9 #include "base/mac/objc_property_releaser.h" |
| 10 #include "base/mac/scoped_cftyperef.h" |
| 11 |
| 12 @interface OmniboxPopupTruncatingLabel () |
| 13 - (void)setup; |
| 14 - (UIImage*)getLinearGradient:(CGRect)rect; |
| 15 @end |
| 16 |
| 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_; |
| 24 } |
| 25 |
| 26 @synthesize truncateMode = truncateMode_; |
| 27 @synthesize attributedText = attributedText_; |
| 28 @synthesize highlighted = highlighted_; |
| 29 @synthesize highlightedText = highlightedText_; |
| 30 @synthesize textAlignment = textAlignment_; |
| 31 |
| 32 - (void)setup { |
| 33 self.backgroundColor = [UIColor clearColor]; |
| 34 self.contentMode = UIViewContentModeRedraw; |
| 35 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 } |
| 45 |
| 46 - (id)initWithFrame:(CGRect)frame { |
| 47 self = [super initWithFrame:frame]; |
| 48 if (self) { |
| 49 propertyReleaser_OmniboxPopupTruncatingLabel_.Init( |
| 50 self, [OmniboxPopupTruncatingLabel class]); |
| 51 [self setup]; |
| 52 } |
| 53 return self; |
| 54 } |
| 55 |
| 56 - (void)awakeFromNib { |
| 57 [super awakeFromNib]; |
| 58 [self setup]; |
| 59 } |
| 60 |
| 61 - (void)setFrame:(CGRect)frame { |
| 62 [super setFrame:frame]; |
| 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(); |
| 78 CGContextSaveGState(context); |
| 79 [textLayer_ setString:highlighted_ ? highlightedText_ : attributedText_]; |
| 80 CGContextClipToMask(context, self.bounds, [gradient_ CGImage]); |
| 81 [textLayer_ renderInContext:context]; |
| 82 |
| 83 CGContextRestoreGState(context); |
| 84 } |
| 85 |
| 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. |
| 105 - (UIImage*)getLinearGradient:(CGRect)rect { |
| 106 // Create an opaque context. |
| 107 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); |
| 108 CGContextRef context = |
| 109 CGBitmapContextCreate(NULL, rect.size.width, rect.size.height, 8, |
| 110 4 * rect.size.width, colorSpace, kCGImageAlphaNone); |
| 111 |
| 112 // White background will mask opaque, black gradient will mask transparent. |
| 113 CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); |
| 114 CGContextFillRect(context, rect); |
| 115 |
| 116 // Create gradient from white to black. |
| 117 CGFloat locs[2] = {0.0f, 1.0f}; |
| 118 CGFloat components[4] = {1.0f, 1.0f, 0.0f, 1.0f}; |
| 119 CGGradientRef gradient = |
| 120 CGGradientCreateWithColorComponents(colorSpace, components, locs, 2); |
| 121 CGColorSpaceRelease(colorSpace); |
| 122 |
| 123 // Draw head and/or tail gradient. |
| 124 CGFloat fadeWidth = |
| 125 std::min(rect.size.height * 2, (CGFloat)floor(rect.size.width / 4)); |
| 126 CGFloat minX = CGRectGetMinX(rect); |
| 127 CGFloat maxX = CGRectGetMaxX(rect); |
| 128 if (self.truncateMode & OmniboxPopupTruncatingTail) { |
| 129 CGFloat startX = maxX - fadeWidth; |
| 130 CGPoint startPoint = CGPointMake(startX, CGRectGetMidY(rect)); |
| 131 CGPoint endPoint = CGPointMake(maxX, CGRectGetMidY(rect)); |
| 132 CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0); |
| 133 } |
| 134 if (self.truncateMode & OmniboxPopupTruncatingHead) { |
| 135 CGFloat startX = minX + fadeWidth; |
| 136 CGPoint startPoint = CGPointMake(startX, CGRectGetMidY(rect)); |
| 137 CGPoint endPoint = CGPointMake(minX, CGRectGetMidY(rect)); |
| 138 CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0); |
| 139 } |
| 140 CGGradientRelease(gradient); |
| 141 |
| 142 // Clean up, return image. |
| 143 CGImageRef ref = CGBitmapContextCreateImage(context); |
| 144 UIImage* image = [UIImage imageWithCGImage:ref]; |
| 145 CGImageRelease(ref); |
| 146 CGContextRelease(context); |
| 147 return image; |
| 148 } |
| 149 |
| 150 @end |
OLD | NEW |