summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ash/accelerators/accelerator_controller_unittest.cc10
-rw-r--r--ash/ash_switches.cc4
-rw-r--r--ash/ash_switches.h1
-rw-r--r--ash/wm/overview/scoped_transform_overview_window.cc5
-rw-r--r--ash/wm/overview/scoped_transform_overview_window.h3
-rw-r--r--ash/wm/overview/window_grid.cc32
-rw-r--r--ash/wm/overview/window_grid.h11
-rw-r--r--ash/wm/overview/window_selector.cc146
-rw-r--r--ash/wm/overview/window_selector.h31
-rw-r--r--ash/wm/overview/window_selector_item.cc49
-rw-r--r--ash/wm/overview/window_selector_item.h12
-rw-r--r--ash/wm/overview/window_selector_panels.cc10
-rw-r--r--ash/wm/overview/window_selector_panels.h1
-rw-r--r--ash/wm/overview/window_selector_unittest.cc119
-rw-r--r--ash/wm/overview/window_selector_window.cc5
-rw-r--r--ash/wm/overview/window_selector_window.h1
-rw-r--r--chrome/app/generated_resources.grd6
-rw-r--r--chrome/browser/about_flags.cc6
-rw-r--r--ui/views/controls/textfield/textfield.cc7
-rw-r--r--ui/views/controls/textfield/textfield.h3
20 files changed, 408 insertions, 54 deletions
diff --git a/ash/accelerators/accelerator_controller_unittest.cc b/ash/accelerators/accelerator_controller_unittest.cc
index ab3ec88..4fecc24 100644
--- a/ash/accelerators/accelerator_controller_unittest.cc
+++ b/ash/accelerators/accelerator_controller_unittest.cc
@@ -1230,18 +1230,20 @@ TEST_F(AcceleratorControllerTest, DisallowedWithNoWindow) {
}
// Make sure we don't alert if we do have a window.
- scoped_ptr<aura::Window> window(
- CreateTestWindowInShellWithBounds(gfx::Rect(5, 5, 20, 20)));
- wm::ActivateWindow(window.get());
+ scoped_ptr<aura::Window> window;
for (size_t i = 0; i < kActionsNeedingWindowLength; ++i) {
+ window.reset(CreateTestWindowInShellWithBounds(gfx::Rect(5, 5, 20, 20)));
+ wm::ActivateWindow(window.get());
delegate->TriggerAccessibilityAlert(A11Y_ALERT_NONE);
GetController()->PerformAction(kActionsNeedingWindow[i], dummy);
EXPECT_NE(delegate->GetLastAccessibilityAlert(), A11Y_ALERT_WINDOW_NEEDED);
}
// Don't alert if we have a minimized window either.
- GetController()->PerformAction(WINDOW_MINIMIZE, dummy);
for (size_t i = 0; i < kActionsNeedingWindowLength; ++i) {
+ window.reset(CreateTestWindowInShellWithBounds(gfx::Rect(5, 5, 20, 20)));
+ wm::ActivateWindow(window.get());
+ GetController()->PerformAction(WINDOW_MINIMIZE, dummy);
delegate->TriggerAccessibilityAlert(A11Y_ALERT_NONE);
GetController()->PerformAction(kActionsNeedingWindow[i], dummy);
EXPECT_NE(delegate->GetLastAccessibilityAlert(), A11Y_ALERT_WINDOW_NEEDED);
diff --git a/ash/ash_switches.cc b/ash/ash_switches.cc
index 48b0e93..87ffcc4 100644
--- a/ash/ash_switches.cc
+++ b/ash/ash_switches.cc
@@ -54,6 +54,10 @@ const char kAshEnableMagnifierKeyScroller[] =
"ash-enable-magnifier-key-scroller";
#endif
+// Enables text filtering with the keyboard in Overview Mode.
+const char kAshDisableTextFilteringInOverviewMode[] =
+ "ash-disable-text-filtering-in-overview-mode";
+
// Enables software based mirroring.
const char kAshEnableSoftwareMirroring[] = "ash-enable-software-mirroring";
diff --git a/ash/ash_switches.h b/ash/ash_switches.h
index 0736a33..6edcdb0 100644
--- a/ash/ash_switches.h
+++ b/ash/ash_switches.h
@@ -29,6 +29,7 @@ ASH_EXPORT extern const char kAshDisableTouchExplorationMode[];
#if defined(OS_CHROMEOS)
ASH_EXPORT extern const char kAshEnableMagnifierKeyScroller[];
#endif
+ASH_EXPORT extern const char kAshDisableTextFilteringInOverviewMode[];
ASH_EXPORT extern const char kAshEnableSoftwareMirroring[];
ASH_EXPORT extern const char kAshEnableSystemSounds[];
ASH_EXPORT extern const char kAshEnableTouchViewTesting[];
diff --git a/ash/wm/overview/scoped_transform_overview_window.cc b/ash/wm/overview/scoped_transform_overview_window.cc
index afc5b00..4e334d7 100644
--- a/ash/wm/overview/scoped_transform_overview_window.cc
+++ b/ash/wm/overview/scoped_transform_overview_window.cc
@@ -97,7 +97,8 @@ ScopedTransformOverviewWindow::ScopedTransformOverviewWindow(
ui::SHOW_STATE_MINIMIZED),
ignored_by_shelf_(ash::wm::GetWindowState(window)->ignored_by_shelf()),
overview_started_(false),
- original_transform_(window->layer()->GetTargetTransform()) {
+ original_transform_(window->layer()->GetTargetTransform()),
+ opacity_(window->layer()->GetTargetOpacity()) {
}
ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() {
@@ -119,6 +120,7 @@ ScopedTransformOverviewWindow::~ScopedTransformOverviewWindow() {
ui::SHOW_STATE_MINIMIZED);
}
ash::wm::GetWindowState(window_)->set_ignored_by_shelf(ignored_by_shelf_);
+ window_->layer()->SetOpacity(opacity_);
}
}
@@ -158,6 +160,7 @@ void ScopedTransformOverviewWindow::RestoreWindow() {
void ScopedTransformOverviewWindow::RestoreWindowOnExit() {
minimized_ = false;
original_transform_ = gfx::Transform();
+ opacity_ = 1;
}
void ScopedTransformOverviewWindow::OnWindowDestroyed() {
diff --git a/ash/wm/overview/scoped_transform_overview_window.h b/ash/wm/overview/scoped_transform_overview_window.h
index 7338754..9c31dbf 100644
--- a/ash/wm/overview/scoped_transform_overview_window.h
+++ b/ash/wm/overview/scoped_transform_overview_window.h
@@ -112,6 +112,9 @@ class ScopedTransformOverviewWindow {
// The original transform of the window before entering overview mode.
gfx::Transform original_transform_;
+ // The original opacity of the window before entering overview mode.
+ float opacity_;
+
DISALLOW_COPY_AND_ASSIGN(ScopedTransformOverviewWindow);
};
diff --git a/ash/wm/overview/window_grid.cc b/ash/wm/overview/window_grid.cc
index 99ea37c..04cb593 100644
--- a/ash/wm/overview/window_grid.cc
+++ b/ash/wm/overview/window_grid.cc
@@ -13,6 +13,7 @@
#include "ash/wm/overview/window_selector_panels.h"
#include "ash/wm/overview/window_selector_window.h"
#include "ash/wm/window_state.h"
+#include "base/i18n/string_search.h"
#include "base/memory/scoped_vector.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/aura/window.h"
@@ -197,6 +198,7 @@ void WindowGrid::PositionWindows(bool animate) {
(total_bounds.width() - num_columns_ * window_size.width())) / 2;
int y_offset = total_bounds.y() + (total_bounds.height() -
num_rows * window_size.height()) / 2;
+
for (size_t i = 0; i < window_list_.size(); ++i) {
gfx::Transform transform;
int column = i % num_columns_;
@@ -218,7 +220,7 @@ void WindowGrid::PositionWindows(bool animate) {
MoveSelectionWidgetToTarget(animate);
}
-bool WindowGrid::Move(WindowSelector::Direction direction) {
+bool WindowGrid::Move(WindowSelector::Direction direction, bool animate) {
bool recreate_selection_widget = false;
bool out_of_bounds = false;
if (!selection_widget_) {
@@ -235,7 +237,8 @@ bool WindowGrid::Move(WindowSelector::Direction direction) {
selected_index_ = 0;
break;
}
- } else {
+ }
+ while (SelectedWindow()->dimmed() || selection_widget_) {
switch (direction) {
case WindowSelector::RIGHT:
if (selected_index_ >= window_list_.size() - 1)
@@ -272,9 +275,13 @@ bool WindowGrid::Move(WindowSelector::Direction direction) {
}
break;
}
+ // Exit the loop if we broke free from the grid or found an active item.
+ if (out_of_bounds || !SelectedWindow()->dimmed())
+ break;
}
- MoveSelectionWidget(direction, recreate_selection_widget, out_of_bounds);
+ MoveSelectionWidget(direction, recreate_selection_widget,
+ out_of_bounds, animate);
return out_of_bounds;
}
@@ -289,6 +296,20 @@ bool WindowGrid::Contains(const aura::Window* window) const {
window_list_.end();
}
+void WindowGrid::FilterItems(const base::string16& pattern) {
+ base::i18n::FixedPatternStringSearchIgnoringCaseAndAccents finder(pattern);
+ for (ScopedVector<WindowSelectorItem>::iterator iter = window_list_.begin();
+ iter != window_list_.end(); iter++) {
+ if (finder.Search((*iter)->SelectionWindow()->title(), NULL, NULL)) {
+ (*iter)->SetDimmed(false);
+ } else {
+ (*iter)->SetDimmed(true);
+ if (selection_widget_ && SelectedWindow() == *iter)
+ selection_widget_.reset();
+ }
+ }
+}
+
void WindowGrid::OnWindowDestroying(aura::Window* window) {
window->RemoveObserver(this);
observed_windows_.erase(window);
@@ -383,7 +404,8 @@ void WindowGrid::InitSelectionWidget(WindowSelector::Direction direction) {
void WindowGrid::MoveSelectionWidget(WindowSelector::Direction direction,
bool recreate_selection_widget,
- bool out_of_bounds) {
+ bool out_of_bounds,
+ bool animate) {
// If the selection widget is already active, fade it out in the selection
// direction.
if (selection_widget_ && (recreate_selection_widget || out_of_bounds)) {
@@ -420,7 +442,7 @@ void WindowGrid::MoveSelectionWidget(WindowSelector::Direction direction,
SelectedWindow()->SendFocusAlert();
// The selection widget is moved to the newly selected item in the same
// grid.
- MoveSelectionWidgetToTarget(true);
+ MoveSelectionWidgetToTarget(animate);
}
void WindowGrid::MoveSelectionWidgetToTarget(bool animate) {
diff --git a/ash/wm/overview/window_grid.h b/ash/wm/overview/window_grid.h
index fe4bf78..0b1797b 100644
--- a/ash/wm/overview/window_grid.h
+++ b/ash/wm/overview/window_grid.h
@@ -57,7 +57,7 @@ class ASH_EXPORT WindowGrid : public aura::WindowObserver {
// Updates |selected_index_| according to the specified |direction| and calls
// MoveSelectionWidget(). Returns |true| if the new selection index is out of
// this window grid bounds.
- bool Move(WindowSelector::Direction direction);
+ bool Move(WindowSelector::Direction direction, bool animate);
// Returns the target selected window, or NULL if there is none selected.
WindowSelectorItem* SelectedWindow() const;
@@ -66,6 +66,12 @@ class ASH_EXPORT WindowGrid : public aura::WindowObserver {
// this grid owns.
bool Contains(const aura::Window* window) const;
+ // Dims the items whose titles do not contain |pattern| and prevents their
+ // selection. The pattern has its accents removed and is converted to
+ // lowercase in a l10n sensitive context.
+ // If |pattern| is empty, no item is dimmed.
+ void FilterItems(const base::string16& pattern);
+
// Returns true if the grid has no more windows.
bool empty() const { return window_list_.empty(); }
@@ -98,7 +104,8 @@ class ASH_EXPORT WindowGrid : public aura::WindowObserver {
// Moves the selection widget to the specified |direction|.
void MoveSelectionWidget(WindowSelector::Direction direction,
bool recreate_selection_widget,
- bool out_of_bounds);
+ bool out_of_bounds,
+ bool animate);
// Moves the selection widget to the targeted window.
void MoveSelectionWidgetToTarget(bool animate);
diff --git a/ash/wm/overview/window_selector.cc b/ash/wm/overview/window_selector.cc
index 467aca6..fd8604e 100644
--- a/ash/wm/overview/window_selector.cc
+++ b/ash/wm/overview/window_selector.cc
@@ -19,6 +19,7 @@
#include "ash/wm/overview/window_selector_item.h"
#include "ash/wm/window_state.h"
#include "base/auto_reset.h"
+#include "base/command_line.h"
#include "base/metrics/histogram.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/window.h"
@@ -27,6 +28,8 @@
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/events/event.h"
#include "ui/gfx/screen.h"
+#include "ui/views/border.h"
+#include "ui/views/controls/textfield/textfield.h"
#include "ui/wm/core/window_util.h"
#include "ui/wm/public/activation_client.h"
@@ -34,6 +37,21 @@ namespace ash {
namespace {
+// The proportion of screen width that the text filter takes.
+const float kTextFilterScreenProportion = 0.5;
+
+// The height of the text filter.
+const int kTextFilterHeight = 50;
+
+// Solid shadow length from the text filter.
+const int kVerticalShadowOffset = 1;
+
+// Amount of blur applied to the text filter shadow.
+const int kShadowBlur = 10;
+
+// Text filter shadow color.
+const SkColor kTextFilterShadow = 0xB0000000;
+
// A comparator for locating a grid with a given root window.
struct RootWindowGridComparator
: public std::unary_function<WindowGrid*, bool> {
@@ -87,6 +105,42 @@ void UpdateShelfVisibility() {
}
}
+// Initializes the text filter on the top of the main root window and requests
+// focus on its textfield.
+views::Widget* CreateTextFilter(views::TextfieldController* controller,
+ aura::Window* root_window) {
+ views::Widget* widget = new views::Widget;
+ views::Widget::InitParams params;
+ params.type = views::Widget::InitParams::TYPE_WINDOW_FRAMELESS;
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
+ params.parent =
+ Shell::GetContainer(root_window, ash::kShellWindowId_OverlayContainer);
+ params.accept_events = true;
+ params.bounds = gfx::Rect(
+ root_window->bounds().width() / 2 * (1 - kTextFilterScreenProportion), 0,
+ root_window->bounds().width() * kTextFilterScreenProportion,
+ kTextFilterHeight);
+ widget->Init(params);
+
+ views::Textfield* textfield = new views::Textfield;
+ textfield->set_controller(controller);
+ textfield->SetBackgroundColor(SK_ColorTRANSPARENT);
+ textfield->SetBorder(views::Border::NullBorder());
+ textfield->SetTextColor(SK_ColorWHITE);
+ textfield->SetShadows(gfx::ShadowValues(1, gfx::ShadowValue(
+ gfx::Point(0, kVerticalShadowOffset), kShadowBlur, kTextFilterShadow)));
+ widget->SetContentsView(textfield);
+
+ gfx::Transform transform;
+ transform.Translate(0, -kTextFilterHeight);
+ widget->GetNativeWindow()->SetTransform(transform);
+ widget->Show();
+ textfield->RequestFocus();
+
+ return widget;
+}
+
} // namespace
WindowSelector::WindowSelector(const WindowList& windows,
@@ -98,7 +152,8 @@ WindowSelector::WindowSelector(const WindowList& windows,
selected_grid_index_(0),
overview_start_time_(base::Time::Now()),
num_key_presses_(0),
- num_items_(0) {
+ num_items_(0),
+ showing_selection_widget_(false) {
DCHECK(delegate_);
Shell* shell = Shell::GetInstance();
shell->OnOverviewModeStarting();
@@ -127,13 +182,11 @@ WindowSelector::WindowSelector(const WindowList& windows,
DCHECK(!grid_list_.empty());
UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.Items", num_items_);
- shell->activation_client()->AddObserver(this);
+ text_filter_widget_.reset(
+ CreateTextFilter(this, Shell::GetPrimaryRootWindow()));
- // Remove focus from active window before entering overview.
- aura::client::GetFocusClient(
- Shell::GetPrimaryRootWindow())->FocusWindow(NULL);
+ shell->activation_client()->AddObserver(this);
- shell->PrependPreTargetHandler(this);
shell->GetScreen()->AddObserver(this);
shell->metrics()->RecordUserMetricsAction(UMA_WINDOW_OVERVIEW);
HideAndTrackNonOverviewWindows();
@@ -168,7 +221,6 @@ WindowSelector::~WindowSelector() {
(*iter)->Show();
}
- shell->RemovePreTargetHandler(this);
shell->GetScreen()->RemoveObserver(this);
size_t remaining_items = 0;
@@ -207,34 +259,35 @@ void WindowSelector::OnGridEmpty(WindowGrid* grid) {
CancelSelection();
}
-void WindowSelector::OnKeyEvent(ui::KeyEvent* event) {
- if (event->type() != ui::ET_KEY_PRESSED)
- return;
+bool WindowSelector::HandleKeyEvent(views::Textfield* sender,
+ const ui::KeyEvent& key_event) {
+ if (key_event.type() != ui::ET_KEY_PRESSED)
+ return false;
- switch (event->key_code()) {
+ switch (key_event.key_code()) {
case ui::VKEY_ESCAPE:
CancelSelection();
break;
case ui::VKEY_UP:
num_key_presses_++;
- Move(WindowSelector::UP);
+ Move(WindowSelector::UP, true);
break;
case ui::VKEY_DOWN:
num_key_presses_++;
- Move(WindowSelector::DOWN);
+ Move(WindowSelector::DOWN, true);
break;
case ui::VKEY_RIGHT:
num_key_presses_++;
- Move(WindowSelector::RIGHT);
+ Move(WindowSelector::RIGHT, true);
break;
case ui::VKEY_LEFT:
num_key_presses_++;
- Move(WindowSelector::LEFT);
+ Move(WindowSelector::LEFT, true);
break;
case ui::VKEY_RETURN:
// Ignore if no item is selected.
if (!grid_list_[selected_grid_index_]->is_selecting())
- return;
+ return false;
UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.ArrowKeyPresses",
num_key_presses_);
UMA_HISTOGRAM_CUSTOM_COUNTS(
@@ -246,10 +299,10 @@ void WindowSelector::OnKeyEvent(ui::KeyEvent* event) {
SelectedWindow()->SelectionWindow())->Activate();
break;
default:
- // Not a key we are interested in.
- return;
+ // Not a key we are interested in, allow the textfield to handle it.
+ return false;
}
- event->StopPropagation();
+ return true;
}
void WindowSelector::OnDisplayAdded(const gfx::Display& display) {
@@ -290,8 +343,11 @@ void WindowSelector::OnWindowDestroying(aura::Window* window) {
void WindowSelector::OnWindowActivated(aura::Window* gained_active,
aura::Window* lost_active) {
- if (ignore_activations_ || !gained_active)
+ if (ignore_activations_ ||
+ !gained_active ||
+ gained_active == text_filter_widget_->GetNativeWindow()) {
return;
+ }
ScopedVector<WindowGrid>::iterator grid =
std::find_if(grid_list_.begin(), grid_list_.end(),
@@ -317,6 +373,43 @@ void WindowSelector::OnAttemptToReactivateWindow(aura::Window* request_active,
OnWindowActivated(request_active, actual_active);
}
+void WindowSelector::ContentsChanged(views::Textfield* sender,
+ const base::string16& new_contents) {
+ if (CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kAshDisableTextFilteringInOverviewMode)) {
+ return;
+ }
+
+ bool should_show_selection_widget = !new_contents.empty();
+ if (showing_selection_widget_ != should_show_selection_widget) {
+ ui::ScopedLayerAnimationSettings animation_settings(
+ text_filter_widget_->GetNativeWindow()->layer()->GetAnimator());
+ animation_settings.SetPreemptionStrategy(
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+ animation_settings.SetTweenType(showing_selection_widget_ ?
+ gfx::Tween::FAST_OUT_LINEAR_IN : gfx::Tween::LINEAR_OUT_SLOW_IN);
+
+ gfx::Transform transform;
+ if (should_show_selection_widget)
+ transform.Translate(0, 0);
+ else
+ transform.Translate(0, -kTextFilterHeight);
+
+ text_filter_widget_->GetNativeWindow()->SetTransform(transform);
+ showing_selection_widget_ = should_show_selection_widget;
+ }
+ for (ScopedVector<WindowGrid>::iterator iter = grid_list_.begin();
+ iter != grid_list_.end(); iter++) {
+ (*iter)->FilterItems(new_contents);
+ }
+
+ // If the selection widget is not active, execute a Move() command so that it
+ // shows up on the first undimmed item.
+ if (grid_list_[selected_grid_index_]->is_selecting())
+ return;
+ Move(WindowSelector::RIGHT, false);
+}
+
void WindowSelector::PositionWindows(bool animate) {
for (ScopedVector<WindowGrid>::iterator iter = grid_list_.begin();
iter != grid_list_.end(); iter++) {
@@ -379,16 +472,15 @@ void WindowSelector::ResetFocusRestoreWindow(bool focus) {
restore_focus_window_ = NULL;
}
-void WindowSelector::Move(Direction direction) {
- bool overflowed = grid_list_[selected_grid_index_]->Move(direction);
- if (overflowed) {
- // The grid reported that the movement command corresponds to the next
- // root window, identify it and call Move() on it to initialize the
- // selection widget.
+void WindowSelector::Move(Direction direction, bool animate) {
+ // Keep calling Move() on the grids until one of them reports no overflow or
+ // we made a full cycle on all the grids.
+ for (size_t i = 0;
+ i <= grid_list_.size() &&
+ grid_list_[selected_grid_index_]->Move(direction, animate); i++) {
// TODO(nsatragno): If there are more than two monitors, move between grids
// in the requested direction.
selected_grid_index_ = (selected_grid_index_ + 1) % grid_list_.size();
- grid_list_[selected_grid_index_]->Move(direction);
}
}
diff --git a/ash/wm/overview/window_selector.h b/ash/wm/overview/window_selector.h
index f9ca51a..2b44700 100644
--- a/ash/wm/overview/window_selector.h
+++ b/ash/wm/overview/window_selector.h
@@ -17,6 +17,7 @@
#include "ui/aura/window_tracker.h"
#include "ui/events/event_handler.h"
#include "ui/gfx/display_observer.h"
+#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/wm/public/activation_change_observer.h"
namespace aura {
@@ -32,6 +33,11 @@ namespace ui {
class LocatedEvent;
}
+namespace views {
+class Textfield;
+class Widget;
+}
+
namespace ash {
class WindowSelectorDelegate;
class WindowSelectorItem;
@@ -41,10 +47,10 @@ class WindowGrid;
// The WindowSelector shows a grid of all of your windows, allowing to select
// one by clicking or tapping on it.
class ASH_EXPORT WindowSelector
- : public ui::EventHandler,
- public gfx::DisplayObserver,
+ : public gfx::DisplayObserver,
public aura::WindowObserver,
- public aura::client::ActivationChangeObserver {
+ public aura::client::ActivationChangeObserver,
+ public views::TextfieldController {
public:
enum Direction {
LEFT,
@@ -66,9 +72,6 @@ class ASH_EXPORT WindowSelector
// Called when the last window selector item from a grid is deleted.
void OnGridEmpty(WindowGrid* grid);
- // ui::EventHandler:
- virtual void OnKeyEvent(ui::KeyEvent* event) OVERRIDE;
-
// gfx::DisplayObserver:
virtual void OnDisplayAdded(const gfx::Display& display) OVERRIDE;
virtual void OnDisplayRemoved(const gfx::Display& display) OVERRIDE;
@@ -86,6 +89,12 @@ class ASH_EXPORT WindowSelector
aura::Window* request_active,
aura::Window* actual_active) OVERRIDE;
+ // views::TextfieldController:
+ virtual void ContentsChanged(views::Textfield* sender,
+ const base::string16& new_contents) OVERRIDE;
+ virtual bool HandleKeyEvent(views::Textfield* sender,
+ const ui::KeyEvent& key_event) OVERRIDE;
+
private:
friend class WindowSelectorTest;
@@ -103,7 +112,7 @@ class ASH_EXPORT WindowSelector
// Helper function that moves the selection widget to |direction| on the
// corresponding window grid.
- void Move(Direction direction);
+ void Move(Direction direction, bool animate);
// Tracks observed windows.
std::set<aura::Window*> observed_windows_;
@@ -142,6 +151,14 @@ class ASH_EXPORT WindowSelector
// The number of items in the overview.
size_t num_items_;
+ // Indicates if we are showing the selection widget.
+ bool showing_selection_widget_;
+
+ // Window text filter widget. As the user writes on it, we filter the items
+ // in the overview. It is also responsible for handling overview key events,
+ // such as enter key to select.
+ scoped_ptr<views::Widget> text_filter_widget_;
+
DISALLOW_COPY_AND_ASSIGN(WindowSelector);
};
diff --git a/ash/wm/overview/window_selector_item.cc b/ash/wm/overview/window_selector_item.cc
index e6ce66e..cd3bbaf 100644
--- a/ash/wm/overview/window_selector_item.cc
+++ b/ash/wm/overview/window_selector_item.cc
@@ -75,8 +75,43 @@ static const int kShadowBlur = 10;
const int WindowSelectorItem::kFadeInMilliseconds = 80;
+// Opacity for dimmed items.
+static const float kDimmedItemOpacity = 0.5f;
+
+views::Widget* CreateWindowLabel(aura::Window* root_window,
+ const base::string16 title) {
+ views::Widget* widget = new views::Widget;
+ views::Widget::InitParams params;
+ params.type = views::Widget::InitParams::TYPE_POPUP;
+ params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
+ params.opacity = views::Widget::InitParams::TRANSLUCENT_WINDOW;
+ params.parent =
+ Shell::GetContainer(root_window, ash::kShellWindowId_OverlayContainer);
+ params.accept_events = false;
+ params.visible_on_all_workspaces = true;
+ widget->set_focus_on_creation(false);
+ widget->Init(params);
+ views::Label* label = new views::Label;
+ label->SetEnabledColor(kLabelColor);
+ label->SetBackgroundColor(kLabelBackground);
+ label->set_shadows(gfx::ShadowValues(1, gfx::ShadowValue(
+ gfx::Point(0, kVerticalShadowOffset), kShadowBlur, kLabelShadow)));
+ ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance();
+ label->SetFontList(bundle.GetFontList(ui::ResourceBundle::BoldFont));
+ label->SetText(title);
+ views::BoxLayout* layout = new views::BoxLayout(views::BoxLayout::kVertical,
+ 0,
+ kVerticalLabelPadding,
+ 0);
+ label->SetLayoutManager(layout);
+ widget->SetContentsView(label);
+ widget->Show();
+ return widget;
+}
+
WindowSelectorItem::WindowSelectorItem()
- : root_window_(NULL),
+ : dimmed_(false),
+ root_window_(NULL),
in_bounds_update_(false),
window_label_view_(NULL) {
}
@@ -134,12 +169,19 @@ void WindowSelectorItem::SendFocusAlert() const {
activate_window_button_->SendFocusAlert();
}
+void WindowSelectorItem::SetDimmed(bool dimmed) {
+ dimmed_ = dimmed;
+ SetOpacity(dimmed ? kDimmedItemOpacity : 1.0f);
+}
+
void WindowSelectorItem::ButtonPressed(views::Button* sender,
const ui::Event& event) {
views::Widget::GetWidgetForNativeView(SelectionWindow())->Close();
}
void WindowSelectorItem::OnWindowTitleChanged(aura::Window* window) {
+ // TODO(flackr): Maybe add the new title to a vector of titles so that we can
+ // filter any of the titles the window had while in the overview session.
if (window == SelectionWindow())
window_label_view_->SetText(window->title());
}
@@ -207,6 +249,11 @@ void WindowSelectorItem::UpdateCloseButtonBounds(aura::Window* root_window,
}
}
+void WindowSelectorItem::SetOpacity(float opacity) {
+ window_label_->GetNativeWindow()->layer()->SetOpacity(opacity);
+ close_button_->GetNativeWindow()->layer()->SetOpacity(opacity);
+}
+
void WindowSelectorItem::UpdateWindowLabels(const gfx::Rect& window_bounds,
aura::Window* root_window,
bool animate) {
diff --git a/ash/wm/overview/window_selector_item.h b/ash/wm/overview/window_selector_item.h
index 2cae674..279aed8 100644
--- a/ash/wm/overview/window_selector_item.h
+++ b/ash/wm/overview/window_selector_item.h
@@ -79,6 +79,11 @@ class WindowSelectorItem : public views::ButtonListener,
// label is read.
void SendFocusAlert() const;
+ // Sets if the item is dimmed in the overview. Changing the value will also
+ // change the visibility of the transform windows.
+ virtual void SetDimmed(bool dimmed);
+ bool dimmed() const { return dimmed_; }
+
const gfx::Rect& bounds() const { return bounds_; }
const gfx::Rect& target_bounds() const { return target_bounds_; }
@@ -100,6 +105,13 @@ class WindowSelectorItem : public views::ButtonListener,
// Sets the bounds used by the selector item's windows.
void set_bounds(const gfx::Rect& bounds) { bounds_ = bounds; }
+ // Changes the opacity of all the windows the item owns.
+ virtual void SetOpacity(float opacity);
+
+ // True if the item is being shown in the overview, false if it's being
+ // filtered.
+ bool dimmed_;
+
private:
friend class WindowSelectorTest;
diff --git a/ash/wm/overview/window_selector_panels.cc b/ash/wm/overview/window_selector_panels.cc
index e4ab9be..d3c6920 100644
--- a/ash/wm/overview/window_selector_panels.cc
+++ b/ash/wm/overview/window_selector_panels.cc
@@ -205,4 +205,14 @@ void WindowSelectorPanels::SetItemBounds(aura::Window* root_window,
}
}
+void WindowSelectorPanels::SetOpacity(float opacity) {
+ // TODO(flackr): find a way to make panels that are hidden behind other panels
+ // look nice.
+ for (WindowList::iterator iter = transform_windows_.begin();
+ iter != transform_windows_.end(); iter++) {
+ (*iter)->window()->layer()->SetOpacity(opacity);
+ }
+ WindowSelectorItem::SetOpacity(opacity);
+}
+
} // namespace ash
diff --git a/ash/wm/overview/window_selector_panels.h b/ash/wm/overview/window_selector_panels.h
index b3ea49f..0e22a37 100644
--- a/ash/wm/overview/window_selector_panels.h
+++ b/ash/wm/overview/window_selector_panels.h
@@ -39,6 +39,7 @@ class WindowSelectorPanels : public WindowSelectorItem {
virtual void SetItemBounds(aura::Window* root_window,
const gfx::Rect& target_bounds,
bool animate) OVERRIDE;
+ virtual void SetOpacity(float opacity) OVERRIDE;
private:
typedef ScopedVector<ScopedTransformOverviewWindow> WindowList;
diff --git a/ash/wm/overview/window_selector_unittest.cc b/ash/wm/overview/window_selector_unittest.cc
index d482f55..5e5969c 100644
--- a/ash/wm/overview/window_selector_unittest.cc
+++ b/ash/wm/overview/window_selector_unittest.cc
@@ -30,6 +30,7 @@
#include "base/compiler_specific.h"
#include "base/memory/scoped_vector.h"
#include "base/run_loop.h"
+#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/client/cursor_client.h"
@@ -202,6 +203,19 @@ class WindowSelectorTest : public test::AshTestBase {
SelectedWindow()->SelectionWindow();
}
+ const bool selection_widget_active() {
+ WindowSelector* ws = ash::Shell::GetInstance()->
+ window_selector_controller()->window_selector_.get();
+ return ws->grid_list_[ws->selected_grid_index_]->is_selecting();
+ }
+
+ bool showing_filter_widget() {
+ WindowSelector* ws = ash::Shell::GetInstance()->
+ window_selector_controller()->window_selector_.get();
+ return ws->text_filter_widget_->GetNativeWindow()->layer()->
+ GetTargetTransform().IsIdentity();
+ }
+
views::Widget* GetCloseButton(ash::WindowSelectorItem* window) {
return window->close_button_.get();
}
@@ -224,10 +238,22 @@ class WindowSelectorTest : public test::AshTestBase {
GetCloseButton(window_item)->GetNativeView()))));
}
+ void FilterItems(const base::StringPiece& pattern) {
+ ash::Shell::GetInstance()->
+ window_selector_controller()->window_selector_.get()->
+ ContentsChanged(NULL, base::UTF8ToUTF16(pattern));
+ }
+
test::ShelfViewTestAPI* shelf_view_test() {
return shelf_view_test_.get();
}
+ views::Widget* text_filter_widget() {
+ return ash::Shell::GetInstance()->
+ window_selector_controller()->window_selector_.get()->
+ text_filter_widget_.get();
+ }
+
private:
aura::test::TestWindowDelegate delegate_;
NonActivatableActivationDelegate non_activatable_activation_delegate_;
@@ -266,10 +292,10 @@ TEST_F(WindowSelectorTest, Basic) {
// Hide the cursor before entering overview to test that it will be shown.
aura::client::GetCursorClient(root_window)->HideCursor();
- // In overview mode the windows should no longer overlap and focus should
- // be removed from the window.
+ // In overview mode the windows should no longer overlap and the text filter
+ // widget should be focused.
ToggleOverview();
- EXPECT_EQ(NULL, GetFocusedWindow());
+ EXPECT_EQ(text_filter_widget()->GetNativeWindow(), GetFocusedWindow());
EXPECT_FALSE(WindowsOverlapping(window1.get(), window2.get()));
EXPECT_FALSE(WindowsOverlapping(window1.get(), panel1.get()));
// Panels 1 and 2 should still be overlapping being in a single selector
@@ -294,7 +320,7 @@ TEST_F(WindowSelectorTest, BasicGesture) {
wm::ActivateWindow(window1.get());
EXPECT_EQ(window1.get(), GetFocusedWindow());
ToggleOverview();
- EXPECT_EQ(NULL, GetFocusedWindow());
+ EXPECT_EQ(text_filter_widget()->GetNativeWindow(), GetFocusedWindow());
aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow(),
window2.get());
generator.GestureTapAt(gfx::ToEnclosingRect(
@@ -502,9 +528,9 @@ TEST_F(WindowSelectorTest, CancelRestoresFocus) {
wm::ActivateWindow(window.get());
EXPECT_EQ(window.get(), GetFocusedWindow());
- // In overview mode, focus should be removed.
+ // In overview mode, the text filter widget should be focused.
ToggleOverview();
- EXPECT_EQ(NULL, GetFocusedWindow());
+ EXPECT_EQ(text_filter_widget()->GetNativeWindow(), GetFocusedWindow());
// If canceling overview mode, focus should be restored.
ToggleOverview();
@@ -1014,4 +1040,85 @@ TEST_F(WindowSelectorTest, CloseButtonOnPanels) {
EXPECT_FALSE(IsSelecting());
}
+// Creates three windows and tests filtering them by title.
+TEST_F(WindowSelectorTest, BasicTextFiltering) {
+ gfx::Rect bounds(0, 0, 100, 100);
+ scoped_ptr<aura::Window> window2(CreateWindow(bounds));
+ scoped_ptr<aura::Window> window1(CreateWindow(bounds));
+ scoped_ptr<aura::Window> window0(CreateWindow(bounds));
+ base::string16 window2_title = base::UTF8ToUTF16("Highway to test");
+ base::string16 window1_title = base::UTF8ToUTF16("For those about to test");
+ base::string16 window0_title = base::UTF8ToUTF16("We salute you");
+ window0->SetTitle(window0_title);
+ window1->SetTitle(window1_title);
+ window2->SetTitle(window2_title);
+ ToggleOverview();
+ EXPECT_FALSE(selection_widget_active());
+ EXPECT_FALSE(showing_filter_widget());
+ FilterItems("Test");
+
+ // The selection widget should appear when filtering starts, and should be
+ // selecting the first matching window.
+ EXPECT_TRUE(selection_widget_active());
+ EXPECT_TRUE(showing_filter_widget());
+ EXPECT_EQ(GetSelectedWindow(), window1.get());
+
+ // Window 0 has no "test" on it so it should be the only dimmed item.
+ std::vector<WindowSelectorItem*> items = GetWindowItemsForRoot(0);
+ EXPECT_TRUE(items[0]->dimmed());
+ EXPECT_FALSE(items[1]->dimmed());
+ EXPECT_FALSE(items[2]->dimmed());
+
+ // No items match the search.
+ FilterItems("I'm testing 'n testing");
+ EXPECT_TRUE(items[0]->dimmed());
+ EXPECT_TRUE(items[1]->dimmed());
+ EXPECT_TRUE(items[2]->dimmed());
+
+ // All the items should match the empty string. The filter widget should also
+ // disappear.
+ FilterItems("");
+ EXPECT_FALSE(showing_filter_widget());
+ EXPECT_FALSE(items[0]->dimmed());
+ EXPECT_FALSE(items[1]->dimmed());
+ EXPECT_FALSE(items[2]->dimmed());
+}
+
+// Tests selecting in the overview with dimmed and undimmed items.
+TEST_F(WindowSelectorTest, TextFilteringSelection) {
+ gfx::Rect bounds(0, 0, 100, 100);
+ scoped_ptr<aura::Window> window2(CreateWindow(bounds));
+ scoped_ptr<aura::Window> window1(CreateWindow(bounds));
+ scoped_ptr<aura::Window> window0(CreateWindow(bounds));
+ base::string16 window2_title = base::UTF8ToUTF16("Rock and roll");
+ base::string16 window1_title = base::UTF8ToUTF16("Rock and");
+ base::string16 window0_title = base::UTF8ToUTF16("Rock");
+ window0->SetTitle(window0_title);
+ window1->SetTitle(window1_title);
+ window2->SetTitle(window2_title);
+ ToggleOverview();
+ SendKey(ui::VKEY_RIGHT);
+ EXPECT_TRUE(selection_widget_active());
+ EXPECT_EQ(GetSelectedWindow(), window0.get());
+
+ // Dim the first item, the selection should jump to the next item.
+ std::vector<WindowSelectorItem*> items = GetWindowItemsForRoot(0);
+ FilterItems("Rock and");
+ EXPECT_EQ(GetSelectedWindow(), window1.get());
+
+ // Cycle the selection, the dimmed window should not be selected.
+ SendKey(ui::VKEY_RIGHT);
+ EXPECT_EQ(GetSelectedWindow(), window2.get());
+ SendKey(ui::VKEY_RIGHT);
+ EXPECT_EQ(GetSelectedWindow(), window1.get());
+
+ // Dimming all the items should hide the selection widget.
+ FilterItems("Pop");
+ EXPECT_FALSE(selection_widget_active());
+
+ // Undimming one window should automatically select it.
+ FilterItems("Rock and roll");
+ EXPECT_EQ(GetSelectedWindow(), window2.get());
+}
+
} // namespace ash
diff --git a/ash/wm/overview/window_selector_window.cc b/ash/wm/overview/window_selector_window.cc
index ed06e8e..1ece154 100644
--- a/ash/wm/overview/window_selector_window.cc
+++ b/ash/wm/overview/window_selector_window.cc
@@ -75,4 +75,9 @@ void WindowSelectorWindow::SetItemBounds(aura::Window* root_window,
animate);
}
+void WindowSelectorWindow::SetOpacity(float opacity) {
+ transform_window_.window()->layer()->SetOpacity(opacity);
+ WindowSelectorItem::SetOpacity(opacity);
+}
+
} // namespace ash
diff --git a/ash/wm/overview/window_selector_window.h b/ash/wm/overview/window_selector_window.h
index e508438..5d58085 100644
--- a/ash/wm/overview/window_selector_window.h
+++ b/ash/wm/overview/window_selector_window.h
@@ -39,6 +39,7 @@ class WindowSelectorWindow : public WindowSelectorItem {
virtual void SetItemBounds(aura::Window* root_window,
const gfx::Rect& target_bounds,
bool animate) OVERRIDE;
+ virtual void SetOpacity(float opacity) OVERRIDE;
private:
// The window with a scoped transform represented by this selector item.
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index 6552411..31fcee0 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -5952,6 +5952,12 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_ASH_ENABLE_TOUCH_VIEW_TESTING_DESCRIPTION" desc="Description for the flag to enable the TouchView testing mode.">
Enable Ctrl+Alt+Shift+8 to toggle the TouchView maximizing mode.
</message>
+ <message name="IDS_FLAGS_ASH_DISABLE_TEXT_FILTERING_IN_OVERVIEW_MODE_NAME" desc="Title for the flag to disable window filtering in overview mode by inputing text">
+ Disable text filtering in Overview Mode.
+ </message>
+ <message name="IDS_FLAGS_ASH_DISABLE_TEXT_FILTERING_IN_OVERVIEW_MODE_DESCRIPTION" desc="Description for the flag to disable window filtering in overview mode by inputing text">
+ Disables filtering the windows shown in overview mode by entering text.
+ </message>
</if>
<message name="IDS_GENERIC_EXPERIMENT_CHOICE_AUTOMATIC" desc="Generic 'Automatic' experiment choice option name.">
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index a87a228..8693d82 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -1080,6 +1080,12 @@ const Experiment kExperiments[] = {
kOsCrOS,
SINGLE_VALUE_TYPE(ash::switches::kAshEnableTouchViewTesting),
},
+ { "ash-disable-text-filtering-in-overview-mode",
+ IDS_FLAGS_ASH_DISABLE_TEXT_FILTERING_IN_OVERVIEW_MODE_NAME,
+ IDS_FLAGS_ASH_DISABLE_TEXT_FILTERING_IN_OVERVIEW_MODE_DESCRIPTION,
+ kOsCrOS,
+ SINGLE_VALUE_TYPE(ash::switches::kAshDisableTextFilteringInOverviewMode),
+ },
#endif
#if defined(OS_CHROMEOS)
{
diff --git a/ui/views/controls/textfield/textfield.cc b/ui/views/controls/textfield/textfield.cc
index 09791e9..9407f2d 100644
--- a/ui/views/controls/textfield/textfield.cc
+++ b/ui/views/controls/textfield/textfield.cc
@@ -409,6 +409,11 @@ void Textfield::UseDefaultSelectionTextColor() {
SchedulePaint();
}
+void Textfield::SetShadows(const gfx::ShadowValues& shadows) {
+ GetRenderText()->set_shadows(shadows);
+ SchedulePaint();
+}
+
SkColor Textfield::GetSelectionBackgroundColor() const {
return use_default_selection_background_color_ ?
GetNativeTheme()->GetSystemColor(
@@ -601,7 +606,7 @@ bool Textfield::OnMousePressed(const ui::MouseEvent& event) {
ui::Clipboard::GetForCurrentThread(),
ui::CLIPBOARD_TYPE_SELECTION).WriteText(base::string16());
OnAfterUserAction();
- } else if(!read_only()) {
+ } else if (!read_only()) {
PasteSelectionClipboard(event);
}
}
diff --git a/ui/views/controls/textfield/textfield.h b/ui/views/controls/textfield/textfield.h
index e836177..f32ec89 100644
--- a/ui/views/controls/textfield/textfield.h
+++ b/ui/views/controls/textfield/textfield.h
@@ -120,6 +120,9 @@ class VIEWS_EXPORT Textfield : public View,
void SetSelectionBackgroundColor(SkColor color);
void UseDefaultSelectionBackgroundColor();
+ // Set drop shadows underneath the text.
+ void SetShadows(const gfx::ShadowValues& shadows);
+
// Gets/Sets whether or not the cursor is enabled.
bool GetCursorEnabled() const;
void SetCursorEnabled(bool enabled);