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/window_resizer.h"
7 #include "ash/screen_ash.h"
9 #include "ash/wm/property_util.h"
10 #include "ash/wm/window_util.h"
11 #include "ui/aura/client/aura_constants.h"
12 #include "ui/aura/root_window.h"
13 #include "ui/aura/window.h"
14 #include "ui/aura/window_delegate.h"
15 #include "ui/base/hit_test.h"
16 #include "ui/base/ui_base_types.h"
17 #include "ui/compositor/scoped_layer_animation_settings.h"
18 #include "ui/gfx/display.h"
19 #include "ui/gfx/screen.h"
25 int GetPositionChangeDirectionForWindowComponent(int window_component
) {
26 int pos_change_direction
= WindowResizer::kBoundsChangeDirection_None
;
27 switch (window_component
) {
32 pos_change_direction
|=
33 WindowResizer::kBoundsChangeDirection_Horizontal
|
34 WindowResizer::kBoundsChangeDirection_Vertical
;
39 pos_change_direction
|= WindowResizer::kBoundsChangeDirection_Vertical
;
44 pos_change_direction
|= WindowResizer::kBoundsChangeDirection_Horizontal
;
49 return pos_change_direction
;
52 int GetSizeChangeDirectionForWindowComponent(int window_component
) {
53 int size_change_direction
= WindowResizer::kBoundsChangeDirection_None
;
54 switch (window_component
) {
61 size_change_direction
|=
62 WindowResizer::kBoundsChangeDirection_Horizontal
|
63 WindowResizer::kBoundsChangeDirection_Vertical
;
67 size_change_direction
|= WindowResizer::kBoundsChangeDirection_Vertical
;
71 size_change_direction
|= WindowResizer::kBoundsChangeDirection_Horizontal
;
76 return size_change_direction
;
79 // Returns true for resize components along the right edge, where a drag in
80 // positive x will make the window larger.
81 bool IsRightEdge(int window_component
) {
82 return window_component
== HTTOPRIGHT
||
83 window_component
== HTRIGHT
||
84 window_component
== HTBOTTOMRIGHT
||
85 window_component
== HTGROWBOX
;
91 const int WindowResizer::kBoundsChange_None
= 0;
93 const int WindowResizer::kBoundsChange_Repositions
= 1;
95 const int WindowResizer::kBoundsChange_Resizes
= 2;
98 const int WindowResizer::kBoundsChangeDirection_None
= 0;
100 const int WindowResizer::kBoundsChangeDirection_Horizontal
= 1;
102 const int WindowResizer::kBoundsChangeDirection_Vertical
= 2;
104 WindowResizer::Details::Details()
106 window_component(HTNOWHERE
),
108 position_change_direction(0),
109 size_change_direction(0),
110 is_resizable(false) {
113 WindowResizer::Details::Details(aura::Window
* window
,
114 const gfx::Point
& location
,
115 int window_component
)
117 initial_bounds_in_parent(window
->bounds()),
118 restore_bounds(gfx::Rect()),
119 initial_location_in_parent(location
),
120 initial_opacity(window
->layer()->opacity()),
121 window_component(window_component
),
122 bounds_change(GetBoundsChangeForWindowComponent(window_component
)),
123 position_change_direction(
124 GetPositionChangeDirectionForWindowComponent(window_component
)),
125 size_change_direction(
126 GetSizeChangeDirectionForWindowComponent(window_component
)),
127 is_resizable(bounds_change
!= kBoundsChangeDirection_None
) {
128 if (wm::IsWindowNormal(window
) &&
129 GetRestoreBoundsInScreen(window
) &&
130 window_component
== HTCAPTION
)
131 restore_bounds
= *GetRestoreBoundsInScreen(window
);
134 WindowResizer::Details::~Details() {
137 WindowResizer::WindowResizer() {
140 WindowResizer::~WindowResizer() {
144 int WindowResizer::GetBoundsChangeForWindowComponent(int component
) {
145 int bounds_change
= WindowResizer::kBoundsChange_None
;
152 bounds_change
|= WindowResizer::kBoundsChange_Repositions
|
153 WindowResizer::kBoundsChange_Resizes
;
156 bounds_change
|= WindowResizer::kBoundsChange_Repositions
;
162 bounds_change
|= WindowResizer::kBoundsChange_Resizes
;
167 return bounds_change
;
171 gfx::Rect
WindowResizer::CalculateBoundsForDrag(
172 const Details
& details
,
173 const gfx::Point
& passed_location
) {
174 if (!details
.is_resizable
)
175 return details
.initial_bounds_in_parent
;
177 gfx::Point location
= passed_location
;
178 gfx::Rect work_area
=
179 ScreenAsh::GetDisplayWorkAreaBoundsInParent(details
.window
);
181 int delta_x
= location
.x() - details
.initial_location_in_parent
.x();
182 int delta_y
= location
.y() - details
.initial_location_in_parent
.y();
184 // The minimize size constraint may limit how much we change the window
185 // position. For example, dragging the left edge to the right should stop
186 // repositioning the window when the minimize size is reached.
187 gfx::Size size
= GetSizeForDrag(details
, &delta_x
, &delta_y
);
188 gfx::Point origin
= GetOriginForDrag(details
, delta_x
, delta_y
);
189 gfx::Rect
new_bounds(origin
, size
);
191 // Sizing has to keep the result on the screen. Note that this correction
192 // has to come first since it might have an impact on the origin as well as
194 if (details
.bounds_change
& kBoundsChange_Resizes
) {
195 if (details
.size_change_direction
& kBoundsChangeDirection_Horizontal
) {
196 if (IsRightEdge(details
.window_component
) &&
197 new_bounds
.right() < work_area
.x() + kMinimumOnScreenArea
) {
198 int delta
= work_area
.x() + kMinimumOnScreenArea
- new_bounds
.right();
199 new_bounds
.set_width(new_bounds
.width() + delta
);
200 } else if (new_bounds
.x() > work_area
.right() - kMinimumOnScreenArea
) {
201 int width
= new_bounds
.right() - work_area
.right() +
202 kMinimumOnScreenArea
;
203 new_bounds
.set_x(work_area
.right() - kMinimumOnScreenArea
);
204 new_bounds
.set_width(width
);
207 if (details
.size_change_direction
& kBoundsChangeDirection_Vertical
) {
208 if (!IsBottomEdge(details
.window_component
) &&
209 new_bounds
.y() > work_area
.bottom() - kMinimumOnScreenArea
) {
210 int height
= new_bounds
.bottom() - work_area
.bottom() +
211 kMinimumOnScreenArea
;
212 new_bounds
.set_y(work_area
.bottom() - kMinimumOnScreenArea
);
213 new_bounds
.set_height(height
);
214 } else if (details
.window_component
== HTBOTTOM
||
215 details
.window_component
== HTBOTTOMRIGHT
||
216 details
.window_component
== HTBOTTOMLEFT
) {
217 // Update bottom edge to stay in the work area when we are resizing
218 // by dragging the bottom edge or corners.
219 if (new_bounds
.bottom() > work_area
.bottom())
220 new_bounds
.Inset(0, 0, 0,
221 new_bounds
.bottom() - work_area
.bottom());
224 if (details
.bounds_change
& kBoundsChange_Repositions
&&
225 new_bounds
.y() < 0) {
226 int delta
= new_bounds
.y();
228 new_bounds
.set_height(new_bounds
.height() + delta
);
232 if (details
.bounds_change
& kBoundsChange_Repositions
) {
233 // When we might want to reposition a window which is also restored to its
234 // previous size, to keep the cursor within the dragged window.
235 if (!details
.restore_bounds
.IsEmpty()) {
236 // However - it is not desirable to change the origin if the window would
237 // be still hit by the cursor.
238 if (details
.initial_location_in_parent
.x() >
239 details
.initial_bounds_in_parent
.x() + details
.restore_bounds
.width())
240 new_bounds
.set_x(location
.x() - details
.restore_bounds
.width() / 2);
243 // Make sure that |new_bounds| doesn't leave any of the displays. Note that
244 // the |work_area| above isn't good for this check since it is the work area
245 // for the current display but the window can move to a different one.
246 aura::Window
* parent
= details
.window
->parent();
247 gfx::Rect new_bounds_in_screen
=
248 ScreenAsh::ConvertRectToScreen(parent
, new_bounds
);
249 const gfx::Display
& display
=
250 Shell::GetScreen()->GetDisplayMatching(new_bounds_in_screen
);
251 gfx::Rect screen_work_area
= display
.work_area();
252 screen_work_area
.Inset(kMinimumOnScreenArea
, 0);
253 if (!screen_work_area
.Intersects(new_bounds_in_screen
)) {
254 // Make sure that the x origin does not leave the current display.
255 new_bounds_in_screen
.set_x(
256 std::max(screen_work_area
.x() - new_bounds
.width(),
257 std::min(screen_work_area
.right(),
258 new_bounds_in_screen
.x())));
260 ScreenAsh::ConvertRectFromScreen(parent
, new_bounds_in_screen
);
268 bool WindowResizer::IsBottomEdge(int window_component
) {
269 return window_component
== HTBOTTOMLEFT
||
270 window_component
== HTBOTTOM
||
271 window_component
== HTBOTTOMRIGHT
||
272 window_component
== HTGROWBOX
;
276 gfx::Point
WindowResizer::GetOriginForDrag(const Details
& details
,
279 gfx::Point origin
= details
.initial_bounds_in_parent
.origin();
280 if (details
.bounds_change
& kBoundsChange_Repositions
) {
281 int pos_change_direction
=
282 GetPositionChangeDirectionForWindowComponent(details
.window_component
);
283 if (pos_change_direction
& kBoundsChangeDirection_Horizontal
)
284 origin
.Offset(delta_x
, 0);
285 if (pos_change_direction
& kBoundsChangeDirection_Vertical
)
286 origin
.Offset(0, delta_y
);
292 gfx::Size
WindowResizer::GetSizeForDrag(const Details
& details
,
295 gfx::Size size
= details
.initial_bounds_in_parent
.size();
296 if (details
.bounds_change
& kBoundsChange_Resizes
) {
297 gfx::Size min_size
= details
.window
->delegate()->GetMinimumSize();
298 size
.SetSize(GetWidthForDrag(details
, min_size
.width(), delta_x
),
299 GetHeightForDrag(details
, min_size
.height(), delta_y
));
300 } else if (!details
.restore_bounds
.IsEmpty()) {
301 size
= details
.restore_bounds
.size();
307 int WindowResizer::GetWidthForDrag(const Details
& details
,
310 int width
= details
.initial_bounds_in_parent
.width();
311 if (details
.size_change_direction
& kBoundsChangeDirection_Horizontal
) {
312 // Along the right edge, positive delta_x increases the window size.
313 int x_multiplier
= IsRightEdge(details
.window_component
) ? 1 : -1;
314 width
+= x_multiplier
* (*delta_x
);
316 // Ensure we don't shrink past the minimum width and clamp delta_x
317 // for the window origin computation.
318 if (width
< min_width
) {
320 *delta_x
= -x_multiplier
* (details
.initial_bounds_in_parent
.width() -
324 // And don't let the window go bigger than the display.
325 int max_width
= Shell::GetScreen()->GetDisplayNearestWindow(
326 details
.window
).bounds().width();
327 gfx::Size max_size
= details
.window
->delegate()->GetMaximumSize();
328 if (max_size
.width() != 0)
329 max_width
= std::min(max_width
, max_size
.width());
330 if (width
> max_width
) {
332 *delta_x
= -x_multiplier
* (details
.initial_bounds_in_parent
.width() -
340 int WindowResizer::GetHeightForDrag(const Details
& details
,
343 int height
= details
.initial_bounds_in_parent
.height();
344 if (details
.size_change_direction
& kBoundsChangeDirection_Vertical
) {
345 // Along the bottom edge, positive delta_y increases the window size.
346 int y_multiplier
= IsBottomEdge(details
.window_component
) ? 1 : -1;
347 height
+= y_multiplier
* (*delta_y
);
349 // Ensure we don't shrink past the minimum height and clamp delta_y
350 // for the window origin computation.
351 if (height
< min_height
) {
353 *delta_y
= -y_multiplier
* (details
.initial_bounds_in_parent
.height() -
357 // And don't let the window go bigger than the display.
358 int max_height
= Shell::GetScreen()->GetDisplayNearestWindow(
359 details
.window
).bounds().height();
360 gfx::Size max_size
= details
.window
->delegate()->GetMaximumSize();
361 if (max_size
.height() != 0)
362 max_height
= std::min(max_height
, max_size
.height());
363 if (height
> max_height
) {
365 *delta_y
= -y_multiplier
* (details
.initial_bounds_in_parent
.height() -