• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/command_line.h"
15 #include "ui/aura/window.h"
16 #include "ui/aura/window_delegate.h"
17 #include "ui/gfx/screen.h"
18 
19 namespace ash {
20 namespace internal {
21 
22 namespace {
23 
24 // A list of ideal window widths in DIP which will be used to populate the
25 // |usable_width_| list.
26 const int kIdealWidth[] = { 1280, 1024, 768, 640 };
27 
28 // Windows are initially snapped to the size in |usable_width_| at index 0.
29 // The index into |usable_width_| is changed if any of the following happen:
30 // . The user stops moving the mouse for |kDelayBeforeIncreaseMS| and then
31 //   moves the mouse again.
32 // . The mouse moves |kPixelsBeforeAdjust| horizontal pixels.
33 // . The mouse is against the edge of the screen and the mouse is moved
34 //   |kMovesBeforeAdjust| times.
35 const int kDelayBeforeIncreaseMS = 500;
36 const int kMovesBeforeAdjust = 25;
37 const int kPixelsBeforeAdjust = 100;
38 
39 // The maximum fraction of the screen width that a snapped window is allowed
40 // to take up.
41 const int kMaximumScreenPercent = 90;
42 
43 // The width that a window should be snapped to if resizing is disabled in the
44 // SnapSizer for devices with small screen resolutions.
45 const int kDefaultWidthSmallScreen = 1024;
46 
47 // Returns the minimum width that |window| can be snapped to. The returned width
48 // may not be in the width list generated by BuildIdealWidthList().
GetMinWidth(aura::Window * window)49 int GetMinWidth(aura::Window* window) {
50   return window->delegate() ? window->delegate()->GetMinimumSize().width() : 0;
51 }
52 
53 // Returns the maximum width that |window| can be snapped to. The returned width
54 // may not be in the width list generated by BuildIdealWidthList().
55 // The aura::WindowDelegate's max size is ignored because
56 // ash::wm::CanSnapWindow() returns false when a max size is specified.
GetMaxWidth(aura::Window * window)57 int GetMaxWidth(aura::Window* window) {
58   gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window));
59   return std::max(work_area.width() * kMaximumScreenPercent / 100,
60                   GetMinWidth(window));
61 }
62 
63 // Returns the width that |window| should be snapped to if resizing is disabled
64 // in the SnapSizer.
GetDefaultWidth(aura::Window * window)65 int GetDefaultWidth(aura::Window* window) {
66   gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window));
67 
68   int width = 0;
69   if (!CommandLine::ForCurrentProcess()->HasSwitch(
70           switches::kAshMultipleSnapWindowWidths)) {
71     width = work_area.width() / 2;
72   } else {
73     width = std::max(kDefaultWidthSmallScreen, work_area.width() / 2);
74   }
75 
76   width = std::min(width, GetMaxWidth(window));
77   return std::max(width, GetMinWidth(window));
78 }
79 
80 // Creates the list of possible width for the current screen configuration:
81 // Returns a list with items from |kIdealWidth| which fit on the screen and
82 // supplement it with the 'half of screen' size. Furthermore, add an entry for
83 // 90% of the screen size if it is smaller than the biggest value in the
84 // |kIdealWidth| list (to get a step between the values).
BuildIdealWidthList(aura::Window * window)85 std::vector<int> BuildIdealWidthList(aura::Window* window) {
86   if (!CommandLine::ForCurrentProcess()->HasSwitch(
87           switches::kAshMultipleSnapWindowWidths)) {
88     return std::vector<int>(1u, GetDefaultWidth(window));
89   }
90 
91   int minimum_width = GetMinWidth(window);
92   int maximum_width = GetMaxWidth(window);
93 
94   gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window));
95   int half_width = work_area.width() / 2;
96   if (half_width < minimum_width || half_width > maximum_width)
97     half_width = 0;
98 
99   std::vector<int> ideal_width_list;
100   for (size_t i = 0; i < arraysize(kIdealWidth); i++) {
101     if (kIdealWidth[i] >= minimum_width && kIdealWidth[i] <= maximum_width) {
102       if (i && !ideal_width_list.size() && maximum_width != kIdealWidth[i])
103         ideal_width_list.push_back(maximum_width);
104       if (half_width > kIdealWidth[i])
105         ideal_width_list.push_back(half_width);
106       if (half_width >= kIdealWidth[i])
107         half_width = 0;
108       ideal_width_list.push_back(kIdealWidth[i]);
109     }
110   }
111   if (half_width)
112     ideal_width_list.push_back(half_width);
113   if (ideal_width_list.empty()) {
114     if (minimum_width > 0)
115       ideal_width_list.push_back(minimum_width);
116     else
117       ideal_width_list.push_back(maximum_width);
118   }
119 
120   return ideal_width_list;
121 }
122 
123 // Changes |window|'s bounds to |snap_bounds| while preserving the restore
124 // bounds.
SnapWindowToBounds(wm::WindowState * window_state,SnapSizer::Edge edge,const gfx::Rect & snap_bounds)125 void SnapWindowToBounds(wm::WindowState* window_state,
126                         SnapSizer::Edge edge,
127                         const gfx::Rect& snap_bounds) {
128   if (edge == SnapSizer::LEFT_EDGE) {
129     window_state->SnapLeft(snap_bounds);
130   } else {
131     window_state->SnapRight(snap_bounds);
132   }
133 }
134 
135 }  // namespace
136 
SnapSizer(wm::WindowState * window_state,const gfx::Point & start,Edge edge,InputType input_type)137 SnapSizer::SnapSizer(wm::WindowState* window_state,
138                      const gfx::Point& start,
139                      Edge edge,
140                      InputType input_type)
141     : window_state_(window_state),
142       edge_(edge),
143       time_last_update_(base::TimeTicks::Now()),
144       size_index_(0),
145       end_of_sequence_(false),
146       resize_disabled_(false),
147       num_moves_since_adjust_(0),
148       last_adjust_x_(start.x()),
149       last_update_x_(start.x()),
150       start_x_(start.x()),
151       input_type_(input_type),
152       usable_width_(BuildIdealWidthList(window_state->window())) {
153   DCHECK(!usable_width_.empty());
154   target_bounds_ = GetTargetBounds();
155 }
156 
~SnapSizer()157 SnapSizer::~SnapSizer() {
158 }
159 
SnapWindow(wm::WindowState * window_state,SnapSizer::Edge edge)160 void SnapSizer::SnapWindow(wm::WindowState* window_state,
161                            SnapSizer::Edge edge) {
162   if (!window_state->CanSnap())
163     return;
164   internal::SnapSizer sizer(window_state, gfx::Point(), edge,
165       internal::SnapSizer::OTHER_INPUT);
166   SnapWindowToBounds(window_state, edge,
167                      sizer.GetSnapBounds(window_state->window()->bounds()));
168 }
169 
SnapWindowToTargetBounds()170 void SnapSizer::SnapWindowToTargetBounds() {
171   SnapWindowToBounds(window_state_, edge_, target_bounds());
172 }
173 
Update(const gfx::Point & location)174 void SnapSizer::Update(const gfx::Point& location) {
175   // See description above for details on this behavior.
176   num_moves_since_adjust_++;
177   if ((base::TimeTicks::Now() - time_last_update_).InMilliseconds() >
178       kDelayBeforeIncreaseMS) {
179     ChangeBounds(location.x(),
180                  CalculateIncrement(location.x(), last_update_x_));
181   } else {
182     bool along_edge = AlongEdge(location.x());
183     int pixels_before_adjust = kPixelsBeforeAdjust;
184     if (input_type_ == TOUCH_MAXIMIZE_BUTTON_INPUT) {
185       const gfx::Rect& workspace_bounds =
186           window_state_->window()->parent()->bounds();
187       if (start_x_ > location.x()) {
188         pixels_before_adjust =
189             std::min(pixels_before_adjust, start_x_ / 10);
190       } else {
191         pixels_before_adjust =
192             std::min(pixels_before_adjust,
193                      (workspace_bounds.width() - start_x_) / 10);
194       }
195     }
196     if (std::abs(location.x() - last_adjust_x_) >= pixels_before_adjust ||
197         (along_edge && num_moves_since_adjust_ >= kMovesBeforeAdjust)) {
198       ChangeBounds(location.x(),
199                    CalculateIncrement(location.x(), last_adjust_x_));
200     }
201   }
202   last_update_x_ = location.x();
203   time_last_update_ = base::TimeTicks::Now();
204 }
205 
GetSnapBounds(const gfx::Rect & bounds)206 gfx::Rect SnapSizer::GetSnapBounds(const gfx::Rect& bounds) {
207   int current = 0;
208   if (!resize_disabled_) {
209     for (current = usable_width_.size() - 1; current >= 0; current--) {
210       gfx::Rect target = GetTargetBoundsForSize(current);
211       if (target == bounds) {
212         ++current;
213         break;
214       }
215     }
216   }
217   if (current < 0)
218     current = 0;
219   return GetTargetBoundsForSize(current % usable_width_.size());
220 }
221 
SelectDefaultSizeAndDisableResize()222 void SnapSizer::SelectDefaultSizeAndDisableResize() {
223   resize_disabled_ = true;
224   size_index_ = 0;
225   end_of_sequence_ = false;
226   target_bounds_ = GetTargetBounds();
227 }
228 
GetTargetBoundsForSize(size_t size_index) const229 gfx::Rect SnapSizer::GetTargetBoundsForSize(size_t size_index) const {
230   gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(
231       window_state_->window()));
232   int y = work_area.y();
233   int max_y = work_area.bottom();
234   int width = 0;
235   if (resize_disabled_) {
236     width = GetDefaultWidth(window_state_->window());
237   } else {
238     DCHECK(size_index < usable_width_.size());
239     width = usable_width_[size_index];
240   }
241 
242   if (edge_ == LEFT_EDGE) {
243     int x = work_area.x();
244     int mid_x = x + width;
245     return gfx::Rect(x, y, mid_x - x, max_y - y);
246   }
247   int max_x = work_area.right();
248   int x = max_x - width;
249   return gfx::Rect(x , y, max_x - x, max_y - y);
250 }
251 
CalculateIncrement(int x,int reference_x) const252 int SnapSizer::CalculateIncrement(int x, int reference_x) const {
253   if (AlongEdge(x))
254     return 1;
255   if (x == reference_x)
256     return 0;
257   if (edge_ == LEFT_EDGE) {
258     if (x < reference_x)
259       return 1;
260     return -1;
261   }
262   // edge_ == RIGHT_EDGE.
263   if (x > reference_x)
264     return 1;
265   return -1;
266 }
267 
ChangeBounds(int x,int delta)268 void SnapSizer::ChangeBounds(int x, int delta) {
269   end_of_sequence_ =
270       delta > 0 && size_index_ == static_cast<int>(usable_width_.size()) - 1;
271   int index = std::min(static_cast<int>(usable_width_.size()) - 1,
272                        std::max(size_index_ + delta, 0));
273   if (index != size_index_) {
274     size_index_ = index;
275     target_bounds_ = GetTargetBounds();
276   }
277   num_moves_since_adjust_ = 0;
278   last_adjust_x_ = x;
279 }
280 
GetTargetBounds() const281 gfx::Rect SnapSizer::GetTargetBounds() const {
282   return GetTargetBoundsForSize(size_index_);
283 }
284 
AlongEdge(int x) const285 bool SnapSizer::AlongEdge(int x) const {
286   gfx::Rect area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(
287       window_state_->window()));
288   return (x <= area.x()) || (x >= area.right() - 1);
289 }
290 
291 }  // namespace internal
292 }  // namespace ash
293