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/panels/panel_window_resizer.h" 6 7 #include "ash/display/display_controller.h" 8 #include "ash/launcher/launcher.h" 9 #include "ash/screen_ash.h" 10 #include "ash/shelf/shelf_types.h" 11 #include "ash/shelf/shelf_widget.h" 12 #include "ash/shell.h" 13 #include "ash/shell_window_ids.h" 14 #include "ash/wm/coordinate_conversion.h" 15 #include "ash/wm/panels/panel_layout_manager.h" 16 #include "ash/wm/property_util.h" 17 #include "ash/wm/window_properties.h" 18 #include "ui/aura/client/aura_constants.h" 19 #include "ui/aura/env.h" 20 #include "ui/aura/root_window.h" 21 #include "ui/aura/window.h" 22 #include "ui/aura/window_delegate.h" 23 #include "ui/base/hit_test.h" 24 #include "ui/base/ui_base_types.h" 25 #include "ui/gfx/screen.h" 26 #include "ui/views/widget/widget.h" 27 28 namespace ash { 29 30 namespace { 31 const int kPanelSnapToLauncherDistance = 30; 32 33 internal::PanelLayoutManager* GetPanelLayoutManager( 34 aura::Window* panel_container) { 35 DCHECK(panel_container->id() == internal::kShellWindowId_PanelContainer); 36 return static_cast<internal::PanelLayoutManager*>( 37 panel_container->layout_manager()); 38 } 39 40 } // namespace 41 42 PanelWindowResizer::~PanelWindowResizer() { 43 if (destroyed_) 44 *destroyed_ = true; 45 } 46 47 // static 48 PanelWindowResizer* 49 PanelWindowResizer::Create(WindowResizer* next_window_resizer, 50 aura::Window* window, 51 const gfx::Point& location, 52 int window_component, 53 aura::client::WindowMoveSource source) { 54 Details details(window, location, window_component, source); 55 return details.is_resizable ? 56 new PanelWindowResizer(next_window_resizer, details) : NULL; 57 } 58 59 void PanelWindowResizer::Drag(const gfx::Point& location, int event_flags) { 60 last_location_ = location; 61 wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_); 62 bool destroyed = false; 63 if (!did_move_or_resize_) { 64 did_move_or_resize_ = true; 65 StartedDragging(); 66 } 67 68 // Check if the destination has changed displays. 69 gfx::Screen* screen = Shell::GetScreen(); 70 const gfx::Display dst_display = 71 screen->GetDisplayNearestPoint(last_location_); 72 if (dst_display.id() != 73 screen->GetDisplayNearestWindow(panel_container_->GetRootWindow()).id()) { 74 // The panel is being dragged to a new display. If the previous container is 75 // the current parent of the panel it will be informed of the end of drag 76 // when the panel is reparented, otherwise let the previous container know 77 // the drag is complete. If we told the panel's parent that the drag was 78 // complete it would begin positioning the panel. 79 if (GetTarget()->parent() != panel_container_) 80 GetPanelLayoutManager(panel_container_)->FinishDragging(); 81 aura::RootWindow* dst_root = Shell::GetInstance()->display_controller()-> 82 GetRootWindowForDisplayId(dst_display.id()); 83 panel_container_ = Shell::GetContainer( 84 dst_root, internal::kShellWindowId_PanelContainer); 85 86 // The panel's parent already knows that the drag is in progress for this 87 // panel. 88 if (panel_container_ && GetTarget()->parent() != panel_container_) 89 GetPanelLayoutManager(panel_container_)->StartDragging(GetTarget()); 90 } 91 gfx::Point offset; 92 gfx::Rect bounds(CalculateBoundsForDrag(details_, location)); 93 should_attach_ = AttachToLauncher(bounds, &offset); 94 gfx::Point modified_location(location.x() + offset.x(), 95 location.y() + offset.y()); 96 destroyed_ = &destroyed; 97 next_window_resizer_->Drag(modified_location, event_flags); 98 99 // TODO(flackr): Refactor the way WindowResizer calls into other window 100 // resizers to avoid the awkward pattern here for checking if 101 // next_window_resizer_ destroys the resizer object. 102 if (destroyed) 103 return; 104 destroyed_ = NULL; 105 if (should_attach_ && 106 !(details_.bounds_change & WindowResizer::kBoundsChange_Resizes)) { 107 UpdateLauncherPosition(); 108 } 109 } 110 111 void PanelWindowResizer::CompleteDrag(int event_flags) { 112 // The root window can change when dragging into a different screen. 113 next_window_resizer_->CompleteDrag(event_flags); 114 FinishDragging(); 115 } 116 117 void PanelWindowResizer::RevertDrag() { 118 next_window_resizer_->RevertDrag(); 119 should_attach_ = was_attached_; 120 FinishDragging(); 121 } 122 123 aura::Window* PanelWindowResizer::GetTarget() { 124 return next_window_resizer_->GetTarget(); 125 } 126 127 const gfx::Point& PanelWindowResizer::GetInitialLocation() const { 128 return details_.initial_location_in_parent; 129 } 130 131 PanelWindowResizer::PanelWindowResizer(WindowResizer* next_window_resizer, 132 const Details& details) 133 : details_(details), 134 next_window_resizer_(next_window_resizer), 135 panel_container_(NULL), 136 initial_panel_container_(NULL), 137 did_move_or_resize_(false), 138 was_attached_(GetTarget()->GetProperty(internal::kPanelAttachedKey)), 139 should_attach_(was_attached_), 140 destroyed_(NULL) { 141 DCHECK(details_.is_resizable); 142 panel_container_ = Shell::GetContainer( 143 details.window->GetRootWindow(), 144 internal::kShellWindowId_PanelContainer); 145 initial_panel_container_ = panel_container_; 146 } 147 148 bool PanelWindowResizer::AttachToLauncher(const gfx::Rect& bounds, 149 gfx::Point* offset) { 150 bool should_attach = false; 151 if (panel_container_) { 152 internal::PanelLayoutManager* panel_layout_manager = 153 GetPanelLayoutManager(panel_container_); 154 gfx::Rect launcher_bounds = ScreenAsh::ConvertRectFromScreen( 155 GetTarget()->parent(), 156 panel_layout_manager->launcher()-> 157 shelf_widget()->GetWindowBoundsInScreen()); 158 switch (panel_layout_manager->launcher()->alignment()) { 159 case SHELF_ALIGNMENT_BOTTOM: 160 if (bounds.bottom() >= (launcher_bounds.y() - 161 kPanelSnapToLauncherDistance)) { 162 should_attach = true; 163 offset->set_y(launcher_bounds.y() - bounds.height() - bounds.y()); 164 } 165 break; 166 case SHELF_ALIGNMENT_LEFT: 167 if (bounds.x() <= (launcher_bounds.right() + 168 kPanelSnapToLauncherDistance)) { 169 should_attach = true; 170 offset->set_x(launcher_bounds.right() - bounds.x()); 171 } 172 break; 173 case SHELF_ALIGNMENT_RIGHT: 174 if (bounds.right() >= (launcher_bounds.x() - 175 kPanelSnapToLauncherDistance)) { 176 should_attach = true; 177 offset->set_x(launcher_bounds.x() - bounds.width() - bounds.x()); 178 } 179 break; 180 case SHELF_ALIGNMENT_TOP: 181 if (bounds.y() <= (launcher_bounds.bottom() + 182 kPanelSnapToLauncherDistance)) { 183 should_attach = true; 184 offset->set_y(launcher_bounds.bottom() - bounds.y()); 185 } 186 break; 187 } 188 } 189 return should_attach; 190 } 191 192 void PanelWindowResizer::StartedDragging() { 193 // Tell the panel layout manager that we are dragging this panel before 194 // attaching it so that it does not get repositioned. 195 if (panel_container_) 196 GetPanelLayoutManager(panel_container_)->StartDragging(GetTarget()); 197 if (!was_attached_) { 198 // Attach the panel while dragging placing it in front of other panels. 199 GetTarget()->SetProperty(internal::kContinueDragAfterReparent, true); 200 GetTarget()->SetProperty(internal::kPanelAttachedKey, true); 201 // We use root window coordinates to ensure that during the drag the panel 202 // is reparented to a container in the root window that has that window. 203 GetTarget()->SetDefaultParentByRootWindow( 204 GetTarget()->GetRootWindow(), 205 GetTarget()->GetRootWindow()->GetBoundsInScreen()); 206 } 207 } 208 209 void PanelWindowResizer::FinishDragging() { 210 if (!did_move_or_resize_) 211 return; 212 if (GetTarget()->GetProperty(internal::kPanelAttachedKey) != 213 should_attach_) { 214 GetTarget()->SetProperty(internal::kPanelAttachedKey, should_attach_); 215 // We use last known location to ensure that after the drag the panel 216 // is reparented to a container in the root window that has that location. 217 GetTarget()->SetDefaultParentByRootWindow( 218 GetTarget()->GetRootWindow(), 219 gfx::Rect(last_location_, gfx::Size())); 220 } 221 222 // If we started the drag in one root window and moved into another root 223 // but then canceled the drag we may need to inform the original layout 224 // manager that the drag is finished. 225 if (initial_panel_container_ != panel_container_) 226 GetPanelLayoutManager(initial_panel_container_)->FinishDragging(); 227 if (panel_container_) 228 GetPanelLayoutManager(panel_container_)->FinishDragging(); 229 } 230 231 void PanelWindowResizer::UpdateLauncherPosition() { 232 if (panel_container_) { 233 GetPanelLayoutManager(panel_container_)->launcher()-> 234 UpdateIconPositionForWindow(GetTarget()); 235 } 236 } 237 238 } // namespace aura 239