diff options
Diffstat (limited to 'chromium/ash/wm/window_resizer.cc')
-rw-r--r-- | chromium/ash/wm/window_resizer.cc | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/chromium/ash/wm/window_resizer.cc b/chromium/ash/wm/window_resizer.cc new file mode 100644 index 00000000000..372ce4889e0 --- /dev/null +++ b/chromium/ash/wm/window_resizer.cc @@ -0,0 +1,432 @@ +// 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/window_resizer.h" + +#include "ash/screen_ash.h" +#include "ash/shell.h" +#include "ash/shell_window_ids.h" +#include "ash/wm/coordinate_conversion.h" +#include "ash/wm/dock/docked_window_layout_manager.h" +#include "ash/wm/window_state.h" +#include "ash/wm/window_util.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/base/ui_base_types.h" +#include "ui/compositor/scoped_layer_animation_settings.h" +#include "ui/gfx/display.h" +#include "ui/gfx/screen.h" + +namespace ash { + +namespace { + +int GetPositionChangeDirectionForWindowComponent(int window_component) { + int pos_change_direction = WindowResizer::kBoundsChangeDirection_None; + switch (window_component) { + case HTTOPLEFT: + case HTBOTTOMRIGHT: + case HTGROWBOX: + case HTCAPTION: + pos_change_direction |= + WindowResizer::kBoundsChangeDirection_Horizontal | + WindowResizer::kBoundsChangeDirection_Vertical; + break; + case HTTOP: + case HTTOPRIGHT: + case HTBOTTOM: + pos_change_direction |= WindowResizer::kBoundsChangeDirection_Vertical; + break; + case HTBOTTOMLEFT: + case HTRIGHT: + case HTLEFT: + pos_change_direction |= WindowResizer::kBoundsChangeDirection_Horizontal; + break; + default: + break; + } + return pos_change_direction; +} + +int GetSizeChangeDirectionForWindowComponent(int window_component) { + int size_change_direction = WindowResizer::kBoundsChangeDirection_None; + switch (window_component) { + case HTTOPLEFT: + case HTTOPRIGHT: + case HTBOTTOMLEFT: + case HTBOTTOMRIGHT: + case HTGROWBOX: + case HTCAPTION: + size_change_direction |= + WindowResizer::kBoundsChangeDirection_Horizontal | + WindowResizer::kBoundsChangeDirection_Vertical; + break; + case HTTOP: + case HTBOTTOM: + size_change_direction |= WindowResizer::kBoundsChangeDirection_Vertical; + break; + case HTRIGHT: + case HTLEFT: + size_change_direction |= WindowResizer::kBoundsChangeDirection_Horizontal; + break; + default: + break; + } + return size_change_direction; +} + +// Returns true for resize components along the right edge, where a drag in +// positive x will make the window larger. +bool IsRightEdge(int window_component) { + return window_component == HTTOPRIGHT || + window_component == HTRIGHT || + window_component == HTBOTTOMRIGHT || + window_component == HTGROWBOX; +} + +} // namespace + +// static +const int WindowResizer::kBoundsChange_None = 0; +// static +const int WindowResizer::kBoundsChange_Repositions = 1; +// static +const int WindowResizer::kBoundsChange_Resizes = 2; + +// static +const int WindowResizer::kBoundsChangeDirection_None = 0; +// static +const int WindowResizer::kBoundsChangeDirection_Horizontal = 1; +// static +const int WindowResizer::kBoundsChangeDirection_Vertical = 2; + +WindowResizer::Details::Details() + : window(NULL), + window_state(NULL), + window_component(HTNOWHERE), + bounds_change(0), + position_change_direction(0), + size_change_direction(0), + is_resizable(false), + source(aura::client::WINDOW_MOVE_SOURCE_MOUSE) { +} + +WindowResizer::Details::Details(aura::Window* window, + const gfx::Point& location, + int window_component, + aura::client::WindowMoveSource source) + : window(window), + window_state(wm::GetWindowState(window)), + initial_bounds_in_parent(window->bounds()), + restore_bounds(gfx::Rect()), + initial_location_in_parent(location), + initial_opacity(window->layer()->opacity()), + window_component(window_component), + bounds_change(GetBoundsChangeForWindowComponent(window_component)), + position_change_direction( + GetPositionChangeDirectionForWindowComponent(window_component)), + size_change_direction( + GetSizeChangeDirectionForWindowComponent(window_component)), + is_resizable(bounds_change != kBoundsChangeDirection_None), + source(source) { + if (window_state->IsNormalShowState() && + window_state->HasRestoreBounds() && + window_component == HTCAPTION) + restore_bounds = window_state->GetRestoreBoundsInScreen(); +} + +WindowResizer::Details::~Details() { +} + +WindowResizer::WindowResizer() { +} + +WindowResizer::~WindowResizer() { +} + +// static +int WindowResizer::GetBoundsChangeForWindowComponent(int component) { + int bounds_change = WindowResizer::kBoundsChange_None; + switch (component) { + case HTTOPLEFT: + case HTTOP: + case HTTOPRIGHT: + case HTLEFT: + case HTBOTTOMLEFT: + bounds_change |= WindowResizer::kBoundsChange_Repositions | + WindowResizer::kBoundsChange_Resizes; + break; + case HTCAPTION: + bounds_change |= WindowResizer::kBoundsChange_Repositions; + break; + case HTRIGHT: + case HTBOTTOMRIGHT: + case HTBOTTOM: + case HTGROWBOX: + bounds_change |= WindowResizer::kBoundsChange_Resizes; + break; + default: + break; + } + return bounds_change; +} + +// static +gfx::Rect WindowResizer::CalculateBoundsForDrag( + const Details& details, + const gfx::Point& passed_location) { + if (!details.is_resizable) + return details.initial_bounds_in_parent; + + gfx::Point location = passed_location; + int delta_x = location.x() - details.initial_location_in_parent.x(); + int delta_y = location.y() - details.initial_location_in_parent.y(); + + AdjustDeltaForTouchResize(details, &delta_x, &delta_y); + + // The minimize size constraint may limit how much we change the window + // position. For example, dragging the left edge to the right should stop + // repositioning the window when the minimize size is reached. + gfx::Size size = GetSizeForDrag(details, &delta_x, &delta_y); + gfx::Point origin = GetOriginForDrag(details, delta_x, delta_y); + gfx::Rect new_bounds(origin, size); + + // Sizing has to keep the result on the screen. Note that this correction + // has to come first since it might have an impact on the origin as well as + // on the size. + if (details.bounds_change & kBoundsChange_Resizes) { + gfx::Rect work_area = + Shell::GetScreen()->GetDisplayNearestWindow(details.window).work_area(); + aura::Window* dock_container = Shell::GetContainer( + details.window->GetRootWindow(), + internal::kShellWindowId_DockedContainer); + internal::DockedWindowLayoutManager* dock_layout = + static_cast<internal::DockedWindowLayoutManager*>( + dock_container->layout_manager()); + + work_area.Union(dock_layout->docked_bounds()); + work_area = ScreenAsh::ConvertRectFromScreen(details.window->parent(), + work_area); + if (details.size_change_direction & kBoundsChangeDirection_Horizontal) { + if (IsRightEdge(details.window_component) && + new_bounds.right() < work_area.x() + kMinimumOnScreenArea) { + int delta = work_area.x() + kMinimumOnScreenArea - new_bounds.right(); + new_bounds.set_width(new_bounds.width() + delta); + } else if (new_bounds.x() > work_area.right() - kMinimumOnScreenArea) { + int width = new_bounds.right() - work_area.right() + + kMinimumOnScreenArea; + new_bounds.set_x(work_area.right() - kMinimumOnScreenArea); + new_bounds.set_width(width); + } + } + if (details.size_change_direction & kBoundsChangeDirection_Vertical) { + if (!IsBottomEdge(details.window_component) && + new_bounds.y() > work_area.bottom() - kMinimumOnScreenArea) { + int height = new_bounds.bottom() - work_area.bottom() + + kMinimumOnScreenArea; + new_bounds.set_y(work_area.bottom() - kMinimumOnScreenArea); + new_bounds.set_height(height); + } else if (details.window_component == HTBOTTOM || + details.window_component == HTBOTTOMRIGHT || + details.window_component == HTBOTTOMLEFT) { + // Update bottom edge to stay in the work area when we are resizing + // by dragging the bottom edge or corners. + if (new_bounds.bottom() > work_area.bottom()) + new_bounds.Inset(0, 0, 0, + new_bounds.bottom() - work_area.bottom()); + } + } + if (details.bounds_change & kBoundsChange_Repositions && + new_bounds.y() < 0) { + int delta = new_bounds.y(); + new_bounds.set_y(0); + new_bounds.set_height(new_bounds.height() + delta); + } + } + + if (details.bounds_change & kBoundsChange_Repositions) { + // When we might want to reposition a window which is also restored to its + // previous size, to keep the cursor within the dragged window. + if (!details.restore_bounds.IsEmpty()) { + // However - it is not desirable to change the origin if the window would + // be still hit by the cursor. + if (details.initial_location_in_parent.x() > + details.initial_bounds_in_parent.x() + details.restore_bounds.width()) + new_bounds.set_x(location.x() - details.restore_bounds.width() / 2); + } + + // Make sure that |new_bounds| doesn't leave any of the displays. Note that + // the |work_area| above isn't good for this check since it is the work area + // for the current display but the window can move to a different one. + aura::Window* parent = details.window->parent(); + gfx::Point passed_location_in_screen(passed_location); + wm::ConvertPointToScreen(parent, &passed_location_in_screen); + gfx::Rect near_passed_location(passed_location_in_screen, gfx::Size()); + // Use a pointer location (matching the logic in DragWindowResizer) to + // calculate the target display after the drag. + const gfx::Display& display = + Shell::GetScreen()->GetDisplayMatching(near_passed_location); + aura::Window* dock_container = Shell::GetContainer( + wm::GetRootWindowMatching(near_passed_location), + internal::kShellWindowId_DockedContainer); + internal::DockedWindowLayoutManager* dock_layout = + static_cast<internal::DockedWindowLayoutManager*>( + dock_container->layout_manager()); + + gfx::Rect screen_work_area = display.work_area(); + screen_work_area.Union(dock_layout->docked_bounds()); + screen_work_area.Inset(kMinimumOnScreenArea, 0); + gfx::Rect new_bounds_in_screen = + ScreenAsh::ConvertRectToScreen(parent, new_bounds); + if (!screen_work_area.Intersects(new_bounds_in_screen)) { + // Make sure that the x origin does not leave the current display. + new_bounds_in_screen.set_x( + std::max(screen_work_area.x() - new_bounds.width(), + std::min(screen_work_area.right(), + new_bounds_in_screen.x()))); + new_bounds = + ScreenAsh::ConvertRectFromScreen(parent, new_bounds_in_screen); + } + } + + return new_bounds; +} + +// static +bool WindowResizer::IsBottomEdge(int window_component) { + return window_component == HTBOTTOMLEFT || + window_component == HTBOTTOM || + window_component == HTBOTTOMRIGHT || + window_component == HTGROWBOX; +} + +// static +void WindowResizer::AdjustDeltaForTouchResize(const Details& details, + int* delta_x, + int* delta_y) { + if (details.source != aura::client::WINDOW_MOVE_SOURCE_TOUCH || + !(details.bounds_change & kBoundsChange_Resizes)) + return; + + if (details.size_change_direction & kBoundsChangeDirection_Horizontal) { + if (IsRightEdge(details.window_component)) { + *delta_x += details.initial_location_in_parent.x() - + details.initial_bounds_in_parent.right(); + } else { + *delta_x += details.initial_location_in_parent.x() - + details.initial_bounds_in_parent.x(); + } + } + if (details.size_change_direction & kBoundsChangeDirection_Vertical) { + if (IsBottomEdge(details.window_component)) { + *delta_y += details.initial_location_in_parent.y() - + details.initial_bounds_in_parent.bottom(); + } else { + *delta_y += details.initial_location_in_parent.y() - + details.initial_bounds_in_parent.y(); + } + } +} + +// static +gfx::Point WindowResizer::GetOriginForDrag(const Details& details, + int delta_x, + int delta_y) { + gfx::Point origin = details.initial_bounds_in_parent.origin(); + if (details.bounds_change & kBoundsChange_Repositions) { + int pos_change_direction = + GetPositionChangeDirectionForWindowComponent(details.window_component); + if (pos_change_direction & kBoundsChangeDirection_Horizontal) + origin.Offset(delta_x, 0); + if (pos_change_direction & kBoundsChangeDirection_Vertical) + origin.Offset(0, delta_y); + } + return origin; +} + +// static +gfx::Size WindowResizer::GetSizeForDrag(const Details& details, + int* delta_x, + int* delta_y) { + gfx::Size size = details.initial_bounds_in_parent.size(); + if (details.bounds_change & kBoundsChange_Resizes) { + gfx::Size min_size = details.window->delegate()->GetMinimumSize(); + size.SetSize(GetWidthForDrag(details, min_size.width(), delta_x), + GetHeightForDrag(details, min_size.height(), delta_y)); + } else if (!details.restore_bounds.IsEmpty()) { + size = details.restore_bounds.size(); + } + return size; +} + +// static +int WindowResizer::GetWidthForDrag(const Details& details, + int min_width, + int* delta_x) { + int width = details.initial_bounds_in_parent.width(); + if (details.size_change_direction & kBoundsChangeDirection_Horizontal) { + // Along the right edge, positive delta_x increases the window size. + int x_multiplier = IsRightEdge(details.window_component) ? 1 : -1; + width += x_multiplier * (*delta_x); + + // Ensure we don't shrink past the minimum width and clamp delta_x + // for the window origin computation. + if (width < min_width) { + width = min_width; + *delta_x = -x_multiplier * (details.initial_bounds_in_parent.width() - + min_width); + } + + // And don't let the window go bigger than the display. + int max_width = Shell::GetScreen()->GetDisplayNearestWindow( + details.window).bounds().width(); + gfx::Size max_size = details.window->delegate()->GetMaximumSize(); + if (max_size.width() != 0) + max_width = std::min(max_width, max_size.width()); + if (width > max_width) { + width = max_width; + *delta_x = -x_multiplier * (details.initial_bounds_in_parent.width() - + max_width); + } + } + return width; +} + +// static +int WindowResizer::GetHeightForDrag(const Details& details, + int min_height, + int* delta_y) { + int height = details.initial_bounds_in_parent.height(); + if (details.size_change_direction & kBoundsChangeDirection_Vertical) { + // Along the bottom edge, positive delta_y increases the window size. + int y_multiplier = IsBottomEdge(details.window_component) ? 1 : -1; + height += y_multiplier * (*delta_y); + + // Ensure we don't shrink past the minimum height and clamp delta_y + // for the window origin computation. + if (height < min_height) { + height = min_height; + *delta_y = -y_multiplier * (details.initial_bounds_in_parent.height() - + min_height); + } + + // And don't let the window go bigger than the display. + int max_height = Shell::GetScreen()->GetDisplayNearestWindow( + details.window).bounds().height(); + gfx::Size max_size = details.window->delegate()->GetMaximumSize(); + if (max_size.height() != 0) + max_height = std::min(max_height, max_size.height()); + if (height > max_height) { + height = max_height; + *delta_y = -y_multiplier * (details.initial_bounds_in_parent.height() - + max_height); + } + } + return height; +} + +} // namespace aura |