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/home_card_impl.h"
10 #include "athena/env/public/athena_env.h"
11 #include "athena/home/app_list_view_delegate.h"
12 #include "athena/home/athena_start_page_view.h"
13 #include "athena/home/home_card_constants.h"
14 #include "athena/home/minimized_home.h"
15 #include "athena/home/public/app_model_builder.h"
16 #include "athena/screen/public/screen_manager.h"
17 #include "athena/util/container_priorities.h"
18 #include "athena/wm/public/window_manager.h"
19 #include "ui/app_list/search_provider.h"
20 #include "ui/app_list/views/app_list_main_view.h"
21 #include "ui/app_list/views/contents_view.h"
22 #include "ui/aura/layout_manager.h"
23 #include "ui/aura/window.h"
24 #include "ui/compositor/layer.h"
25 #include "ui/compositor/scoped_layer_animation_settings.h"
26 #include "ui/gfx/animation/tween.h"
27 #include "ui/views/layout/fill_layout.h"
28 #include "ui/views/widget/widget.h"
29 #include "ui/views/widget/widget_delegate.h"
30 #include "ui/wm/core/shadow_types.h"
31 #include "ui/wm/core/visibility_controller.h"
32 #include "ui/wm/public/activation_client.h"
37 HomeCard* instance = NULL;
39 gfx::Rect GetBoundsForState(const gfx::Rect& screen_bounds,
40 HomeCard::State state) {
42 case HomeCard::HIDDEN:
45 case HomeCard::VISIBLE_CENTERED:
48 // Do not change the home_card's size, only changes the top position
49 // instead, because size change causes unnecessary re-layouts.
50 case HomeCard::VISIBLE_BOTTOM:
52 screen_bounds.bottom() - kHomeCardHeight,
53 screen_bounds.width(),
54 screen_bounds.height());
55 case HomeCard::VISIBLE_MINIMIZED:
57 screen_bounds.bottom() - kHomeCardMinimizedHeight,
58 screen_bounds.width(),
59 screen_bounds.height());
68 // Makes sure the homecard is center-aligned horizontally and bottom-aligned
70 class HomeCardLayoutManager : public aura::LayoutManager {
72 HomeCardLayoutManager()
74 minimized_layer_(NULL) {}
76 virtual ~HomeCardLayoutManager() {}
78 void Layout(bool animate, gfx::Tween::Type tween_type) {
79 // |home_card| could be detached from the root window (e.g. when it is being
81 if (!home_card_ || !home_card_->IsVisible() || !home_card_->GetRootWindow())
84 scoped_ptr<ui::ScopedLayerAnimationSettings> settings;
86 settings.reset(new ui::ScopedLayerAnimationSettings(
87 home_card_->layer()->GetAnimator()));
88 settings->SetTweenType(tween_type);
90 SetChildBoundsDirect(home_card_, GetBoundsForState(
91 home_card_->GetRootWindow()->bounds(), HomeCard::Get()->GetState()));
94 void SetMinimizedLayer(ui::Layer* minimized_layer) {
95 minimized_layer_ = minimized_layer;
96 UpdateMinimizedHomeBounds();
100 void UpdateMinimizedHomeBounds() {
101 gfx::Rect minimized_bounds = minimized_layer_->parent()->bounds();
102 minimized_bounds.set_y(
103 minimized_bounds.bottom() - kHomeCardMinimizedHeight);
104 minimized_bounds.set_height(kHomeCardMinimizedHeight);
105 minimized_layer_->SetBounds(minimized_bounds);
108 // aura::LayoutManager:
109 virtual void OnWindowResized() OVERRIDE {
110 Layout(false, gfx::Tween::LINEAR);
111 UpdateMinimizedHomeBounds();
113 virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE {
116 Layout(false, gfx::Tween::LINEAR);
119 virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE {
120 if (home_card_ == child)
123 virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE {
125 virtual void OnChildWindowVisibilityChanged(aura::Window* child,
126 bool visible) OVERRIDE {
127 if (home_card_ == child)
128 Layout(false, gfx::Tween::LINEAR);
130 virtual void SetChildBounds(aura::Window* child,
131 const gfx::Rect& requested_bounds) OVERRIDE {
132 SetChildBoundsDirect(child, requested_bounds);
135 aura::Window* home_card_;
136 ui::Layer* minimized_layer_;
138 DISALLOW_COPY_AND_ASSIGN(HomeCardLayoutManager);
141 // The container view of home card contents of each state.
142 class HomeCardView : public views::WidgetDelegateView {
144 HomeCardView(app_list::AppListViewDelegate* view_delegate,
145 aura::Window* container,
146 HomeCardGestureManager::Delegate* gesture_delegate)
147 : gesture_delegate_(gesture_delegate) {
148 SetLayoutManager(new views::FillLayout());
149 // Ideally AppListMainView should be used here and have AthenaStartPageView
150 // as its child view, so that custom pages and apps grid are available in
152 // TODO(mukai): make it so after the detailed UI has been fixed.
153 main_view_ = new AthenaStartPageView(view_delegate);
154 AddChildView(main_view_);
157 void SetStateProgress(HomeCard::State from_state,
158 HomeCard::State to_state,
160 // TODO(mukai): not clear the focus, but simply close the virtual keyboard.
161 GetFocusManager()->ClearFocus();
162 if (from_state == HomeCard::VISIBLE_CENTERED)
163 main_view_->SetLayoutState(1.0f - progress);
164 else if (to_state == HomeCard::VISIBLE_CENTERED)
165 main_view_->SetLayoutState(progress);
169 void SetStateWithAnimation(HomeCard::State state,
170 gfx::Tween::Type tween_type) {
171 UpdateShadow(state != HomeCard::VISIBLE_MINIMIZED);
172 if (state == HomeCard::VISIBLE_CENTERED)
173 main_view_->RequestFocusOnSearchBox();
175 GetWidget()->GetFocusManager()->ClearFocus();
177 main_view_->SetLayoutStateWithAnimation(
178 (state == HomeCard::VISIBLE_CENTERED) ? 1.0f : 0.0f, tween_type);
181 void ClearGesture() {
182 gesture_manager_.reset();
186 virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE {
187 if (!gesture_manager_ &&
188 event->type() == ui::ET_GESTURE_SCROLL_BEGIN) {
189 gesture_manager_.reset(new HomeCardGestureManager(
191 GetWidget()->GetNativeWindow()->GetRootWindow()->bounds()));
194 if (gesture_manager_)
195 gesture_manager_->ProcessGestureEvent(event);
197 virtual bool OnMousePressed(const ui::MouseEvent& event) OVERRIDE {
198 if (HomeCard::Get()->GetState() == HomeCard::VISIBLE_MINIMIZED &&
199 event.IsLeftMouseButton() && event.GetClickCount() == 1) {
200 athena::WindowManager::Get()->ToggleOverview();
207 void UpdateShadow(bool should_show) {
209 GetWidget()->GetNativeWindow(),
210 should_show ? wm::SHADOW_TYPE_RECTANGULAR : wm::SHADOW_TYPE_NONE);
213 // views::WidgetDelegate:
214 virtual views::View* GetContentsView() OVERRIDE {
218 AthenaStartPageView* main_view_;
219 scoped_ptr<HomeCardGestureManager> gesture_manager_;
220 HomeCardGestureManager::Delegate* gesture_delegate_;
222 DISALLOW_COPY_AND_ASSIGN(HomeCardView);
225 HomeCardImpl::HomeCardImpl(AppModelBuilder* model_builder)
226 : model_builder_(model_builder),
228 original_state_(VISIBLE_MINIMIZED),
229 home_card_widget_(NULL),
230 home_card_view_(NULL),
231 layout_manager_(NULL),
232 activation_client_(NULL) {
235 WindowManager::Get()->AddObserver(this);
238 HomeCardImpl::~HomeCardImpl() {
240 WindowManager::Get()->RemoveObserver(this);
241 if (activation_client_)
242 activation_client_->RemoveObserver(this);
243 home_card_widget_->CloseNow();
245 // Reset the view delegate first as it access search provider during
247 view_delegate_.reset();
248 search_provider_.reset();
252 void HomeCardImpl::Init() {
253 InstallAccelerators();
254 ScreenManager::ContainerParams params("HomeCardContainer", CP_HOME_CARD);
255 params.can_activate_children = true;
256 aura::Window* container = ScreenManager::Get()->CreateContainer(params);
257 layout_manager_ = new HomeCardLayoutManager();
259 container->SetLayoutManager(layout_manager_);
260 wm::SetChildWindowVisibilityChangesAnimated(container);
262 view_delegate_.reset(new AppListViewDelegate(model_builder_.get()));
263 if (search_provider_)
264 view_delegate_->RegisterSearchProvider(search_provider_.get());
266 home_card_view_ = new HomeCardView(view_delegate_.get(), container, this);
267 home_card_widget_ = new views::Widget();
268 views::Widget::InitParams widget_params(
269 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
270 widget_params.parent = container;
271 widget_params.delegate = home_card_view_;
272 widget_params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
273 home_card_widget_->Init(widget_params);
275 minimized_home_ = CreateMinimizedHome();
276 container->layer()->Add(minimized_home_->layer());
277 container->layer()->StackAtTop(minimized_home_->layer());
278 layout_manager_->SetMinimizedLayer(minimized_home_->layer());
280 SetState(VISIBLE_MINIMIZED);
281 home_card_view_->Layout();
284 aura::client::GetActivationClient(container->GetRootWindow());
285 if (activation_client_)
286 activation_client_->AddObserver(this);
288 AthenaEnv::Get()->SetDisplayWorkAreaInsets(
289 gfx::Insets(0, 0, kHomeCardMinimizedHeight, 0));
292 aura::Window* HomeCardImpl::GetHomeCardWindowForTest() const {
293 return home_card_widget_ ? home_card_widget_->GetNativeWindow() : NULL;
296 void HomeCardImpl::InstallAccelerators() {
297 const AcceleratorData accelerator_data[] = {
298 {TRIGGER_ON_PRESS, ui::VKEY_L, ui::EF_CONTROL_DOWN,
299 COMMAND_SHOW_HOME_CARD, AF_NONE},
301 AcceleratorManager::Get()->RegisterAccelerators(
302 accelerator_data, arraysize(accelerator_data), this);
305 void HomeCardImpl::SetState(HomeCard::State state) {
309 // Update |state_| before changing the visibility of the widgets, so that
310 // LayoutManager callbacks get the correct state.
311 HomeCard::State old_state = state_;
313 original_state_ = state;
315 if (old_state == VISIBLE_MINIMIZED ||
316 state_ == VISIBLE_MINIMIZED) {
317 minimized_home_->layer()->SetVisible(true);
319 ui::ScopedLayerAnimationSettings settings(
320 minimized_home_->layer()->GetAnimator());
321 minimized_home_->layer()->SetVisible(state_ == VISIBLE_MINIMIZED);
322 minimized_home_->layer()->SetOpacity(
323 state_ == VISIBLE_MINIMIZED ? 1.0f : 0.0f);
326 if (state_ == HIDDEN) {
327 home_card_widget_->Hide();
329 if (state_ == VISIBLE_MINIMIZED)
330 home_card_widget_->ShowInactive();
332 home_card_widget_->Show();
333 home_card_view_->SetStateWithAnimation(state, gfx::Tween::EASE_IN_OUT);
334 layout_manager_->Layout(true, gfx::Tween::EASE_IN_OUT);
338 HomeCard::State HomeCardImpl::GetState() {
342 void HomeCardImpl::RegisterSearchProvider(
343 app_list::SearchProvider* search_provider) {
344 DCHECK(!search_provider_);
345 search_provider_.reset(search_provider);
346 view_delegate_->RegisterSearchProvider(search_provider_.get());
349 void HomeCardImpl::UpdateVirtualKeyboardBounds(
350 const gfx::Rect& bounds) {
351 if (state_ == VISIBLE_MINIMIZED && !bounds.IsEmpty()) {
353 original_state_ = VISIBLE_MINIMIZED;
354 } else if (state_ == VISIBLE_BOTTOM && !bounds.IsEmpty()) {
355 SetState(VISIBLE_CENTERED);
356 original_state_ = VISIBLE_BOTTOM;
357 } else if (state_ != original_state_ && bounds.IsEmpty()) {
358 SetState(original_state_);
362 bool HomeCardImpl::IsCommandEnabled(int command_id) const {
366 bool HomeCardImpl::OnAcceleratorFired(int command_id,
367 const ui::Accelerator& accelerator) {
368 DCHECK_EQ(COMMAND_SHOW_HOME_CARD, command_id);
370 if (state_ == VISIBLE_CENTERED && original_state_ != VISIBLE_BOTTOM)
371 SetState(VISIBLE_MINIMIZED);
372 else if (state_ == VISIBLE_MINIMIZED)
373 SetState(VISIBLE_CENTERED);
377 void HomeCardImpl::OnGestureEnded(State final_state, bool is_fling) {
378 home_card_view_->ClearGesture();
379 if (state_ != final_state &&
380 (state_ == VISIBLE_MINIMIZED || final_state == VISIBLE_MINIMIZED)) {
381 SetState(final_state);
382 WindowManager::Get()->ToggleOverview();
384 state_ = final_state;
385 // When the animation happens after a fling, EASE_IN_OUT would cause weird
386 // slow-down right after the finger release because of slow-in. Therefore
387 // EASE_OUT is better.
388 gfx::Tween::Type tween_type =
389 is_fling ? gfx::Tween::EASE_OUT : gfx::Tween::EASE_IN_OUT;
390 home_card_view_->SetStateWithAnimation(state_, tween_type);
391 layout_manager_->Layout(true, tween_type);
395 void HomeCardImpl::OnGestureProgressed(
396 State from_state, State to_state, float progress) {
397 if (from_state == VISIBLE_MINIMIZED || to_state == VISIBLE_MINIMIZED) {
398 minimized_home_->layer()->SetVisible(true);
400 (from_state == VISIBLE_MINIMIZED) ? 1.0f - progress : progress;
401 minimized_home_->layer()->SetOpacity(opacity);
403 gfx::Rect screen_bounds =
404 home_card_widget_->GetNativeWindow()->GetRootWindow()->bounds();
405 home_card_widget_->SetBounds(gfx::Tween::RectValueBetween(
407 GetBoundsForState(screen_bounds, from_state),
408 GetBoundsForState(screen_bounds, to_state)));
410 home_card_view_->SetStateProgress(from_state, to_state, progress);
412 // TODO(mukai): signals the update to the window manager so that it shows the
413 // intermediate visual state of overview mode.
416 void HomeCardImpl::OnOverviewModeEnter() {
417 if (state_ == HIDDEN || state_ == VISIBLE_MINIMIZED)
418 SetState(VISIBLE_BOTTOM);
421 void HomeCardImpl::OnOverviewModeExit() {
422 SetState(VISIBLE_MINIMIZED);
425 void HomeCardImpl::OnSplitViewModeEnter() {
428 void HomeCardImpl::OnSplitViewModeExit() {
431 void HomeCardImpl::OnWindowActivated(aura::Window* gained_active,
432 aura::Window* lost_active) {
433 if (state_ != HIDDEN &&
434 gained_active != home_card_widget_->GetNativeWindow()) {
435 SetState(VISIBLE_MINIMIZED);
440 HomeCard* HomeCard::Create(AppModelBuilder* model_builder) {
441 (new HomeCardImpl(model_builder))->Init();
447 void HomeCard::Shutdown() {
454 HomeCard* HomeCard::Get() {
459 } // namespace athena