 Chromium Code Reviews
 Chromium Code Reviews Issue 1099403005:
  [AiS] changing mac omnibox suggestions form NSMatrix to NSTableView  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 1099403005:
  [AiS] changing mac omnibox suggestions form NSMatrix to NSTableView  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| OLD | NEW | 
|---|---|
| 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/foundation_util.h" | |
| 11 #include "base/mac/scoped_nsobject.h" | 12 #include "base/mac/scoped_nsobject.h" | 
| 12 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" | 
| 13 #include "base/strings/string_util.h" | 14 #include "base/strings/string_util.h" | 
| 14 #include "base/strings/sys_string_conversions.h" | 15 #include "base/strings/sys_string_conversions.h" | 
| 15 #include "base/strings/utf_string_conversions.h" | 16 #include "base/strings/utf_string_conversions.h" | 
| 16 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h" | 17 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h" | 
| 17 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h" | 18 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h" | 
| 18 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" | 19 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" | 
| 19 #include "chrome/grit/generated_resources.h" | 20 #include "chrome/grit/generated_resources.h" | 
| 20 #include "components/omnibox/suggestion_answer.h" | 21 #include "components/omnibox/suggestion_answer.h" | 
| 21 #include "skia/ext/skia_utils_mac.h" | 22 #include "skia/ext/skia_utils_mac.h" | 
| 22 #include "ui/base/l10n/l10n_util.h" | 23 #include "ui/base/l10n/l10n_util.h" | 
| 23 #include "ui/gfx/font.h" | 24 #include "ui/gfx/font.h" | 
| 24 | 25 | 
| 25 namespace { | 26 namespace { | 
| 26 | 27 | 
| 28 // How much to adjust the cell sizing up from the default determined | |
| 29 // by the font. | |
| 30 const CGFloat kCellHeightAdjust = 6.0; | |
| 31 | |
| 32 // How large the icon should be when displayed. | |
| 33 const CGFloat kImageSize = 19.0; | |
| 34 | |
| 27 // How far to offset image column from the left. | 35 // How far to offset image column from the left. | 
| 28 const CGFloat kImageXOffset = 5.0; | 36 const CGFloat kImageXOffset = 5.0; | 
| 29 | 37 | 
| 30 // How far to offset the text column from the left. | 38 // How far to offset the text column from the left. | 
| 31 const CGFloat kTextStartOffset = 28.0; | 39 const CGFloat kTextStartOffset = 28.0; | 
| 32 | 40 | 
| 33 // Rounding radius of selection and hover background on popup items. | 41 // Rounding radius of selection and hover background on popup items. | 
| 34 const CGFloat kCellRoundingRadius = 2.0; | 42 const CGFloat kCellRoundingRadius = 2.0; | 
| 35 | 43 | 
| 36 // Flips the given |rect| in context of the given |frame|. | 44 // Flips the given |rect| in context of the given |frame|. | 
| (...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 272 value:DimTextColor() | 280 value:DimTextColor() | 
| 273 range:range]; | 281 range:range]; | 
| 274 } | 282 } | 
| 275 } | 283 } | 
| 276 | 284 | 
| 277 return attributedString; | 285 return attributedString; | 
| 278 } | 286 } | 
| 279 | 287 | 
| 280 } // namespace | 288 } // namespace | 
| 281 | 289 | 
| 282 @implementation OmniboxPopupCell | 290 @interface OmniboxPopupCell () | 
| 291 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString | |
| 292 withFrame:(NSRect)cellFrame | |
| 293 atOffset:(CGFloat)offset | |
| 294 withMaxWidth:(int)maxWidth; | |
| 295 @end | |
| 283 | 296 | 
| 284 - (instancetype)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 @synthesize contents = contents_; | 
| 
groby-ooo-7-16
2015/06/12 18:51:07
This is the "default" mode for @synthesize. Since
 
dschuyler
2015/06/12 21:39:14
It seems that there is a warning that requires it.
 | |
