Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(484)

Side by Side Diff: chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.mm

Issue 1099403005: [AiS] changing mac omnibox suggestions form NSMatrix to NSTableView (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: unit test fix Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 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 "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h" 5 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 9
10 #include "base/i18n/rtl.h" 10 #include "base/i18n/rtl.h"
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
148 value:DimTextColor() 148 value:DimTextColor()
149 range:range]; 149 range:range];
150 } 150 }
151 } 151 }
152 152
153 return as; 153 return as;
154 } 154 }
155 155
156 } // namespace 156 } // namespace
157 157
158 @implementation OmniboxPopupCell 158 @implementation OmniboxPopupCellData
159 159
160 - (id)init { 160 - (id)initWithMatch:(const AutocompleteMatch&)match {
161 self = [super init]; 161 self = [super init];
162 if (self) { 162 if (!self)
163 [self setImagePosition:NSImageLeft]; 163 return self;
164 [self setBordered:NO];
165 [self setButtonType:NSRadioButton];
166 164
167 // Without this highlighting messes up white areas of images. 165 const base::string16& raw_separator =
168 [self setHighlightsBy:NSNoCellMask]; 166 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR);
167 separator_.reset(
168 [CreateAttributedString(raw_separator, DimTextColor()) retain]);
169 169
170 const base::string16& raw_separator = 170 isContentsRTL_ =
171 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); 171 (base::i18n::RIGHT_TO_LEFT ==
172 separator_.reset( 172 base::i18n::GetFirstStrongCharacterDirection(match.contents));
173 [CreateAttributedString(raw_separator, DimTextColor()) retain]); 173 matchType_ = match.type;
174
175 // Prefix may not have any characters with strong directionality, and may take
176 // the UI directionality. But prefix needs to appear in continuation of the
177 // contents so we force the directionality.
178 NSTextAlignment textAlignment =
179 isContentsRTL_ ? NSRightTextAlignment : NSLeftTextAlignment;
180 prefix_.reset(
181 [CreateAttributedString(base::UTF8ToUTF16(match.GetAdditionalInfo(
182 kACMatchPropertyContentsPrefix)),
183 ContentTextColor(), textAlignment) retain]);
184
185 contents_.reset([CreateClassifiedAttributedString(
186 match.contents, ContentTextColor(), match.contents_class) retain]);
187
188 if (match.answer) {
189 base::string16 answerString;
190 DCHECK(!match.answer->second_line().text_fields().empty());
191 for (const SuggestionAnswer::TextField& textField :
192 match.answer->second_line().text_fields())
193 answerString += textField.text();
194 const base::char16 space(' ');
195 const SuggestionAnswer::TextField* textField =
196 match.answer->second_line().additional_text();
197 if (textField)
198 answerString += space + textField->text();
199 textField = match.answer->second_line().status_text();
200 if (textField)
201 answerString += space + textField->text();
202 description_.reset([CreateClassifiedAttributedString(
203 answerString, DimTextColor(), match.description_class) retain]);
204 } else if (match.description.empty()) {
205 description_.reset();
206 } else {
207 description_.reset([CreateClassifiedAttributedString(
208 match.description, DimTextColor(), match.description_class) retain]);
174 } 209 }
175 return self; 210 return self;
176 } 211 }
177 212
178 - (void)setMatch:(const AutocompleteMatch&)match { 213 - (void)setContents:(NSAttributedString*)contents {
179 match_ = match; 214 contents_.reset([contents retain]);
180 NSAttributedString *contents = CreateClassifiedAttributedString( 215 }
181 match_.contents, ContentTextColor(), match_.contents_class);
182 [self setAttributedTitle:contents];
183 216
184 if (match_.answer) { 217 - (void)setImage:(NSImage*)image {
185 base::string16 answerString; 218 image_.reset([image retain]);
186 DCHECK(!match_.answer->second_line().text_fields().empty()); 219 }
187 for (const SuggestionAnswer::TextField& textField : 220
188 match_.answer->second_line().text_fields()) 221 - (void)setRect:(NSRect)rect {
189 answerString += textField.text(); 222 rect_ = rect;
190 const base::char16 space(' '); 223 }
191 const SuggestionAnswer::TextField* textField = 224
192 match_.answer->second_line().additional_text(); 225 - (NSRect)rect {
193 if (textField) 226 return rect_;
194 answerString += space + textField->text();
195 textField = match_.answer->second_line().status_text();
196 if (textField)
197 answerString += space + textField->text();
198 description_.reset([CreateClassifiedAttributedString(
199 answerString, DimTextColor(), match_.description_class) retain]);
200 } else if (match_.description.empty()) {
201 description_.reset();
202 } else {
203 description_.reset([CreateClassifiedAttributedString(
204 match_.description, DimTextColor(), match_.description_class) retain]);
205 }
206 } 227 }
207 228
208 - (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth { 229 - (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth {
209 maxMatchContentsWidth_ = maxMatchContentsWidth; 230 maxMatchContentsWidth_ = maxMatchContentsWidth;
210 } 231 }
211 232
212 - (void)setContentsOffset:(CGFloat)contentsOffset { 233 - (void)setContentsOffset:(CGFloat)contentsOffset {
213 contentsOffset_ = contentsOffset; 234 contentsOffset_ = contentsOffset;
214 } 235 }
215 236
216 // The default NSButtonCell drawing leaves the image flush left and 237 - (CGFloat)getMatchContentsWidth {
217 // the title next to the image. This spaces things out to line up 238 return contents_ ? [contents_ size].width : 0;
218 // with the star button and autocomplete field. 239 }
219 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { 240
220 if ([self state] == NSOnState || [self isHighlighted]) { 241 - (id)copyWithZone:(NSZone*)zone {
221 if ([self state] == NSOnState) 242 // Note: Before changing this code, please check with groby, shess, or
222 [SelectedBackgroundColor() set]; 243 // dschuyler about why it's written with swaps and local data.
223 else 244 base::scoped_nsobject<NSAttributedString> contents;
224 [HoveredBackgroundColor() set]; 245 base::scoped_nsobject<NSAttributedString> separator;
225 NSBezierPath* path = 246 base::scoped_nsobject<NSAttributedString> description;
226 [NSBezierPath bezierPathWithRoundedRect:cellFrame 247 base::scoped_nsobject<NSAttributedString> prefix;
227 xRadius:kCellRoundingRadius 248 base::scoped_nsobject<NSImage> image;
228 yRadius:kCellRoundingRadius]; 249 contents_.swap(contents);
229 [path fill]; 250 separator_.swap(separator);
230 } 251 description_.swap(description);
252 prefix_.swap(prefix);
253 image_.swap(image);
254
255 OmniboxPopupCellData* copy = [[OmniboxPopupCellData alloc] init];
256 copy->contents_.reset(contents);
257 copy->separator_.reset(separator);
258 copy->description_.reset(description);
259 copy->prefix_.reset(prefix);
260 copy->image_.reset(image);
261 copy->maxMatchContentsWidth_ = maxMatchContentsWidth_;
262 copy->contentsOffset_ = contentsOffset_;
263 copy->isContentsRTL_ = isContentsRTL_;
264 copy->matchType_ = matchType_;
265 copy->rect_ = rect_;
266
267 contents_.swap(contents);
268 separator_.swap(separator);
269 description_.swap(description);
270 prefix_.swap(prefix);
271 image_.swap(image);
272 return copy;
273 }
274
275 - (void)drawMatchWithFrame:(NSRect)cellFrame
276 inCell:(OmniboxPopupCell*)cell
277 inView:(NSView*)controlView {
278 CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
279 CGFloat contentsWidth = [self getMatchContentsWidth];
280 CGFloat separatorWidth = [separator_ size].width;
281 CGFloat descriptionWidth = description_ ? [description_ size].width : 0;
282 int contentsMaxWidth, descriptionMaxWidth;
283 OmniboxPopupModel::ComputeMatchMaxWidths(
284 ceilf(contentsWidth), ceilf(separatorWidth), ceilf(descriptionWidth),
285 ceilf(remainingWidth), !AutocompleteMatch::IsSearchType(matchType_),
286 &contentsMaxWidth, &descriptionMaxWidth);
231 287
232 // Put the image centered vertically but in a fixed column. 288 // Put the image centered vertically but in a fixed column.
233 NSImage* image = [self image]; 289 NSImage* image = image_;
shrike 2015/05/14 18:02:39 Why do you have to create a local variable instead
dschuyler 2015/05/14 22:28:20 It was done that was previously. I'm not sure why
Scott Hess - ex-Googler 2015/05/15 00:09:38 I suspect [self image] was using the cell's image
234 if (image) { 290 if (image) {
235 NSRect imageRect = cellFrame; 291 NSRect imageRect = cellFrame;
236 imageRect.size = [image size]; 292 imageRect.size = [image size];
237 imageRect.origin.y += 293 imageRect.origin.y +=
238 std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0); 294 std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0);
239 imageRect.origin.x += kImageXOffset; 295 imageRect.origin.x += kImageXOffset;
240 [image drawInRect:FlipIfRTL(imageRect, cellFrame) 296 [image drawInRect:FlipIfRTL(imageRect, cellFrame)
241 fromRect:NSZeroRect // Entire image 297 fromRect:NSZeroRect
242 operation:NSCompositeSourceOver 298 operation:NSCompositeSourceOver
243 fraction:1.0 299 fraction:1.0
244 respectFlipped:YES 300 respectFlipped:YES
245 hints:nil]; 301 hints:nil];
246 } 302 }
247 303
248 [self drawMatchWithFrame:cellFrame inView:controlView];
249 }
250
251 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
252 NSAttributedString* contents = [self attributedTitle];
253
254 CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
255 CGFloat contentsWidth = [self getMatchContentsWidth];
256 CGFloat separatorWidth = [separator_ size].width;
257 CGFloat descriptionWidth = description_.get() ? [description_ size].width : 0;
258 int contentsMaxWidth, descriptionMaxWidth;
259 OmniboxPopupModel::ComputeMatchMaxWidths(
260 ceilf(contentsWidth),
261 ceilf(separatorWidth),
262 ceilf(descriptionWidth),
263 ceilf(remainingWidth),
264 !AutocompleteMatch::IsSearchType(match_.type),
265 &contentsMaxWidth,
266 &descriptionMaxWidth);
267
268 CGFloat offset = kTextStartOffset; 304 CGFloat offset = kTextStartOffset;
269 if (match_.type == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { 305 if (matchType_ == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) {
270 // Infinite suggestions are rendered with a prefix (usually ellipsis), which 306 // Infinite suggestions are rendered with a prefix (usually ellipsis), which
271 // appear vertically stacked. 307 // appear vertically stacked.
272 offset += [self drawMatchPrefixWithFrame:cellFrame 308 offset += [self drawMatchPrefixWithFrame:cellFrame
309 inCell:cell
273 inView:controlView 310 inView:controlView
274 withContentsMaxWidth:&contentsMaxWidth]; 311 withContentsMaxWidth:&contentsMaxWidth];
275 } 312 }
276 offset += [self drawMatchPart:contents 313 offset += [cell drawMatchPart:contents_
277 withFrame:cellFrame 314 withFrame:cellFrame
278 atOffset:offset 315 atOffset:offset
279 withMaxWidth:contentsMaxWidth 316 withMaxWidth:contentsMaxWidth
280 inView:controlView]; 317 inView:controlView];
281 318
282 if (descriptionMaxWidth != 0) { 319 if (descriptionMaxWidth != 0) {
283 offset += [self drawMatchPart:separator_ 320 offset += [cell drawMatchPart:separator_
284 withFrame:cellFrame 321 withFrame:cellFrame
285 atOffset:offset 322 atOffset:offset
286 withMaxWidth:separatorWidth 323 withMaxWidth:separatorWidth
287 inView:controlView]; 324 inView:controlView];
288 offset += [self drawMatchPart:description_ 325 offset += [cell drawMatchPart:description_
289 withFrame:cellFrame 326 withFrame:cellFrame
290 atOffset:offset 327 atOffset:offset
291 withMaxWidth:descriptionMaxWidth 328 withMaxWidth:descriptionMaxWidth
292 inView:controlView]; 329 inView:controlView];
293 } 330 }
294 } 331 }
295 332
296 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame 333 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame
334 inCell:(OmniboxPopupCell*)cell
297 inView:(NSView*)controlView 335 inView:(NSView*)controlView
298 withContentsMaxWidth:(int*)contentsMaxWidth { 336 withContentsMaxWidth:(int*)contentsMaxWidth {
299 CGFloat offset = 0.0f; 337 CGFloat offset = 0.0f;
300 CGFloat remainingWidth = GetContentAreaWidth(cellFrame); 338 CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
301 bool isRTL = base::i18n::IsRTL(); 339 bool isRTL = base::i18n::IsRTL();
302 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT ==
303 base::i18n::GetFirstStrongCharacterDirection(match_.contents));
304 // Prefix may not have any characters with strong directionality, and may take
305 // the UI directionality. But prefix needs to appear in continuation of the
306 // contents so we force the directionality.
307 NSTextAlignment textAlignment = isContentsRTL ?
308 NSRightTextAlignment : NSLeftTextAlignment;
309 prefix_.reset([CreateAttributedString(base::UTF8ToUTF16(
310 match_.GetAdditionalInfo(kACMatchPropertyContentsPrefix)),
311 ContentTextColor(), textAlignment) retain]);
312 CGFloat prefixWidth = [prefix_ size].width; 340 CGFloat prefixWidth = [prefix_ size].width;
313 341
314 CGFloat prefixOffset = 0.0f; 342 CGFloat prefixOffset = 0.0f;
315 if (isRTL != isContentsRTL) { 343 if (isRTL != isContentsRTL_) {
316 // The contents is rendered between the contents offset extending towards 344 // The contents is rendered between the contents offset extending towards
317 // the start edge, while prefix is rendered in opposite direction. Ideally 345 // the start edge, while prefix is rendered in opposite direction. Ideally
318 // the prefix should be rendered at |contentsOffset_|. If that is not 346 // the prefix should be rendered at |contentsOffset_|. If that is not
319 // sufficient to render the widest suggestion, we increase it to 347 // sufficient to render the widest suggestion, we increase it to
320 // |maxMatchContentsWidth_|. If |remainingWidth| is not sufficient to 348 // |maxMatchContentsWidth_|. If |remainingWidth| is not sufficient to
321 // accommodate that, we reduce the offset so that the prefix gets rendered. 349 // accommodate that, we reduce the offset so that the prefix gets rendered.
322 prefixOffset = std::min( 350 prefixOffset = std::min(
323 remainingWidth - prefixWidth, std::max(contentsOffset_, 351 remainingWidth - prefixWidth, std::max(contentsOffset_,
324 maxMatchContentsWidth_)); 352 maxMatchContentsWidth_));
325 offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth); 353 offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth);
326 } else { // The direction of contents is same as UI direction. 354 } else { // The direction of contents is same as UI direction.
327 // Ideally the offset should be |contentsOffset_|. If the max total width 355 // Ideally the offset should be |contentsOffset_|. If the max total width
328 // (|prefixWidth| + |maxMatchContentsWidth_|) from offset will exceed the 356 // (|prefixWidth| + |maxMatchContentsWidth_|) from offset will exceed the
329 // |remainingWidth|, then we shift the offset to the left , so that all 357 // |remainingWidth|, then we shift the offset to the left , so that all
330 // postfix suggestions are visible. 358 // postfix suggestions are visible.
331 // We have to render the prefix, so offset has to be at least |prefixWidth|. 359 // We have to render the prefix, so offset has to be at least |prefixWidth|.
332 offset = std::max(prefixWidth, 360 offset = std::max(
361 prefixWidth,
333 std::min(remainingWidth - maxMatchContentsWidth_, contentsOffset_)); 362 std::min(remainingWidth - maxMatchContentsWidth_, contentsOffset_));
334 prefixOffset = offset - prefixWidth; 363 prefixOffset = offset - prefixWidth;
335 } 364 }
336 *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth), 365 *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth),
337 *contentsMaxWidth); 366 *contentsMaxWidth);
338 [self drawMatchPart:prefix_ 367 [cell drawMatchPart:prefix_
339 withFrame:cellFrame 368 withFrame:cellFrame
340 atOffset:prefixOffset + kTextStartOffset 369 atOffset:prefixOffset + kTextStartOffset
341 withMaxWidth:prefixWidth 370 withMaxWidth:prefixWidth
342 inView:controlView]; 371 inView:controlView];
343 return offset; 372 return offset;
344 } 373 }
345 374
346 - (CGFloat)drawMatchPart:(NSAttributedString*)as 375 @end
376
377 @implementation OmniboxPopupCell
378
379 - (void)setCellData:(OmniboxPopupCellData*)cellData {
380 cellData_.reset([cellData retain]);
381 }
382
383 - (id)copyWithZone:(NSZone*)zone {
384 // Note: Before changing this code, please check with groby, shess, or
385 // dschuyler about why it's written with swaps and local data.
386 base::scoped_nsobject<OmniboxPopupCellData> cellData(nil);
387 cellData_.swap(cellData);
388 OmniboxPopupCell* copy = [super copyWithZone:zone];
389 copy->cellData_ = cellData;
390 cellData_.swap(cellData);
391 return copy;
392 }
393
394 // The default NSButtonCell drawing leaves the image flush left and
395 // the title next to the image. This spaces things out to line up
396 // with the star button and autocomplete field.
397 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
398 if ([self state] == NSOnState || [self isHighlighted]) {
399 if ([self state] == NSOnState)
400 [SelectedBackgroundColor() set];
401 else
402 [HoveredBackgroundColor() set];
403 NSBezierPath* path =
404 [NSBezierPath bezierPathWithRoundedRect:cellFrame
405 xRadius:kCellRoundingRadius
406 yRadius:kCellRoundingRadius];
407 [path fill];
408 }
409
410 [cellData_ drawMatchWithFrame:cellFrame inCell:self inView:controlView];
411 }
412
413 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
347 withFrame:(NSRect)cellFrame 414 withFrame:(NSRect)cellFrame
348 atOffset:(CGFloat)offset 415 atOffset:(CGFloat)offset
349 withMaxWidth:(int)maxWidth 416 withMaxWidth:(int)maxWidth
350 inView:(NSView*)controlView { 417 inView:(NSView*)controlView {
351 if (offset > NSWidth(cellFrame)) 418 if (offset > NSWidth(cellFrame))
352 return 0.0f; 419 return 0.0f;
353 NSRect renderRect = ShiftRect(cellFrame, offset); 420 NSRect renderRect = ShiftRect(cellFrame, offset);
354 renderRect.size.width = 421 renderRect.size.width =
355 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth)); 422 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth));
356 if (renderRect.size.width != 0) { 423 NSRect textRect =
357 [self drawTitle:as 424 [attributedString boundingRectWithSize:renderRect.size options:nil];
358 withFrame:FlipIfRTL(renderRect, cellFrame) 425 renderRect.origin.y +=
359 inView:controlView]; 426 std::floor((NSHeight(cellFrame) - NSHeight(textRect)) / 2.0);
360 } 427 if (renderRect.size.width != 0)
428 [attributedString drawInRect:FlipIfRTL(renderRect, cellFrame)];
361 return NSWidth(renderRect); 429 return NSWidth(renderRect);
362 } 430 }
363 431
364 - (CGFloat)getMatchContentsWidth {
365 NSAttributedString* contents = [self attributedTitle];
366 return contents ? [contents size].width : 0;
367 }
368
369
370 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match { 432 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match {
371 const base::string16& inputText = base::UTF8ToUTF16( 433 const base::string16& inputText = base::UTF8ToUTF16(
372 match.GetAdditionalInfo(kACMatchPropertyInputText)); 434 match.GetAdditionalInfo(kACMatchPropertyInputText));
373 int contentsStartIndex = 0; 435 int contentsStartIndex = 0;
374 base::StringToInt( 436 base::StringToInt(
375 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex), 437 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex),
376 &contentsStartIndex); 438 &contentsStartIndex);
377 // Ignore invalid state. 439 // Ignore invalid state.
378 if (!StartsWith(match.fill_into_edit, inputText, true) 440 if (!StartsWith(match.fill_into_edit, inputText, true)
379 || !EndsWith(match.fill_into_edit, match.contents, true) 441 || !EndsWith(match.fill_into_edit, match.contents, true)
380 || ((size_t)contentsStartIndex >= inputText.length())) { 442 || ((size_t)contentsStartIndex >= inputText.length())) {
381 return 0; 443 return 0;
382 } 444 }
383 bool isRTL = base::i18n::IsRTL(); 445 bool isRTL = base::i18n::IsRTL();
384 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT == 446 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT ==
385 base::i18n::GetFirstStrongCharacterDirection(match.contents)); 447 base::i18n::GetFirstStrongCharacterDirection(match.contents));
386 448
387 // Color does not matter. 449 // Color does not matter.
388 NSAttributedString* as = CreateAttributedString(inputText, DimTextColor()); 450 NSAttributedString* attributedString =
389 base::scoped_nsobject<NSTextStorage> textStorage([[NSTextStorage alloc] 451 CreateAttributedString(inputText, DimTextColor());
390 initWithAttributedString:as]); 452 base::scoped_nsobject<NSTextStorage> textStorage(
453 [[NSTextStorage alloc] initWithAttributedString:attributedString]);
391 base::scoped_nsobject<NSLayoutManager> layoutManager( 454 base::scoped_nsobject<NSLayoutManager> layoutManager(
392 [[NSLayoutManager alloc] init]); 455 [[NSLayoutManager alloc] init]);
393 base::scoped_nsobject<NSTextContainer> textContainer( 456 base::scoped_nsobject<NSTextContainer> textContainer(
394 [[NSTextContainer alloc] init]); 457 [[NSTextContainer alloc] init]);
395 [layoutManager addTextContainer:textContainer]; 458 [layoutManager addTextContainer:textContainer];
396 [textStorage addLayoutManager:layoutManager]; 459 [textStorage addLayoutManager:layoutManager];
397 460
398 NSUInteger charIndex = static_cast<NSUInteger>(contentsStartIndex); 461 NSUInteger charIndex = static_cast<NSUInteger>(contentsStartIndex);
399 NSUInteger glyphIndex = 462 NSUInteger glyphIndex =
400 [layoutManager glyphIndexForCharacterAtIndex:charIndex]; 463 [layoutManager glyphIndexForCharacterAtIndex:charIndex];
401 464
402 // This offset is computed from the left edge of the glyph always from the 465 // This offset is computed from the left edge of the glyph always from the
403 // left edge of the string, irrespective of the directionality of UI or text. 466 // left edge of the string, irrespective of the directionality of UI or text.
404 CGFloat glyphOffset = [layoutManager locationForGlyphAtIndex:glyphIndex].x; 467 CGFloat glyphOffset = [layoutManager locationForGlyphAtIndex:glyphIndex].x;
405 468
406 CGFloat inputWidth = [as size].width; 469 CGFloat inputWidth = [attributedString size].width;
407 470
408 // The offset obtained above may need to be corrected because the left-most 471 // The offset obtained above may need to be corrected because the left-most
409 // glyph may not have 0 offset. So we find the offset of left-most glyph, and 472 // glyph may not have 0 offset. So we find the offset of left-most glyph, and
410 // subtract it from the offset of the glyph we obtained above. 473 // subtract it from the offset of the glyph we obtained above.
411 CGFloat minOffset = glyphOffset; 474 CGFloat minOffset = glyphOffset;
412 475
413 // If content is RTL, we are interested in the right-edge of the glyph. 476 // If content is RTL, we are interested in the right-edge of the glyph.
414 // Unfortunately the bounding rect computation methods from NSLayoutManager or 477 // Unfortunately the bounding rect computation methods from NSLayoutManager or
415 // NSFont don't work correctly with bidirectional text. So we compute the 478 // NSFont don't work correctly with bidirectional text. So we compute the
416 // glyph width by finding the closest glyph offset to the right of the glyph 479 // glyph width by finding the closest glyph offset to the right of the glyph
417 // we are looking for. 480 // we are looking for.
418 CGFloat glyphWidth = inputWidth; 481 CGFloat glyphWidth = inputWidth;
419 482
420 for (NSUInteger i = 0; i < [as length]; i++) { 483 for (NSUInteger i = 0; i < [attributedString length]; i++) {
421 if (i == charIndex) continue; 484 if (i == charIndex) continue;
422 glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:i]; 485 glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:i];
423 CGFloat offset = [layoutManager locationForGlyphAtIndex:glyphIndex].x; 486 CGFloat offset = [layoutManager locationForGlyphAtIndex:glyphIndex].x;
424 minOffset = std::min(minOffset, offset); 487 minOffset = std::min(minOffset, offset);
425 if (offset > glyphOffset) 488 if (offset > glyphOffset)
426 glyphWidth = std::min(glyphWidth, offset - glyphOffset); 489 glyphWidth = std::min(glyphWidth, offset - glyphOffset);
427 } 490 }
428 glyphOffset -= minOffset; 491 glyphOffset -= minOffset;
429 if (glyphWidth == 0) 492 if (glyphWidth == 0)
430 glyphWidth = inputWidth - glyphOffset; 493 glyphWidth = inputWidth - glyphOffset;
431 if (isContentsRTL) 494 if (isContentsRTL)
432 glyphOffset += glyphWidth; 495 glyphOffset += glyphWidth;
433 return isRTL ? (inputWidth - glyphOffset) : glyphOffset; 496 return isRTL ? (inputWidth - glyphOffset) : glyphOffset;
434 } 497 }
435 498
436 @end 499 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698