diff options
author | koz@chromium.org <koz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-28 07:07:55 +0000 |
---|---|---|
committer | koz@chromium.org <koz@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-28 07:07:55 +0000 |
commit | 3075983f95a3554e915bc89994a6b0f492ea3bd4 (patch) | |
tree | 9545cc03a19568cd213050c28c8323177165ebbb /ash | |
parent | 941dcc7e34455b128cf7de0c36ceea6e365b2a26 (diff) | |
download | chromium_src-3075983f95a3554e915bc89994a6b0f492ea3bd4.zip chromium_src-3075983f95a3554e915bc89994a6b0f492ea3bd4.tar.gz chromium_src-3075983f95a3554e915bc89994a6b0f492ea3bd4.tar.bz2 |
Make resizing of attached windows in ash honour min/max size constraints.
BUG=152065
Review URL: https://chromiumcodereview.appspot.com/11416194
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@169874 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer.cc | 234 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer.h | 58 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_window_resizer_unittest.cc | 237 |
3 files changed, 447 insertions, 82 deletions
diff --git a/ash/wm/workspace/workspace_window_resizer.cc b/ash/wm/workspace/workspace_window_resizer.cc index a717314..cd8996a 100644 --- a/ash/wm/workspace/workspace_window_resizer.cc +++ b/ash/wm/workspace/workspace_window_resizer.cc @@ -235,6 +235,75 @@ const int WorkspaceWindowResizer::kMinOnscreenHeight = 32; // static const int WorkspaceWindowResizer::kScreenEdgeInset = 8; +// Represents the width or height of a window with constraints on its minimum +// and maximum size. 0 represents a lack of a constraint. +class WindowSize { + public: + WindowSize(int size, int min, int max) + : size_(size), + min_(min), + max_(max) { + // Grow the min/max bounds to include the starting size. + if (is_underflowing()) + min_ = size_; + if (is_overflowing()) + max_ = size_; + } + + bool is_at_capacity(bool shrinking) { + return size_ == (shrinking ? min_ : max_); + } + + int size() const { + return size_; + } + + bool has_min() const { + return min_ != 0; + } + + bool has_max() const { + return max_ != 0; + } + + bool is_valid() const { + return !is_overflowing() && !is_underflowing(); + } + + bool is_overflowing() const { + return has_max() && size_ > max_; + } + + bool is_underflowing() const { + return has_min() && size_ < min_; + } + + // Add |amount| to this WindowSize not exceeding min or max size constraints. + // Returns by how much |size_| + |amount| exceeds the min/max constraints. + int Add(int amount) { + DCHECK(is_valid()); + int new_value = size_ + amount; + + if (has_min() && new_value < min_) { + size_ = min_; + return new_value - min_; + } + + if (has_max() && new_value > max_) { + size_ = max_; + return new_value - max_; + } + + size_ = new_value; + return 0; + } + + private: + int size_; + int min_; + int max_; +}; + WorkspaceWindowResizer::~WorkspaceWindowResizer() { Shell* shell = Shell::GetInstance(); shell->mouse_cursor_filter()->set_mouse_warp_mode( @@ -299,7 +368,7 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location_in_parent, } if (!attached_windows_.empty()) - LayoutAttachedWindows(bounds); + LayoutAttachedWindows(&bounds); if (bounds != window()->bounds()) { bool destroyed = false; destroyed_ = &destroyed; @@ -446,24 +515,10 @@ WorkspaceWindowResizer::WorkspaceWindowResizer( // This way we don't snap on resize. int min_size = std::min(initial_size, std::max(PrimaryAxisSize(min), kMinOnscreenSize)); - min_size_.push_back(min_size); total_min_ += min_size; total_initial_size_ += initial_size; total_available += std::max(min_size, initial_size) - min_size; } - - for (size_t i = 0; i < attached_windows_.size(); ++i) { - expand_fraction_.push_back( - static_cast<float>(initial_size_[i]) / - static_cast<float>(total_initial_size_)); - if (total_initial_size_ != total_min_) { - compress_fraction_.push_back( - static_cast<float>(initial_size_[i] - min_size_[i]) / - static_cast<float>(total_available)); - } else { - compress_fraction_.push_back(0.0f); - } - } } gfx::Rect WorkspaceWindowResizer::GetFinalBounds( @@ -476,17 +531,30 @@ gfx::Rect WorkspaceWindowResizer::GetFinalBounds( } void WorkspaceWindowResizer::LayoutAttachedWindows( - const gfx::Rect& bounds) { + gfx::Rect* bounds) { gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window())); + int initial_size = PrimaryAxisSize(details_.initial_bounds_in_parent.size()); + int current_size = PrimaryAxisSize(bounds->size()); + int start = PrimaryAxisCoordinate(bounds->right(), bounds->bottom()); + int end = PrimaryAxisCoordinate(work_area.right(), work_area.bottom()); + + int delta = current_size - initial_size; + int available_size = end - start; std::vector<int> sizes; - CalculateAttachedSizes( - PrimaryAxisSize(details_.initial_bounds_in_parent.size()), - PrimaryAxisSize(bounds.size()), - PrimaryAxisCoordinate(bounds.right(), bounds.bottom()), - PrimaryAxisCoordinate(work_area.right(), work_area.bottom()), - &sizes); + int leftovers = CalculateAttachedSizes(delta, available_size, &sizes); + + // Reallocate any leftover pixels back into the main window. This is + // necessary when, for example, the main window shrinks, but none of the + // attached windows can grow without exceeding their max size constraints. + // Adding the pixels back to the main window effectively prevents the main + // window from resizing too far. + if (details_.window_component == HTRIGHT) + bounds->set_width(bounds->width() + leftovers); + else + bounds->set_height(bounds->height() + leftovers); + DCHECK_EQ(attached_windows_.size(), sizes.size()); - int last = PrimaryAxisCoordinate(bounds.right(), bounds.bottom()); + int last = PrimaryAxisCoordinate(bounds->right(), bounds->bottom()); for (size_t i = 0; i < attached_windows_.size(); ++i) { gfx::Rect attached_bounds(attached_windows_[i]->bounds()); if (details_.window_component == HTRIGHT) { @@ -501,40 +569,98 @@ void WorkspaceWindowResizer::LayoutAttachedWindows( } } -void WorkspaceWindowResizer::CalculateAttachedSizes( - int initial_size, - int current_size, - int start, - int end, +int WorkspaceWindowResizer::CalculateAttachedSizes( + int delta, + int available_size, std::vector<int>* sizes) const { - sizes->clear(); - if (current_size < initial_size) { - // If the primary window is sized smaller, resize the attached windows. - int current = start; - int delta = initial_size - current_size; - for (size_t i = 0; i < attached_windows_.size(); ++i) { - int next = current + initial_size_[i] + expand_fraction_[i] * delta; - if (i + 1 == attached_windows_.size()) - next = start + total_initial_size_ + (initial_size - current_size); - sizes->push_back(next - current); - current = next; - } - } else if (start <= end - total_initial_size_) { - // All the windows fit at their initial size; tile them horizontally. - for (size_t i = 0; i < attached_windows_.size(); ++i) - sizes->push_back(initial_size_[i]); + std::vector<WindowSize> window_sizes; + CreateBucketsForAttached(&window_sizes); + + // How much we need to grow the attached by (collectively). + int grow_attached_by = 0; + if (delta > 0) { + // If the attached windows don't fit when at their initial size, we will + // have to shrink them by how much they overflow. + if (total_initial_size_ >= available_size) + grow_attached_by = available_size - total_initial_size_; } else { - DCHECK_NE(total_initial_size_, total_min_); - int delta = total_initial_size_ - (end - start); - int current = start; - for (size_t i = 0; i < attached_windows_.size(); ++i) { - int size = initial_size_[i] - - static_cast<int>(compress_fraction_[i] * delta); - if (i + 1 == attached_windows_.size()) - size = end - current; - current += size; - sizes->push_back(size); + // If we're shrinking, we grow the attached so the total size remains + // constant. + grow_attached_by = -delta; + } + + int leftover_pixels = 0; + while (grow_attached_by != 0) { + int leftovers = GrowFairly(grow_attached_by, window_sizes); + if (leftovers == grow_attached_by) { + leftover_pixels = leftovers; + break; } + grow_attached_by = leftovers; + } + + for (size_t i = 0; i < window_sizes.size(); ++i) + sizes->push_back(window_sizes[i].size()); + + return leftover_pixels; +} + +int WorkspaceWindowResizer::GrowFairly( + int pixels, + std::vector<WindowSize>& sizes) const { + bool shrinking = pixels < 0; + std::vector<WindowSize*> nonfull_windows; + for (size_t i = 0; i < sizes.size(); ++i) { + if (!sizes[i].is_at_capacity(shrinking)) + nonfull_windows.push_back(&sizes[i]); + } + std::vector<float> ratios; + CalculateGrowthRatios(nonfull_windows, &ratios); + + int remaining_pixels = pixels; + bool add_leftover_pixels_to_last = true; + for (size_t i = 0; i < nonfull_windows.size(); ++i) { + int grow_by = pixels * ratios[i]; + // Put any leftover pixels into the last window. + if (i == nonfull_windows.size() - 1 && add_leftover_pixels_to_last) + grow_by = remaining_pixels; + int remainder = nonfull_windows[i]->Add(grow_by); + int consumed = grow_by - remainder; + remaining_pixels -= consumed; + if (nonfull_windows[i]->is_at_capacity(shrinking) && remainder > 0) { + // Because this window overflowed, some of the pixels in + // |remaining_pixels| aren't there due to rounding errors. Rather than + // unfairly giving all those pixels to the last window, we refrain from + // allocating them so that this function can be called again to distribute + // the pixels fairly. + add_leftover_pixels_to_last = false; + } + } + return remaining_pixels; +} + +void WorkspaceWindowResizer::CalculateGrowthRatios( + const std::vector<WindowSize*>& sizes, + std::vector<float>* out_ratios) const { + DCHECK(out_ratios->empty()); + int total_value = 0; + for (size_t i = 0; i < sizes.size(); ++i) + total_value += sizes[i]->size(); + + for (size_t i = 0; i < sizes.size(); ++i) + out_ratios->push_back( + (static_cast<float>(sizes[i]->size())) / total_value); +} + +void WorkspaceWindowResizer::CreateBucketsForAttached( + std::vector<WindowSize>* sizes) const { + for (size_t i = 0; i < attached_windows_.size(); i++) { + int initial_size = initial_size_[i]; + aura::WindowDelegate* delegate = attached_windows_[i]->delegate(); + int min = PrimaryAxisSize(delegate->GetMinimumSize()); + int max = PrimaryAxisSize(delegate->GetMaximumSize()); + + sizes->push_back(WindowSize(initial_size, min, max)); } } diff --git a/ash/wm/workspace/workspace_window_resizer.h b/ash/wm/workspace/workspace_window_resizer.h index c952f11..a948cc8 100644 --- a/ash/wm/workspace/workspace_window_resizer.h +++ b/ash/wm/workspace/workspace_window_resizer.h @@ -27,6 +27,7 @@ namespace internal { class PhantomWindowController; class SnapSizer; +class WindowSize; // WindowResizer implementation for workspaces. This enforces that windows are // not allowed to vertically move or resize outside of the work area. As windows @@ -94,17 +95,36 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer { gfx::Rect GetFinalBounds(const gfx::Rect& bounds) const; // Lays out the attached windows. |bounds| is the bounds of the main window. - void LayoutAttachedWindows(const gfx::Rect& bounds); - - // Calculates the size (along the primary axis) of the attached windows. - // |initial_size| is the initial size of the main window, |current_size| the - // new size of the main window, |start| the position to layout the attached - // windows from and |end| the coordinate to position to. - void CalculateAttachedSizes(int initial_size, - int current_size, - int start, - int end, - std::vector<int>* sizes) const; + void LayoutAttachedWindows(gfx::Rect* bounds); + + // Calculates the new sizes of the attached windows, given that the main + // window has been resized (along the primary axis) by |delta|. + // |available_size| is the maximum length of the space that the attached + // windows are allowed to occupy (ie: the distance between the right/bottom + // edge of the primary window and the right/bottom of the desktop area). + // Populates |sizes| with the desired sizes of the attached windows, and + // returns the number of pixels that couldn't be allocated to the attached + // windows (due to min/max size constraints). + // Note the return value can be positive or negative, a negative value + // indicating that that many pixels couldn't be removed from the attached + // windows. + int CalculateAttachedSizes( + int delta, + int available_size, + std::vector<int>* sizes) const; + + // Divides |amount| evenly between |sizes|. If |amount| is negative it + // indicates how many pixels |sizes| should be shrunk by. + // Returns how many pixels failed to be allocated/removed from |sizes|. + int GrowFairly(int amount, std::vector<WindowSize>& sizes) const; + + // Calculate the ratio of pixels that each WindowSize in |sizes| should + // receive when growing or shrinking. + void CalculateGrowthRatios(const std::vector<WindowSize*>& sizes, + std::vector<float>* out_ratios) const; + + // Adds a WindowSize to |sizes| for each attached window. + void CreateBucketsForAttached(std::vector<WindowSize>* sizes) const; // If possible snaps the window to a neary window. Updates |bounds| if there // was a close enough window. @@ -177,21 +197,7 @@ class ASH_EXPORT WorkspaceWindowResizer : public WindowResizer { // primary axis. std::vector<int> initial_size_; - // The min size of each of the windows in |attached_windows_| along the - // primary axis. - std::vector<int> min_size_; - - // The amount each of the windows in |attached_windows_| can be compressed. - // This is a fraction of the amount a window can be compressed over the total - // space the windows can be compressed. - std::vector<float> compress_fraction_; - - // The amount each of the windows in |attached_windows_| should be expanded - // by. This is used when the user drags to the left/up. In this case the main - // window shrinks and the attached windows expand. - std::vector<float> expand_fraction_; - - // Sum of sizes in |min_size_|. + // Sum of the minimum sizes of the attached windows. int total_min_; // Sum of the sizes in |initial_size_|. diff --git a/ash/wm/workspace/workspace_window_resizer_unittest.cc b/ash/wm/workspace/workspace_window_resizer_unittest.cc index d8570ee..23ba208 100644 --- a/ash/wm/workspace/workspace_window_resizer_unittest.cc +++ b/ash/wm/workspace/workspace_window_resizer_unittest.cc @@ -97,12 +97,19 @@ class WorkspaceWindowResizerTest : public test::AshTestBase { window3_->Init(ui::LAYER_NOT_DRAWN); window3_->SetParent(NULL); window3_->set_id(3); + + window4_.reset(new aura::Window(&delegate4_)); + window4_->SetType(aura::client::WINDOW_TYPE_NORMAL); + window4_->Init(ui::LAYER_NOT_DRAWN); + window4_->SetParent(NULL); + window4_->set_id(4); } virtual void TearDown() OVERRIDE { window_.reset(); window2_.reset(); window3_.reset(); + window4_.reset(); AshTestBase::TearDown(); } @@ -144,9 +151,11 @@ class WorkspaceWindowResizerTest : public test::AshTestBase { TestWindowDelegate delegate_; TestWindowDelegate delegate2_; TestWindowDelegate delegate3_; + TestWindowDelegate delegate4_; scoped_ptr<aura::Window> window_; scoped_ptr<aura::Window> window2_; scoped_ptr<aura::Window> window3_; + scoped_ptr<aura::Window> window4_; private: DISALLOW_COPY_AND_ASSIGN(WorkspaceWindowResizerTest); @@ -416,8 +425,8 @@ TEST_F(WorkspaceWindowResizerTest, MAYBE_AttachedResize_BOTTOM_3) { // Move it 296 things should compress. resizer->Drag(CalculateDragPoint(*resizer, -10, 296), 0); EXPECT_EQ("300,100 300x496", window_->bounds().ToString()); - EXPECT_EQ("300,596 200x122", window2_->bounds().ToString()); - EXPECT_EQ("300,718 200x82", window3_->bounds().ToString()); + EXPECT_EQ("300,596 200x123", window2_->bounds().ToString()); + EXPECT_EQ("300,719 200x81", window3_->bounds().ToString()); // Move it so much everything ends up at its min. resizer->Drag(CalculateDragPoint(*resizer, 50, 798), 0); @@ -1523,5 +1532,229 @@ TEST_F(WorkspaceWindowResizerTest, PhantomSnapMaxSize) { } } +TEST_F(WorkspaceWindowResizerTest, DontRewardRightmostWindowForOverflows) { + aura::RootWindow* root = Shell::GetPrimaryRootWindow(); + root->SetHostSize(gfx::Size(600, 800)); + + Shell::GetInstance()->SetDisplayWorkAreaInsets(root, gfx::Insets()); + + // Four 100x100 windows flush against eachother, starting at 100,100. + window_->SetBounds(gfx::Rect( 100, 100, 100, 100)); + window2_->SetBounds(gfx::Rect(200, 100, 100, 100)); + window3_->SetBounds(gfx::Rect(300, 100, 100, 100)); + window4_->SetBounds(gfx::Rect(400, 100, 100, 100)); + delegate2_.set_max_size(gfx::Size(101, 0)); + + std::vector<aura::Window*> windows; + windows.push_back(window2_.get()); + windows.push_back(window3_.get()); + windows.push_back(window4_.get()); + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTRIGHT, windows)); + ASSERT_TRUE(resizer.get()); + // Move it 51 to the left, which should contract w1 and expand w2-4. + // w2 will hit its max size straight away, and in doing so will leave extra + // pixels that a naive implementation may award to the rightmost window. A + // fair implementation will give 25 pixels to each of the other windows. + resizer->Drag(CalculateDragPoint(*resizer, -51, 0), 0); + EXPECT_EQ("100,100 49x100", window_->bounds().ToString()); + EXPECT_EQ("149,100 101x100", window2_->bounds().ToString()); + EXPECT_EQ("250,100 125x100", window3_->bounds().ToString()); + EXPECT_EQ("375,100 125x100", window4_->bounds().ToString()); +} + +TEST_F(WorkspaceWindowResizerTest, DontExceedMaxWidth) { + aura::RootWindow* root = Shell::GetPrimaryRootWindow(); + root->SetHostSize(gfx::Size(600, 800)); + + Shell::GetInstance()->SetDisplayWorkAreaInsets(root, gfx::Insets()); + + // Four 100x100 windows flush against eachother, starting at 100,100. + window_->SetBounds(gfx::Rect( 100, 100, 100, 100)); + window2_->SetBounds(gfx::Rect(200, 100, 100, 100)); + window3_->SetBounds(gfx::Rect(300, 100, 100, 100)); + window4_->SetBounds(gfx::Rect(400, 100, 100, 100)); + delegate2_.set_max_size(gfx::Size(101, 0)); + delegate3_.set_max_size(gfx::Size(101, 0)); + + std::vector<aura::Window*> windows; + windows.push_back(window2_.get()); + windows.push_back(window3_.get()); + windows.push_back(window4_.get()); + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTRIGHT, windows)); + ASSERT_TRUE(resizer.get()); + // Move it 52 to the left, which should contract w1 and expand w2-4. + resizer->Drag(CalculateDragPoint(*resizer, -52, 0), 0); + EXPECT_EQ("100,100 48x100", window_->bounds().ToString()); + EXPECT_EQ("148,100 101x100", window2_->bounds().ToString()); + EXPECT_EQ("249,100 101x100", window3_->bounds().ToString()); + EXPECT_EQ("350,100 150x100", window4_->bounds().ToString()); +} + +TEST_F(WorkspaceWindowResizerTest, DontExceedMaxHeight) { + aura::RootWindow* root = Shell::GetPrimaryRootWindow(); + root->SetHostSize(gfx::Size(600, 800)); + + Shell::GetInstance()->SetDisplayWorkAreaInsets(root, gfx::Insets()); + + // Four 100x100 windows flush against eachother, starting at 100,100. + window_->SetBounds(gfx::Rect( 100, 100, 100, 100)); + window2_->SetBounds(gfx::Rect(100, 200, 100, 100)); + window3_->SetBounds(gfx::Rect(100, 300, 100, 100)); + window4_->SetBounds(gfx::Rect(100, 400, 100, 100)); + delegate2_.set_max_size(gfx::Size(0, 101)); + delegate3_.set_max_size(gfx::Size(0, 101)); + + std::vector<aura::Window*> windows; + windows.push_back(window2_.get()); + windows.push_back(window3_.get()); + windows.push_back(window4_.get()); + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTBOTTOM, windows)); + ASSERT_TRUE(resizer.get()); + // Move it 52 up, which should contract w1 and expand w2-4. + resizer->Drag(CalculateDragPoint(*resizer, 0, -52), 0); + EXPECT_EQ("100,100 100x48", window_->bounds().ToString()); + EXPECT_EQ("100,148 100x101", window2_->bounds().ToString()); + EXPECT_EQ("100,249 100x101", window3_->bounds().ToString()); + EXPECT_EQ("100,350 100x150", window4_->bounds().ToString()); +} + +TEST_F(WorkspaceWindowResizerTest, DontExceedMinHeight) { + aura::RootWindow* root = Shell::GetPrimaryRootWindow(); + root->SetHostSize(gfx::Size(600, 500)); + + Shell::GetInstance()->SetDisplayWorkAreaInsets(root, gfx::Insets()); + + // Four 100x100 windows flush against eachother, starting at 100,100. + window_->SetBounds(gfx::Rect( 100, 100, 100, 100)); + window2_->SetBounds(gfx::Rect(100, 200, 100, 100)); + window3_->SetBounds(gfx::Rect(100, 300, 100, 100)); + window4_->SetBounds(gfx::Rect(100, 400, 100, 100)); + delegate2_.set_min_size(gfx::Size(0, 99)); + delegate3_.set_min_size(gfx::Size(0, 99)); + + std::vector<aura::Window*> windows; + windows.push_back(window2_.get()); + windows.push_back(window3_.get()); + windows.push_back(window4_.get()); + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTBOTTOM, windows)); + ASSERT_TRUE(resizer.get()); + // Move it 52 down, which should expand w1 and contract w2-4. + resizer->Drag(CalculateDragPoint(*resizer, 0, 52), 0); + EXPECT_EQ("100,100 100x152", window_->bounds().ToString()); + EXPECT_EQ("100,252 100x99", window2_->bounds().ToString()); + EXPECT_EQ("100,351 100x99", window3_->bounds().ToString()); + EXPECT_EQ("100,450 100x50", window4_->bounds().ToString()); +} + +TEST_F(WorkspaceWindowResizerTest, DontExpandRightmostPastMaxWidth) { + aura::RootWindow* root = Shell::GetPrimaryRootWindow(); + root->SetHostSize(gfx::Size(600, 800)); + + Shell::GetInstance()->SetDisplayWorkAreaInsets(root, gfx::Insets()); + + // Three 100x100 windows flush against eachother, starting at 100,100. + window_->SetBounds(gfx::Rect( 100, 100, 100, 100)); + window2_->SetBounds(gfx::Rect(200, 100, 100, 100)); + window3_->SetBounds(gfx::Rect(300, 100, 100, 100)); + delegate3_.set_max_size(gfx::Size(101, 0)); + + std::vector<aura::Window*> windows; + windows.push_back(window2_.get()); + windows.push_back(window3_.get()); + windows.push_back(window4_.get()); + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTRIGHT, windows)); + ASSERT_TRUE(resizer.get()); + // Move it 51 to the left, which should contract w1 and expand w2-3. + resizer->Drag(CalculateDragPoint(*resizer, -51, 0), 0); + EXPECT_EQ("100,100 49x100", window_->bounds().ToString()); + EXPECT_EQ("149,100 150x100", window2_->bounds().ToString()); + EXPECT_EQ("299,100 101x100", window3_->bounds().ToString()); +} + +TEST_F(WorkspaceWindowResizerTest, DontContractMainIfAttachedAreStretched) { + aura::RootWindow* root = Shell::GetPrimaryRootWindow(); + root->SetHostSize(gfx::Size(600, 800)); + + Shell::GetInstance()->SetDisplayWorkAreaInsets(root, gfx::Insets()); + + // Three 100x100 windows flush against eachother, starting at 100,100. + window_->SetBounds(gfx::Rect( 100, 100, 100, 100)); + window2_->SetBounds(gfx::Rect(200, 100, 100, 100)); + window3_->SetBounds(gfx::Rect(300, 100, 100, 100)); + delegate2_.set_max_size(gfx::Size(101, 0)); + delegate3_.set_max_size(gfx::Size(101, 0)); + + std::vector<aura::Window*> windows; + windows.push_back(window2_.get()); + windows.push_back(window3_.get()); + windows.push_back(window4_.get()); + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTRIGHT, windows)); + ASSERT_TRUE(resizer.get()); + // Move it 52 to the left, which should contract w1 and expand w2-3. + resizer->Drag(CalculateDragPoint(*resizer, -52, 0), 0); + EXPECT_EQ("100,100 98x100", window_->bounds().ToString()); + EXPECT_EQ("198,100 101x100", window2_->bounds().ToString()); + EXPECT_EQ("299,100 101x100", window3_->bounds().ToString()); +} + +TEST_F(WorkspaceWindowResizerTest, MainWindowHonoursMaxWidth) { + aura::RootWindow* root = Shell::GetPrimaryRootWindow(); + root->SetHostSize(gfx::Size(400, 800)); + + Shell::GetInstance()->SetDisplayWorkAreaInsets(root, gfx::Insets()); + + // Three 100x100 windows flush against eachother, starting at 100,100. + window_->SetBounds(gfx::Rect( 100, 100, 100, 100)); + window2_->SetBounds(gfx::Rect(200, 100, 100, 100)); + window3_->SetBounds(gfx::Rect(300, 100, 100, 100)); + delegate_.set_max_size(gfx::Size(102, 0)); + + std::vector<aura::Window*> windows; + windows.push_back(window2_.get()); + windows.push_back(window3_.get()); + windows.push_back(window4_.get()); + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTRIGHT, windows)); + ASSERT_TRUE(resizer.get()); + // Move it 50 to the right, which should expand w1 and contract w2-3, as they + // won't fit in the root window in their original sizes. + resizer->Drag(CalculateDragPoint(*resizer, 50, 0), 0); + EXPECT_EQ("100,100 102x100", window_->bounds().ToString()); + EXPECT_EQ("202,100 99x100", window2_->bounds().ToString()); + EXPECT_EQ("301,100 99x100", window3_->bounds().ToString()); +} + +TEST_F(WorkspaceWindowResizerTest, MainWindowHonoursMinWidth) { + aura::RootWindow* root = Shell::GetPrimaryRootWindow(); + root->SetHostSize(gfx::Size(400, 800)); + + Shell::GetInstance()->SetDisplayWorkAreaInsets(root, gfx::Insets()); + + // Three 100x100 windows flush against eachother, starting at 100,100. + window_->SetBounds(gfx::Rect( 100, 100, 100, 100)); + window2_->SetBounds(gfx::Rect(200, 100, 100, 100)); + window3_->SetBounds(gfx::Rect(300, 100, 100, 100)); + delegate_.set_min_size(gfx::Size(98, 0)); + + std::vector<aura::Window*> windows; + windows.push_back(window2_.get()); + windows.push_back(window3_.get()); + windows.push_back(window4_.get()); + scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create( + window_.get(), gfx::Point(), HTRIGHT, windows)); + ASSERT_TRUE(resizer.get()); + // Move it 50 to the left, which should contract w1 and expand w2-3. + resizer->Drag(CalculateDragPoint(*resizer, -50, 0), 0); + EXPECT_EQ("100,100 98x100", window_->bounds().ToString()); + EXPECT_EQ("198,100 101x100", window2_->bounds().ToString()); + EXPECT_EQ("299,100 101x100", window3_->bounds().ToString()); +} + } // namespace internal } // namespace ash |