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