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/auto_window_management.h"
      6 
      7 #include "ash/ash_switches.h"
      8 #include "ash/shell.h"
      9 #include "ash/wm/property_util.h"
     10 #include "ash/wm/window_animations.h"
     11 #include "ash/wm/window_util.h"
     12 #include "base/command_line.h"
     13 #include "ui/aura/window.h"
     14 #include "ui/aura/client/aura_constants.h"
     15 #include "ui/compositor/layer_animator.h"
     16 #include "ui/compositor/scoped_layer_animation_settings.h"
     17 #include "ui/gfx/screen.h"
     18 
     19 namespace ash {
     20 namespace internal {
     21 
     22 namespace {
     23 
     24 // The time in milliseconds which should be used to visually move a window
     25 // through an automatic "intelligent" window management option.
     26 const int kWindowAutoMoveDurationMS = 125;
     27 
     28 // Check if any management should be performed (with a given |window|).
     29 bool UseAutoWindowMagerForWindow(const aura::Window* window) {
     30   return !CommandLine::ForCurrentProcess()->HasSwitch(
     31              switches::kAshDisableAutoWindowPlacement) &&
     32          GetTrackedByWorkspace(window) &&
     33          wm::IsWindowPositionManaged(window);
     34 }
     35 
     36 // Check if a given |window| can be managed. This includes that it's state is
     37 // not minimized/maximized/the user has changed it's size by hand already.
     38 // It furthermore checks for the WindowIsManaged status.
     39 bool WindowPositionCanBeManaged(const aura::Window* window) {
     40   return (wm::IsWindowPositionManaged(window) &&
     41           !wm::IsWindowMinimized(window) &&
     42           !wm::IsWindowMaximized(window) &&
     43           !wm::HasUserChangedWindowPositionOrSize(window));
     44 }
     45 
     46 // Given a |window|, return the only |other_window| which has an impact on
     47 // the automated windows location management. If there is more then one window,
     48 // false is returned, but the |other_window| will be set to the first one
     49 // found.
     50 // If the return value is true a single window was found.
     51 bool GetOtherVisibleAndManageableWindow(const aura::Window* window,
     52                                         aura::Window** other_window) {
     53   *other_window = NULL;
     54   const aura::Window::Windows& windows = window->parent()->children();
     55   // Find a single open managed window.
     56   for (size_t i = 0; i < windows.size(); i++) {
     57     aura::Window* iterated_window = windows[i];
     58     if (window != iterated_window &&
     59         iterated_window->type() == aura::client::WINDOW_TYPE_NORMAL &&
     60         iterated_window->TargetVisibility() &&
     61         wm::IsWindowPositionManaged(iterated_window)) {
     62       // Bail if we find a second usable window.
     63       if (*other_window)
     64         return false;
     65       *other_window = iterated_window;
     66     }
     67   }
     68   return *other_window != NULL;
     69 }
     70 
     71 // Get the work area for a given |window|.
     72 gfx::Rect GetWorkAreaForWindow(aura::Window* window) {
     73 #if defined(OS_WIN)
     74   // On Win 8, the host window can't be resized, so
     75   // use window's bounds instead.
     76   // TODO(oshima): Emulate host window resize on win8.
     77   gfx::Rect work_area = gfx::Rect(window->parent()->bounds().size());
     78   work_area.Inset(Shell::GetScreen()->GetDisplayMatching(
     79       work_area).GetWorkAreaInsets());
     80   return work_area;
     81 #else
     82   return Shell::GetScreen()->GetDisplayNearestWindow(window).work_area();
     83 #endif
     84 }
     85 
     86 // Move the given |bounds| on the available |parent_width| to the
     87 // direction. If |move_right| is true, the rectangle gets moved to the right
     88 // corner, otherwise to the left one.
     89 bool MoveRectToOneSide(int parent_width, bool move_right, gfx::Rect* bounds) {
     90   if (move_right) {
     91     if (parent_width > bounds->right()) {
     92       bounds->set_x(parent_width - bounds->width());
     93       return true;
     94     }
     95   } else {
     96     if (0 < bounds->x()) {
     97       bounds->set_x(0);
     98       return true;
     99     }
    100   }
    101   return false;
    102 }
    103 
    104 // Move a |window| to a new |bound|. Animate if desired by user.
    105 // Note: The function will do nothing if the bounds did not change.
    106 void SetBoundsAnimated(aura::Window* window, const gfx::Rect& bounds) {
    107   if (bounds == window->GetTargetBounds())
    108     return;
    109 
    110   if (views::corewm::WindowAnimationsDisabled(window)) {
    111     window->SetBounds(bounds);
    112     return;
    113   }
    114 
    115   ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator());
    116   settings.SetTransitionDuration(
    117       base::TimeDelta::FromMilliseconds(kWindowAutoMoveDurationMS));
    118   window->SetBounds(bounds);
    119 }
    120 
    121 // Move |window| into the center of the screen - or restore it to the previous
    122 // position.
    123 void AutoPlaceSingleWindow(aura::Window* window, bool animated) {
    124   gfx::Rect work_area = GetWorkAreaForWindow(window);
    125   gfx::Rect bounds = window->bounds();
    126   const gfx::Rect* user_defined_area =
    127       ash::wm::GetPreAutoManageWindowBounds(window);
    128   if (user_defined_area) {
    129     bounds = *user_defined_area;
    130     ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(work_area, &bounds);
    131   } else {
    132     // Center the window (only in x).
    133     bounds.set_x((work_area.width() - bounds.width()) / 2);
    134   }
    135 
    136   if (animated)
    137     SetBoundsAnimated(window, bounds);
    138   else
    139     window->SetBounds(bounds);
    140 }
    141 
    142 } // namespace
    143 
    144 void RearrangeVisibleWindowOnHideOrRemove(const aura::Window* removed_window) {
    145   if (!UseAutoWindowMagerForWindow(removed_window))
    146     return;
    147   // Find a single open browser window.
    148   aura::Window* other_shown_window = NULL;
    149   if (!GetOtherVisibleAndManageableWindow(removed_window,
    150                                           &other_shown_window) ||
    151       !WindowPositionCanBeManaged(other_shown_window))
    152     return;
    153   AutoPlaceSingleWindow(other_shown_window, true);
    154 }
    155 
    156 void RearrangeVisibleWindowOnShow(aura::Window* added_window) {
    157   if (!UseAutoWindowMagerForWindow(added_window) ||
    158       wm::HasUserChangedWindowPositionOrSize(added_window) ||
    159       !added_window->TargetVisibility())
    160     return;
    161   // Find a single open managed window.
    162   aura::Window* other_shown_window = NULL;
    163   if (!GetOtherVisibleAndManageableWindow(added_window,
    164                                           &other_shown_window)) {
    165     // It could be that this window is the first window joining the workspace.
    166     if (!WindowPositionCanBeManaged(added_window) || other_shown_window)
    167       return;
    168     // Since we might be going from 0 to 1 window, we have to arrange the new
    169     // window to a good default.
    170     AutoPlaceSingleWindow(added_window, false);
    171     return;
    172   }
    173 
    174   // When going from one to two windows both windows loose their "positioned
    175   // by user" flags.
    176   ash::wm::SetUserHasChangedWindowPositionOrSize(added_window, false);
    177   ash::wm::SetUserHasChangedWindowPositionOrSize(other_shown_window, false);
    178 
    179   if (WindowPositionCanBeManaged(other_shown_window)) {
    180     gfx::Rect work_area = GetWorkAreaForWindow(added_window);
    181 
    182     // Push away the other window after remembering its current position.
    183     gfx::Rect other_bounds = other_shown_window->bounds();
    184     ash::wm::SetPreAutoManageWindowBounds(other_shown_window, other_bounds);
    185 
    186     bool move_right = other_bounds.CenterPoint().x() > work_area.width() / 2;
    187     if (MoveRectToOneSide(work_area.width(), move_right, &other_bounds))
    188       SetBoundsAnimated(other_shown_window, other_bounds);
    189 
    190     // Remember the current location of the new window and push it also to the
    191     // opposite location (if needed).
    192     // Since it is just coming into view, we do not need to animate it.
    193     gfx::Rect added_bounds = added_window->bounds();
    194     ash::wm::SetPreAutoManageWindowBounds(added_window, added_bounds);
    195     if (MoveRectToOneSide(work_area.width(), !move_right, &added_bounds))
    196       added_window->SetBounds(added_bounds);
    197   }
    198 }
    199 
    200 } // namespace internal
    201 } // namespace ash
    202