diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-13 03:33:17 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-03-13 03:33:17 +0000 |
commit | 3c3455a7310fa80397d722c915a5baa6cd29e3a1 (patch) | |
tree | 2a1409dfd35fd3a86237de060774c462161dadda /ash/wm/workspace | |
parent | cee58a36a2263b9404ac822bf6e41b70c34be90e (diff) | |
download | chromium_src-3c3455a7310fa80397d722c915a5baa6cd29e3a1.zip chromium_src-3c3455a7310fa80397d722c915a5baa6cd29e3a1.tar.gz chromium_src-3c3455a7310fa80397d722c915a5baa6cd29e3a1.tar.bz2 |
Makes dragging to the left/right edge resize the window to half the
screen size.
TEST=none
BUG=116213
R=ben@chromium.org
Review URL: https://chromiumcodereview.appspot.com/9691012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@126321 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash/wm/workspace')
-rw-r--r-- | ash/wm/workspace/phantom_window_controller.cc | 85 | ||||
-rw-r--r-- | ash/wm/workspace/phantom_window_controller.h | 22 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer.cc | 137 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer.h | 48 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer_unittest.cc | 29 |
5 files changed, 289 insertions, 32 deletions
diff --git a/ash/wm/workspace/phantom_window_controller.cc b/ash/wm/workspace/phantom_window_controller.cc index a54e889..11e2568 100644 --- a/ash/wm/workspace/phantom_window_controller.cc +++ b/ash/wm/workspace/phantom_window_controller.cc @@ -21,12 +21,10 @@ namespace internal { namespace { -const int kRoundRectSize = 3; - -// Paints the background of the phantom window. -class BackgroundPainter : public views::Painter { +// Paints the background of the phantom window for TYPE_DESTINATION. +class DestinationPainter : public views::Painter { public: - BackgroundPainter() {} + DestinationPainter() {} // views::Painter overrides: virtual void Paint(gfx::Canvas* canvas, const gfx::Size& size) OVERRIDE { @@ -34,7 +32,53 @@ class BackgroundPainter : public views::Painter { } private: - DISALLOW_COPY_AND_ASSIGN(BackgroundPainter); + DISALLOW_COPY_AND_ASSIGN(DestinationPainter); +}; + +// Amount to inset from the bounds for EdgePainter. +const int kInsetSize = 4; + +// Size of the round rect used by EdgePainter. +const int kRoundRectSize = 4; + +// Paints the background of the phantom window for TYPE_EDGE. +class EdgePainter : public views::Painter { + public: + EdgePainter() {} + + // views::Painter overrides: + virtual void Paint(gfx::Canvas* canvas, const gfx::Size& size) OVERRIDE { + int x = kInsetSize; + int y = kInsetSize; + int w = size.width() - kInsetSize * 2; + int h = size.height() - kInsetSize * 2; + bool inset = (w > 0 && h > 0); + if (w < 0 || h < 0) { + x = 0; + y = 0; + w = size.width(); + h = size.height(); + } + SkPaint paint; + paint.setColor(SkColorSetARGB(100, 0, 0, 0)); + paint.setStyle(SkPaint::kFill_Style); + paint.setAntiAlias(true); + canvas->sk_canvas()->drawRoundRect( + gfx::RectToSkRect(gfx::Rect(x, y, w, h)), + SkIntToScalar(kRoundRectSize), SkIntToScalar(kRoundRectSize), paint); + if (!inset) + return; + + paint.setColor(SkColorSetARGB(200, 255, 255, 255)); + paint.setStyle(SkPaint::kStroke_Style); + paint.setStrokeWidth(SkIntToScalar(2)); + canvas->sk_canvas()->drawRoundRect( + gfx::RectToSkRect(gfx::Rect(x, y, w, h)), SkIntToScalar(kRoundRectSize), + SkIntToScalar(kRoundRectSize), paint); + } + + private: + DISALLOW_COPY_AND_ASSIGN(EdgePainter); }; // Used to delete the widget after a delay, or if the window is deleted. @@ -79,10 +123,11 @@ class DelayedWidgetDeleter : public aura::WindowObserver { } // namespace PhantomWindowController::PhantomWindowController(aura::Window* window, + Type type, int delay_ms) : window_(window), - delay_ms_(delay_ms), - phantom_widget_(NULL) { + type_(type), + delay_ms_(delay_ms) { } PhantomWindowController::~PhantomWindowController() { @@ -92,9 +137,14 @@ PhantomWindowController::~PhantomWindowController() { void PhantomWindowController::Show(const gfx::Rect& bounds) { if (bounds == bounds_) return; - if (phantom_widget_.get()) - phantom_widget_->Hide(); bounds_ = bounds; + if (phantom_widget_.get()) { + if (type_ == TYPE_EDGE) { + phantom_widget_->SetBounds(bounds); + return; + } + phantom_widget_->Hide(); + } show_timer_.Stop(); show_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(delay_ms_), this, &PhantomWindowController::ShowNow); @@ -105,6 +155,10 @@ void PhantomWindowController::Hide() { show_timer_.Stop(); } +bool PhantomWindowController::IsShowing() const { + return phantom_widget_.get() != NULL; +} + void PhantomWindowController::DelayedClose(int delay_ms) { show_timer_.Stop(); if (!phantom_widget_.get() || !phantom_widget_->IsVisible()) @@ -129,12 +183,17 @@ void PhantomWindowController::ShowNow() { phantom_widget_->SetVisibilityChangedAnimationsEnabled(false); phantom_widget_->GetNativeWindow()->SetName("PhantomWindow"); views::View* content_view = new views::View; + views::Painter* painter = type_ == TYPE_DESTINATION ? + static_cast<views::Painter*>(new DestinationPainter) : + static_cast<views::Painter*>(new EdgePainter); content_view->set_background( - views::Background::CreateBackgroundPainter(true, - new BackgroundPainter)); + views::Background::CreateBackgroundPainter(true, painter)); phantom_widget_->SetContentsView(content_view); phantom_widget_->SetBounds(bounds_); - phantom_widget_->StackBelow(window_); + if (type_ == TYPE_DESTINATION) + phantom_widget_->StackBelow(window_); + else + phantom_widget_->StackAbove(window_); phantom_widget_->Show(); return; } diff --git a/ash/wm/workspace/phantom_window_controller.h b/ash/wm/workspace/phantom_window_controller.h index c4e631b..f3981c5 100644 --- a/ash/wm/workspace/phantom_window_controller.h +++ b/ash/wm/workspace/phantom_window_controller.h @@ -24,13 +24,26 @@ namespace internal { // PhantomWindowController is responsible for showing a phantom beneath an // existing window. PhantomWindowController is used during dragging a window to -// give an indication where on a grid the window will land. +// give an indication of where the window will land. class ASH_EXPORT PhantomWindowController { public: + enum Type { + // Used for showing an indication of where on the grid the window will land. + TYPE_DESTINATION, + + // Used when the window is placed along the edge of the screen. + TYPE_EDGE, + }; + // |delay_ms| specifies the delay before the phantom is shown. - PhantomWindowController(aura::Window* window, int delay_ms); + PhantomWindowController(aura::Window* window, Type type, int delay_ms); ~PhantomWindowController(); + Type type() const { return type_; } + + // Bounds last passed to Show(). + const gfx::Rect& bounds() const { return bounds_; } + // Shows the phantom window at the specified location (coordinates of the // parent). This does not immediately show the window. void Show(const gfx::Rect& bounds); @@ -38,6 +51,9 @@ class ASH_EXPORT PhantomWindowController { // Hides the phantom. void Hide(); + // Returns true if the phantom is showing. + bool IsShowing() const; + // Closes the phantom window after a delay (in milliseconds). void DelayedClose(int delay_ms); @@ -48,6 +64,8 @@ class ASH_EXPORT PhantomWindowController { // Window the phantom is placed beneath. aura::Window* window_; + const Type type_; + // Delay before closing. const int delay_ms_; diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc index 9ceff64..092646e 100644 --- a/ash/wm/workspace/workspace_window_resizer.cc +++ b/ash/wm/workspace/workspace_window_resizer.cc @@ -5,6 +5,7 @@ #include "ash/wm/workspace/workspace_window_resizer.h" #include "ash/shell.h" +#include "ash/wm/property_util.h" #include "ash/wm/root_window_event_filter.h" #include "ash/wm/window_util.h" #include "ash/wm/workspace/phantom_window_controller.h" @@ -28,7 +29,7 @@ namespace { const int kSnapDurationMS = 100; // Delay before the phantom window is shown (in milliseconds). -const int kPhantomDelayMS = 200; +const int kPhantomDelayMS = 400; const aura::WindowProperty<int> kHeightBeforeObscuredProp = {0}; const aura::WindowProperty<int>* const kHeightBeforeObscuredKey = @@ -64,6 +65,12 @@ void ClearWidthBeforeObscured(aura::Window* window) { } // namespace +WorkspaceWindowResizer::PhantomPlacement::PhantomPlacement() : type(TYPE_NONE) { +} + +WorkspaceWindowResizer::PhantomPlacement::~PhantomPlacement() { +} + // static const int WorkspaceWindowResizer::kMinOnscreenSize = 20; @@ -73,6 +80,27 @@ WorkspaceWindowResizer::~WorkspaceWindowResizer() { } // static +gfx::Rect WorkspaceWindowResizer::GetBoundsForWindowAlongEdge( + aura::Window* window, + WorkspaceWindowResizer::EdgeType edge, + int grid_size) { + gfx::Rect work_area(gfx::Screen::GetMonitorWorkAreaNearestWindow(window)); + int y = WindowResizer::AlignToGridRoundUp(work_area.y(), grid_size); + int max_y = + WindowResizer::AlignToGridRoundDown(work_area.bottom(), grid_size); + if (edge == LEFT_EDGE) { + int x = WindowResizer::AlignToGridRoundUp(work_area.x(), grid_size); + int mid_x = WindowResizer::AlignToGridRoundUp( + work_area.x() + work_area.width() / 2, grid_size); + return gfx::Rect(x, y, mid_x - x, max_y - y); + } + int x = WindowResizer::AlignToGridRoundUp( + work_area.x() + work_area.width() / 2, grid_size); + int max_x = WindowResizer::AlignToGridRoundDown(work_area.right(), grid_size); + return gfx::Rect(x , y, max_x - x, max_y - y); +} + +// static WorkspaceWindowResizer* WorkspaceWindowResizer::Create( aura::Window* window, const gfx::Point& location, @@ -90,7 +118,7 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location) { AdjustBoundsForMainWindow(&bounds); if (bounds != details_.window->bounds()) did_move_or_resize_ = true; - UpdatePhantomWindow(); + UpdatePhantomWindow(location); if (!attached_windows_.empty()) { if (details_.window_component == HTRIGHT) LayoutAttachedWindowsHorizontally(bounds); @@ -104,14 +132,22 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location) { void WorkspaceWindowResizer::CompleteDrag() { if (phantom_window_controller_.get()) { - phantom_window_controller_->DelayedClose(kSnapDurationMS); + if (phantom_placement_.type == TYPE_DESTINATION) + phantom_window_controller_->DelayedClose(kSnapDurationMS); phantom_window_controller_.reset(); } + if (!did_move_or_resize_ || details_.window_component != HTCAPTION) + return; + + if (phantom_placement_.type == TYPE_LEFT_EDGE || + phantom_placement_.type == TYPE_RIGHT_EDGE) { + if (!GetRestoreBounds(details_.window)) + SetRestoreBounds(details_.window, details_.initial_bounds); + details_.window->SetBounds(phantom_placement_.bounds); + return; + } - // This code only matters when dragging the caption and there's a grid, so - // it doesn't need to worry about attached windows. - if (details_.grid_size <= 1 || !did_move_or_resize_ || - details_.window_component != HTCAPTION) + if (details_.grid_size <= 1) return; gfx::Rect bounds(GetFinalBounds()); @@ -169,7 +205,8 @@ WorkspaceWindowResizer::WorkspaceWindowResizer( did_move_or_resize_(false), root_filter_(NULL), total_min_(0), - total_initial_size_(0) { + total_initial_size_(0), + num_mouse_moves_since_bounds_change_(0) { DCHECK(details_.is_resizable); root_filter_ = Shell::GetInstance()->root_filter(); if (root_filter_) @@ -240,6 +277,12 @@ WorkspaceWindowResizer::WorkspaceWindowResizer( } gfx::Rect WorkspaceWindowResizer::GetFinalBounds() const { + if (phantom_window_controller_.get() && + phantom_window_controller_->IsShowing() && + phantom_window_controller_->type() == + PhantomWindowController::TYPE_EDGE) { + return phantom_window_controller_->bounds(); + } gfx::Rect bounds(AdjustBoundsToGrid(details_)); if (GetHeightBeforeObscured(window()) || constrain_size_) { // Two things can happen: @@ -416,17 +459,83 @@ int WorkspaceWindowResizer::PrimaryAxisCoordinate(int x, int y) const { return 0; } -void WorkspaceWindowResizer::UpdatePhantomWindow() { - if (!did_move_or_resize_ || details_.window_component != HTCAPTION || - !wm::IsWindowNormal(details_.window) || details_.grid_size <= 1) +void WorkspaceWindowResizer::UpdatePhantomWindow(const gfx::Point& location) { + if (!did_move_or_resize_ || details_.window_component != HTCAPTION) return; - gfx::Rect phantom_bounds(GetFinalBounds()); + PhantomPlacement last_placement = phantom_placement_; + phantom_placement_ = GetPhantomPlacement(location); + if (phantom_placement_.type == TYPE_NONE) { + phantom_window_controller_.reset(); + return; + } + PhantomWindowController::Type phantom_type; + if (phantom_placement_.type == TYPE_LEFT_EDGE || + phantom_placement_.type == TYPE_RIGHT_EDGE) { + phantom_type = PhantomWindowController::TYPE_EDGE; + UpdatePhantomWindowBoundsAlongEdge(last_placement); + } else { + phantom_type = PhantomWindowController::TYPE_DESTINATION; + } + if (phantom_window_controller_.get() && + phantom_window_controller_->type() != phantom_type) { + phantom_window_controller_.reset(); + } if (!phantom_window_controller_.get()) { phantom_window_controller_.reset( - new PhantomWindowController(details_.window, kPhantomDelayMS)); + new PhantomWindowController(details_.window, phantom_type, + kPhantomDelayMS)); + } + phantom_window_controller_->Show(phantom_placement_.bounds); +} + +void WorkspaceWindowResizer::UpdatePhantomWindowBoundsAlongEdge( + const PhantomPlacement& last_placement) { + if (last_placement.type == phantom_placement_.type) { + int grid_size = std::max(1, details_.grid_size); + if (++num_mouse_moves_since_bounds_change_ >= grid_size / 2) { + gfx::Rect area(gfx::Screen::GetMonitorAreaNearestWindow(details_.window)); + gfx::Rect bounds(last_placement.bounds); + if (last_placement.type == TYPE_LEFT_EDGE) { + bounds.set_width(std::min(WindowResizer::AlignToGridRoundDown( + area.width() - bounds.x(), grid_size), + bounds.width() + grid_size)); + } else { + int x = std::max( + WindowResizer::AlignToGridRoundUp(area.x(), grid_size), + bounds.x() - grid_size); + bounds.set_width(phantom_placement_.bounds.width() - x); + bounds.set_x(x); + } + phantom_placement_.bounds = bounds; + num_mouse_moves_since_bounds_change_ = 0; + } else { + phantom_placement_.bounds = last_placement.bounds; + } + } else { + num_mouse_moves_since_bounds_change_ = 0; + } +} + +WorkspaceWindowResizer::PhantomPlacement +WorkspaceWindowResizer::GetPhantomPlacement(const gfx::Point& location) { + // TODO: this likely only wants total monitor area, not the area of a single + // monitor. + PhantomPlacement placement; + gfx::Rect area(gfx::Screen::GetMonitorAreaNearestWindow(details_.window)); + if (location.x() <= area.x()) { + placement.type = TYPE_LEFT_EDGE; + placement.bounds = GetBoundsForWindowAlongEdge( + details_.window, LEFT_EDGE, details_.grid_size); + } else if (location.x() >= area.right() - 1) { + placement.type = TYPE_RIGHT_EDGE; + placement.bounds = GetBoundsForWindowAlongEdge( + details_.window, RIGHT_EDGE, details_.grid_size); + } else if (details_.grid_size > 1 && wm::IsWindowNormal(details_.window)) { + placement.bounds = GetFinalBounds(); + placement.type = TYPE_DESTINATION; } - phantom_window_controller_->Show(phantom_bounds); + return placement; } } // namespace internal diff --git a/ash/wm/workspace/workspace_window_resizer.h b/ash/wm/workspace/workspace_window_resizer.h index d548173..eaa2b1d7 100644 --- a/ash/wm/workspace/workspace_window_resizer.h +++ b/ash/wm/workspace/workspace_window_resizer.h @@ -25,6 +25,12 @@ class RootWindowEventFilter; // attempt to restore the old height. class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer { public: + // Used when the window is dragged against the edge of the screen. + enum EdgeType { + LEFT_EDGE, + RIGHT_EDGE + }; + // When dragging an attached window this is the min size we'll make sure is // visibile. In the vertical direction we take the max of this and that from // the delegate. @@ -32,6 +38,12 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer { virtual ~WorkspaceWindowResizer(); + // Returns the bounds for a window along the specified edge. + static gfx::Rect GetBoundsForWindowAlongEdge( + aura::Window* window, + EdgeType edge, + int grid_size); + static WorkspaceWindowResizer* Create( aura::Window* window, const gfx::Point& location, @@ -56,6 +68,23 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer { const std::vector<aura::Window*>& attached_windows); private: + // Location of the phanton window. + enum PhantomType { + TYPE_LEFT_EDGE, + TYPE_RIGHT_EDGE, + TYPE_DESTINATION, + TYPE_NONE + }; + + // Type and bounds of the phantom window. + struct PhantomPlacement { + PhantomPlacement(); + ~PhantomPlacement(); + + PhantomType type; + gfx::Rect bounds; + }; + // Returns the final bounds to place the window at. This differs from // the current if there is a grid. gfx::Rect GetFinalBounds() const; @@ -85,7 +114,16 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer { int PrimaryAxisCoordinate(int x, int y) const; // Updates the bounds of the phantom window. - void UpdatePhantomWindow(); + void UpdatePhantomWindow(const gfx::Point& location); + + // Updates |phantom_placement_| when type is one of TYPE_LEFT_EDGE or + // TYPE_RIGHT_EDGE. + void UpdatePhantomWindowBoundsAlongEdge( + const PhantomPlacement& last_placement); + + // Returns a PhantomPlacement for the specified point. TYPE_NONE is used if + // the location doesn't have a valid phantom location. + PhantomPlacement GetPhantomPlacement(const gfx::Point& location); aura::Window* window() const { return details_.window; } @@ -124,6 +162,14 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer { // is a grid and the caption is being dragged. scoped_ptr<PhantomWindowController> phantom_window_controller_; + // Last PhantomPlacement. + PhantomPlacement phantom_placement_; + + // Number of mouse moves since the last bounds change. Only used for phantom + // placement to track when the mouse is moved while pushed against the edge of + // the screen. + int num_mouse_moves_since_bounds_change_; + DISALLOW_COPY_AND_ASSIGN(WorkspaceWindowResizer); }; diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc index d129d1e..83b5770 100644 --- a/ash/wm/workspace/workspace_window_resizer_unittest.cc +++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc @@ -6,6 +6,7 @@ #include "ash/shell.h" #include "ash/test/ash_test_base.h" +#include "ash/wm/property_util.h" #include "base/string_number_conversions.h" #include "ui/aura/root_window.h" #include "ui/aura/screen_aura.h" @@ -198,7 +199,7 @@ TEST_F(WorkspaceWindowResizerTest, ShrinkWithGrid) { window_.get(), gfx::Point(), HTCAPTION, 5, empty_windows())); ASSERT_TRUE(resizer.get()); // Drag down 8 pixels. - resizer->Drag(CalculateDragPoint(*resizer, 0, 8)); + resizer->Drag(CalculateDragPoint(*resizer, 10, 8)); resizer->CompleteDrag(); EXPECT_EQ(310, window_->bounds().y()); EXPECT_EQ(kRootHeight - 310, window_->bounds().height()); @@ -633,9 +634,33 @@ TEST_F(WorkspaceWindowResizerTest, AttachedResize_BOTTOM_RememberHeight) { EXPECT_EQ("300,400 200x150", window2_->bounds().ToString()); EXPECT_EQ("300,550 200x100", window3_->bounds().ToString()); } +} + +// Assertions around dragging to the left/right edge of the screen. +TEST_F(WorkspaceWindowResizerTest, Edge) { + window_->SetBounds(gfx::Rect(20, 30, 50, 60)); + { + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTCAPTION, 0, empty_windows())); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 0, 10)); + resizer->CompleteDrag(); + EXPECT_EQ("0,0 400x600", window_->bounds().ToString()); + ASSERT_TRUE(GetRestoreBounds(window_.get())); + EXPECT_EQ("20,30 50x60", GetRestoreBounds(window_.get())->ToString()); + } + // Try the same with the right side. + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTCAPTION, 0, empty_windows())); + ASSERT_TRUE(resizer.get()); + resizer->Drag(CalculateDragPoint(*resizer, 800, 10)); + resizer->CompleteDrag(); + EXPECT_EQ("400,0 400x600", window_->bounds().ToString()); + ASSERT_TRUE(GetRestoreBounds(window_.get())); + EXPECT_EQ("20,30 50x60", GetRestoreBounds(window_.get())->ToString()); } } // namespace } // namespace test -} // namespace aura +} // namespace ash |