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