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

Side by Side Diff: ash/app_list/app_list_item_view.cc

Issue 10388032: Move app list from ash to ui. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: fix win_aura bot and comments in #5 Created 8 years, 7 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 | Annotate | Revision Log
« no previous file with comments | « ash/app_list/app_list_item_view.h ('k') | ash/app_list/app_list_model.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 "ash/app_list/app_list_item_view.h"
6
7 #include "ash/app_list/app_list.h"
8 #include "ash/app_list/app_list_item_model.h"
9 #include "ash/app_list/app_list_model_view.h"
10 #include "ash/app_list/drop_shadow_label.h"
11 #include "ash/app_list/icon_cache.h"
12 #include "base/bind.h"
13 #include "base/message_loop.h"
14 #include "base/synchronization/cancellation_flag.h"
15 #include "base/threading/worker_pool.h"
16 #include "base/utf_string_conversions.h"
17 #include "ui/base/accessibility/accessible_view_state.h"
18 #include "ui/base/animation/throb_animation.h"
19 #include "ui/base/resource/resource_bundle.h"
20 #include "ui/gfx/canvas.h"
21 #include "ui/gfx/font.h"
22 #include "ui/gfx/shadow_value.h"
23 #include "ui/gfx/skbitmap_operations.h"
24 #include "ui/views/controls/image_view.h"
25 #include "ui/views/controls/menu/menu_item_view.h"
26 #include "ui/views/controls/menu/menu_model_adapter.h"
27 #include "ui/views/controls/menu/menu_runner.h"
28
29 namespace ash {
30
31 namespace {
32
33 const int kTopBottomPadding = 10;
34 const int kIconTitleSpacing = 10;
35
36 const SkColor kTitleColor = SK_ColorWHITE;
37 const SkColor kTitleColorV2 = SkColorSetARGB(0xFF, 0x88, 0x88, 0x88);
38
39 // 0.33 black
40 const SkColor kHoverAndPushedColor = SkColorSetARGB(0x55, 0x00, 0x00, 0x00);
41
42 // 0.16 black
43 const SkColor kSelectedColor = SkColorSetARGB(0x2A, 0x00, 0x00, 0x00);
44
45 const SkColor kHighlightedColor = kHoverAndPushedColor;
46
47 // FontSize/IconSize ratio = 24 / 128, which means we should get 24 font size
48 // when icon size is 128.
49 const float kFontSizeToIconSizeRatio = 0.1875f;
50
51 // Font smaller than kBoldFontSize needs to be bold.
52 const int kBoldFontSize = 14;
53
54 const int kMinFontSize = 12;
55
56 const int kMinTitleChars = 15;
57
58 const int kLeftRightPaddingChars = 1;
59
60 const gfx::Font& GetTitleFontForIconSize(const gfx::Size& size) {
61 static int icon_height;
62 static gfx::Font* font = NULL;
63
64 if (font && icon_height == size.height())
65 return *font;
66
67 delete font;
68
69 icon_height = size.height();
70 int font_size = std::max(
71 static_cast<int>(icon_height * kFontSizeToIconSizeRatio),
72 kMinFontSize);
73
74 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
75 gfx::Font title_font(rb.GetFont(ui::ResourceBundle::BaseFont).GetFontName(),
76 font_size);
77 if (font_size <= kBoldFontSize)
78 title_font = title_font.DeriveFont(0, gfx::Font::BOLD);
79 font = new gfx::Font(title_font);
80 return *font;
81 }
82
83 // An image view that is not interactive.
84 class StaticImageView : public views::ImageView {
85 public:
86 StaticImageView() : ImageView() {
87 }
88
89 private:
90 // views::View overrides:
91 virtual bool HitTest(const gfx::Point& l) const OVERRIDE {
92 return false;
93 }
94
95 DISALLOW_COPY_AND_ASSIGN(StaticImageView);
96 };
97
98 // A minimum title width set by test to override the default logic that derives
99 // the min width from font.
100 int g_min_title_width = 0;
101
102 } // namespace
103
104 // static
105 const char AppListItemView::kViewClassName[] = "ash/app_list/AppListItemView";
106
107 // AppListItemView::IconOperation wraps background icon processing.
108 class AppListItemView::IconOperation
109 : public base::RefCountedThreadSafe<AppListItemView::IconOperation> {
110 public:
111 IconOperation(const SkBitmap& bitmap, const gfx::Size& size)
112 : bitmap_(bitmap),
113 size_(size) {
114 }
115
116 static void Run(scoped_refptr<IconOperation> op) {
117 op->ResizeAndGenerateShadow();
118 }
119
120 // Padding space around icon to contain its shadow. Note it should be at least
121 // the max size of shadow radius + shadow offset in shadow generation code.
122 static const int kShadowPadding = 15;
123
124 void ResizeAndGenerateShadow() {
125 // If you change shadow radius and shadow offset, please also update
126 // kShadowPaddingAbove.
127 const SkColor kShadowColor[] = {
128 SkColorSetARGB(0xCC, 0, 0, 0),
129 SkColorSetARGB(0x33, 0, 0, 0),
130 SkColorSetARGB(0x4C, 0, 0, 0),
131 };
132 const gfx::Point kShadowOffset[] = {
133 gfx::Point(0, 0),
134 gfx::Point(0, 4),
135 gfx::Point(0, 5),
136 };
137 const SkScalar kShadowRadius[] = {
138 SkIntToScalar(2),
139 SkIntToScalar(4),
140 SkIntToScalar(10),
141 };
142
143 if (cancel_flag_.IsSet())
144 return;
145
146 if (size_ != gfx::Size(bitmap_.width(), bitmap_.height()))
147 bitmap_ = SkBitmapOperations::CreateResizedBitmap(bitmap_, size_);
148
149 if (cancel_flag_.IsSet())
150 return;
151
152 bitmap_ = SkBitmapOperations::CreateDropShadow(
153 bitmap_,
154 arraysize(kShadowColor),
155 kShadowColor,
156 kShadowOffset,
157 kShadowRadius);
158 }
159
160 void Cancel() {
161 cancel_flag_.Set();
162 }
163
164 const SkBitmap& bitmap() const {
165 return bitmap_;
166 }
167
168 private:
169 friend class base::RefCountedThreadSafe<AppListItemView::IconOperation>;
170
171 base::CancellationFlag cancel_flag_;
172
173 SkBitmap bitmap_;
174 const gfx::Size size_;
175
176 DISALLOW_COPY_AND_ASSIGN(IconOperation);
177 };
178
179 AppListItemView::AppListItemView(AppListModelView* list_model_view,
180 AppListItemModel* model,
181 views::ButtonListener* listener)
182 : CustomButton(listener),
183 model_(model),
184 list_model_view_(list_model_view),
185 icon_(new StaticImageView),
186 title_(new DropShadowLabel),
187 selected_(false),
188 ALLOW_THIS_IN_INITIALIZER_LIST(apply_shadow_factory_(this)) {
189 title_->SetBackgroundColor(0);
190
191 if (internal::AppList::UseAppListV2()) {
192 title_->SetEnabledColor(kTitleColorV2);
193 } else {
194 title_->SetEnabledColor(kTitleColor);
195 const gfx::ShadowValue kTitleShadows[] = {
196 gfx::ShadowValue(gfx::Point(0, 0), 1, SkColorSetARGB(0x66, 0, 0, 0)),
197 gfx::ShadowValue(gfx::Point(0, 0), 10, SkColorSetARGB(0x66, 0, 0, 0)),
198 gfx::ShadowValue(gfx::Point(0, 2), 2, SkColorSetARGB(0x66, 0, 0, 0)),
199 gfx::ShadowValue(gfx::Point(0, 2), 4, SkColorSetARGB(0x66, 0, 0, 0)),
200 };
201 title_->SetTextShadows(arraysize(kTitleShadows), kTitleShadows);
202 }
203
204 AddChildView(icon_);
205 AddChildView(title_);
206
207 ItemIconChanged();
208 ItemTitleChanged();
209 model_->AddObserver(this);
210
211 set_context_menu_controller(this);
212 set_request_focus_on_press(false);
213 set_focusable(true);
214 }
215
216 AppListItemView::~AppListItemView() {
217 model_->RemoveObserver(this);
218 CancelPendingIconOperation();
219 }
220
221 // static
222 gfx::Size AppListItemView::GetPreferredSizeForIconSize(
223 const gfx::Size& icon_size) {
224 int min_title_width = g_min_title_width;
225 // Fixed 20px is used for left/right padding before switching to padding
226 // based on number of chars. It is also a number used for test case
227 // AppList.ModelViewCalculateLayout.
228 int left_right_padding = 20;
229 if (min_title_width == 0) {
230 const gfx::Font& title_font = GetTitleFontForIconSize(icon_size);
231 // Use big char such as 'G' to calculate min title width.
232 min_title_width = kMinTitleChars *
233 title_font.GetStringWidth(ASCIIToUTF16("G"));
234 left_right_padding = kLeftRightPaddingChars *
235 title_font.GetAverageCharacterWidth();
236 }
237
238 int dimension = std::max(icon_size.width() * 2, min_title_width);
239 gfx::Size size(dimension, dimension);
240 size.Enlarge(left_right_padding, kTopBottomPadding);
241 return size;
242 }
243
244 // static
245 void AppListItemView::SetMinTitleWidth(int width) {
246 g_min_title_width = width;
247 }
248
249 void AppListItemView::SetIconSize(const gfx::Size& size) {
250 if (icon_size_ == size)
251 return;
252
253 icon_size_ = size;
254 title_->SetFont(GetTitleFontForIconSize(size));
255 UpdateIcon();
256 }
257
258 void AppListItemView::SetSelected(bool selected) {
259 if (selected == selected_)
260 return;
261
262 RequestFocus();
263 selected_ = selected;
264 SchedulePaint();
265 }
266
267 void AppListItemView::UpdateIcon() {
268 // Skip if |icon_size_| has not been determined.
269 if (icon_size_.IsEmpty())
270 return;
271
272 SkBitmap icon = model_->icon();
273 // Clear icon and bail out if model icon is empty.
274 if (icon.empty()) {
275 icon_->SetImage(NULL);
276 return;
277 }
278
279 CancelPendingIconOperation();
280
281 SkBitmap shadow;
282 if (IconCache::GetInstance()->Get(icon, icon_size_, &shadow)) {
283 icon_->SetImage(shadow);
284 } else {
285 // Schedule resize and shadow generation.
286 icon_op_ = new IconOperation(icon, icon_size_);
287 base::WorkerPool::PostTaskAndReply(
288 FROM_HERE,
289 base::Bind(&IconOperation::Run, icon_op_),
290 base::Bind(&AppListItemView::ApplyShadow,
291 apply_shadow_factory_.GetWeakPtr(),
292 icon_op_),
293 true /* task_is_slow */);
294 }
295 }
296
297 void AppListItemView::CancelPendingIconOperation() {
298 // Set canceled flag of previous request to skip unneeded processing.
299 if (icon_op_.get())
300 icon_op_->Cancel();
301
302 // Cancel reply callback for previous request.
303 apply_shadow_factory_.InvalidateWeakPtrs();
304 }
305
306 void AppListItemView::ApplyShadow(scoped_refptr<IconOperation> op) {
307 icon_->SetImage(op->bitmap());
308 IconCache::GetInstance()->Put(model_->icon(), icon_size_, op->bitmap());
309
310 DCHECK(op.get() == icon_op_.get());
311 icon_op_ = NULL;
312 }
313
314 void AppListItemView::ItemIconChanged() {
315 UpdateIcon();
316 }
317
318 void AppListItemView::ItemTitleChanged() {
319 title_->SetText(UTF8ToUTF16(model_->title()));
320 }
321
322 void AppListItemView::ItemHighlightedChanged() {
323 SchedulePaint();
324 }
325
326 std::string AppListItemView::GetClassName() const {
327 return kViewClassName;
328 }
329
330 gfx::Size AppListItemView::GetPreferredSize() {
331 return GetPreferredSizeForIconSize(icon_size_);
332 }
333
334 void AppListItemView::Layout() {
335 gfx::Rect rect(GetContentsBounds());
336
337 int left_right_padding = kLeftRightPaddingChars *
338 title_->font().GetAverageCharacterWidth();
339 rect.Inset(left_right_padding, kTopBottomPadding);
340
341 gfx::Size title_size = title_->GetPreferredSize();
342 int height = icon_size_.height() + kIconTitleSpacing +
343 title_size.height();
344 int y = rect.y() + (rect.height() - height) / 2;
345
346 gfx::Rect icon_bounds(rect.x(), y, rect.width(), icon_size_.height());
347 icon_bounds.Inset(0, -IconOperation::kShadowPadding);
348 icon_->SetBoundsRect(icon_bounds);
349
350 title_->SetBounds(rect.x(),
351 y + icon_size_.height() + kIconTitleSpacing,
352 rect.width(),
353 title_size.height());
354 }
355
356 void AppListItemView::OnPaint(gfx::Canvas* canvas) {
357 gfx::Rect rect(GetContentsBounds());
358
359 if (model_->highlighted()) {
360 canvas->FillRect(rect, kHighlightedColor);
361 } else if (hover_animation_->is_animating()) {
362 int alpha = SkColorGetA(kHoverAndPushedColor) *
363 hover_animation_->GetCurrentValue();
364 canvas->FillRect(rect, SkColorSetA(kHoverAndPushedColor, alpha));
365 } else if (state() == BS_HOT || state() == BS_PUSHED) {
366 canvas->FillRect(rect, kHoverAndPushedColor);
367 } else if (selected_) {
368 canvas->FillRect(rect, kSelectedColor);
369 }
370 }
371
372 void AppListItemView::GetAccessibleState(ui::AccessibleViewState* state) {
373 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON;
374 state->name = UTF8ToUTF16(model_->title());
375 }
376
377 void AppListItemView::ShowContextMenuForView(views::View* source,
378 const gfx::Point& point) {
379 ui::MenuModel* menu_model = model_->GetContextMenuModel();
380 if (!menu_model)
381 return;
382
383 views::MenuModelAdapter menu_adapter(menu_model);
384 context_menu_runner_.reset(
385 new views::MenuRunner(new views::MenuItemView(&menu_adapter)));
386 menu_adapter.BuildMenu(context_menu_runner_->GetMenu());
387 if (context_menu_runner_->RunMenuAt(
388 GetWidget(), NULL, gfx::Rect(point, gfx::Size()),
389 views::MenuItemView::TOPLEFT, views::MenuRunner::HAS_MNEMONICS) ==
390 views::MenuRunner::MENU_DELETED)
391 return;
392 }
393
394 void AppListItemView::StateChanged() {
395 if (state() == BS_HOT || state() == BS_PUSHED) {
396 list_model_view_->SetSelectedItem(this);
397 } else {
398 list_model_view_->ClearSelectedItem(this);
399 model_->SetHighlighted(false);
400 }
401 }
402
403 } // namespace ash
OLDNEW
« no previous file with comments | « ash/app_list/app_list_item_view.h ('k') | ash/app_list/app_list_model.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698