| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ash/wm/workspace/snap_sizer.h" | 5 #include "ash/wm/workspace/snap_sizer.h" |
| 6 | 6 |
| 7 #include <cmath> | 7 #include <cmath> |
| 8 | 8 |
| 9 #include "ash/screen_ash.h" | 9 #include "ash/screen_ash.h" |
| 10 #include "ash/wm/property_util.h" | 10 #include "ash/wm/property_util.h" |
| 11 #include "ash/wm/window_resizer.h" | 11 #include "ash/wm/window_resizer.h" |
| 12 #include "ash/wm/window_util.h" | 12 #include "ash/wm/window_util.h" |
| 13 #include "ui/aura/window.h" | 13 #include "ui/aura/window.h" |
| 14 #include "ui/aura/window_delegate.h" |
| 14 #include "ui/gfx/screen.h" | 15 #include "ui/gfx/screen.h" |
| 15 | 16 |
| 16 namespace ash { | 17 namespace ash { |
| 17 namespace internal { | 18 namespace internal { |
| 18 | 19 |
| 19 namespace { | 20 namespace { |
| 20 | 21 |
| 21 // A list of ideal window width in pixel which will be used to populate the | 22 // Windows are initially snapped to the bounds for the state at index 0 in |
| 22 // |usable_width_| list. | 23 // |states_|. |
| 23 const int kIdealWidth[] = { 1280, 1024, 768, 640 }; | 24 // The index into |states_| is changed if any of the following happen: |
| 24 | |
| 25 // Windows are initially snapped to the size in |usable_width_| at index 0. | |
| 26 // The index into |usable_width_| is changed if any of the following happen: | |
| 27 // . The user stops moving the mouse for |kDelayBeforeIncreaseMS| and then | 25 // . The user stops moving the mouse for |kDelayBeforeIncreaseMS| and then |
| 28 // moves the mouse again. | 26 // moves the mouse again. |
| 29 // . The mouse moves |kPixelsBeforeAdjust| horizontal pixels. | 27 // . The mouse moves |kPixelsBeforeAdjust| horizontal pixels. |
| 30 // . The mouse is against the edge of the screen and the mouse is moved | 28 // . The mouse is against the edge of the screen and the mouse is moved |
| 31 // |kMovesBeforeAdjust| times. | 29 // |kMovesBeforeAdjust| times. |
| 32 const int kDelayBeforeIncreaseMS = 500; | 30 const int kDelayBeforeIncreaseMS = 500; |
| 33 const int kMovesBeforeAdjust = 25; | 31 const int kMovesBeforeAdjust = 25; |
| 34 const int kPixelsBeforeAdjust = 100; | 32 const int kPixelsBeforeAdjust = 100; |
| 35 | 33 |
| 36 // When the smallest resolution does not fit on the screen, we take this | 34 // The percent of the screen's width that a side maximized window takes up. |
| 37 // fraction of the available space. | 35 const int kSideMaximizedScreenWidthPercent = 50; |
| 38 const int kMinimumScreenPercent = 90; | |
| 39 | 36 |
| 40 // Create the list of possible width for the current screen configuration: | 37 bool CanSideMaximizeWindow(aura::Window* window) { |
| 41 // Fill the |usable_width_| list with items from |kIdealWidth| which fit on | 38 if (!wm::CanResizeWindow(window)) |
| 42 // the screen and supplement it with the 'half of screen' size. Furthermore, | 39 return false; |
| 43 // add an entry for 90% of the screen size if it is smaller then the biggest | 40 // If a window has a maximum size defined, snapping may make it too big. |
| 44 // value in the |kIdealWidth| list (to get a step between the values). | 41 return window->delegate() ? |
| 45 std::vector<int> BuildIdealWidthList(aura::Window* window) { | 42 window->delegate()->GetMaximumSize().IsEmpty() : true; |
| 46 std::vector<int> ideal_width_list; | 43 } |
| 44 |
| 45 gfx::Rect GetSideMaximizedWindowBoundsInParent(aura::Window* window, |
| 46 SnapSizer::Edge edge) { |
| 47 gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window)); | 47 gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window)); |
| 48 int half_size = work_area.width() / 2; | 48 int width = work_area.width() * kSideMaximizedScreenWidthPercent / 100; |
| 49 int maximum_width = (kMinimumScreenPercent * work_area.width()) / 100; | 49 if (window->delegate()) |
| 50 for (size_t i = 0; i < arraysize(kIdealWidth); i++) { | 50 width = std::max(width, window->delegate()->GetMinimumSize().width()); |
| 51 if (maximum_width >= kIdealWidth[i]) { | 51 int x = (edge == SnapSizer::LEFT_EDGE) ? |
| 52 if (i && !ideal_width_list.size() && maximum_width != kIdealWidth[i]) | 52 work_area.x() : work_area.right() - width; |
| 53 ideal_width_list.push_back(maximum_width); | 53 return gfx::Rect(x, work_area.y(), width, work_area.height()); |
| 54 if (half_size > kIdealWidth[i]) | 54 } |
| 55 ideal_width_list.push_back(half_size); | 55 |
| 56 if (half_size >= kIdealWidth[i]) | 56 void SideMaximizeWindow(aura::Window* window, SnapSizer::Edge edge) { |
| 57 half_size = 0; | 57 gfx::Rect new_bounds(GetSideMaximizedWindowBoundsInParent(window, edge)); |
| 58 ideal_width_list.push_back(kIdealWidth[i]); | 58 |
| 59 } | 59 if (wm::IsWindowFullscreen(window) || wm::IsWindowMaximized(window)) { |
| 60 // Before we can set the bounds we need to restore the window. |
| 61 // Restoring the window will set the window to its restored bounds. |
| 62 // To avoid an unnecessary bounds changes (which may have side effects) |
| 63 // we set the restore bounds to the bounds we want, restore the window, |
| 64 // then reset the restore bounds. This way no unnecessary bounds |
| 65 // changes occurs and the original restore bounds is remembered. |
| 66 gfx::Rect restore = *GetRestoreBoundsInScreen(window); |
| 67 SetRestoreBoundsInParent(window, new_bounds); |
| 68 wm::RestoreWindow(window); |
| 69 SetRestoreBoundsInScreen(window, restore); |
| 70 } else { |
| 71 // Others might have set up a restore rectangle already. If so, we should |
| 72 // not overwrite the restore rectangle. |
| 73 if (GetRestoreBoundsInScreen(window) == NULL) |
| 74 SetRestoreBoundsInScreen(window, window->GetBoundsInScreen()); |
| 75 window->SetBounds(new_bounds); |
| 60 } | 76 } |
| 61 if (half_size) | |
| 62 ideal_width_list.push_back(half_size); | |
| 63 | |
| 64 return ideal_width_list; | |
| 65 } | 77 } |
| 66 | 78 |
| 67 } // namespace | 79 } // namespace |
| 68 | 80 |
| 69 SnapSizer::SnapSizer(aura::Window* window, | 81 SnapSizer::SnapSizer(aura::Window* window, |
| 70 const gfx::Point& start, | 82 const gfx::Point& start, |
| 71 Edge edge, | 83 Edge edge, |
| 72 InputType input_type) | 84 StepBehavior step_behavior) |
| 73 : window_(window), | 85 : window_(window), |
| 74 edge_(edge), | 86 edge_(edge), |
| 75 time_last_update_(base::TimeTicks::Now()), | 87 time_last_update_(base::TimeTicks::Now()), |
| 76 size_index_(0), | |
| 77 resize_disabled_(false), | |
| 78 num_moves_since_adjust_(0), | 88 num_moves_since_adjust_(0), |
| 79 last_adjust_x_(start.x()), | 89 last_adjust_x_(start.x()), |
| 80 last_update_x_(start.x()), | 90 last_update_x_(start.x()), |
| 81 start_x_(start.x()), | 91 start_x_(start.x()), |
| 82 input_type_(input_type), | 92 state_index_(0), |
| 83 usable_width_(BuildIdealWidthList(window)) { | 93 states_(BuildValidStatesList(window, step_behavior)) { |
| 84 DCHECK(!usable_width_.empty()); | 94 DCHECK(!states_.empty()); |
| 85 target_bounds_ = GetTargetBounds(); | 95 target_bounds_ = GetTargetBounds(); |
| 86 } | 96 } |
| 87 | 97 |
| 88 SnapSizer::~SnapSizer() { | 98 SnapSizer::~SnapSizer() { |
| 89 } | 99 } |
| 90 | 100 |
| 91 void SnapSizer::SnapWindow(aura::Window* window, SnapSizer::Edge edge) { | 101 // static |
| 92 if (!wm::CanSnapWindow(window)) | 102 bool SnapSizer::CanSnapWindow(aura::Window* window, Edge edge) { |
| 93 return; | 103 // TODO(varkha): Use |edge| to determine whether |window| can be docked. |
| 94 internal::SnapSizer sizer(window, gfx::Point(), edge, | 104 return CanSideMaximizeWindow(window); |
| 95 internal::SnapSizer::OTHER_INPUT); | 105 } |
| 96 if (wm::IsWindowFullscreen(window) || wm::IsWindowMaximized(window)) { | 106 |
| 97 // Before we can set the bounds we need to restore the window. | 107 // static |
| 98 // Restoring the window will set the window to its restored bounds. | 108 void SnapSizer::SnapWindow(aura::Window* window, |
| 99 // To avoid an unnecessary bounds changes (which may have side effects) | 109 Edge edge, |
| 100 // we set the restore bounds to the bounds we want, restore the window, | 110 StepBehavior step_behavior) { |
| 101 // then reset the restore bounds. This way no unnecessary bounds | 111 SnapSizer(window, gfx::Point(), edge, step_behavior).Snap(); |
| 102 // changes occurs and the original restore bounds is remembered. | |
| 103 gfx::Rect restore = *GetRestoreBoundsInScreen(window); | |
| 104 SetRestoreBoundsInParent(window, sizer.GetSnapBounds(window->bounds())); | |
| 105 wm::RestoreWindow(window); | |
| 106 SetRestoreBoundsInScreen(window, restore); | |
| 107 } else { | |
| 108 window->SetBounds(sizer.GetSnapBounds(window->bounds())); | |
| 109 } | |
| 110 } | 112 } |
| 111 | 113 |
| 112 void SnapSizer::Update(const gfx::Point& location) { | 114 void SnapSizer::Update(const gfx::Point& location) { |
| 113 // See description above for details on this behavior. | 115 // See description above for details on this behavior. |
| 114 num_moves_since_adjust_++; | 116 num_moves_since_adjust_++; |
| 115 if ((base::TimeTicks::Now() - time_last_update_).InMilliseconds() > | 117 if ((base::TimeTicks::Now() - time_last_update_).InMilliseconds() > |
| 116 kDelayBeforeIncreaseMS) { | 118 kDelayBeforeIncreaseMS) { |
| 117 ChangeBounds(location.x(), | 119 IncrementState(CalculateIncrement(location.x(), last_update_x_)); |
| 118 CalculateIncrement(location.x(), last_update_x_)); | 120 num_moves_since_adjust_ = 0; |
| 121 last_adjust_x_ = location.x(); |
| 119 } else { | 122 } else { |
| 120 bool along_edge = AlongEdge(location.x()); | 123 bool along_edge = AlongEdge(location.x()); |
| 121 int pixels_before_adjust = kPixelsBeforeAdjust; | 124 if (std::abs(location.x() - last_adjust_x_) >= kPixelsBeforeAdjust || |
| 122 if (input_type_ == TOUCH_MAXIMIZE_BUTTON_INPUT) { | |
| 123 const gfx::Rect& workspace_bounds = window_->parent()->bounds(); | |
| 124 if (start_x_ > location.x()) { | |
| 125 pixels_before_adjust = | |
| 126 std::min(pixels_before_adjust, start_x_ / 10); | |
| 127 } else { | |
| 128 pixels_before_adjust = | |
| 129 std::min(pixels_before_adjust, | |
| 130 (workspace_bounds.width() - start_x_) / 10); | |
| 131 } | |
| 132 } | |
| 133 if (std::abs(location.x() - last_adjust_x_) >= pixels_before_adjust || | |
| 134 (along_edge && num_moves_since_adjust_ >= kMovesBeforeAdjust)) { | 125 (along_edge && num_moves_since_adjust_ >= kMovesBeforeAdjust)) { |
| 135 ChangeBounds(location.x(), | 126 IncrementState(CalculateIncrement(location.x(), last_adjust_x_)); |
| 136 CalculateIncrement(location.x(), last_adjust_x_)); | 127 num_moves_since_adjust_ = 0; |
| 128 last_adjust_x_ = location.x(); |
| 137 } | 129 } |
| 138 } | 130 } |
| 139 last_update_x_ = location.x(); | 131 last_update_x_ = location.x(); |
| 140 time_last_update_ = base::TimeTicks::Now(); | 132 time_last_update_ = base::TimeTicks::Now(); |
| 141 } | 133 } |
| 142 | 134 |
| 143 gfx::Rect SnapSizer::GetSnapBounds(const gfx::Rect& bounds) { | 135 // static |
| 144 int current = 0; | 136 std::vector<SnapSizer::State> SnapSizer::BuildValidStatesList( |
| 145 if (!resize_disabled_) { | 137 aura::Window* window, |
| 146 for (current = usable_width_.size() - 1; current >= 0; current--) { | 138 StepBehavior step_behavior) { |
| 147 gfx::Rect target = GetTargetBoundsForSize(current); | 139 // If |step_behavior| == STEP_NO, the size of the returned list should be |
| 148 if (target == bounds) { | 140 // capped to a size of 1. |
| 149 ++current; | 141 std::vector<SnapSizer::State> states; |
| 150 break; | 142 DCHECK(CanSideMaximizeWindow(window)); |
| 151 } | 143 states.push_back(SIDE_MAXIMIZE); |
| 152 } | 144 return states; |
| 153 } | |
| 154 return GetTargetBoundsForSize(current % usable_width_.size()); | |
| 155 } | 145 } |
| 156 | 146 |
| 157 void SnapSizer::SelectDefaultSizeAndDisableResize() { | 147 void SnapSizer::Snap() { |
| 158 resize_disabled_ = true; | 148 SideMaximizeWindow(window_, edge_); |
| 159 size_index_ = 0; | |
| 160 target_bounds_ = GetTargetBounds(); | |
| 161 } | |
| 162 | |
| 163 gfx::Rect SnapSizer::GetTargetBoundsForSize(size_t size_index) const { | |
| 164 gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window_)); | |
| 165 int y = work_area.y(); | |
| 166 // We don't align to the bottom of the grid as the launcher may not | |
| 167 // necessarily align to the grid (happens when auto-hidden). | |
| 168 int max_y = work_area.bottom(); | |
| 169 int width = 0; | |
| 170 if (resize_disabled_) { | |
| 171 // Make sure that we keep the size of the window smaller then a certain | |
| 172 // fraction of the screen space. | |
| 173 int minimum_size = (kMinimumScreenPercent * work_area.width()) / 100; | |
| 174 width = std::max(std::min(minimum_size, 1024), work_area.width() / 2); | |
| 175 } else { | |
| 176 DCHECK(size_index < usable_width_.size()); | |
| 177 width = usable_width_[size_index]; | |
| 178 } | |
| 179 | |
| 180 if (edge_ == LEFT_EDGE) { | |
| 181 int x = work_area.x(); | |
| 182 int mid_x = x + width; | |
| 183 return gfx::Rect(x, y, mid_x - x, max_y - y); | |
| 184 } | |
| 185 int max_x = work_area.right(); | |
| 186 int x = max_x - width; | |
| 187 return gfx::Rect(x , y, max_x - x, max_y - y); | |
| 188 } | 149 } |
| 189 | 150 |
| 190 int SnapSizer::CalculateIncrement(int x, int reference_x) const { | 151 int SnapSizer::CalculateIncrement(int x, int reference_x) const { |
| 191 if (AlongEdge(x)) | 152 if (AlongEdge(x)) |
| 192 return 1; | 153 return 1; |
| 193 if (x == reference_x) | 154 if (x == reference_x) |
| 194 return 0; | 155 return 0; |
| 195 if (edge_ == LEFT_EDGE) { | 156 if (edge_ == LEFT_EDGE) { |
| 196 if (x < reference_x) | 157 if (x < reference_x) |
| 197 return 1; | 158 return 1; |
| 198 return -1; | 159 return -1; |
| 199 } | 160 } |
| 200 // edge_ == RIGHT_EDGE. | 161 // edge_ == RIGHT_EDGE. |
| 201 if (x > reference_x) | 162 if (x > reference_x) |
| 202 return 1; | 163 return 1; |
| 203 return -1; | 164 return -1; |
| 204 } | 165 } |
| 205 | 166 |
| 206 void SnapSizer::ChangeBounds(int x, int delta) { | 167 gfx::Rect SnapSizer::GetTargetBounds() const { |
| 207 int index = std::min(static_cast<int>(usable_width_.size()) - 1, | 168 State state = states_[state_index_]; |
| 208 std::max(size_index_ + delta, 0)); | 169 DCHECK_EQ(SIDE_MAXIMIZE, state); |
| 209 if (index != size_index_) { | 170 return GetSideMaximizedWindowBoundsInParent(window_, edge_); |
| 210 size_index_ = index; | 171 } |
| 172 |
| 173 void SnapSizer::IncrementState(int delta) { |
| 174 int new_index = state_index_ + delta; |
| 175 if (new_index >= 0 && |
| 176 new_index < static_cast<int>(states_.size()) && |
| 177 new_index != state_index_) { |
| 178 state_index_ = new_index; |
| 211 target_bounds_ = GetTargetBounds(); | 179 target_bounds_ = GetTargetBounds(); |
| 212 } | 180 } |
| 213 num_moves_since_adjust_ = 0; | |
| 214 last_adjust_x_ = x; | |
| 215 } | |
| 216 | |
| 217 gfx::Rect SnapSizer::GetTargetBounds() const { | |
| 218 return GetTargetBoundsForSize(size_index_); | |
| 219 } | 181 } |
| 220 | 182 |
| 221 bool SnapSizer::AlongEdge(int x) const { | 183 bool SnapSizer::AlongEdge(int x) const { |
| 222 gfx::Rect area(ScreenAsh::GetDisplayBoundsInParent(window_)); | 184 gfx::Rect area(ScreenAsh::GetDisplayBoundsInParent(window_)); |
| 223 return (x <= area.x()) || (x >= area.right() - 1); | 185 return (x <= area.x()) || (x >= area.right() - 1); |
| 224 } | 186 } |
| 225 | 187 |
| 226 } // namespace internal | 188 } // namespace internal |
| 227 } // namespace ash | 189 } // namespace ash |
| OLD | NEW |