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/panels/panel_window_resizer.h"
7 #include "ash/display/display_controller.h"
8 #include "ash/launcher/launcher.h"
9 #include "ash/screen_ash.h"
10 #include "ash/shelf/shelf_types.h"
11 #include "ash/shelf/shelf_widget.h"
12 #include "ash/shell.h"
13 #include "ash/shell_window_ids.h"
14 #include "ash/wm/coordinate_conversion.h"
15 #include "ash/wm/panels/panel_layout_manager.h"
16 #include "ash/wm/property_util.h"
17 #include "ash/wm/window_properties.h"
18 #include "ui/aura/client/aura_constants.h"
19 #include "ui/aura/env.h"
20 #include "ui/aura/root_window.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_delegate.h"
23 #include "ui/base/hit_test.h"
24 #include "ui/base/ui_base_types.h"
25 #include "ui/gfx/screen.h"
26 #include "ui/views/widget/widget.h"
31 const int kPanelSnapToLauncherDistance
= 30;
33 internal::PanelLayoutManager
* GetPanelLayoutManager(
34 aura::Window
* panel_container
) {
35 DCHECK(panel_container
->id() == internal::kShellWindowId_PanelContainer
);
36 return static_cast<internal::PanelLayoutManager
*>(
37 panel_container
->layout_manager());
42 PanelWindowResizer::~PanelWindowResizer() {
49 PanelWindowResizer::Create(WindowResizer
* next_window_resizer
,
51 const gfx::Point
& location
,
53 aura::client::WindowMoveSource source
) {
54 Details
details(window
, location
, window_component
, source
);
55 return details
.is_resizable
?
56 new PanelWindowResizer(next_window_resizer
, details
) : NULL
;
59 void PanelWindowResizer::Drag(const gfx::Point
& location
, int event_flags
) {
60 last_location_
= location
;
61 wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_
);
62 bool destroyed
= false;
63 if (!did_move_or_resize_
) {
64 did_move_or_resize_
= true;
68 // Check if the destination has changed displays.
69 gfx::Screen
* screen
= Shell::GetScreen();
70 const gfx::Display dst_display
=
71 screen
->GetDisplayNearestPoint(last_location_
);
72 if (dst_display
.id() !=
73 screen
->GetDisplayNearestWindow(panel_container_
->GetRootWindow()).id()) {
74 // The panel is being dragged to a new display. If the previous container is
75 // the current parent of the panel it will be informed of the end of drag
76 // when the panel is reparented, otherwise let the previous container know
77 // the drag is complete. If we told the panel's parent that the drag was
78 // complete it would begin positioning the panel.
79 if (GetTarget()->parent() != panel_container_
)
80 GetPanelLayoutManager(panel_container_
)->FinishDragging();
81 aura::RootWindow
* dst_root
= Shell::GetInstance()->display_controller()->
82 GetRootWindowForDisplayId(dst_display
.id());
83 panel_container_
= Shell::GetContainer(
84 dst_root
, internal::kShellWindowId_PanelContainer
);
86 // The panel's parent already knows that the drag is in progress for this
88 if (panel_container_
&& GetTarget()->parent() != panel_container_
)
89 GetPanelLayoutManager(panel_container_
)->StartDragging(GetTarget());
92 gfx::Rect
bounds(CalculateBoundsForDrag(details_
, location
));
93 should_attach_
= AttachToLauncher(bounds
, &offset
);
94 gfx::Point
modified_location(location
.x() + offset
.x(),
95 location
.y() + offset
.y());
96 destroyed_
= &destroyed
;
97 next_window_resizer_
->Drag(modified_location
, event_flags
);
99 // TODO(flackr): Refactor the way WindowResizer calls into other window
100 // resizers to avoid the awkward pattern here for checking if
101 // next_window_resizer_ destroys the resizer object.
105 if (should_attach_
&&
106 !(details_
.bounds_change
& WindowResizer::kBoundsChange_Resizes
)) {
107 UpdateLauncherPosition();
111 void PanelWindowResizer::CompleteDrag(int event_flags
) {
112 // The root window can change when dragging into a different screen.
113 next_window_resizer_
->CompleteDrag(event_flags
);
117 void PanelWindowResizer::RevertDrag() {
118 next_window_resizer_
->RevertDrag();
119 should_attach_
= was_attached_
;
123 aura::Window
* PanelWindowResizer::GetTarget() {
124 return next_window_resizer_
->GetTarget();
127 const gfx::Point
& PanelWindowResizer::GetInitialLocation() const {
128 return details_
.initial_location_in_parent
;
131 PanelWindowResizer::PanelWindowResizer(WindowResizer
* next_window_resizer
,
132 const Details
& details
)
134 next_window_resizer_(next_window_resizer
),
135 panel_container_(NULL
),
136 initial_panel_container_(NULL
),
137 did_move_or_resize_(false),
138 was_attached_(GetTarget()->GetProperty(internal::kPanelAttachedKey
)),
139 should_attach_(was_attached_
),
141 DCHECK(details_
.is_resizable
);
142 panel_container_
= Shell::GetContainer(
143 details
.window
->GetRootWindow(),
144 internal::kShellWindowId_PanelContainer
);
145 initial_panel_container_
= panel_container_
;
148 bool PanelWindowResizer::AttachToLauncher(const gfx::Rect
& bounds
,
149 gfx::Point
* offset
) {
150 bool should_attach
= false;
151 if (panel_container_
) {
152 internal::PanelLayoutManager
* panel_layout_manager
=
153 GetPanelLayoutManager(panel_container_
);
154 gfx::Rect launcher_bounds
= ScreenAsh::ConvertRectFromScreen(
155 GetTarget()->parent(),
156 panel_layout_manager
->launcher()->
157 shelf_widget()->GetWindowBoundsInScreen());
158 switch (panel_layout_manager
->launcher()->alignment()) {
159 case SHELF_ALIGNMENT_BOTTOM
:
160 if (bounds
.bottom() >= (launcher_bounds
.y() -
161 kPanelSnapToLauncherDistance
)) {
162 should_attach
= true;
163 offset
->set_y(launcher_bounds
.y() - bounds
.height() - bounds
.y());
166 case SHELF_ALIGNMENT_LEFT
:
167 if (bounds
.x() <= (launcher_bounds
.right() +
168 kPanelSnapToLauncherDistance
)) {
169 should_attach
= true;
170 offset
->set_x(launcher_bounds
.right() - bounds
.x());
173 case SHELF_ALIGNMENT_RIGHT
:
174 if (bounds
.right() >= (launcher_bounds
.x() -
175 kPanelSnapToLauncherDistance
)) {
176 should_attach
= true;
177 offset
->set_x(launcher_bounds
.x() - bounds
.width() - bounds
.x());
180 case SHELF_ALIGNMENT_TOP
:
181 if (bounds
.y() <= (launcher_bounds
.bottom() +
182 kPanelSnapToLauncherDistance
)) {
183 should_attach
= true;
184 offset
->set_y(launcher_bounds
.bottom() - bounds
.y());
189 return should_attach
;
192 void PanelWindowResizer::StartedDragging() {
193 // Tell the panel layout manager that we are dragging this panel before
194 // attaching it so that it does not get repositioned.
195 if (panel_container_
)
196 GetPanelLayoutManager(panel_container_
)->StartDragging(GetTarget());
197 if (!was_attached_
) {
198 // Attach the panel while dragging placing it in front of other panels.
199 GetTarget()->SetProperty(internal::kContinueDragAfterReparent
, true);
200 GetTarget()->SetProperty(internal::kPanelAttachedKey
, true);
201 // We use root window coordinates to ensure that during the drag the panel
202 // is reparented to a container in the root window that has that window.
203 GetTarget()->SetDefaultParentByRootWindow(
204 GetTarget()->GetRootWindow(),
205 GetTarget()->GetRootWindow()->GetBoundsInScreen());
209 void PanelWindowResizer::FinishDragging() {
210 if (!did_move_or_resize_
)
212 if (GetTarget()->GetProperty(internal::kPanelAttachedKey
) !=
214 GetTarget()->SetProperty(internal::kPanelAttachedKey
, should_attach_
);
215 // We use last known location to ensure that after the drag the panel
216 // is reparented to a container in the root window that has that location.
217 GetTarget()->SetDefaultParentByRootWindow(
218 GetTarget()->GetRootWindow(),
219 gfx::Rect(last_location_
, gfx::Size()));
222 // If we started the drag in one root window and moved into another root
223 // but then canceled the drag we may need to inform the original layout
224 // manager that the drag is finished.
225 if (initial_panel_container_
!= panel_container_
)
226 GetPanelLayoutManager(initial_panel_container_
)->FinishDragging();
227 if (panel_container_
)
228 GetPanelLayoutManager(panel_container_
)->FinishDragging();
231 void PanelWindowResizer::UpdateLauncherPosition() {
232 if (panel_container_
) {
233 GetPanelLayoutManager(panel_container_
)->launcher()->
234 UpdateIconPositionForWindow(GetTarget());