Upstream version 11.39.266.0
[platform/framework/web/crosswalk.git] / src / athena / home / athena_start_page_view.cc
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.
4
5 #include "athena/home/athena_start_page_view.h"
6
7 #include "athena/home/home_card_constants.h"
8 #include "athena/system/public/system_ui.h"
9 #include "base/bind.h"
10 #include "base/strings/string_util.h"
11 #include "third_party/skia/include/core/SkPaint.h"
12 #include "third_party/skia/include/core/SkPath.h"
13 #include "ui/app_list/app_list_item.h"
14 #include "ui/app_list/app_list_item_list.h"
15 #include "ui/app_list/app_list_model.h"
16 #include "ui/app_list/app_list_view_delegate.h"
17 #include "ui/app_list/search_box_model.h"
18 #include "ui/app_list/views/search_box_view.h"
19 #include "ui/app_list/views/search_result_list_view.h"
20 #include "ui/compositor/closure_animation_observer.h"
21 #include "ui/compositor/scoped_layer_animation_settings.h"
22 #include "ui/gfx/canvas.h"
23 #include "ui/views/background.h"
24 #include "ui/views/border.h"
25 #include "ui/views/controls/textfield/textfield.h"
26 #include "ui/views/layout/box_layout.h"
27 #include "ui/views/layout/fill_layout.h"
28 #include "ui/views/round_rect_painter.h"
29
30 namespace {
31
32 const size_t kMaxIconNum = 3;
33 const int kIconSize = 50;
34 const int kIconMargin = 25;
35
36 const int kTopMargin = 100;
37
38 // Copied from ui/app_list/views/start_page_view.cc
39 const int kInstantContainerSpacing = 20;
40 const int kWebViewWidth = 500;
41 const int kWebViewHeight = 105;
42 const int kSearchBoxBorderWidth = 1;
43 const int kSearchBoxCornerRadius = 2;
44
45 // Taken from the mock. The width is not specified by pixel but the search box
46 // covers 6 icons with margin.
47 const int kSearchBoxWidth = kIconSize * 6 + kIconMargin * 7;
48 const int kSearchBoxHeight = 40;
49
50 gfx::Size GetIconContainerSize() {
51   return gfx::Size(kIconSize *  kMaxIconNum + kIconMargin * (kMaxIconNum - 1),
52                    kIconSize);
53 }
54
55 class PlaceHolderButton : public views::ImageButton,
56                           public views::ButtonListener {
57  public:
58   PlaceHolderButton()
59       : ImageButton(this) {
60     gfx::Canvas canvas(gfx::Size(kIconSize, kIconSize), 1.0f, true);
61     SkPaint paint;
62     paint.setStyle(SkPaint::kFill_Style);
63     paint.setColor(SkColorSetRGB(86, 119, 252));
64     paint.setFlags(SkPaint::kAntiAlias_Flag);
65     canvas.DrawCircle(
66         gfx::Point(kIconSize / 2, kIconSize / 2), kIconSize / 2, paint);
67
68     scoped_ptr<gfx::ImageSkia> image(
69         new gfx::ImageSkia(canvas.ExtractImageRep()));
70     SetImage(STATE_NORMAL, image.get());
71   }
72
73  private:
74   // views::ButtonListener:
75   virtual void ButtonPressed(views::Button* sender,
76                              const ui::Event& event) OVERRIDE {
77     // Do nothing: remove these place holders.
78   }
79
80   DISALLOW_COPY_AND_ASSIGN(PlaceHolderButton);
81 };
82
83 class AppIconButton : public views::ImageButton,
84                       public views::ButtonListener {
85  public:
86   explicit AppIconButton(app_list::AppListItem* item)
87       : ImageButton(this),
88         item_(item) {
89     // TODO(mukai): icon should be resized.
90     SetImage(STATE_NORMAL, &item->icon());
91   }
92
93  private:
94   // views::ButtonListener:
95   virtual void ButtonPressed(views::Button* sender,
96                              const ui::Event& event) OVERRIDE {
97     DCHECK_EQ(sender, this);
98     item_->Activate(event.flags());
99   }
100
101   app_list::AppListItem* item_;
102
103   DISALLOW_COPY_AND_ASSIGN(AppIconButton);
104 };
105
106 // The background to paint the round rectangle of the view area.
107 class RoundRectBackground : public views::Background {
108  public:
109   RoundRectBackground(SkColor color, int corner_radius)
110       : color_(color),
111         corner_radius_(corner_radius) {}
112   virtual ~RoundRectBackground() {}
113
114  private:
115   // views::Background:
116   virtual void Paint(gfx::Canvas* canvas, views::View* view) const OVERRIDE {
117     SkPaint paint;
118     paint.setStyle(SkPaint::kFill_Style);
119     paint.setColor(color_);
120     canvas->DrawRoundRect(view->GetContentsBounds(), corner_radius_, paint);
121   }
122
123   SkColor color_;
124   int corner_radius_;
125
126   DISALLOW_COPY_AND_ASSIGN(RoundRectBackground);
127 };
128
129 class SearchBoxContainer : public views::View {
130  public:
131   explicit SearchBoxContainer(app_list::SearchBoxView* search_box)
132       : search_box_(search_box) {
133     search_box->set_background(
134         new RoundRectBackground(SK_ColorWHITE, kSearchBoxCornerRadius));
135     search_box->SetBorder(views::Border::CreateBorderPainter(
136         new views::RoundRectPainter(SK_ColorGRAY, kSearchBoxCornerRadius),
137         gfx::Insets(kSearchBoxBorderWidth, kSearchBoxBorderWidth,
138                     kSearchBoxBorderWidth, kSearchBoxBorderWidth)));
139     SetLayoutManager(new views::FillLayout());
140     AddChildView(search_box_);
141   }
142   virtual ~SearchBoxContainer() {}
143
144  private:
145   // views::View:
146   virtual gfx::Size GetPreferredSize() const OVERRIDE {
147     return gfx::Size(kSearchBoxWidth, kSearchBoxHeight);
148   }
149
150   // Owned by the views hierarchy.
151   app_list::SearchBoxView* search_box_;
152
153   DISALLOW_COPY_AND_ASSIGN(SearchBoxContainer);
154 };
155
156 }  // namespace
157
158 namespace athena {
159
160 // static
161 const char AthenaStartPageView::kViewClassName[] = "AthenaStartPageView";
162
163 AthenaStartPageView::LayoutData::LayoutData()
164     : system_info_opacity(1.0f),
165       logo_opacity(1.0f),
166       background_opacity(1.0f) {
167 }
168
169 AthenaStartPageView::AthenaStartPageView(
170     app_list::AppListViewDelegate* view_delegate)
171     : delegate_(view_delegate),
172       layout_state_(0.0f),
173       weak_factory_(this) {
174   background_ = new views::View();
175   background_->set_background(
176       views::Background::CreateSolidBackground(SK_ColorWHITE));
177   background_->SetPaintToLayer(true);
178   background_->SetFillsBoundsOpaquely(false);
179   AddChildView(background_);
180
181   system_info_view_ =
182       SystemUI::Get()->CreateSystemInfoView(SystemUI::COLOR_SCHEME_DARK);
183   system_info_view_->SetPaintToLayer(true);
184   system_info_view_->SetFillsBoundsOpaquely(false);
185   AddChildView(system_info_view_);
186
187   logo_ = view_delegate->CreateStartPageWebView(
188       gfx::Size(kWebViewWidth, kWebViewHeight));
189   logo_->SetPaintToLayer(true);
190   logo_->SetFillsBoundsOpaquely(false);
191   logo_->SetSize(gfx::Size(kWebViewWidth, kWebViewHeight));
192   AddChildView(logo_);
193
194   search_results_view_ = new app_list::SearchResultListView(
195       NULL, view_delegate);
196   // search_results_view_'s size will shrink after settings results.
197   search_results_height_ = static_cast<views::View*>(
198       search_results_view_)->GetHeightForWidth(kSearchBoxWidth);
199   search_results_view_->SetResults(view_delegate->GetModel()->results());
200
201   search_results_view_->SetVisible(false);
202   search_results_view_->SetPaintToLayer(true);
203   search_results_view_->SetFillsBoundsOpaquely(false);
204   AddChildView(search_results_view_);
205
206   app_icon_container_ = new views::View();
207   AddChildView(app_icon_container_);
208   app_icon_container_->SetPaintToLayer(true);
209   app_icon_container_->layer()->SetFillsBoundsOpaquely(false);
210   app_icon_container_->SetLayoutManager(new views::BoxLayout(
211       views::BoxLayout::kHorizontal, 0, 0, kIconMargin));
212   app_list::AppListItemList* top_level =
213       view_delegate->GetModel()->top_level_item_list();
214   for (size_t i = 0; i < std::min(top_level->item_count(), kMaxIconNum); ++i)
215     app_icon_container_->AddChildView(new AppIconButton(top_level->item_at(i)));
216   app_icon_container_->SetSize(GetIconContainerSize());
217
218   control_icon_container_ = new views::View();
219   control_icon_container_->SetPaintToLayer(true);
220   control_icon_container_->SetFillsBoundsOpaquely(false);
221   AddChildView(control_icon_container_);
222   control_icon_container_->SetLayoutManager(new views::BoxLayout(
223       views::BoxLayout::kHorizontal, 0, 0, kIconMargin));
224   for (size_t i = 0; i < kMaxIconNum; ++i)
225     control_icon_container_->AddChildView(new PlaceHolderButton());
226   control_icon_container_->SetSize(GetIconContainerSize());
227
228   search_box_view_ = new app_list::SearchBoxView(this, view_delegate);
229   search_box_view_->set_contents_view(this);
230   search_box_view_->search_box()->set_id(kHomeCardSearchBoxId);
231   search_box_container_ = new SearchBoxContainer(search_box_view_);
232   search_box_container_->SetPaintToLayer(true);
233   search_box_container_->SetFillsBoundsOpaquely(false);
234   search_box_container_->SetSize(search_box_container_->GetPreferredSize());
235   AddChildView(search_box_container_);
236 }
237
238 AthenaStartPageView::~AthenaStartPageView() {}
239
240 void AthenaStartPageView::RequestFocusOnSearchBox() {
241   search_box_view_->search_box()->RequestFocus();
242 }
243
244 void AthenaStartPageView::SetLayoutState(float layout_state) {
245   layout_state_ = layout_state;
246   Layout();
247 }
248
249 void AthenaStartPageView::SetLayoutStateWithAnimation(
250     float layout_state,
251     gfx::Tween::Type tween_type) {
252   ui::ScopedLayerAnimationSettings system_info(
253       system_info_view_->layer()->GetAnimator());
254   ui::ScopedLayerAnimationSettings logo(logo_->layer()->GetAnimator());
255   ui::ScopedLayerAnimationSettings search_box(
256       search_box_container_->layer()->GetAnimator());
257   ui::ScopedLayerAnimationSettings icons(
258       app_icon_container_->layer()->GetAnimator());
259   ui::ScopedLayerAnimationSettings controls(
260       control_icon_container_->layer()->GetAnimator());
261
262   system_info.SetTweenType(tween_type);
263   logo.SetTweenType(tween_type);
264   search_box.SetTweenType(tween_type);
265   icons.SetTweenType(tween_type);
266   controls.SetTweenType(tween_type);
267
268   SetLayoutState(layout_state);
269 }
270
271 AthenaStartPageView::LayoutData AthenaStartPageView::CreateBottomBounds(
272     int width) {
273   LayoutData state;
274   state.icons.set_size(app_icon_container_->size());
275   state.icons.set_x(kIconMargin);
276   state.icons.set_y(kIconMargin);
277
278   state.controls.set_size(control_icon_container_->size());
279   state.controls.set_x(width - kIconMargin - state.controls.width());
280   state.controls.set_y(kIconMargin);
281
282   int search_box_max_width =
283       state.controls.x() - state.icons.right() - kIconMargin * 2;
284   state.search_box.set_width(std::min(search_box_max_width, kSearchBoxWidth));
285   state.search_box.set_height(search_box_container_->height());
286   state.search_box.set_x((width - state.search_box.width()) / 2);
287   state.search_box.set_y((kHomeCardHeight - state.search_box.height()) / 2);
288
289   state.system_info_opacity = 0.0f;
290   state.logo_opacity = 0.0f;
291   state.background_opacity = 0.9f;
292   return state;
293 }
294
295 AthenaStartPageView::LayoutData AthenaStartPageView::CreateCenteredBounds(
296     int width) {
297   LayoutData state;
298
299   state.search_box.set_size(search_box_container_->GetPreferredSize());
300   state.search_box.set_x((width - state.search_box.width()) / 2);
301   state.search_box.set_y(logo_->bounds().bottom() + kInstantContainerSpacing);
302
303   state.icons.set_size(app_icon_container_->size());
304   state.icons.set_x(width / 2 - state.icons.width() - kIconMargin / 2);
305   state.icons.set_y(state.search_box.bottom() + kInstantContainerSpacing);
306
307   state.controls.set_size(control_icon_container_->size());
308   state.controls.set_x(width / 2 + kIconMargin / 2 + kIconMargin % 2);
309   state.controls.set_y(state.icons.y());
310
311   state.system_info_opacity = 1.0f;
312   state.logo_opacity = 1.0f;
313   state.background_opacity = 1.0f;
314   return state;
315 }
316
317 void AthenaStartPageView::LayoutSearchResults(bool should_show_search_results) {
318   if (should_show_search_results ==
319       search_results_view_->layer()->GetTargetVisibility()) {
320     return;
321   }
322   if (GetContentsBounds().height() <= kHomeCardHeight) {
323     search_results_view_->SetVisible(false);
324     Layout();
325     return;
326   }
327
328   gfx::Rect search_box_bounds = search_box_container_->bounds();
329   if (!search_results_view_->visible()) {
330     search_results_view_->SetVisible(true);
331     search_results_view_->SetBounds(
332         search_box_bounds.x(), search_box_bounds.bottom(),
333         search_box_bounds.width(), 0);
334   }
335   logo_->SetVisible(true);
336
337   {
338     ui::ScopedLayerAnimationSettings logo_settings(
339         logo_->layer()->GetAnimator());
340     logo_settings.SetTweenType(gfx::Tween::EASE_IN_OUT);
341     logo_settings.AddObserver(new ui::ClosureAnimationObserver(
342         base::Bind(&AthenaStartPageView::OnSearchResultLayoutAnimationCompleted,
343                    weak_factory_.GetWeakPtr(),
344                    should_show_search_results)));
345
346     ui::ScopedLayerAnimationSettings search_box_settings(
347         search_box_container_->layer()->GetAnimator());
348     search_box_settings.SetTweenType(gfx::Tween::EASE_IN_OUT);
349
350     ui::ScopedLayerAnimationSettings search_results_settings(
351         search_results_view_->layer()->GetAnimator());
352     search_results_settings.SetTweenType(gfx::Tween::EASE_IN_OUT);
353
354     if (should_show_search_results) {
355       logo_->layer()->SetOpacity(0.0f);
356       search_box_bounds.set_y(
357           search_box_bounds.y() - search_results_height_ -
358           kInstantContainerSpacing);
359       search_box_container_->SetBoundsRect(search_box_bounds);
360       search_results_view_->SetBounds(
361           search_box_bounds.x(),
362           search_box_bounds.bottom() + kInstantContainerSpacing,
363           search_box_bounds.width(),
364           search_results_height_);
365     } else {
366       logo_->layer()->SetOpacity(1.0f);
367       search_box_bounds.set_y(
368           logo_->bounds().bottom() + kInstantContainerSpacing);
369       search_box_container_->SetBoundsRect(search_box_bounds);
370
371       gfx::Rect search_results_bounds = search_results_view_->bounds();
372       search_results_bounds.set_y(search_results_bounds.bottom());
373       search_results_bounds.set_height(0);
374       search_results_view_->SetBoundsRect(search_results_bounds);
375     }
376   }
377 }
378
379 void AthenaStartPageView::OnSearchResultLayoutAnimationCompleted(
380     bool should_show_search_results) {
381   logo_->SetVisible(!should_show_search_results);
382   search_results_view_->SetVisible(should_show_search_results);
383 }
384
385 void AthenaStartPageView::Layout() {
386   search_results_view_->SetVisible(false);
387
388   system_info_view_->SetBounds(
389       0, 0, width(), system_info_view_->GetPreferredSize().height());
390
391   gfx::Rect logo_bounds(x() + width() / 2 - kWebViewWidth / 2, y() + kTopMargin,
392                         kWebViewWidth, kWebViewHeight);
393   logo_->SetBoundsRect(logo_bounds);
394
395   LayoutData bottom_bounds = CreateBottomBounds(width());
396   LayoutData centered_bounds = CreateCenteredBounds(width());
397
398   system_info_view_->layer()->SetOpacity(gfx::Tween::FloatValueBetween(
399       gfx::Tween::CalculateValue(gfx::Tween::EASE_IN_2, layout_state_),
400       bottom_bounds.system_info_opacity, centered_bounds.system_info_opacity));
401   system_info_view_->SetVisible(
402       system_info_view_->layer()->GetTargetOpacity() != 0.0f);
403
404   logo_->layer()->SetOpacity(gfx::Tween::FloatValueBetween(
405       gfx::Tween::CalculateValue(gfx::Tween::EASE_IN_2, layout_state_),
406       bottom_bounds.logo_opacity, centered_bounds.logo_opacity));
407   logo_->SetVisible(logo_->layer()->GetTargetOpacity() != 0.0f);
408
409   app_icon_container_->SetBoundsRect(gfx::Tween::RectValueBetween(
410       layout_state_, bottom_bounds.icons, centered_bounds.icons));
411   control_icon_container_->SetBoundsRect(gfx::Tween::RectValueBetween(
412       layout_state_, bottom_bounds.controls, centered_bounds.controls));
413   search_box_container_->SetBoundsRect(gfx::Tween::RectValueBetween(
414       layout_state_, bottom_bounds.search_box, centered_bounds.search_box));
415
416   background_->SetBoundsRect(bounds());
417   background_->layer()->SetOpacity(gfx::Tween::FloatValueBetween(
418       layout_state_,
419       bottom_bounds.background_opacity,
420       centered_bounds.background_opacity));
421 }
422
423 bool AthenaStartPageView::OnKeyPressed(const ui::KeyEvent& key_event) {
424   return search_results_view_->visible() &&
425       search_results_view_->OnKeyPressed(key_event);
426 }
427
428 void AthenaStartPageView::QueryChanged(app_list::SearchBoxView* sender) {
429   delegate_->StartSearch();
430
431   base::string16 query;
432   base::TrimWhitespace(
433       delegate_->GetModel()->search_box()->text(), base::TRIM_ALL, &query);
434
435   if (!query.empty())
436     search_results_view_->SetSelectedIndex(0);
437
438   LayoutSearchResults(!query.empty());
439 }
440
441 }  // namespace athena