| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/wm/overview/scoped_window_copy.h" |
| |
| #include "ash/screen_ash.h" |
| #include "ash/shell.h" |
| #include "ui/aura/client/aura_constants.h" |
| #include "ui/aura/client/screen_position_client.h" |
| #include "ui/aura/root_window.h" |
| #include "ui/aura/window.h" |
| #include "ui/compositor/layer_animation_observer.h" |
| #include "ui/gfx/display.h" |
| #include "ui/views/corewm/shadow_types.h" |
| #include "ui/views/corewm/window_util.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Creates a copy of |window| with |recreated_layer| in the |target_root|. |
| views::Widget* CreateCopyOfWindow(aura::Window* target_root, |
| aura::Window* src_window, |
| ui::Layer* recreated_layer) { |
| // Save and remove the transform from the layer to later reapply to both the |
| // source and newly created copy window. |
| gfx::Transform transform = recreated_layer->transform(); |
| recreated_layer->SetTransform(gfx::Transform()); |
| |
| src_window->SetTransform(transform); |
| views::Widget* widget = new views::Widget; |
| views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); |
| params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; |
| params.parent = src_window->parent(); |
| params.can_activate = false; |
| params.keep_on_top = true; |
| widget->set_focus_on_creation(false); |
| widget->Init(params); |
| widget->SetVisibilityChangedAnimationsEnabled(false); |
| std::string name = src_window->name() + " (Copy)"; |
| widget->GetNativeWindow()->SetName(name); |
| views::corewm::SetShadowType(widget->GetNativeWindow(), |
| views::corewm::SHADOW_TYPE_RECTANGULAR); |
| |
| // Set the bounds in the target root window. |
| gfx::Display target_display = |
| Shell::GetScreen()->GetDisplayNearestWindow(target_root); |
| aura::client::ScreenPositionClient* screen_position_client = |
| aura::client::GetScreenPositionClient(src_window->GetRootWindow()); |
| if (screen_position_client && target_display.is_valid()) { |
| screen_position_client->SetBounds(widget->GetNativeWindow(), |
| src_window->GetBoundsInScreen(), target_display); |
| } else { |
| widget->SetBounds(src_window->GetBoundsInScreen()); |
| } |
| widget->StackAbove(src_window); |
| |
| // Move the |recreated_layer| to the newly created window. |
| recreated_layer->set_delegate(src_window->layer()->delegate()); |
| gfx::Rect layer_bounds = recreated_layer->bounds(); |
| layer_bounds.set_origin(gfx::Point(0, 0)); |
| recreated_layer->SetBounds(layer_bounds); |
| recreated_layer->SetVisible(false); |
| recreated_layer->parent()->Remove(recreated_layer); |
| |
| aura::Window* window = widget->GetNativeWindow(); |
| recreated_layer->SetVisible(true); |
| window->layer()->Add(recreated_layer); |
| window->layer()->StackAtTop(recreated_layer); |
| window->layer()->SetOpacity(1); |
| window->SetTransform(transform); |
| window->Show(); |
| return widget; |
| } |
| |
| } // namespace |
| |
| // An observer which closes the widget and deletes the layer after an |
| // animation finishes. |
| class CleanupWidgetAfterAnimationObserver : public ui::LayerAnimationObserver { |
| public: |
| CleanupWidgetAfterAnimationObserver(views::Widget* widget, ui::Layer* layer); |
| |
| // Takes ownership of the widget. At this point the class will delete itself |
| // and clean up the layer when there are no pending animations. |
| void TakeOwnershipOfWidget(); |
| |
| // ui::LayerAnimationObserver: |
| virtual void OnLayerAnimationEnded( |
| ui::LayerAnimationSequence* sequence) OVERRIDE; |
| virtual void OnLayerAnimationAborted( |
| ui::LayerAnimationSequence* sequence) OVERRIDE; |
| virtual void OnLayerAnimationScheduled( |
| ui::LayerAnimationSequence* sequence) OVERRIDE; |
| |
| private: |
| virtual ~CleanupWidgetAfterAnimationObserver(); |
| |
| // If the necessary conditions have been satisfied to destruct this |
| // class, deletes itself and cleans up the widget and layer. |
| void MaybeDestruct(); |
| |
| views::Widget* widget_; |
| ui::Layer* layer_; |
| bool owns_widget_; |
| int pending_animations_; |
| |
| DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver); |
| }; |
| |
| CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver( |
| views::Widget* widget, |
| ui::Layer* layer) |
| : widget_(widget), |
| layer_(layer), |
| owns_widget_(false), |
| pending_animations_(0) { |
| widget_->GetNativeWindow()->layer()->GetAnimator()->AddObserver(this); |
| } |
| |
| void CleanupWidgetAfterAnimationObserver::TakeOwnershipOfWidget() { |
| owns_widget_ = true; |
| MaybeDestruct(); |
| } |
| |
| void CleanupWidgetAfterAnimationObserver::OnLayerAnimationEnded( |
| ui::LayerAnimationSequence* sequence) { |
| pending_animations_--; |
| MaybeDestruct(); |
| } |
| |
| void CleanupWidgetAfterAnimationObserver::OnLayerAnimationAborted( |
| ui::LayerAnimationSequence* sequence) { |
| pending_animations_--; |
| MaybeDestruct(); |
| } |
| |
| void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled( |
| ui::LayerAnimationSequence* sequence) { |
| pending_animations_++; |
| } |
| |
| CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() { |
| widget_->GetNativeWindow()->layer()->GetAnimator()->RemoveObserver(this); |
| widget_->Close(); |
| widget_ = NULL; |
| if (layer_) { |
| views::corewm::DeepDeleteLayers(layer_); |
| layer_ = NULL; |
| } |
| } |
| |
| void CleanupWidgetAfterAnimationObserver::MaybeDestruct() { |
| if (pending_animations_ || !owns_widget_) |
| return; |
| delete this; |
| } |
| |
| ScopedWindowCopy::ScopedWindowCopy(aura::Window* target_root, |
| aura::Window* src_window) { |
| layer_ = views::corewm::RecreateWindowLayers(src_window, true); |
| widget_ = CreateCopyOfWindow(target_root, src_window, layer_); |
| cleanup_observer_ = new CleanupWidgetAfterAnimationObserver(widget_, layer_); |
| } |
| |
| ScopedWindowCopy::~ScopedWindowCopy() { |
| // The cleanup observer will delete itself and the window when any pending |
| // animations have completed. |
| cleanup_observer_->TakeOwnershipOfWidget(); |
| } |
| |
| aura::Window* ScopedWindowCopy::GetWindow() { |
| return widget_->GetNativeWindow(); |
| } |
| |
| } // namespace ash |