// 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