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/browser/ui/omnibox/omnibox_popup_non_view.h" | 19 #include "chrome/browser/ui/omnibox/omnibox_popup_non_view.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 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
119 DCHECK_GT(matrix_width, 0.0); | 110 DCHECK_GT(matrix_width, 0.0); |
120 | 111 |
121 // Load the results into the popup's matrix. | 112 // Load the results into the popup's matrix. |
122 const size_t rows = GetResult().size(); | 113 const size_t rows = GetResult().size(); |
123 DCHECK_GT(rows, 0U); | 114 DCHECK_GT(rows, 0U); |
124 [matrix_ renewRows:rows columns:1]; | 115 [matrix_ renewRows:rows columns:1]; |
125 for (size_t ii = 0; ii < rows; ++ii) { | 116 for (size_t ii = 0; ii < rows; ++ii) { |
126 OmniboxPopupCell* cell = [matrix_ cellAtRow:ii column:0]; | 117 OmniboxPopupCell* cell = [matrix_ cellAtRow:ii column:0]; |
127 const AutocompleteMatch& match = GetResult().match_at(ii); | 118 const AutocompleteMatch& match = GetResult().match_at(ii); |
128 [cell setImage:ImageForMatch(match)]; | 119 [cell setImage:ImageForMatch(match)]; |
129 [cell setAttributedTitle:MatchText(match, result_font, matrix_width)]; | 120 [cell setContentText:DecorateMatchedString(match.contents, |
| 121 match.contents_class, |
| 122 ContentTextColor(), |
| 123 DimContentTextColor(), |
| 124 result_font)]; |
| 125 if (match.description.empty()) { |
| 126 [cell setDescriptionText:nil]; |
| 127 } else { |
| 128 [cell setDescriptionText:DecorateMatchedString(match.description, |
| 129 match.description_class, |
| 130 DimContentTextColor(), |
| 131 DimContentTextColor(), |
| 132 result_font)]; |
| 133 } |
130 } | 134 } |
131 | 135 |
132 // Set the cell size to fit a line of text in the cell's font. All | 136 // Set the cell size to fit a line of text in the cell's font. All |
133 // cells should use the same font and each should layout in one | 137 // cells should use the same font and each should layout in one |
134 // line, so they should all be about the same height. | 138 // line, so they should all be about the same height. |
135 const NSSize cell_size = [[matrix_ cellAtRow:0 column:0] cellSize]; | 139 const NSSize cell_size = [[matrix_ cellAtRow:0 column:0] cellSize]; |
136 DCHECK_GT(cell_size.height, 0.0); | 140 DCHECK_GT(cell_size.height, 0.0); |
137 const CGFloat cell_height = cell_size.height + kCellHeightAdjust; | 141 const CGFloat cell_height = cell_size.height + kCellHeightAdjust; |
138 [matrix_ setCellSize:NSMakeSize(matrix_width, cell_height)]; | 142 [matrix_ setCellSize:NSMakeSize(matrix_width, cell_height)]; |
139 | 143 |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
171 size_t row) { | 175 size_t row) { |
172 OpenURLForRow(row, | 176 OpenURLForRow(row, |
173 ui::WindowOpenDispositionFromNSEvent([NSApp currentEvent])); | 177 ui::WindowOpenDispositionFromNSEvent([NSApp currentEvent])); |
174 } | 178 } |
175 | 179 |
176 void OmniboxPopupViewMac::OnMatrixRowMiddleClicked(OmniboxPopupMatrix* matrix, | 180 void OmniboxPopupViewMac::OnMatrixRowMiddleClicked(OmniboxPopupMatrix* matrix, |
177 size_t row) { | 181 size_t row) { |
178 OpenURLForRow(row, NEW_BACKGROUND_TAB); | 182 OpenURLForRow(row, NEW_BACKGROUND_TAB); |
179 } | 183 } |
180 | 184 |
181 // Return the text to show for the match, based on the match's | |
182 // contents and description. Result will be in |font|, with the | |
183 // boldfaced version used for matches. | |
184 NSAttributedString* OmniboxPopupViewMac::MatchText( | |
185 const AutocompleteMatch& match, | |
186 gfx::Font& font, | |
187 float cell_width) { | |
188 NSMutableAttributedString *as = | |
189 DecorateMatchedString(match.contents, | |
190 match.contents_class, | |
191 ContentTextColor(), | |
192 DimContentTextColor(), | |
193 font); | |
194 | |
195 // If there is a description, append it, separated from the contents | |
196 // with an en dash, and decorated with a distinct color. | |
197 if (!match.description.empty()) { | |
198 // Make sure the current string fits w/in kMaxContentsFraction of | |
199 // the cell to make sure the description will be at least | |
200 // partially visible. | |
201 // TODO(shess): Consider revising our NSCell subclass to have two | |
202 // bits and just draw them right, rather than truncating here. | |
203 const float text_width = cell_width - kTextXOffset; | |
204 as = ElideString(as, match.contents, font, | |
205 text_width * kMaxContentsFraction); | |
206 | |
207 NSDictionary* attributes = @{ | |
208 NSFontAttributeName : font.GetNativeFont(), | |
209 NSForegroundColorAttributeName : ContentTextColor() | |
210 }; | |
211 NSString* raw_en_dash = @" \u2013 "; | |
212 NSAttributedString* en_dash = | |
213 [[[NSAttributedString alloc] initWithString:raw_en_dash | |
214 attributes:attributes] autorelease]; | |
215 | |
216 // In Windows, a boolean force_dim is passed as true for the | |
217 // description. Here, we pass the dim text color for both normal and dim, | |
218 // to accomplish the same thing. | |
219 NSAttributedString* description = | |
220 DecorateMatchedString(match.description, match.description_class, | |
221 DimContentTextColor(), | |
222 DimContentTextColor(), | |
223 font); | |
224 | |
225 [as appendAttributedString:en_dash]; | |
226 [as appendAttributedString:description]; | |
227 } | |
228 | |
229 NSMutableParagraphStyle* style = | |
230 [[[NSMutableParagraphStyle alloc] init] autorelease]; | |
231 [style setLineBreakMode:NSLineBreakByTruncatingTail]; | |
232 [style setTighteningFactorForTruncation:0.0]; | |
233 [as addAttribute:NSParagraphStyleAttributeName value:style | |
234 range:NSMakeRange(0, [as length])]; | |
235 | |
236 return as; | |
237 } | |
238 | |
239 // static | 185 // static |
240 NSMutableAttributedString* OmniboxPopupViewMac::DecorateMatchedString( | 186 NSMutableAttributedString* OmniboxPopupViewMac::DecorateMatchedString( |
241 const string16& match_string, | 187 const string16& match_string, |
242 const AutocompleteMatch::ACMatchClassifications& classifications, | 188 const AutocompleteMatch::ACMatchClassifications& classifications, |
243 NSColor* text_color, | 189 NSColor* text_color, |
244 NSColor* dim_text_color, | 190 NSColor* dim_text_color, |
245 gfx::Font& font) { | 191 gfx::Font& font) { |
246 // Cache for on-demand computation of the bold version of |font|. | 192 // Cache for on-demand computation of the bold version of |font|. |
247 NSFont* bold_font = nil; | 193 NSFont* bold_font = nil; |
248 | 194 |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
294 if (0 != (i->style & ACMatchClassification::DIM)) { | 240 if (0 != (i->style & ACMatchClassification::DIM)) { |
295 [as addAttribute:NSForegroundColorAttributeName | 241 [as addAttribute:NSForegroundColorAttributeName |
296 value:dim_text_color | 242 value:dim_text_color |
297 range:range]; | 243 range:range]; |
298 } | 244 } |
299 } | 245 } |
300 | 246 |
301 return as; | 247 return as; |
302 } | 248 } |
303 | 249 |
304 NSMutableAttributedString* OmniboxPopupViewMac::ElideString( | |
305 NSMutableAttributedString* a_string, | |
306 const string16& original_string, | |
307 const gfx::Font& font, | |
308 const float width) { | |
309 // If it already fits, nothing to be done. | |
310 if ([a_string size].width <= width) { | |
311 return a_string; | |
312 } | |
313 | |
314 // If ElideText() decides to do nothing, nothing to be done. | |
315 const string16 elided = | |
316 ui::ElideText(original_string, font, width, ui::ELIDE_AT_END); | |
317 if (0 == elided.compare(original_string)) { | |
318 return a_string; | |
319 } | |
320 | |
321 // If everything was elided away, clear the string. | |
322 if (elided.empty()) { | |
323 [a_string deleteCharactersInRange:NSMakeRange(0, [a_string length])]; | |
324 return a_string; | |
325 } | |
326 | |
327 // The ellipses should be the last character, and everything before | |
328 // that should match the original string. | |
329 const size_t i(elided.length() - 1); | |
330 DCHECK_NE(0, elided.compare(0, i, original_string)); | |
331 | |
332 // Replace the end of |aString| with the ellipses from |elided|. | |
333 NSString* s = base::SysUTF16ToNSString(elided.substr(i)); | |
334 [a_string replaceCharactersInRange:NSMakeRange(i, [a_string length] - i) | |
335 withString:s]; | |
336 | |
337 return a_string; | |
338 } | |
339 | |
340 const AutocompleteResult& OmniboxPopupViewMac::GetResult() const { | 250 const AutocompleteResult& OmniboxPopupViewMac::GetResult() const { |
341 return model_->result(); | 251 return model_->result(); |
342 } | 252 } |
343 | 253 |
344 void OmniboxPopupViewMac::CreatePopupIfNeeded() { | 254 void OmniboxPopupViewMac::CreatePopupIfNeeded() { |
345 if (!popup_) { | 255 if (!popup_) { |
346 popup_.reset( | 256 popup_.reset( |
347 [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater | 257 [[NSWindow alloc] initWithContentRect:ui::kWindowSizeDeterminedLater |
348 styleMask:NSBorderlessWindowMask | 258 styleMask:NSBorderlessWindowMask |
349 backing:NSBackingStoreBuffered | 259 backing:NSBackingStoreBuffered |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
473 void OmniboxPopupViewMac::OpenURLForRow(size_t row, | 383 void OmniboxPopupViewMac::OpenURLForRow(size_t row, |
474 WindowOpenDisposition disposition) { | 384 WindowOpenDisposition disposition) { |
475 DCHECK_GE(row, 0u); | 385 DCHECK_GE(row, 0u); |
476 | 386 |
477 // OpenMatch() may close the popup, which will clear the result set and, by | 387 // OpenMatch() may close the popup, which will clear the result set and, by |
478 // extension, |match| and its contents. So copy the relevant match out to | 388 // extension, |match| and its contents. So copy the relevant match out to |
479 // make sure it stays alive until the call completes. | 389 // make sure it stays alive until the call completes. |
480 AutocompleteMatch match = GetResult().match_at(row); | 390 AutocompleteMatch match = GetResult().match_at(row); |
481 omnibox_view_->OpenMatch(match, disposition, GURL(), row); | 391 omnibox_view_->OpenMatch(match, disposition, GURL(), row); |
482 } | 392 } |
OLD | NEW |