summaryrefslogtreecommitdiff
path: root/chromium/ash/wm/drag_window_resizer.cc
blob: e8f977e6418ba041b27c87755a4298423c8a84e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/wm/drag_window_resizer.h"

#include "ash/display/mouse_cursor_event_filter.h"
#include "ash/screen_ash.h"
#include "ash/shell.h"
#include "ash/wm/coordinate_conversion.h"
#include "ash/wm/drag_window_controller.h"
#include "ash/wm/property_util.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/base/hit_test.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/screen.h"

namespace ash {
namespace internal {

namespace {

// The maximum opacity of the drag phantom window.
const float kMaxOpacity = 0.8f;

// Returns true if Ash has more than one root window.
bool HasSecondaryRootWindow() {
  return Shell::GetAllRootWindows().size() > 1;
}

// When there are two root windows, returns one of the root windows which is not
// |root_window|. Returns NULL if only one root window exists.
aura::RootWindow* GetAnotherRootWindow(aura::RootWindow* root_window) {
  Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
  if (root_windows.size() < 2)
    return NULL;
  DCHECK_EQ(2U, root_windows.size());
  if (root_windows[0] == root_window)
    return root_windows[1];
  return root_windows[0];
}

}  // namespace

// static
DragWindowResizer* DragWindowResizer::instance_ = NULL;

DragWindowResizer::~DragWindowResizer() {
  Shell* shell = Shell::GetInstance();
  shell->mouse_cursor_filter()->set_mouse_warp_mode(
      MouseCursorEventFilter::WARP_ALWAYS);
  shell->mouse_cursor_filter()->HideSharedEdgeIndicator();
  if (instance_ == this)
    instance_ = NULL;

  if (destroyed_)
    *destroyed_ = true;
}

// static
DragWindowResizer* DragWindowResizer::Create(
    WindowResizer* next_window_resizer,
    aura::Window* window,
    const gfx::Point& location,
    int window_component,
    aura::client::WindowMoveSource source) {
  Details details(window, location, window_component, source);
  return details.is_resizable ?
      new DragWindowResizer(next_window_resizer, details) : NULL;
}

void DragWindowResizer::Drag(const gfx::Point& location, int event_flags) {
  bool destroyed = false;
  destroyed_ = &destroyed;
  next_window_resizer_->Drag(location, event_flags);

  // TODO(flackr): Refactor the way WindowResizer calls into other window
  // resizers to avoid the awkward pattern here for checking if
  // next_window_resizer_ destroys the resizer object.
  if (destroyed)
    return;
  destroyed_ = NULL;
  last_mouse_location_ = location;

  // Show a phantom window for dragging in another root window.
  if (HasSecondaryRootWindow()) {
    gfx::Point location_in_screen = location;
    wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen);
    const bool in_original_root =
        wm::GetRootWindowAt(location_in_screen) == GetTarget()->GetRootWindow();
    UpdateDragWindow(GetTarget()->bounds(), in_original_root);
  } else {
    drag_window_controller_.reset();
  }
}

void DragWindowResizer::CompleteDrag(int event_flags) {
  next_window_resizer_->CompleteDrag(event_flags);

  GetTarget()->layer()->SetOpacity(details_.initial_opacity);
  drag_window_controller_.reset();

  // Check if the destination is another display.
  gfx::Point last_mouse_location_in_screen = last_mouse_location_;
  wm::ConvertPointToScreen(GetTarget()->parent(),
                           &last_mouse_location_in_screen);
  gfx::Screen* screen = Shell::GetScreen();
  const gfx::Display dst_display =
      screen->GetDisplayNearestPoint(last_mouse_location_in_screen);

  if (dst_display.id() !=
      screen->GetDisplayNearestWindow(GetTarget()->GetRootWindow()).id()) {
    const gfx::Rect dst_bounds =
        ScreenAsh::ConvertRectToScreen(GetTarget()->parent(),
                                       GetTarget()->bounds());
    GetTarget()->SetBoundsInScreen(dst_bounds, dst_display);
  }
}

void DragWindowResizer::RevertDrag() {
  next_window_resizer_->RevertDrag();

  drag_window_controller_.reset();
  GetTarget()->layer()->SetOpacity(details_.initial_opacity);
}

aura::Window* DragWindowResizer::GetTarget() {
  return next_window_resizer_->GetTarget();
}

const gfx::Point& DragWindowResizer::GetInitialLocation() const {
  return details_.initial_location_in_parent;
}

DragWindowResizer::DragWindowResizer(WindowResizer* next_window_resizer,
                                     const Details& details)
    : next_window_resizer_(next_window_resizer),
      details_(details),
      destroyed_(NULL) {
  // The pointer should be confined in one display during resizing a window
  // because the window cannot span two displays at the same time anyway. The
  // exception is window/tab dragging operation. During that operation,
  // |mouse_warp_mode_| should be set to WARP_DRAG so that the user could move a
  // window/tab to another display.
  MouseCursorEventFilter* mouse_cursor_filter =
      Shell::GetInstance()->mouse_cursor_filter();
  mouse_cursor_filter->set_mouse_warp_mode(
      ShouldAllowMouseWarp() ?
      MouseCursorEventFilter::WARP_DRAG : MouseCursorEventFilter::WARP_NONE);
  if (ShouldAllowMouseWarp()) {
    mouse_cursor_filter->ShowSharedEdgeIndicator(
        details.window->GetRootWindow());
  }
  instance_ = this;
}

void DragWindowResizer::UpdateDragWindow(const gfx::Rect& bounds,
                                         bool in_original_root) {
  if (details_.window_component != HTCAPTION || !ShouldAllowMouseWarp())
    return;

  // It's available. Show a phantom window on the display if needed.
  aura::RootWindow* another_root =
      GetAnotherRootWindow(GetTarget()->GetRootWindow());
  const gfx::Rect root_bounds_in_screen(another_root->GetBoundsInScreen());
  const gfx::Rect bounds_in_screen =
      ScreenAsh::ConvertRectToScreen(GetTarget()->parent(), bounds);
  gfx::Rect bounds_in_another_root =
      gfx::IntersectRects(root_bounds_in_screen, bounds_in_screen);
  const float fraction_in_another_window =
      (bounds_in_another_root.width() * bounds_in_another_root.height()) /
      static_cast<float>(bounds.width() * bounds.height());

  if (fraction_in_another_window > 0) {
    if (!drag_window_controller_) {
      drag_window_controller_.reset(
          new DragWindowController(GetTarget()));
      // Always show the drag phantom on the |another_root| window.
      drag_window_controller_->SetDestinationDisplay(
          Shell::GetScreen()->GetDisplayNearestWindow(another_root));
      drag_window_controller_->Show();
    } else {
      // No animation.
      drag_window_controller_->SetBounds(bounds_in_screen);
    }
    const float phantom_opacity =
      !in_original_root ? 1 : (kMaxOpacity * fraction_in_another_window);
    const float window_opacity =
        in_original_root ? 1 : (kMaxOpacity * (1 - fraction_in_another_window));
    drag_window_controller_->SetOpacity(phantom_opacity);
    GetTarget()->layer()->SetOpacity(window_opacity);
  } else {
    drag_window_controller_.reset();
    GetTarget()->layer()->SetOpacity(1.0f);
  }
}

bool DragWindowResizer::ShouldAllowMouseWarp() {
  return (details_.window_component == HTCAPTION) &&
      !GetTarget()->transient_parent() &&
      (GetTarget()->type() == aura::client::WINDOW_TYPE_NORMAL ||
       GetTarget()->type() == aura::client::WINDOW_TYPE_PANEL);
}

}  // namespace internal
}  // namespace ash