panel_window_resizer.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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/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"
27
28namespace ash {
29
30namespace {
31const int kPanelSnapToLauncherDistance = 30;
32
33internal::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());
38}
39
40}  // namespace
41
42PanelWindowResizer::~PanelWindowResizer() {
43  if (destroyed_)
44    *destroyed_ = true;
45}
46
47// static
48PanelWindowResizer*
49PanelWindowResizer::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
59void 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;
65    StartedDragging();
66  }
67
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);
85
86    // The panel's parent already knows that the drag is in progress for this
87    // panel.
88    if (panel_container_ && GetTarget()->parent() != panel_container_)
89      GetPanelLayoutManager(panel_container_)->StartDragging(GetTarget());
90  }
91  gfx::Point offset;
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);
98
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.
102  if (destroyed)
103    return;
104  destroyed_ = NULL;
105  if (should_attach_ &&
106      !(details_.bounds_change & WindowResizer::kBoundsChange_Resizes)) {
107    UpdateLauncherPosition();
108  }
109}
110
111void PanelWindowResizer::CompleteDrag(int event_flags) {
112  // The root window can change when dragging into a different screen.
113  next_window_resizer_->CompleteDrag(event_flags);
114  FinishDragging();
115}
116
117void PanelWindowResizer::RevertDrag() {
118  next_window_resizer_->RevertDrag();
119  should_attach_ = was_attached_;
120  FinishDragging();
121}
122
123aura::Window* PanelWindowResizer::GetTarget() {
124  return next_window_resizer_->GetTarget();
125}
126
127const gfx::Point& PanelWindowResizer::GetInitialLocation() const {
128  return details_.initial_location_in_parent;
129}
130
131PanelWindowResizer::PanelWindowResizer(WindowResizer* next_window_resizer,
132                                       const Details& details)
133    : 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_),
140      destroyed_(NULL) {
141  DCHECK(details_.is_resizable);
142  panel_container_ = Shell::GetContainer(
143      details.window->GetRootWindow(),
144      internal::kShellWindowId_PanelContainer);
145  initial_panel_container_ = panel_container_;
146}
147
148bool 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());
164        }
165        break;
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());
171        }
172        break;
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());
178        }
179        break;
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());
185        }
186        break;
187    }
188  }
189  return should_attach;
190}
191
192void 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());
206  }
207}
208
209void PanelWindowResizer::FinishDragging() {
210  if (!did_move_or_resize_)
211    return;
212  if (GetTarget()->GetProperty(internal::kPanelAttachedKey) !=
213      should_attach_) {
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()));
220  }
221
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();
229}
230
231void PanelWindowResizer::UpdateLauncherPosition() {
232  if (panel_container_) {
233    GetPanelLayoutManager(panel_container_)->launcher()->
234        UpdateIconPositionForWindow(GetTarget());
235  }
236}
237
238}  // namespace aura
239