summaryrefslogtreecommitdiffstats
path: root/ash/wm/drag_window_resizer.cc
blob: f6a714c28b0cd93267fe9c2c0661cbb84b08ef35 (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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// 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_util.h"
#include "ash/shell.h"
#include "ash/wm/coordinate_conversion.h"
#include "ash/wm/drag_window_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "base/memory/weak_ptr.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/base/hit_test.h"
#include "ui/base/ui_base_types.h"
#include "ui/gfx/screen.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/window_util.h"

namespace ash {
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 HasSecondaryRootWindows() {
  return Shell::GetAllRootWindows().size() > 1;
}

// When there are more than one root windows, returns all root windows which are
// not |root_window|. Returns an empty vector if only one root window exists.
aura::Window::Windows GetOtherRootWindows(aura::Window* root_window) {
  aura::Window::Windows root_windows = Shell::GetAllRootWindows();
  aura::Window::Windows other_root_windows;
  if (root_windows.size() < 2)
    return other_root_windows;
  for (size_t i = 0; i < root_windows.size(); ++i) {
    if (root_windows[i] != root_window)
      other_root_windows.push_back(root_windows[i]);
  }
  return other_root_windows;
}

}  // namespace

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

DragWindowResizer::~DragWindowResizer() {
  if (window_state_)
    window_state_->DeleteDragDetails();
  Shell* shell = Shell::GetInstance();
  shell->mouse_cursor_filter()->set_mouse_warp_enabled(true);
  shell->mouse_cursor_filter()->HideSharedEdgeIndicator();
  if (instance_ == this)
    instance_ = NULL;
}

// static
DragWindowResizer* DragWindowResizer::Create(
    WindowResizer* next_window_resizer,
    wm::WindowState* window_state) {
  return new DragWindowResizer(next_window_resizer, window_state);
}

void DragWindowResizer::Drag(const gfx::Point& location, int event_flags) {
  base::WeakPtr<DragWindowResizer> resizer(weak_ptr_factory_.GetWeakPtr());
  next_window_resizer_->Drag(location, event_flags);

  if (!resizer)
    return;

  last_mouse_location_ = location;
  // Show a phantom window for dragging in another root window.
  if (HasSecondaryRootWindows()) {
    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_controllers_.clear();
  }
}

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

  GetTarget()->layer()->SetOpacity(details().initial_opacity);
  drag_window_controllers_.clear();

  // 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()) {
    // Adjust the size and position so that it doesn't exceed the size of
    // work area.
    const gfx::Size& size = dst_display.work_area().size();
    gfx::Rect bounds = GetTarget()->bounds();
    if (bounds.width() > size.width()) {
      int diff = bounds.width() - size.width();
      bounds.set_x(bounds.x() + diff / 2);
      bounds.set_width(size.width());
    }
    if (bounds.height() > size.height())
      bounds.set_height(size.height());

    gfx::Rect dst_bounds =
        ScreenUtil::ConvertRectToScreen(GetTarget()->parent(), bounds);

    // Adjust the position so that the cursor is on the window.
    if (!dst_bounds.Contains(last_mouse_location_in_screen)) {
      if (last_mouse_location_in_screen.x() < dst_bounds.x())
        dst_bounds.set_x(last_mouse_location_in_screen.x());
      else if (last_mouse_location_in_screen.x() > dst_bounds.right())
        dst_bounds.set_x(
            last_mouse_location_in_screen.x() - dst_bounds.width());
    }
    ash::wm::AdjustBoundsToEnsureMinimumWindowVisibility(
        dst_display.bounds(), &dst_bounds);

    GetTarget()->SetBoundsInScreen(dst_bounds, dst_display);
  }
}

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

  drag_window_controllers_.clear();
  GetTarget()->layer()->SetOpacity(details().initial_opacity);
}

DragWindowResizer::DragWindowResizer(WindowResizer* next_window_resizer,
                                     wm::WindowState* window_state)
    : WindowResizer(window_state),
      next_window_resizer_(next_window_resizer),
      weak_ptr_factory_(this) {
  // 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_enabled(ShouldAllowMouseWarp());
  if (ShouldAllowMouseWarp())
    mouse_cursor_filter->ShowSharedEdgeIndicator(GetTarget()->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::Window::Windows other_roots =
      GetOtherRootWindows(GetTarget()->GetRootWindow());
  size_t drag_window_controller_count = 0;
  for (size_t i = 0; i < other_roots.size(); ++i) {
    aura::Window* another_root = other_roots[i];
    const gfx::Rect root_bounds_in_screen(another_root->GetBoundsInScreen());
    const gfx::Rect bounds_in_screen =
      ScreenUtil::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_controllers_.size() < ++drag_window_controller_count)
        drag_window_controllers_.resize(drag_window_controller_count);
      ScopedVector<DragWindowController>::reference drag_window_controller =
        drag_window_controllers_.back();
      if (!drag_window_controller) {
        drag_window_controller = 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 {
      GetTarget()->layer()->SetOpacity(1.0f);
    }
  }

  // If we have more drag window controllers allocated than needed, release the
  // excess controllers by shrinking the vector |drag_window_controller_|.
  DCHECK_GE(drag_window_controllers_.size(), drag_window_controller_count);
  if (drag_window_controllers_.size() > drag_window_controller_count)
    drag_window_controllers_.resize(drag_window_controller_count);
}

bool DragWindowResizer::ShouldAllowMouseWarp() {
  return details().window_component == HTCAPTION &&
         !::wm::GetTransientParent(GetTarget()) &&
         wm::IsWindowUserPositionable(GetTarget());
}

}  // namespace ash