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/screen_util.h"
9 #include "ash/shelf/shelf.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/window_state.h"
17 #include "ash/wm/window_util.h"
18 #include "base/memory/weak_ptr.h"
19 #include "ui/aura/client/aura_constants.h"
20 #include "ui/aura/client/window_tree_client.h"
21 #include "ui/aura/env.h"
22 #include "ui/aura/root_window.h"
23 #include "ui/aura/window.h"
24 #include "ui/aura/window_delegate.h"
25 #include "ui/base/hit_test.h"
26 #include "ui/base/ui_base_types.h"
27 #include "ui/gfx/screen.h"
28 #include "ui/views/widget/widget.h"
33 const int kPanelSnapToLauncherDistance
= 30;
35 internal::PanelLayoutManager
* GetPanelLayoutManager(
36 aura::Window
* panel_container
) {
37 DCHECK(panel_container
->id() == internal::kShellWindowId_PanelContainer
);
38 return static_cast<internal::PanelLayoutManager
*>(
39 panel_container
->layout_manager());
44 PanelWindowResizer::~PanelWindowResizer() {
49 PanelWindowResizer::Create(WindowResizer
* next_window_resizer
,
50 wm::WindowState
* window_state
) {
51 return new PanelWindowResizer(next_window_resizer
, window_state
);
54 void PanelWindowResizer::Drag(const gfx::Point
& location
, int event_flags
) {
55 last_location_
= location
;
56 wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_
);
57 if (!did_move_or_resize_
) {
58 did_move_or_resize_
= true;
62 // Check if the destination has changed displays.
63 gfx::Screen
* screen
= Shell::GetScreen();
64 const gfx::Display dst_display
=
65 screen
->GetDisplayNearestPoint(last_location_
);
66 if (dst_display
.id() !=
67 screen
->GetDisplayNearestWindow(panel_container_
->GetRootWindow()).id()) {
68 // The panel is being dragged to a new display. If the previous container is
69 // the current parent of the panel it will be informed of the end of drag
70 // when the panel is reparented, otherwise let the previous container know
71 // the drag is complete. If we told the panel's parent that the drag was
72 // complete it would begin positioning the panel.
73 if (GetTarget()->parent() != panel_container_
)
74 GetPanelLayoutManager(panel_container_
)->FinishDragging();
75 aura::Window
* dst_root
= Shell::GetInstance()->display_controller()->
76 GetRootWindowForDisplayId(dst_display
.id());
77 panel_container_
= Shell::GetContainer(
78 dst_root
, internal::kShellWindowId_PanelContainer
);
80 // The panel's parent already knows that the drag is in progress for this
82 if (panel_container_
&& GetTarget()->parent() != panel_container_
)
83 GetPanelLayoutManager(panel_container_
)->StartDragging(GetTarget());
86 gfx::Rect
bounds(CalculateBoundsForDrag(location
));
87 if (!(details().bounds_change
& WindowResizer::kBoundsChange_Resizes
)) {
88 window_state_
->drag_details()->should_attach_to_shelf
=
89 AttachToLauncher(bounds
, &offset
);
91 gfx::Point
modified_location(location
.x() + offset
.x(),
92 location
.y() + offset
.y());
94 base::WeakPtr
<PanelWindowResizer
> resizer(weak_ptr_factory_
.GetWeakPtr());
95 next_window_resizer_
->Drag(modified_location
, event_flags
);
99 if (details().should_attach_to_shelf
&&
100 !(details().bounds_change
& WindowResizer::kBoundsChange_Resizes
)) {
101 UpdateLauncherPosition();
105 void PanelWindowResizer::CompleteDrag() {
106 // The root window can change when dragging into a different screen.
107 next_window_resizer_
->CompleteDrag();
111 void PanelWindowResizer::RevertDrag() {
112 next_window_resizer_
->RevertDrag();
113 window_state_
->drag_details()->should_attach_to_shelf
= was_attached_
;
117 PanelWindowResizer::PanelWindowResizer(WindowResizer
* next_window_resizer
,
118 wm::WindowState
* window_state
)
119 : WindowResizer(window_state
),
120 next_window_resizer_(next_window_resizer
),
121 panel_container_(NULL
),
122 initial_panel_container_(NULL
),
123 did_move_or_resize_(false),
124 was_attached_(window_state
->panel_attached()),
125 weak_ptr_factory_(this) {
126 DCHECK(details().is_resizable
);
127 panel_container_
= Shell::GetContainer(
128 GetTarget()->GetRootWindow(),
129 internal::kShellWindowId_PanelContainer
);
130 initial_panel_container_
= panel_container_
;
133 bool PanelWindowResizer::AttachToLauncher(const gfx::Rect
& bounds
,
134 gfx::Point
* offset
) {
135 bool should_attach
= false;
136 if (panel_container_
) {
137 internal::PanelLayoutManager
* panel_layout_manager
=
138 GetPanelLayoutManager(panel_container_
);
139 gfx::Rect launcher_bounds
= ScreenUtil::ConvertRectFromScreen(
140 GetTarget()->parent(),
141 panel_layout_manager
->shelf()->
142 shelf_widget()->GetWindowBoundsInScreen());
143 switch (panel_layout_manager
->shelf()->alignment()) {
144 case SHELF_ALIGNMENT_BOTTOM
:
145 if (bounds
.bottom() >= (launcher_bounds
.y() -
146 kPanelSnapToLauncherDistance
)) {
147 should_attach
= true;
148 offset
->set_y(launcher_bounds
.y() - bounds
.height() - bounds
.y());
151 case SHELF_ALIGNMENT_LEFT
:
152 if (bounds
.x() <= (launcher_bounds
.right() +
153 kPanelSnapToLauncherDistance
)) {
154 should_attach
= true;
155 offset
->set_x(launcher_bounds
.right() - bounds
.x());
158 case SHELF_ALIGNMENT_RIGHT
:
159 if (bounds
.right() >= (launcher_bounds
.x() -
160 kPanelSnapToLauncherDistance
)) {
161 should_attach
= true;
162 offset
->set_x(launcher_bounds
.x() - bounds
.width() - bounds
.x());
165 case SHELF_ALIGNMENT_TOP
:
166 if (bounds
.y() <= (launcher_bounds
.bottom() +
167 kPanelSnapToLauncherDistance
)) {
168 should_attach
= true;
169 offset
->set_y(launcher_bounds
.bottom() - bounds
.y());
174 return should_attach
;
177 void PanelWindowResizer::StartedDragging() {
178 // Tell the panel layout manager that we are dragging this panel before
179 // attaching it so that it does not get repositioned.
180 if (panel_container_
)
181 GetPanelLayoutManager(panel_container_
)->StartDragging(GetTarget());
182 if (!was_attached_
) {
183 // Attach the panel while dragging placing it in front of other panels.
184 window_state_
->set_continue_drag_after_reparent(true);
185 window_state_
->set_panel_attached(true);
186 // We use root window coordinates to ensure that during the drag the panel
187 // is reparented to a container in the root window that has that window.
188 aura::Window
* target
= GetTarget();
189 aura::Window
* target_root
= target
->GetRootWindow();
190 aura::Window
* old_parent
= target
->parent();
191 aura::client::ParentWindowWithContext(
192 target
, target_root
, target_root
->GetBoundsInScreen());
193 wm::ReparentTransientChildrenOfChild(target
, old_parent
, target
->parent());
197 void PanelWindowResizer::FinishDragging() {
198 if (!did_move_or_resize_
)
200 if (window_state_
->panel_attached() != details().should_attach_to_shelf
) {
201 window_state_
->set_panel_attached(details().should_attach_to_shelf
);
202 // We use last known location to ensure that after the drag the panel
203 // is reparented to a container in the root window that has that location.
204 aura::Window
* target
= GetTarget();
205 aura::Window
* target_root
= target
->GetRootWindow();
206 aura::Window
* old_parent
= target
->parent();
207 aura::client::ParentWindowWithContext(
208 target
, target_root
, gfx::Rect(last_location_
, gfx::Size()));
209 wm::ReparentTransientChildrenOfChild(target
, old_parent
, target
->parent());
212 // If we started the drag in one root window and moved into another root
213 // but then canceled the drag we may need to inform the original layout
214 // manager that the drag is finished.
215 if (initial_panel_container_
!= panel_container_
)
216 GetPanelLayoutManager(initial_panel_container_
)->FinishDragging();
217 if (panel_container_
)
218 GetPanelLayoutManager(panel_container_
)->FinishDragging();
221 void PanelWindowResizer::UpdateLauncherPosition() {
222 if (panel_container_
) {
223 GetPanelLayoutManager(panel_container_
)->shelf()->
224 UpdateIconPositionForWindow(GetTarget());