Commit 22f020b3 authored by wutao's avatar wutao Committed by Commit Bot

cros: Focus on search box when KSV window inits

This cl puts the focus on the search box when Keyboard Shortcut Viewer
(KSV):
1. The first time the window is created.
2. Every time it exits search mode by Esc or back arrow.

Bug: 822333
Test: manual test.
Change-Id: Ia0fdeb744e3090d246769aee58c9ee42d58e14e5
Reviewed-on: https://chromium-review.googlesource.com/978784Reviewed-by: default avatarQiang Xu <warx@google.com>
Commit-Queue: Tao Wu <wutao@chromium.org>
Cr-Commit-Position: refs/heads/master@{#545615}
parent d8ee4019
......@@ -139,11 +139,79 @@ views::Widget* KeyboardShortcutView::Show(gfx::NativeWindow context) {
new gfx::ImageSkia(*icon));
g_ksv_view->GetWidget()->Show();
g_ksv_view->RequestFocusForActiveTab();
g_ksv_view->search_box_view_->search_box()->RequestFocus();
}
return g_ksv_view->GetWidget();
}
void KeyboardShortcutView::Layout() {
gfx::Rect content_bounds(GetContentsBounds());
if (content_bounds.IsEmpty())
return;
constexpr int kSearchBoxTopPadding = 8;
constexpr int kSearchBoxBottomPadding = 16;
constexpr int kSearchBoxHorizontalPadding = 30;
const int left = content_bounds.x();
const int top = content_bounds.y();
gfx::Rect search_box_bounds(search_box_view_->GetPreferredSize());
search_box_bounds.set_width(
std::min(search_box_bounds.width(),
content_bounds.width() - 2 * kSearchBoxHorizontalPadding));
search_box_bounds.set_x(
left + (content_bounds.width() - search_box_bounds.width()) / 2);
search_box_bounds.set_y(top + kSearchBoxTopPadding);
search_box_view_->SetBoundsRect(search_box_bounds);
views::View* content_view = categories_tabbed_pane_->visible()
? categories_tabbed_pane_
: search_results_container_;
const int search_box_used_height = search_box_bounds.height() +
kSearchBoxTopPadding +
kSearchBoxBottomPadding;
content_view->SetBounds(left, top + search_box_used_height,
content_bounds.width(),
content_bounds.height() - search_box_used_height);
}
void KeyboardShortcutView::QueryChanged(search_box::SearchBoxViewBase* sender) {
const bool query_empty = sender->IsSearchBoxTrimmedQueryEmpty();
if (is_search_box_empty_ != query_empty) {
is_search_box_empty_ = query_empty;
UpdateViewsLayout(/*is_search_box_active=*/true);
}
debounce_timer_.Stop();
// If search box is empty, do not show |search_results_container_|.
if (query_empty)
return;
// TODO(wutao): This timeout value is chosen based on subjective search
// latency tests on Minnie. Objective method or UMA is desired.
constexpr base::TimeDelta kTimeOut(base::TimeDelta::FromMilliseconds(250));
debounce_timer_.Start(
FROM_HERE, kTimeOut,
base::Bind(&KeyboardShortcutView::ShowSearchResults,
base::Unretained(this), sender->search_box()->text()));
}
void KeyboardShortcutView::BackButtonPressed() {
search_box_view_->ClearSearch();
search_box_view_->SetSearchBoxActive(false);
}
void KeyboardShortcutView::ActiveChanged(
search_box::SearchBoxViewBase* sender) {
const bool is_search_box_active = sender->is_search_box_active();
is_search_box_empty_ = sender->IsSearchBoxTrimmedQueryEmpty();
sender->ShowBackOrGoogleIcon(is_search_box_active);
if (is_search_box_active) {
base::RecordAction(
base::UserMetricsAction("KeyboardShortcutViewer.Search"));
}
UpdateViewsLayout(is_search_box_active);
}
KeyboardShortcutView::KeyboardShortcutView() {
DCHECK_EQ(g_ksv_view, nullptr);
g_ksv_view = this;
......@@ -235,99 +303,6 @@ void KeyboardShortcutView::InitCategoriesTabbedPane() {
}
}
void KeyboardShortcutView::RequestFocusForActiveTab() {
// Get the |tab_strip_| of the |categories_tabbed_pane_| in order to set focus
// on the selected tab.
categories_tabbed_pane_->child_at(0)
->child_at(active_tab_index_)
->RequestFocus();
}
bool KeyboardShortcutView::CanMaximize() const {
return false;
}
bool KeyboardShortcutView::CanMinimize() const {
return true;
}
bool KeyboardShortcutView::CanResize() const {
return false;
}
views::ClientView* KeyboardShortcutView::CreateClientView(
views::Widget* widget) {
return new views::ClientView(widget, this);
}
void KeyboardShortcutView::Layout() {
gfx::Rect content_bounds(GetContentsBounds());
if (content_bounds.IsEmpty())
return;
constexpr int kSearchBoxTopPadding = 8;
constexpr int kSearchBoxBottomPadding = 16;
constexpr int kSearchBoxHorizontalPadding = 30;
const int left = content_bounds.x();
const int top = content_bounds.y();
gfx::Rect search_box_bounds(search_box_view_->GetPreferredSize());
search_box_bounds.set_width(
std::min(search_box_bounds.width(),
content_bounds.width() - 2 * kSearchBoxHorizontalPadding));
search_box_bounds.set_x(
left + (content_bounds.width() - search_box_bounds.width()) / 2);
search_box_bounds.set_y(top + kSearchBoxTopPadding);
search_box_view_->SetBoundsRect(search_box_bounds);
views::View* content_view = categories_tabbed_pane_->visible()
? categories_tabbed_pane_
: search_results_container_;
const int search_box_used_height = search_box_bounds.height() +
kSearchBoxTopPadding +
kSearchBoxBottomPadding;
content_view->SetBounds(left, top + search_box_used_height,
content_bounds.width(),
content_bounds.height() - search_box_used_height);
}
void KeyboardShortcutView::BackButtonPressed() {
search_box_view_->ClearSearch();
search_box_view_->SetSearchBoxActive(false);
}
void KeyboardShortcutView::QueryChanged(search_box::SearchBoxViewBase* sender) {
const bool query_empty = sender->IsSearchBoxTrimmedQueryEmpty();
if (is_search_box_empty_ != query_empty) {
is_search_box_empty_ = query_empty;
UpdateViewsLayout(/*is_search_box_active=*/true);
}
debounce_timer_.Stop();
// If search box is empty, do not show |search_results_container_|.
if (query_empty)
return;
// TODO(wutao): This timeout value is chosen based on subjective search
// latency tests on Minnie. Objective method or UMA is desired.
constexpr base::TimeDelta kTimeOut(base::TimeDelta::FromMilliseconds(250));
debounce_timer_.Start(
FROM_HERE, kTimeOut,
base::Bind(&KeyboardShortcutView::ShowSearchResults,
base::Unretained(this), sender->search_box()->text()));
}
void KeyboardShortcutView::ActiveChanged(
search_box::SearchBoxViewBase* sender) {
const bool is_search_box_active = sender->is_search_box_active();
is_search_box_empty_ = sender->IsSearchBoxTrimmedQueryEmpty();
sender->ShowBackOrGoogleIcon(is_search_box_active);
if (is_search_box_active) {
base::RecordAction(
base::UserMetricsAction("KeyboardShortcutViewer.Search"));
}
UpdateViewsLayout(is_search_box_active);
}
void KeyboardShortcutView::UpdateViewsLayout(bool is_search_box_active) {
// 1. Search box is not active: show |categories_tabbed_pane_| and focus on
// active tab.
......@@ -348,8 +323,6 @@ void KeyboardShortcutView::UpdateViewsLayout(bool is_search_box_active) {
// Select the category that was active before entering search mode.
categories_tabbed_pane_->SelectTabAt(active_tab_index_);
}
if (!is_search_box_active)
RequestFocusForActiveTab();
}
categories_tabbed_pane_->SetVisible(!should_show_search_results);
search_results_container_->SetVisible(should_show_search_results);
......@@ -425,6 +398,23 @@ void KeyboardShortcutView::ShowSearchResults(
SchedulePaint();
}
bool KeyboardShortcutView::CanMaximize() const {
return false;
}
bool KeyboardShortcutView::CanMinimize() const {
return true;
}
bool KeyboardShortcutView::CanResize() const {
return false;
}
views::ClientView* KeyboardShortcutView::CreateClientView(
views::Widget* widget) {
return new views::ClientView(widget, this);
}
KeyboardShortcutView* KeyboardShortcutView::GetInstanceForTesting() {
return g_ksv_view;
}
......@@ -438,4 +428,8 @@ KeyboardShortcutView::GetShortcutViewsForTesting() const {
return shortcut_views_;
}
KSVSearchBoxView* KeyboardShortcutView::GetSearchBoxViewForTesting() {
return search_box_view_.get();
}
} // namespace keyboard_shortcut_viewer
......@@ -53,10 +53,6 @@ class KeyboardShortcutView : public views::WidgetDelegateView,
// |shortcut_views_|, called on construction and when exiting search mode.
void InitCategoriesTabbedPane();
// Put focus on the active tab. Used when the first time to show the widget or
// after exiting search mode.
void RequestFocusForActiveTab();
// Update views' layout based on search box status.
void UpdateViewsLayout(bool is_search_box_active);
......@@ -73,6 +69,7 @@ class KeyboardShortcutView : public views::WidgetDelegateView,
int GetTabCountForTesting() const;
const std::vector<std::unique_ptr<KeyboardShortcutItemView>>&
GetShortcutViewsForTesting() const;
KSVSearchBoxView* GetSearchBoxViewForTesting();
// Owned by views hierarchy.
// The container for category tabs and lists of KeyboardShortcutItemViews.
......
......@@ -8,8 +8,10 @@
#include "ash/components/shortcut_viewer/keyboard_shortcut_viewer_metadata.h"
#include "ash/components/shortcut_viewer/views/keyboard_shortcut_item_view.h"
#include "ash/components/shortcut_viewer/views/ksv_search_box_view.h"
#include "ash/test/ash_test_base.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/widget/widget.h"
namespace keyboard_shortcut_viewer {
......@@ -31,6 +33,25 @@ class KeyboardShortcutViewTest : public ash::AshTestBase {
return GetView()->GetShortcutViewsForTesting();
}
KSVSearchBoxView* GetSearchBoxView() {
DCHECK(GetView());
return GetView()->GetSearchBoxViewForTesting();
}
void KeyPress(ui::KeyboardCode key_code, bool should_insert) {
ui::KeyEvent event(ui::ET_KEY_PRESSED, key_code, ui::EF_NONE);
GetSearchBoxView()->OnKeyEvent(&event);
if (!should_insert)
return;
// Emulates the input method.
if (::isalnum(static_cast<int>(key_code))) {
base::char16 character = ::tolower(static_cast<int>(key_code));
GetSearchBoxView()->search_box()->InsertText(
base::string16(1, character));
}
}
private:
KeyboardShortcutView* GetView() const {
return KeyboardShortcutView::GetInstanceForTesting();
......@@ -95,4 +116,41 @@ TEST_F(KeyboardShortcutViewTest, TopLineCenterAlignedInItemView) {
widget->CloseNow();
}
// Test that the focus is on search box when window inits and exits search mode.
TEST_F(KeyboardShortcutViewTest, FocusOnSearchBox) {
// Showing the widget.
views::Widget* widget = KeyboardShortcutView::Show(CurrentContext());
// Case 1: when window creates. The focus should be on search box.
EXPECT_TRUE(GetSearchBoxView()->search_box()->HasFocus());
// Press a key should enter search mode.
KeyPress(ui::VKEY_A, /*should_insert=*/true);
EXPECT_TRUE(GetSearchBoxView()->back_button()->visible());
EXPECT_FALSE(GetSearchBoxView()->search_box()->text().empty());
// Case 2: Exit search mode by clicking |back_button|. The focus should be on
// search box.
GetSearchBoxView()->ButtonPressed(
GetSearchBoxView()->back_button(),
ui::MouseEvent(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
base::TimeTicks(), ui::EF_LEFT_MOUSE_BUTTON,
ui::EF_LEFT_MOUSE_BUTTON));
EXPECT_TRUE(GetSearchBoxView()->search_box()->text().empty());
EXPECT_TRUE(GetSearchBoxView()->search_box()->HasFocus());
// Enter search mode again.
KeyPress(ui::VKEY_A, /*should_insert=*/true);
EXPECT_FALSE(GetSearchBoxView()->search_box()->text().empty());
// Case 3: Exit search mode by pressing |VKEY_ESCAPE|. The focus should be on
// search box.
KeyPress(ui::VKEY_ESCAPE, /*should_insert=*/false);
EXPECT_TRUE(GetSearchBoxView()->search_box()->text().empty());
EXPECT_TRUE(GetSearchBoxView()->search_box()->HasFocus());
// Cleaning up.
widget->CloseNow();
}
} // namespace keyboard_shortcut_viewer
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