summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ash/ash.gyp5
-rw-r--r--ash/ash_switches.cc4
-rw-r--r--ash/ash_switches.h1
-rw-r--r--ash/display/screen_position_controller.cc11
-rw-r--r--ash/root_window_controller.cc61
-rw-r--r--ash/shell/window_watcher.cc32
-rw-r--r--ash/shell/window_watcher.h7
-rw-r--r--ash/shell_window_ids.h37
-rw-r--r--ash/wm/activation_controller.cc51
-rw-r--r--ash/wm/always_on_top_controller.cc26
-rw-r--r--ash/wm/always_on_top_controller.h2
-rw-r--r--ash/wm/base_layout_manager.cc5
-rw-r--r--ash/wm/window_cycle_controller.cc79
-rw-r--r--ash/wm/window_cycle_controller.h1
-rw-r--r--ash/wm/window_util.h3
-rw-r--r--ash/wm/workspace/workspace2.cc86
-rw-r--r--ash/wm/workspace/workspace2.h74
-rw-r--r--ash/wm/workspace/workspace_manager2.cc522
-rw-r--r--ash/wm/workspace/workspace_manager2.h149
-rw-r--r--ash/wm/workspace/workspace_manager2_unittest.cc764
-rw-r--r--ash/wm/workspace_controller.cc29
-rw-r--r--ash/wm/workspace_controller.h6
-rw-r--r--ash/wm/workspace_controller_test_helper.cc3
-rw-r--r--ash/wm/workspace_controller_test_helper.h5
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/about_flags.cc7
26 files changed, 1908 insertions, 68 deletions
diff --git a/ash/ash.gyp b/ash/ash.gyp
index 0c58a10..ceaa0a9 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -346,12 +346,16 @@
'wm/workspace/snap_types.h',
'wm/workspace/workspace.cc',
'wm/workspace/workspace.h',
+ 'wm/workspace/workspace2.cc',
+ 'wm/workspace/workspace2.h',
'wm/workspace/workspace_event_filter.cc',
'wm/workspace/workspace_event_filter.h',
'wm/workspace/workspace_layout_manager.cc',
'wm/workspace/workspace_layout_manager.h',
'wm/workspace/workspace_manager.cc',
'wm/workspace/workspace_manager.h',
+ 'wm/workspace/workspace_manager2.cc',
+ 'wm/workspace/workspace_manager2.h',
'wm/workspace/workspace_types.h',
'wm/workspace/workspace_window_resizer.cc',
'wm/workspace/workspace_window_resizer.h',
@@ -489,6 +493,7 @@
'wm/workspace/workspace_event_filter_test_helper.h',
'wm/workspace/workspace_event_filter_unittest.cc',
'wm/workspace/workspace_manager_unittest.cc',
+ 'wm/workspace/workspace_manager2_unittest.cc',
'wm/workspace/workspace_window_resizer_unittest.cc',
],
'conditions': [
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 4ca3052..5eca1ce 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -13,7 +13,9 @@ const char kAshDebugShortcuts[] = "ash-debug-shortcuts";
// Enables the Oak tree viewer.
const char kAshEnableOak[] = "ash-enable-oak";
-// Disables extended desktop.
+// Enables Workspace2.
+const char kAshEnableWorkspace2[] = "ash-enable-workspace2";
+
const char kAshExtendedDesktopDisabled[] = "ash-extended-desktop-disabled";
// Disable using Ash notifications.
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index 042afc5..4480359 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -17,6 +17,7 @@ namespace switches {
// Please keep alphabetized.
ASH_EXPORT extern const char kAshDebugShortcuts[];
ASH_EXPORT extern const char kAshEnableOak[];
+ASH_EXPORT extern const char kAshEnableWorkspace2[];
ASH_EXPORT extern const char kAshExtendedDesktopDisabled[];
ASH_EXPORT extern const char kAshNotifyDisabled[];
ASH_EXPORT extern const char kAshTouchHud[];
diff --git a/ash/display/screen_position_controller.cc b/ash/display/screen_position_controller.cc
index 1deb4e2..c398937 100644
--- a/ash/display/screen_position_controller.cc
+++ b/ash/display/screen_position_controller.cc
@@ -5,10 +5,12 @@
#include "ash/display/screen_position_controller.h"
#include "ash/display/display_controller.h"
+#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/wm/system_modal_container_layout_manager.h"
#include "ash/wm/window_properties.h"
+#include "ash/wm/workspace_controller.h"
#include "ui/aura/client/activation_client.h"
#include "ui/aura/client/capture_client.h"
#include "ui/aura/client/stacking_client.h"
@@ -97,8 +99,7 @@ void ScreenPositionController::SetBounds(aura::Window* window,
aura::Window* dst_container = NULL;
if (dst_root != window->GetRootWindow()) {
int container_id = window->parent()->id();
- // All containers that uses screen coordinates must have valid
- // window ids.
+ // All containers that uses screen coordinates must have valid window ids.
DCHECK_GE(container_id, 0);
// Don't move modal screen.
if (!SystemModalContainerLayoutManager::IsModalScreen(window))
@@ -117,6 +118,12 @@ void ScreenPositionController::SetBounds(aura::Window* window,
if (active && focused != active)
tracker.Add(active);
+ if (dst_container->id() == kShellWindowId_WorkspaceContainer) {
+ dst_container =
+ GetRootWindowController(dst_root)->workspace_controller()->
+ GetParentForNewWindow(window);
+ }
+
dst_container->AddChild(window);
MoveAllTransientChildrenToNewRoot(display, window);
diff --git a/ash/root_window_controller.cc b/ash/root_window_controller.cc
index 15fba6b..6cb6c08 100644
--- a/ash/root_window_controller.cc
+++ b/ash/root_window_controller.cc
@@ -50,19 +50,62 @@ aura::Window* CreateContainer(int window_id,
return container;
}
-void MoveAllWindows(aura::RootWindow* src,
- aura::RootWindow* dst) {
- // Windows move only from secondary displays to the primary
- // display, so no need to move windows in the containers that are
- // available only in the primary display (launcher, panels etc)
+// Returns all the children of the workspace windows, eg the standard top-level
+// windows.
+std::vector<aura::Window*> GetWorkspaceWindows(aura::RootWindow* root) {
+ using aura::Window;
+
+ std::vector<Window*> windows;
+ Window* container = Shell::GetContainer(
+ root, internal::kShellWindowId_DefaultContainer);
+ for (Window::Windows::const_reverse_iterator i =
+ container->children().rbegin();
+ i != container->children().rend(); ++i) {
+ Window* workspace_window = *i;
+ if (workspace_window->id() == internal::kShellWindowId_WorkspaceContainer) {
+ windows.insert(windows.end(), workspace_window->children().begin(),
+ workspace_window->children().end());
+ }
+ }
+ return windows;
+}
+
+// Reparents |window| to |new_parent|.
+void ReparentWindow(aura::Window* window, aura::Window* new_parent) {
+ // Update the restore bounds to make it relative to the display.
+ gfx::Rect restore_bounds(GetRestoreBoundsInParent(window));
+ new_parent->AddChild(window);
+ if (!restore_bounds.IsEmpty())
+ SetRestoreBoundsInParent(window, restore_bounds);
+}
+
+// Reparents the appropriate set of windows from |src| to |dst|.
+void ReparentAllWindows(aura::RootWindow* src, aura::RootWindow* dst) {
+ // Set of windows to move.
const int kContainerIdsToMove[] = {
internal::kShellWindowId_DefaultContainer,
internal::kShellWindowId_AlwaysOnTopContainer,
internal::kShellWindowId_SystemModalContainer,
internal::kShellWindowId_LockSystemModalContainer,
};
+ // For Workspace2 we need to manually reparent the windows. This way
+ // Workspace2 can move the windows to the appropriate workspace.
+ if (internal::WorkspaceController::IsWorkspace2Enabled()) {
+ std::vector<aura::Window*> windows(GetWorkspaceWindows(src));
+ internal::WorkspaceController* workspace_controller =
+ GetRootWindowController(dst)->workspace_controller();
+ for (size_t i = 0; i < windows.size(); ++i) {
+ aura::Window* new_parent =
+ workspace_controller->GetParentForNewWindow(windows[i]);
+ ReparentWindow(windows[i], new_parent);
+ }
+ }
for (size_t i = 0; i < arraysize(kContainerIdsToMove); i++) {
int id = kContainerIdsToMove[i];
+ if (id == internal::kShellWindowId_DefaultContainer &&
+ internal::WorkspaceController::IsWorkspace2Enabled())
+ continue;
+
aura::Window* src_container = Shell::GetContainer(src, id);
aura::Window* dst_container = Shell::GetContainer(dst, id);
aura::Window::Windows children = src_container->children();
@@ -73,11 +116,7 @@ void MoveAllWindows(aura::RootWindow* src,
if (internal::SystemModalContainerLayoutManager::IsModalScreen(window))
continue;
- // Update the restore bounds to make it relative to the display.
- gfx::Rect restore_bounds(GetRestoreBoundsInParent(window));
- dst_container->AddChild(window);
- if (!restore_bounds.IsEmpty())
- SetRestoreBoundsInParent(window, restore_bounds);
+ ReparentWindow(window, dst_container);
}
}
}
@@ -319,7 +358,7 @@ void RootWindowController::MoveWindowsTo(aura::RootWindow* dst) {
if (active && focused != active)
tracker.Add(active);
- MoveAllWindows(root_window_.get(), dst);
+ ReparentAllWindows(root_window_.get(), dst);
// Restore focused or active window if it's still alive.
if (focused && tracker.Contains(focused) && dst->Contains(focused)) {
diff --git a/ash/shell/window_watcher.cc b/ash/shell/window_watcher.cc
index dfd54ff..a85d8f6 100644
--- a/ash/shell/window_watcher.cc
+++ b/ash/shell/window_watcher.cc
@@ -13,17 +13,45 @@
namespace ash {
namespace shell {
+class WindowWatcher::WorkspaceWindowWatcher : public aura::WindowObserver {
+ public:
+ explicit WorkspaceWindowWatcher(WindowWatcher* watcher) : watcher_(watcher) {
+ watcher_->window_->AddObserver(this);
+ for (size_t i = 0; i < watcher_->window_->children().size(); ++i)
+ watcher_->window_->children()[i]->AddObserver(watcher_);
+ }
+
+ virtual ~WorkspaceWindowWatcher() {
+ watcher_->window_->RemoveObserver(this);
+ for (size_t i = 0; i < watcher_->window_->children().size(); ++i)
+ watcher_->window_->children()[i]->RemoveObserver(watcher_);
+ }
+
+ virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE {
+ new_window->AddObserver(watcher_);
+ }
+
+ virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE {
+ DCHECK(window->children().empty());
+ window->RemoveObserver(watcher_);
+ }
+
+ private:
+ WindowWatcher* watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkspaceWindowWatcher);
+};
+
WindowWatcher::WindowWatcher()
: window_(ash::Shell::GetInstance()->launcher()->window_container()),
panel_container_(ash::Shell::GetContainer(
Shell::GetPrimaryRootWindow(),
internal::kShellWindowId_PanelContainer)) {
- window_->AddObserver(this);
+ workspace_window_watcher_.reset(new WorkspaceWindowWatcher(this));
panel_container_->AddObserver(this);
}
WindowWatcher::~WindowWatcher() {
- window_->RemoveObserver(this);
panel_container_->RemoveObserver(this);
}
diff --git a/ash/shell/window_watcher.h b/ash/shell/window_watcher.h
index 42c7cdc..ee81b5a 100644
--- a/ash/shell/window_watcher.h
+++ b/ash/shell/window_watcher.h
@@ -10,6 +10,7 @@
#include "ash/launcher/launcher_types.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
+#include "base/memory/scoped_ptr.h"
#include "ui/aura/window_observer.h"
namespace aura {
@@ -19,6 +20,8 @@ class Window;
namespace ash {
namespace shell {
+// TODO(sky): fix this class, its a bit broke with workspace2.
+
// WindowWatcher is responsible for listening for newly created windows and
// creating items on the Launcher for them.
class WindowWatcher : public aura::WindowObserver {
@@ -34,6 +37,8 @@ class WindowWatcher : public aura::WindowObserver {
virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
private:
+ class WorkspaceWindowWatcher;
+
typedef std::map<ash::LauncherID, aura::Window*> IDToWindow;
// Window watching for newly created windows to be added to.
@@ -44,6 +49,8 @@ class WindowWatcher : public aura::WindowObserver {
// Maps from window to the id we gave it.
IDToWindow id_to_window_;
+ scoped_ptr<WorkspaceWindowWatcher> workspace_window_watcher_;
+
DISALLOW_COPY_AND_ASSIGN(WindowWatcher);
};
diff --git a/ash/shell_window_ids.h b/ash/shell_window_ids.h
index 8ac0f18..d837725 100644
--- a/ash/shell_window_ids.h
+++ b/ash/shell_window_ids.h
@@ -34,54 +34,63 @@ const int kShellWindowId_UnparentedControlContainer = 3;
// The desktop background window.
const int kShellWindowId_DesktopBackgroundContainer = 4;
+// TODO(sky): rename kShellWindowId_DefaultContainer when Workspace2 is the
+// default.
+
// The container for standard top-level windows.
+// WARNING: when Workspace2 is enabled the only children of
+// kShellWindowId_DefaultContainer are kShellWindowId_WorkspaceContainer.
const int kShellWindowId_DefaultContainer = 5;
+// Used by Worskpace2 for each workspace. Contains standard top-level windows.
+// WARNING: there may be more than one container with this id.
+const int kShellWindowId_WorkspaceContainer = 6;
+
// The container for top-level windows with the 'always-on-top' flag set.
-const int kShellWindowId_AlwaysOnTopContainer = 6;
+const int kShellWindowId_AlwaysOnTopContainer = 7;
// The container for panel windows.
-const int kShellWindowId_PanelContainer = 7;
+const int kShellWindowId_PanelContainer = 8;
// The container for the launcher.
-const int kShellWindowId_LauncherContainer = 8;
+const int kShellWindowId_LauncherContainer = 9;
// The container for the app list.
-const int kShellWindowId_AppListContainer = 9;
+const int kShellWindowId_AppListContainer = 10;
// The container for user-specific modal windows.
-const int kShellWindowId_SystemModalContainer = 10;
+const int kShellWindowId_SystemModalContainer = 11;
// The container for input method components such like candidate windows. They
// are almost panels but have no activations/focus, and they should appear over
// the AppList and SystemModal dialogs.
-const int kShellWindowId_InputMethodContainer = 11;
+const int kShellWindowId_InputMethodContainer = 12;
// The container for the lock screen background.
-const int kShellWindowId_LockScreenBackgroundContainer = 12;
+const int kShellWindowId_LockScreenBackgroundContainer = 13;
// The container for the lock screen.
-const int kShellWindowId_LockScreenContainer = 13;
+const int kShellWindowId_LockScreenContainer = 14;
// The container for the lock screen modal windows.
-const int kShellWindowId_LockSystemModalContainer = 14;
+const int kShellWindowId_LockSystemModalContainer = 15;
// The container for the status area.
-const int kShellWindowId_StatusContainer = 15;
+const int kShellWindowId_StatusContainer = 16;
// The container for menus.
-const int kShellWindowId_MenuContainer = 16;
+const int kShellWindowId_MenuContainer = 17;
// The container for drag/drop images and tooltips.
-const int kShellWindowId_DragImageAndTooltipContainer = 17;
+const int kShellWindowId_DragImageAndTooltipContainer = 18;
// The container for bubbles briefly overlaid onscreen to show settings changes
// (volume, brightness, etc.).
-const int kShellWindowId_SettingBubbleContainer = 18;
+const int kShellWindowId_SettingBubbleContainer = 19;
// The container for special components overlaid onscreen, such as the
// region selector for partial screenshots.
-const int kShellWindowId_OverlayContainer = 19;
+const int kShellWindowId_OverlayContainer = 20;
} // namespace internal
diff --git a/ash/wm/activation_controller.cc b/ash/wm/activation_controller.cc
index 3685d79..6859dd3 100644
--- a/ash/wm/activation_controller.cc
+++ b/ash/wm/activation_controller.cc
@@ -4,10 +4,13 @@
#include "ash/wm/activation_controller.h"
+#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
+#include "ash/wm/property_util.h"
#include "ash/wm/window_modality_controller.h"
#include "ash/wm/window_util.h"
+#include "ash/wm/workspace_controller.h"
#include "base/auto_reset.h"
#include "ui/aura/client/activation_change_observer.h"
#include "ui/aura/client/activation_delegate.h"
@@ -33,6 +36,7 @@ const int kWindowContainerIds[] = {
kShellWindowId_SystemModalContainer,
kShellWindowId_AlwaysOnTopContainer,
kShellWindowId_AppListContainer,
+ // TODO(sky): defaultcontainer shouldn't be in the list with workspace2.
kShellWindowId_DefaultContainer,
// Panel, launcher and status are intentionally checked after other
@@ -46,9 +50,18 @@ const int kWindowContainerIds[] = {
// Returns true if children of |window| can be activated.
// These are the only containers in which windows can receive focus.
bool SupportsChildActivation(aura::Window* window) {
+ // TODO(sky): straighten this out when workspace2 is the default.
+ // kShellWindowId_WorkspaceContainer isn't in |kWindowContainerIds| since it
+ // needs to be special cased in GetTopmostWindowToActivate().
+ if (window->id() == kShellWindowId_WorkspaceContainer)
+ return true;
+
for (size_t i = 0; i < arraysize(kWindowContainerIds); i++) {
- if (window->id() == kWindowContainerIds[i])
+ if (window->id() == kWindowContainerIds[i] &&
+ (window->id() != kShellWindowId_DefaultContainer ||
+ !WorkspaceController::IsWorkspace2Enabled())) {
return true;
+ }
}
return false;
}
@@ -78,7 +91,9 @@ enum ActivateVisibilityType {
bool VisibilityMatches(aura::Window* window, ActivateVisibilityType type) {
bool visible = (type == CURRENT_VISIBILITY) ? window->IsVisible() :
window->TargetVisibility();
- return visible || wm::IsWindowMinimized(window);
+ return visible || wm::IsWindowMinimized(window) ||
+ (window->TargetVisibility() &&
+ window->parent()->id() == kShellWindowId_WorkspaceContainer);
}
// Returns true if |window| can be activated or deactivated.
@@ -250,6 +265,19 @@ void ActivationController::ActivateWindowWithEvent(aura::Window* window,
if (window && !CanActivateWindowWithEvent(window, event, CURRENT_VISIBILITY))
return;
+ // Make sure the workspace manager switches to the workspace for window.
+ // Without this CanReceiveEvents() below returns false and activation never
+ // changes. CanReceiveEvents() returns false if |window| isn't in the active
+ // workspace, in which case its parent is not visible.
+ // TODO(sky): if I instead change the opacity of the parent this isn't an
+ // issue, but will make animations trickier... Consider which one is better.
+ if (window) {
+ internal::RootWindowController* root_window_controller =
+ GetRootWindowController(window->GetRootWindow());
+ root_window_controller->workspace_controller()->
+ SetActiveWorkspaceByWindow(window);
+ }
+
// Restore minimized window. This needs to be done before CanReceiveEvents()
// is called as that function checks window visibility.
if (window && wm::IsWindowMinimized(window))
@@ -321,12 +349,10 @@ aura::Window* ActivationController::GetTopmostWindowToActivate(
aura::Window* window = NULL;
for (; !window && current_container_index < arraysize(kWindowContainerIds);
current_container_index++) {
-
aura::Window::Windows containers =
Shell::GetAllContainers(kWindowContainerIds[current_container_index]);
for (aura::Window::Windows::const_iterator iter = containers.begin();
- iter != containers.end();
- ++iter) {
+ iter != containers.end() && !window; ++iter) {
window = GetTopmostWindowToActivateInContainer((*iter), ignore);
}
}
@@ -336,6 +362,21 @@ aura::Window* ActivationController::GetTopmostWindowToActivate(
aura::Window* ActivationController::GetTopmostWindowToActivateInContainer(
aura::Window* container,
aura::Window* ignore) const {
+ // Workspace2 has an extra level of windows that needs to be special cased.
+ if (container->id() == kShellWindowId_DefaultContainer &&
+ WorkspaceController::IsWorkspace2Enabled()) {
+ for (aura::Window::Windows::const_reverse_iterator i =
+ container->children().rbegin();
+ i != container->children().rend(); ++i) {
+ if ((*i)->IsVisible()) {
+ aura::Window* window = GetTopmostWindowToActivateInContainer(
+ *i, ignore);
+ if (window)
+ return window;
+ }
+ }
+ return NULL;
+ }
for (aura::Window::Windows::const_reverse_iterator i =
container->children().rbegin();
i != container->children().rend();
diff --git a/ash/wm/always_on_top_controller.cc b/ash/wm/always_on_top_controller.cc
index 2c9e920..8d148db 100644
--- a/ash/wm/always_on_top_controller.cc
+++ b/ash/wm/always_on_top_controller.cc
@@ -4,6 +4,9 @@
#include "ash/wm/always_on_top_controller.h"
+#include "ash/root_window_controller.h"
+#include "ash/wm/property_util.h"
+#include "ash/wm/workspace_controller.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
@@ -22,31 +25,40 @@ AlwaysOnTopController::~AlwaysOnTopController() {
always_on_top_container_->RemoveObserver(this);
}
-void AlwaysOnTopController::SetContainers(aura::Window* default_container,
+void AlwaysOnTopController::SetContainers(
+ aura::Window* default_container,
aura::Window* always_on_top_container) {
+ if (WorkspaceController::IsWorkspace2Enabled())
+ default_container = NULL;
+
// Both containers should have no children.
- DCHECK(default_container->children().empty());
DCHECK(always_on_top_container->children().empty());
// We are not handling any containers yet.
DCHECK(default_container_ == NULL && always_on_top_container_ == NULL);
default_container_ = default_container;
- default_container_->AddObserver(this);
+ if (default_container_)
+ default_container_->AddObserver(this);
always_on_top_container_ = always_on_top_container;
always_on_top_container_->AddObserver(this);
}
aura::Window* AlwaysOnTopController::GetContainer(aura::Window* window) const {
- DCHECK(default_container_ && always_on_top_container_);
- return !window->GetProperty(aura::client::kAlwaysOnTopKey) ?
- default_container_ : always_on_top_container_;
+ DCHECK(always_on_top_container_ &&
+ (default_container_ || WorkspaceController::IsWorkspace2Enabled()));
+ if (window->GetProperty(aura::client::kAlwaysOnTopKey))
+ return always_on_top_container_;
+ if (default_container_)
+ return default_container_;
+ return GetRootWindowController(always_on_top_container_->GetRootWindow())->
+ workspace_controller()->GetParentForNewWindow(window);
}
void AlwaysOnTopController::OnWindowAdded(aura::Window* child) {
// Observe direct child of the containers.
- if (child->parent() == default_container_ ||
+ if ((default_container_ && child->parent() == default_container_) ||
child->parent() == always_on_top_container_) {
child->AddObserver(this);
}
diff --git a/ash/wm/always_on_top_controller.h b/ash/wm/always_on_top_controller.h
index 22fe342..386ce27 100644
--- a/ash/wm/always_on_top_controller.h
+++ b/ash/wm/always_on_top_controller.h
@@ -41,6 +41,8 @@ class AlwaysOnTopController : public aura::WindowObserver {
intptr_t old) OVERRIDE;
virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+ // TODO(sky): remove this after workspace2 is the default.
+ // Is NULL if workspace2 is enabled.
aura::Window* default_container_;
aura::Window* always_on_top_container_;
diff --git a/ash/wm/base_layout_manager.cc b/ash/wm/base_layout_manager.cc
index cd582ad..bf4e47c 100644
--- a/ash/wm/base_layout_manager.cc
+++ b/ash/wm/base_layout_manager.cc
@@ -11,6 +11,7 @@
#include "ash/wm/window_animations.h"
#include "ash/wm/window_properties.h"
#include "ash/wm/window_util.h"
+#include "ash/wm/workspace_controller.h"
#include "base/command_line.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/root_window.h"
@@ -143,7 +144,9 @@ void BaseLayoutManager::OnWindowPropertyChanged(aura::Window* window,
SetRestoreBoundsInParent(window, window->bounds());
}
// Minimized state handles its own animations.
- bool animate = (old_state != ui::SHOW_STATE_MINIMIZED);
+ // TODO(sky): get animations to work with Workspace2.
+ bool animate = (old_state != ui::SHOW_STATE_MINIMIZED) &&
+ !WorkspaceController::IsWorkspace2Enabled();
UpdateBoundsFromShowState(window, animate);
ShowStateChanged(window, old_state);
}
diff --git a/ash/wm/window_cycle_controller.cc b/ash/wm/window_cycle_controller.cc
index 3223018..9f12a13 100644
--- a/ash/wm/window_cycle_controller.cc
+++ b/ash/wm/window_cycle_controller.cc
@@ -11,6 +11,7 @@
#include "ash/wm/activation_controller.h"
#include "ash/wm/window_cycle_list.h"
#include "ash/wm/window_util.h"
+#include "ash/wm/workspace_controller.h"
#include "ui/aura/event_filter.h"
#include "ui/aura/root_window.h"
#include "ui/base/event.h"
@@ -84,6 +85,34 @@ ui::GestureStatus WindowCycleEventFilter::PreHandleGestureEvent(
return ui::GESTURE_STATUS_UNKNOWN; // Not handled.
}
+// Adds all the children of |window| to |windows|.
+void AddAllChildren(aura::Window* window,
+ WindowCycleList::WindowList* windows) {
+ const WindowCycleList::WindowList& children(window->children());
+ windows->insert(windows->end(), children.begin(), children.end());
+}
+
+// Adds all the children of all of |window|s children to |windows|.
+void AddWorkspace2Children(aura::Window* window,
+ WindowCycleList::WindowList* windows) {
+ for (size_t i = 0; i < window->children().size(); ++i)
+ AddAllChildren(window->children()[i], windows);
+}
+
+// Adds the windows that can be cycled through for the specified window id to
+// |windows|.
+void AddCycleWindows(aura::RootWindow* root,
+ int container_id,
+ WindowCycleList::WindowList* windows) {
+ aura::Window* container = Shell::GetContainer(root, container_id);
+ if (container_id == internal::kShellWindowId_DefaultContainer &&
+ internal::WorkspaceController::IsWorkspace2Enabled()) {
+ AddWorkspace2Children(container, windows);
+ } else {
+ AddAllChildren(container, windows);
+ }
+}
+
} // namespace
//////////////////////////////////////////////////////////////////////////////
@@ -91,7 +120,7 @@ ui::GestureStatus WindowCycleEventFilter::PreHandleGestureEvent(
WindowCycleController::WindowCycleController(
internal::ActivationController* activation_controller)
- : activation_controller_(activation_controller) {
+ : activation_controller_(activation_controller) {
activation_controller_->AddObserver(this);
}
@@ -104,6 +133,16 @@ WindowCycleController::~WindowCycleController() {
if (container)
container->RemoveObserver(this);
}
+ aura::Window* default_container =
+ Shell::GetContainer(*iter, internal::kShellWindowId_DefaultContainer);
+ if (default_container) {
+ for (size_t i = 0; i < default_container->children().size(); ++i) {
+ aura::Window* workspace_window = default_container->children()[i];
+ DCHECK_EQ(internal::kShellWindowId_WorkspaceContainer,
+ workspace_window->id());
+ workspace_window->RemoveObserver(this);
+ }
+ }
}
activation_controller_->RemoveObserver(this);
@@ -152,26 +191,19 @@ std::vector<aura::Window*> WindowCycleController::BuildWindowList(
WindowCycleList::WindowList windows;
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
+ aura::RootWindow* active_root = Shell::GetActiveRootWindow();
for (Shell::RootWindowList::const_iterator iter = root_windows.begin();
iter != root_windows.end(); ++iter) {
- if (*iter == Shell::GetActiveRootWindow())
+ if (*iter == active_root)
continue;
- for (size_t i = 0; i < arraysize(kContainerIds); ++i) {
- aura::Window* container = Shell::GetContainer(*iter, kContainerIds[i]);
- WindowCycleList::WindowList children = container->children();
- windows.insert(windows.end(), children.begin(), children.end());
- }
+ for (size_t i = 0; i < arraysize(kContainerIds); ++i)
+ AddCycleWindows(*iter, kContainerIds[i], &windows);
}
// Add windows in the active root windows last so that the topmost window
// in the active root window becomes the front of the list.
- for (size_t i = 0; i < arraysize(kContainerIds); ++i) {
- aura::Window* container =
- Shell::GetContainer(Shell::GetActiveRootWindow(), kContainerIds[i]);
-
- WindowCycleList::WindowList children = container->children();
- windows.insert(windows.end(), children.begin(), children.end());
- }
+ for (size_t i = 0; i < arraysize(kContainerIds); ++i)
+ AddCycleWindows(active_root, kContainerIds[i], &windows);
// Removes unfocusable windows.
WindowCycleList::WindowList::iterator last =
@@ -209,6 +241,16 @@ void WindowCycleController::OnRootWindowAdded(aura::RootWindow* root_window) {
Shell::GetContainer(root_window, kContainerIds[i]);
container->AddObserver(this);
}
+
+ aura::Window* default_container =
+ Shell::GetContainer(root_window,
+ internal::kShellWindowId_DefaultContainer);
+ for (size_t i = 0; i < default_container->children().size(); ++i) {
+ aura::Window* workspace_window = default_container->children()[i];
+ DCHECK_EQ(internal::kShellWindowId_WorkspaceContainer,
+ workspace_window->id());
+ workspace_window->AddObserver(this);
+ }
}
//////////////////////////////////////////////////////////////////////////////
@@ -247,7 +289,7 @@ bool WindowCycleController::IsTrackedContainer(aura::Window* window) {
return true;
}
}
- return false;
+ return window->id() == internal::kShellWindowId_WorkspaceContainer;
}
void WindowCycleController::InstallEventFilter() {
@@ -263,8 +305,15 @@ void WindowCycleController::OnWindowActivated(aura::Window* active,
}
}
+void WindowCycleController::OnWindowAdded(aura::Window* window) {
+ if (window->id() == internal::kShellWindowId_WorkspaceContainer)
+ window->AddObserver(this);
+}
+
void WindowCycleController::OnWillRemoveWindow(aura::Window* window) {
mru_windows_.remove(window);
+ if (window->id() == internal::kShellWindowId_WorkspaceContainer)
+ window->RemoveObserver(this);
}
void WindowCycleController::OnWindowDestroying(aura::Window* window) {
diff --git a/ash/wm/window_cycle_controller.h b/ash/wm/window_cycle_controller.h
index bd77b9f..76836cb 100644
--- a/ash/wm/window_cycle_controller.h
+++ b/ash/wm/window_cycle_controller.h
@@ -100,6 +100,7 @@ class ASH_EXPORT WindowCycleController
aura::Window* old_active) OVERRIDE;
// Overridden from WindowObserver:
+ virtual void OnWindowAdded(aura::Window* window) OVERRIDE;
virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h
index e994f4d..756e212 100644
--- a/ash/wm/window_util.h
+++ b/ash/wm/window_util.h
@@ -17,9 +17,6 @@ class Layer;
}
namespace ash {
-namespace internal {
-class RootWindowController;
-}
namespace wm {
// Convenience setters/getters for |aura::client::kRootWindowActiveWindow|.
diff --git a/ash/wm/workspace/workspace2.cc b/ash/wm/workspace/workspace2.cc
new file mode 100644
index 0000000..9d585b5
--- /dev/null
+++ b/ash/wm/workspace/workspace2.cc
@@ -0,0 +1,86 @@
+// 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/workspace2.h"
+
+#include "ash/shell_window_ids.h"
+#include "ash/wm/property_util.h"
+#include "ash/wm/window_properties.h"
+#include "ash/wm/window_util.h"
+#include "ash/wm/workspace/workspace_event_filter.h"
+#include "ash/wm/workspace/workspace_manager2.h"
+#include "ui/aura/window.h"
+
+namespace ash {
+namespace internal {
+
+Workspace2::Workspace2(WorkspaceManager2* manager,
+ aura::Window* parent,
+ bool is_maximized)
+ : is_maximized_(is_maximized),
+ workspace_manager_(manager),
+ window_(new aura::Window(NULL)),
+ event_filter_(new WorkspaceEventFilter(window_)) {
+ window_->set_id(kShellWindowId_WorkspaceContainer);
+ window_->SetName("WorkspaceContainer");
+ window_->Init(ui::LAYER_NOT_DRAWN);
+ window_->Show();
+ window_->SetParent(parent);
+ window_->SetEventFilter(event_filter_);
+ window_->SetProperty(internal::kUsesScreenCoordinatesKey, true);
+}
+
+Workspace2::~Workspace2() {
+ // ReleaseWindow() should have been invoked before we're deleted.
+ DCHECK(!window_);
+}
+
+aura::Window* Workspace2::ReleaseWindow() {
+ // Remove the LayoutManager and EventFilter as they refer back to us and/or
+ // WorkspaceManager.
+ window_->SetLayoutManager(NULL);
+ window_->SetEventFilter(NULL);
+ aura::Window* window = window_;
+ window_ = NULL;
+ event_filter_ = NULL;
+ return window;
+}
+
+void Workspace2::SetGridSize(int grid_size) {
+ event_filter_->set_grid_size(grid_size);
+}
+
+bool Workspace2::ShouldMoveToPending() const {
+ if (!is_maximized_)
+ return false;
+
+ bool has_visible_non_maximized_window = false;
+ for (size_t i = 0; i < window_->children().size(); ++i) {
+ aura::Window* child(window_->children()[i]);
+ if (!child->TargetVisibility() || wm::IsWindowMinimized(child))
+ continue;
+ if (WorkspaceManager2::IsMaximized(child))
+ return false;
+
+ if (GetTrackedByWorkspace(child) && !GetPersistsAcrossAllWorkspaces(child))
+ has_visible_non_maximized_window = true;
+ }
+ return !has_visible_non_maximized_window;
+}
+
+int Workspace2::GetNumMaximizedWindows() const {
+ int count = 0;
+ for (size_t i = 0; i < window_->children().size(); ++i) {
+ aura::Window* child = window_->children()[i];
+ if (WorkspaceManager2::IsMaximized(child) ||
+ WorkspaceManager2::WillRestoreMaximized(child)) {
+ if (++count == 2)
+ return count;
+ }
+ }
+ return count;
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/wm/workspace/workspace2.h b/ash/wm/workspace/workspace2.h
new file mode 100644
index 0000000..3045424
--- /dev/null
+++ b/ash/wm/workspace/workspace2.h
@@ -0,0 +1,74 @@
+// 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_WORKSPACE2_H_
+#define ASH_WM_WORKSPACE_WORKSPACE_H_
+
+#include <vector>
+
+#include "ash/ash_export.h"
+#include "base/basictypes.h"
+
+namespace aura {
+class Window;
+}
+
+namespace gfx {
+class Rect;
+}
+
+namespace ash {
+namespace internal {
+
+class WorkspaceEventFilter;
+class WorkspaceManager2;
+
+// Workspace is used to maintain either a single maximized windows (including
+// transients and other windows) or any number of windows (for the
+// desktop). Workspace is used by WorkspaceManager to manage a set of windows.
+class ASH_EXPORT Workspace2 {
+ public:
+ Workspace2(WorkspaceManager2* manager,
+ aura::Window* parent,
+ bool is_maximized);
+ ~Workspace2();
+
+ // Resets state. This should be used before destroying the Workspace.
+ aura::Window* ReleaseWindow();
+
+ bool is_maximized() const { return is_maximized_; }
+
+ aura::Window* window() { return window_; }
+
+ WorkspaceManager2* workspace_manager() { return workspace_manager_; }
+
+ void SetGridSize(int grid_size);
+
+ // Returns true if the Workspace should be moved to pending. This is true
+ // if there are no visible maximized windows.
+ bool ShouldMoveToPending() const;
+
+ // Returns the number of maximized windows (including minimized windows that
+ // would be maximized on restore). This does not consider visibility.
+ int GetNumMaximizedWindows() const;
+
+ private:
+ // Is this a workspace for maximized windows?
+ const bool is_maximized_;
+
+ WorkspaceManager2* workspace_manager_;
+
+ // Our Window, owned by |parent| passed to the constructor.
+ aura::Window* window_;
+
+ // Owned by |window_|.
+ WorkspaceEventFilter* event_filter_;
+
+ DISALLOW_COPY_AND_ASSIGN(Workspace2);
+};
+
+} // namespace internal
+} // namespace ash
+
+#endif // ASH_WM_WORKSPACE_WORKSPACE2_H_
diff --git a/ash/wm/workspace/workspace_manager2.cc b/ash/wm/workspace/workspace_manager2.cc
new file mode 100644
index 0000000..385e346
--- /dev/null
+++ b/ash/wm/workspace/workspace_manager2.cc
@@ -0,0 +1,522 @@
+// 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_manager2.h"
+
+#include <algorithm>
+#include <functional>
+
+#include "ash/screen_ash.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/always_on_top_controller.h"
+#include "ash/wm/base_layout_manager.h"
+#include "ash/wm/property_util.h"
+#include "ash/wm/shelf_layout_manager.h"
+#include "ash/wm/window_animations.h"
+#include "ash/wm/window_properties.h"
+#include "ash/wm/window_util.h"
+#include "ash/wm/workspace/workspace2.h"
+#include "base/auto_reset.h"
+#include "base/logging.h"
+#include "base/stl_util.h"
+#include "base/stringprintf.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/env.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#include "ui/aura/window_property.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/compositor/layer.h"
+#include "ui/compositor/layer_animator.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/screen.h"
+#include "ui/gfx/transform.h"
+
+DECLARE_WINDOW_PROPERTY_TYPE(ash::internal::Workspace2*);
+
+using aura::Window;
+
+namespace ash {
+namespace internal {
+
+DEFINE_WINDOW_PROPERTY_KEY(Workspace2*, kWorkspaceKey, NULL);
+
+namespace {
+
+// Changes the parent of |window| and all its transient children to
+// |new_parent|. If |stack_beneach| is non-NULL all the windows are stacked
+// beneath it.
+void ReparentWindow(Window* window,
+ Window* new_parent,
+ Window* stack_beneath) {
+ window->SetParent(new_parent);
+ if (stack_beneath)
+ new_parent->StackChildBelow(window, stack_beneath);
+ for (size_t i = 0; i < window->transient_children().size(); ++i)
+ ReparentWindow(window->transient_children()[i], new_parent, stack_beneath);
+}
+
+// Workspace -------------------------------------------------------------------
+
+// LayoutManager installed on the parent window of all the Workspace windows (eg
+// |WorkspaceManager2::contents_view_|).
+class WorkspaceManagerLayoutManager2 : public BaseLayoutManager {
+ public:
+ WorkspaceManagerLayoutManager2(Window* window)
+ : BaseLayoutManager(window->GetRootWindow()),
+ window_(window) {
+ }
+ virtual ~WorkspaceManagerLayoutManager2() {}
+
+ // Overridden from BaseWorkspaceLayoutManager:
+ virtual void OnWindowResized() OVERRIDE {
+ for (size_t i = 0; i < window_->children().size(); ++i)
+ window_->children()[i]->SetBounds(gfx::Rect(window_->bounds().size()));
+ }
+ virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {
+ // Only workspaces should be added as children.
+ DCHECK_EQ(kShellWindowId_WorkspaceContainer, child->id());
+ child->SetBounds(gfx::Rect(window_->bounds().size()));
+ }
+
+ private:
+ Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkspaceManagerLayoutManager2);
+};
+
+} // namespace
+
+// WorkspaceLayoutManager ------------------------------------------------------
+
+// LayoutManager installed on the window each workspace contains.
+class WorkspaceManager2::WorkspaceLayoutManager : public BaseLayoutManager {
+ public:
+ WorkspaceLayoutManager(aura::RootWindow* root_window, Workspace2* workspace)
+ : BaseLayoutManager(root_window),
+ workspace_(workspace) {
+ }
+ virtual ~WorkspaceLayoutManager() {
+ }
+
+ // Overridden from BaseWorkspaceLayoutManager:
+ virtual void OnWindowAddedToLayout(Window* child) OVERRIDE {
+ BaseLayoutManager::OnWindowAddedToLayout(child);
+ child->SetProperty(kWorkspaceKey, workspace_);
+ workspace_manager()->OnWindowAddedToWorkspace(workspace_, child);
+ }
+
+ virtual void OnWillRemoveWindowFromLayout(Window* child) OVERRIDE {
+ BaseLayoutManager::OnWillRemoveWindowFromLayout(child);
+ child->ClearProperty(kWorkspaceKey);
+ }
+
+ virtual void OnWindowRemovedFromLayout(Window* child) OVERRIDE {
+ BaseLayoutManager::OnWindowRemovedFromLayout(child);
+ workspace_manager()->OnWindowRemovedFromWorkspace(workspace_, child);
+ }
+
+ virtual void OnChildWindowVisibilityChanged(Window* child,
+ bool visibile) OVERRIDE {
+ BaseLayoutManager::OnChildWindowVisibilityChanged(child, visibile);
+ workspace_manager()->OnWorkspaceChildWindowVisibilityChanged(workspace_,
+ child);
+ }
+
+ virtual void SetChildBounds(Window* child,
+ const gfx::Rect& requested_bounds) OVERRIDE {
+ BaseLayoutManager::SetChildBounds(child, requested_bounds);
+ workspace_manager()->OnWorkspaceWindowChildBoundsChanged(workspace_, child);
+ }
+
+
+ // Overriden from WindowObserver:
+ virtual void OnWindowPropertyChanged(Window* window,
+ const void* key,
+ intptr_t old) {
+ BaseLayoutManager::OnWindowPropertyChanged(window, key, old);
+
+ if (key == aura::client::kAlwaysOnTopKey &&
+ window->GetProperty(aura::client::kAlwaysOnTopKey)) {
+ internal::AlwaysOnTopController* controller =
+ window->GetRootWindow()->GetProperty(
+ internal::kAlwaysOnTopControllerKey);
+ controller->GetContainer(window)->AddChild(window);
+ }
+ }
+
+ protected:
+ // Overriden from WindowObserver:
+ virtual void ShowStateChanged(Window* window,
+ ui::WindowShowState last_show_state) OVERRIDE {
+ // NOTE: we can't use BaseLayoutManager::ShowStateChanged() as we need to
+ // forward to WorkspaceManager before the window is hidden.
+ if (wm::IsWindowMinimized(window)) {
+ // Save the previous show state so that we can correctly restore it.
+ window->SetProperty(internal::kRestoreShowStateKey, last_show_state);
+ SetWindowVisibilityAnimationType(
+ window, WINDOW_VISIBILITY_ANIMATION_TYPE_MINIMIZE);
+
+ workspace_manager()->OnWorkspaceWindowShowStateChanged(
+ workspace_, window, last_show_state);
+
+ // Hide the window.
+ window->Hide();
+
+ // Activate another window.
+ if (wm::IsActiveWindow(window))
+ wm::DeactivateWindow(window);
+ } else {
+ if ((window->TargetVisibility() ||
+ last_show_state == ui::SHOW_STATE_MINIMIZED) &&
+ !window->layer()->visible()) {
+ // The layer may be hidden if the window was previously minimized. Make
+ // sure it's visible.
+ window->Show();
+ }
+ workspace_manager()->OnWorkspaceWindowShowStateChanged(
+ workspace_, window, last_show_state);
+ }
+ }
+
+ private:
+ WorkspaceManager2* workspace_manager() {
+ return workspace_->workspace_manager();
+ }
+
+ Workspace2* workspace_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManager);
+};
+
+// WorkspaceManager2 -----------------------------------------------------------
+
+WorkspaceManager2::WorkspaceManager2(Window* contents_view)
+ : contents_view_(contents_view),
+ active_workspace_(NULL),
+ grid_size_(0),
+ shelf_(NULL),
+ in_move_(false) {
+ // Clobber any existing event filter.
+ contents_view->SetEventFilter(NULL);
+ // |contents_view| takes ownership of WorkspaceManagerLayoutManager2.
+ contents_view->SetLayoutManager(
+ new WorkspaceManagerLayoutManager2(contents_view));
+ active_workspace_ = CreateWorkspace(false);
+ workspaces_.push_back(active_workspace_);
+}
+
+WorkspaceManager2::~WorkspaceManager2() {
+ // Release the windows, they'll be destroyed when |contents_view_| is
+ // destroyed.
+ std::for_each(workspaces_.begin(), workspaces_.end(),
+ std::mem_fun(&Workspace2::ReleaseWindow));
+ std::for_each(pending_workspaces_.begin(), pending_workspaces_.end(),
+ std::mem_fun(&Workspace2::ReleaseWindow));
+ STLDeleteElements(&workspaces_);
+ STLDeleteElements(&pending_workspaces_);
+}
+
+// static
+bool WorkspaceManager2::IsMaximized(Window* window) {
+ return wm::IsWindowFullscreen(window) || wm::IsWindowMaximized(window);
+}
+
+// static
+bool WorkspaceManager2::WillRestoreMaximized(aura::Window* window) {
+ if (!wm::IsWindowMinimized(window))
+ return false;
+
+ ui::WindowShowState restore_state =
+ window->GetProperty(internal::kRestoreShowStateKey);
+ return restore_state == ui::SHOW_STATE_MAXIMIZED ||
+ restore_state == ui::SHOW_STATE_FULLSCREEN;
+
+}
+
+bool WorkspaceManager2::IsInMaximizedMode() const {
+ return active_workspace_ && active_workspace_->is_maximized();
+}
+
+void WorkspaceManager2::SetGridSize(int size) {
+ grid_size_ = size;
+ std::for_each(workspaces_.begin(), workspaces_.end(),
+ std::bind2nd(std::mem_fun(&Workspace2::SetGridSize),
+ grid_size_));
+ std::for_each(pending_workspaces_.begin(), pending_workspaces_.end(),
+ std::bind2nd(std::mem_fun(&Workspace2::SetGridSize),
+ grid_size_));
+}
+
+int WorkspaceManager2::GetGridSize() const {
+ return grid_size_;
+}
+
+WorkspaceWindowState WorkspaceManager2::GetWindowState() const {
+ if (!shelf_)
+ return WORKSPACE_WINDOW_STATE_DEFAULT;
+
+ gfx::Rect shelf_bounds(shelf_->GetIdealBounds());
+ const Window::Windows& windows(active_workspace_->window()->children());
+ bool window_overlaps_launcher = false;
+ bool has_maximized_window = false;
+ for (Window::Windows::const_iterator i = windows.begin();
+ i != windows.end(); ++i) {
+ ui::Layer* layer = (*i)->layer();
+ if (!layer->GetTargetVisibility() || layer->GetTargetOpacity() == 0.0f)
+ continue;
+ if (wm::IsWindowMaximized(*i)) {
+ // An untracked window may still be fullscreen so we keep iterating when
+ // we hit a maximized window.
+ has_maximized_window = true;
+ } else if (wm::IsWindowFullscreen(*i)) {
+ return WORKSPACE_WINDOW_STATE_FULL_SCREEN;
+ }
+ if (!window_overlaps_launcher && (*i)->bounds().Intersects(shelf_bounds))
+ window_overlaps_launcher = true;
+ }
+ if (has_maximized_window)
+ return WORKSPACE_WINDOW_STATE_MAXIMIZED;
+
+ return window_overlaps_launcher ?
+ WORKSPACE_WINDOW_STATE_WINDOW_OVERLAPS_SHELF :
+ WORKSPACE_WINDOW_STATE_DEFAULT;
+}
+
+void WorkspaceManager2::SetShelf(ShelfLayoutManager* shelf) {
+ shelf_ = shelf;
+}
+
+void WorkspaceManager2::SetActiveWorkspaceByWindow(Window* window) {
+ Workspace2* workspace = FindBy(window);
+ if (!workspace)
+ return;
+
+ if (workspace != active_workspace_) {
+ // If the window persists across all workspaces, move it to the current
+ // workspace.
+ if (GetPersistsAcrossAllWorkspaces(window) && !IsMaximized(window))
+ ReparentWindow(window, active_workspace_->window(), NULL);
+ else
+ SetActiveWorkspace(workspace);
+ }
+ UpdateShelfVisibility();
+}
+
+Window* WorkspaceManager2::GetParentForNewWindow(Window* window) {
+ if (window->transient_parent()) {
+ DCHECK(contents_view_->Contains(window->transient_parent()));
+ DCHECK(!IsMaximized(window));
+ return window->transient_parent()->parent();
+ }
+
+ if (IsMaximized(window)) {
+ // Wait for the window to be made active before showing the workspace.
+ Workspace2* workspace = CreateWorkspace(false);
+ pending_workspaces_.insert(workspace);
+ return workspace->window();
+ }
+
+ if (!GetTrackedByWorkspace(window) || GetPersistsAcrossAllWorkspaces(window))
+ return active_workspace_->window();
+
+ return desktop_workspace()->window();
+}
+
+void WorkspaceManager2::UpdateShelfVisibility() {
+ if (shelf_)
+ shelf_->UpdateVisibilityState();
+}
+
+Workspace2* WorkspaceManager2::FindBy(Window* window) const {
+ while (window) {
+ Workspace2* workspace = window->GetProperty(kWorkspaceKey);
+ if (workspace)
+ return workspace;
+ window = window->transient_parent();
+ }
+ return NULL;
+}
+
+void WorkspaceManager2::SetActiveWorkspace(Workspace2* workspace) {
+ DCHECK(workspace);
+ if (active_workspace_ == workspace)
+ return;
+
+ // TODO: sort out animations.
+
+ pending_workspaces_.erase(workspace);
+
+ // Adjust the z-order. No need to adjust the z-order for the desktop since
+ // it always stays at the bottom.
+ if (workspace != desktop_workspace()) {
+ if (FindWorkspace(workspace) == workspaces_.end()) {
+ contents_view_->StackChildAbove(workspace->window(),
+ workspaces_.back()->window());
+ workspaces_.push_back(workspace);
+ }
+ }
+
+ active_workspace_ = workspace;
+
+ for (size_t i = 0; i < workspaces_.size(); ++i) {
+ if (workspaces_[i] == active_workspace_)
+ workspaces_[i]->window()->Show();
+ else
+ workspaces_[i]->window()->Hide();
+ }
+}
+
+WorkspaceManager2::Workspaces::iterator
+WorkspaceManager2::FindWorkspace(Workspace2* workspace) {
+ return std::find(workspaces_.begin(), workspaces_.end(), workspace);
+}
+
+Workspace2* WorkspaceManager2::CreateWorkspace(bool maximized) {
+ Workspace2* workspace = new Workspace2(this, contents_view_, maximized);
+ workspace->SetGridSize(grid_size_);
+ workspace->window()->SetLayoutManager(
+ new WorkspaceLayoutManager(contents_view_->GetRootWindow(), workspace));
+ return workspace;
+}
+
+void WorkspaceManager2::MoveWorkspaceToPendingOrDelete(
+ Workspace2* workspace,
+ Window* stack_beneath) {
+ // We're all ready moving windows.
+ if (in_move_)
+ return;
+
+ DCHECK_NE(desktop_workspace(), workspace);
+
+ if (workspace == active_workspace_)
+ SelectNextWorkspace();
+
+ AutoReset<bool> setter(&in_move_, true);
+
+ // Move all non-maximized/fullscreen windows to the desktop.
+ {
+ // Build the list of windows to move. Exclude maximized/fullscreen and
+ // windows with transient parents.
+ Window::Windows to_move;
+ Window* w_window = workspace->window();
+ for (size_t i = 0; i < w_window->children().size(); ++i) {
+ Window* child = w_window->children()[i];
+ if (!child->transient_parent() && !IsMaximized(child) &&
+ !WillRestoreMaximized(child)) {
+ to_move.push_back(child);
+ }
+ }
+ // Move the windows, but make sure the window is still a child of |w_window|
+ // before moving (moving may cascade and cause other windows to move).
+ for (size_t i = 0; i < to_move.size(); ++i) {
+ if (std::find(w_window->children().begin(), w_window->children().end(),
+ to_move[i]) != w_window->children().end()) {
+ ReparentWindow(to_move[i], desktop_workspace()->window(),
+ stack_beneath);
+ }
+ }
+ }
+
+ {
+ Workspaces::iterator workspace_i(FindWorkspace(workspace));
+ if (workspace_i != workspaces_.end())
+ workspaces_.erase(workspace_i);
+ }
+
+ if (workspace->window()->children().empty()) {
+ pending_workspaces_.erase(workspace);
+ delete workspace->ReleaseWindow();
+ delete workspace;
+ } else {
+ pending_workspaces_.insert(workspace);
+ }
+}
+
+void WorkspaceManager2::SelectNextWorkspace() {
+ DCHECK_NE(active_workspace_, desktop_workspace());
+
+ Workspaces::const_iterator workspace_i(FindWorkspace(active_workspace_));
+ Workspaces::const_iterator next_workspace_i(workspace_i + 1);
+ if (next_workspace_i != workspaces_.end())
+ SetActiveWorkspace(*next_workspace_i);
+ else
+ SetActiveWorkspace(*(workspace_i - 1));
+ UpdateShelfVisibility();
+}
+
+void WorkspaceManager2::OnWindowAddedToWorkspace(Workspace2* workspace,
+ Window* child) {
+ // Do nothing (other than updating shelf visibility) as the right parent was
+ // chosen by way of GetParentForNewWindow() or we explicitly moved the window
+ // to the workspace.
+ if (workspace == active_workspace_)
+ UpdateShelfVisibility();
+}
+
+void WorkspaceManager2::OnWindowRemovedFromWorkspace(Workspace2* workspace,
+ Window* child) {
+ if (workspace->ShouldMoveToPending())
+ MoveWorkspaceToPendingOrDelete(workspace, NULL);
+}
+
+void WorkspaceManager2::OnWorkspaceChildWindowVisibilityChanged(
+ Workspace2* workspace,
+ Window* child) {
+ if (workspace->ShouldMoveToPending())
+ MoveWorkspaceToPendingOrDelete(workspace, NULL);
+}
+
+void WorkspaceManager2::OnWorkspaceWindowChildBoundsChanged(
+ Workspace2* workspace,
+ Window* child) {
+}
+
+void WorkspaceManager2::OnWorkspaceWindowShowStateChanged(
+ Workspace2* workspace,
+ Window* child,
+ ui::WindowShowState last_show_state) {
+ if (wm::IsWindowMinimized(child)) {
+ if (workspace->ShouldMoveToPending())
+ MoveWorkspaceToPendingOrDelete(workspace, NULL);
+ } else {
+ // Here's the cases that need to be handled:
+ // . More than one maximized window: move newly maximized window into
+ // own workspace.
+ // . One maximized window and not in a maximized workspace: move window
+ // into own workspace.
+ // . No maximized window and not in desktop: move to desktop and further
+ // any existing windows are stacked beneath |child|.
+ const bool is_active = wm::IsActiveWindow(child);
+ Workspace2* new_workspace = NULL;
+ const int max_count = workspace->GetNumMaximizedWindows();
+ if (max_count == 0) {
+ if (workspace != desktop_workspace()) {
+ {
+ AutoReset<bool> setter(&in_move_, true);
+ ReparentWindow(child, desktop_workspace()->window(), NULL);
+ }
+ MoveWorkspaceToPendingOrDelete(workspace, child);
+ new_workspace = desktop_workspace();
+ }
+ } else if (max_count == 1) {
+ if (workspace == desktop_workspace()) {
+ new_workspace = CreateWorkspace(true);
+ pending_workspaces_.insert(new_workspace);
+ ReparentWindow(child, new_workspace->window(), NULL);
+ }
+ } else {
+ new_workspace = CreateWorkspace(true);
+ pending_workspaces_.insert(new_workspace);
+ ReparentWindow(child, new_workspace->window(), NULL);
+ }
+ if (is_active && new_workspace)
+ SetActiveWorkspace(new_workspace);
+ }
+ UpdateShelfVisibility();
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/wm/workspace/workspace_manager2.h b/ash/wm/workspace/workspace_manager2.h
new file mode 100644
index 0000000..de72b6a
--- /dev/null
+++ b/ash/wm/workspace/workspace_manager2.h
@@ -0,0 +1,149 @@
+// 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_MANAGER2_H_
+#define ASH_WM_WORKSPACE_WORKSPACE_MANAGER2_H_
+
+#include <set>
+#include <string>
+#include <vector>
+
+#include "ash/ash_export.h"
+#include "ash/wm/workspace/base_workspace_manager.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "ui/base/ui_base_types.h"
+
+namespace aura {
+class Window;
+}
+
+namespace gfx {
+class Point;
+class Rect;
+}
+
+namespace ash {
+namespace internal {
+
+class ShelfLayoutManager;
+class WorkspaceManagerTest2;
+class Workspace2;
+
+// WorkspaceManager manages multiple workspaces in the desktop. Workspaces are
+// implicitly created as windows are maximized (or made fullscreen), and
+// destroyed when maximized windows are closed or restored. There is always one
+// workspace for the desktop.
+// Internally WorkspaceManager2 creates a Window for each Workspace. As windows
+// are maximized and restored they are reparented to the right Window.
+class ASH_EXPORT WorkspaceManager2 : public BaseWorkspaceManager {
+ public:
+ explicit WorkspaceManager2(aura::Window* viewport);
+ virtual ~WorkspaceManager2();
+
+ // Returns true if |window| is considered maximized and should exist in its
+ // own workspace.
+ static bool IsMaximized(aura::Window* window);
+
+ // Returns true if |window| is minimized and will restore to a maximized
+ // window.
+ static bool WillRestoreMaximized(aura::Window* window);
+
+ // BaseWorkspaceManager2 overrides:
+ virtual bool IsInMaximizedMode() const OVERRIDE;
+ virtual void SetGridSize(int size) OVERRIDE;
+ virtual int GetGridSize() const OVERRIDE;
+ virtual WorkspaceWindowState GetWindowState() const OVERRIDE;
+ virtual void SetShelf(ShelfLayoutManager* shelf) OVERRIDE;
+ virtual void SetActiveWorkspaceByWindow(aura::Window* window) OVERRIDE;
+ virtual aura::Window* GetParentForNewWindow(aura::Window* window) OVERRIDE;
+
+ private:
+ friend class WorkspaceManager2Test;
+
+ class LayoutManager;
+ class WorkspaceLayoutManager;
+
+ typedef std::vector<Workspace2*> Workspaces;
+
+ // Updates the visibility and whether any windows overlap the shelf.
+ void UpdateShelfVisibility();
+
+ // Returns the workspace that contains |window|.
+ Workspace2* FindBy(aura::Window* window) const;
+
+ // Sets the active workspace.
+ void SetActiveWorkspace(Workspace2* workspace);
+
+ // Returns the bounds of the work area.
+ gfx::Rect GetWorkAreaBounds() const;
+
+ // Returns an iterator into |workspaces_| for |workspace|.
+ Workspaces::iterator FindWorkspace(Workspace2* workspace);
+
+ Workspace2* desktop_workspace() { return workspaces_[0]; }
+ const Workspace2* desktop_workspace() const { return workspaces_[0]; }
+
+ // Creates a new workspace. The Workspace is not added to anything and is
+ // owned by the caller.
+ Workspace2* CreateWorkspace(bool maximized);
+
+ // Moves all the non-maximized child windows of |workspace| to the desktop
+ // stacked beneath |stack_beneath| (if non-NULL). After moving child windows
+ // if |workspace| contains no children it is deleted, otherwise it it moved to
+ // |pending_workspaces_|.
+ void MoveWorkspaceToPendingOrDelete(Workspace2* workspace,
+ aura::Window* stack_beneath);
+
+ // Selects the next workspace.
+ void SelectNextWorkspace();
+
+ // These methods are forwarded from the LayoutManager installed on the
+ // Workspace's window.
+ void OnWindowAddedToWorkspace(Workspace2* workspace, aura::Window* child);
+ void OnWindowRemovedFromWorkspace(Workspace2* workspace, aura::Window* child);
+ void OnWorkspaceChildWindowVisibilityChanged(Workspace2* workspace,
+ aura::Window* child);
+ void OnWorkspaceWindowChildBoundsChanged(Workspace2* workspace,
+ aura::Window* child);
+ void OnWorkspaceWindowShowStateChanged(Workspace2* workspace,
+ aura::Window* child,
+ ui::WindowShowState last_show_state);
+
+ aura::Window* contents_view_;
+
+ Workspace2* active_workspace_;
+
+ // The set of active workspaces. There is always at least one in this stack,
+ // which identifies the desktop.
+ Workspaces workspaces_;
+
+ // The set of workspaces not currently active. Workspaces ended up here in
+ // two scenarios:
+ // . Prior to adding a window a new worskpace is created for it. The
+ // Workspace is added to this set.
+ // . When the maximized window is minimized the workspace is added here.
+ // Once any window in the workspace is activated the workspace is moved to
+ // |workspaces_|.
+ std::set<Workspace2*> pending_workspaces_;
+
+ // See description above setter.
+ int grid_size_;
+
+ // Owned by the Shell. May be NULL.
+ ShelfLayoutManager* shelf_;
+
+ // Whether or not we're in MoveWorkspaceToPendingOrDelete(). As
+ // MoveWorkspaceToPendingOrDelete() may trigger another call to
+ // MoveWorkspaceToPendingOrDelete() we use this to avoid doing anything if
+ // already in MoveWorkspaceToPendingOrDelete().
+ bool in_move_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkspaceManager2);
+};
+
+} // namespace internal
+} // namespace ash
+
+#endif // ASH_WM_WORKSPACE_WORKSPACE_MANAGER2_H_
diff --git a/ash/wm/workspace/workspace_manager2_unittest.cc b/ash/wm/workspace/workspace_manager2_unittest.cc
new file mode 100644
index 0000000..2b3327c
--- /dev/null
+++ b/ash/wm/workspace/workspace_manager2_unittest.cc
@@ -0,0 +1,764 @@
+// 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_manager2.h"
+
+#include "ash/ash_switches.h"
+#include "ash/screen_ash.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/wm/activation_controller.h"
+#include "ash/wm/property_util.h"
+#include "ash/wm/shelf_layout_manager.h"
+#include "ash/wm/window_util.h"
+#include "ash/wm/workspace/workspace2.h"
+#include "ash/wm/workspace_controller_test_helper.h"
+#include "base/command_line.h"
+#include "base/string_number_conversions.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/window.h"
+#include "ui/base/ui_base_types.h"
+#include "ui/compositor/layer.h"
+#include "ui/gfx/screen.h"
+
+using aura::Window;
+
+namespace ash {
+namespace internal {
+
+class WorkspaceManager2Test : public test::AshTestBase {
+ public:
+ WorkspaceManager2Test() : manager_(NULL) {}
+ virtual ~WorkspaceManager2Test() {}
+
+ aura::Window* CreateTestWindowUnparented() {
+ aura::Window* window = new aura::Window(NULL);
+ window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ window->SetType(aura::client::WINDOW_TYPE_NORMAL);
+ window->Init(ui::LAYER_TEXTURED);
+ return window;
+ }
+
+ aura::Window* CreateTestWindow() {
+ aura::Window* window = new aura::Window(NULL);
+ window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ window->SetType(aura::client::WINDOW_TYPE_NORMAL);
+ window->Init(ui::LAYER_TEXTURED);
+ window->SetParent(NULL);
+ return window;
+ }
+
+ aura::Window* GetViewport() {
+ return Shell::GetContainer(Shell::GetPrimaryRootWindow(),
+ kShellWindowId_DefaultContainer);
+ }
+
+ const std::vector<Workspace2*>& workspaces() const {
+ return manager_->workspaces_;
+ }
+
+ gfx::Rect GetFullscreenBounds(aura::Window* window) {
+ return gfx::Screen::GetDisplayNearestWindow(window).bounds();
+ }
+
+ Workspace2* active_workspace() {
+ return manager_->active_workspace_;
+ }
+
+ Workspace2* FindBy(aura::Window* window) const {
+ return manager_->FindBy(window);
+ }
+
+ std::string WorkspaceStateString(Workspace2* workspace) {
+ return (workspace->is_maximized() ? "M" : "") +
+ base::IntToString(static_cast<int>(
+ workspace->window()->children().size()));
+ }
+
+ int active_index() {
+ return static_cast<int>(
+ manager_->FindWorkspace(manager_->active_workspace_) -
+ manager_->workspaces_.begin());
+ }
+
+ std::string StateString() {
+ std::string result;
+ for (size_t i = 0; i < manager_->workspaces_.size(); ++i) {
+ if (i > 0)
+ result += " ";
+ result += WorkspaceStateString(manager_->workspaces_[i]);
+ }
+
+ if (!manager_->pending_workspaces_.empty()) {
+ result += " P=";
+ for (std::set<Workspace2*>::const_iterator i =
+ manager_->pending_workspaces_.begin();
+ i != manager_->pending_workspaces_.end(); ++i) {
+ if (i != manager_->pending_workspaces_.begin())
+ result += " ";
+ result += WorkspaceStateString(*i);
+ }
+ }
+
+ result += " active=" + base::IntToString(active_index());
+ return result;
+ }
+
+ // Overridden from AshTestBase:
+ virtual void SetUp() OVERRIDE {
+ CommandLine::ForCurrentProcess()->AppendSwitch(
+ switches::kAshEnableWorkspace2);
+ test::AshTestBase::SetUp();
+ WorkspaceControllerTestHelper workspace_helper(
+ Shell::TestApi(Shell::GetInstance()).workspace_controller());
+ manager_ = workspace_helper.workspace_manager2();
+ manager_->SetGridSize(0);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ manager_ = NULL;
+ test::AshTestBase::TearDown();
+ }
+
+ protected:
+ WorkspaceManager2* manager_;
+
+ private:
+ scoped_ptr<ActivationController> activation_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(WorkspaceManager2Test);
+};
+
+// Assertions around adding a normal window.
+TEST_F(WorkspaceManager2Test, AddNormalWindowWhenEmpty) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->SetBounds(gfx::Rect(0, 0, 250, 251));
+
+ EXPECT_TRUE(GetRestoreBoundsInScreen(w1.get()) == NULL);
+
+ w1->Show();
+
+ EXPECT_TRUE(GetRestoreBoundsInScreen(w1.get()) == NULL);
+
+ ASSERT_TRUE(w1->layer() != NULL);
+ EXPECT_TRUE(w1->layer()->visible());
+
+ EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
+
+ // Should be 1 workspace for the desktop, not maximized.
+ ASSERT_EQ("1 active=0", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[0]->window()->children()[0]);
+}
+
+// Assertions around maximizing/unmaximizing.
+TEST_F(WorkspaceManager2Test, SingleMaximizeWindow) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->SetBounds(gfx::Rect(0, 0, 250, 251));
+
+ w1->Show();
+ wm::ActivateWindow(w1.get());
+
+ EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
+
+ ASSERT_TRUE(w1->layer() != NULL);
+ EXPECT_TRUE(w1->layer()->visible());
+
+ EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
+
+ // Maximize the window.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+
+ EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
+
+ // Should be 2 workspaces, the second maximized with w1.
+ ASSERT_EQ("0 M1 active=1", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[1]->window()->children()[0]);
+ EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()).width(),
+ w1->bounds().width());
+ EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()).height(),
+ w1->bounds().height());
+
+ // Restore the window.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+
+ // Should be 1 workspace for the desktop.
+ ASSERT_EQ("1 active=0", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[0]->window()->children()[0]);
+ EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
+}
+
+// Assertions around closing the last window in a workspace.
+TEST_F(WorkspaceManager2Test, CloseLastWindowInWorkspace) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ scoped_ptr<Window> w2(CreateTestWindow());
+ w1->SetBounds(gfx::Rect(0, 0, 250, 251));
+ w1->Show();
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ w2->Show();
+ wm::ActivateWindow(w1.get());
+
+ // Should be 1 workspace and 1 pending, !maximized and maximized. The second
+ // workspace is pending since the window wasn't active.
+ ASSERT_EQ("1 P=M1 active=0", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[0]->window()->children()[0]);
+
+ // Close w2.
+ w2.reset();
+
+ // Should have one workspace.
+ ASSERT_EQ("1 active=0", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[0]->window()->children()[0]);
+ EXPECT_TRUE(w1->IsVisible());
+}
+
+// Assertions around adding a maximized window when empty.
+TEST_F(WorkspaceManager2Test, AddMaximizedWindowWhenEmpty) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->SetBounds(gfx::Rect(0, 0, 250, 251));
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ w1->Show();
+ wm::ActivateWindow(w1.get());
+
+ ASSERT_TRUE(w1->layer() != NULL);
+ EXPECT_TRUE(w1->layer()->visible());
+ gfx::Rect work_area(
+ ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()));
+ EXPECT_EQ(work_area.width(), w1->bounds().width());
+ EXPECT_EQ(work_area.height(), w1->bounds().height());
+
+ // Should be 2 workspaces (since we always keep the desktop).
+ ASSERT_EQ("0 M1 active=1", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[1]->window()->children()[0]);
+}
+
+// Assertions around two windows and toggling one to be maximized.
+TEST_F(WorkspaceManager2Test, MaximizeWithNormalWindow) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ scoped_ptr<Window> w2(CreateTestWindow());
+ w1->SetBounds(gfx::Rect(0, 0, 250, 251));
+ w1->Show();
+
+ ASSERT_TRUE(w1->layer() != NULL);
+ EXPECT_TRUE(w1->layer()->visible());
+
+ w2->SetBounds(gfx::Rect(0, 0, 50, 51));
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ w2->Show();
+ wm::ActivateWindow(w2.get());
+
+ // Should now be two workspaces.
+ ASSERT_EQ("1 M1 active=1", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[0]->window()->children()[0]);
+ EXPECT_EQ(w2.get(), workspaces()[1]->window()->children()[0]);
+
+ gfx::Rect work_area(ScreenAsh::GetMaximizedWindowBoundsInParent(w1.get()));
+ EXPECT_EQ(work_area.width(), w2->bounds().width());
+ EXPECT_EQ(work_area.height(), w2->bounds().height());
+
+ // Restore w2, which should then go back to one workspace.
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ ASSERT_EQ("2 active=0", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[0]->window()->children()[0]);
+ EXPECT_EQ(w2.get(), workspaces()[0]->window()->children()[1]);
+ EXPECT_EQ(50, w2->bounds().width());
+ EXPECT_EQ(51, w2->bounds().height());
+ EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
+}
+
+// Assertions around two maximized windows.
+TEST_F(WorkspaceManager2Test, TwoMaximized) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ scoped_ptr<Window> w2(CreateTestWindow());
+ w1->SetBounds(gfx::Rect(0, 0, 250, 251));
+ w1->Show();
+ wm::ActivateWindow(w1.get());
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ ASSERT_EQ("1 M1 active=1", StateString());
+
+ w2->SetBounds(gfx::Rect(0, 0, 50, 51));
+ w2->Show();
+ wm::ActivateWindow(w2.get());
+ ASSERT_EQ("1 M1 active=0", StateString());
+
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
+ ASSERT_EQ("0 M1 M1 active=2", StateString());
+
+ // The last stacked window (|w2|) should be last since it was maximized last.
+ EXPECT_EQ(w1.get(), workspaces()[1]->window()->children()[0]);
+ EXPECT_EQ(w2.get(), workspaces()[2]->window()->children()[0]);
+}
+
+// Makes sure requests to change the bounds of a normal window go through.
+TEST_F(WorkspaceManager2Test, ChangeBoundsOfNormalWindow) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->Show();
+
+ // Setting the bounds should go through since the window is in the normal
+ // workspace.
+ w1->SetBounds(gfx::Rect(0, 0, 200, 500));
+ EXPECT_EQ(200, w1->bounds().width());
+ EXPECT_EQ(500, w1->bounds().height());
+}
+
+// Verifies the bounds is not altered when showing and grid is enabled.
+TEST_F(WorkspaceManager2Test, SnapToGrid) {
+ manager_->SetGridSize(8);
+
+ scoped_ptr<Window> w1(CreateTestWindowUnparented());
+ w1->SetBounds(gfx::Rect(1, 6, 25, 30));
+ w1->SetParent(NULL);
+ // We are not aligning this anymore this way. When the window gets shown
+ // the window is expected to be handled differently, but this cannot be
+ // tested with this test. So the result of this test should be that the
+ // bounds are exactly as passed in.
+ EXPECT_EQ("1,6 25x30", w1->bounds().ToString());
+}
+
+// Assertions around a fullscreen window.
+TEST_F(WorkspaceManager2Test, SingleFullscreenWindow) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->SetBounds(gfx::Rect(0, 0, 250, 251));
+ // Make the window fullscreen.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
+ w1->Show();
+ wm::ActivateWindow(w1.get());
+
+ // Should be 2 workspaces, normal and maximized.
+ ASSERT_EQ("0 M1 active=1", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[1]->window()->children()[0]);
+ EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
+ EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
+
+ // Restore the window. Use SHOW_STATE_DEFAULT as that is what we'll end up
+ // with when using views::Widget.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT);
+ EXPECT_EQ("0,0 250x251", w1->bounds().ToString());
+
+ // Should be 1 workspace for the desktop.
+ ASSERT_EQ("1 active=0", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[0]->window()->children()[0]);
+ EXPECT_EQ(250, w1->bounds().width());
+ EXPECT_EQ(251, w1->bounds().height());
+
+ // Back to fullscreen.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
+ ASSERT_EQ("0 M1 active=1", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[1]->window()->children()[0]);
+ EXPECT_EQ(GetFullscreenBounds(w1.get()).width(), w1->bounds().width());
+ EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height());
+ ASSERT_TRUE(GetRestoreBoundsInScreen(w1.get()));
+ EXPECT_EQ("0,0 250x251", GetRestoreBoundsInScreen(w1.get())->ToString());
+}
+
+// Makes sure switching workspaces doesn't show transient windows.
+TEST_F(WorkspaceManager2Test, DontShowTransientsOnSwitch) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ scoped_ptr<Window> w2(CreateTestWindow());
+
+ w1->SetBounds(gfx::Rect(0, 0, 250, 251));
+ w2->SetBounds(gfx::Rect(0, 0, 250, 251));
+ w1->AddTransientChild(w2.get());
+
+ w1->Show();
+
+ scoped_ptr<Window> w3(CreateTestWindow());
+ w3->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ w3->Show();
+ wm::ActivateWindow(w3.get());
+
+ EXPECT_FALSE(w1->layer()->IsDrawn());
+ EXPECT_FALSE(w2->layer()->IsDrawn());
+ EXPECT_TRUE(w3->layer()->IsDrawn());
+
+ wm::ActivateWindow(w1.get());
+ EXPECT_TRUE(w1->layer()->IsDrawn());
+ EXPECT_FALSE(w2->layer()->IsDrawn());
+ EXPECT_FALSE(w3->layer()->IsDrawn());
+}
+
+// Assertions around minimizing a single window.
+TEST_F(WorkspaceManager2Test, MinimizeSingleWindow) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+
+ w1->Show();
+ ASSERT_EQ("1 active=0", StateString());
+
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+ ASSERT_EQ("1 active=0", StateString());
+ EXPECT_FALSE(w1->layer()->IsDrawn());
+
+ // Show the window.
+ w1->Show();
+ EXPECT_TRUE(wm::IsWindowNormal(w1.get()));
+ ASSERT_EQ("1 active=0", StateString());
+ EXPECT_TRUE(w1->layer()->IsDrawn());
+}
+
+// Assertions around minimizing a maximized window.
+TEST_F(WorkspaceManager2Test, MinimizeMaximizedWindow) {
+ // Two windows, w1 normal, w2 maximized.
+ scoped_ptr<Window> w1(CreateTestWindow());
+ scoped_ptr<Window> w2(CreateTestWindow());
+ w1->Show();
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ w2->Show();
+ wm::ActivateWindow(w2.get());
+ ASSERT_EQ("1 M1 active=1", StateString());
+
+ // Minimize w2.
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+ ASSERT_EQ("1 P=M1 active=0", StateString());
+ EXPECT_TRUE(w1->layer()->IsDrawn());
+ EXPECT_FALSE(w2->layer()->IsDrawn());
+
+ // Show the window, which should trigger unminimizing.
+ w2->Show();
+ ASSERT_EQ("1 P=M1 active=0", StateString());
+
+ wm::ActivateWindow(w2.get());
+ ASSERT_EQ("1 M1 active=1", StateString());
+
+ EXPECT_TRUE(wm::IsWindowMaximized(w2.get()));
+ EXPECT_FALSE(w1->layer()->IsDrawn());
+ EXPECT_TRUE(w2->layer()->IsDrawn());
+
+ // Minimize the window, which should hide the window and activate another.
+ EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+ EXPECT_FALSE(wm::IsActiveWindow(w2.get()));
+ EXPECT_FALSE(w2->layer()->IsDrawn());
+ EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
+
+ // Make the window normal.
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ ASSERT_EQ("2 active=0", StateString());
+ EXPECT_EQ(w1.get(), workspaces()[0]->window()->children()[0]);
+ EXPECT_EQ(w2.get(), workspaces()[0]->window()->children()[1]);
+ EXPECT_TRUE(w2->layer()->IsDrawn());
+}
+
+// Verifies ShelfLayoutManager's visibility/auto-hide state is correctly
+// updated.
+TEST_F(WorkspaceManager2Test, ShelfStateUpdated) {
+ // Since ShelfLayoutManager queries for mouse location, move the mouse so
+ // it isn't over the shelf.
+ aura::test::EventGenerator generator(
+ Shell::GetPrimaryRootWindow(), gfx::Point());
+ generator.MoveMouseTo(0, 0);
+
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->SetBounds(gfx::Rect(0, 1, 101, 102));
+ w1->Show();
+ wm::ActivateWindow(w1.get());
+
+ ShelfLayoutManager* shelf = Shell::GetInstance()->shelf();
+
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+
+ // Maximize the window.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE, shelf->visibility_state());
+ EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
+
+ // Restore.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+ EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
+
+ // Fullscreen.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
+ EXPECT_EQ(ShelfLayoutManager::HIDDEN, shelf->visibility_state());
+
+ // Normal.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+ EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
+
+ // Maximize again.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE, shelf->visibility_state());
+ EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
+
+ // Minimize.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+
+ // Restore.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+ EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
+
+ // Create another window, maximized.
+ scoped_ptr<Window> w2(CreateTestWindow());
+ w2->SetBounds(gfx::Rect(10, 11, 250, 251));
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ w2->Show();
+ wm::ActivateWindow(w2.get());
+ EXPECT_EQ(1, active_index());
+ EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE, shelf->visibility_state());
+ EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
+ EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
+
+ // Switch to w1.
+ wm::ActivateWindow(w1.get());
+ EXPECT_EQ(0, active_index());
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+ EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
+ EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w2.get()).ToString(),
+ w2->bounds().ToString());
+
+ // Switch to w2.
+ wm::ActivateWindow(w2.get());
+ EXPECT_EQ(1, active_index());
+ EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE, shelf->visibility_state());
+ EXPECT_EQ(ShelfLayoutManager::AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
+ EXPECT_EQ("0,1 101x102", w1->bounds().ToString());
+ EXPECT_EQ(ScreenAsh::GetMaximizedWindowBoundsInParent(w2.get()).ToString(),
+ w2->bounds().ToString());
+}
+
+// Verifies persist across all workspaces.
+TEST_F(WorkspaceManager2Test, PersistAcrossAllWorkspaces) {
+ // Create a maximized window.
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->Show();
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ wm::ActivateWindow(w1.get());
+ ASSERT_EQ("0 M1 active=1", StateString());
+
+ // Create a window that persists across all workspaces. It should be placed in
+ // the current maximized workspace.
+ scoped_ptr<Window> w2(CreateTestWindow());
+ SetPersistsAcrossAllWorkspaces(
+ w2.get(),
+ WINDOW_PERSISTS_ACROSS_ALL_WORKSPACES_VALUE_YES);
+ w2->Show();
+ ASSERT_EQ("1 M1 active=1", StateString());
+
+ // Activate w2, which should move it to the 2nd workspace.
+ wm::ActivateWindow(w2.get());
+ ASSERT_EQ("0 M2 active=1", StateString());
+
+ // Restoring w2 should drop the persists window back to the desktop, and drop
+ // it to the bottom of the stack.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ ASSERT_EQ("2 active=0", StateString());
+ EXPECT_EQ(w2.get(), workspaces()[0]->window()->children()[0]);
+ EXPECT_EQ(w1.get(), workspaces()[0]->window()->children()[1]);
+
+ // Repeat, but this time minimize. The minimize window should end up in
+ // pending.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ w2.reset(CreateTestWindow());
+ SetPersistsAcrossAllWorkspaces(
+ w2.get(),
+ WINDOW_PERSISTS_ACROSS_ALL_WORKSPACES_VALUE_YES);
+ w2->Show();
+ ASSERT_EQ("1 M1 active=1", StateString());
+ wm::ActivateWindow(w2.get());
+ ASSERT_EQ("0 M2 active=1", StateString());
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+ ASSERT_EQ("1 P=M1 active=0", StateString());
+ EXPECT_EQ(w2.get(), workspaces()[0]->window()->children()[0]);
+}
+
+// Verifies that when a window persists across all workpaces is activated that
+// it moves to the current workspace.
+TEST_F(WorkspaceManager2Test, ActivatePersistAcrossAllWorkspacesWhenNotActive) {
+ // Create a window that persists across all workspaces.
+ scoped_ptr<Window> w2(CreateTestWindow());
+ SetPersistsAcrossAllWorkspaces(
+ w2.get(),
+ WINDOW_PERSISTS_ACROSS_ALL_WORKSPACES_VALUE_YES);
+ w2->Show();
+ ASSERT_EQ("1 active=0", StateString());
+
+ // Create a maximized window.
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->Show();
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ wm::ActivateWindow(w1.get());
+ ASSERT_EQ("1 M1 active=1", StateString());
+
+ // Activate the persists across all workspace window. It should move to the
+ // current workspace.
+ wm::ActivateWindow(w2.get());
+ ASSERT_EQ("0 M2 active=1", StateString());
+ // The window that persists across all workspaces should be moved to the top
+ // of the stacking order.
+ EXPECT_EQ(w1.get(), workspaces()[1]->window()->children()[0]);
+ EXPECT_EQ(w2.get(), workspaces()[1]->window()->children()[1]);
+ EXPECT_TRUE(wm::IsActiveWindow(w2.get()));
+}
+
+// Verifies Show()ing a minimized window that persists across all workspaces
+// unminimizes the window.
+TEST_F(WorkspaceManager2Test, ShowMinimizedPersistWindow) {
+ // Create a window that persists across all workspaces.
+ scoped_ptr<Window> w1(CreateTestWindow());
+ SetPersistsAcrossAllWorkspaces(
+ w1.get(),
+ WINDOW_PERSISTS_ACROSS_ALL_WORKSPACES_VALUE_YES);
+ w1->Show();
+ wm::ActivateWindow(w1.get());
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+ EXPECT_FALSE(w1->IsVisible());
+ w1->Show();
+ EXPECT_TRUE(w1->IsVisible());
+}
+
+// Test that we report we're in the fullscreen state even if the fullscreen
+// window isn't being managed by us (http://crbug.com/123931).
+TEST_F(WorkspaceManager2Test, GetWindowStateWithUnmanagedFullscreenWindow) {
+ ShelfLayoutManager* shelf = Shell::GetInstance()->shelf();
+
+ // We need to create a regular window first so there's an active workspace.
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->Show();
+
+ scoped_ptr<Window> w2(CreateTestWindow());
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
+ SetPersistsAcrossAllWorkspaces(
+ w2.get(),
+ WINDOW_PERSISTS_ACROSS_ALL_WORKSPACES_VALUE_YES);
+ w2->Show();
+ wm::ActivateWindow(w2.get());
+
+ ASSERT_EQ("1 M1 active=1", StateString());
+
+ EXPECT_EQ(ShelfLayoutManager::HIDDEN, shelf->visibility_state());
+ EXPECT_EQ(WORKSPACE_WINDOW_STATE_FULL_SCREEN, manager_->GetWindowState());
+
+ w2->Hide();
+ ASSERT_EQ("1 P=M1 active=0", StateString());
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+
+ w2->Show();
+ ASSERT_EQ("1 P=M1 active=0", StateString());
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+ EXPECT_EQ(WORKSPACE_WINDOW_STATE_DEFAULT, manager_->GetWindowState());
+
+ wm::ActivateWindow(w2.get());
+ ASSERT_EQ("1 M1 active=1", StateString());
+ EXPECT_EQ(ShelfLayoutManager::HIDDEN, shelf->visibility_state());
+ EXPECT_EQ(WORKSPACE_WINDOW_STATE_FULL_SCREEN, manager_->GetWindowState());
+
+ w2.reset();
+ ASSERT_EQ("1 active=0", StateString());
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+ EXPECT_EQ(WORKSPACE_WINDOW_STATE_DEFAULT, manager_->GetWindowState());
+}
+
+// Variant of GetWindowStateWithUnmanagedFullscreenWindow that uses a maximized
+// window rather than a normal window.
+TEST_F(WorkspaceManager2Test,
+ GetWindowStateWithUnmanagedFullscreenWindowWithMaximized) {
+ ShelfLayoutManager* shelf = Shell::GetInstance()->shelf();
+ shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
+
+ // Make the first window maximized.
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ w1->Show();
+
+ scoped_ptr<Window> w2(CreateTestWindow());
+ w2->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
+ SetPersistsAcrossAllWorkspaces(
+ w2.get(),
+ WINDOW_PERSISTS_ACROSS_ALL_WORKSPACES_VALUE_YES);
+ w2->Show();
+ wm::ActivateWindow(w2.get());
+
+ // Even though auto-hide behavior is NEVER full-screen windows cause the shelf
+ // to hide.
+ EXPECT_EQ(ShelfLayoutManager::HIDDEN, shelf->visibility_state());
+ EXPECT_EQ(WORKSPACE_WINDOW_STATE_FULL_SCREEN,
+ manager_->GetWindowState());
+
+ w2->Hide();
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+
+ w2->Show();
+ wm::ActivateWindow(w2.get());
+ EXPECT_EQ(ShelfLayoutManager::HIDDEN, shelf->visibility_state());
+ EXPECT_EQ(WORKSPACE_WINDOW_STATE_FULL_SCREEN,
+ manager_->GetWindowState());
+
+ w2.reset();
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE, shelf->visibility_state());
+}
+
+// Verifies a window marked as persisting across all workspaces ends up in its
+// own workspace when maximized.
+TEST_F(WorkspaceManager2Test, MaximizeDontPersistEndsUpInOwnWorkspace) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+
+ SetPersistsAcrossAllWorkspaces(
+ w1.get(),
+ WINDOW_PERSISTS_ACROSS_ALL_WORKSPACES_VALUE_YES);
+ w1->Show();
+
+ ASSERT_EQ("1 active=0", StateString());
+
+ // Maximize should trigger containing the window.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ ASSERT_EQ("0 P=M1 active=0", StateString());
+
+ // And resetting to normal should remove it.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ ASSERT_EQ("1 active=0", StateString());
+}
+
+// Verifies going from maximized to minimized sets the right state for painting
+// the background of the launcher.
+TEST_F(WorkspaceManager2Test, MinimizeResetsVisibility) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->Show();
+ wm::ActivateWindow(w1.get());
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+ EXPECT_EQ(ShelfLayoutManager::VISIBLE,
+ Shell::GetInstance()->shelf()->visibility_state());
+ EXPECT_FALSE(Shell::GetInstance()->launcher()->paints_background());
+}
+
+// Verifies transients are moved when maximizing.
+TEST_F(WorkspaceManager2Test, MoveTransientOnMaximize) {
+ scoped_ptr<Window> w1(CreateTestWindow());
+ w1->Show();
+ scoped_ptr<Window> w2(CreateTestWindow());
+ w1->AddTransientChild(w2.get());
+ w2->Show();
+ wm::ActivateWindow(w1.get());
+ ASSERT_EQ("2 active=0", StateString());
+
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED);
+ ASSERT_EQ("0 M2 active=1", StateString());
+ EXPECT_TRUE(wm::IsActiveWindow(w1.get()));
+
+ // Create another transient child of |w1|. We do this unparented, set up the
+ // transient parent then set parent. This is how NativeWidgetAura does things
+ // too.
+ scoped_ptr<Window> w3(CreateTestWindowUnparented());
+ w1->AddTransientChild(w3.get());
+ w3->SetParent(NULL);
+ w3->Show();
+ ASSERT_EQ("0 M3 active=1", StateString());
+
+ // Minimize the window. All the transients are hidden as a result, so it ends
+ // up in pending.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MINIMIZED);
+ ASSERT_EQ("0 P=M3 active=0", StateString());
+
+ // Restore and everything should go back to the first workspace.
+ w1->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL);
+ ASSERT_EQ("3 active=0", StateString());
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/ash/wm/workspace_controller.cc b/ash/wm/workspace_controller.cc
index 0592513..6abd041 100644
--- a/ash/wm/workspace_controller.cc
+++ b/ash/wm/workspace_controller.cc
@@ -4,10 +4,13 @@
#include "ash/wm/workspace_controller.h"
+#include "ash/ash_switches.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace/workspace_event_filter.h"
#include "ash/wm/workspace/workspace_layout_manager.h"
#include "ash/wm/workspace/workspace_manager.h"
+#include "ash/wm/workspace/workspace_manager2.h"
+#include "base/command_line.h"
#include "ui/aura/client/activation_client.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/root_window.h"
@@ -28,12 +31,18 @@ WorkspaceController::WorkspaceController(aura::Window* viewport)
layout_manager_(NULL),
event_filter_(NULL) {
aura::RootWindow* root_window = viewport->GetRootWindow();
- event_filter_ = new WorkspaceEventFilter(viewport);
- viewport->SetEventFilter(event_filter_);
- WorkspaceManager* workspace_manager = new WorkspaceManager(viewport);
- workspace_manager_.reset(workspace_manager);
- layout_manager_ = new WorkspaceLayoutManager(root_window, workspace_manager);
- viewport->SetLayoutManager(layout_manager_);
+ if (IsWorkspace2Enabled()) {
+ WorkspaceManager2* workspace_manager = new WorkspaceManager2(viewport);
+ workspace_manager_.reset(workspace_manager);
+ } else {
+ WorkspaceManager* workspace_manager = new WorkspaceManager(viewport);
+ workspace_manager_.reset(workspace_manager);
+ layout_manager_ = new WorkspaceLayoutManager(
+ root_window, workspace_manager);
+ viewport->SetLayoutManager(layout_manager_);
+ event_filter_ = new WorkspaceEventFilter(viewport);
+ viewport->SetEventFilter(event_filter_);
+ }
aura::client::GetActivationClient(root_window)->AddObserver(this);
SetGridSize(kGridSize);
}
@@ -42,10 +51,16 @@ WorkspaceController::~WorkspaceController() {
aura::client::GetActivationClient(viewport_->GetRootWindow())->
RemoveObserver(this);
// WorkspaceLayoutManager may attempt to access state from us. Destroy it now.
- if (viewport_->layout_manager() == layout_manager_)
+ if (layout_manager_ && viewport_->layout_manager() == layout_manager_)
viewport_->SetLayoutManager(NULL);
}
+// static
+bool WorkspaceController::IsWorkspace2Enabled() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAshEnableWorkspace2);
+}
+
bool WorkspaceController::IsInMaximizedMode() const {
return workspace_manager_->IsInMaximizedMode();
}
diff --git a/ash/wm/workspace_controller.h b/ash/wm/workspace_controller.h
index d7f7b6b..46d6efb 100644
--- a/ash/wm/workspace_controller.h
+++ b/ash/wm/workspace_controller.h
@@ -33,6 +33,9 @@ class ASH_EXPORT WorkspaceController
explicit WorkspaceController(aura::Window* viewport);
virtual ~WorkspaceController();
+ // Returns true if Workspace2 is enabled.
+ static bool IsWorkspace2Enabled();
+
// Returns true if in maximized or fullscreen mode.
bool IsInMaximizedMode() const;
@@ -62,6 +65,9 @@ class ASH_EXPORT WorkspaceController
scoped_ptr<BaseWorkspaceManager> workspace_manager_;
+ // TODO(sky): remove |layout_manager_| and |event_filter_| when Workspace2
+ // is the default.
+
// Owned by the window its attached to.
WorkspaceLayoutManager* layout_manager_;
diff --git a/ash/wm/workspace_controller_test_helper.cc b/ash/wm/workspace_controller_test_helper.cc
index 75177ef..df83eca 100644
--- a/ash/wm/workspace_controller_test_helper.cc
+++ b/ash/wm/workspace_controller_test_helper.cc
@@ -20,6 +20,9 @@ WorkspaceControllerTestHelper::~WorkspaceControllerTestHelper() {
}
WorkspaceEventFilter* WorkspaceControllerTestHelper::GetFilter() {
+ if (WorkspaceController::IsWorkspace2Enabled())
+ return static_cast<WorkspaceEventFilter*>(
+ controller_->viewport_->children()[0]->event_filter());
return controller_->event_filter_;
}
diff --git a/ash/wm/workspace_controller_test_helper.h b/ash/wm/workspace_controller_test_helper.h
index c48f096..38abfb2 100644
--- a/ash/wm/workspace_controller_test_helper.h
+++ b/ash/wm/workspace_controller_test_helper.h
@@ -7,6 +7,7 @@
#include "ash/wm/workspace_controller.h"
#include "ash/wm/workspace/workspace_manager.h"
+#include "ash/wm/workspace/workspace_manager2.h"
namespace ash {
namespace internal {
@@ -25,6 +26,10 @@ class WorkspaceControllerTestHelper {
return static_cast<WorkspaceManager*>(
controller_->workspace_manager_.get());
}
+ WorkspaceManager2* workspace_manager2() {
+ return static_cast<WorkspaceManager2*>(
+ controller_->workspace_manager_.get());
+ }
private:
WorkspaceController* controller_;
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 068b131..0ebf4c3 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6033,6 +6033,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_DISABLE_BOOT_ANIMATION_DESCRIPTION" desc="Description for the flag to disable wallpaper boot animation (except for OOBE).">
Disables wallpaper boot animation (except for OOBE case).
</message>
+ <message name="IDS_FLAGS_ENABLE_WORKSPACE2" desc="Description for the flag to turn on workspace2.">
+ Enable new window stacking.
+ </message>
+ <message name="IDS_FLAGS_ENABLE_WORKSPACE2_DESCRIPTION" desc="Description for the flag to turn on workspace2.">
+ Enable new window stacking.
+ </message>
</if>
<message name="IDS_FLAGS_OLD_CHECKBOX_STYLE" desc="Name of the flag to turn off the new checkbox style.">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index 54438cd..51ef078 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -852,6 +852,13 @@ const Experiment kExperiments[] = {
kOsCrOS,
SINGLE_VALUE_TYPE(switches::kDisableBootAnimation),
},
+ {
+ "enable-workspace2",
+ IDS_FLAGS_ENABLE_WORKSPACE2,
+ IDS_FLAGS_ENABLE_WORKSPACE2_DESCRIPTION,
+ kOsCrOS,
+ SINGLE_VALUE_TYPE(ash::switches::kAshEnableWorkspace2),
+ },
#endif
{
"enable-views-textfield",