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; |
pkotwicz
2013/08/30 21:10:57
- I now make the side maximized window take the wi
| |
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()); | |
pkotwicz
2013/08/30 21:10:57
With the new caption button style we are removing
| |
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) { |
93 return; | 103 return CanSideMaximizeWindow(window); |
94 internal::SnapSizer sizer(window, gfx::Point(), edge, | 104 } |
95 internal::SnapSizer::OTHER_INPUT); | 105 |
96 if (wm::IsWindowFullscreen(window) || wm::IsWindowMaximized(window)) { | 106 // static |
97 // Before we can set the bounds we need to restore the window. | 107 void SnapSizer::SnapWindow(aura::Window* window, |
98 // Restoring the window will set the window to its restored bounds. | 108 Edge edge, |
99 // To avoid an unnecessary bounds changes (which may have side effects) | 109 StepBehavior step_behavior) { |
100 // we set the restore bounds to the bounds we want, restore the window, | 110 SnapSizer(window, gfx::Point(), edge, step_behavior).Snap(); |
101 // then reset the restore bounds. This way no unnecessary bounds | |
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 } | 111 } |
111 | 112 |
112 void SnapSizer::Update(const gfx::Point& location) { | 113 void SnapSizer::Update(const gfx::Point& location) { |
113 // See description above for details on this behavior. | 114 // See description above for details on this behavior. |
114 num_moves_since_adjust_++; | 115 num_moves_since_adjust_++; |
115 if ((base::TimeTicks::Now() - time_last_update_).InMilliseconds() > | 116 if ((base::TimeTicks::Now() - time_last_update_).InMilliseconds() > |
116 kDelayBeforeIncreaseMS) { | 117 kDelayBeforeIncreaseMS) { |
117 ChangeBounds(location.x(), | 118 IncrementState(CalculateIncrement(location.x(), last_update_x_)); |
118 CalculateIncrement(location.x(), last_update_x_)); | 119 num_moves_since_adjust_ = 0; |
120 last_adjust_x_ = location.x(); | |
119 } else { | 121 } else { |
120 bool along_edge = AlongEdge(location.x()); | 122 bool along_edge = AlongEdge(location.x()); |
121 int pixels_before_adjust = kPixelsBeforeAdjust; | 123 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)) { | 124 (along_edge && num_moves_since_adjust_ >= kMovesBeforeAdjust)) { |
135 ChangeBounds(location.x(), | 125 IncrementState(CalculateIncrement(location.x(), last_adjust_x_)); |
136 CalculateIncrement(location.x(), last_adjust_x_)); | 126 num_moves_since_adjust_ = 0; |
127 last_adjust_x_ = location.x(); | |
137 } | 128 } |
138 } | 129 } |
139 last_update_x_ = location.x(); | 130 last_update_x_ = location.x(); |
140 time_last_update_ = base::TimeTicks::Now(); | 131 time_last_update_ = base::TimeTicks::Now(); |
141 } | 132 } |
142 | 133 |
143 gfx::Rect SnapSizer::GetSnapBounds(const gfx::Rect& bounds) { | 134 // static |
144 int current = 0; | 135 std::vector<SnapSizer::State> SnapSizer::BuildValidStatesList( |
145 if (!resize_disabled_) { | 136 aura::Window* window, |
146 for (current = usable_width_.size() - 1; current >= 0; current--) { | 137 StepBehavior step_behavior) { |
147 gfx::Rect target = GetTargetBoundsForSize(current); | 138 // If |step_behavior| == STEP_NO, the size of the returned list should be |
148 if (target == bounds) { | 139 // capped to a size of 1. |
149 ++current; | 140 std::vector<SnapSizer::State> states; |
150 break; | 141 DCHECK(CanSideMaximizeWindow(window)); |
151 } | 142 states.push_back(SIDE_MAXIMIZE); |
152 } | 143 return states; |
153 } | |
154 return GetTargetBoundsForSize(current % usable_width_.size()); | |
155 } | 144 } |
156 | 145 |
157 void SnapSizer::SelectDefaultSizeAndDisableResize() { | 146 void SnapSizer::Snap() { |
158 resize_disabled_ = true; | 147 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 } | 148 } |
189 | 149 |
190 int SnapSizer::CalculateIncrement(int x, int reference_x) const { | 150 int SnapSizer::CalculateIncrement(int x, int reference_x) const { |
191 if (AlongEdge(x)) | 151 if (AlongEdge(x)) |
192 return 1; | 152 return 1; |
193 if (x == reference_x) | 153 if (x == reference_x) |
194 return 0; | 154 return 0; |
195 if (edge_ == LEFT_EDGE) { | 155 if (edge_ == LEFT_EDGE) { |
196 if (x < reference_x) | 156 if (x < reference_x) |
197 return 1; | 157 return 1; |
198 return -1; | 158 return -1; |
199 } | 159 } |
200 // edge_ == RIGHT_EDGE. | 160 // edge_ == RIGHT_EDGE. |
201 if (x > reference_x) | 161 if (x > reference_x) |
202 return 1; | 162 return 1; |
203 return -1; | 163 return -1; |
204 } | 164 } |
205 | 165 |
206 void SnapSizer::ChangeBounds(int x, int delta) { | 166 gfx::Rect SnapSizer::GetTargetBounds() const { |
207 int index = std::min(static_cast<int>(usable_width_.size()) - 1, | 167 State state = states_[state_index_]; |
208 std::max(size_index_ + delta, 0)); | 168 DCHECK_EQ(SIDE_MAXIMIZE, state); |
209 if (index != size_index_) { | 169 return GetSideMaximizedWindowBoundsInParent(window_, edge_); |
210 size_index_ = index; | 170 } |
171 | |
172 void SnapSizer::IncrementState(int delta) { | |
173 int new_index = state_index_ + delta; | |
174 if (new_index >= 0 && | |
175 new_index < static_cast<int>(states_.size()) && | |
176 new_index != state_index_) { | |
177 state_index_ = new_index; | |
211 target_bounds_ = GetTargetBounds(); | 178 target_bounds_ = GetTargetBounds(); |
212 } | 179 } |
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 } | 180 } |
220 | 181 |
221 bool SnapSizer::AlongEdge(int x) const { | 182 bool SnapSizer::AlongEdge(int x) const { |
222 gfx::Rect area(ScreenAsh::GetDisplayBoundsInParent(window_)); | 183 gfx::Rect area(ScreenAsh::GetDisplayBoundsInParent(window_)); |
223 return (x <= area.x()) || (x >= area.right() - 1); | 184 return (x <= area.x()) || (x >= area.right() - 1); |
224 } | 185 } |
225 | 186 |
226 } // namespace internal | 187 } // namespace internal |
227 } // namespace ash | 188 } // namespace ash |
OLD | NEW |