Commit 8fef7430 authored by zork@chromium.org's avatar zork@chromium.org

Add always on top windows to the alt+tab list

BUG=114631
TEST=Open Google Talk.  Try to alt+tab away from it, and then back.


Review URL: https://chromiumcodereview.appspot.com/10700057

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@150094 0039d316-1c4b-4281-b951-d872f2087c98
parent 979b48da
......@@ -219,11 +219,11 @@ TEST_F(ExtendedDesktopTest, CycleWindows) {
views::Widget* d2_w2 = CreateTestWidget(gfx::Rect(800, 200, 100, 100));
EXPECT_EQ(root_windows[1], d2_w2->GetNativeView()->GetRootWindow());
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(d2_w1->GetNativeView()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(d1_w2->GetNativeView()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(d2_w1->GetNativeView()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(d1_w1->GetNativeView()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(d2_w2->GetNativeView()));
......@@ -232,10 +232,10 @@ TEST_F(ExtendedDesktopTest, CycleWindows) {
controller->HandleCycleWindow(WindowCycleController::BACKWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(d1_w1->GetNativeView()));
controller->HandleCycleWindow(WindowCycleController::BACKWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(d1_w2->GetNativeView()));
controller->HandleCycleWindow(WindowCycleController::BACKWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(d2_w1->GetNativeView()));
controller->HandleCycleWindow(WindowCycleController::BACKWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(d1_w2->GetNativeView()));
controller->HandleCycleWindow(WindowCycleController::BACKWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(d2_w2->GetNativeView()));
}
......
......@@ -17,7 +17,7 @@ namespace ash {
namespace {
bool HasFocusableWindow() {
return !WindowCycleController::BuildWindowList().empty();
return !WindowCycleController::BuildWindowList(NULL).empty();
}
} // namespace
......
......@@ -457,7 +457,8 @@ void Shell::Init() {
high_contrast_controller_.reset(new HighContrastController);
video_detector_.reset(new VideoDetector);
window_cycle_controller_.reset(new WindowCycleController);
window_cycle_controller_.reset(
new WindowCycleController(activation_controller_.get()));
tooltip_controller_.reset(new internal::TooltipController(
drag_drop_controller_.get()));
......@@ -704,6 +705,7 @@ void Shell::InitRootWindowController(
DCHECK(visibility_controller_.get());
DCHECK(drag_drop_controller_.get());
DCHECK(capture_controller_.get());
DCHECK(window_cycle_controller_.get());
root_window->set_focus_manager(focus_manager_.get());
input_method_filter_->SetInputMethodPropertyInRootWindow(root_window);
......@@ -735,6 +737,8 @@ void Shell::InitRootWindowController(
root_window->GetChildById(internal::kShellWindowId_AlwaysOnTopContainer));
root_window->SetProperty(internal::kAlwaysOnTopControllerKey,
always_on_top_controller);
window_cycle_controller_->OnRootWindowAdded(root_window);
}
////////////////////////////////////////////////////////////////////////////////
......
......@@ -6,9 +6,9 @@
#include <algorithm>
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/shell_window_ids.h"
#include "ash/wm/activation_controller.h"
#include "ash/wm/window_cycle_list.h"
#include "ash/wm/window_util.h"
#include "ui/aura/event.h"
......@@ -19,6 +19,12 @@ namespace ash {
namespace {
// List of containers whose children we will cycle through.
const int kContainerIds[] = {
internal::kShellWindowId_DefaultContainer,
internal::kShellWindowId_AlwaysOnTopContainer
};
// Filter to watch for the termination of a keyboard gesture to cycle through
// multiple windows.
class WindowCycleEventFilter : public aura::EventFilter {
......@@ -82,10 +88,24 @@ ui::GestureStatus WindowCycleEventFilter::PreHandleGestureEvent(
//////////////////////////////////////////////////////////////////////////////
// WindowCycleController, public:
WindowCycleController::WindowCycleController() {
WindowCycleController::WindowCycleController(
internal::ActivationController* activation_controller)
: activation_controller_(activation_controller) {
activation_controller_->AddObserver(this);
}
WindowCycleController::~WindowCycleController() {
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
for (Shell::RootWindowList::const_iterator iter = root_windows.begin();
iter != root_windows.end(); ++iter) {
for (size_t i = 0; i < arraysize(kContainerIds); ++i) {
aura::Window* container = Shell::GetContainer(*iter, kContainerIds[i]);
if (container)
container->RemoveObserver(this);
}
}
activation_controller_->RemoveObserver(this);
StopCycling();
}
......@@ -126,7 +146,8 @@ void WindowCycleController::AltKeyReleased() {
}
// static
std::vector<aura::Window*> WindowCycleController::BuildWindowList() {
std::vector<aura::Window*> WindowCycleController::BuildWindowList(
const std::list<aura::Window*>* mru_windows) {
WindowCycleList::WindowList windows;
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
......@@ -134,19 +155,22 @@ std::vector<aura::Window*> WindowCycleController::BuildWindowList() {
iter != root_windows.end(); ++iter) {
if (*iter == Shell::GetActiveRootWindow())
continue;
aura::Window* default_container = Shell::GetContainer(
*iter, internal::kShellWindowId_DefaultContainer);
WindowCycleList::WindowList children = default_container->children();
windows.insert(windows.end(), children.begin(), children.end());
for (size_t i = 0; i < arraysize(kContainerIds); ++i) {
aura::Window* container = Shell::GetContainer(*iter, kContainerIds[i]);
WindowCycleList::WindowList children = container->children();
windows.insert(windows.end(), children.begin(), children.end());
}
}
// Add windows in the active root windows last so that the topmost window
// in the active root window becomes the front of the list.
aura::Window* default_container = Shell::GetContainer(
Shell::GetActiveRootWindow(),
internal::kShellWindowId_DefaultContainer);
for (size_t i = 0; i < arraysize(kContainerIds); ++i) {
aura::Window* container =
Shell::GetContainer(Shell::GetActiveRootWindow(), kContainerIds[i]);
WindowCycleList::WindowList children = default_container->children();
windows.insert(windows.end(), children.begin(), children.end());
WindowCycleList::WindowList children = container->children();
windows.insert(windows.end(), children.begin(), children.end());
}
// Removes unfocusable windows.
WindowCycleList::WindowList::iterator last =
......@@ -155,16 +179,42 @@ std::vector<aura::Window*> WindowCycleController::BuildWindowList() {
windows.end(),
std::not1(std::ptr_fun(ash::wm::CanActivateWindow)));
windows.erase(last, windows.end());
// Put the windows in the mru_windows list at the head, if it's available.
if (mru_windows) {
// Iterate through the list backwards, so that we can move each window to
// the front of the windows list as we find them.
for (std::list<aura::Window*>::const_reverse_iterator ix =
mru_windows->rbegin();
ix != mru_windows->rend(); ++ix) {
WindowCycleList::WindowList::iterator window =
std::find(windows.begin(), windows.end(), *ix);
if (window != windows.end()) {
windows.erase(window);
windows.push_back(*ix);
}
}
}
// Window cycling expects the topmost window at the front of the list.
std::reverse(windows.begin(), windows.end());
return windows;
}
void WindowCycleController::OnRootWindowAdded(aura::RootWindow* root_window) {
for (size_t i = 0; i < arraysize(kContainerIds); ++i) {
aura::Window* container =
Shell::GetContainer(root_window, kContainerIds[i]);
container->AddObserver(this);
}
}
//////////////////////////////////////////////////////////////////////////////
// WindowCycleController, private:
void WindowCycleController::StartCycling() {
windows_.reset(new WindowCycleList(BuildWindowList()));
windows_.reset(new WindowCycleList(BuildWindowList(&mru_windows_)));
}
void WindowCycleController::Step(Direction direction) {
......@@ -180,6 +230,23 @@ void WindowCycleController::StopCycling() {
Shell::GetInstance()->RemoveEnvEventFilter(event_filter_.get());
event_filter_.reset();
}
// Add the currently focused window to the MRU list
aura::Window* active_window = wm::GetActiveWindow();
mru_windows_.remove(active_window);
mru_windows_.push_front(active_window);
}
// static
bool WindowCycleController::IsTrackedContainer(aura::Window* window) {
if (!window)
return false;
for (size_t i = 0; i < arraysize(kContainerIds); ++i) {
if (window->id() == kContainerIds[i]) {
return true;
}
}
return false;
}
void WindowCycleController::InstallEventFilter() {
......@@ -187,4 +254,20 @@ void WindowCycleController::InstallEventFilter() {
Shell::GetInstance()->AddEnvEventFilter(event_filter_.get());
}
void WindowCycleController::OnWindowActivated(aura::Window* active,
aura::Window* old_active) {
if (active && !IsCycling() && IsTrackedContainer(active->parent())) {
mru_windows_.remove(active);
mru_windows_.push_front(active);
}
}
void WindowCycleController::OnWillRemoveWindow(aura::Window* window) {
mru_windows_.remove(window);
}
void WindowCycleController::OnWindowDestroying(aura::Window* window) {
window->RemoveObserver(this);
}
} // namespace ash
......@@ -5,34 +5,47 @@
#ifndef ASH_WM_WINDOW_CYCLE_CONTROLLER_H_
#define ASH_WM_WINDOW_CYCLE_CONTROLLER_H_
#include <list>
#include <vector>
#include "ash/ash_export.h"
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "ui/aura/client/activation_change_observer.h"
#include "ui/aura/window_observer.h"
namespace aura {
class EventFilter;
class RootWindow;
class Window;
}
namespace ash {
namespace internal {
class ActivationController;
}
class WindowCycleList;
// Controls cycling through windows with the keyboard, for example, via alt-tab.
// Windows are sorted primarily by most recently used, and then by screen order.
// We activate windows as you cycle through them, so the order on the screen
// may change during the gesture. Thus we maintain the state of the windows
// may change during the gesture, but the most recently used list isn't updated
// until the cycling ends. Thus we maintain the state of the windows
// at the beginning of the gesture so you can cycle through in a consistent
// order.
class ASH_EXPORT WindowCycleController {
class ASH_EXPORT WindowCycleController
: public aura::client::ActivationChangeObserver,
public aura::WindowObserver {
public:
enum Direction {
FORWARD,
BACKWARD
};
WindowCycleController();
~WindowCycleController();
explicit WindowCycleController(
internal::ActivationController* activation_controller);
virtual ~WindowCycleController();
// Returns true if cycling through windows is enabled. This is false at
// certain times, such as when the lock screen is visible.
......@@ -53,11 +66,18 @@ class ASH_EXPORT WindowCycleController {
// Returns the WindowCycleList. Really only useful for testing.
const WindowCycleList* windows() const { return windows_.get(); }
// Set up the observers to handle window changes for the containers we care
// about. Called when a new root window is added.
void OnRootWindowAdded(aura::RootWindow* root_window);
// Returns the set of windows to cycle through. This method creates
// the vector based on the current set of windows across all valid root
// windows. As a result it is not necessarily the same as the set of
// windows being iterated over.
static std::vector<aura::Window*> BuildWindowList();
// If |mru_windows| is not NULL, windows in this list are put at the head of
// the window list.
static std::vector<aura::Window*> BuildWindowList(
const std::list<aura::Window*>* mru_windows);
private:
// Call to start cycling windows. You must call StopCycling() when done.
......@@ -72,11 +92,28 @@ class ASH_EXPORT WindowCycleController {
// Stops the current window cycle and cleans up the event filter.
void StopCycling();
// Checks if the window represents a container whose children we track.
static bool IsTrackedContainer(aura::Window* window);
// Overridden from ActivationChangeObserver:
virtual void OnWindowActivated(aura::Window* active,
aura::Window* old_active) OVERRIDE;
// Overridden from WindowObserver:
virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
scoped_ptr<WindowCycleList> windows_;
// Event filter to watch for release of alt key.
scoped_ptr<aura::EventFilter> event_filter_;
// List of windows that have been activated in containers that we cycle
// through, sorted by most recently used.
std::list<aura::Window*> mru_windows_;
internal::ActivationController* activation_controller_;
DISALLOW_COPY_AND_ASSIGN(WindowCycleController);
};
......
......@@ -6,6 +6,8 @@
#include <algorithm>
#include "ash/display/display_controller.h"
#include "ash/display/multi_display_manager.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/test/ash_test_base.h"
......@@ -13,6 +15,7 @@
#include "ash/wm/window_cycle_list.h"
#include "ash/wm/window_util.h"
#include "base/memory/scoped_ptr.h"
#include "ui/aura/env.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
#include "ui/gfx/rect.h"
......@@ -237,6 +240,227 @@ TEST_F(WindowCycleControllerTest, Minimized) {
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
}
TEST_F(WindowCycleControllerTest, AlwaysOnTopWindow) {
WindowCycleController* controller =
Shell::GetInstance()->window_cycle_controller();
// Set up several windows to use to test cycling.
Window* default_container =
Shell::GetContainer(
Shell::GetPrimaryRootWindow(),
internal::kShellWindowId_DefaultContainer);
scoped_ptr<Window> window0(CreateTestWindowWithId(0, default_container));
scoped_ptr<Window> window1(CreateTestWindowWithId(1, default_container));
Window* top_container =
Shell::GetContainer(
Shell::GetPrimaryRootWindow(),
internal::kShellWindowId_AlwaysOnTopContainer);
scoped_ptr<Window> window2(CreateTestWindowWithId(2, top_container));
wm::ActivateWindow(window0.get());
// Simulate pressing and releasing Alt-tab.
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
// Window lists should return the topmost window in front.
ASSERT_TRUE(controller->windows());
ASSERT_EQ(3u, controller->windows()->windows().size());
EXPECT_EQ(window0.get(), controller->windows()->windows()[0]);
EXPECT_EQ(window2.get(), controller->windows()->windows()[1]);
EXPECT_EQ(window1.get(), controller->windows()->windows()[2]);
controller->AltKeyReleased();
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
controller->AltKeyReleased();
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
}
TEST_F(WindowCycleControllerTest, AlwaysOnTopMultiWindow) {
WindowCycleController* controller =
Shell::GetInstance()->window_cycle_controller();
// Set up several windows to use to test cycling.
Window* default_container =
Shell::GetContainer(
Shell::GetPrimaryRootWindow(),
internal::kShellWindowId_DefaultContainer);
scoped_ptr<Window> window0(CreateTestWindowWithId(0, default_container));
scoped_ptr<Window> window1(CreateTestWindowWithId(1, default_container));
Window* top_container =
Shell::GetContainer(
Shell::GetPrimaryRootWindow(),
internal::kShellWindowId_AlwaysOnTopContainer);
scoped_ptr<Window> window2(CreateTestWindowWithId(2, top_container));
scoped_ptr<Window> window3(CreateTestWindowWithId(3, top_container));
wm::ActivateWindow(window0.get());
// Simulate pressing and releasing Alt-tab.
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
// Window lists should return the topmost window in front.
ASSERT_TRUE(controller->windows());
ASSERT_EQ(4u, controller->windows()->windows().size());
EXPECT_EQ(window0.get(), controller->windows()->windows()[0]);
EXPECT_EQ(window3.get(), controller->windows()->windows()[1]);
EXPECT_EQ(window2.get(), controller->windows()->windows()[2]);
EXPECT_EQ(window1.get(), controller->windows()->windows()[3]);
controller->AltKeyReleased();
EXPECT_TRUE(wm::IsActiveWindow(window3.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
controller->AltKeyReleased();
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window3.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
}
TEST_F(WindowCycleControllerTest, AlwaysOnTopMultipleRootWindows) {
// Set up a second root window
UpdateDisplay("0+0-1000x600,1001+0-600x400");
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
ASSERT_EQ(2U, root_windows.size());
// Move the active root window to the secondary.
Shell::GetInstance()->set_active_root_window(root_windows[1]);
WindowCycleController* controller =
Shell::GetInstance()->window_cycle_controller();
// Set up several windows to use to test cycling.
Window* default_container0 =
Shell::GetContainer(
root_windows[0],
internal::kShellWindowId_DefaultContainer);
scoped_ptr<Window> window0(CreateTestWindowWithId(0, default_container0));
Window* top_container0 =
Shell::GetContainer(
root_windows[0],
internal::kShellWindowId_AlwaysOnTopContainer);
scoped_ptr<Window> window1(CreateTestWindowWithId(1, top_container0));
// Set up several windows to use to test cycling.
Window* default_container1 =
Shell::GetContainer(
root_windows[1],
internal::kShellWindowId_DefaultContainer);
scoped_ptr<Window> window2(CreateTestWindowWithId(2, default_container1));
Window* top_container1 =
Shell::GetContainer(
root_windows[1],
internal::kShellWindowId_AlwaysOnTopContainer);
scoped_ptr<Window> window3(CreateTestWindowWithId(3, top_container1));
wm::ActivateWindow(window2.get());
// Simulate pressing and releasing Alt-tab.
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
// Window lists should return the topmost window in front.
ASSERT_TRUE(controller->windows());
ASSERT_EQ(4u, controller->windows()->windows().size());
EXPECT_EQ(window2.get(), controller->windows()->windows()[0]);
EXPECT_EQ(window3.get(), controller->windows()->windows()[1]);
EXPECT_EQ(window1.get(), controller->windows()->windows()[2]);
EXPECT_EQ(window0.get(), controller->windows()->windows()[3]);
controller->AltKeyReleased();
EXPECT_TRUE(wm::IsActiveWindow(window3.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
controller->AltKeyReleased();
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window3.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
}
TEST_F(WindowCycleControllerTest, MostRecentlyUsed) {
WindowCycleController* controller =
Shell::GetInstance()->window_cycle_controller();
// Set up several windows to use to test cycling.
Window* container =
Shell::GetContainer(
Shell::GetPrimaryRootWindow(),
internal::kShellWindowId_DefaultContainer);
scoped_ptr<Window> window0(CreateTestWindowWithId(0, container));
scoped_ptr<Window> window1(CreateTestWindowWithId(1, container));
scoped_ptr<Window> window2(CreateTestWindowWithId(2, container));
wm::ActivateWindow(window0.get());
// Simulate pressing and releasing Alt-tab.
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
// Window lists should return the topmost window in front.
ASSERT_TRUE(controller->windows());
ASSERT_EQ(3u, controller->windows()->windows().size());
EXPECT_EQ(window0.get(), controller->windows()->windows()[0]);
EXPECT_EQ(window2.get(), controller->windows()->windows()[1]);
EXPECT_EQ(window1.get(), controller->windows()->windows()[2]);
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
controller->AltKeyReleased();
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
controller->AltKeyReleased();
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window1.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window2.get()));
controller->HandleCycleWindow(WindowCycleController::FORWARD, true);
EXPECT_TRUE(wm::IsActiveWindow(window0.get()));
}
} // namespace
} // namespace ash
......@@ -101,7 +101,7 @@ gfx::Rect WindowPositioner::SmartPopupPosition(
const gfx::Rect& work_area,
int grid) {
const std::vector<aura::Window*> windows =
ash::WindowCycleController::BuildWindowList();
ash::WindowCycleController::BuildWindowList(NULL);
std::vector<const gfx::Rect*> regions;
// Process the window list and check if we can bail immediately.
......
......@@ -44,7 +44,7 @@ aura::Window* GetTopWindow() {
// Get a list of all windows.
const std::vector<aura::Window*> windows =
ash::WindowCycleController::BuildWindowList();
ash::WindowCycleController::BuildWindowList(NULL);
if (windows.empty())
return NULL;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment