summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ash/accelerators/accelerator_controller.cc19
-rw-r--r--ash/ash.gyp6
-rw-r--r--ash/ash_switches.cc3
-rw-r--r--ash/ash_switches.h1
-rw-r--r--ash/shell.cc2
-rw-r--r--ash/shell.h5
-rw-r--r--ash/shell_delegate.h1
-rw-r--r--ash/wm/window_selector.cc216
-rw-r--r--ash/wm/window_selector.h79
-rw-r--r--ash/wm/window_selector_controller.cc61
-rw-r--r--ash/wm/window_selector_controller.h57
-rw-r--r--ash/wm/window_selector_delegate.h31
-rw-r--r--ash/wm/window_selector_unittest.cc202
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/about_flags.cc7
-rw-r--r--chrome/browser/ui/ash/chrome_shell_delegate.cc3
16 files changed, 698 insertions, 1 deletions
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index 8b9f9d4..b443691 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -45,6 +45,7 @@
#include "ash/wm/power_button_controller.h"
#include "ash/wm/property_util.h"
#include "ash/wm/window_cycle_controller.h"
+#include "ash/wm/window_selector_controller.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace/snap_sizer.h"
#include "base/bind.h"
@@ -95,7 +96,11 @@ bool HandleCycleWindowMRU(WindowCycleController::Direction direction,
void HandleCycleWindowLinear(CycleDirection direction) {
Shell::GetInstance()->
- window_cycle_controller()->HandleLinearCycleWindow();
+ window_cycle_controller()->HandleLinearCycleWindow();
+}
+
+void ToggleOverviewMode() {
+ Shell::GetInstance()->window_selector_controller()->ToggleOverview();
}
bool HandleAccessibleFocusCycle(bool reverse) {
@@ -521,11 +526,23 @@ bool AcceleratorController::PerformAction(int action,
return HandleCycleWindowMRU(WindowCycleController::FORWARD,
accelerator.IsAltDown());
case CYCLE_BACKWARD_LINEAR:
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAshEnableOverviewMode)) {
+ shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_OVERVIEW_F5);
+ ToggleOverviewMode();
+ return true;
+ }
if (key_code == ui::VKEY_MEDIA_LAUNCH_APP1)
shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_PREVWINDOW_F5);
HandleCycleWindowLinear(CYCLE_BACKWARD);
return true;
case CYCLE_FORWARD_LINEAR:
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAshEnableOverviewMode)) {
+ shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_OVERVIEW_F5);
+ ToggleOverviewMode();
+ return true;
+ }
if (key_code == ui::VKEY_MEDIA_LAUNCH_APP1)
shell->delegate()->RecordUserMetricsAction(UMA_ACCEL_NEXTWINDOW_F5);
HandleCycleWindowLinear(CYCLE_FORWARD);
diff --git a/ash/ash.gyp b/ash/ash.gyp
index dc8ee39..ed00c8d 100644
--- a/ash/ash.gyp
+++ b/ash/ash.gyp
@@ -484,6 +484,11 @@
'wm/window_properties.h',
'wm/window_resizer.cc',
'wm/window_resizer.h',
+ 'wm/window_selector.cc',
+ 'wm/window_selector.h',
+ 'wm/window_selector_controller.cc',
+ 'wm/window_selector_controller.h',
+ 'wm/window_selector_delegate.h',
'wm/window_util.cc',
'wm/window_util.h',
'wm/workspace_controller.cc',
@@ -745,6 +750,7 @@
'wm/window_cycle_controller_unittest.cc',
'wm/window_manager_unittest.cc',
'wm/window_modality_controller_unittest.cc',
+ 'wm/window_selector_unittest.cc',
'wm/window_util_unittest.cc',
'wm/workspace_controller_test_helper.cc',
'wm/workspace_controller_test_helper.h',
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 0c1ee11..b47ba50 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -114,6 +114,9 @@ const char kAshEnableMemoryMonitor[] = "ash-enable-memory-monitor";
// Enables the Oak tree viewer.
const char kAshEnableOak[] = "ash-enable-oak";
+// Enables overview mode for window switching.
+const char kAshEnableOverviewMode[] = "ash-enable-overview-mode";
+
// Enables "sticky" edges instead of "snap-to-edge"
const char kAshEnableStickyEdges[] = "ash-enable-sticky-edges";
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index 49158e6..7e5d471 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -51,6 +51,7 @@ ASH_EXPORT extern const char kAshEnableMemoryMonitor[];
#endif
ASH_EXPORT extern const char kAshEnableImmersiveFullscreen[];
ASH_EXPORT extern const char kAshEnableOak[];
+ASH_EXPORT extern const char kAshEnableOverviewMode[];
ASH_EXPORT extern const char kAshEnableStickyEdges[];
ASH_EXPORT extern const char kAshEnableTrayDragging[];
ASH_EXPORT extern const char kAshForceMirrorMode[];
diff --git a/ash/shell.cc b/ash/shell.cc
index 818b309..c7b4b1f 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -67,6 +67,7 @@
#include "ash/wm/window_animations.h"
#include "ash/wm/window_cycle_controller.h"
#include "ash/wm/window_properties.h"
+#include "ash/wm/window_selector_controller.h"
#include "ash/wm/window_util.h"
#include "ash/wm/workspace_controller.h"
#include "base/bind.h"
@@ -577,6 +578,7 @@ void Shell::Init() {
high_contrast_controller_.reset(new HighContrastController);
video_detector_.reset(new VideoDetector);
window_cycle_controller_.reset(new WindowCycleController());
+ window_selector_controller_.reset(new WindowSelectorController());
tooltip_controller_.reset(new views::corewm::TooltipController(
gfx::SCREEN_TYPE_ALTERNATE));
diff --git a/ash/shell.h b/ash/shell.h
index b6c669c..237a1a8 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -98,6 +98,7 @@ class UserWallpaperDelegate;
class VideoDetector;
class WebNotificationTray;
class WindowCycleController;
+class WindowSelectorController;
namespace internal {
class AcceleratorFilter;
@@ -326,6 +327,9 @@ class ASH_EXPORT Shell
WindowCycleController* window_cycle_controller() {
return window_cycle_controller_.get();
}
+ WindowSelectorController* window_selector_controller() {
+ return window_selector_controller_.get();
+ }
internal::FocusCycler* focus_cycler() {
return focus_cycler_.get();
}
@@ -560,6 +564,7 @@ class ASH_EXPORT Shell
scoped_ptr<UserActivityDetector> user_activity_detector_;
scoped_ptr<VideoDetector> video_detector_;
scoped_ptr<WindowCycleController> window_cycle_controller_;
+ scoped_ptr<WindowSelectorController> window_selector_controller_;
scoped_ptr<internal::FocusCycler> focus_cycler_;
scoped_ptr<DisplayController> display_controller_;
scoped_ptr<HighContrastController> high_contrast_controller_;
diff --git a/ash/shell_delegate.h b/ash/shell_delegate.h
index 0e7fcb4..2bf08c1 100644
--- a/ash/shell_delegate.h
+++ b/ash/shell_delegate.h
@@ -60,6 +60,7 @@ enum UserMetricsAction {
UMA_ACCEL_NEWTAB_T,
UMA_ACCEL_NEXTWINDOW_F5,
UMA_ACCEL_NEXTWINDOW_TAB,
+ UMA_ACCEL_OVERVIEW_F5,
UMA_ACCEL_PREVWINDOW_F5,
UMA_ACCEL_PREVWINDOW_TAB,
UMA_ACCEL_EXIT_FIRST_Q,
diff --git a/ash/wm/window_selector.cc b/ash/wm/window_selector.cc
new file mode 100644
index 0000000..f9de84c
--- /dev/null
+++ b/ash/wm/window_selector.cc
@@ -0,0 +1,216 @@
+// Copyright 2013 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/window_selector.h"
+
+#include <algorithm>
+
+#include "ash/screen_ash.h"
+#include "ash/shell.h"
+#include "ash/shell_window_ids.h"
+#include "ash/wm/window_selector_delegate.h"
+#include "ash/wm/window_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/window.h"
+#include "ui/base/events/event.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
+#include "ui/gfx/interpolated_transform.h"
+#include "ui/gfx/transform_util.h"
+#include "ui/views/corewm/window_animations.h"
+
+namespace ash {
+
+namespace {
+
+const float kCardAspectRatio = 4.0f / 3.0f;
+const int kWindowMargin = 20;
+const int kMinCardsMajor = 3;
+const int kOverviewTransitionMilliseconds = 100;
+
+// Applies a transform to |window| to fit within |target_bounds| while
+// maintaining its aspect ratio.
+void TransformWindowToFitBounds(aura::Window* window,
+ const gfx::Rect& target_bounds) {
+ const gfx::Rect bounds = window->bounds();
+ float scale = std::min(1.0f,
+ std::min(static_cast<float>(target_bounds.width()) / bounds.width(),
+ static_cast<float>(target_bounds.height()) / bounds.height()));
+ gfx::Transform transform;
+ gfx::Vector2d offset(
+ 0.5 * (target_bounds.width() - scale * bounds.width()),
+ 0.5 * (target_bounds.height() - scale * bounds.height()));
+ transform.Translate(target_bounds.x() - bounds.x() + offset.x(),
+ target_bounds.y() - bounds.y() + offset.y());
+ transform.Scale(scale, scale);
+ // TODO(flackr): The window bounds or transform could change during overview
+ // mode. WindowSelector should create a copy of the window so that the
+ // displayed windows are not affected by changes happening in the background.
+ // This will be necessary for alt-tab cycling as well, as some windows will
+ // be coming from other displays: http://crbug.com/263481.
+ window->SetTransform(transform);
+}
+
+} // namespace
+
+WindowSelector::WindowSelector(const WindowList& windows,
+ WindowSelectorDelegate* delegate)
+ : delegate_(delegate) {
+ DCHECK(delegate_);
+ for (size_t i = 0; i < windows.size(); ++i) {
+ windows[i]->AddObserver(this);
+ WindowDetails details;
+ details.window = windows[i];
+ details.minimized = windows[i]->GetProperty(aura::client::kShowStateKey) ==
+ ui::SHOW_STATE_MINIMIZED;
+ details.original_transform = windows[i]->layer()->transform();
+ if (details.minimized) {
+ windows[i]->Show();
+ }
+ windows_.push_back(details);
+ }
+ PositionWindows();
+ ash::Shell::GetInstance()->AddPreTargetHandler(this);
+}
+
+WindowSelector::~WindowSelector() {
+ for (size_t i = 0; i < windows_.size(); i++) {
+ ui::ScopedLayerAnimationSettings animation_settings(
+ windows_[i].window->layer()->GetAnimator());
+ animation_settings.SetPreemptionStrategy(
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+ animation_settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kOverviewTransitionMilliseconds));
+ windows_[i].window->RemoveObserver(this);
+ gfx::Transform transform;
+ windows_[i].window->SetTransform(windows_[i].original_transform);
+ if (windows_[i].minimized) {
+ // Setting opacity 0 and visible false ensures that the property change
+ // to SHOW_STATE_MINIMIZED will not animate the window from its original
+ // bounds to the minimized position.
+ windows_[i].window->layer()->SetOpacity(0);
+ windows_[i].window->layer()->SetVisible(false);
+ windows_[i].window->SetProperty(aura::client::kShowStateKey,
+ ui::SHOW_STATE_MINIMIZED);
+ }
+ }
+ ash::Shell::GetInstance()->RemovePreTargetHandler(this);
+}
+
+void WindowSelector::OnEvent(ui::Event* event) {
+ // TODO(flackr): This will prevent anything else from working while overview
+ // mode is active. This should only stop events from being sent to the windows
+ // in the overview but still allow interaction with the launcher / tray and
+ // hotkeys http://crbug.com/264289.
+ EventHandler::OnEvent(event);
+ event->StopPropagation();
+}
+
+void WindowSelector::OnMouseEvent(ui::MouseEvent* event) {
+ if (event->type() != ui::ET_MOUSE_RELEASED)
+ return;
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+ if (!target->HitTest(event->location()))
+ return;
+
+ HandleSelectionEvent(event);
+}
+
+void WindowSelector::OnGestureEvent(ui::GestureEvent* event) {
+ if (event->type() != ui::ET_GESTURE_TAP)
+ return;
+ HandleSelectionEvent(event);
+}
+
+void WindowSelector::OnWindowDestroyed(aura::Window* window) {
+ std::vector<WindowDetails>::iterator iter =
+ std::find(windows_.begin(), windows_.end(), window);
+ DCHECK(iter != windows_.end());
+ windows_.erase(iter);
+ if (windows_.empty()) {
+ delegate_->OnSelectionCanceled();
+ return;
+ }
+
+ PositionWindows();
+}
+
+void WindowSelector::HandleSelectionEvent(ui::Event* event) {
+ aura::Window* target = static_cast<aura::Window*>(event->target());
+
+ for (size_t i = 0; i < windows_.size(); i++) {
+ if (windows_[i].window->Contains(target)) {
+ // The selected window should not be minimized when window selection is
+ // ended.
+ windows_[i].minimized = false;
+ windows_[i].original_transform = gfx::Transform();
+
+ // The delegate may delete the WindowSelector, assume the object may no
+ // longer valid after calling this.
+ delegate_->OnWindowSelected(windows_[i].window);
+ return;
+ }
+ }
+}
+
+void WindowSelector::PositionWindows() {
+ Shell::RootWindowList root_window_list = Shell::GetAllRootWindows();
+ for (size_t i = 0; i < root_window_list.size(); ++i) {
+ PositionWindowsOnRoot(root_window_list[i]);
+ }
+}
+
+void WindowSelector::PositionWindowsOnRoot(aura::RootWindow* root_window) {
+ gfx::Size window_size;
+ gfx::Rect total_bounds = ScreenAsh::GetDisplayWorkAreaBoundsInParent(
+ Shell::GetContainer(root_window,
+ internal::kShellWindowId_DefaultContainer));
+
+ std::vector<WindowDetails> windows;
+ for (size_t i = 0; i < windows_.size(); ++i) {
+ if (windows_[i].window->GetRootWindow() == root_window)
+ windows.push_back(windows_[i]);
+ }
+ if (windows.empty())
+ return;
+
+ // Find the minimum number of windows per row that will fit all of the
+ // windows on screen.
+ size_t columns = std::max(
+ total_bounds.width() > total_bounds.height() ? kMinCardsMajor : 1,
+ static_cast<int>(ceil(sqrt(total_bounds.width() * windows.size() /
+ (kCardAspectRatio * total_bounds.height())))));
+ size_t rows = ((windows.size() + columns - 1) / columns);
+ window_size.set_width(std::min(
+ static_cast<int>(total_bounds.width() / columns),
+ static_cast<int>(total_bounds.height() * kCardAspectRatio / rows)));
+ window_size.set_height(window_size.width() / kCardAspectRatio);
+
+ // Calculate the X and Y offsets necessary to center the grid.
+ int x_offset = total_bounds.x() + ((windows.size() >= columns ? 0 :
+ (columns - windows.size()) * window_size.width()) +
+ (total_bounds.width() - columns * window_size.width())) / 2;
+ int y_offset = total_bounds.y() + (total_bounds.height() -
+ rows * window_size.height()) / 2;
+ for (size_t i = 0; i < windows.size(); ++i) {
+ ui::ScopedLayerAnimationSettings animation_settings(
+ windows[i].window->layer()->GetAnimator());
+ animation_settings.SetPreemptionStrategy(
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+ animation_settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kOverviewTransitionMilliseconds));
+ gfx::Transform transform;
+ int column = i % columns;
+ int row = i / columns;
+ gfx::Rect target_bounds(window_size.width() * column + x_offset,
+ window_size.height() * row + y_offset,
+ window_size.width(),
+ window_size.height());
+ target_bounds.Inset(kWindowMargin, kWindowMargin);
+ TransformWindowToFitBounds(windows[i].window, target_bounds);
+ }
+}
+
+} // namespace ash
diff --git a/ash/wm/window_selector.h b/ash/wm/window_selector.h
new file mode 100644
index 0000000..900787d
--- /dev/null
+++ b/ash/wm/window_selector.h
@@ -0,0 +1,79 @@
+// Copyright 2013 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_WINDOW_SELECTOR_H_
+#define ASH_WM_WINDOW_SELECTOR_H_
+
+#include <vector>
+
+#include "base/compiler_specific.h"
+#include "ui/aura/window_observer.h"
+#include "ui/base/events/event_handler.h"
+#include "ui/gfx/transform.h"
+
+namespace aura {
+class RootWindow;
+}
+
+namespace ash {
+
+class WindowSelectorDelegate;
+
+// The WindowSelector shows a grid of all of your windows and allows selecting
+// a window by clicking or tapping on it.
+class WindowSelector : public ui::EventHandler,
+ public aura::WindowObserver {
+ public:
+ typedef std::vector<aura::Window*> WindowList;
+
+ WindowSelector(const WindowList& windows,
+ WindowSelectorDelegate* delegate);
+ virtual ~WindowSelector();
+
+ // ui::EventHandler:
+ virtual void OnEvent(ui::Event* event) OVERRIDE;
+ virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
+ virtual void OnGestureEvent(ui::GestureEvent* event) OVERRIDE;
+
+ // aura::WindowObserver:
+ virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
+
+ private:
+ struct WindowDetails {
+ WindowDetails() : window(NULL), minimized(false) {}
+
+ bool operator==(const aura::Window* other_window) const {
+ return window == other_window;
+ }
+
+ // A weak pointer to the window.
+ aura::Window* window;
+
+ // If true, the window was minimized and this should be restored if the
+ // window was not selected.
+ bool minimized;
+
+ // The original transform of the window before entering overview mode.
+ gfx::Transform original_transform;
+ };
+
+ void HandleSelectionEvent(ui::Event* event);
+ void PositionWindows();
+ void PositionWindowsOnRoot(aura::RootWindow* root_window);
+
+ void SelectWindow(aura::Window*);
+
+ // List of weak pointers of windows to select from.
+ std::vector<WindowDetails> windows_;
+
+ // Weak pointer to the selector delegate which will be called when a
+ // selection is made.
+ WindowSelectorDelegate* delegate_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowSelector);
+};
+
+} // namespace ash
+
+#endif // ASH_WM_WINDOW_SELECTOR_H_
diff --git a/ash/wm/window_selector_controller.cc b/ash/wm/window_selector_controller.cc
new file mode 100644
index 0000000..c7fe696
--- /dev/null
+++ b/ash/wm/window_selector_controller.cc
@@ -0,0 +1,61 @@
+// Copyright 2013 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/window_selector_controller.h"
+
+#include "ash/session_state_delegate.h"
+#include "ash/shell.h"
+#include "ash/wm/mru_window_tracker.h"
+#include "ash/wm/window_selector.h"
+#include "ash/wm/window_util.h"
+
+namespace ash {
+
+WindowSelectorController::WindowSelectorController() {
+}
+
+WindowSelectorController::~WindowSelectorController() {
+}
+
+// static
+bool WindowSelectorController::CanSelect() {
+ // Don't allow a window overview if the screen is locked or a modal dialog is
+ // open.
+ return !Shell::GetInstance()->session_state_delegate()->IsScreenLocked() &&
+ !Shell::GetInstance()->IsSystemModalWindowOpen();
+}
+
+void WindowSelectorController::ToggleOverview() {
+ if (window_selector_.get()) {
+ window_selector_.reset();
+ } else {
+ std::vector<aura::Window*> windows = ash::Shell::GetInstance()->
+ mru_window_tracker()->BuildMruWindowList();
+ // Don't enter overview mode with no windows.
+ if (windows.empty())
+ return;
+
+ // Deactivating the window will hide popup windows like the omnibar or
+ // open menus.
+ aura::Window* active_window = wm::GetActiveWindow();
+ if (active_window)
+ wm::DeactivateWindow(active_window);
+ window_selector_.reset(new WindowSelector(windows, this));
+ }
+}
+
+bool WindowSelectorController::IsSelecting() {
+ return window_selector_.get() != NULL;
+}
+
+void WindowSelectorController::OnWindowSelected(aura::Window* window) {
+ window_selector_.reset();
+ wm::ActivateWindow(window);
+}
+
+void WindowSelectorController::OnSelectionCanceled() {
+ window_selector_.reset();
+}
+
+} // namespace ash
diff --git a/ash/wm/window_selector_controller.h b/ash/wm/window_selector_controller.h
new file mode 100644
index 0000000..8b11005
--- /dev/null
+++ b/ash/wm/window_selector_controller.h
@@ -0,0 +1,57 @@
+// Copyright 2013 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_WINDOW_SELECTOR_CONTROLLER_H_
+#define ASH_WM_WINDOW_SELECTOR_CONTROLLER_H_
+
+#include <list>
+#include <vector>
+
+#include "ash/ash_export.h"
+#include "ash/wm/window_selector_delegate.h"
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "ui/aura/window_observer.h"
+
+namespace aura {
+class Window;
+}
+
+namespace ash {
+
+class WindowSelector;
+
+// Manages a window selector which displays an overview of all windows and
+// allows selecting a window to activate it.
+class ASH_EXPORT WindowSelectorController
+ : public WindowSelectorDelegate {
+ public:
+ WindowSelectorController();
+ virtual ~WindowSelectorController();
+
+ // Returns true if selecting windows in an overview is enabled. This is false
+ // at certain times, such as when the lock screen is visible.
+ static bool CanSelect();
+
+ // Enters overview mode. This is essentially the window cycling mode however
+ // not released on releasing the alt key and allows selecting with the mouse
+ // or touch rather than keypresses.
+ void ToggleOverview();
+
+ // Returns true if window selection mode is active.
+ bool IsSelecting();
+
+ // WindowSelectorDelegate:
+ virtual void OnWindowSelected(aura::Window* window) OVERRIDE;
+ virtual void OnSelectionCanceled() OVERRIDE;
+
+ private:
+ scoped_ptr<WindowSelector> window_selector_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowSelectorController);
+};
+
+} // namespace ash
+
+#endif // ASH_WM_WINDOW_SELECTOR_CONTROLLER_H_
diff --git a/ash/wm/window_selector_delegate.h b/ash/wm/window_selector_delegate.h
new file mode 100644
index 0000000..d6d0490
--- /dev/null
+++ b/ash/wm/window_selector_delegate.h
@@ -0,0 +1,31 @@
+// Copyright 2013 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_WINDOW_SELECTOR_DELEGATE_H_
+#define ASH_WM_WINDOW_SELECTOR_DELEGATE_H_
+
+#include "base/compiler_specific.h"
+
+namespace aura {
+class Window;
+}
+
+namespace ash {
+
+// Implement this class to handle the selection event from WindowSelector.
+class WindowSelectorDelegate {
+ public:
+ // Invoked when a window is selected.
+ virtual void OnWindowSelected(aura::Window* window) = 0;
+
+ // Invoked if selection is canceled.
+ virtual void OnSelectionCanceled() = 0;
+
+ protected:
+ virtual ~WindowSelectorDelegate() {}
+};
+
+} // namespace ash
+
+#endif // ASH_WM_WINDOW_SELECTOR_DELEGATE_H_
diff --git a/ash/wm/window_selector_unittest.cc b/ash/wm/window_selector_unittest.cc
new file mode 100644
index 0000000..01a96bf
--- /dev/null
+++ b/ash/wm/window_selector_unittest.cc
@@ -0,0 +1,202 @@
+// Copyright 2013 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/root_window_controller.h"
+#include "ash/screen_ash.h"
+#include "ash/shell.h"
+#include "ash/test/ash_test_base.h"
+#include "ash/test/shell_test_api.h"
+#include "ash/wm/mru_window_tracker.h"
+#include "ash/wm/window_selector_controller.h"
+#include "ash/wm/window_util.h"
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_vector.h"
+#include "base/run_loop.h"
+#include "ui/aura/client/aura_constants.h"
+#include "ui/aura/root_window.h"
+#include "ui/aura/test/event_generator.h"
+#include "ui/aura/test/test_window_delegate.h"
+#include "ui/aura/test/test_windows.h"
+#include "ui/aura/window.h"
+#include "ui/compositor/layer_animator.h"
+#include "ui/gfx/rect_conversions.h"
+#include "ui/gfx/transform.h"
+
+namespace ash {
+namespace internal {
+
+namespace {
+
+class LayerAnimationObserver : public ui::LayerAnimationObserver {
+ public:
+ LayerAnimationObserver(ui::Layer* layer)
+ : layer_(layer), animating_(false), message_loop_running_(false) {
+ layer_->GetAnimator()->AddObserver(this);
+ }
+
+ virtual ~LayerAnimationObserver() {
+ layer_->GetAnimator()->RemoveObserver(this);
+ }
+
+ virtual void OnLayerAnimationEnded(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {
+ AnimationDone();
+ }
+
+ virtual void OnLayerAnimationScheduled(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {
+ animating_ = true;
+ }
+
+ virtual void OnLayerAnimationAborted(
+ ui::LayerAnimationSequence* sequence) OVERRIDE {
+ AnimationDone();
+ }
+
+ void WaitUntilDone() {
+ while (animating_) {
+ message_loop_running_ = true;
+ base::MessageLoop::current()->Run();
+ message_loop_running_ = false;
+ }
+ }
+
+ private:
+ void AnimationDone() {
+ animating_ = false;
+ if (message_loop_running_)
+ base::MessageLoop::current()->Quit();
+ }
+
+ ui::Layer* layer_;
+ bool animating_;
+ bool message_loop_running_;
+
+ DISALLOW_COPY_AND_ASSIGN(LayerAnimationObserver);
+};
+
+} // namespace
+
+class WindowSelectorTest : public test::AshTestBase {
+ public:
+ WindowSelectorTest() {}
+ virtual ~WindowSelectorTest() {}
+
+ aura::Window* CreateWindow(const gfx::Rect& bounds) {
+ return CreateTestWindowInShellWithDelegate(&wd, -1, bounds);
+ }
+
+ bool WindowsOverlapping(aura::Window* window1, aura::Window* window2) {
+ gfx::RectF window1_bounds = GetTransformedTargetBounds(window1);
+ gfx::RectF window2_bounds = GetTransformedTargetBounds(window2);
+ return window1_bounds.Intersects(window2_bounds);
+ }
+
+ void ToggleOverview() {
+ std::vector<aura::Window*> windows = ash::Shell::GetInstance()->
+ mru_window_tracker()->BuildMruWindowList();
+ ScopedVector<LayerAnimationObserver> animations;
+ for (size_t i = 0; i < windows.size(); ++i) {
+ animations.push_back(new LayerAnimationObserver(windows[i]->layer()));
+ }
+ ash::Shell::GetInstance()->window_selector_controller()->ToggleOverview();
+ for (size_t i = 0; i < animations.size(); ++i) {
+ animations[i]->WaitUntilDone();
+ }
+ }
+
+ gfx::RectF GetTransformedBounds(aura::Window* window) {
+ gfx::RectF bounds(window->layer()->bounds());
+ window->layer()->transform().TransformRect(&bounds);
+ return bounds;
+ }
+
+ gfx::RectF GetTransformedTargetBounds(aura::Window* window) {
+ gfx::RectF bounds(window->layer()->GetTargetBounds());
+ window->layer()->GetTargetTransform().TransformRect(&bounds);
+ return bounds;
+ }
+
+ void ClickWindow(aura::Window* window) {
+ aura::test::EventGenerator event_generator(window->GetRootWindow(), window);
+ gfx::RectF target = GetTransformedBounds(window);
+ event_generator.ClickLeftButton();
+ }
+
+ private:
+ aura::test::TestWindowDelegate wd;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowSelectorTest);
+};
+
+// Tests entering overview mode with two windows and selecting one.
+TEST_F(WindowSelectorTest, Basic) {
+ gfx::Rect bounds(0, 0, 400, 400);
+ scoped_ptr<aura::Window> window1(CreateWindow(bounds));
+ scoped_ptr<aura::Window> window2(CreateWindow(bounds));
+ EXPECT_TRUE(WindowsOverlapping(window1.get(), window2.get()));
+ wm::ActivateWindow(window2.get());
+ EXPECT_FALSE(wm::IsActiveWindow(window1.get()));
+ EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
+
+ // In overview mode the windows should no longer overlap.
+ ToggleOverview();
+ EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get()));
+
+ // Clicking window 1 should activate it.
+ ClickWindow(window1.get());
+ EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
+ EXPECT_FALSE(wm::IsActiveWindow(window2.get()));
+}
+
+// Tests that overview mode is exited if the last remaining window is destroyed.
+TEST_F(WindowSelectorTest, LastWindowDestroyed) {
+ gfx::Rect bounds(0, 0, 400, 400);
+ scoped_ptr<aura::Window> window1(CreateWindow(bounds));
+ scoped_ptr<aura::Window> window2(CreateWindow(bounds));
+ ToggleOverview();
+
+ window1.reset();
+ window2.reset();
+ EXPECT_FALSE(ash::Shell::GetInstance()->window_selector_controller()->
+ IsSelecting());
+}
+
+// Tests that windows remain on the display they are currently on in overview
+// mode.
+TEST_F(WindowSelectorTest, MultipleDisplays) {
+ if (!SupportsMultipleDisplays())
+ return;
+
+ UpdateDisplay("400x400,400x400");
+ Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
+
+ scoped_ptr<aura::Window> window1(CreateWindow(gfx::Rect(0, 0, 100, 100)));
+ scoped_ptr<aura::Window> window2(CreateWindow(gfx::Rect(0, 0, 100, 100)));
+ scoped_ptr<aura::Window> window3(CreateWindow(gfx::Rect(450, 0, 100, 100)));
+ scoped_ptr<aura::Window> window4(CreateWindow(gfx::Rect(450, 0, 100, 100)));
+ EXPECT_EQ(root_windows[0], window1->GetRootWindow());
+ EXPECT_EQ(root_windows[0], window2->GetRootWindow());
+ EXPECT_EQ(root_windows[1], window3->GetRootWindow());
+ EXPECT_EQ(root_windows[1], window4->GetRootWindow());
+
+ // In overview mode, each window remains in the same root window.
+ ToggleOverview();
+ EXPECT_EQ(root_windows[0], window1->GetRootWindow());
+ EXPECT_EQ(root_windows[0], window2->GetRootWindow());
+ EXPECT_EQ(root_windows[1], window3->GetRootWindow());
+ EXPECT_EQ(root_windows[1], window4->GetRootWindow());
+ root_windows[0]->bounds().Contains(
+ ToEnclosingRect(GetTransformedBounds(window1.get())));
+ root_windows[0]->bounds().Contains(
+ ToEnclosingRect(GetTransformedBounds(window2.get())));
+ root_windows[1]->bounds().Contains(
+ ToEnclosingRect(GetTransformedBounds(window3.get())));
+ root_windows[1]->bounds().Contains(
+ ToEnclosingRect(GetTransformedBounds(window4.get())));
+}
+
+} // namespace internal
+} // namespace ash
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index cc3ad40..604684d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -6733,6 +6733,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_OVERSCROLL_HISTORY_NAVIGATION_DESCRIPTION" desc="Description for the flag to disable history navigation from horizontal overscroll.">
Experimental history navigation in response to horizontal overscroll.
</message>
+ <message name="IDS_FLAGS_OVERVIEW_MODE_NAME" desc="Title for the flag to enable window overview mode.">
+ Enable overview mode.
+ </message>
+ <message name="IDS_FLAGS_OVERVIEW_MODE_DESCRIPTION" desc="Description for the flag to enable window overview mode.">
+ Enable overview mode, activated by pushing the switch window button.
+ </message>
<message name="IDS_FLAGS_SCROLL_END_EFFECT_NAME" desc="Title for the flag for scroll end effect from vertical overscroll.">
Scroll end effect
</message>
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index f02a8a9..dff32ea 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1042,6 +1042,13 @@ const Experiment kExperiments[] = {
SINGLE_VALUE_TYPE(switches::kDisableMinimizeOnSecondLauncherItemClick)
},
{
+ "enable-overview-mode",
+ IDS_FLAGS_OVERVIEW_MODE_NAME,
+ IDS_FLAGS_OVERVIEW_MODE_DESCRIPTION,
+ kOsCrOS,
+ SINGLE_VALUE_TYPE(ash::switches::kAshEnableOverviewMode)
+ },
+ {
"show-touch-hud",
IDS_FLAGS_SHOW_TOUCH_HUD_NAME,
IDS_FLAGS_SHOW_TOUCH_HUD_DESCRIPTION,
diff --git a/chrome/browser/ui/ash/chrome_shell_delegate.cc b/chrome/browser/ui/ash/chrome_shell_delegate.cc
index c2d6888..1cb4879 100644
--- a/chrome/browser/ui/ash/chrome_shell_delegate.cc
+++ b/chrome/browser/ui/ash/chrome_shell_delegate.cc
@@ -314,6 +314,9 @@ void ChromeShellDelegate::RecordUserMetricsAction(
case ash::UMA_ACCEL_NEXTWINDOW_TAB:
content::RecordAction(content::UserMetricsAction("Accel_NextWindow_Tab"));
break;
+ case ash::UMA_ACCEL_OVERVIEW_F5:
+ content::RecordAction(content::UserMetricsAction("Accel_Overview_F5"));
+ break;
case ash::UMA_ACCEL_PREVWINDOW_F5:
content::RecordAction(content::UserMetricsAction("Accel_PrevWindow_F5"));
break;