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/public/home_card.h"
6
7#include "athena/home/app_list_view_delegate.h"
8#include "athena/home/public/app_model_builder.h"
9#include "athena/input/public/accelerator_manager.h"
10#include "athena/screen/public/screen_manager.h"
11#include "ui/app_list/search_provider.h"
12#include "ui/app_list/views/app_list_view.h"
13#include "ui/aura/layout_manager.h"
14#include "ui/aura/window.h"
15#include "ui/wm/core/visibility_controller.h"
16#include "ui/wm/core/window_animations.h"
17
18namespace athena {
19namespace {
20
21HomeCard* instance = NULL;
22
23class HomeCardLayoutManager : public aura::LayoutManager {
24 public:
25  explicit HomeCardLayoutManager(aura::Window* container)
26      : container_(container) {}
27  virtual ~HomeCardLayoutManager() {}
28
29 private:
30  // aura::LayoutManager:
31  virtual void OnWindowResized() OVERRIDE { Layout(); }
32  virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE { Layout(); }
33  virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {}
34  virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {
35    Layout();
36  }
37  virtual void OnChildWindowVisibilityChanged(aura::Window* child,
38                                              bool visible) OVERRIDE {
39    Layout();
40  }
41  virtual void SetChildBounds(aura::Window* child,
42                              const gfx::Rect& requested_bounds) OVERRIDE {
43    SetChildBoundsDirect(child, gfx::Rect(requested_bounds.size()));
44  }
45
46  void Layout() {
47    const int kHomeCardHeight = 150;
48    const int kHomeCardHorizontalMargin = 50;
49    // Currently the home card is provided as a bubble and the bounds has to be
50    // increased to cancel the shadow.
51    // TODO(mukai): stops using the bubble and remove this.
52    const int kHomeCardShadowWidth = 30;
53    if (container_->children().size() < 1)
54      return;
55    aura::Window* home_card = container_->children()[0];
56    if (!home_card->IsVisible())
57      return;
58    gfx::Rect screen_bounds = home_card->GetRootWindow()->bounds();
59    gfx::Rect card_bounds = screen_bounds;
60    card_bounds.Inset(kHomeCardHorizontalMargin,
61                      screen_bounds.height() - kHomeCardHeight,
62                      kHomeCardHorizontalMargin,
63                      0);
64    card_bounds.Inset(-kHomeCardShadowWidth, -kHomeCardShadowWidth);
65    SetChildBoundsDirect(home_card, card_bounds);
66  }
67
68  aura::Window* container_;
69
70  DISALLOW_COPY_AND_ASSIGN(HomeCardLayoutManager);
71};
72
73class HomeCardImpl : public HomeCard, public AcceleratorHandler {
74 public:
75  explicit HomeCardImpl(AppModelBuilder* model_builder);
76  virtual ~HomeCardImpl();
77
78  void Init();
79
80 private:
81  enum Command {
82    COMMAND_SHOW_HOME_CARD,
83  };
84  void InstallAccelerators();
85
86  // Overridden from HomeCard:
87  virtual void RegisterSearchProvider(
88      app_list::SearchProvider* search_provider) OVERRIDE;
89
90  // AcceleratorHandler:
91  virtual bool IsCommandEnabled(int command_id) const OVERRIDE { return true; }
92  virtual bool OnAcceleratorFired(int command_id,
93                                  const ui::Accelerator& accelerator) OVERRIDE {
94    DCHECK_EQ(COMMAND_SHOW_HOME_CARD, command_id);
95    if (home_card_widget_->IsVisible())
96      home_card_widget_->Hide();
97    else
98      home_card_widget_->Show();
99    return true;
100  }
101
102  scoped_ptr<AppModelBuilder> model_builder_;
103
104  views::Widget* home_card_widget_;
105  AppListViewDelegate* view_delegate_;
106
107  // Right now HomeCard allows only one search provider.
108  // TODO(mukai): port app-list's SearchController and Mixer.
109  scoped_ptr<app_list::SearchProvider> search_provider_;
110
111  DISALLOW_COPY_AND_ASSIGN(HomeCardImpl);
112};
113
114HomeCardImpl::HomeCardImpl(AppModelBuilder* model_builder)
115    : model_builder_(model_builder),
116      home_card_widget_(NULL) {
117  DCHECK(!instance);
118  instance = this;
119}
120
121HomeCardImpl::~HomeCardImpl() {
122  DCHECK(instance);
123  home_card_widget_->CloseNow();
124  view_delegate_ = NULL;
125  instance = NULL;
126}
127
128void HomeCardImpl::RegisterSearchProvider(
129    app_list::SearchProvider* search_provider) {
130  DCHECK(!search_provider_);
131  search_provider_.reset(search_provider);
132  view_delegate_->RegisterSearchProvider(search_provider_.get());
133}
134
135void HomeCardImpl::Init() {
136  InstallAccelerators();
137
138  aura::Window* container =
139      ScreenManager::Get()->CreateContainer("HomeCardContainer");
140  container->SetLayoutManager(new HomeCardLayoutManager(container));
141  wm::SetChildWindowVisibilityChangesAnimated(container);
142
143  view_delegate_ = new AppListViewDelegate(model_builder_.get());
144  if (search_provider_)
145    view_delegate_->RegisterSearchProvider(search_provider_.get());
146  app_list::AppListView* view = new app_list::AppListView(view_delegate_);
147  view->InitAsBubbleAtFixedLocation(
148      container,
149      0 /* initial_apps_page */,
150      gfx::Point(),
151      views::BubbleBorder::FLOAT,
152      true /* border_accepts_events */);
153  home_card_widget_ = view->GetWidget();
154  view->ShowWhenReady();
155}
156
157void HomeCardImpl::InstallAccelerators() {
158  const AcceleratorData accelerator_data[] = {
159      {TRIGGER_ON_PRESS, ui::VKEY_L, ui::EF_CONTROL_DOWN,
160       COMMAND_SHOW_HOME_CARD, AF_NONE},
161  };
162  AcceleratorManager::Get()->RegisterAccelerators(
163      accelerator_data, arraysize(accelerator_data), this);
164}
165
166}  // namespace
167
168// static
169HomeCard* HomeCard::Create(AppModelBuilder* model_builder) {
170  (new HomeCardImpl(model_builder))->Init();
171  DCHECK(instance);
172  return instance;
173}
174
175// static
176void HomeCard::Shutdown() {
177  DCHECK(instance);
178  delete instance;
179  instance = NULL;
180}
181
182// static
183HomeCard* HomeCard::Get() {
184  DCHECK(instance);
185  return instance;
186}
187
188}  // namespace athena
189