Merge WindowOverview into WindowSelector

This change reduces the amount of code its complexity, and is the natural follow up from having alt tab separated into a different class.

Please note that no functionality has been changed, and that the change is pretty much only moving code around.

BUG=371884
TEST=WindowSelectorTest.*

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@271142 0039d316-1c4b-4281-b951-d872f2087c98
parent 275b1b4d
...@@ -592,8 +592,6 @@ ...@@ -592,8 +592,6 @@
'wm/overview/scoped_transform_overview_window.h', 'wm/overview/scoped_transform_overview_window.h',
'wm/overview/scoped_window_copy.cc', 'wm/overview/scoped_window_copy.cc',
'wm/overview/scoped_window_copy.h', 'wm/overview/scoped_window_copy.h',
'wm/overview/window_overview.cc',
'wm/overview/window_overview.h',
'wm/overview/window_selector.cc', 'wm/overview/window_selector.cc',
'wm/overview/window_selector.h', 'wm/overview/window_selector.h',
'wm/window_cycle_controller.cc', 'wm/window_cycle_controller.cc',
......
// 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/overview/window_overview.h"
#include <algorithm>
#include "ash/accessibility_delegate.h"
#include "ash/metrics/user_metrics_recorder.h"
#include "ash/screen_util.h"
#include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/switchable_windows.h"
#include "ash/wm/overview/scoped_transform_overview_window.h"
#include "ash/wm/overview/window_selector.h"
#include "ash/wm/overview/window_selector_item.h"
#include "base/metrics/histogram.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/events/event.h"
#include "ui/gfx/screen.h"
#include "ui/views/background.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
// Conceptually the window overview is a table or grid of cells having this
// fixed aspect ratio. The number of columns is determined by maximizing the
// area of them based on the number of windows.
const float kCardAspectRatio = 4.0f / 3.0f;
// In the conceptual overview table, the window margin is the space reserved
// around the window within the cell. This margin does not overlap so the
// closest distance between adjacent windows will be twice this amount.
const int kWindowMargin = 30;
// The minimum number of cards along the major axis (i.e. horizontally on a
// landscape orientation).
const int kMinCardsMajor = 3;
// The duration of transition animations on the overview selector.
const int kOverviewSelectorTransitionMilliseconds = 100;
// The color and opacity of the overview selector.
const SkColor kWindowOverviewSelectionColor = SK_ColorBLACK;
const float kWindowOverviewSelectionOpacity = 0.5f;
// The padding or amount of the window selector widget visible around the edges
// of the currently selected window.
const int kWindowOverviewSelectionPadding = 25;
// A comparator for locating a given target window.
struct WindowSelectorItemComparator
: public std::unary_function<WindowSelectorItem*, bool> {
explicit WindowSelectorItemComparator(const aura::Window* target_window)
: target(target_window) {
}
bool operator()(WindowSelectorItem* window) const {
return window->TargetedWindow(target) != NULL;
}
const aura::Window* target;
};
// An observer which holds onto the passed widget until the animation is
// complete.
class CleanupWidgetAfterAnimationObserver : public ui::LayerAnimationObserver {
public:
explicit CleanupWidgetAfterAnimationObserver(
scoped_ptr<views::Widget> widget);
// ui::LayerAnimationObserver:
virtual void OnLayerAnimationEnded(
ui::LayerAnimationSequence* sequence) OVERRIDE;
virtual void OnLayerAnimationAborted(
ui::LayerAnimationSequence* sequence) OVERRIDE;
virtual void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) OVERRIDE;
private:
virtual ~CleanupWidgetAfterAnimationObserver();
scoped_ptr<views::Widget> widget_;
DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver);
};
CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver(
scoped_ptr<views::Widget> widget)
: widget_(widget.Pass()) {
widget_->GetNativeWindow()->layer()->GetAnimator()->AddObserver(this);
}
CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() {
widget_->GetNativeWindow()->layer()->GetAnimator()->RemoveObserver(this);
}
void CleanupWidgetAfterAnimationObserver::OnLayerAnimationEnded(
ui::LayerAnimationSequence* sequence) {
delete this;
}
void CleanupWidgetAfterAnimationObserver::OnLayerAnimationAborted(
ui::LayerAnimationSequence* sequence) {
delete this;
}
void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) {
}
} // namespace
WindowOverview::WindowOverview(WindowSelector* window_selector,
WindowSelectorItemList* windows)
: window_selector_(window_selector),
windows_(windows),
selection_index_(0),
overview_start_time_(base::Time::Now()),
cursor_client_(NULL) {
Shell* shell = Shell::GetInstance();
shell->OnOverviewModeStarting();
for (WindowSelectorItemList::iterator iter = windows_->begin();
iter != windows_->end(); ++iter) {
(*iter)->PrepareForOverview();
}
PositionWindows(/* animate */ true);
DCHECK(!windows_->empty());
cursor_client_ = aura::client::GetCursorClient(
windows_->front()->GetRootWindow());
if (cursor_client_) {
cursor_client_->SetCursor(ui::kCursorPointer);
cursor_client_->ShowCursor();
// TODO(flackr): Only prevent cursor changes for windows in the overview.
// This will be easier to do without exposing the overview mode code if the
// cursor changes are moved to ToplevelWindowEventHandler::HandleMouseMoved
// as suggested there.
cursor_client_->LockCursor();
}
shell->PrependPreTargetHandler(this);
shell->GetScreen()->AddObserver(this);
shell->metrics()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW);
HideAndTrackNonOverviewWindows();
// Send an a11y alert.
shell->accessibility_delegate()->TriggerAccessibilityAlert(
A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED);
}
WindowOverview::~WindowOverview() {
const aura::WindowTracker::Windows hidden_windows = hidden_windows_.windows();
for (aura::WindowTracker::Windows::const_iterator iter =
hidden_windows.begin(); iter != hidden_windows.end(); ++iter) {
ui::ScopedLayerAnimationSettings settings(
(*iter)->layer()->GetAnimator());
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
ScopedTransformOverviewWindow::kTransitionMilliseconds));
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
(*iter)->layer()->SetOpacity(1);
(*iter)->Show();
}
if (cursor_client_)
cursor_client_->UnlockCursor();
ash::Shell* shell = ash::Shell::GetInstance();
shell->RemovePreTargetHandler(this);
shell->GetScreen()->RemoveObserver(this);
UMA_HISTOGRAM_MEDIUM_TIMES(
"Ash.WindowSelector.TimeInOverview",
base::Time::Now() - overview_start_time_);
shell->OnOverviewModeEnding();
}
void WindowOverview::SetSelection(size_t index) {
gfx::Rect target_bounds(GetSelectionBounds(index));
if (selection_widget_) {
// If the selection widget is already active, determine the animation to
// use to animate the widget to the new bounds.
int change = static_cast<int>(index) - static_cast<int>(selection_index_);
int windows = static_cast<int>(windows_->size());
// If moving from the first to the last or last to the first index,
// convert the delta to be +/- 1.
if (windows > 2 && abs(change) == windows - 1) {
if (change < 0)
change += windows;
else
change -= windows;
}
if (selection_index_ < windows_->size() &&
(*windows_)[selection_index_]->target_bounds().y() !=
(*windows_)[index]->target_bounds().y() &&
abs(change) == 1) {
// The selection has changed forward or backwards by one with a change
// in the height of the target. In this case create a new selection widget
// to fade in on the new position and animate and fade out the old one.
gfx::Display dst_display = gfx::Screen::GetScreenFor(
selection_widget_->GetNativeWindow())->GetDisplayMatching(
target_bounds);
gfx::Vector2d fade_out_direction(
change * ((*windows_)[selection_index_]->target_bounds().width() +
2 * kWindowMargin), 0);
aura::Window* old_selection = selection_widget_->GetNativeWindow();
// CleanupWidgetAfterAnimationObserver will delete itself (and the
// widget) when the animation is complete.
new CleanupWidgetAfterAnimationObserver(selection_widget_.Pass());
ui::ScopedLayerAnimationSettings animation_settings(
old_selection->layer()->GetAnimator());
animation_settings.SetTransitionDuration(
base::TimeDelta::FromMilliseconds(
kOverviewSelectorTransitionMilliseconds));
animation_settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
old_selection->SetBoundsInScreen(
GetSelectionBounds(selection_index_) + fade_out_direction,
dst_display);
old_selection->Hide();
old_selection->layer()->SetOpacity(0);
InitializeSelectionWidget();
selection_widget_->GetNativeWindow()->SetBoundsInScreen(
target_bounds - fade_out_direction, dst_display);
// New selection widget starts with 0 opacity and fades in.
selection_widget_->GetNativeWindow()->layer()->SetOpacity(0);
}
ui::ScopedLayerAnimationSettings animation_settings(
selection_widget_->GetNativeWindow()->layer()->GetAnimator());
animation_settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
kOverviewSelectorTransitionMilliseconds));
animation_settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
selection_widget_->SetBounds(target_bounds);
selection_widget_->GetNativeWindow()->layer()->SetOpacity(
kWindowOverviewSelectionOpacity);
} else {
InitializeSelectionWidget();
selection_widget_->SetBounds(target_bounds);
}
selection_index_ = index;
}
void WindowOverview::OnWindowsChanged() {
PositionWindows(/* animate */ true);
}
void WindowOverview::OnKeyEvent(ui::KeyEvent* event) {
if (GetTargetedWindow(static_cast<aura::Window*>(event->target())))
event->StopPropagation();
if (event->type() != ui::ET_KEY_PRESSED)
return;
if (event->key_code() == ui::VKEY_ESCAPE)
window_selector_->CancelSelection();
}
void WindowOverview::OnMouseEvent(ui::MouseEvent* event) {
aura::Window* target = GetEventTarget(event);
if (!target)
return;
event->SetHandled();
if (event->type() != ui::ET_MOUSE_RELEASED)
return;
window_selector_->SelectWindow(target);
}
void WindowOverview::OnScrollEvent(ui::ScrollEvent* event) {
// Set the handled flag to prevent delivering scroll events to the window but
// still allowing other pretarget handlers to process the scroll event.
if (GetTargetedWindow(static_cast<aura::Window*>(event->target())))
event->SetHandled();
}
void WindowOverview::OnTouchEvent(ui::TouchEvent* event) {
// Existing touches should be allowed to continue. This prevents getting
// stuck in a gesture or with pressed fingers being tracked elsewhere.
if (event->type() != ui::ET_TOUCH_PRESSED)
return;
aura::Window* target = GetEventTarget(event);
if (!target)
return;
// TODO(flackr): StopPropogation prevents generation of gesture events.
// We should find a better way to prevent events from being delivered to
// the window, perhaps a transparent window in front of the target window
// or using EventClientImpl::CanProcessEventsWithinSubtree and then a tap
// gesture could be used to activate the window.
event->SetHandled();
window_selector_->SelectWindow(target);
}
void WindowOverview::OnDisplayBoundsChanged(const gfx::Display& display) {
PositionWindows(/* animate */ false);
}
void WindowOverview::OnDisplayAdded(const gfx::Display& display) {
}
void WindowOverview::OnDisplayRemoved(const gfx::Display& display) {
}
aura::Window* WindowOverview::GetEventTarget(ui::LocatedEvent* event) {
aura::Window* target = static_cast<aura::Window*>(event->target());
// If the target window doesn't actually contain the event location (i.e.
// mouse down over the window and mouse up elsewhere) then do not select the
// window.
if (!target->ContainsPoint(event->location()))
return NULL;
return GetTargetedWindow(target);
}
aura::Window* WindowOverview::GetTargetedWindow(aura::Window* window) {
for (WindowSelectorItemList::iterator iter = windows_->begin();
iter != windows_->end(); ++iter) {
aura::Window* selected = (*iter)->TargetedWindow(window);
if (selected)
return selected;
}
return NULL;
}
void WindowOverview::HideAndTrackNonOverviewWindows() {
// Add the windows to hidden_windows first so that if any are destroyed
// while hiding them they are tracked.
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
for (aura::Window::Windows::const_iterator root_iter = root_windows.begin();
root_iter != root_windows.end(); ++root_iter) {
for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) {
aura::Window* container = Shell::GetContainer(*root_iter,
kSwitchableWindowContainerIds[i]);
for (aura::Window::Windows::const_iterator iter =
container->children().begin(); iter != container->children().end();
++iter) {
if (GetTargetedWindow(*iter) || !(*iter)->IsVisible())
continue;
hidden_windows_.Add(*iter);
}
}
}
// Copy the window list as it can change during iteration.
const aura::WindowTracker::Windows hidden_windows(hidden_windows_.windows());
for (aura::WindowTracker::Windows::const_iterator iter =
hidden_windows.begin(); iter != hidden_windows.end(); ++iter) {
if (!hidden_windows_.Contains(*iter))
continue;
ui::ScopedLayerAnimationSettings settings(
(*iter)->layer()->GetAnimator());
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
ScopedTransformOverviewWindow::kTransitionMilliseconds));
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
(*iter)->Hide();
// Hiding the window can result in it being destroyed.
if (!hidden_windows_.Contains(*iter))
continue;
(*iter)->layer()->SetOpacity(0);
}
}
void WindowOverview::PositionWindows(bool 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,
bool animate) {
std::vector<WindowSelectorItem*> windows;
for (WindowSelectorItemList::iterator iter = windows_->begin();
iter != windows_->end(); ++iter) {
if ((*iter)->GetRootWindow() == root_window)
windows.push_back(*iter);
}
if (windows.empty())
return;
gfx::Size window_size;
gfx::Rect total_bounds = ScreenUtil::ConvertRectToScreen(
root_window,
ScreenUtil::GetDisplayWorkAreaBoundsInParent(
Shell::GetContainer(root_window, kShellWindowId_DefaultContainer)));
// 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) {
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);
windows[i]->SetBounds(root_window, target_bounds, animate);
}
}
void WindowOverview::InitializeSelectionWidget() {
selection_widget_.reset(new views::Widget);
views::Widget::InitParams params;
params.type = views::Widget::InitParams::TYPE_POPUP;
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(windows_->front()->GetRootWindow(),
kShellWindowId_DefaultContainer);
params.accept_events = false;
selection_widget_->set_focus_on_creation(false);
selection_widget_->Init(params);
views::View* content_view = new views::View;
content_view->set_background(
views::Background::CreateSolidBackground(kWindowOverviewSelectionColor));
selection_widget_->SetContentsView(content_view);
selection_widget_->Show();
selection_widget_->GetNativeWindow()->parent()->StackChildAtBottom(
selection_widget_->GetNativeWindow());
selection_widget_->GetNativeWindow()->layer()->SetOpacity(
kWindowOverviewSelectionOpacity);
}
gfx::Rect WindowOverview::GetSelectionBounds(size_t index) {
gfx::Rect bounds((*windows_)[index]->bounds());
bounds.Inset(-kWindowOverviewSelectionPadding,
-kWindowOverviewSelectionPadding);
return bounds;
}
} // namespace ash
// 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_OVERVIEW_WINDOW_OVERVIEW_H_
#define ASH_WM_OVERVIEW_WINDOW_OVERVIEW_H_
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
#include "base/time/time.h"
#include "ui/aura/window_tracker.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/display_observer.h"
#include "ui/gfx/rect.h"
namespace aura {
class Window;
namespace client {
class CursorClient;
}
} // namespace aura
namespace ui {
class LocatedEvent;
}
namespace views {
class Widget;
}
namespace ash {
class WindowSelector;
class WindowSelectorItem;
// The WindowOverview shows a grid of all of your windows and allows selecting
// a window by clicking or tapping on it. It also displays a selection widget
// used to indicate the current selection when alt-tabbing between windows.
class WindowOverview : public ui::EventHandler,
public gfx::DisplayObserver {
public:
typedef ScopedVector<WindowSelectorItem> WindowSelectorItemList;
// Enters an overview mode displaying |windows| and dispatches methods
// on |window_selector| when a window is selected or selection is canceled.
// If |single_root_window| is not NULL, all windows will be positioned on the
// given root window.
WindowOverview(WindowSelector* window_selector,
WindowSelectorItemList* windows);
virtual ~WindowOverview();
// Sets the selected window to be the window in position |index|.
void SetSelection(size_t index);
// Dispatched when the list of windows has changed.
void OnWindowsChanged();
// ui::EventHandler:
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
// gfx::DisplayObserver:
virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE;
virtual void OnDisplayAdded(const gfx::Display& display) OVERRIDE;
virtual void OnDisplayRemoved(const gfx::Display& display) OVERRIDE;
private:
// Returns the target of |event| or NULL if the event is not targeted at
// any of the windows in the selector.
aura::Window* GetEventTarget(ui::LocatedEvent* event);
// Returns the top-level window selected by targeting |window| or NULL if
// no overview window was found for |window|.
aura::Window* GetTargetedWindow(aura::Window* window);
// Hide and track all hidden windows not in overview.
void HideAndTrackNonOverviewWindows();
// Position all of the windows based on the current selection mode.
void PositionWindows(bool animate);
// Position all of the windows from |root_window| on |root_window|.
void PositionWindowsFromRoot(aura::Window* root_window, bool animate);
// Creates the selection widget.
void InitializeSelectionWidget();
// Returns the bounds for the selection widget for the windows_ at |index|.
gfx::Rect GetSelectionBounds(size_t index);
// Weak pointer to the window selector which owns this class.
WindowSelector* window_selector_;
// A weak pointer to the collection of windows in the overview wrapped by a
// helper class which restores their state and helps transform them to other
// root windows.
WindowSelectorItemList* windows_;
// Widget indicating which window is currently selected.
scoped_ptr<views::Widget> selection_widget_;
// Index of the currently selected window. This is used to determine when the
// selection changes rows and use a different animation.
size_t selection_index_;
// The time when overview was started.
base::Time overview_start_time_;
// The cursor client used to lock the current cursor during overview.
aura::client::CursorClient* cursor_client_;
// Tracks windows which were hidden because they were not part of the
// overview.
aura::WindowTracker hidden_windows_;
DISALLOW_COPY_AND_ASSIGN(WindowOverview);
};
} // namespace ash
#endif // ASH_WM_OVERVIEW_WINDOW_OVERVIEW_H_
...@@ -6,12 +6,17 @@ ...@@ -6,12 +6,17 @@
#include <algorithm> #include <algorithm>
#include "ash/accessibility_delegate.h"
#include "ash/ash_switches.h" #include "ash/ash_switches.h"
#include "ash/metrics/user_metrics_recorder.h"
#include "ash/root_window_controller.h" #include "ash/root_window_controller.h"
#include "ash/screen_util.h"
#include "ash/shell.h" #include "ash/shell.h"
#include "ash/shell_window_ids.h"
#include "ash/switchable_windows.h" #include "ash/switchable_windows.h"
#include "ash/wm/overview/window_overview.h" #include "ash/wm/overview/scoped_transform_overview_window.h"
#include "ash/wm/overview/window_selector_delegate.h" #include "ash/wm/overview/window_selector_delegate.h"
#include "ash/wm/overview/window_selector_item.h"
#include "ash/wm/overview/window_selector_panels.h" #include "ash/wm/overview/window_selector_panels.h"
#include "ash/wm/overview/window_selector_window.h" #include "ash/wm/overview/window_selector_window.h"
#include "ash/wm/window_state.h" #include "ash/wm/window_state.h"
...@@ -19,9 +24,18 @@ ...@@ -19,9 +24,18 @@
#include "base/command_line.h" #include "base/command_line.h"
#include "base/metrics/histogram.h" #include "base/metrics/histogram.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/focus_client.h" #include "ui/aura/client/focus_client.h"
#include "ui/aura/window.h" #include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/aura/window_observer.h" #include "ui/aura/window_observer.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/events/event.h"
#include "ui/gfx/screen.h"
#include "ui/views/background.h"
#include "ui/views/widget/widget.h"
#include "ui/wm/core/window_util.h" #include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_client.h" #include "ui/wm/public/activation_client.h"
...@@ -29,20 +43,81 @@ namespace ash { ...@@ -29,20 +43,81 @@ namespace ash {
namespace { namespace {
// A comparator for locating a given selectable window. // Conceptually the window overview is a table or grid of cells having this
// fixed aspect ratio. The number of columns is determined by maximizing the
// area of them based on the number of windows.
const float kCardAspectRatio = 4.0f / 3.0f;
// In the conceptual overview table, the window margin is the space reserved
// around the window within the cell. This margin does not overlap so the
// closest distance between adjacent windows will be twice this amount.
const int kWindowMargin = 30;
// The minimum number of cards along the major axis (i.e. horizontally on a
// landscape orientation).
const int kMinCardsMajor = 3;
// A comparator for locating a given target window.
struct WindowSelectorItemComparator struct WindowSelectorItemComparator
: public std::unary_function<WindowSelectorItem*, bool> { : public std::unary_function<WindowSelectorItem*, bool> {
explicit WindowSelectorItemComparator(const aura::Window* window) explicit WindowSelectorItemComparator(const aura::Window* target_window)
: window_(window) { : target(target_window) {
} }
bool operator()(WindowSelectorItem* window) const { bool operator()(WindowSelectorItem* window) const {
return window->HasSelectableWindow(window_); return window->HasSelectableWindow(target);
} }
const aura::Window* window_; const aura::Window* target;
};
// An observer which holds onto the passed widget until the animation is
// complete.
class CleanupWidgetAfterAnimationObserver : public ui::LayerAnimationObserver {
public:
explicit CleanupWidgetAfterAnimationObserver(
scoped_ptr<views::Widget> widget);
// ui::LayerAnimationObserver:
virtual void OnLayerAnimationEnded(
ui::LayerAnimationSequence* sequence) OVERRIDE;
virtual void OnLayerAnimationAborted(
ui::LayerAnimationSequence* sequence) OVERRIDE;
virtual void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) OVERRIDE;
private:
virtual ~CleanupWidgetAfterAnimationObserver();
scoped_ptr<views::Widget> widget_;
DISALLOW_COPY_AND_ASSIGN(CleanupWidgetAfterAnimationObserver);
}; };
CleanupWidgetAfterAnimationObserver::CleanupWidgetAfterAnimationObserver(
scoped_ptr<views::Widget> widget)
: widget_(widget.Pass()) {
widget_->GetNativeWindow()->layer()->GetAnimator()->AddObserver(this);
}
CleanupWidgetAfterAnimationObserver::~CleanupWidgetAfterAnimationObserver() {
widget_->GetNativeWindow()->layer()->GetAnimator()->RemoveObserver(this);
}
void CleanupWidgetAfterAnimationObserver::OnLayerAnimationEnded(
ui::LayerAnimationSequence* sequence) {
delete this;
}
void CleanupWidgetAfterAnimationObserver::OnLayerAnimationAborted(
ui::LayerAnimationSequence* sequence) {
delete this;
}
void CleanupWidgetAfterAnimationObserver::OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) {
}
// A comparator for locating a selectable window given a targeted window. // A comparator for locating a selectable window given a targeted window.
struct WindowSelectorItemTargetComparator struct WindowSelectorItemTargetComparator
: public std::unary_function<WindowSelectorItem*, bool> { : public std::unary_function<WindowSelectorItem*, bool> {
...@@ -146,14 +221,41 @@ WindowSelector::WindowSelector(const WindowList& windows, ...@@ -146,14 +221,41 @@ WindowSelector::WindowSelector(const WindowList& windows,
} }
WindowSelector::~WindowSelector() { WindowSelector::~WindowSelector() {
ash::Shell* shell = ash::Shell::GetInstance();
ResetFocusRestoreWindow(true); ResetFocusRestoreWindow(true);
for (std::set<aura::Window*>::iterator iter = observed_windows_.begin(); for (std::set<aura::Window*>::iterator iter = observed_windows_.begin();
iter != observed_windows_.end(); ++iter) { iter != observed_windows_.end(); ++iter) {
(*iter)->RemoveObserver(this); (*iter)->RemoveObserver(this);
} }
Shell::GetInstance()->activation_client()->RemoveObserver(this); shell->activation_client()->RemoveObserver(this);
aura::Window::Windows root_windows = Shell::GetAllRootWindows(); aura::Window::Windows root_windows = Shell::GetAllRootWindows();
window_overview_.reset();
const aura::WindowTracker::Windows hidden_windows(hidden_windows_.windows());
for (aura::WindowTracker::Windows::const_iterator iter =
hidden_windows.begin(); iter != hidden_windows.end(); ++iter) {
ui::ScopedLayerAnimationSettings settings(
(*iter)->layer()->GetAnimator());
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
ScopedTransformOverviewWindow::kTransitionMilliseconds));
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
(*iter)->layer()->SetOpacity(1);
(*iter)->Show();
}
if (cursor_client_)
cursor_client_->UnlockCursor();
shell->RemovePreTargetHandler(this);
shell->GetScreen()->RemoveObserver(this);
UMA_HISTOGRAM_MEDIUM_TIMES(
"Ash.WindowSelector.TimeInOverview",
base::Time::Now() - overview_start_time_);
// TODO(nsatragno): Change this to OnOverviewModeEnded and move it to when
// everything is done.
shell->OnOverviewModeEnding();
// Clearing the window list resets the ignored_by_shelf flag on the windows. // Clearing the window list resets the ignored_by_shelf flag on the windows.
windows_.clear(); windows_.clear();
UpdateShelfVisibility(); UpdateShelfVisibility();
...@@ -175,6 +277,64 @@ void WindowSelector::CancelSelection() { ...@@ -175,6 +277,64 @@ void WindowSelector::CancelSelection() {
delegate_->OnSelectionCanceled(); delegate_->OnSelectionCanceled();
} }
void WindowSelector::OnKeyEvent(ui::KeyEvent* event) {
if (GetTargetedWindow(static_cast<aura::Window*>(event->target())))
event->StopPropagation();
if (event->type() != ui::ET_KEY_PRESSED)
return;
if (event->key_code() == ui::VKEY_ESCAPE)
CancelSelection();
}
void WindowSelector::OnMouseEvent(ui::MouseEvent* event) {
aura::Window* target = GetEventTarget(event);
if (!target)
return;
event->SetHandled();
if (event->type() != ui::ET_MOUSE_RELEASED)
return;
SelectWindow(target);
}
void WindowSelector::OnScrollEvent(ui::ScrollEvent* event) {
// Set the handled flag to prevent delivering scroll events to the window but
// still allowing other pretarget handlers to process the scroll event.
if (GetTargetedWindow(static_cast<aura::Window*>(event->target())))
event->SetHandled();
}
void WindowSelector::OnTouchEvent(ui::TouchEvent* event) {
// Existing touches should be allowed to continue. This prevents getting
// stuck in a gesture or with pressed fingers being tracked elsewhere.
if (event->type() != ui::ET_TOUCH_PRESSED)
return;
aura::Window* target = GetEventTarget(event);
if (!target)
return;
// TODO(flackr): StopPropogation prevents generation of gesture events.
// We should find a better way to prevent events from being delivered to
// the window, perhaps a transparent window in front of the target window
// or using EventClientImpl::CanProcessEventsWithinSubtree and then a tap
// gesture could be used to activate the window.
event->SetHandled();
SelectWindow(target);
}
void WindowSelector::OnDisplayBoundsChanged(const gfx::Display& display) {
PositionWindows(/* animate */ false);
}
void WindowSelector::OnDisplayAdded(const gfx::Display& display) {
}
void WindowSelector::OnDisplayRemoved(const gfx::Display& display) {
}
void WindowSelector::OnWindowAdded(aura::Window* new_window) { void WindowSelector::OnWindowAdded(aura::Window* new_window) {
if (new_window->type() != ui::wm::WINDOW_TYPE_NORMAL && if (new_window->type() != ui::wm::WINDOW_TYPE_NORMAL &&
new_window->type() != ui::wm::WINDOW_TYPE_PANEL) { new_window->type() != ui::wm::WINDOW_TYPE_PANEL) {
...@@ -215,16 +375,12 @@ void WindowSelector::OnWindowDestroying(aura::Window* window) { ...@@ -215,16 +375,12 @@ void WindowSelector::OnWindowDestroying(aura::Window* window) {
CancelSelection(); CancelSelection();
return; return;
} }
if (window_overview_) PositionWindows(true);
window_overview_->OnWindowsChanged();
} }
void WindowSelector::OnWindowBoundsChanged(aura::Window* window, void WindowSelector::OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds, const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) { const gfx::Rect& new_bounds) {
if (!window_overview_)
return;
ScopedVector<WindowSelectorItem>::iterator iter = ScopedVector<WindowSelectorItem>::iterator iter =
std::find_if(windows_.begin(), windows_.end(), std::find_if(windows_.begin(), windows_.end(),
WindowSelectorItemTargetComparator(window)); WindowSelectorItemTargetComparator(window));
...@@ -258,15 +414,135 @@ void WindowSelector::OnAttemptToReactivateWindow(aura::Window* request_active, ...@@ -258,15 +414,135 @@ void WindowSelector::OnAttemptToReactivateWindow(aura::Window* request_active,
} }
void WindowSelector::StartOverview() { void WindowSelector::StartOverview() {
DCHECK(!window_overview_);
// Remove focus from active window before entering overview. // Remove focus from active window before entering overview.
aura::client::GetFocusClient( aura::client::GetFocusClient(
Shell::GetPrimaryRootWindow())->FocusWindow(NULL); Shell::GetPrimaryRootWindow())->FocusWindow(NULL);
window_overview_.reset(new WindowOverview(this, &windows_)); Shell* shell = Shell::GetInstance();
shell->OnOverviewModeStarting();
for (WindowSelectorItemList::iterator iter = windows_.begin();
iter != windows_.end(); ++iter) {
(*iter)->PrepareForOverview();
}
PositionWindows(/* animate */ true);
DCHECK(!windows_.empty());
cursor_client_ = aura::client::GetCursorClient(
windows_.front()->GetRootWindow());
if (cursor_client_) {
cursor_client_->SetCursor(ui::kCursorPointer);
cursor_client_->ShowCursor();
// TODO(flackr): Only prevent cursor changes for windows in the overview.
// This will be easier to do without exposing the overview mode code if the
// cursor changes are moved to ToplevelWindowEventHandler::HandleMouseMoved
// as suggested there.
cursor_client_->LockCursor();
}
shell->PrependPreTargetHandler(this);
shell->GetScreen()->AddObserver(this);
shell->metrics()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW);
HideAndTrackNonOverviewWindows();
// Send an a11y alert.
shell->accessibility_delegate()->TriggerAccessibilityAlert(
A11Y_ALERT_WINDOW_OVERVIEW_MODE_ENTERED);
UpdateShelfVisibility(); UpdateShelfVisibility();
} }
void WindowSelector::PositionWindows(bool 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 WindowSelector::PositionWindowsFromRoot(aura::Window* root_window,
bool animate) {
std::vector<WindowSelectorItem*> windows;
for (WindowSelectorItemList::iterator iter = windows_.begin();
iter != windows_.end(); ++iter) {
if ((*iter)->GetRootWindow() == root_window)
windows.push_back(*iter);
}
if (windows.empty())
return;
gfx::Size window_size;
gfx::Rect total_bounds = ScreenUtil::ConvertRectToScreen(
root_window,
ScreenUtil::GetDisplayWorkAreaBoundsInParent(
Shell::GetContainer(root_window, kShellWindowId_DefaultContainer)));
// 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) {
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);
windows[i]->SetBounds(root_window, target_bounds, animate);
}
}
void WindowSelector::HideAndTrackNonOverviewWindows() {
// Add the windows to hidden_windows first so that if any are destroyed
// while hiding them they are tracked.
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
for (aura::Window::Windows::const_iterator root_iter = root_windows.begin();
root_iter != root_windows.end(); ++root_iter) {
for (size_t i = 0; i < kSwitchableWindowContainerIdsLength; ++i) {
aura::Window* container = Shell::GetContainer(*root_iter,
kSwitchableWindowContainerIds[i]);
for (aura::Window::Windows::const_iterator iter =
container->children().begin(); iter != container->children().end();
++iter) {
if (GetTargetedWindow(*iter) || !(*iter)->IsVisible())
continue;
hidden_windows_.Add(*iter);
}
}
}
// Copy the window list as it can change during iteration.
const aura::WindowTracker::Windows hidden_windows(hidden_windows_.windows());
for (aura::WindowTracker::Windows::const_iterator iter =
hidden_windows.begin(); iter != hidden_windows.end(); ++iter) {
if (!hidden_windows_.Contains(*iter))
continue;
ui::ScopedLayerAnimationSettings settings(
(*iter)->layer()->GetAnimator());
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
ScopedTransformOverviewWindow::kTransitionMilliseconds));
settings.SetPreemptionStrategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
(*iter)->Hide();
// Hiding the window can result in it being destroyed.
if (!hidden_windows_.Contains(*iter))
continue;
(*iter)->layer()->SetOpacity(0);
}
}
void WindowSelector::ResetFocusRestoreWindow(bool focus) { void WindowSelector::ResetFocusRestoreWindow(bool focus) {
if (!restore_focus_window_) if (!restore_focus_window_)
return; return;
...@@ -283,4 +559,25 @@ void WindowSelector::ResetFocusRestoreWindow(bool focus) { ...@@ -283,4 +559,25 @@ void WindowSelector::ResetFocusRestoreWindow(bool focus) {
restore_focus_window_ = NULL; restore_focus_window_ = NULL;
} }
aura::Window* WindowSelector::GetEventTarget(ui::LocatedEvent* event) {
aura::Window* target = static_cast<aura::Window*>(event->target());
// If the target window doesn't actually contain the event location (i.e.
// mouse down over the window and mouse up elsewhere) then do not select the
// window.
if (!target->ContainsPoint(event->location()))
return NULL;
return GetTargetedWindow(target);
}
aura::Window* WindowSelector::GetTargetedWindow(aura::Window* window) {
for (WindowSelectorItemList::iterator iter = windows_.begin();
iter != windows_.end(); ++iter) {
aura::Window* selected = (*iter)->TargetedWindow(window);
if (selected)
return selected;
}
return NULL;
}
} // namespace ash } // namespace ash
...@@ -14,25 +14,42 @@ ...@@ -14,25 +14,42 @@
#include "base/memory/scoped_vector.h" #include "base/memory/scoped_vector.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "ui/aura/window_observer.h" #include "ui/aura/window_observer.h"
#include "ui/aura/window_tracker.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/display_observer.h"
#include "ui/wm/public/activation_change_observer.h" #include "ui/wm/public/activation_change_observer.h"
namespace aura { namespace aura {
class RootWindow; class RootWindow;
class Window;
namespace client {
class CursorClient;
} // namespace client
} // namespace aura
namespace gfx {
class Rect;
}
namespace ui {
class LocatedEvent;
} }
namespace ash { namespace ash {
class WindowOverview;
class WindowSelectorDelegate; class WindowSelectorDelegate;
class WindowSelectorItem; class WindowSelectorItem;
class WindowSelectorTest; class WindowSelectorTest;
// The WindowSelector allows selecting a window by clicking or tapping on it // The WindowSelector shows a grid of all of your windows, allowing to select
// (entering Overview Mode). // one by clicking or tapping on it.
class ASH_EXPORT WindowSelector class ASH_EXPORT WindowSelector
: public aura::WindowObserver, : public ui::EventHandler,
public gfx::DisplayObserver,
public aura::WindowObserver,
public aura::client::ActivationChangeObserver { public aura::client::ActivationChangeObserver {
public: public:
typedef std::vector<aura::Window*> WindowList; typedef std::vector<aura::Window*> WindowList;
typedef ScopedVector<WindowSelectorItem> WindowSelectorItemList;
WindowSelector(const WindowList& windows, WindowSelector(const WindowList& windows,
WindowSelectorDelegate* delegate); WindowSelectorDelegate* delegate);
...@@ -44,6 +61,17 @@ class ASH_EXPORT WindowSelector ...@@ -44,6 +61,17 @@ class ASH_EXPORT WindowSelector
// Cancels window selection. // Cancels window selection.
void CancelSelection(); void CancelSelection();
// ui::EventHandler:
virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
virtual void OnMouseEvent(ui::MouseEvent* event) OVERRIDE;
virtual void OnScrollEvent(ui::ScrollEvent* event) OVERRIDE;
virtual void OnTouchEvent(ui::TouchEvent* event) OVERRIDE;
// gfx::DisplayObserver:
virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE;
virtual void OnDisplayAdded(const gfx::Display& display) OVERRIDE;
virtual void OnDisplayRemoved(const gfx::Display& display) OVERRIDE;
// aura::WindowObserver: // aura::WindowObserver:
virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE; virtual void OnWindowAdded(aura::Window* new_window) OVERRIDE;
virtual void OnWindowBoundsChanged(aura::Window* window, virtual void OnWindowBoundsChanged(aura::Window* window,
...@@ -51,7 +79,7 @@ class ASH_EXPORT WindowSelector ...@@ -51,7 +79,7 @@ class ASH_EXPORT WindowSelector
const gfx::Rect& new_bounds) OVERRIDE; const gfx::Rect& new_bounds) OVERRIDE;
virtual void OnWindowDestroying(aura::Window* window) OVERRIDE; virtual void OnWindowDestroying(aura::Window* window) OVERRIDE;
// Overridden from aura::client::ActivationChangeObserver: // aura::client::ActivationChangeObserver:
virtual void OnWindowActivated(aura::Window* gained_active, virtual void OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) OVERRIDE; aura::Window* lost_active) OVERRIDE;
virtual void OnAttemptToReactivateWindow( virtual void OnAttemptToReactivateWindow(
...@@ -64,10 +92,26 @@ class ASH_EXPORT WindowSelector ...@@ -64,10 +92,26 @@ class ASH_EXPORT WindowSelector
// Begins positioning windows such that all windows are visible on the screen. // Begins positioning windows such that all windows are visible on the screen.
void StartOverview(); void StartOverview();
// Position all of the windows based on the current selection mode.
void PositionWindows(bool animate);
// Position all of the windows from |root_window| on |root_window|.
void PositionWindowsFromRoot(aura::Window* root_window, bool animate);
// Resets the stored window from RemoveFocusAndSetRestoreWindow to NULL. If // Resets the stored window from RemoveFocusAndSetRestoreWindow to NULL. If
// Hide and track all hidden windows not in overview.
void HideAndTrackNonOverviewWindows();
// |focus|, restores focus to the stored window. // |focus|, restores focus to the stored window.
void ResetFocusRestoreWindow(bool focus); void ResetFocusRestoreWindow(bool focus);
// Returns the target of |event| or NULL if the event is not targeted at
// any of the windows in the selector.
aura::Window* GetEventTarget(ui::LocatedEvent* event);
// Returns the top-level window selected by targeting |window| or NULL if
// no overview window was found for |window|.
aura::Window* GetTargetedWindow(aura::Window* window);
// The collection of items in the overview wrapped by a helper class which // The collection of items in the overview wrapped by a helper class which
// restores their state and helps transform them to other root windows. // restores their state and helps transform them to other root windows.
ScopedVector<WindowSelectorItem> windows_; ScopedVector<WindowSelectorItem> windows_;
...@@ -75,8 +119,6 @@ class ASH_EXPORT WindowSelector ...@@ -75,8 +119,6 @@ class ASH_EXPORT WindowSelector
// Tracks observed windows. // Tracks observed windows.
std::set<aura::Window*> observed_windows_; std::set<aura::Window*> observed_windows_;
scoped_ptr<WindowOverview> window_overview_;
// Weak pointer to the selector delegate which will be called when a // Weak pointer to the selector delegate which will be called when a
// selection is made. // selection is made.
WindowSelectorDelegate* delegate_; WindowSelectorDelegate* delegate_;
...@@ -90,6 +132,16 @@ class ASH_EXPORT WindowSelector ...@@ -90,6 +132,16 @@ class ASH_EXPORT WindowSelector
// used to prevent handling the resulting expected activation. // used to prevent handling the resulting expected activation.
bool ignore_activations_; bool ignore_activations_;
// The cursor client used to lock the current cursor during overview.
aura::client::CursorClient* cursor_client_;
// The time when overview was started.
base::Time overview_start_time_;
// Tracks windows which were hidden because they were not part of the
// overview.
aura::WindowTracker hidden_windows_;
DISALLOW_COPY_AND_ASSIGN(WindowSelector); DISALLOW_COPY_AND_ASSIGN(WindowSelector);
}; };
......
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