diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-30 15:16:57 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-30 15:16:57 +0000 |
commit | b5f3060dafc8514114fadff3011a3921a0f29bc6 (patch) | |
tree | 6be43c40161f7dd0ce58062b0f8ced76c7536a9c /ash | |
parent | ada425fc5d779300bd32c38db7ad571ba3855288 (diff) | |
download | chromium_src-b5f3060dafc8514114fadff3011a3921a0f29bc6.zip chromium_src-b5f3060dafc8514114fadff3011a3921a0f29bc6.tar.gz chromium_src-b5f3060dafc8514114fadff3011a3921a0f29bc6.tar.bz2 |
Handful of workspace changes:
. Correctly deal with fullscreen.
. wires up grid size and sets it appropriately.
. maxes windows open initially maximized if necessary.
I've also wired up showing a menu when you right click on the desktop
that lets you configure things. This is only temporary, but I figure
worthwhile to play with various settings.
BUG=111285
TEST=none
R=ben@chromium.org
Review URL: https://chromiumcodereview.appspot.com/9295024
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@119674 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ash')
-rw-r--r-- | ash/desktop_background/desktop_background_view.cc | 5 | ||||
-rw-r--r-- | ash/shell.cc | 11 | ||||
-rw-r--r-- | ash/shell.h | 5 | ||||
-rw-r--r-- | ash/wm/property_util.h | 12 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_event_filter.h | 3 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_layout_manager.cc | 34 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_layout_manager.h | 4 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_manager.cc | 111 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_manager.h | 50 | ||||
-rw-r--r-- | ash/wm/workspace/workspace_manager_unittest.cc | 117 | ||||
-rw-r--r-- | ash/wm/workspace_controller.cc | 117 | ||||
-rw-r--r-- | ash/wm/workspace_controller.h | 61 |
12 files changed, 408 insertions, 122 deletions
diff --git a/ash/desktop_background/desktop_background_view.cc b/ash/desktop_background/desktop_background_view.cc index 27401cf..1db7390 100644 --- a/ash/desktop_background/desktop_background_view.cc +++ b/ash/desktop_background/desktop_background_view.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. @@ -44,7 +44,8 @@ bool DesktopBackgroundView::OnMousePressed(const views::MouseEvent& event) { } void DesktopBackgroundView::OnMouseReleased(const views::MouseEvent& event) { - Shell::GetInstance()->ToggleOverview(); + if (event.IsRightMouseButton()) + Shell::GetInstance()->ShowBackgroundMenu(GetWidget(), event.location()); } views::Widget* CreateDesktopBackground() { diff --git a/ash/shell.cc b/ash/shell.cc index b889e0b..1d56dae 100644 --- a/ash/shell.cc +++ b/ash/shell.cc @@ -372,9 +372,10 @@ size_t Shell::GetRootWindowEventFilterCount() const { aura::RootWindow::GetInstance()->event_filter())->GetFilterCount(); } -void Shell::ToggleOverview() { +void Shell::ShowBackgroundMenu(views::Widget* widget, + const gfx::Point& location) { if (workspace_controller_.get()) - workspace_controller_->ToggleOverview(); + workspace_controller_->ShowMenu(widget, location); } void Shell::ToggleAppList() { @@ -486,12 +487,6 @@ void Shell::SetupNormalWindowMode() { // Workspace manager has its own layout managers. workspace_controller_.reset( new internal::WorkspaceController(default_container)); - workspace_controller_->SetLauncherModel(launcher_->model()); - default_container->SetEventFilter( - new internal::WorkspaceEventFilter(default_container)); - default_container->SetLayoutManager( - new internal::WorkspaceLayoutManager( - workspace_controller_->workspace_manager())); } else { // Default layout manager. internal::ToplevelLayoutManager* toplevel_layout_manager = diff --git a/ash/shell.h b/ash/shell.h index a1dcb76..ab4da7b 100644 --- a/ash/shell.h +++ b/ash/shell.h @@ -24,6 +24,7 @@ class RootWindow; class Window; } namespace gfx { +class Point; class Rect; class Size; } @@ -88,8 +89,8 @@ class ASH_EXPORT Shell { void RemoveRootWindowEventFilter(aura::EventFilter* filter); size_t GetRootWindowEventFilterCount() const; - // Toggles between overview mode and normal mode. - void ToggleOverview(); + // Shows the background menu over |widget|. + void ShowBackgroundMenu(views::Widget* widget, const gfx::Point& location); // Toggles app list. void ToggleAppList(); diff --git a/ash/wm/property_util.h b/ash/wm/property_util.h index 916a24a..5eec8ba 100644 --- a/ash/wm/property_util.h +++ b/ash/wm/property_util.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. @@ -6,6 +6,8 @@ #define ASH_WM_PROPERTY_UTIL_H_ #pragma once +#include "ash/ash_export.h" + namespace aura { class Window; } @@ -18,19 +20,19 @@ namespace ash { // Sets the restore bounds property on |window|. Deletes existing bounds value // if exists. -void SetRestoreBounds(aura::Window* window, const gfx::Rect& bounds); +ASH_EXPORT void SetRestoreBounds(aura::Window* window, const gfx::Rect& bounds); // Same as SetRestoreBounds(), but does nothing if the restore bounds have // already been set. The bounds used are the bounds of the window. -void SetRestoreBoundsIfNotSet(aura::Window* window); +ASH_EXPORT void SetRestoreBoundsIfNotSet(aura::Window* window); // Returns the restore bounds property on |window|. NULL if the // restore bounds property does not exist for |window|. |window| // owns the bounds object. -const gfx::Rect* GetRestoreBounds(aura::Window* window); +ASH_EXPORT const gfx::Rect* GetRestoreBounds(aura::Window* window); // Deletes and clears the restore bounds property on |window|. -void ClearRestoreBounds(aura::Window* window); +ASH_EXPORT void ClearRestoreBounds(aura::Window* window); } diff --git a/ash/wm/workspace/workspace_event_filter.h b/ash/wm/workspace/workspace_event_filter.h index 6224c10..c38beaf 100644 --- a/ash/wm/workspace/workspace_event_filter.h +++ b/ash/wm/workspace/workspace_event_filter.h @@ -40,7 +40,7 @@ class WorkspaceEventFilter : public ToplevelWindowEventFilter, // If the mouse is currently over a portion of the window that should // trigger a drag or resize, drag_state_ is set appropriately and true // is returned. If the mouse is not over a portion of the window that should - // trigger a more or resize, drag_state_ is not updated and false is returend. + // trigger a more or resize, drag_state_ is not updated and false is returned. bool UpdateDragState(); // Updates the top-level window under the mouse so that we can change @@ -48,6 +48,7 @@ class WorkspaceEventFilter : public ToplevelWindowEventFilter, void UpdateHoveredWindow(aura::Window* toplevel); DragState drag_state_; + // Top-level window under the mouse cursor. aura::Window* hovered_window_; diff --git a/ash/wm/workspace/workspace_layout_manager.cc b/ash/wm/workspace/workspace_layout_manager.cc index 99409e7..e876d96 100644 --- a/ash/wm/workspace/workspace_layout_manager.cc +++ b/ash/wm/workspace/workspace_layout_manager.cc @@ -34,13 +34,11 @@ WorkspaceLayoutManager::~WorkspaceLayoutManager() {} void WorkspaceLayoutManager::PrepareForMoveOrResize( aura::Window* drag, aura::MouseEvent* event) { - workspace_manager_->set_ignored_window(drag); } void WorkspaceLayoutManager::CancelMoveOrResize( aura::Window* drag, aura::MouseEvent* event) { - workspace_manager_->set_ignored_window(NULL); } void WorkspaceLayoutManager::ProcessMove( @@ -54,14 +52,12 @@ void WorkspaceLayoutManager::EndMove( aura::Window* drag, aura::MouseEvent* evnet) { // TODO: see comment in ProcessMove. - workspace_manager_->set_ignored_window(NULL); } void WorkspaceLayoutManager::EndResize( aura::Window* drag, aura::MouseEvent* evnet) { // TODO: see comment in ProcessMove. - workspace_manager_->set_ignored_window(NULL); } //////////////////////////////////////////////////////////////////////////////// @@ -75,8 +71,23 @@ void WorkspaceLayoutManager::OnWindowAddedToLayout(aura::Window* child) { if (!workspace_manager_->IsManagedWindow(child)) return; - if (child->IsVisible()) + if (child->IsVisible()) { workspace_manager_->AddWindow(child); + } else if (window_util::IsWindowMaximized(child) || + workspace_manager_->ShouldMaximize(child)) { + if (!GetRestoreBounds(child)) + SetRestoreBounds(child, child->GetTargetBounds()); + if (!window_util::IsWindowMaximized(child)) + window_util::MaximizeWindow(child); + SetChildBoundsDirect(child, + gfx::Screen::GetMonitorWorkAreaNearestWindow(child)); + } else if (window_util::IsWindowFullscreen(child)) { + SetChildBoundsDirect(child, + gfx::Screen::GetMonitorAreaNearestWindow(child)); + } else { + SetChildBoundsDirect( + child, workspace_manager_->AlignBoundsToGrid(child->GetTargetBounds())); + } } void WorkspaceLayoutManager::OnWillRemoveWindowFromLayout( @@ -99,14 +110,17 @@ void WorkspaceLayoutManager::OnChildWindowVisibilityChanged( void WorkspaceLayoutManager::SetChildBounds( aura::Window* child, const gfx::Rect& requested_bounds) { - // 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))) { + !workspace_manager_->IsManagedWindow(child)) { + // Allow requests for |ignored_window_| or unmanaged windows. SetChildBoundsDirect(child, requested_bounds); + } else if (!window_util::IsWindowMaximized(child) && + !window_util::IsWindowFullscreen(child)) { + // Align normal windows to the grid. + SetChildBoundsDirect( + child, workspace_manager_->AlignBoundsToGrid(requested_bounds)); } + // All other requests we ignore. } } // namespace internal diff --git a/ash/wm/workspace/workspace_layout_manager.h b/ash/wm/workspace/workspace_layout_manager.h index 4a1ca87..7be5143 100644 --- a/ash/wm/workspace/workspace_layout_manager.h +++ b/ash/wm/workspace/workspace_layout_manager.h @@ -46,10 +46,10 @@ class ASH_EXPORT WorkspaceLayoutManager : public aura::LayoutManager { void ProcessMove(aura::Window* window, aura::MouseEvent* event); // Invoked when a user finished moving window. - void EndMove(aura::Window* drag, aura::MouseEvent* evnet); + void EndMove(aura::Window* drag, aura::MouseEvent* event); // Invoked when a user finished resizing window. - void EndResize(aura::Window* drag, aura::MouseEvent* evnet); + void EndResize(aura::Window* drag, aura::MouseEvent* event); // Overridden from aura::LayoutManager: virtual void OnWindowResized() OVERRIDE; diff --git a/ash/wm/workspace/workspace_manager.cc b/ash/wm/workspace/workspace_manager.cc index 9fc41e9..78cd418 100644 --- a/ash/wm/workspace/workspace_manager.cc +++ b/ash/wm/workspace/workspace_manager.cc @@ -46,6 +46,24 @@ void SetWindowLayerVisibility(const std::vector<aura::Window*>& windows, } } +// TODO(sky): this is a copy of that in ToplevelWindowEventFilter. Figure out +// the right place to put it. +int AlignToGrid(int location, int grid_size) { + if (grid_size <= 1 || location % grid_size == 0) + return location; + return floor(static_cast<float>(location) / static_cast<float>(grid_size) + + .5f) * grid_size; +} + +gfx::Rect AlignRectToGrid(const gfx::Rect& rect, int grid_size) { + if (grid_size <= 1) + return rect; + return gfx::Rect(AlignToGrid(rect.x(), grid_size), + AlignToGrid(rect.y(), grid_size), + AlignToGrid(rect.width(), grid_size), + AlignToGrid(rect.height(), grid_size)); +} + } namespace ash { @@ -54,13 +72,18 @@ namespace internal { //////////////////////////////////////////////////////////////////////////////// // WindowManager, public: +// static +const int WorkspaceManager::kOpenMaximizedThreshold = 1600; + WorkspaceManager::WorkspaceManager(aura::Window* contents_view) : contents_view_(contents_view), active_workspace_(NULL), workspace_size_( gfx::Screen::GetMonitorAreaNearestWindow(contents_view_).size()), is_overview_(false), - ignored_window_(NULL) { + ignored_window_(NULL), + grid_size_(0), + open_new_windows_maximized_(true) { DCHECK(contents_view); } @@ -79,27 +102,39 @@ bool WorkspaceManager::IsManagedWindow(aura::Window* window) const { !window->transient_parent(); } +bool WorkspaceManager::ShouldMaximize(aura::Window* window) const { + return !window->GetProperty(aura::client::kShowStateKey) && + open_new_windows_maximized_ && + contents_view_->bounds().width() < kOpenMaximizedThreshold; +} + 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); + if (ShouldMaximize(window)) { + window->SetIntProperty(aura::client::kShowStateKey, + ui::SHOW_STATE_MAXIMIZED); + } else { + 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()); + if (window_util::IsWindowMaximized(window) || + window_util::IsWindowFullscreen(window)) { + SetFullScreenOrMaximizedBounds(window); + } else { + if (grid_size_ > 1) + SetWindowBounds(window, AlignBoundsToGrid(window->GetTargetBounds())); } + // Add the observer after we change the state in anyway. + window->AddObserver(this); + Workspace* workspace = NULL; Workspace::Type type_for_window = Workspace::TypeForWindow(window); switch (type_for_window) { @@ -178,6 +213,12 @@ void WorkspaceManager::SetWorkspaceSize(const gfx::Size& workspace_size) { } } +gfx::Rect WorkspaceManager::AlignBoundsToGrid(const gfx::Rect& bounds) { + if (grid_size_ <= 1) + return bounds; + return AlignRectToGrid(bounds, grid_size_); +} + void WorkspaceManager::OnWindowPropertyChanged(aura::Window* window, const char* name, void* old) { @@ -186,14 +227,18 @@ void WorkspaceManager::OnWindowPropertyChanged(aura::Window* window, 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); + DCHECK(FindBy(window)); + + Workspace::Type old_type = FindBy(window)->type(); + Workspace::Type new_type = Workspace::TypeForWindow(window); + if (new_type != old_type) { + OnTypeOfWorkspacedNeededChanged(window); + } else if (new_type == Workspace::TYPE_MAXIMIZED) { + // Even though the type didn't change, the window may have gone from + // maximized to fullscreen. Adjust the bounds appropriately. + SetFullScreenOrMaximizedBounds(window); + } } //////////////////////////////////////////////////////////////////////////////// @@ -272,8 +317,6 @@ int WorkspaceManager::GetWorkspaceIndexContaining(aura::Window* window) const { 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; @@ -283,25 +326,33 @@ void WorkspaceManager::SetWindowBoundsFromRestoreBounds(aura::Window* window) { Workspace* workspace = FindBy(window); DCHECK(workspace); const gfx::Rect* restore = GetRestoreBounds(window); + gfx::Rect bounds; if (restore) { - SetWindowBounds(window, - restore->AdjustToFit(workspace->GetWorkAreaBounds())); + bounds = restore->AdjustToFit(workspace->GetWorkAreaBounds()); } else { - SetWindowBounds(window, window->bounds().AdjustToFit( - workspace->GetWorkAreaBounds())); + bounds = window->bounds().AdjustToFit(workspace->GetWorkAreaBounds()); } + SetWindowBounds(window, AlignRectToGrid(bounds, grid_size_)); ash::ClearRestoreBounds(window); } -void WorkspaceManager::MaximizedStateChanged(aura::Window* window) { +void WorkspaceManager::SetFullScreenOrMaximizedBounds(aura::Window* window) { + if (!GetRestoreBounds(window)) + SetRestoreBounds(window, window->GetTargetBounds()); + if (window_util::IsWindowMaximized(window)) + SetWindowBounds(window, GetWorkAreaBounds()); + else if (window_util::IsWindowFullscreen(window)) + SetWindowBounds(window, gfx::Screen::GetMonitorAreaNearestWindow(window)); +} + +void WorkspaceManager::OnTypeOfWorkspacedNeededChanged(aura::Window* window) { + // TODO: needs to handle transitioning to split. DCHECK(IsManagedWindow(window)); - bool is_maximized = window_util::IsWindowMaximized(window); Workspace* current_workspace = FindBy(window); DCHECK(current_workspace); - if (is_maximized) { + if (Workspace::TypeForWindow(window) == Workspace::TYPE_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); @@ -311,7 +362,7 @@ void WorkspaceManager::MaximizedStateChanged(aura::Window* window) { } else { current_workspace->SetType(Workspace::TYPE_MAXIMIZED); } - SetWindowBounds(window, GetWorkAreaBounds()); + SetFullScreenOrMaximizedBounds(window); } else { // Maximized -> unmaximized; move window to unmaximized workspace (or reuse // current if there isn't one). @@ -326,10 +377,8 @@ void WorkspaceManager::MaximizedStateChanged(aura::Window* window) { } else { current_workspace->SetType(Workspace::TYPE_NORMAL); } - SetWindowBoundsFromRestoreBounds(window); } - SetActiveWorkspace(current_workspace); } diff --git a/ash/wm/workspace/workspace_manager.h b/ash/wm/workspace/workspace_manager.h index 055893e..b081cb2 100644 --- a/ash/wm/workspace/workspace_manager.h +++ b/ash/wm/workspace/workspace_manager.h @@ -31,12 +31,19 @@ class WorkspaceManagerTest; // WorkspaceManager manages multiple workspaces in the desktop. class ASH_EXPORT WorkspaceManager : public aura::WindowObserver{ public: + // If open_new_windows_maximized() is true and the size of the viewport is + // smaller than this value, newly created windows are forced maximized. + static const int kOpenMaximizedThreshold; + explicit WorkspaceManager(aura::Window* viewport); virtual ~WorkspaceManager(); // Returns true if |window| should be managed by the WorkspaceManager. bool IsManagedWindow(aura::Window* window) const; + // Returns true if |window| should be maximized. + bool ShouldMaximize(aura::Window* window) const; + // Adds/removes a window creating/destroying workspace as necessary. void AddWindow(aura::Window* window); void RemoveWindow(aura::Window* window); @@ -62,13 +69,28 @@ class ASH_EXPORT WorkspaceManager : public aura::WindowObserver{ // Sets the size of a single workspace (all workspaces have the same size). void SetWorkspaceSize(const gfx::Size& workspace_size); - // Sets/Returns the ignored window that the workspace manager does not - // set bounds on. - void set_ignored_window(aura::Window* ignored_window) { - ignored_window_ = ignored_window; - } + // Returns the window the layout manager should allow the size to be set for. + // TODO: maybe this should be set on WorkspaceLayoutManager. aura::Window* ignored_window() { return ignored_window_; } + // Sets whether newly added windows open maximized. This is only applicable if + // the size of the root window is less than kOpenMaximizedThreshold. Default + // is true. + void set_open_new_windows_maximized(bool value) { + open_new_windows_maximized_ = value; + } + bool open_new_windows_maximized() const { + return open_new_windows_maximized_; + } + + // Sets the size of the grid. Newly added windows are forced to align to the + // size of the grid. + void set_grid_size(int size) { grid_size_ = size; } + int grid_size() const { return grid_size_; } + + // Returns a bounds aligned to the grid. Returns |bounds| if grid_size is 0. + gfx::Rect AlignBoundsToGrid(const gfx::Rect& bounds); + // Overriden from aura::WindowObserver: virtual void OnWindowPropertyChanged(aura::Window* window, const char* name, @@ -78,6 +100,11 @@ class ASH_EXPORT WorkspaceManager : public aura::WindowObserver{ friend class Workspace; friend class WorkspaceManagerTest; + // See description above getter. + void set_ignored_window(aura::Window* ignored_window) { + ignored_window_ = ignored_window; + } + void AddWorkspace(Workspace* workspace); void RemoveWorkspace(Workspace* workspace); @@ -104,8 +131,11 @@ class ASH_EXPORT WorkspaceManager : public aura::WindowObserver{ // 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); + // Reset the bounds of |window|. Use when |window| is fullscreen or maximized. + void SetFullScreenOrMaximizedBounds(aura::Window* window); + + // Invoked when the type of workspace needed for |window| changes. + void OnTypeOfWorkspacedNeededChanged(aura::Window* window); // Returns the Workspace whose type is TYPE_NORMAL, or NULL if there isn't // one. @@ -127,6 +157,12 @@ class ASH_EXPORT WorkspaceManager : public aura::WindowObserver{ // The window that WorkspaceManager does not set the bounds on. aura::Window* ignored_window_; + // See description above setter. + int grid_size_; + + // See description above setter. + bool open_new_windows_maximized_; + DISALLOW_COPY_AND_ASSIGN(WorkspaceManager); }; diff --git a/ash/wm/workspace/workspace_manager_unittest.cc b/ash/wm/workspace/workspace_manager_unittest.cc index 623ffe4..2c736cc 100644 --- a/ash/wm/workspace/workspace_manager_unittest.cc +++ b/ash/wm/workspace/workspace_manager_unittest.cc @@ -6,6 +6,8 @@ #include "ash/shell_window_ids.h" #include "ash/wm/activation_controller.h" +#include "ash/wm/property_util.h" +#include "ash/wm/window_util.h" #include "ash/wm/workspace/workspace.h" #include "ash/wm/workspace/workspace_layout_manager.h" #include "ui/aura/client/aura_constants.h" @@ -14,6 +16,7 @@ #include "ui/aura/test/aura_test_base.h" #include "ui/aura/window.h" #include "ui/base/ui_base_types.h" +#include "ui/gfx/screen.h" using aura::Window; @@ -43,8 +46,17 @@ class WorkspaceManagerTest : public aura::test::AuraTestBase { aura::test::AuraTestBase::TearDown(); } + aura::Window* CreateTestWindowUnparented() { + aura::Window* window = new aura::Window(NULL); + window->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); + window->SetType(aura::client::WINDOW_TYPE_NORMAL); + window->Init(ui::Layer::LAYER_TEXTURED); + return window; + } + aura::Window* CreateTestWindow() { aura::Window* window = new aura::Window(NULL); + window->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_NORMAL); window->SetType(aura::client::WINDOW_TYPE_NORMAL); window->Init(ui::Layer::LAYER_TEXTURED); window->SetParent(viewport()); @@ -63,6 +75,10 @@ class WorkspaceManagerTest : public aura::test::AuraTestBase { return manager_->GetWorkAreaBounds(); } + gfx::Rect GetFullscreenBounds(aura::Window* window) { + return gfx::Screen::GetMonitorAreaNearestWindow(window); + } + Workspace* active_workspace() { return manager_->active_workspace_; } @@ -284,5 +300,106 @@ TEST_F(WorkspaceManagerTest, ChangeBoundsOfNormalWindow) { EXPECT_EQ(500, w1->bounds().height()); } +// Assertions around open new windows maximized. +TEST_F(WorkspaceManagerTest, OpenNewWindowsMaximized) { + scoped_ptr<Window> w1(CreateTestWindowUnparented()); + + // Default is true for open new windows maximized. + EXPECT_TRUE(manager_->open_new_windows_maximized()); + // SHOW_STATE_DEFAULT should end up maximized. + w1->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT); + w1->SetBounds(gfx::Rect(50, 51, 52, 53)); + w1->SetParent(viewport()); + // Maximized state and bounds should be set as soon as w1 is added to the + // parent. + EXPECT_TRUE(window_util::IsWindowMaximized(w1.get())); + EXPECT_EQ(gfx::Screen::GetMonitorWorkAreaNearestWindow(w1.get()), + w1->bounds()); + w1->Show(); + EXPECT_TRUE(window_util::IsWindowMaximized(w1.get())); + EXPECT_EQ(gfx::Screen::GetMonitorWorkAreaNearestWindow(w1.get()), + w1->bounds()); + // Restored bounds should be saved. + ASSERT_TRUE(GetRestoreBounds(w1.get())); + EXPECT_EQ(gfx::Rect(50, 51, 52, 53), *GetRestoreBounds(w1.get())); + + // Show state normal should end up normal. + scoped_ptr<Window> w2(CreateTestWindow()); + w2->SetBounds(gfx::Rect(60, 61, 62, 63)); + w2->Show(); + EXPECT_EQ(gfx::Rect(60, 61, 62, 63), w2->bounds()); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, + w2->GetIntProperty(aura::client::kShowStateKey)); + + // If open news windows maximized is false, SHOW_STATE_DEFAULT should end as + // SHOW_STATE_NORMAL. + manager_->set_open_new_windows_maximized(false); + scoped_ptr<Window> w3(CreateTestWindowUnparented()); + // Show state default should end up normal when open new windows maximized is + // false. + w3->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_DEFAULT); + w3->SetBounds(gfx::Rect(70, 71, 72, 73)); + w3->SetParent(viewport()); + w3->Show(); + EXPECT_EQ(gfx::Rect(70, 71, 72, 73), w3->bounds()); + EXPECT_EQ(ui::SHOW_STATE_NORMAL, + w3->GetIntProperty(aura::client::kShowStateKey)); +} + +// Assertions around grid size. +TEST_F(WorkspaceManagerTest, SnapToGrid) { + manager_->set_grid_size(8); + + // Verify snap to grid when bounds are set before parented. + scoped_ptr<Window> w1(CreateTestWindowUnparented()); + w1->SetBounds(gfx::Rect(1, 6, 25, 30)); + w1->SetParent(viewport()); + EXPECT_EQ(gfx::Rect(0, 8, 24, 32), w1->bounds()); + + // Verify snap to grid when bounds are set after parented. + w1.reset(CreateTestWindow()); + w1->SetBounds(gfx::Rect(1, 6, 25, 30)); + EXPECT_EQ(gfx::Rect(0, 8, 24, 32), w1->bounds()); +} + +// Assertions around a fullscreen window. +TEST_F(WorkspaceManagerTest, SingleFullscreenWindow) { + scoped_ptr<Window> w1(CreateTestWindow()); + w1->SetBounds(gfx::Rect(0, 0, 250, 251)); + // Make the window fullscreen. + w1->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); + w1->Show(); + + // 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(GetFullscreenBounds(w1.get()).width(), w1->bounds().width()); + EXPECT_EQ(GetFullscreenBounds(w1.get()).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()); + + // Back to fullscreen. + w1->SetIntProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN); + 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(GetFullscreenBounds(w1.get()).width(), w1->bounds().width()); + EXPECT_EQ(GetFullscreenBounds(w1.get()).height(), w1->bounds().height()); + ASSERT_TRUE(GetRestoreBounds(w1.get())); + EXPECT_EQ(gfx::Rect(0, 0, 250, 251), *GetRestoreBounds(w1.get())); +} + } // namespace internal } // namespace ash diff --git a/ash/wm/workspace_controller.cc b/ash/wm/workspace_controller.cc index 8f9d7cf..b91cdeb 100644 --- a/ash/wm/workspace_controller.cc +++ b/ash/wm/workspace_controller.cc @@ -4,31 +4,45 @@ #include "ash/wm/workspace_controller.h" -#include "ash/launcher/launcher.h" -#include "ash/launcher/launcher_model.h" #include "ash/shell.h" #include "ash/wm/window_util.h" +#include "ash/wm/workspace/workspace_event_filter.h" #include "ash/wm/workspace/workspace_layout_manager.h" #include "ash/wm/workspace/workspace_manager.h" +#include "base/utf_string_conversions.h" #include "ui/aura/client/activation_client.h" #include "ui/aura/client/aura_constants.h" #include "ui/aura/root_window.h" #include "ui/aura/window.h" +#include "ui/views/controls/menu/menu_model_adapter.h" +#include "ui/views/controls/menu/menu_runner.h" namespace ash { namespace internal { +namespace { + +// Size of the grid when a grid is enabled. +const int kGridSize = 8; + +} // namespace + WorkspaceController::WorkspaceController(aura::Window* viewport) - : workspace_manager_(new WorkspaceManager(viewport)), - launcher_model_(NULL), - ignore_move_event_(false) { + : viewport_(viewport), + workspace_manager_(new WorkspaceManager(viewport)), + layout_manager_(NULL), + event_filter_(NULL) { + event_filter_ = new WorkspaceEventFilter(viewport); + viewport->SetEventFilter(event_filter_); + layout_manager_ = new WorkspaceLayoutManager(workspace_manager_.get()); + viewport->SetLayoutManager(layout_manager_); aura::RootWindow::GetInstance()->AddRootWindowObserver(this); aura::RootWindow::GetInstance()->AddObserver(this); + workspace_manager_->set_grid_size(kGridSize); + event_filter_->set_grid_size(kGridSize); } WorkspaceController::~WorkspaceController() { - if (launcher_model_) - launcher_model_->RemoveObserver(this); aura::RootWindow::GetInstance()->RemoveObserver(this); aura::RootWindow::GetInstance()->RemoveRootWindowObserver(this); } @@ -37,22 +51,28 @@ void WorkspaceController::ToggleOverview() { workspace_manager_->SetOverview(!workspace_manager_->is_overview()); } -void WorkspaceController::SetLauncherModel(LauncherModel* launcher_model) { - DCHECK(!launcher_model_); - launcher_model_ = launcher_model; - launcher_model_->AddObserver(this); +void WorkspaceController::ShowMenu(views::Widget* widget, + const gfx::Point& location) { + ui::SimpleMenuModel menu_model(this); + // This is just for testing and will be ripped out before we ship, so none of + // the strings are localized. + menu_model.AddCheckItem(MENU_SNAP_TO_GRID, + ASCIIToUTF16("Snap to grid")); + menu_model.AddCheckItem(MENU_OPEN_MAXIMIZED, + ASCIIToUTF16("Maximize new windows")); + views::MenuModelAdapter menu_model_adapter(&menu_model); + menu_runner_.reset(new views::MenuRunner(menu_model_adapter.CreateMenu())); + if (menu_runner_->RunMenuAt( + widget, NULL, gfx::Rect(location, gfx::Size()), + views::MenuItemView::TOPRIGHT, views::MenuRunner::HAS_MNEMONICS) == + views::MenuRunner::MENU_DELETED) + return; } -//////////////////////////////////////////////////////////////////////////////// -// WorkspaceController, aura::RootWindowObserver overrides: - void WorkspaceController::OnRootWindowResized(const gfx::Size& new_size) { workspace_manager_->SetWorkspaceSize(new_size); } -//////////////////////////////////////////////////////////////////////////////// -// WorkspaceController, aura::WindowObserver overrides: - void WorkspaceController::OnWindowPropertyChanged(aura::Window* window, const char* key, void* old) { @@ -60,27 +80,62 @@ void WorkspaceController::OnWindowPropertyChanged(aura::Window* window, workspace_manager_->SetActiveWorkspaceByWindow(GetActiveWindow()); } -//////////////////////////////////////////////////////////////////////////////// -// WorkspaceController, ash::LauncherModelObserver overrides: +bool WorkspaceController::IsCommandIdChecked(int command_id) const { + switch (static_cast<MenuItem>(command_id)) { + case MENU_SNAP_TO_GRID: + return workspace_manager_->grid_size() != 0; + + case MENU_OPEN_MAXIMIZED: + return workspace_manager_->open_new_windows_maximized(); -void WorkspaceController::LauncherItemAdded(int index) { + default: + break; + } + return false; } -void WorkspaceController::LauncherItemRemoved(int index) { +bool WorkspaceController::IsCommandIdEnabled(int command_id) const { + switch (static_cast<MenuItem>(command_id)) { + case MENU_OPEN_MAXIMIZED: + return workspace_manager_->contents_view()->bounds().width() < + WorkspaceManager::kOpenMaximizedThreshold; + + default: + return true; + } + return true; } -void WorkspaceController::LauncherItemMoved(int start_index, int target_index) { - if (ignore_move_event_) - return; - // TODO: there is no longer a 1-1 mapping between the launcher and windows; - // decide how we want to handle it. - DCHECK(launcher_model_); - NOTIMPLEMENTED(); +void WorkspaceController::ExecuteCommand(int command_id) { + switch (static_cast<MenuItem>(command_id)) { + case MENU_SNAP_TO_GRID: { + int size = workspace_manager_->grid_size() == 0 ? kGridSize : 0; + workspace_manager_->set_grid_size(size); + event_filter_->set_grid_size(size); + if (!size) + return; + for (size_t i = 0; i < viewport_->children().size(); ++i) { + aura::Window* window = viewport_->children()[i]; + if (!window_util::IsWindowMaximized(window) && + !window_util::IsWindowFullscreen(window)) { + window->SetBounds(workspace_manager_->AlignBoundsToGrid( + window->GetTargetBounds())); + } + } + break; + } + + case MENU_OPEN_MAXIMIZED: + workspace_manager_->set_open_new_windows_maximized( + !workspace_manager_->open_new_windows_maximized()); + break; + } } -void WorkspaceController::LauncherItemChanged( - int index, - const ash::LauncherItem& old_item) { +bool WorkspaceController::GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) { + return false; } } // namespace internal diff --git a/ash/wm/workspace_controller.h b/ash/wm/workspace_controller.h index 9e541d9..b582898 100644 --- a/ash/wm/workspace_controller.h +++ b/ash/wm/workspace_controller.h @@ -6,48 +6,55 @@ #define ASH_WM_WORKSPACE_CONTROLLER_H_ #pragma once -#include "ash/launcher/launcher_model_observer.h" +#include "ash/ash_export.h" #include "base/basictypes.h" #include "base/memory/scoped_ptr.h" #include "ui/aura/root_window_observer.h" #include "ui/aura/window_observer.h" -#include "ash/ash_export.h" +#include "ui/base/models/simple_menu_model.h" namespace aura { class Window; } namespace gfx { +class Point; class Size; } -namespace ash { -class LauncherModel; +namespace views { +class MenuRunner; +class Widget; +} +namespace ash { namespace internal { +class WorkspaceEventFilter; +class WorkspaceLayoutManager; class WorkspaceManager; -// WorkspaceController owns a WorkspaceManager. WorkspaceController bridges -// events from RootWindowObserver translating them to WorkspaceManager, and -// a move event between Launcher and Workspace. +// WorkspaceController acts as a central place that ties together all the +// various workspace pieces: WorkspaceManager, WorkspaceLayoutManager and +// WorkspaceEventFilter. class ASH_EXPORT WorkspaceController : public aura::RootWindowObserver, public aura::WindowObserver, - public ash::LauncherModelObserver { + public ui::SimpleMenuModel::Delegate { public: - explicit WorkspaceController(aura::Window* workspace_viewport); + explicit WorkspaceController(aura::Window* viewport); virtual ~WorkspaceController(); void ToggleOverview(); - void SetLauncherModel(LauncherModel* launcher_model); - // Returns the workspace manager that this controler owns. WorkspaceManager* workspace_manager() { return workspace_manager_.get(); } + // Shows the menu allowing you to configure various aspects of workspaces. + void ShowMenu(views::Widget* widget, const gfx::Point& location); + // aura::RootWindowObserver overrides: virtual void OnRootWindowResized(const gfx::Size& new_size) OVERRIDE; @@ -56,23 +63,31 @@ class ASH_EXPORT WorkspaceController : const char* key, void* old) OVERRIDE; - // Invoked after an item has been added to the model. - virtual void LauncherItemAdded(int index) OVERRIDE; - virtual void LauncherItemRemoved(int index) OVERRIDE; - virtual void LauncherItemMoved(int start_index, int target_index) OVERRIDE; - virtual void LauncherItemChanged(int index, - const ash::LauncherItem& old_item) OVERRIDE; - virtual void LauncherItemWillChange(int index) OVERRIDE {} + // ui::SimpleMenuModel::Delegate overrides: + virtual bool IsCommandIdChecked(int command_id) const OVERRIDE; + virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE; + virtual void ExecuteCommand(int command_id) OVERRIDE; + virtual bool GetAcceleratorForCommandId( + int command_id, + ui::Accelerator* accelerator) OVERRIDE; private: + enum MenuItem { + MENU_SNAP_TO_GRID, + MENU_OPEN_MAXIMIZED, + }; + + aura::Window* viewport_; + scoped_ptr<WorkspaceManager> workspace_manager_; - // Owned by Launcher. - LauncherModel* launcher_model_; + // Owned by the window its attached to. + WorkspaceLayoutManager* layout_manager_; + + // Owned the window set on. + WorkspaceEventFilter* event_filter_; - // True while the controller is moving window either on workspace or launcher. - // Used to prevent infinite recursive call between the workspace and launcher. - bool ignore_move_event_; + scoped_ptr<views::MenuRunner> menu_runner_; DISALLOW_COPY_AND_ASSIGN(WorkspaceController); }; |