summaryrefslogtreecommitdiffstats
path: root/ash
diff options
context:
space:
mode:
authorsky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-25 22:30:50 +0000
committersky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-02-25 22:30:50 +0000
commit5ae18725d3bba85f251f6d0f76453c2d891290ff (patch)
treedddb996f5538c201b840b2cc6965dbc61714ef5f /ash
parent0239e8694892e4ad603f77f758bbff30b3aff216 (diff)
downloadchromium_src-5ae18725d3bba85f251f6d0f76453c2d891290ff.zip
chromium_src-5ae18725d3bba85f251f6d0f76453c2d891290ff.tar.gz
chromium_src-5ae18725d3bba85f251f6d0f76453c2d891290ff.tar.bz2
Attempt 4:
Makes managed mode constrain the height of windows so they don't overlap the launcher. BUG=115581 TEST=see bug, covered by unit tests too R=ben@chromium.org TBR=ben@chromium.org Review URL: https://chromiumcodereview.appspot.com/9466032 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@123674 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r--ash/ash.gyp3
-rw-r--r--ash/wm/toplevel_window_event_filter.cc14
-rw-r--r--ash/wm/toplevel_window_event_filter.h6
-rw-r--r--ash/wm/toplevel_window_event_filter_unittest.cc1
-rw-r--r--ash/wm/window_resizer.cc18
-rw-r--r--ash/wm/window_resizer.h14
-rw-r--r--ash/wm/workspace/workspace_event_filter.cc55
-rw-r--r--ash/wm/workspace/workspace_event_filter.h22
-rw-r--r--ash/wm/workspace/workspace_layout_manager.cc40
-rw-r--r--ash/wm/workspace/workspace_layout_manager.h15
-rw-r--r--ash/wm/workspace/workspace_window_resizer.cc113
-rw-r--r--ash/wm/workspace/workspace_window_resizer.h52
-rw-r--r--ash/wm/workspace/workspace_window_resizer_unittest.cc274
13 files changed, 504 insertions, 123 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp
index ce530c7..fa3b733 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -202,6 +202,8 @@
'wm/workspace/workspace_layout_manager.h',
'wm/workspace/workspace_manager.cc',
'wm/workspace/workspace_manager.h',
+ 'wm/workspace/workspace_window_resizer.cc',
+ 'wm/workspace/workspace_window_resizer.h',
],
'conditions': [
['OS=="mac"', {
@@ -282,6 +284,7 @@
'wm/window_cycle_controller_unittest.cc',
'wm/window_modality_controller_unittest.cc',
'wm/workspace/workspace_manager_unittest.cc',
+ 'wm/workspace/workspace_window_resizer_unittest.cc',
'wm/workspace_controller_unittest.cc',
'<(SHARED_INTERMEDIATE_DIR)/ui/gfx/gfx_resources.rc',
diff --git a/ash/wm/toplevel_window_event_filter.cc b/ash/wm/toplevel_window_event_filter.cc
index 20ae5e5..78645fef 100644
--- a/ash/wm/toplevel_window_event_filter.cc
+++ b/ash/wm/toplevel_window_event_filter.cc
@@ -48,8 +48,7 @@ bool ToplevelWindowEventFilter::PreHandleMouseEvent(aura::Window* target,
target->delegate()->GetNonClientComponent(event->location());
if (WindowResizer::GetBoundsChangeForWindowComponent(component)) {
window_resizer_.reset(
- new WindowResizer(target, event->location(), component,
- grid_size_));
+ CreateWindowResizer(target, event->location(), component));
if (!window_resizer_->is_resizable())
window_resizer_.reset();
} else {
@@ -93,7 +92,7 @@ ui::GestureStatus ToplevelWindowEventFilter::PreHandleGestureEvent(
}
in_gesture_resize_ = true;
window_resizer_.reset(
- new WindowResizer(target, event->location(), component, grid_size_));
+ CreateWindowResizer(target, event->location(), component));
if (!window_resizer_->is_resizable())
window_resizer_.reset();
break;
@@ -125,7 +124,7 @@ void ToplevelWindowEventFilter::RunMoveLoop(aura::Window* source) {
aura::Window::ConvertPointToWindow(
Shell::GetRootWindow(), source, &source_mouse_location);
window_resizer_.reset(
- new WindowResizer(source, source_mouse_location, HTCAPTION, grid_size_));
+ CreateWindowResizer(source, source_mouse_location, HTCAPTION));
#if !defined(OS_MACOSX)
MessageLoopForUI::current()->RunWithDispatcher(
aura::Env::GetInstance()->GetDispatcher());
@@ -143,6 +142,13 @@ void ToplevelWindowEventFilter::EndMoveLoop() {
Shell::GetRootWindow()->PostNativeEvent(ui::CreateNoopEvent());
}
+WindowResizer* ToplevelWindowEventFilter::CreateWindowResizer(
+ aura::Window* window,
+ const gfx::Point& point,
+ int window_component) {
+ return new WindowResizer(window, point, window_component, grid_size_);
+}
+
void ToplevelWindowEventFilter::CompleteDrag(aura::Window* window) {
scoped_ptr<WindowResizer> resizer(window_resizer_.release());
if (resizer.get())
diff --git a/ash/wm/toplevel_window_event_filter.h b/ash/wm/toplevel_window_event_filter.h
index f6899de..01bced7 100644
--- a/ash/wm/toplevel_window_event_filter.h
+++ b/ash/wm/toplevel_window_event_filter.h
@@ -54,6 +54,12 @@ class ASH_EXPORT ToplevelWindowEventFilter :
virtual void RunMoveLoop(aura::Window* source) OVERRIDE;
virtual void EndMoveLoop() OVERRIDE;
+ protected:
+ // Creates a new WindowResizer.
+ virtual WindowResizer* CreateWindowResizer(aura::Window* window,
+ const gfx::Point& point,
+ int window_component);
+
private:
// Invoked when the mouse is released to cleanup after a drag.
void CompleteDrag(aura::Window* window);
diff --git a/ash/wm/toplevel_window_event_filter_unittest.cc b/ash/wm/toplevel_window_event_filter_unittest.cc
index 611ba5e..2f436a7 100644
--- a/ash/wm/toplevel_window_event_filter_unittest.cc
+++ b/ash/wm/toplevel_window_event_filter_unittest.cc
@@ -25,7 +25,6 @@
#endif
#endif
-
namespace ash {
namespace test {
diff --git a/ash/wm/window_resizer.cc b/ash/wm/window_resizer.cc
index 9cc1c2a..fbb523c 100644
--- a/ash/wm/window_resizer.cc
+++ b/ash/wm/window_resizer.cc
@@ -205,13 +205,16 @@ void WindowResizer::Drag(const gfx::Point& location) {
void WindowResizer::CompleteDrag() {
if (grid_size_ <= 1 || !did_move_or_resize_)
return;
- const gfx::Rect& bounds(window_->bounds());
- int x = AlignToGrid(bounds.x(), grid_size_);
- int y = AlignToGrid(bounds.y(), grid_size_);
- gfx::Rect new_bounds(x, y, bounds.width(), bounds.height());
+ gfx::Rect new_bounds(GetFinalBounds());
if (new_bounds == window_->bounds())
return;
+ if (new_bounds.size() != window_->bounds().size()) {
+ // Don't attempt to animate a size change.
+ window_->SetBounds(new_bounds);
+ return;
+ }
+
ui::ScopedLayerAnimationSettings scoped_setter(
window_->layer()->GetAnimator());
// Use a small duration since the grid is small.
@@ -256,6 +259,13 @@ gfx::Rect WindowResizer::GetBoundsForDrag(const gfx::Point& location) {
return new_bounds;
}
+gfx::Rect WindowResizer::GetFinalBounds() {
+ const gfx::Rect& bounds(window_->bounds());
+ int x = AlignToGrid(bounds.x(), grid_size_);
+ int y = AlignToGrid(bounds.y(), grid_size_);
+ return gfx::Rect(x, y, bounds.width(), bounds.height());
+}
+
gfx::Point WindowResizer::GetOriginForDrag(
int delta_x,
int delta_y) const {
diff --git a/ash/wm/window_resizer.h b/ash/wm/window_resizer.h
index f44081b..777eff3 100644
--- a/ash/wm/window_resizer.h
+++ b/ash/wm/window_resizer.h
@@ -38,7 +38,7 @@ class ASH_EXPORT WindowResizer {
const gfx::Point& location,
int window_component,
int grid_size);
- ~WindowResizer();
+ virtual ~WindowResizer();
// Returns a bitmask of the kBoundsChange_ values.
static int GetBoundsChangeForWindowComponent(int component);
@@ -64,12 +64,20 @@ class ASH_EXPORT WindowResizer {
}
int window_component() const { return window_component_; }
aura::Window* window() const { return window_; }
+ int grid_size() const { return grid_size_; }
+ bool did_move_or_resize() const { return did_move_or_resize_; }
+ int bounds_change() const { return bounds_change_; }
- private:
+ protected:
// Returns the bounds to give to the window once the mouse has moved to
// |location|.
- gfx::Rect GetBoundsForDrag(const gfx::Point& location);
+ virtual gfx::Rect GetBoundsForDrag(const gfx::Point& location);
+
+ // Returns the final bounds. This differs from current bounds if a grid_size
+ // was specified.
+ virtual gfx::Rect GetFinalBounds();
+ private:
// Returns the new origin of the window. The arguments are the difference
// between the current location and the initial location.
gfx::Point GetOriginForDrag(int delta_x, int delta_y) const;
diff --git a/ash/wm/workspace/workspace_event_filter.cc b/ash/wm/workspace/workspace_event_filter.cc
index e64bc90..0fe8bb2 100644
--- a/ash/wm/workspace/workspace_event_filter.cc
+++ b/ash/wm/workspace/workspace_event_filter.cc
@@ -7,6 +7,7 @@
#include "ash/wm/window_frame.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace/workspace_layout_manager.h"
+#include "ash/wm/workspace/workspace_window_resizer.h"
#include "ui/aura/event.h"
#include "ui/aura/window.h"
#include "ui/base/hit_test.h"
@@ -31,8 +32,6 @@ namespace internal {
WorkspaceEventFilter::WorkspaceEventFilter(aura::Window* owner)
: ToplevelWindowEventFilter(owner),
- owner_(owner),
- drag_state_(DRAG_NONE),
hovered_window_(NULL) {
}
@@ -43,57 +42,18 @@ WorkspaceEventFilter::~WorkspaceEventFilter() {
bool WorkspaceEventFilter::PreHandleMouseEvent(aura::Window* target,
aura::MouseEvent* event) {
- WorkspaceLayoutManager* layout_manager =
- static_cast<WorkspaceLayoutManager*>(owner_->layout_manager());
- DCHECK(layout_manager);
-
- // TODO(oshima|derat): Incorporate the logic below and introduce DragObserver
- // (or something similar) to decouple DCLM.
-
- // Notify layout manager that drag event may move/resize the target wnidow.
- if (event->type() == ui::ET_MOUSE_DRAGGED && drag_state_ == DRAG_NONE)
- layout_manager->PrepareForMoveOrResize(target, event);
-
- bool handled = ToplevelWindowEventFilter::PreHandleMouseEvent(target, event);
-
switch (event->type()) {
- case ui::ET_MOUSE_DRAGGED:
- // Cancel move/resize if the event wasn't handled, or
- // drag_state_ didn't move to MOVE or RESIZE.
- if (handled) {
- switch (drag_state_) {
- case DRAG_NONE:
- if (!UpdateDragState())
- layout_manager->CancelMoveOrResize(target, event);
- break;
- case DRAG_MOVE:
- layout_manager->ProcessMove(target, event);
- break;
- case DRAG_RESIZE:
- break;
- }
- } else {
- layout_manager->CancelMoveOrResize(target, event);
- }
- break;
case ui::ET_MOUSE_ENTERED:
UpdateHoveredWindow(GetActivatableWindow(target));
break;
+ case ui::ET_MOUSE_CAPTURE_CHANGED:
case ui::ET_MOUSE_EXITED:
UpdateHoveredWindow(NULL);
break;
- case ui::ET_MOUSE_RELEASED:
- if (drag_state_ == DRAG_MOVE)
- layout_manager->EndMove(target, event);
- else if (drag_state_ == DRAG_RESIZE)
- layout_manager->EndResize(target, event);
-
- drag_state_ = DRAG_NONE;
- break;
default:
break;
}
- return handled;
+ return ToplevelWindowEventFilter::PreHandleMouseEvent(target, event);
}
void WorkspaceEventFilter::OnWindowDestroyed(aura::Window* window) {
@@ -102,9 +62,12 @@ void WorkspaceEventFilter::OnWindowDestroyed(aura::Window* window) {
hovered_window_ = NULL;
}
-bool WorkspaceEventFilter::UpdateDragState() {
- // TODO(sky): nuke this method.
- return false;
+WindowResizer* WorkspaceEventFilter::CreateWindowResizer(
+ aura::Window* window,
+ const gfx::Point& point,
+ int window_component) {
+ return
+ new WorkspaceWindowResizer(window, point, window_component, grid_size());
}
void WorkspaceEventFilter::UpdateHoveredWindow(
diff --git a/ash/wm/workspace/workspace_event_filter.h b/ash/wm/workspace/workspace_event_filter.h
index 2c15c37..5598185 100644
--- a/ash/wm/workspace/workspace_event_filter.h
+++ b/ash/wm/workspace/workspace_event_filter.h
@@ -30,27 +30,17 @@ class WorkspaceEventFilter : public ToplevelWindowEventFilter,
// Overriden from WindowObserver:
virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
- private:
- enum DragState {
- DRAG_NONE,
- DRAG_MOVE,
- DRAG_RESIZE
- };
-
- // If the mouse is currently over a portion of the window that should
- // trigger a drag or resize, drag_state_ is set appropriately and true
- // is returned. If the mouse is not over a portion of the window that should
- // trigger a more or resize, drag_state_ is not updated and false is returned.
- bool UpdateDragState();
+ protected:
+ // Overriden from ToplevelWindowEventFilter:
+ virtual WindowResizer* CreateWindowResizer(aura::Window* window,
+ const gfx::Point& point,
+ int window_component) OVERRIDE;
+ private:
// Updates the top-level window under the mouse so that we can change
// the look of the caption area based on mouse-hover.
void UpdateHoveredWindow(aura::Window* toplevel);
- aura::Window* owner_;
-
- DragState drag_state_;
-
// Top-level window under the mouse cursor.
aura::Window* hovered_window_;
diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc
index a705059..bbe26f57 100644
--- a/ash/wm/workspace/workspace_layout_manager.cc
+++ b/ash/wm/workspace/workspace_layout_manager.cc
@@ -31,38 +31,6 @@ WorkspaceLayoutManager::WorkspaceLayoutManager(
WorkspaceLayoutManager::~WorkspaceLayoutManager() {}
-void WorkspaceLayoutManager::PrepareForMoveOrResize(
- aura::Window* drag,
- aura::MouseEvent* event) {
-}
-
-void WorkspaceLayoutManager::CancelMoveOrResize(
- aura::Window* drag,
- aura::MouseEvent* event) {
-}
-
-void WorkspaceLayoutManager::ProcessMove(
- aura::Window* drag,
- aura::MouseEvent* event) {
- // TODO: needs implementation for TYPE_SPLIT. For TYPE_SPLIT I want to
- // disallow eventfilter from moving and instead deal with it here.
-}
-
-void WorkspaceLayoutManager::EndMove(
- aura::Window* drag,
- aura::MouseEvent* evnet) {
- // TODO: see comment in ProcessMove.
-}
-
-void WorkspaceLayoutManager::EndResize(
- aura::Window* drag,
- aura::MouseEvent* evnet) {
- // TODO: see comment in ProcessMove.
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// WorkspaceLayoutManager, aura::LayoutManager implementation:
-
void WorkspaceLayoutManager::OnWindowResized() {
// Workspace is updated via RootWindowObserver::OnRootWindowResized.
}
@@ -112,10 +80,14 @@ void WorkspaceLayoutManager::SetChildBounds(
aura::Window* child,
const gfx::Rect& requested_bounds) {
gfx::Rect child_bounds(requested_bounds);
- if (window_util::IsWindowMaximized(child))
+ if (window_util::IsWindowMaximized(child)) {
child_bounds = gfx::Screen::GetMonitorWorkAreaNearestWindow(child);
- else if (window_util::IsWindowFullscreen(child))
+ } else if (window_util::IsWindowFullscreen(child)) {
child_bounds = gfx::Screen::GetMonitorAreaNearestWindow(child);
+ } else {
+ child_bounds = gfx::Screen::GetMonitorWorkAreaNearestWindow(child).
+ AdjustToFit(requested_bounds);
+ }
SetChildBoundsDirect(child, child_bounds);
}
diff --git a/ash/wm/workspace/workspace_layout_manager.h b/ash/wm/workspace/workspace_layout_manager.h
index 7be5143..e350ad2 100644
--- a/ash/wm/workspace/workspace_layout_manager.h
+++ b/ash/wm/workspace/workspace_layout_manager.h
@@ -36,21 +36,6 @@ class ASH_EXPORT WorkspaceLayoutManager : public aura::LayoutManager {
return workspace_manager_;
}
- // Invoked when a window receives drag event.
- void PrepareForMoveOrResize(aura::Window* drag, aura::MouseEvent* event);
-
- // Invoked when a drag event didn't start any drag operation.
- void CancelMoveOrResize(aura::Window* drag, aura::MouseEvent* event);
-
- // Invoked when a drag event moved the |window|.
- void ProcessMove(aura::Window* window, aura::MouseEvent* event);
-
- // Invoked when a user finished moving window.
- void EndMove(aura::Window* drag, aura::MouseEvent* event);
-
- // Invoked when a user finished resizing window.
- void EndResize(aura::Window* drag, aura::MouseEvent* event);
-
// Overridden from aura::LayoutManager:
virtual void OnWindowResized() OVERRIDE;
virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE;
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc
new file mode 100644
index 0000000..a16178c
--- /dev/null
+++ b/ash/wm/workspace/workspace_window_resizer.cc
@@ -0,0 +1,113 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/workspace/workspace_window_resizer.h"
+
+#include "ui/aura/window.h"
+#include "ui/aura/window_delegate.h"
+#include "ui/aura/window_property.h"
+#include "ui/gfx/screen.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(int)
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+const aura::WindowProperty<int> kHeightBeforeObscuredProp = {0};
+const aura::WindowProperty<int>* const kHeightBeforeObscuredKey =
+ &kHeightBeforeObscuredProp;
+
+} // namespace
+
+WorkspaceWindowResizer::WorkspaceWindowResizer(aura::Window* window,
+ const gfx::Point& location,
+ int window_component,
+ int grid_size)
+ : WindowResizer(window, location, window_component, grid_size) {
+ if (is_resizable() && GetHeightBeforeObscured(window) &&
+ (!WindowTouchesBottomOfScreen() ||
+ bounds_change() != kBoundsChange_Repositions)) {
+ ClearHeightBeforeObscured(window);
+ }
+}
+
+WorkspaceWindowResizer::~WorkspaceWindowResizer() {
+}
+
+gfx::Rect WorkspaceWindowResizer::GetBoundsForDrag(const gfx::Point& location) {
+ if (!is_resizable())
+ return WindowResizer::GetBoundsForDrag(location);
+
+ gfx::Rect bounds(WindowResizer::GetBoundsForDrag(location));
+ AdjustBounds(&bounds);
+ return bounds;
+}
+
+gfx::Rect WorkspaceWindowResizer::GetFinalBounds() {
+ if (grid_size() <= 1 || !GetHeightBeforeObscured(window()))
+ return WindowResizer::GetFinalBounds();
+
+ gfx::Rect initial_bounds(window()->bounds());
+ bool at_bottom = WindowTouchesBottomOfScreen();
+ gfx::Rect bounds(WindowResizer::GetFinalBounds());
+ if (at_bottom && bounds.y() != initial_bounds.y()) {
+ if (bounds.y() < initial_bounds.y()) {
+ bounds.set_height(bounds.height() + grid_size() -
+ (initial_bounds.y() - bounds.y()));
+ }
+ AdjustBounds(&bounds);
+ }
+ return bounds;
+}
+
+// static
+void WorkspaceWindowResizer::SetHeightBeforeObscured(aura::Window* window,
+ int height) {
+ window->SetProperty(kHeightBeforeObscuredKey, height);
+}
+
+// static
+void WorkspaceWindowResizer::ClearHeightBeforeObscured(aura::Window* window) {
+ window->SetProperty(kHeightBeforeObscuredKey, 0);
+}
+
+// static
+int WorkspaceWindowResizer::GetHeightBeforeObscured(aura::Window* window) {
+ return window->GetProperty(kHeightBeforeObscuredKey);
+}
+
+void WorkspaceWindowResizer::AdjustBounds(gfx::Rect* bounds) const {
+ gfx::Rect work_area(gfx::Screen::GetMonitorWorkAreaNearestWindow(window()));
+ if (bounds->bottom() < work_area.bottom()) {
+ int height = GetHeightBeforeObscured(window());
+ if (!height)
+ return;
+ height = std::max(bounds->height(), height);
+ bounds->set_height(std::min(work_area.bottom() - bounds->y(), height));
+ return;
+ }
+
+ if (bounds->bottom() == work_area.bottom())
+ return;
+
+ if (!GetHeightBeforeObscured(window()))
+ SetHeightBeforeObscured(window(), window()->bounds().height());
+
+ gfx::Size min_size = window()->delegate()->GetMinimumSize();
+ bounds->set_height(std::max(0, work_area.bottom() - bounds->y()));
+ if (bounds->height() < min_size.height()) {
+ bounds->set_height(min_size.height());
+ bounds->set_y(work_area.bottom() - min_size.height());
+ }
+}
+
+bool WorkspaceWindowResizer::WindowTouchesBottomOfScreen() const {
+ gfx::Rect work_area(gfx::Screen::GetMonitorWorkAreaNearestWindow(window()));
+ return window()->bounds().bottom() == work_area.bottom();
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/wm/workspace/workspace_window_resizer.h b/ash/wm/workspace/workspace_window_resizer.h
new file mode 100644
index 0000000..d32b0be
--- /dev/null
+++ b/ash/wm/workspace/workspace_window_resizer.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef ASH_WM_WORKSPACE_WINDOW_RESIZER_H_
+#define ASH_WM_WORKSPACE_WINDOW_RESIZER_H_
+#pragma once
+
+#include "ash/wm/window_resizer.h"
+#include "base/compiler_specific.h"
+
+namespace ash {
+namespace internal {
+
+// WindowResizer implementation for workspaces. This enforces that windows are
+// not allowed to vertically move or resize outside of the work area. As windows
+// are moved outside the work area they are shrunk. We remember the height of
+// the window before it was moved so that if the window is again moved up we
+// attempt to restore the old height.
+class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer {
+ public:
+ WorkspaceWindowResizer(aura::Window* window,
+ const gfx::Point& location,
+ int window_component,
+ int grid_size);
+ virtual ~WorkspaceWindowResizer();
+
+ protected:
+ // WindowResizer overrides:
+ virtual gfx::Rect GetBoundsForDrag(const gfx::Point& location) OVERRIDE;
+ virtual gfx::Rect GetFinalBounds() OVERRIDE;
+
+ private:
+ // Used to maintain the height of the window before we started collapsing it.
+ static void SetHeightBeforeObscured(aura::Window* window, int height);
+ static void ClearHeightBeforeObscured(aura::Window* window);
+ static int GetHeightBeforeObscured(aura::Window* window);
+
+ // Adjusts the bounds to enforce that windows are vertically contained in the
+ // work area.
+ void AdjustBounds(gfx::Rect* bounds) const;
+
+ // Returns true if the window touches the bottom of the work area.
+ bool WindowTouchesBottomOfScreen() const;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkspaceWindowResizer);
+};
+
+} // namespace internal
+} // namespace ash
+
+#endif // ASH_WM_WORKSPACE_WINDOW_RESIZER_H_
diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc
new file mode 100644
index 0000000..7787ddc
--- /dev/null
+++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc
@@ -0,0 +1,274 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ash/wm/workspace/workspace_window_resizer.h"
+
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/screen_aura.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/base/hit_test.h"
+
+namespace ash {
+namespace internal {
+namespace {
+
+const int kRootHeight = 600;
+
+// A simple window delegate that returns the specified min size.
+class TestWindowDelegate : public aura::test::TestWindowDelegate {
+ public:
+ TestWindowDelegate() {
+ }
+ virtual ~TestWindowDelegate() {}
+
+ void set_min_size(const gfx::Size& size) {
+ min_size_ = size;
+ }
+
+ private:
+ // Overridden from aura::Test::TestWindowDelegate:
+ virtual gfx::Size GetMinimumSize() const OVERRIDE {
+ return min_size_;
+ }
+
+ gfx::Size min_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWindowDelegate);
+};
+
+class WorkspaceWindowResizerTest : public test::AshTestBase {
+ public:
+ WorkspaceWindowResizerTest() : window_(NULL) {}
+ virtual ~WorkspaceWindowResizerTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ AshTestBase::SetUp();
+ aura::RootWindow* root = Shell::GetInstance()->GetRootWindow();
+ root->SetBounds(gfx::Rect(0, 0, 800, kRootHeight));
+ gfx::Rect root_bounds(root->bounds());
+ EXPECT_EQ(kRootHeight, root_bounds.height());
+ root->screen()->set_work_area_insets(gfx::Insets());
+ window_.reset(new aura::Window(&delegate_));
+ window_->Init(ui::Layer::LAYER_NOT_DRAWN);
+ window_->SetParent(Shell::GetInstance()->GetRootWindow());
+ }
+
+ virtual void TearDown() OVERRIDE {
+ window_.reset();
+ AshTestBase::TearDown();
+ }
+
+ protected:
+ gfx::Point CalculateDragPoint(const WindowResizer& resizer,
+ int delta_y) const {
+ gfx::Point location = resizer.initial_location_in_parent();
+ location.set_y(location.y() + delta_y);
+ aura::Window::ConvertPointToWindow(window_->parent(), window_.get(),
+ &location);
+ return location;
+ }
+
+ TestWindowDelegate delegate_;
+ scoped_ptr<aura::Window> window_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WorkspaceWindowResizerTest);
+};
+
+// Assertions around making sure dragging shrinks when appropriate.
+TEST_F(WorkspaceWindowResizerTest, ShrinkOnDrag) {
+ int initial_y = 300;
+ window_->SetBounds(gfx::Rect(0, initial_y, 400, 296));
+
+ // Drag down past the bottom of the screen, height should stop when it hits
+ // the bottom.
+ {
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTBOTTOM, 0);
+ EXPECT_TRUE(resizer.is_resizable());
+ resizer.Drag(CalculateDragPoint(resizer, 600));
+ EXPECT_EQ(kRootHeight - initial_y, window_->bounds().height());
+
+ // Drag up 10 and make sure height is the same.
+ resizer.Drag(CalculateDragPoint(resizer, 590));
+ EXPECT_EQ(kRootHeight - initial_y, window_->bounds().height());
+ }
+
+ {
+ // Move the window down 10 pixels, the height should change.
+ int initial_height = window_->bounds().height();
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTCAPTION, 0);
+ resizer.Drag(CalculateDragPoint(resizer, 10));
+ EXPECT_EQ(initial_height - 10, window_->bounds().height());
+
+ // Move up 10, height should grow.
+ resizer.Drag(CalculateDragPoint(resizer, 0));
+ EXPECT_EQ(initial_height, window_->bounds().height());
+
+ // Move up another 10, height shouldn't change.
+ resizer.Drag(CalculateDragPoint(resizer, -10));
+ EXPECT_EQ(initial_height, window_->bounds().height());
+ }
+}
+
+// More assertions around making sure dragging shrinks when appropriate.
+TEST_F(WorkspaceWindowResizerTest, ShrinkOnDrag2) {
+ window_->SetBounds(gfx::Rect(0, 300, 400, 300));
+
+ // Drag down past the bottom of the screen, height should stop when it hits
+ // the bottom.
+ {
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTCAPTION, 0);
+ EXPECT_TRUE(resizer.is_resizable());
+ resizer.Drag(CalculateDragPoint(resizer, 200));
+ EXPECT_EQ(500, window_->bounds().y());
+ EXPECT_EQ(100, window_->bounds().height());
+ // End and start a new drag session.
+ }
+
+ {
+ // Drag up 400.
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTCAPTION, 0);
+ resizer.Drag(CalculateDragPoint(resizer, -400));
+ EXPECT_EQ(100, window_->bounds().y());
+ EXPECT_EQ(300, window_->bounds().height());
+ }
+}
+
+// Moves enough to shrink, then moves up twice to expose more than was initially
+// exposed.
+TEST_F(WorkspaceWindowResizerTest, ShrinkMoveThanMoveUp) {
+ window_->SetBounds(gfx::Rect(0, 300, 400, 300));
+
+ // Drag down past the bottom of the screen, height should stop when it hits
+ // the bottom.
+ {
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTCAPTION, 0);
+ EXPECT_TRUE(resizer.is_resizable());
+ resizer.Drag(CalculateDragPoint(resizer, 200));
+ EXPECT_EQ(500, window_->bounds().y());
+ EXPECT_EQ(100, window_->bounds().height());
+ // End and start a new drag session.
+ }
+
+ {
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTCAPTION, 0);
+ resizer.Drag(CalculateDragPoint(resizer, -400));
+ resizer.Drag(CalculateDragPoint(resizer, -450));
+ EXPECT_EQ(50, window_->bounds().y());
+ EXPECT_EQ(300, window_->bounds().height());
+ }
+}
+
+// Makes sure shrinking honors the grid appropriately.
+TEST_F(WorkspaceWindowResizerTest, ShrinkWithGrid) {
+ window_->SetBounds(gfx::Rect(0, 300, 400, 296));
+
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTCAPTION, 5);
+ EXPECT_TRUE(resizer.is_resizable());
+ // Drag down 8 pixels.
+ resizer.Drag(CalculateDragPoint(resizer, 8));
+ resizer.CompleteDrag();
+ EXPECT_EQ(310, window_->bounds().y());
+ EXPECT_EQ(kRootHeight - 310, window_->bounds().height());
+}
+
+// Makes sure once a window has been shrunk it can grow bigger than obscured
+// height
+TEST_F(WorkspaceWindowResizerTest, ShrinkThanGrow) {
+ int initial_y = 400;
+ int initial_height = 150;
+ window_->SetBounds(gfx::Rect(0, initial_y, 400, initial_height));
+
+ // Most past the bottom of the screen, height should stop when it hits the
+ // bottom.
+ {
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTCAPTION, 0);
+ resizer.Drag(CalculateDragPoint(resizer, 150));
+ EXPECT_EQ(550, window_->bounds().y());
+ EXPECT_EQ(50, window_->bounds().height());
+ }
+
+ // Resize the window 500 pixels up.
+ {
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTTOP, 0);
+ resizer.Drag(CalculateDragPoint(resizer, -500));
+ EXPECT_EQ(50, window_->bounds().y());
+ EXPECT_EQ(550, window_->bounds().height());
+ }
+}
+
+// Makes sure once a window has been shrunk it can grow bigger than obscured
+// height
+TEST_F(WorkspaceWindowResizerTest, DontRememberAfterMove) {
+ window_->SetBounds(gfx::Rect(0, 300, 400, 300));
+
+ // Most past the bottom of the screen, height should stop when it hits the
+ // bottom.
+ {
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTCAPTION, 0);
+ resizer.Drag(CalculateDragPoint(resizer, 150));
+ EXPECT_EQ(450, window_->bounds().y());
+ EXPECT_EQ(150, window_->bounds().height());
+ resizer.Drag(CalculateDragPoint(resizer, -150));
+ EXPECT_EQ(150, window_->bounds().y());
+ EXPECT_EQ(300, window_->bounds().height());
+ }
+
+ // Resize it slightly.
+ {
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTBOTTOM, 0);
+ resizer.Drag(CalculateDragPoint(resizer, -100));
+ EXPECT_EQ(150, window_->bounds().y());
+ EXPECT_EQ(200, window_->bounds().height());
+ }
+
+ {
+ // Move it down then back up.
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTCAPTION, 0);
+ resizer.Drag(CalculateDragPoint(resizer, 400));
+ EXPECT_EQ(550, window_->bounds().y());
+ EXPECT_EQ(50, window_->bounds().height());
+
+ resizer.Drag(CalculateDragPoint(resizer, 0));
+ EXPECT_EQ(150, window_->bounds().y());
+ EXPECT_EQ(200, window_->bounds().height());
+ }
+}
+
+// Makes sure we honor the min size.
+TEST_F(WorkspaceWindowResizerTest, HonorMin) {
+ delegate_.set_min_size(gfx::Size(50, 100));
+ window_->SetBounds(gfx::Rect(0, 300, 400, 300));
+
+ // Most past the bottom of the screen, height should stop when it hits the
+ // bottom.
+ {
+ WorkspaceWindowResizer resizer(window_.get(), gfx::Point(), HTCAPTION, 0);
+ resizer.Drag(CalculateDragPoint(resizer, 350));
+ EXPECT_EQ(500, window_->bounds().y());
+ EXPECT_EQ(100, window_->bounds().height());
+
+ resizer.Drag(CalculateDragPoint(resizer, 300));
+ EXPECT_EQ(500, window_->bounds().y());
+ EXPECT_EQ(100, window_->bounds().height());
+
+ resizer.Drag(CalculateDragPoint(resizer, 250));
+ EXPECT_EQ(500, window_->bounds().y());
+ EXPECT_EQ(100, window_->bounds().height());
+
+ resizer.Drag(CalculateDragPoint(resizer, 100));
+ EXPECT_EQ(400, window_->bounds().y());
+ EXPECT_EQ(200, window_->bounds().height());
+
+ resizer.Drag(CalculateDragPoint(resizer, -100));
+ EXPECT_EQ(200, window_->bounds().y());
+ EXPECT_EQ(300, window_->bounds().height());
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace aura