Home | History | Annotate | Download | only in wm
      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/wm/window_manager_impl.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "athena/screen/public/screen_manager.h"
     10 #include "athena/util/container_priorities.h"
     11 #include "athena/wm/bezel_controller.h"
     12 #include "athena/wm/public/window_manager_observer.h"
     13 #include "athena/wm/split_view_controller.h"
     14 #include "athena/wm/title_drag_controller.h"
     15 #include "athena/wm/window_list_provider_impl.h"
     16 #include "athena/wm/window_overview_mode.h"
     17 #include "base/bind.h"
     18 #include "base/logging.h"
     19 #include "ui/aura/layout_manager.h"
     20 #include "ui/aura/window.h"
     21 #include "ui/compositor/closure_animation_observer.h"
     22 #include "ui/compositor/scoped_layer_animation_settings.h"
     23 #include "ui/gfx/display.h"
     24 #include "ui/gfx/screen.h"
     25 #include "ui/wm/core/shadow_controller.h"
     26 #include "ui/wm/core/window_util.h"
     27 #include "ui/wm/core/wm_state.h"
     28 #include "ui/wm/public/activation_client.h"
     29 #include "ui/wm/public/window_types.h"
     30 
     31 namespace athena {
     32 namespace {
     33 class WindowManagerImpl* instance = NULL;
     34 
     35 void SetWindowState(aura::Window* window,
     36                     const gfx::Rect& bounds,
     37                     const gfx::Transform& transform) {
     38   window->SetBounds(bounds);
     39   window->SetTransform(transform);
     40 }
     41 
     42 }  // namespace
     43 
     44 class AthenaContainerLayoutManager : public aura::LayoutManager {
     45  public:
     46   AthenaContainerLayoutManager();
     47   virtual ~AthenaContainerLayoutManager();
     48 
     49  private:
     50   // aura::LayoutManager:
     51   virtual void OnWindowResized() OVERRIDE;
     52   virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE;
     53   virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE;
     54   virtual void OnWindowRemovedFromLayout(aura::Window* child) OVERRIDE;
     55   virtual void OnChildWindowVisibilityChanged(aura::Window* child,
     56                                               bool visible) OVERRIDE;
     57   virtual void SetChildBounds(aura::Window* child,
     58                               const gfx::Rect& requested_bounds) OVERRIDE;
     59 
     60   DISALLOW_COPY_AND_ASSIGN(AthenaContainerLayoutManager);
     61 };
     62 
     63 AthenaContainerLayoutManager::AthenaContainerLayoutManager() {
     64 }
     65 
     66 AthenaContainerLayoutManager::~AthenaContainerLayoutManager() {
     67 }
     68 
     69 void AthenaContainerLayoutManager::OnWindowResized() {
     70   // Resize all the existing windows.
     71   const aura::Window::Windows& list =
     72       instance->window_list_provider_->GetWindowList();
     73   const gfx::Size work_area =
     74       gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size();
     75   bool is_splitview = instance->split_view_controller_->IsSplitViewModeActive();
     76   gfx::Size split_size;
     77   if (is_splitview) {
     78     CHECK(instance->split_view_controller_->left_window());
     79     split_size =
     80         instance->split_view_controller_->left_window()->bounds().size();
     81   }
     82 
     83   for (aura::Window::Windows::const_iterator iter = list.begin();
     84        iter != list.end();
     85        ++iter) {
     86     aura::Window* window = *iter;
     87     if (is_splitview) {
     88       if (window == instance->split_view_controller_->left_window())
     89         window->SetBounds(gfx::Rect(split_size));
     90       else if (window == instance->split_view_controller_->right_window())
     91         window->SetBounds(
     92             gfx::Rect(gfx::Point(split_size.width(), 0), split_size));
     93       else
     94         window->SetBounds(gfx::Rect(work_area));
     95     } else {
     96       window->SetBounds(gfx::Rect(work_area));
     97     }
     98   }
     99 }
    100 
    101 void AthenaContainerLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
    102   if (!instance->window_list_provider_->IsWindowInList(child))
    103     return;
    104 
    105   if (instance->split_view_controller_->IsSplitViewModeActive() &&
    106       !instance->IsOverviewModeActive()) {
    107     instance->split_view_controller_->ReplaceWindow(
    108         instance->split_view_controller_->left_window(), child);
    109   } else {
    110     gfx::Size size =
    111         gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size();
    112     child->SetBounds(gfx::Rect(size));
    113   }
    114 
    115   if (instance->IsOverviewModeActive()) {
    116     // TODO(pkotwicz|oshima). Creating a new window should only exit overview
    117     // mode if the new window is activated. crbug.com/415266
    118     instance->OnSelectWindow(child);
    119   }
    120 }
    121 
    122 void AthenaContainerLayoutManager::OnWillRemoveWindowFromLayout(
    123     aura::Window* child) {
    124 }
    125 
    126 void AthenaContainerLayoutManager::OnWindowRemovedFromLayout(
    127     aura::Window* child) {
    128 }
    129 
    130 void AthenaContainerLayoutManager::OnChildWindowVisibilityChanged(
    131     aura::Window* child,
    132     bool visible) {
    133 }
    134 
    135 void AthenaContainerLayoutManager::SetChildBounds(
    136     aura::Window* child,
    137     const gfx::Rect& requested_bounds) {
    138   if (!requested_bounds.IsEmpty())
    139     SetChildBoundsDirect(child, requested_bounds);
    140 }
    141 
    142 WindowManagerImpl::WindowManagerImpl() {
    143   ScreenManager::ContainerParams params("DefaultContainer", CP_DEFAULT);
    144   params.can_activate_children = true;
    145   container_.reset(ScreenManager::Get()->CreateDefaultContainer(params));
    146   container_->SetLayoutManager(new AthenaContainerLayoutManager);
    147   container_->AddObserver(this);
    148   window_list_provider_.reset(new WindowListProviderImpl(container_.get()));
    149   bezel_controller_.reset(new BezelController(container_.get()));
    150   split_view_controller_.reset(
    151       new SplitViewController(container_.get(), window_list_provider_.get()));
    152   AddObserver(split_view_controller_.get());
    153   bezel_controller_->set_left_right_delegate(split_view_controller_.get());
    154   container_->AddPreTargetHandler(bezel_controller_.get());
    155   title_drag_controller_.reset(new TitleDragController(container_.get(), this));
    156   wm_state_.reset(new wm::WMState());
    157   aura::client::ActivationClient* activation_client =
    158       aura::client::GetActivationClient(container_->GetRootWindow());
    159   shadow_controller_.reset(new wm::ShadowController(activation_client));
    160   instance = this;
    161   InstallAccelerators();
    162 }
    163 
    164 WindowManagerImpl::~WindowManagerImpl() {
    165   overview_.reset();
    166   RemoveObserver(split_view_controller_.get());
    167   split_view_controller_.reset();
    168   window_list_provider_.reset();
    169   if (container_) {
    170     container_->RemoveObserver(this);
    171     container_->RemovePreTargetHandler(bezel_controller_.get());
    172   }
    173   // |title_drag_controller_| needs to be reset before |container_|.
    174   title_drag_controller_.reset();
    175   container_.reset();
    176   instance = NULL;
    177 }
    178 
    179 void WindowManagerImpl::ToggleSplitView() {
    180   if (IsOverviewModeActive())
    181     return;
    182 
    183   if (split_view_controller_->IsSplitViewModeActive()) {
    184     split_view_controller_->DeactivateSplitMode();
    185     FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeExit());
    186     // Relayout so that windows are maximzied.
    187     container_->layout_manager()->OnWindowResized();
    188   } else if (split_view_controller_->CanActivateSplitViewMode()) {
    189     FOR_EACH_OBSERVER(WindowManagerObserver,
    190                       observers_,
    191                       OnSplitViewModeEnter());
    192     split_view_controller_->ActivateSplitMode(NULL, NULL, NULL);
    193   }
    194 }
    195 
    196 void WindowManagerImpl::ToggleOverview() {
    197   if (IsOverviewModeActive()) {
    198     SetInOverview(false);
    199 
    200     // Activate the window which was active prior to entering overview.
    201     const aura::Window::Windows windows =
    202         window_list_provider_->GetWindowList();
    203     if (!windows.empty()) {
    204       aura::Window* window = windows.back();
    205       // Show the window in case the exit overview animation has finished and
    206       // |window| was hidden.
    207       window->Show();
    208 
    209       wm::ActivateWindow(window);
    210     }
    211   } else {
    212     SetInOverview(true);
    213   }
    214 }
    215 
    216 bool WindowManagerImpl::IsOverviewModeActive() {
    217   return overview_;
    218 }
    219 
    220 void WindowManagerImpl::SetInOverview(bool active) {
    221   bool in_overview = !!overview_;
    222   if (active == in_overview)
    223     return;
    224 
    225   bezel_controller_->set_left_right_delegate(
    226       active ? NULL : split_view_controller_.get());
    227   if (active) {
    228     FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnOverviewModeEnter());
    229 
    230     // Note: The window_list_provider_ resembles the exact window list of the
    231     // container, so no re-stacking is required before showing the OverviewMode.
    232     overview_ = WindowOverviewMode::Create(
    233         container_.get(), window_list_provider_.get(),
    234         split_view_controller_.get(), this);
    235   } else {
    236     overview_.reset();
    237     FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnOverviewModeExit());
    238   }
    239 }
    240 
    241 void WindowManagerImpl::InstallAccelerators() {
    242   const AcceleratorData accelerator_data[] = {
    243       {TRIGGER_ON_PRESS, ui::VKEY_F6, ui::EF_NONE, CMD_TOGGLE_OVERVIEW,
    244        AF_NONE},
    245       {TRIGGER_ON_PRESS, ui::VKEY_F6, ui::EF_CONTROL_DOWN,
    246        CMD_TOGGLE_SPLIT_VIEW, AF_NONE},
    247   };
    248   AcceleratorManager::Get()->RegisterAccelerators(
    249       accelerator_data, arraysize(accelerator_data), this);
    250 }
    251 
    252 void WindowManagerImpl::AddObserver(WindowManagerObserver* observer) {
    253   observers_.AddObserver(observer);
    254 }
    255 
    256 void WindowManagerImpl::RemoveObserver(WindowManagerObserver* observer) {
    257   observers_.RemoveObserver(observer);
    258 }
    259 
    260 void WindowManagerImpl::ToggleSplitViewForTest() {
    261   ToggleSplitView();
    262 }
    263 
    264 WindowListProvider* WindowManagerImpl::GetWindowListProvider() {
    265   return window_list_provider_.get();
    266 }
    267 
    268 void WindowManagerImpl::OnSelectWindow(aura::Window* window) {
    269   SetInOverview(false);
    270 
    271   // Show the window in case the exit overview animation has finished and
    272   // |window| was hidden.
    273   window->Show();
    274 
    275   wm::ActivateWindow(window);
    276 
    277   if (split_view_controller_->IsSplitViewModeActive()) {
    278     split_view_controller_->DeactivateSplitMode();
    279     FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeExit());
    280   }
    281   // If |window| does not have the size of the work-area, then make sure it is
    282   // resized.
    283   const gfx::Size work_area =
    284       gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().work_area().size();
    285   if (window->GetTargetBounds().size() != work_area) {
    286     const gfx::Rect& window_bounds = window->bounds();
    287     const gfx::Rect desired_bounds(work_area);
    288     gfx::Transform transform;
    289     transform.Translate(desired_bounds.x() - window_bounds.x(),
    290                         desired_bounds.y() - window_bounds.y());
    291     transform.Scale(desired_bounds.width() / window_bounds.width(),
    292                     desired_bounds.height() / window_bounds.height());
    293     ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
    294     settings.SetPreemptionStrategy(
    295         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
    296     settings.AddObserver(
    297         new ui::ClosureAnimationObserver(base::Bind(&SetWindowState,
    298                                                     base::Unretained(window),
    299                                                     desired_bounds,
    300                                                     gfx::Transform())));
    301     window->SetTransform(transform);
    302   }
    303 }
    304 
    305 void WindowManagerImpl::OnSelectSplitViewWindow(aura::Window* left,
    306                                                 aura::Window* right,
    307                                                 aura::Window* to_activate) {
    308   SetInOverview(false);
    309   FOR_EACH_OBSERVER(WindowManagerObserver, observers_, OnSplitViewModeEnter());
    310   split_view_controller_->ActivateSplitMode(left, right, to_activate);
    311 }
    312 
    313 void WindowManagerImpl::OnWindowDestroying(aura::Window* window) {
    314   if (window == container_)
    315     container_.reset();
    316 }
    317 
    318 bool WindowManagerImpl::IsCommandEnabled(int command_id) const {
    319   return true;
    320 }
    321 
    322 bool WindowManagerImpl::OnAcceleratorFired(int command_id,
    323                                            const ui::Accelerator& accelerator) {
    324   switch (command_id) {
    325     case CMD_TOGGLE_OVERVIEW:
    326       ToggleOverview();
    327       break;
    328     case CMD_TOGGLE_SPLIT_VIEW:
    329       ToggleSplitView();
    330       break;
    331   }
    332   return true;
    333 }
    334 
    335 aura::Window* WindowManagerImpl::GetWindowBehind(aura::Window* window) {
    336   const aura::Window::Windows& windows = window_list_provider_->GetWindowList();
    337   aura::Window::Windows::const_reverse_iterator iter =
    338       std::find(windows.rbegin(), windows.rend(), window);
    339   CHECK(iter != windows.rend());
    340   ++iter;
    341   aura::Window* behind = NULL;
    342   if (iter != windows.rend())
    343     behind = *iter++;
    344 
    345   if (split_view_controller_->IsSplitViewModeActive()) {
    346     aura::Window* left = split_view_controller_->left_window();
    347     aura::Window* right = split_view_controller_->right_window();
    348     CHECK(window == left || window == right);
    349     if (behind == left || behind == right)
    350       behind = (iter == windows.rend()) ? NULL : *iter;
    351   }
    352 
    353   return behind;
    354 }
    355 
    356 void WindowManagerImpl::OnTitleDragStarted(aura::Window* window) {
    357   aura::Window* next_window = GetWindowBehind(window);
    358   if (!next_window)
    359     return;
    360   // Make sure |window| is active.
    361   wm::ActivateWindow(window);
    362 
    363   // Make sure |next_window| is visibile.
    364   next_window->Show();
    365 
    366   // Position |next_window| correctly (left aligned if it's larger than
    367   // |window|, and center aligned otherwise).
    368   int dx = window->bounds().x() - next_window->bounds().x();
    369   if (next_window->bounds().width() < window->bounds().width())
    370     dx -= (next_window->bounds().width() - window->bounds().width()) / 2;
    371 
    372   if (dx) {
    373     gfx::Transform transform;
    374     transform.Translate(dx, 0);
    375     next_window->SetTransform(transform);
    376   }
    377 }
    378 
    379 void WindowManagerImpl::OnTitleDragCompleted(aura::Window* window) {
    380   aura::Window* next_window = GetWindowBehind(window);
    381   if (!next_window)
    382     return;
    383   if (split_view_controller_->IsSplitViewModeActive()) {
    384     split_view_controller_->ReplaceWindow(window, next_window);
    385   } else {
    386     ui::ScopedLayerAnimationSettings
    387         settings(next_window->layer()->GetAnimator());
    388     settings.AddObserver(new ui::ClosureAnimationObserver(
    389         base::Bind(&SetWindowState,
    390                    base::Unretained(next_window),
    391                    window->bounds(),
    392                    gfx::Transform())));
    393 
    394     gfx::Transform transform;
    395     transform.Scale(window->bounds().width() / next_window->bounds().width(),
    396                     window->bounds().height() / next_window->bounds().height());
    397     transform.Translate(window->bounds().x() - next_window->bounds().x(), 0);
    398     next_window->SetTransform(transform);
    399 
    400     wm::ActivateWindow(next_window);
    401   }
    402   window->Hide();
    403 }
    404 
    405 void WindowManagerImpl::OnTitleDragCanceled(aura::Window* window) {
    406   aura::Window* next_window = GetWindowBehind(window);
    407   if (!next_window)
    408     return;
    409   next_window->SetTransform(gfx::Transform());
    410   next_window->Hide();
    411 }
    412 
    413 // static
    414 WindowManager* WindowManager::Create() {
    415   DCHECK(!instance);
    416   new WindowManagerImpl;
    417   DCHECK(instance);
    418   return instance;
    419 }
    420 
    421 // static
    422 void WindowManager::Shutdown() {
    423   DCHECK(instance);
    424   delete instance;
    425   DCHECK(!instance);
    426 }
    427 
    428 // static
    429 WindowManager* WindowManager::Get() {
    430   DCHECK(instance);
    431   return instance;
    432 }
    433 
    434 }  // namespace athena
    435