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