| // 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 |