Home | History | Annotate | Download | only in dock
      1 // Copyright 2013 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/dock/docked_window_resizer.h"
      6 
      7 #include "ash/ash_switches.h"
      8 #include "ash/launcher/launcher.h"
      9 #include "ash/root_window_controller.h"
     10 #include "ash/screen_ash.h"
     11 #include "ash/shelf/shelf_types.h"
     12 #include "ash/shelf/shelf_widget.h"
     13 #include "ash/shell.h"
     14 #include "ash/shell_window_ids.h"
     15 #include "ash/wm/coordinate_conversion.h"
     16 #include "ash/wm/dock/docked_window_layout_manager.h"
     17 #include "ash/wm/property_util.h"
     18 #include "ash/wm/window_properties.h"
     19 #include "ash/wm/workspace_controller.h"
     20 #include "base/command_line.h"
     21 #include "ui/aura/client/aura_constants.h"
     22 #include "ui/aura/env.h"
     23 #include "ui/aura/root_window.h"
     24 #include "ui/aura/window.h"
     25 #include "ui/aura/window_delegate.h"
     26 #include "ui/base/hit_test.h"
     27 #include "ui/base/ui_base_types.h"
     28 #include "ui/gfx/screen.h"
     29 #include "ui/views/widget/widget.h"
     30 
     31 namespace ash {
     32 
     33 namespace {
     34 
     35 // Distance in pixels that the cursor must move past an edge for a window
     36 // to move beyond that edge.
     37 const int kStickyDistance = 64;
     38 
     39 // How far in pixels we have to drag to undock a window.
     40 const int kSnapToDockDistance = 32;
     41 
     42 }  // namespace
     43 
     44 DockedWindowResizer::~DockedWindowResizer() {
     45   if (destroyed_)
     46     *destroyed_ = true;
     47 }
     48 
     49 // static
     50 DockedWindowResizer*
     51 DockedWindowResizer::Create(WindowResizer* next_window_resizer,
     52                             aura::Window* window,
     53                             const gfx::Point& location,
     54                             int window_component,
     55                             aura::client::WindowMoveSource source) {
     56   Details details(window, location, window_component, source);
     57   return details.is_resizable ?
     58       new DockedWindowResizer(next_window_resizer, details) : NULL;
     59 }
     60 
     61 void DockedWindowResizer::Drag(const gfx::Point& location, int event_flags) {
     62   last_location_ = location;
     63   wm::ConvertPointToScreen(GetTarget()->parent(), &last_location_);
     64   bool destroyed = false;
     65   if (!did_move_or_resize_) {
     66     did_move_or_resize_ = true;
     67     StartedDragging();
     68   }
     69   gfx::Point offset;
     70   gfx::Rect bounds(CalculateBoundsForDrag(details_, location));
     71   bool set_tracked_by_workspace = MaybeSnapToEdge(bounds, &offset);
     72 
     73   // Temporarily clear kWindowTrackedByWorkspaceKey for windows that are snapped
     74   // to screen edges e.g. when they are docked. This prevents the windows from
     75   // getting snapped to other nearby windows during the drag.
     76   bool tracked_by_workspace = GetTrackedByWorkspace(GetTarget());
     77   if (set_tracked_by_workspace)
     78     SetTrackedByWorkspace(GetTarget(), false);
     79   gfx::Point modified_location(location.x() + offset.x(),
     80                                location.y() + offset.y());
     81   destroyed_ = &destroyed;
     82   next_window_resizer_->Drag(modified_location, event_flags);
     83 
     84   // TODO(varkha): Refactor the way WindowResizer calls into other window
     85   // resizers to avoid the awkward pattern here for checking if
     86   // next_window_resizer_ destroys the resizer object.
     87   if (destroyed)
     88     return;
     89   destroyed_ = NULL;
     90   if (set_tracked_by_workspace)
     91     SetTrackedByWorkspace(GetTarget(), tracked_by_workspace);
     92 }
     93 
     94 void DockedWindowResizer::CompleteDrag(int event_flags) {
     95   // Temporarily clear kWindowTrackedByWorkspaceKey for panels so that they
     96   // don't get forced into the workspace that may be shrunken because of docked
     97   // windows.
     98   bool tracked_by_workspace = GetTrackedByWorkspace(GetTarget());
     99   bool set_tracked_by_workspace =
    100       (was_docked_ && GetTarget()->type() == aura::client::WINDOW_TYPE_PANEL);
    101   if (set_tracked_by_workspace)
    102     SetTrackedByWorkspace(GetTarget(), false);
    103   // The root window can change when dragging into a different screen.
    104   next_window_resizer_->CompleteDrag(event_flags);
    105   if (set_tracked_by_workspace)
    106     SetTrackedByWorkspace(GetTarget(), tracked_by_workspace);
    107   FinishDragging();
    108 }
    109 
    110 void DockedWindowResizer::RevertDrag() {
    111   // Temporarily clear kWindowTrackedByWorkspaceKey for panels so that they
    112   // don't get forced into the workspace that may be shrunken because of docked
    113   // windows.
    114   bool tracked_by_workspace = GetTrackedByWorkspace(GetTarget());
    115   bool set_tracked_by_workspace =
    116       (was_docked_ && GetTarget()->type() == aura::client::WINDOW_TYPE_PANEL);
    117   if (set_tracked_by_workspace)
    118     SetTrackedByWorkspace(GetTarget(), false);
    119   next_window_resizer_->RevertDrag();
    120   if (set_tracked_by_workspace)
    121     SetTrackedByWorkspace(GetTarget(), tracked_by_workspace);
    122   FinishDragging();
    123 }
    124 
    125 aura::Window* DockedWindowResizer::GetTarget() {
    126   return next_window_resizer_->GetTarget();
    127 }
    128 
    129 const gfx::Point& DockedWindowResizer::GetInitialLocation() const {
    130   return details_.initial_location_in_parent;
    131 }
    132 
    133 DockedWindowResizer::DockedWindowResizer(WindowResizer* next_window_resizer,
    134                                          const Details& details)
    135     : details_(details),
    136       next_window_resizer_(next_window_resizer),
    137       dock_layout_(NULL),
    138       did_move_or_resize_(false),
    139       was_docked_(false),
    140       destroyed_(NULL) {
    141   DCHECK(details_.is_resizable);
    142   aura::Window* dock_container = Shell::GetContainer(
    143       details.window->GetRootWindow(),
    144       internal::kShellWindowId_DockedContainer);
    145   DCHECK(dock_container->id() == internal::kShellWindowId_DockedContainer);
    146   dock_layout_ = static_cast<internal::DockedWindowLayoutManager*>(
    147       dock_container->layout_manager());
    148   was_docked_ = details.window->parent() == dock_container;
    149 }
    150 
    151 bool DockedWindowResizer::MaybeSnapToEdge(const gfx::Rect& bounds,
    152                                           gfx::Point* offset) {
    153   aura::Window* dock_container = Shell::GetContainer(
    154       wm::GetRootWindowAt(last_location_),
    155       internal::kShellWindowId_DockedContainer);
    156   DCHECK(dock_container->id() == internal::kShellWindowId_DockedContainer);
    157   internal::DockedWindowLayoutManager* dock_layout =
    158       static_cast<internal::DockedWindowLayoutManager*>(
    159           dock_container->layout_manager());
    160   internal::DockedAlignment dock_alignment = dock_layout->CalculateAlignment();
    161   gfx::Rect dock_bounds = ScreenAsh::ConvertRectFromScreen(
    162       GetTarget()->parent(), dock_container->GetBoundsInScreen());
    163   // Windows only snap magnetically when they are close to the edge of the
    164   // screen and when the cursor is over other docked windows.
    165   // When a window being dragged is the last window that was previously
    166   // docked it is still allowed to magnetically snap to either side.
    167   bool can_snap = was_docked_ ||
    168       internal::DockedWindowLayoutManager::ShouldWindowDock(GetTarget(),
    169                                                             last_location_);
    170   if (!can_snap)
    171     return false;
    172 
    173   if (dock_alignment == internal::DOCKED_ALIGNMENT_LEFT ||
    174       (dock_alignment == internal::DOCKED_ALIGNMENT_NONE && was_docked_)) {
    175     const int distance = bounds.x() - dock_bounds.x();
    176     if (distance < kSnapToDockDistance && distance > -kStickyDistance) {
    177       offset->set_x(-distance);
    178       return true;
    179     }
    180   }
    181   if (dock_alignment == internal::DOCKED_ALIGNMENT_RIGHT ||
    182       (dock_alignment == internal::DOCKED_ALIGNMENT_NONE && was_docked_)) {
    183     const int distance = dock_bounds.right() - bounds.right();
    184     if (distance < kSnapToDockDistance && distance > -kStickyDistance) {
    185       offset->set_x(distance);
    186       return true;
    187     }
    188   }
    189   return false;
    190 }
    191 
    192 void DockedWindowResizer::StartedDragging() {
    193   // Tell the dock layout manager that we are dragging this window.
    194   dock_layout_->StartDragging(GetTarget());
    195 }
    196 
    197 void DockedWindowResizer::FinishDragging() {
    198   if (!did_move_or_resize_)
    199     return;
    200 
    201   aura::Window* window = GetTarget();
    202   bool should_dock = was_docked_;
    203   if ((details_.bounds_change & WindowResizer::kBoundsChange_Repositions) &&
    204       !(details_.bounds_change & WindowResizer::kBoundsChange_Resizes) &&
    205       CommandLine::ForCurrentProcess()->HasSwitch(
    206           switches::kAshEnableDockedWindows)) {
    207     const bool attached_panel =
    208         window->type() == aura::client::WINDOW_TYPE_PANEL &&
    209         window->GetProperty(internal::kPanelAttachedKey);
    210     should_dock = !attached_panel &&
    211         internal::DockedWindowLayoutManager::ShouldWindowDock(window,
    212                                                               last_location_);
    213   }
    214 
    215   // Check if desired docked state is not same as current.
    216   // If not same dock or undock accordingly.
    217   if (should_dock !=
    218       (window->parent()->id() == internal::kShellWindowId_DockedContainer)) {
    219     if (should_dock) {
    220       aura::Window* dock_container = Shell::GetContainer(
    221           window->GetRootWindow(),
    222           internal::kShellWindowId_DockedContainer);
    223       dock_container->AddChild(window);
    224     } else {
    225       // Reparent the window back to workspace.
    226       // We need to be careful to give SetDefaultParentByRootWindow location in
    227       // the right root window (matching the logic in DragWindowResizer) based
    228       // on which root window a mouse pointer is in. We want to undock into the
    229       // right screen near the edge of a multiscreen setup (based on where the
    230       // mouse is).
    231       gfx::Rect near_last_location(last_location_, gfx::Size());
    232       // Reparenting will cause Relayout and possible dock shrinking.
    233       window->SetDefaultParentByRootWindow(window->GetRootWindow(),
    234                                            near_last_location);
    235     }
    236   }
    237   dock_layout_->FinishDragging();
    238 }
    239 
    240 }  // namespace aura
    241