Home | History | Annotate | Download | only in wm
      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/drag_window_resizer.h"
      6 
      7 #include "ash/display/mouse_cursor_event_filter.h"
      8 #include "ash/screen_ash.h"
      9 #include "ash/shell.h"
     10 #include "ash/wm/coordinate_conversion.h"
     11 #include "ash/wm/drag_window_controller.h"
     12 #include "ash/wm/property_util.h"
     13 #include "ui/aura/client/aura_constants.h"
     14 #include "ui/aura/env.h"
     15 #include "ui/aura/root_window.h"
     16 #include "ui/aura/window.h"
     17 #include "ui/aura/window_delegate.h"
     18 #include "ui/base/hit_test.h"
     19 #include "ui/base/ui_base_types.h"
     20 #include "ui/gfx/screen.h"
     21 
     22 namespace ash {
     23 namespace internal {
     24 
     25 namespace {
     26 
     27 // The maximum opacity of the drag phantom window.
     28 const float kMaxOpacity = 0.8f;
     29 
     30 // Returns true if Ash has more than one root window.
     31 bool HasSecondaryRootWindow() {
     32   return Shell::GetAllRootWindows().size() > 1;
     33 }
     34 
     35 // When there are two root windows, returns one of the root windows which is not
     36 // |root_window|. Returns NULL if only one root window exists.
     37 aura::RootWindow* GetAnotherRootWindow(aura::RootWindow* root_window) {
     38   Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
     39   if (root_windows.size() < 2)
     40     return NULL;
     41   DCHECK_EQ(2U, root_windows.size());
     42   if (root_windows[0] == root_window)
     43     return root_windows[1];
     44   return root_windows[0];
     45 }
     46 
     47 }  // namespace
     48 
     49 // static
     50 DragWindowResizer* DragWindowResizer::instance_ = NULL;
     51 
     52 DragWindowResizer::~DragWindowResizer() {
     53   Shell* shell = Shell::GetInstance();
     54   shell->mouse_cursor_filter()->set_mouse_warp_mode(
     55       MouseCursorEventFilter::WARP_ALWAYS);
     56   shell->mouse_cursor_filter()->HideSharedEdgeIndicator();
     57   if (instance_ == this)
     58     instance_ = NULL;
     59 
     60   if (destroyed_)
     61     *destroyed_ = true;
     62 }
     63 
     64 // static
     65 DragWindowResizer* DragWindowResizer::Create(
     66     WindowResizer* next_window_resizer,
     67     aura::Window* window,
     68     const gfx::Point& location,
     69     int window_component,
     70     aura::client::WindowMoveSource source) {
     71   Details details(window, location, window_component, source);
     72   return details.is_resizable ?
     73       new DragWindowResizer(next_window_resizer, details) : NULL;
     74 }
     75 
     76 void DragWindowResizer::Drag(const gfx::Point& location, int event_flags) {
     77   bool destroyed = false;
     78   destroyed_ = &destroyed;
     79   next_window_resizer_->Drag(location, event_flags);
     80 
     81   // TODO(flackr): Refactor the way WindowResizer calls into other window
     82   // resizers to avoid the awkward pattern here for checking if
     83   // next_window_resizer_ destroys the resizer object.
     84   if (destroyed)
     85     return;
     86   destroyed_ = NULL;
     87   last_mouse_location_ = location;
     88 
     89   // Show a phantom window for dragging in another root window.
     90   if (HasSecondaryRootWindow()) {
     91     gfx::Point location_in_screen = location;
     92     wm::ConvertPointToScreen(GetTarget()->parent(), &location_in_screen);
     93     const bool in_original_root =
     94         wm::GetRootWindowAt(location_in_screen) == GetTarget()->GetRootWindow();
     95     UpdateDragWindow(GetTarget()->bounds(), in_original_root);
     96   } else {
     97     drag_window_controller_.reset();
     98   }
     99 }
    100 
    101 void DragWindowResizer::CompleteDrag(int event_flags) {
    102   next_window_resizer_->CompleteDrag(event_flags);
    103 
    104   GetTarget()->layer()->SetOpacity(details_.initial_opacity);
    105   drag_window_controller_.reset();
    106 
    107   // Check if the destination is another display.
    108   gfx::Point last_mouse_location_in_screen = last_mouse_location_;
    109   wm::ConvertPointToScreen(GetTarget()->parent(),
    110                            &last_mouse_location_in_screen);
    111   gfx::Screen* screen = Shell::GetScreen();
    112   const gfx::Display dst_display =
    113       screen->GetDisplayNearestPoint(last_mouse_location_in_screen);
    114 
    115   if (dst_display.id() !=
    116       screen->GetDisplayNearestWindow(GetTarget()->GetRootWindow()).id()) {
    117     const gfx::Rect dst_bounds =
    118         ScreenAsh::ConvertRectToScreen(GetTarget()->parent(),
    119                                        GetTarget()->bounds());
    120     GetTarget()->SetBoundsInScreen(dst_bounds, dst_display);
    121   }
    122 }
    123 
    124 void DragWindowResizer::RevertDrag() {
    125   next_window_resizer_->RevertDrag();
    126 
    127   drag_window_controller_.reset();
    128   GetTarget()->layer()->SetOpacity(details_.initial_opacity);
    129 }
    130 
    131 aura::Window* DragWindowResizer::GetTarget() {
    132   return next_window_resizer_->GetTarget();
    133 }
    134 
    135 const gfx::Point& DragWindowResizer::GetInitialLocation() const {
    136   return details_.initial_location_in_parent;
    137 }
    138 
    139 DragWindowResizer::DragWindowResizer(WindowResizer* next_window_resizer,
    140                                      const Details& details)
    141     : next_window_resizer_(next_window_resizer),
    142       details_(details),
    143       destroyed_(NULL) {
    144   // The pointer should be confined in one display during resizing a window
    145   // because the window cannot span two displays at the same time anyway. The
    146   // exception is window/tab dragging operation. During that operation,
    147   // |mouse_warp_mode_| should be set to WARP_DRAG so that the user could move a
    148   // window/tab to another display.
    149   MouseCursorEventFilter* mouse_cursor_filter =
    150       Shell::GetInstance()->mouse_cursor_filter();
    151   mouse_cursor_filter->set_mouse_warp_mode(
    152       ShouldAllowMouseWarp() ?
    153       MouseCursorEventFilter::WARP_DRAG : MouseCursorEventFilter::WARP_NONE);
    154   if (ShouldAllowMouseWarp()) {
    155     mouse_cursor_filter->ShowSharedEdgeIndicator(
    156         details.window->GetRootWindow());
    157   }
    158   instance_ = this;
    159 }
    160 
    161 void DragWindowResizer::UpdateDragWindow(const gfx::Rect& bounds,
    162                                          bool in_original_root) {
    163   if (details_.window_component != HTCAPTION || !ShouldAllowMouseWarp())
    164     return;
    165 
    166   // It's available. Show a phantom window on the display if needed.
    167   aura::RootWindow* another_root =
    168       GetAnotherRootWindow(GetTarget()->GetRootWindow());
    169   const gfx::Rect root_bounds_in_screen(another_root->GetBoundsInScreen());
    170   const gfx::Rect bounds_in_screen =
    171       ScreenAsh::ConvertRectToScreen(GetTarget()->parent(), bounds);
    172   gfx::Rect bounds_in_another_root =
    173       gfx::IntersectRects(root_bounds_in_screen, bounds_in_screen);
    174   const float fraction_in_another_window =
    175       (bounds_in_another_root.width() * bounds_in_another_root.height()) /
    176       static_cast<float>(bounds.width() * bounds.height());
    177 
    178   if (fraction_in_another_window > 0) {
    179     if (!drag_window_controller_) {
    180       drag_window_controller_.reset(
    181           new DragWindowController(GetTarget()));
    182       // Always show the drag phantom on the |another_root| window.
    183       drag_window_controller_->SetDestinationDisplay(
    184           Shell::GetScreen()->GetDisplayNearestWindow(another_root));
    185       drag_window_controller_->Show();
    186     } else {
    187       // No animation.
    188       drag_window_controller_->SetBounds(bounds_in_screen);
    189     }
    190     const float phantom_opacity =
    191       !in_original_root ? 1 : (kMaxOpacity * fraction_in_another_window);
    192     const float window_opacity =
    193         in_original_root ? 1 : (kMaxOpacity * (1 - fraction_in_another_window));
    194     drag_window_controller_->SetOpacity(phantom_opacity);
    195     GetTarget()->layer()->SetOpacity(window_opacity);
    196   } else {
    197     drag_window_controller_.reset();
    198     GetTarget()->layer()->SetOpacity(1.0f);
    199   }
    200 }
    201 
    202 bool DragWindowResizer::ShouldAllowMouseWarp() {
    203   return (details_.window_component == HTCAPTION) &&
    204       !GetTarget()->transient_parent() &&
    205       (GetTarget()->type() == aura::client::WINDOW_TYPE_NORMAL ||
    206        GetTarget()->type() == aura::client::WINDOW_TYPE_PANEL);
    207 }
    208 
    209 }  // namespace internal
    210 }  // namespace ash
    211