Home | History | Annotate | Download | only in views
      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/views/contents_view.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/logging.h"
     10 #include "ui/app_list/app_list_constants.h"
     11 #include "ui/app_list/pagination_model.h"
     12 #include "ui/app_list/views/app_list_main_view.h"
     13 #include "ui/app_list/views/apps_grid_view.h"
     14 #include "ui/app_list/views/search_result_list_view.h"
     15 #include "ui/base/events/event.h"
     16 #include "ui/views/animation/bounds_animator.h"
     17 #include "ui/views/view_model.h"
     18 #include "ui/views/view_model_utils.h"
     19 
     20 namespace app_list {
     21 
     22 namespace {
     23 
     24 const int kPreferredIconDimension = 48;
     25 
     26 // Indexes of interesting views in ViewModel of ContentsView.
     27 const int kIndexAppsGrid = 0;
     28 const int kIndexSearchResults = 1;
     29 
     30 const int kMinMouseWheelToSwitchPage = 20;
     31 const int kMinScrollToSwitchPage = 20;
     32 const int kMinHorizVelocityToSwitchPage = 800;
     33 
     34 const double kFinishTransitionThreshold = 0.33;
     35 
     36 // Helpers to get certain child view from |model|.
     37 AppsGridView* GetAppsGridView(views::ViewModel* model) {
     38   return static_cast<AppsGridView*>(model->view_at(kIndexAppsGrid));
     39 }
     40 
     41 SearchResultListView* GetSearchResultListView(views::ViewModel* model) {
     42   return static_cast<SearchResultListView*>(
     43       model->view_at(kIndexSearchResults));
     44 }
     45 
     46 }  // namespace
     47 
     48 ContentsView::ContentsView(AppListMainView* app_list_main_view,
     49                            PaginationModel* pagination_model,
     50                            AppListModel* model)
     51     : show_state_(SHOW_APPS),
     52       pagination_model_(pagination_model),
     53       view_model_(new views::ViewModel),
     54       bounds_animator_(new views::BoundsAnimator(this)) {
     55   DCHECK(model);
     56   pagination_model_->SetTransitionDurations(
     57       kPageTransitionDurationInMs,
     58       kOverscrollPageTransitionDurationMs);
     59 
     60   apps_grid_view_ = new AppsGridView(app_list_main_view, pagination_model);
     61   apps_grid_view_->SetLayout(kPreferredIconDimension,
     62                              kPreferredCols,
     63                              kPreferredRows);
     64   AddChildView(apps_grid_view_);
     65   view_model_->Add(apps_grid_view_, kIndexAppsGrid);
     66 
     67   SearchResultListView* search_results_view = new SearchResultListView(
     68       app_list_main_view);
     69   AddChildView(search_results_view);
     70   view_model_->Add(search_results_view, kIndexSearchResults);
     71 
     72   GetAppsGridView(view_model_.get())->SetModel(model);
     73   GetSearchResultListView(view_model_.get())->SetResults(model->results());
     74 }
     75 
     76 ContentsView::~ContentsView() {
     77 }
     78 
     79 void ContentsView::SetDragAndDropHostOfCurrentAppList(
     80     app_list::ApplicationDragAndDropHost* drag_and_drop_host) {
     81   apps_grid_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
     82 }
     83 
     84 void ContentsView::SetShowState(ShowState show_state) {
     85   if (show_state_ == show_state)
     86     return;
     87 
     88   show_state_ = show_state;
     89   ShowStateChanged();
     90 }
     91 
     92 void ContentsView::ShowStateChanged() {
     93   if (show_state_ == SHOW_SEARCH_RESULTS) {
     94     // TODO(xiyuan): Highlight default match instead of the first.
     95     SearchResultListView* results_view =
     96         GetSearchResultListView(view_model_.get());
     97     if (results_view->visible())
     98       results_view->SetSelectedIndex(0);
     99   }
    100 
    101   AnimateToIdealBounds();
    102 }
    103 
    104 void ContentsView::CalculateIdealBounds() {
    105   gfx::Rect rect(GetContentsBounds());
    106   if (rect.IsEmpty())
    107     return;
    108 
    109   gfx::Rect grid_frame(rect);
    110   gfx::Rect results_frame(rect);
    111 
    112   // Offsets apps grid and result list based on |show_state_|.
    113   // SearchResultListView is on top of apps grid. Visible view is left in
    114   // visible area and invisible ones is put out of the visible area.
    115   int contents_area_height = rect.height();
    116   switch (show_state_) {
    117     case SHOW_APPS:
    118       results_frame.Offset(0, -contents_area_height);
    119       break;
    120     case SHOW_SEARCH_RESULTS:
    121       grid_frame.Offset(0, contents_area_height);
    122       break;
    123     default:
    124       NOTREACHED() << "Unknown show_state_ " << show_state_;
    125       break;
    126   }
    127 
    128   view_model_->set_ideal_bounds(kIndexAppsGrid, grid_frame);
    129   view_model_->set_ideal_bounds(kIndexSearchResults, results_frame);
    130 }
    131 
    132 void ContentsView::AnimateToIdealBounds() {
    133   CalculateIdealBounds();
    134   for (int i = 0; i < view_model_->view_size(); ++i) {
    135     bounds_animator_->AnimateViewTo(view_model_->view_at(i),
    136                                     view_model_->ideal_bounds(i));
    137   }
    138 }
    139 
    140 void ContentsView::ShowSearchResults(bool show) {
    141   SetShowState(show ? SHOW_SEARCH_RESULTS : SHOW_APPS);
    142 }
    143 
    144 void ContentsView::Prerender() {
    145   const int selected_page = std::max(0, pagination_model_->selected_page());
    146   GetAppsGridView(view_model_.get())->Prerender(selected_page);
    147 }
    148 
    149 gfx::Size ContentsView::GetPreferredSize() {
    150   const gfx::Size grid_size =
    151       GetAppsGridView(view_model_.get())->GetPreferredSize();
    152   const gfx::Size results_size =
    153       GetSearchResultListView(view_model_.get())->GetPreferredSize();
    154 
    155   int width = std::max(grid_size.width(), results_size.width());
    156   int height = std::max(grid_size.height(), results_size.height());
    157   return gfx::Size(width, height);
    158 }
    159 
    160 void ContentsView::Layout() {
    161   CalculateIdealBounds();
    162   views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
    163 }
    164 
    165 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
    166   switch (show_state_) {
    167     case SHOW_APPS:
    168       return GetAppsGridView(view_model_.get())->OnKeyPressed(event);
    169     case SHOW_SEARCH_RESULTS:
    170       return GetSearchResultListView(view_model_.get())->OnKeyPressed(event);
    171     default:
    172       NOTREACHED() << "Unknown show state " << show_state_;
    173   }
    174   return false;
    175 }
    176 
    177 bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent& event) {
    178   if (show_state_ != SHOW_APPS)
    179     return false;
    180 
    181   int offset;
    182   if (abs(event.x_offset()) > abs(event.y_offset()))
    183     offset = event.x_offset();
    184   else
    185     offset = event.y_offset();
    186 
    187   if (abs(offset) > kMinMouseWheelToSwitchPage) {
    188     if (!pagination_model_->has_transition()) {
    189       pagination_model_->SelectPageRelative(
    190           offset > 0 ? -1 : 1, true);
    191     }
    192     return true;
    193   }
    194 
    195   return false;
    196 }
    197 
    198 void ContentsView::OnGestureEvent(ui::GestureEvent* event) {
    199   if (show_state_ != SHOW_APPS)
    200     return;
    201 
    202   switch (event->type()) {
    203     case ui::ET_GESTURE_SCROLL_BEGIN:
    204       pagination_model_->StartScroll();
    205       event->SetHandled();
    206       return;
    207     case ui::ET_GESTURE_SCROLL_UPDATE:
    208       // event->details.scroll_x() > 0 means moving contents to right. That is,
    209       // transitioning to previous page.
    210       pagination_model_->UpdateScroll(
    211           event->details().scroll_x() / GetContentsBounds().width());
    212       event->SetHandled();
    213       return;
    214     case ui::ET_GESTURE_SCROLL_END:
    215       pagination_model_->EndScroll(pagination_model_->
    216           transition().progress < kFinishTransitionThreshold);
    217       event->SetHandled();
    218       return;
    219     case ui::ET_SCROLL_FLING_START: {
    220       pagination_model_->EndScroll(true);
    221       if (fabs(event->details().velocity_x()) > kMinHorizVelocityToSwitchPage) {
    222         pagination_model_->SelectPageRelative(
    223             event->details().velocity_x() < 0 ? 1 : -1,
    224             true);
    225       }
    226       event->SetHandled();
    227       return;
    228     }
    229     default:
    230       break;
    231   }
    232 }
    233 
    234 void ContentsView::OnScrollEvent(ui::ScrollEvent* event) {
    235   if (show_state_ != SHOW_APPS ||
    236       event->type() == ui::ET_SCROLL_FLING_CANCEL) {
    237     return;
    238   }
    239 
    240   float offset;
    241   if (abs(event->x_offset()) > abs(event->y_offset()))
    242     offset = event->x_offset();
    243   else
    244     offset = event->y_offset();
    245 
    246   if (abs(offset) > kMinScrollToSwitchPage) {
    247     if (!pagination_model_->has_transition()) {
    248       pagination_model_->SelectPageRelative(offset > 0 ? -1 : 1,
    249                                             true);
    250     }
    251     event->SetHandled();
    252     event->StopPropagation();
    253   }
    254 }
    255 
    256 }  // namespace app_list
    257