Home | History | Annotate | Download | only in wm
      1 // Copyright (c) 2012 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 "ash/wm/base_layout_manager.h"
      6 
      7 #include "ash/screen_ash.h"
      8 #include "ash/session_state_delegate.h"
      9 #include "ash/shelf/shelf_layout_manager.h"
     10 #include "ash/shell.h"
     11 #include "ash/wm/window_animations.h"
     12 #include "ash/wm/window_properties.h"
     13 #include "ash/wm/window_util.h"
     14 #include "ash/wm/workspace/workspace_window_resizer.h"
     15 #include "ui/aura/client/activation_client.h"
     16 #include "ui/aura/client/aura_constants.h"
     17 #include "ui/aura/root_window.h"
     18 #include "ui/aura/window.h"
     19 #include "ui/base/ui_base_types.h"
     20 #include "ui/compositor/layer.h"
     21 #include "ui/gfx/screen.h"
     22 #include "ui/views/corewm/corewm_switches.h"
     23 #include "ui/views/corewm/window_util.h"
     24 
     25 namespace ash {
     26 namespace internal {
     27 
     28 /////////////////////////////////////////////////////////////////////////////
     29 // BaseLayoutManager, public:
     30 
     31 BaseLayoutManager::BaseLayoutManager(aura::RootWindow* root_window)
     32     : root_window_(root_window) {
     33   Shell::GetInstance()->activation_client()->AddObserver(this);
     34   Shell::GetInstance()->AddShellObserver(this);
     35   root_window_->AddObserver(this);
     36 }
     37 
     38 BaseLayoutManager::~BaseLayoutManager() {
     39   if (root_window_)
     40     root_window_->RemoveObserver(this);
     41   for (WindowSet::const_iterator i = windows_.begin(); i != windows_.end(); ++i)
     42     (*i)->RemoveObserver(this);
     43   Shell::GetInstance()->RemoveShellObserver(this);
     44   Shell::GetInstance()->activation_client()->RemoveObserver(this);
     45 }
     46 
     47 // static
     48 gfx::Rect BaseLayoutManager::BoundsWithScreenEdgeVisible(
     49     aura::Window* window,
     50     const gfx::Rect& restore_bounds) {
     51   gfx::Rect max_bounds =
     52       ash::ScreenAsh::GetMaximizedWindowBoundsInParent(window);
     53   // If the restore_bounds are more than 1 grid step away from the size the
     54   // window would be when maximized, inset it.
     55   max_bounds.Inset(ash::internal::WorkspaceWindowResizer::kScreenEdgeInset,
     56                    ash::internal::WorkspaceWindowResizer::kScreenEdgeInset);
     57   if (restore_bounds.Contains(max_bounds))
     58     return max_bounds;
     59   return restore_bounds;
     60 }
     61 
     62 /////////////////////////////////////////////////////////////////////////////
     63 // BaseLayoutManager, LayoutManager overrides:
     64 
     65 void BaseLayoutManager::OnWindowResized() {
     66 }
     67 
     68 void BaseLayoutManager::OnWindowAddedToLayout(aura::Window* child) {
     69   windows_.insert(child);
     70   child->AddObserver(this);
     71   // Only update the bounds if the window has a show state that depends on the
     72   // workspace area.
     73   if (wm::IsWindowMaximized(child) || wm::IsWindowFullscreen(child))
     74     UpdateBoundsFromShowState(child);
     75 }
     76 
     77 void BaseLayoutManager::OnWillRemoveWindowFromLayout(aura::Window* child) {
     78   windows_.erase(child);
     79   child->RemoveObserver(this);
     80 }
     81 
     82 void BaseLayoutManager::OnWindowRemovedFromLayout(aura::Window* child) {
     83 }
     84 
     85 void BaseLayoutManager::OnChildWindowVisibilityChanged(aura::Window* child,
     86                                                        bool visible) {
     87   if (visible && wm::IsWindowMinimized(child)) {
     88     // Attempting to show a minimized window. Unminimize it.
     89     child->SetProperty(aura::client::kShowStateKey,
     90                        child->GetProperty(aura::client::kRestoreShowStateKey));
     91     child->ClearProperty(aura::client::kRestoreShowStateKey);
     92   }
     93 }
     94 
     95 void BaseLayoutManager::SetChildBounds(aura::Window* child,
     96                                        const gfx::Rect& requested_bounds) {
     97   gfx::Rect child_bounds(requested_bounds);
     98   // Some windows rely on this to set their initial bounds.
     99   if (wm::IsWindowMaximized(child))
    100     child_bounds = ScreenAsh::GetMaximizedWindowBoundsInParent(child);
    101   else if (wm::IsWindowFullscreen(child))
    102     child_bounds = ScreenAsh::GetDisplayBoundsInParent(child);
    103   SetChildBoundsDirect(child, child_bounds);
    104 }
    105 
    106 /////////////////////////////////////////////////////////////////////////////
    107 // BaseLayoutManager, ash::ShellObserver overrides:
    108 
    109 void BaseLayoutManager::OnDisplayWorkAreaInsetsChanged() {
    110   AdjustAllWindowsBoundsForWorkAreaChange(
    111       ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED);
    112 }
    113 
    114 /////////////////////////////////////////////////////////////////////////////
    115 // BaseLayoutManager, WindowObserver overrides:
    116 
    117 void BaseLayoutManager::OnWindowPropertyChanged(aura::Window* window,
    118                                                 const void* key,
    119                                                 intptr_t old) {
    120   if (key == aura::client::kShowStateKey) {
    121     ui::WindowShowState old_state = static_cast<ui::WindowShowState>(old);
    122     ui::WindowShowState new_state =
    123         window->GetProperty(aura::client::kShowStateKey);
    124     if (old_state != new_state && old_state != ui::SHOW_STATE_MINIMIZED &&
    125         !GetRestoreBoundsInScreen(window) &&
    126         ((new_state == ui::SHOW_STATE_MAXIMIZED &&
    127           old_state != ui::SHOW_STATE_FULLSCREEN) ||
    128          (new_state == ui::SHOW_STATE_FULLSCREEN &&
    129           old_state != ui::SHOW_STATE_MAXIMIZED))) {
    130       SetRestoreBoundsInParent(window, window->bounds());
    131     }
    132 
    133     UpdateBoundsFromShowState(window);
    134     ShowStateChanged(window, old_state);
    135   }
    136 }
    137 
    138 void BaseLayoutManager::OnWindowDestroying(aura::Window* window) {
    139   if (root_window_ == window) {
    140     root_window_->RemoveObserver(this);
    141     root_window_ = NULL;
    142   }
    143 }
    144 
    145 void BaseLayoutManager::OnWindowBoundsChanged(aura::Window* window,
    146                                               const gfx::Rect& old_bounds,
    147                                               const gfx::Rect& new_bounds) {
    148   if (root_window_ == window)
    149     AdjustAllWindowsBoundsForWorkAreaChange(ADJUST_WINDOW_DISPLAY_SIZE_CHANGED);
    150 }
    151 
    152 //////////////////////////////////////////////////////////////////////////////
    153 // BaseLayoutManager, aura::client::ActivationChangeObserver implementation:
    154 
    155 void BaseLayoutManager::OnWindowActivated(aura::Window* gained_active,
    156                                           aura::Window* lost_active) {
    157   if (views::corewm::UseFocusController()) {
    158     if (gained_active && wm::IsWindowMinimized(gained_active) &&
    159         !gained_active->IsVisible()) {
    160       gained_active->Show();
    161       DCHECK(!wm::IsWindowMinimized(gained_active));
    162     }
    163   }
    164 }
    165 
    166 //////////////////////////////////////////////////////////////////////////////
    167 // BaseLayoutManager, protected:
    168 
    169 void BaseLayoutManager::ShowStateChanged(aura::Window* window,
    170                                          ui::WindowShowState last_show_state) {
    171   if (wm::IsWindowMinimized(window)) {
    172     // Save the previous show state so that we can correctly restore it.
    173     window->SetProperty(aura::client::kRestoreShowStateKey, last_show_state);
    174     views::corewm::SetWindowVisibilityAnimationType(
    175         window, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
    176 
    177     // Hide the window.
    178     window->Hide();
    179     // Activate another window.
    180     if (wm::IsActiveWindow(window))
    181       wm::DeactivateWindow(window);
    182   } else if ((window->TargetVisibility() ||
    183               last_show_state == ui::SHOW_STATE_MINIMIZED) &&
    184              !window->layer()->visible()) {
    185     // The layer may be hidden if the window was previously minimized. Make
    186     // sure it's visible.
    187     window->Show();
    188     if (last_show_state == ui::SHOW_STATE_MINIMIZED &&
    189         !wm::IsWindowMaximized(window) &&
    190         !wm::IsWindowFullscreen(window)) {
    191       window->ClearProperty(internal::kWindowRestoresToRestoreBounds);
    192     }
    193   }
    194 }
    195 
    196 void BaseLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange(
    197     AdjustWindowReason reason) {
    198   // Don't do any adjustments of the insets while we are in screen locked mode.
    199   // This would happen if the launcher was auto hidden before the login screen
    200   // was shown and then gets shown when the login screen gets presented.
    201   if (reason == ADJUST_WINDOW_WORK_AREA_INSETS_CHANGED &&
    202       Shell::GetInstance()->session_state_delegate()->IsScreenLocked())
    203     return;
    204 
    205   // If a user plugs an external display into a laptop running Aura the
    206   // display size will change.  Maximized windows need to resize to match.
    207   // We also do this when developers running Aura on a desktop manually resize
    208   // the host window.
    209   // We also need to do this when the work area insets changes.
    210   for (WindowSet::const_iterator it = windows_.begin();
    211        it != windows_.end();
    212        ++it) {
    213     AdjustWindowBoundsForWorkAreaChange(*it, reason);
    214   }
    215 }
    216 
    217 void BaseLayoutManager::AdjustWindowBoundsForWorkAreaChange(
    218     aura::Window* window,
    219     AdjustWindowReason reason) {
    220   if (wm::IsWindowMaximized(window)) {
    221     SetChildBoundsDirect(
    222         window, ScreenAsh::GetMaximizedWindowBoundsInParent(window));
    223   } else if (wm::IsWindowFullscreen(window)) {
    224     SetChildBoundsDirect(
    225         window, ScreenAsh::GetDisplayBoundsInParent(window));
    226   } else {
    227     // The work area may be smaller than the full screen.
    228     gfx::Rect display_rect =
    229         ScreenAsh::GetDisplayWorkAreaBoundsInParent(window);
    230     // Put as much of the window as possible within the display area.
    231     gfx::Rect bounds = window->bounds();
    232     bounds.AdjustToFit(display_rect);
    233     window->SetBounds(bounds);
    234   }
    235 }
    236 
    237 //////////////////////////////////////////////////////////////////////////////
    238 // BaseLayoutManager, private:
    239 
    240 void BaseLayoutManager::UpdateBoundsFromShowState(aura::Window* window) {
    241   switch (window->GetProperty(aura::client::kShowStateKey)) {
    242     case ui::SHOW_STATE_DEFAULT:
    243     case ui::SHOW_STATE_NORMAL: {
    244       const gfx::Rect* restore = GetRestoreBoundsInScreen(window);
    245       if (restore) {
    246         gfx::Rect bounds_in_parent =
    247             ScreenAsh::ConvertRectFromScreen(window->parent(), *restore);
    248         SetChildBoundsDirect(window,
    249                              BoundsWithScreenEdgeVisible(window,
    250                                                          bounds_in_parent));
    251       }
    252       ClearRestoreBounds(window);
    253       break;
    254     }
    255 
    256     case ui::SHOW_STATE_MAXIMIZED:
    257       SetChildBoundsDirect(window,
    258                            ScreenAsh::GetMaximizedWindowBoundsInParent(window));
    259       break;
    260 
    261     case ui::SHOW_STATE_FULLSCREEN:
    262       // Don't animate the full-screen window transition.
    263       // TODO(jamescook): Use animation here.  Be sure the lock screen works.
    264       SetChildBoundsDirect(
    265           window, ScreenAsh::GetDisplayBoundsInParent(window));
    266       break;
    267 
    268     default:
    269       break;
    270   }
    271 }
    272 
    273 }  // namespace internal
    274 }  // namespace ash
    275