Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // For WinDDK ATL compatibility, these ATL headers must come first. | 5 // For WinDDK ATL compatibility, these ATL headers must come first. |
| 6 #include "build/build_config.h" | 6 #include "build/build_config.h" |
| 7 #if defined(OS_WIN) | 7 #if defined(OS_WIN) |
| 8 #include <atlbase.h> // NOLINT | 8 #include <atlbase.h> // NOLINT |
| 9 #include <atlwin.h> // NOLINT | 9 #include <atlwin.h> // NOLINT |
| 10 #endif | 10 #endif |
| 11 | 11 |
| 12 #include "chrome/browser/ui/views/autocomplete/autocomplete_result_view.h" | 12 #include "chrome/browser/ui/views/autocomplete/autocomplete_result_view.h" |
| 13 | 13 |
| 14 #include <algorithm> // NOLINT | 14 #include <algorithm> // NOLINT |
| 15 | 15 |
| 16 #include "base/i18n/bidi_line_iterator.h" | 16 #include "base/i18n/bidi_line_iterator.h" |
| 17 #include "chrome/browser/extensions/extension_service.h" | |
| 18 #include "chrome/browser/search_engines/template_url.h" | |
| 19 #include "chrome/browser/search_engines/template_url_model.h" | |
| 17 #include "chrome/browser/ui/views/autocomplete/autocomplete_result_view_model.h" | 20 #include "chrome/browser/ui/views/autocomplete/autocomplete_result_view_model.h" |
| 18 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" | 21 #include "chrome/browser/ui/views/location_bar/location_bar_view.h" |
| 19 #include "grit/generated_resources.h" | 22 #include "grit/generated_resources.h" |
| 20 #include "grit/theme_resources.h" | 23 #include "grit/theme_resources.h" |
| 21 #include "ui/base/l10n/l10n_util.h" | 24 #include "ui/base/l10n/l10n_util.h" |
| 22 #include "ui/base/resource/resource_bundle.h" | 25 #include "ui/base/resource/resource_bundle.h" |
| 23 #include "ui/base/text/text_elider.h" | 26 #include "ui/base/text/text_elider.h" |
| 24 #include "ui/gfx/canvas_skia.h" | 27 #include "ui/gfx/canvas_skia.h" |
| 25 #include "ui/gfx/color_utils.h" | 28 #include "ui/gfx/color_utils.h" |
| 29 #include "views/controls/image_view.h" | |
| 30 #include "views/controls/label.h" | |
| 26 | 31 |
| 27 #if defined(OS_LINUX) | 32 #if defined(OS_LINUX) |
| 28 #include "chrome/browser/ui/gtk/gtk_util.h" | 33 #include "chrome/browser/ui/gtk/gtk_util.h" |
| 29 #include "ui/gfx/skia_utils_gtk.h" | 34 #include "ui/gfx/skia_utils_gtk.h" |
| 30 #endif | 35 #endif |
| 31 | 36 |
| 32 namespace { | 37 namespace { |
| 33 | 38 |
| 34 const char16 kEllipsis[] = { 0x2026 }; | 39 const char16 kEllipsis[] = { 0x2026 }; |
| 35 | 40 |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 99 return right_ - x; | 104 return right_ - x; |
| 100 } | 105 } |
| 101 | 106 |
| 102 private: | 107 private: |
| 103 int center_; | 108 int center_; |
| 104 int right_; | 109 int right_; |
| 105 | 110 |
| 106 DISALLOW_COPY_AND_ASSIGN(MirroringContext); | 111 DISALLOW_COPY_AND_ASSIGN(MirroringContext); |
| 107 }; | 112 }; |
| 108 | 113 |
| 114 class AutocompleteResultView::IconLabelView : public views::View { | |
| 115 static const int kLineOffset = 1; | |
|
Peter Kasting
2011/04/07 20:19:21
Nit: Declare in private section, or in the functio
| |
| 116 static const int kLinePadding = 2; | |
| 117 static const int kEdgeOffset = 2; | |
| 118 static const int kIconPadding = 3; | |
| 119 static const int kLabelEdgePadding = 5; | |
| 120 | |
| 121 public: | |
| 122 IconLabelView::IconLabelView(gfx::Font font) { | |
|
Peter Kasting
2011/04/07 20:19:21
Nit: Write definitions out-of-line, even for class
| |
| 123 image_ = new views::ImageView(); | |
| 124 AddChildView(image_); | |
| 125 image_->SetImage( | |
| 126 ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_OMNIBOX_SEARCH)); | |
| 127 label_ = new views::Label(); | |
| 128 AddChildView(label_); | |
| 129 label_->SetFont(font); | |
| 130 label_->SetHorizontalAlignment(views::Label::ALIGN_LEFT); | |
| 131 } | |
| 132 | |
| 133 virtual void Layout() { | |
| 134 const int icon_width = image_->GetPreferredSize().width(); | |
| 135 image_->SetBounds(kEdgeOffset + kIconPadding, (height() - icon_width) / 2, | |
| 136 icon_width, icon_width); | |
| 137 const int label_padding = icon_width + kEdgeOffset + kIconPadding + | |
| 138 LocationBarView::kItemPadding; | |
| 139 const int label_height = label_->GetPreferredSize().height(); | |
| 140 label_->SetBounds(label_padding, (height() - label_height) / 2, width() - | |
| 141 label_padding, label_height); | |
| 142 } | |
| 143 | |
| 144 virtual gfx::Size GetPreferredSize() { | |
| 145 gfx::Size size(image_->GetPreferredSize()); | |
| 146 size.Enlarge(label_->GetPreferredSize().width() + kLabelEdgePadding + | |
| 147 kEdgeOffset + kIconPadding + LocationBarView::kItemPadding, 0); | |
| 148 return size; | |
| 149 } | |
| 150 | |
| 151 virtual void OnPaint(gfx::Canvas* canvas) { | |
| 152 if (selected) { | |
| 153 canvas->Save(); | |
| 154 canvas->ClipRectInt(kEdgeOffset, 0, width() - kEdgeOffset, height()); | |
| 155 canvas->AsCanvasSkia()->drawColor(GetColor(SELECTED, BACKGROUND)); | |
| 156 canvas->Restore(); | |
| 157 } else { | |
| 158 canvas->AsCanvasSkia()->drawColor(GetColor(NORMAL, BACKGROUND)); | |
| 159 } | |
| 160 | |
| 161 SkPaint paint; | |
| 162 paint.setColor(GetColor(NORMAL, DIMMED_TEXT)); | |
| 163 paint.setStrokeWidth(SkIntToScalar(1)); | |
| 164 paint.setAntiAlias(true); | |
| 165 | |
| 166 canvas->AsCanvasSkia()->drawLine(SkIntToScalar(kLineOffset), | |
| 167 SkIntToScalar(kLinePadding), SkIntToScalar(kLineOffset), | |
| 168 SkIntToScalar(height() - kLinePadding * 2), paint); | |
| 169 } | |
| 170 | |
| 171 void SetLabelColor(SkColor color) { | |
| 172 label_->SetColor(color); | |
| 173 } | |
| 174 | |
| 175 void SetLabelText(const string16& text) { | |
| 176 label_->SetText(UTF16ToWide(text)); | |
| 177 } | |
| 178 | |
| 179 void SetSelected(bool flag) { | |
| 180 selected = flag; | |
| 181 | |
| 182 if (selected) | |
| 183 image_->SetImage( | |
| 184 ResourceBundle::GetSharedInstance(). | |
| 185 GetBitmapNamed(IDR_OMNIBOX_SEARCH_SELECTED)); | |
| 186 else | |
| 187 image_->SetImage( | |
| 188 ResourceBundle::GetSharedInstance().GetBitmapNamed(IDR_OMNIBOX_SEARCH)); | |
| 189 | |
| 190 SchedulePaint(); | |
| 191 } | |
| 192 | |
| 193 private: | |
| 194 views::ImageView* image_; | |
| 195 views::Label* label_; | |
| 196 bool selected; | |
| 197 }; | |
| 198 | |
| 109 AutocompleteResultView::AutocompleteResultView( | 199 AutocompleteResultView::AutocompleteResultView( |
| 110 AutocompleteResultViewModel* model, | 200 AutocompleteResultViewModel* model, |
| 111 int model_index, | 201 int model_index, |
| 112 const gfx::Font& font, | 202 const gfx::Font& font, |
| 113 const gfx::Font& bold_font) | 203 const gfx::Font& bold_font) |
| 114 : icon_vertical_padding_(kMinimumIconVerticalPadding), | 204 : icon_vertical_padding_(kMinimumIconVerticalPadding), |
| 115 text_vertical_padding_(kMinimumTextVerticalPadding), | 205 text_vertical_padding_(kMinimumTextVerticalPadding), |
| 116 model_(model), | 206 model_(model), |
| 117 model_index_(model_index), | 207 model_index_(model_index), |
| 118 normal_font_(font), | 208 normal_font_(font), |
| 119 bold_font_(bold_font), | 209 bold_font_(bold_font), |
| 120 ellipsis_width_(font.GetStringWidth(string16(kEllipsis))), | 210 ellipsis_width_(font.GetStringWidth(string16(kEllipsis))), |
| 121 mirroring_context_(new MirroringContext()), | 211 mirroring_context_(new MirroringContext()), |
| 122 match_(NULL, 0, false, AutocompleteMatch::URL_WHAT_YOU_TYPED) { | 212 match_(NULL, 0, false, AutocompleteMatch::URL_WHAT_YOU_TYPED), |
| 213 search_view_(new IconLabelView(font)), | |
| 214 search_expanded_(false) { | |
| 123 CHECK_GE(model_index, 0); | 215 CHECK_GE(model_index, 0); |
| 124 if (icon_size_ == 0) { | 216 if (icon_size_ == 0) { |
| 125 icon_size_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( | 217 icon_size_ = ResourceBundle::GetSharedInstance().GetBitmapNamed( |
| 126 AutocompleteMatch::TypeToIcon(AutocompleteMatch::URL_WHAT_YOU_TYPED))-> | 218 AutocompleteMatch::TypeToIcon(AutocompleteMatch::URL_WHAT_YOU_TYPED))-> |
| 127 width(); | 219 width(); |
| 128 } | 220 } |
| 221 | |
| 222 animation_.reset(new ui::SlideAnimation(this)); | |
|
Peter Kasting
2011/04/07 20:19:21
Nit: You can do this in the initializer list (use
| |
| 223 animation_->SetSlideDuration(500); | |
| 224 | |
| 225 AddChildView(search_view_.get()); | |
| 129 } | 226 } |
| 130 | 227 |
| 131 AutocompleteResultView::~AutocompleteResultView() { | 228 AutocompleteResultView::~AutocompleteResultView() { |
| 132 } | 229 } |
| 133 | 230 |
| 134 void AutocompleteResultView::OnPaint(gfx::Canvas* canvas) { | 231 void AutocompleteResultView::OnPaint(gfx::Canvas* canvas) { |
| 135 const ResultViewState state = GetState(); | 232 const ResultViewState state = GetState(); |
| 136 if (state != NORMAL) | 233 |
| 234 if (state != NORMAL && !search_expanded_) | |
| 137 canvas->AsCanvasSkia()->drawColor(GetColor(state, BACKGROUND)); | 235 canvas->AsCanvasSkia()->drawColor(GetColor(state, BACKGROUND)); |
| 138 | 236 |
| 139 // Paint the icon. | 237 // Paint the icon. |
| 140 canvas->DrawBitmapInt(*GetIcon(), GetMirroredXForRect(icon_bounds_), | 238 canvas->DrawBitmapInt(*GetIcon(), GetMirroredXForRect(icon_bounds_), |
| 141 icon_bounds_.y()); | 239 icon_bounds_.y()); |
| 142 | 240 |
| 143 // Paint the text. | 241 // Paint the text. |
| 144 int x = GetMirroredXForRect(text_bounds_); | 242 int x = GetMirroredXForRect(text_bounds_); |
| 145 mirroring_context_->Initialize(x, text_bounds_.width()); | 243 mirroring_context_->Initialize(x, text_bounds_.width()); |
| 146 PaintMatch(canvas, match_, x); | 244 PaintMatch(canvas, match_, x); |
| 147 } | 245 } |
| 148 | 246 |
| 149 void AutocompleteResultView::Layout() { | 247 void AutocompleteResultView::Layout() { |
| 150 icon_bounds_.SetRect(LocationBarView::kEdgeItemPadding, | 248 icon_bounds_.SetRect(LocationBarView::kEdgeItemPadding, |
| 151 (height() - icon_size_) / 2, icon_size_, icon_size_); | 249 (height() - icon_size_) / 2, icon_size_, icon_size_); |
| 152 int text_x = icon_bounds_.right() + LocationBarView::kItemPadding; | 250 const int text_x = icon_bounds_.right() + LocationBarView::kItemPadding; |
| 153 int font_height = std::max(normal_font_.GetHeight(), bold_font_.GetHeight()); | 251 const int font_height = std::max(normal_font_.GetHeight(), |
| 154 text_bounds_.SetRect(text_x, std::max(0, (height() - font_height) / 2), | 252 bold_font_.GetHeight()); |
| 155 std::max(bounds().width() - text_x - LocationBarView::kEdgeItemPadding, | 253 const int text_y = std::max(0, (height() - font_height) / 2); |
| 156 0), font_height); | 254 |
| 255 if (keyword_.get() && keyword_->is_keyword_hint) { | |
| 256 gfx::Size search_size = search_view_->GetPreferredSize(); | |
| 257 const double expanded_x = text_x + | |
|
Peter Kasting
2011/04/07 20:19:21
Nit: float is plenty of precision, no need for dou
| |
| 258 normal_font_.GetStringWidth(UTF8ToUTF16(ui::kEllipsis)) + | |
| 259 LocationBarView::kItemPadding; | |
| 260 const double collapsed_x = bounds().width() - search_size.width(); | |
| 261 const int search_x = static_cast<int>(collapsed_x - ((collapsed_x - | |
| 262 expanded_x) * animation_->GetCurrentValue())); | |
| 263 | |
| 264 text_bounds_.SetRect(text_x, text_y, std::max(search_x - text_x, 0), | |
| 265 font_height); | |
| 266 search_view_->SetBounds(search_x, 0, std::max(bounds().width() - | |
| 267 search_x, 0), height()); | |
| 268 } else { | |
| 269 text_bounds_.SetRect(text_x, text_y, | |
| 270 std::max(bounds().width() - text_x, 0), font_height); | |
| 271 } | |
| 157 } | 272 } |
| 158 | 273 |
| 159 gfx::Size AutocompleteResultView::GetPreferredSize() { | 274 gfx::Size AutocompleteResultView::GetPreferredSize() { |
| 160 return gfx::Size(0, GetPreferredHeight(normal_font_, bold_font_)); | 275 return gfx::Size(0, GetPreferredHeight(normal_font_, bold_font_)); |
| 161 } | 276 } |
| 162 | 277 |
| 163 int AutocompleteResultView::GetPreferredHeight( | 278 int AutocompleteResultView::GetPreferredHeight( |
| 164 const gfx::Font& font, | 279 const gfx::Font& font, |
| 165 const gfx::Font& bold_font) { | 280 const gfx::Font& bold_font) { |
| 166 int text_height = std::max(font.GetHeight(), bold_font.GetHeight()) + | 281 int text_height = std::max(font.GetHeight(), bold_font.GetHeight()) + |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 262 return model_->IsHoveredIndex(model_index_) ? HOVERED : NORMAL; | 377 return model_->IsHoveredIndex(model_index_) ? HOVERED : NORMAL; |
| 263 } | 378 } |
| 264 | 379 |
| 265 const SkBitmap* AutocompleteResultView::GetIcon() const { | 380 const SkBitmap* AutocompleteResultView::GetIcon() const { |
| 266 const SkBitmap* bitmap = model_->GetSpecialIcon(model_index_); | 381 const SkBitmap* bitmap = model_->GetSpecialIcon(model_index_); |
| 267 if (bitmap) | 382 if (bitmap) |
| 268 return bitmap; | 383 return bitmap; |
| 269 | 384 |
| 270 int icon = match_.starred ? | 385 int icon = match_.starred ? |
| 271 IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match_.type); | 386 IDR_OMNIBOX_STAR : AutocompleteMatch::TypeToIcon(match_.type); |
| 272 if (model_->IsSelectedIndex(model_index_)) { | 387 if (model_->IsSelectedIndex(model_index_) && !search_expanded_) { |
| 273 switch (icon) { | 388 switch (icon) { |
| 274 case IDR_OMNIBOX_HTTP: icon = IDR_OMNIBOX_HTTP_SELECTED; break; | 389 case IDR_OMNIBOX_HTTP: icon = IDR_OMNIBOX_HTTP_SELECTED; break; |
| 275 case IDR_OMNIBOX_HISTORY: icon = IDR_OMNIBOX_HISTORY_SELECTED; break; | 390 case IDR_OMNIBOX_HISTORY: icon = IDR_OMNIBOX_HISTORY_SELECTED; break; |
| 276 case IDR_OMNIBOX_SEARCH: icon = IDR_OMNIBOX_SEARCH_SELECTED; break; | 391 case IDR_OMNIBOX_SEARCH: icon = IDR_OMNIBOX_SEARCH_SELECTED; break; |
| 277 case IDR_OMNIBOX_STAR: icon = IDR_OMNIBOX_STAR_SELECTED; break; | 392 case IDR_OMNIBOX_STAR: icon = IDR_OMNIBOX_STAR_SELECTED; break; |
| 278 default: NOTREACHED(); break; | 393 default: NOTREACHED(); break; |
| 279 } | 394 } |
| 280 } | 395 } |
| 281 return ResourceBundle::GetSharedInstance().GetBitmapNamed(icon); | 396 return ResourceBundle::GetSharedInstance().GetBitmapNamed(icon); |
| 282 } | 397 } |
| (...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 347 | 462 |
| 348 current_run->classifications.push_back(ClassificationData()); | 463 current_run->classifications.push_back(ClassificationData()); |
| 349 ClassificationData* current_data = | 464 ClassificationData* current_data = |
| 350 ¤t_run->classifications.back(); | 465 ¤t_run->classifications.back(); |
| 351 current_data->text = text.substr(text_start, text_end - text_start); | 466 current_data->text = text.substr(text_start, text_end - text_start); |
| 352 | 467 |
| 353 // Calculate style-related data. | 468 // Calculate style-related data. |
| 354 const int style = classifications[i].style; | 469 const int style = classifications[i].style; |
| 355 const bool use_bold_font = !!(style & ACMatchClassification::MATCH); | 470 const bool use_bold_font = !!(style & ACMatchClassification::MATCH); |
| 356 current_data->font = &(use_bold_font ? bold_font_ : normal_font_); | 471 current_data->font = &(use_bold_font ? bold_font_ : normal_font_); |
| 357 const ResultViewState state = GetState(); | 472 const ResultViewState state = search_expanded_ ? NORMAL : GetState(); |
| 358 if (style & ACMatchClassification::URL) | 473 if (style & ACMatchClassification::URL) |
| 359 current_data->color = GetColor(state, URL); | 474 current_data->color = GetColor(state, URL); |
| 360 else if (style & ACMatchClassification::DIM) | 475 else if (style & ACMatchClassification::DIM) |
| 361 current_data->color = GetColor(state, DIMMED_TEXT); | 476 current_data->color = GetColor(state, DIMMED_TEXT); |
| 362 else | 477 else |
| 363 current_data->color = GetColor(state, force_dim ? DIMMED_TEXT : TEXT); | 478 current_data->color = GetColor(state, force_dim ? DIMMED_TEXT : TEXT); |
| 364 current_data->pixel_width = | 479 current_data->pixel_width = |
| 365 current_data->font->GetStringWidth(current_data->text); | 480 current_data->font->GetStringWidth(current_data->text); |
| 366 current_run->pixel_width += current_data->pixel_width; | 481 current_run->pixel_width += current_data->pixel_width; |
| 367 } | 482 } |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 503 // * It's normal, and will be able to draw successfully with the | 618 // * It's normal, and will be able to draw successfully with the |
| 504 // ellipsis we'll append to it, or | 619 // ellipsis we'll append to it, or |
| 505 // * It is also bold, in which case we don't want to fall back | 620 // * It is also bold, in which case we don't want to fall back |
| 506 // to a normal ellipsis anyway (see comment above). | 621 // to a normal ellipsis anyway (see comment above). |
| 507 } | 622 } |
| 508 } | 623 } |
| 509 | 624 |
| 510 // We couldn't draw anything. | 625 // We couldn't draw anything. |
| 511 runs->clear(); | 626 runs->clear(); |
| 512 } | 627 } |
| 628 | |
| 629 void AutocompleteResultView::SetMatch(const AutocompleteMatch& match) { | |
| 630 const bool same_match = match.destination_url == match_.destination_url; | |
| 631 match_ = match; | |
| 632 keyword_ = match.keyword; | |
| 633 | |
| 634 const bool keyword_mode = keyword_.get() && keyword_->is_keyword_hint && | |
| 635 keyword_->is_keyword_mode; | |
| 636 | |
| 637 if (keyword_mode && !search_expanded_) | |
| 638 animation_->Show(); | |
| 639 else if (same_match && !keyword_mode && search_expanded_) | |
| 640 animation_->Hide(); | |
| 641 else if (!same_match || (same_match && !animation_->is_animating())) { | |
| 642 animation_->Stop(); | |
| 643 animation_->Reset(); | |
| 644 } | |
| 645 | |
| 646 search_expanded_ = keyword_mode; | |
| 647 search_view_->SetSelected(search_expanded_); | |
| 648 search_view_->SetVisible(keyword_.get() && keyword_->is_keyword_hint); | |
| 649 | |
| 650 if (search_expanded_) { | |
| 651 int message_id = keyword_->template_url->IsExtensionKeyword() ? | |
| 652 IDS_EXTENSION_KEYWORD_COMMAND : IDS_KEYWORD_SEARCH; | |
| 653 string16 text = l10n_util::GetStringFUTF16(message_id, | |
| 654 keyword_->text, l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE)); | |
| 655 text.append( | |
| 656 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_MATCH_DESCRIPTION_SEPARATOR)); | |
| 657 text.append( | |
| 658 l10n_util::GetStringFUTF16(IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION, | |
| 659 keyword_->text)); | |
| 660 search_view_->SetLabelText(text); | |
| 661 search_view_->SetLabelColor(GetColor(SELECTED, DIMMED_TEXT)); | |
| 662 } else { | |
| 663 search_view_->SetLabelText(UTF8ToUTF16(ui::kEllipsis)); | |
| 664 search_view_->SetLabelColor(GetColor(NORMAL, DIMMED_TEXT)); | |
| 665 } | |
| 666 Layout(); | |
| 667 } | |
| 668 | |
| 669 void AutocompleteResultView::AnimationProgressed(const ui::Animation* animation) { | |
| 670 Layout(); | |
| 671 SchedulePaint(); | |
| 672 } | |
| 673 | |
| 674 void AutocompleteResultView::AnimationCanceled(const ui::Animation* animation) { | |
| 675 Layout(); | |
| 676 SchedulePaint(); | |
| 677 } | |
| OLD | NEW |