diff options
author | yusukes@chromium.org <yusukes@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-10 17:41:41 +0000 |
---|---|---|
committer | yusukes@chromium.org <yusukes@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-08-10 17:41:41 +0000 |
commit | 4b058b24ae58375119cc10a59c349f3ce19083eb (patch) | |
tree | e896d309b78e23b65840cd363fd8a1689766cbd9 | |
parent | 924f86d56139b76a8c9c0ae58aca54317177917e (diff) | |
download | chromium_src-4b058b24ae58375119cc10a59c349f3ce19083eb.zip chromium_src-4b058b24ae58375119cc10a59c349f3ce19083eb.tar.gz chromium_src-4b058b24ae58375119cc10a59c349f3ce19083eb.tar.bz2 |
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
-rw-r--r-- | ash/wm/window_resizer.cc | 1 | ||||
-rw-r--r-- | ash/wm/window_resizer.h | 3 | ||||
-rw-r--r-- | ash/wm/workspace/phantom_window_controller.cc | 95 | ||||
-rw-r--r-- | ash/wm/workspace/phantom_window_controller.h | 30 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer.cc | 38 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer.h | 12 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer_unittest.cc | 78 |
7 files changed, 236 insertions, 21 deletions
diff --git a/ash/wm/window_resizer.cc b/ash/wm/window_resizer.cc index 9875106..623be33 100644 --- a/ash/wm/window_resizer.cc +++ b/ash/wm/window_resizer.cc @@ -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( diff --git a/ash/wm/window_resizer.h b/ash/wm/window_resizer.h index 80ae948..ab37ce5 100644 --- a/ash/wm/window_resizer.h +++ b/ash/wm/window_resizer.h @@ -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; diff --git a/ash/wm/workspace/phantom_window_controller.cc b/ash/wm/workspace/phantom_window_controller.cc index cebcb2a..5d756cc 100644 --- a/ash/wm/workspace/phantom_window_controller.cc +++ b/ash/wm/workspace/phantom_window_controller.cc @@ -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 diff --git a/ash/wm/workspace/phantom_window_controller.h b/ash/wm/workspace/phantom_window_controller.h index 7ea76c4..44262ea 100644 --- a/ash/wm/workspace/phantom_window_controller.h +++ b/ash/wm/workspace/phantom_window_controller.h @@ -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); }; diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc index 978ca72..a936679 100644 --- a/ash/wm/workspace/workspace_window_resizer.cc +++ b/ash/wm/workspace/workspace_window_resizer.cc @@ -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); } } diff --git a/ash/wm/workspace/workspace_window_resizer.h b/ash/wm/workspace/workspace_window_resizer.h index 51e06ab..9667b7a 100644 --- a/ash/wm/workspace/workspace_window_resizer.h +++ b/ash/wm/workspace/workspace_window_resizer.h @@ -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. diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc index 8291dfc..72eca4b 100644 --- a/ash/wm/workspace/workspace_window_resizer_unittest.cc +++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc @@ -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 |