| 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/apps_grid_view.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "ui/app_list/app_list_item_view.h" | |
| 10 #include "ui/app_list/apps_grid_view_delegate.h" | |
| 11 #include "ui/app_list/page_switcher.h" | |
| 12 #include "ui/app_list/pagination_model.h" | |
| 13 #include "ui/app_list/pulsing_block_view.h" | |
| 14 #include "ui/base/animation/animation.h" | |
| 15 #include "ui/base/events/event.h" | |
| 16 #include "ui/views/border.h" | |
| 17 #include "ui/views/view_model_utils.h" | |
| 18 #include "ui/views/widget/widget.h" | |
| 19 | |
| 20 #if defined(USE_AURA) | |
| 21 #include "ui/aura/root_window.h" | |
| 22 #endif | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 // Padding space in pixels for fixed layout. | |
| 27 const int kLeftRightPadding = 20; | |
| 28 const int kTopPadding = 1; | |
| 29 | |
| 30 // Padding space in pixels between pages. | |
| 31 const int kPagePadding = 40; | |
| 32 | |
| 33 // Preferred tile size when showing in fixed layout. | |
| 34 const int kPreferredTileWidth = 88; | |
| 35 const int kPreferredTileHeight = 98; | |
| 36 | |
| 37 // Width in pixels of the area on the sides that triggers a page flip. | |
| 38 const int kPageFlipZoneSize = 40; | |
| 39 | |
| 40 // Delay in milliseconds to do the page flip. | |
| 41 const int kPageFlipDelayInMs = 1000; | |
| 42 | |
| 43 // RowMoveAnimationDelegate is used when moving an item into a different row. | |
| 44 // Before running the animation, the item's layer is re-created and kept in | |
| 45 // the original position, then the item is moved to just before its target | |
| 46 // position and opacity set to 0. When the animation runs, this delegate moves | |
| 47 // the layer and fades it out while fading in the item at the same time. | |
| 48 class RowMoveAnimationDelegate | |
| 49 : public views::BoundsAnimator::OwnedAnimationDelegate { | |
| 50 public: | |
| 51 RowMoveAnimationDelegate(views::View* view, | |
| 52 ui::Layer* layer, | |
| 53 const gfx::Rect& layer_target) | |
| 54 : view_(view), | |
| 55 layer_(layer), | |
| 56 layer_start_(layer ? layer->bounds() : gfx::Rect()), | |
| 57 layer_target_(layer_target) { | |
| 58 } | |
| 59 virtual ~RowMoveAnimationDelegate() {} | |
| 60 | |
| 61 // ui::AnimationDelegate overrides: | |
| 62 virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE { | |
| 63 view_->layer()->SetOpacity(animation->GetCurrentValue()); | |
| 64 view_->layer()->ScheduleDraw(); | |
| 65 | |
| 66 if (layer_) { | |
| 67 layer_->SetOpacity(1 - animation->GetCurrentValue()); | |
| 68 layer_->SetBounds(animation->CurrentValueBetween(layer_start_, | |
| 69 layer_target_)); | |
| 70 layer_->ScheduleDraw(); | |
| 71 } | |
| 72 } | |
| 73 virtual void AnimationEnded(const ui::Animation* animation) OVERRIDE { | |
| 74 view_->layer()->SetOpacity(1.0f); | |
| 75 view_->layer()->ScheduleDraw(); | |
| 76 } | |
| 77 virtual void AnimationCanceled(const ui::Animation* animation) OVERRIDE { | |
| 78 view_->layer()->SetOpacity(1.0f); | |
| 79 view_->layer()->ScheduleDraw(); | |
| 80 } | |
| 81 | |
| 82 private: | |
| 83 // The view that needs to be wrapped. Owned by views hierarchy. | |
| 84 views::View* view_; | |
| 85 | |
| 86 scoped_ptr<ui::Layer> layer_; | |
| 87 const gfx::Rect layer_start_; | |
| 88 const gfx::Rect layer_target_; | |
| 89 | |
| 90 DISALLOW_COPY_AND_ASSIGN(RowMoveAnimationDelegate); | |
| 91 }; | |
| 92 | |
| 93 } // namespace | |
| 94 | |
| 95 namespace app_list { | |
| 96 | |
| 97 AppsGridView::AppsGridView(AppsGridViewDelegate* delegate, | |
| 98 PaginationModel* pagination_model) | |
| 99 : model_(NULL), | |
| 100 delegate_(delegate), | |
| 101 pagination_model_(pagination_model), | |
| 102 page_switcher_view_(new PageSwitcher(pagination_model)), | |
| 103 cols_(0), | |
| 104 rows_per_page_(0), | |
| 105 selected_view_(NULL), | |
| 106 drag_view_(NULL), | |
| 107 drag_pointer_(NONE), | |
| 108 page_flip_target_(-1), | |
| 109 page_flip_delay_in_ms_(kPageFlipDelayInMs), | |
| 110 ALLOW_THIS_IN_INITIALIZER_LIST(bounds_animator_(this)) { | |
| 111 pagination_model_->AddObserver(this); | |
| 112 AddChildView(page_switcher_view_); | |
| 113 } | |
| 114 | |
| 115 AppsGridView::~AppsGridView() { | |
| 116 if (model_) { | |
| 117 model_->RemoveObserver(this); | |
| 118 model_->apps()->RemoveObserver(this); | |
| 119 } | |
| 120 pagination_model_->RemoveObserver(this); | |
| 121 } | |
| 122 | |
| 123 void AppsGridView::SetLayout(int icon_size, int cols, int rows_per_page) { | |
| 124 icon_size_.SetSize(icon_size, icon_size); | |
| 125 cols_ = cols; | |
| 126 rows_per_page_ = rows_per_page; | |
| 127 | |
| 128 set_border(views::Border::CreateEmptyBorder(kTopPadding, | |
| 129 kLeftRightPadding, | |
| 130 0, | |
| 131 kLeftRightPadding)); | |
| 132 } | |
| 133 | |
| 134 void AppsGridView::SetModel(AppListModel* model) { | |
| 135 if (model_) { | |
| 136 model_->RemoveObserver(this); | |
| 137 model_->apps()->RemoveObserver(this); | |
| 138 } | |
| 139 | |
| 140 model_ = model; | |
| 141 if (model_) { | |
| 142 model_->AddObserver(this); | |
| 143 model_->apps()->AddObserver(this); | |
| 144 } | |
| 145 Update(); | |
| 146 } | |
| 147 | |
| 148 void AppsGridView::SetSelectedView(views::View* view) { | |
| 149 if (IsSelectedView(view) || IsDraggedView(view)) | |
| 150 return; | |
| 151 | |
| 152 Index index = GetIndexOfView(view); | |
| 153 if (IsValidIndex(index)) | |
| 154 SetSelectedItemByIndex(index); | |
| 155 } | |
| 156 | |
| 157 void AppsGridView::ClearSelectedView(views::View* view) { | |
| 158 if (view && IsSelectedView(view)) { | |
| 159 selected_view_->SchedulePaint(); | |
| 160 selected_view_ = NULL; | |
| 161 } | |
| 162 } | |
| 163 | |
| 164 bool AppsGridView::IsSelectedView(const views::View* view) const { | |
| 165 return selected_view_ == view; | |
| 166 } | |
| 167 | |
| 168 void AppsGridView::EnsureViewVisible(const views::View* view) { | |
| 169 if (pagination_model_->has_transition()) | |
| 170 return; | |
| 171 | |
| 172 Index index = GetIndexOfView(view); | |
| 173 if (IsValidIndex(index)) | |
| 174 pagination_model_->SelectPage(index.page, false); | |
| 175 } | |
| 176 | |
| 177 void AppsGridView::InitiateDrag(views::View* view, | |
| 178 Pointer pointer, | |
| 179 const ui::LocatedEvent& event) { | |
| 180 if (drag_view_ || pulsing_blocks_model_.view_size()) | |
| 181 return; | |
| 182 | |
| 183 drag_view_ = view; | |
| 184 drag_start_ = event.location(); | |
| 185 } | |
| 186 | |
| 187 void AppsGridView::UpdateDrag(views::View* view, | |
| 188 Pointer pointer, | |
| 189 const ui::LocatedEvent& event) { | |
| 190 if (!dragging() && drag_view_ && | |
| 191 ExceededDragThreshold(event.location() - drag_start_)) { | |
| 192 drag_pointer_ = pointer; | |
| 193 // Move the view to the front so that it appears on top of other views. | |
| 194 ReorderChildView(drag_view_, -1); | |
| 195 bounds_animator_.StopAnimatingView(drag_view_); | |
| 196 } | |
| 197 if (drag_pointer_ != pointer) | |
| 198 return; | |
| 199 | |
| 200 ExtractDragLocation(event, &last_drag_point_); | |
| 201 | |
| 202 const Index last_drop_target = drop_target_; | |
| 203 CalculateDropTarget(last_drag_point_, false); | |
| 204 MaybeStartPageFlipTimer(last_drag_point_); | |
| 205 | |
| 206 gfx::Point page_switcher_point(last_drag_point_); | |
| 207 views::View::ConvertPointToTarget(this, page_switcher_view_, | |
| 208 &page_switcher_point); | |
| 209 page_switcher_view_->UpdateUIForDragPoint(page_switcher_point); | |
| 210 | |
| 211 if (last_drop_target != drop_target_) | |
| 212 AnimateToIdealBounds(); | |
| 213 drag_view_->SetPosition( | |
| 214 gfx::PointAtOffsetFromOrigin(last_drag_point_ - drag_start_)); | |
| 215 } | |
| 216 | |
| 217 void AppsGridView::EndDrag(bool cancel) { | |
| 218 if (!cancel && dragging() && drag_view_) { | |
| 219 CalculateDropTarget(last_drag_point_, true); | |
| 220 if (IsValidIndex(drop_target_)) | |
| 221 MoveItemInModel(drag_view_, drop_target_); | |
| 222 } | |
| 223 | |
| 224 drag_pointer_ = NONE; | |
| 225 drop_target_ = Index(); | |
| 226 if (drag_view_) { | |
| 227 drag_view_ = NULL; | |
| 228 AnimateToIdealBounds(); | |
| 229 } | |
| 230 | |
| 231 page_flip_timer_.Stop(); | |
| 232 page_flip_target_ = -1; | |
| 233 } | |
| 234 | |
| 235 bool AppsGridView::IsDraggedView(const views::View* view) const { | |
| 236 return drag_view_ == view; | |
| 237 } | |
| 238 | |
| 239 gfx::Size AppsGridView::GetPreferredSize() { | |
| 240 const gfx::Insets insets(GetInsets()); | |
| 241 const gfx::Size tile_size = gfx::Size(kPreferredTileWidth, | |
| 242 kPreferredTileHeight); | |
| 243 const int page_switcher_height = | |
| 244 page_switcher_view_->GetPreferredSize().height(); | |
| 245 return gfx::Size( | |
| 246 tile_size.width() * cols_ + insets.width(), | |
| 247 tile_size.height() * rows_per_page_ + | |
| 248 page_switcher_height + insets.height()); | |
| 249 } | |
| 250 | |
| 251 void AppsGridView::Layout() { | |
| 252 if (bounds_animator_.IsAnimating()) | |
| 253 bounds_animator_.Cancel(); | |
| 254 | |
| 255 CalculateIdealBounds(); | |
| 256 for (int i = 0; i < view_model_.view_size(); ++i) { | |
| 257 views::View* view = view_model_.view_at(i); | |
| 258 if (view != drag_view_) | |
| 259 view->SetBoundsRect(view_model_.ideal_bounds(i)); | |
| 260 } | |
| 261 views::ViewModelUtils::SetViewBoundsToIdealBounds(pulsing_blocks_model_); | |
| 262 | |
| 263 const int page_switcher_height = | |
| 264 page_switcher_view_->GetPreferredSize().height(); | |
| 265 gfx::Rect rect(GetContentsBounds()); | |
| 266 rect.set_y(rect.bottom() - page_switcher_height); | |
| 267 rect.set_height(page_switcher_height); | |
| 268 page_switcher_view_->SetBoundsRect(rect); | |
| 269 } | |
| 270 | |
| 271 bool AppsGridView::OnKeyPressed(const ui::KeyEvent& event) { | |
| 272 bool handled = false; | |
| 273 if (selected_view_) | |
| 274 handled = selected_view_->OnKeyPressed(event); | |
| 275 | |
| 276 if (!handled) { | |
| 277 switch (event.key_code()) { | |
| 278 case ui::VKEY_LEFT: | |
| 279 MoveSelected(0, -1); | |
| 280 return true; | |
| 281 case ui::VKEY_RIGHT: | |
| 282 MoveSelected(0, 1); | |
| 283 return true; | |
| 284 case ui::VKEY_UP: | |
| 285 MoveSelected(0, -cols_); | |
| 286 return true; | |
| 287 case ui::VKEY_DOWN: | |
| 288 MoveSelected(0, cols_); | |
| 289 return true; | |
| 290 case ui::VKEY_PRIOR: { | |
| 291 MoveSelected(-1, 0); | |
| 292 return true; | |
| 293 } | |
| 294 case ui::VKEY_NEXT: { | |
| 295 MoveSelected(1, 0); | |
| 296 return true; | |
| 297 } | |
| 298 default: | |
| 299 break; | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 return handled; | |
| 304 } | |
| 305 | |
| 306 bool AppsGridView::OnKeyReleased(const ui::KeyEvent& event) { | |
| 307 bool handled = false; | |
| 308 if (selected_view_) | |
| 309 handled = selected_view_->OnKeyReleased(event); | |
| 310 | |
| 311 return handled; | |
| 312 } | |
| 313 | |
| 314 void AppsGridView::ViewHierarchyChanged(bool is_add, | |
| 315 views::View* parent, | |
| 316 views::View* child) { | |
| 317 if (!is_add && parent == this) { | |
| 318 if (selected_view_ == child) | |
| 319 selected_view_ = NULL; | |
| 320 | |
| 321 if (drag_view_ == child) | |
| 322 EndDrag(true); | |
| 323 | |
| 324 bounds_animator_.StopAnimatingView(child); | |
| 325 } | |
| 326 } | |
| 327 | |
| 328 void AppsGridView::Update() { | |
| 329 DCHECK(!selected_view_ && !drag_view_); | |
| 330 | |
| 331 view_model_.Clear(); | |
| 332 if (model_ && model_->apps()->item_count()) | |
| 333 ListItemsAdded(0, model_->apps()->item_count()); | |
| 334 } | |
| 335 | |
| 336 void AppsGridView::UpdatePaging() { | |
| 337 if (!view_model_.view_size() || !tiles_per_page()) { | |
| 338 pagination_model_->SetTotalPages(0); | |
| 339 return; | |
| 340 } | |
| 341 | |
| 342 pagination_model_->SetTotalPages( | |
| 343 (view_model_.view_size() - 1) / tiles_per_page() + 1); | |
| 344 } | |
| 345 | |
| 346 void AppsGridView::UpdatePulsingBlockViews() { | |
| 347 const int available_slots = | |
| 348 tiles_per_page() - model_->apps()->item_count() % tiles_per_page(); | |
| 349 const int desired = model_->status() == AppListModel::STATUS_SYNCING ? | |
| 350 available_slots : 0; | |
| 351 | |
| 352 if (pulsing_blocks_model_.view_size() == desired) | |
| 353 return; | |
| 354 | |
| 355 while (pulsing_blocks_model_.view_size() > desired) { | |
| 356 views::View* view = pulsing_blocks_model_.view_at(0); | |
| 357 pulsing_blocks_model_.Remove(0); | |
| 358 delete view; | |
| 359 } | |
| 360 | |
| 361 while (pulsing_blocks_model_.view_size() < desired) { | |
| 362 views::View* view = new PulsingBlockView( | |
| 363 gfx::Size(kPreferredTileWidth, kPreferredTileHeight), true); | |
| 364 pulsing_blocks_model_.Add(view, 0); | |
| 365 AddChildView(view); | |
| 366 } | |
| 367 } | |
| 368 | |
| 369 views::View* AppsGridView::CreateViewForItemAtIndex(size_t index) { | |
| 370 DCHECK_LT(index, model_->apps()->item_count()); | |
| 371 AppListItemView* view = new AppListItemView(this, | |
| 372 model_->apps()->GetItemAt(index)); | |
| 373 view->SetIconSize(icon_size_); | |
| 374 #if defined(USE_AURA) | |
| 375 view->SetPaintToLayer(true); | |
| 376 view->SetFillsBoundsOpaquely(false); | |
| 377 #endif | |
| 378 return view; | |
| 379 } | |
| 380 | |
| 381 void AppsGridView::SetSelectedItemByIndex(const Index& index) { | |
| 382 if (GetIndexOfView(selected_view_) == index) | |
| 383 return; | |
| 384 | |
| 385 views::View* new_selection = GetViewAtIndex(index); | |
| 386 if (!new_selection) | |
| 387 return; // Keep current selection. | |
| 388 | |
| 389 if (selected_view_) | |
| 390 selected_view_->SchedulePaint(); | |
| 391 | |
| 392 selected_view_ = new_selection; | |
| 393 EnsureViewVisible(selected_view_); | |
| 394 selected_view_->SchedulePaint(); | |
| 395 if (GetWidget()) { | |
| 396 GetWidget()->NotifyAccessibilityEvent( | |
| 397 selected_view_, ui::AccessibilityTypes::EVENT_FOCUS, true); | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 bool AppsGridView::IsValidIndex(const Index& index) const { | |
| 402 return index.page >= 0 && index.page < pagination_model_->total_pages() && | |
| 403 index.slot >= 0 && index.slot < tiles_per_page() && | |
| 404 index.page * tiles_per_page() + index.slot < view_model_.view_size(); | |
| 405 } | |
| 406 | |
| 407 AppsGridView::Index AppsGridView::GetIndexOfView( | |
| 408 const views::View* view) const { | |
| 409 const int model_index = view_model_.GetIndexOfView(view); | |
| 410 if (model_index == -1) | |
| 411 return Index(); | |
| 412 | |
| 413 return Index(model_index / tiles_per_page(), model_index % tiles_per_page()); | |
| 414 } | |
| 415 | |
| 416 views::View* AppsGridView::GetViewAtIndex(const Index& index) const { | |
| 417 if (!IsValidIndex(index)) | |
| 418 return NULL; | |
| 419 | |
| 420 const int model_index = index.page * tiles_per_page() + index.slot; | |
| 421 return view_model_.view_at(model_index); | |
| 422 } | |
| 423 | |
| 424 void AppsGridView::MoveSelected(int page_delta, int slot_delta) { | |
| 425 if (!selected_view_) | |
| 426 return SetSelectedItemByIndex(Index(0, 0)); | |
| 427 | |
| 428 const Index& selected = GetIndexOfView(selected_view_); | |
| 429 int target_slot = selected.slot + slot_delta; | |
| 430 if (target_slot < 0) { | |
| 431 page_delta += (target_slot + 1) / tiles_per_page() - 1; | |
| 432 target_slot = tiles_per_page() + (target_slot + 1) % tiles_per_page() - 1; | |
| 433 } else if (target_slot > tiles_per_page()) { | |
| 434 page_delta += target_slot / tiles_per_page(); | |
| 435 target_slot %= tiles_per_page(); | |
| 436 } | |
| 437 | |
| 438 int target_page = std::min(pagination_model_->total_pages() - 1, | |
| 439 std::max(selected.page + page_delta, 0)); | |
| 440 SetSelectedItemByIndex(Index(target_page, target_slot)); | |
| 441 } | |
| 442 | |
| 443 void AppsGridView::CalculateIdealBounds() { | |
| 444 gfx::Rect rect(GetContentsBounds()); | |
| 445 if (rect.IsEmpty()) | |
| 446 return; | |
| 447 | |
| 448 gfx::Size tile_size(kPreferredTileWidth, kPreferredTileHeight); | |
| 449 | |
| 450 gfx::Rect grid_rect(gfx::Size(tile_size.width() * cols_, | |
| 451 tile_size.height() * rows_per_page_)); | |
| 452 grid_rect.Intersect(rect); | |
| 453 | |
| 454 // Page width including padding pixels. A tile.x + page_width means the same | |
| 455 // tile slot in the next page. | |
| 456 const int page_width = grid_rect.width() + kPagePadding; | |
| 457 | |
| 458 // If there is a transition, calculates offset for current and target page. | |
| 459 const int current_page = pagination_model_->selected_page(); | |
| 460 const PaginationModel::Transition& transition = | |
| 461 pagination_model_->transition(); | |
| 462 const bool is_valid = | |
| 463 pagination_model_->is_valid_page(transition.target_page); | |
| 464 | |
| 465 // Transition to right means negative offset. | |
| 466 const int dir = transition.target_page > current_page ? -1 : 1; | |
| 467 const int transition_offset = is_valid ? | |
| 468 transition.progress * page_width * dir : 0; | |
| 469 | |
| 470 const int total_views = | |
| 471 view_model_.view_size() + pulsing_blocks_model_.view_size(); | |
| 472 int slot_index = 0; | |
| 473 for (int i = 0; i < total_views; ++i) { | |
| 474 if (i < view_model_.view_size() && view_model_.view_at(i) == drag_view_) | |
| 475 continue; | |
| 476 | |
| 477 int page = slot_index / tiles_per_page(); | |
| 478 int slot = slot_index % tiles_per_page(); | |
| 479 | |
| 480 if (drop_target_.page == page && drop_target_.slot == slot) { | |
| 481 ++slot_index; | |
| 482 page = slot_index / tiles_per_page(); | |
| 483 slot = slot_index % tiles_per_page(); | |
| 484 } | |
| 485 | |
| 486 // Decides an x_offset for current item. | |
| 487 int x_offset = 0; | |
| 488 if (page < current_page) | |
| 489 x_offset = -page_width; | |
| 490 else if (page > current_page) | |
| 491 x_offset = page_width; | |
| 492 | |
| 493 if (is_valid) { | |
| 494 if (page == current_page || page == transition.target_page) | |
| 495 x_offset += transition_offset; | |
| 496 } | |
| 497 | |
| 498 const int row = slot / cols_; | |
| 499 const int col = slot % cols_; | |
| 500 gfx::Rect tile_slot( | |
| 501 gfx::Point(grid_rect.x() + col * tile_size.width() + x_offset, | |
| 502 grid_rect.y() + row * tile_size.height()), | |
| 503 tile_size); | |
| 504 if (i < view_model_.view_size()) { | |
| 505 view_model_.set_ideal_bounds(i, tile_slot); | |
| 506 } else { | |
| 507 pulsing_blocks_model_.set_ideal_bounds(i - view_model_.view_size(), | |
| 508 tile_slot); | |
| 509 } | |
| 510 | |
| 511 ++slot_index; | |
| 512 } | |
| 513 } | |
| 514 | |
| 515 void AppsGridView::AnimateToIdealBounds() { | |
| 516 const gfx::Rect visible_bounds(GetVisibleBounds()); | |
| 517 | |
| 518 CalculateIdealBounds(); | |
| 519 for (int i = 0; i < view_model_.view_size(); ++i) { | |
| 520 views::View* view = view_model_.view_at(i); | |
| 521 if (view == drag_view_) | |
| 522 continue; | |
| 523 | |
| 524 const gfx::Rect& target = view_model_.ideal_bounds(i); | |
| 525 if (bounds_animator_.GetTargetBounds(view) == target) | |
| 526 continue; | |
| 527 | |
| 528 const gfx::Rect& current = view->bounds(); | |
| 529 const bool current_visible = visible_bounds.Intersects(current); | |
| 530 const bool target_visible = visible_bounds.Intersects(target); | |
| 531 const bool visible = current_visible || target_visible; | |
| 532 | |
| 533 const int y_diff = target.y() - current.y(); | |
| 534 if (visible && y_diff && y_diff % kPreferredTileHeight == 0) { | |
| 535 AnimationBetweenRows(view, | |
| 536 current_visible, | |
| 537 current, | |
| 538 target_visible, | |
| 539 target); | |
| 540 } else { | |
| 541 bounds_animator_.AnimateViewTo(view, target); | |
| 542 } | |
| 543 } | |
| 544 } | |
| 545 | |
| 546 void AppsGridView::AnimationBetweenRows(views::View* view, | |
| 547 bool animate_current, | |
| 548 const gfx::Rect& current, | |
| 549 bool animate_target, | |
| 550 const gfx::Rect& target) { | |
| 551 // Determine page of |current| and |target|. -1 means in the left invisible | |
| 552 // page, 0 is the center visible page and 1 means in the right invisible page. | |
| 553 const int current_page = current.x() < 0 ? -1 : | |
| 554 current.x() >= width() ? 1 : 0; | |
| 555 const int target_page = target.x() < 0 ? -1 : | |
| 556 target.x() >= width() ? 1 : 0; | |
| 557 | |
| 558 const int dir = current_page < target_page || | |
| 559 (current_page == target_page && current.y() < target.y()) ? 1 : -1; | |
| 560 | |
| 561 #if defined(USE_AURA) | |
| 562 scoped_ptr<ui::Layer> layer; | |
| 563 if (animate_current) { | |
| 564 layer.reset(view->RecreateLayer()); | |
| 565 layer->SuppressPaint(); | |
| 566 | |
| 567 view->SetFillsBoundsOpaquely(false); | |
| 568 view->layer()->SetOpacity(0.f); | |
| 569 } | |
| 570 | |
| 571 gfx::Rect current_out(current); | |
| 572 current_out.Offset(dir * kPreferredTileWidth, 0); | |
| 573 #endif | |
| 574 | |
| 575 gfx::Rect target_in(target); | |
| 576 if (animate_target) | |
| 577 target_in.Offset(-dir * kPreferredTileWidth, 0); | |
| 578 view->SetBoundsRect(target_in); | |
| 579 bounds_animator_.AnimateViewTo(view, target); | |
| 580 | |
| 581 #if defined(USE_AURA) | |
| 582 bounds_animator_.SetAnimationDelegate( | |
| 583 view, | |
| 584 new RowMoveAnimationDelegate(view, layer.release(), current_out), | |
| 585 true); | |
| 586 #endif | |
| 587 } | |
| 588 | |
| 589 void AppsGridView::ExtractDragLocation(const ui::LocatedEvent& event, | |
| 590 gfx::Point* drag_point) { | |
| 591 #if defined(USE_AURA) | |
| 592 // Use root location of |event| instead of location in |drag_view_|'s | |
| 593 // coordinates because |drag_view_| has a scale transform and location | |
| 594 // could have integer round error and causes jitter. | |
| 595 *drag_point = event.root_location(); | |
| 596 | |
| 597 // GetWidget() could be NULL for tests. | |
| 598 if (GetWidget()) { | |
| 599 aura::Window::ConvertPointToTarget( | |
| 600 GetWidget()->GetNativeWindow()->GetRootWindow(), | |
| 601 GetWidget()->GetNativeWindow(), | |
| 602 drag_point); | |
| 603 } | |
| 604 | |
| 605 views::View::ConvertPointFromWidget(this, drag_point); | |
| 606 #else | |
| 607 // For non-aura, root location is not clearly defined but |drag_view_| does | |
| 608 // not have the scale transform. So no round error would be introduced and | |
| 609 // it's okay to use View::ConvertPointToTarget. | |
| 610 *drag_point = event.location(); | |
| 611 views::View::ConvertPointToTarget(drag_view_, this, drag_point); | |
| 612 #endif | |
| 613 } | |
| 614 | |
| 615 void AppsGridView::CalculateDropTarget(const gfx::Point& drag_point, | |
| 616 bool use_page_button_hovering) { | |
| 617 const int current_page = pagination_model_->selected_page(); | |
| 618 | |
| 619 if (use_page_button_hovering && | |
| 620 page_switcher_view_->bounds().Contains(drag_point)) { | |
| 621 gfx::Point page_switcher_point(drag_point); | |
| 622 views::View::ConvertPointToTarget(this, page_switcher_view_, | |
| 623 &page_switcher_point); | |
| 624 int page = page_switcher_view_->GetPageForPoint(page_switcher_point); | |
| 625 if (pagination_model_->is_valid_page(page)) { | |
| 626 drop_target_.page = page; | |
| 627 drop_target_.slot = tiles_per_page() - 1; | |
| 628 } | |
| 629 } else { | |
| 630 const int drop_row = drag_point.y() / kPreferredTileHeight; | |
| 631 const int drop_col = std::min(cols_ - 1, | |
| 632 drag_point.x() / kPreferredTileWidth); | |
| 633 | |
| 634 drop_target_.page = current_page; | |
| 635 drop_target_.slot = std::max(0, std::min( | |
| 636 tiles_per_page() - 1, | |
| 637 drop_row * cols_ + drop_col)); | |
| 638 } | |
| 639 | |
| 640 // Limits to the last possible slot on last page. | |
| 641 if (drop_target_.page == pagination_model_->total_pages() - 1) { | |
| 642 drop_target_.slot = std::min( | |
| 643 (view_model_.view_size() - 1) % tiles_per_page(), | |
| 644 drop_target_.slot); | |
| 645 } | |
| 646 } | |
| 647 | |
| 648 void AppsGridView::MaybeStartPageFlipTimer(const gfx::Point& drag_point) { | |
| 649 int new_page_flip_target = -1; | |
| 650 | |
| 651 if (page_switcher_view_->bounds().Contains(drag_point)) { | |
| 652 gfx::Point page_switcher_point(drag_point); | |
| 653 views::View::ConvertPointToTarget(this, page_switcher_view_, | |
| 654 &page_switcher_point); | |
| 655 new_page_flip_target = | |
| 656 page_switcher_view_->GetPageForPoint(page_switcher_point); | |
| 657 } | |
| 658 | |
| 659 // TODO(xiyuan): Fix this for RTL. | |
| 660 if (new_page_flip_target == -1 && drag_point.x() < kPageFlipZoneSize) | |
| 661 new_page_flip_target = pagination_model_->selected_page() - 1; | |
| 662 | |
| 663 if (new_page_flip_target == -1 && | |
| 664 drag_point.x() > width() - kPageFlipZoneSize) { | |
| 665 new_page_flip_target = pagination_model_->selected_page() + 1; | |
| 666 } | |
| 667 | |
| 668 if (new_page_flip_target == page_flip_target_) | |
| 669 return; | |
| 670 | |
| 671 if (pagination_model_->is_valid_page(new_page_flip_target)) { | |
| 672 page_flip_target_ = new_page_flip_target; | |
| 673 page_flip_timer_.Stop(); | |
| 674 | |
| 675 if (page_flip_target_ != pagination_model_->selected_page()) { | |
| 676 page_flip_timer_.Start(FROM_HERE, | |
| 677 base::TimeDelta::FromMilliseconds(page_flip_delay_in_ms_), | |
| 678 this, &AppsGridView::OnPageFlipTimer); | |
| 679 } | |
| 680 } else { | |
| 681 page_flip_target_ = -1; | |
| 682 page_flip_timer_.Stop(); | |
| 683 } | |
| 684 } | |
| 685 | |
| 686 void AppsGridView::OnPageFlipTimer() { | |
| 687 DCHECK(pagination_model_->is_valid_page(page_flip_target_)); | |
| 688 pagination_model_->SelectPage(page_flip_target_, true); | |
| 689 } | |
| 690 | |
| 691 void AppsGridView::MoveItemInModel(views::View* item_view, | |
| 692 const Index& target) { | |
| 693 int current_model_index = view_model_.GetIndexOfView(item_view); | |
| 694 DCHECK_GE(current_model_index, 0); | |
| 695 | |
| 696 int target_model_index = target.page * tiles_per_page() + target.slot; | |
| 697 if (target_model_index == current_model_index) | |
| 698 return; | |
| 699 | |
| 700 model_->apps()->RemoveObserver(this); | |
| 701 model_->apps()->Move(current_model_index, target_model_index); | |
| 702 view_model_.Move(current_model_index, target_model_index); | |
| 703 model_->apps()->AddObserver(this); | |
| 704 | |
| 705 if (pagination_model_->selected_page() != target.page) | |
| 706 pagination_model_->SelectPage(target.page, false); | |
| 707 } | |
| 708 | |
| 709 void AppsGridView::ButtonPressed(views::Button* sender, | |
| 710 const ui::Event& event) { | |
| 711 if (dragging()) | |
| 712 return; | |
| 713 | |
| 714 if (sender->GetClassName() != AppListItemView::kViewClassName) | |
| 715 return; | |
| 716 | |
| 717 if (delegate_) { | |
| 718 delegate_->ActivateApp(static_cast<AppListItemView*>(sender)->model(), | |
| 719 event.flags()); | |
| 720 } | |
| 721 } | |
| 722 | |
| 723 void AppsGridView::ListItemsAdded(size_t start, size_t count) { | |
| 724 EndDrag(true); | |
| 725 | |
| 726 for (size_t i = start; i < start + count; ++i) { | |
| 727 views::View* view = CreateViewForItemAtIndex(i); | |
| 728 view_model_.Add(view, i); | |
| 729 AddChildView(view); | |
| 730 } | |
| 731 | |
| 732 UpdatePaging(); | |
| 733 UpdatePulsingBlockViews(); | |
| 734 Layout(); | |
| 735 SchedulePaint(); | |
| 736 } | |
| 737 | |
| 738 void AppsGridView::ListItemsRemoved(size_t start, size_t count) { | |
| 739 EndDrag(true); | |
| 740 | |
| 741 for (size_t i = 0; i < count; ++i) { | |
| 742 views::View* view = view_model_.view_at(start); | |
| 743 view_model_.Remove(start); | |
| 744 delete view; | |
| 745 } | |
| 746 | |
| 747 UpdatePaging(); | |
| 748 UpdatePulsingBlockViews(); | |
| 749 Layout(); | |
| 750 SchedulePaint(); | |
| 751 } | |
| 752 | |
| 753 void AppsGridView::ListItemMoved(size_t index, size_t target_index) { | |
| 754 EndDrag(true); | |
| 755 view_model_.Move(index, target_index); | |
| 756 | |
| 757 UpdatePaging(); | |
| 758 AnimateToIdealBounds(); | |
| 759 } | |
| 760 | |
| 761 void AppsGridView::ListItemsChanged(size_t start, size_t count) { | |
| 762 NOTREACHED(); | |
| 763 } | |
| 764 | |
| 765 void AppsGridView::TotalPagesChanged() { | |
| 766 } | |
| 767 | |
| 768 void AppsGridView::SelectedPageChanged(int old_selected, int new_selected) { | |
| 769 if (dragging()) { | |
| 770 CalculateDropTarget(last_drag_point_, true); | |
| 771 Layout(); | |
| 772 MaybeStartPageFlipTimer(last_drag_point_); | |
| 773 } else { | |
| 774 Layout(); | |
| 775 } | |
| 776 } | |
| 777 | |
| 778 void AppsGridView::TransitionChanged() { | |
| 779 // Update layout for valid page transition only since over-scroll no longer | |
| 780 // animates app icons. | |
| 781 const PaginationModel::Transition& transition = | |
| 782 pagination_model_->transition(); | |
| 783 if (pagination_model_->is_valid_page(transition.target_page)) | |
| 784 Layout(); | |
| 785 } | |
| 786 | |
| 787 void AppsGridView::OnAppListModelStatusChanged() { | |
| 788 UpdatePulsingBlockViews(); | |
| 789 Layout(); | |
| 790 SchedulePaint(); | |
| 791 } | |
| 792 | |
| 793 } // namespace app_list | |
| OLD | NEW |