Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(320)

Side by Side Diff: ui/app_list/search_result_view.cc

Issue 11784034: Skeleton for app_list on OSX, and refactoring for enable_app_list=1 on OS=="mac". (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase for 175876 and 175961 Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/app_list/search_result_view.h ('k') | ui/app_list/test/apps_grid_view_test_api.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "ui/app_list/search_result_view.h"
6
7 #include "ui/app_list/app_list_constants.h"
8 #include "ui/app_list/search_result.h"
9 #include "ui/app_list/search_result_list_view.h"
10 #include "ui/gfx/canvas.h"
11 #include "ui/gfx/font.h"
12 #include "ui/gfx/image/image_skia_operations.h"
13 #include "ui/gfx/render_text.h"
14 #include "ui/views/controls/button/image_button.h"
15 #include "ui/views/controls/image_view.h"
16
17 namespace {
18
19 const int kPreferredWidth = 300;
20 const int kPreferredHeight = 52;
21 const int kIconDimension = 32;
22 const int kIconPadding = 14;
23 const int kIconViewWidth = kIconDimension + 2 * kIconPadding;
24 const int kTextTrailPadding = kIconPadding;
25 const int kBorderSize = 1;
26
27 // Maximum dimensions of a search result's action icons.
28 const int kActionIconDimension = 24;
29
30 // Total space (including padding) used for each action icon's button.
31 const int kActionButtonWidth = 32;
32
33 // Extra margin at the right of the rightmost action icon.
34 const int kActionButtonRightMargin = 8;
35
36 const SkColor kBorderColor = SkColorSetARGB(0x0F, 0, 0, 0);
37
38 const SkColor kDefaultTextColor = SkColorSetRGB(0x33, 0x33, 0x33);
39 const SkColor kDimmedTextColor = SkColorSetRGB(0x96, 0x96, 0x96);
40 const SkColor kURLTextColor = SkColorSetRGB(0x00, 0x99, 0x33);
41
42 const SkColor kSelectedBorderColor = kBorderColor;
43 const SkColor kSelectedBackgroundColor = SkColorSetARGB(0x0F, 0x4D, 0x90, 0xFE);
44 const SkColor kHoverAndPushedColor = SkColorSetARGB(0x05, 0, 0, 0);
45
46 // A non-interactive image view to display result icon.
47 class IconView : public views::ImageView {
48 public:
49 IconView() : ImageView() {}
50 virtual ~IconView() {}
51
52 private:
53 // views::View overrides:
54 virtual bool HitTestRect(const gfx::Rect& rect) const OVERRIDE {
55 return false;
56 }
57
58 DISALLOW_COPY_AND_ASSIGN(IconView);
59 };
60
61 // Creates a RenderText of given |text| and |styles|. Caller takes ownership
62 // of returned RenderText.
63 gfx::RenderText* CreateRenderText(const string16& text,
64 const app_list::SearchResult::Tags& tags) {
65 gfx::RenderText* render_text = gfx::RenderText::CreateInstance();
66 render_text->SetText(text);
67
68 gfx::StyleRange default_style;
69 default_style.foreground = kDefaultTextColor;
70 render_text->set_default_style(default_style);
71 render_text->ApplyDefaultStyle();
72
73 for (app_list::SearchResult::Tags::const_iterator it = tags.begin();
74 it != tags.end();
75 ++it) {
76 // NONE means default style so do nothing.
77 if (it->styles == app_list::SearchResult::Tag::NONE)
78 continue;
79
80 gfx::StyleRange style;
81 style.range = it->range;
82
83 if (it->styles & app_list::SearchResult::Tag::MATCH)
84 style.font_style = gfx::Font::BOLD;
85 if (it->styles & app_list::SearchResult::Tag::URL)
86 style.foreground = kURLTextColor;
87 if (it->styles & app_list::SearchResult::Tag::DIM)
88 style.foreground = kDimmedTextColor;
89
90 render_text->ApplyStyleRange(style);
91 }
92
93 return render_text;
94 }
95
96 } // namespace
97
98 namespace app_list {
99
100 // static
101 const char SearchResultView::kViewClassName[] = "ui/app_list/SearchResultView";
102
103 SearchResultView::SearchResultView(SearchResultListView* list_view,
104 SearchResultViewDelegate* delegate)
105 : views::CustomButton(this),
106 result_(NULL),
107 list_view_(list_view),
108 delegate_(delegate),
109 icon_(new IconView) {
110 AddChildView(icon_);
111 }
112
113 SearchResultView::~SearchResultView() {
114 ClearResultNoRepaint();
115 }
116
117 void SearchResultView::SetResult(SearchResult* result) {
118 ClearResultNoRepaint();
119
120 result_ = result;
121 if (result_)
122 result_->AddObserver(this);
123
124 OnIconChanged();
125 OnActionIconsChanged();
126 UpdateTitleText();
127 UpdateDetailsText();
128 SchedulePaint();
129 }
130
131 void SearchResultView::ClearResultNoRepaint() {
132 if (result_)
133 result_->RemoveObserver(this);
134 result_ = NULL;
135 }
136
137 void SearchResultView::UpdateTitleText() {
138 if (!result_ || result_->title().empty()) {
139 title_text_.reset();
140 } else {
141 title_text_.reset(CreateRenderText(result_->title(),
142 result_->title_tags()));
143 }
144 }
145
146 void SearchResultView::UpdateDetailsText() {
147 if (!result_ || result_->details().empty()) {
148 details_text_.reset();
149 } else {
150 details_text_.reset(CreateRenderText(result_->details(),
151 result_->details_tags()));
152 }
153 }
154
155 std::string SearchResultView::GetClassName() const {
156 return kViewClassName;
157 }
158
159 gfx::Size SearchResultView::GetPreferredSize() {
160 return gfx::Size(kPreferredWidth, kPreferredHeight);
161 }
162
163 void SearchResultView::Layout() {
164 gfx::Rect rect(GetContentsBounds());
165 if (rect.IsEmpty())
166 return;
167
168 gfx::Rect icon_bounds(rect);
169 icon_bounds.set_width(kIconViewWidth);
170 icon_bounds.Inset(kIconPadding, (rect.height() - kIconDimension) / 2);
171 icon_bounds.Intersect(rect);
172 icon_->SetBoundsRect(icon_bounds);
173
174 size_t num_buttons = action_buttons_.size();
175 for (size_t i = 0; i < num_buttons; ++i) {
176 views::ImageButton* button = action_buttons_[i];
177 gfx::Rect button_bounds(
178 rect.right() - kActionButtonRightMargin -
179 (num_buttons - i) * kActionButtonWidth,
180 (rect.y() + rect.height() - kActionIconDimension) / 2,
181 kActionButtonWidth, kActionIconDimension);
182 button->SetBoundsRect(button_bounds);
183 }
184 }
185
186 void SearchResultView::OnPaint(gfx::Canvas* canvas) {
187 gfx::Rect rect(GetContentsBounds());
188 if (rect.IsEmpty())
189 return;
190
191 gfx::Rect content_rect(rect);
192 content_rect.set_height(rect.height() - kBorderSize);
193
194 canvas->FillRect(content_rect, kContentsBackgroundColor);
195
196 bool selected = list_view_->IsResultViewSelected(this);
197 if (selected) {
198 canvas->FillRect(content_rect, kSelectedBackgroundColor);
199 } else if (state() == STATE_HOVERED || state() == STATE_PRESSED) {
200 canvas->FillRect(content_rect, kHoverAndPushedColor);
201 }
202
203 gfx::Rect border_bottom = gfx::SubtractRects(rect, content_rect);
204 canvas->FillRect(border_bottom,
205 selected ? kSelectedBorderColor : kBorderColor);
206
207 gfx::Rect text_bounds(rect);
208 text_bounds.set_x(kIconViewWidth);
209 text_bounds.set_width(
210 rect.width() - kIconViewWidth - kTextTrailPadding -
211 action_buttons_.size() * kActionButtonWidth -
212 (!action_buttons_.empty() ? kActionButtonRightMargin : 0));
213 text_bounds.set_x(GetMirroredXWithWidthInView(text_bounds.x(),
214 text_bounds.width()));
215
216 if (title_text_.get() && details_text_.get()) {
217 gfx::Size title_size(text_bounds.width(),
218 title_text_->GetStringSize().height());
219 gfx::Size details_size(text_bounds.width(),
220 details_text_->GetStringSize().height());
221 int total_height = title_size.height() + + details_size.height();
222 int y = text_bounds.y() + (text_bounds.height() - total_height) / 2;
223
224 title_text_->SetDisplayRect(gfx::Rect(gfx::Point(text_bounds.x(), y),
225 title_size));
226 title_text_->Draw(canvas);
227
228 y += title_size.height();
229 details_text_->SetDisplayRect(gfx::Rect(gfx::Point(text_bounds.x(), y),
230 details_size));
231 details_text_->Draw(canvas);
232 } else if (title_text_.get()) {
233 gfx::Size title_size(text_bounds.width(),
234 title_text_->GetStringSize().height());
235 gfx::Rect centered_title_rect(text_bounds);
236 centered_title_rect.ClampToCenteredSize(title_size);
237 title_text_->SetDisplayRect(centered_title_rect);
238 title_text_->Draw(canvas);
239 }
240 }
241
242 void SearchResultView::ButtonPressed(views::Button* sender,
243 const ui::Event& event) {
244 if (sender == this) {
245 delegate_->SearchResultActivated(this, event);
246 return;
247 }
248
249 for (size_t i = 0; i < action_buttons_.size(); ++i) {
250 if (sender == action_buttons_[i]) {
251 delegate_->SearchResultActionActivated(this, i, event);
252 return;
253 }
254 }
255 NOTREACHED() << "Got unexpected button press on " << sender;
256 }
257
258 void SearchResultView::OnIconChanged() {
259 gfx::ImageSkia image(result_ ? result_->icon() : gfx::ImageSkia());
260 // Note this might leave the view with an old icon. But it is needed to avoid
261 // flash when a SearchResult's icon is loaded asynchronously. In this case, it
262 // looks nicer to keep the stale icon for a little while on screen instead of
263 // clearing it out. It should work correctly as long as the SearchResult does
264 // not forget to SetIcon when it's ready.
265 if (image.isNull())
266 return;
267
268 // Scales down big icons but leave small ones unchanged.
269 if (image.width() > kIconDimension || image.height() > kIconDimension) {
270 image = gfx::ImageSkiaOperations::CreateResizedImage(
271 image,
272 skia::ImageOperations::RESIZE_BEST,
273 gfx::Size(kIconDimension, kIconDimension));
274 } else {
275 icon_->ResetImageSize();
276 }
277
278 icon_->SetImage(image);
279 }
280
281 void SearchResultView::OnActionIconsChanged() {
282 while (action_buttons_.size() >
283 (result_ ? result_->action_icons().size() : 0)) {
284 RemoveChildView(action_buttons_.back());
285 action_buttons_.pop_back();
286 }
287
288 if (result_) {
289 while (action_buttons_.size() < result_->action_icons().size()) {
290 views::ImageButton* button = new views::ImageButton(this);
291 button->SetImageAlignment(views::ImageButton::ALIGN_CENTER,
292 views::ImageButton::ALIGN_MIDDLE);
293 AddChildView(button);
294 action_buttons_.push_back(button);
295 }
296
297 const SearchResult::ActionIconSets& icons = result_->action_icons();
298 for (size_t i = 0; i < icons.size(); ++i) {
299 const SearchResult::ActionIconSet& icon = icons.at(i);
300 views::ImageButton* button = action_buttons_[i];
301 button->SetImage(views::CustomButton::STATE_NORMAL, &icon.base_image);
302 button->SetImage(views::CustomButton::STATE_HOVERED, &icon.hover_image);
303 button->SetImage(views::CustomButton::STATE_PRESSED, &icon.pressed_image);
304 button->SetTooltipText(icon.tooltip_text);
305 }
306 }
307 }
308
309 } // namespace app_list
OLDNEW
« no previous file with comments | « ui/app_list/search_result_view.h ('k') | ui/app_list/test/apps_grid_view_test_api.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698