| OLD | NEW |
| (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/app_list_item_view.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/utf_string_conversions.h" | |
| 10 #include "ui/app_list/app_list_item_model.h" | |
| 11 #include "ui/app_list/apps_grid_view.h" | |
| 12 #include "ui/base/accessibility/accessible_view_state.h" | |
| 13 #include "ui/base/animation/throb_animation.h" | |
| 14 #include "ui/base/resource/resource_bundle.h" | |
| 15 #include "ui/compositor/layer.h" | |
| 16 #include "ui/compositor/scoped_layer_animation_settings.h" | |
| 17 #include "ui/gfx/canvas.h" | |
| 18 #include "ui/gfx/font.h" | |
| 19 #include "ui/gfx/image/image_skia_operations.h" | |
| 20 #include "ui/gfx/transform_util.h" | |
| 21 #include "ui/views/controls/image_view.h" | |
| 22 #include "ui/views/controls/label.h" | |
| 23 #include "ui/views/controls/menu/menu_item_view.h" | |
| 24 #include "ui/views/controls/menu/menu_model_adapter.h" | |
| 25 #include "ui/views/controls/menu/menu_runner.h" | |
| 26 | |
| 27 namespace app_list { | |
| 28 | |
| 29 namespace { | |
| 30 | |
| 31 const int kTopBottomPadding = 10; | |
| 32 const int kTopPadding = 20; | |
| 33 const int kIconTitleSpacing = 7; | |
| 34 | |
| 35 const SkColor kTitleColor = SkColorSetRGB(0x5A, 0x5A, 0x5A); | |
| 36 const SkColor kTitleHoverColor = SkColorSetRGB(0x3C, 0x3C, 0x3C); | |
| 37 | |
| 38 const SkColor kHoverAndPushedColor = SkColorSetARGB(0x19, 0, 0, 0); | |
| 39 const SkColor kSelectedColor = SkColorSetARGB(0x0D, 0, 0, 0); | |
| 40 const SkColor kHighlightedColor = kHoverAndPushedColor; | |
| 41 | |
| 42 const int kLeftRightPaddingChars = 1; | |
| 43 | |
| 44 // Scale to transform the icon when a drag starts. | |
| 45 const float kDraggingIconScale = 1.5f; | |
| 46 | |
| 47 // Delay in milliseconds of when the dragging UI should be shown for mouse drag. | |
| 48 const int kMouseDragUIDelayInMs = 100; | |
| 49 | |
| 50 } // namespace | |
| 51 | |
| 52 // static | |
| 53 const char AppListItemView::kViewClassName[] = "ui/app_list/AppListItemView"; | |
| 54 | |
| 55 AppListItemView::AppListItemView(AppsGridView* apps_grid_view, | |
| 56 AppListItemModel* model) | |
| 57 : CustomButton(apps_grid_view), | |
| 58 model_(model), | |
| 59 apps_grid_view_(apps_grid_view), | |
| 60 icon_(new views::ImageView), | |
| 61 title_(new views::Label), | |
| 62 ui_state_(UI_STATE_NORMAL), | |
| 63 touch_dragging_(false) { | |
| 64 icon_->set_interactive(false); | |
| 65 | |
| 66 ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance(); | |
| 67 title_->SetBackgroundColor(0); | |
| 68 title_->SetAutoColorReadabilityEnabled(false); | |
| 69 title_->SetEnabledColor(kTitleColor); | |
| 70 title_->SetFont(rb.GetFont(ui::ResourceBundle::SmallBoldFont)); | |
| 71 title_->SetHorizontalAlignment(gfx::ALIGN_LEFT); | |
| 72 | |
| 73 const gfx::ShadowValue kIconShadows[] = { | |
| 74 gfx::ShadowValue(gfx::Point(0, 2), 2, SkColorSetARGB(0x24, 0, 0, 0)), | |
| 75 }; | |
| 76 icon_shadows_.assign(kIconShadows, kIconShadows + arraysize(kIconShadows)); | |
| 77 | |
| 78 AddChildView(icon_); | |
| 79 AddChildView(title_); | |
| 80 | |
| 81 ItemIconChanged(); | |
| 82 ItemTitleChanged(); | |
| 83 model_->AddObserver(this); | |
| 84 | |
| 85 set_context_menu_controller(this); | |
| 86 set_request_focus_on_press(false); | |
| 87 } | |
| 88 | |
| 89 AppListItemView::~AppListItemView() { | |
| 90 model_->RemoveObserver(this); | |
| 91 } | |
| 92 | |
| 93 void AppListItemView::SetIconSize(const gfx::Size& size) { | |
| 94 if (icon_size_ == size) | |
| 95 return; | |
| 96 | |
| 97 icon_size_ = size; | |
| 98 UpdateIcon(); | |
| 99 } | |
| 100 | |
| 101 void AppListItemView::UpdateIcon() { | |
| 102 // Skip if |icon_size_| has not been determined. | |
| 103 if (icon_size_.IsEmpty()) | |
| 104 return; | |
| 105 | |
| 106 gfx::ImageSkia icon = model_->icon(); | |
| 107 // Clear icon and bail out if model icon is empty. | |
| 108 if (icon.isNull()) { | |
| 109 icon_->SetImage(NULL); | |
| 110 return; | |
| 111 } | |
| 112 | |
| 113 gfx::ImageSkia resized(gfx::ImageSkiaOperations::CreateResizedImage(icon, | |
| 114 skia::ImageOperations::RESIZE_BEST, icon_size_)); | |
| 115 gfx::ImageSkia shadow( | |
| 116 gfx::ImageSkiaOperations::CreateImageWithDropShadow(resized, | |
| 117 icon_shadows_)); | |
| 118 icon_->SetImage(shadow); | |
| 119 } | |
| 120 | |
| 121 void AppListItemView::SetUIState(UIState state) { | |
| 122 if (ui_state_ == state) | |
| 123 return; | |
| 124 | |
| 125 ui_state_ = state; | |
| 126 | |
| 127 #if defined(USE_AURA) | |
| 128 ui::ScopedLayerAnimationSettings settings(layer()->GetAnimator()); | |
| 129 switch (ui_state_) { | |
| 130 case UI_STATE_NORMAL: | |
| 131 title_->SetVisible(true); | |
| 132 layer()->SetTransform(gfx::Transform()); | |
| 133 break; | |
| 134 case UI_STATE_DRAGGING: | |
| 135 title_->SetVisible(false); | |
| 136 const gfx::Rect bounds(layer()->bounds().size()); | |
| 137 layer()->SetTransform(gfx::GetScaleTransform( | |
| 138 bounds.CenterPoint(), | |
| 139 kDraggingIconScale)); | |
| 140 break; | |
| 141 } | |
| 142 #endif | |
| 143 } | |
| 144 | |
| 145 void AppListItemView::SetTouchDragging(bool touch_dragging) { | |
| 146 if (touch_dragging_ == touch_dragging) | |
| 147 return; | |
| 148 | |
| 149 touch_dragging_ = touch_dragging; | |
| 150 SetUIState(touch_dragging_ ? UI_STATE_DRAGGING : UI_STATE_NORMAL); | |
| 151 } | |
| 152 | |
| 153 void AppListItemView::OnMouseDragTimer() { | |
| 154 DCHECK(apps_grid_view_->IsDraggedView(this)); | |
| 155 SetUIState(UI_STATE_DRAGGING); | |
| 156 } | |
| 157 | |
| 158 void AppListItemView::ItemIconChanged() { | |
| 159 UpdateIcon(); | |
| 160 } | |
| 161 | |
| 162 void AppListItemView::ItemTitleChanged() { | |
| 163 title_->SetText(UTF8ToUTF16(model_->title())); | |
| 164 } | |
| 165 | |
| 166 void AppListItemView::ItemHighlightedChanged() { | |
| 167 apps_grid_view_->EnsureViewVisible(this); | |
| 168 SchedulePaint(); | |
| 169 } | |
| 170 | |
| 171 std::string AppListItemView::GetClassName() const { | |
| 172 return kViewClassName; | |
| 173 } | |
| 174 | |
| 175 void AppListItemView::Layout() { | |
| 176 gfx::Rect rect(GetContentsBounds()); | |
| 177 | |
| 178 const int left_right_padding = kLeftRightPaddingChars * | |
| 179 title_->font().GetAverageCharacterWidth(); | |
| 180 rect.Inset(left_right_padding, kTopPadding, left_right_padding, 0); | |
| 181 const int y = rect.y(); | |
| 182 | |
| 183 gfx::Rect icon_bounds(rect.x(), y, rect.width(), icon_size_.height()); | |
| 184 icon_bounds.Inset(gfx::ShadowValue::GetMargin(icon_shadows_)); | |
| 185 icon_->SetBoundsRect(icon_bounds); | |
| 186 | |
| 187 const gfx::Size title_size = title_->GetPreferredSize(); | |
| 188 gfx::Rect title_bounds(rect.x() + (rect.width() - title_size.width()) / 2, | |
| 189 y + icon_size_.height() + kIconTitleSpacing, | |
| 190 title_size.width(), | |
| 191 title_size.height()); | |
| 192 title_bounds.Intersect(rect); | |
| 193 title_->SetBoundsRect(title_bounds); | |
| 194 } | |
| 195 | |
| 196 void AppListItemView::OnPaint(gfx::Canvas* canvas) { | |
| 197 if (apps_grid_view_->IsDraggedView(this)) | |
| 198 return; | |
| 199 | |
| 200 gfx::Rect rect(GetContentsBounds()); | |
| 201 | |
| 202 if (model_->highlighted()) { | |
| 203 canvas->FillRect(rect, kHighlightedColor); | |
| 204 } else if (hover_animation_->is_animating()) { | |
| 205 int alpha = SkColorGetA(kHoverAndPushedColor) * | |
| 206 hover_animation_->GetCurrentValue(); | |
| 207 canvas->FillRect(rect, SkColorSetA(kHoverAndPushedColor, alpha)); | |
| 208 } else if (state() == STATE_HOVERED || state() == STATE_PRESSED) { | |
| 209 canvas->FillRect(rect, kHoverAndPushedColor); | |
| 210 } else if (apps_grid_view_->IsSelectedView(this)) { | |
| 211 canvas->FillRect(rect, kSelectedColor); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 void AppListItemView::GetAccessibleState(ui::AccessibleViewState* state) { | |
| 216 state->role = ui::AccessibilityTypes::ROLE_PUSHBUTTON; | |
| 217 state->name = UTF8ToUTF16(model_->title()); | |
| 218 } | |
| 219 | |
| 220 void AppListItemView::ShowContextMenuForView(views::View* source, | |
| 221 const gfx::Point& point) { | |
| 222 ui::MenuModel* menu_model = model_->GetContextMenuModel(); | |
| 223 if (!menu_model) | |
| 224 return; | |
| 225 | |
| 226 views::MenuModelAdapter menu_adapter(menu_model); | |
| 227 context_menu_runner_.reset( | |
| 228 new views::MenuRunner(new views::MenuItemView(&menu_adapter))); | |
| 229 menu_adapter.BuildMenu(context_menu_runner_->GetMenu()); | |
| 230 if (context_menu_runner_->RunMenuAt( | |
| 231 GetWidget(), NULL, gfx::Rect(point, gfx::Size()), | |
| 232 views::MenuItemView::TOPLEFT, views::MenuRunner::HAS_MNEMONICS) == | |
| 233 views::MenuRunner::MENU_DELETED) | |
| 234 return; | |
| 235 } | |
| 236 | |
| 237 void AppListItemView::StateChanged() { | |
| 238 if (state() == STATE_HOVERED || state() == STATE_PRESSED) { | |
| 239 apps_grid_view_->SetSelectedView(this); | |
| 240 title_->SetEnabledColor(kTitleHoverColor); | |
| 241 } else { | |
| 242 apps_grid_view_->ClearSelectedView(this); | |
| 243 model_->SetHighlighted(false); | |
| 244 title_->SetEnabledColor(kTitleColor); | |
| 245 } | |
| 246 } | |
| 247 | |
| 248 bool AppListItemView::ShouldEnterPushedState(const ui::Event& event) { | |
| 249 // Don't enter pushed state for ET_GESTURE_TAP_DOWN so that hover gray | |
| 250 // background does not show up during scroll. | |
| 251 if (event.type() == ui::ET_GESTURE_TAP_DOWN) | |
| 252 return false; | |
| 253 | |
| 254 return views::CustomButton::ShouldEnterPushedState(event); | |
| 255 } | |
| 256 | |
| 257 bool AppListItemView::OnMousePressed(const ui::MouseEvent& event) { | |
| 258 CustomButton::OnMousePressed(event); | |
| 259 | |
| 260 if (!ShouldEnterPushedState(event)) | |
| 261 return true; | |
| 262 | |
| 263 apps_grid_view_->InitiateDrag(this, AppsGridView::MOUSE, event); | |
| 264 | |
| 265 if (apps_grid_view_->IsDraggedView(this)) { | |
| 266 mouse_drag_timer_.Start(FROM_HERE, | |
| 267 base::TimeDelta::FromMilliseconds(kMouseDragUIDelayInMs), | |
| 268 this, &AppListItemView::OnMouseDragTimer); | |
| 269 } | |
| 270 return true; | |
| 271 } | |
| 272 | |
| 273 void AppListItemView::OnMouseReleased(const ui::MouseEvent& event) { | |
| 274 CustomButton::OnMouseReleased(event); | |
| 275 apps_grid_view_->EndDrag(false); | |
| 276 mouse_drag_timer_.Stop(); | |
| 277 SetUIState(UI_STATE_NORMAL); | |
| 278 } | |
| 279 | |
| 280 void AppListItemView::OnMouseCaptureLost() { | |
| 281 CustomButton::OnMouseCaptureLost(); | |
| 282 apps_grid_view_->EndDrag(true); | |
| 283 mouse_drag_timer_.Stop(); | |
| 284 SetUIState(UI_STATE_NORMAL); | |
| 285 } | |
| 286 | |
| 287 bool AppListItemView::OnMouseDragged(const ui::MouseEvent& event) { | |
| 288 CustomButton::OnMouseDragged(event); | |
| 289 apps_grid_view_->UpdateDrag(this, AppsGridView::MOUSE, event); | |
| 290 | |
| 291 // Shows dragging UI when it's confirmed without waiting for the timer. | |
| 292 if (ui_state_ != UI_STATE_DRAGGING && | |
| 293 apps_grid_view_->dragging() && | |
| 294 apps_grid_view_->IsDraggedView(this)) { | |
| 295 mouse_drag_timer_.Stop(); | |
| 296 SetUIState(UI_STATE_DRAGGING); | |
| 297 } | |
| 298 return true; | |
| 299 } | |
| 300 | |
| 301 void AppListItemView::OnGestureEvent(ui::GestureEvent* event) { | |
| 302 switch (event->type()) { | |
| 303 case ui::ET_GESTURE_SCROLL_BEGIN: | |
| 304 if (touch_dragging_) { | |
| 305 apps_grid_view_->InitiateDrag(this, AppsGridView::TOUCH, *event); | |
| 306 event->SetHandled(); | |
| 307 } | |
| 308 break; | |
| 309 case ui::ET_GESTURE_SCROLL_UPDATE: | |
| 310 if (touch_dragging_) { | |
| 311 apps_grid_view_->UpdateDrag(this, AppsGridView::TOUCH, *event); | |
| 312 event->SetHandled(); | |
| 313 } | |
| 314 break; | |
| 315 case ui::ET_GESTURE_SCROLL_END: | |
| 316 case ui::ET_SCROLL_FLING_START: | |
| 317 if (touch_dragging_) { | |
| 318 SetTouchDragging(false); | |
| 319 apps_grid_view_->EndDrag(false); | |
| 320 event->SetHandled(); | |
| 321 } | |
| 322 break; | |
| 323 case ui::ET_GESTURE_LONG_PRESS: | |
| 324 if (!apps_grid_view_->has_dragged_view()) | |
| 325 SetTouchDragging(true); | |
| 326 event->SetHandled(); | |
| 327 break; | |
| 328 case ui::ET_GESTURE_END: | |
| 329 if (touch_dragging_) { | |
| 330 SetTouchDragging(false); | |
| 331 | |
| 332 gfx::Point location(event->location()); | |
| 333 ConvertPointToScreen(this, &location); | |
| 334 ShowContextMenu(location, true); | |
| 335 } | |
| 336 break; | |
| 337 default: | |
| 338 break; | |
| 339 } | |
| 340 if (!event->handled()) | |
| 341 CustomButton::OnGestureEvent(event); | |
| 342 } | |
| 343 | |
| 344 } // namespace app_list | |
| OLD | NEW |