Views Omnibox: tolerate minor click-to-select-all dragging.
[chromium-blink-merge.git] / ui / app_list / views / contents_view.cc
blob8dab0942b296053e4f5bb11924d26871ca3ced1c
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.
5 #include "ui/app_list/views/contents_view.h"
7 #include <algorithm>
9 #include "base/logging.h"
10 #include "ui/app_list/app_list_constants.h"
11 #include "ui/app_list/app_list_view_delegate.h"
12 #include "ui/app_list/pagination_model.h"
13 #include "ui/app_list/views/app_list_main_view.h"
14 #include "ui/app_list/views/apps_container_view.h"
15 #include "ui/app_list/views/apps_grid_view.h"
16 #include "ui/app_list/views/search_result_list_view.h"
17 #include "ui/events/event.h"
18 #include "ui/views/animation/bounds_animator.h"
19 #include "ui/views/view_model.h"
20 #include "ui/views/view_model_utils.h"
22 namespace app_list {
24 namespace {
26 // Indexes of interesting views in ViewModel of ContentsView.
27 const int kIndexAppsContainer = 0;
28 const int kIndexSearchResults = 1;
30 const int kMinMouseWheelToSwitchPage = 20;
31 const int kMinScrollToSwitchPage = 20;
32 const int kMinHorizVelocityToSwitchPage = 800;
34 const double kFinishTransitionThreshold = 0.33;
36 AppsContainerView* GetAppsContainerView(views::ViewModel* model) {
37 return static_cast<AppsContainerView*>(model->view_at(kIndexAppsContainer));
40 SearchResultListView* GetSearchResultListView(views::ViewModel* model) {
41 return static_cast<SearchResultListView*>(
42 model->view_at(kIndexSearchResults));
45 } // namespace
47 ContentsView::ContentsView(AppListMainView* app_list_main_view,
48 PaginationModel* pagination_model,
49 AppListModel* model,
50 AppListViewDelegate* view_delegate)
51 : show_state_(SHOW_APPS),
52 pagination_model_(pagination_model),
53 view_delegate_(view_delegate),
54 view_model_(new views::ViewModel),
55 bounds_animator_(new views::BoundsAnimator(this)) {
56 DCHECK(model);
57 pagination_model_->SetTransitionDurations(
58 kPageTransitionDurationInMs,
59 kOverscrollPageTransitionDurationMs);
61 content::WebContents* start_page_contents =
62 view_delegate ? view_delegate->GetStartPageContents() : NULL;
63 apps_container_view_ = new AppsContainerView(
64 app_list_main_view, pagination_model, model, start_page_contents);
65 AddChildView(apps_container_view_);
66 view_model_->Add(apps_container_view_, kIndexAppsContainer);
68 SearchResultListView* search_results_view = new SearchResultListView(
69 app_list_main_view);
70 AddChildView(search_results_view);
71 view_model_->Add(search_results_view, kIndexSearchResults);
73 GetSearchResultListView(view_model_.get())->SetResults(model->results());
76 ContentsView::~ContentsView() {
79 void ContentsView::CancelDrag() {
80 if (apps_container_view_->apps_grid_view()->has_dragged_view())
81 apps_container_view_->apps_grid_view()->EndDrag(true);
84 void ContentsView::SetDragAndDropHostOfCurrentAppList(
85 ApplicationDragAndDropHost* drag_and_drop_host) {
86 apps_container_view_->apps_grid_view()->
87 SetDragAndDropHostOfCurrentAppList(drag_and_drop_host);
90 void ContentsView::SetShowState(ShowState show_state) {
91 if (show_state_ == show_state)
92 return;
94 show_state_ = show_state;
95 ShowStateChanged();
98 void ContentsView::ShowStateChanged() {
99 if (show_state_ == SHOW_SEARCH_RESULTS) {
100 // TODO(xiyuan): Highlight default match instead of the first.
101 SearchResultListView* results_view =
102 GetSearchResultListView(view_model_.get());
103 if (results_view->visible())
104 results_view->SetSelectedIndex(0);
107 AnimateToIdealBounds();
110 void ContentsView::CalculateIdealBounds() {
111 gfx::Rect rect(GetContentsBounds());
112 if (rect.IsEmpty())
113 return;
115 gfx::Rect container_frame(rect);
116 gfx::Rect results_frame(rect);
118 // Offsets apps grid and result list based on |show_state_|.
119 // SearchResultListView is on top of apps grid. Visible view is left in
120 // visible area and invisible ones is put out of the visible area.
121 int contents_area_height = rect.height();
122 switch (show_state_) {
123 case SHOW_APPS:
124 results_frame.Offset(0, -contents_area_height);
125 break;
126 case SHOW_SEARCH_RESULTS:
127 container_frame.Offset(0, contents_area_height);
128 break;
129 default:
130 NOTREACHED() << "Unknown show_state_ " << show_state_;
131 break;
134 view_model_->set_ideal_bounds(kIndexAppsContainer, container_frame);
135 view_model_->set_ideal_bounds(kIndexSearchResults, results_frame);
138 void ContentsView::AnimateToIdealBounds() {
139 CalculateIdealBounds();
140 for (int i = 0; i < view_model_->view_size(); ++i) {
141 bounds_animator_->AnimateViewTo(view_model_->view_at(i),
142 view_model_->ideal_bounds(i));
146 void ContentsView::ShowSearchResults(bool show) {
147 SetShowState(show ? SHOW_SEARCH_RESULTS : SHOW_APPS);
148 SearchResultListView* results_view =
149 GetSearchResultListView(view_model_.get());
150 if (show)
151 results_view->SetAutoLaunchTimeout(view_delegate_->GetAutoLaunchTimeout());
152 else
153 results_view->CancelAutoLaunchTimeout();
156 void ContentsView::ShowFolderContent(AppListFolderItem* item) {
157 apps_container_view_->ShowActiveFolder(item);
160 void ContentsView::CancelAutoLaunch() {
161 GetSearchResultListView(view_model_.get())->CancelAutoLaunchTimeout();
162 view_delegate_->AutoLaunchCanceled();
165 void ContentsView::Prerender() {
166 const int selected_page = std::max(0, pagination_model_->selected_page());
167 apps_container_view_->apps_grid_view()->Prerender(selected_page);
170 gfx::Size ContentsView::GetPreferredSize() {
171 const gfx::Size container_size = GetAppsContainerView(view_model_.get())->
172 apps_grid_view()->GetPreferredSize();
173 const gfx::Size results_size =
174 GetSearchResultListView(view_model_.get())->GetPreferredSize();
176 int width = std::max(container_size.width(), results_size.width());
177 int height = std::max(container_size.height(), results_size.height());
178 return gfx::Size(width, height);
181 void ContentsView::Layout() {
182 CalculateIdealBounds();
183 views::ViewModelUtils::SetViewBoundsToIdealBounds(*view_model_);
186 bool ContentsView::OnKeyPressed(const ui::KeyEvent& event) {
187 CancelAutoLaunch();
188 switch (show_state_) {
189 case SHOW_APPS:
190 return GetAppsContainerView(view_model_.get())->OnKeyPressed(event);
191 case SHOW_SEARCH_RESULTS:
192 return GetSearchResultListView(view_model_.get())->OnKeyPressed(event);
193 default:
194 NOTREACHED() << "Unknown show state " << show_state_;
196 return false;
199 bool ContentsView::OnMouseWheel(const ui::MouseWheelEvent& event) {
200 if (show_state_ != SHOW_APPS)
201 return false;
203 int offset;
204 if (abs(event.x_offset()) > abs(event.y_offset()))
205 offset = event.x_offset();
206 else
207 offset = event.y_offset();
209 if (abs(offset) > kMinMouseWheelToSwitchPage) {
210 if (!pagination_model_->has_transition()) {
211 pagination_model_->SelectPageRelative(
212 offset > 0 ? -1 : 1, true);
214 return true;
217 return false;
220 void ContentsView::OnGestureEvent(ui::GestureEvent* event) {
221 if (show_state_ != SHOW_APPS)
222 return;
224 switch (event->type()) {
225 case ui::ET_GESTURE_SCROLL_BEGIN:
226 pagination_model_->StartScroll();
227 event->SetHandled();
228 return;
229 case ui::ET_GESTURE_SCROLL_UPDATE:
230 // event->details.scroll_x() > 0 means moving contents to right. That is,
231 // transitioning to previous page.
232 pagination_model_->UpdateScroll(
233 event->details().scroll_x() / GetContentsBounds().width());
234 event->SetHandled();
235 return;
236 case ui::ET_GESTURE_SCROLL_END:
237 pagination_model_->EndScroll(pagination_model_->
238 transition().progress < kFinishTransitionThreshold);
239 event->SetHandled();
240 return;
241 case ui::ET_SCROLL_FLING_START: {
242 pagination_model_->EndScroll(true);
243 if (fabs(event->details().velocity_x()) > kMinHorizVelocityToSwitchPage) {
244 pagination_model_->SelectPageRelative(
245 event->details().velocity_x() < 0 ? 1 : -1,
246 true);
248 event->SetHandled();
249 return;
251 default:
252 break;
256 void ContentsView::OnScrollEvent(ui::ScrollEvent* event) {
257 if (show_state_ != SHOW_APPS ||
258 event->type() == ui::ET_SCROLL_FLING_CANCEL) {
259 return;
262 float offset;
263 if (abs(event->x_offset()) > abs(event->y_offset()))
264 offset = event->x_offset();
265 else
266 offset = event->y_offset();
268 if (abs(offset) > kMinScrollToSwitchPage) {
269 if (!pagination_model_->has_transition()) {
270 pagination_model_->SelectPageRelative(offset > 0 ? -1 : 1,
271 true);
273 event->SetHandled();
274 event->StopPropagation();
278 } // namespace app_list