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

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: moved description access to test code Created 5 years, 6 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"
11 #include "base/mac/scoped_nsobject.h" 11 #include "base/mac/scoped_nsobject.h"
12 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h" 13 #include "base/strings/string_util.h"
14 #include "base/strings/sys_string_conversions.h" 14 #include "base/strings/sys_string_conversions.h"
15 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h" 16 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h"
17 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h" 17 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h"
18 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" 18 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
19 #include "chrome/grit/generated_resources.h" 19 #include "chrome/grit/generated_resources.h"
20 #include "components/omnibox/suggestion_answer.h" 20 #include "components/omnibox/suggestion_answer.h"
21 #include "skia/ext/skia_utils_mac.h" 21 #include "skia/ext/skia_utils_mac.h"
22 #include "ui/base/l10n/l10n_util.h" 22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/gfx/font.h" 23 #include "ui/gfx/font.h"
24 24
25 namespace { 25 namespace {
26 26
27 // How much to adjust the cell sizing up from the default determined
28 // by the font.
29 const CGFloat kCellHeightAdjust = 6.0;
30
31 // How large the icon should be when displayed.
32 const CGFloat kImageSize = 19.0;
33
27 // How far to offset image column from the left. 34 // How far to offset image column from the left.
28 const CGFloat kImageXOffset = 5.0; 35 const CGFloat kImageXOffset = 5.0;
29 36
30 // How far to offset the text column from the left. 37 // How far to offset the text column from the left.
31 const CGFloat kTextStartOffset = 28.0; 38 const CGFloat kTextStartOffset = 28.0;
32 39
33 // Rounding radius of selection and hover background on popup items. 40 // Rounding radius of selection and hover background on popup items.
34 const CGFloat kCellRoundingRadius = 2.0; 41 const CGFloat kCellRoundingRadius = 2.0;
35 42
36 // Flips the given |rect| in context of the given |frame|. 43 // Flips the given |rect| in context of the given |frame|.
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
272 value:DimTextColor() 279 value:DimTextColor()
273 range:range]; 280 range:range];
274 } 281 }
275 } 282 }
276 283
277 return attributedString; 284 return attributedString;
278 } 285 }
279 286
280 } // namespace 287 } // namespace
281 288
282 @implementation OmniboxPopupCell 289 @interface OmniboxPopupCell ()
290 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
291 withFrame:(NSRect)cellFrame
292 atOffset:(CGFloat)offset
293 withMaxWidth:(int)maxWidth
294 inView:(NSView*)controlView;
295 @end
283 296
284 - (id)init { 297 @implementation OmniboxPopupCellData
285 self = [super init];
286 if (self) {
287 [self setImagePosition:NSImageLeft];
288 [self setBordered:NO];
289 [self setButtonType:NSRadioButton];
290 298
291 // Without this highlighting messes up white areas of images. 299 - (instancetype)initWithMatch:(const AutocompleteMatch&)match
292 [self setHighlightsBy:NSNoCellMask]; 300 image:(NSImage*)image {
301 if ((self = [super init])) {
302 image_.reset([image retain]);
293 303
294 const base::string16& raw_separator = 304 base::string16 raw_separator =
295 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); 305 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR);
296 separator_.reset( 306 separator_.reset(
297 [CreateAttributedString(raw_separator, DimTextColor()) retain]); 307 [CreateAttributedString(raw_separator, DimTextColor()) retain]);
308
309 isContentsRTL_ =
310 (base::i18n::RIGHT_TO_LEFT ==
311 base::i18n::GetFirstStrongCharacterDirection(match.contents));
312 matchType_ = match.type;
313
314 // Prefix may not have any characters with strong directionality, and may
315 // take the UI directionality. But prefix needs to appear in continuation
316 // of the contents so we force the directionality.
317 NSTextAlignment textAlignment =
318 isContentsRTL_ ? NSRightTextAlignment : NSLeftTextAlignment;
319 prefix_.reset(
320 [CreateAttributedString(base::UTF8ToUTF16(match.GetAdditionalInfo(
321 kACMatchPropertyContentsPrefix)),
322 ContentTextColor(), textAlignment) retain]);
323
324 contents_.reset([CreateClassifiedAttributedString(
325 match.contents, ContentTextColor(), match.contents_class) retain]);
326
327 if (match.answer) {
328 base::scoped_nsobject<NSMutableAttributedString> answerString(
329 [[NSMutableAttributedString alloc] init]);
330 DCHECK(!match.answer->second_line().text_fields().empty());
331 for (const SuggestionAnswer::TextField& textField :
332 match.answer->second_line().text_fields()) {
333 [answerString
334 appendAttributedString:CreateAnswerString(textField.text(),
335 textField.type())];
336 }
337 const base::string16 space(base::ASCIIToUTF16(" "));
338 // const base::char16 space(' ');
339 const SuggestionAnswer::TextField* textField =
340 match.answer->second_line().additional_text();
341 if (textField) {
342 [answerString
343 appendAttributedString:CreateAnswerString(space + textField->text(),
344 textField->type())];
345 }
346 textField = match.answer->second_line().status_text();
347 if (textField) {
348 [answerString
349 appendAttributedString:CreateAnswerString(space + textField->text(),
350 textField->type())];
351 }
352 description_.reset(answerString.release());
353 } else if (match.description.empty()) {
354 description_.reset();
355 } else {
356 description_.reset([CreateClassifiedAttributedString(
357 match.description, DimTextColor(), match.description_class) retain]);
358 }
298 } 359 }
299 return self; 360 return self;
300 } 361 }
301 362
363 - (id)copyWithZone:(NSZone*)zone {
groby-ooo-7-16 2015/06/05 01:28:47 Why does the cellData need copyWithZone?
dschuyler 2015/06/09 01:30:46 copyWithZone is called by setObjectValue. Maybe I
364 OmniboxPopupCellData* copy = [[OmniboxPopupCellData alloc] init];
365 copy->contents_.reset([contents_ retain]);
366 copy->separator_.reset([separator_ retain]);
367 copy->description_.reset([description_ retain]);
368 copy->prefix_.reset([prefix_ retain]);
369 copy->image_.reset([image_ retain]);
370 copy->answerImage_.reset([answerImage_ retain]);
371 copy->maxMatchContentsWidth_ = maxMatchContentsWidth_;
372 copy->contentsOffset_ = contentsOffset_;
373 copy->isContentsRTL_ = isContentsRTL_;
374 copy->matchType_ = matchType_;
375 return copy;
376 }
377
378 - (void)setContents:(NSAttributedString*)contents {
379 contents_.reset([contents retain]);
380 }
381
382 - (void)setImage:(NSImage*)image {
383 image_.reset([image retain]);
384 }
385
302 - (void)setAnswerImage:(NSImage*)image { 386 - (void)setAnswerImage:(NSImage*)image {
303 answerImage_.reset([image retain]); 387 answerImage_.reset([image retain]);
304 } 388 }
305 389
306 - (void)setMatch:(const AutocompleteMatch&)match {
307 match_ = match;
308 NSAttributedString *contents = CreateClassifiedAttributedString(
309 match_.contents, ContentTextColor(), match_.contents_class);
310 [self setAttributedTitle:contents];
311 [self setAnswerImage:nil];
312 if (match_.answer) {
313 base::scoped_nsobject<NSMutableAttributedString> answerString(
314 [[NSMutableAttributedString alloc] init]);
315 DCHECK(!match_.answer->second_line().text_fields().empty());
316 for (const SuggestionAnswer::TextField& textField :
317 match_.answer->second_line().text_fields()) {
318 NSAttributedString* as =
319 CreateAnswerString(textField.text(), textField.type());
320 [answerString appendAttributedString:as];
321 }
322 const base::char16 space(' ');
323 const SuggestionAnswer::TextField* textField =
324 match_.answer->second_line().additional_text();
325 if (textField) {
326 [answerString
327 appendAttributedString:CreateAnswerString(space + textField->text(),
328 textField->type())];
329 }
330 textField = match_.answer->second_line().status_text();
331 if (textField) {
332 [answerString
333 appendAttributedString:CreateAnswerString(space + textField->text(),
334 textField->type())];
335 }
336 description_.reset(answerString.release());
337 } else if (match_.description.empty()) {
338 description_.reset();
339 } else {
340 description_.reset([CreateClassifiedAttributedString(
341 match_.description, DimTextColor(), match_.description_class) retain]);
342 }
343 }
344
345 - (NSAttributedString*)description { 390 - (NSAttributedString*)description {
346 return description_; 391 return description_;
347 } 392 }
348 393
349 - (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth { 394 - (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth {
350 maxMatchContentsWidth_ = maxMatchContentsWidth; 395 maxMatchContentsWidth_ = maxMatchContentsWidth;
351 } 396 }
352 397
353 - (void)setContentsOffset:(CGFloat)contentsOffset { 398 - (void)setContentsOffset:(CGFloat)contentsOffset {
354 contentsOffset_ = contentsOffset; 399 contentsOffset_ = contentsOffset;
355 } 400 }
356 401
357 // The default NSButtonCell drawing leaves the image flush left and 402 - (CGFloat)getMatchContentsWidth {
358 // the title next to the image. This spaces things out to line up 403 return [contents_ size].width;
359 // with the star button and autocomplete field. 404 }
360 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { 405
361 if ([self state] == NSOnState || [self isHighlighted]) { 406 - (CGFloat)rowHeight {
362 if ([self state] == NSOnState) 407 return kImageSize + kCellHeightAdjust;
363 [SelectedBackgroundColor() set]; 408 }
364 else 409
365 [HoveredBackgroundColor() set]; 410 - (void)drawMatchWithFrame:(NSRect)cellFrame
groby-ooo-7-16 2015/06/05 01:28:47 Draw routines really belong on the cell, not the d
dschuyler 2015/06/09 01:30:46 Done.
366 NSBezierPath* path = 411 inCell:(OmniboxPopupCell*)cell
367 [NSBezierPath bezierPathWithRoundedRect:cellFrame 412 inView:(NSView*)controlView {
368 xRadius:kCellRoundingRadius 413 CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
369 yRadius:kCellRoundingRadius]; 414 CGFloat contentsWidth = [self getMatchContentsWidth];
370 [path fill]; 415 CGFloat separatorWidth = [separator_ size].width;
371 } 416 CGFloat descriptionWidth = description_ ? [description_ size].width : 0;
417 int contentsMaxWidth, descriptionMaxWidth;
418 OmniboxPopupModel::ComputeMatchMaxWidths(
419 ceilf(contentsWidth), ceilf(separatorWidth), ceilf(descriptionWidth),
420 ceilf(remainingWidth), !AutocompleteMatch::IsSearchType(matchType_),
421 &contentsMaxWidth, &descriptionMaxWidth);
372 422
373 // Put the image centered vertically but in a fixed column. 423 // Put the image centered vertically but in a fixed column.
374 NSImage* image = [self image]; 424 if (image_) {
375 if (image) {
376 NSRect imageRect = cellFrame; 425 NSRect imageRect = cellFrame;
377 imageRect.size = [image size]; 426 imageRect.size = [image_ size];
378 imageRect.origin.y += 427 imageRect.origin.y +=
379 std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0); 428 std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0);
380 imageRect.origin.x += kImageXOffset; 429 imageRect.origin.x += kImageXOffset;
381 [image drawInRect:FlipIfRTL(imageRect, cellFrame) 430 [image_ drawInRect:FlipIfRTL(imageRect, cellFrame)
382 fromRect:NSZeroRect // Entire image 431 fromRect:NSZeroRect
383 operation:NSCompositeSourceOver 432 operation:NSCompositeSourceOver
384 fraction:1.0 433 fraction:1.0
385 respectFlipped:YES 434 respectFlipped:YES
386 hints:nil]; 435 hints:nil];
387 } 436 }
388 437
389 [self drawMatchWithFrame:cellFrame inView:controlView];
390 }
391
392 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
393 NSAttributedString* contents = [self attributedTitle];
394
395 CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
396 CGFloat contentsWidth = [self getMatchContentsWidth];
397 CGFloat separatorWidth = [separator_ size].width;
398 CGFloat descriptionWidth = description_.get() ? [description_ size].width : 0;
399 int contentsMaxWidth, descriptionMaxWidth;
400 OmniboxPopupModel::ComputeMatchMaxWidths(
401 ceilf(contentsWidth),
402 ceilf(separatorWidth),
403 ceilf(descriptionWidth),
404 ceilf(remainingWidth),
405 !AutocompleteMatch::IsSearchType(match_.type),
406 &contentsMaxWidth,
407 &descriptionMaxWidth);
408
409 CGFloat offset = kTextStartOffset; 438 CGFloat offset = kTextStartOffset;
410 if (match_.type == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { 439 if (matchType_ == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) {
411 // Infinite suggestions are rendered with a prefix (usually ellipsis), which 440 // Infinite suggestions are rendered with a prefix (usually ellipsis), which
412 // appear vertically stacked. 441 // appear vertically stacked.
413 offset += [self drawMatchPrefixWithFrame:cellFrame 442 offset += [self drawMatchPrefixWithFrame:cellFrame
443 inCell:cell
414 inView:controlView 444 inView:controlView
415 withContentsMaxWidth:&contentsMaxWidth]; 445 withContentsMaxWidth:&contentsMaxWidth];
416 } 446 }
417 offset += [self drawMatchPart:contents 447 offset += [cell drawMatchPart:contents_
418 withFrame:cellFrame 448 withFrame:cellFrame
419 atOffset:offset 449 atOffset:offset
420 withMaxWidth:contentsMaxWidth 450 withMaxWidth:contentsMaxWidth
421 inView:controlView]; 451 inView:controlView];
422 452
423 if (descriptionMaxWidth != 0) { 453 if (descriptionMaxWidth != 0) {
424 offset += [self drawMatchPart:separator_ 454 offset += [cell drawMatchPart:separator_
425 withFrame:cellFrame 455 withFrame:cellFrame
426 atOffset:offset 456 atOffset:offset
427 withMaxWidth:separatorWidth 457 withMaxWidth:separatorWidth
428 inView:controlView]; 458 inView:controlView];
429 if (answerImage_) { 459 if (answerImage_) {
430 NSRect imageRect = NSMakeRect(offset, NSMinY(cellFrame), 460 NSRect imageRect = NSMakeRect(offset, NSMinY(cellFrame),
431 NSHeight(cellFrame), NSHeight(cellFrame)); 461 NSHeight(cellFrame), NSHeight(cellFrame));
432 [answerImage_ drawInRect:FlipIfRTL(imageRect, cellFrame) 462 [answerImage_ drawInRect:FlipIfRTL(imageRect, cellFrame)
433 fromRect:NSZeroRect 463 fromRect:NSZeroRect
434 operation:NSCompositeSourceOver 464 operation:NSCompositeSourceOver
435 fraction:1.0 465 fraction:1.0
436 respectFlipped:YES 466 respectFlipped:YES
437 hints:nil]; 467 hints:nil];
438 offset += NSWidth(imageRect); 468 offset += NSWidth(imageRect);
439 } 469 }
440 offset += [self drawMatchPart:description_ 470 offset += [cell drawMatchPart:description_
441 withFrame:cellFrame 471 withFrame:cellFrame
442 atOffset:offset 472 atOffset:offset
443 withMaxWidth:descriptionMaxWidth 473 withMaxWidth:descriptionMaxWidth
444 inView:controlView]; 474 inView:controlView];
445 } 475 }
446 } 476 }
447 477
448 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame 478 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame
479 inCell:(OmniboxPopupCell*)cell
449 inView:(NSView*)controlView 480 inView:(NSView*)controlView
450 withContentsMaxWidth:(int*)contentsMaxWidth { 481 withContentsMaxWidth:(int*)contentsMaxWidth {
451 CGFloat offset = 0.0f; 482 CGFloat offset = 0.0f;
452 CGFloat remainingWidth = GetContentAreaWidth(cellFrame); 483 CGFloat remainingWidth = GetContentAreaWidth(cellFrame);
453 bool isRTL = base::i18n::IsRTL();
454 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT ==
455 base::i18n::GetFirstStrongCharacterDirection(match_.contents));
456 // Prefix may not have any characters with strong directionality, and may take
457 // the UI directionality. But prefix needs to appear in continuation of the
458 // contents so we force the directionality.
459 NSTextAlignment textAlignment = isContentsRTL ?
460 NSRightTextAlignment : NSLeftTextAlignment;
461 prefix_.reset([CreateAttributedString(base::UTF8ToUTF16(
462 match_.GetAdditionalInfo(kACMatchPropertyContentsPrefix)),
463 ContentTextColor(), textAlignment) retain]);
464 CGFloat prefixWidth = [prefix_ size].width; 484 CGFloat prefixWidth = [prefix_ size].width;
465 485
466 CGFloat prefixOffset = 0.0f; 486 CGFloat prefixOffset = 0.0f;
467 if (isRTL != isContentsRTL) { 487 if (base::i18n::IsRTL() != isContentsRTL_) {
468 // The contents is rendered between the contents offset extending towards 488 // The contents is rendered between the contents offset extending towards
469 // the start edge, while prefix is rendered in opposite direction. Ideally 489 // the start edge, while prefix is rendered in opposite direction. Ideally
470 // the prefix should be rendered at |contentsOffset_|. If that is not 490 // the prefix should be rendered at |contentsOffset_|. If that is not
471 // sufficient to render the widest suggestion, we increase it to 491 // sufficient to render the widest suggestion, we increase it to
472 // |maxMatchContentsWidth_|. If |remainingWidth| is not sufficient to 492 // |maxMatchContentsWidth_|. If |remainingWidth| is not sufficient to
473 // accommodate that, we reduce the offset so that the prefix gets rendered. 493 // accommodate that, we reduce the offset so that the prefix gets rendered.
474 prefixOffset = std::min( 494 prefixOffset = std::min(
475 remainingWidth - prefixWidth, std::max(contentsOffset_, 495 remainingWidth - prefixWidth, std::max(contentsOffset_,
476 maxMatchContentsWidth_)); 496 maxMatchContentsWidth_));
477 offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth); 497 offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth);
478 } else { // The direction of contents is same as UI direction. 498 } else { // The direction of contents is same as UI direction.
479 // Ideally the offset should be |contentsOffset_|. If the max total width 499 // Ideally the offset should be |contentsOffset_|. If the max total width
480 // (|prefixWidth| + |maxMatchContentsWidth_|) from offset will exceed the 500 // (|prefixWidth| + |maxMatchContentsWidth_|) from offset will exceed the
481 // |remainingWidth|, then we shift the offset to the left , so that all 501 // |remainingWidth|, then we shift the offset to the left , so that all
482 // postfix suggestions are visible. 502 // postfix suggestions are visible.
483 // We have to render the prefix, so offset has to be at least |prefixWidth|. 503 // We have to render the prefix, so offset has to be at least |prefixWidth|.
484 offset = std::max(prefixWidth, 504 offset = std::max(
505 prefixWidth,
485 std::min(remainingWidth - maxMatchContentsWidth_, contentsOffset_)); 506 std::min(remainingWidth - maxMatchContentsWidth_, contentsOffset_));
486 prefixOffset = offset - prefixWidth; 507 prefixOffset = offset - prefixWidth;
487 } 508 }
488 *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth), 509 *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth),
489 *contentsMaxWidth); 510 *contentsMaxWidth);
490 [self drawMatchPart:prefix_ 511 [cell drawMatchPart:prefix_
491 withFrame:cellFrame 512 withFrame:cellFrame
492 atOffset:prefixOffset + kTextStartOffset 513 atOffset:prefixOffset + kTextStartOffset
493 withMaxWidth:prefixWidth 514 withMaxWidth:prefixWidth
494 inView:controlView]; 515 inView:controlView];
495 return offset; 516 return offset;
496 } 517 }
497 518
498 - (CGFloat)drawMatchPart:(NSAttributedString*)as 519 @end
520
521 @implementation OmniboxPopupCell
522
523 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView {
524 if ([self state] == NSOnState || [self isHighlighted]) {
525 if ([self state] == NSOnState)
526 [SelectedBackgroundColor() set];
527 else
528 [HoveredBackgroundColor() set];
529 NSBezierPath* path =
530 [NSBezierPath bezierPathWithRoundedRect:cellFrame
531 xRadius:kCellRoundingRadius
532 yRadius:kCellRoundingRadius];
533 [path fill];
534 }
535
536 [[self objectValue] drawMatchWithFrame:cellFrame
537 inCell:self
538 inView:controlView];
539 }
540
541 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString
499 withFrame:(NSRect)cellFrame 542 withFrame:(NSRect)cellFrame
500 atOffset:(CGFloat)offset 543 atOffset:(CGFloat)offset
501 withMaxWidth:(int)maxWidth 544 withMaxWidth:(int)maxWidth
502 inView:(NSView*)controlView { 545 inView:(NSView*)controlView {
503 if (offset > NSWidth(cellFrame)) 546 if (offset > NSWidth(cellFrame))
504 return 0.0f; 547 return 0.0f;
505 NSRect renderRect = ShiftRect(cellFrame, offset); 548 NSRect renderRect = ShiftRect(cellFrame, offset);
506 renderRect.size.width = 549 renderRect.size.width =
507 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth)); 550 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth));
508 if (renderRect.size.width != 0) { 551 NSRect textRect =
509 [self drawTitle:as 552 [attributedString boundingRectWithSize:renderRect.size options:nil];
510 withFrame:FlipIfRTL(renderRect, cellFrame) 553 renderRect.origin.y +=
511 inView:controlView]; 554 std::floor((NSHeight(cellFrame) - NSHeight(textRect)) / 2.0);
512 } 555 if (NSWidth(renderRect) > 0.0)
556 [attributedString drawInRect:FlipIfRTL(renderRect, cellFrame)];
513 return NSWidth(renderRect); 557 return NSWidth(renderRect);
514 } 558 }
515 559
516 - (CGFloat)getMatchContentsWidth {
517 NSAttributedString* contents = [self attributedTitle];
518 return contents ? [contents size].width : 0;
519 }
520
521
522 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match { 560 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match {
523 const base::string16& inputText = base::UTF8ToUTF16( 561 const base::string16& inputText = base::UTF8ToUTF16(
524 match.GetAdditionalInfo(kACMatchPropertyInputText)); 562 match.GetAdditionalInfo(kACMatchPropertyInputText));
525 int contentsStartIndex = 0; 563 int contentsStartIndex = 0;
526 base::StringToInt( 564 base::StringToInt(
527 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex), 565 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex),
528 &contentsStartIndex); 566 &contentsStartIndex);
529 // Ignore invalid state. 567 // Ignore invalid state.
530 if (!StartsWith(match.fill_into_edit, inputText, true) 568 if (!StartsWith(match.fill_into_edit, inputText, true)
531 || !EndsWith(match.fill_into_edit, match.contents, true) 569 || !EndsWith(match.fill_into_edit, match.contents, true)
532 || ((size_t)contentsStartIndex >= inputText.length())) { 570 || ((size_t)contentsStartIndex >= inputText.length())) {
533 return 0; 571 return 0;
534 } 572 }
535 bool isRTL = base::i18n::IsRTL(); 573 bool isRTL = base::i18n::IsRTL();
536 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT == 574 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT ==
537 base::i18n::GetFirstStrongCharacterDirection(match.contents)); 575 base::i18n::GetFirstStrongCharacterDirection(match.contents));
538 576
539 // Color does not matter. 577 // Color does not matter.
540 NSAttributedString* as = CreateAttributedString(inputText, DimTextColor()); 578 NSAttributedString* attributedString =
541 base::scoped_nsobject<NSTextStorage> textStorage([[NSTextStorage alloc] 579 CreateAttributedString(inputText, DimTextColor());
542 initWithAttributedString:as]); 580 base::scoped_nsobject<NSTextStorage> textStorage(
581 [[NSTextStorage alloc] initWithAttributedString:attributedString]);
543 base::scoped_nsobject<NSLayoutManager> layoutManager( 582 base::scoped_nsobject<NSLayoutManager> layoutManager(
544 [[NSLayoutManager alloc] init]); 583 [[NSLayoutManager alloc] init]);
545 base::scoped_nsobject<NSTextContainer> textContainer( 584 base::scoped_nsobject<NSTextContainer> textContainer(
546 [[NSTextContainer alloc] init]); 585 [[NSTextContainer alloc] init]);
547 [layoutManager addTextContainer:textContainer]; 586 [layoutManager addTextContainer:textContainer];
548 [textStorage addLayoutManager:layoutManager]; 587 [textStorage addLayoutManager:layoutManager];
549 588
550 NSUInteger charIndex = static_cast<NSUInteger>(contentsStartIndex); 589 NSUInteger charIndex = static_cast<NSUInteger>(contentsStartIndex);
551 NSUInteger glyphIndex = 590 NSUInteger glyphIndex =
552 [layoutManager glyphIndexForCharacterAtIndex:charIndex]; 591 [layoutManager glyphIndexForCharacterAtIndex:charIndex];
553 592
554 // This offset is computed from the left edge of the glyph always from the 593 // This offset is computed from the left edge of the glyph always from the
555 // left edge of the string, irrespective of the directionality of UI or text. 594 // left edge of the string, irrespective of the directionality of UI or text.
556 CGFloat glyphOffset = [layoutManager locationForGlyphAtIndex:glyphIndex].x; 595 CGFloat glyphOffset = [layoutManager locationForGlyphAtIndex:glyphIndex].x;
557 596
558 CGFloat inputWidth = [as size].width; 597 CGFloat inputWidth = [attributedString size].width;
559 598
560 // The offset obtained above may need to be corrected because the left-most 599 // The offset obtained above may need to be corrected because the left-most
561 // glyph may not have 0 offset. So we find the offset of left-most glyph, and 600 // glyph may not have 0 offset. So we find the offset of left-most glyph, and
562 // subtract it from the offset of the glyph we obtained above. 601 // subtract it from the offset of the glyph we obtained above.
563 CGFloat minOffset = glyphOffset; 602 CGFloat minOffset = glyphOffset;
564 603
565 // If content is RTL, we are interested in the right-edge of the glyph. 604 // If content is RTL, we are interested in the right-edge of the glyph.
566 // Unfortunately the bounding rect computation methods from NSLayoutManager or 605 // Unfortunately the bounding rect computation methods from NSLayoutManager or
567 // NSFont don't work correctly with bidirectional text. So we compute the 606 // NSFont don't work correctly with bidirectional text. So we compute the
568 // glyph width by finding the closest glyph offset to the right of the glyph 607 // glyph width by finding the closest glyph offset to the right of the glyph
569 // we are looking for. 608 // we are looking for.
570 CGFloat glyphWidth = inputWidth; 609 CGFloat glyphWidth = inputWidth;
571 610
572 for (NSUInteger i = 0; i < [as length]; i++) { 611 for (NSUInteger i = 0; i < [attributedString length]; i++) {
573 if (i == charIndex) continue; 612 if (i == charIndex) continue;
574 glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:i]; 613 glyphIndex = [layoutManager glyphIndexForCharacterAtIndex:i];
575 CGFloat offset = [layoutManager locationForGlyphAtIndex:glyphIndex].x; 614 CGFloat offset = [layoutManager locationForGlyphAtIndex:glyphIndex].x;
576 minOffset = std::min(minOffset, offset); 615 minOffset = std::min(minOffset, offset);
577 if (offset > glyphOffset) 616 if (offset > glyphOffset)
578 glyphWidth = std::min(glyphWidth, offset - glyphOffset); 617 glyphWidth = std::min(glyphWidth, offset - glyphOffset);
579 } 618 }
580 glyphOffset -= minOffset; 619 glyphOffset -= minOffset;
581 if (glyphWidth == 0) 620 if (glyphWidth == 0)
582 glyphWidth = inputWidth - glyphOffset; 621 glyphWidth = inputWidth - glyphOffset;
583 if (isContentsRTL) 622 if (isContentsRTL)
584 glyphOffset += glyphWidth; 623 glyphOffset += glyphWidth;
585 return isRTL ? (inputWidth - glyphOffset) : glyphOffset; 624 return isRTL ? (inputWidth - glyphOffset) : glyphOffset;
586 } 625 }
587 626
588 @end 627 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698