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 "grit/ui_resources.h"
     11 #include "ui/app_list/app_list_constants.h"
     12 #include "ui/app_list/app_list_switches.h"
     13 #include "ui/app_list/app_list_view_delegate.h"
     14 #include "ui/app_list/views/app_list_folder_view.h"
     15 #include "ui/app_list/views/app_list_main_view.h"
     16 #include "ui/app_list/views/apps_container_view.h"
     17 #include "ui/app_list/views/apps_grid_view.h"
     18 #include "ui/app_list/views/contents_switcher_view.h"
     19 #include "ui/app_list/views/search_result_list_view.h"
     20 #include "ui/app_list/views/start_page_view.h"
     21 #include "ui/base/resource/resource_bundle.h"
     22 #include "ui/events/event.h"
     23 #include "ui/views/view_model.h"
     24 #include "ui/views/view_model_utils.h"
     25 
     26 namespace app_list {
     27 
     28 namespace {
     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 }  // namespace
     37 
     38 ContentsView::ContentsView(AppListMainView* app_list_main_view)
     39     : search_results_view_(NULL),
     40       start_page_view_(NULL),
     41       app_list_main_view_(app_list_main_view),
     42       contents_switcher_view_(NULL),
     43       view_model_(new views::ViewModel) {
     44   pagination_model_.AddObserver(this);
     45 }
     46 
     47 ContentsView::~ContentsView() {
     48   pagination_model_.RemoveObserver(this);
     49 }
     50 
     51 void ContentsView::InitNamedPages(AppListModel* model,
     52                                   AppListViewDelegate* view_delegate) {
     53   DCHECK(model);
     54 
     55   if (app_list::switches::IsExperimentalAppListEnabled()) {
     56     start_page_view_ = new StartPageView(app_list_main_view_, view_delegate);
     57     AddLauncherPage(
     58         start_page_view_, IDR_APP_LIST_SEARCH_ICON, NAMED_PAGE_START);
     59   } else {
     60     search_results_view_ =
     61         new SearchResultListView(app_list_main_view_, view_delegate);
     62     AddLauncherPage(search_results_view_, 0, NAMED_PAGE_SEARCH_RESULTS);
     63     search_results_view_->SetResults(model->results());
     64   }
     65 
     66   apps_container_view_ = new AppsContainerView(app_list_main_view_, model);
     67   int apps_page_index = AddLauncherPage(
     68       apps_container_view_, IDR_APP_LIST_APPS_ICON, NAMED_PAGE_APPS);
     69 
     70   pagination_model_.SelectPage(apps_page_index, false);
     71 }
     72 
     73 void ContentsView::CancelDrag() {
     74   if (apps_container_view_->apps_grid_view()->has_dragged_view())
     75     apps_container_view_->apps_grid_view()->EndDrag(true);
     76   if (apps_container_view_->app_list_folder_view()
     77           ->items_grid_view()
     78           ->has_dragged_view()) {
     79     apps_container_view_->app_list_folder_view()->items_grid_view()->EndDrag(
     80         true);
     81   }
     82 }
     83 
     84 void ContentsView::SetDragAndDropHostOfCurrentAppList(
     85     ApplicationDragAndDropHost* drag_and_drop_host) {
     86   apps_container_view_->SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
     87 }
     88 
     89 void ContentsView::SetActivePage(int page_index) {
     90   if (GetActivePageIndex() == page_index)
     91     return;
     92 
     93   SetActivePageInternal(page_index, false);
     94 }
     95 
     96 int ContentsView::GetActivePageIndex() const {
     97   // The active page is changed at the beginning of an animation, not the end.
     98   return pagination_model_.SelectedTargetPage();
     99 }
    100 
    101 bool ContentsView::IsNamedPageActive(NamedPage named_page) const {
    102   std::map<NamedPage, int>::const_iterator it =
    103       named_page_to_view_.find(named_page);
    104   if (it == named_page_to_view_.end())
    105     return false;
    106   return it->second == GetActivePageIndex();
    107 }
    108 
    109 int ContentsView::GetPageIndexForNamedPage(NamedPage named_page) const {
    110   // Find the index of the view corresponding to the given named_page.
    111   std::map<NamedPage, int>::const_iterator it =
    112       named_page_to_view_.find(named_page);
    113   // GetPageIndexForNamedPage should never be called on a named_page that does
    114   // not have a corresponding view.
    115   DCHECK(it != named_page_to_view_.end());
    116   return it->second;
    117 }
    118 
    119 int ContentsView::NumLauncherPages() const {
    120   return pagination_model_.total_pages();
    121 }
    122 
    123 void ContentsView::SetActivePageInternal(int page_index,
    124                                          bool show_search_results) {
    125   // Start animating to the new page.
    126   pagination_model_.SelectPage(page_index, true);
    127   ActivePageChanged(show_search_results);
    128 }
    129 
    130 void ContentsView::ActivePageChanged(bool show_search_results) {
    131   // TODO(xiyuan): Highlight default match instead of the first.
    132   if (IsNamedPageActive(NAMED_PAGE_SEARCH_RESULTS) &&
    133       search_results_view_->visible()) {
    134     search_results_view_->SetSelectedIndex(0);
    135   }
    136   if (search_results_view_)
    137     search_results_view_->UpdateAutoLaunchState();
    138 
    139   if (IsNamedPageActive(NAMED_PAGE_START)) {
    140     if (show_search_results)
    141       start_page_view_->ShowSearchResults();
    142     else
    143       start_page_view_->Reset();
    144   }
    145 
    146   // Notify parent AppListMainView of the page change.
    147   app_list_main_view_->UpdateSearchBoxVisibility();
    148 }
    149 
    150 void ContentsView::ShowSearchResults(bool show) {
    151   NamedPage new_named_page = show ? NAMED_PAGE_SEARCH_RESULTS : NAMED_PAGE_APPS;
    152   if (app_list::switches::IsExperimentalAppListEnabled())
    153     new_named_page = NAMED_PAGE_START;
    154 
    155   SetActivePageInternal(GetPageIndexForNamedPage(new_named_page), show);
    156 }
    157 
    158 bool ContentsView::IsShowingSearchResults() const {
    159   return app_list::switches::IsExperimentalAppListEnabled()
    160              ? IsNamedPageActive(NAMED_PAGE_START) &&
    161                    start_page_view_->IsShowingSearchResults()
    162              : IsNamedPageActive(NAMED_PAGE_SEARCH_RESULTS);
    163 }
    164 
    165 void ContentsView::UpdatePageBounds() {
    166   gfx::Rect rect(GetContentsBounds());
    167   if (rect.IsEmpty())
    168     return;
    169 
    170   // The bounds calculations will potentially be mid-transition (depending on
    171   // the state of the PaginationModel).
    172   int current_page = std::max(0, pagination_model_.selected_page());
    173   int target_page = current_page;
    174   double progress = 1;
    175   if (pagination_model_.has_transition()) {
    176     const PaginationModel::Transition& transition =
    177         pagination_model_.transition();
    178     if (pagination_model_.is_valid_page(transition.target_page)) {
    179       target_page = transition.target_page;
    180       progress = transition.progress;
    181     }
    182   }
    183 
    184   gfx::Rect incoming_target(rect);
    185   gfx::Rect outgoing_target(rect);
    186   int dir = target_page > current_page ? -1 : 1;
    187 
    188   if (app_list::switches::IsExperimentalAppListEnabled()) {
    189     // The experimental app list transitions horizontally.
    190     int page_width = rect.width();
    191     int transition_offset = progress * page_width * dir;
    192 
    193     outgoing_target.set_x(transition_offset);
    194     incoming_target.set_x(dir < 0 ? transition_offset + page_width
    195                                   : transition_offset - page_width);
    196   } else {
    197     // The normal app list transitions vertically.
    198     int page_height = rect.height();
    199     int transition_offset = progress * page_height * dir;
    200 
    201     outgoing_target.set_y(transition_offset);
    202     incoming_target.set_y(dir < 0 ? transition_offset + page_height
    203                                   : transition_offset - page_height);
    204   }
    205 
    206   view_model_->view_at(current_page)->SetBoundsRect(outgoing_target);
    207   view_model_->view_at(target_page)->SetBoundsRect(incoming_target);
    208 }
    209 
    210 PaginationModel* ContentsView::GetAppsPaginationModel() {
    211   return apps_container_view_->apps_grid_view()->pagination_model();
    212 }
    213 
    214 void ContentsView::ShowFolderContent(AppListFolderItem* item) {
    215   apps_container_view_->ShowActiveFolder(item);
    216 }
    217 
    218 void ContentsView::Prerender() {
    219   const int selected_page =
    220       std::max(0, GetAppsPaginationModel()->selected_page());
    221   apps_container_view_->apps_grid_view()->Prerender(selected_page);
    222 }
    223 
    224 views::View* ContentsView::GetPageView(int index) {
    225   return view_model_->view_at(index);
    226 }
    227 
    228 void ContentsView::AddBlankPageForTesting() {
    229   AddLauncherPage(new views::View, 0);
    230 }
    231 
    232 int ContentsView::AddLauncherPage(views::View* view, int resource_id) {
    233   int page_index = view_model_->view_size();
    234   AddChildView(view);
    235   view_model_->Add(view, page_index);
    236   pagination_model_.SetTotalPages(view_model_->view_size());
    237   if (contents_switcher_view_)
    238     contents_switcher_view_->AddSwitcherButton(resource_id, page_index);
    239   return page_index;
    240 }
    241 
    242 int ContentsView::AddLauncherPage(views::View* view,
    243                                   int resource_id,
    244                                   NamedPage named_page) {
    245   int page_index = AddLauncherPage(view, resource_id);
    246   named_page_to_view_.insert(std::pair<NamedPage, int>(named_page, page_index));
    247   return page_index;
    248 }
    249 
    250 gfx::Size ContentsView::GetPreferredSize() const {
    251   const gfx::Size container_size =
    252       apps_container_view_->apps_grid_view()->GetPreferredSize();
    253   const gfx::Size results_size = search_results_view_
    254                                      ? search_results_view_->GetPreferredSize()
    255                                      : gfx::Size();
    256 
    257   int width = std::max(container_size.width(), results_size.width());
    258   int height = std::max(container_size.height(), results_size.height());
    259   return gfx::Size(width, height);
    260 }
    261 
    262 void ContentsView::Layout() {
    263   // Immediately finish all current animations.
    264   pagination_model_.FinishAnimation();
    265 
    266   // Move the current view onto the screen, and all other views off screen to
    267   // the left. (Since we are not animating, we don't need to be careful about
    268   // which side we place the off-screen views onto.)
    269   gfx::Rect rect(GetContentsBounds());
    270   if (rect.IsEmpty())
    271     return;
    272 
    273   gfx::Rect offscreen_target(rect);
    274   offscreen_target.set_x(-rect.width());
    275 
    276   for (int i = 0; i < view_model_->view_size(); ++i) {
    277     view_model_->view_at(i)->SetBoundsRect(
    278         i == pagination_model_.SelectedTargetPage() ? rect : offscreen_target);
    279   }
    280 }
    281 
    282 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
    283   return view_model_->view_at(GetActivePageIndex())->OnKeyPressed(event);
    284 }
    285 
    286 bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent& event) {
    287   if (!IsNamedPageActive(NAMED_PAGE_APPS))
    288     return false;
    289 
    290   int offset;
    291   if (abs(event.x_offset()) > abs(event.y_offset()))
    292     offset = event.x_offset();
    293   else
    294     offset = event.y_offset();
    295 
    296   if (abs(offset) > kMinMouseWheelToSwitchPage) {
    297     if (!GetAppsPaginationModel()->has_transition()) {
    298       GetAppsPaginationModel()->SelectPageRelative(offset > 0 ? -1 : 1, true);
    299     }
    300     return true;
    301   }
    302 
    303   return false;
    304 }
    305 
    306 void ContentsView::TotalPagesChanged() {
    307 }
    308 
    309 void ContentsView::SelectedPageChanged(int old_selected, int new_selected) {
    310 }
    311 
    312 void ContentsView::TransitionStarted() {
    313 }
    314 
    315 void ContentsView::TransitionChanged() {
    316   UpdatePageBounds();
    317 }
    318 
    319 void ContentsView::OnGestureEvent(ui::GestureEvent* event) {
    320   if (!IsNamedPageActive(NAMED_PAGE_APPS))
    321     return;
    322 
    323   switch (event->type()) {
    324     case ui::ET_GESTURE_SCROLL_BEGIN:
    325       GetAppsPaginationModel()->StartScroll();
    326       event->SetHandled();
    327       return;
    328     case ui::ET_GESTURE_SCROLL_UPDATE:
    329       // event->details.scroll_x() > 0 means moving contents to right. That is,
    330       // transitioning to previous page.
    331       GetAppsPaginationModel()->UpdateScroll(event->details().scroll_x() /
    332                                              GetContentsBounds().width());
    333       event->SetHandled();
    334       return;
    335     case ui::ET_GESTURE_SCROLL_END:
    336       GetAppsPaginationModel()->EndScroll(
    337           GetAppsPaginationModel()->transition().progress <
    338           kFinishTransitionThreshold);
    339       event->SetHandled();
    340       return;
    341     case ui::ET_SCROLL_FLING_START: {
    342       GetAppsPaginationModel()->EndScroll(true);
    343       if (fabs(event->details().velocity_x()) > kMinHorizVelocityToSwitchPage) {
    344         GetAppsPaginationModel()->SelectPageRelative(
    345             event->details().velocity_x() < 0 ? 1 : -1, true);
    346       }
    347       event->SetHandled();
    348       return;
    349     }
    350     default:
    351       break;
    352   }
    353 }
    354 
    355 void ContentsView::OnScrollEvent(ui::ScrollEvent* event) {
    356   if (!IsNamedPageActive(NAMED_PAGE_APPS) ||
    357       event->type() == ui::ET_SCROLL_FLING_CANCEL) {
    358     return;
    359   }
    360 
    361   float offset;
    362   if (std::abs(event->x_offset()) > std::abs(event->y_offset()))
    363     offset = event->x_offset();
    364   else
    365     offset = event->y_offset();
    366 
    367   if (std::abs(offset) > kMinScrollToSwitchPage) {
    368     if (!GetAppsPaginationModel()->has_transition()) {
    369       GetAppsPaginationModel()->SelectPageRelative(offset > 0 ? -1 : 1, true);
    370     }
    371     event->SetHandled();
    372     event->StopPropagation();
    373   }
    374 }
    375 
    376 }  // namespace app_list
    377