| 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 "ui/app_list/search_result_view.h" | 5 #include "ui/app_list/search_result_view.h" |
| 6 | 6 |
| 7 #include "ui/app_list/search_result.h" | 7 #include "ui/app_list/search_result.h" |
| 8 #include "ui/app_list/search_result_list_view.h" | 8 #include "ui/app_list/search_result_list_view.h" |
| 9 #include "ui/gfx/canvas.h" | 9 #include "ui/gfx/canvas.h" |
| 10 #include "ui/gfx/font.h" | 10 #include "ui/gfx/font.h" |
| 11 #include "ui/gfx/image/image_skia_operations.h" |
| 11 #include "ui/gfx/render_text.h" | 12 #include "ui/gfx/render_text.h" |
| 13 #include "ui/views/controls/button/image_button.h" |
| 12 #include "ui/views/controls/image_view.h" | 14 #include "ui/views/controls/image_view.h" |
| 13 | 15 |
| 14 namespace { | 16 namespace { |
| 15 | 17 |
| 16 const int kPreferredWidth = 300; | 18 const int kPreferredWidth = 300; |
| 17 const int kPreferredHeight = 52; | 19 const int kPreferredHeight = 52; |
| 18 const int kIconDimension = 32; | 20 const int kIconDimension = 32; |
| 19 const int kIconPadding = 14; | 21 const int kIconPadding = 14; |
| 20 const int kIconViewWidth = kIconDimension + 2 * kIconPadding; | 22 const int kIconViewWidth = kIconDimension + 2 * kIconPadding; |
| 21 const int kTextTrailPadding = kIconPadding; | 23 const int kTextTrailPadding = kIconPadding; |
| 22 const int kBorderSize = 1; | 24 const int kBorderSize = 1; |
| 23 | 25 |
| 26 // Maximum dimensions of a search result's action icons. |
| 27 const int kActionIconDimension = 24; |
| 28 |
| 29 // Total space (including padding) used for each action icon's button. |
| 30 const int kActionButtonWidth = 32; |
| 31 |
| 32 // Extra margin at the right of the rightmost action icon. |
| 33 const int kActionButtonRightMargin = 8; |
| 34 |
| 24 const SkColor kBorderColor = SkColorSetARGB(0x0F, 0, 0, 0); | 35 const SkColor kBorderColor = SkColorSetARGB(0x0F, 0, 0, 0); |
| 25 | 36 |
| 26 const SkColor kDefaultTextColor = SkColorSetRGB(0x33, 0x33, 0x33); | 37 const SkColor kDefaultTextColor = SkColorSetRGB(0x33, 0x33, 0x33); |
| 27 const SkColor kDimmedTextColor = SkColorSetRGB(0x96, 0x96, 0x96); | 38 const SkColor kDimmedTextColor = SkColorSetRGB(0x96, 0x96, 0x96); |
| 28 const SkColor kURLTextColor = SkColorSetRGB(0x00, 0x99, 0x33); | 39 const SkColor kURLTextColor = SkColorSetRGB(0x00, 0x99, 0x33); |
| 29 | 40 |
| 30 const SkColor kSelectedBorderColor = kBorderColor; | 41 const SkColor kSelectedBorderColor = kBorderColor; |
| 31 const SkColor kSelectedBackgroundColor = SkColorSetARGB(0x0F, 0x4D, 0x90, 0xFE); | 42 const SkColor kSelectedBackgroundColor = SkColorSetARGB(0x0F, 0x4D, 0x90, 0xFE); |
| 32 const SkColor kHoverAndPushedColor = SkColorSetARGB(0x05, 0, 0, 0); | 43 const SkColor kHoverAndPushedColor = SkColorSetARGB(0x05, 0, 0, 0); |
| 33 | 44 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 82 } | 93 } |
| 83 | 94 |
| 84 } // namespace | 95 } // namespace |
| 85 | 96 |
| 86 namespace app_list { | 97 namespace app_list { |
| 87 | 98 |
| 88 // static | 99 // static |
| 89 const char SearchResultView::kViewClassName[] = "ui/app_list/SearchResultView"; | 100 const char SearchResultView::kViewClassName[] = "ui/app_list/SearchResultView"; |
| 90 | 101 |
| 91 SearchResultView::SearchResultView(SearchResultListView* list_view, | 102 SearchResultView::SearchResultView(SearchResultListView* list_view, |
| 92 views::ButtonListener* listener) | 103 SearchResultViewDelegate* delegate) |
| 93 : views::CustomButton(listener), | 104 : views::CustomButton(this), |
| 94 result_(NULL), | 105 result_(NULL), |
| 95 list_view_(list_view), | 106 list_view_(list_view), |
| 107 delegate_(delegate), |
| 96 icon_(new IconView) { | 108 icon_(new IconView) { |
| 97 AddChildView(icon_); | 109 AddChildView(icon_); |
| 98 } | 110 } |
| 99 | 111 |
| 100 SearchResultView::~SearchResultView() { | 112 SearchResultView::~SearchResultView() { |
| 101 ClearResultNoRepaint(); | 113 ClearResultNoRepaint(); |
| 102 } | 114 } |
| 103 | 115 |
| 104 void SearchResultView::SetResult(SearchResult* result) { | 116 void SearchResultView::SetResult(SearchResult* result) { |
| 105 ClearResultNoRepaint(); | 117 ClearResultNoRepaint(); |
| 106 | 118 |
| 107 result_ = result; | 119 result_ = result; |
| 108 if (result_) | 120 if (result_) |
| 109 result_->AddObserver(this); | 121 result_->AddObserver(this); |
| 110 | 122 |
| 111 OnIconChanged(); | 123 OnIconChanged(); |
| 124 OnActionIconsChanged(); |
| 112 UpdateTitleText(); | 125 UpdateTitleText(); |
| 113 UpdateDetailsText(); | 126 UpdateDetailsText(); |
| 114 SchedulePaint(); | 127 SchedulePaint(); |
| 115 } | 128 } |
| 116 | 129 |
| 117 void SearchResultView::ClearResultNoRepaint() { | 130 void SearchResultView::ClearResultNoRepaint() { |
| 118 if (result_) | 131 if (result_) |
| 119 result_->RemoveObserver(this); | 132 result_->RemoveObserver(this); |
| 120 result_ = NULL; | 133 result_ = NULL; |
| 121 } | 134 } |
| (...skipping 26 matching lines...) Expand all Loading... |
| 148 | 161 |
| 149 void SearchResultView::Layout() { | 162 void SearchResultView::Layout() { |
| 150 gfx::Rect rect(GetContentsBounds()); | 163 gfx::Rect rect(GetContentsBounds()); |
| 151 if (rect.IsEmpty()) | 164 if (rect.IsEmpty()) |
| 152 return; | 165 return; |
| 153 | 166 |
| 154 gfx::Rect icon_bounds(rect); | 167 gfx::Rect icon_bounds(rect); |
| 155 icon_bounds.set_width(kIconViewWidth); | 168 icon_bounds.set_width(kIconViewWidth); |
| 156 icon_bounds.Inset(kIconPadding, (rect.height() - kIconDimension) / 2); | 169 icon_bounds.Inset(kIconPadding, (rect.height() - kIconDimension) / 2); |
| 157 icon_->SetBoundsRect(icon_bounds.Intersect(rect)); | 170 icon_->SetBoundsRect(icon_bounds.Intersect(rect)); |
| 171 |
| 172 size_t num_buttons = action_buttons_.size(); |
| 173 for (size_t i = 0; i < num_buttons; ++i) { |
| 174 views::ImageButton* button = action_buttons_[i]; |
| 175 gfx::Rect button_bounds( |
| 176 rect.right() - kActionButtonRightMargin - |
| 177 (num_buttons - i) * kActionButtonWidth, |
| 178 (rect.y() + rect.height() - kActionIconDimension) / 2, |
| 179 kActionButtonWidth, kActionIconDimension); |
| 180 button->SetBoundsRect(button_bounds); |
| 181 } |
| 158 } | 182 } |
| 159 | 183 |
| 160 void SearchResultView::OnPaint(gfx::Canvas* canvas) { | 184 void SearchResultView::OnPaint(gfx::Canvas* canvas) { |
| 161 gfx::Rect rect(GetContentsBounds()); | 185 gfx::Rect rect(GetContentsBounds()); |
| 162 if (rect.IsEmpty()) | 186 if (rect.IsEmpty()) |
| 163 return; | 187 return; |
| 164 | 188 |
| 165 gfx::Rect content_rect(rect); | 189 gfx::Rect content_rect(rect); |
| 166 content_rect.set_height(rect.height() - kBorderSize); | 190 content_rect.set_height(rect.height() - kBorderSize); |
| 167 | 191 |
| 168 bool selected = list_view_->IsResultViewSelected(this); | 192 bool selected = list_view_->IsResultViewSelected(this); |
| 169 if (selected) { | 193 if (selected) { |
| 170 canvas->FillRect(content_rect, kSelectedBackgroundColor); | 194 canvas->FillRect(content_rect, kSelectedBackgroundColor); |
| 171 } else if (state() == BS_HOT || state() == BS_PUSHED) { | 195 } else if (state() == BS_HOT || state() == BS_PUSHED) { |
| 172 canvas->FillRect(content_rect, kHoverAndPushedColor); | 196 canvas->FillRect(content_rect, kHoverAndPushedColor); |
| 173 } | 197 } |
| 174 | 198 |
| 175 gfx::Rect border_bottom = rect.Subtract(content_rect); | 199 gfx::Rect border_bottom = rect.Subtract(content_rect); |
| 176 canvas->FillRect(border_bottom, | 200 canvas->FillRect(border_bottom, |
| 177 selected ? kSelectedBorderColor : kBorderColor); | 201 selected ? kSelectedBorderColor : kBorderColor); |
| 178 | 202 |
| 179 gfx::Rect text_bounds(rect); | 203 gfx::Rect text_bounds(rect); |
| 180 text_bounds.set_x(kIconViewWidth); | 204 text_bounds.set_x(kIconViewWidth); |
| 181 text_bounds.set_width(rect.width() - kIconViewWidth - kTextTrailPadding); | 205 text_bounds.set_width( |
| 206 rect.width() - kIconViewWidth - kTextTrailPadding - |
| 207 action_buttons_.size() * kActionButtonWidth - |
| 208 (!action_buttons_.empty() ? kActionButtonRightMargin : 0)); |
| 182 | 209 |
| 183 if (title_text_.get() && details_text_.get()) { | 210 if (title_text_.get() && details_text_.get()) { |
| 184 gfx::Size title_size(text_bounds.width(), | 211 gfx::Size title_size(text_bounds.width(), |
| 185 title_text_->GetStringSize().height()); | 212 title_text_->GetStringSize().height()); |
| 186 gfx::Size details_size(text_bounds.width(), | 213 gfx::Size details_size(text_bounds.width(), |
| 187 details_text_->GetStringSize().height()); | 214 details_text_->GetStringSize().height()); |
| 188 int total_height = title_size.height() + + details_size.height(); | 215 int total_height = title_size.height() + + details_size.height(); |
| 189 int y = text_bounds.y() + (text_bounds.height() - total_height) / 2; | 216 int y = text_bounds.y() + (text_bounds.height() - total_height) / 2; |
| 190 | 217 |
| 191 title_text_->SetDisplayRect(gfx::Rect(gfx::Point(text_bounds.x(), y), | 218 title_text_->SetDisplayRect(gfx::Rect(gfx::Point(text_bounds.x(), y), |
| 192 title_size)); | 219 title_size)); |
| 193 title_text_->Draw(canvas); | 220 title_text_->Draw(canvas); |
| 194 | 221 |
| 195 y += title_size.height(); | 222 y += title_size.height(); |
| 196 details_text_->SetDisplayRect(gfx::Rect(gfx::Point(text_bounds.x(), y), | 223 details_text_->SetDisplayRect(gfx::Rect(gfx::Point(text_bounds.x(), y), |
| 197 details_size)); | 224 details_size)); |
| 198 details_text_->Draw(canvas); | 225 details_text_->Draw(canvas); |
| 199 } else if (title_text_.get()) { | 226 } else if (title_text_.get()) { |
| 200 gfx::Size title_size(text_bounds.width(), | 227 gfx::Size title_size(text_bounds.width(), |
| 201 title_text_->GetStringSize().height()); | 228 title_text_->GetStringSize().height()); |
| 202 title_text_->SetDisplayRect(text_bounds.Center(title_size)); | 229 title_text_->SetDisplayRect(text_bounds.Center(title_size)); |
| 203 title_text_->Draw(canvas); | 230 title_text_->Draw(canvas); |
| 204 } | 231 } |
| 205 } | 232 } |
| 206 | 233 |
| 234 void SearchResultView::ButtonPressed(views::Button* sender, |
| 235 const ui::Event& event) { |
| 236 if (sender == this) { |
| 237 delegate_->SearchResultActivated(this, event); |
| 238 return; |
| 239 } |
| 240 |
| 241 for (size_t i = 0; i < action_buttons_.size(); ++i) { |
| 242 if (sender == action_buttons_[i]) { |
| 243 delegate_->SearchResultActionActivated(this, i, event); |
| 244 return; |
| 245 } |
| 246 } |
| 247 NOTREACHED() << "Got unexpected button press on " << sender; |
| 248 } |
| 249 |
| 207 void SearchResultView::OnIconChanged() { | 250 void SearchResultView::OnIconChanged() { |
| 208 gfx::ImageSkia image(result_ ? result_->icon() : gfx::ImageSkia()); | 251 gfx::ImageSkia image(result_ ? result_->icon() : gfx::ImageSkia()); |
| 209 // Note this might leave the view with an old icon. But it is needed to avoid | 252 // Note this might leave the view with an old icon. But it is needed to avoid |
| 210 // flash when a SearchResult's icon is loaded asynchronously. In this case, it | 253 // flash when a SearchResult's icon is loaded asynchronously. In this case, it |
| 211 // looks nicer to keep the stale icon for a little while on screen instead of | 254 // looks nicer to keep the stale icon for a little while on screen instead of |
| 212 // clearing it out. It should work correctly as long as the SearchResult does | 255 // clearing it out. It should work correctly as long as the SearchResult does |
| 213 // not forget to SetIcon when it's ready. | 256 // not forget to SetIcon when it's ready. |
| 214 if (image.isNull()) | 257 if (image.isNull()) |
| 215 return; | 258 return; |
| 216 | 259 |
| 217 // Scales down big icons but leave small ones unchanged. | 260 // Scales down big icons but leave small ones unchanged. |
| 218 if (image.width() > kIconDimension || image.height() > kIconDimension) | 261 if (image.width() > kIconDimension || image.height() > kIconDimension) { |
| 219 icon_->SetImageSize(gfx::Size(kIconDimension, kIconDimension)); | 262 image = gfx::ImageSkiaOperations::CreateResizedImage( |
| 220 else | 263 image, |
| 264 skia::ImageOperations::RESIZE_BEST, |
| 265 gfx::Size(kIconDimension, kIconDimension)); |
| 266 } else { |
| 221 icon_->ResetImageSize(); | 267 icon_->ResetImageSize(); |
| 268 } |
| 222 | 269 |
| 223 icon_->SetImage(image); | 270 icon_->SetImage(image); |
| 224 } | 271 } |
| 225 | 272 |
| 273 void SearchResultView::OnActionIconsChanged() { |
| 274 while (action_buttons_.size() > |
| 275 (result_ ? result_->action_icons().size() : 0)) { |
| 276 RemoveChildView(action_buttons_.back()); |
| 277 action_buttons_.pop_back(); |
| 278 } |
| 279 |
| 280 if (result_) { |
| 281 while (action_buttons_.size() < result_->action_icons().size()) { |
| 282 views::ImageButton* button = new views::ImageButton(this); |
| 283 button->SetImageAlignment(views::ImageButton::ALIGN_CENTER, |
| 284 views::ImageButton::ALIGN_MIDDLE); |
| 285 AddChildView(button); |
| 286 action_buttons_.push_back(button); |
| 287 } |
| 288 |
| 289 const SearchResult::ActionIconSets& icons = result_->action_icons(); |
| 290 for (size_t i = 0; i < icons.size(); ++i) { |
| 291 const SearchResult::ActionIconSet& icon = icons.at(i); |
| 292 views::ImageButton* button = action_buttons_[i]; |
| 293 button->SetImage(views::CustomButton::BS_NORMAL, &icon.base_image); |
| 294 button->SetImage(views::CustomButton::BS_HOT, &icon.hover_image); |
| 295 button->SetImage(views::CustomButton::BS_PUSHED, &icon.pressed_image); |
| 296 button->SetTooltipText(icon.tooltip_text); |
| 297 } |
| 298 } |
| 299 } |
| 300 |
| 226 } // namespace app_list | 301 } // namespace app_list |
| OLD | NEW |