Chromium Code Reviews| 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 |