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/drag_window_resizer.h" 6 7 #include "ash/display/mouse_cursor_event_filter.h" 8 #include "ash/screen_ash.h" 9 #include "ash/shell.h" 10 #include "ash/wm/coordinate_conversion.h" 11 #include "ash/wm/drag_window_controller.h" 12 #include "ash/wm/property_util.h" 13 #include "ui/aura/client/aura_constants.h" 14 #include "ui/aura/env.h" 15 #include "ui/aura/root_window.h" 16 #include "ui/aura/window.h" 17 #include "ui/aura/window_delegate.h" 18 #include "ui/base/hit_test.h" 19 #include "ui/base/ui_base_types.h" 20 #include "ui/gfx/screen.h" 21 22 namespace ash { 23 namespace internal { 24 25 namespace { 26 27 // The maximum opacity of the drag phantom window. 28 const float kMaxOpacity = 0.8f; 29 30 // Returns true if Ash has more than one root window. 31 bool HasSecondaryRootWindow() { 32 return Shell::GetAllRootWindows().size() > 1; 33 } 34 35 // When there are two root windows, returns one of the root windows which is not 36 // |root_window|. Returns NULL if only one root window exists. 37 aura::RootWindow* GetAnotherRootWindow(aura::RootWindow* root_window) { 38 Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); 39 if (root_windows.size() < 2) 40 return NULL; 41 DCHECK_EQ(2U, root_windows.size()); 42 if (root_windows[0] == root_window) 43 return root_windows[1]; 44 return root_windows[0]; 45 } 46 47 } // namespace 48 49 // static 50 DragWindowResizer* DragWindowResizer::instance_ = NULL; 51 52 DragWindowResizer::~DragWindowResizer() { 53 Shell* shell = Shell::GetInstance(); 54 shell->mouse_cursor_filter()->set_mouse_warp_mode( 55 MouseCursorEventFilter::WARP_ALWAYS); 56 shell->mouse_cursor_filter()->HideSharedEdgeIndicator(); 57 if (instance_ == this) 58 instance_ = NULL; 59 60 if (destroyed_) 61 *destroyed_ = true; 62 } 63 64 // static 65 DragWindowResizer* DragWindowResizer::Create( 66 WindowResizer* next_window_resizer, 67 aura::Window* window, 68 const gfx::Point& location, 69 int window_component, 70 aura::client::WindowMoveSource source) { 71 Details details(window, location, window_component, source); 72 return details.is_resizable ? 73 new DragWindowResizer(next_window_resizer, details) : NULL; 74 } 75 76 void DragWindowResizer::Drag(const gfx::Point& location, int event_flags) { 77 bool destroyed = false; 78 destroyed_ = &destroyed; 79 next_window_resizer_->Drag(location, event_flags); 80 81 // TODO(flackr): Refactor the way WindowResizer calls into other window 82 // resizers to avoid the awkward pattern here for checking if 83 // next_window_resizer_ destroys the resizer object. 84 if (destroyed) 85 return; 86 destroyed_ = NULL; 87 last_mouse_location_ = location; 88 89 // Show a phantom window for dragging in another root window. 90 if (HasSecondaryRootWindow()) { 91 gfx::Point location_in_screen = location; 92 wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen); 93 const bool in_original_root = 94 wm::GetRootWindowAt(location_in_screen) == GetTarget()->GetRootWindow(); 95 UpdateDragWindow(GetTarget()->bounds(), in_original_root); 96 } else { 97 drag_window_controller_.reset(); 98 } 99 } 100 101 void DragWindowResizer::CompleteDrag(int event_flags) { 102 next_window_resizer_->CompleteDrag(event_flags); 103 104 GetTarget()->layer()->SetOpacity(details_.initial_opacity); 105 drag_window_controller_.reset(); 106 107 // Check if the destination is another display. 108 gfx::Point last_mouse_location_in_screen = last_mouse_location_; 109 wm::ConvertPointToScreen(GetTarget()->parent(), 110 &last_mouse_location_in_screen); 111 gfx::Screen* screen = Shell::GetScreen(); 112 const gfx::Display dst_display = 113 screen->GetDisplayNearestPoint(last_mouse_location_in_screen); 114 115 if (dst_display.id() != 116 screen->GetDisplayNearestWindow(GetTarget()->GetRootWindow()).id()) { 117 const gfx::Rect dst_bounds = 118 ScreenAsh::ConvertRectToScreen(GetTarget()->parent(), 119 GetTarget()->bounds()); 120 GetTarget()->SetBoundsInScreen(dst_bounds, dst_display); 121 } 122 } 123 124 void DragWindowResizer::RevertDrag() { 125 next_window_resizer_->RevertDrag(); 126 127 drag_window_controller_.reset(); 128 GetTarget()->layer()->SetOpacity(details_.initial_opacity); 129 } 130 131 aura::Window* DragWindowResizer::GetTarget() { 132 return next_window_resizer_->GetTarget(); 133 } 134 135 const gfx::Point& DragWindowResizer::GetInitialLocation() const { 136 return details_.initial_location_in_parent; 137 } 138 139 DragWindowResizer::DragWindowResizer(WindowResizer* next_window_resizer, 140 const Details& details) 141 : next_window_resizer_(next_window_resizer), 142 details_(details), 143 destroyed_(NULL) { 144 // The pointer should be confined in one display during resizing a window 145 // because the window cannot span two displays at the same time anyway. The 146 // exception is window/tab dragging operation. During that operation, 147 // |mouse_warp_mode_| should be set to WARP_DRAG so that the user could move a 148 // window/tab to another display. 149 MouseCursorEventFilter* mouse_cursor_filter = 150 Shell::GetInstance()->mouse_cursor_filter(); 151 mouse_cursor_filter->set_mouse_warp_mode( 152 ShouldAllowMouseWarp() ? 153 MouseCursorEventFilter::WARP_DRAG : MouseCursorEventFilter::WARP_NONE); 154 if (ShouldAllowMouseWarp()) { 155 mouse_cursor_filter->ShowSharedEdgeIndicator( 156 details.window->GetRootWindow()); 157 } 158 instance_ = this; 159 } 160 161 void DragWindowResizer::UpdateDragWindow(const gfx::Rect& bounds, 162 bool in_original_root) { 163 if (details_.window_component != HTCAPTION || !ShouldAllowMouseWarp()) 164 return; 165 166 // It's available. Show a phantom window on the display if needed. 167 aura::RootWindow* another_root = 168 GetAnotherRootWindow(GetTarget()->GetRootWindow()); 169 const gfx::Rect root_bounds_in_screen(another_root->GetBoundsInScreen()); 170 const gfx::Rect bounds_in_screen = 171 ScreenAsh::ConvertRectToScreen(GetTarget()->parent(), bounds); 172 gfx::Rect bounds_in_another_root = 173 gfx::IntersectRects(root_bounds_in_screen, bounds_in_screen); 174 const float fraction_in_another_window = 175 (bounds_in_another_root.width() * bounds_in_another_root.height()) / 176 static_cast<float>(bounds.width() * bounds.height()); 177 178 if (fraction_in_another_window > 0) { 179 if (!drag_window_controller_) { 180 drag_window_controller_.reset( 181 new DragWindowController(GetTarget())); 182 // Always show the drag phantom on the |another_root| window. 183 drag_window_controller_->SetDestinationDisplay( 184 Shell::GetScreen()->GetDisplayNearestWindow(another_root)); 185 drag_window_controller_->Show(); 186 } else { 187 // No animation. 188 drag_window_controller_->SetBounds(bounds_in_screen); 189 } 190 const float phantom_opacity = 191 !in_original_root ? 1 : (kMaxOpacity * fraction_in_another_window); 192 const float window_opacity = 193 in_original_root ? 1 : (kMaxOpacity * (1 - fraction_in_another_window)); 194 drag_window_controller_->SetOpacity(phantom_opacity); 195 GetTarget()->layer()->SetOpacity(window_opacity); 196 } else { 197 drag_window_controller_.reset(); 198 GetTarget()->layer()->SetOpacity(1.0f); 199 } 200 } 201 202 bool DragWindowResizer::ShouldAllowMouseWarp() { 203 return (details_.window_component == HTCAPTION) && 204 !GetTarget()->transient_parent() && 205 (GetTarget()->type() == aura::client::WINDOW_TYPE_NORMAL || 206 GetTarget()->type() == aura::client::WINDOW_TYPE_PANEL); 207 } 208 209 } // namespace internal 210 } // namespace ash 211