summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbshe@chromium.org <bshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-30 20:50:48 +0000
committerbshe@chromium.org <bshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-08-30 20:50:48 +0000
commit357b18ca88fb9b50cdc548aefc96f4b512b80822 (patch)
tree98734160f1df9f423a656c463ebf3e51c7d9d254
parentea5ace2eab528fd7272744585199d2625411f9ca (diff)
downloadchromium_src-357b18ca88fb9b50cdc548aefc96f4b512b80822.zip
chromium_src-357b18ca88fb9b50cdc548aefc96f4b512b80822.tar.gz
chromium_src-357b18ca88fb9b50cdc548aefc96f4b512b80822.tar.bz2
Delay virtual keyboard hiding for 100ms
This CL should fix this issue that we are experiencing(note it is not always reproducible): - In a maximized window, clicking button may not take any effect due to immediate keyboard relayout (go to http://unixpapa.com/js/testkey.html and try hit reset button after typed a bunch of keys) It also workarounds the other two issues: 1. keyboard flicking when quickly switch from one input field to another field 2. In webdev javascript console, the input feild temoporary lost focus when candidate popup window shows and it may try to hide and relayout keyboard, which makes the console unuseable with virtual keyboard. The root problem for the above two issues are tracked in issue 281493. BUG= Review URL: https://chromiumcodereview.appspot.com/22831045 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@220648 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ui/keyboard/keyboard_controller.cc50
-rw-r--r--ui/keyboard/keyboard_controller.h10
-rw-r--r--ui/keyboard/keyboard_controller_unittest.cc105
3 files changed, 118 insertions, 47 deletions
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc
index 1a3d88d..0977f89 100644
--- a/ui/keyboard/keyboard_controller.cc
+++ b/ui/keyboard/keyboard_controller.cc
@@ -4,6 +4,7 @@
#include "ui/keyboard/keyboard_controller.h"
+#include "base/bind.h"
#include "ui/aura/layout_manager.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
@@ -19,6 +20,8 @@
namespace {
+const int kHideKeyboardDelayMs = 100;
+
gfx::Rect KeyboardBoundsFromWindowBounds(const gfx::Rect& window_bounds) {
const float kKeyboardHeightRatio = 0.3f;
return gfx::Rect(
@@ -118,7 +121,9 @@ class KeyboardLayoutManager : public aura::LayoutManager {
KeyboardController::KeyboardController(KeyboardControllerProxy* proxy)
: proxy_(proxy),
container_(NULL),
- input_method_(NULL) {
+ input_method_(NULL),
+ keyboard_visible_(false),
+ weak_factory_(this) {
CHECK(proxy);
input_method_ = proxy_->GetInputMethod();
input_method_->AddObserver(this);
@@ -166,7 +171,7 @@ void KeyboardController::OnTextInputStateChanged(
if (!container_)
return;
- bool was_showing = container_->IsVisible();
+ bool was_showing = keyboard_visible_;
bool should_show = was_showing;
if (!client || client->GetTextInputType() == ui::TEXT_INPUT_TYPE_NONE) {
should_show = false;
@@ -182,20 +187,26 @@ void KeyboardController::OnTextInputStateChanged(
}
if (was_showing != should_show) {
- gfx::Rect new_bounds(
- should_show ? container_->children()[0]->bounds() : gfx::Rect());
-
- FOR_EACH_OBSERVER(
- KeyboardControllerObserver,
- observer_list_,
- OnKeyboardBoundsChanging(new_bounds));
-
- if (should_show)
+ if (should_show) {
+ keyboard_visible_ = true;
+ weak_factory_.InvalidateWeakPtrs();
+ if (container_->IsVisible())
+ return;
+
+ FOR_EACH_OBSERVER(
+ KeyboardControllerObserver,
+ observer_list_,
+ OnKeyboardBoundsChanging(container_->children()[0]->bounds()));
proxy_->ShowKeyboardContainer(container_);
- else
- proxy_->HideKeyboardContainer(container_);
+ } else {
+ keyboard_visible_ = false;
+ base::MessageLoop::current()->PostDelayedTask(
+ FROM_HERE,
+ base::Bind(&KeyboardController::HideKeyboard,
+ weak_factory_.GetWeakPtr()),
+ base::TimeDelta::FromMilliseconds(kHideKeyboardDelayMs));
+ }
}
-
// TODO(bryeung): whenever the TextInputClient changes we need to notify the
// keyboard (with the TextInputType) so that it can reset it's state (e.g.
// abandon compositions in progress)
@@ -207,4 +218,15 @@ void KeyboardController::OnInputMethodDestroyed(
input_method_ = NULL;
}
+void KeyboardController::HideKeyboard() {
+ FOR_EACH_OBSERVER(KeyboardControllerObserver,
+ observer_list_,
+ OnKeyboardBoundsChanging(gfx::Rect()));
+ proxy_->HideKeyboardContainer(container_);
+}
+
+bool KeyboardController::WillHideKeyboard() const {
+ return weak_factory_.HasWeakPtrs();
+}
+
} // namespace keyboard
diff --git a/ui/keyboard/keyboard_controller.h b/ui/keyboard/keyboard_controller.h
index 6022339..e1574f2 100644
--- a/ui/keyboard/keyboard_controller.h
+++ b/ui/keyboard/keyboard_controller.h
@@ -61,9 +61,19 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver,
virtual void OnInputMethodDestroyed(
const ui::InputMethod* input_method) OVERRIDE;
+ // Hides virtual keyboard and notifies observer bounds change.
+ // This functions should be called with a delay to avoid layout flicker
+ // when the focus of input field quickly change.
+ void HideKeyboard();
+
+ // Returns true if keyboard is scheduled to hide.
+ bool WillHideKeyboard() const;
+
scoped_ptr<KeyboardControllerProxy> proxy_;
aura::Window* container_;
ui::InputMethod* input_method_;
+ bool keyboard_visible_;
+ base::WeakPtrFactory<KeyboardController> weak_factory_;
ObserverList<KeyboardControllerObserver> observer_list_;
diff --git a/ui/keyboard/keyboard_controller_unittest.cc b/ui/keyboard/keyboard_controller_unittest.cc
index 49c695a..107600f 100644
--- a/ui/keyboard/keyboard_controller_unittest.cc
+++ b/ui/keyboard/keyboard_controller_unittest.cc
@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "base/bind.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -158,6 +159,27 @@ class TestTextInputClient : public ui::TextInputClient {
DISALLOW_COPY_AND_ASSIGN(TestTextInputClient);
};
+class KeyboardContainerObserver : public aura::WindowObserver {
+ public:
+ explicit KeyboardContainerObserver(aura::Window* window) : window_(window) {
+ window_->AddObserver(this);
+ }
+ virtual ~KeyboardContainerObserver() {
+ window_->RemoveObserver(this);
+ }
+
+ private:
+ virtual void OnWindowVisibilityChanged(aura::Window* window,
+ bool visible) OVERRIDE {
+ if (!visible)
+ base::MessageLoop::current()->Quit();
+ }
+
+ aura::Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeyboardContainerObserver);
+};
+
} // namespace
class KeyboardControllerTest : public testing::Test {
@@ -170,6 +192,8 @@ class KeyboardControllerTest : public testing::Test {
aura_test_helper_->SetUp();
ui::SetUpInputMethodFactoryForTesting();
focus_controller_.reset(new TestFocusController(root_window()));
+ proxy_ = new TestKeyboardControllerProxy();
+ controller_.reset(new KeyboardController(proxy_));
}
virtual void TearDown() OVERRIDE {
@@ -178,37 +202,43 @@ class KeyboardControllerTest : public testing::Test {
}
aura::RootWindow* root_window() { return aura_test_helper_->root_window(); }
+ KeyboardControllerProxy* proxy() { return proxy_; }
+ KeyboardController* controller() { return controller_.get(); }
- void ShowKeyboard(KeyboardController* controller) {
+ void ShowKeyboard() {
TestTextInputClient test_text_input_client(ui::TEXT_INPUT_TYPE_TEXT);
- controller->OnTextInputStateChanged(&test_text_input_client);
+ controller_->OnTextInputStateChanged(&test_text_input_client);
}
protected:
+ bool WillHideKeyboard() {
+ return controller_->WillHideKeyboard();
+ }
+
base::MessageLoopForUI message_loop_;
scoped_ptr<aura::test::AuraTestHelper> aura_test_helper_;
scoped_ptr<TestFocusController> focus_controller_;
private:
+ KeyboardControllerProxy* proxy_;
+ scoped_ptr<KeyboardController> controller_;
+
DISALLOW_COPY_AND_ASSIGN(KeyboardControllerTest);
};
TEST_F(KeyboardControllerTest, KeyboardSize) {
- KeyboardControllerProxy* proxy = new TestKeyboardControllerProxy();
- KeyboardController controller(proxy);
-
- scoped_ptr<aura::Window> container(controller.GetContainerWindow());
+ scoped_ptr<aura::Window> container(controller()->GetContainerWindow());
gfx::Rect bounds(0, 0, 100, 100);
container->SetBounds(bounds);
- const gfx::Rect& before_bounds = proxy->GetKeyboardWindow()->bounds();
+ const gfx::Rect& before_bounds = proxy()->GetKeyboardWindow()->bounds();
gfx::Rect new_bounds(
before_bounds.x(), before_bounds.y(),
before_bounds.width() / 2, before_bounds.height() / 2);
// The KeyboardController's LayoutManager shouldn't let this happen
- proxy->GetKeyboardWindow()->SetBounds(new_bounds);
- ASSERT_EQ(before_bounds, proxy->GetKeyboardWindow()->bounds());
+ proxy()->GetKeyboardWindow()->SetBounds(new_bounds);
+ ASSERT_EQ(before_bounds, proxy()->GetKeyboardWindow()->bounds());
}
// Tests that tapping/clicking inside the keyboard does not give it focus.
@@ -222,16 +252,14 @@ TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) {
window->Show();
window->Focus();
- KeyboardControllerProxy* proxy = new TestKeyboardControllerProxy();
- KeyboardController controller(proxy);
-
- scoped_ptr<aura::Window> keyboard_container(controller.GetContainerWindow());
+ scoped_ptr<aura::Window> keyboard_container(
+ controller()->GetContainerWindow());
keyboard_container->SetBounds(root_bounds);
root_window()->AddChild(keyboard_container.get());
keyboard_container->Show();
- ShowKeyboard(&controller);
+ ShowKeyboard();
EXPECT_TRUE(window->IsVisible());
EXPECT_TRUE(keyboard_container->IsVisible());
@@ -244,7 +272,7 @@ TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) {
keyboard_container->AddPreTargetHandler(&observer);
aura::test::EventGenerator generator(root_window());
- generator.MoveMouseTo(proxy->GetKeyboardWindow()->bounds().CenterPoint());
+ generator.MoveMouseTo(proxy()->GetKeyboardWindow()->bounds().CenterPoint());
generator.ClickLeftButton();
EXPECT_TRUE(window->HasFocus());
EXPECT_FALSE(keyboard_container->HasFocus());
@@ -260,32 +288,43 @@ TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) {
TEST_F(KeyboardControllerTest, VisibilityChangeWithTextInputTypeChange) {
const gfx::Rect& root_bounds = root_window()->bounds();
- aura::test::EventCountDelegate delegate;
- scoped_ptr<aura::Window> window(new aura::Window(&delegate));
- window->Init(ui::LAYER_NOT_DRAWN);
- window->SetBounds(root_bounds);
- root_window()->AddChild(window.get());
- window->Show();
- window->Focus();
-
- KeyboardControllerProxy* proxy = new TestKeyboardControllerProxy();
- ui::InputMethod* input_method = proxy->GetInputMethod();
- TestTextInputClient input_client(ui::TEXT_INPUT_TYPE_TEXT);
- TestTextInputClient no_input_client(ui::TEXT_INPUT_TYPE_NONE);
- input_method->SetFocusedTextInputClient(&input_client);
- KeyboardController controller(proxy);
-
- scoped_ptr<aura::Window> keyboard_container(controller.GetContainerWindow());
+ ui::InputMethod* input_method = proxy()->GetInputMethod();
+ TestTextInputClient input_client_0(ui::TEXT_INPUT_TYPE_TEXT);
+ TestTextInputClient input_client_1(ui::TEXT_INPUT_TYPE_TEXT);
+ TestTextInputClient input_client_2(ui::TEXT_INPUT_TYPE_TEXT);
+ TestTextInputClient no_input_client_0(ui::TEXT_INPUT_TYPE_NONE);
+ TestTextInputClient no_input_client_1(ui::TEXT_INPUT_TYPE_NONE);
+ input_method->SetFocusedTextInputClient(&input_client_0);
+
+ scoped_ptr<aura::Window> keyboard_container(
+ controller()->GetContainerWindow());
+ scoped_ptr<KeyboardContainerObserver> keyboard_container_observer(
+ new KeyboardContainerObserver(keyboard_container.get()));
keyboard_container->SetBounds(root_bounds);
root_window()->AddChild(keyboard_container.get());
EXPECT_TRUE(keyboard_container->IsVisible());
- input_method->SetFocusedTextInputClient(&no_input_client);
+ input_method->SetFocusedTextInputClient(&no_input_client_0);
+ // Keyboard should not immediately hide itself. It is delayed to avoid layout
+ // flicker when the focus of input field quickly change.
+ EXPECT_TRUE(keyboard_container->IsVisible());
+ EXPECT_TRUE(WillHideKeyboard());
+ // Wait for hide keyboard to finish.
+ base::MessageLoop::current()->Run();
EXPECT_FALSE(keyboard_container->IsVisible());
- input_method->SetFocusedTextInputClient(&input_client);
+ input_method->SetFocusedTextInputClient(&input_client_1);
+ EXPECT_TRUE(keyboard_container->IsVisible());
+
+ // Schedule to hide keyboard.
+ input_method->SetFocusedTextInputClient(&no_input_client_1);
+ EXPECT_TRUE(WillHideKeyboard());
+ // Cancel keyboard hide.
+ input_method->SetFocusedTextInputClient(&input_client_2);
+
+ EXPECT_FALSE(WillHideKeyboard());
EXPECT_TRUE(keyboard_container->IsVisible());
}