- add sources.
[platform/framework/web/crosswalk.git] / src / ash / wm / workspace / snap_sizer.cc
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.
4
5 #include "ash/wm/workspace/snap_sizer.h"
6
7 #include <cmath>
8
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"
17
18 namespace ash {
19 namespace internal {
20
21 namespace {
22
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 };
26
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;
37
38 // The maximum fraction of the screen width that a snapped window is allowed
39 // to take up.
40 const int kMaximumScreenPercent = 90;
41
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;
45
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;
50 }
51
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,
59                   GetMinWidth(window));
60 }
61
62 // Returns the width that |window| should be snapped to if resizing is disabled
63 // in the SnapSizer.
64 int GetDefaultWidth(aura::Window* window) {
65   gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window));
66
67   int width = 0;
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,
71     // and close).
72     width = work_area.width() / 2;
73   } else {
74     width = std::max(kDefaultWidthSmallScreen, work_area.width() / 2);
75   }
76
77   width = std::min(width, GetMaxWidth(window));
78   return std::max(width, GetMinWidth(window));
79 }
80
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));
92   }
93
94   int minimum_width = GetMinWidth(window);
95   int maximum_width = GetMaxWidth(window);
96
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)
100     half_width = 0;
101
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])
110         half_width = 0;
111       ideal_width_list.push_back(kIdealWidth[i]);
112     }
113   }
114   if (half_width)
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);
119     else
120       ideal_width_list.push_back(maximum_width);
121   }
122
123   return ideal_width_list;
124 }
125
126 // Changes |window|'s bounds to |snap_bounds| while preserving the restore
127 // bounds.
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);
133   } else {
134     window_state->SnapRight(snap_bounds);
135   }
136 }
137
138 }  // namespace
139
140 SnapSizer::SnapSizer(wm::WindowState* window_state,
141                      const gfx::Point& start,
142                      Edge edge,
143                      InputType input_type)
144     : window_state_(window_state),
145       edge_(edge),
146       time_last_update_(base::TimeTicks::Now()),
147       size_index_(0),
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()),
153       start_x_(start.x()),
154       input_type_(input_type),
155       usable_width_(BuildIdealWidthList(window_state->window())) {
156   DCHECK(!usable_width_.empty());
157   target_bounds_ = GetTargetBounds();
158 }
159
160 SnapSizer::~SnapSizer() {
161 }
162
163 void SnapSizer::SnapWindow(wm::WindowState* window_state,
164                            SnapSizer::Edge edge) {
165   if (!window_state->CanSnap())
166     return;
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()));
171 }
172
173 void SnapSizer::SnapWindowToTargetBounds() {
174   SnapWindowToBounds(window_state_, edge_, target_bounds());
175 }
176
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_));
184   } else {
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);
193       } else {
194         pixels_before_adjust =
195             std::min(pixels_before_adjust,
196                      (workspace_bounds.width() - start_x_) / 10);
197       }
198     }
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_));
203     }
204   }
205   last_update_x_ = location.x();
206   time_last_update_ = base::TimeTicks::Now();
207 }
208
209 gfx::Rect SnapSizer::GetSnapBounds(const gfx::Rect& bounds) {
210   int current = 0;
211   if (!resize_disabled_) {
212     for (current = usable_width_.size() - 1; current >= 0; current--) {
213       gfx::Rect target = GetTargetBoundsForSize(current);
214       if (target == bounds) {
215         ++current;
216         break;
217       }
218     }
219   }
220   if (current < 0)
221     current = 0;
222   return GetTargetBoundsForSize(current % usable_width_.size());
223 }
224
225 void SnapSizer::SelectDefaultSizeAndDisableResize() {
226   resize_disabled_ = true;
227   size_index_ = 0;
228   end_of_sequence_ = false;
229   target_bounds_ = GetTargetBounds();
230 }
231
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();
237   int width = 0;
238   if (resize_disabled_) {
239     width = GetDefaultWidth(window_state_->window());
240   } else {
241     DCHECK(size_index < usable_width_.size());
242     width = usable_width_[size_index];
243   }
244
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);
249   }
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);
253 }
254
255 int SnapSizer::CalculateIncrement(int x, int reference_x) const {
256   if (AlongEdge(x))
257     return 1;
258   if (x == reference_x)
259     return 0;
260   if (edge_ == LEFT_EDGE) {
261     if (x < reference_x)
262       return 1;
263     return -1;
264   }
265   // edge_ == RIGHT_EDGE.
266   if (x > reference_x)
267     return 1;
268   return -1;
269 }
270
271 void SnapSizer::ChangeBounds(int x, int delta) {
272   end_of_sequence_ =
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_) {
277     size_index_ = index;
278     target_bounds_ = GetTargetBounds();
279   }
280   num_moves_since_adjust_ = 0;
281   last_adjust_x_ = x;
282 }
283
284 gfx::Rect SnapSizer::GetTargetBounds() const {
285   return GetTargetBoundsForSize(size_index_);
286 }
287
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);
292 }
293
294 }  // namespace internal
295 }  // namespace ash