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) { |
| 248 if (line < GetHiddenMatchCount()) |
| 249 return; |
340 // TODO(deanm): Is it possible to use some constant for the width, instead | 250 // TODO(deanm): Is it possible to use some constant for the width, instead |
341 // of having to query the width of the window? | 251 // of having to query the width of the window? |
342 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); | 252 GdkWindow* gdk_window = gtk_widget_get_window(GTK_WIDGET(window_)); |
343 GdkRectangle line_rect = GetRectForLine( | 253 GdkRectangle line_rect = GetRectForLine( |
344 line, GetWindowRect(gdk_window).width()).ToGdkRectangle(); | 254 line, GetWindowRect(gdk_window).width()).ToGdkRectangle(); |
345 gdk_window_invalidate_rect(gdk_window, &line_rect, FALSE); | 255 gdk_window_invalidate_rect(gdk_window, &line_rect, FALSE); |
346 } | 256 } |
347 | 257 |
348 void OmniboxPopupViewGtk::UpdatePopupAppearance() { | 258 void OmniboxPopupViewGtk::UpdatePopupAppearance() { |
349 const AutocompleteResult& result = model_->result(); | 259 const AutocompleteResult& result = GetResult(); |
350 if (result.empty()) { | 260 const size_t hidden_matches = GetHiddenMatchCount(); |
| 261 if (result.size() <= hidden_matches) { |
351 Hide(); | 262 Hide(); |
352 return; | 263 return; |
353 } | 264 } |
354 | 265 |
355 Show(result.size()); | 266 Show(result.size() - hidden_matches); |
356 gtk_widget_queue_draw(window_); | 267 gtk_widget_queue_draw(window_); |
357 } | 268 } |
358 | 269 |
359 gfx::Rect OmniboxPopupViewGtk::GetTargetBounds() { | 270 gfx::Rect OmniboxPopupViewGtk::GetTargetBounds() { |
360 if (!gtk_widget_get_realized(window_)) | 271 if (!gtk_widget_get_realized(window_)) |
361 return gfx::Rect(); | 272 return gfx::Rect(); |
362 | 273 |
363 gfx::Rect retval = ui::GetWidgetScreenBounds(window_); | 274 gfx::Rect retval = ui::GetWidgetScreenBounds(window_); |
364 | 275 |
365 // The widget bounds don't update synchronously so may be out of sync with | 276 // 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_, | 331 gtk_util::AverageColors(content_text_color_, |
421 background_color_); | 332 background_color_); |
422 selected_content_dim_text_color_ = | 333 selected_content_dim_text_color_ = |
423 gtk_util::AverageColors(selected_content_text_color_, | 334 gtk_util::AverageColors(selected_content_text_color_, |
424 selected_background_color_); | 335 selected_background_color_); |
425 | 336 |
426 // Set the background color, so we don't need to paint it manually. | 337 // Set the background color, so we don't need to paint it manually. |
427 gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &background_color_); | 338 gtk_widget_modify_bg(window_, GTK_STATE_NORMAL, &background_color_); |
428 } | 339 } |
429 | 340 |
| 341 size_t OmniboxPopupViewGtk::LineFromY(int y) const { |
| 342 // model_ may be NULL in unit tests. |
| 343 if (model_) |
| 344 DCHECK_NE(0U, model_->result().size()); |
| 345 size_t line = std::max(y - kBorderThickness, 0) / kHeightPerResult; |
| 346 return std::min(line + GetHiddenMatchCount(), GetResult().size() - 1); |
| 347 } |
| 348 |
| 349 gfx::Rect OmniboxPopupViewGtk::GetRectForLine(size_t line, int width) const { |
| 350 size_t visible_line = line - GetHiddenMatchCount(); |
| 351 return gfx::Rect(kBorderThickness, |
| 352 (visible_line * kHeightPerResult) + kBorderThickness, |
| 353 width - (kBorderThickness * 2), |
| 354 kHeightPerResult); |
| 355 } |
| 356 |
| 357 size_t OmniboxPopupViewGtk::GetHiddenMatchCount() const { |
| 358 return GetResult().ShouldHideTopMatch() ? 1 : 0; |
| 359 } |
| 360 |
| 361 const AutocompleteResult& OmniboxPopupViewGtk::GetResult() const { |
| 362 return model_->result(); |
| 363 } |
| 364 |
| 365 // static |
| 366 void OmniboxPopupViewGtk::SetupLayoutForMatch( |
| 367 PangoLayout* layout, |
| 368 const string16& text, |
| 369 const AutocompleteMatch::ACMatchClassifications& classifications, |
| 370 const GdkColor* base_color, |
| 371 const GdkColor* dim_color, |
| 372 const GdkColor* url_color, |
| 373 const std::string& prefix_text) { |
| 374 // In RTL, mark text with left-to-right embedding mark if there is no strong |
| 375 // RTL characters inside it, so the ending punctuation displays correctly |
| 376 // and the eliding ellipsis displays correctly. We only mark the text with |
| 377 // LRE. Wrapping it with LRE and PDF by calling AdjustStringForLocaleDirection |
| 378 // or WrapStringWithLTRFormatting will render the elllipsis at the left of the |
| 379 // elided pure LTR text. |
| 380 bool marked_with_lre = false; |
| 381 string16 localized_text = text; |
| 382 // Pango is really easy to overflow and send into a computational death |
| 383 // spiral that can corrupt the screen. Assume that we'll never have more than |
| 384 // 2000 characters, which should be a safe assumption until we all get robot |
| 385 // eyes. http://crbug.com/66576 |
| 386 if (localized_text.length() > 2000) |
| 387 localized_text = localized_text.substr(0, 2000); |
| 388 bool is_rtl = base::i18n::IsRTL(); |
| 389 if (is_rtl && !base::i18n::StringContainsStrongRTLChars(localized_text)) { |
| 390 localized_text.insert(0, 1, base::i18n::kLeftToRightEmbeddingMark); |
| 391 marked_with_lre = true; |
| 392 } |
| 393 |
| 394 // We can have a prefix, or insert additional characters while processing the |
| 395 // classifications. We need to take this in to account when we translate the |
| 396 // UTF-16 offsets in the classification into text_utf8 byte offsets. |
| 397 size_t additional_offset = prefix_text.length(); // Length in utf-8 bytes. |
| 398 std::string text_utf8 = prefix_text + UTF16ToUTF8(localized_text); |
| 399 |
| 400 PangoAttrList* attrs = pango_attr_list_new(); |
| 401 |
| 402 // TODO(deanm): This is a hack, just to handle coloring prefix_text. |
| 403 // Hopefully I can clean up the match situation a bit and this will |
| 404 // come out cleaner. For now, apply the base color to the whole text |
| 405 // so that our prefix will have the base color applied. |
| 406 PangoAttribute* base_fg_attr = pango_attr_foreground_new( |
| 407 base_color->red, base_color->green, base_color->blue); |
| 408 pango_attr_list_insert(attrs, base_fg_attr); // Ownership taken. |
| 409 |
| 410 // Walk through the classifications, they are linear, in order, and should |
| 411 // cover the entire text. We create a bunch of overlapping attributes, |
| 412 // extending from the offset to the end of the string. The ones created |
| 413 // later will override the previous ones, meaning we will still setup each |
| 414 // portion correctly, we just don't need to compute the end offset. |
| 415 for (ACMatchClassifications::const_iterator i = classifications.begin(); |
| 416 i != classifications.end(); ++i) { |
| 417 size_t offset = GetUTF8Offset(localized_text, i->offset) + |
| 418 additional_offset; |
| 419 |
| 420 // TODO(deanm): All the colors should probably blend based on whether this |
| 421 // result is selected or not. This would include the green URLs. Right |
| 422 // now the caller is left to blend only the base color. Do we need to |
| 423 // handle things like DIM urls? Turns out DIM means something different |
| 424 // than you'd think, all of the description text is not DIM, it is a |
| 425 // special case that is not very common, but we should figure out and |
| 426 // support it. |
| 427 const GdkColor* color = base_color; |
| 428 if (i->style & ACMatchClassification::URL) { |
| 429 color = url_color; |
| 430 // Insert a left to right embedding to make sure that URLs are shown LTR. |
| 431 if (is_rtl && !marked_with_lre) { |
| 432 std::string lre(kLRE); |
| 433 text_utf8.insert(offset, lre); |
| 434 additional_offset += lre.length(); |
| 435 } |
| 436 } |
| 437 |
| 438 if (i->style & ACMatchClassification::DIM) |
| 439 color = dim_color; |
| 440 |
| 441 PangoAttribute* fg_attr = pango_attr_foreground_new( |
| 442 color->red, color->green, color->blue); |
| 443 fg_attr->start_index = offset; |
| 444 pango_attr_list_insert(attrs, fg_attr); // Ownership taken. |
| 445 |
| 446 // Matched portions are bold, otherwise use the normal weight. |
| 447 PangoWeight weight = (i->style & ACMatchClassification::MATCH) ? |
| 448 PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL; |
| 449 PangoAttribute* weight_attr = pango_attr_weight_new(weight); |
| 450 weight_attr->start_index = offset; |
| 451 pango_attr_list_insert(attrs, weight_attr); // Ownership taken. |
| 452 } |
| 453 |
| 454 pango_layout_set_text(layout, text_utf8.data(), text_utf8.length()); |
| 455 pango_layout_set_attributes(layout, attrs); // Ref taken. |
| 456 pango_attr_list_unref(attrs); |
| 457 } |
| 458 |
430 void OmniboxPopupViewGtk::Show(size_t num_results) { | 459 void OmniboxPopupViewGtk::Show(size_t num_results) { |
431 gint origin_x, origin_y; | 460 gint origin_x, origin_y; |
432 GdkWindow* gdk_window = gtk_widget_get_window(location_bar_); | 461 GdkWindow* gdk_window = gtk_widget_get_window(location_bar_); |
433 gdk_window_get_origin(gdk_window, &origin_x, &origin_y); | 462 gdk_window_get_origin(gdk_window, &origin_x, &origin_y); |
434 GtkAllocation allocation; | 463 GtkAllocation allocation; |
435 gtk_widget_get_allocation(location_bar_, &allocation); | 464 gtk_widget_get_allocation(location_bar_, &allocation); |
436 | 465 |
437 int horizontal_offset = 1; | 466 int horizontal_offset = 1; |
438 gtk_window_move(GTK_WINDOW(window_), | 467 gtk_window_move(GTK_WINDOW(window_), |
439 origin_x + allocation.x - kBorderThickness + horizontal_offset, | 468 origin_x + allocation.x - kBorderThickness + horizontal_offset, |
(...skipping 13 matching lines...) Expand all Loading... |
453 } | 482 } |
454 | 483 |
455 void OmniboxPopupViewGtk::StackWindow() { | 484 void OmniboxPopupViewGtk::StackWindow() { |
456 gfx::NativeView omnibox_view = omnibox_view_->GetNativeView(); | 485 gfx::NativeView omnibox_view = omnibox_view_->GetNativeView(); |
457 DCHECK(GTK_IS_WIDGET(omnibox_view)); | 486 DCHECK(GTK_IS_WIDGET(omnibox_view)); |
458 GtkWidget* toplevel = gtk_widget_get_toplevel(omnibox_view); | 487 GtkWidget* toplevel = gtk_widget_get_toplevel(omnibox_view); |
459 DCHECK(gtk_widget_is_toplevel(toplevel)); | 488 DCHECK(gtk_widget_is_toplevel(toplevel)); |
460 ui::StackPopupWindow(window_, toplevel); | 489 ui::StackPopupWindow(window_, toplevel); |
461 } | 490 } |
462 | 491 |
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, | 492 void OmniboxPopupViewGtk::AcceptLine(size_t line, |
470 WindowOpenDisposition disposition) { | 493 WindowOpenDisposition disposition) { |
471 // OpenMatch() may close the popup, which will clear the result set and, by | 494 // 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 | 495 // extension, |match| and its contents. So copy the relevant match out to |
473 // make sure it stays alive until the call completes. | 496 // make sure it stays alive until the call completes. |
474 AutocompleteMatch match = model_->result().match_at(line); | 497 AutocompleteMatch match = GetResult().match_at(line); |
475 omnibox_view_->OpenMatch(match, disposition, GURL(), line); | 498 omnibox_view_->OpenMatch(match, disposition, GURL(), line); |
476 } | 499 } |
477 | 500 |
478 gfx::Image OmniboxPopupViewGtk::IconForMatch( | 501 gfx::Image OmniboxPopupViewGtk::IconForMatch( |
479 const AutocompleteMatch& match, | 502 const AutocompleteMatch& match, |
480 bool selected, | 503 bool selected, |
481 bool is_selected_keyword) { | 504 bool is_selected_keyword) { |
482 const gfx::Image image = model_->GetIconIfExtensionMatch(match); | 505 const gfx::Image image = model_->GetIconIfExtensionMatch(match); |
483 if (!image.IsEmpty()) | 506 if (!image.IsEmpty()) |
484 return image; | 507 return image; |
(...skipping 29 matching lines...) Expand all Loading... |
514 } | 537 } |
515 } | 538 } |
516 | 539 |
517 return theme_service_->GetImageNamed(icon); | 540 return theme_service_->GetImageNamed(icon); |
518 } | 541 } |
519 | 542 |
520 void OmniboxPopupViewGtk::GetVisibleMatchForInput( | 543 void OmniboxPopupViewGtk::GetVisibleMatchForInput( |
521 size_t index, | 544 size_t index, |
522 const AutocompleteMatch** match, | 545 const AutocompleteMatch** match, |
523 bool* is_selected_keyword) { | 546 bool* is_selected_keyword) { |
524 const AutocompleteResult& result = model_->result(); | 547 const AutocompleteResult& result = GetResult(); |
525 | 548 |
526 if (result.match_at(index).associated_keyword.get() && | 549 if (result.match_at(index).associated_keyword.get() && |
527 model_->selected_line() == index && | 550 model_->selected_line() == index && |
528 model_->selected_line_state() == OmniboxPopupModel::KEYWORD) { | 551 model_->selected_line_state() == OmniboxPopupModel::KEYWORD) { |
529 *match = result.match_at(index).associated_keyword.get(); | 552 *match = result.match_at(index).associated_keyword.get(); |
530 *is_selected_keyword = true; | 553 *is_selected_keyword = true; |
531 return; | 554 return; |
532 } | 555 } |
533 | 556 |
534 *match = &result.match_at(index); | 557 *match = &result.match_at(index); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
587 default: | 610 default: |
588 // Don't open the result. | 611 // Don't open the result. |
589 break; | 612 break; |
590 } | 613 } |
591 return TRUE; | 614 return TRUE; |
592 } | 615 } |
593 | 616 |
594 gboolean OmniboxPopupViewGtk::HandleExpose(GtkWidget* widget, | 617 gboolean OmniboxPopupViewGtk::HandleExpose(GtkWidget* widget, |
595 GdkEventExpose* event) { | 618 GdkEventExpose* event) { |
596 bool ltr = !base::i18n::IsRTL(); | 619 bool ltr = !base::i18n::IsRTL(); |
597 const AutocompleteResult& result = model_->result(); | 620 const AutocompleteResult& result = GetResult(); |
598 | 621 |
599 gfx::Rect window_rect = GetWindowRect(event->window); | 622 gfx::Rect window_rect = GetWindowRect(event->window); |
600 gfx::Rect damage_rect = gfx::Rect(event->area); | 623 gfx::Rect damage_rect = gfx::Rect(event->area); |
601 // Handle when our window is super narrow. A bunch of the calculations | 624 // 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 | 625 // 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. | 626 // useful in such a small window anyway. Just don't paint anything. |
604 // This means we won't draw the border, but, yeah, whatever. | 627 // This means we won't draw the border, but, yeah, whatever. |
605 // TODO(deanm): Make the code more robust and remove this check. | 628 // TODO(deanm): Make the code more robust and remove this check. |
606 if (window_rect.width() < (kIconAreaWidth * 3)) | 629 if (window_rect.width() < (kIconAreaWidth * 3)) |
607 return TRUE; | 630 return TRUE; |
608 | 631 |
609 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); | 632 cairo_t* cr = gdk_cairo_create(gtk_widget_get_window(widget)); |
610 gdk_cairo_rectangle(cr, &event->area); | 633 gdk_cairo_rectangle(cr, &event->area); |
611 cairo_clip(cr); | 634 cairo_clip(cr); |
612 | 635 |
613 // This assert is kinda ugly, but it would be more currently unneeded work | 636 // 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 | 637 // 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. | 638 // in writing that code now, and explode if that day ever comes. |
616 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied); | 639 COMPILE_ASSERT(kBorderThickness == 1, border_1px_implied); |
617 // Draw the 1px border around the entire window. | 640 // Draw the 1px border around the entire window. |
618 gdk_cairo_set_source_color(cr, &border_color_); | 641 gdk_cairo_set_source_color(cr, &border_color_); |
619 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height()); | 642 cairo_rectangle(cr, 0, 0, window_rect.width(), window_rect.height()); |
620 cairo_stroke(cr); | 643 cairo_stroke(cr); |
621 | 644 |
622 pango_layout_set_height(layout_, kHeightPerResult * PANGO_SCALE); | 645 pango_layout_set_height(layout_, kHeightPerResult * PANGO_SCALE); |
623 | 646 |
624 for (size_t i = 0; i < result.size(); ++i) { | 647 for (size_t i = GetHiddenMatchCount(); i < result.size(); ++i) { |
625 gfx::Rect line_rect = GetRectForLine(i, window_rect.width()); | 648 gfx::Rect line_rect = GetRectForLine(i, window_rect.width()); |
626 // Only repaint and layout damaged lines. | 649 // Only repaint and layout damaged lines. |
627 if (!line_rect.Intersects(damage_rect)) | 650 if (!line_rect.Intersects(damage_rect)) |
628 continue; | 651 continue; |
629 | 652 |
630 const AutocompleteMatch* match = NULL; | 653 const AutocompleteMatch* match = NULL; |
631 bool is_selected_keyword = false; | 654 bool is_selected_keyword = false; |
632 GetVisibleMatchForInput(i, &match, &is_selected_keyword); | 655 GetVisibleMatchForInput(i, &match, &is_selected_keyword); |
633 bool is_selected = (model_->selected_line() == i); | 656 bool is_selected = (model_->selected_line() == i); |
634 bool is_hovered = (model_->hovered_line() == i); | 657 bool is_hovered = (model_->hovered_line() == i); |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
725 theme_service_->GetImageNamed( | 748 theme_service_->GetImageNamed( |
726 is_selected ? IDR_OMNIBOX_TTS_DARK : | 749 is_selected ? IDR_OMNIBOX_TTS_DARK : |
727 IDR_OMNIBOX_TTS), | 750 IDR_OMNIBOX_TTS), |
728 icon_start_x, line_rect.y() + kIconTopPadding); | 751 icon_start_x, line_rect.y() + kIconTopPadding); |
729 } | 752 } |
730 } | 753 } |
731 | 754 |
732 cairo_destroy(cr); | 755 cairo_destroy(cr); |
733 return TRUE; | 756 return TRUE; |
734 } | 757 } |
OLD | NEW |