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