summaryrefslogtreecommitdiffstats
path: root/ash
diff options
context:
space:
mode:
authorvarkha@chromium.org <varkha@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-14 21:01:16 +0000
committervarkha@chromium.org <varkha@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-14 21:01:16 +0000
commit2ea4be7edcf6a667463f4114766fbbb55125b081 (patch)
tree78c28f74eb7fccdf27a82c0eed833a43b594726e /ash
parent991caba1671a399d3aaf424c63c2d0653c3ace57 (diff)
downloadchromium_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.cc92
-rw-r--r--ash/wm/workspace/phantom_window_controller.h41
-rw-r--r--ash/wm/workspace/workspace_window_resizer.cc2
-rw-r--r--ash/wm/workspace/workspace_window_resizer.h1
-rw-r--r--ash/wm/workspace/workspace_window_resizer_unittest.cc145
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