| 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/i18n/rtl.h" | 12 #include "base/i18n/rtl.h" |
| 13 #include "base/mac/foundation_util.h" | 13 #include "base/mac/foundation_util.h" |
| 14 #include "base/mac/scoped_nsobject.h" | 14 #include "base/mac/scoped_nsobject.h" |
| 15 #include "base/strings/string_number_conversions.h" | 15 #include "base/strings/string_number_conversions.h" |
| 16 #include "base/strings/string_util.h" | 16 #include "base/strings/string_util.h" |
| 17 #include "base/strings/sys_string_conversions.h" | 17 #include "base/strings/sys_string_conversions.h" |
| 18 #include "base/strings/utf_string_conversions.h" | 18 #include "base/strings/utf_string_conversions.h" |
| 19 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h" | 19 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h" |
| 20 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h" | 20 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h" |
| 21 #import "chrome/browser/ui/cocoa/themed_window.h" |
| 21 #include "chrome/grit/generated_resources.h" | 22 #include "chrome/grit/generated_resources.h" |
| 22 #include "components/omnibox/browser/omnibox_popup_model.h" | 23 #include "components/omnibox/browser/omnibox_popup_model.h" |
| 23 #include "components/omnibox/browser/suggestion_answer.h" | 24 #include "components/omnibox/browser/suggestion_answer.h" |
| 24 #include "skia/ext/skia_utils_mac.h" | 25 #include "skia/ext/skia_utils_mac.h" |
| 25 #include "ui/base/l10n/l10n_util.h" | 26 #include "ui/base/l10n/l10n_util.h" |
| 27 #include "ui/base/material_design/material_design_controller.h" |
| 28 #include "ui/gfx/color_palette.h" |
| 26 #include "ui/gfx/font.h" | 29 #include "ui/gfx/font.h" |
| 27 | 30 |
| 28 namespace { | 31 namespace { |
| 29 | 32 |
| 30 // How far to offset image column from the left. | 33 // How far to offset image column from the left. |
| 31 const CGFloat kImageXOffset = 5.0; | 34 const CGFloat kImageXOffset = 5.0; |
| 32 | 35 |
| 33 // How far to offset image and text. | 36 // How far to offset text. |
| 34 const CGFloat kPaddingOffset = 3.0; | 37 const CGFloat kVerticalTextPadding = 3.0; |
| 35 | 38 |
| 36 // How far to offset the text column from the left. | 39 const CGFloat kVerticalImagePadding = 3.0; |
| 40 const CGFloat kMaterialVerticalImagePadding = 5.0; |
| 41 |
| 37 const CGFloat kTextStartOffset = 28.0; | 42 const CGFloat kTextStartOffset = 28.0; |
| 43 const CGFloat kMaterialTextStartOffset = 25.0; |
| 38 | 44 |
| 39 // Rounding radius of selection and hover background on popup items. | 45 // Rounding radius of selection and hover background on popup items. |
| 40 const CGFloat kCellRoundingRadius = 2.0; | 46 const CGFloat kCellRoundingRadius = 2.0; |
| 41 | 47 |
| 48 // How far to offset the image. |
| 49 CGFloat VerticalImagePadding() { |
| 50 if (!ui::MaterialDesignController::IsModeMaterial()) { |
| 51 return kVerticalImagePadding; |
| 52 } |
| 53 return kMaterialVerticalImagePadding; |
| 54 } |
| 55 |
| 56 // How far to offset the text column from the left. |
| 57 CGFloat TextStartOffset() { |
| 58 if (!ui::MaterialDesignController::IsModeMaterial()) { |
| 59 return kTextStartOffset; |
| 60 } |
| 61 return kMaterialTextStartOffset; |
| 62 } |
| 63 |
| 42 // Flips the given |rect| in context of the given |frame|. | 64 // Flips the given |rect| in context of the given |frame|. |
| 43 NSRect FlipIfRTL(NSRect rect, NSRect frame) { | 65 NSRect FlipIfRTL(NSRect rect, NSRect frame) { |
| 44 DCHECK_LE(NSMinX(frame), NSMinX(rect)); | 66 DCHECK_LE(NSMinX(frame), NSMinX(rect)); |
| 45 DCHECK_GE(NSMaxX(frame), NSMaxX(rect)); | 67 DCHECK_GE(NSMaxX(frame), NSMaxX(rect)); |
| 46 if (base::i18n::IsRTL()) { | 68 if (base::i18n::IsRTL()) { |
| 47 NSRect result = rect; | 69 NSRect result = rect; |
| 48 result.origin.x = NSMinX(frame) + (NSMaxX(frame) - NSMaxX(rect)); | 70 result.origin.x = NSMinX(frame) + (NSMaxX(frame) - NSMaxX(rect)); |
| 49 return result; | 71 return result; |
| 50 } | 72 } |
| 51 return rect; | 73 return rect; |
| 52 } | 74 } |
| 53 | 75 |
| 54 NSColor* SelectedBackgroundColor() { | 76 NSColor* SelectedBackgroundColor(BOOL is_dark_theme) { |
| 77 if (is_dark_theme && ui::MaterialDesignController::IsModeMaterial()) { |
| 78 return skia::SkColorToSRGBNSColor(SkColorSetA(SK_ColorWHITE, 0x14)); |
| 79 } |
| 55 return [NSColor selectedControlColor]; | 80 return [NSColor selectedControlColor]; |
| 56 } | 81 } |
| 57 NSColor* HoveredBackgroundColor() { | 82 |
| 83 NSColor* HoveredBackgroundColor(BOOL is_dark_theme) { |
| 84 if (is_dark_theme) { |
| 85 return skia::SkColorToSRGBNSColor(SkColorSetA(SK_ColorWHITE, 0x0D)); |
| 86 } |
| 58 return [NSColor controlHighlightColor]; | 87 return [NSColor controlHighlightColor]; |
| 59 } | 88 } |
| 60 | 89 |
| 61 NSColor* ContentTextColor() { | 90 NSColor* ContentTextColor(BOOL is_dark_theme) { |
| 91 if (ui::MaterialDesignController::IsModeMaterial() && is_dark_theme) { |
| 92 return [NSColor whiteColor]; |
| 93 } |
| 62 return [NSColor blackColor]; | 94 return [NSColor blackColor]; |
| 63 } | 95 } |
| 64 NSColor* DimTextColor() { | 96 NSColor* DimTextColor(BOOL is_dark_theme) { |
| 65 return [NSColor darkGrayColor]; | 97 if (!ui::MaterialDesignController::IsModeMaterial()) { |
| 98 return [NSColor darkGrayColor]; |
| 99 } |
| 100 if (is_dark_theme) { |
| 101 return skia::SkColorToSRGBNSColor(SkColorSetA(SK_ColorWHITE, 0x7F)); |
| 102 } |
| 103 return skia::SkColorToSRGBNSColor(SkColorSetRGB(0x64, 0x64, 0x64)); |
| 66 } | 104 } |
| 67 NSColor* PositiveTextColor() { | 105 NSColor* PositiveTextColor() { |
| 68 return skia::SkColorToCalibratedNSColor(SkColorSetRGB(0x3d, 0x94, 0x00)); | 106 return skia::SkColorToCalibratedNSColor(SkColorSetRGB(0x3d, 0x94, 0x00)); |
| 69 } | 107 } |
| 70 NSColor* NegativeTextColor() { | 108 NSColor* NegativeTextColor() { |
| 71 return skia::SkColorToCalibratedNSColor(SkColorSetRGB(0xdd, 0x4b, 0x39)); | 109 return skia::SkColorToCalibratedNSColor(SkColorSetRGB(0xdd, 0x4b, 0x39)); |
| 72 } | 110 } |
| 73 NSColor* URLTextColor() { | 111 NSColor* URLTextColor(BOOL is_dark_theme) { |
| 112 if (ui::MaterialDesignController::IsModeMaterial() && is_dark_theme) { |
| 113 return skia::SkColorToSRGBNSColor(gfx::kGoogleBlue300); |
| 114 } |
| 74 return [NSColor colorWithCalibratedRed:0.0 green:0.55 blue:0.0 alpha:1.0]; | 115 return [NSColor colorWithCalibratedRed:0.0 green:0.55 blue:0.0 alpha:1.0]; |
| 75 } | 116 } |
| 76 | 117 |
| 77 NSFont* FieldFont() { | 118 NSFont* FieldFont() { |
| 78 return OmniboxViewMac::GetFieldFont(gfx::Font::NORMAL); | 119 return OmniboxViewMac::GetFieldFont(gfx::Font::NORMAL); |
| 79 } | 120 } |
| 80 NSFont* BoldFieldFont() { | 121 NSFont* BoldFieldFont() { |
| 81 return OmniboxViewMac::GetFieldFont(gfx::Font::BOLD); | 122 return OmniboxViewMac::GetFieldFont(gfx::Font::BOLD); |
| 82 } | 123 } |
| 83 NSFont* LargeFont() { | 124 NSFont* LargeFont() { |
| 84 return OmniboxViewMac::GetLargeFont(gfx::Font::NORMAL); | 125 return OmniboxViewMac::GetLargeFont(gfx::Font::NORMAL); |
| 85 } | 126 } |
| 86 NSFont* LargeSuperscriptFont() { | 127 NSFont* LargeSuperscriptFont() { |
| 87 NSFont* font = OmniboxViewMac::GetLargeFont(gfx::Font::NORMAL); | 128 NSFont* font = OmniboxViewMac::GetLargeFont(gfx::Font::NORMAL); |
| 88 // Calculate a slightly smaller font. The ratio here is somewhat arbitrary. | 129 // Calculate a slightly smaller font. The ratio here is somewhat arbitrary. |
| 89 // Proportions from 5/9 to 5/7 all look pretty good. | 130 // Proportions from 5/9 to 5/7 all look pretty good. |
| 90 CGFloat size = [font pointSize] * 5.0 / 9.0; | 131 CGFloat size = [font pointSize] * 5.0 / 9.0; |
| 91 NSFontDescriptor* descriptor = [font fontDescriptor]; | 132 NSFontDescriptor* descriptor = [font fontDescriptor]; |
| 92 return [NSFont fontWithDescriptor:descriptor size:size]; | 133 return [NSFont fontWithDescriptor:descriptor size:size]; |
| 93 } | 134 } |
| 94 NSFont* SmallFont() { | 135 NSFont* SmallFont() { |
| 95 return OmniboxViewMac::GetSmallFont(gfx::Font::NORMAL); | 136 return OmniboxViewMac::GetSmallFont(gfx::Font::NORMAL); |
| 96 } | 137 } |
| 97 | 138 |
| 98 CGFloat GetContentAreaWidth(NSRect cellFrame) { | 139 CGFloat GetContentAreaWidth(NSRect cellFrame) { |
| 99 return NSWidth(cellFrame) - kTextStartOffset; | 140 return NSWidth(cellFrame) - TextStartOffset(); |
| 100 } | 141 } |
| 101 | 142 |
| 102 NSAttributedString* CreateAnswerStringHelper(const base::string16& text, | 143 NSAttributedString* CreateAnswerStringHelper(const base::string16& text, |
| 103 NSInteger style_type, | 144 NSInteger style_type, |
| 104 bool is_bold) { | 145 bool is_bold, |
| 146 BOOL is_dark_theme) { |
| 105 NSDictionary* answer_style = nil; | 147 NSDictionary* answer_style = nil; |
| 148 NSFont* answer_font = nil; |
| 149 bool is_mode_material = ui::MaterialDesignController::IsModeMaterial(); |
| 106 switch (style_type) { | 150 switch (style_type) { |
| 107 case SuggestionAnswer::TOP_ALIGNED: | 151 case SuggestionAnswer::TOP_ALIGNED: |
| 108 answer_style = @{ | 152 answer_style = @{ |
| 109 NSForegroundColorAttributeName : DimTextColor(), | 153 NSForegroundColorAttributeName : DimTextColor(is_dark_theme), |
| 110 NSFontAttributeName : LargeSuperscriptFont(), | 154 NSFontAttributeName : LargeSuperscriptFont(), |
| 111 NSSuperscriptAttributeName : @1 | 155 NSSuperscriptAttributeName : @1 |
| 112 }; | 156 }; |
| 113 break; | 157 break; |
| 114 case SuggestionAnswer::DESCRIPTION_NEGATIVE: | 158 case SuggestionAnswer::DESCRIPTION_NEGATIVE: |
| 115 answer_style = @{ | 159 answer_style = @{ |
| 116 NSForegroundColorAttributeName : NegativeTextColor(), | 160 NSForegroundColorAttributeName : NegativeTextColor(), |
| 117 NSFontAttributeName : LargeSuperscriptFont() | 161 NSFontAttributeName : LargeSuperscriptFont() |
| 118 }; | 162 }; |
| 119 break; | 163 break; |
| 120 case SuggestionAnswer::DESCRIPTION_POSITIVE: | 164 case SuggestionAnswer::DESCRIPTION_POSITIVE: |
| 121 answer_style = @{ | 165 answer_style = @{ |
| 122 NSForegroundColorAttributeName : PositiveTextColor(), | 166 NSForegroundColorAttributeName : PositiveTextColor(), |
| 123 NSFontAttributeName : LargeSuperscriptFont() | 167 NSFontAttributeName : LargeSuperscriptFont() |
| 124 }; | 168 }; |
| 125 break; | 169 break; |
| 126 case SuggestionAnswer::PERSONALIZED_SUGGESTION: | 170 case SuggestionAnswer::PERSONALIZED_SUGGESTION: |
| 127 answer_style = @{ | 171 answer_style = @{ |
| 128 NSForegroundColorAttributeName : ContentTextColor(), | 172 NSForegroundColorAttributeName : ContentTextColor(is_dark_theme), |
| 129 NSFontAttributeName : FieldFont() | 173 NSFontAttributeName : FieldFont() |
| 130 }; | 174 }; |
| 131 break; | 175 break; |
| 132 case SuggestionAnswer::ANSWER_TEXT_MEDIUM: | 176 case SuggestionAnswer::ANSWER_TEXT_MEDIUM: |
| 133 answer_style = @{ | 177 answer_style = @{ |
| 134 NSForegroundColorAttributeName : ContentTextColor(), | 178 NSForegroundColorAttributeName : ContentTextColor(is_dark_theme), |
| 135 NSFontAttributeName : FieldFont() | 179 NSFontAttributeName : FieldFont() |
| 136 }; | 180 }; |
| 137 break; | 181 break; |
| 138 case SuggestionAnswer::ANSWER_TEXT_LARGE: | 182 case SuggestionAnswer::ANSWER_TEXT_LARGE: |
| 139 answer_style = @{ | 183 answer_style = @{ |
| 140 NSForegroundColorAttributeName : ContentTextColor(), | 184 NSForegroundColorAttributeName : ContentTextColor(is_dark_theme), |
| 141 NSFontAttributeName : LargeFont() | 185 NSFontAttributeName : LargeFont() |
| 142 }; | 186 }; |
| 143 break; | 187 break; |
| 144 case SuggestionAnswer::SUGGESTION_SECONDARY_TEXT_SMALL: | 188 case SuggestionAnswer::SUGGESTION_SECONDARY_TEXT_SMALL: |
| 189 answer_font = is_mode_material ? FieldFont() |
| 190 : SmallFont(); |
| 145 answer_style = @{ | 191 answer_style = @{ |
| 146 NSForegroundColorAttributeName : DimTextColor(), | 192 NSForegroundColorAttributeName : DimTextColor(is_dark_theme), |
| 147 NSFontAttributeName : SmallFont() | 193 NSFontAttributeName : answer_font |
| 148 }; | 194 }; |
| 149 break; | 195 break; |
| 150 case SuggestionAnswer::SUGGESTION_SECONDARY_TEXT_MEDIUM: | 196 case SuggestionAnswer::SUGGESTION_SECONDARY_TEXT_MEDIUM: |
| 197 answer_font = is_mode_material ? LargeSuperscriptFont() |
| 198 : FieldFont(); |
| 151 answer_style = @{ | 199 answer_style = @{ |
| 152 NSForegroundColorAttributeName : DimTextColor(), | 200 NSForegroundColorAttributeName : DimTextColor(is_dark_theme), |
| 153 NSFontAttributeName : FieldFont() | 201 NSFontAttributeName : answer_font |
| 154 }; | 202 }; |
| 155 break; | 203 break; |
| 156 case SuggestionAnswer::SUGGESTION: // Fall through. | 204 case SuggestionAnswer::SUGGESTION: // Fall through. |
| 157 default: | 205 default: |
| 158 answer_style = @{ | 206 answer_style = @{ |
| 159 NSForegroundColorAttributeName : ContentTextColor (), | 207 NSForegroundColorAttributeName : ContentTextColor(is_dark_theme), |
| 160 NSFontAttributeName : FieldFont() | 208 NSFontAttributeName : FieldFont() |
| 161 }; | 209 }; |
| 162 break; | 210 break; |
| 163 } | 211 } |
| 164 | 212 |
| 165 if (is_bold) { | 213 if (is_bold) { |
| 166 NSMutableDictionary* bold_style = [answer_style mutableCopy]; | 214 NSMutableDictionary* bold_style = [answer_style mutableCopy]; |
| 167 // TODO(dschuyler): Account for bolding fonts other than FieldFont. | 215 // TODO(dschuyler): Account for bolding fonts other than FieldFont. |
| 168 // Field font is the only one currently necessary to bold. | 216 // Field font is the only one currently necessary to bold. |
| 169 [bold_style setObject:BoldFieldFont() forKey:NSFontAttributeName]; | 217 [bold_style setObject:BoldFieldFont() forKey:NSFontAttributeName]; |
| 170 answer_style = bold_style; | 218 answer_style = bold_style; |
| 171 } | 219 } |
| 172 | 220 |
| 173 return [[[NSAttributedString alloc] | 221 return [[[NSAttributedString alloc] |
| 174 initWithString:base::SysUTF16ToNSString(text) | 222 initWithString:base::SysUTF16ToNSString(text) |
| 175 attributes:answer_style] autorelease]; | 223 attributes:answer_style] autorelease]; |
| 176 } | 224 } |
| 177 | 225 |
| 178 NSAttributedString* CreateAnswerString(const base::string16& text, | 226 NSAttributedString* CreateAnswerString(const base::string16& text, |
| 179 NSInteger style_type) { | 227 NSInteger style_type, |
| 228 BOOL is_dark_theme) { |
| 180 // TODO(dschuyler): make this better. Right now this only supports unnested | 229 // TODO(dschuyler): make this better. Right now this only supports unnested |
| 181 // bold tags. In the future we'll need to flag unexpected tags while adding | 230 // bold tags. In the future we'll need to flag unexpected tags while adding |
| 182 // support for b, i, u, sub, and sup. We'll also need to support HTML | 231 // support for b, i, u, sub, and sup. We'll also need to support HTML |
| 183 // entities (< for '<', etc.). | 232 // entities (< for '<', etc.). |
| 184 const base::string16 begin_tag = base::ASCIIToUTF16("<b>"); | 233 const base::string16 begin_tag = base::ASCIIToUTF16("<b>"); |
| 185 const base::string16 end_tag = base::ASCIIToUTF16("</b>"); | 234 const base::string16 end_tag = base::ASCIIToUTF16("</b>"); |
| 186 size_t begin = 0; | 235 size_t begin = 0; |
| 187 base::scoped_nsobject<NSMutableAttributedString> result( | 236 base::scoped_nsobject<NSMutableAttributedString> result( |
| 188 [[NSMutableAttributedString alloc] init]); | 237 [[NSMutableAttributedString alloc] init]); |
| 189 while (true) { | 238 while (true) { |
| 190 size_t end = text.find(begin_tag, begin); | 239 size_t end = text.find(begin_tag, begin); |
| 191 if (end == base::string16::npos) { | 240 if (end == base::string16::npos) { |
| 192 [result | 241 [result |
| 193 appendAttributedString:CreateAnswerStringHelper( | 242 appendAttributedString:CreateAnswerStringHelper( |
| 194 text.substr(begin), | 243 text.substr(begin), |
| 195 style_type, false)]; | 244 style_type, |
| 245 false, |
| 246 is_dark_theme)]; |
| 196 break; | 247 break; |
| 197 } | 248 } |
| 198 [result appendAttributedString:CreateAnswerStringHelper( | 249 [result appendAttributedString:CreateAnswerStringHelper( |
| 199 text.substr(begin, end - begin), | 250 text.substr(begin, end - begin), |
| 200 style_type, false)]; | 251 style_type, |
| 252 false, |
| 253 is_dark_theme)]; |
| 201 begin = end + begin_tag.length(); | 254 begin = end + begin_tag.length(); |
| 202 end = text.find(end_tag, begin); | 255 end = text.find(end_tag, begin); |
| 203 if (end == base::string16::npos) | 256 if (end == base::string16::npos) |
| 204 break; | 257 break; |
| 205 [result appendAttributedString:CreateAnswerStringHelper( | 258 [result appendAttributedString:CreateAnswerStringHelper( |
| 206 text.substr(begin, end - begin), | 259 text.substr(begin, end - begin), |
| 207 style_type, true)]; | 260 style_type, |
| 261 true, |
| 262 is_dark_theme)]; |
| 208 begin = end + end_tag.length(); | 263 begin = end + end_tag.length(); |
| 209 } | 264 } |
| 210 return result.autorelease(); | 265 return result.autorelease(); |
| 211 } | 266 } |
| 212 | 267 |
| 213 NSAttributedString* CreateAnswerLine(const SuggestionAnswer::ImageLine& line) { | 268 NSAttributedString* CreateAnswerLine(const SuggestionAnswer::ImageLine& line, |
| 269 BOOL is_dark_theme) { |
| 214 base::scoped_nsobject<NSMutableAttributedString> answer_string( | 270 base::scoped_nsobject<NSMutableAttributedString> answer_string( |
| 215 [[NSMutableAttributedString alloc] init]); | 271 [[NSMutableAttributedString alloc] init]); |
| 216 DCHECK(!line.text_fields().empty()); | 272 DCHECK(!line.text_fields().empty()); |
| 217 for (const SuggestionAnswer::TextField& text_field : line.text_fields()) { | 273 for (const SuggestionAnswer::TextField& text_field : line.text_fields()) { |
| 218 [answer_string | 274 [answer_string |
| 219 appendAttributedString:CreateAnswerString(text_field.text(), | 275 appendAttributedString:CreateAnswerString(text_field.text(), |
| 220 text_field.type())]; | 276 text_field.type(), |
| 277 is_dark_theme)]; |
| 221 } | 278 } |
| 222 const base::string16 space(base::ASCIIToUTF16(" ")); | 279 const base::string16 space(base::ASCIIToUTF16(" ")); |
| 223 const SuggestionAnswer::TextField* text_field = line.additional_text(); | 280 const SuggestionAnswer::TextField* text_field = line.additional_text(); |
| 224 if (text_field) { | 281 if (text_field) { |
| 225 [answer_string | 282 [answer_string |
| 226 appendAttributedString:CreateAnswerString(space + text_field->text(), | 283 appendAttributedString:CreateAnswerString(space + text_field->text(), |
| 227 text_field->type())]; | 284 text_field->type(), |
| 285 is_dark_theme)]; |
| 228 } | 286 } |
| 229 text_field = line.status_text(); | 287 text_field = line.status_text(); |
| 230 if (text_field) { | 288 if (text_field) { |
| 231 [answer_string | 289 [answer_string |
| 232 appendAttributedString:CreateAnswerString(space + text_field->text(), | 290 appendAttributedString:CreateAnswerString(space + text_field->text(), |
| 233 text_field->type())]; | 291 text_field->type(), |
| 292 is_dark_theme)]; |
| 234 } | 293 } |
| 235 base::scoped_nsobject<NSMutableParagraphStyle> style( | 294 base::scoped_nsobject<NSMutableParagraphStyle> style( |
| 236 [[NSMutableParagraphStyle alloc] init]); | 295 [[NSMutableParagraphStyle alloc] init]); |
| 237 [style setLineBreakMode:NSLineBreakByTruncatingTail]; | 296 [style setLineBreakMode:NSLineBreakByTruncatingTail]; |
| 238 [style setTighteningFactorForTruncation:0.0]; | 297 [style setTighteningFactorForTruncation:0.0]; |
| 239 [answer_string addAttribute:NSParagraphStyleAttributeName | 298 [answer_string addAttribute:NSParagraphStyleAttributeName |
| 240 value:style | 299 value:style |
| 241 range:NSMakeRange(0, [answer_string length])]; | 300 range:NSMakeRange(0, [answer_string length])]; |
| 242 return answer_string.autorelease(); | 301 return answer_string.autorelease(); |
| 243 } | 302 } |
| (...skipping 26 matching lines...) Expand all Loading... |
| 270 | 329 |
| 271 NSMutableAttributedString* CreateAttributedString( | 330 NSMutableAttributedString* CreateAttributedString( |
| 272 const base::string16& text, | 331 const base::string16& text, |
| 273 NSColor* text_color) { | 332 NSColor* text_color) { |
| 274 return CreateAttributedString(text, text_color, NSNaturalTextAlignment); | 333 return CreateAttributedString(text, text_color, NSNaturalTextAlignment); |
| 275 } | 334 } |
| 276 | 335 |
| 277 NSAttributedString* CreateClassifiedAttributedString( | 336 NSAttributedString* CreateClassifiedAttributedString( |
| 278 const base::string16& text, | 337 const base::string16& text, |
| 279 NSColor* text_color, | 338 NSColor* text_color, |
| 280 const ACMatchClassifications& classifications) { | 339 const ACMatchClassifications& classifications, |
| 340 BOOL is_dark_theme) { |
| 281 NSMutableAttributedString* attributedString = | 341 NSMutableAttributedString* attributedString = |
| 282 CreateAttributedString(text, text_color); | 342 CreateAttributedString(text, text_color); |
| 283 NSUInteger match_length = [attributedString length]; | 343 NSUInteger match_length = [attributedString length]; |
| 284 | 344 |
| 285 // Mark up the runs which differ from the default. | 345 // Mark up the runs which differ from the default. |
| 286 for (ACMatchClassifications::const_iterator i = classifications.begin(); | 346 for (ACMatchClassifications::const_iterator i = classifications.begin(); |
| 287 i != classifications.end(); ++i) { | 347 i != classifications.end(); ++i) { |
| 288 const bool is_last = ((i + 1) == classifications.end()); | 348 const bool is_last = ((i + 1) == classifications.end()); |
| 289 const NSUInteger next_offset = | 349 const NSUInteger next_offset = |
| 290 (is_last ? match_length : static_cast<NSUInteger>((i + 1)->offset)); | 350 (is_last ? match_length : static_cast<NSUInteger>((i + 1)->offset)); |
| 291 const NSUInteger location = static_cast<NSUInteger>(i->offset); | 351 const NSUInteger location = static_cast<NSUInteger>(i->offset); |
| 292 const NSUInteger length = next_offset - static_cast<NSUInteger>(i->offset); | 352 const NSUInteger length = next_offset - static_cast<NSUInteger>(i->offset); |
| 293 // Guard against bad, off-the-end classification ranges. | 353 // Guard against bad, off-the-end classification ranges. |
| 294 if (location >= match_length || length <= 0) | 354 if (location >= match_length || length <= 0) |
| 295 break; | 355 break; |
| 296 const NSRange range = | 356 const NSRange range = |
| 297 NSMakeRange(location, std::min(length, match_length - location)); | 357 NSMakeRange(location, std::min(length, match_length - location)); |
| 298 | 358 |
| 299 if (0 != (i->style & ACMatchClassification::MATCH)) { | 359 if (0 != (i->style & ACMatchClassification::MATCH)) { |
| 300 [attributedString addAttribute:NSFontAttributeName | 360 [attributedString addAttribute:NSFontAttributeName |
| 301 value:BoldFieldFont() | 361 value:BoldFieldFont() |
| 302 range:range]; | 362 range:range]; |
| 303 } | 363 } |
| 304 | 364 |
| 305 if (0 != (i->style & ACMatchClassification::URL)) { | 365 if (0 != (i->style & ACMatchClassification::URL)) { |
| 306 [attributedString addAttribute:NSForegroundColorAttributeName | 366 [attributedString addAttribute:NSForegroundColorAttributeName |
| 307 value:URLTextColor() | 367 value:URLTextColor(is_dark_theme) |
| 308 range:range]; | 368 range:range]; |
| 309 } else if (0 != (i->style & ACMatchClassification::DIM)) { | 369 } else if (0 != (i->style & ACMatchClassification::DIM)) { |
| 310 [attributedString addAttribute:NSForegroundColorAttributeName | 370 [attributedString addAttribute:NSForegroundColorAttributeName |
| 311 value:DimTextColor() | 371 value:DimTextColor(is_dark_theme) |
| 312 range:range]; | 372 range:range]; |
| 313 } | 373 } |
| 314 } | 374 } |
| 315 | 375 |
| 316 return attributedString; | 376 return attributedString; |
| 317 } | 377 } |
| 318 | 378 |
| 319 } // namespace | 379 } // namespace |
| 320 | 380 |
| 321 @interface OmniboxPopupCell () | 381 @interface OmniboxPopupCell () |
| 322 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString | 382 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString |
| 323 withFrame:(NSRect)cellFrame | 383 withFrame:(NSRect)cellFrame |
| 324 origin:(NSPoint)origin | 384 origin:(NSPoint)origin |
| 325 withMaxWidth:(int)maxWidth; | 385 withMaxWidth:(int)maxWidth |
| 386 forDarkTheme:(BOOL)isDarkTheme; |
| 326 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame | 387 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame |
| 327 tableView:(OmniboxPopupMatrix*)tableView | 388 tableView:(OmniboxPopupMatrix*)tableView |
| 328 withContentsMaxWidth:(int*)contentsMaxWidth; | 389 withContentsMaxWidth:(int*)contentsMaxWidth |
| 390 forDarkTheme:(BOOL)isDarkTheme; |
| 329 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView; | 391 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView; |
| 330 @end | 392 @end |
| 331 | 393 |
| 332 @implementation OmniboxPopupCellData | 394 @implementation OmniboxPopupCellData |
| 333 | 395 |
| 334 @synthesize contents = contents_; | 396 @synthesize contents = contents_; |
| 335 @synthesize description = description_; | 397 @synthesize description = description_; |
| 336 @synthesize prefix = prefix_; | 398 @synthesize prefix = prefix_; |
| 337 @synthesize image = image_; | 399 @synthesize image = image_; |
| 400 @synthesize incognitoImage = incognitoImage_; |
| 338 @synthesize answerImage = answerImage_; | 401 @synthesize answerImage = answerImage_; |
| 339 @synthesize contentsOffset = contentsOffset_; | 402 @synthesize contentsOffset = contentsOffset_; |
| 340 @synthesize isContentsRTL = isContentsRTL_; | 403 @synthesize isContentsRTL = isContentsRTL_; |
| 341 @synthesize isAnswer = isAnswer_; | 404 @synthesize isAnswer = isAnswer_; |
| 342 @synthesize matchType = matchType_; | 405 @synthesize matchType = matchType_; |
| 343 | 406 |
| 344 - (instancetype)initWithMatch:(const AutocompleteMatch&)match | 407 - (instancetype)initWithMatch:(const AutocompleteMatch&)match |
| 345 contentsOffset:(CGFloat)contentsOffset | 408 contentsOffset:(CGFloat)contentsOffset |
| 346 image:(NSImage*)image | 409 image:(NSImage*)image |
| 347 answerImage:(NSImage*)answerImage { | 410 answerImage:(NSImage*)answerImage |
| 411 forDarkTheme:(BOOL)isDarkTheme { |
| 348 if ((self = [super init])) { | 412 if ((self = [super init])) { |
| 349 image_ = [image retain]; | 413 image_ = [image retain]; |
| 350 answerImage_ = [answerImage retain]; | 414 answerImage_ = [answerImage retain]; |
| 351 contentsOffset_ = contentsOffset; | 415 contentsOffset_ = contentsOffset; |
| 352 | 416 |
| 353 isContentsRTL_ = | 417 isContentsRTL_ = |
| 354 (base::i18n::RIGHT_TO_LEFT == | 418 (base::i18n::RIGHT_TO_LEFT == |
| 355 base::i18n::GetFirstStrongCharacterDirection(match.contents)); | 419 base::i18n::GetFirstStrongCharacterDirection(match.contents)); |
| 356 matchType_ = match.type; | 420 matchType_ = match.type; |
| 357 | 421 |
| 358 // Prefix may not have any characters with strong directionality, and may | 422 // Prefix may not have any characters with strong directionality, and may |
| 359 // take the UI directionality. But prefix needs to appear in continuation | 423 // take the UI directionality. But prefix needs to appear in continuation |
| 360 // of the contents so we force the directionality. | 424 // of the contents so we force the directionality. |
| 361 NSTextAlignment textAlignment = | 425 NSTextAlignment textAlignment = |
| 362 isContentsRTL_ ? NSRightTextAlignment : NSLeftTextAlignment; | 426 isContentsRTL_ ? NSRightTextAlignment : NSLeftTextAlignment; |
| 363 prefix_ = | 427 prefix_ = |
| 364 [CreateAttributedString(base::UTF8ToUTF16(match.GetAdditionalInfo( | 428 [CreateAttributedString(base::UTF8ToUTF16(match.GetAdditionalInfo( |
| 365 kACMatchPropertyContentsPrefix)), | 429 kACMatchPropertyContentsPrefix)), |
| 366 ContentTextColor(), textAlignment) retain]; | 430 ContentTextColor(isDarkTheme), |
| 431 textAlignment) retain]; |
| 367 | 432 |
| 368 isAnswer_ = !!match.answer; | 433 isAnswer_ = !!match.answer; |
| 369 if (isAnswer_) { | 434 if (isAnswer_) { |
| 370 contents_ = [CreateAnswerLine(match.answer->first_line()) retain]; | 435 contents_ = |
| 371 description_ = [CreateAnswerLine(match.answer->second_line()) retain]; | 436 [CreateAnswerLine(match.answer->first_line(), isDarkTheme) retain]; |
| 437 description_ = |
| 438 [CreateAnswerLine(match.answer->second_line(), isDarkTheme) retain]; |
| 372 } else { | 439 } else { |
| 373 contents_ = [CreateClassifiedAttributedString( | 440 contents_ = |
| 374 match.contents, ContentTextColor(), match.contents_class) retain]; | 441 [CreateClassifiedAttributedString(match.contents, |
| 442 ContentTextColor(isDarkTheme), |
| 443 match.contents_class, |
| 444 isDarkTheme) retain]; |
| 375 if (!match.description.empty()) { | 445 if (!match.description.empty()) { |
| 376 description_ = [CreateClassifiedAttributedString( | 446 description_ = |
| 377 match.description, DimTextColor(), match.description_class) retain]; | 447 [CreateClassifiedAttributedString(match.description, |
| 448 DimTextColor(isDarkTheme), |
| 449 match.description_class, |
| 450 isDarkTheme) retain]; |
| 378 } | 451 } |
| 379 } | 452 } |
| 380 } | 453 } |
| 381 return self; | 454 return self; |
| 382 } | 455 } |
| 383 | 456 |
| 457 - (void)dealloc { |
| 458 [incognitoImage_ release]; |
| 459 [super dealloc]; |
| 460 } |
| 461 |
| 384 - (instancetype)copyWithZone:(NSZone*)zone { | 462 - (instancetype)copyWithZone:(NSZone*)zone { |
| 385 return [self retain]; | 463 return [self retain]; |
| 386 } | 464 } |
| 387 | 465 |
| 388 - (CGFloat)getMatchContentsWidth { | 466 - (CGFloat)getMatchContentsWidth { |
| 389 return [contents_ size].width; | 467 return [contents_ size].width; |
| 390 } | 468 } |
| 391 | 469 |
| 392 @end | 470 @end |
| 393 | 471 |
| 394 @implementation OmniboxPopupCell | 472 @implementation OmniboxPopupCell |
| 395 | 473 |
| 396 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 474 - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
| 475 OmniboxPopupMatrix* matrix = |
| 476 base::mac::ObjCCastStrict<OmniboxPopupMatrix>(controlView); |
| 477 BOOL isDarkTheme = [matrix hasDarkTheme]; |
| 478 |
| 397 if ([self state] == NSOnState || [self isHighlighted]) { | 479 if ([self state] == NSOnState || [self isHighlighted]) { |
| 398 if ([self state] == NSOnState) | 480 if ([self state] == NSOnState) { |
| 399 [SelectedBackgroundColor() set]; | 481 [SelectedBackgroundColor(isDarkTheme) set]; |
| 400 else | 482 } else { |
| 401 [HoveredBackgroundColor() set]; | 483 [HoveredBackgroundColor(isDarkTheme) set]; |
| 402 NSBezierPath* path = | 484 } |
| 403 [NSBezierPath bezierPathWithRoundedRect:cellFrame | 485 if (ui::MaterialDesignController::IsModeMaterial()) { |
| 404 xRadius:kCellRoundingRadius | 486 NSRectFillUsingOperation(cellFrame, NSCompositeSourceOver); |
| 405 yRadius:kCellRoundingRadius]; | 487 } else { |
| 406 [path fill]; | 488 NSBezierPath* path = |
| 489 [NSBezierPath bezierPathWithRoundedRect:cellFrame |
| 490 xRadius:kCellRoundingRadius |
| 491 yRadius:kCellRoundingRadius]; |
| 492 [path fill]; |
| 493 } |
| 407 } | 494 } |
| 408 | 495 |
| 409 [self drawMatchWithFrame:cellFrame inView:controlView]; | 496 [self drawMatchWithFrame:cellFrame inView:controlView]; |
| 410 } | 497 } |
| 411 | 498 |
| 412 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { | 499 - (void)drawMatchWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { |
| 413 OmniboxPopupCellData* cellData = | 500 OmniboxPopupCellData* cellData = |
| 414 base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]); | 501 base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]); |
| 415 OmniboxPopupMatrix* tableView = | 502 OmniboxPopupMatrix* tableView = |
| 416 base::mac::ObjCCastStrict<OmniboxPopupMatrix>(controlView); | 503 base::mac::ObjCCastStrict<OmniboxPopupMatrix>(controlView); |
| 417 CGFloat remainingWidth = GetContentAreaWidth(cellFrame); | 504 CGFloat remainingWidth = |
| 505 GetContentAreaWidth(cellFrame) - [tableView contentLeftPadding]; |
| 418 CGFloat contentsWidth = [cellData getMatchContentsWidth]; | 506 CGFloat contentsWidth = [cellData getMatchContentsWidth]; |
| 419 CGFloat separatorWidth = [[tableView separator] size].width; | 507 CGFloat separatorWidth = [[tableView separator] size].width; |
| 420 CGFloat descriptionWidth = | 508 CGFloat descriptionWidth = |
| 421 [cellData description] ? [[cellData description] size].width : 0; | 509 [cellData description] ? [[cellData description] size].width : 0; |
| 422 int contentsMaxWidth, descriptionMaxWidth; | 510 int contentsMaxWidth, descriptionMaxWidth; |
| 423 OmniboxPopupModel::ComputeMatchMaxWidths( | 511 OmniboxPopupModel::ComputeMatchMaxWidths( |
| 424 ceilf(contentsWidth), ceilf(separatorWidth), ceilf(descriptionWidth), | 512 ceilf(contentsWidth), ceilf(separatorWidth), ceilf(descriptionWidth), |
| 425 ceilf(remainingWidth), | 513 ceilf(remainingWidth), |
| 426 [cellData isAnswer], | 514 [cellData isAnswer], |
| 427 !AutocompleteMatch::IsSearchType([cellData matchType]), | 515 !AutocompleteMatch::IsSearchType([cellData matchType]), |
| 428 &contentsMaxWidth, | 516 &contentsMaxWidth, |
| 429 &descriptionMaxWidth); | 517 &descriptionMaxWidth); |
| 430 | 518 |
| 519 NSWindow* parentWindow = [[controlView window] parentWindow]; |
| 520 BOOL isDarkTheme = [parentWindow hasDarkTheme]; |
| 431 NSRect imageRect = cellFrame; | 521 NSRect imageRect = cellFrame; |
| 432 imageRect.size = [[cellData image] size]; | 522 NSImage* theImage = |
| 433 imageRect.origin.x += kImageXOffset; | 523 isDarkTheme ? [cellData incognitoImage] : [cellData image]; |
| 434 imageRect.origin.y += kPaddingOffset; | 524 imageRect.size = [theImage size]; |
| 435 [[cellData image] drawInRect:FlipIfRTL(imageRect, cellFrame) | 525 imageRect.origin.x += kImageXOffset + [tableView contentLeftPadding]; |
| 436 fromRect:NSZeroRect | 526 imageRect.origin.y += VerticalImagePadding(); |
| 437 operation:NSCompositeSourceOver | 527 [theImage drawInRect:FlipIfRTL(imageRect, cellFrame) |
| 438 fraction:1.0 | 528 fromRect:NSZeroRect |
| 439 respectFlipped:YES | 529 operation:NSCompositeSourceOver |
| 440 hints:nil]; | 530 fraction:1.0 |
| 531 respectFlipped:YES |
| 532 hints:nil]; |
| 441 | 533 |
| 442 NSPoint origin = NSMakePoint(kTextStartOffset, kPaddingOffset); | 534 NSPoint origin = NSMakePoint( |
| 535 TextStartOffset() + [tableView contentLeftPadding], kVerticalTextPadding); |
| 443 if ([cellData matchType] == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { | 536 if ([cellData matchType] == AutocompleteMatchType::SEARCH_SUGGEST_TAIL) { |
| 444 // Infinite suggestions are rendered with a prefix (usually ellipsis), which | 537 // Infinite suggestions are rendered with a prefix (usually ellipsis), which |
| 445 // appear vertically stacked. | 538 // appear vertically stacked. |
| 446 origin.x += [self drawMatchPrefixWithFrame:cellFrame | 539 origin.x += [self drawMatchPrefixWithFrame:cellFrame |
| 447 tableView:tableView | 540 tableView:tableView |
| 448 withContentsMaxWidth:&contentsMaxWidth]; | 541 withContentsMaxWidth:&contentsMaxWidth |
| 542 forDarkTheme:isDarkTheme]; |
| 449 } | 543 } |
| 450 origin.x += [self drawMatchPart:[cellData contents] | 544 origin.x += [self drawMatchPart:[cellData contents] |
| 451 withFrame:cellFrame | 545 withFrame:cellFrame |
| 452 origin:origin | 546 origin:origin |
| 453 withMaxWidth:contentsMaxWidth]; | 547 withMaxWidth:contentsMaxWidth |
| 548 forDarkTheme:isDarkTheme]; |
| 454 | 549 |
| 455 if (descriptionMaxWidth > 0) { | 550 if (descriptionMaxWidth > 0) { |
| 456 if ([cellData isAnswer]) { | 551 if ([cellData isAnswer]) { |
| 457 origin = | 552 origin = NSMakePoint(TextStartOffset() + [tableView contentLeftPadding], |
| 458 NSMakePoint(kTextStartOffset, kContentLineHeight - kPaddingOffset); | 553 kContentLineHeight - kVerticalTextPadding); |
| 459 CGFloat imageSize = [tableView answerLineHeight]; | 554 CGFloat imageSize = [tableView answerLineHeight]; |
| 460 NSRect imageRect = | 555 NSRect imageRect = |
| 461 NSMakeRect(NSMinX(cellFrame) + origin.x, NSMinY(cellFrame) + origin.y, | 556 NSMakeRect(NSMinX(cellFrame) + origin.x, NSMinY(cellFrame) + origin.y, |
| 462 imageSize, imageSize); | 557 imageSize, imageSize); |
| 463 [[cellData answerImage] drawInRect:FlipIfRTL(imageRect, cellFrame) | 558 [[cellData answerImage] drawInRect:FlipIfRTL(imageRect, cellFrame) |
| 464 fromRect:NSZeroRect | 559 fromRect:NSZeroRect |
| 465 operation:NSCompositeSourceOver | 560 operation:NSCompositeSourceOver |
| 466 fraction:1.0 | 561 fraction:1.0 |
| 467 respectFlipped:YES | 562 respectFlipped:YES |
| 468 hints:nil]; | 563 hints:nil]; |
| 469 if ([cellData answerImage]) | 564 if ([cellData answerImage]) { |
| 470 origin.x += imageSize + kPaddingOffset; | 565 origin.x += imageSize + VerticalImagePadding(); |
| 566 |
| 567 // Have to nudge the baseline down 1pt in Material Design for the text |
| 568 // that follows, so that it's the same as the bottom of the image. |
| 569 if (ui::MaterialDesignController::IsModeMaterial()) { |
| 570 origin.y += 1; |
| 571 } |
| 572 } |
| 471 } else { | 573 } else { |
| 472 origin.x += [self drawMatchPart:[tableView separator] | 574 origin.x += [self drawMatchPart:[tableView separator] |
| 473 withFrame:cellFrame | 575 withFrame:cellFrame |
| 474 origin:origin | 576 origin:origin |
| 475 withMaxWidth:separatorWidth]; | 577 withMaxWidth:separatorWidth |
| 578 forDarkTheme:isDarkTheme]; |
| 476 } | 579 } |
| 477 origin.x += [self drawMatchPart:[cellData description] | 580 origin.x += [self drawMatchPart:[cellData description] |
| 478 withFrame:cellFrame | 581 withFrame:cellFrame |
| 479 origin:origin | 582 origin:origin |
| 480 withMaxWidth:descriptionMaxWidth]; | 583 withMaxWidth:descriptionMaxWidth |
| 584 forDarkTheme:isDarkTheme]; |
| 481 } | 585 } |
| 482 } | 586 } |
| 483 | 587 |
| 484 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame | 588 - (CGFloat)drawMatchPrefixWithFrame:(NSRect)cellFrame |
| 485 tableView:(OmniboxPopupMatrix*)tableView | 589 tableView:(OmniboxPopupMatrix*)tableView |
| 486 withContentsMaxWidth:(int*)contentsMaxWidth { | 590 withContentsMaxWidth:(int*)contentsMaxWidth |
| 591 forDarkTheme:(BOOL)isDarkTheme { |
| 487 OmniboxPopupCellData* cellData = | 592 OmniboxPopupCellData* cellData = |
| 488 base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]); | 593 base::mac::ObjCCastStrict<OmniboxPopupCellData>([self objectValue]); |
| 489 CGFloat offset = 0.0f; | 594 CGFloat offset = 0.0f; |
| 490 CGFloat remainingWidth = GetContentAreaWidth(cellFrame); | 595 CGFloat remainingWidth = |
| 596 GetContentAreaWidth(cellFrame) - [tableView contentLeftPadding]; |
| 491 CGFloat prefixWidth = [[cellData prefix] size].width; | 597 CGFloat prefixWidth = [[cellData prefix] size].width; |
| 492 | 598 |
| 493 CGFloat prefixOffset = 0.0f; | 599 CGFloat prefixOffset = 0.0f; |
| 494 if (base::i18n::IsRTL() != [cellData isContentsRTL]) { | 600 if (base::i18n::IsRTL() != [cellData isContentsRTL]) { |
| 495 // The contents is rendered between the contents offset extending towards | 601 // The contents is rendered between the contents offset extending towards |
| 496 // the start edge, while prefix is rendered in opposite direction. Ideally | 602 // the start edge, while prefix is rendered in opposite direction. Ideally |
| 497 // the prefix should be rendered at |contentsOffset_|. If that is not | 603 // the prefix should be rendered at |contentsOffset_|. If that is not |
| 498 // sufficient to render the widest suggestion, we increase it to | 604 // sufficient to render the widest suggestion, we increase it to |
| 499 // |maxMatchContentsWidth|. If |remainingWidth| is not sufficient to | 605 // |maxMatchContentsWidth|. If |remainingWidth| is not sufficient to |
| 500 // accommodate that, we reduce the offset so that the prefix gets rendered. | 606 // accommodate that, we reduce the offset so that the prefix gets rendered. |
| 501 prefixOffset = std::min( | 607 prefixOffset = std::min( |
| 502 remainingWidth - prefixWidth, | 608 remainingWidth - prefixWidth, |
| 503 std::max([cellData contentsOffset], [tableView maxMatchContentsWidth])); | 609 std::max([cellData contentsOffset], [tableView maxMatchContentsWidth])); |
| 504 offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth); | 610 offset = std::max<CGFloat>(0.0, prefixOffset - *contentsMaxWidth); |
| 505 } else { // The direction of contents is same as UI direction. | 611 } else { // The direction of contents is same as UI direction. |
| 506 // Ideally the offset should be |contentsOffset_|. If the max total width | 612 // Ideally the offset should be |contentsOffset_|. If the max total width |
| 507 // (|prefixWidth| + |maxMatchContentsWidth|) from offset will exceed the | 613 // (|prefixWidth| + |maxMatchContentsWidth|) from offset will exceed the |
| 508 // |remainingWidth|, then we shift the offset to the left , so that all | 614 // |remainingWidth|, then we shift the offset to the left , so that all |
| 509 // postfix suggestions are visible. | 615 // postfix suggestions are visible. |
| 510 // We have to render the prefix, so offset has to be at least |prefixWidth|. | 616 // We have to render the prefix, so offset has to be at least |prefixWidth|. |
| 511 offset = | 617 offset = |
| 512 std::max(prefixWidth, | 618 std::max(prefixWidth, |
| 513 std::min(remainingWidth - [tableView maxMatchContentsWidth], | 619 std::min(remainingWidth - [tableView maxMatchContentsWidth], |
| 514 [cellData contentsOffset])); | 620 [cellData contentsOffset])); |
| 515 prefixOffset = offset - prefixWidth; | 621 prefixOffset = offset - prefixWidth; |
| 516 } | 622 } |
| 517 *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth), | 623 *contentsMaxWidth = std::min((int)ceilf(remainingWidth - prefixWidth), |
| 518 *contentsMaxWidth); | 624 *contentsMaxWidth); |
| 625 NSPoint origin = NSMakePoint( |
| 626 prefixOffset + TextStartOffset() + [tableView contentLeftPadding], 0); |
| 519 [self drawMatchPart:[cellData prefix] | 627 [self drawMatchPart:[cellData prefix] |
| 520 withFrame:cellFrame | 628 withFrame:cellFrame |
| 521 origin:NSMakePoint(prefixOffset + kTextStartOffset, 0) | 629 origin:origin |
| 522 withMaxWidth:prefixWidth]; | 630 withMaxWidth:prefixWidth |
| 631 forDarkTheme:isDarkTheme]; |
| 523 return offset; | 632 return offset; |
| 524 } | 633 } |
| 525 | 634 |
| 526 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString | 635 - (CGFloat)drawMatchPart:(NSAttributedString*)attributedString |
| 527 withFrame:(NSRect)cellFrame | 636 withFrame:(NSRect)cellFrame |
| 528 origin:(NSPoint)origin | 637 origin:(NSPoint)origin |
| 529 withMaxWidth:(int)maxWidth { | 638 withMaxWidth:(int)maxWidth |
| 639 forDarkTheme:(BOOL)isDarkTheme { |
| 530 NSRect renderRect = NSIntersectionRect( | 640 NSRect renderRect = NSIntersectionRect( |
| 531 cellFrame, NSOffsetRect(cellFrame, origin.x, origin.y)); | 641 cellFrame, NSOffsetRect(cellFrame, origin.x, origin.y)); |
| 532 renderRect.size.width = | 642 renderRect.size.width = |
| 533 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth)); | 643 std::min(NSWidth(renderRect), static_cast<CGFloat>(maxWidth)); |
| 534 if (!NSIsEmptyRect(renderRect)) | 644 if (!NSIsEmptyRect(renderRect)) |
| 535 [attributedString drawInRect:FlipIfRTL(renderRect, cellFrame)]; | 645 [attributedString drawInRect:FlipIfRTL(renderRect, cellFrame)]; |
| 536 return NSWidth(renderRect); | 646 return NSWidth(renderRect); |
| 537 } | 647 } |
| 538 | 648 |
| 539 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match { | 649 + (CGFloat)computeContentsOffset:(const AutocompleteMatch&)match { |
| 540 const base::string16& inputText = base::UTF8ToUTF16( | 650 const base::string16& inputText = base::UTF8ToUTF16( |
| 541 match.GetAdditionalInfo(kACMatchPropertyInputText)); | 651 match.GetAdditionalInfo(kACMatchPropertyInputText)); |
| 542 int contentsStartIndex = 0; | 652 int contentsStartIndex = 0; |
| 543 base::StringToInt( | 653 base::StringToInt( |
| 544 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex), | 654 match.GetAdditionalInfo(kACMatchPropertyContentsStartIndex), |
| 545 &contentsStartIndex); | 655 &contentsStartIndex); |
| 546 // Ignore invalid state. | 656 // Ignore invalid state. |
| 547 if (!base::StartsWith(match.fill_into_edit, inputText, | 657 if (!base::StartsWith(match.fill_into_edit, inputText, |
| 548 base::CompareCase::SENSITIVE) || | 658 base::CompareCase::SENSITIVE) || |
| 549 !base::EndsWith(match.fill_into_edit, match.contents, | 659 !base::EndsWith(match.fill_into_edit, match.contents, |
| 550 base::CompareCase::SENSITIVE) || | 660 base::CompareCase::SENSITIVE) || |
| 551 ((size_t)contentsStartIndex >= inputText.length())) { | 661 ((size_t)contentsStartIndex >= inputText.length())) { |
| 552 return 0; | 662 return 0; |
| 553 } | 663 } |
| 554 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT == | 664 bool isContentsRTL = (base::i18n::RIGHT_TO_LEFT == |
| 555 base::i18n::GetFirstStrongCharacterDirection(match.contents)); | 665 base::i18n::GetFirstStrongCharacterDirection(match.contents)); |
| 556 | 666 |
| 557 // Color does not matter. | 667 // Color does not matter. |
| 558 NSAttributedString* attributedString = | 668 NSAttributedString* attributedString = |
| 559 CreateAttributedString(inputText, DimTextColor()); | 669 CreateAttributedString(inputText, DimTextColor(false)); |
| 560 base::scoped_nsobject<NSTextStorage> textStorage( | 670 base::scoped_nsobject<NSTextStorage> textStorage( |
| 561 [[NSTextStorage alloc] initWithAttributedString:attributedString]); | 671 [[NSTextStorage alloc] initWithAttributedString:attributedString]); |
| 562 base::scoped_nsobject<NSLayoutManager> layoutManager( | 672 base::scoped_nsobject<NSLayoutManager> layoutManager( |
| 563 [[NSLayoutManager alloc] init]); | 673 [[NSLayoutManager alloc] init]); |
| 564 base::scoped_nsobject<NSTextContainer> textContainer( | 674 base::scoped_nsobject<NSTextContainer> textContainer( |
| 565 [[NSTextContainer alloc] init]); | 675 [[NSTextContainer alloc] init]); |
| 566 [layoutManager addTextContainer:textContainer]; | 676 [layoutManager addTextContainer:textContainer]; |
| 567 [textStorage addLayoutManager:layoutManager]; | 677 [textStorage addLayoutManager:layoutManager]; |
| 568 | 678 |
| 569 NSUInteger charIndex = static_cast<NSUInteger>(contentsStartIndex); | 679 NSUInteger charIndex = static_cast<NSUInteger>(contentsStartIndex); |
| (...skipping 27 matching lines...) Expand all Loading... |
| 597 glyphWidth = std::min(glyphWidth, offset - glyphOffset); | 707 glyphWidth = std::min(glyphWidth, offset - glyphOffset); |
| 598 } | 708 } |
| 599 glyphOffset -= minOffset; | 709 glyphOffset -= minOffset; |
| 600 if (glyphWidth == 0) | 710 if (glyphWidth == 0) |
| 601 glyphWidth = inputWidth - glyphOffset; | 711 glyphWidth = inputWidth - glyphOffset; |
| 602 if (isContentsRTL) | 712 if (isContentsRTL) |
| 603 glyphOffset += glyphWidth; | 713 glyphOffset += glyphWidth; |
| 604 return base::i18n::IsRTL() ? (inputWidth - glyphOffset) : glyphOffset; | 714 return base::i18n::IsRTL() ? (inputWidth - glyphOffset) : glyphOffset; |
| 605 } | 715 } |
| 606 | 716 |
| 607 + (NSAttributedString*)createSeparatorString { | 717 + (NSAttributedString*)createSeparatorStringForDarkTheme:(BOOL)isDarkTheme { |
| 608 base::string16 raw_separator = | 718 base::string16 raw_separator = |
| 609 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); | 719 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR); |
| 610 return CreateAttributedString(raw_separator, DimTextColor()); | 720 return CreateAttributedString(raw_separator, DimTextColor(isDarkTheme)); |
| 611 } | 721 } |
| 612 | 722 |
| 613 @end | 723 @end |
| OLD | NEW |