blob: 639a5d104582e2d5b74a9dc443f8bdd0f97ad66d [file] [log] [blame]
// 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/aura/wm_window_aura.h"
#include "ash/common/wm/window_positioning_utils.h"
#include "ash/common/wm/window_state.h"
#include "ash/display/mouse_cursor_event_filter.h"
#include "ash/screen_util.h"
#include "ash/shell.h"
#include "ash/wm/drag_window_controller.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/display/screen.h"
#include "ui/wm/core/coordinate_conversion.h"
#include "ui/wm/core/window_util.h"
namespace ash {
// 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 (display::Screen::GetScreen()->GetNumDisplays() > 1) {
gfx::Point location_in_screen = location;
::wm::ConvertPointToScreen(GetAuraTarget()->parent(), &location_in_screen);
UpdateDragWindow(GetAuraTarget()->bounds(), location_in_screen);
} else {
drag_window_controller_.reset();
}
}
void DragWindowResizer::CompleteDrag() {
next_window_resizer_->CompleteDrag();
GetAuraTarget()->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(GetAuraTarget()->parent(),
&last_mouse_location_in_screen);
display::Screen* screen = display::Screen::GetScreen();
const display::Display dst_display =
screen->GetDisplayNearestPoint(last_mouse_location_in_screen);
if (dst_display.id() !=
screen->GetDisplayNearestWindow(GetAuraTarget()->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 = GetAuraTarget()->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(GetAuraTarget()->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);
GetAuraTarget()->SetBoundsInScreen(dst_bounds, dst_display);
}
}
void DragWindowResizer::RevertDrag() {
next_window_resizer_->RevertDrag();
drag_window_controller_.reset();
GetAuraTarget()->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(
GetAuraTarget()->GetRootWindow());
instance_ = this;
}
void DragWindowResizer::UpdateDragWindow(
const gfx::Rect& bounds_in_parent,
const gfx::Point& drag_location_in_screen) {
if (details().window_component != HTCAPTION || !ShouldAllowMouseWarp())
return;
if (!drag_window_controller_)
drag_window_controller_.reset(new DragWindowController(GetAuraTarget()));
const gfx::Rect bounds_in_screen = ScreenUtil::ConvertRectToScreen(
GetAuraTarget()->parent(), bounds_in_parent);
gfx::Rect root_bounds_in_screen =
GetAuraTarget()->GetRootWindow()->GetBoundsInScreen();
float opacity = 1.0f;
if (!root_bounds_in_screen.Contains(drag_location_in_screen)) {
gfx::Rect visible_bounds = root_bounds_in_screen;
visible_bounds.Intersect(bounds_in_screen);
opacity = DragWindowController::GetDragWindowOpacity(bounds_in_screen,
visible_bounds);
}
GetAuraTarget()->layer()->SetOpacity(opacity);
drag_window_controller_->Update(bounds_in_screen, drag_location_in_screen);
}
bool DragWindowResizer::ShouldAllowMouseWarp() {
return details().window_component == HTCAPTION &&
!::wm::GetTransientParent(GetAuraTarget()) &&
wm::IsWindowUserPositionable(GetAuraTarget());
}
aura::Window* DragWindowResizer::GetAuraTarget() {
return WmWindowAura::GetAuraWindow(GetTarget());
}
} // namespace ash