Home | History | Annotate | Download | only in workspace
      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/workspace/workspace_layout_manager.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "ash/display/display_controller.h"
     10 #include "ash/root_window_controller.h"
     11 #include "ash/screen_util.h"
     12 #include "ash/session/session_state_delegate.h"
     13 #include "ash/shelf/shelf_layout_manager.h"
     14 #include "ash/shell.h"
     15 #include "ash/wm/always_on_top_controller.h"
     16 #include "ash/wm/window_animations.h"
     17 #include "ash/wm/window_positioner.h"
     18 #include "ash/wm/window_properties.h"
     19 #include "ash/wm/window_state.h"
     20 #include "ash/wm/window_util.h"
     21 #include "ash/wm/wm_event.h"
     22 #include "ash/wm/workspace/workspace_layout_manager_delegate.h"
     23 #include "ui/aura/client/aura_constants.h"
     24 #include "ui/aura/window.h"
     25 #include "ui/aura/window_observer.h"
     26 #include "ui/base/ime/input_method.h"
     27 #include "ui/base/ime/text_input_client.h"
     28 #include "ui/base/ui_base_types.h"
     29 #include "ui/compositor/layer.h"
     30 #include "ui/events/event.h"
     31 #include "ui/gfx/screen.h"
     32 #include "ui/keyboard/keyboard_controller_observer.h"
     33 #include "ui/wm/core/window_util.h"
     34 #include "ui/wm/public/activation_client.h"
     35 
     36 using aura::Window;
     37 
     38 namespace ash {
     39 
     40 WorkspaceLayoutManager::WorkspaceLayoutManager(aura::Window* window)
     41     : shelf_(NULL),
     42       window_(window),
     43       root_window_(window->GetRootWindow()),
     44       work_area_in_parent_(ScreenUtil::ConvertRectFromScreen(
     45           window_,
     46           Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area())),
     47       is_fullscreen_(GetRootWindowController(
     48           window->GetRootWindow())->GetWindowForFullscreenMode() != NULL) {
     49   Shell::GetInstance()->activation_client()->AddObserver(this);
     50   Shell::GetInstance()->AddShellObserver(this);
     51   root_window_->AddObserver(this);
     52 }
     53 
     54 WorkspaceLayoutManager::~WorkspaceLayoutManager() {
     55   if (root_window_)
     56     root_window_->RemoveObserver(this);
     57   for (WindowSet::const_iterator i = windows_.begin(); i != windows_.end(); ++i)
     58     (*i)->RemoveObserver(this);
     59   Shell::GetInstance()->RemoveShellObserver(this);
     60   Shell::GetInstance()->activation_client()->RemoveObserver(this);
     61 }
     62 
     63 void WorkspaceLayoutManager::SetShelf(ShelfLayoutManager* shelf) {
     64   shelf_ = shelf;
     65 }
     66 
     67 void WorkspaceLayoutManager::SetMaximizeBackdropDelegate(
     68     scoped_ptr<WorkspaceLayoutManagerDelegate> delegate) {
     69   backdrop_delegate_.reset(delegate.release());
     70 }
     71 
     72 //////////////////////////////////////////////////////////////////////////////
     73 // WorkspaceLayoutManager, aura::LayoutManager implementation:
     74 
     75 void WorkspaceLayoutManager::OnWindowAddedToLayout(Window* child) {
     76   wm::WindowState* window_state = wm::GetWindowState(child);
     77   wm::WMEvent event(wm::WM_EVENT_ADDED_TO_WORKSPACE);
     78   window_state->OnWMEvent(&event);
     79   windows_.insert(child);
     80   child->AddObserver(this);
     81   window_state->AddObserver(this);
     82   UpdateShelfVisibility();
     83   UpdateFullscreenState();
     84   if (backdrop_delegate_)
     85     backdrop_delegate_->OnWindowAddedToLayout(child);
     86   WindowPositioner::RearrangeVisibleWindowOnShow(child);
     87 }
     88 
     89 void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout(Window* child) {
     90   windows_.erase(child);
     91   child->RemoveObserver(this);
     92   wm::GetWindowState(child)->RemoveObserver(this);
     93 
     94   if (child->TargetVisibility())
     95     WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child);
     96 }
     97 
     98 void WorkspaceLayoutManager::OnWindowRemovedFromLayout(Window* child) {
     99   UpdateShelfVisibility();
    100   UpdateFullscreenState();
    101   if (backdrop_delegate_)
    102     backdrop_delegate_->OnWindowRemovedFromLayout(child);
    103 }
    104 
    105 void WorkspaceLayoutManager::OnChildWindowVisibilityChanged(Window* child,
    106                                                             bool visible) {
    107   wm::WindowState* window_state = wm::GetWindowState(child);
    108   // Attempting to show a minimized window. Unminimize it.
    109   if (visible && window_state->IsMinimized())
    110     window_state->Unminimize();
    111 
    112   if (child->TargetVisibility())
    113     WindowPositioner::RearrangeVisibleWindowOnShow(child);
    114   else
    115     WindowPositioner::RearrangeVisibleWindowOnHideOrRemove(child);
    116   UpdateFullscreenState();
    117   UpdateShelfVisibility();
    118   if (backdrop_delegate_)
    119     backdrop_delegate_->OnChildWindowVisibilityChanged(child, visible);
    120 }
    121 
    122 void WorkspaceLayoutManager::SetChildBounds(
    123     Window* child,
    124     const gfx::Rect& requested_bounds) {
    125   wm::WindowState* window_state = wm::GetWindowState(child);
    126   wm::SetBoundsEvent event(wm::WM_EVENT_SET_BOUNDS, requested_bounds);
    127   window_state->OnWMEvent(&event);
    128   UpdateShelfVisibility();
    129 }
    130 
    131 //////////////////////////////////////////////////////////////////////////////
    132 // WorkspaceLayoutManager, keyboard::KeyboardControllerObserver implementation:
    133 
    134 void WorkspaceLayoutManager::OnKeyboardBoundsChanging(
    135     const gfx::Rect& new_bounds) {
    136   aura::Window* root_window = window_->GetRootWindow();
    137   ui::InputMethod* input_method =
    138       root_window->GetProperty(aura::client::kRootWindowInputMethodKey);
    139   ui::TextInputClient* text_input_client = input_method->GetTextInputClient();
    140   if (!text_input_client)
    141     return;
    142   aura::Window *window = text_input_client->GetAttachedWindow();
    143   if (!window || !window_->Contains(window))
    144     return;
    145   gfx::Rect window_bounds = ScreenUtil::ConvertRectToScreen(
    146       window_,
    147       window->GetTargetBounds());
    148   gfx::Rect intersect = gfx::IntersectRects(window_bounds, new_bounds);
    149   int shift = std::min(intersect.height(),
    150                        window->bounds().y() - work_area_in_parent_.y());
    151   if (shift > 0) {
    152     gfx::Point origin(window->bounds().x(), window->bounds().y() - shift);
    153     SetChildBounds(window, gfx::Rect(origin, window->bounds().size()));
    154   }
    155 }
    156 
    157 //////////////////////////////////////////////////////////////////////////////
    158 // WorkspaceLayoutManager, ash::ShellObserver implementation:
    159 
    160 void WorkspaceLayoutManager::OnDisplayWorkAreaInsetsChanged() {
    161   const gfx::Rect work_area(ScreenUtil::ConvertRectFromScreen(
    162       window_,
    163       Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area()));
    164   if (work_area != work_area_in_parent_) {
    165     const wm::WMEvent event(wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED);
    166     AdjustAllWindowsBoundsForWorkAreaChange(&event);
    167   }
    168   if (backdrop_delegate_)
    169     backdrop_delegate_->OnDisplayWorkAreaInsetsChanged();
    170 }
    171 
    172 //////////////////////////////////////////////////////////////////////////////
    173 // WorkspaceLayoutManager, aura::WindowObserver implementation:
    174 
    175 void WorkspaceLayoutManager::OnWindowHierarchyChanged(
    176     const WindowObserver::HierarchyChangeParams& params) {
    177   if (!wm::GetWindowState(params.target)->IsActive())
    178     return;
    179   // If the window is already tracked by the workspace this update would be
    180   // redundant as the fullscreen and shelf state would have been handled in
    181   // OnWindowAddedToLayout.
    182   if (windows_.find(params.target) != windows_.end())
    183     return;
    184 
    185   // If the active window has moved to this root window then update the
    186   // fullscreen state.
    187   // TODO(flackr): Track the active window leaving this root window and update
    188   // the fullscreen state accordingly.
    189   if (params.new_parent && params.new_parent->GetRootWindow() == root_window_) {
    190     UpdateFullscreenState();
    191     UpdateShelfVisibility();
    192   }
    193 }
    194 
    195 void WorkspaceLayoutManager::OnWindowPropertyChanged(Window* window,
    196                                                      const void* key,
    197                                                      intptr_t old) {
    198   if (key == aura::client::kAlwaysOnTopKey &&
    199       window->GetProperty(aura::client::kAlwaysOnTopKey)) {
    200     GetRootWindowController(window->GetRootWindow())->
    201         always_on_top_controller()->GetContainer(window)->AddChild(window);
    202   }
    203 }
    204 
    205 void WorkspaceLayoutManager::OnWindowStackingChanged(aura::Window* window) {
    206   UpdateShelfVisibility();
    207   UpdateFullscreenState();
    208   if (backdrop_delegate_)
    209     backdrop_delegate_->OnWindowStackingChanged(window);
    210 }
    211 
    212 void WorkspaceLayoutManager::OnWindowDestroying(aura::Window* window) {
    213   if (root_window_ == window) {
    214     root_window_->RemoveObserver(this);
    215     root_window_ = NULL;
    216   }
    217 }
    218 
    219 void WorkspaceLayoutManager::OnWindowBoundsChanged(aura::Window* window,
    220                                               const gfx::Rect& old_bounds,
    221                                               const gfx::Rect& new_bounds) {
    222   if (root_window_ == window) {
    223     const wm::WMEvent wm_event(wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED);
    224     AdjustAllWindowsBoundsForWorkAreaChange(&wm_event);
    225   }
    226 }
    227 
    228 //////////////////////////////////////////////////////////////////////////////
    229 // WorkspaceLayoutManager,
    230 // aura::client::ActivationChangeObserver implementation:
    231 
    232 void WorkspaceLayoutManager::OnWindowActivated(aura::Window* gained_active,
    233                                                aura::Window* lost_active) {
    234   wm::WindowState* window_state = wm::GetWindowState(gained_active);
    235   if (window_state && window_state->IsMinimized() &&
    236       !gained_active->IsVisible()) {
    237     window_state->Unminimize();
    238     DCHECK(!window_state->IsMinimized());
    239   }
    240   UpdateFullscreenState();
    241   UpdateShelfVisibility();
    242 }
    243 
    244 //////////////////////////////////////////////////////////////////////////////
    245 // WorkspaceLayoutManager, wm::WindowStateObserver implementation:
    246 
    247 void WorkspaceLayoutManager::OnPostWindowStateTypeChange(
    248     wm::WindowState* window_state,
    249     wm::WindowStateType old_type) {
    250 
    251   // Notify observers that fullscreen state may be changing.
    252   if (window_state->IsFullscreen() ||
    253       old_type == wm::WINDOW_STATE_TYPE_FULLSCREEN) {
    254     UpdateFullscreenState();
    255   }
    256 
    257   UpdateShelfVisibility();
    258   if (backdrop_delegate_)
    259     backdrop_delegate_->OnPostWindowStateTypeChange(window_state, old_type);
    260 }
    261 
    262 //////////////////////////////////////////////////////////////////////////////
    263 // WorkspaceLayoutManager, private:
    264 
    265 void WorkspaceLayoutManager::AdjustAllWindowsBoundsForWorkAreaChange(
    266     const wm::WMEvent* event) {
    267   DCHECK(event->type() == wm::WM_EVENT_DISPLAY_BOUNDS_CHANGED ||
    268          event->type() == wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED);
    269 
    270   work_area_in_parent_ = ScreenUtil::ConvertRectFromScreen(
    271       window_,
    272       Shell::GetScreen()->GetDisplayNearestWindow(window_).work_area());
    273 
    274   // Don't do any adjustments of the insets while we are in screen locked mode.
    275   // This would happen if the launcher was auto hidden before the login screen
    276   // was shown and then gets shown when the login screen gets presented.
    277   if (event->type() == wm::WM_EVENT_WORKAREA_BOUNDS_CHANGED &&
    278       Shell::GetInstance()->session_state_delegate()->IsScreenLocked())
    279     return;
    280 
    281   // If a user plugs an external display into a laptop running Aura the
    282   // display size will change.  Maximized windows need to resize to match.
    283   // We also do this when developers running Aura on a desktop manually resize
    284   // the host window.
    285   // We also need to do this when the work area insets changes.
    286   for (WindowSet::const_iterator it = windows_.begin();
    287        it != windows_.end();
    288        ++it) {
    289     wm::GetWindowState(*it)->OnWMEvent(event);
    290   }
    291 }
    292 
    293 void WorkspaceLayoutManager::UpdateShelfVisibility() {
    294   if (shelf_)
    295     shelf_->UpdateVisibilityState();
    296 }
    297 
    298 void WorkspaceLayoutManager::UpdateFullscreenState() {
    299   // TODO(flackr): The fullscreen state is currently tracked per workspace
    300   // but the shell notification implies a per root window state. Currently
    301   // only windows in the default workspace container will go fullscreen but
    302   // this should really be tracked by the RootWindowController since
    303   // technically any container could get a fullscreen window.
    304   if (!shelf_)
    305     return;
    306   bool is_fullscreen = GetRootWindowController(
    307       window_->GetRootWindow())->GetWindowForFullscreenMode() != NULL;
    308   if (is_fullscreen != is_fullscreen_) {
    309     ash::Shell::GetInstance()->NotifyFullscreenStateChange(
    310         is_fullscreen, window_->GetRootWindow());
    311     is_fullscreen_ = is_fullscreen;
    312   }
    313 }
    314 
    315 }  // namespace ash
    316