Home | History | Annotate | Download | only in overview
      1 // Copyright 2013 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/overview/scoped_transform_overview_window.h"
      6 
      7 #include "ash/screen_util.h"
      8 #include "ash/shell_window_ids.h"
      9 #include "ash/wm/overview/scoped_window_copy.h"
     10 #include "ash/wm/overview/window_selector_item.h"
     11 #include "ash/wm/window_state.h"
     12 #include "ash/wm/window_util.h"
     13 #include "ui/aura/client/aura_constants.h"
     14 #include "ui/aura/client/screen_position_client.h"
     15 #include "ui/aura/window.h"
     16 #include "ui/compositor/scoped_layer_animation_settings.h"
     17 #include "ui/gfx/animation/tween.h"
     18 #include "ui/views/widget/widget.h"
     19 #include "ui/wm/core/window_animations.h"
     20 #include "ui/wm/core/window_util.h"
     21 
     22 namespace ash {
     23 
     24 namespace {
     25 
     26 // The animation settings used for window selector animations.
     27 class WindowSelectorAnimationSettings
     28     : public ui::ScopedLayerAnimationSettings {
     29  public:
     30   WindowSelectorAnimationSettings(aura::Window* window) :
     31       ui::ScopedLayerAnimationSettings(window->layer()->GetAnimator()) {
     32     SetPreemptionStrategy(
     33         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
     34     SetTransitionDuration(base::TimeDelta::FromMilliseconds(
     35         ScopedTransformOverviewWindow::kTransitionMilliseconds));
     36     SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
     37   }
     38 
     39   virtual ~WindowSelectorAnimationSettings() {
     40   }
     41 };
     42 
     43 void SetTransformOnWindow(aura::Window* window,
     44                           const gfx::Transform& transform,
     45                           bool animate) {
     46   if (animate) {
     47     WindowSelectorAnimationSettings animation_settings(window);
     48     window->SetTransform(transform);
     49   } else {
     50     window->SetTransform(transform);
     51   }
     52 }
     53 
     54 gfx::Transform TranslateTransformOrigin(const gfx::Vector2d& new_origin,
     55                                         const gfx::Transform& transform) {
     56   gfx::Transform result;
     57   result.Translate(-new_origin.x(), -new_origin.y());
     58   result.PreconcatTransform(transform);
     59   result.Translate(new_origin.x(), new_origin.y());
     60   return result;
     61 }
     62 
     63 void SetTransformOnWindowAndAllTransientChildren(
     64     aura::Window* window,
     65     const gfx::Transform& transform,
     66     bool animate) {
     67   SetTransformOnWindow(window, transform, animate);
     68 
     69   aura::Window::Windows transient_children =
     70       ::wm::GetTransientChildren(window);
     71   for (aura::Window::Windows::iterator iter = transient_children.begin();
     72        iter != transient_children.end(); ++iter) {
     73     aura::Window* transient_child = *iter;
     74     gfx::Rect window_bounds = window->bounds();
     75     gfx::Rect child_bounds = transient_child->bounds();
     76     gfx::Transform transient_window_transform(
     77         TranslateTransformOrigin(child_bounds.origin() - window_bounds.origin(),
     78                                  transform));
     79     SetTransformOnWindow(transient_child, transient_window_transform, animate);
     80   }
     81 }
     82 
     83 aura::Window* GetModalTransientParent(aura::Window* window) {
     84   if (window->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_WINDOW)
     85     return ::wm::GetTransientParent(window);
     86   return NULL;
     87 }
     88 
     89 }  // namespace
     90 
     91 const int ScopedTransformOverviewWindow::kTransitionMilliseconds = 200;
     92 
     93 ScopedTransformOverviewWindow::ScopedTransformOverviewWindow(
     94         aura::Window* window)
     95     : window_(window),
     96       minimized_(window->GetProperty(aura::client::kShowStateKey) ==
     97                  ui::SHOW_STATE_MINIMIZED),
     98       ignored_by_shelf_(ash::wm::GetWindowState(window)->ignored_by_shelf()),
     99       overview_started_(false),
    100       original_transform_(window->layer()->GetTargetTransform()) {
    101 }
    102 
    103 ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() {
    104   if (window_) {
    105     WindowSelectorAnimationSettings animation_settings(window_);
    106     gfx::Transform transform;
    107     SetTransformOnWindowAndTransientChildren(original_transform_, true);
    108     if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) !=
    109         ui::SHOW_STATE_MINIMIZED) {
    110       // Setting opacity 0 and visible false ensures that the property change
    111       // to SHOW_STATE_MINIMIZED will not animate the window from its original
    112       // bounds to the minimized position.
    113       // Hiding the window needs to be done before the target opacity is 0,
    114       // otherwise the layer's visibility will not be updated
    115       // (See VisibilityController::UpdateLayerVisibility).
    116       window_->Hide();
    117       window_->layer()->SetOpacity(0);
    118       window_->SetProperty(aura::client::kShowStateKey,
    119                            ui::SHOW_STATE_MINIMIZED);
    120     }
    121     ash::wm::GetWindowState(window_)->set_ignored_by_shelf(ignored_by_shelf_);
    122   }
    123 }
    124 
    125 bool ScopedTransformOverviewWindow::Contains(const aura::Window* target) const {
    126   for (ScopedVector<ScopedWindowCopy>::const_iterator iter =
    127       window_copies_.begin(); iter != window_copies_.end(); ++iter) {
    128     if ((*iter)->GetWindow()->Contains(target))
    129       return true;
    130   }
    131   aura::Window* window = window_;
    132   while (window) {
    133     if (window->Contains(target))
    134       return true;
    135     window = GetModalTransientParent(window);
    136   }
    137   return false;
    138 }
    139 
    140 gfx::Rect ScopedTransformOverviewWindow::GetBoundsInScreen() const {
    141   gfx::Rect bounds;
    142   aura::Window* window = window_;
    143   while (window) {
    144     bounds.Union(ScreenUtil::ConvertRectToScreen(window->parent(),
    145                                                 window->GetTargetBounds()));
    146     window = GetModalTransientParent(window);
    147   }
    148   return bounds;
    149 }
    150 
    151 void ScopedTransformOverviewWindow::RestoreWindow() {
    152   if (minimized_ && window_->GetProperty(aura::client::kShowStateKey) ==
    153       ui::SHOW_STATE_MINIMIZED) {
    154     window_->Show();
    155   }
    156 }
    157 
    158 void ScopedTransformOverviewWindow::RestoreWindowOnExit() {
    159   minimized_ = false;
    160   original_transform_ = gfx::Transform();
    161 }
    162 
    163 void ScopedTransformOverviewWindow::OnWindowDestroyed() {
    164   window_ = NULL;
    165 }
    166 
    167 gfx::Rect ScopedTransformOverviewWindow::ShrinkRectToFitPreservingAspectRatio(
    168     const gfx::Rect& rect,
    169     const gfx::Rect& bounds) {
    170   DCHECK(!rect.IsEmpty());
    171   DCHECK(!bounds.IsEmpty());
    172   float scale = std::min(1.0f,
    173       std::min(static_cast<float>(bounds.width()) / rect.width(),
    174                static_cast<float>(bounds.height()) / rect.height()));
    175   return gfx::Rect(bounds.x() + 0.5 * (bounds.width() - scale * rect.width()),
    176                    bounds.y() + 0.5 * (bounds.height() - scale * rect.height()),
    177                    rect.width() * scale,
    178                    rect.height() * scale);
    179 }
    180 
    181 gfx::Transform ScopedTransformOverviewWindow::GetTransformForRect(
    182     const gfx::Rect& src_rect,
    183     const gfx::Rect& dst_rect) {
    184   DCHECK(!src_rect.IsEmpty());
    185   DCHECK(!dst_rect.IsEmpty());
    186   gfx::Transform transform;
    187   transform.Translate(dst_rect.x() - src_rect.x(),
    188                       dst_rect.y() - src_rect.y());
    189   transform.Scale(static_cast<float>(dst_rect.width()) / src_rect.width(),
    190                   static_cast<float>(dst_rect.height()) / src_rect.height());
    191   return transform;
    192 }
    193 
    194 void ScopedTransformOverviewWindow::SetTransform(
    195     aura::Window* root_window,
    196     const gfx::Transform& transform,
    197     bool animate) {
    198   DCHECK(overview_started_);
    199 
    200   if (root_window != window_->GetRootWindow()) {
    201     if (!window_copies_.empty()) {
    202       bool bounds_or_hierarchy_changed = false;
    203       aura::Window* window = window_;
    204       for (ScopedVector<ScopedWindowCopy>::reverse_iterator iter =
    205                window_copies_.rbegin();
    206            !bounds_or_hierarchy_changed && iter != window_copies_.rend();
    207            ++iter, window = GetModalTransientParent(window)) {
    208         if (!window) {
    209           bounds_or_hierarchy_changed = true;
    210         } else if ((*iter)->GetWindow()->GetBoundsInScreen() !=
    211                 window->GetBoundsInScreen()) {
    212           bounds_or_hierarchy_changed = true;
    213         }
    214       }
    215       // Clearing the window copies array will force it to be recreated.
    216       // TODO(flackr): If only the position changed and not the size,
    217       // update the existing window copy's position and continue to use it.
    218       if (bounds_or_hierarchy_changed)
    219         window_copies_.clear();
    220     }
    221     if (window_copies_.empty()) {
    222       // TODO(flackr): Create copies of the transient children windows as well.
    223       // Currently they will only be visible on the window's initial display.
    224       CopyWindowAndTransientParents(root_window, window_);
    225     }
    226   }
    227   SetTransformOnWindowAndTransientChildren(transform, animate);
    228 }
    229 
    230 void ScopedTransformOverviewWindow::CopyWindowAndTransientParents(
    231     aura::Window* target_root,
    232     aura::Window* window) {
    233   aura::Window* modal_parent = GetModalTransientParent(window);
    234   if (modal_parent)
    235     CopyWindowAndTransientParents(target_root, modal_parent);
    236   window_copies_.push_back(new ScopedWindowCopy(target_root, window));
    237 }
    238 
    239 void ScopedTransformOverviewWindow::SetTransformOnWindowAndTransientChildren(
    240     const gfx::Transform& transform,
    241     bool animate) {
    242   gfx::Point origin(GetBoundsInScreen().origin());
    243   aura::Window* window = window_;
    244   while (::wm::GetTransientParent(window))
    245     window = ::wm::GetTransientParent(window);
    246   for (ScopedVector<ScopedWindowCopy>::const_iterator iter =
    247       window_copies_.begin(); iter != window_copies_.end(); ++iter) {
    248     SetTransformOnWindow(
    249         (*iter)->GetWindow(),
    250         TranslateTransformOrigin(ScreenUtil::ConvertRectToScreen(
    251             (*iter)->GetWindow()->parent(),
    252             (*iter)->GetWindow()->GetTargetBounds()).origin() - origin,
    253             transform),
    254         animate);
    255   }
    256   SetTransformOnWindowAndAllTransientChildren(
    257       window,
    258       TranslateTransformOrigin(ScreenUtil::ConvertRectToScreen(
    259           window->parent(), window->GetTargetBounds()).origin() - origin,
    260           transform),
    261       animate);
    262 }
    263 
    264 void ScopedTransformOverviewWindow::PrepareForOverview() {
    265   DCHECK(!overview_started_);
    266   overview_started_ = true;
    267   ash::wm::GetWindowState(window_)->set_ignored_by_shelf(true);
    268   RestoreWindow();
    269 }
    270 
    271 }  // namespace ash
    272