diff --git a/ash/ash.gyp b/ash/ash.gyp index a2bf778f2baaa..f78a02101f671 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -138,8 +138,6 @@ 'wm/shadow_types.h', 'wm/shelf_layout_manager.cc', 'wm/shelf_layout_manager.h', - 'wm/show_state_controller.h', - 'wm/show_state_controller.cc', 'wm/stacking_controller.cc', 'wm/stacking_controller.h', 'wm/status_area_layout_manager.cc', @@ -176,7 +174,6 @@ 'wm/workspace/workspace_layout_manager.h', 'wm/workspace/workspace_manager.cc', 'wm/workspace/workspace_manager.h', - 'wm/workspace/workspace_observer.h', ], }, { @@ -238,7 +235,6 @@ 'wm/window_cycle_controller_unittest.cc', 'wm/window_modality_controller_unittest.cc', 'wm/workspace_controller_unittest.cc', - 'wm/workspace/workspace_layout_manager_unittest.cc', 'wm/workspace/workspace_manager_unittest.cc', '<(SHARED_INTERMEDIATE_DIR)/ui/gfx/gfx_resources.rc', diff --git a/ash/shell/shell_main.cc b/ash/shell/shell_main.cc index ea52da8493b2b..93029909995e5 100644 --- a/ash/shell/shell_main.cc +++ b/ash/shell/shell_main.cc @@ -157,7 +157,9 @@ class ShellDelegateImpl : public ash::ShellDelegate { virtual void LauncherItemClicked( const ash::LauncherItem& item) OVERRIDE { - ash::ActivateWindow(watcher_->GetWindowByID(item.id)); + aura::Window* window = watcher_->GetWindowByID(item.id); + window->Show(); + ash::ActivateWindow(window); } virtual int GetBrowserShortcutResourceId() OVERRIDE { diff --git a/ash/wm/show_state_controller.cc b/ash/wm/show_state_controller.cc deleted file mode 100644 index 9b670c784f2cc..0000000000000 --- a/ash/wm/show_state_controller.cc +++ /dev/null @@ -1,47 +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/show_state_controller.h" - -#include "ash/wm/property_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/window.h" -#include "ui/base/ui_base_types.h" - -namespace ash { -namespace internal { - -ShowStateController::ShowStateController( - WorkspaceManager* workspace_manager) - : workspace_manager_(workspace_manager) { -} - -ShowStateController::~ShowStateController() { -} - -void ShowStateController::OnWindowPropertyChanged(aura::Window* window, - const char* name, - void* old) { - if (name != aura::client::kShowStateKey) - return; - if (window->GetIntProperty(name) == ui::SHOW_STATE_NORMAL) { - // Restore the size of window first, then let Workspace layout the window. - const gfx::Rect* restore = GetRestoreBounds(window); - window->SetProperty(aura::client::kRestoreBoundsKey, NULL); - if (restore) - window->SetBounds(*restore); - delete restore; - } else if (old == reinterpret_cast<void*>(ui::SHOW_STATE_NORMAL)) { - // Store the restore bounds only if previous state is normal. - DCHECK(window->GetProperty(aura::client::kRestoreBoundsKey) == NULL); - SetRestoreBounds(window, window->GetTargetBounds()); - } - - workspace_manager_->FindBy(window)->Layout(window); -} - -} // namespace internal -} // namespace ash diff --git a/ash/wm/show_state_controller.h b/ash/wm/show_state_controller.h deleted file mode 100644 index 5b3191f578ca2..0000000000000 --- a/ash/wm/show_state_controller.h +++ /dev/null @@ -1,44 +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_SHOW_STATE_CONTROLLER_H_ -#define ASH_WM_SHOW_STATE_CONTROLLER_H_ -#pragma once - -#include "base/basictypes.h" -#include "base/compiler_specific.h" -#include "ui/aura/window_observer.h" - -namespace aura { -class Window; -} - -namespace ash { -namespace internal { - -class WorkspaceManager; - -// ShowStateController controls the window's bounds when -// the window's show state property has changed. -class ShowStateController : public aura::WindowObserver { -public: - explicit ShowStateController(WorkspaceManager* layout_manager); - virtual ~ShowStateController(); - - // aura::WindowObserver overrides: - virtual void OnWindowPropertyChanged(aura::Window* window, - const char* name, - void* old) OVERRIDE; - - private: - // |workspace_maanger_| is owned by |WorkspaceController|. - WorkspaceManager* workspace_manager_; - - DISALLOW_COPY_AND_ASSIGN(ShowStateController); -}; - -} // namepsace internal -} // namepsace ash - -#endif // ASH_WM_SHOW_STATE_CONTROLLER_H_ diff --git a/ash/wm/window_util.cc b/ash/wm/window_util.cc index 25e84f6db8c2f..055244d490b89 100644 --- a/ash/wm/window_util.cc +++ b/ash/wm/window_util.cc @@ -14,6 +14,12 @@ namespace ash { +namespace { + +const char kOpenWindowSplitKey[] = "OpenWindowSplit"; + +} // namespace + void ActivateWindow(aura::Window* window) { aura::client::GetActivationClient()->ActivateWindow(window); } @@ -64,5 +70,13 @@ bool HasFullscreenWindow(const WindowSet& windows) { return false; } +void SetOpenWindowSplit(aura::Window* window, bool value) { + window->SetIntProperty(kOpenWindowSplitKey, value ? 1 : 0); +} + +bool GetOpenWindowSplit(aura::Window* window) { + return window->GetIntProperty(kOpenWindowSplitKey) == 1; +} + } // namespace window_util } // namespace ash diff --git a/ash/wm/window_util.h b/ash/wm/window_util.h index e43800721bb6a..f76b8aaf05727 100644 --- a/ash/wm/window_util.h +++ b/ash/wm/window_util.h @@ -49,6 +49,11 @@ ASH_EXPORT void RestoreWindow(aura::Window* window); typedef std::set<aura::Window*> WindowSet; ASH_EXPORT bool HasFullscreenWindow(const WindowSet& windows); +// Sets whether the window should be open in a split mode. Only applicable when +// workspaces are used. +ASH_EXPORT void SetOpenWindowSplit(aura::Window* window, bool value); +ASH_EXPORT bool GetOpenWindowSplit(aura::Window* window); + } // namespace window_util } // namespace ash diff --git a/ash/wm/workspace/workspace.cc b/ash/wm/workspace/workspace.cc index 574662b50e96b..3f5ebbe873bc6 100644 --- a/ash/wm/workspace/workspace.cc +++ b/ash/wm/workspace/workspace.cc @@ -14,41 +14,13 @@ #include "ui/aura/root_window.h" #include "ui/aura/window.h" #include "ui/base/ui_base_types.h" -#include "ui/gfx/compositor/layer.h" -#include "ui/gfx/compositor/layer_animator.h" -#include "ui/gfx/compositor/scoped_layer_animation_settings.h" - -namespace { -// Horizontal margin between windows. -const int kWindowHorizontalMargin = 10; - -// Maximum number of windows a workspace can have. -size_t g_max_windows_per_workspace = 2; - -// Returns the bounds of the window that should be used to calculate -// the layout. It uses the restore bounds if exits, or -// the target bounds of the window. The target bounds is the -// final destination of |window| if the window's layer is animating, -// or the current bounds of the window of no animation is currently -// in progress. -gfx::Rect GetLayoutBounds(aura::Window* window) { - const gfx::Rect* restore_bounds = ash::GetRestoreBounds(window); - return restore_bounds ? *restore_bounds : window->GetTargetBounds(); -} - -// Returns the width of the window that should be used to calculate -// the layout. See |GetLayoutBounds| for more details. -int GetLayoutWidth(aura::Window* window) { - return GetLayoutBounds(window).width(); -} - -} // namespace namespace ash { namespace internal { Workspace::Workspace(WorkspaceManager* manager) - : workspace_manager_(manager) { + : type_(TYPE_NORMAL), + workspace_manager_(manager) { workspace_manager_->AddWorkspace(this); } @@ -56,15 +28,34 @@ Workspace::~Workspace() { workspace_manager_->RemoveWorkspace(this); } -void Workspace::SetBounds(const gfx::Rect& bounds) { - bool bounds_changed = bounds_ != bounds; - bounds_ = bounds; - if (bounds_changed) - Layout(NULL); +// static +Workspace::Type Workspace::TypeForWindow(aura::Window* window) { + if (window_util::GetOpenWindowSplit(window)) + return TYPE_SPLIT; + if (window_util::IsWindowMaximized(window) || + window_util::IsWindowFullscreen(window)) { + return TYPE_MAXIMIZED; + } + return TYPE_NORMAL; +} + +void Workspace::SetType(Type type) { + // Can only change the type when there are no windows, or the type of window + // matches the type changing to. We need only check the first window as CanAdd + // only allows new windows if the type matches. + DCHECK(windows_.empty() || TypeForWindow(windows_[0]) == type); + type_ = type; +} + +void Workspace::WorkspaceSizeChanged() { + if (!windows_.empty()) { + // TODO: need to handle size changing. + NOTIMPLEMENTED(); + } } gfx::Rect Workspace::GetWorkAreaBounds() const { - return workspace_manager_->GetWorkAreaBounds(bounds_); + return workspace_manager_->GetWorkAreaBounds(); } bool Workspace::AddWindowAfter(aura::Window* window, aura::Window* after) { @@ -72,15 +63,19 @@ bool Workspace::AddWindowAfter(aura::Window* window, aura::Window* after) { return false; DCHECK(!Contains(window)); - if (!after) { // insert at the end. + aura::Window::Windows::iterator i = + std::find(windows_.begin(), windows_.end(), after); + if (!after || i == windows_.end()) windows_.push_back(window); - } else { - DCHECK(Contains(after)); - aura::Window::Windows::iterator i = - std::find(windows_.begin(), windows_.end(), after); + else windows_.insert(++i, window); + + if (type_ == TYPE_MAXIMIZED) { + workspace_manager_->SetWindowBounds(window, GetWorkAreaBounds()); + } else if (type_ == TYPE_SPLIT) { + // TODO: this needs to adjust bounds appropriately. + workspace_manager_->SetWindowBounds(window, GetWorkAreaBounds()); } - Layout(window); return true; } @@ -88,138 +83,17 @@ bool Workspace::AddWindowAfter(aura::Window* window, aura::Window* after) { void Workspace::RemoveWindow(aura::Window* window) { DCHECK(Contains(window)); windows_.erase(std::find(windows_.begin(), windows_.end(), window)); - Layout(NULL); + // TODO: this needs to adjust things. } bool Workspace::Contains(aura::Window* window) const { return std::find(windows_.begin(), windows_.end(), window) != windows_.end(); } -aura::Window* Workspace::FindRotateWindowForLocation( - const gfx::Point& position) { - aura::Window* active = ash::GetActiveWindow(); - if (GetTotalWindowsWidth() < bounds_.width()) { - // If all windows fit to the width of the workspace, it returns the - // window which contains |position|'s x coordinate. - for (aura::Window::Windows::const_iterator i = windows_.begin(); - i != windows_.end(); - ++i) { - if (active == *i) - continue; - gfx::Rect bounds = (*i)->GetTargetBounds(); - if (bounds.x() < position.x() && position.x() < bounds.right()) - return *i; - } - } else if (bounds_.x() < position.x() && position.x() < bounds_.right()) { - // If windows are overlapping, it divides the workspace into - // regions with the same width, and returns the Nth window that - // corresponds to the region that contains the |position|. - int width = bounds_.width() / windows_.size(); - size_t index = (position.x() - bounds_.x()) / width; - DCHECK(index < windows_.size()); - aura::Window* window = windows_[index]; - if (window != active) - return window; - } - return NULL; -} - -void Workspace::RotateWindows(aura::Window* source, aura::Window* target) { - DCHECK(Contains(source)); - DCHECK(Contains(target)); - aura::Window::Windows::iterator source_iter = - std::find(windows_.begin(), windows_.end(), source); - aura::Window::Windows::iterator target_iter = - std::find(windows_.begin(), windows_.end(), target); - DCHECK(source_iter != target_iter); - if (source_iter < target_iter) - std::rotate(source_iter, source_iter + 1, target_iter + 1); - else - std::rotate(target_iter, source_iter, source_iter + 1); - Layout(NULL); -} - -aura::Window* Workspace::ShiftWindows(aura::Window* insert, - aura::Window* until, - aura::Window* target, - ShiftDirection direction) { - DCHECK(until); - DCHECK(!Contains(insert)); - - bool shift_reached_until = GetIndexOf(until) >= 0; - if (shift_reached_until) { - // Calling RemoveWindow here causes the animation set in Layout below - // to be ignored. See crbug.com/102413. - windows_.erase(std::find(windows_.begin(), windows_.end(), until)); - } - aura::Window* pushed = NULL; - if (direction == SHIFT_TO_RIGHT) { - aura::Window::Windows::iterator iter = - std::find(windows_.begin(), windows_.end(), target); - // Insert at |target| position, or at the begining. - if (iter == windows_.end()) - iter = windows_.begin(); - windows_.insert(iter, insert); - if (!shift_reached_until) { - pushed = windows_.back(); - windows_.erase(--windows_.end()); - } - } else { - aura::Window::Windows::iterator iter = - std::find(windows_.begin(), windows_.end(), target); - // Insert after |target|, or at the end. - if (iter != windows_.end()) - ++iter; - windows_.insert(iter, insert); - if (!shift_reached_until) { - pushed = windows_.front(); - windows_.erase(windows_.begin()); - } - } - Layout(NULL); - return pushed; -} - void Workspace::Activate() { workspace_manager_->SetActiveWorkspace(this); } -void Workspace::Layout(aura::Window* no_animation) { - aura::Window* ignore = workspace_manager_->ignored_window(); - workspace_manager_->set_layout_in_progress(true); - gfx::Rect work_area = workspace_manager_->GetWorkAreaBounds(bounds_); - int total_width = GetTotalWindowsWidth(); - if (total_width < work_area.width()) { - int dx = (work_area.width() - total_width) / 2; - for (aura::Window::Windows::iterator i = windows_.begin(); - i != windows_.end(); - ++i) { - if (*i != ignore) { - MoveWindowTo(*i, - gfx::Point(work_area.x() + dx, work_area.y()), - no_animation != *i); - } - dx += GetLayoutWidth(*i) + kWindowHorizontalMargin; - } - } else { - DCHECK_LT(windows_.size(), 3U); - // TODO(oshima): This is messy. Figure out general algorithm to - // layout more than 2 windows. - if (windows_[0] != ignore) { - MoveWindowTo(windows_[0], - work_area.origin(), - no_animation != windows_[0]); - } - if (windows_.size() == 2 && windows_[1] != ignore) { - MoveWindowTo(windows_[1], - gfx::Point(work_area.right() - GetLayoutWidth(windows_[1]), - work_area.y()), - no_animation != windows_[1]); - } - } - workspace_manager_->set_layout_in_progress(false); -} - bool Workspace::ContainsFullscreenWindow() const { for (aura::Window::Windows::const_iterator i = windows_.begin(); i != windows_.end(); @@ -240,47 +114,7 @@ int Workspace::GetIndexOf(aura::Window* window) const { } bool Workspace::CanAdd(aura::Window* window) const { - // TODO(oshima): This should be based on available space and the - // size of the |window|. - //NOTIMPLEMENTED(); - return windows_.size() < g_max_windows_per_workspace; -} - -void Workspace::MoveWindowTo( - aura::Window* window, - const gfx::Point& origin, - bool animate) { - gfx::Rect bounds = GetLayoutBounds(window); - gfx::Rect work_area = GetWorkAreaBounds(); - // Make sure the window isn't bigger than the workspace size. - bounds.SetRect(origin.x(), origin.y(), - std::min(work_area.width(), bounds.width()), - std::min(work_area.height(), bounds.height())); - if (animate) { - ui::ScopedLayerAnimationSettings settings(window->layer()->GetAnimator()); - window->SetBounds(bounds); - } else { - window->SetBounds(bounds); - } -} - -int Workspace::GetTotalWindowsWidth() const { - int total_width = 0; - for (aura::Window::Windows::const_iterator i = windows_.begin(); - i != windows_.end(); - ++i) { - if (total_width) - total_width += kWindowHorizontalMargin; - total_width += GetLayoutWidth(*i); - } - return total_width; -} - -// static -size_t Workspace::SetMaxWindowsCount(size_t max) { - int old = g_max_windows_per_workspace; - g_max_windows_per_workspace = max; - return old; + return TypeForWindow(window) == type_; } } // namespace internal diff --git a/ash/wm/workspace/workspace.h b/ash/wm/workspace/workspace.h index 999df8493ef87..5441d5b8fc442 100644 --- a/ash/wm/workspace/workspace.h +++ b/ash/wm/workspace/workspace.h @@ -22,17 +22,24 @@ namespace internal { class WorkspaceManager; class WorkspaceTest; -// A workspace is a partial area of the entire desktop (viewport) that -// is visible to the user at a given time. The size of the workspace is -// generally the same as the size of the monitor, and the desktop can -// have multiple workspaces. -// A workspace contains a limited number of windows and the workspace -// manager may create a new workspace if there is not enough room for -// a new window. +// A workspace contains a number of windows. The number of windows a Workspace +// may contain is dictated by the type. Typically only one workspace is visible +// at a time. The exception to that is when overview mode is active. class ASH_EXPORT Workspace { public: - explicit Workspace(WorkspaceManager* manager); - virtual ~Workspace(); + // Type of workspace. The type of workspace dictates the types of windows the + // workspace can contain. + enum Type { + // The workspace holds a single maximized or full screen window. + TYPE_MAXIMIZED, + + // Workspace contains multiple windows that are split (also known as + // co-maximized). + TYPE_SPLIT, + + // Workspace contains non-maximized windows that can be moved in anyway. + TYPE_NORMAL, + }; // Specifies the direction to shift windows in |ShiftWindows()|. enum ShiftDirection { @@ -40,15 +47,25 @@ class ASH_EXPORT Workspace { SHIFT_TO_LEFT }; - // Returns true if this workspace has no windows. + explicit Workspace(WorkspaceManager* manager); + virtual ~Workspace(); + + // Returns the type of workspace that can contain |window|. + static Type TypeForWindow(aura::Window* window); + + // The type of this Workspace. + void SetType(Type type); + Type type() const { return type_; } + + // Returns true if this workspace has no windows. bool is_empty() const { return windows_.empty(); } + size_t num_windows() const { return windows_.size(); } + const std::vector<aura::Window*>& windows() const { return windows_; } - // Sets/gets bounds of this workspace. - const gfx::Rect& bounds() { return bounds_; } - void SetBounds(const gfx::Rect& bounds); + // Invoked when the size of the workspace changes. + void WorkspaceSizeChanged(); - // Returns the work area bounds of this workspace in viewport - // coordinates. + // Returns the work area bounds of this workspace in viewport coordinates. gfx::Rect GetWorkAreaBounds() const; // Adds the |window| at the position after the window |after|. It @@ -63,35 +80,9 @@ class ASH_EXPORT Workspace { // Return true if this workspace has |window|. bool Contains(aura::Window* window) const; - // Returns a window to rotate to based on |position|. - aura::Window* FindRotateWindowForLocation(const gfx::Point& position); - - // Rotates the windows by removing |source| and inserting it to the - // position that |target| was in. It re-layouts windows except for |source|. - void RotateWindows(aura::Window* source, aura::Window* target); - - // Shift the windows in the workspace by inserting |window| until it - // reaches |until|. If |direction| is |SHIFT_TO_RIGHT|, |insert| is - // inserted at the position of |target| or at the beginning if - // |target| is NULL. If |direction| is |SHIFT_TO_LEFT|, |insert| is - // inserted after the position of |target|, or at the end if - // |target| is NULL. It returns the window that is overflowed by - // shifting, or NULL if shifting stopped at |until|. - aura::Window* ShiftWindows(aura::Window* insert, - aura::Window* until, - aura::Window* target, - ShiftDirection direction); - // Activates this workspace. void Activate(); - // Layout windows. The workspace doesn't set bounds on - // |WorkspaceManager::ignored_window| if it's set. It still uses the window's - // bounds to calculate bounds for other windows. Moving animation is - // applied to all windows except for the window specified by |no_animation| - // and |ignore|. - void Layout(aura::Window* no_animation); - // Returns true if the workspace contains a fullscreen window. bool ContainsFullscreenWindow() const; @@ -108,24 +99,10 @@ class ASH_EXPORT Workspace { // Returns true if the given |window| can be added to this workspace. bool CanAdd(aura::Window* window) const; - // Moves |window| to the given point. It performs animation when - // |animate| is true. - void MoveWindowTo(aura::Window* window, - const gfx::Point& origin, - bool animate); - - // Returns the sum of all window's width. - int GetTotalWindowsWidth() const; - - // Test only: Changes how may windows workspace can have. - // Returns the current value so that it can be reverted back to - // original value. - static size_t SetMaxWindowsCount(size_t max); + Type type_; WorkspaceManager* workspace_manager_; - gfx::Rect bounds_; - // Windows in the workspace in layout order. std::vector<aura::Window*> windows_; diff --git a/ash/wm/workspace/workspace_event_filter.cc b/ash/wm/workspace/workspace_event_filter.cc index 66aecb491bf3f..0988e2bee61f2 100644 --- a/ash/wm/workspace/workspace_event_filter.cc +++ b/ash/wm/workspace/workspace_event_filter.cc @@ -38,6 +38,8 @@ WorkspaceEventFilter::WorkspaceEventFilter(aura::Window* owner) } WorkspaceEventFilter::~WorkspaceEventFilter() { + if (hovered_window_) + hovered_window_->RemoveObserver(this); } bool WorkspaceEventFilter::PreHandleMouseEvent(aura::Window* target, @@ -95,6 +97,12 @@ bool WorkspaceEventFilter::PreHandleMouseEvent(aura::Window* target, return handled; } +void WorkspaceEventFilter::OnWindowDestroyed(aura::Window* window) { + DCHECK_EQ(hovered_window_, window); + hovered_window_->RemoveObserver(this); + hovered_window_ = NULL; +} + bool WorkspaceEventFilter::UpdateDragState() { DCHECK_EQ(DRAG_NONE, drag_state_); switch (window_component()) { @@ -122,9 +130,13 @@ void WorkspaceEventFilter::UpdateHoveredWindow( aura::Window* toplevel_window) { if (toplevel_window == hovered_window_) return; + if (hovered_window_) + hovered_window_->RemoveObserver(this); WindowHoverChanged(hovered_window_, false); hovered_window_ = toplevel_window; WindowHoverChanged(hovered_window_, true); + if (hovered_window_) + hovered_window_->AddObserver(this); } } // namespace internal diff --git a/ash/wm/workspace/workspace_event_filter.h b/ash/wm/workspace/workspace_event_filter.h index d6765fa38b54c..6224c1099b5b9 100644 --- a/ash/wm/workspace/workspace_event_filter.h +++ b/ash/wm/workspace/workspace_event_filter.h @@ -7,6 +7,7 @@ #pragma once #include "ash/wm/toplevel_window_event_filter.h" +#include "ui/aura/window_observer.h" namespace aura { class MouseEvent; @@ -16,7 +17,8 @@ class Window; namespace ash { namespace internal { -class WorkspaceEventFilter : public ToplevelWindowEventFilter { +class WorkspaceEventFilter : public ToplevelWindowEventFilter, + public aura::WindowObserver { public: explicit WorkspaceEventFilter(aura::Window* owner); virtual ~WorkspaceEventFilter(); @@ -25,6 +27,9 @@ class WorkspaceEventFilter : public ToplevelWindowEventFilter { virtual bool PreHandleMouseEvent(aura::Window* target, aura::MouseEvent* event) OVERRIDE; + // Overriden from WindowObserver: + virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE; + private: enum DragState { DRAG_NONE, diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc index a4b95d43a23f9..99409e77ec8dc 100644 --- a/ash/wm/workspace/workspace_layout_manager.cc +++ b/ash/wm/workspace/workspace_layout_manager.cc @@ -5,7 +5,6 @@ #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" @@ -27,8 +26,7 @@ namespace internal { WorkspaceLayoutManager::WorkspaceLayoutManager( WorkspaceManager* workspace_manager) - : workspace_manager_(workspace_manager), - show_state_controller_(new ShowStateController(workspace_manager)) { + : workspace_manager_(workspace_manager) { } WorkspaceLayoutManager::~WorkspaceLayoutManager() {} @@ -48,43 +46,22 @@ void WorkspaceLayoutManager::CancelMoveOrResize( 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); + // TODO: needs implementation for TYPE_SPLIT. For TYPE_SPLIT I want to + // disallow eventfilter from moving and instead deal with it here. } void WorkspaceLayoutManager::EndMove( aura::Window* drag, aura::MouseEvent* evnet) { - // TODO(oshima): finish moving window between workspaces. + // TODO: see comment in ProcessMove. 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) { + // TODO: see comment in ProcessMove. workspace_manager_->set_ignored_window(NULL); - Workspace* workspace = workspace_manager_->GetActiveWorkspace(); - if (workspace) - workspace->Layout(NULL); - workspace_manager_->SetOverview(false); } //////////////////////////////////////////////////////////////////////////////// @@ -95,95 +72,41 @@ void WorkspaceLayoutManager::OnWindowResized() { } void WorkspaceLayoutManager::OnWindowAddedToLayout(aura::Window* child) { - if (child->type() != aura::client::WINDOW_TYPE_NORMAL || - child->transient_parent()) { + if (!workspace_manager_->IsManagedWindow(child)) 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(); + if (child->IsVisible()) + workspace_manager_->AddWindow(child); } 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; + workspace_manager_->RemoveWindow(child); } void WorkspaceLayoutManager::OnChildWindowVisibilityChanged( aura::Window* child, bool visible) { - NOTIMPLEMENTED(); + if (!workspace_manager_->IsManagedWindow(child)) + return; + if (visible) + workspace_manager_->AddWindow(child); + else + workspace_manager_->RemoveWindow(child); } 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); + // Allow setting the bounds for any window we don't care about, isn't visible, + // or we're setting the bounds of. All other request are dropped on the floor. + if (child == workspace_manager_->ignored_window() || + !workspace_manager_->IsManagedWindow(child) || !child->IsVisible() || + (!window_util::IsWindowMaximized(child) && + !window_util::IsWindowFullscreen(child))) { + SetChildBoundsDirect(child, requested_bounds); } - - ui::WindowShowState show_state = static_cast<ui::WindowShowState>( - 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 diff --git a/ash/wm/workspace/workspace_layout_manager.h b/ash/wm/workspace/workspace_layout_manager.h index 3fdceb8151ad6..4a1ca87929e61 100644 --- a/ash/wm/workspace/workspace_layout_manager.h +++ b/ash/wm/workspace/workspace_layout_manager.h @@ -6,11 +6,10 @@ #define ASH_WM_WORKSPACE_WORKSPACE_LAYOUT_MANAGER_H_ #pragma once +#include "ash/ash_export.h" #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; @@ -24,10 +23,9 @@ class Rect; namespace ash { namespace internal { -class ShowStateController; class WorkspaceManager; -// LayoutManager for the default window container. +// LayoutManager for top level windows when WorkspaceManager is enabled. class ASH_EXPORT WorkspaceLayoutManager : public aura::LayoutManager { public: explicit WorkspaceLayoutManager(WorkspaceManager* workspace_manager); @@ -61,12 +59,11 @@ class ASH_EXPORT WorkspaceLayoutManager : public aura::LayoutManager { bool visibile) OVERRIDE; virtual void SetChildBounds(aura::Window* child, const gfx::Rect& requested_bounds) OVERRIDE; + private: // Owned by WorkspaceController. WorkspaceManager* workspace_manager_; - scoped_ptr<ShowStateController> show_state_controller_; - DISALLOW_COPY_AND_ASSIGN(WorkspaceLayoutManager); }; diff --git a/ash/wm/workspace/workspace_layout_manager_unittest.cc b/ash/wm/workspace/workspace_layout_manager_unittest.cc deleted file mode 100644 index ac2504e550f67..0000000000000 --- a/ash/wm/workspace/workspace_layout_manager_unittest.cc +++ /dev/null @@ -1,341 +0,0 @@ -// 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<aura::Window> container_; - scoped_ptr<ash::internal::WorkspaceController> 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<ui::WindowShowState>( - 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<aura::Window> 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<aura::Window> 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<aura::Window> 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<aura::Window> 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<aura::Window> 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<aura::Window> 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<aura::Window> 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<aura::Window> 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_manager.cc b/ash/wm/workspace/workspace_manager.cc index a9884b3bf677c..9fc41e9d0a262 100644 --- a/ash/wm/workspace/workspace_manager.cc +++ b/ash/wm/workspace/workspace_manager.cc @@ -6,14 +6,17 @@ #include <algorithm> +#include "ash/wm/property_util.h" +#include "ash/wm/window_util.h" #include "ash/wm/workspace/workspace.h" -#include "ash/wm/workspace/workspace_observer.h" #include "base/auto_reset.h" #include "base/logging.h" #include "base/stl_util.h" +#include "ui/aura/client/aura_constants.h" #include "ui/aura/root_window.h" #include "ui/aura/screen_aura.h" #include "ui/aura/window.h" +#include "ui/base/ui_base_types.h" #include "ui/gfx/compositor/layer.h" #include "ui/gfx/compositor/layer_animator.h" #include "ui/gfx/compositor/scoped_layer_animation_settings.h" @@ -29,6 +32,20 @@ const int kWorkspaceHorizontalMargin = 50; const float kMaxOverviewScale = 0.9f; const float kMinOverviewScale = 0.3f; +// Sets the visibility of the layer of each window in |windows| to |value|. We +// set the visibility of the layer rather than the Window so that views doesn't +// see the visibility change. +// TODO: revisit this. Ideally the Window would think it's still visibile after +// this. May be possible after Ben lands his changes. +void SetWindowLayerVisibility(const std::vector<aura::Window*>& windows, + bool value) { + for (size_t i = 0; i < windows.size(); ++i){ + if (windows[i]->layer()) + windows[i]->layer()->SetVisible(value); + SetWindowLayerVisibility(windows[i]->transient_children(), value); + } +} + } namespace ash { @@ -43,20 +60,173 @@ WorkspaceManager::WorkspaceManager(aura::Window* contents_view) workspace_size_( gfx::Screen::GetMonitorAreaNearestWindow(contents_view_).size()), is_overview_(false), - layout_in_progress_(false), ignored_window_(NULL) { DCHECK(contents_view); } WorkspaceManager::~WorkspaceManager() { + for (size_t i = 0; i < workspaces_.size(); ++i) { + Workspace* workspace = workspaces_[i]; + for (size_t j = 0; j < workspace->windows().size(); ++j) + workspace->windows()[j]->RemoveObserver(this); + } std::vector<Workspace*> copy_to_delete(workspaces_); STLDeleteElements(©_to_delete); } -Workspace* WorkspaceManager::CreateWorkspace() { - Workspace* workspace = new Workspace(this); - LayoutWorkspaces(); - return workspace; +bool WorkspaceManager::IsManagedWindow(aura::Window* window) const { + return window->type() == aura::client::WINDOW_TYPE_NORMAL && + !window->transient_parent(); +} + +void WorkspaceManager::AddWindow(aura::Window* window) { + DCHECK(IsManagedWindow(window)); + + if (FindBy(window)) + return; // Already know about this window. + + window->AddObserver(this); + + if (!window->GetProperty(aura::client::kShowStateKey)) { + // TODO: set maximized if width < x. + window->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); + } + + // TODO: handle fullscreen. + if (window_util::IsWindowMaximized(window)) { + if (!GetRestoreBounds(window)) + SetRestoreBounds(window, window->GetTargetBounds()); + else + SetWindowBounds(window, GetWorkAreaBounds()); + } + + Workspace* workspace = NULL; + Workspace::Type type_for_window = Workspace::TypeForWindow(window); + switch (type_for_window) { + case Workspace::TYPE_SPLIT: + // Splits either go in current workspace (if maximized or split). If the + // current workspace isn't split/maximized, then create a maximized + // workspace. + workspace = GetActiveWorkspace(); + if (workspace && + (workspace->type() == Workspace::TYPE_SPLIT || + workspace->type() == Workspace::TYPE_MAXIMIZED)) { + // TODO: this needs to reset bounds of any existing windows in + // workspace. + workspace->SetType(Workspace::TYPE_SPLIT); + } else { + type_for_window = Workspace::TYPE_MAXIMIZED; + workspace = NULL; + } + break; + + case Workspace::TYPE_NORMAL: + // All normal windows go in the same workspace. + workspace = GetNormalWorkspace(); + break; + + case Workspace::TYPE_MAXIMIZED: + // All maximized windows go in their own workspace. + break; + + default: + NOTREACHED(); + break; + } + + if (!workspace) { + workspace = new Workspace(this); + workspace->SetType(type_for_window); + } + workspace->AddWindowAfter(window, NULL); + workspace->Activate(); +} + +void WorkspaceManager::RemoveWindow(aura::Window* window) { + Workspace* workspace = FindBy(window); + if (!workspace) + return; + window->RemoveObserver(this); + workspace->RemoveWindow(window); + if (workspace->is_empty()) + delete workspace; +} + +void WorkspaceManager::SetActiveWorkspaceByWindow(aura::Window* window) { + Workspace* workspace = FindBy(window); + if (workspace) + workspace->Activate(); +} + +gfx::Rect WorkspaceManager::GetDragAreaBounds() { + return GetWorkAreaBounds(); +} + +void WorkspaceManager::SetOverview(bool overview) { + if (is_overview_ == overview) + return; + NOTIMPLEMENTED(); +} + +void WorkspaceManager::SetWorkspaceSize(const gfx::Size& workspace_size) { + if (workspace_size == workspace_size_) + return; + workspace_size_ = workspace_size; + for (Workspaces::const_iterator i = workspaces_.begin(); + i != workspaces_.end(); ++i) { + (*i)->WorkspaceSizeChanged(); + } +} + +void WorkspaceManager::OnWindowPropertyChanged(aura::Window* window, + const char* name, + void* old) { + if (!IsManagedWindow(window)) + return; + + if (name != aura::client::kShowStateKey) + return; + // TODO: handle fullscreen. + bool is_maximized = window->GetIntProperty(name) == ui::SHOW_STATE_MAXIMIZED; + bool was_maximized = + (old == reinterpret_cast<void*>(ui::SHOW_STATE_MAXIMIZED)); + if (is_maximized == was_maximized) + return; + + MaximizedStateChanged(window); +} + +//////////////////////////////////////////////////////////////////////////////// +// WorkspaceManager, private: + +void WorkspaceManager::AddWorkspace(Workspace* workspace) { + DCHECK(std::find(workspaces_.begin(), workspaces_.end(), + workspace) == workspaces_.end()); + if (active_workspace_) { + // New workspaces go right after current workspace. + Workspaces::iterator i = std::find(workspaces_.begin(), workspaces_.end(), + active_workspace_); + workspaces_.insert(++i, workspace); + } else { + workspaces_.push_back(workspace); + } +} + +void WorkspaceManager::RemoveWorkspace(Workspace* workspace) { + Workspaces::iterator i = std::find(workspaces_.begin(), + workspaces_.end(), + workspace); + DCHECK(i != workspaces_.end()); + i = workspaces_.erase(i); + if (active_workspace_ == workspace) { + // TODO: need mru order. + if (i != workspaces_.end()) + SetActiveWorkspace(*i); + else if (!workspaces_.empty()) + SetActiveWorkspace(workspaces_.back()); + else + active_workspace_ = NULL; + } } Workspace* WorkspaceManager::GetActiveWorkspace() const { @@ -68,176 +238,22 @@ Workspace* WorkspaceManager::FindBy(aura::Window* window) const { return index < 0 ? NULL : workspaces_[index]; } -aura::Window* WorkspaceManager::FindRotateWindowForLocation( - const gfx::Point& point) { - for (Workspaces::const_iterator i = workspaces_.begin(); - i != workspaces_.end(); - ++i) { - aura::Window* window = (*i)->FindRotateWindowForLocation(point); - if (window) - return window; - } - return NULL; -} - -void WorkspaceManager::LayoutWorkspaces() { - UpdateContentsView(); - - gfx::Rect bounds(workspace_size_); - int x = 0; - for (Workspaces::const_iterator i = workspaces_.begin(); - i != workspaces_.end(); - ++i) { - Workspace* workspace = *i; - bounds.set_x(x); - workspace->SetBounds(bounds); - x += bounds.width() + kWorkspaceHorizontalMargin; - } -} - -gfx::Rect WorkspaceManager::GetDragAreaBounds() { - return GetWorkAreaBounds(gfx::Rect(contents_view_->bounds().size())); -} - -void WorkspaceManager::SetOverview(bool overview) { - if (is_overview_ == overview) - return; - is_overview_ = overview; - - ui::Transform transform; - if (is_overview_) { - // TODO(oshima|sky): We limit the how small windows can be shrinked - // in overview mode, thus part of the contents_view may not be visible. - // We need to add capability to scroll/move contents_view in overview mode. - float scale = std::min( - kMaxOverviewScale, - workspace_size_.width() / - static_cast<float>(contents_view_->bounds().width())); - scale = std::max(kMinOverviewScale, scale); - - transform.SetScale(scale, scale); - - int overview_width = contents_view_->bounds().width() * scale; - int dx = 0; - if (overview_width < workspace_size_.width()) { - dx = (workspace_size_.width() - overview_width) / 2; - } else if (active_workspace_) { - // Center the active workspace. - int active_workspace_mid_x = (active_workspace_->bounds().x() + - active_workspace_->bounds().width() / 2) * scale; - dx = workspace_size_.width() / 2 - active_workspace_mid_x; - dx = std::min(0, std::max(dx, workspace_size_.width() - overview_width)); - } - - transform.SetTranslateX(dx); - transform.SetTranslateY(workspace_size_.height() * (1.0f - scale) / 2); - } else if (active_workspace_) { - transform.SetTranslateX(-active_workspace_->bounds().x()); - } - - ui::ScopedLayerAnimationSettings settings( - contents_view_->layer()->GetAnimator()); - contents_view_->layer()->SetTransform(transform); -} - -void WorkspaceManager::RotateWindows(aura::Window* source, - aura::Window* target) { - DCHECK(source); - DCHECK(target); - int source_ws_index = GetWorkspaceIndexContaining(source); - int target_ws_index = GetWorkspaceIndexContaining(target); - DCHECK(source_ws_index >= 0); - DCHECK(target_ws_index >= 0); - if (source_ws_index == target_ws_index) { - workspaces_[source_ws_index]->RotateWindows(source, target); - } else { - aura::Window* insert = source; - aura::Window* target_to_insert = target; - if (source_ws_index < target_ws_index) { - for (int i = target_ws_index; i >= source_ws_index; --i) { - insert = workspaces_[i]->ShiftWindows( - insert, source, target_to_insert, Workspace::SHIFT_TO_LEFT); - // |target| can only be in the 1st workspace. - target_to_insert = NULL; - } - } else { - for (int i = target_ws_index; i <= source_ws_index; ++i) { - insert = workspaces_[i]->ShiftWindows( - insert, source, target_to_insert, Workspace::SHIFT_TO_RIGHT); - // |target| can only be in the 1st workspace. - target_to_insert = NULL; - } - } - } - FOR_EACH_OBSERVER(WorkspaceObserver, observers_, - WindowMoved(this, source, target)); - workspaces_[target_ws_index]->Activate(); -} - -void WorkspaceManager::SetWorkspaceSize(const gfx::Size& workspace_size) { - if (workspace_size == workspace_size_) - return; - workspace_size_ = workspace_size; - LayoutWorkspaces(); -} - -void WorkspaceManager::AddObserver(WorkspaceObserver* observer) { - observers_.AddObserver(observer); -} - -void WorkspaceManager::RemoveObserver(WorkspaceObserver* observer) { - observers_.RemoveObserver(observer); -} - -//////////////////////////////////////////////////////////////////////////////// -// WorkspaceManager, private: - -void WorkspaceManager::AddWorkspace(Workspace* workspace) { - Workspaces::iterator i = std::find(workspaces_.begin(), - workspaces_.end(), - workspace); - DCHECK(i == workspaces_.end()); - workspaces_.push_back(workspace); -} - -void WorkspaceManager::RemoveWorkspace(Workspace* workspace) { - Workspaces::iterator i = std::find(workspaces_.begin(), - workspaces_.end(), - workspace); - DCHECK(i != workspaces_.end()); - Workspace* old = NULL; - - if (workspace == active_workspace_) { - old = active_workspace_; - active_workspace_ = NULL; - } - workspaces_.erase(i); - LayoutWorkspaces(); - - if (old) { - FOR_EACH_OBSERVER(WorkspaceObserver, observers_, - ActiveWorkspaceChanged(this, old)); - } -} - void WorkspaceManager::SetActiveWorkspace(Workspace* workspace) { if (active_workspace_ == workspace) return; DCHECK(std::find(workspaces_.begin(), workspaces_.end(), workspace) != workspaces_.end()); - Workspace* old = active_workspace_; + if (active_workspace_) + SetWindowLayerVisibility(active_workspace_->windows(), false); active_workspace_ = workspace; + if (active_workspace_) + SetWindowLayerVisibility(active_workspace_->windows(), true); is_overview_ = false; - UpdateContentsView(); - - FOR_EACH_OBSERVER(WorkspaceObserver, observers_, - ActiveWorkspaceChanged(this, old)); } -gfx::Rect WorkspaceManager::GetWorkAreaBounds( - const gfx::Rect& workspace_bounds) { - gfx::Rect bounds = workspace_bounds; +gfx::Rect WorkspaceManager::GetWorkAreaBounds() { + gfx::Rect bounds(workspace_size_); bounds.Inset( aura::RootWindow::GetInstance()->screen()->work_area_insets()); return bounds; @@ -254,23 +270,75 @@ int WorkspaceManager::GetWorkspaceIndexContaining(aura::Window* window) const { return -1; } -void WorkspaceManager::UpdateContentsView() { - int num_workspaces = std::max(1, static_cast<int>(workspaces_.size())); - int total_width = workspace_size_.width() * num_workspaces + - kWorkspaceHorizontalMargin * (num_workspaces - 1); - gfx::Rect bounds(0, 0, total_width, workspace_size_.height()); +void WorkspaceManager::SetWindowBounds(aura::Window* window, + const gfx::Rect& bounds) { + // TODO: I suspect it's possible for this to be invoked when ignored_window_ + // is non-NULL. + ignored_window_ = window; + window->SetBounds(bounds); + ignored_window_ = NULL; +} - if (contents_view_->GetTargetBounds() != bounds) - contents_view_->SetBounds(bounds); - - // Move to active workspace. - if (active_workspace_) { - ui::Transform transform; - transform.SetTranslateX(-active_workspace_->bounds().x()); - ui::ScopedLayerAnimationSettings settings( - contents_view_->layer()->GetAnimator()); - contents_view_->SetTransform(transform); +void WorkspaceManager::SetWindowBoundsFromRestoreBounds(aura::Window* window) { + Workspace* workspace = FindBy(window); + DCHECK(workspace); + const gfx::Rect* restore = GetRestoreBounds(window); + if (restore) { + SetWindowBounds(window, + restore->AdjustToFit(workspace->GetWorkAreaBounds())); + } else { + SetWindowBounds(window, window->bounds().AdjustToFit( + workspace->GetWorkAreaBounds())); } + ash::ClearRestoreBounds(window); +} + +void WorkspaceManager::MaximizedStateChanged(aura::Window* window) { + DCHECK(IsManagedWindow(window)); + bool is_maximized = window_util::IsWindowMaximized(window); + Workspace* current_workspace = FindBy(window); + DCHECK(current_workspace); + if (is_maximized) { + // Unmaximized -> maximized; create a new workspace (unless current only has + // one window). + SetRestoreBounds(window, window->GetTargetBounds()); + if (current_workspace->num_windows() != 1) { + current_workspace->RemoveWindow(window); + Workspace* workspace = new Workspace(this); + workspace->SetType(Workspace::TYPE_MAXIMIZED); + workspace->AddWindowAfter(window, NULL); + current_workspace = workspace; + } else { + current_workspace->SetType(Workspace::TYPE_MAXIMIZED); + } + SetWindowBounds(window, GetWorkAreaBounds()); + } else { + // Maximized -> unmaximized; move window to unmaximized workspace (or reuse + // current if there isn't one). + window_util::SetOpenWindowSplit(window, false); + Workspace* workspace = GetNormalWorkspace(); + if (workspace) { + current_workspace->RemoveWindow(window); + DCHECK(current_workspace->is_empty()); + workspace->AddWindowAfter(window, NULL); + delete current_workspace; + current_workspace = workspace; + } else { + current_workspace->SetType(Workspace::TYPE_NORMAL); + } + + SetWindowBoundsFromRestoreBounds(window); + } + + SetActiveWorkspace(current_workspace); +} + +Workspace* WorkspaceManager::GetNormalWorkspace() { + for (size_t i = 0; i < workspaces_.size(); ++i) { + if (workspaces_[i]->type() == Workspace::TYPE_NORMAL) + return workspaces_[i]; + } + return NULL; } } // namespace internal diff --git a/ash/wm/workspace/workspace_manager.h b/ash/wm/workspace/workspace_manager.h index 99560e94643da..055893e1f0095 100644 --- a/ash/wm/workspace/workspace_manager.h +++ b/ash/wm/workspace/workspace_manager.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -9,8 +9,8 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" -#include "base/observer_list.h" #include "ash/ash_export.h" +#include "ui/aura/window_observer.h" #include "ui/gfx/insets.h" #include "ui/gfx/size.h" @@ -26,34 +26,32 @@ class Rect; namespace ash { namespace internal { class Workspace; -class WorkspaceObserver; +class WorkspaceManagerTest; // WorkspaceManager manages multiple workspaces in the desktop. -class ASH_EXPORT WorkspaceManager { +class ASH_EXPORT WorkspaceManager : public aura::WindowObserver{ public: explicit WorkspaceManager(aura::Window* viewport); virtual ~WorkspaceManager(); + // Returns true if |window| should be managed by the WorkspaceManager. + bool IsManagedWindow(aura::Window* window) const; + + // Adds/removes a window creating/destroying workspace as necessary. + void AddWindow(aura::Window* window); + void RemoveWindow(aura::Window* window); + + // Activates the workspace containing |window|. Does nothing if |window| is + // NULL or not contained in a workspace. + void SetActiveWorkspaceByWindow(aura::Window* window); + // Returns the Window this WorkspaceManager controls. aura::Window* contents_view() { return contents_view_; } - // Create new workspace. Workspace objects are managed by - // this WorkspaceManager. Deleting workspace will automatically - // remove the workspace from the workspace_manager. - Workspace* CreateWorkspace(); - - // Returns the active workspace. - Workspace* GetActiveWorkspace() const; - - // Returns the workspace that contanis the |window|. - Workspace* FindBy(aura::Window* window) const; - // Returns the window for rotate operation based on the |location|. + // TODO: this isn't currently used; remove if we do away with overview. aura::Window* FindRotateWindowForLocation(const gfx::Point& location); - // Sets the bounds of all workspaces. - void LayoutWorkspaces(); - // Returns the bounds in which a window can be moved/resized. gfx::Rect GetDragAreaBounds(); @@ -61,25 +59,9 @@ class ASH_EXPORT WorkspaceManager { void SetOverview(bool overview); bool is_overview() const { return is_overview_; } - // Rotate windows by moving |source| window to the position of |target|. - void RotateWindows(aura::Window* source, aura::Window* target); - // Sets the size of a single workspace (all workspaces have the same size). void SetWorkspaceSize(const gfx::Size& workspace_size); - // Adds/Removes workspace observer. - void AddObserver(WorkspaceObserver* observer); - void RemoveObserver(WorkspaceObserver* observer); - - // Returns true if this workspace manager is laying out windows. - // When true, LayoutManager must give windows their requested bounds. - bool layout_in_progress() const { return layout_in_progress_; } - - // Sets the |layout_in_progress_| flag. - void set_layout_in_progress(bool layout_in_progress) { - layout_in_progress_ = layout_in_progress; - } - // Sets/Returns the ignored window that the workspace manager does not // set bounds on. void set_ignored_window(aura::Window* ignored_window) { @@ -87,23 +69,47 @@ class ASH_EXPORT WorkspaceManager { } aura::Window* ignored_window() { return ignored_window_; } + // Overriden from aura::WindowObserver: + virtual void OnWindowPropertyChanged(aura::Window* window, + const char* name, + void* old) OVERRIDE; + private: friend class Workspace; + friend class WorkspaceManagerTest; void AddWorkspace(Workspace* workspace); void RemoveWorkspace(Workspace* workspace); + // Returns the active workspace. + Workspace* GetActiveWorkspace() const; + + // Returns the workspace that contanis the |window|. + Workspace* FindBy(aura::Window* window) const; + // Sets the active workspace. void SetActiveWorkspace(Workspace* workspace); - // Returns the bounds of the work are given |workspace_bounds|. - gfx::Rect GetWorkAreaBounds(const gfx::Rect& workspace_bounds); + // Returns the bounds of the work area. + gfx::Rect GetWorkAreaBounds(); // Returns the index of the workspace that contains the |window|. int GetWorkspaceIndexContaining(aura::Window* window) const; - // Update contents_view size and move the viewport to the active workspace. - void UpdateContentsView(); + // Sets the bounds of |window|. This sets |ignored_window_| to |window| so + // that the bounds change is allowed through. + void SetWindowBounds(aura::Window* window, const gfx::Rect& bounds); + + // Resets the bounds of |window| to its restored bounds (if set), ensuring + // it fits in the the windows current workspace. + void SetWindowBoundsFromRestoreBounds(aura::Window* window); + + // Invoked when the maximized state of |window| changes. + void MaximizedStateChanged(aura::Window* window); + + // Returns the Workspace whose type is TYPE_NORMAL, or NULL if there isn't + // one. + Workspace* GetNormalWorkspace(); aura::Window* contents_view_; @@ -118,14 +124,9 @@ class ASH_EXPORT WorkspaceManager { // True if the workspace manager is in overview mode. bool is_overview_; - // True if this layout manager is laying out windows. - bool layout_in_progress_; - // The window that WorkspaceManager does not set the bounds on. aura::Window* ignored_window_; - ObserverList<WorkspaceObserver> observers_; - DISALLOW_COPY_AND_ASSIGN(WorkspaceManager); }; diff --git a/ash/wm/workspace/workspace_manager_unittest.cc b/ash/wm/workspace/workspace_manager_unittest.cc index 51df1943d22d5..c8753f056dc3f 100644 --- a/ash/wm/workspace/workspace_manager_unittest.cc +++ b/ash/wm/workspace/workspace_manager_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -7,7 +7,7 @@ #include "ash/shell_window_ids.h" #include "ash/wm/activation_controller.h" #include "ash/wm/workspace/workspace.h" -#include "ash/wm/workspace/workspace_observer.h" +#include "ash/wm/workspace/workspace_layout_manager.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/root_window.h" #include "ui/aura/screen_aura.h" @@ -15,81 +15,27 @@ #include "ui/aura/window.h" #include "ui/base/ui_base_types.h" -namespace { -using ash::internal::Workspace; -using ash::internal::WorkspaceManager; using aura::Window; -class TestWorkspaceObserver : public ash::internal::WorkspaceObserver { - public: - explicit TestWorkspaceObserver(WorkspaceManager* manager) - : manager_(manager), - move_source_(NULL), - move_target_(NULL), - active_workspace_(NULL), - old_active_workspace_(NULL) { - manager_->AddObserver(this); - } - - virtual ~TestWorkspaceObserver() { - manager_->RemoveObserver(this); - } - - Window* move_source() { return move_source_; } - Window* move_target() { return move_target_; } - Workspace* active_workspace() { return active_workspace_; } - Workspace* old_active_workspace() { return old_active_workspace_; } - - // Resets the observer states. - void reset() { - active_workspace_ = NULL; - old_active_workspace_ = NULL; - move_source_ = NULL; - move_target_ = NULL; - } - - // Overridden from WorkspaceObserver: - virtual void WindowMoved(WorkspaceManager* manager, - Window* source, - Window* target) { - move_source_ = source; - move_target_ = target; - } - virtual void ActiveWorkspaceChanged(WorkspaceManager* manager, - Workspace* old) { - old_active_workspace_ = old; - active_workspace_ = manager->GetActiveWorkspace(); - } - - private: - WorkspaceManager* manager_; - Window* move_source_; - Window* move_target_; - Workspace* active_workspace_; - Workspace* old_active_workspace_; - - DISALLOW_COPY_AND_ASSIGN(TestWorkspaceObserver); -}; - -} // namespace - namespace ash { namespace internal { -class WorkspaceManagerTestBase : public aura::test::AuraTestBase { +class WorkspaceManagerTest : public aura::test::AuraTestBase { public: - WorkspaceManagerTestBase() { + WorkspaceManagerTest() : layout_manager_(NULL) { aura::RootWindow::GetInstance()->set_id( internal::kShellWindowId_DefaultContainer); activation_controller_.reset(new internal::ActivationController); activation_controller_->set_default_container_for_test( aura::RootWindow::GetInstance()); } - virtual ~WorkspaceManagerTestBase() {} + virtual ~WorkspaceManagerTest() {} virtual void SetUp() OVERRIDE { aura::test::AuraTestBase::SetUp(); manager_.reset(new WorkspaceManager(viewport())); + layout_manager_ = new WorkspaceLayoutManager(manager_.get()); + viewport()->SetLayoutManager(layout_manager_); } virtual void TearDown() OVERRIDE { @@ -99,468 +45,243 @@ class WorkspaceManagerTestBase : public aura::test::AuraTestBase { aura::Window* CreateTestWindow() { aura::Window* window = new aura::Window(NULL); - window->Init(ui::Layer::LAYER_HAS_NO_TEXTURE); + window->SetType(aura::client::WINDOW_TYPE_NORMAL); + window->Init(ui::Layer::LAYER_HAS_TEXTURE); + window->SetParent(viewport()); return window; } aura::Window* viewport() { return aura::RootWindow::GetInstance(); } + + const std::vector<Workspace*>& workspaces() const { + return manager_->workspaces_; + } + + gfx::Rect GetWorkAreaBounds() { + return manager_->GetWorkAreaBounds(); + } + + Workspace* active_workspace() { + return manager_->active_workspace_; + } + + Workspace* FindBy(aura::Window* window) const { + return manager_->FindBy(window); + } + scoped_ptr<WorkspaceManager> manager_; + // Owned by viewport(). + WorkspaceLayoutManager* layout_manager_; + private: scoped_ptr<internal::ActivationController> activation_controller_; - DISALLOW_COPY_AND_ASSIGN(WorkspaceManagerTestBase); + DISALLOW_COPY_AND_ASSIGN(WorkspaceManagerTest); }; -class WorkspaceManagerTest : public WorkspaceManagerTestBase { -}; - -TEST_F(WorkspaceManagerTest, WorkspaceManagerCreateAddFind) { +// Assertions around adding a normal window. +TEST_F(WorkspaceManagerTest, AddNormalWindowWhenEmpty) { scoped_ptr<Window> w1(CreateTestWindow()); - scoped_ptr<Window> w2(CreateTestWindow()); + w1->SetBounds(gfx::Rect(0, 0, 250, 251)); - Workspace* ws1 = manager_->CreateWorkspace(); - ws1->AddWindowAfter(w1.get(), NULL); - // w2 is not a part of any workspace yet. - EXPECT_EQ(NULL, manager_->FindBy(w2.get())); + ASSERT_TRUE(manager_->IsManagedWindow(w1.get())); + EXPECT_FALSE(FindBy(w1.get())); - // w2 is in ws2 workspace. - Workspace* ws2 = manager_->CreateWorkspace(); - ws2->AddWindowAfter(w2.get(), NULL); - EXPECT_EQ(ws2, manager_->FindBy(w2.get())); - - // Make sure |FindBy(w1.get())| still returns - // correct workspace. - EXPECT_EQ(ws1, manager_->FindBy(w1.get())); - - // once workspace is gone, w2 shouldn't match - // any workspace. - delete ws2; - EXPECT_EQ(NULL, manager_->FindBy(w2.get())); - - // Reset now before windows are destroyed. - manager_.reset(); -} - -TEST_F(WorkspaceManagerTest, LayoutWorkspaces) { - manager_->SetWorkspaceSize(gfx::Size(100, 100)); - EXPECT_EQ("0,0 100x100", viewport()->bounds().ToString()); - - Workspace* ws1 = manager_->CreateWorkspace(); - manager_->LayoutWorkspaces(); - - // ws1 is laied out in left most position. - EXPECT_EQ(100, viewport()->bounds().width()); - EXPECT_EQ("0,0 100x100", ws1->bounds().ToString()); - - // ws2 is laied out next to ws1, with 50 margin. - Workspace* ws2 = manager_->CreateWorkspace(); - manager_->LayoutWorkspaces(); - - EXPECT_EQ(250, viewport()->bounds().width()); - EXPECT_EQ("0,0 100x100", ws1->bounds().ToString()); - EXPECT_EQ("150,0 100x100", ws2->bounds().ToString()); -} - -// Makes sure the bounds of window are resized if the workspace size shrinks. -TEST_F(WorkspaceManagerTest, ResizeDuringLayout) { - manager_->SetWorkspaceSize(gfx::Size(100, 100)); - EXPECT_EQ("0,0 100x100", viewport()->bounds().ToString()); - - Workspace* ws1 = manager_->CreateWorkspace(); - scoped_ptr<Window> w1(CreateTestWindow()); - w1->SetBounds(gfx::Rect(0, 0, 100, 100)); - viewport()->AddChild(w1.get()); - EXPECT_TRUE(ws1->AddWindowAfter(w1.get(), NULL)); - manager_->SetWorkspaceSize(gfx::Size(50, 50)); - - // ws1 is laied out in left most position. - EXPECT_EQ("0,0 50x50", ws1->bounds().ToString()); - EXPECT_EQ("0,0 50x50", w1->layer()->GetTargetBounds().ToString()); -} - -TEST_F(WorkspaceManagerTest, WorkspaceManagerDragArea) { - aura::RootWindow::GetInstance()->screen()->set_work_area_insets( - gfx::Insets(10, 10, 10, 10)); - viewport()->SetBounds(gfx::Rect(0, 0, 200, 200)); - EXPECT_EQ("10,10 180x180", manager_->GetDragAreaBounds().ToString()); -} - -TEST_F(WorkspaceManagerTest, WorkspaceManagerActivate) { - TestWorkspaceObserver observer(manager_.get()); - Workspace* ws1 = manager_->CreateWorkspace(); - Workspace* ws2 = manager_->CreateWorkspace(); - EXPECT_EQ(NULL, manager_->GetActiveWorkspace()); - - // Activate ws1. - ws1->Activate(); - EXPECT_EQ(ws1, manager_->GetActiveWorkspace()); - EXPECT_EQ(NULL, observer.old_active_workspace()); - EXPECT_EQ(ws1, observer.active_workspace()); - observer.reset(); - - // Activate ws2. - ws2->Activate(); - EXPECT_EQ(ws2, manager_->GetActiveWorkspace()); - EXPECT_EQ(ws1, observer.old_active_workspace()); - EXPECT_EQ(ws2, observer.active_workspace()); - observer.reset(); - - // Deleting active workspace sets active workspace to NULL. - delete ws2; - EXPECT_EQ(NULL, manager_->GetActiveWorkspace()); - EXPECT_EQ(ws2, observer.old_active_workspace()); - EXPECT_EQ(NULL, observer.active_workspace()); -} - -TEST_F(WorkspaceManagerTest, FindRotateWindow) { - manager_->SetWorkspaceSize(gfx::Size(500, 300)); - - Workspace* ws1 = manager_->CreateWorkspace(); - scoped_ptr<Window> w11(CreateTestWindow()); - w11->SetBounds(gfx::Rect(0, 0, 100, 100)); - ws1->AddWindowAfter(w11.get(), NULL); - - scoped_ptr<Window> w12(CreateTestWindow()); - w12->SetBounds(gfx::Rect(0, 0, 100, 100)); - ws1->AddWindowAfter(w12.get(), NULL); - manager_->LayoutWorkspaces(); - - // Workspaces are 0-<lmgn>-145-<w11>-245-<wmng>-255-<w12>-355-<rmgn>-500. - EXPECT_EQ(NULL, manager_->FindRotateWindowForLocation(gfx::Point(0, 0))); - EXPECT_EQ(NULL, manager_->FindRotateWindowForLocation(gfx::Point(100, 0))); - EXPECT_EQ(w11.get(), - manager_->FindRotateWindowForLocation(gfx::Point(150, 0))); - EXPECT_EQ(w12.get(), - manager_->FindRotateWindowForLocation(gfx::Point(300, 0))); - EXPECT_EQ(NULL, manager_->FindRotateWindowForLocation(gfx::Point(400, 0))); - - w11->SetBounds(gfx::Rect(0, 0, 400, 100)); - w12->SetBounds(gfx::Rect(0, 0, 200, 100)); - manager_->FindBy(w11.get())->Layout(NULL); - EXPECT_EQ(w11.get(), - manager_->FindRotateWindowForLocation(gfx::Point(10, 0))); - EXPECT_EQ(w11.get(), - manager_->FindRotateWindowForLocation(gfx::Point(240, 0))); - EXPECT_EQ(w12.get(), - manager_->FindRotateWindowForLocation(gfx::Point(260, 0))); - EXPECT_EQ(w12.get(), - manager_->FindRotateWindowForLocation(gfx::Point(490, 0))); - - Workspace* ws2 = manager_->CreateWorkspace(); - scoped_ptr<Window> w21(CreateTestWindow()); - w21->SetBounds(gfx::Rect(0, 0, 100, 100)); - ws2->AddWindowAfter(w21.get(), NULL); - manager_->LayoutWorkspaces(); - - // 2nd workspace starts from 500+50 and the window is centered 750-850. - EXPECT_EQ(NULL, manager_->FindRotateWindowForLocation(gfx::Point(600, 0))); - EXPECT_EQ(NULL, manager_->FindRotateWindowForLocation(gfx::Point(740, 0))); - EXPECT_EQ(w21.get(), - manager_->FindRotateWindowForLocation(gfx::Point(760, 0))); - EXPECT_EQ(w21.get(), - manager_->FindRotateWindowForLocation(gfx::Point(840, 0))); - EXPECT_EQ(NULL, manager_->FindRotateWindowForLocation(gfx::Point(860, 0))); - - // Reset now before windows are destroyed. - manager_.reset(); -} - -TEST_F(WorkspaceManagerTest, RotateWindows) { - scoped_ptr<TestWorkspaceObserver> observer( - new TestWorkspaceObserver(manager_.get())); - Workspace* ws1 = manager_->CreateWorkspace(); - Workspace* ws2 = manager_->CreateWorkspace(); - - scoped_ptr<Window> w11(CreateTestWindow()); - ws1->AddWindowAfter(w11.get(), NULL); - - scoped_ptr<Window> w21(CreateTestWindow()); - scoped_ptr<Window> w22(CreateTestWindow()); - ws2->AddWindowAfter(w21.get(), NULL); - ws2->AddWindowAfter(w22.get(), NULL); - - EXPECT_EQ(0, ws1->GetIndexOf(w11.get())); - EXPECT_EQ(0, ws2->GetIndexOf(w21.get())); - EXPECT_EQ(1, ws2->GetIndexOf(w22.get())); - - // Rotate right most to left most. - manager_->RotateWindows(w22.get(), w11.get()); - EXPECT_EQ(w22.get(), observer->move_source()); - EXPECT_EQ(w11.get(), observer->move_target()); - - EXPECT_EQ(0, ws1->GetIndexOf(w22.get())); - EXPECT_EQ(0, ws2->GetIndexOf(w11.get())); - EXPECT_EQ(1, ws2->GetIndexOf(w21.get())); - - // Rotate left most to right most. - manager_->RotateWindows(w22.get(), w21.get()); - EXPECT_EQ(0, ws1->GetIndexOf(w11.get())); - EXPECT_EQ(0, ws2->GetIndexOf(w21.get())); - EXPECT_EQ(1, ws2->GetIndexOf(w22.get())); - EXPECT_EQ(w22.get(), observer->move_source()); - EXPECT_EQ(w21.get(), observer->move_target()); - - // Rotate left most to 1st element in 2nd workspace. - manager_->RotateWindows(w11.get(), w21.get()); - EXPECT_EQ(0, ws1->GetIndexOf(w21.get())); - EXPECT_EQ(0, ws2->GetIndexOf(w11.get())); - EXPECT_EQ(1, ws2->GetIndexOf(w22.get())); - EXPECT_EQ(w11.get(), observer->move_source()); - EXPECT_EQ(w21.get(), observer->move_target()); - - // Rotate middle to right most. - manager_->RotateWindows(w11.get(), w22.get()); - EXPECT_EQ(0, ws1->GetIndexOf(w21.get())); - EXPECT_EQ(0, ws2->GetIndexOf(w22.get())); - EXPECT_EQ(1, ws2->GetIndexOf(w11.get())); - EXPECT_EQ(w11.get(), observer->move_source()); - EXPECT_EQ(w22.get(), observer->move_target()); - - // Rotate middle to left most. - manager_->RotateWindows(w22.get(), w21.get()); - EXPECT_EQ(0, ws1->GetIndexOf(w22.get())); - EXPECT_EQ(0, ws2->GetIndexOf(w21.get())); - EXPECT_EQ(1, ws2->GetIndexOf(w11.get())); - EXPECT_EQ(w22.get(), observer->move_source()); - EXPECT_EQ(w21.get(), observer->move_target()); - - // Reset now before windows are destroyed. - observer.reset(); - manager_.reset(); -} - -class WorkspaceTest : public WorkspaceManagerTestBase { -}; - -TEST_F(WorkspaceTest, WorkspaceBasic) { - Workspace* ws = manager_->CreateWorkspace(); - // Sanity check - EXPECT_TRUE(ws->is_empty()); - - scoped_ptr<Window> w1(CreateTestWindow()); - scoped_ptr<Window> w2(CreateTestWindow()); - scoped_ptr<Window> w3(CreateTestWindow()); - // ws is empty and can accomodate new window. - EXPECT_TRUE(ws->CanAdd(w1.get())); - - // Add w1. - EXPECT_TRUE(ws->AddWindowAfter(w1.get(), NULL)); - EXPECT_TRUE(ws->Contains(w1.get())); - EXPECT_FALSE(ws->is_empty()); - - // The workspac still has room for next window. - EXPECT_TRUE(ws->CanAdd(w2.get())); - EXPECT_TRUE(ws->AddWindowAfter(w2.get(), NULL)); - EXPECT_TRUE(ws->Contains(w2.get())); - - // The workspace no longer accepts new window. - EXPECT_FALSE(ws->CanAdd(w3.get())); - EXPECT_FALSE(ws->AddWindowAfter(w3.get(), NULL)); - EXPECT_FALSE(ws->Contains(w3.get())); - - // Check if the window has correct layout index. - EXPECT_EQ(0, ws->GetIndexOf(w1.get())); - EXPECT_EQ(1, ws->GetIndexOf(w2.get())); - EXPECT_EQ(-1, ws->GetIndexOf(w3.get())); - - // w1 is gone, so no index for w2. - ws->RemoveWindow(w1.get()); - EXPECT_EQ(-1, ws->GetIndexOf(w1.get())); - EXPECT_EQ(0, ws->GetIndexOf(w2.get())); - EXPECT_FALSE(ws->Contains(w1.get())); - - // Add w1 back. w1 now has index = 1. - EXPECT_TRUE(ws->AddWindowAfter(w1.get(), w2.get())); - EXPECT_EQ(0, ws->GetIndexOf(w2.get())); - EXPECT_EQ(1, ws->GetIndexOf(w1.get())); - - // Reset now before windows are destroyed. - manager_.reset(); -} - -TEST_F(WorkspaceTest, RotateWindows) { - size_t orig_max = Workspace::SetMaxWindowsCount(3); - Workspace* ws = manager_->CreateWorkspace(); - scoped_ptr<Window> w1(CreateTestWindow()); - scoped_ptr<Window> w2(CreateTestWindow()); - scoped_ptr<Window> w3(CreateTestWindow()); - ws->AddWindowAfter(w1.get(), NULL); - ws->AddWindowAfter(w2.get(), NULL); - ws->AddWindowAfter(w3.get(), NULL); - - EXPECT_EQ(0, ws->GetIndexOf(w1.get())); - EXPECT_EQ(1, ws->GetIndexOf(w2.get())); - EXPECT_EQ(2, ws->GetIndexOf(w3.get())); - - // Rotate to left. - ws->RotateWindows(w1.get(), w3.get()); - EXPECT_EQ(0, ws->GetIndexOf(w2.get())); - EXPECT_EQ(1, ws->GetIndexOf(w3.get())); - EXPECT_EQ(2, ws->GetIndexOf(w1.get())); - - // Rotate to right. - ws->RotateWindows(w1.get(), w2.get()); - EXPECT_EQ(0, ws->GetIndexOf(w1.get())); - EXPECT_EQ(1, ws->GetIndexOf(w2.get())); - EXPECT_EQ(2, ws->GetIndexOf(w3.get())); - - // Rotating to the middle from left. - ws->RotateWindows(w1.get(), w2.get()); - EXPECT_EQ(0, ws->GetIndexOf(w2.get())); - EXPECT_EQ(1, ws->GetIndexOf(w1.get())); - EXPECT_EQ(2, ws->GetIndexOf(w3.get())); - - // Rotating to the middle from right. - ws->RotateWindows(w3.get(), w1.get()); - EXPECT_EQ(0, ws->GetIndexOf(w2.get())); - EXPECT_EQ(1, ws->GetIndexOf(w3.get())); - EXPECT_EQ(2, ws->GetIndexOf(w1.get())); - - // Reset now before windows are destroyed. - manager_.reset(); - Workspace::SetMaxWindowsCount(orig_max); -} - -TEST_F(WorkspaceTest, ShiftWindowsSingle) { - Workspace* ws = manager_->CreateWorkspace(); - // Single window in a workspace case. - scoped_ptr<Window> w1(CreateTestWindow()); - ws->AddWindowAfter(w1.get(), NULL); - - scoped_ptr<Window> w2(CreateTestWindow()); - - // Sanity check. - EXPECT_EQ(0, ws->GetIndexOf(w1.get())); - EXPECT_EQ(-1, ws->GetIndexOf(w2.get())); - - // Insert |w2| at the beginning and shift. - aura::Window* overflow = - ws->ShiftWindows( - w2.get(), w2.get(), NULL, Workspace::SHIFT_TO_RIGHT); - EXPECT_EQ(w1.get(), overflow); - EXPECT_EQ(-1, ws->GetIndexOf(w1.get())); - EXPECT_EQ(0, ws->GetIndexOf(w2.get())); - - // Insert |w1| at the end and shift. - overflow = ws->ShiftWindows( - w1.get(), w1.get(), NULL, Workspace::SHIFT_TO_LEFT); - EXPECT_EQ(w2.get(), overflow); - EXPECT_EQ(-1, ws->GetIndexOf(w2.get())); - EXPECT_EQ(0, ws->GetIndexOf(w1.get())); - - // Insert |w2| at the begining and shift up to the w1. - overflow = ws->ShiftWindows( - w2.get(), w1.get(), NULL, Workspace::SHIFT_TO_RIGHT); - EXPECT_EQ(NULL, overflow); - EXPECT_EQ(-1, ws->GetIndexOf(w1.get())); - EXPECT_EQ(0, ws->GetIndexOf(w2.get())); - - // Insert |w1| at the end and shift up to the w2. - overflow = ws->ShiftWindows( - w1.get(), w2.get(), NULL, Workspace::SHIFT_TO_LEFT); - EXPECT_EQ(NULL, overflow); - EXPECT_EQ(-1, ws->GetIndexOf(w2.get())); - EXPECT_EQ(0, ws->GetIndexOf(w1.get())); - - // Reset now before windows are destroyed. - manager_.reset(); -} - -TEST_F(WorkspaceTest, ShiftWindowsMultiple) { - Workspace* ws = manager_->CreateWorkspace(); - // Single window in a workspace case. - scoped_ptr<Window> w1(CreateTestWindow()); - scoped_ptr<Window> w2(CreateTestWindow()); - ws->AddWindowAfter(w1.get(), NULL); - ws->AddWindowAfter(w2.get(), NULL); - - scoped_ptr<Window> w3(CreateTestWindow()); - - // Sanity check. - EXPECT_EQ(0, ws->GetIndexOf(w1.get())); - EXPECT_EQ(1, ws->GetIndexOf(w2.get())); - EXPECT_EQ(-1, ws->GetIndexOf(w3.get())); - - // Insert |w3| at the beginning and shift. - aura::Window* overflow = - ws->ShiftWindows(w3.get(), w3.get(), NULL, - Workspace::SHIFT_TO_RIGHT); - EXPECT_EQ(w2.get(), overflow); - EXPECT_EQ(-1, ws->GetIndexOf(w2.get())); - EXPECT_EQ(0, ws->GetIndexOf(w3.get())); - EXPECT_EQ(1, ws->GetIndexOf(w1.get())); - - // Insert |w3| at the end and shift. - overflow = ws->ShiftWindows(w2.get(), w2.get(), NULL, - Workspace::SHIFT_TO_LEFT); - EXPECT_EQ(w3.get(), overflow); - EXPECT_EQ(-1, ws->GetIndexOf(w3.get())); - EXPECT_EQ(0, ws->GetIndexOf(w1.get())); - EXPECT_EQ(1, ws->GetIndexOf(w2.get())); - - // Insert |w3| at the begining and shift up to the w1. - overflow = ws->ShiftWindows(w3.get(), w1.get(), NULL, - Workspace::SHIFT_TO_RIGHT); - EXPECT_EQ(NULL, overflow); - EXPECT_EQ(-1, ws->GetIndexOf(w1.get())); - EXPECT_EQ(0, ws->GetIndexOf(w3.get())); - EXPECT_EQ(1, ws->GetIndexOf(w2.get())); - - // Insert |w1| at the end and shift up to the w2. - overflow = ws->ShiftWindows(w1.get(), w2.get(), NULL, - Workspace::SHIFT_TO_LEFT); - EXPECT_EQ(NULL, overflow); - EXPECT_EQ(-1, ws->GetIndexOf(w2.get())); - EXPECT_EQ(0, ws->GetIndexOf(w3.get())); - EXPECT_EQ(1, ws->GetIndexOf(w1.get())); - - scoped_ptr<Window> unused(CreateTestWindow()); - - // Insert |w2| at the |w3| and shift to right. - overflow = ws->ShiftWindows(w2.get(), unused.get(), w3.get(), - Workspace::SHIFT_TO_RIGHT); - EXPECT_EQ(w1.get(), overflow); - EXPECT_EQ(-1, ws->GetIndexOf(w1.get())); - EXPECT_EQ(0, ws->GetIndexOf(w2.get())); - EXPECT_EQ(1, ws->GetIndexOf(w3.get())); - - // Insert |w1| at the |w2| and shift to left. - overflow = ws->ShiftWindows(w1.get(), unused.get(), w2.get(), - Workspace::SHIFT_TO_LEFT); - EXPECT_EQ(w2.get(), overflow); - EXPECT_EQ(-1, ws->GetIndexOf(w2.get())); - EXPECT_EQ(0, ws->GetIndexOf(w1.get())); - EXPECT_EQ(1, ws->GetIndexOf(w3.get())); - - // Reset now before windows are destroyed. - manager_.reset(); -} - -TEST_F(WorkspaceTest, ContainsFullscreenWindow) { - Workspace* ws = manager_->CreateWorkspace(); - scoped_ptr<Window> w1(CreateTestWindow()); - scoped_ptr<Window> w2(CreateTestWindow()); - ws->AddWindowAfter(w1.get(), NULL); - ws->AddWindowAfter(w2.get(), NULL); w1->Show(); + + ASSERT_TRUE(w1->layer() != NULL); + EXPECT_TRUE(w1->layer()->visible()); + + EXPECT_EQ(250, w1->bounds().width()); + EXPECT_EQ(251, w1->bounds().height()); + + // Should be 1 workspace, TYPE_NORNMAL with w1. + ASSERT_EQ(1u, workspaces().size()); + EXPECT_EQ(Workspace::TYPE_NORMAL, workspaces()[0]->type()); + ASSERT_EQ(1u, workspaces()[0]->windows().size()); + EXPECT_EQ(w1.get(), workspaces()[0]->windows()[0]); +} + +// Assertions around maximizing/unmaximizing. +TEST_F(WorkspaceManagerTest, SingleMaximizeWindow) { + scoped_ptr<Window> w1(CreateTestWindow()); + w1->SetBounds(gfx::Rect(0, 0, 250, 251)); + + ASSERT_TRUE(manager_->IsManagedWindow(w1.get())); + + w1->Show(); + + ASSERT_TRUE(w1->layer() != NULL); + EXPECT_TRUE(w1->layer()->visible()); + + EXPECT_EQ(250, w1->bounds().width()); + EXPECT_EQ(251, w1->bounds().height()); + + // Maximize the window. + w1->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); + + // Should be 1 workspace, TYPE_MAXIMIZED with w1. + ASSERT_EQ(1u, workspaces().size()); + EXPECT_EQ(Workspace::TYPE_MAXIMIZED, workspaces()[0]->type()); + ASSERT_EQ(1u, workspaces()[0]->windows().size()); + EXPECT_EQ(w1.get(), workspaces()[0]->windows()[0]); + EXPECT_EQ(GetWorkAreaBounds().width(), w1->bounds().width()); + EXPECT_EQ(GetWorkAreaBounds().height(), w1->bounds().height()); + + // Restore the window. + w1->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); + + // Should be 1 workspace, TYPE_NORMAL with w1. + ASSERT_EQ(1u, workspaces().size()); + EXPECT_EQ(Workspace::TYPE_NORMAL, workspaces()[0]->type()); + ASSERT_EQ(1u, workspaces()[0]->windows().size()); + EXPECT_EQ(w1.get(), workspaces()[0]->windows()[0]); + EXPECT_EQ(250, w1->bounds().width()); + EXPECT_EQ(251, w1->bounds().height()); +} + +// Assertions around closing the last window in a workspace. +TEST_F(WorkspaceManagerTest, CloseLastWindowInWorkspace) { + scoped_ptr<Window> w1(CreateTestWindow()); + scoped_ptr<Window> w2(CreateTestWindow()); + w1->SetBounds(gfx::Rect(0, 0, 250, 251)); + w1->Show(); + w2->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); w2->Show(); - EXPECT_FALSE(ws->ContainsFullscreenWindow()); + // Should be 2 workspaces, TYPE_NORMAL with w1, and TYPE_MAXIMIZED with w2. + ASSERT_EQ(2u, workspaces().size()); + EXPECT_EQ(Workspace::TYPE_NORMAL, workspaces()[0]->type()); + ASSERT_EQ(1u, workspaces()[0]->windows().size()); + EXPECT_EQ(w1.get(), workspaces()[0]->windows()[0]); + EXPECT_EQ(Workspace::TYPE_MAXIMIZED, workspaces()[1]->type()); + ASSERT_EQ(1u, workspaces()[1]->windows().size()); + EXPECT_EQ(w2.get(), workspaces()[1]->windows()[0]); + EXPECT_FALSE(w1->layer()->visible()); + EXPECT_TRUE(w2->layer()->visible()); + // TYPE_MAXIMIZED workspace should be active. + EXPECT_EQ(workspaces()[1], active_workspace()); - w1->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); - EXPECT_TRUE(ws->ContainsFullscreenWindow()); + // Close w2. + w2.reset(); - w1->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); - EXPECT_FALSE(ws->ContainsFullscreenWindow()); + // Should have one workspace, TYPE_NORMAL with w1. + ASSERT_EQ(1u, workspaces().size()); + EXPECT_EQ(Workspace::TYPE_NORMAL, workspaces()[0]->type()); + ASSERT_EQ(1u, workspaces()[0]->windows().size()); + EXPECT_EQ(w1.get(), workspaces()[0]->windows()[0]); + EXPECT_TRUE(w1->layer()->visible()); + EXPECT_EQ(workspaces()[0], active_workspace()); +} - w2->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); - EXPECT_TRUE(ws->ContainsFullscreenWindow()); +// Assertions around adding a maximized window when empty. +TEST_F(WorkspaceManagerTest, AddMaximizedWindowWhenEmpty) { + scoped_ptr<Window> w1(CreateTestWindow()); + w1->SetBounds(gfx::Rect(0, 0, 250, 251)); + w1->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); + w1->Show(); - w2->Hide(); - EXPECT_FALSE(ws->ContainsFullscreenWindow()); + ASSERT_TRUE(w1->layer() != NULL); + EXPECT_TRUE(w1->layer()->visible()); + gfx::Rect work_area( + gfx::Screen::GetMonitorWorkAreaNearestWindow(w1.get())); + EXPECT_EQ(work_area.width(), w1->bounds().width()); + EXPECT_EQ(work_area.height(), w1->bounds().height()); + + // Should be 1 workspace, TYPE_NORNMAL with w1. + ASSERT_EQ(1u, workspaces().size()); + EXPECT_EQ(Workspace::TYPE_MAXIMIZED, workspaces()[0]->type()); + ASSERT_EQ(1u, workspaces()[0]->windows().size()); + EXPECT_EQ(w1.get(), workspaces()[0]->windows()[0]); +} + +// Assertions around two windows and toggling one to be maximized. +TEST_F(WorkspaceManagerTest, 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->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); + w2->Show(); + + // Should now be two workspaces. + ASSERT_EQ(2u, workspaces().size()); + EXPECT_EQ(Workspace::TYPE_NORMAL, workspaces()[0]->type()); + ASSERT_EQ(1u, workspaces()[0]->windows().size()); + EXPECT_EQ(w1.get(), workspaces()[0]->windows()[0]); + EXPECT_EQ(Workspace::TYPE_MAXIMIZED, workspaces()[1]->type()); + ASSERT_EQ(1u, workspaces()[1]->windows().size()); + EXPECT_EQ(w2.get(), workspaces()[1]->windows()[0]); + ASSERT_TRUE(w1->layer() != NULL); + EXPECT_FALSE(w1->layer()->visible()); + ASSERT_TRUE(w2->layer() != NULL); + EXPECT_TRUE(w2->layer()->visible()); + + gfx::Rect work_area( + gfx::Screen::GetMonitorWorkAreaNearestWindow(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->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); + ASSERT_EQ(1u, workspaces().size()); + EXPECT_EQ(Workspace::TYPE_NORMAL, workspaces()[0]->type()); + ASSERT_EQ(2u, workspaces()[0]->windows().size()); + EXPECT_EQ(w1.get(), workspaces()[0]->windows()[0]); + EXPECT_EQ(w2.get(), workspaces()[0]->windows()[1]); + EXPECT_EQ(50, w2->bounds().width()); + EXPECT_EQ(51, w2->bounds().height()); + ASSERT_TRUE(w1->layer() != NULL); + EXPECT_TRUE(w1->layer()->visible()); + ASSERT_TRUE(w2->layer() != NULL); + EXPECT_TRUE(w2->layer()->visible()); +} + +// Assertions around two maximized windows. +TEST_F(WorkspaceManagerTest, TwoMaximized) { + scoped_ptr<Window> w1(CreateTestWindow()); + scoped_ptr<Window> w2(CreateTestWindow()); + w1->SetBounds(gfx::Rect(0, 0, 250, 251)); + w1->Show(); + w1->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); + + w2->SetBounds(gfx::Rect(0, 0, 50, 51)); + w2->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_MAXIMIZED); + w2->Show(); + + // Should now be two workspaces. + ASSERT_EQ(2u, workspaces().size()); + EXPECT_EQ(Workspace::TYPE_MAXIMIZED, workspaces()[0]->type()); + ASSERT_EQ(1u, workspaces()[0]->windows().size()); + EXPECT_EQ(w1.get(), workspaces()[0]->windows()[0]); + EXPECT_EQ(Workspace::TYPE_MAXIMIZED, workspaces()[1]->type()); + ASSERT_EQ(1u, workspaces()[1]->windows().size()); + EXPECT_EQ(w2.get(), workspaces()[1]->windows()[0]); + ASSERT_TRUE(w1->layer() != NULL); + EXPECT_FALSE(w1->layer()->visible()); + ASSERT_TRUE(w2->layer() != NULL); + EXPECT_TRUE(w2->layer()->visible()); +} + +// Makes sure requests to change the bounds of a normal window go through. +TEST_F(WorkspaceManagerTest, ChangeBoundsOfNormalWindow) { + scoped_ptr<Window> w1(CreateTestWindow()); + w1->Show(); + + EXPECT_TRUE(manager_->IsManagedWindow(w1.get())); + // 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()); } } // namespace internal diff --git a/ash/wm/workspace/workspace_observer.h b/ash/wm/workspace/workspace_observer.h deleted file mode 100644 index c6e055bffae37..0000000000000 --- a/ash/wm/workspace/workspace_observer.h +++ /dev/null @@ -1,40 +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_WORKSPACE_WORKSPACE_OBSERVER_H_ -#define ASH_WM_WORKSPACE_WORKSPACE_OBSERVER_H_ -#pragma once - -#include "ash/ash_export.h" - -namespace aura { -class Window; -} - -namespace ash { -namespace internal { -class Workspace; -class WorkspaceManager; - -// A class to observe changes in workspace state. -class ASH_EXPORT WorkspaceObserver { - public: - // Invoked when |start| window is moved and inserted - // at the |target| window's position by |WorkspaceManager::RotateWindow|. - virtual void WindowMoved(WorkspaceManager* manager, - aura::Window* source, - aura::Window* target) = 0; - - // Invoked when the active workspace changes. |old| is - // the old active workspace and can be NULL. - virtual void ActiveWorkspaceChanged(WorkspaceManager* manager, - Workspace* old) = 0; - protected: - virtual ~WorkspaceObserver() {} -}; - -} // namespace internal -} // namespace ash - -#endif // ASH_WM_WORKSPACE_WORKSPACE_OBSERVER_H_ diff --git a/ash/wm/workspace_controller.cc b/ash/wm/workspace_controller.cc index c12f8c95a1bc8..8f9d7cf614331 100644 --- a/ash/wm/workspace_controller.cc +++ b/ash/wm/workspace_controller.cc @@ -8,7 +8,6 @@ #include "ash/launcher/launcher_model.h" #include "ash/shell.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" @@ -23,13 +22,11 @@ WorkspaceController::WorkspaceController(aura::Window* viewport) : workspace_manager_(new WorkspaceManager(viewport)), launcher_model_(NULL), ignore_move_event_(false) { - workspace_manager_->AddObserver(this); aura::RootWindow::GetInstance()->AddRootWindowObserver(this); aura::RootWindow::GetInstance()->AddObserver(this); } WorkspaceController::~WorkspaceController() { - workspace_manager_->RemoveObserver(this); if (launcher_model_) launcher_model_->RemoveObserver(this); aura::RootWindow::GetInstance()->RemoveObserver(this); @@ -59,32 +56,8 @@ void WorkspaceController::OnRootWindowResized(const gfx::Size& new_size) { void WorkspaceController::OnWindowPropertyChanged(aura::Window* window, const char* key, void* old) { - if (key == aura::client::kRootWindowActiveWindow) { - // FindBy handles NULL. - Workspace* workspace = workspace_manager_->FindBy(GetActiveWindow()); - if (workspace) - workspace->Activate(); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// WorkspaceController, ash::internal::WorkspaceObserver overrides: - -void WorkspaceController::WindowMoved(WorkspaceManager* manager, - aura::Window* source, - aura::Window* target) { - if (ignore_move_event_ || !launcher_model_) - return; - // TODO: there is no longer a 1-1 mapping between the launcher and windows; - // decide how we want to handle it. - NOTIMPLEMENTED(); -} - -void WorkspaceController::ActiveWorkspaceChanged(WorkspaceManager* manager, - Workspace* old) { - // TODO(oshima): Update Launcher and Status area state when the active - // workspace's fullscreen state changes. - //NOTIMPLEMENTED(); + if (key == aura::client::kRootWindowActiveWindow) + workspace_manager_->SetActiveWorkspaceByWindow(GetActiveWindow()); } //////////////////////////////////////////////////////////////////////////////// diff --git a/ash/wm/workspace_controller.h b/ash/wm/workspace_controller.h index 2d65e4238d716..9e541d984f457 100644 --- a/ash/wm/workspace_controller.h +++ b/ash/wm/workspace_controller.h @@ -7,7 +7,6 @@ #pragma once #include "ash/launcher/launcher_model_observer.h" -#include "ash/wm/workspace/workspace_observer.h" #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "ui/aura/root_window_observer.h" @@ -35,7 +34,6 @@ class WorkspaceManager; class ASH_EXPORT WorkspaceController : public aura::RootWindowObserver, public aura::WindowObserver, - public ash::internal::WorkspaceObserver, public ash::LauncherModelObserver { public: explicit WorkspaceController(aura::Window* workspace_viewport); @@ -58,13 +56,6 @@ class ASH_EXPORT WorkspaceController : const char* key, void* old) OVERRIDE; - // WorkspaceObserver overrides: - virtual void WindowMoved(WorkspaceManager* manager, - aura::Window* source, - aura::Window* target) OVERRIDE; - virtual void ActiveWorkspaceChanged(WorkspaceManager* manager, - Workspace* old) OVERRIDE; - // Invoked after an item has been added to the model. virtual void LauncherItemAdded(int index) OVERRIDE; virtual void LauncherItemRemoved(int index) OVERRIDE; diff --git a/ash/wm/workspace_controller_unittest.cc b/ash/wm/workspace_controller_unittest.cc index 5438a6ff715a7..5bd2ac46e70fc 100644 --- a/ash/wm/workspace_controller_unittest.cc +++ b/ash/wm/workspace_controller_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// 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. @@ -67,53 +67,5 @@ class WorkspaceControllerTest : public aura::test::AuraTestBase { DISALLOW_COPY_AND_ASSIGN(WorkspaceControllerTest); }; -TEST_F(WorkspaceControllerTest, Overview) { - workspace_manager()->SetWorkspaceSize(gfx::Size(500, 300)); - - // Creating two workspaces, ws1 which contains window w1, - // and ws2 which contains window w2. - Workspace* ws1 = workspace_manager()->CreateWorkspace(); - scoped_ptr<Window> w1(CreateTestWindow()); - EXPECT_TRUE(ws1->AddWindowAfter(w1.get(), NULL)); - - Workspace* ws2 = workspace_manager()->CreateWorkspace(); - scoped_ptr<Window> w2(CreateTestWindow()); - EXPECT_TRUE(ws2->AddWindowAfter(w2.get(), NULL)); - - // Activating a window switches the active workspace. - ash::ActivateWindow(w2.get()); - EXPECT_EQ(ws2, workspace_manager()->GetActiveWorkspace()); - - // The size of contents_view() is now ws1(500) + ws2(500) + margin(50). - EXPECT_EQ("0,0 1050x300", contents_view()->bounds().ToString()); - EXPECT_FALSE(workspace_manager()->is_overview()); - workspace_manager()->SetOverview(true); - EXPECT_TRUE(workspace_manager()->is_overview()); - - // Switching overview mode doesn't change the active workspace. - EXPECT_EQ(ws2, workspace_manager()->GetActiveWorkspace()); - - // Activating window w1 switches the active window and - // the mode back to normal mode. - ash::ActivateWindow(w1.get()); - EXPECT_EQ(ws1, workspace_manager()->GetActiveWorkspace()); - EXPECT_FALSE(workspace_manager()->is_overview()); - - // Deleting w1 without StackingClient resets the active workspace - ws1->RemoveWindow(w1.get()); - delete ws1; - w1.reset(); - EXPECT_EQ(ws2, workspace_manager()->GetActiveWorkspace()); - EXPECT_EQ("0,0 500x300", contents_view()->bounds().ToString()); - ws2->RemoveWindow(w2.get()); - delete ws2; - // The size of contents_view() for no workspace case must be - // same as one contents_view() case. - EXPECT_EQ("0,0 500x300", contents_view()->bounds().ToString()); - - // Reset now before windows are destroyed. - controller_.reset(); -} - } // namespace internal } // namespace ash diff --git a/chrome/browser/ui/views/aura/launcher_icon_updater.cc b/chrome/browser/ui/views/aura/launcher_icon_updater.cc index 94e8cd4f3e0d2..a79c5e59fe4c7 100644 --- a/chrome/browser/ui/views/aura/launcher_icon_updater.cc +++ b/chrome/browser/ui/views/aura/launcher_icon_updater.cc @@ -112,6 +112,7 @@ void LauncherIconUpdater::ActivateByID(ash::LauncherID id) { NOTREACHED(); return; } + updater->window_->Show(); ash::ActivateWindow(updater->window_); if (tab) { updater->tab_model_->ActivateTabAt(