|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "ash/wm/workspace/workspace_window_resizer.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <cmath> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "ash/display/display_controller.h" | 
|  | #include "ash/display/mouse_cursor_event_filter.h" | 
|  | #include "ash/screen_ash.h" | 
|  | #include "ash/shell.h" | 
|  | #include "ash/wm/coordinate_conversion.h" | 
|  | #include "ash/wm/cursor_manager.h" | 
|  | #include "ash/wm/property_util.h" | 
|  | #include "ash/wm/window_util.h" | 
|  | #include "ash/wm/workspace/phantom_window_controller.h" | 
|  | #include "ash/wm/workspace/snap_sizer.h" | 
|  | #include "ui/aura/client/aura_constants.h" | 
|  | #include "ui/aura/root_window.h" | 
|  | #include "ui/aura/window.h" | 
|  | #include "ui/aura/window_delegate.h" | 
|  | #include "ui/base/hit_test.h" | 
|  | #include "ui/compositor/layer.h" | 
|  | #include "ui/gfx/screen.h" | 
|  | #include "ui/gfx/transform.h" | 
|  |  | 
|  | namespace ash { | 
|  | namespace internal { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Duration of the animation when snapping the window into place. | 
|  | const int kSnapDurationMS = 100; | 
|  |  | 
|  | // The maximum opacity of the drag phantom window. | 
|  | const float kMaxOpacity = 0.8f; | 
|  |  | 
|  | // Returns true if should snap to the edge. | 
|  | bool ShouldSnapToEdge(int distance_from_edge, int grid_size) { | 
|  | return distance_from_edge < grid_size && | 
|  | distance_from_edge > -grid_size * 2; | 
|  | } | 
|  |  | 
|  | // Returns true if Ash has more than one root window. | 
|  | bool HasSecondaryRootWindow() { | 
|  | return Shell::GetAllRootWindows().size() > 1; | 
|  | } | 
|  |  | 
|  | // When there are two root windows, returns one of the root windows which is not | 
|  | // |root_window|. Returns NULL if only one root window exists. | 
|  | aura::RootWindow* GetAnotherRootWindow(aura::RootWindow* root_window) { | 
|  | Shell::RootWindowList root_windows = Shell::GetAllRootWindows(); | 
|  | if (root_windows.size() < 2) | 
|  | return NULL; | 
|  | DCHECK_EQ(2U, root_windows.size()); | 
|  | if (root_windows[0] == root_window) | 
|  | return root_windows[1]; | 
|  | return root_windows[0]; | 
|  | } | 
|  |  | 
|  | // Returns the origin for |src| when magnetically attaching to |attach_to| | 
|  | // along the edge |edge|. | 
|  | gfx::Point OriginForMagneticAttach(const gfx::Rect& src, | 
|  | const gfx::Rect& attach_to, | 
|  | MagnetismEdge edge) { | 
|  | switch (edge) { | 
|  | case MAGNETISM_EDGE_TOP: | 
|  | return gfx::Point(src.x(), attach_to.bottom()); | 
|  | case MAGNETISM_EDGE_LEFT: | 
|  | return gfx::Point(attach_to.right(), src.y()); | 
|  | case MAGNETISM_EDGE_BOTTOM: | 
|  | return gfx::Point(src.x(), attach_to.y() - src.height()); | 
|  | case MAGNETISM_EDGE_RIGHT: | 
|  | return gfx::Point(attach_to.x() - src.width(), src.y()); | 
|  | } | 
|  | NOTREACHED(); | 
|  | return gfx::Point(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | const int WorkspaceWindowResizer::kMinOnscreenSize = 20; | 
|  |  | 
|  | // static | 
|  | const int WorkspaceWindowResizer::kMinOnscreenHeight = 32; | 
|  |  | 
|  | // static | 
|  | const int WorkspaceWindowResizer::kScreenEdgeInset = 8; | 
|  |  | 
|  | WorkspaceWindowResizer::~WorkspaceWindowResizer() { | 
|  | Shell* shell = Shell::GetInstance(); | 
|  | shell->mouse_cursor_filter()->set_mouse_warp_mode( | 
|  | MouseCursorEventFilter::WARP_ALWAYS); | 
|  | shell->mouse_cursor_filter()->HideSharedEdgeIndicator(); | 
|  | shell->cursor_manager()->UnlockCursor(); | 
|  |  | 
|  | // Delete phantom controllers first so that they will never see the deleted | 
|  | // |layer_|. | 
|  | snap_phantom_window_controller_.reset(); | 
|  | drag_phantom_window_controller_.reset(); | 
|  |  | 
|  | if (layer_) | 
|  | wm::DeepDeleteLayers(layer_); | 
|  |  | 
|  | if (destroyed_) | 
|  | *destroyed_ = true; | 
|  | } | 
|  |  | 
|  | // static | 
|  | WorkspaceWindowResizer* WorkspaceWindowResizer::Create( | 
|  | aura::Window* window, | 
|  | const gfx::Point& location_in_parent, | 
|  | int window_component, | 
|  | const std::vector<aura::Window*>& attached_windows) { | 
|  | Details details(window, location_in_parent, window_component); | 
|  | return details.is_resizable ? | 
|  | new WorkspaceWindowResizer(details, attached_windows) : NULL; | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::Drag(const gfx::Point& location_in_parent, | 
|  | int event_flags) { | 
|  | last_mouse_location_ = location_in_parent; | 
|  |  | 
|  | int grid_size = event_flags & ui::EF_CONTROL_DOWN ? 0 : kScreenEdgeInset; | 
|  | gfx::Rect bounds =  // in |window()->parent()|'s coordinates. | 
|  | CalculateBoundsForDrag(details_, location_in_parent); | 
|  |  | 
|  | if (wm::IsWindowNormal(window())) | 
|  | AdjustBoundsForMainWindow(&bounds, grid_size); | 
|  |  | 
|  | if (bounds != window()->bounds()) { | 
|  | if (!did_move_or_resize_) { | 
|  | if (!details_.restore_bounds.IsEmpty()) | 
|  | ClearRestoreBounds(window()); | 
|  | RestackWindows(); | 
|  | } | 
|  | did_move_or_resize_ = true; | 
|  | } | 
|  |  | 
|  | gfx::Point location_in_screen = location_in_parent; | 
|  | wm::ConvertPointToScreen(window()->parent(), &location_in_screen); | 
|  | const bool in_original_root = | 
|  | wm::GetRootWindowAt(location_in_screen) == window()->GetRootWindow(); | 
|  | // Hide a phantom window for snapping if the cursor is in another root window. | 
|  | if (in_original_root) { | 
|  | UpdateSnapPhantomWindow(location_in_parent, bounds); | 
|  | } else { | 
|  | snap_type_ = SNAP_NONE; | 
|  | snap_phantom_window_controller_.reset(); | 
|  | } | 
|  |  | 
|  | if (!attached_windows_.empty()) | 
|  | LayoutAttachedWindows(bounds); | 
|  | if (bounds != window()->bounds()) { | 
|  | bool destroyed = false; | 
|  | destroyed_ = &destroyed; | 
|  | window()->SetBounds(bounds); | 
|  | if (destroyed) | 
|  | return; | 
|  | destroyed_ = NULL; | 
|  | } | 
|  | // Show a phantom window for dragging in another root window. | 
|  | if (HasSecondaryRootWindow()) | 
|  | UpdateDragPhantomWindow(bounds, in_original_root); | 
|  | else | 
|  | drag_phantom_window_controller_.reset(); | 
|  |  | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::CompleteDrag(int event_flags) { | 
|  | window()->layer()->SetOpacity(details_.initial_opacity); | 
|  | drag_phantom_window_controller_.reset(); | 
|  | snap_phantom_window_controller_.reset(); | 
|  | if (!did_move_or_resize_ || details_.window_component != HTCAPTION) | 
|  | return; | 
|  |  | 
|  | if (snap_type_ == SNAP_LEFT_EDGE || snap_type_ == SNAP_RIGHT_EDGE) { | 
|  | if (!GetRestoreBoundsInScreen(window())) | 
|  | SetRestoreBoundsInParent(window(), details_.restore_bounds.IsEmpty() ? | 
|  | details_.initial_bounds : | 
|  | details_.restore_bounds); | 
|  | window()->SetBounds(snap_sizer_->target_bounds()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | gfx::Rect bounds(GetFinalBounds(window()->bounds())); | 
|  |  | 
|  | // Check if the destination is another display. | 
|  | gfx::Point last_mouse_location_in_screen = last_mouse_location_; | 
|  | wm::ConvertPointToScreen(window()->parent(), &last_mouse_location_in_screen); | 
|  | const gfx::Display dst_display = | 
|  | gfx::Screen::GetDisplayNearestPoint(last_mouse_location_in_screen); | 
|  |  | 
|  | if (dst_display.id() != | 
|  | gfx::Screen::GetDisplayNearestWindow(window()->GetRootWindow()).id()) { | 
|  | // Don't animate when moving to another display. | 
|  | const gfx::Rect dst_bounds = | 
|  | ScreenAsh::ConvertRectToScreen(window()->parent(), bounds); | 
|  | window()->SetBoundsInScreen(dst_bounds, dst_display); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::RevertDrag() { | 
|  | window()->layer()->SetOpacity(details_.initial_opacity); | 
|  | drag_phantom_window_controller_.reset(); | 
|  | snap_phantom_window_controller_.reset(); | 
|  | Shell::GetInstance()->mouse_cursor_filter()->HideSharedEdgeIndicator(); | 
|  |  | 
|  | if (!did_move_or_resize_) | 
|  | return; | 
|  |  | 
|  | window()->SetBounds(details_.initial_bounds); | 
|  | if (!details_.restore_bounds.IsEmpty()) | 
|  | SetRestoreBoundsInScreen(details_.window, details_.restore_bounds); | 
|  |  | 
|  | if (details_.window_component == HTRIGHT) { | 
|  | int last_x = details_.initial_bounds.right(); | 
|  | for (size_t i = 0; i < attached_windows_.size(); ++i) { | 
|  | gfx::Rect bounds(attached_windows_[i]->bounds()); | 
|  | bounds.set_x(last_x); | 
|  | bounds.set_width(initial_size_[i]); | 
|  | attached_windows_[i]->SetBounds(bounds); | 
|  | last_x = attached_windows_[i]->bounds().right(); | 
|  | } | 
|  | } else { | 
|  | int last_y = details_.initial_bounds.bottom(); | 
|  | for (size_t i = 0; i < attached_windows_.size(); ++i) { | 
|  | gfx::Rect bounds(attached_windows_[i]->bounds()); | 
|  | bounds.set_y(last_y); | 
|  | bounds.set_height(initial_size_[i]); | 
|  | attached_windows_[i]->SetBounds(bounds); | 
|  | last_y = attached_windows_[i]->bounds().bottom(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | aura::Window* WorkspaceWindowResizer::GetTarget() { | 
|  | return details_.window; | 
|  | } | 
|  |  | 
|  | WorkspaceWindowResizer::WorkspaceWindowResizer( | 
|  | const Details& details, | 
|  | const std::vector<aura::Window*>& attached_windows) | 
|  | : details_(details), | 
|  | attached_windows_(attached_windows), | 
|  | did_move_or_resize_(false), | 
|  | total_min_(0), | 
|  | total_initial_size_(0), | 
|  | snap_type_(SNAP_NONE), | 
|  | num_mouse_moves_since_bounds_change_(0), | 
|  | layer_(NULL), | 
|  | destroyed_(NULL), | 
|  | magnetism_window_(NULL), | 
|  | magnetism_edge_(MAGNETISM_EDGE_TOP) { | 
|  | DCHECK(details_.is_resizable); | 
|  |  | 
|  | Shell* shell = Shell::GetInstance(); | 
|  | shell->cursor_manager()->LockCursor(); | 
|  |  | 
|  | // The pointer should be confined in one display during resizing a window | 
|  | // because the window cannot span two displays at the same time anyway. The | 
|  | // exception is window/tab dragging operation. During that operation, | 
|  | // |mouse_warp_mode_| should be set to WARP_DRAG so that the user could move a | 
|  | // window/tab to another display. | 
|  | MouseCursorEventFilter* mouse_cursor_filter = shell->mouse_cursor_filter(); | 
|  | mouse_cursor_filter->set_mouse_warp_mode( | 
|  | ShouldAllowMouseWarp() ? | 
|  | MouseCursorEventFilter::WARP_DRAG : MouseCursorEventFilter::WARP_NONE); | 
|  | if (ShouldAllowMouseWarp()) { | 
|  | mouse_cursor_filter->ShowSharedEdgeIndicator( | 
|  | details.window->GetRootWindow()); | 
|  | } | 
|  |  | 
|  | // Only support attaching to the right/bottom. | 
|  | DCHECK(attached_windows_.empty() || | 
|  | (details.window_component == HTRIGHT || | 
|  | details.window_component == HTBOTTOM)); | 
|  |  | 
|  | // TODO: figure out how to deal with window going off the edge. | 
|  |  | 
|  | // Calculate sizes so that we can maintain the ratios if we need to resize. | 
|  | int total_available = 0; | 
|  | for (size_t i = 0; i < attached_windows_.size(); ++i) { | 
|  | gfx::Size min(attached_windows_[i]->delegate()->GetMinimumSize()); | 
|  | int initial_size = PrimaryAxisSize(attached_windows_[i]->bounds().size()); | 
|  | initial_size_.push_back(initial_size); | 
|  | // If current size is smaller than the min, use the current size as the min. | 
|  | // This way we don't snap on resize. | 
|  | int min_size = std::min(initial_size, | 
|  | std::max(PrimaryAxisSize(min), kMinOnscreenSize)); | 
|  | min_size_.push_back(min_size); | 
|  | total_min_ += min_size; | 
|  | total_initial_size_ += initial_size; | 
|  | total_available += std::max(min_size, initial_size) - min_size; | 
|  | } | 
|  |  | 
|  | for (size_t i = 0; i < attached_windows_.size(); ++i) { | 
|  | expand_fraction_.push_back( | 
|  | static_cast<float>(initial_size_[i]) / | 
|  | static_cast<float>(total_initial_size_)); | 
|  | if (total_initial_size_ != total_min_) { | 
|  | compress_fraction_.push_back( | 
|  | static_cast<float>(initial_size_[i] - min_size_[i]) / | 
|  | static_cast<float>(total_available)); | 
|  | } else { | 
|  | compress_fraction_.push_back(0.0f); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | gfx::Rect WorkspaceWindowResizer::GetFinalBounds( | 
|  | const gfx::Rect& bounds) const { | 
|  | if (snap_phantom_window_controller_.get() && | 
|  | snap_phantom_window_controller_->IsShowing()) { | 
|  | return snap_phantom_window_controller_->bounds(); | 
|  | } | 
|  | return bounds; | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::LayoutAttachedWindows( | 
|  | const gfx::Rect& bounds) { | 
|  | gfx::Rect work_area(ScreenAsh::GetDisplayWorkAreaBoundsInParent(window())); | 
|  | std::vector<int> sizes; | 
|  | CalculateAttachedSizes( | 
|  | PrimaryAxisSize(details_.initial_bounds.size()), | 
|  | PrimaryAxisSize(bounds.size()), | 
|  | PrimaryAxisCoordinate(bounds.right(), bounds.bottom()), | 
|  | PrimaryAxisCoordinate(work_area.right(), work_area.bottom()), | 
|  | &sizes); | 
|  | DCHECK_EQ(attached_windows_.size(), sizes.size()); | 
|  | int last = PrimaryAxisCoordinate(bounds.right(), bounds.bottom()); | 
|  | for (size_t i = 0; i < attached_windows_.size(); ++i) { | 
|  | gfx::Rect attached_bounds(attached_windows_[i]->bounds()); | 
|  | if (details_.window_component == HTRIGHT) { | 
|  | attached_bounds.set_x(last); | 
|  | attached_bounds.set_width(sizes[i]); | 
|  | } else { | 
|  | attached_bounds.set_y(last); | 
|  | attached_bounds.set_height(sizes[i]); | 
|  | } | 
|  | attached_windows_[i]->SetBounds(attached_bounds); | 
|  | last += sizes[i]; | 
|  | } | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::CalculateAttachedSizes( | 
|  | int initial_size, | 
|  | int current_size, | 
|  | int start, | 
|  | int end, | 
|  | std::vector<int>* sizes) const { | 
|  | sizes->clear(); | 
|  | if (current_size < initial_size) { | 
|  | // If the primary window is sized smaller, resize the attached windows. | 
|  | int current = start; | 
|  | int delta = initial_size - current_size; | 
|  | for (size_t i = 0; i < attached_windows_.size(); ++i) { | 
|  | int next = current + initial_size_[i] + expand_fraction_[i] * delta; | 
|  | if (i + 1 == attached_windows_.size()) | 
|  | next = start + total_initial_size_ + (initial_size - current_size); | 
|  | sizes->push_back(next - current); | 
|  | current = next; | 
|  | } | 
|  | } else if (start <= end - total_initial_size_) { | 
|  | // All the windows fit at their initial size; tile them horizontally. | 
|  | for (size_t i = 0; i < attached_windows_.size(); ++i) | 
|  | sizes->push_back(initial_size_[i]); | 
|  | } else { | 
|  | DCHECK_NE(total_initial_size_, total_min_); | 
|  | int delta = total_initial_size_ - (end - start); | 
|  | int current = start; | 
|  | for (size_t i = 0; i < attached_windows_.size(); ++i) { | 
|  | int size = initial_size_[i] - | 
|  | static_cast<int>(compress_fraction_[i] * delta); | 
|  | if (i + 1 == attached_windows_.size()) | 
|  | size = end - current; | 
|  | current += size; | 
|  | sizes->push_back(size); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::MagneticallySnapToOtherWindows(gfx::Rect* bounds) { | 
|  | // If we snapped to a window then check it first. That way we don't bounce | 
|  | // around when close to multiple edges. | 
|  | if (magnetism_window_) { | 
|  | if (window_tracker_.Contains(magnetism_window_) && | 
|  | MagnetismMatcher::ShouldAttachOnEdge( | 
|  | *bounds, magnetism_window_->bounds(), magnetism_edge_)) { | 
|  | bounds->set_origin( | 
|  | OriginForMagneticAttach(*bounds, magnetism_window_->bounds(), | 
|  | magnetism_edge_)); | 
|  | return; | 
|  | } | 
|  | window_tracker_.Remove(magnetism_window_); | 
|  | magnetism_window_ = NULL; | 
|  | } | 
|  |  | 
|  | MagnetismMatcher matcher(*bounds); | 
|  | aura::Window* parent = window()->parent(); | 
|  | const aura::Window::Windows& windows(parent->children()); | 
|  | for (aura::Window::Windows::const_reverse_iterator i = windows.rbegin(); | 
|  | i != windows.rend() && !matcher.AreEdgesObscured(); ++i) { | 
|  | aura::Window* other = *i; | 
|  | if (other == window() || !other->IsVisible()) | 
|  | continue; | 
|  | if (matcher.ShouldAttach(other->bounds(), &magnetism_edge_)) { | 
|  | magnetism_window_ = other; | 
|  | window_tracker_.Add(magnetism_window_); | 
|  | bounds->set_origin( | 
|  | OriginForMagneticAttach(*bounds, magnetism_window_->bounds(), | 
|  | magnetism_edge_)); | 
|  | return; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::AdjustBoundsForMainWindow( | 
|  | gfx::Rect* bounds, | 
|  | int grid_size) { | 
|  |  | 
|  | gfx::Point last_mouse_location_in_screen = last_mouse_location_; | 
|  | wm::ConvertPointToScreen(window()->parent(), &last_mouse_location_in_screen); | 
|  | gfx::Display display = | 
|  | gfx::Screen::GetDisplayNearestPoint(last_mouse_location_in_screen); | 
|  | gfx::Rect work_area = | 
|  | ScreenAsh::ConvertRectFromScreen(window()->parent(), display.work_area()); | 
|  | if (details_.window_component == HTCAPTION) { | 
|  | // Adjust the bounds to the work area where the mouse cursor is located. | 
|  | // Always keep kMinOnscreenHeight on the bottom. | 
|  | int max_y = work_area.bottom() - kMinOnscreenHeight; | 
|  | if (bounds->y() > max_y) { | 
|  | bounds->set_y(max_y); | 
|  | } else if (bounds->y() <= work_area.y()) { | 
|  | // Don't allow dragging above the top of the display until the mouse | 
|  | // cursor reaches the work area above if any. | 
|  | bounds->set_y(work_area.y()); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (grid_size > 0 && details_.window_component == HTCAPTION) { | 
|  | SnapToWorkAreaEdges(work_area, bounds, grid_size); | 
|  |  | 
|  | MagneticallySnapToOtherWindows(bounds); | 
|  | } | 
|  |  | 
|  | if (attached_windows_.empty()) | 
|  | return; | 
|  |  | 
|  | if (details_.window_component == HTRIGHT) { | 
|  | bounds->set_width(std::min(bounds->width(), | 
|  | work_area.right() - total_min_ - bounds->x())); | 
|  | } else { | 
|  | DCHECK_EQ(HTBOTTOM, details_.window_component); | 
|  | bounds->set_height(std::min(bounds->height(), | 
|  | work_area.bottom() - total_min_ - bounds->y())); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::SnapToWorkAreaEdges( | 
|  | const gfx::Rect& work_area, | 
|  | gfx::Rect* bounds, | 
|  | int grid_size) const { | 
|  | int left_edge = work_area.x(); | 
|  | int right_edge = work_area.right(); | 
|  | int top_edge = work_area.y(); | 
|  | int bottom_edge = work_area.bottom(); | 
|  | if (ShouldSnapToEdge(bounds->x() - left_edge, grid_size)) { | 
|  | bounds->set_x(left_edge); | 
|  | } else if (ShouldSnapToEdge(right_edge - bounds->right(), | 
|  | grid_size)) { | 
|  | bounds->set_x(right_edge - bounds->width()); | 
|  | } | 
|  | if (ShouldSnapToEdge(bounds->y() - top_edge, grid_size)) { | 
|  | bounds->set_y(top_edge); | 
|  | } else if (ShouldSnapToEdge(bottom_edge - bounds->bottom(), grid_size) && | 
|  | bounds->height() < (bottom_edge - top_edge)) { | 
|  | // Only snap to the bottom if the window is smaller than the work area. | 
|  | // Doing otherwise can lead to window snapping in weird ways as it bounces | 
|  | // between snapping to top then bottom. | 
|  | bounds->set_y(bottom_edge - bounds->height()); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool WorkspaceWindowResizer::TouchesBottomOfScreen() const { | 
|  | gfx::Rect work_area( | 
|  | ScreenAsh::GetDisplayWorkAreaBoundsInParent(window())); | 
|  | return (attached_windows_.empty() && | 
|  | window()->bounds().bottom() == work_area.bottom()) || | 
|  | (!attached_windows_.empty() && | 
|  | attached_windows_.back()->bounds().bottom() == work_area.bottom()); | 
|  | } | 
|  |  | 
|  | int WorkspaceWindowResizer::PrimaryAxisSize(const gfx::Size& size) const { | 
|  | return PrimaryAxisCoordinate(size.width(), size.height()); | 
|  | } | 
|  |  | 
|  | int WorkspaceWindowResizer::PrimaryAxisCoordinate(int x, int y) const { | 
|  | switch (details_.window_component) { | 
|  | case HTRIGHT: | 
|  | return x; | 
|  | case HTBOTTOM: | 
|  | return y; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::UpdateDragPhantomWindow(const gfx::Rect& bounds, | 
|  | bool in_original_root) { | 
|  | if (!did_move_or_resize_ || details_.window_component != HTCAPTION || | 
|  | !ShouldAllowMouseWarp()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | // It's available. Show a phantom window on the display if needed. | 
|  | aura::RootWindow* another_root = | 
|  | GetAnotherRootWindow(window()->GetRootWindow()); | 
|  | const gfx::Rect root_bounds_in_screen(another_root->GetBoundsInScreen()); | 
|  | const gfx::Rect bounds_in_screen = | 
|  | ScreenAsh::ConvertRectToScreen(window()->parent(), bounds); | 
|  | const gfx::Rect bounds_in_another_root = | 
|  | root_bounds_in_screen.Intersect(bounds_in_screen); | 
|  |  | 
|  | const float fraction_in_another_window = | 
|  | (bounds_in_another_root.width() * bounds_in_another_root.height()) / | 
|  | static_cast<float>(bounds.width() * bounds.height()); | 
|  | const float phantom_opacity = | 
|  | !in_original_root ? 1 : (kMaxOpacity * fraction_in_another_window); | 
|  | const float window_opacity = | 
|  | in_original_root ? 1 : (kMaxOpacity * (1 - fraction_in_another_window)); | 
|  |  | 
|  | if (fraction_in_another_window > 0) { | 
|  | if (!drag_phantom_window_controller_.get()) { | 
|  | drag_phantom_window_controller_.reset( | 
|  | new PhantomWindowController(window())); | 
|  | drag_phantom_window_controller_->set_style( | 
|  | PhantomWindowController::STYLE_DRAGGING); | 
|  | // Always show the drag phantom on the |another_root| window. | 
|  | drag_phantom_window_controller_->SetDestinationDisplay( | 
|  | gfx::Screen::GetDisplayMatching(another_root->GetBoundsInScreen())); | 
|  | if (!layer_) | 
|  | RecreateWindowLayers(); | 
|  | drag_phantom_window_controller_->Show(bounds_in_screen, layer_); | 
|  | } else { | 
|  | // No animation. | 
|  | drag_phantom_window_controller_->SetBounds(bounds_in_screen); | 
|  | } | 
|  | drag_phantom_window_controller_->SetOpacity(phantom_opacity); | 
|  | window()->layer()->SetOpacity(window_opacity); | 
|  | } else { | 
|  | drag_phantom_window_controller_.reset(); | 
|  | window()->layer()->SetOpacity(1.0f); | 
|  | } | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::UpdateSnapPhantomWindow(const gfx::Point& location, | 
|  | const gfx::Rect& bounds) { | 
|  | if (!did_move_or_resize_ || details_.window_component != HTCAPTION) | 
|  | return; | 
|  |  | 
|  | SnapType last_type = snap_type_; | 
|  | snap_type_ = GetSnapType(location); | 
|  | if (snap_type_ == SNAP_NONE || snap_type_ != last_type) { | 
|  | snap_phantom_window_controller_.reset(); | 
|  | snap_sizer_.reset(); | 
|  | if (snap_type_ == SNAP_NONE) | 
|  | return; | 
|  | } | 
|  | if (!snap_sizer_.get()) { | 
|  | SnapSizer::Edge edge = (snap_type_ == SNAP_LEFT_EDGE) ? | 
|  | SnapSizer::LEFT_EDGE : SnapSizer::RIGHT_EDGE; | 
|  | snap_sizer_.reset(new SnapSizer(window(), location, edge)); | 
|  | } else { | 
|  | snap_sizer_->Update(location); | 
|  | } | 
|  | if (!snap_phantom_window_controller_.get()) { | 
|  | snap_phantom_window_controller_.reset( | 
|  | new PhantomWindowController(window())); | 
|  | } | 
|  | snap_phantom_window_controller_->Show(ScreenAsh::ConvertRectToScreen( | 
|  | window()->parent(), snap_sizer_->target_bounds()), NULL); | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::RestackWindows() { | 
|  | if (attached_windows_.empty()) | 
|  | return; | 
|  | // Build a map from index in children to window, returning if there is a | 
|  | // window with a different parent. | 
|  | typedef std::map<size_t, aura::Window*> IndexToWindowMap; | 
|  | IndexToWindowMap map; | 
|  | aura::Window* parent = window()->parent(); | 
|  | const aura::Window::Windows& windows(parent->children()); | 
|  | map[std::find(windows.begin(), windows.end(), window()) - | 
|  | windows.begin()] = window(); | 
|  | for (std::vector<aura::Window*>::const_iterator i = | 
|  | attached_windows_.begin(); i != attached_windows_.end(); ++i) { | 
|  | if ((*i)->parent() != parent) | 
|  | return; | 
|  | size_t index = | 
|  | std::find(windows.begin(), windows.end(), *i) - windows.begin(); | 
|  | map[index] = *i; | 
|  | } | 
|  |  | 
|  | // Reorder the windows starting at the topmost. | 
|  | parent->StackChildAtTop(map.rbegin()->second); | 
|  | for (IndexToWindowMap::const_reverse_iterator i = map.rbegin(); | 
|  | i != map.rend(); ) { | 
|  | aura::Window* window = i->second; | 
|  | ++i; | 
|  | if (i != map.rend()) | 
|  | parent->StackChildBelow(i->second, window); | 
|  | } | 
|  | } | 
|  |  | 
|  | WorkspaceWindowResizer::SnapType WorkspaceWindowResizer::GetSnapType( | 
|  | const gfx::Point& location) const { | 
|  | // TODO: this likely only wants total display area, not the area of a single | 
|  | // display. | 
|  | gfx::Rect area(ScreenAsh::GetDisplayBoundsInParent(window())); | 
|  | if (location.x() <= area.x()) | 
|  | return SNAP_LEFT_EDGE; | 
|  | if (location.x() >= area.right() - 1) | 
|  | return SNAP_RIGHT_EDGE; | 
|  | return SNAP_NONE; | 
|  | } | 
|  |  | 
|  | bool WorkspaceWindowResizer::ShouldAllowMouseWarp() const { | 
|  | return (details_.window_component == HTCAPTION) && | 
|  | (window()->GetProperty(aura::client::kModalKey) == ui::MODAL_TYPE_NONE) && | 
|  | (window()->type() == aura::client::WINDOW_TYPE_NORMAL); | 
|  | } | 
|  |  | 
|  | void WorkspaceWindowResizer::RecreateWindowLayers() { | 
|  | DCHECK(!layer_); | 
|  | layer_ = wm::RecreateWindowLayers(window(), true); | 
|  | layer_->set_delegate(window()->layer()->delegate()); | 
|  | // Place the layer at (0, 0) of the PhantomWindowController's window. | 
|  | gfx::Rect layer_bounds = layer_->bounds(); | 
|  | layer_bounds.set_origin(gfx::Point(0, 0)); | 
|  | layer_->SetBounds(layer_bounds); | 
|  | layer_->SetVisible(false); | 
|  | // Detach it from the current container. | 
|  | layer_->parent()->Remove(layer_); | 
|  | } | 
|  |  | 
|  | }  // namespace internal | 
|  | }  // namespace ash |