Commit 4b058b24 authored by yusukes@chromium.org's avatar yusukes@chromium.org

While dragging a window, show a semi-transparent aura window instead of the...

While dragging a window, show a semi-transparent aura window instead of the standard gray phantom window.

BUG=136816
TEST=new unit tests passed on try


Review URL: https://chromiumcodereview.appspot.com/10823199

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@151073 0039d316-1c4b-4281-b951-d872f2087c98
parent 924f86d5
......@@ -113,6 +113,7 @@ WindowResizer::Details::Details(aura::Window* window,
: window(window),
initial_bounds(window->bounds()),
initial_location_in_parent(location),
initial_opacity(window->layer()->opacity()),
window_component(window_component),
bounds_change(GetBoundsChangeForWindowComponent(window_component)),
position_change_direction(
......
......@@ -74,6 +74,9 @@ class ASH_EXPORT WindowResizer {
// Location passed to the constructor, in |window->parent()|'s coordinates.
gfx::Point initial_location_in_parent;
// Initial opacity of the window.
float initial_opacity;
// The component the user pressed on.
int window_component;
......
......@@ -8,12 +8,16 @@
#include "ash/shell_window_ids.h"
#include "ash/wm/coordinate_conversion.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "ui/aura/client/screen_position_client.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_observer.h"
#include "ui/base/animation/slide_animation.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/skia_util.h"
#include "ui/views/painter.h"
#include "ui/views/view.h"
......@@ -30,7 +34,7 @@ const int kInsetSize = 4;
// Size of the round rect used by EdgePainter.
const int kRoundRectSize = 4;
// Paints the background of the phantom window.
// Paints the background of the phantom window for window snapping.
class EdgePainter : public views::Painter {
public:
EdgePainter() {}
......@@ -70,18 +74,58 @@ class EdgePainter : public views::Painter {
DISALLOW_COPY_AND_ASSIGN(EdgePainter);
};
// Paints the background of the phantom window for window dragging.
class WindowPainter : public views::Painter,
public aura::WindowObserver {
public:
explicit WindowPainter(aura::Window* window)
: window_(window) {
window_->AddObserver(this);
}
virtual ~WindowPainter() {
if (window_)
window_->RemoveObserver(this);
}
// views::Painter overrides:
virtual void Paint(gfx::Canvas* canvas, const gfx::Size& size) OVERRIDE {
// TODO(yusukes): Paint child windows of the |window_| correctly. Current
// code does not paint e.g. web content area in the window. crbug.com/141766
if (window_ && window_->delegate())
window_->delegate()->OnPaint(canvas);
}
private:
// aura::WindowObserver overrides:
virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE {
DCHECK_EQ(window_, window);
window_ = NULL;
}
aura::Window* window_;
DISALLOW_COPY_AND_ASSIGN(WindowPainter);
};
} // namespace
PhantomWindowController::PhantomWindowController(aura::Window* window)
: window_(window),
phantom_below_window_(NULL),
phantom_widget_(NULL) {
phantom_widget_(NULL),
style_(STYLE_SHADOW) {
}
PhantomWindowController::~PhantomWindowController() {
Hide();
}
void PhantomWindowController::SetDestinationDisplay(
const gfx::Display& dst_display) {
dst_display_ = dst_display;
}
void PhantomWindowController::Show(const gfx::Rect& bounds) {
if (bounds == bounds_)
return;
......@@ -102,7 +146,7 @@ void PhantomWindowController::SetBounds(const gfx::Rect& bounds) {
DCHECK(IsShowing());
animation_.reset();
bounds_ = bounds;
phantom_widget_->SetBounds(bounds_);
SetBoundsInternal(bounds);
}
void PhantomWindowController::Hide() {
......@@ -115,10 +159,27 @@ bool PhantomWindowController::IsShowing() const {
return phantom_widget_ != NULL;
}
void PhantomWindowController::set_style(Style style) {
// Cannot change |style_| after the widget is initialized.
DCHECK(!phantom_widget_);
style_ = style;
}
void PhantomWindowController::SetOpacity(float opacity) {
DCHECK(phantom_widget_);
ui::Layer* layer = phantom_widget_->GetNativeWindow()->layer();
ui::ScopedLayerAnimationSettings scoped_setter(layer->GetAnimator());
layer->SetOpacity(opacity);
}
float PhantomWindowController::GetOpacity() const {
DCHECK(phantom_widget_);
return phantom_widget_->GetNativeWindow()->layer()->opacity();
}
void PhantomWindowController::AnimationProgressed(
const ui::Animation* animation) {
phantom_widget_->SetBounds(
animation->CurrentValueBetween(start_bounds_, bounds_));
SetBoundsInternal(animation->CurrentValueBetween(start_bounds_, bounds_));
}
void PhantomWindowController::CreatePhantomWidget(const gfx::Rect& bounds) {
......@@ -138,10 +199,18 @@ void PhantomWindowController::CreatePhantomWidget(const gfx::Rect& bounds) {
phantom_widget_->SetVisibilityChangedAnimationsEnabled(false);
phantom_widget_->GetNativeWindow()->SetName("PhantomWindow");
views::View* content_view = new views::View;
content_view->set_background(
views::Background::CreateBackgroundPainter(true, new EdgePainter));
switch (style_) {
case STYLE_SHADOW:
content_view->set_background(
views::Background::CreateBackgroundPainter(true, new EdgePainter));
break;
case STYLE_WINDOW:
content_view->set_background(views::Background::CreateBackgroundPainter(
true, new WindowPainter(window_)));
break;
}
phantom_widget_->SetContentsView(content_view);
phantom_widget_->SetBounds(bounds);
SetBoundsInternal(bounds);
if (phantom_below_window_)
phantom_widget_->StackBelow(phantom_below_window_);
else
......@@ -154,5 +223,15 @@ void PhantomWindowController::CreatePhantomWidget(const gfx::Rect& bounds) {
layer->SetOpacity(1);
}
void PhantomWindowController::SetBoundsInternal(const gfx::Rect& bounds) {
aura::Window* window = phantom_widget_->GetNativeWindow();
aura::client::ScreenPositionClient* screen_position_client =
aura::client::GetScreenPositionClient(window->GetRootWindow());
if (screen_position_client && dst_display_.id() != -1)
screen_position_client->SetBounds(window, bounds, dst_display_);
else
phantom_widget_->SetBounds(bounds);
}
} // namespace internal
} // namespace ash
......@@ -9,10 +9,12 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "ui/base/animation/animation_delegate.h"
#include "ui/gfx/display.h"
#include "ui/gfx/rect.h"
namespace aura {
class Window;
class RootWindow;
}
namespace ui {
......@@ -30,9 +32,17 @@ namespace internal {
// of a window. It's used used during dragging a window to show a snap location.
class ASH_EXPORT PhantomWindowController : public ui::AnimationDelegate {
public:
enum Style {
STYLE_SHADOW, // for window snapping.
STYLE_WINDOW, // for window dragging.
};
explicit PhantomWindowController(aura::Window* window);
virtual ~PhantomWindowController();
// Sets the display where the phantom is placed.
void SetDestinationDisplay(const gfx::Display& dst_display);
// Bounds last passed to Show().
const gfx::Rect& bounds() const { return bounds_; }
......@@ -56,6 +66,14 @@ class ASH_EXPORT PhantomWindowController : public ui::AnimationDelegate {
phantom_below_window_ = phantom_below_window;
}
// Sets/gets the style of the phantom window.
void set_style(Style style);
Style style() const { return style_; }
// Sets/gets the opacity of the phantom window.
void SetOpacity(float opacity);
float GetOpacity() const;
// ui::AnimationDelegate overrides:
virtual void AnimationProgressed(const ui::Animation* animation) OVERRIDE;
......@@ -63,9 +81,18 @@ class ASH_EXPORT PhantomWindowController : public ui::AnimationDelegate {
// Creates and shows the |phantom_widget_| at |bounds|.
void CreatePhantomWidget(const gfx::Rect& bounds);
// Sets bounds of the phantom window. The window is shown on |dst_display_|
// if its id() is valid. Otherwise, a display nearest to |bounds| is chosen.
void SetBoundsInternal(const gfx::Rect& bounds);
// Window the phantom is placed beneath.
aura::Window* window_;
// The display where the phantom is placed. When dst_display_.id() is -1 (i.e.
// the default), a display nearest to the current |bounds_| is automatically
// used.
gfx::Display dst_display_;
// If set, the phantom window should get stacked below this window.
aura::Window* phantom_below_window_;
......@@ -81,6 +108,9 @@ class ASH_EXPORT PhantomWindowController : public ui::AnimationDelegate {
// Used to transition the bounds.
scoped_ptr<ui::SlideAnimation> animation_;
// The style of the phantom window.
Style style_;
DISALLOW_COPY_AND_ASSIGN(PhantomWindowController);
};
......
......@@ -34,6 +34,9 @@ namespace {
// Duration of the animation when snapping the window into place.
const int kSnapDurationMS = 100;
// The maximum opacity of the drag phantom window.
const float kMaxOpacity = 0.8f;
// Returns true if should snap to the edge.
bool ShouldSnapToEdge(int distance_from_edge, int grid_size) {
return distance_from_edge <= grid_size / 2 &&
......@@ -121,7 +124,7 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location, int event_flags) {
// Show a phantom window for dragging in another root window.
if (HasSecondaryRootWindow())
UpdateDragPhantomWindow(bounds);
UpdateDragPhantomWindow(bounds, in_original_root);
else
drag_phantom_window_controller_.reset();
......@@ -133,6 +136,7 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location, int event_flags) {
}
void WorkspaceWindowResizer::CompleteDrag(int event_flags) {
window()->layer()->SetOpacity(details_.initial_opacity);
drag_phantom_window_controller_.reset();
snap_phantom_window_controller_.reset();
if (!did_move_or_resize_ || details_.window_component != HTCAPTION)
......@@ -182,6 +186,7 @@ void WorkspaceWindowResizer::CompleteDrag(int event_flags) {
}
void WorkspaceWindowResizer::RevertDrag() {
window()->layer()->SetOpacity(details_.initial_opacity);
drag_phantom_window_controller_.reset();
snap_phantom_window_controller_.reset();
......@@ -439,7 +444,8 @@ int WorkspaceWindowResizer::PrimaryAxisCoordinate(int x, int y) const {
return 0;
}
void WorkspaceWindowResizer::UpdateDragPhantomWindow(const gfx::Rect& bounds) {
void WorkspaceWindowResizer::UpdateDragPhantomWindow(const gfx::Rect& bounds,
bool in_original_root) {
if (!did_move_or_resize_ || details_.window_component != HTCAPTION ||
!ShouldAllowMouseWarp()) {
return;
......@@ -451,18 +457,36 @@ void WorkspaceWindowResizer::UpdateDragPhantomWindow(const gfx::Rect& bounds) {
const gfx::Rect root_bounds_in_screen(another_root->GetBoundsInScreen());
const gfx::Rect bounds_in_screen =
ScreenAsh::ConvertRectToScreen(window()->parent(), bounds);
const gfx::Rect phantom(root_bounds_in_screen.Intersect(bounds_in_screen));
if (!phantom.IsEmpty()) {
const gfx::Rect bounds_in_another_root =
root_bounds_in_screen.Intersect(bounds_in_screen);
const float fraction_in_another_window =
(bounds_in_another_root.width() * bounds_in_another_root.height()) /
static_cast<float>(bounds.width() * bounds.height());
const float phantom_opacity =
!in_original_root ? 1 : (kMaxOpacity * fraction_in_another_window);
const float window_opacity =
in_original_root ? 1 : (kMaxOpacity * (1 - fraction_in_another_window));
if (fraction_in_another_window > 0) {
if (!drag_phantom_window_controller_.get()) {
drag_phantom_window_controller_.reset(
new PhantomWindowController(window()));
drag_phantom_window_controller_->Show(phantom);
drag_phantom_window_controller_->set_style(
PhantomWindowController::STYLE_WINDOW);
// Always show the drag phantom on the |another_root| window.
drag_phantom_window_controller_->SetDestinationDisplay(
gfx::Screen::GetDisplayMatching(another_root->GetBoundsInScreen()));
drag_phantom_window_controller_->Show(bounds_in_screen);
} else {
drag_phantom_window_controller_->SetBounds(phantom); // no animation
// No animation.
drag_phantom_window_controller_->SetBounds(bounds_in_screen);
}
drag_phantom_window_controller_->SetOpacity(phantom_opacity);
window()->layer()->SetOpacity(window_opacity);
} else {
drag_phantom_window_controller_.reset();
window()->layer()->SetOpacity(1.0f);
}
}
......
......@@ -9,6 +9,7 @@
#include "ash/wm/window_resizer.h"
#include "base/compiler_specific.h"
#include "base/gtest_prod_util.h"
#include "base/memory/scoped_ptr.h"
namespace aura {
......@@ -62,6 +63,8 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer {
const std::vector<aura::Window*>& attached_windows);
private:
FRIEND_TEST_ALL_PREFIXES(WorkspaceWindowResizerTest, PhantomStyle);
// Type of snapping.
enum SnapType {
// Snap to the left/right edge of the screen.
......@@ -114,8 +117,9 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer {
const gfx::Rect& bounds,
int grid_size);
// Updates the bounds of the phantom window for window dragging.
void UpdateDragPhantomWindow(const gfx::Rect& bounds);
// Updates the bounds of the phantom window for window dragging. Set true on
// |in_original_root| if the pointer is still in |window()->GetRootWindow()|.
void UpdateDragPhantomWindow(const gfx::Rect& bounds, bool in_original_root);
// Restacks the windows z-order position so that one of the windows is at the
// top of the z-order, and the rest directly underneath it.
......@@ -165,9 +169,7 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer {
// is a grid and the caption is being dragged.
scoped_ptr<PhantomWindowController> snap_phantom_window_controller_;
// For now, we show a phantom window on the other root window during dragging.
// TODO(yusukes): Show a semi-transparent image (screen shot) of the window
// instead.
// Shows a semi-transparent image of the window being dragged.
scoped_ptr<PhantomWindowController> drag_phantom_window_controller_;
// Used to determine the target position of a snap.
......
......@@ -10,6 +10,7 @@
#include "ash/test/ash_test_base.h"
#include "ash/wm/property_util.h"
#include "ash/wm/workspace_controller.h"
#include "ash/wm/workspace/phantom_window_controller.h"
#include "base/string_number_conversions.h"
#include "ui/aura/root_window.h"
#include "ui/aura/test/test_window_delegate.h"
......@@ -62,16 +63,19 @@ class WorkspaceWindowResizerTest : public test::AshTestBase {
EXPECT_EQ(kRootHeight, root_bounds.height());
Shell::GetInstance()->SetDisplayWorkAreaInsets(root, gfx::Insets());
window_.reset(new aura::Window(&delegate_));
window_->SetType(aura::client::WINDOW_TYPE_NORMAL);
window_->Init(ui::LAYER_NOT_DRAWN);
window_->SetParent(default_container);
window_->set_id(1);
window2_.reset(new aura::Window(&delegate2_));
window2_->SetType(aura::client::WINDOW_TYPE_NORMAL);
window2_->Init(ui::LAYER_NOT_DRAWN);
window2_->SetParent(default_container);
window2_->set_id(2);
window3_.reset(new aura::Window(&delegate3_));
window3_->SetType(aura::client::WINDOW_TYPE_NORMAL);
window3_->Init(ui::LAYER_NOT_DRAWN);
window3_->SetParent(default_container);
window3_->set_id(3);
......@@ -133,6 +137,8 @@ class WorkspaceWindowResizerTest : public test::AshTestBase {
DISALLOW_COPY_AND_ASSIGN(WorkspaceWindowResizerTest);
};
} // namespace
// Fails on win_aura since wm::GetRootWindowRelativeToWindow is not implemented
// yet for the platform.
#if defined(OS_WIN)
......@@ -140,10 +146,12 @@ class WorkspaceWindowResizerTest : public test::AshTestBase {
DISABLED_WindowDragWithMultiMonitors
#define MAYBE_WindowDragWithMultiMonitorsRightToLeft \
DISABLED_WindowDragWithMultiMonitorsRightToLeft
#define MAYBE_PhantomStyle DISABLED_PhantomStyle
#else
#define MAYBE_WindowDragWithMultiMonitors WindowDragWithMultiMonitors
#define MAYBE_WindowDragWithMultiMonitorsRightToLeft \
WindowDragWithMultiMonitorsRightToLeft
#define MAYBE_PhantomStyle PhantomStyle
#endif
// Assertions around attached window resize dragging from the right with 2
......@@ -569,6 +577,75 @@ TEST_F(WorkspaceWindowResizerTest,
}
}
// Verifies the style of the drag phantom window is correct.
TEST_F(WorkspaceWindowResizerTest, MAYBE_PhantomStyle) {
UpdateDisplay("800x600,800x600");
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
gfx::Screen::GetPrimaryDisplay());
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
{
SetGridSize(0);
scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create(
window_.get(), gfx::Point(), HTCAPTION, empty_windows()));
ASSERT_TRUE(resizer.get());
EXPECT_FALSE(resizer->snap_phantom_window_controller_.get());
EXPECT_FALSE(resizer->drag_phantom_window_controller_.get());
// The pointer is inside the primary root. Both phantoms should be NULL.
resizer->Drag(CalculateDragPoint(*resizer, 10, 10), 0);
EXPECT_FALSE(resizer->snap_phantom_window_controller_.get());
EXPECT_FALSE(resizer->drag_phantom_window_controller_.get());
// The window spans both root windows.
resizer->Drag(CalculateDragPoint(*resizer, 798, 10), 0);
EXPECT_FALSE(resizer->snap_phantom_window_controller_.get());
PhantomWindowController* controller =
resizer->drag_phantom_window_controller_.get();
ASSERT_TRUE(controller);
EXPECT_EQ(PhantomWindowController::STYLE_WINDOW, controller->style());
// |window_| should be opaque since the pointer is still on the primary
// root window. The phantom should be semi-transparent.
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
EXPECT_GT(1.0f, controller->GetOpacity());
// Enter the pointer to the secondary display.
resizer->Drag(CalculateDragPoint(*resizer, 0, 610), 0);
EXPECT_FALSE(resizer->snap_phantom_window_controller_.get());
controller = resizer->drag_phantom_window_controller_.get();
ASSERT_TRUE(controller);
EXPECT_EQ(PhantomWindowController::STYLE_WINDOW, controller->style());
// |window_| should be transparent, and the phantom should be opaque.
EXPECT_GT(1.0f, window_->layer()->opacity());
EXPECT_FLOAT_EQ(1.0f, controller->GetOpacity());
resizer->CompleteDrag(0);
EXPECT_EQ(root_windows[1], window_->GetRootWindow());
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
}
// Do the same test with RevertDrag().
window_->SetBoundsInScreen(gfx::Rect(0, 0, 50, 60),
gfx::Screen::GetPrimaryDisplay());
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
{
scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create(
window_.get(), gfx::Point(), HTCAPTION, empty_windows()));
ASSERT_TRUE(resizer.get());
EXPECT_FALSE(resizer->snap_phantom_window_controller_.get());
EXPECT_FALSE(resizer->drag_phantom_window_controller_.get());
resizer->Drag(CalculateDragPoint(*resizer, 0, 610), 0);
resizer->RevertDrag();
EXPECT_EQ(root_windows[0], window_->GetRootWindow());
EXPECT_FLOAT_EQ(1.0f, window_->layer()->opacity());
}
}
// Verifies windows are correctly restacked when reordering multiple windows.
TEST_F(WorkspaceWindowResizerTest, RestackAttached) {
window_->SetBounds(gfx::Rect( 0, 0, 200, 300));
......@@ -765,6 +842,5 @@ TEST_F(WorkspaceWindowResizerTest, CtrlCompleteDragMoveToExactPosition) {
EXPECT_EQ("106,124 320x160", window_->bounds().ToString());
}
} // namespace
} // namespace test
} // namespace ash
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment