OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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/util/label_link_controller.h" | 5 #import "ios/chrome/browser/ui/util/label_link_controller.h" |
6 | 6 |
7 #include <map> | 7 #include <map> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/ios/ios_util.h" | 10 #include "base/ios/ios_util.h" |
11 #include "base/ios/weak_nsobject.h" | |
12 #include "base/logging.h" | 11 #include "base/logging.h" |
13 #include "base/mac/foundation_util.h" | 12 #include "base/mac/foundation_util.h" |
14 #include "base/mac/scoped_block.h" | |
15 #import "base/mac/scoped_nsobject.h" | |
16 #import "base/strings/sys_string_conversions.h" | 13 #import "base/strings/sys_string_conversions.h" |
17 #include "ios/chrome/browser/ui/ui_util.h" | 14 #include "ios/chrome/browser/ui/ui_util.h" |
18 #import "ios/chrome/browser/ui/util/label_observer.h" | 15 #import "ios/chrome/browser/ui/util/label_observer.h" |
19 #import "ios/chrome/browser/ui/util/text_region_mapper.h" | 16 #import "ios/chrome/browser/ui/util/text_region_mapper.h" |
20 #import "ios/chrome/browser/ui/util/transparent_link_button.h" | 17 #import "ios/chrome/browser/ui/util/transparent_link_button.h" |
21 #import "net/base/mac/url_conversions.h" | 18 #import "net/base/mac/url_conversions.h" |
22 #include "url/gurl.h" | 19 #include "url/gurl.h" |
23 | 20 |
| 21 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 22 #error "This file requires ARC support." |
| 23 #endif |
| 24 |
24 #pragma mark - LinkLayout | 25 #pragma mark - LinkLayout |
25 | 26 |
26 // Object encapsulating the range of a link and the frames corresponding with | 27 // Object encapsulating the range of a link and the frames corresponding with |
27 // that range. | 28 // that range. |
28 @interface LinkLayout : NSObject { | 29 @interface LinkLayout : NSObject |
29 // Backing objects for properties of same name. | |
30 base::scoped_nsobject<NSArray> _frames; | |
31 } | |
32 | 30 |
33 // Designated initializer. | 31 // Designated initializer. |
34 - (instancetype)initWithRange:(NSRange)range NS_DESIGNATED_INITIALIZER; | 32 - (instancetype)initWithRange:(NSRange)range NS_DESIGNATED_INITIALIZER; |
35 - (instancetype)init NS_UNAVAILABLE; | 33 - (instancetype)init NS_UNAVAILABLE; |
36 | 34 |
37 // The range passed on initialization. | 35 // The range passed on initialization. |
38 @property(nonatomic, readonly) NSRange range; | 36 @property(nonatomic, readonly) NSRange range; |
39 | 37 |
40 // The frames calculated for |_range|. | 38 // The frames calculated for |_range|. |
41 @property(nonatomic, retain) NSArray* frames; | 39 @property(nonatomic, strong) NSArray* frames; |
42 | 40 |
43 @end | 41 @end |
44 | 42 |
45 @implementation LinkLayout | 43 @implementation LinkLayout |
46 | 44 |
47 @synthesize range = _range; | 45 @synthesize range = _range; |
| 46 @synthesize frames = _frames; |
48 | 47 |
49 - (instancetype)initWithRange:(NSRange)range { | 48 - (instancetype)initWithRange:(NSRange)range { |
50 if ((self = [super init])) { | 49 if ((self = [super init])) { |
51 DCHECK_NE(range.location, static_cast<NSUInteger>(NSNotFound)); | 50 DCHECK_NE(range.location, static_cast<NSUInteger>(NSNotFound)); |
52 DCHECK_NE(range.length, 0U); | 51 DCHECK_NE(range.length, 0U); |
53 _range = range; | 52 _range = range; |
54 } | 53 } |
55 return self; | 54 return self; |
56 } | 55 } |
57 | 56 |
58 #pragma mark - Accessors | |
59 | |
60 - (void)setFrames:(NSArray*)frames { | |
61 _frames.reset([frames retain]); | |
62 } | |
63 | |
64 - (NSArray*)frames { | |
65 return _frames.get(); | |
66 } | |
67 | |
68 @end | 57 @end |
69 | 58 |
70 #pragma mark - LabelLinkController | 59 #pragma mark - LabelLinkController |
71 | 60 |
72 @interface LabelLinkController () | 61 @interface LabelLinkController () |
73 // Private property exposed publically in testing interface. | 62 // Private property exposed publically in testing interface. |
74 @property(nonatomic, assign) Class textMapperClass; | 63 @property(nonatomic, unsafe_unretained) Class textMapperClass; |
75 | 64 |
76 // The original attributed text set on the label. This may be different from | 65 // The original attributed text set on the label. This may be different from |
77 // the label's |attributedText| property, as additional style attributes may be | 66 // the label's |attributedText| property, as additional style attributes may be |
78 // introduced for links. | 67 // introduced for links. |
79 @property(nonatomic, readonly) NSAttributedString* originalLabelText; | 68 @property(nonatomic, strong, readonly) NSAttributedString* originalLabelText; |
80 | 69 |
81 // The array of TransparentLinkButtons inserted above the label. | 70 // The array of TransparentLinkButtons inserted above the label. |
82 @property(nonatomic, readonly) NSArray* linkButtons; | 71 @property(nonatomic, strong, readonly) NSMutableArray* linkButtons; |
83 | 72 |
84 // Adds LabelObserverActions to the LabelObserver corresponding to |_label|. | 73 // Adds LabelObserverActions to the LabelObserver corresponding to |_label|. |
85 - (void)addLabelObserverActions; | 74 - (void)addLabelObserverActions; |
86 | 75 |
87 // Clears all defined links and any data associated with them. Update the | 76 // Clears all defined links and any data associated with them. Update the |
88 // original attributed text from the controlled label. | 77 // original attributed text from the controlled label. |
89 - (void)reset; | 78 - (void)reset; |
90 | 79 |
91 // Handle a change to the label that changes the positioning of glyphs but not | 80 // Handle a change to the label that changes the positioning of glyphs but not |
92 // any styling of those glyphs. Forces a recomputation of the tap regions, and | 81 // any styling of those glyphs. Forces a recomputation of the tap regions, and |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
125 // are cleared. | 114 // are cleared. |
126 // If there are tap buttons, but they are not subviews of |_label|'s superview | 115 // If there are tap buttons, but they are not subviews of |_label|'s superview |
127 // (if _label's superview has changed since the buttons were created), then | 116 // (if _label's superview has changed since the buttons were created), then |
128 // the tap buttons are migrated into the new superview. | 117 // the tap buttons are migrated into the new superview. |
129 - (void)updateTapButtons; | 118 - (void)updateTapButtons; |
130 | 119 |
131 @end | 120 @end |
132 | 121 |
133 @implementation LabelLinkController { | 122 @implementation LabelLinkController { |
134 // Ivars immutable for the lifetime of the object. | 123 // Ivars immutable for the lifetime of the object. |
135 base::mac::ScopedBlock<ProceduralBlockWithURL> _action; | 124 ProceduralBlockWithURL _action; |
136 base::scoped_nsobject<UILabel> _label; | 125 UILabel* _label; |
137 base::scoped_nsobject<UITapGestureRecognizer> _linkTapRecognizer; | 126 UITapGestureRecognizer* _linkTapRecognizer; |
138 | |
139 // Ivas backing properties. | |
140 base::scoped_nsobject<UIColor> _linkColor; | |
141 base::scoped_nsobject<UIFont> _linkFont; | |
142 | 127 |
143 // Ivars that reset when label text changes. | 128 // Ivars that reset when label text changes. |
144 base::scoped_nsobject<NSMutableDictionary> _layoutsForURLs; | 129 NSMutableDictionary* _layoutsForURLs; |
145 base::scoped_nsobject<NSAttributedString> _originalLabelText; | |
146 CGRect _lastLabelFrame; | 130 CGRect _lastLabelFrame; |
147 | 131 |
148 // Ivars that reset when text or bounds change. | 132 // Ivars that reset when text or bounds change. |
149 base::scoped_nsprotocol<id<TextRegionMapper>> _textMapper; | 133 id<TextRegionMapper> _textMapper; |
150 | 134 |
151 // Internal tracking. | 135 // Internal tracking. |
152 BOOL _justUpdatedStyles; | 136 BOOL _justUpdatedStyles; |
153 base::scoped_nsobject<NSMutableArray> _linkButtons; | 137 LabelObserver* _labelObserver; |
154 base::scoped_nsobject<LabelObserver> _labelObserver; | |
155 } | 138 } |
156 | 139 |
157 @synthesize showTapAreas = _showTapAreas; | 140 @synthesize showTapAreas = _showTapAreas; |
158 @synthesize textMapperClass = _textMapperClass; | 141 @synthesize textMapperClass = _textMapperClass; |
159 @synthesize linkUnderlineStyle = _linkUnderlineStyle; | 142 @synthesize linkUnderlineStyle = _linkUnderlineStyle; |
| 143 @synthesize linkButtons = _linkButtons; |
| 144 @synthesize originalLabelText = _originalLabelText; |
| 145 @synthesize linkFont = _linkFont; |
| 146 @synthesize linkColor = _linkColor; |
160 | 147 |
161 - (instancetype)initWithLabel:(UILabel*)label | 148 - (instancetype)initWithLabel:(UILabel*)label |
162 action:(ProceduralBlockWithURL)action { | 149 action:(ProceduralBlockWithURL)action { |
163 if ((self = [super init])) { | 150 if ((self = [super init])) { |
164 DCHECK(label); | 151 DCHECK(label); |
165 _label.reset([label retain]); | 152 _label = label; |
166 _action.reset(action, base::scoped_policy::RETAIN); | 153 _action = [action copy]; |
167 _linkUnderlineStyle = NSUnderlineStyleNone; | 154 _linkUnderlineStyle = NSUnderlineStyleNone; |
168 [self reset]; | 155 [self reset]; |
169 | 156 |
170 _labelObserver.reset([[LabelObserver observerForLabel:_label] retain]); | 157 _labelObserver = [LabelObserver observerForLabel:_label]; |
171 [_labelObserver startObserving]; | 158 [_labelObserver startObserving]; |
172 [self addLabelObserverActions]; | 159 [self addLabelObserverActions]; |
173 | 160 |
174 self.textMapperClass = [CoreTextRegionMapper class]; | 161 self.textMapperClass = [CoreTextRegionMapper class]; |
175 _linkButtons.reset([[NSMutableArray alloc] init]); | 162 _linkButtons = [[NSMutableArray alloc] init]; |
176 } | 163 } |
177 return self; | 164 return self; |
178 } | 165 } |
179 | 166 |
180 - (NSAttributedString*)originalLabelText { | |
181 return _originalLabelText.get(); | |
182 } | |
183 | |
184 - (NSArray*)linkButtons { | |
185 return _linkButtons.get(); | |
186 } | |
187 | |
188 - (void)addLabelObserverActions { | 167 - (void)addLabelObserverActions { |
189 base::WeakNSObject<LabelLinkController> weakSelf(self); | 168 __weak LabelLinkController* weakSelf = self; |
190 [_labelObserver addStyleChangedAction:^(UILabel* label) { | 169 [_labelObserver addStyleChangedAction:^(UILabel* label) { |
191 // One of the style properties has been changed, which will silently | 170 // One of the style properties has been changed, which will silently |
192 // update the label's attributedText. | 171 // update the label's attributedText. |
193 if (!weakSelf) | 172 if (!weakSelf) |
194 return; | 173 return; |
195 base::scoped_nsobject<LabelLinkController> strongSelf([weakSelf retain]); | 174 LabelLinkController* strongSelf = weakSelf; |
196 [strongSelf labelStyleInvalidated]; | 175 [strongSelf labelStyleInvalidated]; |
197 }]; | 176 }]; |
198 [_labelObserver addTextChangedAction:^(UILabel* label) { | 177 [_labelObserver addTextChangedAction:^(UILabel* label) { |
199 if (!weakSelf) | 178 if (!weakSelf) |
200 return; | 179 return; |
201 base::scoped_nsobject<LabelLinkController> strongSelf([weakSelf retain]); | 180 LabelLinkController* strongSelf = weakSelf; |
202 NSString* originalText = [[strongSelf originalLabelText] string]; | 181 NSString* originalText = [[strongSelf originalLabelText] string]; |
203 if ([label.text isEqualToString:originalText]) { | 182 if ([label.text isEqualToString:originalText]) { |
204 // The actual text of the label didn't change, so this was a change to | 183 // The actual text of the label didn't change, so this was a change to |
205 // the string attributes only. | 184 // the string attributes only. |
206 [strongSelf labelStyleInvalidated]; | 185 [strongSelf labelStyleInvalidated]; |
207 } else { | 186 } else { |
208 // The label text has changed, so start everything from scratch. | 187 // The label text has changed, so start everything from scratch. |
209 [strongSelf reset]; | 188 [strongSelf reset]; |
210 } | 189 } |
211 }]; | 190 }]; |
212 [_labelObserver addLayoutChangedAction:^(UILabel* label) { | 191 [_labelObserver addLayoutChangedAction:^(UILabel* label) { |
213 if (!weakSelf) | 192 if (!weakSelf) |
214 return; | 193 return; |
215 base::scoped_nsobject<LabelLinkController> strongSelf([weakSelf retain]); | 194 LabelLinkController* strongSelf = weakSelf; |
216 [strongSelf labelLayoutInvalidated]; | 195 [strongSelf labelLayoutInvalidated]; |
217 NSArray* linkButtons = [strongSelf linkButtons]; | 196 NSArray* linkButtons = [strongSelf linkButtons]; |
218 // If this layout change corresponds to |label|'s moving to a new superview, | 197 // If this layout change corresponds to |label|'s moving to a new superview, |
219 // update the tap buttons so that they are inserted above |label| in the new | 198 // update the tap buttons so that they are inserted above |label| in the new |
220 // hierarchy. | 199 // hierarchy. |
221 if (linkButtons.count && label.superview != [linkButtons[0] superview]) | 200 if (linkButtons.count && label.superview != [linkButtons[0] superview]) |
222 [strongSelf updateTapButtons]; | 201 [strongSelf updateTapButtons]; |
223 }]; | 202 }]; |
224 } | 203 } |
225 | 204 |
226 - (void)dealloc { | 205 - (void)dealloc { |
227 [self clearTapButtons]; | 206 [self clearTapButtons]; |
228 [_labelObserver stopObserving]; | 207 [_labelObserver stopObserving]; |
229 [super dealloc]; | |
230 } | 208 } |
231 | 209 |
232 - (void)addLinkWithRange:(NSRange)range url:(GURL)url { | 210 - (void)addLinkWithRange:(NSRange)range url:(GURL)url { |
233 DCHECK(url.is_valid()); | 211 DCHECK(url.is_valid()); |
234 if (!_layoutsForURLs) | 212 if (!_layoutsForURLs) |
235 _layoutsForURLs.reset([[NSMutableDictionary alloc] init]); | 213 _layoutsForURLs = [[NSMutableDictionary alloc] init]; |
236 NSURL* key = net::NSURLWithGURL(url); | 214 NSURL* key = net::NSURLWithGURL(url); |
237 base::scoped_nsobject<LinkLayout> layout( | 215 LinkLayout* layout = [[LinkLayout alloc] initWithRange:range]; |
238 [[LinkLayout alloc] initWithRange:range]); | |
239 [_layoutsForURLs setObject:layout forKey:key]; | 216 [_layoutsForURLs setObject:layout forKey:key]; |
240 [self updateStyles]; | 217 [self updateStyles]; |
241 } | 218 } |
242 | 219 |
243 - (UIColor*)linkColor { | |
244 return _linkColor.get(); | |
245 } | |
246 | |
247 - (void)setLinkColor:(UIColor*)linkColor { | 220 - (void)setLinkColor:(UIColor*)linkColor { |
248 _linkColor.reset([linkColor copy]); | 221 _linkColor = [linkColor copy]; |
249 [self updateStyles]; | 222 [self updateStyles]; |
250 } | 223 } |
251 | 224 |
252 - (void)setLinkUnderlineStyle:(NSUnderlineStyle)underlineStyle { | 225 - (void)setLinkUnderlineStyle:(NSUnderlineStyle)underlineStyle { |
253 _linkUnderlineStyle = underlineStyle; | 226 _linkUnderlineStyle = underlineStyle; |
254 [self updateStyles]; | 227 [self updateStyles]; |
255 } | 228 } |
256 | 229 |
257 - (UIFont*)linkFont { | |
258 return _linkFont.get(); | |
259 } | |
260 | |
261 - (void)setLinkFont:(UIFont*)linkFont { | 230 - (void)setLinkFont:(UIFont*)linkFont { |
262 _linkFont.reset([linkFont retain]); | 231 _linkFont = linkFont; |
263 [self updateStyles]; | 232 [self updateStyles]; |
264 } | 233 } |
265 | 234 |
266 - (void)setShowTapAreas:(BOOL)showTapAreas { | 235 - (void)setShowTapAreas:(BOOL)showTapAreas { |
267 #ifndef NDEBUG | 236 #ifndef NDEBUG |
268 for (TransparentLinkButton* button in _linkButtons.get()) { | 237 for (TransparentLinkButton* button in _linkButtons) { |
269 button.debug = showTapAreas; | 238 button.debug = showTapAreas; |
270 } | 239 } |
271 #endif // NDEBUG | 240 #endif // NDEBUG |
272 _showTapAreas = showTapAreas; | 241 _showTapAreas = showTapAreas; |
273 } | 242 } |
274 | 243 |
275 #pragma mark - internal methods | 244 #pragma mark - internal methods |
276 | 245 |
277 - (void)reset { | 246 - (void)reset { |
278 _originalLabelText.reset([[_label attributedText] copy]); | 247 _originalLabelText = [[_label attributedText] copy]; |
279 _textMapper.reset(); | 248 _textMapper = nil; |
280 _lastLabelFrame = CGRectZero; | 249 _lastLabelFrame = CGRectZero; |
281 _layoutsForURLs.reset(); | 250 _layoutsForURLs = nil; |
282 } | 251 } |
283 | 252 |
284 - (void)labelLayoutInvalidated { | 253 - (void)labelLayoutInvalidated { |
285 _textMapper.reset(); | 254 _textMapper = nil; |
286 [self updateTapRects]; | 255 [self updateTapRects]; |
287 } | 256 } |
288 | 257 |
289 - (void)labelStyleInvalidated { | 258 - (void)labelStyleInvalidated { |
290 // If the style invalidation was triggered by this class updating link styles, | 259 // If the style invalidation was triggered by this class updating link styles, |
291 // then the original label text is still correct, but the tap rects still need | 260 // then the original label text is still correct, but the tap rects still need |
292 // to be updated. Otherwise, update the original label text, and then update | 261 // to be updated. Otherwise, update the original label text, and then update |
293 // styles. This will set |_justUpdatedStyles| and trigger another call to | 262 // styles. This will set |_justUpdatedStyles| and trigger another call to |
294 // this method via KVO. | 263 // this method via KVO. |
295 if (_justUpdatedStyles) { | 264 if (_justUpdatedStyles) { |
296 // TODO(crbug.com/664648): Remove _justUpdatedStyles due to bug that | 265 // TODO(crbug.com/664648): Remove _justUpdatedStyles due to bug that |
297 // prevents proper style updates after successive label format changes. | 266 // prevents proper style updates after successive label format changes. |
298 _justUpdatedStyles = NO; | 267 _justUpdatedStyles = NO; |
299 } else if (![_originalLabelText isEqual:[_label attributedText]]) { | 268 } else if (![_originalLabelText isEqual:[_label attributedText]]) { |
300 _originalLabelText.reset([[_label attributedText] copy]); | 269 _originalLabelText = [[_label attributedText] copy]; |
301 [self updateStyles]; | 270 [self updateStyles]; |
302 } | 271 } |
303 _lastLabelFrame = CGRectZero; | 272 _lastLabelFrame = CGRectZero; |
304 [self labelLayoutInvalidated]; | 273 [self labelLayoutInvalidated]; |
305 } | 274 } |
306 | 275 |
307 - (void)updateStyles { | 276 - (void)updateStyles { |
308 if (![_layoutsForURLs count]) | 277 if (![_layoutsForURLs count]) |
309 return; | 278 return; |
310 | 279 |
311 __block base::scoped_nsobject<NSMutableAttributedString> labelText( | 280 __block NSMutableAttributedString* labelText = |
312 [_originalLabelText mutableCopy]); | 281 [_originalLabelText mutableCopy]; |
313 [_layoutsForURLs enumerateKeysAndObjectsUsingBlock:^( | 282 [_layoutsForURLs enumerateKeysAndObjectsUsingBlock:^( |
314 NSURL* key, LinkLayout* layout, BOOL* stop) { | 283 NSURL* key, LinkLayout* layout, BOOL* stop) { |
315 if (_linkColor) { | 284 if (_linkColor) { |
316 [labelText addAttribute:NSForegroundColorAttributeName | 285 [labelText addAttribute:NSForegroundColorAttributeName |
317 value:_linkColor | 286 value:_linkColor |
318 range:layout.range]; | 287 range:layout.range]; |
319 } | 288 } |
320 if (_linkUnderlineStyle != NSUnderlineStyleNone) { | 289 if (_linkUnderlineStyle != NSUnderlineStyleNone) { |
321 [labelText addAttribute:NSUnderlineStyleAttributeName | 290 [labelText addAttribute:NSUnderlineStyleAttributeName |
322 value:@(_linkUnderlineStyle) | 291 value:@(_linkUnderlineStyle) |
323 range:layout.range]; | 292 range:layout.range]; |
324 } | 293 } |
325 if (_linkFont) { | 294 if (_linkFont) { |
326 [labelText addAttribute:NSFontAttributeName | 295 [labelText addAttribute:NSFontAttributeName |
327 value:_linkFont | 296 value:_linkFont |
328 range:layout.range]; | 297 range:layout.range]; |
329 } | 298 } |
330 }]; | 299 }]; |
331 _justUpdatedStyles = YES; | 300 _justUpdatedStyles = YES; |
332 [_label setAttributedText:labelText]; | 301 [_label setAttributedText:labelText]; |
333 _textMapper.reset(); | 302 _textMapper = nil; |
334 } | 303 } |
335 | 304 |
336 - (void)updateTapRects { | 305 - (void)updateTapRects { |
337 // Don't update if the label hasn't changed size or position. | 306 // Don't update if the label hasn't changed size or position. |
338 if (CGRectEqualToRect([_label frame], _lastLabelFrame)) | 307 if (CGRectEqualToRect([_label frame], _lastLabelFrame)) |
339 return; | 308 return; |
340 // Don't update if there are no links. | 309 // Don't update if there are no links. |
341 if (![_layoutsForURLs count]) | 310 if (![_layoutsForURLs count]) |
342 return; | 311 return; |
343 | 312 |
344 _lastLabelFrame = [_label frame]; | 313 _lastLabelFrame = [_label frame]; |
345 [self clearTapButtons]; | 314 [self clearTapButtons]; |
346 | 315 |
347 // If the label bounds are zero in either dimension, no rects are possible. | 316 // If the label bounds are zero in either dimension, no rects are possible. |
348 if (0.0 == _lastLabelFrame.size.width || 0.0 == _lastLabelFrame.size.height) | 317 if (0.0 == _lastLabelFrame.size.width || 0.0 == _lastLabelFrame.size.height) |
349 return; | 318 return; |
350 | 319 |
351 if (!_textMapper) | 320 if (!_textMapper) |
352 [self resetTextMapper]; | 321 [self resetTextMapper]; |
353 | 322 |
354 for (LinkLayout* layout in [_layoutsForURLs allValues]) { | 323 for (LinkLayout* layout in [_layoutsForURLs allValues]) { |
355 base::scoped_nsobject<NSMutableArray> frames([[NSMutableArray alloc] init]); | 324 NSMutableArray* frames = [[NSMutableArray alloc] init]; |
356 NSArray* rects = [_textMapper rectsForRange:layout.range]; | 325 NSArray* rects = [_textMapper rectsForRange:layout.range]; |
357 for (NSUInteger rectIdx = 0; rectIdx < [rects count]; ++rectIdx) { | 326 for (NSUInteger rectIdx = 0; rectIdx < [rects count]; ++rectIdx) { |
358 CGRect frame = [rects[rectIdx] CGRectValue]; | 327 CGRect frame = [rects[rectIdx] CGRectValue]; |
359 frame = [[_label superview] convertRect:frame fromView:_label]; | 328 frame = [[_label superview] convertRect:frame fromView:_label]; |
360 [frames addObject:[NSValue valueWithCGRect:frame]]; | 329 [frames addObject:[NSValue valueWithCGRect:frame]]; |
361 } | 330 } |
362 layout.frames = frames; | 331 layout.frames = frames; |
363 } | 332 } |
364 [self updateTapButtons]; | 333 [self updateTapButtons]; |
365 } | 334 } |
366 | 335 |
367 - (void)resetTextMapper { | 336 - (void)resetTextMapper { |
368 DCHECK([self.textMapperClass conformsToProtocol:@protocol(TextRegionMapper)]); | 337 DCHECK([self.textMapperClass conformsToProtocol:@protocol(TextRegionMapper)]); |
369 _textMapper.reset([[self.textMapperClass alloc] | 338 _textMapper = [[self.textMapperClass alloc] |
370 initWithAttributedString:[_label attributedText] | 339 initWithAttributedString:[_label attributedText] |
371 bounds:[_label bounds]]); | 340 bounds:[_label bounds]]; |
372 } | 341 } |
373 | 342 |
374 - (void)clearTapButtons { | 343 - (void)clearTapButtons { |
375 for (TransparentLinkButton* button in _linkButtons.get()) { | 344 for (TransparentLinkButton* button in _linkButtons) { |
376 [button removeFromSuperview]; | 345 [button removeFromSuperview]; |
377 } | 346 } |
378 [_linkButtons removeAllObjects]; | 347 [_linkButtons removeAllObjects]; |
379 } | 348 } |
380 | 349 |
381 - (void)updateTapButtons { | 350 - (void)updateTapButtons { |
382 // If the label has no superview, clear any existing buttons. | 351 // If the label has no superview, clear any existing buttons. |
383 if (![_label superview]) { | 352 if (![_label superview]) { |
384 [self clearTapButtons]; | 353 [self clearTapButtons]; |
385 return; | 354 return; |
386 } else if ([_linkButtons count]) { | 355 } else if ([_linkButtons count]) { |
387 // If the buttons are currently in some view other than the label's | 356 // If the buttons are currently in some view other than the label's |
388 // superview, repatriate them. | 357 // superview, repatriate them. |
389 if (base::mac::ObjCCast<TransparentLinkButton>(_linkButtons[0]).superview != | 358 if (base::mac::ObjCCast<TransparentLinkButton>(_linkButtons[0]).superview != |
390 [_label superview]) { | 359 [_label superview]) { |
391 for (TransparentLinkButton* button in _linkButtons.get()) { | 360 for (TransparentLinkButton* button in _linkButtons) { |
392 CGRect newFrame = | 361 CGRect newFrame = |
393 [[_label superview] convertRect:button.frame fromView:button]; | 362 [[_label superview] convertRect:button.frame fromView:button]; |
394 [[_label superview] insertSubview:button aboveSubview:_label]; | 363 [[_label superview] insertSubview:button aboveSubview:_label]; |
395 button.frame = newFrame; | 364 button.frame = newFrame; |
396 } | 365 } |
397 } | 366 } |
398 } | 367 } |
399 // If there are no buttons, make some and put them in the label's superview. | 368 // If there are no buttons, make some and put them in the label's superview. |
400 if (![_linkButtons count] && _layoutsForURLs) { | 369 if (![_linkButtons count] && _layoutsForURLs) { |
401 [_layoutsForURLs enumerateKeysAndObjectsUsingBlock:^( | 370 [_layoutsForURLs enumerateKeysAndObjectsUsingBlock:^( |
(...skipping 17 matching lines...) Expand all Loading... |
419 } | 388 } |
420 }]; | 389 }]; |
421 } | 390 } |
422 } | 391 } |
423 | 392 |
424 #pragma mark - Tap Handlers | 393 #pragma mark - Tap Handlers |
425 | 394 |
426 - (void)linkButtonTapped:(id)sender { | 395 - (void)linkButtonTapped:(id)sender { |
427 TransparentLinkButton* button = | 396 TransparentLinkButton* button = |
428 base::mac::ObjCCast<TransparentLinkButton>(sender); | 397 base::mac::ObjCCast<TransparentLinkButton>(sender); |
429 _action.get()(button.URL); | 398 _action(button.URL); |
430 } | 399 } |
431 | 400 |
432 #pragma mark - Test facilitators | 401 #pragma mark - Test facilitators |
433 | 402 |
434 - (NSArray*)tapRectsForURL:(GURL)url { | 403 - (NSArray*)tapRectsForURL:(GURL)url { |
435 NSURL* key = net::NSURLWithGURL(url); | 404 NSURL* key = net::NSURLWithGURL(url); |
436 LinkLayout* layout = [_layoutsForURLs objectForKey:key]; | 405 LinkLayout* layout = [_layoutsForURLs objectForKey:key]; |
437 return layout.frames; | 406 return layout.frames; |
438 } | 407 } |
439 | 408 |
440 - (void)tapLabelAtPoint:(CGPoint)point { | 409 - (void)tapLabelAtPoint:(CGPoint)point { |
441 [_layoutsForURLs enumerateKeysAndObjectsUsingBlock:^( | 410 [_layoutsForURLs enumerateKeysAndObjectsUsingBlock:^( |
442 NSURL* key, LinkLayout* layout, BOOL* stop) { | 411 NSURL* key, LinkLayout* layout, BOOL* stop) { |
443 for (NSValue* frameValue in layout.frames) { | 412 for (NSValue* frameValue in layout.frames) { |
444 CGRect frame = [frameValue CGRectValue]; | 413 CGRect frame = [frameValue CGRectValue]; |
445 if (CGRectContainsPoint(frame, point)) { | 414 if (CGRectContainsPoint(frame, point)) { |
446 _action.get()(net::GURLWithNSURL(key)); | 415 _action(net::GURLWithNSURL(key)); |
447 *stop = YES; | 416 *stop = YES; |
448 break; | 417 break; |
449 } | 418 } |
450 } | 419 } |
451 }]; | 420 }]; |
452 } | 421 } |
453 | 422 |
454 @end | 423 @end |
OLD | NEW |