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 <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <cmath> | 10 #include <cmath> |
11 | 11 |
12 #include "base/feature_list.h" | |
12 #include "base/i18n/rtl.h" | 13 #include "base/i18n/rtl.h" |
13 #include "base/mac/foundation_util.h" | 14 #include "base/mac/foundation_util.h" |
14 #include "base/mac/objc_property_releaser.h" | 15 #include "base/mac/objc_property_releaser.h" |
15 #include "base/mac/scoped_nsobject.h" | 16 #include "base/mac/scoped_nsobject.h" |
16 #include "base/metrics/field_trial_params.h" | 17 #include "base/metrics/field_trial_params.h" |
17 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
18 #include "base/strings/string_util.h" | 19 #include "base/strings/string_util.h" |
19 #include "base/strings/sys_string_conversions.h" | 20 #include "base/strings/sys_string_conversions.h" |
20 #include "base/strings/utf_string_conversions.h" | 21 #include "base/strings/utf_string_conversions.h" |
21 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h" | 22 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h" |
(...skipping 11 matching lines...) Expand all Loading... | |
33 | 34 |
34 namespace { | 35 namespace { |
35 | 36 |
36 // Extra padding beyond the vertical text padding. | 37 // Extra padding beyond the vertical text padding. |
37 constexpr CGFloat kMaterialExtraVerticalImagePadding = 2.0; | 38 constexpr CGFloat kMaterialExtraVerticalImagePadding = 2.0; |
38 | 39 |
39 constexpr CGFloat kMaterialTextStartOffset = 27.0; | 40 constexpr CGFloat kMaterialTextStartOffset = 27.0; |
40 | 41 |
41 constexpr CGFloat kMaterialImageXOffset = 6.0; | 42 constexpr CGFloat kMaterialImageXOffset = 6.0; |
42 | 43 |
44 constexpr CGFloat kDefaultVerticalMargin = 3.0; | |
45 | |
46 constexpr CGFloat kDefaultTextHeight = 19; | |
47 | |
43 // Returns the margin that should appear at the top and bottom of the result. | 48 // Returns the margin that should appear at the top and bottom of the result. |
44 CGFloat GetVerticalMargin() { | 49 CGFloat GetVerticalMargin() { |
45 constexpr CGFloat kDefaultVerticalMargin = 3.0; | |
46 | |
47 return base::GetFieldTrialParamByFeatureAsInt( | 50 return base::GetFieldTrialParamByFeatureAsInt( |
48 omnibox::kUIExperimentVerticalMargin, | 51 omnibox::kUIExperimentVerticalMargin, |
49 OmniboxFieldTrial::kUIVerticalMarginParam, kDefaultVerticalMargin); | 52 OmniboxFieldTrial::kUIVerticalMarginParam, kDefaultVerticalMargin); |
50 } | 53 } |
51 | 54 |
52 // Flips the given |rect| in context of the given |frame|. | 55 // Flips the given |rect| in context of the given |frame|. |
53 NSRect FlipIfRTL(NSRect rect, NSRect frame) { | 56 NSRect FlipIfRTL(NSRect rect, NSRect frame) { |
54 DCHECK_LE(NSMinX(frame), NSMinX(rect)); | 57 DCHECK_LE(NSMinX(frame), NSMinX(rect)); |
55 DCHECK_GE(NSMaxX(frame), NSMaxX(rect)); | 58 DCHECK_GE(NSMaxX(frame), NSMaxX(rect)); |
56 if (base::i18n::IsRTL()) { | 59 if (base::i18n::IsRTL()) { |
(...skipping 360 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
417 [CreateAnswerLine(match.answer->second_line(), isDarkTheme) retain]; | 420 [CreateAnswerLine(match.answer->second_line(), isDarkTheme) retain]; |
418 maxLines_ = match.answer->second_line().num_text_lines(); | 421 maxLines_ = match.answer->second_line().num_text_lines(); |
419 } else { | 422 } else { |
420 contents_ = [CreateClassifiedAttributedString( | 423 contents_ = [CreateClassifiedAttributedString( |
421 match.contents, ContentTextColor(isDarkTheme), match.contents_class, | 424 match.contents, ContentTextColor(isDarkTheme), match.contents_class, |
422 isDarkTheme) retain]; | 425 isDarkTheme) retain]; |
423 if (!match.description.empty()) { | 426 if (!match.description.empty()) { |
424 description_ = [CreateClassifiedAttributedString( | 427 description_ = [CreateClassifiedAttributedString( |
425 match.description, DimTextColor(isDarkTheme), | 428 match.description, DimTextColor(isDarkTheme), |
426 match.description_class, isDarkTheme) retain]; | 429 match.description_class, isDarkTheme) retain]; |
430 | |
431 if (base::FeatureList::IsEnabled( | |
432 omnibox::kUIExperimentVerticalLayout) && | |
433 !AutocompleteMatch::IsSearchType(match.type)) { | |
434 // Somtimes swap the contents and description in vertical layouts. | |
435 // TODO(tommycli): If vertical layouts become permanent, swap these | |
436 // in the underlying match instead of the UI code. | |
437 NSAttributedString* temp = contents_; | |
438 contents_ = description_; | |
439 description_ = temp; | |
440 } | |
427 } | 441 } |
428 maxLines_ = 1; | |
tommycli
2017/05/26 22:28:14
Unused for non-answers. Probably good to followup
| |
429 } | 442 } |
430 propertyReleaser_OmniboxPopupCellData_.Init(self, | 443 propertyReleaser_OmniboxPopupCellData_.Init(self, |
431 [OmniboxPopupCellData class]); | 444 [OmniboxPopupCellData class]); |
432 } | 445 } |
433 return self; | 446 return self; |
434 } | 447 } |
435 | 448 |
436 - (instancetype)copyWithZone:(NSZone*)zone { | 449 - (instancetype)copyWithZone:(NSZone*)zone { |
437 return [self retain]; | 450 return [self retain]; |
438 } | 451 } |
(...skipping 17 matching lines...) Expand all Loading... | |
456 } else { | 469 } else { |
457 [HoveredBackgroundColor(isDarkTheme) set]; | 470 [HoveredBackgroundColor(isDarkTheme) set]; |
458 } | 471 } |
459 NSRectFillUsingOperation(cellFrame, NSCompositeSourceOver); | 472 NSRectFillUsingOperation(cellFrame, NSCompositeSourceOver); |
460 } | 473 } |
461 | 474 |
462 [self drawMatchWithFrame:cellFrame inView:controlView]; | 475 [self drawMatchWithFrame:cellFrame inView:controlView]; |
463 } | 476 } |
464 | 477 |
465 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 478 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
479 bool is_vertical_layout = | |
480 base::FeatureList::IsEnabled(omnibox::kUIExperimentVerticalLayout); | |
481 | |
466 OmniboxPopupCellData* cellData = | 482 OmniboxPopupCellData* cellData = |
467 base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]); | 483 base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]); |
468 OmniboxPopupMatrix* tableView = | 484 OmniboxPopupMatrix* tableView = |
469 base::mac::ObjCCastStrict<OmniboxPopupMatrix>(controlView); | 485 base::mac::ObjCCastStrict<OmniboxPopupMatrix>(controlView); |
470 CGFloat remainingWidth = [OmniboxPopupCell getContentAreaWidth:cellFrame] - | 486 CGFloat remainingWidth = [OmniboxPopupCell getContentAreaWidth:cellFrame] - |
471 [tableView contentLeftPadding]; | 487 [tableView contentLeftPadding]; |
472 CGFloat contentsWidth = [cellData getMatchContentsWidth]; | 488 CGFloat contentsWidth = [cellData getMatchContentsWidth]; |
473 CGFloat separatorWidth = [[tableView separator] size].width; | 489 CGFloat separatorWidth = [[tableView separator] size].width; |
474 CGFloat descriptionWidth = | 490 CGFloat descriptionWidth = |
475 [cellData description] ? [[cellData description] size].width : 0; | 491 [cellData description] ? [[cellData description] size].width : 0; |
476 int contentsMaxWidth, descriptionMaxWidth; | 492 int contentsMaxWidth, descriptionMaxWidth; |
477 OmniboxPopupModel::ComputeMatchMaxWidths( | 493 OmniboxPopupModel::ComputeMatchMaxWidths( |
478 ceilf(contentsWidth), ceilf(separatorWidth), ceilf(descriptionWidth), | 494 ceilf(contentsWidth), ceilf(separatorWidth), ceilf(descriptionWidth), |
479 ceilf(remainingWidth), | 495 ceilf(remainingWidth), [cellData isAnswer] || is_vertical_layout, |
480 [cellData isAnswer], | 496 !AutocompleteMatch::IsSearchType([cellData matchType]), &contentsMaxWidth, |
481 !AutocompleteMatch::IsSearchType([cellData matchType]), | |
482 &contentsMaxWidth, | |
483 &descriptionMaxWidth); | 497 &descriptionMaxWidth); |
484 | 498 |
499 CGFloat half_line_height = (kDefaultTextHeight + kDefaultVerticalMargin) / 2; | |
500 | |
485 NSWindow* parentWindow = [[controlView window] parentWindow]; | 501 NSWindow* parentWindow = [[controlView window] parentWindow]; |
486 BOOL isDarkTheme = [parentWindow hasDarkTheme]; | 502 BOOL isDarkTheme = [parentWindow hasDarkTheme]; |
487 NSRect imageRect = cellFrame; | 503 NSRect imageRect = cellFrame; |
488 NSImage* theImage = | 504 NSImage* theImage = |
489 isDarkTheme ? [cellData incognitoImage] : [cellData image]; | 505 isDarkTheme ? [cellData incognitoImage] : [cellData image]; |
490 imageRect.size = [theImage size]; | 506 imageRect.size = [theImage size]; |
491 imageRect.origin.x += kMaterialImageXOffset + [tableView contentLeftPadding]; | 507 imageRect.origin.x += kMaterialImageXOffset + [tableView contentLeftPadding]; |
492 imageRect.origin.y += | 508 imageRect.origin.y += |
493 GetVerticalMargin() + kMaterialExtraVerticalImagePadding; | 509 GetVerticalMargin() + kMaterialExtraVerticalImagePadding; |
510 if (is_vertical_layout) | |
511 imageRect.origin.y += half_line_height; | |
494 [theImage drawInRect:FlipIfRTL(imageRect, cellFrame) | 512 [theImage drawInRect:FlipIfRTL(imageRect, cellFrame) |
495 fromRect:NSZeroRect | 513 fromRect:NSZeroRect |
496 operation:NSCompositeSourceOver | 514 operation:NSCompositeSourceOver |
497 fraction:1.0 | 515 fraction:1.0 |
498 respectFlipped:YES | 516 respectFlipped:YES |
499 hints:nil]; | 517 hints:nil]; |
500 | 518 |
501 NSPoint origin = | 519 CGFloat left = kMaterialTextStartOffset + [tableView contentLeftPadding]; |
502 NSMakePoint(kMaterialTextStartOffset + [tableView contentLeftPadding], | 520 NSPoint origin = NSMakePoint(left, GetVerticalMargin()); |
503 GetVerticalMargin()); | 521 |
522 // For matches lacking description in vertical layout, center vertically. | |
523 if (is_vertical_layout && descriptionMaxWidth == 0) | |
524 origin.y += half_line_height; | |
525 | |
504 if ([cellData matchType] == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { | 526 if ([cellData matchType] == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { |
505 // Tail suggestions are rendered with a prefix (usually ellipsis), which | 527 // Tail suggestions are rendered with a prefix (usually ellipsis), which |
506 // appear vertically stacked. | 528 // appear vertically stacked. |
507 origin.x += [self drawMatchPrefixWithFrame:cellFrame | 529 origin.x += [self drawMatchPrefixWithFrame:cellFrame |
508 tableView:tableView | 530 tableView:tableView |
509 withContentsMaxWidth:&contentsMaxWidth | 531 withContentsMaxWidth:&contentsMaxWidth |
510 forDarkTheme:isDarkTheme]; | 532 forDarkTheme:isDarkTheme]; |
511 } | 533 } |
512 origin.x += [self drawMatchPart:[cellData contents] | 534 origin.x += [self drawMatchPart:[cellData contents] |
513 withFrame:cellFrame | 535 withFrame:cellFrame |
514 origin:origin | 536 origin:origin |
515 withMaxWidth:contentsMaxWidth | 537 withMaxWidth:contentsMaxWidth |
516 forDarkTheme:isDarkTheme]; | 538 forDarkTheme:isDarkTheme]; |
517 | 539 |
518 if (descriptionMaxWidth > 0) { | 540 if (descriptionMaxWidth > 0) { |
519 if ([cellData isAnswer]) { | 541 if ([cellData isAnswer]) { |
520 origin = NSMakePoint( | 542 origin = NSMakePoint(left, [OmniboxPopupCell getContentTextHeight:false] - |
521 kMaterialTextStartOffset + [tableView contentLeftPadding], | 543 GetVerticalMargin()); |
522 [OmniboxPopupCell getContentTextHeight] - GetVerticalMargin()); | |
523 CGFloat imageSize = [tableView answerLineHeight]; | 544 CGFloat imageSize = [tableView answerLineHeight]; |
524 NSRect imageRect = | 545 NSRect imageRect = |
525 NSMakeRect(NSMinX(cellFrame) + origin.x, NSMinY(cellFrame) + origin.y, | 546 NSMakeRect(NSMinX(cellFrame) + origin.x, NSMinY(cellFrame) + origin.y, |
526 imageSize, imageSize); | 547 imageSize, imageSize); |
527 [[cellData answerImage] drawInRect:FlipIfRTL(imageRect, cellFrame) | 548 [[cellData answerImage] drawInRect:FlipIfRTL(imageRect, cellFrame) |
528 fromRect:NSZeroRect | 549 fromRect:NSZeroRect |
529 operation:NSCompositeSourceOver | 550 operation:NSCompositeSourceOver |
530 fraction:1.0 | 551 fraction:1.0 |
531 respectFlipped:YES | 552 respectFlipped:YES |
532 hints:nil]; | 553 hints:nil]; |
533 if ([cellData answerImage]) { | 554 if ([cellData answerImage]) { |
534 origin.x += imageSize + kMaterialImageXOffset; | 555 origin.x += imageSize + kMaterialImageXOffset; |
535 | 556 |
536 // Have to nudge the baseline down 1pt in Material Design for the text | 557 // Have to nudge the baseline down 1pt in Material Design for the text |
537 // that follows, so that it's the same as the bottom of the image. | 558 // that follows, so that it's the same as the bottom of the image. |
538 origin.y += 1; | 559 origin.y += 1; |
539 } | 560 } |
540 } else { | 561 } else { |
541 origin.x += [self drawMatchPart:[tableView separator] | 562 if (is_vertical_layout) { |
542 withFrame:cellFrame | 563 origin.x = left; |
543 origin:origin | 564 origin.y += half_line_height * 2; |
544 withMaxWidth:separatorWidth | 565 } else { |
545 forDarkTheme:isDarkTheme]; | 566 origin.x += [self drawMatchPart:[tableView separator] |
567 withFrame:cellFrame | |
568 origin:origin | |
569 withMaxWidth:separatorWidth | |
570 forDarkTheme:isDarkTheme]; | |
571 } | |
546 } | 572 } |
547 origin.x += [self drawMatchPart:[cellData description] | 573 [self drawMatchPart:[cellData description] |
548 withFrame:cellFrame | 574 withFrame:cellFrame |
549 origin:origin | 575 origin:origin |
550 withMaxWidth:descriptionMaxWidth | 576 withMaxWidth:descriptionMaxWidth |
551 forDarkTheme:isDarkTheme]; | 577 forDarkTheme:isDarkTheme]; |
552 } | 578 } |
553 } | 579 } |
554 | 580 |
555 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame | 581 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame |
556 tableView:(OmniboxPopupMatrix*)tableView | 582 tableView:(OmniboxPopupMatrix*)tableView |
557 withContentsMaxWidth:(int*)contentsMaxWidth | 583 withContentsMaxWidth:(int*)contentsMaxWidth |
558 forDarkTheme:(BOOL)isDarkTheme { | 584 forDarkTheme:(BOOL)isDarkTheme { |
559 OmniboxPopupCellData* cellData = | 585 OmniboxPopupCellData* cellData = |
560 base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]); | 586 base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]); |
561 CGFloat offset = 0.0f; | 587 CGFloat offset = 0.0f; |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
688 + (NSAttributedString*)createSeparatorStringForDarkTheme:(BOOL)isDarkTheme { | 714 + (NSAttributedString*)createSeparatorStringForDarkTheme:(BOOL)isDarkTheme { |
689 base::string16 raw_separator = | 715 base::string16 raw_separator = |
690 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); | 716 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); |
691 return CreateAttributedString(raw_separator, DimTextColor(isDarkTheme)); | 717 return CreateAttributedString(raw_separator, DimTextColor(isDarkTheme)); |
692 } | 718 } |
693 | 719 |
694 + (CGFloat)getContentAreaWidth:(NSRect)cellFrame { | 720 + (CGFloat)getContentAreaWidth:(NSRect)cellFrame { |
695 return NSWidth(cellFrame) - kMaterialTextStartOffset; | 721 return NSWidth(cellFrame) - kMaterialTextStartOffset; |
696 } | 722 } |
697 | 723 |
698 + (CGFloat)getContentTextHeight { | 724 + (CGFloat)getContentTextHeight:(BOOL)isTwoLine { |
699 constexpr CGFloat kDefaultTextHeight = 19; | 725 CGFloat height = kDefaultTextHeight + 2 * GetVerticalMargin(); |
700 return kDefaultTextHeight + 2 * GetVerticalMargin(); | 726 if (isTwoLine) |
727 height += kDefaultTextHeight + kDefaultVerticalMargin; | |
728 return height; | |
701 } | 729 } |
702 | 730 |
703 @end | 731 @end |
OLD | NEW |