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 |