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
3 // found in the LICENSE file.
5 #include "ash/wm/workspace/snap_sizer.h"
9 #include "ash/ash_switches.h"
10 #include "ash/screen_ash.h"
11 #include "ash/wm/window_resizer.h"
12 #include "ash/wm/window_state.h"
13 #include "ash/wm/window_util.h"
14 #include "ui/aura/window.h"
15 #include "ui/aura/window_delegate.h"
16 #include "ui/gfx/screen.h"
23 // A list of ideal window widths in DIP which will be used to populate the
24 // |usable_width_| list.
25 const int kIdealWidth[] = { 1280, 1024, 768, 640 };
27 // Windows are initially snapped to the size in |usable_width_| at index 0.
28 // The index into |usable_width_| is changed if any of the following happen:
29 // . The user stops moving the mouse for |kDelayBeforeIncreaseMS| and then
30 // moves the mouse again.
31 // . The mouse moves |kPixelsBeforeAdjust| horizontal pixels.
32 // . The mouse is against the edge of the screen and the mouse is moved
33 // |kMovesBeforeAdjust| times.
34 const int kDelayBeforeIncreaseMS = 500;
35 const int kMovesBeforeAdjust = 25;
36 const int kPixelsBeforeAdjust = 100;
38 // The maximum fraction of the screen width that a snapped window is allowed
40 const int kMaximumScreenPercent = 90;
42 // The width that a window should be snapped to if resizing is disabled in the
43 // SnapSizer for devices with small screen resolutions.
44 const int kDefaultWidthSmallScreen = 1024;
46 // Returns the minimum width that |window| can be snapped to. The returned width
47 // may not be in the width list generated by BuildIdealWidthList().
48 int GetMinWidth(aura::Window* window) {
49 return window->delegate() ? window->delegate()->GetMinimumSize().width() : 0;
52 // Returns the maximum width that |window| can be snapped to. The returned width
53 // may not be in the width list generated by BuildIdealWidthList().
54 // The aura::WindowDelegate's max size is ignored because
55 // ash::wm::CanSnapWindow() returns false when a max size is specified.
56 int GetMaxWidth(aura::Window* window) {
57 gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window));
58 return std::max(work_area.width() * kMaximumScreenPercent / 100,
62 // Returns the width that |window| should be snapped to if resizing is disabled
64 int GetDefaultWidth(aura::Window* window) {
65 gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window));
68 if (ash::switches::UseAlternateFrameCaptionButtonStyle()) {
69 // Only the 'half of screen' width is supported when using the alternate
70 // visual style for the frame caption buttons (minimize, maximize, restore,
72 width = work_area.width() / 2;
74 width = std::max(kDefaultWidthSmallScreen, work_area.width() / 2);
77 width = std::min(width, GetMaxWidth(window));
78 return std::max(width, GetMinWidth(window));
81 // Creates the list of possible width for the current screen configuration:
82 // Returns a list with items from |kIdealWidth| which fit on the screen and
83 // supplement it with the 'half of screen' size. Furthermore, add an entry for
84 // 90% of the screen size if it is smaller than the biggest value in the
85 // |kIdealWidth| list (to get a step between the values).
86 std::vector<int> BuildIdealWidthList(aura::Window* window) {
87 if (ash::switches::UseAlternateFrameCaptionButtonStyle()) {
88 // Only the 'half of screen' width is supported when using the alternate
89 // visual style for the frame caption buttons (minimize, maximize,
90 // restore, and close).
91 return std::vector<int>(1u, GetDefaultWidth(window));
94 int minimum_width = GetMinWidth(window);
95 int maximum_width = GetMaxWidth(window);
97 gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window));
98 int half_width = work_area.width() / 2;
99 if (half_width < minimum_width || half_width > maximum_width)
102 std::vector<int> ideal_width_list;
103 for (size_t i = 0; i < arraysize(kIdealWidth); i++) {
104 if (kIdealWidth[i] >= minimum_width && kIdealWidth[i] <= maximum_width) {
105 if (i && !ideal_width_list.size() && maximum_width != kIdealWidth[i])
106 ideal_width_list.push_back(maximum_width);
107 if (half_width > kIdealWidth[i])
108 ideal_width_list.push_back(half_width);
109 if (half_width >= kIdealWidth[i])
111 ideal_width_list.push_back(kIdealWidth[i]);
115 ideal_width_list.push_back(half_width);
116 if (ideal_width_list.empty()) {
117 if (minimum_width > 0)
118 ideal_width_list.push_back(minimum_width);
120 ideal_width_list.push_back(maximum_width);
123 return ideal_width_list;
126 // Changes |window|'s bounds to |snap_bounds| while preserving the restore
128 void SnapWindowToBounds(wm::WindowState* window_state,
129 SnapSizer::Edge edge,
130 const gfx::Rect& snap_bounds) {
131 if (edge == SnapSizer::LEFT_EDGE) {
132 window_state->SnapLeft(snap_bounds);
134 window_state->SnapRight(snap_bounds);
140 SnapSizer::SnapSizer(wm::WindowState* window_state,
141 const gfx::Point& start,
143 InputType input_type)
144 : window_state_(window_state),
146 time_last_update_(base::TimeTicks::Now()),
148 end_of_sequence_(false),
149 resize_disabled_(false),
150 num_moves_since_adjust_(0),
151 last_adjust_x_(start.x()),
152 last_update_x_(start.x()),
154 input_type_(input_type),
155 usable_width_(BuildIdealWidthList(window_state->window())) {
156 DCHECK(!usable_width_.empty());
157 target_bounds_ = GetTargetBounds();
160 SnapSizer::~SnapSizer() {
163 void SnapSizer::SnapWindow(wm::WindowState* window_state,
164 SnapSizer::Edge edge) {
165 if (!window_state->CanSnap())
167 internal::SnapSizer sizer(window_state, gfx::Point(), edge,
168 internal::SnapSizer::OTHER_INPUT);
169 SnapWindowToBounds(window_state, edge,
170 sizer.GetSnapBounds(window_state->window()->bounds()));
173 void SnapSizer::SnapWindowToTargetBounds() {
174 SnapWindowToBounds(window_state_, edge_, target_bounds());
177 void SnapSizer::Update(const gfx::Point& location) {
178 // See description above for details on this behavior.
179 num_moves_since_adjust_++;
180 if ((base::TimeTicks::Now() - time_last_update_).InMilliseconds() >
181 kDelayBeforeIncreaseMS) {
182 ChangeBounds(location.x(),
183 CalculateIncrement(location.x(), last_update_x_));
185 bool along_edge = AlongEdge(location.x());
186 int pixels_before_adjust = kPixelsBeforeAdjust;
187 if (input_type_ == TOUCH_MAXIMIZE_BUTTON_INPUT) {
188 const gfx::Rect& workspace_bounds =
189 window_state_->window()->parent()->bounds();
190 if (start_x_ > location.x()) {
191 pixels_before_adjust =
192 std::min(pixels_before_adjust, start_x_ / 10);
194 pixels_before_adjust =
195 std::min(pixels_before_adjust,
196 (workspace_bounds.width() - start_x_) / 10);
199 if (std::abs(location.x() - last_adjust_x_) >= pixels_before_adjust ||
200 (along_edge && num_moves_since_adjust_ >= kMovesBeforeAdjust)) {
201 ChangeBounds(location.x(),
202 CalculateIncrement(location.x(), last_adjust_x_));
205 last_update_x_ = location.x();
206 time_last_update_ = base::TimeTicks::Now();
209 gfx::Rect SnapSizer::GetSnapBounds(const gfx::Rect& bounds) {
211 if (!resize_disabled_) {
212 for (current = usable_width_.size() - 1; current >= 0; current--) {
213 gfx::Rect target = GetTargetBoundsForSize(current);
214 if (target == bounds) {
222 return GetTargetBoundsForSize(current % usable_width_.size());
225 void SnapSizer::SelectDefaultSizeAndDisableResize() {
226 resize_disabled_ = true;
228 end_of_sequence_ = false;
229 target_bounds_ = GetTargetBounds();
232 gfx::Rect SnapSizer::GetTargetBoundsForSize(size_t size_index) const {
233 gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(
234 window_state_->window()));
235 int y = work_area.y();
236 int max_y = work_area.bottom();
238 if (resize_disabled_) {
239 width = GetDefaultWidth(window_state_->window());
241 DCHECK(size_index < usable_width_.size());
242 width = usable_width_[size_index];
245 if (edge_ == LEFT_EDGE) {
246 int x = work_area.x();
247 int mid_x = x + width;
248 return gfx::Rect(x, y, mid_x - x, max_y - y);
250 int max_x = work_area.right();
251 int x = max_x - width;
252 return gfx::Rect(x , y, max_x - x, max_y - y);
255 int SnapSizer::CalculateIncrement(int x, int reference_x) const {
258 if (x == reference_x)
260 if (edge_ == LEFT_EDGE) {
265 // edge_ == RIGHT_EDGE.
271 void SnapSizer::ChangeBounds(int x, int delta) {
273 delta > 0 && size_index_ == static_cast<int>(usable_width_.size()) - 1;
274 int index = std::min(static_cast<int>(usable_width_.size()) - 1,
275 std::max(size_index_ + delta, 0));
276 if (index != size_index_) {
278 target_bounds_ = GetTargetBounds();
280 num_moves_since_adjust_ = 0;
284 gfx::Rect SnapSizer::GetTargetBounds() const {
285 return GetTargetBoundsForSize(size_index_);
288 bool SnapSizer::AlongEdge(int x) const {
289 gfx::Rect area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(
290 window_state_->window()));
291 return (x <= area.x()) || (x >= area.right() - 1);
294 } // namespace internal