diff options
author | varkha@chromium.org <varkha@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-14 21:01:16 +0000 |
---|---|---|
committer | varkha@chromium.org <varkha@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-14 21:01:16 +0000 |
commit | 2ea4be7edcf6a667463f4114766fbbb55125b081 (patch) | |
tree | 78c28f74eb7fccdf27a82c0eed833a43b594726e /ash | |
parent | 991caba1671a399d3aaf424c63c2d0653c3ace57 (diff) | |
download | chromium_src-2ea4be7edcf6a667463f4114766fbbb55125b081.zip chromium_src-2ea4be7edcf6a667463f4114766fbbb55125b081.tar.gz chromium_src-2ea4be7edcf6a667463f4114766fbbb55125b081.tar.bz2 |
Animate phantom snap window across displays
BUG=264024
Review URL: https://chromiumcodereview.appspot.com/22429008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@217644 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r-- | ash/wm/workspace/phantom_window_controller.cc | 92 | ||||
-rw-r--r-- | ash/wm/workspace/phantom_window_controller.h | 41 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer.cc | 2 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer.h | 1 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer_unittest.cc | 145 |
5 files changed, 239 insertions, 42 deletions
diff --git a/ash/wm/workspace/phantom_window_controller.cc b/ash/wm/workspace/phantom_window_controller.cc index 4287d5b..67272c1 100644 --- a/ash/wm/workspace/phantom_window_controller.cc +++ b/ash/wm/workspace/phantom_window_controller.cc @@ -8,6 +8,7 @@ #include "ash/shell_window_ids.h" #include "ash/wm/coordinate_conversion.h" #include "third_party/skia/include/core/SkCanvas.h" +#include "ui/aura/root_window.h" #include "ui/aura/window.h" #include "ui/base/animation/slide_animation.h" #include "ui/compositor/layer.h" @@ -91,25 +92,56 @@ void EdgePainter::Paint(gfx::Canvas* canvas, const gfx::Size& size) { PhantomWindowController::PhantomWindowController(aura::Window* window) : window_(window), phantom_below_window_(NULL), - phantom_widget_(NULL) { + phantom_widget_(NULL), + phantom_widget_start_(NULL) { } PhantomWindowController::~PhantomWindowController() { Hide(); } -void PhantomWindowController::Show(const gfx::Rect& bounds) { - if (bounds == bounds_) +void PhantomWindowController::Show(const gfx::Rect& bounds_in_screen) { + if (bounds_in_screen == bounds_in_screen_) return; - bounds_ = bounds; - if (!phantom_widget_) { - // Show the phantom at the bounds of the window. We'll animate to the target - // bounds. + bounds_in_screen_ = bounds_in_screen; + aura::RootWindow* target_root = wm::GetRootWindowMatching(bounds_in_screen); + // Show the phantom at the current bounds of the window. We'll animate to the + // target bounds. If phantom exists, update the start bounds. + if (!phantom_widget_) start_bounds_ = window_->GetBoundsInScreen(); - CreatePhantomWidget(start_bounds_); - } else { + else start_bounds_ = phantom_widget_->GetWindowBoundsInScreen(); + if (phantom_widget_ && + phantom_widget_->GetNativeWindow()->GetRootWindow() != target_root) { + phantom_widget_->Close(); + phantom_widget_ = NULL; + } + if (!phantom_widget_) + phantom_widget_ = CreatePhantomWidget(target_root, start_bounds_); + + // Create a secondary widget in a second screen if start_bounds_ lie at least + // partially in that other screen. This allows animations to start or restart + // in one root window and progress into another root. + aura::RootWindow* start_root = wm::GetRootWindowMatching(start_bounds_); + if (start_root == target_root) { + Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); + for (size_t i = 0; i < root_windows.size(); ++i) { + if (root_windows[i] != target_root && + root_windows[i]->GetBoundsInScreen().Intersects(start_bounds_)) { + start_root = root_windows[i]; + break; + } + } } + if (phantom_widget_start_ && + (phantom_widget_start_->GetNativeWindow()->GetRootWindow() != start_root + || start_root == target_root)) { + phantom_widget_start_->Close(); + phantom_widget_start_ = NULL; + } + if (!phantom_widget_start_ && start_root != target_root) + phantom_widget_start_ = CreatePhantomWidget(start_root, start_bounds_); + animation_.reset(new ui::SlideAnimation(this)); animation_->SetTweenType(ui::Tween::EASE_IN); const int kAnimationDurationMS = 200; @@ -121,6 +153,9 @@ void PhantomWindowController::Hide() { if (phantom_widget_) phantom_widget_->Close(); phantom_widget_ = NULL; + if (phantom_widget_start_) + phantom_widget_start_->Close(); + phantom_widget_start_ = NULL; } bool PhantomWindowController::IsShowing() const { @@ -129,45 +164,50 @@ bool PhantomWindowController::IsShowing() const { void PhantomWindowController::AnimationProgressed( const ui::Animation* animation) { - phantom_widget_->SetBounds( - animation->CurrentValueBetween(start_bounds_, bounds_)); + const gfx::Rect current_bounds = + animation->CurrentValueBetween(start_bounds_, bounds_in_screen_); + if (phantom_widget_start_) + phantom_widget_start_->SetBounds(current_bounds); + phantom_widget_->SetBounds(current_bounds); } -void PhantomWindowController::CreatePhantomWidget(const gfx::Rect& bounds) { - DCHECK(!phantom_widget_); - phantom_widget_ = new views::Widget; +views::Widget* PhantomWindowController::CreatePhantomWidget( + aura::RootWindow* root_window, + const gfx::Rect& bounds_in_screen) { + views::Widget* phantom_widget = new views::Widget; views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW; // PhantomWindowController is used by FrameMaximizeButton to highlight the // launcher button. Put the phantom in the same window as the launcher so that // the phantom is visible. - params.parent = Shell::GetContainer(wm::GetRootWindowMatching(bounds), + params.parent = Shell::GetContainer(root_window, kShellWindowId_ShelfContainer); params.can_activate = false; params.keep_on_top = true; - phantom_widget_->set_focus_on_creation(false); - phantom_widget_->Init(params); - phantom_widget_->SetVisibilityChangedAnimationsEnabled(false); - phantom_widget_->GetNativeWindow()->SetName("PhantomWindow"); - phantom_widget_->GetNativeWindow()->set_id(kShellWindowId_PhantomWindow); + phantom_widget->set_focus_on_creation(false); + phantom_widget->Init(params); + phantom_widget->SetVisibilityChangedAnimationsEnabled(false); + phantom_widget->GetNativeWindow()->SetName("PhantomWindow"); + phantom_widget->GetNativeWindow()->set_id(kShellWindowId_PhantomWindow); views::View* content_view = new views::View; content_view->set_background( views::Background::CreateBackgroundPainter(true, new EdgePainter)); - phantom_widget_->SetContentsView(content_view); - phantom_widget_->SetBounds(bounds); + phantom_widget->SetContentsView(content_view); + phantom_widget->SetBounds(bounds_in_screen); if (phantom_below_window_) - phantom_widget_->StackBelow(phantom_below_window_); + phantom_widget->StackBelow(phantom_below_window_); else - phantom_widget_->StackAbove(window_); + phantom_widget->StackAbove(window_); // Show the widget after all the setups. - phantom_widget_->Show(); + phantom_widget->Show(); // Fade the window in. - ui::Layer* widget_layer = phantom_widget_->GetNativeWindow()->layer(); + ui::Layer* widget_layer = phantom_widget->GetNativeWindow()->layer(); widget_layer->SetOpacity(0); ui::ScopedLayerAnimationSettings scoped_setter(widget_layer->GetAnimator()); widget_layer->SetOpacity(1); + return phantom_widget; } } // namespace internal diff --git a/ash/wm/workspace/phantom_window_controller.h b/ash/wm/workspace/phantom_window_controller.h index d7622fc..5cd40da 100644 --- a/ash/wm/workspace/phantom_window_controller.h +++ b/ash/wm/workspace/phantom_window_controller.h @@ -13,6 +13,7 @@ #include "ui/gfx/rect.h" namespace aura { +class RootWindow; class Window; } @@ -35,13 +36,14 @@ class ASH_EXPORT PhantomWindowController : public ui::AnimationDelegate { virtual ~PhantomWindowController(); // Bounds last passed to Show(). - const gfx::Rect& bounds() const { return bounds_; } + const gfx::Rect& bounds_in_screen() const { return bounds_in_screen_; } - // Shows the phantom window at the specified location (coordinates of the - // parent). If |layer| is non-NULL, it is shown on top of the phantom window. - // |layer| is owned by the caller. + // Animates the phantom window towards |bounds_in_screen|. + // Creates two (if start bounds intersect any root window other than the + // root window that matches the target bounds) or one (otherwise) phantom + // widgets to display animated rectangle in each root. // This does not immediately show the window. - void Show(const gfx::Rect& bounds); + void Show(const gfx::Rect& bounds_in_screen); // Hides the phantom. void Hide(); @@ -59,12 +61,12 @@ class ASH_EXPORT PhantomWindowController : public ui::AnimationDelegate { virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE; private: - FRIEND_TEST_ALL_PREFIXES(WorkspaceWindowResizerTest, PhantomStyle); + FRIEND_TEST_ALL_PREFIXES(WorkspaceWindowResizerTest, PhantomWindowShow); - // Creates and shows the |phantom_widget_| at |bounds|. - // |layer| is shown on top of the phantom window if it is non-NULL. - // |layer| is not owned by this object. - void CreatePhantomWidget(const gfx::Rect& bounds); + // Creates, shows and returns a phantom widget at |bounds| + // with kShellWindowId_ShelfContainer in |root_window| as a parent. + views::Widget* CreatePhantomWidget(aura::RootWindow* root_window, + const gfx::Rect& bounds_in_screen); // Window the phantom is placed beneath. aura::Window* window_; @@ -72,15 +74,24 @@ class ASH_EXPORT PhantomWindowController : public ui::AnimationDelegate { // If set, the phantom window should get stacked below this window. aura::Window* phantom_below_window_; - // Initially the bounds of |window_|. Each time Show() is invoked - // |start_bounds_| is then reset to the bounds of |phantom_widget_| and - // |bounds_| is set to the value passed into Show(). The animation animates - // between these two values. + // Initially the bounds of |window_| (in screen coordinates). + // Each time Show() is invoked |start_bounds_| is then reset to the bounds of + // |phantom_widget_| and |bounds_| is set to the value passed into Show(). + // The animation animates between these two values. gfx::Rect start_bounds_; - gfx::Rect bounds_; + // Target bounds of the animation in screen coordinates. + gfx::Rect bounds_in_screen_; + + // The primary phantom representation of the window. It is parented by the + // root window matching the target bounds. views::Widget* phantom_widget_; + // If the animation starts on another display, this is the secondary phantom + // representation of the window used on the initial display, otherwise this is + // NULL. This allows animation to progress from one display into the other. + views::Widget* phantom_widget_start_; + // Used to transition the bounds. scoped_ptr<ui::SlideAnimation> animation_; diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc index d1f8492..4b8c0aa 100644 --- a/ash/wm/workspace/workspace_window_resizer.cc +++ b/ash/wm/workspace/workspace_window_resizer.cc @@ -509,7 +509,7 @@ gfx::Rect WorkspaceWindowResizer::GetFinalBounds( const gfx::Rect& bounds) const { if (snap_phantom_window_controller_.get() && snap_phantom_window_controller_->IsShowing()) { - return snap_phantom_window_controller_->bounds(); + return snap_phantom_window_controller_->bounds_in_screen(); } return bounds; } diff --git a/ash/wm/workspace/workspace_window_resizer.h b/ash/wm/workspace/workspace_window_resizer.h index 5e99081..76176b1 100644 --- a/ash/wm/workspace/workspace_window_resizer.h +++ b/ash/wm/workspace/workspace_window_resizer.h @@ -69,6 +69,7 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer { private: FRIEND_TEST_ALL_PREFIXES(WorkspaceWindowResizerTest, CancelSnapPhantom); FRIEND_TEST_ALL_PREFIXES(WorkspaceWindowResizerTest, PhantomSnapMaxSize); + FRIEND_TEST_ALL_PREFIXES(WorkspaceWindowResizerTest, PhantomWindowShow); // Type of snapping. enum SnapType { diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc index b1fd004..8459c54 100644 --- a/ash/wm/workspace/workspace_window_resizer_unittest.cc +++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc @@ -30,6 +30,36 @@ #include "ui/gfx/screen.h" #include "ui/views/widget/widget.h" +namespace ui { + +// Class to provide access to SlideAnimation internals for testing. +class SlideAnimation::TestApi { + public: + explicit TestApi(SlideAnimation* animation) : animation_(animation) {} + + void SetStartTime(base::TimeTicks ticks) { + animation_->SetStartTime(ticks); + } + + void Step(base::TimeTicks ticks) { + animation_->Step(ticks); + } + + void RunTillComplete() { + SetStartTime(base::TimeTicks()); + Step(base::TimeTicks() + + base::TimeDelta::FromMilliseconds(animation_->GetSlideDuration())); + EXPECT_EQ(1.0, animation_->GetCurrentValue()); + } + + private: + SlideAnimation* animation_; + + DISALLOW_COPY_AND_ASSIGN(TestApi); +}; + +} + namespace ash { namespace internal { namespace { @@ -170,6 +200,12 @@ class WorkspaceWindowResizerTest : public test::AshTestBase { touch_resize_window_->set_hit_test_bounds_override_inner(mouse_insets); } + // Simulate running the animation. + void RunAnimationTillComplete(ui::SlideAnimation* animation) { + ui::SlideAnimation::TestApi test_api(animation); + test_api.RunTillComplete(); + } + TestWindowDelegate delegate_; TestWindowDelegate delegate2_; TestWindowDelegate delegate3_; @@ -1829,5 +1865,114 @@ TEST_F(WorkspaceWindowResizerTest, TouchResizeToEdge_BOTTOM) { EXPECT_EQ(gfx::Rect(100, 100, 600, kRootHeight - 100).ToString(), touch_resize_window_->bounds().ToString()); } + +TEST_F(WorkspaceWindowResizerTest, PhantomWindowShow) { + if (!SupportsMultipleDisplays()) + return; + + UpdateDisplay("500x400,500x400"); + window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60), + Shell::GetScreen()->GetPrimaryDisplay()); + Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); + EXPECT_EQ(root_windows[0], window_->GetRootWindow()); + + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTCAPTION, + aura::client::WINDOW_MOVE_SOURCE_MOUSE, empty_windows())); + ASSERT_TRUE(resizer.get()); + EXPECT_FALSE(resizer->snap_phantom_window_controller_.get()); + + // The pointer is on the edge but not shared. The snap phantom window + // controller should be non-NULL. + resizer->Drag(CalculateDragPoint(*resizer, 499, 0), 0); + EXPECT_TRUE(resizer->snap_phantom_window_controller_.get()); + PhantomWindowController* phantom_controller( + resizer->snap_phantom_window_controller_.get()); + + // phantom widget only in the left screen. + phantom_controller->Show(gfx::Rect(100, 100, 50, 60)); + EXPECT_TRUE(phantom_controller->phantom_widget_); + EXPECT_FALSE(phantom_controller->phantom_widget_start_); + EXPECT_EQ( + root_windows[0], + phantom_controller->phantom_widget_->GetNativeWindow()->GetRootWindow()); + + // Move phantom widget into the right screen. Test that 2 widgets got created. + phantom_controller->Show(gfx::Rect(600, 100, 50, 60)); + EXPECT_TRUE(phantom_controller->phantom_widget_); + EXPECT_TRUE(phantom_controller->phantom_widget_start_); + EXPECT_EQ( + root_windows[1], + phantom_controller->phantom_widget_->GetNativeWindow()->GetRootWindow()); + EXPECT_EQ( + root_windows[0], + phantom_controller->phantom_widget_start_->GetNativeWindow()-> + GetRootWindow()); + RunAnimationTillComplete(phantom_controller->animation_.get()); + + // Move phantom widget only in the right screen. Start widget should close. + phantom_controller->Show(gfx::Rect(700, 100, 50, 60)); + EXPECT_TRUE(phantom_controller->phantom_widget_); + EXPECT_FALSE(phantom_controller->phantom_widget_start_); + EXPECT_EQ( + root_windows[1], + phantom_controller->phantom_widget_->GetNativeWindow()->GetRootWindow()); + RunAnimationTillComplete(phantom_controller->animation_.get()); + + // Move phantom widget into the left screen. Start widget should open. + phantom_controller->Show(gfx::Rect(100, 100, 50, 60)); + EXPECT_TRUE(phantom_controller->phantom_widget_); + EXPECT_TRUE(phantom_controller->phantom_widget_start_); + EXPECT_EQ( + root_windows[0], + phantom_controller->phantom_widget_->GetNativeWindow()->GetRootWindow()); + EXPECT_EQ( + root_windows[1], + phantom_controller->phantom_widget_start_->GetNativeWindow()-> + GetRootWindow()); + RunAnimationTillComplete(phantom_controller->animation_.get()); + + // Move phantom widget while in the left screen. Start widget should close. + phantom_controller->Show(gfx::Rect(200, 100, 50, 60)); + EXPECT_TRUE(phantom_controller->phantom_widget_); + EXPECT_FALSE(phantom_controller->phantom_widget_start_); + EXPECT_EQ( + root_windows[0], + phantom_controller->phantom_widget_->GetNativeWindow()->GetRootWindow()); + RunAnimationTillComplete(phantom_controller->animation_.get()); + + // Move phantom widget spanning both screens with most of the window in the + // right screen. Two widgets are created. + phantom_controller->Show(gfx::Rect(495, 100, 50, 60)); + EXPECT_TRUE(phantom_controller->phantom_widget_); + EXPECT_TRUE(phantom_controller->phantom_widget_start_); + EXPECT_EQ( + root_windows[1], + phantom_controller->phantom_widget_->GetNativeWindow()->GetRootWindow()); + EXPECT_EQ( + root_windows[0], + phantom_controller->phantom_widget_start_->GetNativeWindow()-> + GetRootWindow()); + RunAnimationTillComplete(phantom_controller->animation_.get()); + + // Move phantom widget back into the left screen. Phantom widgets should swap. + phantom_controller->Show(gfx::Rect(200, 100, 50, 60)); + EXPECT_TRUE(phantom_controller->phantom_widget_); + EXPECT_TRUE(phantom_controller->phantom_widget_start_); + EXPECT_EQ( + root_windows[0], + phantom_controller->phantom_widget_->GetNativeWindow()->GetRootWindow()); + EXPECT_EQ( + root_windows[1], + phantom_controller->phantom_widget_start_->GetNativeWindow()-> + GetRootWindow()); + RunAnimationTillComplete(phantom_controller->animation_.get()); + + // Hide phantom controller. Both widgets should close. + phantom_controller->Hide(); + EXPECT_FALSE(phantom_controller->phantom_widget_); + EXPECT_FALSE(phantom_controller->phantom_widget_start_); +} + } // namespace internal } // namespace ash |