diff options
Diffstat (limited to 'ash/wm')
-rw-r--r-- | ash/wm/overview/scoped_transform_overview_window.cc | 5 | ||||
-rw-r--r-- | ash/wm/overview/scoped_transform_overview_window.h | 3 | ||||
-rw-r--r-- | ash/wm/overview/window_grid.cc | 32 | ||||
-rw-r--r-- | ash/wm/overview/window_grid.h | 11 | ||||
-rw-r--r-- | ash/wm/overview/window_selector.cc | 146 | ||||
-rw-r--r-- | ash/wm/overview/window_selector.h | 31 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_item.cc | 49 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_item.h | 12 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_panels.cc | 10 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_panels.h | 1 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_unittest.cc | 119 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_window.cc | 5 | ||||
-rw-r--r-- | ash/wm/overview/window_selector_window.h | 1 |
13 files changed, 376 insertions, 49 deletions
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. |