X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fash%2Fwm%2Foverview%2Fwindow_selector.cc;h=d1aba53162b1e8458bc9903f8e11bf28b31575fe;hb=4a1a0bdd01eef90b0826a0e761d3379d3715c10f;hp=4c3930c1d767cb0062f266572d60b23886cbdd9e;hpb=b1be5ca53587d23e7aeb77b26861fdc0a181ffd8;p=platform%2Fframework%2Fweb%2Fcrosswalk.git diff --git a/src/ash/wm/overview/window_selector.cc b/src/ash/wm/overview/window_selector.cc index 4c3930c..d1aba53 100644 --- a/src/ash/wm/overview/window_selector.cc +++ b/src/ash/wm/overview/window_selector.cc @@ -19,14 +19,23 @@ #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 "third_party/skia/include/core/SkPaint.h" +#include "third_party/skia/include/core/SkPath.h" #include "ui/aura/client/focus_client.h" #include "ui/aura/window.h" #include "ui/aura/window_event_dispatcher.h" #include "ui/aura/window_observer.h" +#include "ui/base/resource/resource_bundle.h" #include "ui/compositor/scoped_layer_animation_settings.h" #include "ui/events/event.h" +#include "ui/gfx/canvas.h" #include "ui/gfx/screen.h" +#include "ui/gfx/skia_util.h" +#include "ui/views/border.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/layout/box_layout.h" #include "ui/wm/core/window_util.h" #include "ui/wm/public/activation_client.h" @@ -34,6 +43,29 @@ namespace ash { namespace { +// The proportion of screen width that the text filter takes. +const float kTextFilterScreenProportion = 0.25; + +// The amount of padding surrounding the text in the text filtering textbox. +const int kTextFilterHorizontalPadding = 8; + +// The distance between the top of the screen and the top edge of the +// text filtering textbox. +const int kTextFilterDistanceFromTop = 32; + +// The height of the text filtering textbox. +const int kTextFilterHeight = 32; + +// The font style used for text filtering. +static const ::ui::ResourceBundle::FontStyle kTextFilterFontStyle = + ::ui::ResourceBundle::FontStyle::MediumFont; + +// The alpha value for the background of the text filtering textbox. +const unsigned char kTextFilterOpacity = 180; + +// The radius used for the rounded corners on the text filtering textbox. +const int kTextFilterCornerRadius = 1; + // A comparator for locating a grid with a given root window. struct RootWindowGridComparator : public std::unary_function { @@ -76,6 +108,42 @@ struct WindowSelectorItemForRoot const aura::Window* root_window; }; +// A View having rounded corners and a specified background color which is +// only painted within the bounds defined by the rounded corners. +// TODO(tdanderson): This duplicates code from RoundedImageView. Refactor these +// classes and move into ui/views. +class RoundedContainerView : public views::View { + public: + RoundedContainerView(int corner_radius, SkColor background) + : corner_radius_(corner_radius), + background_(background) { + } + + virtual ~RoundedContainerView() {} + + virtual void OnPaint(gfx::Canvas* canvas) OVERRIDE { + views::View::OnPaint(canvas); + + SkScalar radius = SkIntToScalar(corner_radius_); + const SkScalar kRadius[8] = {radius, radius, radius, radius, + radius, radius, radius, radius}; + SkPath path; + gfx::Rect bounds(size()); + path.addRoundRect(gfx::RectToSkRect(bounds), kRadius); + + SkPaint paint; + paint.setAntiAlias(true); + canvas->ClipPath(path, true); + canvas->DrawColor(background_); + } + + private: + int corner_radius_; + SkColor background_; + + DISALLOW_COPY_AND_ASSIGN(RoundedContainerView); +}; + // Triggers a shelf visibility update on all root window controllers. void UpdateShelfVisibility() { Shell::RootWindowControllerList root_window_controllers = @@ -87,8 +155,64 @@ 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), + kTextFilterDistanceFromTop, + root_window->bounds().width() * kTextFilterScreenProportion, + kTextFilterHeight); + widget->Init(params); + + // Use |container| to specify the padding surrounding the text and to give + // the textfield rounded corners. + views::View* container = new RoundedContainerView( + kTextFilterCornerRadius, SkColorSetARGB(kTextFilterOpacity, 0, 0, 0)); + ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); + int text_height = bundle.GetFontList(kTextFilterFontStyle).GetHeight(); + DCHECK(text_height); + int vertical_padding = (kTextFilterHeight - text_height) / 2; + container->SetLayoutManager(new views::BoxLayout(views::BoxLayout::kVertical, + kTextFilterHorizontalPadding, + vertical_padding, + 0)); + + views::Textfield* textfield = new views::Textfield; + textfield->set_controller(controller); + textfield->SetBackgroundColor(SK_ColorTRANSPARENT); + textfield->SetBorder(views::Border::NullBorder()); + textfield->SetTextColor(SK_ColorWHITE); + textfield->SetFontList(bundle.GetFontList(kTextFilterFontStyle)); + + container->AddChildView(textfield); + widget->SetContentsView(container); + + // The textfield initially contains no text, so shift its position to be + // outside the visible bounds of the screen. + gfx::Transform transform; + transform.Translate(0, -WindowSelector::kTextFilterBottomEdge); + widget->GetNativeWindow()->SetTransform(transform); + widget->Show(); + textfield->RequestFocus(); + + return widget; +} + } // namespace +const int WindowSelector::kTextFilterBottomEdge = + kTextFilterDistanceFromTop + kTextFilterHeight; + WindowSelector::WindowSelector(const WindowList& windows, WindowSelectorDelegate* delegate) : delegate_(delegate), @@ -98,7 +222,10 @@ 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), + text_filter_string_length_(0), + num_times_textfield_cleared_(0) { DCHECK(delegate_); Shell* shell = Shell::GetInstance(); shell->OnOverviewModeStarting(); @@ -136,13 +263,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(); @@ -177,7 +302,6 @@ WindowSelector::~WindowSelector() { (*iter)->Show(); } - shell->RemovePreTargetHandler(this); shell->GetScreen()->RemoveObserver(this); size_t remaining_items = 0; @@ -192,7 +316,21 @@ WindowSelector::~WindowSelector() { UMA_HISTOGRAM_MEDIUM_TIMES("Ash.WindowSelector.TimeInOverview", base::Time::Now() - overview_start_time_); - // TODO(nsatragno): Change this to OnOverviewModeEnded and move it to when + // Record metrics related to text filtering. + UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.TextFilteringStringLength", + text_filter_string_length_); + UMA_HISTOGRAM_COUNTS_100("Ash.WindowSelector.TextFilteringTextfieldCleared", + num_times_textfield_cleared_); + if (text_filter_string_length_) { + UMA_HISTOGRAM_MEDIUM_TIMES( + "Ash.WindowSelector.TimeInOverviewWithTextFiltering", + base::Time::Now() - overview_start_time_); + UMA_HISTOGRAM_COUNTS_100( + "Ash.WindowSelector.ItemsWhenTextFilteringUsed", + remaining_items); + } + + // TODO(flackr): Change this to OnOverviewModeEnded and move it to when // everything is done. shell->OnOverviewModeEnding(); @@ -210,40 +348,42 @@ void WindowSelector::OnGridEmpty(WindowGrid* grid) { std::find(grid_list_.begin(), grid_list_.end(), grid); DCHECK(iter != grid_list_.end()); grid_list_.erase(iter); - // TODO(nsatragno): Use the previous index for more than two displays. + // TODO(flackr): Use the previous index for more than two displays. selected_grid_index_ = 0; if (grid_list_.empty()) 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: + case ui::VKEY_TAB: 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( @@ -255,17 +395,17 @@ 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) { } void WindowSelector::OnDisplayRemoved(const gfx::Display& display) { - // TODO(nsatragno): Keep window selection active on remaining displays. + // TODO(flackr): Keep window selection active on remaining displays. CancelSelection(); } @@ -299,8 +439,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::iterator grid = std::find_if(grid_list_.begin(), grid_list_.end(), @@ -326,6 +469,50 @@ 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; + } + + text_filter_string_length_ = new_contents.length(); + if (!text_filter_string_length_) + num_times_textfield_cleared_++; + + 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); + text_filter_widget_->GetNativeWindow()->layer()->SetOpacity(1); + } else { + transform.Translate(0, -kTextFilterBottomEdge); + text_filter_widget_->GetNativeWindow()->layer()->SetOpacity(0); + } + + text_filter_widget_->GetNativeWindow()->SetTransform(transform); + showing_selection_widget_ = should_show_selection_widget; + } + for (ScopedVector::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::iterator iter = grid_list_.begin(); iter != grid_list_.end(); iter++) { @@ -388,16 +575,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. - // TODO(nsatragno): If there are more than two monitors, move between grids +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(flackr): 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); } }