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