| 292 [self setHighlightsBy:NSNoCellMask]; | 300 @synthesize description = description_; | 
| 301 @synthesize prefix = prefix_; | |
| 302 @synthesize image = image_; | |
| 303 @synthesize answerImage = answerImage_; | |
| 304 @synthesize contentsOffset = contentsOffset_; | |
| 305 @synthesize isContentsRTL = isContentsRTL_; | |
| 306 @synthesize matchType = matchType_; | |
| 293 | 307 | 
| 294 const base::string16& raw_separator = | 308 - (instancetype)initWithMatch:(const AutocompleteMatch&)match | 
| 295 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); | 309 image:(NSImage*)image | 
| 296 separator_.reset( | 310 answerImage:(NSImage*)answerImage { | 
| 297 [CreateAttributedString(raw_separator, DimTextColor()) retain]); | 311 if ((self = [super init])) { | 
| 312 image_ = [image retain]; | |
| 313 answerImage_ = [answerImage retain]; | |
| 314 | |
| 315 isContentsRTL_ = | |
| 316 (base::i18n::RIGHT_TO_LEFT == | |
| 317 base::i18n::GetFirstStrongCharacterDirection(match.contents)); | |
| 318 matchType_ = match.type; | |
| 319 | |
| 320 // Prefix may not have any characters with strong directionality, and may | |
| 321 // take the UI directionality. But prefix needs to appear in continuation | |
| 322 // of the contents so we force the directionality. | |
| 323 NSTextAlignment textAlignment = | |
| 324 isContentsRTL_ ? NSRightTextAlignment : NSLeftTextAlignment; | |
| 325 prefix_ = | |
| 326 [CreateAttributedString(base::UTF8ToUTF16(match.GetAdditionalInfo( | |
| 327 kACMatchPropertyContentsPrefix)), | |
| 328 ContentTextColor(), textAlignment) retain]; | |
| 329 | |
| 330 contents_ = [CreateClassifiedAttributedString( | |
| 331 match.contents, ContentTextColor(), match.contents_class) retain]; | |
| 332 | |
| 333 if (match.answer) { | |
| 334 base::scoped_nsobject<NSMutableAttributedString> answerString( | |
| 335 [[NSMutableAttributedString alloc] init]); | |
| 336 DCHECK(!match.answer->second_line().text_fields().empty()); | |
| 337 for (const SuggestionAnswer::TextField& textField : | |
| 338 match.answer->second_line().text_fields()) { | |
| 339 [answerString | |
| 340 appendAttributedString:CreateAnswerString(textField.text(), | |
| 341 textField.type())]; | |
| 342 } | |
| 343 const base::string16 space(base::ASCIIToUTF16(" ")); | |
| 344 // const base::char16 space(' '); | |
| 
groby-ooo-7-16
2015/06/12 18:51:07
Kill comment
 
dschuyler
2015/06/12 21:39:14
Done.
 | |
| 345 const SuggestionAnswer::TextField* textField = | |
| 346 match.answer->second_line().additional_text(); | |
| 347 if (textField) { | |
| 348 [answerString | |
| 349 appendAttributedString:CreateAnswerString(space + textField->text(), | |
| 350 textField->type())]; | |
| 351 } | |
| 352 textField = match.answer->second_line().status_text(); | |
| 353 if (textField) { | |
| 354 [answerString | |
| 355 appendAttributedString:CreateAnswerString(space + textField->text(), | |
| 356 textField->type())]; | |
| 357 } | |
| 358 description_ = [answerString.release() retain]; | |
| 
groby-ooo-7-16
2015/06/12 18:51:06
scoped_nsobject's release is not ObjC's release -
 
Scott Hess - ex-Googler
2015/06/12 20:21:32
IMHO this would be pretty clear to a C++ programme
 
groby-ooo-7-16
2015/06/12 21:01:54
swap won't work, since description_ isn't scoped_n
 
dschuyler
2015/06/12 21:39:14
Done.
 | |
| 359 } else if (match.description.empty()) { | |
| 360 description_ = nil; | |
| 
groby-ooo-7-16
2015/06/12 18:51:07
Not necessary - all ivars are initialized to nil i
 
dschuyler
2015/06/12 21:39:14
Done.
 | |
| 361 } else { | |
| 362 description_ = [CreateClassifiedAttributedString( | |
| 363 match.description, DimTextColor(), match.description_class) retain]; | |
| 364 } | |
| 298 } | 365 } | 
| 299 return self; | 366 return self; | 
| 300 } | 367 } | 
| 301 | 368 | 
| 302 - (void)setAnswerImage:(NSImage*)image { | 369 - (instancetype)copyWithZone:(NSZone*)zone { | 
| 303 answerImage_.reset([image retain]); | 370 return [self retain]; | 
| 304 } | 371 } | 
| 305 | 372 | 
| 306 - (void)setMatch:(const AutocompleteMatch&)match { | 373 - (CGFloat)getMatchContentsWidth { | 
| 307 match_ = match; | 374 return [contents_ size].width; | 
| 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* attributedString = | |
| 319 CreateAnswerString(textField.text(), textField.type()); | |
| 320 [answerString appendAttributedString:attributedString]; | |
| 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 } | 375 } | 
| 344 | 376 | 
| 345 - (NSAttributedString*)description { | 377 - (CGFloat)rowHeight { | 
| 346 return description_; | 378 return kImageSize + kCellHeightAdjust; | 
| 347 } | 379 } | 
| 348 | 380 | 
| 349 - (void)setMaxMatchContentsWidth:(CGFloat)maxMatchContentsWidth { | 381 @end | 
| 350 maxMatchContentsWidth_ = maxMatchContentsWidth; | |
| 351 } | |
| 352 | 382 | 
| 353 - (void)setContentsOffset:(CGFloat)contentsOffset { | 383 @implementation OmniboxPopupCell | 
| 354 contentsOffset_ = contentsOffset; | |
| 355 } | |
| 356 | 384 | 
| 357 // The default NSButtonCell drawing leaves the image flush left and | 385 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 
| 358 // the title next to the image. This spaces things out to line up | |
| 359 // with the star button and autocomplete field. | |
| 360 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { | |
| 361 if ([self state] == NSOnState || [self isHighlighted]) { | 386 if ([self state] == NSOnState || [self isHighlighted]) { | 
| 362 if ([self state] == NSOnState) | 387 if ([self state] == NSOnState) | 
| 363 [SelectedBackgroundColor() set]; | 388 [SelectedBackgroundColor() set]; | 
| 364 else | 389 else | 
| 365 [HoveredBackgroundColor() set]; | 390 [HoveredBackgroundColor() set]; | 
| 366 NSBezierPath* path = | 391 NSBezierPath* path = | 
| 367 [NSBezierPath bezierPathWithRoundedRect:cellFrame | 392 [NSBezierPath bezierPathWithRoundedRect:cellFrame | 
| 368 xRadius:kCellRoundingRadius | 393 xRadius:kCellRoundingRadius | 
| 369 yRadius:kCellRoundingRadius]; | 394 yRadius:kCellRoundingRadius]; | 
| 370 [path fill]; | 395 [path fill]; | 
| 371 } | 396 } | 
| 372 | 397 | 
| 398 [self drawMatchWithFrame:cellFrame | |
| 399 withCellData:[self objectValue] | |
| 400 inView:controlView]; | |
| 401 } | |
| 402 | |
| 403 - (void)drawMatchWithFrame:(NSRect)cellFrame | |
| 404 withCellData:(OmniboxPopupCellData*)cellData | |
| 405 inView:(NSView*)controlView { | |
| 406 OmniboxPopupMatrix* tableView = | |
| 407 base::mac::ObjCCastStrict<OmniboxPopupMatrix>(controlView); | |
| 408 CGFloat remainingWidth = GetContentAreaWidth(cellFrame); | |
| 409 CGFloat contentsWidth = [cellData getMatchContentsWidth]; | |
| 410 CGFloat separatorWidth = [[tableView separator] size].width; | |
| 411 CGFloat descriptionWidth = | |
| 412 [cellData description] ? [[cellData description] size].width : 0; | |
| 413 int contentsMaxWidth, descriptionMaxWidth; | |
| 414 OmniboxPopupModel::ComputeMatchMaxWidths( | |
| 415 ceilf(contentsWidth), ceilf(separatorWidth), ceilf(descriptionWidth), | |
| 416 ceilf(remainingWidth), | |
| 417 !AutocompleteMatch::IsSearchType([cellData matchType]), &contentsMaxWidth, | |
| 418 &descriptionMaxWidth); | |
| 419 | |
| 373 // Put the image centered vertically but in a fixed column. | 420 // Put the image centered vertically but in a fixed column. | 
| 374 NSImage* image = [self image]; | 421 if ([cellData image]) { | 
| 
groby-ooo-7-16
2015/06/12 18:51:07
This check is not necessary. (A nil image would ju
 
dschuyler
2015/06/12 21:39:14
Done.
 | |
| 375 if (image) { | |
| 376 NSRect imageRect = cellFrame; | 422 NSRect imageRect = cellFrame; | 
| 377 imageRect.size = [image size]; | 423 imageRect.size = [[cellData image] size]; | 
| 378 imageRect.origin.y += | 424 imageRect.origin.y += | 
| 379 std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0); | 425 std::floor((NSHeight(cellFrame) - NSHeight(imageRect)) / 2.0); | 
| 380 imageRect.origin.x += kImageXOffset; | 426 imageRect.origin.x += kImageXOffset; | 
| 381 [image drawInRect:FlipIfRTL(imageRect, cellFrame) | 427 [[cellData image] drawInRect:FlipIfRTL(imageRect, cellFrame) | 
| 382 fromRect:NSZeroRect // Entire image | 428 fromRect:NSZeroRect | 
| 383 operation:NSCompositeSourceOver | 429 operation:NSCompositeSourceOver | 
| 384 fraction:1.0 | 430 fraction:1.0 | 
| 385 respectFlipped:YES | 431 respectFlipped:YES | 
| 386 hints:nil]; | 432 hints:nil]; | 
| 387 } | 433 } | 
| 388 | 434 | 
| 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; | 435 CGFloat offset = kTextStartOffset; | 
| 410 if (match_.type == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { | 436 if ([cellData matchType] == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { | 
| 411 // Infinite suggestions are rendered with a prefix (usually ellipsis), which | 437 // Infinite suggestions are rendered with a prefix (usually ellipsis), which | 
| 412 // appear vertically stacked. | 438 // appear vertically stacked. | 
| 413 offset += [self drawMatchPrefixWithFrame:cellFrame | 439 offset += [self drawMatchPrefixWithFrame:cellFrame | 
| 440 withCellData:cellData | |
| 414 inView:controlView | 441 inView:controlView | 
| 415 withContentsMaxWidth:&contentsMaxWidth]; | 442 withContentsMaxWidth:&contentsMaxWidth]; | 
| 416 } | 443 } | 
| 417 offset += [self drawMatchPart:contents | 444 offset += [self drawMatchPart:[cellData contents] | 
| 418 withFrame:cellFrame | 445 withFrame:cellFrame | 
| 419 atOffset:offset | 446 atOffset:offset | 
| 420 withMaxWidth:contentsMaxWidth | 447 withMaxWidth:contentsMaxWidth]; | 
| 421 inView:controlView]; | |
| 422 | 448 | 
| 423 if (descriptionMaxWidth != 0) { | 449 if (descriptionMaxWidth != 0) { | 
| 424 offset += [self drawMatchPart:separator_ | 450 offset += [self drawMatchPart:[tableView separator] | 
| 425 withFrame:cellFrame | 451 withFrame:cellFrame | 
| 426 atOffset:offset | 452 atOffset:offset | 
| 427 withMaxWidth:separatorWidth | 453 withMaxWidth:separatorWidth]; | 
| 428 inView:controlView]; | 454 if ([cellData answerImage]) { | 
| 
groby-ooo-7-16
2015/06/12 18:51:07
Unnecessary check
 
dschuyler
2015/06/12 21:39:14
Done.
 | |
| 429 if (answerImage_) { | |
| 430 NSRect imageRect = NSMakeRect(offset, NSMinY(cellFrame), | 455 NSRect imageRect = NSMakeRect(offset, NSMinY(cellFrame), | 
| 431 NSHeight(cellFrame), NSHeight(cellFrame)); | 456 NSHeight(cellFrame), NSHeight(cellFrame)); | 
| 432 [answerImage_ drawInRect:FlipIfRTL(imageRect, cellFrame) | 457 [[cellData answerImage] drawInRect:FlipIfRTL(imageRect, cellFrame) | 
| 433 fromRect:NSZeroRect | 458 fromRect:NSZeroRect | 
| 434 operation:NSCompositeSourceOver | 459 operation:NSCompositeSourceOver | 
| 435 fraction:1.0 | 460 fraction:1.0 | 
| 436 respectFlipped:YES | 461 respectFlipped:YES | 
| 437 hints:nil]; | 462 hints:nil]; | 
| 438 offset += NSWidth(imageRect); | 463 offset += NSWidth(imageRect); | 
| 439 } | 464 } | 
| 440 offset += [self drawMatchPart:description_ | 465 offset += [self drawMatchPart:[cellData description] | 
| 441 withFrame:cellFrame | 466 withFrame:cellFrame | 
| 442 atOffset:offset | 467 atOffset:offset | 
| 443 withMaxWidth:descriptionMaxWidth | 468 withMaxWidth:descriptionMaxWidth]; | 
| 444 inView:controlView]; | |
| 445 } | 469 } | 
| 446 } | 470 } | 
| 447 | 471 | 
| 448 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame | 472 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame | 
| 
groby-ooo-7-16
2015/06/12 18:51:06
Didn't see a prototype for this?
 
dschuyler
2015/06/12 21:39:14
Done.
 | |
| 473 withCellData:(OmniboxPopupCellData*)cellData | |
| 449 inView:(NSView*)controlView | 474 inView:(NSView*)controlView | 
| 450 withContentsMaxWidth:(int*)contentsMaxWidth { | 475 withContentsMaxWidth:(int*)contentsMaxWidth { | 
| 451 CGFloat offset = 0.0f; | 476 CGFloat offset = 0.0f; | 
| 452 CGFloat remainingWidth = GetContentAreaWidth(cellFrame); | 477 CGFloat remainingWidth = GetContentAreaWidth(cellFrame); | 
| 453 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT == | 478 CGFloat prefixWidth = [[cellData prefix] size].width; | 
| 454 base::i18n::GetFirstStrongCharacterDirection(match_.contents)); | 479 | 
| 455 // Prefix may not have any characters with strong directionality, and may take | 480 OmniboxPopupMatrix* tableView = | 
| 456 // the UI directionality. But prefix needs to appear in continuation of the | 481 base::mac::ObjCCastStrict<OmniboxPopupMatrix>(controlView); | 
| 
groby-ooo-7-16
2015/06/12 18:51:06
You could just pass in the OmniboxPopupMatrix dire
 
dschuyler
2015/06/12 21:39:14
Done.
 | |
| 457 // contents so we force the directionality. | |
| 458 NSTextAlignment textAlignment = isContentsRTL ? | |
| 459 NSRightTextAlignment : NSLeftTextAlignment; | |
| 460 prefix_.reset([CreateAttributedString(base::UTF8ToUTF16( | |
| 461 match_.GetAdditionalInfo(kACMatchPropertyContentsPrefix)), | |
| 462 ContentTextColor(), textAlignment) retain]); | |
| 463 CGFloat prefixWidth = [prefix_ size].width; | |
| 464 | 482 | 
| 465 CGFloat prefixOffset = 0.0f; | 483 CGFloat prefixOffset = 0.0f; | 
| 466 if (base::i18n::IsRTL() != isContentsRTL) { | 484 if (base::i18n::IsRTL() != [cellData isContentsRTL]) { | 
| 467 // The contents is rendered between the contents offset extending towards | 485 // The contents is rendered between the contents offset extending towards | 
| 468 // the start edge, while prefix is rendered in opposite direction. Ideally | 486 // the start edge, while prefix is rendered in opposite direction. Ideally | 
| 469 // the prefix should be rendered at |contentsOffset_|. If that is not | 487 // the prefix should be rendered at |contentsOffset_|. If that is not | 
| 470 // sufficient to render the widest suggestion, we increase it to | 488 // sufficient to render the widest suggestion, we increase it to | 
| 471 // |maxMatchContentsWidth_|. If |remainingWidth| is not sufficient to | 489 // |maxMatchContentsWidth|. If |remainingWidth| is not sufficient to | 
| 472 // accommodate that, we reduce the offset so that the prefix gets rendered. | 490 // accommodate that, we reduce the offset so that the prefix gets rendered. | 
| 473 prefixOffset = std::min( | 491 prefixOffset = std::min( | 
| 474 remainingWidth - prefixWidth, std::max(contentsOffset_, | 492 remainingWidth - prefixWidth, | 
| 475 maxMatchContentsWidth_)); | 493 std::max([cellData contentsOffset], [tableView maxMatchContentsWidth])); | 
| 476 offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth); | 494 offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth); | 
| 477 } else { // The direction of contents is same as UI direction. | 495 } else { // The direction of contents is same as UI direction. | 
| 478 // Ideally the offset should be |contentsOffset_|. If the max total width | 496 // Ideally the offset should be |contentsOffset_|. If the max total width | 
| 479 // (|prefixWidth| + |maxMatchContentsWidth_|) from offset will exceed the | 497 // (|prefixWidth| + |maxMatchContentsWidth|) from offset will exceed the | 
| 480 // |remainingWidth|, then we shift the offset to the left , so that all | 498 // |remainingWidth|, then we shift the offset to the left , so that all | 
| 481 // postfix suggestions are visible. | 499 // postfix suggestions are visible. | 
| 482 // We have to render the prefix, so offset has to be at least |prefixWidth|. | 500 // We have to render the prefix, so offset has to be at least |prefixWidth|. | 
| 483 offset = std::max(prefixWidth, | 501 offset = | 
| 484 std::min(remainingWidth - maxMatchContentsWidth_, contentsOffset_)); | 502 std::max(prefixWidth, | 
| 503 std::min(remainingWidth - [tableView maxMatchContentsWidth], | |
| 504 [cellData contentsOffset])); | |
| 485 prefixOffset = offset - prefixWidth; | 505 prefixOffset = offset - prefixWidth; | 
| 486 } | 506 } | 
| 487 *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth), | 507 *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth), | 
| 488 *contentsMaxWidth); | 508 *contentsMaxWidth); | 
| 489 [self drawMatchPart:prefix_ | 509 [self drawMatchPart:[cellData prefix] | 
| 490 withFrame:cellFrame | 510 withFrame:cellFrame | 
| 491 atOffset:prefixOffset + kTextStartOffset | 511 atOffset:prefixOffset + kTextStartOffset | 
| 492 withMaxWidth:prefixWidth | 512 withMaxWidth:prefixWidth]; | 
| 493 inView:controlView]; | |
| 494 return offset; | 513 return offset; | 
| 495 } | 514 } | 
| 496 | 515 | 
| 497 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString | 516 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString | 
| 498 withFrame:(NSRect)cellFrame | 517 withFrame:(NSRect)cellFrame | 
| 499 atOffset:(CGFloat)offset | 518 atOffset:(CGFloat)offset | 
| 500 withMaxWidth:(int)maxWidth | 519 withMaxWidth:(int)maxWidth { | 
| 501 inView:(NSView*)controlView { | |
| 502 if (offset > NSWidth(cellFrame)) | 520 if (offset > NSWidth(cellFrame)) | 
| 503 return 0.0f; | 521 return 0.0f; | 
| 504 NSRect renderRect = ShiftRect(cellFrame, offset); | 522 NSRect renderRect = ShiftRect(cellFrame, offset); | 
| 505 renderRect.size.width = | 523 renderRect.size.width = | 
| 506 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth)); | 524 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth)); | 
| 507 if (renderRect.size.width != 0) { | 525 NSRect textRect = | 
| 508 [self drawTitle:attributedString | 526 [attributedString boundingRectWithSize:renderRect.size options:nil]; | 
| 509 withFrame:FlipIfRTL(renderRect, cellFrame) | 527 renderRect.origin.y += | 
| 510 inView:controlView]; | 528 std::floor((NSHeight(cellFrame) - NSHeight(textRect)) / 2.0); | 
| 
groby-ooo-7-16
2015/06/12 18:51:06
Since this is only part of the match, are you sure
 
dschuyler
2015/06/12 21:39:14
I'm not 100% sure.  I do have a bug to look into t
 | |
| 511 } | 529 if (NSWidth(renderRect) > 0.0) | 
| 530 [attributedString drawInRect:FlipIfRTL(renderRect, cellFrame)]; | |
| 512 return NSWidth(renderRect); | 531 return NSWidth(renderRect); | 
| 513 } | 532 } | 
| 514 | 533 | 
| 515 - (CGFloat)getMatchContentsWidth { | |
| 516 NSAttributedString* contents = [self attributedTitle]; | |
| 517 return contents ? [contents size].width : 0; | |
| 518 } | |
| 519 | |
| 520 | |
| 521 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match { | 534 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match { | 
| 522 const base::string16& inputText = base::UTF8ToUTF16( | 535 const base::string16& inputText = base::UTF8ToUTF16( | 
| 523 match.GetAdditionalInfo(kACMatchPropertyInputText)); | 536 match.GetAdditionalInfo(kACMatchPropertyInputText)); | 
| 524 int contentsStartIndex = 0; | 537 int contentsStartIndex = 0; | 
| 525 base::StringToInt( | 538 base::StringToInt( | 
| 526 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex), | 539 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex), | 
| 527 &contentsStartIndex); | 540 &contentsStartIndex); | 
| 528 // Ignore invalid state. | 541 // Ignore invalid state. | 
| 529 if (!StartsWith(match.fill_into_edit, inputText, true) | 542 if (!StartsWith(match.fill_into_edit, inputText, true) | 
| 530 || !EndsWith(match.fill_into_edit, match.contents, true) | 543 || !EndsWith(match.fill_into_edit, match.contents, true) | 
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 577 glyphWidth = std::min(glyphWidth, offset - glyphOffset); | 590 glyphWidth = std::min(glyphWidth, offset - glyphOffset); | 
| 578 } | 591 } | 
| 579 glyphOffset -= minOffset; | 592 glyphOffset -= minOffset; | 
| 580 if (glyphWidth == 0) | 593 if (glyphWidth == 0) | 
| 581 glyphWidth = inputWidth - glyphOffset; | 594 glyphWidth = inputWidth - glyphOffset; | 
| 582 if (isContentsRTL) | 595 if (isContentsRTL) | 
| 583 glyphOffset += glyphWidth; | 596 glyphOffset += glyphWidth; | 
| 584 return base::i18n::IsRTL() ? (inputWidth - glyphOffset) : glyphOffset; | 597 return base::i18n::IsRTL() ? (inputWidth - glyphOffset) : glyphOffset; | 
| 585 } | 598 } | 
| 586 | 599 | 
| 600 + (NSAttributedString*)createSeparatorString { | |
| 601 base::string16 raw_separator = | |
| 602 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); | |
| 603 return CreateAttributedString(raw_separator, DimTextColor()); | |
| 604 } | |
| 605 | |
| 587 @end | 606 @end | 
| OLD | NEW |