From 9633a9d264d54a0ae8d6d0dd9c27b0231f149b24 Mon Sep 17 00:00:00 2001 From: "sky@chromium.org" Date: Sat, 21 Jan 2012 16:40:58 +0000 Subject: Renames and moves some workspace related classes to the workspace directory. BUG=none TEST=none Review URL: https://chromiumcodereview.appspot.com/9226040 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@118620 0039d316-1c4b-4281-b951-d872f2087c98 --- ash/wm/default_container_event_filter.cc | 131 -------- ash/wm/default_container_event_filter.h | 55 ---- ash/wm/default_container_layout_manager.cc | 190 ------------ ash/wm/default_container_layout_manager.h | 77 ----- .../default_container_layout_manager_unittest.cc | 341 --------------------- ash/wm/workspace/workspace_event_filter.cc | 131 ++++++++ ash/wm/workspace/workspace_event_filter.h | 55 ++++ ash/wm/workspace/workspace_layout_manager.cc | 190 ++++++++++++ ash/wm/workspace/workspace_layout_manager.h | 76 +++++ .../workspace/workspace_layout_manager_unittest.cc | 341 +++++++++++++++++++++ ash/wm/workspace_controller.cc | 2 +- 11 files changed, 794 insertions(+), 795 deletions(-) delete mode 100644 ash/wm/default_container_event_filter.cc delete mode 100644 ash/wm/default_container_event_filter.h delete mode 100644 ash/wm/default_container_layout_manager.cc delete mode 100644 ash/wm/default_container_layout_manager.h delete mode 100644 ash/wm/default_container_layout_manager_unittest.cc create mode 100644 ash/wm/workspace/workspace_event_filter.cc create mode 100644 ash/wm/workspace/workspace_event_filter.h create mode 100644 ash/wm/workspace/workspace_layout_manager.cc create mode 100644 ash/wm/workspace/workspace_layout_manager.h create mode 100644 ash/wm/workspace/workspace_layout_manager_unittest.cc (limited to 'ash/wm') diff --git a/ash/wm/default_container_event_filter.cc b/ash/wm/default_container_event_filter.cc deleted file mode 100644 index 241c1af..0000000 --- a/ash/wm/default_container_event_filter.cc +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) 2011 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/default_container_event_filter.h" - -#include "ash/wm/default_container_layout_manager.h" -#include "ash/wm/window_frame.h" -#include "ash/wm/window_util.h" -#include "ui/aura/event.h" -#include "ui/aura/window.h" -#include "ui/base/hit_test.h" - -namespace { - -// Sends OnWindowHoveredChanged(|hovered|) to the WindowFrame for |window|, -// which may be NULL. -void WindowHoverChanged(aura::Window* window, bool hovered) { - if (!window) - return; - ash::WindowFrame* window_frame = - static_cast( - window->GetProperty(ash::kWindowFrameKey)); - if (!window_frame) - return; - window_frame->OnWindowHoverChanged(hovered); -} - -} // namespace - -namespace ash { -namespace internal { - -DefaultContainerEventFilter::DefaultContainerEventFilter(aura::Window* owner) - : ToplevelWindowEventFilter(owner), - drag_state_(DRAG_NONE), - hovered_window_(NULL) { -} - -DefaultContainerEventFilter::~DefaultContainerEventFilter() { -} - -bool DefaultContainerEventFilter::PreHandleMouseEvent(aura::Window* target, - aura::MouseEvent* event) { - DefaultContainerLayoutManager* layout_manager = - static_cast(owner()->layout_manager()); - DCHECK(layout_manager); - - // TODO(oshima|derat): Incorporate the logic below and introduce DragObserver - // (or something similar) to decouple DCLM. - - // Notify layout manager that drag event may move/resize the target wnidow. - if (event->type() == ui::ET_MOUSE_DRAGGED && drag_state_ == DRAG_NONE) - layout_manager->PrepareForMoveOrResize(target, event); - - bool handled = ToplevelWindowEventFilter::PreHandleMouseEvent(target, event); - - switch (event->type()) { - case ui::ET_MOUSE_DRAGGED: - // Cancel move/resize if the event wasn't handled, or - // drag_state_ didn't move to MOVE or RESIZE. - if (handled) { - switch (drag_state_) { - case DRAG_NONE: - if (!UpdateDragState()) - layout_manager->CancelMoveOrResize(target, event); - break; - case DRAG_MOVE: - layout_manager->ProcessMove(target, event); - break; - case DRAG_RESIZE: - break; - } - } else { - layout_manager->CancelMoveOrResize(target, event); - } - break; - case ui::ET_MOUSE_ENTERED: - UpdateHoveredWindow(GetActivatableWindow(target)); - break; - case ui::ET_MOUSE_EXITED: - UpdateHoveredWindow(NULL); - break; - case ui::ET_MOUSE_RELEASED: - if (drag_state_ == DRAG_MOVE) - layout_manager->EndMove(target, event); - else if (drag_state_ == DRAG_RESIZE) - layout_manager->EndResize(target, event); - - drag_state_ = DRAG_NONE; - break; - default: - break; - } - return handled; -} - -bool DefaultContainerEventFilter::UpdateDragState() { - DCHECK_EQ(DRAG_NONE, drag_state_); - switch (window_component()) { - case HTCAPTION: - drag_state_ = DRAG_MOVE; - break; - case HTTOP: - case HTTOPRIGHT: - case HTRIGHT: - case HTBOTTOMRIGHT: - case HTBOTTOM: - case HTBOTTOMLEFT: - case HTLEFT: - case HTTOPLEFT: - case HTGROWBOX: - drag_state_ = DRAG_RESIZE; - break; - default: - return false; - } - return true; -} - -void DefaultContainerEventFilter::UpdateHoveredWindow( - aura::Window* toplevel_window) { - if (toplevel_window == hovered_window_) - return; - WindowHoverChanged(hovered_window_, false); - hovered_window_ = toplevel_window; - WindowHoverChanged(hovered_window_, true); -} - -} // namespace internal -} // namespace ash diff --git a/ash/wm/default_container_event_filter.h b/ash/wm/default_container_event_filter.h deleted file mode 100644 index 55baf01..0000000 --- a/ash/wm/default_container_event_filter.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright (c) 2011 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. - -#ifndef ASH_WM_DEFAULT_CONTAINER_EVENT_FILTER_H_ -#define ASH_WM_DEFAULT_CONTAINER_EVENT_FILTER_H_ -#pragma once - -#include "ash/wm/toplevel_window_event_filter.h" - -namespace aura { -class MouseEvent; -class Window; -} - -namespace ash { -namespace internal { - -class DefaultContainerEventFilter : public ToplevelWindowEventFilter { - public: - explicit DefaultContainerEventFilter(aura::Window* owner); - virtual ~DefaultContainerEventFilter(); - - // Overridden from ToplevelWindowEventFilter: - virtual bool PreHandleMouseEvent(aura::Window* target, - aura::MouseEvent* event) OVERRIDE; - - private: - enum DragState { - DRAG_NONE, - DRAG_MOVE, - DRAG_RESIZE - }; - - // If the mouse is currently over a portion of the window that should - // trigger a drag or resize, drag_state_ is set appropriately and true - // is returned. If the mouse is not over a portion of the window that should - // trigger a more or resize, drag_state_ is not updated and false is returend. - bool UpdateDragState(); - - // Updates the top-level window under the mouse so that we can change - // the look of the caption area based on mouse-hover. - void UpdateHoveredWindow(aura::Window* toplevel); - - DragState drag_state_; - // Top-level window under the mouse cursor. - aura::Window* hovered_window_; - - DISALLOW_COPY_AND_ASSIGN(DefaultContainerEventFilter); -}; - -} // namespace internal -} // namespace ash - -#endif // ASH_WM_DEFAULT_CONTAINER_EVENT_FILTER_H_ diff --git a/ash/wm/default_container_layout_manager.cc b/ash/wm/default_container_layout_manager.cc deleted file mode 100644 index 83360c0..0000000 --- a/ash/wm/default_container_layout_manager.cc +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) 2011 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/default_container_layout_manager.h" - -#include "ash/wm/property_util.h" -#include "ash/wm/show_state_controller.h" -#include "ash/wm/window_util.h" -#include "ash/wm/workspace/workspace.h" -#include "ash/wm/workspace/workspace_manager.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/event.h" -#include "ui/aura/root_window.h" -#include "ui/aura/screen_aura.h" -#include "ui/aura/window.h" -#include "ui/aura/window_observer.h" -#include "ui/base/ui_base_types.h" -#include "ui/gfx/rect.h" -#include "ui/views/widget/native_widget_aura.h" - -namespace ash { -namespace internal { - -//////////////////////////////////////////////////////////////////////////////// -// DefaultContainerLayoutManager, public: - -DefaultContainerLayoutManager::DefaultContainerLayoutManager( - WorkspaceManager* workspace_manager) - : workspace_manager_(workspace_manager), - show_state_controller_(new ShowStateController(workspace_manager)) { -} - -DefaultContainerLayoutManager::~DefaultContainerLayoutManager() {} - -void DefaultContainerLayoutManager::PrepareForMoveOrResize( - aura::Window* drag, - aura::MouseEvent* event) { - workspace_manager_->set_ignored_window(drag); -} - -void DefaultContainerLayoutManager::CancelMoveOrResize( - aura::Window* drag, - aura::MouseEvent* event) { - workspace_manager_->set_ignored_window(NULL); -} - -void DefaultContainerLayoutManager::ProcessMove( - aura::Window* drag, - aura::MouseEvent* event) { - // TODO(oshima): Just zooming out may (and will) move/swap window without - // a users's intent. We probably should scroll viewport, but that may not - // be enough. See crbug.com/101826 for more discussion. - workspace_manager_->SetOverview(true); - - gfx::Point point_in_owner = event->location(); - aura::Window::ConvertPointToWindow( - drag, - workspace_manager_->contents_view(), - &point_in_owner); - // TODO(oshima): We should support simply moving to another - // workspace when the destination workspace has enough room to accomodate. - aura::Window* rotate_target = - workspace_manager_->FindRotateWindowForLocation(point_in_owner); - if (rotate_target) - workspace_manager_->RotateWindows(drag, rotate_target); -} - -void DefaultContainerLayoutManager::EndMove( - aura::Window* drag, - aura::MouseEvent* evnet) { - // TODO(oshima): finish moving window between workspaces. - workspace_manager_->set_ignored_window(NULL); - Workspace* workspace = workspace_manager_->FindBy(drag); - workspace->Layout(NULL); - workspace->Activate(); - workspace_manager_->SetOverview(false); -} - -void DefaultContainerLayoutManager::EndResize( - aura::Window* drag, - aura::MouseEvent* evnet) { - workspace_manager_->set_ignored_window(NULL); - Workspace* workspace = workspace_manager_->GetActiveWorkspace(); - if (workspace) - workspace->Layout(NULL); - workspace_manager_->SetOverview(false); -} - -//////////////////////////////////////////////////////////////////////////////// -// DefaultContainerLayoutManager, aura::LayoutManager implementation: - -void DefaultContainerLayoutManager::OnWindowResized() { - // Workspace is updated via RootWindowObserver::OnRootWindowResized. -} - -void DefaultContainerLayoutManager::OnWindowAddedToLayout(aura::Window* child) { - if (child->type() != aura::client::WINDOW_TYPE_NORMAL || - child->transient_parent()) { - return; - } - - if (!child->GetProperty(aura::client::kShowStateKey)) - child->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); - - child->AddObserver(show_state_controller_.get()); - - Workspace* workspace = workspace_manager_->GetActiveWorkspace(); - if (workspace) { - aura::Window* active = ash::GetActiveWindow(); - // Active window may not be in the default container layer. - if (!workspace->Contains(active)) - active = NULL; - if (workspace->AddWindowAfter(child, active)) - return; - } - // Create new workspace if new |child| doesn't fit to current workspace. - Workspace* new_workspace = workspace_manager_->CreateWorkspace(); - new_workspace->AddWindowAfter(child, NULL); - new_workspace->Activate(); -} - -void DefaultContainerLayoutManager::OnWillRemoveWindowFromLayout( - aura::Window* child) { - child->RemoveObserver(show_state_controller_.get()); - ClearRestoreBounds(child); - - Workspace* workspace = workspace_manager_->FindBy(child); - if (!workspace) - return; - workspace->RemoveWindow(child); - if (workspace->is_empty()) - delete workspace; -} - -void DefaultContainerLayoutManager::OnChildWindowVisibilityChanged( - aura::Window* child, - bool visible) { - NOTIMPLEMENTED(); -} - -void DefaultContainerLayoutManager::SetChildBounds( - aura::Window* child, - const gfx::Rect& requested_bounds) { - gfx::Rect adjusted_bounds = requested_bounds; - - // First, calculate the adjusted bounds. - if (child->type() != aura::client::WINDOW_TYPE_NORMAL || - workspace_manager_->layout_in_progress() || - child->transient_parent()) { - // Use the requested bounds as is. - } else if (child == workspace_manager_->ignored_window()) { - // If a drag window is requesting bounds, make sure its attached to - // the workarea's top and fits within the total drag area. - gfx::Rect drag_area = workspace_manager_->GetDragAreaBounds(); - adjusted_bounds.set_y(drag_area.y()); - adjusted_bounds = adjusted_bounds.AdjustToFit(drag_area); - } else { - Workspace* workspace = workspace_manager_->FindBy(child); - gfx::Rect work_area = workspace->GetWorkAreaBounds(); - adjusted_bounds.set_origin( - gfx::Point(child->GetTargetBounds().x(), work_area.y())); - adjusted_bounds = adjusted_bounds.AdjustToFit(work_area); - } - - ui::WindowShowState show_state = static_cast( - child->GetIntProperty(aura::client::kShowStateKey)); - - // Second, check if the window is either maximized or in fullscreen mode. - if (show_state == ui::SHOW_STATE_MAXIMIZED || - show_state == ui::SHOW_STATE_FULLSCREEN) { - // If the request is not from workspace manager, - // remember the requested bounds. - if (!workspace_manager_->layout_in_progress()) - SetRestoreBounds(child, adjusted_bounds); - - Workspace* workspace = workspace_manager_->FindBy(child); - if (show_state == ui::SHOW_STATE_MAXIMIZED) - adjusted_bounds = workspace->GetWorkAreaBounds(); - else - adjusted_bounds = workspace->bounds(); - // Don't - if (child->GetTargetBounds() == adjusted_bounds) - return; - } - SetChildBoundsDirect(child, adjusted_bounds); -} - -} // namespace internal -} // namespace ash diff --git a/ash/wm/default_container_layout_manager.h b/ash/wm/default_container_layout_manager.h deleted file mode 100644 index b61866d..0000000 --- a/ash/wm/default_container_layout_manager.h +++ /dev/null @@ -1,77 +0,0 @@ -// Copyright (c) 2011 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. - -#ifndef ASH_WM_DEFAULT_CONTAINER_LAYOUT_MANAGER_H_ -#define ASH_WM_DEFAULT_CONTAINER_LAYOUT_MANAGER_H_ -#pragma once - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_ptr.h" -#include "ui/aura/layout_manager.h" -#include "ash/ash_export.h" - -namespace aura { -class MouseEvent; -class Window; -} - -namespace gfx { -class Rect; -} - -namespace ash { -namespace internal { - -class ShowStateController; -class WorkspaceManager; - -// LayoutManager for the default window container. -class ASH_EXPORT DefaultContainerLayoutManager - : public aura::LayoutManager { - public: - explicit DefaultContainerLayoutManager(WorkspaceManager* workspace_manager); - virtual ~DefaultContainerLayoutManager(); - - // Returns the workspace manager for this container. - WorkspaceManager* workspace_manager() { - return workspace_manager_; - } - - // Invoked when a window receives drag event. - void PrepareForMoveOrResize(aura::Window* drag, aura::MouseEvent* event); - - // Invoked when a drag event didn't start any drag operation. - void CancelMoveOrResize(aura::Window* drag, aura::MouseEvent* event); - - // Invoked when a drag event moved the |window|. - void ProcessMove(aura::Window* window, aura::MouseEvent* event); - - // Invoked when a user finished moving window. - void EndMove(aura::Window* drag, aura::MouseEvent* evnet); - - // Invoked when a user finished resizing window. - void EndResize(aura::Window* drag, aura::MouseEvent* evnet); - - // Overridden from aura::LayoutManager: - virtual void OnWindowResized() OVERRIDE; - virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE; - virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE; - virtual void OnChildWindowVisibilityChanged(aura::Window* child, - bool visibile) OVERRIDE; - virtual void SetChildBounds(aura::Window* child, - const gfx::Rect& requested_bounds) OVERRIDE; - private: - // Owned by WorkspaceController. - WorkspaceManager* workspace_manager_; - - scoped_ptr show_state_controller_; - - DISALLOW_COPY_AND_ASSIGN(DefaultContainerLayoutManager); -}; - -} // namespace internal -} // namespace ash - -#endif // ASH_WM_DEFAULT_CONTAINER_LAYOUT_MANAGER_H_ diff --git a/ash/wm/default_container_layout_manager_unittest.cc b/ash/wm/default_container_layout_manager_unittest.cc deleted file mode 100644 index 4e7e067..0000000 --- a/ash/wm/default_container_layout_manager_unittest.cc +++ /dev/null @@ -1,341 +0,0 @@ -// Copyright (c) 2011 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/default_container_layout_manager.h" - -#include "ash/wm/workspace/workspace.h" -#include "ash/wm/workspace/workspace_manager.h" -#include "ash/wm/workspace_controller.h" -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "base/memory/scoped_vector.h" -#include "ui/aura/client/aura_constants.h" -#include "ui/aura/root_window.h" -#include "ui/aura/screen_aura.h" -#include "ui/aura/test/aura_test_base.h" -#include "ui/aura/window.h" -#include "ui/base/ui_base_types.h" -#include "ui/views/widget/native_widget_aura.h" - -namespace ash { -namespace test { - -namespace { - -using views::Widget; -using ash::internal::DefaultContainerLayoutManager; - -class DefaultContainerLayoutManagerTest : public aura::test::AuraTestBase { - public: - DefaultContainerLayoutManagerTest() : layout_manager_(NULL) {} - virtual ~DefaultContainerLayoutManagerTest() {} - - virtual void SetUp() OVERRIDE { - aura::test::AuraTestBase::SetUp(); - aura::RootWindow* root_window = aura::RootWindow::GetInstance(); - container_.reset( - CreateTestWindow(gfx::Rect(0, 0, 500, 400), root_window)); - workspace_controller_.reset( - new ash::internal::WorkspaceController(container_.get())); - layout_manager_ = new DefaultContainerLayoutManager( - workspace_controller_->workspace_manager()); - container_->SetLayoutManager(layout_manager_); - - root_window->SetHostSize(gfx::Size(500, 400)); - } - - aura::Window* CreateTestWindowWithType(const gfx::Rect& bounds, - aura::Window* parent, - aura::client::WindowType type) { - aura::Window* window = new aura::Window(NULL); - window->SetType(type); - window->Init(ui::Layer::LAYER_HAS_NO_TEXTURE); - window->SetBounds(bounds); - window->Show(); - window->SetParent(parent); - return window; - } - - aura::Window* CreateTestWindow(const gfx::Rect& bounds, - aura::Window* parent) { - return CreateTestWindowWithType(bounds, - parent, - aura::client::WINDOW_TYPE_NORMAL); - } - - aura::Window* container() { return container_.get(); } - - DefaultContainerLayoutManager* default_container_layout_manager() { - return layout_manager_; - } - - protected: - ash::internal::WorkspaceManager* workspace_manager() { - return workspace_controller_->workspace_manager(); - } - - private: - scoped_ptr container_; - scoped_ptr workspace_controller_; - // LayoutManager is owned by |container|. - ash::internal::DefaultContainerLayoutManager* layout_manager_; - - private: - DISALLOW_COPY_AND_ASSIGN(DefaultContainerLayoutManagerTest); -}; - -// Utility functions to set and get show state on |window|. -void Maximize(aura::Window* window) { - window->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); -} - -void Fullscreen(aura::Window* window) { - window->SetIntProperty(aura::client::kShowStateKey, - ui::SHOW_STATE_FULLSCREEN); -} - -void Restore(aura::Window* window) { - window->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); -} - -ui::WindowShowState GetShowState(aura::Window* window) { - return static_cast( - window->GetIntProperty(aura::client::kShowStateKey)); -} - -} // namespace - -TEST_F(DefaultContainerLayoutManagerTest, SetBounds) { - // Layout Manager moves the window to (0,0) to fit to draggable area. - scoped_ptr child( - CreateTestWindow(gfx::Rect(0, -1000, 100, 100), container())); - // Window is centered in workspace. - EXPECT_EQ("200,0 100x100", child->bounds().ToString()); - - // DCLM enforces the window height can't be taller than its owner's height. - child->SetBounds(gfx::Rect(0, 0, 100, 500)); - EXPECT_EQ("200,0 100x400", child->bounds().ToString()); - - // DCLM enforces the window width can't be wider than its owner's width. - child->SetBounds(gfx::Rect(0, 0, 900, 500)); - EXPECT_EQ("0,0 500x400", child->bounds().ToString()); - - // Y origin must always be the top of drag area. - child->SetBounds(gfx::Rect(0, 500, 900, 500)); - EXPECT_EQ("0,0 500x400", child->bounds().ToString()); - child->SetBounds(gfx::Rect(0, -500, 900, 500)); - EXPECT_EQ("0,0 500x400", child->bounds().ToString()); -} - -TEST_F(DefaultContainerLayoutManagerTest, DragWindow) { - scoped_ptr child( - CreateTestWindow(gfx::Rect(0, -1000, 50, 50), container())); - gfx::Rect original_bounds = child->bounds(); - - default_container_layout_manager()->PrepareForMoveOrResize( - child.get(), NULL); - // X origin must fit within viewport. - child->SetBounds(gfx::Rect(-100, 500, 50, 50)); - EXPECT_EQ("0,0 50x50", child->GetTargetBounds().ToString()); - child->SetBounds(gfx::Rect(1000, 500, 50, 50)); - EXPECT_EQ("450,0 50x50", child->GetTargetBounds().ToString()); - default_container_layout_manager()->EndMove(child.get(), NULL); - EXPECT_EQ(original_bounds.ToString(), child->GetTargetBounds().ToString()); -} - -TEST_F(DefaultContainerLayoutManagerTest, Popup) { - scoped_ptr popup( - CreateTestWindowWithType(gfx::Rect(0, -1000, 100, 100), - container(), - aura::client::WINDOW_TYPE_POPUP)); - // A popup window can be placed outside of draggable area. - EXPECT_EQ("0,-1000 100x100", popup->bounds().ToString()); - - // A popup window can be moved to outside of draggable area. - popup->SetBounds(gfx::Rect(-100, 0, 100, 100)); - EXPECT_EQ("-100,0 100x100", popup->bounds().ToString()); - - // A popup window can be resized to the size bigger than draggable area. - popup->SetBounds(gfx::Rect(0, 0, 1000, 1000)); - EXPECT_EQ("0,0 1000x1000", popup->bounds().ToString()); -} - -// Make sure a window with a transient parent isn't resized by the layout -// manager. -TEST_F(DefaultContainerLayoutManagerTest, IgnoreTransient) { - scoped_ptr window(new aura::Window(NULL)); - window->SetType(aura::client::WINDOW_TYPE_NORMAL); - window->Init(ui::Layer::LAYER_HAS_NO_TEXTURE); - aura::RootWindow::GetInstance()->AddTransientChild(window.get()); - window->SetBounds(gfx::Rect(0, 0, 200, 200)); - window->Show(); - window->SetParent(container()); - - EXPECT_EQ("0,0 200x200", window->bounds().ToString()); -} - -TEST_F(DefaultContainerLayoutManagerTest, Fullscreen) { - scoped_ptr w( - CreateTestWindow(gfx::Rect(0, 0, 100, 100), container())); - gfx::Rect fullscreen_bounds = - workspace_manager()->FindBy(w.get())->bounds(); - gfx::Rect original_bounds = w->GetTargetBounds(); - - // Restoreing the restored window. - Restore(w.get()); - EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); - EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); - - // Fullscreen - Fullscreen(w.get()); - EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetShowState(w.get())); - EXPECT_EQ(fullscreen_bounds.ToString(), w->bounds().ToString()); - w->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); - EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); - EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); - - Fullscreen(w.get()); - // Setting |ui::SHOW_STATE_FULLSCREEN| should have no additional effect. - Fullscreen(w.get()); - EXPECT_EQ(fullscreen_bounds, w->bounds()); - Restore(w.get()); - EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); - EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); - - // Calling SetBounds() in fullscreen mode should only update the - // restore bounds not change the bounds of the window. - gfx::Rect new_bounds(50, 50, 50, 50); - Fullscreen(w.get()); - w->SetBounds(new_bounds); - EXPECT_EQ(fullscreen_bounds.ToString(), w->bounds().ToString()); - EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetShowState(w.get())); - Restore(w.get()); - EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); - EXPECT_EQ(50, w->bounds().height()); -} - -TEST_F(DefaultContainerLayoutManagerTest, Maximized) { - scoped_ptr w( - CreateTestWindow(gfx::Rect(0, 0, 100, 100), container())); - gfx::Rect original_bounds = w->GetTargetBounds(); - gfx::Rect fullscreen_bounds = - workspace_manager()->FindBy(w.get())->bounds(); - gfx::Rect work_area_bounds = - workspace_manager()->FindBy(w.get())->GetWorkAreaBounds(); - - // Maximized - Maximize(w.get()); - EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetShowState(w.get())); - EXPECT_EQ(work_area_bounds.ToString(), w->bounds().ToString()); - Restore(w.get()); - EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); - EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); - - // Maximize twice - Maximize(w.get()); - Maximize(w.get()); - EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetShowState(w.get())); - EXPECT_EQ(work_area_bounds.ToString(), w->bounds().ToString()); - Restore(w.get()); - EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); - EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); - - // Maximized -> Fullscreen -> Maximized -> Normal - Maximize(w.get()); - EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetShowState(w.get())); - EXPECT_EQ(work_area_bounds.ToString(), w->bounds().ToString()); - Fullscreen(w.get()); - EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetShowState(w.get())); - EXPECT_EQ(fullscreen_bounds.ToString(), w->bounds().ToString()); - Maximize(w.get()); - EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetShowState(w.get())); - EXPECT_EQ(work_area_bounds.ToString(), w->bounds().ToString()); - Restore(w.get()); - EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); - EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); - - // Calling SetBounds() in maximized mode mode should only update the - // restore bounds not change the bounds of the window. - gfx::Rect new_bounds(50, 50, 50, 50); - Maximize(w.get()); - w->SetBounds(new_bounds); - EXPECT_EQ(work_area_bounds.ToString(), w->bounds().ToString()); - Restore(w.get()); - EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); - EXPECT_EQ(50, w->bounds().height()); -} - -// Tests that fullscreen windows get resized after root window is resized. -TEST_F(DefaultContainerLayoutManagerTest, FullscreenAfterRootWindowResize) { - scoped_ptr w1(CreateTestWindow(gfx::Rect(300, 400), - container())); - gfx::Rect window_bounds = w1->GetTargetBounds(); - gfx::Rect fullscreen_bounds = - workspace_manager()->FindBy(w1.get())->bounds(); - - w1->Show(); - EXPECT_EQ(window_bounds.ToString(), w1->bounds().ToString()); - - Fullscreen(w1.get()); - EXPECT_EQ(fullscreen_bounds.ToString(), w1->bounds().ToString()); - - // Resize the root window. - aura::RootWindow* root_window = aura::RootWindow::GetInstance(); - gfx::Size new_root_window_size = root_window->GetHostSize(); - new_root_window_size.Enlarge(100, 200); - root_window->OnHostResized(new_root_window_size); - - gfx::Rect new_fullscreen_bounds = - workspace_manager()->FindBy(w1.get())->bounds(); - EXPECT_NE(fullscreen_bounds.size().ToString(), - new_fullscreen_bounds.size().ToString()); - - EXPECT_EQ(new_fullscreen_bounds.ToString(), - w1->GetTargetBounds().ToString()); - - Restore(w1.get()); - - // The following test does not pass due to crbug.com/102413. - // TODO(oshima): Re-enable this once the bug is fixed. - // EXPECT_EQ(window_bounds.size().ToString(), - // w1->GetTargetBounds().size().ToString()); -} - -// Tests that maximized windows get resized after root_window is resized. -TEST_F(DefaultContainerLayoutManagerTest, MaximizeAfterRootWindowResize) { - scoped_ptr w1(CreateTestWindow(gfx::Rect(300, 400), - container())); - gfx::Rect window_bounds = w1->GetTargetBounds(); - gfx::Rect work_area_bounds = - workspace_manager()->FindBy(w1.get())->GetWorkAreaBounds(); - - w1->Show(); - EXPECT_EQ(window_bounds.ToString(), w1->bounds().ToString()); - - Maximize(w1.get()); - EXPECT_EQ(work_area_bounds.ToString(), w1->bounds().ToString()); - - // Resize the root window. - aura::RootWindow* root_window = aura::RootWindow::GetInstance(); - gfx::Size new_root_window_size = root_window->GetHostSize(); - new_root_window_size.Enlarge(100, 200); - root_window->OnHostResized(new_root_window_size); - - gfx::Rect new_work_area_bounds = - workspace_manager()->FindBy(w1.get())->bounds(); - EXPECT_NE(work_area_bounds.size().ToString(), - new_work_area_bounds.size().ToString()); - - EXPECT_EQ(new_work_area_bounds.ToString(), - w1->GetTargetBounds().ToString()); - - Restore(w1.get()); - // The following test does not pass due to crbug.com/102413. - // TODO(oshima): Re-enable this once the bug is fixed. - // EXPECT_EQ(window_bounds.size().ToString(), - // w1->GetTargetBounds().size().ToString()); -} - -} // namespace test -} // namespace ash diff --git a/ash/wm/workspace/workspace_event_filter.cc b/ash/wm/workspace/workspace_event_filter.cc new file mode 100644 index 0000000..66aecb4 --- /dev/null +++ b/ash/wm/workspace/workspace_event_filter.cc @@ -0,0 +1,131 @@ +// 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_event_filter.h" + +#include "ash/wm/window_frame.h" +#include "ash/wm/window_util.h" +#include "ash/wm/workspace/workspace_layout_manager.h" +#include "ui/aura/event.h" +#include "ui/aura/window.h" +#include "ui/base/hit_test.h" + +namespace { + +// Sends OnWindowHoveredChanged(|hovered|) to the WindowFrame for |window|, +// which may be NULL. +void WindowHoverChanged(aura::Window* window, bool hovered) { + if (!window) + return; + ash::WindowFrame* window_frame = + static_cast( + window->GetProperty(ash::kWindowFrameKey)); + if (!window_frame) + return; + window_frame->OnWindowHoverChanged(hovered); +} + +} // namespace + +namespace ash { +namespace internal { + +WorkspaceEventFilter::WorkspaceEventFilter(aura::Window* owner) + : ToplevelWindowEventFilter(owner), + drag_state_(DRAG_NONE), + hovered_window_(NULL) { +} + +WorkspaceEventFilter::~WorkspaceEventFilter() { +} + +bool WorkspaceEventFilter::PreHandleMouseEvent(aura::Window* target, + aura::MouseEvent* event) { + WorkspaceLayoutManager* layout_manager = + static_cast(owner()->layout_manager()); + DCHECK(layout_manager); + + // TODO(oshima|derat): Incorporate the logic below and introduce DragObserver + // (or something similar) to decouple DCLM. + + // Notify layout manager that drag event may move/resize the target wnidow. + if (event->type() == ui::ET_MOUSE_DRAGGED && drag_state_ == DRAG_NONE) + layout_manager->PrepareForMoveOrResize(target, event); + + bool handled = ToplevelWindowEventFilter::PreHandleMouseEvent(target, event); + + switch (event->type()) { + case ui::ET_MOUSE_DRAGGED: + // Cancel move/resize if the event wasn't handled, or + // drag_state_ didn't move to MOVE or RESIZE. + if (handled) { + switch (drag_state_) { + case DRAG_NONE: + if (!UpdateDragState()) + layout_manager->CancelMoveOrResize(target, event); + break; + case DRAG_MOVE: + layout_manager->ProcessMove(target, event); + break; + case DRAG_RESIZE: + break; + } + } else { + layout_manager->CancelMoveOrResize(target, event); + } + break; + case ui::ET_MOUSE_ENTERED: + UpdateHoveredWindow(GetActivatableWindow(target)); + break; + case ui::ET_MOUSE_EXITED: + UpdateHoveredWindow(NULL); + break; + case ui::ET_MOUSE_RELEASED: + if (drag_state_ == DRAG_MOVE) + layout_manager->EndMove(target, event); + else if (drag_state_ == DRAG_RESIZE) + layout_manager->EndResize(target, event); + + drag_state_ = DRAG_NONE; + break; + default: + break; + } + return handled; +} + +bool WorkspaceEventFilter::UpdateDragState() { + DCHECK_EQ(DRAG_NONE, drag_state_); + switch (window_component()) { + case HTCAPTION: + drag_state_ = DRAG_MOVE; + break; + case HTTOP: + case HTTOPRIGHT: + case HTRIGHT: + case HTBOTTOMRIGHT: + case HTBOTTOM: + case HTBOTTOMLEFT: + case HTLEFT: + case HTTOPLEFT: + case HTGROWBOX: + drag_state_ = DRAG_RESIZE; + break; + default: + return false; + } + return true; +} + +void WorkspaceEventFilter::UpdateHoveredWindow( + aura::Window* toplevel_window) { + if (toplevel_window == hovered_window_) + return; + WindowHoverChanged(hovered_window_, false); + hovered_window_ = toplevel_window; + WindowHoverChanged(hovered_window_, true); +} + +} // namespace internal +} // namespace ash diff --git a/ash/wm/workspace/workspace_event_filter.h b/ash/wm/workspace/workspace_event_filter.h new file mode 100644 index 0000000..d6765fa --- /dev/null +++ b/ash/wm/workspace/workspace_event_filter.h @@ -0,0 +1,55 @@ +// 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. + +#ifndef ASH_WM_WORKSPACE_WORKSPACE_EVENT_FILTER_H_ +#define ASH_WM_WORKSPACE_WORKSPACE_EVENT_FILTER_H_ +#pragma once + +#include "ash/wm/toplevel_window_event_filter.h" + +namespace aura { +class MouseEvent; +class Window; +} + +namespace ash { +namespace internal { + +class WorkspaceEventFilter : public ToplevelWindowEventFilter { + public: + explicit WorkspaceEventFilter(aura::Window* owner); + virtual ~WorkspaceEventFilter(); + + // Overridden from ToplevelWindowEventFilter: + virtual bool PreHandleMouseEvent(aura::Window* target, + aura::MouseEvent* event) OVERRIDE; + + private: + enum DragState { + DRAG_NONE, + DRAG_MOVE, + DRAG_RESIZE + }; + + // If the mouse is currently over a portion of the window that should + // trigger a drag or resize, drag_state_ is set appropriately and true + // is returned. If the mouse is not over a portion of the window that should + // trigger a more or resize, drag_state_ is not updated and false is returend. + bool UpdateDragState(); + + // Updates the top-level window under the mouse so that we can change + // the look of the caption area based on mouse-hover. + void UpdateHoveredWindow(aura::Window* toplevel); + + DragState drag_state_; + // Top-level window under the mouse cursor. + aura::Window* hovered_window_; + + DISALLOW_COPY_AND_ASSIGN(WorkspaceEventFilter); +}; + +} // namespace internal +} // namespace ash + +#endif // ASH_WM_WORKSPACE_WORKSPACE_EVENT_FILTER_H_ diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc new file mode 100644 index 0000000..a4b95d4 --- /dev/null +++ b/ash/wm/workspace/workspace_layout_manager.cc @@ -0,0 +1,190 @@ +// 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_layout_manager.h" + +#include "ash/wm/property_util.h" +#include "ash/wm/show_state_controller.h" +#include "ash/wm/window_util.h" +#include "ash/wm/workspace/workspace.h" +#include "ash/wm/workspace/workspace_manager.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/event.h" +#include "ui/aura/root_window.h" +#include "ui/aura/screen_aura.h" +#include "ui/aura/window.h" +#include "ui/aura/window_observer.h" +#include "ui/base/ui_base_types.h" +#include "ui/gfx/rect.h" +#include "ui/views/widget/native_widget_aura.h" + +namespace ash { +namespace internal { + +//////////////////////////////////////////////////////////////////////////////// +// WorkspaceLayoutManager, public: + +WorkspaceLayoutManager::WorkspaceLayoutManager( + WorkspaceManager* workspace_manager) + : workspace_manager_(workspace_manager), + show_state_controller_(new ShowStateController(workspace_manager)) { +} + +WorkspaceLayoutManager::~WorkspaceLayoutManager() {} + +void WorkspaceLayoutManager::PrepareForMoveOrResize( + aura::Window* drag, + aura::MouseEvent* event) { + workspace_manager_->set_ignored_window(drag); +} + +void WorkspaceLayoutManager::CancelMoveOrResize( + aura::Window* drag, + aura::MouseEvent* event) { + workspace_manager_->set_ignored_window(NULL); +} + +void WorkspaceLayoutManager::ProcessMove( + aura::Window* drag, + aura::MouseEvent* event) { + // TODO(oshima): Just zooming out may (and will) move/swap window without + // a users's intent. We probably should scroll viewport, but that may not + // be enough. See crbug.com/101826 for more discussion. + workspace_manager_->SetOverview(true); + + gfx::Point point_in_owner = event->location(); + aura::Window::ConvertPointToWindow( + drag, + workspace_manager_->contents_view(), + &point_in_owner); + // TODO(oshima): We should support simply moving to another + // workspace when the destination workspace has enough room to accomodate. + aura::Window* rotate_target = + workspace_manager_->FindRotateWindowForLocation(point_in_owner); + if (rotate_target) + workspace_manager_->RotateWindows(drag, rotate_target); +} + +void WorkspaceLayoutManager::EndMove( + aura::Window* drag, + aura::MouseEvent* evnet) { + // TODO(oshima): finish moving window between workspaces. + workspace_manager_->set_ignored_window(NULL); + Workspace* workspace = workspace_manager_->FindBy(drag); + workspace->Layout(NULL); + workspace->Activate(); + workspace_manager_->SetOverview(false); +} + +void WorkspaceLayoutManager::EndResize( + aura::Window* drag, + aura::MouseEvent* evnet) { + workspace_manager_->set_ignored_window(NULL); + Workspace* workspace = workspace_manager_->GetActiveWorkspace(); + if (workspace) + workspace->Layout(NULL); + workspace_manager_->SetOverview(false); +} + +//////////////////////////////////////////////////////////////////////////////// +// WorkspaceLayoutManager, aura::LayoutManager implementation: + +void WorkspaceLayoutManager::OnWindowResized() { + // Workspace is updated via RootWindowObserver::OnRootWindowResized. +} + +void WorkspaceLayoutManager::OnWindowAddedToLayout(aura::Window* child) { + if (child->type() != aura::client::WINDOW_TYPE_NORMAL || + child->transient_parent()) { + return; + } + + if (!child->GetProperty(aura::client::kShowStateKey)) + child->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); + + child->AddObserver(show_state_controller_.get()); + + Workspace* workspace = workspace_manager_->GetActiveWorkspace(); + if (workspace) { + aura::Window* active = ash::GetActiveWindow(); + // Active window may not be in the default container layer. + if (!workspace->Contains(active)) + active = NULL; + if (workspace->AddWindowAfter(child, active)) + return; + } + // Create new workspace if new |child| doesn't fit to current workspace. + Workspace* new_workspace = workspace_manager_->CreateWorkspace(); + new_workspace->AddWindowAfter(child, NULL); + new_workspace->Activate(); +} + +void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout( + aura::Window* child) { + child->RemoveObserver(show_state_controller_.get()); + ClearRestoreBounds(child); + + Workspace* workspace = workspace_manager_->FindBy(child); + if (!workspace) + return; + workspace->RemoveWindow(child); + if (workspace->is_empty()) + delete workspace; +} + +void WorkspaceLayoutManager::OnChildWindowVisibilityChanged( + aura::Window* child, + bool visible) { + NOTIMPLEMENTED(); +} + +void WorkspaceLayoutManager::SetChildBounds( + aura::Window* child, + const gfx::Rect& requested_bounds) { + gfx::Rect adjusted_bounds = requested_bounds; + + // First, calculate the adjusted bounds. + if (child->type() != aura::client::WINDOW_TYPE_NORMAL || + workspace_manager_->layout_in_progress() || + child->transient_parent()) { + // Use the requested bounds as is. + } else if (child == workspace_manager_->ignored_window()) { + // If a drag window is requesting bounds, make sure its attached to + // the workarea's top and fits within the total drag area. + gfx::Rect drag_area = workspace_manager_->GetDragAreaBounds(); + adjusted_bounds.set_y(drag_area.y()); + adjusted_bounds = adjusted_bounds.AdjustToFit(drag_area); + } else { + Workspace* workspace = workspace_manager_->FindBy(child); + gfx::Rect work_area = workspace->GetWorkAreaBounds(); + adjusted_bounds.set_origin( + gfx::Point(child->GetTargetBounds().x(), work_area.y())); + adjusted_bounds = adjusted_bounds.AdjustToFit(work_area); + } + + ui::WindowShowState show_state = static_cast( + child->GetIntProperty(aura::client::kShowStateKey)); + + // Second, check if the window is either maximized or in fullscreen mode. + if (show_state == ui::SHOW_STATE_MAXIMIZED || + show_state == ui::SHOW_STATE_FULLSCREEN) { + // If the request is not from workspace manager, + // remember the requested bounds. + if (!workspace_manager_->layout_in_progress()) + SetRestoreBounds(child, adjusted_bounds); + + Workspace* workspace = workspace_manager_->FindBy(child); + if (show_state == ui::SHOW_STATE_MAXIMIZED) + adjusted_bounds = workspace->GetWorkAreaBounds(); + else + adjusted_bounds = workspace->bounds(); + // Don't + if (child->GetTargetBounds() == adjusted_bounds) + return; + } + SetChildBoundsDirect(child, adjusted_bounds); +} + +} // namespace internal +} // namespace ash diff --git a/ash/wm/workspace/workspace_layout_manager.h b/ash/wm/workspace/workspace_layout_manager.h new file mode 100644 index 0000000..3fdceb8 --- /dev/null +++ b/ash/wm/workspace/workspace_layout_manager.h @@ -0,0 +1,76 @@ +// 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. + +#ifndef ASH_WM_WORKSPACE_WORKSPACE_LAYOUT_MANAGER_H_ +#define ASH_WM_WORKSPACE_WORKSPACE_LAYOUT_MANAGER_H_ +#pragma once + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "ui/aura/layout_manager.h" +#include "ash/ash_export.h" + +namespace aura { +class MouseEvent; +class Window; +} + +namespace gfx { +class Rect; +} + +namespace ash { +namespace internal { + +class ShowStateController; +class WorkspaceManager; + +// LayoutManager for the default window container. +class ASH_EXPORT WorkspaceLayoutManager : public aura::LayoutManager { + public: + explicit WorkspaceLayoutManager(WorkspaceManager* workspace_manager); + virtual ~WorkspaceLayoutManager(); + + // Returns the workspace manager for this container. + WorkspaceManager* workspace_manager() { + return workspace_manager_; + } + + // Invoked when a window receives drag event. + void PrepareForMoveOrResize(aura::Window* drag, aura::MouseEvent* event); + + // Invoked when a drag event didn't start any drag operation. + void CancelMoveOrResize(aura::Window* drag, aura::MouseEvent* event); + + // Invoked when a drag event moved the |window|. + void ProcessMove(aura::Window* window, aura::MouseEvent* event); + + // Invoked when a user finished moving window. + void EndMove(aura::Window* drag, aura::MouseEvent* evnet); + + // Invoked when a user finished resizing window. + void EndResize(aura::Window* drag, aura::MouseEvent* evnet); + + // Overridden from aura::LayoutManager: + virtual void OnWindowResized() OVERRIDE; + virtual void OnWindowAddedToLayout(aura::Window* child) OVERRIDE; + virtual void OnWillRemoveWindowFromLayout(aura::Window* child) OVERRIDE; + virtual void OnChildWindowVisibilityChanged(aura::Window* child, + bool visibile) OVERRIDE; + virtual void SetChildBounds(aura::Window* child, + const gfx::Rect& requested_bounds) OVERRIDE; + private: + // Owned by WorkspaceController. + WorkspaceManager* workspace_manager_; + + scoped_ptr show_state_controller_; + + DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManager); +}; + +} // namespace internal +} // namespace ash + +#endif // ASH_WM_WORKSPACE_WORKSPACE_LAYOUT_MANAGER_H_ diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc new file mode 100644 index 0000000..ac2504e --- /dev/null +++ b/ash/wm/workspace/workspace_layout_manager_unittest.cc @@ -0,0 +1,341 @@ +// 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_layout_manager.h" + +#include "ash/wm/workspace/workspace.h" +#include "ash/wm/workspace/workspace_manager.h" +#include "ash/wm/workspace_controller.h" +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/scoped_vector.h" +#include "ui/aura/client/aura_constants.h" +#include "ui/aura/root_window.h" +#include "ui/aura/screen_aura.h" +#include "ui/aura/test/aura_test_base.h" +#include "ui/aura/window.h" +#include "ui/base/ui_base_types.h" +#include "ui/views/widget/native_widget_aura.h" + +namespace ash { +namespace test { + +namespace { + +using views::Widget; +using ash::internal::WorkspaceLayoutManager; + +class WorkspaceLayoutManagerTest : public aura::test::AuraTestBase { + public: + WorkspaceLayoutManagerTest() : layout_manager_(NULL) {} + virtual ~WorkspaceLayoutManagerTest() {} + + virtual void SetUp() OVERRIDE { + aura::test::AuraTestBase::SetUp(); + aura::RootWindow* root_window = aura::RootWindow::GetInstance(); + container_.reset( + CreateTestWindow(gfx::Rect(0, 0, 500, 400), root_window)); + workspace_controller_.reset( + new ash::internal::WorkspaceController(container_.get())); + layout_manager_ = new WorkspaceLayoutManager( + workspace_controller_->workspace_manager()); + container_->SetLayoutManager(layout_manager_); + + root_window->SetHostSize(gfx::Size(500, 400)); + } + + aura::Window* CreateTestWindowWithType(const gfx::Rect& bounds, + aura::Window* parent, + aura::client::WindowType type) { + aura::Window* window = new aura::Window(NULL); + window->SetType(type); + window->Init(ui::Layer::LAYER_HAS_NO_TEXTURE); + window->SetBounds(bounds); + window->Show(); + window->SetParent(parent); + return window; + } + + aura::Window* CreateTestWindow(const gfx::Rect& bounds, + aura::Window* parent) { + return CreateTestWindowWithType(bounds, + parent, + aura::client::WINDOW_TYPE_NORMAL); + } + + aura::Window* container() { return container_.get(); } + + WorkspaceLayoutManager* default_container_layout_manager() { + return layout_manager_; + } + + protected: + ash::internal::WorkspaceManager* workspace_manager() { + return workspace_controller_->workspace_manager(); + } + + private: + scoped_ptr container_; + scoped_ptr workspace_controller_; + // LayoutManager is owned by |container|. + ash::internal::WorkspaceLayoutManager* layout_manager_; + + private: + DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManagerTest); +}; + +// Utility functions to set and get show state on |window|. +void Maximize(aura::Window* window) { + window->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); +} + +void Fullscreen(aura::Window* window) { + window->SetIntProperty(aura::client::kShowStateKey, + ui::SHOW_STATE_FULLSCREEN); +} + +void Restore(aura::Window* window) { + window->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); +} + +ui::WindowShowState GetShowState(aura::Window* window) { + return static_cast( + window->GetIntProperty(aura::client::kShowStateKey)); +} + +} // namespace + +TEST_F(WorkspaceLayoutManagerTest, SetBounds) { + // Layout Manager moves the window to (0,0) to fit to draggable area. + scoped_ptr child( + CreateTestWindow(gfx::Rect(0, -1000, 100, 100), container())); + // Window is centered in workspace. + EXPECT_EQ("200,0 100x100", child->bounds().ToString()); + + // DCLM enforces the window height can't be taller than its owner's height. + child->SetBounds(gfx::Rect(0, 0, 100, 500)); + EXPECT_EQ("200,0 100x400", child->bounds().ToString()); + + // DCLM enforces the window width can't be wider than its owner's width. + child->SetBounds(gfx::Rect(0, 0, 900, 500)); + EXPECT_EQ("0,0 500x400", child->bounds().ToString()); + + // Y origin must always be the top of drag area. + child->SetBounds(gfx::Rect(0, 500, 900, 500)); + EXPECT_EQ("0,0 500x400", child->bounds().ToString()); + child->SetBounds(gfx::Rect(0, -500, 900, 500)); + EXPECT_EQ("0,0 500x400", child->bounds().ToString()); +} + +TEST_F(WorkspaceLayoutManagerTest, DragWindow) { + scoped_ptr child( + CreateTestWindow(gfx::Rect(0, -1000, 50, 50), container())); + gfx::Rect original_bounds = child->bounds(); + + default_container_layout_manager()->PrepareForMoveOrResize( + child.get(), NULL); + // X origin must fit within viewport. + child->SetBounds(gfx::Rect(-100, 500, 50, 50)); + EXPECT_EQ("0,0 50x50", child->GetTargetBounds().ToString()); + child->SetBounds(gfx::Rect(1000, 500, 50, 50)); + EXPECT_EQ("450,0 50x50", child->GetTargetBounds().ToString()); + default_container_layout_manager()->EndMove(child.get(), NULL); + EXPECT_EQ(original_bounds.ToString(), child->GetTargetBounds().ToString()); +} + +TEST_F(WorkspaceLayoutManagerTest, Popup) { + scoped_ptr popup( + CreateTestWindowWithType(gfx::Rect(0, -1000, 100, 100), + container(), + aura::client::WINDOW_TYPE_POPUP)); + // A popup window can be placed outside of draggable area. + EXPECT_EQ("0,-1000 100x100", popup->bounds().ToString()); + + // A popup window can be moved to outside of draggable area. + popup->SetBounds(gfx::Rect(-100, 0, 100, 100)); + EXPECT_EQ("-100,0 100x100", popup->bounds().ToString()); + + // A popup window can be resized to the size bigger than draggable area. + popup->SetBounds(gfx::Rect(0, 0, 1000, 1000)); + EXPECT_EQ("0,0 1000x1000", popup->bounds().ToString()); +} + +// Make sure a window with a transient parent isn't resized by the layout +// manager. +TEST_F(WorkspaceLayoutManagerTest, IgnoreTransient) { + scoped_ptr window(new aura::Window(NULL)); + window->SetType(aura::client::WINDOW_TYPE_NORMAL); + window->Init(ui::Layer::LAYER_HAS_NO_TEXTURE); + aura::RootWindow::GetInstance()->AddTransientChild(window.get()); + window->SetBounds(gfx::Rect(0, 0, 200, 200)); + window->Show(); + window->SetParent(container()); + + EXPECT_EQ("0,0 200x200", window->bounds().ToString()); +} + +TEST_F(WorkspaceLayoutManagerTest, Fullscreen) { + scoped_ptr w( + CreateTestWindow(gfx::Rect(0, 0, 100, 100), container())); + gfx::Rect fullscreen_bounds = + workspace_manager()->FindBy(w.get())->bounds(); + gfx::Rect original_bounds = w->GetTargetBounds(); + + // Restoreing the restored window. + Restore(w.get()); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); + EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); + + // Fullscreen + Fullscreen(w.get()); + EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetShowState(w.get())); + EXPECT_EQ(fullscreen_bounds.ToString(), w->bounds().ToString()); + w->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); + EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); + + Fullscreen(w.get()); + // Setting |ui::SHOW_STATE_FULLSCREEN| should have no additional effect. + Fullscreen(w.get()); + EXPECT_EQ(fullscreen_bounds, w->bounds()); + Restore(w.get()); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); + EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); + + // Calling SetBounds() in fullscreen mode should only update the + // restore bounds not change the bounds of the window. + gfx::Rect new_bounds(50, 50, 50, 50); + Fullscreen(w.get()); + w->SetBounds(new_bounds); + EXPECT_EQ(fullscreen_bounds.ToString(), w->bounds().ToString()); + EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetShowState(w.get())); + Restore(w.get()); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); + EXPECT_EQ(50, w->bounds().height()); +} + +TEST_F(WorkspaceLayoutManagerTest, Maximized) { + scoped_ptr w( + CreateTestWindow(gfx::Rect(0, 0, 100, 100), container())); + gfx::Rect original_bounds = w->GetTargetBounds(); + gfx::Rect fullscreen_bounds = + workspace_manager()->FindBy(w.get())->bounds(); + gfx::Rect work_area_bounds = + workspace_manager()->FindBy(w.get())->GetWorkAreaBounds(); + + // Maximized + Maximize(w.get()); + EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetShowState(w.get())); + EXPECT_EQ(work_area_bounds.ToString(), w->bounds().ToString()); + Restore(w.get()); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); + EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); + + // Maximize twice + Maximize(w.get()); + Maximize(w.get()); + EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetShowState(w.get())); + EXPECT_EQ(work_area_bounds.ToString(), w->bounds().ToString()); + Restore(w.get()); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); + EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); + + // Maximized -> Fullscreen -> Maximized -> Normal + Maximize(w.get()); + EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetShowState(w.get())); + EXPECT_EQ(work_area_bounds.ToString(), w->bounds().ToString()); + Fullscreen(w.get()); + EXPECT_EQ(ui::SHOW_STATE_FULLSCREEN, GetShowState(w.get())); + EXPECT_EQ(fullscreen_bounds.ToString(), w->bounds().ToString()); + Maximize(w.get()); + EXPECT_EQ(ui::SHOW_STATE_MAXIMIZED, GetShowState(w.get())); + EXPECT_EQ(work_area_bounds.ToString(), w->bounds().ToString()); + Restore(w.get()); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); + EXPECT_EQ(original_bounds.ToString(), w->bounds().ToString()); + + // Calling SetBounds() in maximized mode mode should only update the + // restore bounds not change the bounds of the window. + gfx::Rect new_bounds(50, 50, 50, 50); + Maximize(w.get()); + w->SetBounds(new_bounds); + EXPECT_EQ(work_area_bounds.ToString(), w->bounds().ToString()); + Restore(w.get()); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, GetShowState(w.get())); + EXPECT_EQ(50, w->bounds().height()); +} + +// Tests that fullscreen windows get resized after root window is resized. +TEST_F(WorkspaceLayoutManagerTest, FullscreenAfterRootWindowResize) { + scoped_ptr w1(CreateTestWindow(gfx::Rect(300, 400), + container())); + gfx::Rect window_bounds = w1->GetTargetBounds(); + gfx::Rect fullscreen_bounds = + workspace_manager()->FindBy(w1.get())->bounds(); + + w1->Show(); + EXPECT_EQ(window_bounds.ToString(), w1->bounds().ToString()); + + Fullscreen(w1.get()); + EXPECT_EQ(fullscreen_bounds.ToString(), w1->bounds().ToString()); + + // Resize the root window. + aura::RootWindow* root_window = aura::RootWindow::GetInstance(); + gfx::Size new_root_window_size = root_window->GetHostSize(); + new_root_window_size.Enlarge(100, 200); + root_window->OnHostResized(new_root_window_size); + + gfx::Rect new_fullscreen_bounds = + workspace_manager()->FindBy(w1.get())->bounds(); + EXPECT_NE(fullscreen_bounds.size().ToString(), + new_fullscreen_bounds.size().ToString()); + + EXPECT_EQ(new_fullscreen_bounds.ToString(), + w1->GetTargetBounds().ToString()); + + Restore(w1.get()); + + // The following test does not pass due to crbug.com/102413. + // TODO(oshima): Re-enable this once the bug is fixed. + // EXPECT_EQ(window_bounds.size().ToString(), + // w1->GetTargetBounds().size().ToString()); +} + +// Tests that maximized windows get resized after root_window is resized. +TEST_F(WorkspaceLayoutManagerTest, MaximizeAfterRootWindowResize) { + scoped_ptr w1(CreateTestWindow(gfx::Rect(300, 400), + container())); + gfx::Rect window_bounds = w1->GetTargetBounds(); + gfx::Rect work_area_bounds = + workspace_manager()->FindBy(w1.get())->GetWorkAreaBounds(); + + w1->Show(); + EXPECT_EQ(window_bounds.ToString(), w1->bounds().ToString()); + + Maximize(w1.get()); + EXPECT_EQ(work_area_bounds.ToString(), w1->bounds().ToString()); + + // Resize the root window. + aura::RootWindow* root_window = aura::RootWindow::GetInstance(); + gfx::Size new_root_window_size = root_window->GetHostSize(); + new_root_window_size.Enlarge(100, 200); + root_window->OnHostResized(new_root_window_size); + + gfx::Rect new_work_area_bounds = + workspace_manager()->FindBy(w1.get())->bounds(); + EXPECT_NE(work_area_bounds.size().ToString(), + new_work_area_bounds.size().ToString()); + + EXPECT_EQ(new_work_area_bounds.ToString(), + w1->GetTargetBounds().ToString()); + + Restore(w1.get()); + // The following test does not pass due to crbug.com/102413. + // TODO(oshima): Re-enable this once the bug is fixed. + // EXPECT_EQ(window_bounds.size().ToString(), + // w1->GetTargetBounds().size().ToString()); +} + +} // namespace test +} // namespace ash diff --git a/ash/wm/workspace_controller.cc b/ash/wm/workspace_controller.cc index 0514a87..197bc50 100644 --- a/ash/wm/workspace_controller.cc +++ b/ash/wm/workspace_controller.cc @@ -7,9 +7,9 @@ #include "ash/launcher/launcher.h" #include "ash/launcher/launcher_model.h" #include "ash/shell.h" -#include "ash/wm/default_container_layout_manager.h" #include "ash/wm/window_util.h" #include "ash/wm/workspace/workspace.h" +#include "ash/wm/workspace/workspace_layout_manager.h" #include "ash/wm/workspace/workspace_manager.h" #include "ui/aura/client/activation_client.h" #include "ui/aura/client/aura_constants.h" -- cgit v1.1