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/public/home_card.h"
10 #include "athena/common/container_priorities.h"
11 #include "athena/home/app_list_view_delegate.h"
12 #include "athena/home/athena_start_page_view.h"
13 #include "athena/home/minimized_home.h"
14 #include "athena/home/public/app_model_builder.h"
15 #include "athena/input/public/accelerator_manager.h"
16 #include "athena/screen/public/screen_manager.h"
17 #include "athena/wm/public/window_manager.h"
18 #include "athena/wm/public/window_manager_observer.h"
19 #include "base/bind.h"
20 #include "base/memory/weak_ptr.h"
21 #include "ui/app_list/search_provider.h"
22 #include "ui/app_list/views/app_list_main_view.h"
23 #include "ui/app_list/views/contents_view.h"
24 #include "ui/aura/layout_manager.h"
25 #include "ui/aura/window.h"
26 #include "ui/compositor/closure_animation_observer.h"
27 #include "ui/compositor/scoped_layer_animation_settings.h"
28 #include "ui/views/background.h"
29 #include "ui/views/layout/box_layout.h"
30 #include "ui/views/widget/widget.h"
31 #include "ui/views/widget/widget_delegate.h"
32 #include "ui/wm/core/shadow_types.h"
33 #include "ui/wm/core/visibility_controller.h"
34 #include "ui/wm/core/window_animations.h"
35 #include "ui/wm/public/activation_change_observer.h"
36 #include "ui/wm/public/activation_client.h"
41 HomeCard* instance = NULL;
42 const int kHomeCardHeight = 100;
43 const int kHomeCardMinimizedHeight = 6;
45 gfx::Rect GetBoundsForState(const gfx::Rect& screen_bounds,
46 HomeCard::State state) {
48 case HomeCard::HIDDEN:
51 case HomeCard::VISIBLE_CENTERED:
54 case HomeCard::VISIBLE_BOTTOM:
56 screen_bounds.bottom() - kHomeCardHeight,
57 screen_bounds.width(),
59 case HomeCard::VISIBLE_MINIMIZED:
61 screen_bounds.bottom() - kHomeCardMinimizedHeight,
62 screen_bounds.width(),
63 kHomeCardMinimizedHeight);
70 // Makes sure the homecard is center-aligned horizontally and bottom-aligned
72 class HomeCardLayoutManager : public aura::LayoutManager {
76 virtual ~Delegate() {}
78 virtual HomeCard::State GetState() = 0;
79 virtual aura::Window* GetNativeWindow() = 0;
82 explicit HomeCardLayoutManager(Delegate* delegate)
83 : delegate_(delegate) {}
85 virtual ~HomeCardLayoutManager() {}
88 aura::Window* home_card = delegate_->GetNativeWindow();
89 // |home_card| could be detached from the root window (e.g. when it is being
91 if (!home_card || !home_card->GetRootWindow())
95 ui::ScopedLayerAnimationSettings settings(
96 home_card->layer()->GetAnimator());
97 settings.SetTweenType(gfx::Tween::EASE_IN_OUT);
98 SetChildBoundsDirect(home_card, GetBoundsForState(
99 home_card->GetRootWindow()->bounds(), delegate_->GetState()));
104 // aura::LayoutManager:
105 virtual void OnWindowResized() OVERRIDE { Layout(); }
106 virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE { Layout(); }
107 virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {}
108 virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {
111 virtual void OnChildWindowVisibilityChanged(aura::Window* child,
112 bool visible) OVERRIDE {
115 virtual void SetChildBounds(aura::Window* child,
116 const gfx::Rect& requested_bounds) OVERRIDE {
117 SetChildBoundsDirect(child, requested_bounds);
122 DISALLOW_COPY_AND_ASSIGN(HomeCardLayoutManager);
125 class HomeCardGestureManager {
129 // Called when the gesture has ended. The state of the home card will
130 // end up with |final_state|.
131 virtual void OnGestureEnded(HomeCard::State final_state) = 0;
133 // Called when the gesture position is updated so that |delegate| should
134 // update the visual. The arguments represent the state of the current
135 // gesture position is switching from |from_state| to |to_state|, and
136 // the level of the progress is at |progress|, which is 0 to 1.
137 // |from_state| and |to_state| could be same. For example, if the user moves
138 // the finger down to the bottom of the screen, both states are MINIMIZED.
139 // In that case |progress| is 0.
140 virtual void OnGestureProgressed(
141 HomeCard::State from_state,
142 HomeCard::State to_state,
146 HomeCardGestureManager(Delegate* delegate,
147 const gfx::Rect& screen_bounds)
148 : delegate_(delegate),
149 last_state_(HomeCard::Get()->GetState()),
151 last_estimated_top_(0),
152 screen_bounds_(screen_bounds) {}
154 void ProcessGestureEvent(ui::GestureEvent* event) {
155 switch (event->type()) {
156 case ui::ET_GESTURE_SCROLL_BEGIN:
157 y_offset_ = event->location().y();
160 case ui::ET_GESTURE_SCROLL_END:
162 delegate_->OnGestureEnded(GetClosestState());
164 case ui::ET_GESTURE_SCROLL_UPDATE:
165 UpdateScrollState(*event);
167 case ui::ET_SCROLL_FLING_START: {
168 const ui::GestureEventDetails& details = event->details();
169 const float kFlingCompletionVelocity = 100.0f;
170 if (::fabs(details.velocity_y()) > kFlingCompletionVelocity) {
171 int step = (details.velocity_y() > 0) ? 1 : -1;
172 int new_state = static_cast<int>(last_state_) + step;
173 if (new_state >= HomeCard::VISIBLE_CENTERED &&
174 new_state <= HomeCard::VISIBLE_MINIMIZED) {
175 last_state_ = static_cast<HomeCard::State>(new_state);
177 delegate_->OnGestureEnded(last_state_);
188 HomeCard::State GetClosestState() {
189 // The top position of the bounds for one smaller state than the current
191 int smaller_top = -1;
192 for (int i = HomeCard::VISIBLE_MINIMIZED;
193 i >= HomeCard::VISIBLE_CENTERED; --i) {
194 HomeCard::State state = static_cast<HomeCard::State>(i);
195 int top = GetBoundsForState(screen_bounds_, state).y();
196 if (last_estimated_top_ == top) {
198 } else if (last_estimated_top_ > top) {
202 if (smaller_top - last_estimated_top_ > (smaller_top - top) / 5) {
205 return static_cast<HomeCard::State>(i + 1);
215 void UpdateScrollState(const ui::GestureEvent& event) {
216 last_estimated_top_ = event.root_location().y() - y_offset_;
218 // The bounds which is at one smaller state than the current one.
219 gfx::Rect smaller_bounds;
221 for (int i = HomeCard::VISIBLE_MINIMIZED;
222 i >= HomeCard::VISIBLE_CENTERED; --i) {
223 HomeCard::State state = static_cast<HomeCard::State>(i);
224 const gfx::Rect bounds = GetBoundsForState(screen_bounds_, state);
225 if (last_estimated_top_ == bounds.y()) {
226 delegate_->OnGestureProgressed(last_state_, state, 1.0f);
229 } else if (last_estimated_top_ > bounds.y()) {
230 if (smaller_bounds.IsEmpty()) {
231 // Smaller than minimized -- returning the minimized bounds.
232 delegate_->OnGestureProgressed(last_state_, state, 1.0f);
234 // The finger is between two states.
236 static_cast<float>((smaller_bounds.y() - last_estimated_top_)) /
237 (smaller_bounds.y() - bounds.y());
238 if (last_state_ == state) {
239 if (event.details().scroll_y() > 0) {
240 state = static_cast<HomeCard::State>(state + 1);
241 progress = 1.0f - progress;
243 last_state_ = static_cast<HomeCard::State>(last_state_ + 1);
246 delegate_->OnGestureProgressed(last_state_, state, progress);
251 smaller_bounds = bounds;
257 HomeCard::State last_state_;
259 // The offset from the top edge of the home card and the initial position of
263 // The estimated top edge of the home card after the last touch event.
264 int last_estimated_top_;
266 // The bounds of the screen to compute the home card bounds.
267 gfx::Rect screen_bounds_;
269 DISALLOW_COPY_AND_ASSIGN(HomeCardGestureManager);
272 // The container view of home card contents of each state.
273 class HomeCardView : public views::WidgetDelegateView {
275 HomeCardView(app_list::AppListViewDelegate* view_delegate,
276 aura::Window* container,
277 HomeCardGestureManager::Delegate* gesture_delegate)
278 : gesture_delegate_(gesture_delegate),
279 weak_factory_(this) {
280 bottom_view_ = new AthenaStartPageView(view_delegate);
281 AddChildView(bottom_view_);
282 bottom_view_->SetPaintToLayer(true);
283 bottom_view_->layer()->SetFillsBoundsOpaquely(false);
285 main_view_ = new app_list::AppListMainView(
286 view_delegate, 0 /* initial_apps_page */, container);
287 AddChildView(main_view_);
288 main_view_->set_background(
289 views::Background::CreateSolidBackground(SK_ColorWHITE));
290 main_view_->SetPaintToLayer(true);
292 minimized_view_ = CreateMinimizedHome();
293 minimized_view_->SetPaintToLayer(true);
294 AddChildView(minimized_view_);
297 void SetStateProgress(HomeCard::State from_state,
298 HomeCard::State to_state,
300 if (from_state == HomeCard::VISIBLE_BOTTOM &&
301 to_state == HomeCard::VISIBLE_MINIMIZED) {
302 SetStateProgress(to_state, from_state, 1.0 - progress);
306 // View from minimized to bottom.
307 if (from_state == HomeCard::VISIBLE_MINIMIZED &&
308 to_state == HomeCard::VISIBLE_BOTTOM) {
309 bottom_view_->SetVisible(true);
310 minimized_view_->SetVisible(true);
311 minimized_view_->layer()->SetOpacity(1.0f - progress);
318 void SetState(HomeCard::State state) {
319 bottom_view_->SetVisible(state == HomeCard::VISIBLE_BOTTOM);
320 main_view_->SetVisible(state == HomeCard::VISIBLE_CENTERED);
321 minimized_view_->SetVisible(state == HomeCard::VISIBLE_MINIMIZED);
322 if (minimized_view_->visible())
323 minimized_view_->layer()->SetOpacity(1.0f);
324 if (state == HomeCard::VISIBLE_CENTERED) {
325 app_list::ContentsView* contents_view = main_view_->contents_view();
326 contents_view->SetActivePage(contents_view->GetPageIndexForNamedPage(
327 app_list::ContentsView::NAMED_PAGE_START));
329 wm::SetShadowType(GetWidget()->GetNativeView(),
330 state == HomeCard::VISIBLE_MINIMIZED ?
331 wm::SHADOW_TYPE_NONE :
332 wm::SHADOW_TYPE_RECTANGULAR);
335 void SetStateWithAnimation(HomeCard::State from_state,
336 HomeCard::State to_state) {
337 if ((from_state == HomeCard::VISIBLE_MINIMIZED &&
338 to_state == HomeCard::VISIBLE_BOTTOM) ||
339 (from_state == HomeCard::VISIBLE_BOTTOM &&
340 to_state == HomeCard::VISIBLE_MINIMIZED)) {
341 minimized_view_->SetVisible(true);
342 bottom_view_->SetVisible(true);
344 ui::ScopedLayerAnimationSettings settings(
345 minimized_view_->layer()->GetAnimator());
346 settings.SetTweenType(gfx::Tween::EASE_IN_OUT);
347 settings.AddObserver(new ui::ClosureAnimationObserver(
348 base::Bind(&HomeCardView::SetState,
349 weak_factory_.GetWeakPtr(),
351 minimized_view_->layer()->SetOpacity(
352 (to_state == HomeCard::VISIBLE_MINIMIZED) ? 1.0f : 0.0f);
355 // TODO(mukai): Take care of other transition.
360 void ClearGesture() {
361 gesture_manager_.reset();
365 virtual void Layout() OVERRIDE {
366 for (int i = 0; i < child_count(); ++i) {
367 views::View* child = child_at(i);
368 if (child->visible()) {
369 if (child == minimized_view_) {
370 gfx::Rect minimized_bounds = bounds();
371 minimized_bounds.set_y(
372 minimized_bounds.bottom() - kHomeCardMinimizedHeight);
373 minimized_bounds.set_height(kHomeCardMinimizedHeight);
374 child->SetBoundsRect(minimized_bounds);
376 child->SetBoundsRect(bounds());
381 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
382 if (!gesture_manager_ &&
383 event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
384 gesture_manager_.reset(new HomeCardGestureManager(
386 GetWidget()->GetNativeWindow()->GetRootWindow()->bounds()));
389 if (gesture_manager_)
390 gesture_manager_->ProcessGestureEvent(event);
394 // views::WidgetDelegate:
395 virtual views::View* GetContentsView() OVERRIDE {
399 app_list::AppListMainView* main_view_;
400 views::View* bottom_view_;
401 views::View* minimized_view_;
402 scoped_ptr<HomeCardGestureManager> gesture_manager_;
403 HomeCardGestureManager::Delegate* gesture_delegate_;
404 base::WeakPtrFactory<HomeCardView> weak_factory_;
406 DISALLOW_COPY_AND_ASSIGN(HomeCardView);
409 class HomeCardImpl : public HomeCard,
410 public AcceleratorHandler,
411 public HomeCardLayoutManager::Delegate,
412 public HomeCardGestureManager::Delegate,
413 public WindowManagerObserver,
414 public aura::client::ActivationChangeObserver {
416 explicit HomeCardImpl(AppModelBuilder* model_builder);
417 virtual ~HomeCardImpl();
423 COMMAND_SHOW_HOME_CARD,
425 void InstallAccelerators();
427 // Overridden from HomeCard:
428 virtual void SetState(State state) OVERRIDE;
429 virtual State GetState() OVERRIDE;
430 virtual void RegisterSearchProvider(
431 app_list::SearchProvider* search_provider) OVERRIDE;
432 virtual void UpdateVirtualKeyboardBounds(
433 const gfx::Rect& bounds) OVERRIDE;
435 // AcceleratorHandler:
436 virtual bool IsCommandEnabled(int command_id) const OVERRIDE { return true; }
437 virtual bool OnAcceleratorFired(int command_id,
438 const ui::Accelerator& accelerator) OVERRIDE;
440 // HomeCardLayoutManager::Delegate:
441 virtual aura::Window* GetNativeWindow() OVERRIDE;
443 // HomeCardGestureManager::Delegate:
444 virtual void OnGestureEnded(State final_state) OVERRIDE;
445 virtual void OnGestureProgressed(
446 State from_state, State to_state, float progress) OVERRIDE;
448 // WindowManagerObserver:
449 virtual void OnOverviewModeEnter() OVERRIDE;
450 virtual void OnOverviewModeExit() OVERRIDE;
452 // aura::client::ActivationChangeObserver:
453 virtual void OnWindowActivated(aura::Window* gained_active,
454 aura::Window* lost_active) OVERRIDE;
456 scoped_ptr<AppModelBuilder> model_builder_;
458 HomeCard::State state_;
460 // original_state_ is the state which the home card should go back to after
461 // the virtual keyboard is hidden.
462 HomeCard::State original_state_;
464 views::Widget* home_card_widget_;
465 HomeCardView* home_card_view_;
466 scoped_ptr<AppListViewDelegate> view_delegate_;
467 HomeCardLayoutManager* layout_manager_;
468 aura::client::ActivationClient* activation_client_; // Not owned
470 // Right now HomeCard allows only one search provider.
471 // TODO(mukai): port app-list's SearchController and Mixer.
472 scoped_ptr<app_list::SearchProvider> search_provider_;
474 DISALLOW_COPY_AND_ASSIGN(HomeCardImpl);
477 HomeCardImpl::HomeCardImpl(AppModelBuilder* model_builder)
478 : model_builder_(model_builder),
480 original_state_(VISIBLE_MINIMIZED),
481 home_card_widget_(NULL),
482 home_card_view_(NULL),
483 layout_manager_(NULL),
484 activation_client_(NULL) {
487 WindowManager::GetInstance()->AddObserver(this);
490 HomeCardImpl::~HomeCardImpl() {
492 WindowManager::GetInstance()->RemoveObserver(this);
493 if (activation_client_)
494 activation_client_->RemoveObserver(this);
495 home_card_widget_->CloseNow();
499 void HomeCardImpl::Init() {
500 InstallAccelerators();
501 ScreenManager::ContainerParams params("HomeCardContainer", CP_HOME_CARD);
502 params.can_activate_children = true;
503 aura::Window* container = ScreenManager::Get()->CreateContainer(params);
504 layout_manager_ = new HomeCardLayoutManager(this);
506 container->SetLayoutManager(layout_manager_);
507 wm::SetChildWindowVisibilityChangesAnimated(container);
509 view_delegate_.reset(new AppListViewDelegate(model_builder_.get()));
510 if (search_provider_)
511 view_delegate_->RegisterSearchProvider(search_provider_.get());
513 home_card_view_ = new HomeCardView(view_delegate_.get(), container, this);
514 home_card_widget_ = new views::Widget();
515 views::Widget::InitParams widget_params(
516 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
517 widget_params.parent = container;
518 widget_params.delegate = home_card_view_;
519 widget_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
520 home_card_widget_->Init(widget_params);
522 SetState(VISIBLE_MINIMIZED);
523 home_card_view_->Layout();
526 aura::client::GetActivationClient(container->GetRootWindow());
527 if (activation_client_)
528 activation_client_->AddObserver(this);
531 void HomeCardImpl::InstallAccelerators() {
532 const AcceleratorData accelerator_data[] = {
533 {TRIGGER_ON_PRESS, ui::VKEY_L, ui::EF_CONTROL_DOWN,
534 COMMAND_SHOW_HOME_CARD, AF_NONE},
536 AcceleratorManager::Get()->RegisterAccelerators(
537 accelerator_data, arraysize(accelerator_data), this);
540 void HomeCardImpl::SetState(HomeCard::State state) {
544 // Update |state_| before changing the visibility of the widgets, so that
545 // LayoutManager callbacks get the correct state.
546 HomeCard::State old_state = state_;
548 original_state_ = state;
549 if (state_ == HIDDEN) {
550 home_card_widget_->Hide();
552 if (state_ == VISIBLE_CENTERED)
553 home_card_widget_->Show();
555 home_card_widget_->ShowInactive();
556 home_card_view_->SetStateWithAnimation(old_state, state);
557 layout_manager_->Layout();
561 HomeCard::State HomeCardImpl::GetState() {
565 void HomeCardImpl::RegisterSearchProvider(
566 app_list::SearchProvider* search_provider) {
567 DCHECK(!search_provider_);
568 search_provider_.reset(search_provider);
569 view_delegate_->RegisterSearchProvider(search_provider_.get());
572 void HomeCardImpl::UpdateVirtualKeyboardBounds(
573 const gfx::Rect& bounds) {
574 if (state_ == VISIBLE_MINIMIZED && !bounds.IsEmpty()) {
576 original_state_ = VISIBLE_MINIMIZED;
577 } else if (state_ == VISIBLE_BOTTOM && !bounds.IsEmpty()) {
578 SetState(VISIBLE_CENTERED);
579 original_state_ = VISIBLE_BOTTOM;
580 } else if (state_ != original_state_ && bounds.IsEmpty()) {
581 SetState(original_state_);
585 bool HomeCardImpl::OnAcceleratorFired(int command_id,
586 const ui::Accelerator& accelerator) {
587 DCHECK_EQ(COMMAND_SHOW_HOME_CARD, command_id);
589 if (state_ == VISIBLE_CENTERED && original_state_ != VISIBLE_BOTTOM)
590 SetState(VISIBLE_MINIMIZED);
591 else if (state_ == VISIBLE_MINIMIZED)
592 SetState(VISIBLE_CENTERED);
596 aura::Window* HomeCardImpl::GetNativeWindow() {
597 if (state_ == HIDDEN)
600 return home_card_widget_ ? home_card_widget_->GetNativeWindow() : NULL;
603 void HomeCardImpl::OnGestureEnded(State final_state) {
604 home_card_view_->ClearGesture();
605 if (state_ != final_state &&
606 (state_ == VISIBLE_MINIMIZED || final_state == VISIBLE_MINIMIZED)) {
607 WindowManager::GetInstance()->ToggleOverview();
609 HomeCard::State old_state = state_;
610 state_ = final_state;
611 home_card_view_->SetStateWithAnimation(old_state, final_state);
612 layout_manager_->Layout();
616 void HomeCardImpl::OnGestureProgressed(
617 State from_state, State to_state, float progress) {
618 home_card_view_->SetStateProgress(from_state, to_state, progress);
620 gfx::Rect screen_bounds =
621 home_card_widget_->GetNativeWindow()->GetRootWindow()->bounds();
622 home_card_widget_->SetBounds(gfx::Tween::RectValueBetween(
624 GetBoundsForState(screen_bounds, from_state),
625 GetBoundsForState(screen_bounds, to_state)));
627 // TODO(mukai): signals the update to the window manager so that it shows the
628 // intermediate visual state of overview mode.
631 void HomeCardImpl::OnOverviewModeEnter() {
632 SetState(VISIBLE_BOTTOM);
635 void HomeCardImpl::OnOverviewModeExit() {
636 SetState(VISIBLE_MINIMIZED);
639 void HomeCardImpl::OnWindowActivated(aura::Window* gained_active,
640 aura::Window* lost_active) {
641 if (state_ != HIDDEN &&
642 gained_active != home_card_widget_->GetNativeWindow()) {
643 SetState(VISIBLE_MINIMIZED);
650 HomeCard* HomeCard::Create(AppModelBuilder* model_builder) {
651 (new HomeCardImpl(model_builder))->Init();
657 void HomeCard::Shutdown() {
664 HomeCard* HomeCard::Get() {
669 } // namespace athena