Adding proper dragging behavior for L/R maximized windows.
BUG=141750 TEST=unit tests & visual Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=155840 Review URL: https://chromiumcodereview.appspot.com/10918077 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@156058 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
@ -7,6 +7,7 @@
|
||||
#include "ash/shell.h"
|
||||
#include "ash/wm/coordinate_conversion.h"
|
||||
#include "ash/wm/cursor_manager.h"
|
||||
#include "ash/wm/property_util.h"
|
||||
#include "ui/aura/client/aura_constants.h"
|
||||
#include "ui/aura/env.h"
|
||||
#include "ui/aura/root_window.h"
|
||||
@ -42,6 +43,8 @@ void DefaultWindowResizer::Drag(const gfx::Point& location, int event_flags) {
|
||||
|
||||
gfx::Rect bounds(CalculateBoundsForDrag(details_, location));
|
||||
if (bounds != details_.window->bounds()) {
|
||||
if (!did_move_or_resize_ && !details_.restore_bounds.IsEmpty())
|
||||
ClearRestoreBounds(details_.window);
|
||||
did_move_or_resize_ = true;
|
||||
details_.window->SetBounds(bounds);
|
||||
}
|
||||
@ -55,6 +58,9 @@ void DefaultWindowResizer::RevertDrag() {
|
||||
return;
|
||||
|
||||
details_.window->SetBounds(details_.initial_bounds);
|
||||
|
||||
if (!details_.restore_bounds.IsEmpty())
|
||||
SetRestoreBoundsInScreen(details_.window, details_.restore_bounds);
|
||||
}
|
||||
|
||||
aura::Window* DefaultWindowResizer::GetTarget() {
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "ash/ash_constants.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/shell_window_ids.h"
|
||||
#include "ash/wm/property_util.h"
|
||||
#include "ash/wm/window_util.h"
|
||||
#include "ash/wm/workspace_controller.h"
|
||||
#include "base/logging.h" // DCHECK
|
||||
@ -26,6 +27,7 @@
|
||||
#include "ui/gfx/canvas.h"
|
||||
#include "ui/gfx/font.h"
|
||||
#include "ui/gfx/image/image.h"
|
||||
#include "ui/gfx/screen.h"
|
||||
#include "ui/views/controls/button/image_button.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/widget/widget_delegate.h"
|
||||
@ -246,6 +248,7 @@ int FramePainter::NonClientHitTest(views::NonClientFrameView* view,
|
||||
kResizeAreaCornerSize,
|
||||
kResizeAreaCornerSize,
|
||||
can_ever_resize);
|
||||
frame_component = AdjustFrameHitCodeForMaximizedModes(frame_component);
|
||||
if (frame_component != HTNOWHERE)
|
||||
return frame_component;
|
||||
|
||||
@ -610,6 +613,43 @@ int FramePainter::GetHeaderOpacity(HeaderMode header_mode,
|
||||
return kInactiveWindowOpacity;
|
||||
}
|
||||
|
||||
int FramePainter::AdjustFrameHitCodeForMaximizedModes(int hit_code) {
|
||||
if (hit_code != HTNOWHERE && wm::IsWindowNormal(window_) &&
|
||||
GetRestoreBoundsInScreen(window_)) {
|
||||
// When there is a restore rectangle, a left/right maximized window might
|
||||
// be active.
|
||||
const gfx::Rect& bounds = frame_->GetWindowBoundsInScreen();
|
||||
const gfx::Rect& screen =
|
||||
gfx::Screen::GetDisplayMatching(bounds).work_area();
|
||||
if (bounds.y() == screen.y() && bounds.bottom() == screen.bottom()) {
|
||||
// The window is probably either left or right maximized.
|
||||
if (bounds.x() == screen.x()) {
|
||||
// It is left maximized and we can only allow a right resize.
|
||||
return (hit_code == HTBOTTOMRIGHT ||
|
||||
hit_code == HTTOPRIGHT ||
|
||||
hit_code == HTRIGHT) ? HTRIGHT : HTNOWHERE;
|
||||
} else if (bounds.right() == screen.right()) {
|
||||
// It is right maximized and we can only allow a left resize.
|
||||
return (hit_code == HTBOTTOMLEFT ||
|
||||
hit_code == HTTOPLEFT ||
|
||||
hit_code == HTLEFT) ? HTLEFT : HTNOWHERE;
|
||||
}
|
||||
} else if (bounds.x() == screen.x() &&
|
||||
bounds.right() == screen.right()) {
|
||||
// If horizontal fill mode is activated we don't allow a left/right
|
||||
// resizing.
|
||||
if (hit_code == HTTOPRIGHT ||
|
||||
hit_code == HTTOP ||
|
||||
hit_code == HTTOPLEFT)
|
||||
return HTTOP;
|
||||
return (hit_code == HTBOTTOMRIGHT ||
|
||||
hit_code == HTBOTTOM ||
|
||||
hit_code == HTBOTTOMLEFT) ? HTBOTTOM : HTNOWHERE;
|
||||
}
|
||||
}
|
||||
return hit_code;
|
||||
}
|
||||
|
||||
// static
|
||||
bool FramePainter::UseSoloWindowHeader() {
|
||||
if (!instances_)
|
||||
|
@ -141,6 +141,9 @@ class ASH_EXPORT FramePainter : public aura::WindowObserver,
|
||||
int theme_frame_id,
|
||||
const gfx::ImageSkia* theme_frame_overlay);
|
||||
|
||||
// Adjust frame operations for left / right maximized modes.
|
||||
int AdjustFrameHitCodeForMaximizedModes(int hit_code);
|
||||
|
||||
// Returns true if there is exactly one visible, normal-type window using
|
||||
// a header painted by this class, in which case we should paint a transparent
|
||||
// window header.
|
||||
|
@ -7,18 +7,40 @@
|
||||
#include "ash/shell.h"
|
||||
#include "ash/shell_window_ids.h"
|
||||
#include "ash/test/ash_test_base.h"
|
||||
#include "ash/wm/property_util.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "grit/ui_resources.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "ui/aura/root_window.h"
|
||||
#include "ui/base/hit_test.h"
|
||||
#include "ui/gfx/screen.h"
|
||||
#include "ui/views/controls/button/image_button.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
#include "ui/views/widget/widget_delegate.h"
|
||||
#include "ui/views/window/non_client_view.h"
|
||||
|
||||
using views::Widget;
|
||||
using views::ImageButton;
|
||||
|
||||
namespace {
|
||||
|
||||
class ResizableWidgetDelegate : public views::WidgetDelegate {
|
||||
public:
|
||||
ResizableWidgetDelegate(views::Widget* widget) {
|
||||
widget_ = widget;
|
||||
}
|
||||
|
||||
virtual bool CanResize() const OVERRIDE { return true; }
|
||||
// Implementations of the widget class.
|
||||
virtual views::Widget* GetWidget() OVERRIDE { return widget_; }
|
||||
virtual const views::Widget* GetWidget() const OVERRIDE { return widget_; }
|
||||
|
||||
private:
|
||||
views::Widget* widget_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ResizableWidgetDelegate);
|
||||
};
|
||||
|
||||
// Creates a test widget that owns its native widget.
|
||||
Widget* CreateTestWidget() {
|
||||
Widget* widget = new Widget;
|
||||
@ -37,6 +59,17 @@ Widget* CreateAlwaysOnTopWidget() {
|
||||
return widget;
|
||||
}
|
||||
|
||||
Widget* CreateResizableWidget() {
|
||||
Widget* widget = new Widget;
|
||||
Widget::InitParams params;
|
||||
params.ownership = Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
|
||||
params.keep_on_top = true;
|
||||
params.delegate = new ResizableWidgetDelegate(widget);
|
||||
params.type = Widget::InitParams::TYPE_WINDOW;
|
||||
widget->Init(params);
|
||||
return widget;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace ash {
|
||||
@ -161,4 +194,38 @@ TEST_F(FramePainterTest, GetHeaderOpacity) {
|
||||
&custom_overlay));
|
||||
}
|
||||
|
||||
// Test the hit test function with windows which are "partially maximized".
|
||||
TEST_F(FramePainterTest, HitTestSpecialMaximizedModes) {
|
||||
// Create a widget and a painter for it.
|
||||
scoped_ptr<Widget> w1(CreateResizableWidget());
|
||||
FramePainter p1;
|
||||
ImageButton size1(NULL);
|
||||
ImageButton close1(NULL);
|
||||
p1.Init(w1.get(), NULL, &size1, &close1, FramePainter::SIZE_BUTTON_MAXIMIZES);
|
||||
views::NonClientFrameView* frame = w1->non_client_view()->frame_view();
|
||||
w1->Show();
|
||||
gfx::Rect any_rect = gfx::Rect(0, 0, 100, 100);
|
||||
gfx::Rect screen = gfx::Screen::GetDisplayMatching(any_rect).work_area();
|
||||
w1->SetBounds(any_rect);
|
||||
EXPECT_EQ(HTTOPLEFT, p1.NonClientHitTest(frame, gfx::Point(0, 15)));
|
||||
w1->SetBounds(gfx::Rect(
|
||||
screen.x(), screen.y(), screen.width() / 2, screen.height()));
|
||||
// A hit without a set restore rect should produce a top left hit.
|
||||
EXPECT_EQ(HTTOPLEFT, p1.NonClientHitTest(frame, gfx::Point(0, 15)));
|
||||
ash::SetRestoreBoundsInScreen(w1->GetNativeWindow(), any_rect);
|
||||
// A hit into the corner should produce nowhere - not left.
|
||||
EXPECT_EQ(HTCAPTION, p1.NonClientHitTest(frame, gfx::Point(0, 15)));
|
||||
// A hit into the middle upper area should generate right - not top&right.
|
||||
EXPECT_EQ(HTRIGHT,
|
||||
p1.NonClientHitTest(frame, gfx::Point(screen.width() / 2, 15)));
|
||||
// A hit into the middle should generate right.
|
||||
EXPECT_EQ(HTRIGHT,
|
||||
p1.NonClientHitTest(frame, gfx::Point(screen.width() / 2,
|
||||
screen.height() / 2)));
|
||||
// A hit into the middle lower area should generate right - not bottom&right.
|
||||
EXPECT_EQ(HTRIGHT,
|
||||
p1.NonClientHitTest(frame, gfx::Point(screen.width() / 2,
|
||||
screen.height() - 1)));
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
#include "ash/screen_ash.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/wm/property_util.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"
|
||||
@ -112,6 +114,7 @@ WindowResizer::Details::Details(aura::Window* window,
|
||||
int window_component)
|
||||
: window(window),
|
||||
initial_bounds(window->bounds()),
|
||||
restore_bounds(gfx::Rect()),
|
||||
initial_location_in_parent(location),
|
||||
initial_opacity(window->layer()->opacity()),
|
||||
window_component(window_component),
|
||||
@ -121,6 +124,10 @@ WindowResizer::Details::Details(aura::Window* window,
|
||||
size_change_direction(
|
||||
GetSizeChangeDirectionForWindowComponent(window_component)),
|
||||
is_resizable(bounds_change != kBoundsChangeDirection_None) {
|
||||
if (wm::IsWindowNormal(window) &&
|
||||
GetRestoreBoundsInScreen(window) &&
|
||||
window_component == HTCAPTION)
|
||||
restore_bounds = *GetRestoreBoundsInScreen(window);
|
||||
}
|
||||
|
||||
WindowResizer::Details::~Details() {
|
||||
@ -175,6 +182,17 @@ gfx::Rect WindowResizer::CalculateBoundsForDrag(
|
||||
gfx::Size size = GetSizeForDrag(details, &delta_x, &delta_y);
|
||||
gfx::Point origin = GetOriginForDrag(details, delta_x, delta_y);
|
||||
|
||||
// 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() &&
|
||||
details.bounds_change & kBoundsChange_Repositions) {
|
||||
// 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.x() + details.restore_bounds.width())
|
||||
origin.set_x(location.x() - details.restore_bounds.width() / 2);
|
||||
}
|
||||
|
||||
gfx::Rect new_bounds(origin, size);
|
||||
// Update bottom edge to stay in the work area when we are resizing
|
||||
// by dragging the bottome edge or corners.
|
||||
@ -229,6 +247,8 @@ gfx::Size WindowResizer::GetSizeForDrag(const Details& details,
|
||||
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;
|
||||
}
|
||||
|
@ -64,6 +64,10 @@ class ASH_EXPORT WindowResizer {
|
||||
// Initial bounds of the window.
|
||||
gfx::Rect initial_bounds;
|
||||
|
||||
// Restore bounds (in screen coordinates) of the window before the drag
|
||||
// started. Only set if the window is normal and is being dragged.
|
||||
gfx::Rect restore_bounds;
|
||||
|
||||
// Location passed to the constructor, in |window->parent()|'s coordinates.
|
||||
gfx::Point initial_location_in_parent;
|
||||
|
||||
|
@ -553,9 +553,12 @@ MaximizeBubbleFrameState
|
||||
gfx::Rect screen = gfx::Screen::GetDisplayMatching(bounds).work_area();
|
||||
if (bounds.width() < (screen.width() * kMinSnapSizePercent) / 100)
|
||||
return FRAME_STATE_NONE;
|
||||
// We might still have a horizontally filled window at this point which we
|
||||
// treat as no special state.
|
||||
if (bounds.y() != screen.y() || bounds.height() != screen.height())
|
||||
return FRAME_STATE_NONE;
|
||||
|
||||
// We have to be in a maximize mode at this point.
|
||||
DCHECK(bounds.y() == screen.y());
|
||||
DCHECK(bounds.height() >= screen.height());
|
||||
if (bounds.x() == screen.x())
|
||||
return FRAME_STATE_SNAP_LEFT;
|
||||
if (bounds.right() == screen.right())
|
||||
|
@ -125,8 +125,11 @@ void WorkspaceWindowResizer::Drag(const gfx::Point& location, int event_flags) {
|
||||
if (wm::IsWindowNormal(window()))
|
||||
AdjustBoundsForMainWindow(&bounds, grid_size);
|
||||
if (bounds != window()->bounds()) {
|
||||
if (!did_move_or_resize_)
|
||||
if (!did_move_or_resize_) {
|
||||
if (!details_.restore_bounds.IsEmpty())
|
||||
ClearRestoreBounds(window());
|
||||
RestackWindows();
|
||||
}
|
||||
did_move_or_resize_ = true;
|
||||
}
|
||||
|
||||
@ -164,7 +167,9 @@ void WorkspaceWindowResizer::CompleteDrag(int event_flags) {
|
||||
|
||||
if (snap_type_ == SNAP_LEFT_EDGE || snap_type_ == SNAP_RIGHT_EDGE) {
|
||||
if (!GetRestoreBoundsInScreen(window()))
|
||||
SetRestoreBoundsInParent(window(), details_.initial_bounds);
|
||||
SetRestoreBoundsInParent(window(), details_.restore_bounds.IsEmpty() ?
|
||||
details_.initial_bounds :
|
||||
details_.restore_bounds);
|
||||
window()->SetBounds(snap_sizer_->target_bounds());
|
||||
return;
|
||||
}
|
||||
@ -196,6 +201,9 @@ void WorkspaceWindowResizer::RevertDrag() {
|
||||
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) {
|
||||
|
@ -458,7 +458,6 @@ TEST_F(WorkspaceWindowResizerTest, Edge) {
|
||||
EXPECT_EQ("20,30 50x60",
|
||||
GetRestoreBoundsInScreen(window_.get())->ToString());
|
||||
}
|
||||
|
||||
// Try the same with the right side.
|
||||
scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create(
|
||||
window_.get(), gfx::Point(), HTCAPTION, empty_windows()));
|
||||
@ -869,5 +868,37 @@ TEST_F(WorkspaceWindowResizerTest, TestProperSizerResolutions) {
|
||||
EXPECT_EQ("0,0 640x552", rect.ToString());
|
||||
}
|
||||
|
||||
// Verifies that a dragged window will restore to its pre-maximized size.
|
||||
TEST_F(WorkspaceWindowResizerTest, RestoreToPreMaximizeCoordinates) {
|
||||
window_->SetBounds(gfx::Rect(0, 0, 1000, 1000));
|
||||
SetRestoreBoundsInScreen(window_.get(), gfx::Rect(96, 112, 320, 160));
|
||||
scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create(
|
||||
window_.get(), gfx::Point(), HTCAPTION, empty_windows()));
|
||||
ASSERT_TRUE(resizer.get());
|
||||
// Drag the window to new position by adding (10, 10) to original point,
|
||||
// the window should get restored.
|
||||
resizer->Drag(CalculateDragPoint(*resizer, 10, 10), 0);
|
||||
resizer->CompleteDrag(0);
|
||||
EXPECT_EQ("10,10 320x160", window_->bounds().ToString());
|
||||
// The restore rectangle should get cleared as well.
|
||||
EXPECT_EQ(NULL, GetRestoreBoundsInScreen(window_.get()));
|
||||
}
|
||||
|
||||
// Verifies that a dragged window will restore to its pre-maximized size.
|
||||
TEST_F(WorkspaceWindowResizerTest, RevertResizeOperation) {
|
||||
window_->SetBounds(gfx::Rect(0, 0, 1000, 1000));
|
||||
SetRestoreBoundsInScreen(window_.get(), gfx::Rect(96, 112, 320, 160));
|
||||
scoped_ptr<WorkspaceWindowResizer> resizer(WorkspaceWindowResizer::Create(
|
||||
window_.get(), gfx::Point(), HTCAPTION, empty_windows()));
|
||||
ASSERT_TRUE(resizer.get());
|
||||
// Drag the window to new poistion by adding (180, 16) to original point,
|
||||
// the window should get restored.
|
||||
resizer->Drag(CalculateDragPoint(*resizer, 180, 16), 0);
|
||||
resizer->RevertDrag();
|
||||
EXPECT_EQ("0,0 1000x1000", window_->bounds().ToString());
|
||||
EXPECT_EQ("96,112 320x160",
|
||||
GetRestoreBoundsInScreen(window_.get())->ToString());
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace ash
|
||||
|
@ -1258,7 +1258,7 @@ void Widget::SetInitialBounds(const gfx::Rect& bounds) {
|
||||
if (GetSavedWindowPlacement(&saved_bounds, &saved_show_state_)) {
|
||||
if (saved_show_state_ == ui::SHOW_STATE_MAXIMIZED) {
|
||||
// If we're going to maximize, wait until Show is invoked to set the
|
||||
// bounds. That way we avoid a noticable resize.
|
||||
// bounds. That way we avoid a noticeable resize.
|
||||
initial_restored_bounds_ = saved_bounds;
|
||||
} else {
|
||||
SetBounds(saved_bounds);
|
||||
|
Reference in New Issue
Block a user