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/panels/panel_layout_manager.h"
15 #include "ash/wm/window_state.h"
16 #include "ash/wm/window_util.h"
17 #include "base/memory/weak_ptr.h"
18 #include "ui/aura/client/aura_constants.h"
19 #include "ui/aura/client/window_tree_client.h"
20 #include "ui/aura/env.h"
21 #include "ui/aura/window.h"
22 #include "ui/aura/window_delegate.h"
23 #include "ui/aura/window_event_dispatcher.h"
24 #include "ui/base/hit_test.h"
25 #include "ui/base/ui_base_types.h"
26 #include "ui/gfx/screen.h"
27 #include "ui/views/widget/widget.h"
28 #include "ui/wm/core/coordinate_conversion.h"
33 const int kPanelSnapToLauncherDistance
= 30;
35 PanelLayoutManager
* GetPanelLayoutManager(aura::Window
* panel_container
) {
36 DCHECK(panel_container
->id() == kShellWindowId_PanelContainer
);
37 return static_cast<PanelLayoutManager
*>(panel_container
->layout_manager());
42 PanelWindowResizer::~PanelWindowResizer() {
47 PanelWindowResizer::Create(WindowResizer
* next_window_resizer
,
48 wm::WindowState
* window_state
) {
49 return new PanelWindowResizer(next_window_resizer
, window_state
);
52 void PanelWindowResizer::Drag(const gfx::Point
& location
, int event_flags
) {
53 last_location_
= location
;
54 ::wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_
);
55 if (!did_move_or_resize_
) {
56 did_move_or_resize_
= true;
60 // Check if the destination has changed displays.
61 gfx::Screen
* screen
= Shell::GetScreen();
62 const gfx::Display dst_display
=
63 screen
->GetDisplayNearestPoint(last_location_
);
64 if (dst_display
.id() !=
65 screen
->GetDisplayNearestWindow(panel_container_
->GetRootWindow()).id()) {
66 // The panel is being dragged to a new display. If the previous container is
67 // the current parent of the panel it will be informed of the end of drag
68 // when the panel is reparented, otherwise let the previous container know
69 // the drag is complete. If we told the panel's parent that the drag was
70 // complete it would begin positioning the panel.
71 if (GetTarget()->parent() != panel_container_
)
72 GetPanelLayoutManager(panel_container_
)->FinishDragging();
73 aura::Window
* dst_root
= Shell::GetInstance()->display_controller()->
74 GetRootWindowForDisplayId(dst_display
.id());
76 Shell::GetContainer(dst_root
, kShellWindowId_PanelContainer
);
78 // The panel's parent already knows that the drag is in progress for this
80 if (panel_container_
&& GetTarget()->parent() != panel_container_
)
81 GetPanelLayoutManager(panel_container_
)->StartDragging(GetTarget());
84 gfx::Rect
bounds(CalculateBoundsForDrag(location
));
85 if (!(details().bounds_change
& WindowResizer::kBoundsChange_Resizes
)) {
86 window_state_
->drag_details()->should_attach_to_shelf
=
87 AttachToLauncher(bounds
, &offset
);
89 gfx::Point
modified_location(location
.x() + offset
.x(),
90 location
.y() + offset
.y());
92 base::WeakPtr
<PanelWindowResizer
> resizer(weak_ptr_factory_
.GetWeakPtr());
93 next_window_resizer_
->Drag(modified_location
, event_flags
);
97 if (details().should_attach_to_shelf
&&
98 !(details().bounds_change
& WindowResizer::kBoundsChange_Resizes
)) {
99 UpdateLauncherPosition();
103 void PanelWindowResizer::CompleteDrag() {
104 // The root window can change when dragging into a different screen.
105 next_window_resizer_
->CompleteDrag();
109 void PanelWindowResizer::RevertDrag() {
110 next_window_resizer_
->RevertDrag();
111 window_state_
->drag_details()->should_attach_to_shelf
= was_attached_
;
115 PanelWindowResizer::PanelWindowResizer(WindowResizer
* next_window_resizer
,
116 wm::WindowState
* window_state
)
117 : WindowResizer(window_state
),
118 next_window_resizer_(next_window_resizer
),
119 panel_container_(NULL
),
120 initial_panel_container_(NULL
),
121 did_move_or_resize_(false),
122 was_attached_(window_state
->panel_attached()),
123 weak_ptr_factory_(this) {
124 DCHECK(details().is_resizable
);
125 panel_container_
= Shell::GetContainer(GetTarget()->GetRootWindow(),
126 kShellWindowId_PanelContainer
);
127 initial_panel_container_
= panel_container_
;
130 bool PanelWindowResizer::AttachToLauncher(const gfx::Rect
& bounds
,
131 gfx::Point
* offset
) {
132 bool should_attach
= false;
133 if (panel_container_
) {
134 PanelLayoutManager
* panel_layout_manager
=
135 GetPanelLayoutManager(panel_container_
);
136 gfx::Rect launcher_bounds
= ScreenUtil::ConvertRectFromScreen(
137 GetTarget()->parent(),
138 panel_layout_manager
->shelf()->
139 shelf_widget()->GetWindowBoundsInScreen());
140 switch (panel_layout_manager
->shelf()->alignment()) {
141 case SHELF_ALIGNMENT_BOTTOM
:
142 if (bounds
.bottom() >= (launcher_bounds
.y() -
143 kPanelSnapToLauncherDistance
)) {
144 should_attach
= true;
145 offset
->set_y(launcher_bounds
.y() - bounds
.height() - bounds
.y());
148 case SHELF_ALIGNMENT_LEFT
:
149 if (bounds
.x() <= (launcher_bounds
.right() +
150 kPanelSnapToLauncherDistance
)) {
151 should_attach
= true;
152 offset
->set_x(launcher_bounds
.right() - bounds
.x());
155 case SHELF_ALIGNMENT_RIGHT
:
156 if (bounds
.right() >= (launcher_bounds
.x() -
157 kPanelSnapToLauncherDistance
)) {
158 should_attach
= true;
159 offset
->set_x(launcher_bounds
.x() - bounds
.width() - bounds
.x());
162 case SHELF_ALIGNMENT_TOP
:
163 if (bounds
.y() <= (launcher_bounds
.bottom() +
164 kPanelSnapToLauncherDistance
)) {
165 should_attach
= true;
166 offset
->set_y(launcher_bounds
.bottom() - bounds
.y());
171 return should_attach
;
174 void PanelWindowResizer::StartedDragging() {
175 // Tell the panel layout manager that we are dragging this panel before
176 // attaching it so that it does not get repositioned.
177 if (panel_container_
)
178 GetPanelLayoutManager(panel_container_
)->StartDragging(GetTarget());
179 if (!was_attached_
) {
180 // Attach the panel while dragging placing it in front of other panels.
181 window_state_
->set_panel_attached(true);
182 // We use root window coordinates to ensure that during the drag the panel
183 // is reparented to a container in the root window that has that window.
184 aura::Window
* target
= GetTarget();
185 aura::Window
* target_root
= target
->GetRootWindow();
186 aura::Window
* old_parent
= target
->parent();
187 aura::client::ParentWindowWithContext(
188 target
, target_root
, target_root
->GetBoundsInScreen());
189 wm::ReparentTransientChildrenOfChild(target
, old_parent
, target
->parent());
193 void PanelWindowResizer::FinishDragging() {
194 if (!did_move_or_resize_
)
196 if (window_state_
->panel_attached() != details().should_attach_to_shelf
) {
197 window_state_
->set_panel_attached(details().should_attach_to_shelf
);
198 // We use last known location to ensure that after the drag the panel
199 // is reparented to a container in the root window that has that location.
200 aura::Window
* target
= GetTarget();
201 aura::Window
* target_root
= target
->GetRootWindow();
202 aura::Window
* old_parent
= target
->parent();
203 aura::client::ParentWindowWithContext(
204 target
, target_root
, gfx::Rect(last_location_
, gfx::Size()));
205 wm::ReparentTransientChildrenOfChild(target
, old_parent
, target
->parent());
208 // If we started the drag in one root window and moved into another root
209 // but then canceled the drag we may need to inform the original layout
210 // manager that the drag is finished.
211 if (initial_panel_container_
!= panel_container_
)
212 GetPanelLayoutManager(initial_panel_container_
)->FinishDragging();
213 if (panel_container_
)
214 GetPanelLayoutManager(panel_container_
)->FinishDragging();
217 void PanelWindowResizer::UpdateLauncherPosition() {
218 if (panel_container_
) {
219 GetPanelLayoutManager(panel_container_
)->shelf()->
220 UpdateIconPositionForWindow(GetTarget());