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/window_tree_host_manager.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()
74 ->window_tree_host_manager()
75 ->GetRootWindowForDisplayId(dst_display
.id());
77 Shell::GetContainer(dst_root
, kShellWindowId_PanelContainer
);
79 // The panel's parent already knows that the drag is in progress for this
81 if (panel_container_
&& GetTarget()->parent() != panel_container_
)
82 GetPanelLayoutManager(panel_container_
)->StartDragging(GetTarget());
85 gfx::Rect
bounds(CalculateBoundsForDrag(location
));
86 if (!(details().bounds_change
& WindowResizer::kBoundsChange_Resizes
)) {
87 window_state_
->drag_details()->should_attach_to_shelf
=
88 AttachToLauncher(bounds
, &offset
);
90 gfx::Point
modified_location(location
.x() + offset
.x(),
91 location
.y() + offset
.y());
93 base::WeakPtr
<PanelWindowResizer
> resizer(weak_ptr_factory_
.GetWeakPtr());
94 next_window_resizer_
->Drag(modified_location
, event_flags
);
98 if (details().should_attach_to_shelf
&&
99 !(details().bounds_change
& WindowResizer::kBoundsChange_Resizes
)) {
100 UpdateLauncherPosition();
104 void PanelWindowResizer::CompleteDrag() {
105 // The root window can change when dragging into a different screen.
106 next_window_resizer_
->CompleteDrag();
110 void PanelWindowResizer::RevertDrag() {
111 next_window_resizer_
->RevertDrag();
112 window_state_
->drag_details()->should_attach_to_shelf
= was_attached_
;
116 PanelWindowResizer::PanelWindowResizer(WindowResizer
* next_window_resizer
,
117 wm::WindowState
* window_state
)
118 : WindowResizer(window_state
),
119 next_window_resizer_(next_window_resizer
),
120 panel_container_(NULL
),
121 initial_panel_container_(NULL
),
122 did_move_or_resize_(false),
123 was_attached_(window_state
->panel_attached()),
124 weak_ptr_factory_(this) {
125 DCHECK(details().is_resizable
);
126 panel_container_
= Shell::GetContainer(GetTarget()->GetRootWindow(),
127 kShellWindowId_PanelContainer
);
128 initial_panel_container_
= panel_container_
;
131 bool PanelWindowResizer::AttachToLauncher(const gfx::Rect
& bounds
,
132 gfx::Point
* offset
) {
133 bool should_attach
= false;
134 if (panel_container_
) {
135 PanelLayoutManager
* panel_layout_manager
=
136 GetPanelLayoutManager(panel_container_
);
137 gfx::Rect launcher_bounds
= ScreenUtil::ConvertRectFromScreen(
138 GetTarget()->parent(),
139 panel_layout_manager
->shelf()->
140 shelf_widget()->GetWindowBoundsInScreen());
141 switch (panel_layout_manager
->shelf()->alignment()) {
142 case SHELF_ALIGNMENT_BOTTOM
:
143 if (bounds
.bottom() >= (launcher_bounds
.y() -
144 kPanelSnapToLauncherDistance
)) {
145 should_attach
= true;
146 offset
->set_y(launcher_bounds
.y() - bounds
.height() - bounds
.y());
149 case SHELF_ALIGNMENT_LEFT
:
150 if (bounds
.x() <= (launcher_bounds
.right() +
151 kPanelSnapToLauncherDistance
)) {
152 should_attach
= true;
153 offset
->set_x(launcher_bounds
.right() - bounds
.x());
156 case SHELF_ALIGNMENT_RIGHT
:
157 if (bounds
.right() >= (launcher_bounds
.x() -
158 kPanelSnapToLauncherDistance
)) {
159 should_attach
= true;
160 offset
->set_x(launcher_bounds
.x() - bounds
.width() - bounds
.x());
163 case SHELF_ALIGNMENT_TOP
:
164 if (bounds
.y() <= (launcher_bounds
.bottom() +
165 kPanelSnapToLauncherDistance
)) {
166 should_attach
= true;
167 offset
->set_y(launcher_bounds
.bottom() - bounds
.y());
172 return should_attach
;
175 void PanelWindowResizer::StartedDragging() {
176 // Tell the panel layout manager that we are dragging this panel before
177 // attaching it so that it does not get repositioned.
178 if (panel_container_
)
179 GetPanelLayoutManager(panel_container_
)->StartDragging(GetTarget());
180 if (!was_attached_
) {
181 // Attach the panel while dragging placing it in front of other panels.
182 window_state_
->set_panel_attached(true);
183 // We use root window coordinates to ensure that during the drag the panel
184 // is reparented to a container in the root window that has that window.
185 aura::Window
* target
= GetTarget();
186 aura::Window
* target_root
= target
->GetRootWindow();
187 aura::Window
* old_parent
= target
->parent();
188 aura::client::ParentWindowWithContext(
189 target
, target_root
, target_root
->GetBoundsInScreen());
190 wm::ReparentTransientChildrenOfChild(target
, old_parent
, target
->parent());
194 void PanelWindowResizer::FinishDragging() {
195 if (!did_move_or_resize_
)
197 if (window_state_
->panel_attached() != details().should_attach_to_shelf
) {
198 window_state_
->set_panel_attached(details().should_attach_to_shelf
);
199 // We use last known location to ensure that after the drag the panel
200 // is reparented to a container in the root window that has that location.
201 aura::Window
* target
= GetTarget();
202 aura::Window
* target_root
= target
->GetRootWindow();
203 aura::Window
* old_parent
= target
->parent();
204 aura::client::ParentWindowWithContext(
205 target
, target_root
, gfx::Rect(last_location_
, gfx::Size()));
206 wm::ReparentTransientChildrenOfChild(target
, old_parent
, target
->parent());
209 // If we started the drag in one root window and moved into another root
210 // but then canceled the drag we may need to inform the original layout
211 // manager that the drag is finished.
212 if (initial_panel_container_
!= panel_container_
)
213 GetPanelLayoutManager(initial_panel_container_
)->FinishDragging();
214 if (panel_container_
)
215 GetPanelLayoutManager(panel_container_
)->FinishDragging();
218 void PanelWindowResizer::UpdateLauncherPosition() {
219 if (panel_container_
) {
220 GetPanelLayoutManager(panel_container_
)->shelf()->
221 UpdateIconPositionForWindow(GetTarget());