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