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/home_card_impl.h" 6 7#include <cmath> 8#include <limits> 9 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" 33 34namespace athena { 35namespace { 36 37HomeCard* instance = NULL; 38 39gfx::Rect GetBoundsForState(const gfx::Rect& screen_bounds, 40 HomeCard::State state) { 41 switch (state) { 42 case HomeCard::HIDDEN: 43 break; 44 45 case HomeCard::VISIBLE_CENTERED: 46 return screen_bounds; 47 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: 51 return gfx::Rect(0, 52 screen_bounds.bottom() - kHomeCardHeight, 53 screen_bounds.width(), 54 screen_bounds.height()); 55 case HomeCard::VISIBLE_MINIMIZED: 56 return gfx::Rect(0, 57 screen_bounds.bottom() - kHomeCardMinimizedHeight, 58 screen_bounds.width(), 59 screen_bounds.height()); 60 } 61 62 NOTREACHED(); 63 return gfx::Rect(); 64} 65 66} // namespace 67 68// Makes sure the homecard is center-aligned horizontally and bottom-aligned 69// vertically. 70class HomeCardLayoutManager : public aura::LayoutManager { 71 public: 72 HomeCardLayoutManager() 73 : home_card_(NULL), 74 minimized_layer_(NULL) {} 75 76 virtual ~HomeCardLayoutManager() {} 77 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 80 // destroyed). 81 if (!home_card_ || !home_card_->IsVisible() || !home_card_->GetRootWindow()) 82 return; 83 84 scoped_ptr<ui::ScopedLayerAnimationSettings> settings; 85 if (animate) { 86 settings.reset(new ui::ScopedLayerAnimationSettings( 87 home_card_->layer()->GetAnimator())); 88 settings->SetTweenType(tween_type); 89 } 90 SetChildBoundsDirect(home_card_, GetBoundsForState( 91 home_card_->GetRootWindow()->bounds(), HomeCard::Get()->GetState())); 92 } 93 94 void SetMinimizedLayer(ui::Layer* minimized_layer) { 95 minimized_layer_ = minimized_layer; 96 UpdateMinimizedHomeBounds(); 97 } 98 99 private: 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); 106 } 107 108 // aura::LayoutManager: 109 virtual void OnWindowResized() OVERRIDE { 110 Layout(false, gfx::Tween::LINEAR); 111 UpdateMinimizedHomeBounds(); 112 } 113 virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE { 114 if (!home_card_) { 115 home_card_ = child; 116 Layout(false, gfx::Tween::LINEAR); 117 } 118 } 119 virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE { 120 if (home_card_ == child) 121 home_card_ = NULL; 122 } 123 virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE { 124 } 125 virtual void OnChildWindowVisibilityChanged(aura::Window* child, 126 bool visible) OVERRIDE { 127 if (home_card_ == child) 128 Layout(false, gfx::Tween::LINEAR); 129 } 130 virtual void SetChildBounds(aura::Window* child, 131 const gfx::Rect& requested_bounds) OVERRIDE { 132 SetChildBoundsDirect(child, requested_bounds); 133 } 134 135 aura::Window* home_card_; 136 ui::Layer* minimized_layer_; 137 138 DISALLOW_COPY_AND_ASSIGN(HomeCardLayoutManager); 139}; 140 141// The container view of home card contents of each state. 142class HomeCardView : public views::WidgetDelegateView { 143 public: 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 151 // the home card. 152 // TODO(mukai): make it so after the detailed UI has been fixed. 153 main_view_ = new AthenaStartPageView(view_delegate); 154 AddChildView(main_view_); 155 } 156 157 void SetStateProgress(HomeCard::State from_state, 158 HomeCard::State to_state, 159 float progress) { 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); 166 UpdateShadow(true); 167 } 168 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(); 174 else 175 GetWidget()->GetFocusManager()->ClearFocus(); 176 177 main_view_->SetLayoutStateWithAnimation( 178 (state == HomeCard::VISIBLE_CENTERED) ? 1.0f : 0.0f, tween_type); 179 } 180 181 void ClearGesture() { 182 gesture_manager_.reset(); 183 } 184 185 // views::View: 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( 190 gesture_delegate_, 191 GetWidget()->GetNativeWindow()->GetRootWindow()->bounds())); 192 } 193 194 if (gesture_manager_) 195 gesture_manager_->ProcessGestureEvent(event); 196 } 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(); 201 return true; 202 } 203 return false; 204 } 205 206 private: 207 void UpdateShadow(bool should_show) { 208 wm::SetShadowType( 209 GetWidget()->GetNativeWindow(), 210 should_show ? wm::SHADOW_TYPE_RECTANGULAR : wm::SHADOW_TYPE_NONE); 211 } 212 213 // views::WidgetDelegate: 214 virtual views::View* GetContentsView() OVERRIDE { 215 return this; 216 } 217 218 AthenaStartPageView* main_view_; 219 scoped_ptr<HomeCardGestureManager> gesture_manager_; 220 HomeCardGestureManager::Delegate* gesture_delegate_; 221 222 DISALLOW_COPY_AND_ASSIGN(HomeCardView); 223}; 224 225HomeCardImpl::HomeCardImpl(AppModelBuilder* model_builder) 226 : model_builder_(model_builder), 227 state_(HIDDEN), 228 original_state_(VISIBLE_MINIMIZED), 229 home_card_widget_(NULL), 230 home_card_view_(NULL), 231 layout_manager_(NULL), 232 activation_client_(NULL) { 233 DCHECK(!instance); 234 instance = this; 235 WindowManager::Get()->AddObserver(this); 236} 237 238HomeCardImpl::~HomeCardImpl() { 239 DCHECK(instance); 240 WindowManager::Get()->RemoveObserver(this); 241 if (activation_client_) 242 activation_client_->RemoveObserver(this); 243 home_card_widget_->CloseNow(); 244 245 // Reset the view delegate first as it access search provider during 246 // shutdown. 247 view_delegate_.reset(); 248 search_provider_.reset(); 249 instance = NULL; 250} 251 252void 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(); 258 259 container->SetLayoutManager(layout_manager_); 260 wm::SetChildWindowVisibilityChangesAnimated(container); 261 262 view_delegate_.reset(new AppListViewDelegate(model_builder_.get())); 263 if (search_provider_) 264 view_delegate_->RegisterSearchProvider(search_provider_.get()); 265 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); 274 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()); 279 280 SetState(VISIBLE_MINIMIZED); 281 home_card_view_->Layout(); 282 283 activation_client_ = 284 aura::client::GetActivationClient(container->GetRootWindow()); 285 if (activation_client_) 286 activation_client_->AddObserver(this); 287 288 AthenaEnv::Get()->SetDisplayWorkAreaInsets( 289 gfx::Insets(0, 0, kHomeCardMinimizedHeight, 0)); 290} 291 292aura::Window* HomeCardImpl::GetHomeCardWindowForTest() const { 293 return home_card_widget_ ? home_card_widget_->GetNativeWindow() : NULL; 294} 295 296void 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}, 300 }; 301 AcceleratorManager::Get()->RegisterAccelerators( 302 accelerator_data, arraysize(accelerator_data), this); 303} 304 305void HomeCardImpl::SetState(HomeCard::State state) { 306 if (state_ == state) 307 return; 308 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_; 312 state_ = state; 313 original_state_ = state; 314 315 if (old_state == VISIBLE_MINIMIZED || 316 state_ == VISIBLE_MINIMIZED) { 317 minimized_home_->layer()->SetVisible(true); 318 { 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); 324 } 325 } 326 if (state_ == HIDDEN) { 327 home_card_widget_->Hide(); 328 } else { 329 if (state_ == VISIBLE_MINIMIZED) 330 home_card_widget_->ShowInactive(); 331 else 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); 335 } 336} 337 338HomeCard::State HomeCardImpl::GetState() { 339 return state_; 340} 341 342void 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()); 347} 348 349void HomeCardImpl::UpdateVirtualKeyboardBounds( 350 const gfx::Rect& bounds) { 351 if (state_ == VISIBLE_MINIMIZED && !bounds.IsEmpty()) { 352 SetState(HIDDEN); 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_); 359 } 360} 361 362bool HomeCardImpl::IsCommandEnabled(int command_id) const { 363 return true; 364} 365 366bool HomeCardImpl::OnAcceleratorFired(int command_id, 367 const ui::Accelerator& accelerator) { 368 DCHECK_EQ(COMMAND_SHOW_HOME_CARD, command_id); 369 370 if (state_ == VISIBLE_CENTERED && original_state_ != VISIBLE_BOTTOM) 371 SetState(VISIBLE_MINIMIZED); 372 else if (state_ == VISIBLE_MINIMIZED) 373 SetState(VISIBLE_CENTERED); 374 return true; 375} 376 377void 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(); 383 } else { 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); 392 } 393} 394 395void 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); 399 float opacity = 400 (from_state == VISIBLE_MINIMIZED) ? 1.0f - progress : progress; 401 minimized_home_->layer()->SetOpacity(opacity); 402 } 403 gfx::Rect screen_bounds = 404 home_card_widget_->GetNativeWindow()->GetRootWindow()->bounds(); 405 home_card_widget_->SetBounds(gfx::Tween::RectValueBetween( 406 progress, 407 GetBoundsForState(screen_bounds, from_state), 408 GetBoundsForState(screen_bounds, to_state))); 409 410 home_card_view_->SetStateProgress(from_state, to_state, progress); 411 412 // TODO(mukai): signals the update to the window manager so that it shows the 413 // intermediate visual state of overview mode. 414} 415 416void HomeCardImpl::OnOverviewModeEnter() { 417 if (state_ == HIDDEN || state_ == VISIBLE_MINIMIZED) 418 SetState(VISIBLE_BOTTOM); 419} 420 421void HomeCardImpl::OnOverviewModeExit() { 422 SetState(VISIBLE_MINIMIZED); 423} 424 425void HomeCardImpl::OnSplitViewModeEnter() { 426} 427 428void HomeCardImpl::OnSplitViewModeExit() { 429} 430 431void 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); 436 } 437} 438 439// static 440HomeCard* HomeCard::Create(AppModelBuilder* model_builder) { 441 (new HomeCardImpl(model_builder))->Init(); 442 DCHECK(instance); 443 return instance; 444} 445 446// static 447void HomeCard::Shutdown() { 448 DCHECK(instance); 449 delete instance; 450 instance = NULL; 451} 452 453// static 454HomeCard* HomeCard::Get() { 455 DCHECK(instance); 456 return instance; 457} 458 459} // namespace athena 460