panel_window_resizer.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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      did_move_or_resize_(false),
137      was_attached_(GetTarget()->GetProperty(internal::kPanelAttachedKey)),
138      should_attach_(was_attached_),
139      destroyed_(NULL) {
140  DCHECK(details_.is_resizable);
141  panel_container_ = Shell::GetContainer(
142      details.window->GetRootWindow(),
143      internal::kShellWindowId_PanelContainer);
144}
145
146bool PanelWindowResizer::AttachToLauncher(const gfx::Rect& bounds,
147                                          gfx::Point* offset) {
148  bool should_attach = false;
149  if (panel_container_) {
150    internal::PanelLayoutManager* panel_layout_manager =
151        GetPanelLayoutManager(panel_container_);
152    gfx::Rect launcher_bounds = ScreenAsh::ConvertRectFromScreen(
153        GetTarget()->parent(),
154        panel_layout_manager->launcher()->
155        shelf_widget()->GetWindowBoundsInScreen());
156    switch (panel_layout_manager->launcher()->alignment()) {
157      case SHELF_ALIGNMENT_BOTTOM:
158        if (bounds.bottom() >= (launcher_bounds.y() -
159                                kPanelSnapToLauncherDistance)) {
160          should_attach = true;
161          offset->set_y(launcher_bounds.y() - bounds.height() - bounds.y());
162        }
163        break;
164      case SHELF_ALIGNMENT_LEFT:
165        if (bounds.x() <= (launcher_bounds.right() +
166                           kPanelSnapToLauncherDistance)) {
167          should_attach = true;
168          offset->set_x(launcher_bounds.right() - bounds.x());
169        }
170        break;
171      case SHELF_ALIGNMENT_RIGHT:
172        if (bounds.right() >= (launcher_bounds.x() -
173                               kPanelSnapToLauncherDistance)) {
174          should_attach = true;
175          offset->set_x(launcher_bounds.x() - bounds.width() - bounds.x());
176        }
177        break;
178      case SHELF_ALIGNMENT_TOP:
179        if (bounds.y() <= (launcher_bounds.bottom() +
180                           kPanelSnapToLauncherDistance)) {
181          should_attach = true;
182          offset->set_y(launcher_bounds.bottom() - bounds.y());
183        }
184        break;
185    }
186  }
187  return should_attach;
188}
189
190void PanelWindowResizer::StartedDragging() {
191  // Tell the panel layout manager that we are dragging this panel before
192  // attaching it so that it does not get repositioned.
193  if (panel_container_)
194    GetPanelLayoutManager(panel_container_)->StartDragging(GetTarget());
195  if (!was_attached_) {
196    // Attach the panel while dragging placing it in front of other panels.
197    GetTarget()->SetProperty(internal::kContinueDragAfterReparent, true);
198    GetTarget()->SetProperty(internal::kPanelAttachedKey, true);
199    // We use root window coordinates to ensure that during the drag the panel
200    // is reparented to a container in the root window that has that window.
201    GetTarget()->SetDefaultParentByRootWindow(
202        GetTarget()->GetRootWindow(),
203        GetTarget()->GetRootWindow()->GetBoundsInScreen());
204  }
205}
206
207void PanelWindowResizer::FinishDragging() {
208  if (!did_move_or_resize_)
209    return;
210  if (GetTarget()->GetProperty(internal::kPanelAttachedKey) !=
211      should_attach_) {
212    GetTarget()->SetProperty(internal::kPanelAttachedKey, should_attach_);
213    // We use last known location to ensure that after the drag the panel
214    // is reparented to a container in the root window that has that location.
215    GetTarget()->SetDefaultParentByRootWindow(
216        GetTarget()->GetRootWindow(),
217        gfx::Rect(last_location_, gfx::Size()));
218  }
219  if (panel_container_)
220    GetPanelLayoutManager(panel_container_)->FinishDragging();
221}
222
223void PanelWindowResizer::UpdateLauncherPosition() {
224  if (panel_container_) {
225    GetPanelLayoutManager(panel_container_)->launcher()->
226        UpdateIconPositionForWindow(GetTarget());
227  }
228}
229
230}  // namespace aura
231