| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h" | 5 #include "chrome/browser/ui/cocoa/omnibox/omnibox_popup_view_mac.h" |
| 6 | 6 |
| 7 #include <cmath> | 7 #include <cmath> |
| 8 | 8 |
| 9 #include "base/stl_util.h" | 9 #include "base/stl_util.h" |
| 10 #include "base/strings/sys_string_conversions.h" | 10 #include "base/strings/sys_string_conversions.h" |
| 11 #include "chrome/browser/autocomplete/autocomplete_match.h" | 11 #include "chrome/browser/autocomplete/autocomplete_match.h" |
| 12 #include "chrome/browser/search/search.h" | 12 #include "chrome/browser/search/search.h" |
| 13 #include "chrome/browser/ui/cocoa/browser_window_controller.h" | 13 #include "chrome/browser/ui/cocoa/browser_window_controller.h" |
| 14 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h" | 14 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_cell.h" |
| 15 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.h" | 15 #import "chrome/browser/ui/cocoa/omnibox/omnibox_popup_separator_view.h" |
| 16 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h" | 16 #include "chrome/browser/ui/cocoa/omnibox/omnibox_view_mac.h" |
| 17 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h" | 17 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h" |
| 18 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" | 18 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" |
| 19 #include "chrome/common/autocomplete_match_type.h" | 19 #include "chrome/common/autocomplete_match_type.h" |
| 20 #include "grit/theme_resources.h" | 20 #include "grit/theme_resources.h" |
| 21 #include "skia/ext/skia_utils_mac.h" | 21 #include "skia/ext/skia_utils_mac.h" |
| 22 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" | 22 #import "third_party/GTM/AppKit/GTMNSAnimation+Duration.h" |
| 23 #import "ui/base/cocoa/cocoa_event_utils.h" | 23 #import "ui/base/cocoa/cocoa_event_utils.h" |
| 24 #import "ui/base/cocoa/flipped_view.h" | 24 #import "ui/base/cocoa/flipped_view.h" |
| 25 #include "ui/base/cocoa/window_size_constants.h" | 25 #include "ui/base/cocoa/window_size_constants.h" |
| 26 #include "ui/base/resource/resource_bundle.h" | 26 #include "ui/base/resource/resource_bundle.h" |
| 27 #include "ui/base/text/text_elider.h" | |
| 28 #include "ui/gfx/rect.h" | 27 #include "ui/gfx/rect.h" |
| 29 #include "ui/gfx/scoped_ns_graphics_context_save_gstate_mac.h" | |
| 30 | 28 |
| 31 namespace { | 29 namespace { |
| 32 | 30 |
| 33 // How much to adjust the cell sizing up from the default determined | 31 // How much to adjust the cell sizing up from the default determined |
| 34 // by the font. | 32 // by the font. |
| 35 const CGFloat kCellHeightAdjust = 6.0; | 33 const CGFloat kCellHeightAdjust = 6.0; |
| 36 | 34 |
| 37 // Padding between matrix and the top and bottom of the popup window. | 35 // Padding between matrix and the top and bottom of the popup window. |
| 38 const CGFloat kPopupPaddingVertical = 5.0; | 36 const CGFloat kPopupPaddingVertical = 5.0; |
| 39 | 37 |
| 40 // How far to offset the text column from the left. | |
| 41 const CGFloat kTextXOffset = 28.0; | |
| 42 | |
| 43 // Animation duration when animating the popup window smaller. | 38 // Animation duration when animating the popup window smaller. |
| 44 const NSTimeInterval kShrinkAnimationDuration = 0.1; | 39 const NSTimeInterval kShrinkAnimationDuration = 0.1; |
| 45 | 40 |
| 46 // Maximum fraction of the popup width that can be used to display match | |
| 47 // contents. | |
| 48 const CGFloat kMaxContentsFraction = 0.7; | |
| 49 | |
| 50 // Background colors for different states of the popup elements. | 41 // Background colors for different states of the popup elements. |
| 51 NSColor* BackgroundColor() { | 42 NSColor* BackgroundColor() { |
| 52 return [NSColor controlBackgroundColor]; | 43 return [NSColor controlBackgroundColor]; |
| 53 } | 44 } |
| 54 | 45 |
| 55 NSColor* ContentTextColor() { | 46 NSColor* ContentTextColor() { |
| 56 return [NSColor blackColor]; | 47 return [NSColor blackColor]; |
| 57 } | 48 } |
| 58 NSColor* DimContentTextColor() { | 49 NSColor* DimContentTextColor() { |
| 59 return [NSColor darkGrayColor]; | 50 return [NSColor darkGrayColor]; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 const CGFloat matrix_width = NSWidth([field_ bounds]); | 111 const CGFloat matrix_width = NSWidth([field_ bounds]); |
| 121 DCHECK_GT(matrix_width, 0.0); | 112 DCHECK_GT(matrix_width, 0.0); |
| 122 | 113 |
| 123 // Load the results into the popup's matrix. | 114 // Load the results into the popup's matrix. |
| 124 DCHECK_GT(rows, 0U); | 115 DCHECK_GT(rows, 0U); |
| 125 [matrix_ renewRows:rows columns:1]; | 116 [matrix_ renewRows:rows columns:1]; |
| 126 for (size_t ii = 0; ii < rows; ++ii) { | 117 for (size_t ii = 0; ii < rows; ++ii) { |
| 127 OmniboxPopupCell* cell = [matrix_ cellAtRow:ii column:0]; | 118 OmniboxPopupCell* cell = [matrix_ cellAtRow:ii column:0]; |
| 128 const AutocompleteMatch& match = GetResult().match_at(ii + start_match); | 119 const AutocompleteMatch& match = GetResult().match_at(ii + start_match); |
| 129 [cell setImage:ImageForMatch(match)]; | 120 [cell setImage:ImageForMatch(match)]; |
| 130 [cell setAttributedTitle:MatchText(match, result_font, matrix_width)]; | 121 [cell setContentText:DecorateMatchedString(match.contents, |
| 122 match.contents_class, |
| 123 ContentTextColor(), |
| 124 DimContentTextColor(), |
| 125 result_font)]; |
| 126 if (match.description.empty()) { |
| 127 [cell setDescriptionText:nil]; |
| 128 } else { |
| 129 [cell setDescriptionText:DecorateMatchedString(match.description, |
| 130 match.description_class, |
| 131 DimContentTextColor(), |
| 132 DimContentTextColor(), |
| 133 result_font)]; |
| 134 } |
| 131 } | 135 } |
| 132 | 136 |
| 133 // Set the cell size to fit a line of text in the cell's font. All | 137 // Set the cell size to fit a line of text in the cell's font. All |
| 134 // cells should use the same font and each should layout in one | 138 // cells should use the same font and each should layout in one |
| 135 // line, so they should all be about the same height. | 139 // line, so they should all be about the same height. |
| 136 const NSSize cell_size = [[matrix_ cellAtRow:0 column:0] cellSize]; | 140 const NSSize cell_size = [[matrix_ cellAtRow:0 column:0] cellSize]; |
| 137 DCHECK_GT(cell_size.height, 0.0); | 141 DCHECK_GT(cell_size.height, 0.0); |
| 138 const CGFloat cell_height = cell_size.height + kCellHeightAdjust; | 142 const CGFloat cell_height = cell_size.height + kCellHeightAdjust; |
| 139 [matrix_ setCellSize:NSMakeSize(matrix_width, cell_height)]; | 143 [matrix_ setCellSize:NSMakeSize(matrix_width, cell_height)]; |
| 140 | 144 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 size_t row) { | 182 size_t row) { |
| 179 OpenURLForRow(row, | 183 OpenURLForRow(row, |
| 180 ui::WindowOpenDispositionFromNSEvent([NSApp currentEvent])); | 184 ui::WindowOpenDispositionFromNSEvent([NSApp currentEvent])); |
| 181 } | 185 } |
| 182 | 186 |
| 183 void OmniboxPopupViewMac::OnMatrixRowMiddleClicked(OmniboxPopupMatrix* matrix, | 187 void OmniboxPopupViewMac::OnMatrixRowMiddleClicked(OmniboxPopupMatrix* matrix, |
| 184 size_t row) { | 188 size_t row) { |
| 185 OpenURLForRow(row, NEW_BACKGROUND_TAB); | 189 OpenURLForRow(row, NEW_BACKGROUND_TAB); |
| 186 } | 190 } |
| 187 | 191 |
| 188 // Return the text to show for the match, based on the match's | |
| 189 // contents and description. Result will be in |font|, with the | |
| 190 // boldfaced version used for matches. | |
| 191 NSAttributedString* OmniboxPopupViewMac::MatchText( | |
| 192 const AutocompleteMatch& match, | |
| 193 gfx::Font& font, | |
| 194 float cell_width) { | |
| 195 NSMutableAttributedString *as = | |
| 196 DecorateMatchedString(match.contents, | |
| 197 match.contents_class, | |
| 198 ContentTextColor(), | |
| 199 DimContentTextColor(), | |
| 200 font); | |
| 201 | |
| 202 // If there is a description, append it, separated from the contents | |
| 203 // with an en dash, and decorated with a distinct color. | |
| 204 if (!match.description.empty()) { | |
| 205 // Make sure the current string fits w/in kMaxContentsFraction of | |
| 206 // the cell to make sure the description will be at least | |
| 207 // partially visible. | |
| 208 // TODO(shess): Consider revising our NSCell subclass to have two | |
| 209 // bits and just draw them right, rather than truncating here. | |
| 210 const float text_width = cell_width - kTextXOffset; | |
| 211 as = ElideString(as, match.contents, font, | |
| 212 text_width * kMaxContentsFraction); | |
| 213 | |
| 214 NSDictionary* attributes = @{ | |
| 215 NSFontAttributeName : font.GetNativeFont(), | |
| 216 NSForegroundColorAttributeName : ContentTextColor() | |
| 217 }; | |
| 218 NSString* raw_en_dash = @" \u2013 "; | |
| 219 NSAttributedString* en_dash = | |
| 220 [[[NSAttributedString alloc] initWithString:raw_en_dash | |
| 221 attributes:attributes] autorelease]; | |
| 222 | |
| 223 // In Windows, a boolean force_dim is passed as true for the | |
| 224 // description. Here, we pass the dim text color for both normal and dim, | |
| 225 // to accomplish the same thing. | |
| 226 NSAttributedString* description = | |
| 227 DecorateMatchedString(match.description, match.description_class, | |
| 228 DimContentTextColor(), | |
| 229 DimContentTextColor(), | |
| 230 font); | |
| 231 | |
| 232 [as appendAttributedString:en_dash]; | |
| 233 [as appendAttributedString:description]; | |
| 234 } | |
| 235 | |
| 236 NSMutableParagraphStyle* style = | |
| 237 [[[NSMutableParagraphStyle alloc] init] autorelease]; | |
| 238 [style setLineBreakMode:NSLineBreakByTruncatingTail]; | |
| 239 [style setTighteningFactorForTruncation:0.0]; | |
| 240 [as addAttribute:NSParagraphStyleAttributeName value:style | |
| 241 range:NSMakeRange(0, [as length])]; | |
| 242 | |
| 243 return as; | |
| 244 } | |
| 245 | |
| 246 // static | 192 // static |
| 247 NSMutableAttributedString* OmniboxPopupViewMac::DecorateMatchedString( | 193 NSMutableAttributedString* OmniboxPopupViewMac::DecorateMatchedString( |
| 248 const string16& match_string, | 194 const string16& match_string, |
| 249 const AutocompleteMatch::ACMatchClassifications& classifications, | 195 const AutocompleteMatch::ACMatchClassifications& classifications, |
| 250 NSColor* text_color, | 196 NSColor* text_color, |
| 251 NSColor* dim_text_color, | 197 NSColor* dim_text_color, |
| 252 gfx::Font& font) { | 198 gfx::Font& font) { |
| 253 // Cache for on-demand computation of the bold version of |font|. | 199 // Cache for on-demand computation of the bold version of |font|. |
| 254 NSFont* bold_font = nil; | 200 NSFont* bold_font = nil; |
| 255 | 201 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 301 if (0 != (i->style & ACMatchClassification::DIM)) { | 247 if (0 != (i->style & ACMatchClassification::DIM)) { |
| 302 [as addAttribute:NSForegroundColorAttributeName | 248 [as addAttribute:NSForegroundColorAttributeName |
| 303 value:dim_text_color | 249 value:dim_text_color |
| 304 range:range]; | 250 range:range]; |
| 305 } | 251 } |
| 306 } | 252 } |
| 307 | 253 |
| 308 return as; | 254 return as; |
| 309 } | 255 } |
| 310 | 256 |
| 311 NSMutableAttributedString* OmniboxPopupViewMac::ElideString( | |
| 312 NSMutableAttributedString* a_string, | |
| 313 const string16& original_string, | |
| 314 const gfx::Font& font, | |
| 315 const float width) { | |
| 316 // If it already fits, nothing to be done. | |
| 317 if ([a_string size].width <= width) { | |
| 318 return a_string; | |
| 319 } | |
| 320 | |
| 321 // If ElideText() decides to do nothing, nothing to be done. | |
| 322 const string16 elided = | |
| 323 ui::ElideText(original_string, font, width, ui::ELIDE_AT_END); | |
| 324 if (0 == elided.compare(original_string)) { | |
| 325 return a_string; | |
| 326 } | |
| 327 | |
| 328 // If everything was elided away, clear the string. | |
| 329 if (elided.empty()) { | |
| 330 [a_string deleteCharactersInRange:NSMakeRange(0, [a_string length])]; | |
| 331 return a_string; | |
| 332 } | |
| 333 | |
| 334 // The ellipses should be the last character, and everything before | |
| 335 // that should match the original string. | |
| 336 const size_t i(elided.length() - 1); | |
| 337 DCHECK_NE(0, elided.compare(0, i, original_string)); | |
| 338 | |
| 339 // Replace the end of |aString| with the ellipses from |elided|. | |
| 340 NSString* s = base::SysUTF16ToNSString(elided.substr(i)); | |
| 341 [a_string replaceCharactersInRange:NSMakeRange(i, [a_string length] - i) | |
| 342 withString:s]; | |
| 343 | |
| 344 return a_string; | |
| 345 } | |
| 346 | |
| 347 const AutocompleteResult& OmniboxPopupViewMac::GetResult() const { | 257 const AutocompleteResult& OmniboxPopupViewMac::GetResult() const { |
| 348 return model_->result(); | 258 return model_->result(); |
| 349 } | 259 } |
| 350 | 260 |
| 351 void OmniboxPopupViewMac::CreatePopupIfNeeded() { | 261 void OmniboxPopupViewMac::CreatePopupIfNeeded() { |
| 352 if (!popup_) { | 262 if (!popup_) { |
| 353 popup_.reset( | 263 popup_.reset( |
| 354 [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater | 264 [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater |
| 355 styleMask:NSBorderlessWindowMask | 265 styleMask:NSBorderlessWindowMask |
| 356 backing:NSBackingStoreBuffered | 266 backing:NSBackingStoreBuffered |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 494 void OmniboxPopupViewMac::OpenURLForRow(size_t row, | 404 void OmniboxPopupViewMac::OpenURLForRow(size_t row, |
| 495 WindowOpenDisposition disposition) { | 405 WindowOpenDisposition disposition) { |
| 496 DCHECK_GE(row, 0u); | 406 DCHECK_GE(row, 0u); |
| 497 | 407 |
| 498 // OpenMatch() may close the popup, which will clear the result set and, by | 408 // OpenMatch() may close the popup, which will clear the result set and, by |
| 499 // extension, |match| and its contents. So copy the relevant match out to | 409 // extension, |match| and its contents. So copy the relevant match out to |
| 500 // make sure it stays alive until the call completes. | 410 // make sure it stays alive until the call completes. |
| 501 AutocompleteMatch match = GetResult().match_at(row); | 411 AutocompleteMatch match = GetResult().match_at(row); |
| 502 omnibox_view_->OpenMatch(match, disposition, GURL(), row); | 412 omnibox_view_->OpenMatch(match, disposition, GURL(), row); |
| 503 } | 413 } |
| OLD | NEW |