panel_window_resizer.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/root_window_controller.h"
10#include "ash/screen_ash.h"
11#include "ash/shelf/shelf_types.h"
12#include "ash/shelf/shelf_widget.h"
13#include "ash/shell.h"
14#include "ash/shell_window_ids.h"
15#include "ash/wm/coordinate_conversion.h"
16#include "ash/wm/panels/panel_layout_manager.h"
17#include "ash/wm/property_util.h"
18#include "ash/wm/window_properties.h"
19#include "ui/aura/client/aura_constants.h"
20#include "ui/aura/env.h"
21#include "ui/aura/root_window.h"
22#include "ui/aura/window.h"
23#include "ui/aura/window_delegate.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
29namespace ash {
30
31namespace {
32const int kPanelSnapToLauncherDistance = 30;
33
34internal::PanelLayoutManager* GetPanelLayoutManager(
35    aura::Window* panel_container) {
36  DCHECK(panel_container->id() == internal::kShellWindowId_PanelContainer);
37  return static_cast<internal::PanelLayoutManager*>(
38      panel_container->layout_manager());
39}
40
41}  // namespace
42
43PanelWindowResizer::~PanelWindowResizer() {
44  if (destroyed_)
45    *destroyed_ = true;
46}
47
48// static
49PanelWindowResizer*
50PanelWindowResizer::Create(WindowResizer* next_window_resizer,
51                           aura::Window* window,
52                           const gfx::Point& location,
53                           int window_component) {
54  Details details(window, location, window_component);
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  bool destroyed = false;
61  if (!did_move_or_resize_) {
62    did_move_or_resize_ = true;
63    StartedDragging();
64  }
65  gfx::Point location_in_screen = location;
66  wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen);
67
68  // Check if the destination has changed displays.
69  gfx::Screen* screen = Shell::GetScreen();
70  const gfx::Display dst_display =
71      screen->GetDisplayNearestPoint(location_in_screen);
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
127PanelWindowResizer::PanelWindowResizer(WindowResizer* next_window_resizer,
128                                       const Details& details)
129    : details_(details),
130      next_window_resizer_(next_window_resizer),
131      panel_container_(NULL),
132      did_move_or_resize_(false),
133      was_attached_(GetTarget()->GetProperty(internal::kPanelAttachedKey)),
134      should_attach_(was_attached_),
135      destroyed_(NULL) {
136  DCHECK(details_.is_resizable);
137  panel_container_ = Shell::GetContainer(
138      details.window->GetRootWindow(),
139      internal::kShellWindowId_PanelContainer);
140}
141
142bool PanelWindowResizer::AttachToLauncher(const gfx::Rect& bounds,
143                                          gfx::Point* offset) {
144  bool should_attach = false;
145  if (panel_container_) {
146    internal::PanelLayoutManager* panel_layout_manager =
147        GetPanelLayoutManager(panel_container_);
148    gfx::Rect launcher_bounds = ScreenAsh::ConvertRectFromScreen(
149        GetTarget()->parent(),
150        panel_layout_manager->launcher()->
151        shelf_widget()->GetWindowBoundsInScreen());
152    switch (panel_layout_manager->launcher()->alignment()) {
153      case SHELF_ALIGNMENT_BOTTOM:
154        if (bounds.bottom() >= (launcher_bounds.y() -
155                                kPanelSnapToLauncherDistance)) {
156          should_attach = true;
157          offset->set_y(launcher_bounds.y() - bounds.height() - bounds.y());
158        }
159        break;
160      case SHELF_ALIGNMENT_LEFT:
161        if (bounds.x() <= (launcher_bounds.right() +
162                           kPanelSnapToLauncherDistance)) {
163          should_attach = true;
164          offset->set_x(launcher_bounds.right() - bounds.x());
165        }
166        break;
167      case SHELF_ALIGNMENT_RIGHT:
168        if (bounds.right() >= (launcher_bounds.x() -
169                               kPanelSnapToLauncherDistance)) {
170          should_attach = true;
171          offset->set_x(launcher_bounds.x() - bounds.width() - bounds.x());
172        }
173        break;
174      case SHELF_ALIGNMENT_TOP:
175        if (bounds.y() <= (launcher_bounds.bottom() +
176                           kPanelSnapToLauncherDistance)) {
177          should_attach = true;
178          offset->set_y(launcher_bounds.bottom() - bounds.y());
179        }
180        break;
181    }
182  }
183  return should_attach;
184}
185
186void PanelWindowResizer::StartedDragging() {
187  // Tell the panel layout manager that we are dragging this panel before
188  // attaching it so that it does not get repositioned.
189  if (panel_container_)
190    GetPanelLayoutManager(panel_container_)->StartDragging(GetTarget());
191  if (!was_attached_) {
192    // Attach the panel while dragging placing it in front of other panels.
193    GetTarget()->SetProperty(internal::kContinueDragAfterReparent, true);
194    GetTarget()->SetProperty(internal::kPanelAttachedKey, true);
195    GetTarget()->SetDefaultParentByRootWindow(
196        GetTarget()->GetRootWindow(),
197        GetTarget()->GetBoundsInScreen());
198  }
199}
200
201void PanelWindowResizer::FinishDragging() {
202  if (!did_move_or_resize_)
203    return;
204  if (GetTarget()->GetProperty(internal::kPanelAttachedKey) !=
205      should_attach_) {
206    GetTarget()->SetProperty(internal::kPanelAttachedKey, should_attach_);
207    GetTarget()->SetDefaultParentByRootWindow(
208        GetTarget()->GetRootWindow(),
209        GetTarget()->GetBoundsInScreen());
210  }
211  if (panel_container_)
212    GetPanelLayoutManager(panel_container_)->FinishDragging();
213}
214
215void PanelWindowResizer::UpdateLauncherPosition() {
216  if (panel_container_) {
217    GetPanelLayoutManager(panel_container_)->launcher()->
218        UpdateIconPositionForWindow(GetTarget());
219  }
220}
221
222}  // namespace aura
223