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.
4
5 #include "ash/wm/panels/panel_window_resizer.h"
6
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/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"
29
30 namespace ash {
31
32 namespace {
33 const int kPanelSnapToLauncherDistance = 30;
34
GetPanelLayoutManager(aura::Window * panel_container)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());
40 }
41
42 } // namespace
43
~PanelWindowResizer()44 PanelWindowResizer::~PanelWindowResizer() {
45 }
46
47 // static
48 PanelWindowResizer*
Create(WindowResizer * next_window_resizer,aura::Window * window,const gfx::Point & location,int window_component,aura::client::WindowMoveSource source)49 PanelWindowResizer::Create(WindowResizer* next_window_resizer,
50 aura::Window* window,
51 const gfx::Point& location,
52 int window_component,
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;
57 }
58
Drag(const gfx::Point & location,int event_flags)59 void PanelWindowResizer::Drag(const gfx::Point& location, int event_flags) {
60 last_location_ = location;
61 wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_);
62 if (!did_move_or_resize_) {
63 did_move_or_resize_ = true;
64 StartedDragging();
65 }
66
67 // Check if the destination has changed displays.
68 gfx::Screen* screen = Shell::GetScreen();
69 const gfx::Display dst_display =
70 screen->GetDisplayNearestPoint(last_location_);
71 if (dst_display.id() !=
72 screen->GetDisplayNearestWindow(panel_container_->GetRootWindow()).id()) {
73 // The panel is being dragged to a new display. If the previous container is
74 // the current parent of the panel it will be informed of the end of drag
75 // when the panel is reparented, otherwise let the previous container know
76 // the drag is complete. If we told the panel's parent that the drag was
77 // complete it would begin positioning the panel.
78 if (GetTarget()->parent() != panel_container_)
79 GetPanelLayoutManager(panel_container_)->FinishDragging();
80 aura::Window* dst_root = Shell::GetInstance()->display_controller()->
81 GetRootWindowForDisplayId(dst_display.id());
82 panel_container_ = Shell::GetContainer(
83 dst_root, internal::kShellWindowId_PanelContainer);
84
85 // The panel's parent already knows that the drag is in progress for this
86 // panel.
87 if (panel_container_ && GetTarget()->parent() != panel_container_)
88 GetPanelLayoutManager(panel_container_)->StartDragging(GetTarget());
89 }
90 gfx::Point offset;
91 gfx::Rect bounds(CalculateBoundsForDrag(details_, location));
92 should_attach_ = AttachToLauncher(bounds, &offset);
93 gfx::Point modified_location(location.x() + offset.x(),
94 location.y() + offset.y());
95
96 base::WeakPtr<PanelWindowResizer> resizer(weak_ptr_factory_.GetWeakPtr());
97 next_window_resizer_->Drag(modified_location, event_flags);
98 if (!resizer)
99 return;
100
101 if (should_attach_ &&
102 !(details_.bounds_change & WindowResizer::kBoundsChange_Resizes)) {
103 UpdateLauncherPosition();
104 }
105 }
106
CompleteDrag(int event_flags)107 void PanelWindowResizer::CompleteDrag(int event_flags) {
108 // The root window can change when dragging into a different screen.
109 next_window_resizer_->CompleteDrag(event_flags);
110 FinishDragging();
111 }
112
RevertDrag()113 void PanelWindowResizer::RevertDrag() {
114 next_window_resizer_->RevertDrag();
115 should_attach_ = was_attached_;
116 FinishDragging();
117 }
118
GetTarget()119 aura::Window* PanelWindowResizer::GetTarget() {
120 return next_window_resizer_->GetTarget();
121 }
122
GetInitialLocation() const123 const gfx::Point& PanelWindowResizer::GetInitialLocation() const {
124 return details_.initial_location_in_parent;
125 }
126
PanelWindowResizer(WindowResizer * next_window_resizer,const Details & details)127 PanelWindowResizer::PanelWindowResizer(WindowResizer* next_window_resizer,
128 const Details& details)
129 : details_(details),
130 next_window_resizer_(next_window_resizer),
131 panel_container_(NULL),
132 initial_panel_container_(NULL),
133 did_move_or_resize_(false),
134 was_attached_(wm::GetWindowState(GetTarget())->panel_attached()),
135 should_attach_(was_attached_),
136 weak_ptr_factory_(this) {
137 DCHECK(details_.is_resizable);
138 panel_container_ = Shell::GetContainer(
139 details.window->GetRootWindow(),
140 internal::kShellWindowId_PanelContainer);
141 initial_panel_container_ = panel_container_;
142 }
143
AttachToLauncher(const gfx::Rect & bounds,gfx::Point * offset)144 bool PanelWindowResizer::AttachToLauncher(const gfx::Rect& bounds,
145 gfx::Point* offset) {
146 bool should_attach = false;
147 if (panel_container_) {
148 internal::PanelLayoutManager* panel_layout_manager =
149 GetPanelLayoutManager(panel_container_);
150 gfx::Rect launcher_bounds = ScreenAsh::ConvertRectFromScreen(
151 GetTarget()->parent(),
152 panel_layout_manager->launcher()->
153 shelf_widget()->GetWindowBoundsInScreen());
154 switch (panel_layout_manager->launcher()->alignment()) {
155 case SHELF_ALIGNMENT_BOTTOM:
156 if (bounds.bottom() >= (launcher_bounds.y() -
157 kPanelSnapToLauncherDistance)) {
158 should_attach = true;
159 offset->set_y(launcher_bounds.y() - bounds.height() - bounds.y());
160 }
161 break;
162 case SHELF_ALIGNMENT_LEFT:
163 if (bounds.x() <= (launcher_bounds.right() +
164 kPanelSnapToLauncherDistance)) {
165 should_attach = true;
166 offset->set_x(launcher_bounds.right() - bounds.x());
167 }
168 break;
169 case SHELF_ALIGNMENT_RIGHT:
170 if (bounds.right() >= (launcher_bounds.x() -
171 kPanelSnapToLauncherDistance)) {
172 should_attach = true;
173 offset->set_x(launcher_bounds.x() - bounds.width() - bounds.x());
174 }
175 break;
176 case SHELF_ALIGNMENT_TOP:
177 if (bounds.y() <= (launcher_bounds.bottom() +
178 kPanelSnapToLauncherDistance)) {
179 should_attach = true;
180 offset->set_y(launcher_bounds.bottom() - bounds.y());
181 }
182 break;
183 }
184 }
185 return should_attach;
186 }
187
StartedDragging()188 void PanelWindowResizer::StartedDragging() {
189 // Tell the panel layout manager that we are dragging this panel before
190 // attaching it so that it does not get repositioned.
191 if (panel_container_)
192 GetPanelLayoutManager(panel_container_)->StartDragging(GetTarget());
193 if (!was_attached_) {
194 // Attach the panel while dragging placing it in front of other panels.
195 wm::GetWindowState(GetTarget())->set_continue_drag_after_reparent(true);
196 wm::GetWindowState(GetTarget())->set_panel_attached(true);
197 // We use root window coordinates to ensure that during the drag the panel
198 // is reparented to a container in the root window that has that window.
199 aura::Window* target = GetTarget();
200 aura::Window* target_root = target->GetRootWindow();
201 aura::Window* old_parent = target->parent();
202 aura::client::ParentWindowWithContext(
203 target, target_root, target_root->GetBoundsInScreen());
204 wm::ReparentTransientChildrenOfChild(target, old_parent, target->parent());
205 }
206 }
207
FinishDragging()208 void PanelWindowResizer::FinishDragging() {
209 if (!did_move_or_resize_)
210 return;
211 if (details_.bounds_change & WindowResizer::kBoundsChange_Resizes)
212 should_attach_ = was_attached_;
213 if (wm::GetWindowState(GetTarget())->panel_attached() != should_attach_) {
214 wm::GetWindowState(GetTarget())->set_panel_attached(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 aura::Window* target = GetTarget();
218 aura::Window* target_root = target->GetRootWindow();
219 aura::Window* old_parent = target->parent();
220 aura::client::ParentWindowWithContext(
221 target, target_root, gfx::Rect(last_location_, gfx::Size()));
222 wm::ReparentTransientChildrenOfChild(target, old_parent, target->parent());
223 }
224
225 // If we started the drag in one root window and moved into another root
226 // but then canceled the drag we may need to inform the original layout
227 // manager that the drag is finished.
228 if (initial_panel_container_ != panel_container_)
229 GetPanelLayoutManager(initial_panel_container_)->FinishDragging();
230 if (panel_container_)
231 GetPanelLayoutManager(panel_container_)->FinishDragging();
232 }
233
UpdateLauncherPosition()234 void PanelWindowResizer::UpdateLauncherPosition() {
235 if (panel_container_) {
236 GetPanelLayoutManager(panel_container_)->launcher()->
237 UpdateIconPositionForWindow(GetTarget());
238 }
239 }
240
241 } // namespace aura
242