Home | History | Annotate | Download | only in panels
      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