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/manual_text_framer.h" | 5 #import "ios/chrome/browser/ui/util/manual_text_framer.h" |
6 | 6 |
7 #import <UIKit/UIKit.h> | 7 #import <UIKit/UIKit.h> |
8 | 8 |
9 #include "base/i18n/rtl.h" | 9 #include "base/i18n/rtl.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/mac/foundation_util.h" | 11 #include "base/mac/foundation_util.h" |
12 #include "base/mac/scoped_nsobject.h" | |
13 #import "ios/chrome/browser/ui/util/core_text_util.h" | 12 #import "ios/chrome/browser/ui/util/core_text_util.h" |
14 #import "ios/chrome/browser/ui/util/text_frame.h" | 13 #import "ios/chrome/browser/ui/util/text_frame.h" |
15 #import "ios/chrome/browser/ui/util/unicode_util.h" | 14 #import "ios/chrome/browser/ui/util/unicode_util.h" |
16 | 15 |
| 16 #if !defined(__has_feature) || !__has_feature(objc_arc) |
| 17 #error "This file requires ARC support." |
| 18 #endif |
| 19 |
17 // NOTE: When RTL text is laid out into glyph runs, the glyphs appear in the | 20 // NOTE: When RTL text is laid out into glyph runs, the glyphs appear in the |
18 // visual order in which they appear on screen. In other words, the glyphs are | 21 // visual order in which they appear on screen. In other words, the glyphs are |
19 // arranged in the reverse order of their corresponding characters in the | 22 // arranged in the reverse order of their corresponding characters in the |
20 // original string. | 23 // original string. |
21 | 24 |
22 namespace { | 25 namespace { |
23 // Aligns |value| to the nearest pixel value, rounding by the function indicated | 26 // Aligns |value| to the nearest pixel value, rounding by the function indicated |
24 // by |function|. AlignmentFunction::CEIL should be used to align size values, | 27 // by |function|. AlignmentFunction::CEIL should be used to align size values, |
25 // while AlignmentFunction::FLOOR should be used to align location values. | 28 // while AlignmentFunction::FLOOR should be used to align location values. |
26 enum class AlignmentFunction : short { CEIL = 0, FLOOR }; | 29 enum class AlignmentFunction : short { CEIL = 0, FLOOR }; |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 } | 66 } |
64 return paragraph_strings; | 67 return paragraph_strings; |
65 } | 68 } |
66 } // namespace | 69 } // namespace |
67 | 70 |
68 #pragma mark - ManualTextFrame | 71 #pragma mark - ManualTextFrame |
69 | 72 |
70 // A TextFrame implementation that is manually created by ManualTextFramer. | 73 // A TextFrame implementation that is manually created by ManualTextFramer. |
71 @interface ManualTextFrame : NSObject<TextFrame> { | 74 @interface ManualTextFrame : NSObject<TextFrame> { |
72 // Backing objects for properties of the same name. | 75 // Backing objects for properties of the same name. |
73 base::scoped_nsobject<NSAttributedString> _string; | 76 NSAttributedString* _string; |
74 base::scoped_nsobject<NSMutableArray> _lines; | 77 NSMutableArray* _lines; |
75 } | 78 } |
76 | 79 |
77 // Designated initializer. | 80 // Designated initializer. |
78 - (instancetype)initWithString:(NSAttributedString*)string | 81 - (instancetype)initWithString:(NSAttributedString*)string |
79 inBounds:(CGRect)bounds NS_DESIGNATED_INITIALIZER; | 82 inBounds:(CGRect)bounds NS_DESIGNATED_INITIALIZER; |
80 - (instancetype)init NS_UNAVAILABLE; | 83 - (instancetype)init NS_UNAVAILABLE; |
81 | 84 |
82 // Creates a FramedLine out of |line|, |stringRange|, and |origin|, then adds it | 85 // Creates a FramedLine out of |line|, |stringRange|, and |origin|, then adds it |
83 // to |lines|. | 86 // to |lines|. |
84 - (void)addFramedLineWithLine:(CTLineRef)line | 87 - (void)addFramedLineWithLine:(CTLineRef)line |
85 stringRange:(NSRange)stringRange | 88 stringRange:(NSRange)stringRange |
86 origin:(CGPoint)origin; | 89 origin:(CGPoint)origin; |
87 | 90 |
88 // Redefine property as readwrite. | 91 // Redefine property as readwrite. |
89 @property(nonatomic, readwrite) NSRange framedRange; | 92 @property(nonatomic, readwrite) NSRange framedRange; |
90 | 93 |
91 @end | 94 @end |
92 | 95 |
93 @implementation ManualTextFrame | 96 @implementation ManualTextFrame |
94 | 97 |
95 @synthesize framedRange = _framedRange; | 98 @synthesize framedRange = _framedRange; |
96 @synthesize bounds = _bounds; | 99 @synthesize bounds = _bounds; |
97 | 100 |
98 - (instancetype)initWithString:(NSAttributedString*)string | 101 - (instancetype)initWithString:(NSAttributedString*)string |
99 inBounds:(CGRect)bounds { | 102 inBounds:(CGRect)bounds { |
100 if ((self = [super init])) { | 103 if ((self = [super init])) { |
101 DCHECK(string.string.length); | 104 DCHECK(string.string.length); |
102 _string.reset([string retain]); | 105 _string = string; |
103 _bounds = bounds; | 106 _bounds = bounds; |
104 _lines.reset([[NSMutableArray alloc] init]); | 107 _lines = [[NSMutableArray alloc] init]; |
105 } | 108 } |
106 return self; | 109 return self; |
107 } | 110 } |
108 | 111 |
109 #pragma mark Accessors | 112 #pragma mark Accessors |
110 | 113 |
111 - (NSAttributedString*)string { | 114 - (NSAttributedString*)string { |
112 return _string.get(); | 115 return _string; |
113 } | 116 } |
114 | 117 |
115 - (NSArray*)lines { | 118 - (NSArray*)lines { |
116 return _lines.get(); | 119 return _lines; |
117 } | 120 } |
118 | 121 |
119 #pragma mark Private | 122 #pragma mark Private |
120 | 123 |
121 - (void)addFramedLineWithLine:(CTLineRef)line | 124 - (void)addFramedLineWithLine:(CTLineRef)line |
122 stringRange:(NSRange)stringRange | 125 stringRange:(NSRange)stringRange |
123 origin:(CGPoint)origin { | 126 origin:(CGPoint)origin { |
124 base::scoped_nsobject<FramedLine> framedLine([[FramedLine alloc] | 127 FramedLine* framedLine = [[FramedLine alloc] initWithLine:line |
125 initWithLine:line | 128 stringRange:stringRange |
126 stringRange:stringRange | 129 origin:origin]; |
127 origin:origin]); | |
128 [_lines addObject:framedLine]; | 130 [_lines addObject:framedLine]; |
129 } | 131 } |
130 | 132 |
131 @end | 133 @end |
132 | 134 |
133 #pragma mark - ManualTextFramer Private Interface | 135 #pragma mark - ManualTextFramer Private Interface |
134 | 136 |
135 @interface ManualTextFramer () { | 137 @interface ManualTextFramer () |
136 // Backing objects for properties of the same name. | |
137 base::scoped_nsobject<NSAttributedString> _string; | |
138 base::scoped_nsobject<ManualTextFrame> _manualTextFrame; | |
139 } | |
140 | 138 |
141 // The string passed upon initialization. | 139 // The string passed upon initialization. |
142 @property(nonatomic, readonly) NSAttributedString* string; | 140 @property(strong, nonatomic, readonly) NSAttributedString* string; |
143 | 141 |
144 // The bounds passed upon initialization. | 142 // The bounds passed upon initialization. |
145 @property(nonatomic, readonly) CGRect bounds; | 143 @property(nonatomic, readonly) CGRect bounds; |
146 | 144 |
147 // The width of the bounds passed upon initialization. | 145 // The width of the bounds passed upon initialization. |
148 @property(nonatomic, readonly) CGFloat boundingWidth; | 146 @property(nonatomic, readonly) CGFloat boundingWidth; |
149 | 147 |
150 // The remaining height into which text can be framed. | 148 // The remaining height into which text can be framed. |
151 @property(nonatomic, assign) CGFloat remainingHeight; | 149 @property(nonatomic, assign) CGFloat remainingHeight; |
152 | 150 |
153 // The text frame constructed by |-frameText|. | 151 // The text frame constructed by |-frameText|. |
154 @property(nonatomic, readonly) ManualTextFrame* manualTextFrame; | 152 @property(strong, nonatomic, readonly) ManualTextFrame* manualTextFrame; |
155 | 153 |
156 // Creates a ManualTextFrame and assigns it to |_manualTextFrame|. Returns YES | 154 // Creates a ManualTextFrame and assigns it to |_manualTextFrame|. Returns YES |
157 // if a new text frame was successfully created. | 155 // if a new text frame was successfully created. |
158 - (BOOL)setupManualTextFrame; | 156 - (BOOL)setupManualTextFrame; |
159 | 157 |
160 @end | 158 @end |
161 | 159 |
162 #pragma mark - ParagraphFramer | 160 #pragma mark - ParagraphFramer |
163 | 161 |
164 // ManualTextFramer subclass that frames a single paragraph. A paragraph is | 162 // ManualTextFramer subclass that frames a single paragraph. A paragraph is |
165 // defined as an NSAttributedString which contains either zero newlines or one | 163 // defined as an NSAttributedString which contains either zero newlines or one |
166 // newline as its last character. | 164 // newline as its last character. |
167 @interface ParagraphFramer : ManualTextFramer { | 165 @interface ParagraphFramer : ManualTextFramer { |
168 // Backing objects for properties of the same name. | 166 // Backing objects for properties of the same name. |
169 base::ScopedCFTypeRef<CTLineRef> _line; | 167 base::ScopedCFTypeRef<CTLineRef> _line; |
170 base::scoped_nsobject<NSCharacterSet> _lineEndSet; | |
171 } | 168 } |
172 | 169 |
173 // The CTLine created from |string|. | 170 // The CTLine created from |string|. |
174 @property(nonatomic, readonly) CTLineRef line; | 171 @property(nonatomic, readonly) CTLineRef line; |
175 | 172 |
176 // The effective text alignment for |line|. | 173 // The effective text alignment for |line|. |
177 @property(nonatomic, readonly) NSTextAlignment effectiveAlignment; | 174 @property(nonatomic, readonly) NSTextAlignment effectiveAlignment; |
178 | 175 |
179 // Character set containing characters that are appropriate for line endings. | 176 // Character set containing characters that are appropriate for line endings. |
180 // These characters include whitespaces and newlines (denoting a word boundary), | 177 // These characters include whitespaces and newlines (denoting a word boundary), |
181 // in addition to line-ending characters like hyphens, em dashes, and en dashes. | 178 // in addition to line-ending characters like hyphens, em dashes, and en dashes. |
182 @property(nonatomic, readonly) NSCharacterSet* lineEndSet; | 179 @property(strong, nonatomic, readonly) NSCharacterSet* lineEndSet; |
183 | 180 |
184 // The index of the current run that is being framed. Setting |runIdx| also | 181 // The index of the current run that is being framed. Setting |runIdx| also |
185 // updates |currentRun| and |currentGlyphCount|. | 182 // updates |currentRun| and |currentGlyphCount|. |
186 @property(nonatomic, assign) CFIndex runIdx; | 183 @property(nonatomic, assign) CFIndex runIdx; |
187 | 184 |
188 // The CTRun corresponding with |runIdx| in |line|. | 185 // The CTRun corresponding with |runIdx| in |line|. |
189 @property(nonatomic, readonly) CTRunRef currentRun; | 186 @property(nonatomic, readonly) CTRunRef currentRun; |
190 | 187 |
191 // The glyph count in |currentRun|. | 188 // The glyph count in |currentRun|. |
192 @property(nonatomic, readonly) CFIndex currentGlyphCount; | 189 @property(nonatomic, readonly) CFIndex currentGlyphCount; |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
264 | 261 |
265 @synthesize effectiveAlignment = _effectiveTextAlignment; | 262 @synthesize effectiveAlignment = _effectiveTextAlignment; |
266 @synthesize runIdx = _runIdx; | 263 @synthesize runIdx = _runIdx; |
267 @synthesize currentRun = _currentRun; | 264 @synthesize currentRun = _currentRun; |
268 @synthesize currentGlyphCount = _currentGlyphCount; | 265 @synthesize currentGlyphCount = _currentGlyphCount; |
269 @synthesize framedGlyphCount = _framedGlyphCount; | 266 @synthesize framedGlyphCount = _framedGlyphCount; |
270 @synthesize currentLineRange = _currentLineRange; | 267 @synthesize currentLineRange = _currentLineRange; |
271 @synthesize currentLineWidth = _currentLineWidth; | 268 @synthesize currentLineWidth = _currentLineWidth; |
272 @synthesize currentWhitespaceWidth = _currentWhitespaceWidth; | 269 @synthesize currentWhitespaceWidth = _currentWhitespaceWidth; |
273 @synthesize isRTL = _isRTL; | 270 @synthesize isRTL = _isRTL; |
| 271 @synthesize lineEndSet = _lineEndSet; |
274 | 272 |
275 - (instancetype)initWithString:(NSAttributedString*)string | 273 - (instancetype)initWithString:(NSAttributedString*)string |
276 inBounds:(CGRect)bounds { | 274 inBounds:(CGRect)bounds { |
277 if ((self = [super initWithString:string inBounds:bounds])) { | 275 if ((self = [super initWithString:string inBounds:bounds])) { |
278 NSRange newlineRange = [string.string | 276 NSRange newlineRange = [string.string |
279 rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]]; | 277 rangeOfCharacterFromSet:[NSCharacterSet newlineCharacterSet]]; |
280 DCHECK(newlineRange.location == NSNotFound || | 278 DCHECK(newlineRange.location == NSNotFound || |
281 newlineRange.location == string.string.length - 1); | 279 newlineRange.location == string.string.length - 1); |
282 CTLineRef line = | 280 CTLineRef line = |
283 CTLineCreateWithAttributedString(base::mac::NSToCFCast(string)); | 281 CTLineCreateWithAttributedString(base::mac::NSToCFCast(string)); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
336 | 334 |
337 - (CTLineRef)line { | 335 - (CTLineRef)line { |
338 return _line.get(); | 336 return _line.get(); |
339 } | 337 } |
340 | 338 |
341 - (NSCharacterSet*)lineEndSet { | 339 - (NSCharacterSet*)lineEndSet { |
342 if (!_lineEndSet) { | 340 if (!_lineEndSet) { |
343 NSMutableCharacterSet* lineEndSet = | 341 NSMutableCharacterSet* lineEndSet = |
344 [NSMutableCharacterSet whitespaceAndNewlineCharacterSet]; | 342 [NSMutableCharacterSet whitespaceAndNewlineCharacterSet]; |
345 [lineEndSet addCharactersInString:@"-\u2013\u2014"]; | 343 [lineEndSet addCharactersInString:@"-\u2013\u2014"]; |
346 _lineEndSet.reset([lineEndSet retain]); | 344 _lineEndSet = lineEndSet; |
347 } | 345 } |
348 return _lineEndSet; | 346 return _lineEndSet; |
349 } | 347 } |
350 | 348 |
351 - (void)setRunIdx:(CFIndex)runIdx { | 349 - (void)setRunIdx:(CFIndex)runIdx { |
352 _runIdx = runIdx; | 350 _runIdx = runIdx; |
353 self.framedGlyphCount = 0; | 351 self.framedGlyphCount = 0; |
354 if ([self runIdxIsValid:runIdx]) { | 352 if ([self runIdxIsValid:runIdx]) { |
355 NSArray* runs = base::mac::CFToNSCast(CTLineGetGlyphRuns(self.line)); | 353 NSArray* runs = base::mac::CFToNSCast(CTLineGetGlyphRuns(self.line)); |
356 _currentRun = static_cast<CTRunRef>(runs[_runIdx]); | 354 _currentRun = (__bridge CTRunRef)(runs[_runIdx]); |
357 _currentGlyphCount = CTRunGetGlyphCount(self.currentRun); | 355 _currentGlyphCount = CTRunGetGlyphCount(self.currentRun); |
358 } else { | 356 } else { |
359 _currentRun = nullptr; | 357 _currentRun = nullptr; |
360 _currentGlyphCount = 0; | 358 _currentGlyphCount = 0; |
361 } | 359 } |
362 } | 360 } |
363 | 361 |
364 - (CFIndex)incrementAmount { | 362 - (CFIndex)incrementAmount { |
365 return self.isRTL ? -1 : 1; | 363 return self.isRTL ? -1 : 1; |
366 } | 364 } |
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
555 | 553 |
556 @end | 554 @end |
557 | 555 |
558 #pragma mark - ManualTextFramer | 556 #pragma mark - ManualTextFramer |
559 | 557 |
560 @implementation ManualTextFramer | 558 @implementation ManualTextFramer |
561 | 559 |
562 @synthesize bounds = _bounds; | 560 @synthesize bounds = _bounds; |
563 @synthesize boundingWidth = _boundingWidth; | 561 @synthesize boundingWidth = _boundingWidth; |
564 @synthesize remainingHeight = _remainingHeight; | 562 @synthesize remainingHeight = _remainingHeight; |
| 563 @synthesize string = _string; |
| 564 @synthesize manualTextFrame = _manualTextFrame; |
565 | 565 |
566 - (instancetype)initWithString:(NSAttributedString*)string | 566 - (instancetype)initWithString:(NSAttributedString*)string |
567 inBounds:(CGRect)bounds { | 567 inBounds:(CGRect)bounds { |
568 if ((self = [super init])) { | 568 if ((self = [super init])) { |
569 DCHECK(string.string.length); | 569 DCHECK(string.string.length); |
570 _string.reset([string retain]); | 570 _string = string; |
571 _bounds = bounds; | 571 _bounds = bounds; |
572 _boundingWidth = CGRectGetWidth(bounds); | 572 _boundingWidth = CGRectGetWidth(bounds); |
573 _remainingHeight = CGRectGetHeight(bounds); | 573 _remainingHeight = CGRectGetHeight(bounds); |
574 } | 574 } |
575 return self; | 575 return self; |
576 } | 576 } |
577 | 577 |
578 - (void)frameText { | 578 - (void)frameText { |
579 if (![self setupManualTextFrame]) | 579 if (![self setupManualTextFrame]) |
580 return; | 580 return; |
581 NSRange framedRange = NSMakeRange(0, 0); | 581 NSRange framedRange = NSMakeRange(0, 0); |
582 NSArray* paragraphs = GetParagraphStringsForString(self.string); | 582 NSArray* paragraphs = GetParagraphStringsForString(self.string); |
583 NSUInteger stringRangeOffset = 0; | 583 NSUInteger stringRangeOffset = 0; |
584 for (NSAttributedString* paragraph in paragraphs) { | 584 for (NSAttributedString* paragraph in paragraphs) { |
585 // Frame each paragraph using a ParagraphFramer, then update bookkeeping | 585 // Frame each paragraph using a ParagraphFramer, then update bookkeeping |
586 // variables for the top-level ManualTextFramer. | 586 // variables for the top-level ManualTextFramer. |
587 CGRect remainingBounds = | 587 CGRect remainingBounds = |
588 CGRectMake(0, 0, self.boundingWidth, self.remainingHeight); | 588 CGRectMake(0, 0, self.boundingWidth, self.remainingHeight); |
589 base::scoped_nsobject<ParagraphFramer> framer([[ParagraphFramer alloc] | 589 ParagraphFramer* framer = |
590 initWithString:paragraph | 590 [[ParagraphFramer alloc] initWithString:paragraph |
591 inBounds:remainingBounds]); | 591 inBounds:remainingBounds]; |
592 [framer frameText]; | 592 [framer frameText]; |
593 id<TextFrame> frame = [framer textFrame]; | 593 id<TextFrame> frame = [framer textFrame]; |
594 DCHECK(frame); | 594 DCHECK(frame); |
595 framedRange.length += frame.framedRange.length; | 595 framedRange.length += frame.framedRange.length; |
596 CGFloat paragraphHeight = 0.0; | 596 CGFloat paragraphHeight = 0.0; |
597 for (FramedLine* line in frame.lines) { | 597 for (FramedLine* line in frame.lines) { |
598 NSRange lineRange = line.stringRange; | 598 NSRange lineRange = line.stringRange; |
599 lineRange.location += stringRangeOffset; | 599 lineRange.location += stringRangeOffset; |
600 [self.manualTextFrame addFramedLineWithLine:line.line | 600 [self.manualTextFrame addFramedLineWithLine:line.line |
601 stringRange:lineRange | 601 stringRange:lineRange |
602 origin:line.origin]; | 602 origin:line.origin]; |
603 paragraphHeight += core_text_util::GetLineHeight(self.string, lineRange) + | 603 paragraphHeight += core_text_util::GetLineHeight(self.string, lineRange) + |
604 core_text_util::GetLineSpacing(self.string, lineRange); | 604 core_text_util::GetLineSpacing(self.string, lineRange); |
605 } | 605 } |
606 self.remainingHeight -= paragraphHeight; | 606 self.remainingHeight -= paragraphHeight; |
607 stringRangeOffset += paragraph.string.length; | 607 stringRangeOffset += paragraph.string.length; |
608 } | 608 } |
609 self.manualTextFrame.framedRange = framedRange; | 609 self.manualTextFrame.framedRange = framedRange; |
610 } | 610 } |
611 | 611 |
612 #pragma mark Accessors | 612 #pragma mark Accessors |
613 | 613 |
614 - (NSAttributedString*)string { | |
615 return _string.get(); | |
616 } | |
617 | |
618 - (ManualTextFrame*)manualTextFrame { | |
619 return _manualTextFrame.get(); | |
620 } | |
621 | |
622 - (id<TextFrame>)textFrame { | 614 - (id<TextFrame>)textFrame { |
623 return _manualTextFrame.get(); | 615 return _manualTextFrame; |
624 } | 616 } |
625 | 617 |
626 #pragma mark Private | 618 #pragma mark Private |
627 | 619 |
628 - (BOOL)setupManualTextFrame { | 620 - (BOOL)setupManualTextFrame { |
629 if (_manualTextFrame) | 621 if (_manualTextFrame) |
630 return NO; | 622 return NO; |
631 _manualTextFrame.reset([[ManualTextFrame alloc] initWithString:self.string | 623 _manualTextFrame = |
632 inBounds:self.bounds]); | 624 [[ManualTextFrame alloc] initWithString:self.string inBounds:self.bounds]; |
633 return YES; | 625 return YES; |
634 } | 626 } |
635 | 627 |
636 @end | 628 @end |
OLD | NEW |