Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / athena / home / athena_start_page_view.cc
blobf15e46b4b5b42eb2576eef78e26bf4839b93c2fb
1 // Copyright 2014 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 "athena/home/athena_start_page_view.h"
7 #include "athena/home/home_card_constants.h"
8 #include "base/bind.h"
9 #include "base/strings/string_util.h"
10 #include "third_party/skia/include/core/SkPaint.h"
11 #include "third_party/skia/include/core/SkPath.h"
12 #include "ui/app_list/app_list_item.h"
13 #include "ui/app_list/app_list_item_list.h"
14 #include "ui/app_list/app_list_model.h"
15 #include "ui/app_list/app_list_view_delegate.h"
16 #include "ui/app_list/search_box_model.h"
17 #include "ui/app_list/views/search_box_view.h"
18 #include "ui/app_list/views/search_result_list_view.h"
19 #include "ui/compositor/closure_animation_observer.h"
20 #include "ui/compositor/scoped_layer_animation_settings.h"
21 #include "ui/gfx/canvas.h"
22 #include "ui/views/background.h"
23 #include "ui/views/border.h"
24 #include "ui/views/controls/textfield/textfield.h"
25 #include "ui/views/layout/box_layout.h"
26 #include "ui/views/layout/fill_layout.h"
27 #include "ui/views/round_rect_painter.h"
29 namespace {
31 const size_t kMaxIconNum = 3;
32 const int kIconSize = 50;
33 const int kIconMargin = 25;
35 const int kTopMargin = 100;
37 // Copied from ui/app_list/views/start_page_view.cc
38 const int kInstantContainerSpacing = 20;
39 const int kWebViewWidth = 500;
40 const int kWebViewHeight = 105;
41 const int kSearchBoxBorderWidth = 1;
42 const int kSearchBoxCornerRadius = 2;
43 const int kSearchBoxWidth = 490;
44 const int kSearchBoxHeight = 40;
46 class PlaceHolderButton : public views::ImageButton,
47 public views::ButtonListener {
48 public:
49 PlaceHolderButton()
50 : ImageButton(this) {
51 gfx::Canvas canvas(gfx::Size(kIconSize, kIconSize), 1.0f, true);
52 SkPaint paint;
53 paint.setStyle(SkPaint::kFill_Style);
54 paint.setColor(SkColorSetRGB(86, 119, 252));
55 paint.setFlags(SkPaint::kAntiAlias_Flag);
56 canvas.DrawCircle(
57 gfx::Point(kIconSize / 2, kIconSize / 2), kIconSize / 2, paint);
59 scoped_ptr<gfx::ImageSkia> image(
60 new gfx::ImageSkia(canvas.ExtractImageRep()));
61 SetImage(STATE_NORMAL, image.get());
64 private:
65 // views::ButtonListener:
66 virtual void ButtonPressed(views::Button* sender,
67 const ui::Event& event) OVERRIDE {
68 // Do nothing: remove these place holders.
71 DISALLOW_COPY_AND_ASSIGN(PlaceHolderButton);
74 class AppIconButton : public views::ImageButton,
75 public views::ButtonListener {
76 public:
77 explicit AppIconButton(app_list::AppListItem* item)
78 : ImageButton(this),
79 item_(item) {
80 // TODO(mukai): icon should be resized.
81 SetImage(STATE_NORMAL, &item->icon());
84 private:
85 // views::ButtonListener:
86 virtual void ButtonPressed(views::Button* sender,
87 const ui::Event& event) OVERRIDE {
88 DCHECK_EQ(sender, this);
89 item_->Activate(event.flags());
92 app_list::AppListItem* item_;
94 DISALLOW_COPY_AND_ASSIGN(AppIconButton);
97 // The background to paint the round rectangle of the view area.
98 class RoundRectBackground : public views::Background {
99 public:
100 RoundRectBackground(SkColor color, int corner_radius)
101 : color_(color),
102 corner_radius_(corner_radius) {}
103 virtual ~RoundRectBackground() {}
105 private:
106 // views::Background:
107 virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
108 SkPaint paint;
109 paint.setStyle(SkPaint::kFill_Style);
110 paint.setColor(color_);
111 canvas->DrawRoundRect(view->GetContentsBounds(), corner_radius_, paint);
114 SkColor color_;
115 int corner_radius_;
117 DISALLOW_COPY_AND_ASSIGN(RoundRectBackground);
120 class SearchBoxContainer : public views::View {
121 public:
122 explicit SearchBoxContainer(app_list::SearchBoxView* search_box)
123 : search_box_(search_box) {
124 search_box->set_background(
125 new RoundRectBackground(SK_ColorWHITE, kSearchBoxCornerRadius));
126 search_box->SetBorder(views::Border::CreateBorderPainter(
127 new views::RoundRectPainter(SK_ColorGRAY, kSearchBoxCornerRadius),
128 gfx::Insets(kSearchBoxBorderWidth, kSearchBoxBorderWidth,
129 kSearchBoxBorderWidth, kSearchBoxBorderWidth)));
130 SetLayoutManager(new views::FillLayout());
131 AddChildView(search_box_);
133 virtual ~SearchBoxContainer() {}
135 private:
136 // views::View:
137 virtual gfx::Size GetPreferredSize() const OVERRIDE {
138 return gfx::Size(kSearchBoxWidth, kSearchBoxHeight);
141 // Owned by the views hierarchy.
142 app_list::SearchBoxView* search_box_;
144 DISALLOW_COPY_AND_ASSIGN(SearchBoxContainer);
147 } // namespace
149 namespace athena {
151 // static
152 const char AthenaStartPageView::kViewClassName[] = "AthenaStartPageView";
154 AthenaStartPageView::LayoutData::LayoutData()
155 : logo_opacity(1.0f),
156 background_opacity(1.0f) {
159 AthenaStartPageView::AthenaStartPageView(
160 app_list::AppListViewDelegate* view_delegate)
161 : delegate_(view_delegate),
162 layout_state_(0.0f),
163 weak_factory_(this) {
164 background_ = new views::View();
165 background_->set_background(
166 views::Background::CreateSolidBackground(SK_ColorWHITE));
167 background_->SetPaintToLayer(true);
168 background_->SetFillsBoundsOpaquely(false);
169 AddChildView(background_);
171 logo_ = view_delegate->CreateStartPageWebView(
172 gfx::Size(kWebViewWidth, kWebViewHeight));
173 logo_->SetPaintToLayer(true);
174 logo_->SetFillsBoundsOpaquely(false);
175 logo_->SetSize(gfx::Size(kWebViewWidth, kWebViewHeight));
176 AddChildView(logo_);
178 search_results_view_ = new app_list::SearchResultListView(
179 NULL, view_delegate);
180 // search_results_view_'s size will shrink after settings results.
181 search_results_height_ = static_cast<views::View*>(
182 search_results_view_)->GetHeightForWidth(kSearchBoxWidth);
183 search_results_view_->SetResults(view_delegate->GetModel()->results());
185 search_results_view_->SetVisible(false);
186 search_results_view_->SetPaintToLayer(true);
187 search_results_view_->SetFillsBoundsOpaquely(false);
188 AddChildView(search_results_view_);
190 app_icon_container_ = new views::View();
191 AddChildView(app_icon_container_);
192 app_icon_container_->SetPaintToLayer(true);
193 app_icon_container_->layer()->SetFillsBoundsOpaquely(false);
194 app_icon_container_->SetLayoutManager(new views::BoxLayout(
195 views::BoxLayout::kHorizontal, 0, 0, kIconMargin));
196 app_list::AppListItemList* top_level =
197 view_delegate->GetModel()->top_level_item_list();
198 for (size_t i = 0; i < std::min(top_level->item_count(), kMaxIconNum); ++i)
199 app_icon_container_->AddChildView(new AppIconButton(top_level->item_at(i)));
200 app_icon_container_->SetSize(app_icon_container_->GetPreferredSize());
202 control_icon_container_ = new views::View();
203 control_icon_container_->SetPaintToLayer(true);
204 control_icon_container_->SetFillsBoundsOpaquely(false);
205 AddChildView(control_icon_container_);
206 control_icon_container_->SetLayoutManager(new views::BoxLayout(
207 views::BoxLayout::kHorizontal, 0, 0, kIconMargin));
208 for (size_t i = 0; i < kMaxIconNum; ++i)
209 control_icon_container_->AddChildView(new PlaceHolderButton());
210 control_icon_container_->SetSize(control_icon_container_->GetPreferredSize());
212 search_box_view_ = new app_list::SearchBoxView(this, view_delegate);
213 search_box_view_->set_contents_view(this);
214 search_box_container_ = new SearchBoxContainer(search_box_view_);
215 search_box_container_->SetPaintToLayer(true);
216 search_box_container_->SetFillsBoundsOpaquely(false);
217 search_box_container_->SetSize(search_box_container_->GetPreferredSize());
218 AddChildView(search_box_container_);
221 AthenaStartPageView::~AthenaStartPageView() {}
223 void AthenaStartPageView::RequestFocusOnSearchBox() {
224 search_box_view_->search_box()->RequestFocus();
227 void AthenaStartPageView::SetLayoutState(float layout_state) {
228 layout_state_ = layout_state;
229 Layout();
232 void AthenaStartPageView::SetLayoutStateWithAnimation(float layout_state) {
233 ui::ScopedLayerAnimationSettings logo(logo_->layer()->GetAnimator());
234 ui::ScopedLayerAnimationSettings search_box(
235 search_box_container_->layer()->GetAnimator());
236 ui::ScopedLayerAnimationSettings icons(
237 app_icon_container_->layer()->GetAnimator());
238 ui::ScopedLayerAnimationSettings controls(
239 control_icon_container_->layer()->GetAnimator());
241 logo.SetTweenType(gfx::Tween::EASE_IN_OUT);
242 search_box.SetTweenType(gfx::Tween::EASE_IN_OUT);
243 icons.SetTweenType(gfx::Tween::EASE_IN_OUT);
244 controls.SetTweenType(gfx::Tween::EASE_IN_OUT);
246 SetLayoutState(layout_state);
249 AthenaStartPageView::LayoutData AthenaStartPageView::CreateBottomBounds(
250 int width) {
251 LayoutData state;
252 state.icons.set_size(app_icon_container_->size());
253 state.icons.set_x(kIconMargin);
254 state.icons.set_y(kIconMargin);
256 state.controls.set_size(control_icon_container_->size());
257 state.controls.set_x(width - kIconMargin - state.controls.width());
258 state.controls.set_y(kIconMargin);
260 state.search_box.set_size(search_box_container_->size());
261 state.search_box.set_x((width - state.search_box.width()) / 2);
262 state.search_box.set_y((kHomeCardHeight - state.search_box.height()) / 2);
264 state.logo_opacity = 0.0f;
265 state.background_opacity = 0.9f;
266 return state;
269 AthenaStartPageView::LayoutData AthenaStartPageView::CreateCenteredBounds(
270 int width) {
271 LayoutData state;
273 state.search_box.set_size(search_box_container_->size());
274 state.search_box.set_x((width - state.search_box.width()) / 2);
275 state.search_box.set_y(logo_->bounds().bottom() + kInstantContainerSpacing);
277 state.icons.set_size(app_icon_container_->size());
278 state.icons.set_x(width / 2 - state.icons.width() - kIconMargin / 2);
279 state.icons.set_y(state.search_box.bottom() + kInstantContainerSpacing);
281 state.controls.set_size(control_icon_container_->size());
282 state.controls.set_x(width / 2 + kIconMargin / 2 + kIconMargin % 2);
283 state.controls.set_y(state.icons.y());
285 state.logo_opacity = 1.0f;
286 state.background_opacity = 1.0f;
287 return state;
290 void AthenaStartPageView::LayoutSearchResults(bool should_show_search_results) {
291 if (should_show_search_results ==
292 search_results_view_->layer()->GetTargetVisibility()) {
293 return;
295 if (GetContentsBounds().height() <= kHomeCardHeight) {
296 search_results_view_->SetVisible(false);
297 Layout();
298 return;
301 gfx::Rect search_box_bounds = search_box_container_->bounds();
302 if (!search_results_view_->visible()) {
303 search_results_view_->SetVisible(true);
304 search_results_view_->SetBounds(
305 search_box_bounds.x(), search_box_bounds.bottom(),
306 search_box_bounds.width(), 0);
308 logo_->SetVisible(true);
311 ui::ScopedLayerAnimationSettings logo_settings(
312 logo_->layer()->GetAnimator());
313 logo_settings.SetTweenType(gfx::Tween::EASE_IN_OUT);
314 logo_settings.AddObserver(new ui::ClosureAnimationObserver(
315 base::Bind(&AthenaStartPageView::OnSearchResultLayoutAnimationCompleted,
316 weak_factory_.GetWeakPtr(),
317 should_show_search_results)));
319 ui::ScopedLayerAnimationSettings search_box_settings(
320 search_box_container_->layer()->GetAnimator());
321 search_box_settings.SetTweenType(gfx::Tween::EASE_IN_OUT);
323 ui::ScopedLayerAnimationSettings search_results_settings(
324 search_results_view_->layer()->GetAnimator());
325 search_results_settings.SetTweenType(gfx::Tween::EASE_IN_OUT);
327 if (should_show_search_results) {
328 logo_->layer()->SetOpacity(0.0f);
329 search_box_bounds.set_y(
330 search_box_bounds.y() - search_results_height_ -
331 kInstantContainerSpacing);
332 search_box_container_->SetBoundsRect(search_box_bounds);
333 search_results_view_->SetBounds(
334 search_box_bounds.x(),
335 search_box_bounds.bottom() + kInstantContainerSpacing,
336 search_box_bounds.width(),
337 search_results_height_);
338 } else {
339 logo_->layer()->SetOpacity(1.0f);
340 search_box_bounds.set_y(
341 logo_->bounds().bottom() + kInstantContainerSpacing);
342 search_box_container_->SetBoundsRect(search_box_bounds);
344 gfx::Rect search_results_bounds = search_results_view_->bounds();
345 search_results_bounds.set_y(search_results_bounds.bottom());
346 search_results_bounds.set_height(0);
347 search_results_view_->SetBoundsRect(search_results_bounds);
352 void AthenaStartPageView::OnSearchResultLayoutAnimationCompleted(
353 bool should_show_search_results) {
354 logo_->SetVisible(!should_show_search_results);
355 search_results_view_->SetVisible(should_show_search_results);
358 void AthenaStartPageView::Layout() {
359 search_results_view_->SetVisible(false);
360 gfx::Rect logo_bounds(x() + width() / 2 - kWebViewWidth / 2, y() + kTopMargin,
361 kWebViewWidth, kWebViewHeight);
362 logo_->SetBoundsRect(logo_bounds);
364 LayoutData bottom_bounds = CreateBottomBounds(width());
365 LayoutData centered_bounds = CreateCenteredBounds(width());
367 logo_->layer()->SetOpacity(gfx::Tween::FloatValueBetween(
368 gfx::Tween::CalculateValue(gfx::Tween::EASE_IN_2, layout_state_),
369 bottom_bounds.logo_opacity, centered_bounds.logo_opacity));
370 logo_->SetVisible(logo_->layer()->GetTargetOpacity() != 0.0f);
372 app_icon_container_->SetBoundsRect(gfx::Tween::RectValueBetween(
373 layout_state_, bottom_bounds.icons, centered_bounds.icons));
374 control_icon_container_->SetBoundsRect(gfx::Tween::RectValueBetween(
375 layout_state_, bottom_bounds.controls, centered_bounds.controls));
376 search_box_container_->SetBoundsRect(gfx::Tween::RectValueBetween(
377 layout_state_, bottom_bounds.search_box, centered_bounds.search_box));
379 background_->SetBoundsRect(bounds());
380 background_->layer()->SetOpacity(gfx::Tween::FloatValueBetween(
381 layout_state_,
382 bottom_bounds.background_opacity,
383 centered_bounds.background_opacity));
386 bool AthenaStartPageView::OnKeyPressed(const ui::KeyEvent& key_event) {
387 return search_results_view_->visible() &&
388 search_results_view_->OnKeyPressed(key_event);
391 void AthenaStartPageView::QueryChanged(app_list::SearchBoxView* sender) {
392 delegate_->StartSearch();
394 base::string16 query;
395 base::TrimWhitespace(
396 delegate_->GetModel()->search_box()->text(), base::TRIM_ALL, &query);
398 if (!query.empty())
399 search_results_view_->SetSelectedIndex(0);
401 LayoutSearchResults(!query.empty());
404 } // namespace athena