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/gtk/omnibox/omnibox_popup_view_gtk.h" | 5 #include "chrome/browser/ui/gtk/omnibox/omnibox_popup_view_gtk.h" |
6 | 6 |
7 #include <gtk/gtk.h> | 7 #include <gtk/gtk.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <string> | 10 #include <string> |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
90 // UTF-8 Left-to-right embedding. | 90 // UTF-8 Left-to-right embedding. |
91 const char* kLRE = "\xe2\x80\xaa"; | 91 const char* kLRE = "\xe2\x80\xaa"; |
92 | 92 |
93 // Return a Rect covering the whole area of |window|. | 93 // Return a Rect covering the whole area of |window|. |
94 gfx::Rect GetWindowRect(GdkWindow* window) { | 94 gfx::Rect GetWindowRect(GdkWindow* window) { |
95 gint width = gdk_window_get_width(window); | 95 gint width = gdk_window_get_width(window); |
96 gint height = gdk_window_get_height(window); | 96 gint height = gdk_window_get_height(window); |
97 return gfx::Rect(width, height); | 97 return gfx::Rect(width, height); |
98 } | 98 } |
99 | 99 |
100 // Return a Rect for the space for a result line. This excludes the border, | |
101 // but includes the padding. This is the area that is colored for a selection. | |
102 gfx::Rect GetRectForLine(size_t line, int width) { | |
103 return gfx::Rect(kBorderThickness, | |
104 (line * kHeightPerResult) + kBorderThickness, | |
105 width - (kBorderThickness * 2), | |
106 kHeightPerResult); | |
107 } | |
108 | |
109 // TODO(deanm): Find some better home for this, and make it more efficient. | 100 // TODO(deanm): Find some better home for this, and make it more efficient. |
110 size_t GetUTF8Offset(const string16& text, size_t text_offset) { | 101 size_t GetUTF8Offset(const string16& text, size_t text_offset) { |
111 return UTF16ToUTF8(text.substr(0, text_offset)).length(); | 102 return UTF16ToUTF8(text.substr(0, text_offset)).length(); |
112 } | 103 } |
113 | 104 |
114 // Generates the normal URL color, a green color used in unhighlighted URL | 105 // Generates the normal URL color, a green color used in unhighlighted URL |
115 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the | 106 // text. It is a mix of |kURLTextColor| and the current text color. Unlike the |
116 // selected text color, it is more important to match the qualities of the | 107 // selected text color, it is more important to match the qualities of the |
117 // foreground typeface color instead of taking the background into account. | 108 // foreground typeface color instead of taking the background into account. |
118 GdkColor NormalURLColor(GdkColor foreground) { | 109 GdkColor NormalURLColor(GdkColor foreground) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
162 // The luminance should match the luminance of the foreground text. Again, | 153 // The luminance should match the luminance of the foreground text. Again, |
163 // we clamp so as to have at some amount of color (green) in the text. | 154 // we clamp so as to have at some amount of color (green) in the text. |
164 double opposite_l = fg_hsl.l; | 155 double opposite_l = fg_hsl.l; |
165 double l = std::max(0.1, std::min(0.9, opposite_l)); | 156 double l = std::max(0.1, std::min(0.9, opposite_l)); |
166 | 157 |
167 color_utils::HSL output = { hue_hsl.h, s, l }; | 158 color_utils::HSL output = { hue_hsl.h, s, l }; |
168 return gfx::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255)); | 159 return gfx::SkColorToGdkColor(color_utils::HSLToSkColor(output, 255)); |
169 } | 160 } |
170 } // namespace | 161 } // namespace |
171 | 162 |
172 void OmniboxPopupViewGtk::SetupLayoutForMatch( | |
173 PangoLayout* layout, | |
174 const string16& text, | |
175 const AutocompleteMatch::ACMatchClassifications& classifications, | |
176 const GdkColor* base_color, | |
177 const GdkColor* dim_color, | |
178 const GdkColor* url_color, | |
179 const std::string& prefix_text) { | |
180 // In RTL, mark text with left-to-right embedding mark if there is no strong | |
181 // RTL characters inside it, so the ending punctuation displays correctly | |
182 // and the eliding ellipsis displays correctly. We only mark the text with | |
183 // LRE. Wrapping it with LRE and PDF by calling AdjustStringForLocaleDirection | |
184 // or WrapStringWithLTRFormatting will render the elllipsis at the left of the | |
185 // elided pure LTR text. | |
186 bool marked_with_lre = false; | |
187 string16 localized_text = text; | |
188 // Pango is really easy to overflow and send into a computational death | |
189 // spiral that can corrupt the screen. Assume that we'll never have more than | |
190 // 2000 characters, which should be a safe assumption until we all get robot | |
191 // eyes. http://crbug.com/66576 | |
192 if (localized_text.length() > 2000) | |
193 localized_text = localized_text.substr(0, 2000); | |
194 bool is_rtl = base::i18n::IsRTL(); | |
195 if (is_rtl && !base::i18n::StringContainsStrongRTLChars(localized_text)) { | |
196 localized_text.insert(0, 1, base::i18n::kLeftToRightEmbeddingMark); | |
197 marked_with_lre = true; | |
198 } | |
199 | |
200 // We can have a prefix, or insert additional characters while processing the | |
201 // classifications. We need to take this in to account when we translate the | |
202 // UTF-16 offsets in the classification into text_utf8 byte offsets. | |
203 size_t additional_offset = prefix_text.length(); // Length in utf-8 bytes. | |
204 std::string text_utf8 = prefix_text + UTF16ToUTF8(localized_text); | |
205 | |
206 PangoAttrList* attrs = pango_attr_list_new(); | |
207 | |
208 // TODO(deanm): This is a hack, just to handle coloring prefix_text. | |
209 // Hopefully I can clean up the match situation a bit and this will | |
210 // come out cleaner. For now, apply the base color to the whole text | |
211 // so that our prefix will have the base color applied. | |
212 PangoAttribute* base_fg_attr = pango_attr_foreground_new( | |
213 base_color->red, base_color->green, base_color->blue); | |
214 pango_attr_list_insert(attrs, base_fg_attr); // Ownership taken. | |
215 | |
216 // Walk through the classifications, they are linear, in order, and should | |
217 // cover the entire text. We create a bunch of overlapping attributes, | |
218 // extending from the offset to the end of the string. The ones created | |
219 // later will override the previous ones, meaning we will still setup each | |
220 // portion correctly, we just don't need to compute the end offset. | |
221 for (ACMatchClassifications::const_iterator i = classifications.begin(); | |
222 i != classifications.end(); ++i) { | |
223 size_t offset = GetUTF8Offset(localized_text, i->offset) + | |
224 additional_offset; | |
225 | |
226 // TODO(deanm): All the colors should probably blend based on whether this | |
227 // result is selected or not. This would include the green URLs. Right | |
228 // now the caller is left to blend only the base color. Do we need to | |
229 // handle things like DIM urls? Turns out DIM means something different | |
230 // than you'd think, all of the description text is not DIM, it is a | |
231 // special case that is not very common, but we should figure out and | |
232 // support it. | |
233 const GdkColor* color = base_color; | |
234 if (i->style & ACMatchClassification::URL) { | |
235 color = url_color; | |
236 // Insert a left to right embedding to make sure that URLs are shown LTR. | |
237 if (is_rtl && !marked_with_lre) { | |
238 std::string lre(kLRE); | |
239 text_utf8.insert(offset, lre); | |
240 additional_offset += lre.length(); | |
241 } | |
242 } | |
243 | |
244 if (i->style & ACMatchClassification::DIM) | |
245 color = dim_color; | |
246 | |
247 PangoAttribute* fg_attr = pango_attr_foreground_new( | |
248 color->red, color->green, color->blue); | |
249 fg_attr->start_index = offset; | |
250 pango_attr_list_insert(attrs, fg_attr); // Ownership taken. | |
251 | |
252 // Matched portions are bold, otherwise use the normal weight. | |
253 PangoWeight weight = (i->style & ACMatchClassification::MATCH) ? | |
254 PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL; | |
255 PangoAttribute* weight_attr = pango_attr_weight_new(weight); | |
256 weight_attr->start_index = offset; | |
257 pango_attr_list_insert(attrs, weight_attr); // Ownership taken. | |
258 } | |
259 | |
260 pango_layout_set_text(layout, text_utf8.data(), text_utf8.length()); | |
261 pango_layout_set_attributes(layout, attrs); // Ref taken. | |
262 pango_attr_list_unref(attrs); | |
263 } | |
264 | |
265 OmniboxPopupViewGtk::OmniboxPopupViewGtk(const gfx::Font& font, | 163 OmniboxPopupViewGtk::OmniboxPopupViewGtk(const gfx::Font& font, |
266 OmniboxView* omnibox_view, | 164 OmniboxView* omnibox_view, |
267 OmniboxEditModel* edit_model, | 165 OmniboxEditModel* edit_model, |
268 GtkWidget* location_bar) | 166 GtkWidget* location_bar) |
269 : model_(new OmniboxPopupModel(this, edit_model)), | 167 : omnibox_view_(omnibox_view), |
270 omnibox_view_(omnibox_view), | |
271 location_bar_(location_bar), | 168 location_bar_(location_bar), |
272 window_(gtk_window_new(GTK_WINDOW_POPUP)), | 169 window_(gtk_window_new(GTK_WINDOW_POPUP)), |
273 layout_(NULL), | 170 layout_(NULL), |
274 theme_service_(GtkThemeService::GetFrom(edit_model->profile())), | 171 theme_service_(NULL), |
275 font_(font.DeriveFont(kEditFontAdjust)), | 172 font_(font.DeriveFont(kEditFontAdjust)), |
276 ignore_mouse_drag_(false), | 173 ignore_mouse_drag_(false), |
277 opened_(false) { | 174 opened_(false) { |
175 // edit_model may be NULL in unit tests. | |
176 if (edit_model) { | |
177 model_.reset(new OmniboxPopupModel(this, edit_model)); | |
178 theme_service_ = GtkThemeService::GetFrom(edit_model->profile()); | |
179 } | |
180 } | |
181 | |
182 void OmniboxPopupViewGtk::Init() { | |
278 gtk_widget_set_can_focus(window_, FALSE); | 183 gtk_widget_set_can_focus(window_, FALSE); |
279 // Don't allow the window to be resized. This also forces the window to | 184 // Don't allow the window to be resized. This also forces the window to |
280 // shrink down to the size of its child contents. | 185 // shrink down to the size of its child contents. |
281 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); | 186 gtk_window_set_resizable(GTK_WINDOW(window_), FALSE); |
282 gtk_widget_set_app_paintable(window_, TRUE); | 187 gtk_widget_set_app_paintable(window_, TRUE); |
283 // Have GTK double buffer around the expose signal. | 188 // Have GTK double buffer around the expose signal. |
284 gtk_widget_set_double_buffered(window_, TRUE); | 189 gtk_widget_set_double_buffered(window_, TRUE); |
285 | 190 |
286 // Cache the layout so we don't have to create it for every expose. If we | 191 // Cache the layout so we don't have to create it for every expose. If we |
287 // were a real widget we should handle changing directions, but we're not | 192 // were a real widget we should handle changing directions, but we're not |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
321 // r25080 (the original patch that added rounded corners here) should | 226 // r25080 (the original patch that added rounded corners here) should |
322 // eventually be cherry picked once I know what's going | 227 // eventually be cherry picked once I know what's going |
323 // on. http://crbug.com/22015. | 228 // on. http://crbug.com/22015. |
324 } | 229 } |
325 | 230 |
326 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() { | 231 OmniboxPopupViewGtk::~OmniboxPopupViewGtk() { |
327 // Explicitly destroy our model here, before we destroy our GTK widgets. | 232 // Explicitly destroy our model here, before we destroy our GTK widgets. |
328 // This is because the model destructor can call back into us, and we need | 233 // This is because the model destructor can call back into us, and we need |
329 // to make sure everything is still valid when it does. | 234 // to make sure everything is still valid when it does. |
330 model_.reset(); | 235 model_.reset(); |
331 g_object_unref(layout_); | 236 // layout_ may be NULL in unit tests. |
332 gtk_widget_destroy(window_); | 237 if (layout_) { |
238 g_object_unref(layout_); | |
239 gtk_widget_destroy(window_); | |
240 } | |
333 } | 241 } |
334 | 242 |
335 bool OmniboxPopupViewGtk::IsOpen() const { | 243 bool OmniboxPopupViewGtk::IsOpen() const { |
336 return opened_; | 244 return opened_; |
337 } | 245 } |
338 | 246 |
339 void OmniboxPopupViewGtk::InvalidateLine(size_t line) { | 247 void OmniboxPopupViewGtk::InvalidateLine(size_t line) { |
Evan Stade
2013/09/04 23:33:47
perhaps
DCHECK_LE(GetHiddenLineCount(), line);
Jered
2013/09/09 16:26:26
I added an if here to return early (this actually
| |
340 // TODO(deanm): Is it possible to use some constant for the width, instead | 248 // TODO(deanm): Is it possible to use some constant for the width, instead |
341 // of having to query the width of the window? | 249 // of having to query the width of the window? |
342 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); | 250 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); |
343 GdkRectangle line_rect = GetRectForLine( | 251 GdkRectangle line_rect = GetRectForLine( |
344 line, GetWindowRect(gdk_window).width()).ToGdkRectangle(); | 252 line, GetWindowRect(gdk_window).width()).ToGdkRectangle(); |
345 gdk_window_invalidate_rect(gdk_window, &line_rect, FALSE); | 253 gdk_window_invalidate_rect(gdk_window, &line_rect, FALSE); |
346 } | 254 } |
347 | 255 |
348 void OmniboxPopupViewGtk::UpdatePopupAppearance() { | 256 void OmniboxPopupViewGtk::UpdatePopupAppearance() { |
349 const AutocompleteResult& result = model_->result(); | 257 const AutocompleteResult& result = GetResult(); |
350 if (result.empty()) { | 258 const size_t hidden_matches = GetHiddenMatchCount(); |
259 if (result.size() <= hidden_matches) { | |
351 Hide(); | 260 Hide(); |
352 return; | 261 return; |
353 } | 262 } |
354 | 263 |
355 Show(result.size()); | 264 Show(result.size() - hidden_matches); |
356 gtk_widget_queue_draw(window_); | 265 gtk_widget_queue_draw(window_); |
357 } | 266 } |
358 | 267 |
359 gfx::Rect OmniboxPopupViewGtk::GetTargetBounds() { | 268 gfx::Rect OmniboxPopupViewGtk::GetTargetBounds() { |
360 if (!gtk_widget_get_realized(window_)) | 269 if (!gtk_widget_get_realized(window_)) |
361 return gfx::Rect(); | 270 return gfx::Rect(); |
362 | 271 |
363 gfx::Rect retval = ui::GetWidgetScreenBounds(window_); | 272 gfx::Rect retval = ui::GetWidgetScreenBounds(window_); |
364 | 273 |
365 // The widget bounds don't update synchronously so may be out of sync with | 274 // The widget bounds don't update synchronously so may be out of sync with |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
420 gtk_util::AverageColors(content_text_color_, | 329 gtk_util::AverageColors(content_text_color_, |
421 background_color_); | 330 background_color_); |
422 selected_content_dim_text_color_ = | 331 selected_content_dim_text_color_ = |
423 gtk_util::AverageColors(selected_content_text_color_, | 332 gtk_util::AverageColors(selected_content_text_color_, |
424 selected_background_color_); | 333 selected_background_color_); |
425 | 334 |
426 // Set the background color, so we don't need to paint it manually. | 335 // Set the background color, so we don't need to paint it manually. |
427 gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &background_color_); | 336 gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &background_color_); |
428 } | 337 } |
429 | 338 |
339 size_t OmniboxPopupViewGtk::LineFromY(int y) const { | |
340 DCHECK_NE(0U, model_->result().size()); | |
341 size_t line = std::max(y - kBorderThickness, 0) / kHeightPerResult; | |
342 return std::min(line + GetHiddenMatchCount(), GetResult().size() - 1); | |
343 } | |
344 | |
345 gfx::Rect OmniboxPopupViewGtk::GetRectForLine(size_t line, int width) const { | |
346 size_t visible_line = line - GetHiddenMatchCount(); | |
347 return gfx::Rect(kBorderThickness, | |
348 (visible_line * kHeightPerResult) + kBorderThickness, | |
349 width - (kBorderThickness * 2), | |
350 kHeightPerResult); | |
351 } | |
352 | |
353 size_t OmniboxPopupViewGtk::GetHiddenMatchCount() const { | |
354 return GetResult().ShouldHideTopMatch() ? 1 : 0; | |
355 } | |
356 | |
357 const AutocompleteResult& OmniboxPopupViewGtk::GetResult() const { | |
358 return model_->result(); | |
359 } | |
360 | |
361 // static | |
362 void OmniboxPopupViewGtk::SetupLayoutForMatch( | |
363 PangoLayout* layout, | |
364 const string16& text, | |
365 const AutocompleteMatch::ACMatchClassifications& classifications, | |
366 const GdkColor* base_color, | |
367 const GdkColor* dim_color, | |
368 const GdkColor* url_color, | |
369 const std::string& prefix_text) { | |
370 // In RTL, mark text with left-to-right embedding mark if there is no strong | |
371 // RTL characters inside it, so the ending punctuation displays correctly | |
372 // and the eliding ellipsis displays correctly. We only mark the text with | |
373 // LRE. Wrapping it with LRE and PDF by calling AdjustStringForLocaleDirection | |
374 // or WrapStringWithLTRFormatting will render the elllipsis at the left of the | |
375 // elided pure LTR text. | |
376 bool marked_with_lre = false; | |
377 string16 localized_text = text; | |
378 // Pango is really easy to overflow and send into a computational death | |
379 // spiral that can corrupt the screen. Assume that we'll never have more than | |
380 // 2000 characters, which should be a safe assumption until we all get robot | |
381 // eyes. http://crbug.com/66576 | |
382 if (localized_text.length() > 2000) | |
383 localized_text = localized_text.substr(0, 2000); | |
384 bool is_rtl = base::i18n::IsRTL(); | |
385 if (is_rtl && !base::i18n::StringContainsStrongRTLChars(localized_text)) { | |
386 localized_text.insert(0, 1, base::i18n::kLeftToRightEmbeddingMark); | |
387 marked_with_lre = true; | |
388 } | |
389 | |
390 // We can have a prefix, or insert additional characters while processing the | |
391 // classifications. We need to take this in to account when we translate the | |
392 // UTF-16 offsets in the classification into text_utf8 byte offsets. | |
393 size_t additional_offset = prefix_text.length(); // Length in utf-8 bytes. | |
394 std::string text_utf8 = prefix_text + UTF16ToUTF8(localized_text); | |
395 | |
396 PangoAttrList* attrs = pango_attr_list_new(); | |
397 | |
398 // TODO(deanm): This is a hack, just to handle coloring prefix_text. | |
399 // Hopefully I can clean up the match situation a bit and this will | |
400 // come out cleaner. For now, apply the base color to the whole text | |
401 // so that our prefix will have the base color applied. | |
402 PangoAttribute* base_fg_attr = pango_attr_foreground_new( | |
403 base_color->red, base_color->green, base_color->blue); | |
404 pango_attr_list_insert(attrs, base_fg_attr); // Ownership taken. | |
405 | |
406 // Walk through the classifications, they are linear, in order, and should | |
407 // cover the entire text. We create a bunch of overlapping attributes, | |
408 // extending from the offset to the end of the string. The ones created | |
409 // later will override the previous ones, meaning we will still setup each | |
410 // portion correctly, we just don't need to compute the end offset. | |
411 for (ACMatchClassifications::const_iterator i = classifications.begin(); | |
412 i != classifications.end(); ++i) { | |
413 size_t offset = GetUTF8Offset(localized_text, i->offset) + | |
414 additional_offset; | |
415 | |
416 // TODO(deanm): All the colors should probably blend based on whether this | |
417 // result is selected or not. This would include the green URLs. Right | |
418 // now the caller is left to blend only the base color. Do we need to | |
419 // handle things like DIM urls? Turns out DIM means something different | |
420 // than you'd think, all of the description text is not DIM, it is a | |
421 // special case that is not very common, but we should figure out and | |
422 // support it. | |
423 const GdkColor* color = base_color; | |
424 if (i->style & ACMatchClassification::URL) { | |
425 color = url_color; | |
426 // Insert a left to right embedding to make sure that URLs are shown LTR. | |
427 if (is_rtl && !marked_with_lre) { | |
428 std::string lre(kLRE); | |
429 text_utf8.insert(offset, lre); | |
430 additional_offset += lre.length(); | |
431 } | |
432 } | |
433 | |
434 if (i->style & ACMatchClassification::DIM) | |
435 color = dim_color; | |
436 | |
437 PangoAttribute* fg_attr = pango_attr_foreground_new( | |
438 color->red, color->green, color->blue); | |
439 fg_attr->start_index = offset; | |
440 pango_attr_list_insert(attrs, fg_attr); // Ownership taken. | |
441 | |
442 // Matched portions are bold, otherwise use the normal weight. | |
443 PangoWeight weight = (i->style & ACMatchClassification::MATCH) ? | |
444 PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL; | |
445 PangoAttribute* weight_attr = pango_attr_weight_new(weight); | |
446 weight_attr->start_index = offset; | |
447 pango_attr_list_insert(attrs, weight_attr); // Ownership taken. | |
448 } | |
449 | |
450 pango_layout_set_text(layout, text_utf8.data(), text_utf8.length()); | |
451 pango_layout_set_attributes(layout, attrs); // Ref taken. | |
452 pango_attr_list_unref(attrs); | |
453 } | |
454 | |
430 void OmniboxPopupViewGtk::Show(size_t num_results) { | 455 void OmniboxPopupViewGtk::Show(size_t num_results) { |
431 gint origin_x, origin_y; | 456 gint origin_x, origin_y; |
432 GdkWindow* gdk_window = gtk_widget_get_window(location_bar_); | 457 GdkWindow* gdk_window = gtk_widget_get_window(location_bar_); |
433 gdk_window_get_origin(gdk_window, &origin_x, &origin_y); | 458 gdk_window_get_origin(gdk_window, &origin_x, &origin_y); |
434 GtkAllocation allocation; | 459 GtkAllocation allocation; |
435 gtk_widget_get_allocation(location_bar_, &allocation); | 460 gtk_widget_get_allocation(location_bar_, &allocation); |
436 | 461 |
437 int horizontal_offset = 1; | 462 int horizontal_offset = 1; |
438 gtk_window_move(GTK_WINDOW(window_), | 463 gtk_window_move(GTK_WINDOW(window_), |
439 origin_x + allocation.x - kBorderThickness + horizontal_offset, | 464 origin_x + allocation.x - kBorderThickness + horizontal_offset, |
(...skipping 13 matching lines...) Expand all Loading... | |
453 } | 478 } |
454 | 479 |
455 void OmniboxPopupViewGtk::StackWindow() { | 480 void OmniboxPopupViewGtk::StackWindow() { |
456 gfx::NativeView omnibox_view = omnibox_view_->GetNativeView(); | 481 gfx::NativeView omnibox_view = omnibox_view_->GetNativeView(); |
457 DCHECK(GTK_IS_WIDGET(omnibox_view)); | 482 DCHECK(GTK_IS_WIDGET(omnibox_view)); |
458 GtkWidget* toplevel = gtk_widget_get_toplevel(omnibox_view); | 483 GtkWidget* toplevel = gtk_widget_get_toplevel(omnibox_view); |
459 DCHECK(gtk_widget_is_toplevel(toplevel)); | 484 DCHECK(gtk_widget_is_toplevel(toplevel)); |
460 ui::StackPopupWindow(window_, toplevel); | 485 ui::StackPopupWindow(window_, toplevel); |
461 } | 486 } |
462 | 487 |
463 size_t OmniboxPopupViewGtk::LineFromY(int y) { | |
464 DCHECK_NE(0U, model_->result().size()); | |
465 size_t line = std::max(y - kBorderThickness, 0) / kHeightPerResult; | |
466 return std::min(line, model_->result().size() - 1); | |
467 } | |
468 | |
469 void OmniboxPopupViewGtk::AcceptLine(size_t line, | 488 void OmniboxPopupViewGtk::AcceptLine(size_t line, |
470 WindowOpenDisposition disposition) { | 489 WindowOpenDisposition disposition) { |
471 // OpenMatch() may close the popup, which will clear the result set and, by | 490 // OpenMatch() may close the popup, which will clear the result set and, by |
472 // extension, |match| and its contents. So copy the relevant match out to | 491 // extension, |match| and its contents. So copy the relevant match out to |
473 // make sure it stays alive until the call completes. | 492 // make sure it stays alive until the call completes. |
474 AutocompleteMatch match = model_->result().match_at(line); | 493 AutocompleteMatch match = GetResult().match_at(line); |
475 omnibox_view_->OpenMatch(match, disposition, GURL(), line); | 494 omnibox_view_->OpenMatch(match, disposition, GURL(), line); |
476 } | 495 } |
477 | 496 |
478 gfx::Image OmniboxPopupViewGtk::IconForMatch( | 497 gfx::Image OmniboxPopupViewGtk::IconForMatch( |
479 const AutocompleteMatch& match, | 498 const AutocompleteMatch& match, |
480 bool selected, | 499 bool selected, |
481 bool is_selected_keyword) { | 500 bool is_selected_keyword) { |
482 const gfx::Image image = model_->GetIconIfExtensionMatch(match); | 501 const gfx::Image image = model_->GetIconIfExtensionMatch(match); |
483 if (!image.IsEmpty()) | 502 if (!image.IsEmpty()) |
484 return image; | 503 return image; |
(...skipping 29 matching lines...) Expand all Loading... | |
514 } | 533 } |
515 } | 534 } |
516 | 535 |
517 return theme_service_->GetImageNamed(icon); | 536 return theme_service_->GetImageNamed(icon); |
518 } | 537 } |
519 | 538 |
520 void OmniboxPopupViewGtk::GetVisibleMatchForInput( | 539 void OmniboxPopupViewGtk::GetVisibleMatchForInput( |
521 size_t index, | 540 size_t index, |
522 const AutocompleteMatch** match, | 541 const AutocompleteMatch** match, |
523 bool* is_selected_keyword) { | 542 bool* is_selected_keyword) { |
524 const AutocompleteResult& result = model_->result(); | 543 const AutocompleteResult& result = GetResult(); |
525 | 544 |
526 if (result.match_at(index).associated_keyword.get() && | 545 if (result.match_at(index).associated_keyword.get() && |
527 model_->selected_line() == index && | 546 model_->selected_line() == index && |
528 model_->selected_line_state() == OmniboxPopupModel::KEYWORD) { | 547 model_->selected_line_state() == OmniboxPopupModel::KEYWORD) { |
529 *match = result.match_at(index).associated_keyword.get(); | 548 *match = result.match_at(index).associated_keyword.get(); |
530 *is_selected_keyword = true; | 549 *is_selected_keyword = true; |
531 return; | 550 return; |
532 } | 551 } |
533 | 552 |
534 *match = &result.match_at(index); | 553 *match = &result.match_at(index); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
587 default: | 606 default: |
588 // Don't open the result. | 607 // Don't open the result. |
589 break; | 608 break; |
590 } | 609 } |
591 return TRUE; | 610 return TRUE; |
592 } | 611 } |
593 | 612 |
594 gboolean OmniboxPopupViewGtk::HandleExpose(GtkWidget* widget, | 613 gboolean OmniboxPopupViewGtk::HandleExpose(GtkWidget* widget, |
595 GdkEventExpose* event) { | 614 GdkEventExpose* event) { |
596 bool ltr = !base::i18n::IsRTL(); | 615 bool ltr = !base::i18n::IsRTL(); |
597 const AutocompleteResult& result = model_->result(); | 616 const AutocompleteResult& result = GetResult(); |
598 | 617 |
599 gfx::Rect window_rect = GetWindowRect(event->window); | 618 gfx::Rect window_rect = GetWindowRect(event->window); |
600 gfx::Rect damage_rect = gfx::Rect(event->area); | 619 gfx::Rect damage_rect = gfx::Rect(event->area); |
601 // Handle when our window is super narrow. A bunch of the calculations | 620 // Handle when our window is super narrow. A bunch of the calculations |
602 // below would go negative, and really we're not going to fit anything | 621 // below would go negative, and really we're not going to fit anything |
603 // useful in such a small window anyway. Just don't paint anything. | 622 // useful in such a small window anyway. Just don't paint anything. |
604 // This means we won't draw the border, but, yeah, whatever. | 623 // This means we won't draw the border, but, yeah, whatever. |
605 // TODO(deanm): Make the code more robust and remove this check. | 624 // TODO(deanm): Make the code more robust and remove this check. |
606 if (window_rect.width() < (kIconAreaWidth * 3)) | 625 if (window_rect.width() < (kIconAreaWidth * 3)) |
607 return TRUE; | 626 return TRUE; |
608 | 627 |
609 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); | 628 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); |
610 gdk_cairo_rectangle(cr, &event->area); | 629 gdk_cairo_rectangle(cr, &event->area); |
611 cairo_clip(cr); | 630 cairo_clip(cr); |
612 | 631 |
613 // This assert is kinda ugly, but it would be more currently unneeded work | 632 // This assert is kinda ugly, but it would be more currently unneeded work |
614 // to support painting a border that isn't 1 pixel thick. There is no point | 633 // to support painting a border that isn't 1 pixel thick. There is no point |
615 // in writing that code now, and explode if that day ever comes. | 634 // in writing that code now, and explode if that day ever comes. |
616 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied); | 635 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied); |
617 // Draw the 1px border around the entire window. | 636 // Draw the 1px border around the entire window. |
618 gdk_cairo_set_source_color(cr, &border_color_); | 637 gdk_cairo_set_source_color(cr, &border_color_); |
619 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height()); | 638 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height()); |
620 cairo_stroke(cr); | 639 cairo_stroke(cr); |
621 | 640 |
622 pango_layout_set_height(layout_, kHeightPerResult * PANGO_SCALE); | 641 pango_layout_set_height(layout_, kHeightPerResult * PANGO_SCALE); |
623 | 642 |
624 for (size_t i = 0; i < result.size(); ++i) { | 643 for (size_t i = GetHiddenMatchCount(); i < result.size(); ++i) { |
625 gfx::Rect line_rect = GetRectForLine(i, window_rect.width()); | 644 gfx::Rect line_rect = GetRectForLine(i, window_rect.width()); |
626 // Only repaint and layout damaged lines. | 645 // Only repaint and layout damaged lines. |
627 if (!line_rect.Intersects(damage_rect)) | 646 if (!line_rect.Intersects(damage_rect)) |
628 continue; | 647 continue; |
629 | 648 |
630 const AutocompleteMatch* match = NULL; | 649 const AutocompleteMatch* match = NULL; |
631 bool is_selected_keyword = false; | 650 bool is_selected_keyword = false; |
632 GetVisibleMatchForInput(i, &match, &is_selected_keyword); | 651 GetVisibleMatchForInput(i, &match, &is_selected_keyword); |
633 bool is_selected = (model_->selected_line() == i); | 652 bool is_selected = (model_->selected_line() == i); |
634 bool is_hovered = (model_->hovered_line() == i); | 653 bool is_hovered = (model_->hovered_line() == i); |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
725 theme_service_->GetImageNamed( | 744 theme_service_->GetImageNamed( |
726 is_selected ? IDR_OMNIBOX_TTS_DARK : | 745 is_selected ? IDR_OMNIBOX_TTS_DARK : |
727 IDR_OMNIBOX_TTS), | 746 IDR_OMNIBOX_TTS), |
728 icon_start_x, line_rect.y() + kIconTopPadding); | 747 icon_start_x, line_rect.y() + kIconTopPadding); |
729 } | 748 } |
730 } | 749 } |
731 | 750 |
732 cairo_destroy(cr); | 751 cairo_destroy(cr); |
733 return TRUE; | 752 return TRUE; |
734 } | 753 } |
OLD | NEW |