Separated alt-tab window cycle from overview mode

This change separates the logic into two different classes. It also
- Brings back a bounce animation when there is only one window that was
lost at some point in the past.
- Removes the minimized animation if there is only one window while alt tabbing.

Basically I brought back from the past two classes,
WindowCycleController and WindowCycleList and adapted them
to work with the new codebase. Also, I moved some tests from
WindowSelectorUnittest to WindowCycleControllerUnittest, and 
removed the ones that are now irrelevant. 

Finally, we are also dropping support for alt-tabbing while
in overview mode in favour of cancelling the latter.
BUG=365786
TEST=none

Review URL: https://codereview.chromium.org/260883005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@269997 0039d316-1c4b-4281-b951-d872f2087c98
parent b41e86c1
......@@ -49,6 +49,7 @@
#include "ash/wm/overview/window_selector_controller.h"
#include "ash/wm/partial_screenshot_view.h"
#include "ash/wm/power_button_controller.h"
#include "ash/wm/window_cycle_controller.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ash/wm/wm_event.h"
......@@ -127,8 +128,8 @@ bool HandleCycleBackwardMRU(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_TAB)
base::RecordAction(base::UserMetricsAction("Accel_PrevWindow_Tab"));
Shell::GetInstance()->window_selector_controller()->HandleCycleWindow(
WindowSelector::BACKWARD);
Shell::GetInstance()->window_cycle_controller()->HandleCycleWindow(
WindowCycleController::BACKWARD);
return true;
}
......@@ -136,8 +137,8 @@ bool HandleCycleForwardMRU(const ui::Accelerator& accelerator) {
if (accelerator.key_code() == ui::VKEY_TAB)
base::RecordAction(base::UserMetricsAction("Accel_NextWindow_Tab"));
Shell::GetInstance()->window_selector_controller()->HandleCycleWindow(
WindowSelector::FORWARD);
Shell::GetInstance()->window_cycle_controller()->HandleCycleWindow(
WindowCycleController::FORWARD);
return true;
}
......
......@@ -1199,7 +1199,7 @@ TEST_F(AcceleratorControllerTest, DisallowedWithNoWindow) {
for (size_t i = 0; i < kActionsNeedingWindowLength; ++i) {
delegate->TriggerAccessibilityAlert(A11Y_ALERT_NONE);
GetController()->PerformAction(kActionsNeedingWindow[i], dummy);
EXPECT_EQ(delegate->GetLastAccessibilityAlert(), A11Y_ALERT_NONE);
EXPECT_NE(delegate->GetLastAccessibilityAlert(), A11Y_ALERT_WINDOW_NEEDED);
}
// Don't alert if we have a minimized window either.
......@@ -1207,7 +1207,7 @@ TEST_F(AcceleratorControllerTest, DisallowedWithNoWindow) {
for (size_t i = 0; i < kActionsNeedingWindowLength; ++i) {
delegate->TriggerAccessibilityAlert(A11Y_ALERT_NONE);
GetController()->PerformAction(kActionsNeedingWindow[i], dummy);
EXPECT_EQ(delegate->GetLastAccessibilityAlert(), A11Y_ALERT_NONE);
EXPECT_NE(delegate->GetLastAccessibilityAlert(), A11Y_ALERT_WINDOW_NEEDED);
}
}
......
......@@ -590,6 +590,10 @@
'wm/overview/window_overview.h',
'wm/overview/window_selector.cc',
'wm/overview/window_selector.h',
'wm/window_cycle_controller.cc',
'wm/window_cycle_controller.h',
'wm/window_cycle_list.cc',
'wm/window_cycle_list.h',
'wm/overview/window_selector_controller.cc',
'wm/overview/window_selector_controller.h',
'wm/overview/window_selector_delegate.h',
......@@ -998,6 +1002,7 @@
'wm/toplevel_window_event_handler_unittest.cc',
'wm/video_detector_unittest.cc',
'wm/window_animations_unittest.cc',
'wm/window_cycle_controller_unittest.cc',
'wm/window_manager_unittest.cc',
'wm/window_modality_controller_unittest.cc',
'wm/window_positioner_unittest.cc',
......
......@@ -73,6 +73,7 @@
#include "ash/wm/toplevel_window_event_handler.h"
#include "ash/wm/video_detector.h"
#include "ash/wm/window_animations.h"
#include "ash/wm/window_cycle_controller.h"
#include "ash/wm/window_positioner.h"
#include "ash/wm/window_properties.h"
#include "ash/wm/window_util.h"
......@@ -712,6 +713,7 @@ Shell::~Shell() {
shadow_controller_.reset();
resize_shadow_controller_.reset();
window_cycle_controller_.reset();
window_selector_controller_.reset();
mru_window_tracker_.reset();
......@@ -946,6 +948,7 @@ void Shell::Init() {
high_contrast_controller_.reset(new HighContrastController);
video_detector_.reset(new VideoDetector);
window_selector_controller_.reset(new WindowSelectorController());
window_cycle_controller_.reset(new WindowCycleController());
tooltip_controller_.reset(
new views::corewm::TooltipController(
......
......@@ -363,6 +363,9 @@ class ASH_EXPORT Shell : public SystemModalContainerEventFilterDelegate,
VideoDetector* video_detector() {
return video_detector_.get();
}
WindowCycleController* window_cycle_controller() {
return window_cycle_controller_.get();
}
WindowSelectorController* window_selector_controller() {
return window_selector_controller_.get();
}
......@@ -644,6 +647,7 @@ class ASH_EXPORT Shell : public SystemModalContainerEventFilterDelegate,
scoped_ptr<MruWindowTracker> mru_window_tracker_;
scoped_ptr< ::wm::UserActivityDetector> user_activity_detector_;
scoped_ptr<VideoDetector> video_detector_;
scoped_ptr<WindowCycleController> window_cycle_controller_;
scoped_ptr<WindowSelectorController> window_selector_controller_;
scoped_ptr<FocusCycler> focus_cycler_;
scoped_ptr<DisplayController> display_controller_;
......
......@@ -120,12 +120,10 @@ void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled(
} // namespace
WindowOverview::WindowOverview(WindowSelector* window_selector,
WindowSelectorItemList* windows,
aura::Window* single_root_window)
WindowSelectorItemList* windows)
: window_selector_(window_selector),
windows_(windows),
selection_index_(0),
single_root_window_(single_root_window),
overview_start_time_(base::Time::Now()),
cursor_client_(NULL) {
Shell* shell = Shell::GetInstance();
......@@ -151,11 +149,9 @@ WindowOverview::WindowOverview(WindowSelector* window_selector,
shell->GetScreen()->AddObserver(this);
shell->metrics()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW);
HideAndTrackNonOverviewWindows();
// Send an a11y alert only if the overview was activated by the user.
if (window_selector_->mode() == WindowSelector::OVERVIEW) {
shell->accessibility_delegate()->TriggerAccessibilityAlert(
A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED);
}
// Send an a11y alert.
shell->accessibility_delegate()->TriggerAccessibilityAlert(
A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED);
}
WindowOverview::~WindowOverview() {
......@@ -254,11 +250,6 @@ void WindowOverview::OnWindowsChanged() {
PositionWindows(/* animate */ true);
}
void WindowOverview::MoveToSingleRootWindow(aura::Window* root_window) {
single_root_window_ = root_window;
PositionWindows(/* animate */ true);
}
void WindowOverview::OnKeyEvent(ui::KeyEvent* event) {
if (GetTargetedWindow(static_cast<aura::Window*>(event->target())))
event->StopPropagation();
......@@ -378,18 +369,9 @@ void WindowOverview::HideAndTrackNonOverviewWindows() {
}
void WindowOverview::PositionWindows(bool animate) {
if (single_root_window_) {
std::vector<WindowSelectorItem*> windows;
for (WindowSelectorItemList::iterator iter = windows_->begin();
iter != windows_->end(); ++iter) {
windows.push_back(*iter);
}
PositionWindowsOnRoot(single_root_window_, windows, animate);
} else {
aura::Window::Windows root_window_list = Shell::GetAllRootWindows();
for (size_t i = 0; i < root_window_list.size(); ++i)
PositionWindowsFromRoot(root_window_list[i], animate);
}
aura::Window::Windows root_window_list = Shell::GetAllRootWindows();
for (size_t i = 0; i < root_window_list.size(); ++i)
PositionWindowsFromRoot(root_window_list[i], animate);
}
void WindowOverview::PositionWindowsFromRoot(aura::Window* root_window,
......@@ -400,13 +382,7 @@ void WindowOverview::PositionWindowsFromRoot(aura::Window* root_window,
if ((*iter)->GetRootWindow() == root_window)
windows.push_back(*iter);
}
PositionWindowsOnRoot(root_window, windows, animate);
}
void WindowOverview::PositionWindowsOnRoot(
aura::Window* root_window,
const std::vector<WindowSelectorItem*>& windows,
bool animate) {
if (windows.empty())
return;
......@@ -455,9 +431,7 @@ void WindowOverview::InitializeSelectionWidget() {
params.keep_on_top = false;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
params.opacity = views::Widget::InitParams::OPAQUE_WINDOW;
params.parent = Shell::GetContainer(single_root_window_
? single_root_window_
: windows_->front()->GetRootWindow(),
params.parent = Shell::GetContainer(windows_->front()->GetRootWindow(),
kShellWindowId_DefaultContainer);
params.accept_events = false;
selection_widget_->set_focus_on_creation(false);
......
......@@ -47,8 +47,7 @@ class WindowOverview : public ui::EventHandler,
// If |single_root_window| is not NULL, all windows will be positioned on the
// given root window.
WindowOverview(WindowSelector* window_selector,
WindowSelectorItemList* windows,
aura::Window* single_root_window);
WindowSelectorItemList* windows);
virtual ~WindowOverview();
// Sets the selected window to be the window in position |index|.
......@@ -57,9 +56,6 @@ class WindowOverview : public ui::EventHandler,
// Dispatched when the list of windows has changed.
void OnWindowsChanged();
// Moves the overview to only |root_window|.
void MoveToSingleRootWindow(aura::Window* root_window);
// ui::EventHandler:
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
......@@ -87,10 +83,6 @@ class WindowOverview : public ui::EventHandler,
void PositionWindows(bool animate);
// Position all of the windows from |root_window| on |root_window|.
void PositionWindowsFromRoot(aura::Window* root_window, bool animate);
// Position all of the |windows| to fit on the |root_window|.
void PositionWindowsOnRoot(aura::Window* root_window,
const std::vector<WindowSelectorItem*>& windows,
bool animate);
// Creates the selection widget.
void InitializeSelectionWidget();
......@@ -113,11 +105,6 @@ class WindowOverview : public ui::EventHandler,
// selection changes rows and use a different animation.
size_t selection_index_;
// If NULL, each root window displays an overview of the windows in that
// display. Otherwise, all windows are in a single overview on
// |single_root_window_|.
aura::Window* single_root_window_;
// The time when overview was started.
base::Time overview_start_time_;
......
......@@ -21,10 +21,7 @@
#include "base/strings/string_number_conversions.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_observer.h"
#include "ui/events/event.h"
#include "ui/events/event_handler.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_client.h"
......@@ -74,42 +71,6 @@ struct WindowSelectorItemForRoot
const aura::Window* root_window;
};
// Filter to watch for the termination of a keyboard gesture to cycle through
// multiple windows.
class WindowSelectorEventFilter : public ui::EventHandler {
public:
WindowSelectorEventFilter(WindowSelector* selector);
virtual ~WindowSelectorEventFilter();
// Overridden from ui::EventHandler:
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
private:
// A weak pointer to the WindowSelector which owns this instance.
WindowSelector* selector_;
DISALLOW_COPY_AND_ASSIGN(WindowSelectorEventFilter);
};
// Watch for all keyboard events by filtering the root window.
WindowSelectorEventFilter::WindowSelectorEventFilter(WindowSelector* selector)
: selector_(selector) {
Shell::GetInstance()->AddPreTargetHandler(this);
}
WindowSelectorEventFilter::~WindowSelectorEventFilter() {
Shell::GetInstance()->RemovePreTargetHandler(this);
}
void WindowSelectorEventFilter::OnKeyEvent(ui::KeyEvent* event) {
// Views uses VKEY_MENU for both left and right Alt keys.
if (event->key_code() == ui::VKEY_MENU &&
event->type() == ui::ET_KEY_RELEASED) {
selector_->SelectWindow();
// Warning: |this| will be deleted from here on.
}
}
// Triggers a shelf visibility update on all root window controllers.
void UpdateShelfVisibility() {
Shell::RootWindowControllerList root_window_controllers =
......@@ -121,110 +82,11 @@ void UpdateShelfVisibility() {
}
}
// Returns the window immediately below |window| in the current container.
aura::Window* GetWindowBelow(aura::Window* window) {
aura::Window* parent = window->parent();
if (!parent)
return NULL;
aura::Window* below = NULL;
for (aura::Window::Windows::const_iterator iter = parent->children().begin();
iter != parent->children().end(); ++iter) {
if (*iter == window)
return below;
below = *iter;
}
NOTREACHED();
return NULL;
}
} // namespace
// This class restores and moves a window to the front of the stacking order for
// the duration of the class's scope.
class ScopedShowWindow : public aura::WindowObserver {
public:
ScopedShowWindow();
virtual ~ScopedShowWindow();
// Show |window| at the top of the stacking order.
void Show(aura::Window* window);
// Cancel restoring the window on going out of scope.
void CancelRestore();
aura::Window* window() { return window_; }
// aura::WindowObserver:
virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
private:
// The window being shown.
aura::Window* window_;
// The window immediately below where window_ belongs.
aura::Window* stack_window_above_;
// If true, minimize window_ on going out of scope.
bool minimized_;
DISALLOW_COPY_AND_ASSIGN(ScopedShowWindow);
};
ScopedShowWindow::ScopedShowWindow()
: window_(NULL),
stack_window_above_(NULL),
minimized_(false) {
}
void ScopedShowWindow::Show(aura::Window* window) {
DCHECK(!window_);
window_ = window;
stack_window_above_ = GetWindowBelow(window);
minimized_ = wm::GetWindowState(window)->IsMinimized();
window_->parent()->AddObserver(this);
window_->Show();
wm::GetWindowState(window_)->Activate();
}
ScopedShowWindow::~ScopedShowWindow() {
if (window_) {
window_->parent()->RemoveObserver(this);
// Restore window's stacking position.
if (stack_window_above_)
window_->parent()->StackChildAbove(window_, stack_window_above_);
else
window_->parent()->StackChildAtBottom(window_);
// Restore minimized state.
if (minimized_)
wm::GetWindowState(window_)->Minimize();
}
}
void ScopedShowWindow::CancelRestore() {
if (!window_)
return;
window_->parent()->RemoveObserver(this);
window_ = stack_window_above_ = NULL;
}
void ScopedShowWindow::OnWillRemoveWindow(aura::Window* window) {
if (window == window_) {
CancelRestore();
} else if (window == stack_window_above_) {
// If the window this window was above is removed, use the next window down
// as the restore marker.
stack_window_above_ = GetWindowBelow(stack_window_above_);
}
}
WindowSelector::WindowSelector(const WindowList& windows,
WindowSelector::Mode mode,
WindowSelectorDelegate* delegate)
: mode_(mode),
delegate_(delegate),
selected_window_(0),
: delegate_(delegate),
restore_focus_window_(aura::client::GetFocusClient(
Shell::GetPrimaryRootWindow())->GetFocusedWindow()),
ignore_activations_(false) {
......@@ -280,12 +142,7 @@ WindowSelector::WindowSelector(const WindowList& windows,
}
}
if (mode == WindowSelector::CYCLE) {
cycle_start_time_ = base::Time::Now();
event_handler_.reset(new WindowSelectorEventFilter(this));
} else {
StartOverview();
}
StartOverview();
}
WindowSelector::~WindowSelector() {
......@@ -300,45 +157,10 @@ WindowSelector::~WindowSelector() {
// Clearing the window list resets the ignored_by_shelf flag on the windows.
windows_.clear();
UpdateShelfVisibility();
if (!cycle_start_time_.is_null()) {
UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.CycleTime",
base::Time::Now() - cycle_start_time_);
}
}
void WindowSelector::Step(WindowSelector::Direction direction) {
DCHECK(!windows_.empty());
// Upgrade to CYCLE mode if currently in OVERVIEW mode.
if (mode_ != CYCLE) {
event_handler_.reset(new WindowSelectorEventFilter(this));
DCHECK(window_overview_);
// Set the initial selection window to animate to the new selection.
window_overview_->SetSelection(selected_window_);
window_overview_->MoveToSingleRootWindow(
windows_[selected_window_]->GetRootWindow());
mode_ = CYCLE;
}
selected_window_ = (selected_window_ + windows_.size() +
(direction == WindowSelector::FORWARD ? 1 : -1)) % windows_.size();
if (window_overview_) {
window_overview_->SetSelection(selected_window_);
} else {
base::AutoReset<bool> restoring_focus(&ignore_activations_, true);
showing_window_.reset(new ScopedShowWindow);
showing_window_->Show(windows_[selected_window_]->SelectionWindow());
}
}
void WindowSelector::SelectWindow() {
SelectWindow(windows_[selected_window_]->SelectionWindow());
}
void WindowSelector::SelectWindow(aura::Window* window) {
ResetFocusRestoreWindow(false);
if (showing_window_ && showing_window_->window() == window)
showing_window_->CancelRestore();
ScopedVector<WindowSelectorItem>::iterator iter =
std::find_if(windows_.begin(), windows_.end(),
WindowSelectorItemTargetComparator(window));
......@@ -388,7 +210,6 @@ void WindowSelector::OnWindowDestroying(aura::Window* window) {
if (!(*iter)->empty())
return;
size_t deleted_index = iter - windows_.begin();
windows_.erase(iter);
if (windows_.empty()) {
CancelSelection();
......@@ -396,13 +217,6 @@ void WindowSelector::OnWindowDestroying(aura::Window* window) {
}
if (window_overview_)
window_overview_->OnWindowsChanged();
if (mode_ == CYCLE && selected_window_ >= deleted_index) {
if (selected_window_ > deleted_index)
selected_window_--;
selected_window_ = selected_window_ % windows_.size();
if (window_overview_)
window_overview_->SetSelection(selected_window_);
}
}
void WindowSelector::OnWindowBoundsChanged(aura::Window* window,
......@@ -449,12 +263,7 @@ void WindowSelector::StartOverview() {
aura::client::GetFocusClient(
Shell::GetPrimaryRootWindow())->FocusWindow(NULL);
aura::Window* overview_root = NULL;
if (mode_ == CYCLE)
overview_root = windows_[selected_window_]->GetRootWindow();
window_overview_.reset(new WindowOverview(this, &windows_, overview_root));
if (mode_ == CYCLE)
window_overview_->SetSelection(selected_window_);
window_overview_.reset(new WindowOverview(this, &windows_));
UpdateShelfVisibility();
}
......
......@@ -20,54 +20,30 @@ namespace aura {
class RootWindow;
}
namespace ui {
class EventHandler;
}
namespace ash {
class ScopedShowWindow;
class WindowOverview;
class WindowSelectorDelegate;
class WindowSelectorItem;
class WindowSelectorTest;
// The WindowSelector allows selecting a window by alt-tabbing (CYCLE mode) or
// by clicking or tapping on it (OVERVIEW mode). A WindowOverview will be shown
// in OVERVIEW mode or if the user lingers on a window while alt tabbing.
// The WindowSelector allows selecting a window by clicking or tapping on it
// (entering Overview Mode).
class ASH_EXPORT WindowSelector
: public aura::WindowObserver,
public aura::client::ActivationChangeObserver {
public:
enum Direction {
FORWARD,
BACKWARD
};
enum Mode {
CYCLE,
OVERVIEW
};
typedef std::vector<aura::Window*> WindowList;
WindowSelector(const WindowList& windows,
Mode mode,
WindowSelectorDelegate* delegate);
virtual ~WindowSelector();
// Step to the next window in |direction|.
void Step(Direction direction);
// Choose the currently selected window.
void SelectWindow();
// Choose |window| from the available windows to select.
void SelectWindow(aura::Window* window);
// Cancels window selection.
void CancelSelection();
Mode mode() { return mode_; }
// aura::WindowObserver:
virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE;
virtual void OnWindowBoundsChanged(aura::Window* window,
......@@ -99,29 +75,12 @@ class ASH_EXPORT WindowSelector
// Tracks observed windows.
std::set<aura::Window*> observed_windows_;
// The window selection mode.
Mode mode_;
// An event handler listening for the release of the alt key during alt-tab
// cycling.
scoped_ptr<ui::EventHandler> event_handler_;
// The currently selected window being shown (temporarily brought to the front
// of the stacking order and made visible).
scoped_ptr<ScopedShowWindow> showing_window_;
scoped_ptr<WindowOverview> window_overview_;
// The time when window cycling was started.
base::Time cycle_start_time_;
// Weak pointer to the selector delegate which will be called when a
// selection is made.
WindowSelectorDelegate* delegate_;
// Index of the currently selected window if the mode is CYCLE.
size_t selected_window_;
// A weak pointer to the window which was focused on beginning window
// selection. If window selection is canceled the focus should be restored to
// this window.
......
......@@ -50,31 +50,11 @@ void WindowSelectorController::ToggleOverview() {
if (windows.empty())
return;
window_selector_.reset(
new WindowSelector(windows, WindowSelector::OVERVIEW, this));
window_selector_.reset(new WindowSelector(windows, this));
OnSelectionStarted();
}
}
void WindowSelectorController::HandleCycleWindow(
WindowSelector::Direction direction) {
if (!CanSelect())
return;
if (!IsSelecting()) {
std::vector<aura::Window*> windows = ash::Shell::GetInstance()->
mru_window_tracker()->BuildMruWindowList();
// Don't cycle with no windows.
if (windows.empty())
return;
window_selector_.reset(
new WindowSelector(windows, WindowSelector::CYCLE, this));
OnSelectionStarted();
}
window_selector_->Step(direction);
}
bool WindowSelectorController::IsSelecting() {
return window_selector_.get() != NULL;
}
......
......@@ -41,11 +41,6 @@ class ASH_EXPORT WindowSelectorController
// or touch rather than keypresses.
void ToggleOverview();
// Cycles between windows in the given |direction|. It is assumed that the
// alt key is held down and a key filter is installed to watch for alt being
// released.
void HandleCycleWindow(WindowSelector::Direction direction);
// Returns true if window selection mode is active.
bool IsSelecting();
......
This diff is collapsed.
// Copyright 2014 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_cycle_controller.h"
#include "ash/session/session_state_delegate.h"
#include "ash/shell.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/window_cycle_list.h"
#include "ui/events/event.h"
#include "ui/events/event_handler.h"
namespace ash {
namespace {
// Filter to watch for the termination of a keyboard gesture to cycle through
// multiple windows.
class WindowCycleEventFilter : public ui::EventHandler {
public:
WindowCycleEventFilter();
virtual ~WindowCycleEventFilter();
// Overridden from ui::EventHandler:
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
private:
DISALLOW_COPY_AND_ASSIGN(WindowCycleEventFilter);
};
WindowCycleEventFilter::WindowCycleEventFilter() {
Shell::GetInstance()->AddPreTargetHandler(this);
}
WindowCycleEventFilter::~WindowCycleEventFilter() {
Shell::GetInstance()->RemovePreTargetHandler(this);
}
void WindowCycleEventFilter::OnKeyEvent(ui::KeyEvent* event) {
// Views uses VKEY_MENU for both left and right Alt keys.
if (event->key_code() == ui::VKEY_MENU &&
event->type() == ui::ET_KEY_RELEASED) {
Shell::GetInstance()->window_cycle_controller()->StopCycling();
// Warning: |this| will be deleted from here on.
}
}
} // namespace
//////////////////////////////////////////////////////////////////////////////
// WindowCycleController, public:
WindowCycleController::WindowCycleController() {
}
WindowCycleController::~WindowCycleController() {
}
// static
bool WindowCycleController::CanCycle() {
// Don't allow window cycling if the screen is locked or a modal dialog is
// open.
return !Shell::GetInstance()->session_state_delegate()->IsScreenLocked() &&
!Shell::GetInstance()->IsSystemModalWindowOpen();
}
void WindowCycleController::HandleCycleWindow(Direction direction) {
if (!CanCycle())
return;
if (!IsCycling())
StartCycling();
Step(direction);
}
void WindowCycleController::StartCycling() {
window_cycle_list_.reset(new WindowCycleList(ash::Shell::GetInstance()->
mru_window_tracker()->BuildMruWindowList()));
event_handler_.reset(new WindowCycleEventFilter());
}
//////////////////////////////////////////////////////////////////////////////
// WindowCycleController, private:
void WindowCycleController::Step(Direction direction) {
DCHECK(window_cycle_list_.get());
window_cycle_list_->Step(direction);
}
void WindowCycleController::StopCycling() {
window_cycle_list_.reset();
// Remove our key event filter.
event_handler_.reset();
}
} // namespace ash
// Copyright 2014 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_CYCLE_CONTROLLER_H_
#define ASH_WM_WINDOW_CYCLE_CONTROLLER_H_
#include "ash/ash_export.h"
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
namespace ui {
class EventHandler;
}
namespace ash {
class WindowCycleList;
// Controls cycling through windows with the keyboard 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, 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 {
public:
enum Direction {
FORWARD,
BACKWARD
};
WindowCycleController();
virtual ~WindowCycleController();
// Returns true if cycling through windows is enabled. This is false at
// certain times, such as when the lock screen is visible.
static bool CanCycle();
// Cycles between windows in the given |direction|.
void HandleCycleWindow(Direction direction);
// Returns true if we are in the middle of a window cycling gesture.
bool IsCycling() const { return window_cycle_list_.get() != NULL; }
// Call to start cycling windows. This funtion adds a pre-target handler to
// listen to the alt key release.
void StartCycling();
// Stops the current window cycle and removes the event filter.
void StopCycling();
// Returns the WindowCycleList. Really only useful for testing.
const WindowCycleList* window_cycle_list() const {
return window_cycle_list_.get();
}
private:
// Cycles to the next or previous window based on |direction|.
void Step(Direction direction);
scoped_ptr<WindowCycleList> window_cycle_list_;
// Event handler to watch for release of alt key.
scoped_ptr<ui::EventHandler> event_handler_;
DISALLOW_COPY_AND_ASSIGN(WindowCycleController);
};
} // namespace ash
#endif // ASH_WM_WINDOW_CYCLE_CONTROLLER_H_
This diff is collapsed.
// Copyright 2014 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_cycle_list.h"
#include "ash/shell.h"
#include "ash/wm/mru_window_tracker.h"
#include "ash/wm/window_animations.h"
#include "ash/wm/window_state.h"
#include "ash/wm/window_util.h"
#include "ui/aura/window.h"
namespace ash {
// Returns the window immediately below |window| in the current container.
aura::Window* GetWindowBelow(aura::Window* window) {
aura::Window* parent = window->parent();
if (!parent)
return NULL;
aura::Window::Windows::const_iterator iter =
std::find(parent->children().begin(), parent->children().end(), window);
CHECK(*iter == window);
if (iter != parent->children().begin())
return *(iter - 1);
else
return NULL;
}
// This class restores and moves a window to the front of the stacking order for
// the duration of the class's scope.
class ScopedShowWindow : public aura::WindowObserver {
public:
ScopedShowWindow();
virtual ~ScopedShowWindow();
// Show |window| at the top of the stacking order.
void Show(aura::Window* window);
// Cancel restoring the window on going out of scope.
void CancelRestore();
aura::Window* window() { return window_; }
// aura::WindowObserver:
virtual void OnWillRemoveWindow(aura::Window* window) OVERRIDE;
private:
// The window being shown.
aura::Window* window_;
// The window immediately below where window_ belongs.
aura::Window* stack_window_above_;
// If true, minimize window_ on going out of scope.
bool minimized_;
DISALLOW_COPY_AND_ASSIGN(ScopedShowWindow);
};
ScopedShowWindow::ScopedShowWindow()
: window_(NULL),
stack_window_above_(NULL),
minimized_(false) {
}
ScopedShowWindow::~ScopedShowWindow() {
if (window_) {
window_->parent()->RemoveObserver(this);
// Restore window's stacking position.
if (stack_window_above_)
window_->parent()->StackChildAbove(window_, stack_window_above_);
else
window_->parent()->StackChildAtBottom(window_);
// Restore minimized state.
if (minimized_)
wm::GetWindowState(window_)->Minimize();
}
}
void ScopedShowWindow::Show(aura::Window* window) {
DCHECK(!window_);
window_ = window;
stack_window_above_ = GetWindowBelow(window);
minimized_ = wm::GetWindowState(window)->IsMinimized();
window_->parent()->AddObserver(this);
window_->Show();
wm::GetWindowState(window_)->Activate();
}
void ScopedShowWindow::CancelRestore() {
if (!window_)
return;
window_->parent()->RemoveObserver(this);
window_ = stack_window_above_ = NULL;
}
void ScopedShowWindow::OnWillRemoveWindow(aura::Window* window) {
if (window == window_) {
CancelRestore();
} else if (window == stack_window_above_) {
// If the window this window was above is removed, use the next window down
// as the restore marker.
stack_window_above_ = GetWindowBelow(stack_window_above_);
}
}
WindowCycleList::WindowCycleList(const WindowList& windows)
: windows_(windows),
current_index_(0) {
ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(true);
for (WindowList::const_iterator i = windows_.begin(); i != windows_.end();
++i) {
(*i)->AddObserver(this);
}
}
WindowCycleList::~WindowCycleList() {
ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(false);
for (WindowList::const_iterator i = windows_.begin(); i != windows_.end();
++i) {
(*i)->RemoveObserver(this);
}
if (showing_window_)
showing_window_->CancelRestore();
}
void WindowCycleList::Step(WindowCycleController::Direction direction) {
if (windows_.empty())
return;
// When there is only one window, we should give feedback to the user. If the
// window is minimized, we should also show it.
if (windows_.size() == 1) {
::wm::AnimateWindow(windows_[0], ::wm::WINDOW_ANIMATION_TYPE_BOUNCE);
windows_[0]->Show();
wm::GetWindowState(windows_[0])->Activate();
return;
}
DCHECK(static_cast<size_t>(current_index_) < windows_.size());
// We're in a valid cycle, so step forward or backward.
current_index_ += direction == WindowCycleController::FORWARD ? 1 : -1;
// Wrap to window list size.
current_index_ = (current_index_ + windows_.size()) % windows_.size();
DCHECK(windows_[current_index_]);
// Make sure the next window is visible.
showing_window_.reset(new ScopedShowWindow);
showing_window_->Show(windows_[current_index_]);
}
void WindowCycleList::OnWindowDestroyed(aura::Window* window) {
window->RemoveObserver(this);
WindowList::iterator i = std::find(windows_.begin(), windows_.end(), window);
DCHECK(i != windows_.end());
int removed_index = static_cast<int>(i - windows_.begin());
windows_.erase(i);
if (current_index_ > removed_index ||
current_index_ == static_cast<int>(windows_.size())) {
current_index_--;
}
}
} // namespace ash
// Copyright 2014 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_CYCLE_LIST_H_
#define ASH_WM_WINDOW_CYCLE_LIST_H_
#include <vector>
#include "ash/ash_export.h"
#include "ash/wm/window_cycle_controller.h"
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "ui/aura/window_observer.h"
namespace ash {
class ScopedShowWindow;
// Tracks a set of Windows that can be stepped through. This class is used by
// the WindowCycleController.
class ASH_EXPORT WindowCycleList : public aura::WindowObserver {
public:
typedef std::vector<aura::Window*> WindowList;
explicit WindowCycleList(const WindowList& windows);
virtual ~WindowCycleList();
bool empty() const { return windows_.empty(); }
// Cycles to the next or previous window based on |direction|.
void Step(WindowCycleController::Direction direction);
const WindowList& windows() const { return windows_; }
private:
// aura::WindowObserver overrides:
// There is a chance a window is destroyed, for example by JS code. We need to
// take care of that even if it is not intended for the user to close a window
// while window cycling.
virtual void OnWindowDestroyed(aura::Window* window) OVERRIDE;
// List of weak pointers to windows to use while cycling with the keyboard.
// List is built when the user initiates the gesture (i.e. hits alt-tab the
// first time) and is emptied when the gesture is complete (i.e. releases the
// alt key).
WindowList windows_;
// Current position in the |windows_|
int current_index_;
// Wrapper for the window brought to the front.
scoped_ptr<ScopedShowWindow> showing_window_;
DISALLOW_COPY_AND_ASSIGN(WindowCycleList);
};
} // namespace ash
#endif // ASH_WM_WINDOW_CYCLE_LIST_H_
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