summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc83
-rw-r--r--chrome/browser/ui/ash/ash_keyboard_controller_proxy.h16
-rw-r--r--chrome/browser/ui/ash/ash_keyboard_controller_proxy_browsertest.cc185
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--ui/keyboard/keyboard.gyp1
-rw-r--r--ui/keyboard/keyboard_controller.cc117
-rw-r--r--ui/keyboard/keyboard_controller.h13
-rw-r--r--ui/keyboard/keyboard_controller_proxy.cc3
-rw-r--r--ui/keyboard/keyboard_controller_proxy.h4
-rw-r--r--ui/keyboard/keyboard_controller_unittest.cc129
10 files changed, 272 insertions, 280 deletions
diff --git a/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc b/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc
index 1208e15..a0a2853 100644
--- a/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc
+++ b/chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc
@@ -38,13 +38,6 @@ namespace {
const char* kVirtualKeyboardExtensionID = "mppnpdlheglhdfmldimlhpnegondlapf";
-// The virtual keyboard show/hide animation duration.
-const int kAnimationDurationMs = 200;
-
-// The opacity of virtual keyboard container when show animation starts or
-// hide animation finishes.
-const float kAnimationStartOrAfterHideOpacity = 0.2f;
-
Context::Type TextInputTypeToGeneratedInputTypeEnum(ui::TextInputType type) {
switch (type) {
case ui::TEXT_INPUT_TYPE_NONE:
@@ -79,8 +72,7 @@ Context::Type TextInputTypeToGeneratedInputTypeEnum(ui::TextInputType type) {
} // namespace
-AshKeyboardControllerProxy::AshKeyboardControllerProxy()
- : animation_window_(NULL) {}
+AshKeyboardControllerProxy::AshKeyboardControllerProxy() {}
AshKeyboardControllerProxy::~AshKeyboardControllerProxy() {}
@@ -155,37 +147,12 @@ void AshKeyboardControllerProxy::ShowKeyboardContainer(
if (container->GetRootWindow() != ash::Shell::GetPrimaryRootWindow())
NOTIMPLEMENTED();
- ui::LayerAnimator* container_animator = container->layer()->GetAnimator();
- // If the container is not animating, makes sure the position and opacity
- // are at begin states for animation.
- if (!container_animator->is_animating()) {
- gfx::Transform transform;
- transform.Translate(0, GetKeyboardWindow()->bounds().height());
- container->SetTransform(transform);
- container->layer()->SetOpacity(kAnimationStartOrAfterHideOpacity);
- }
-
- container_animator->set_preemption_strategy(
- ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
-
- {
- // Scope the following animation settings as we don't want to animate
- // visibility change that triggered by a call to the base class function
- // ShowKeyboardContainer with these settings. The container should become
- // visible immediately.
- ui::ScopedLayerAnimationSettings settings(container_animator);
- settings.SetTweenType(gfx::Tween::EASE_IN);
- settings.SetTransitionDuration(
- base::TimeDelta::FromMilliseconds(kAnimationDurationMs));
- container->SetTransform(gfx::Transform());
- container->layer()->SetOpacity(1.0);
- }
-
- // TODO(bshe): Add animation observer and do the workspace resizing after
- // animation finished.
KeyboardControllerProxy::ShowKeyboardContainer(container);
- // GetTextInputClient may return NULL when keyboard-usability-experiment flag
- // is set.
+}
+
+void AshKeyboardControllerProxy::EnsureCaretInWorkArea() {
+ // GetTextInputClient may return NULL when keyboard-usability-experiment
+ // flag is set.
if (GetInputMethod()->GetTextInputClient()) {
gfx::Rect showing_area =
ash::Shell::GetScreen()->GetPrimaryDisplay().work_area();
@@ -193,44 +160,6 @@ void AshKeyboardControllerProxy::ShowKeyboardContainer(
}
}
-void AshKeyboardControllerProxy::HideKeyboardContainer(
- aura::Window* container) {
- // The following animation settings should persist within this function scope.
- // Otherwise, a call to base class function HideKeyboardContainer will hide
- // the container immediately.
- ui::LayerAnimator* container_animator = container->layer()->GetAnimator();
- container_animator->AddObserver(this);
- animation_window_ = container;
- ui::ScopedLayerAnimationSettings settings(container_animator);
- settings.SetTweenType(gfx::Tween::EASE_OUT);
- settings.SetTransitionDuration(
- base::TimeDelta::FromMilliseconds(kAnimationDurationMs));
- gfx::Transform transform;
- transform.Translate(0, GetKeyboardWindow()->bounds().height());
- container->SetTransform(transform);
- container->layer()->SetOpacity(kAnimationStartOrAfterHideOpacity);
-}
-
-void AshKeyboardControllerProxy::OnLayerAnimationEnded(
- ui::LayerAnimationSequence* sequence) {
- if (!animation_window_)
- return;
- ui::LayerAnimator* animator = animation_window_->layer()->GetAnimator();
- if (!animator->is_animating()) {
- KeyboardControllerProxy::HideKeyboardContainer(animation_window_);
- animator->RemoveObserver(this);
- animation_window_ = NULL;
- }
-};
-
-void AshKeyboardControllerProxy::OnLayerAnimationAborted(
- ui::LayerAnimationSequence* sequence) {
- if (!animation_window_)
- return;
- animation_window_->layer()->GetAnimator()->RemoveObserver(this);
- animation_window_ = NULL;
-};
-
void AshKeyboardControllerProxy::SetUpdateInputType(ui::TextInputType type) {
// TODO(bshe): Need to check the affected window's profile once multi-profile
// is supported.
diff --git a/chrome/browser/ui/ash/ash_keyboard_controller_proxy.h b/chrome/browser/ui/ash/ash_keyboard_controller_proxy.h
index 5d84774..655fd87 100644
--- a/chrome/browser/ui/ash/ash_keyboard_controller_proxy.h
+++ b/chrome/browser/ui/ash/ash_keyboard_controller_proxy.h
@@ -9,7 +9,6 @@
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/extensions/extension_function_dispatcher.h"
#include "content/public/browser/web_contents_observer.h"
-#include "ui/compositor/layer_animation_observer.h"
#include "ui/keyboard/keyboard_controller_proxy.h"
class ExtensionFunctionDispatcher;
@@ -32,8 +31,7 @@ class InputMethod;
class AshKeyboardControllerProxy
: public keyboard::KeyboardControllerProxy,
public content::WebContentsObserver,
- public ExtensionFunctionDispatcher::Delegate,
- public ui::LayerAnimationObserver {
+ public ExtensionFunctionDispatcher::Delegate {
public:
AshKeyboardControllerProxy();
virtual ~AshKeyboardControllerProxy();
@@ -51,7 +49,7 @@ class AshKeyboardControllerProxy
const content::MediaResponseCallback& callback) OVERRIDE;
virtual void SetupWebContents(content::WebContents* contents) OVERRIDE;
virtual void ShowKeyboardContainer(aura::Window* container) OVERRIDE;
- virtual void HideKeyboardContainer(aura::Window* container) OVERRIDE;
+ virtual void EnsureCaretInWorkArea() OVERRIDE;
// The overridden implementation dispatches
// chrome.virtualKeyboardPrivate.onTextInputBoxFocused event to extension to
@@ -70,17 +68,7 @@ class AshKeyboardControllerProxy
// content::WebContentsObserver overrides
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
- // ui::LayerAnimationObserver overrides
- virtual void OnLayerAnimationEnded(
- ui::LayerAnimationSequence* sequence) OVERRIDE;
- virtual void OnLayerAnimationAborted(
- ui::LayerAnimationSequence* sequence) OVERRIDE;
- virtual void OnLayerAnimationScheduled(
- ui::LayerAnimationSequence* sequence) OVERRIDE {}
-
scoped_ptr<ExtensionFunctionDispatcher> extension_function_dispatcher_;
- // The keyboard container window for animation.
- aura::Window* animation_window_;
DISALLOW_COPY_AND_ASSIGN(AshKeyboardControllerProxy);
};
diff --git a/chrome/browser/ui/ash/ash_keyboard_controller_proxy_browsertest.cc b/chrome/browser/ui/ash/ash_keyboard_controller_proxy_browsertest.cc
deleted file mode 100644
index 951d468..0000000
--- a/chrome/browser/ui/ash/ash_keyboard_controller_proxy_browsertest.cc
+++ /dev/null
@@ -1,185 +0,0 @@
-// Copyright 2014 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "chrome/browser/ui/ash/ash_keyboard_controller_proxy.h"
-
-#include "ash/shell.h"
-#include "ash/test/ash_test_base.h"
-#include "ui/aura/test/test_window_delegate.h"
-#include "ui/aura/window.h"
-#include "ui/base/ime/input_method.h"
-#include "ui/base/ime/input_method_factory.h"
-#include "ui/compositor/scoped_animation_duration_scale_mode.h"
-#include "ui/compositor/test/layer_animator_test_controller.h"
-#include "ui/keyboard/keyboard_controller.h"
-
-namespace {
-
-// Steps a layer animation until it is completed. Animations must be enabled.
-void RunAnimationForLayer(ui::Layer* layer) {
- // Animations must be enabled for stepping to work.
- ASSERT_NE(ui::ScopedAnimationDurationScaleMode::duration_scale_mode(),
- ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
-
- ui::LayerAnimatorTestController controller(layer->GetAnimator());
- gfx::AnimationContainerElement* element = layer->GetAnimator();
- // Multiple steps are required to complete complex animations.
- // TODO(vollick): This should not be necessary. crbug.com/154017
- while (controller.animator()->is_animating()) {
- controller.StartThreadedAnimationsIfNeeded();
- base::TimeTicks step_time = controller.animator()->last_step_time();
- element->Step(step_time + base::TimeDelta::FromMilliseconds(1000));
- }
-}
-
-} // namespace
-
-class TestAshKeyboardControllerProxy : public AshKeyboardControllerProxy {
- public:
- TestAshKeyboardControllerProxy()
- : window_(new aura::Window(&delegate_)),
- input_method_(ui::CreateInputMethod(NULL,
- gfx::kNullAcceleratedWidget)) {
- window_->Init(aura::WINDOW_LAYER_NOT_DRAWN);
- window_->set_owned_by_parent(false);
- }
-
- virtual ~TestAshKeyboardControllerProxy() {
- window_.reset();
- }
-
- // Overridden from AshKeyboardControllerProxy:
- virtual aura::Window* GetKeyboardWindow() OVERRIDE { return window_.get(); }
- virtual content::BrowserContext* GetBrowserContext() OVERRIDE { return NULL; }
- virtual ui::InputMethod* GetInputMethod() OVERRIDE {
- return input_method_.get();
- }
- virtual void RequestAudioInput(content::WebContents* web_contents,
- const content::MediaStreamRequest& request,
- const content::MediaResponseCallback& callback) OVERRIDE {}
-
- private:
- scoped_ptr<aura::Window> window_;
- aura::test::TestWindowDelegate delegate_;
- scoped_ptr<ui::InputMethod> input_method_;
-
- DISALLOW_COPY_AND_ASSIGN(TestAshKeyboardControllerProxy);
-};
-
-// TODO(bshe): Move this test back to unit test if
-// ui::SetUpInputMethodFactoryForTesting() is safe to be called in unit test.
-class AshKeyboardControllerProxyTest : public ash::test::AshTestBase {
- public:
- AshKeyboardControllerProxyTest() {}
-
- virtual ~AshKeyboardControllerProxyTest() {}
-
- // AshTestBase:
- virtual void SetUp() OVERRIDE;
- virtual void TearDown() OVERRIDE;
-
- TestAshKeyboardControllerProxy* proxy() { return proxy_; }
- keyboard::KeyboardController* controller() { return controller_.get(); }
-
- protected:
- void ShowKeyboard(aura::Window* container);
- void HideKeyboard(aura::Window* container);
-
- TestAshKeyboardControllerProxy* proxy_;
- scoped_ptr<keyboard::KeyboardController> controller_;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(AshKeyboardControllerProxyTest);
-};
-
-void AshKeyboardControllerProxyTest::SetUp() {
- ui::SetUpInputMethodFactoryForTesting();
- AshTestBase::SetUp();
- proxy_ = new TestAshKeyboardControllerProxy();
- controller_.reset(new keyboard::KeyboardController(proxy_));
-
- aura::Window* keyboard_container(controller_->GetContainerWindow());
- keyboard_container->SetBounds(ash::Shell::GetPrimaryRootWindow()->bounds());
- ash::Shell::GetPrimaryRootWindow()->AddChild(keyboard_container);
- keyboard_container->AddChild(proxy_->GetKeyboardWindow());
- ASSERT_NE(proxy_->GetKeyboardWindow()->bounds().height(), 0);
-}
-
-void AshKeyboardControllerProxyTest::TearDown() {
- AshTestBase::TearDown();
-}
-
-void AshKeyboardControllerProxyTest::ShowKeyboard(aura::Window* container) {
- proxy_->ShowKeyboardContainer(container);
-}
-
-void AshKeyboardControllerProxyTest::HideKeyboard(aura::Window* container) {
- proxy_->HideKeyboardContainer(container);
-}
-
-TEST_F(AshKeyboardControllerProxyTest, VirtualKeyboardContainerAnimation) {
- // We cannot short-circuit animations for this test.
- ui::ScopedAnimationDurationScaleMode normal_duration_mode(
- ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
-
- aura::Window* keyboard_container(controller()->GetContainerWindow());
- ui::Layer* layer = keyboard_container->layer();
-
- EXPECT_FALSE(keyboard_container->IsVisible());
- ShowKeyboard(keyboard_container);
-
- // Keyboard container and window should immediately become visible before
- // animation starts.
- EXPECT_TRUE(keyboard_container->IsVisible());
- EXPECT_TRUE(proxy()->GetKeyboardWindow()->IsVisible());
- float show_start_opacity = layer->opacity();
- gfx::Transform transform;
- transform.Translate(0, proxy()->GetKeyboardWindow()->bounds().height());
- EXPECT_EQ(transform, layer->transform());
-
- RunAnimationForLayer(layer);
- EXPECT_TRUE(keyboard_container->IsVisible());
- EXPECT_TRUE(proxy()->GetKeyboardWindow()->IsVisible());
- float show_end_opacity = layer->opacity();
- EXPECT_LT(show_start_opacity, show_end_opacity);
- EXPECT_EQ(gfx::Transform(), layer->transform());
-
- HideKeyboard(keyboard_container);
- EXPECT_TRUE(keyboard_container->IsVisible());
- EXPECT_TRUE(keyboard_container->layer()->visible());
- EXPECT_TRUE(proxy()->GetKeyboardWindow()->IsVisible());
- float hide_start_opacity = layer->opacity();
-
- RunAnimationForLayer(layer);
- EXPECT_FALSE(keyboard_container->IsVisible());
- EXPECT_FALSE(keyboard_container->layer()->visible());
- EXPECT_FALSE(proxy()->GetKeyboardWindow()->IsVisible());
- float hide_end_opacity = layer->opacity();
- EXPECT_GT(hide_start_opacity, hide_end_opacity);
- EXPECT_EQ(transform, layer->transform());
-}
-
-// Test for crbug.com/333284.
-TEST_F(AshKeyboardControllerProxyTest, VirtualKeyboardContainerShowWhileHide) {
- // We cannot short-circuit animations for this test.
- ui::ScopedAnimationDurationScaleMode normal_duration_mode(
- ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
-
- aura::Window* keyboard_container(controller()->GetContainerWindow());
- ui::Layer* layer = keyboard_container->layer();
-
- EXPECT_FALSE(keyboard_container->IsVisible());
- ShowKeyboard(keyboard_container);
- RunAnimationForLayer(layer);
-
- HideKeyboard(keyboard_container);
- // Before hide animation finishes, show keyboard again.
- ShowKeyboard(keyboard_container);
-
- RunAnimationForLayer(layer);
- EXPECT_TRUE(keyboard_container->IsVisible());
- EXPECT_TRUE(proxy()->GetKeyboardWindow()->IsVisible());
- EXPECT_EQ(1.0, layer->opacity());
- EXPECT_EQ(gfx::Transform(), layer->transform());
-}
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index e9475fa..7526dbf 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -1414,7 +1414,6 @@
'browser/ui/app_list/search/people/people_provider_browsertest.cc',
'browser/ui/app_list/search/webstore/webstore_provider_browsertest.cc',
'browser/ui/ash/accelerator_commands_browsertest.cc',
- 'browser/ui/ash/ash_keyboard_controller_proxy_browsertest.cc',
'browser/ui/ash/caps_lock_delegate_chromeos_browsertest.cc',
'browser/ui/ash/launcher/chrome_launcher_controller_browsertest.cc',
'browser/ui/ash/launcher/launcher_favicon_loader_browsertest.cc',
diff --git a/ui/keyboard/keyboard.gyp b/ui/keyboard/keyboard.gyp
index fe685c8..97ea950 100644
--- a/ui/keyboard/keyboard.gyp
+++ b/ui/keyboard/keyboard.gyp
@@ -88,6 +88,7 @@
'../aura/aura.gyp:aura',
'../aura/aura.gyp:aura_test_support',
'../compositor/compositor.gyp:compositor',
+ '../compositor/compositor.gyp:compositor_test_support',
'../gfx/gfx.gyp:gfx',
'../gfx/gfx.gyp:gfx_geometry',
'../resources/ui_resources.gyp:ui_test_pak',
diff --git a/ui/keyboard/keyboard_controller.cc b/ui/keyboard/keyboard_controller.cc
index 9075e64..764b552 100644
--- a/ui/keyboard/keyboard_controller.cc
+++ b/ui/keyboard/keyboard_controller.cc
@@ -14,6 +14,8 @@
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/ime/text_input_type.h"
+#include "ui/compositor/layer_animation_observer.h"
+#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/gfx/path.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/skia_util.h"
@@ -27,6 +29,13 @@ namespace {
const int kHideKeyboardDelayMs = 100;
+// The virtual keyboard show/hide animation duration.
+const int kAnimationDurationMs = 200;
+
+// The opacity of virtual keyboard container when show animation starts or
+// hide animation finishes.
+const float kAnimationStartOrAfterHideOpacity = 0.2f;
+
gfx::Rect KeyboardBoundsFromWindowBounds(const gfx::Rect& window_bounds) {
const float kKeyboardHeightRatio =
keyboard::IsKeyboardUsabilityExperimentEnabled() ? 1.0f : 0.3f;
@@ -118,6 +127,50 @@ class KeyboardWindowDelegate : public aura::WindowDelegate {
namespace keyboard {
+// Observer for both keyboard show and hide animations. It should be owned by
+// KeyboardController.
+class CallbackAnimationObserver : public ui::LayerAnimationObserver {
+ public:
+ CallbackAnimationObserver(ui::LayerAnimator* animator,
+ base::Callback<void(void)> callback);
+ virtual ~CallbackAnimationObserver();
+
+ private:
+ // Overridden from ui::LayerAnimationObserver:
+ virtual void OnLayerAnimationEnded(ui::LayerAnimationSequence* seq) OVERRIDE;
+ virtual void OnLayerAnimationAborted(
+ ui::LayerAnimationSequence* seq) OVERRIDE;
+ virtual void OnLayerAnimationScheduled(
+ ui::LayerAnimationSequence* seq) OVERRIDE {}
+
+ ui::LayerAnimator* animator_;
+ base::Callback<void(void)> callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
+};
+
+CallbackAnimationObserver::CallbackAnimationObserver(
+ ui::LayerAnimator* animator, base::Callback<void(void)> callback)
+ : animator_(animator), callback_(callback) {
+}
+
+CallbackAnimationObserver::~CallbackAnimationObserver() {
+ animator_->RemoveObserver(this);
+}
+
+void CallbackAnimationObserver::OnLayerAnimationEnded(
+ ui::LayerAnimationSequence* seq) {
+ if (animator_->is_animating())
+ return;
+ animator_->RemoveObserver(this);
+ callback_.Run();
+}
+
+void CallbackAnimationObserver::OnLayerAnimationAborted(
+ ui::LayerAnimationSequence* seq) {
+ animator_->RemoveObserver(this);
+}
+
// LayoutManager for the virtual keyboard container. Manages a single window
// (the virtual keyboard) and keeps it positioned at the bottom of the
// owner window.
@@ -218,7 +271,21 @@ void KeyboardController::HideKeyboard(HideReason reason) {
NotifyKeyboardBoundsChanging(gfx::Rect());
- proxy_->HideKeyboardContainer(container_.get());
+ ui::LayerAnimator* container_animator = container_->layer()->GetAnimator();
+ animation_observer_.reset(new CallbackAnimationObserver(
+ container_animator,
+ base::Bind(&KeyboardController::HideAnimationFinished,
+ base::Unretained(this))));
+ container_animator->AddObserver(animation_observer_.get());
+
+ ui::ScopedLayerAnimationSettings settings(container_animator);
+ settings.SetTweenType(gfx::Tween::EASE_OUT);
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kAnimationDurationMs));
+ gfx::Transform transform;
+ transform.Translate(0, proxy_->GetKeyboardWindow()->bounds().height());
+ container_->SetTransform(transform);
+ container_->layer()->SetOpacity(kAnimationStartOrAfterHideOpacity);
}
void KeyboardController::AddObserver(KeyboardControllerObserver* observer) {
@@ -314,11 +381,44 @@ void KeyboardController::OnShowImeIfNeeded() {
// point, it may in the process of hiding. We still need to show keyboard
// container in this case.
if (container_->IsVisible() &&
- !container_->layer()->GetAnimator()->is_animating()) {
+ !container_->layer()->GetAnimator()->is_animating())
return;
+
+ ShowKeyboard();
+}
+
+void KeyboardController::ShowKeyboard() {
+ ui::LayerAnimator* container_animator = container_->layer()->GetAnimator();
+
+ // If the container is not animating, makes sure the position and opacity
+ // are at begin states for animation.
+ if (!container_animator->is_animating()) {
+ gfx::Transform transform;
+ transform.Translate(0, proxy_->GetKeyboardWindow()->bounds().height());
+ container_->SetTransform(transform);
+ container_->layer()->SetOpacity(kAnimationStartOrAfterHideOpacity);
}
- NotifyKeyboardBoundsChanging(container_->children()[0]->bounds());
+ container_animator->set_preemption_strategy(
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
+ animation_observer_.reset(new CallbackAnimationObserver(
+ container_animator,
+ base::Bind(&KeyboardController::ShowAnimationFinished,
+ base::Unretained(this))));
+ container_animator->AddObserver(animation_observer_.get());
+
+ {
+ // Scope the following animation settings as we don't want to animate
+ // visibility change that triggered by a call to the base class function
+ // ShowKeyboardContainer with these settings. The container should become
+ // visible immediately.
+ ui::ScopedLayerAnimationSettings settings(container_animator);
+ settings.SetTweenType(gfx::Tween::EASE_IN);
+ settings.SetTransitionDuration(
+ base::TimeDelta::FromMilliseconds(kAnimationDurationMs));
+ container_->SetTransform(gfx::Transform());
+ container_->layer()->SetOpacity(1.0);
+ }
proxy_->ShowKeyboardContainer(container_.get());
}
@@ -327,4 +427,15 @@ bool KeyboardController::WillHideKeyboard() const {
return weak_factory_.HasWeakPtrs();
}
+void KeyboardController::ShowAnimationFinished() {
+ // Notify observers after animation finished to prevent reveal desktop
+ // background during animation.
+ NotifyKeyboardBoundsChanging(proxy_->GetKeyboardWindow()->bounds());
+ proxy_->EnsureCaretInWorkArea();
+}
+
+void KeyboardController::HideAnimationFinished() {
+ proxy_->HideKeyboardContainer(container_.get());
+}
+
} // namespace keyboard
diff --git a/ui/keyboard/keyboard_controller.h b/ui/keyboard/keyboard_controller.h
index a513659..fecbe16 100644
--- a/ui/keyboard/keyboard_controller.h
+++ b/ui/keyboard/keyboard_controller.h
@@ -27,6 +27,7 @@ class TextInputClient;
namespace keyboard {
+class CallbackAnimationObserver;
class KeyboardControllerObserver;
class KeyboardControllerProxy;
class KeyboardLayoutManager;
@@ -99,11 +100,23 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver,
const ui::InputMethod* input_method) OVERRIDE;
virtual void OnShowImeIfNeeded() OVERRIDE;
+ // Show virtual keyboard immediately with animation.
+ void ShowKeyboard();
+
// Returns true if keyboard is scheduled to hide.
bool WillHideKeyboard() const;
+ // Called when show and hide animation finished successfully. If the animation
+ // is aborted, it won't be called.
+ void ShowAnimationFinished();
+ void HideAnimationFinished();
+
scoped_ptr<KeyboardControllerProxy> proxy_;
scoped_ptr<aura::Window> container_;
+ // CallbackAnimationObserver should destructed before container_ because it
+ // uses container_'s animator.
+ scoped_ptr<CallbackAnimationObserver> animation_observer_;
+
ui::InputMethod* input_method_;
bool keyboard_visible_;
bool lock_keyboard_;
diff --git a/ui/keyboard/keyboard_controller_proxy.cc b/ui/keyboard/keyboard_controller_proxy.cc
index 778c8bf..8923d21 100644
--- a/ui/keyboard/keyboard_controller_proxy.cc
+++ b/ui/keyboard/keyboard_controller_proxy.cc
@@ -184,6 +184,9 @@ void KeyboardControllerProxy::SetUpdateInputType(ui::TextInputType type) {
}
}
+void KeyboardControllerProxy::EnsureCaretInWorkArea() {
+}
+
void KeyboardControllerProxy::SetupWebContents(content::WebContents* contents) {
}
diff --git a/ui/keyboard/keyboard_controller_proxy.h b/ui/keyboard/keyboard_controller_proxy.h
index c028b74..de4473e 100644
--- a/ui/keyboard/keyboard_controller_proxy.h
+++ b/ui/keyboard/keyboard_controller_proxy.h
@@ -74,6 +74,10 @@ class KEYBOARD_EXPORT KeyboardControllerProxy {
// type the of focused input box.
virtual void SetUpdateInputType(ui::TextInputType type);
+ // Ensures caret in current work area (not occluded by virtual keyboard
+ // window).
+ virtual void EnsureCaretInWorkArea();
+
protected:
// Gets the BrowserContext to use for creating the WebContents hosting the
// keyboard.
diff --git a/ui/keyboard/keyboard_controller_unittest.cc b/ui/keyboard/keyboard_controller_unittest.cc
index 98e0895..c2f0c86 100644
--- a/ui/keyboard/keyboard_controller_unittest.cc
+++ b/ui/keyboard/keyboard_controller_unittest.cc
@@ -18,14 +18,34 @@
#include "ui/base/ime/input_method_factory.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/compositor/layer_type.h"
+#include "ui/compositor/scoped_animation_duration_scale_mode.h"
+#include "ui/compositor/test/layer_animator_test_controller.h"
#include "ui/gfx/rect.h"
#include "ui/keyboard/keyboard_controller.h"
+#include "ui/keyboard/keyboard_controller_observer.h"
#include "ui/keyboard/keyboard_controller_proxy.h"
#include "ui/keyboard/keyboard_switches.h"
namespace keyboard {
namespace {
+// Steps a layer animation until it is completed. Animations must be enabled.
+void RunAnimationForLayer(ui::Layer* layer) {
+ // Animations must be enabled for stepping to work.
+ ASSERT_NE(ui::ScopedAnimationDurationScaleMode::duration_scale_mode(),
+ ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
+
+ ui::LayerAnimatorTestController controller(layer->GetAnimator());
+ gfx::AnimationContainerElement* element = layer->GetAnimator();
+ // Multiple steps are required to complete complex animations.
+ // TODO(vollick): This should not be necessary. crbug.com/154017
+ while (controller.animator()->is_animating()) {
+ controller.StartThreadedAnimationsIfNeeded();
+ base::TimeTicks step_time = controller.animator()->last_step_time();
+ element->Step(step_time + base::TimeDelta::FromMilliseconds(1000));
+ }
+}
+
// An event handler that focuses a window when it is clicked/touched on. This is
// used to match the focus manger behaviour in ash and views.
class TestFocusController : public ui::EventHandler {
@@ -453,6 +473,115 @@ TEST_F(KeyboardControllerTest, KeyboardResizingFromContents) {
EXPECT_EQ(180, keyboard_window->bounds().height());
}
+class KeyboardControllerAnimationTest : public KeyboardControllerTest,
+ public KeyboardControllerObserver {
+ public:
+ KeyboardControllerAnimationTest() {}
+ virtual ~KeyboardControllerAnimationTest() {}
+
+ virtual void SetUp() OVERRIDE {
+ // We cannot short-circuit animations for this test.
+ ui::ScopedAnimationDurationScaleMode normal_duration_mode(
+ ui::ScopedAnimationDurationScaleMode::NORMAL_DURATION);
+
+ KeyboardControllerTest::SetUp();
+
+ const gfx::Rect& root_bounds = root_window()->bounds();
+ keyboard_container()->SetBounds(root_bounds);
+ root_window()->AddChild(keyboard_container());
+ controller()->AddObserver(this);
+ }
+
+ virtual void TearDown() OVERRIDE {
+ controller()->RemoveObserver(this);
+ KeyboardControllerTest::TearDown();
+ }
+
+ protected:
+ // KeyboardControllerObserver overrides
+ virtual void OnKeyboardBoundsChanging(const gfx::Rect& new_bounds) OVERRIDE {
+ notified_bounds_ = new_bounds;
+ }
+
+ const gfx::Rect& notified_bounds() { return notified_bounds_; }
+
+ aura::Window* keyboard_container() {
+ return controller()->GetContainerWindow();
+ }
+
+ aura::Window* keyboard_window() {
+ return proxy()->GetKeyboardWindow();
+ }
+
+ private:
+ gfx::Rect notified_bounds_;
+
+ DISALLOW_COPY_AND_ASSIGN(KeyboardControllerAnimationTest);
+};
+
+// Tests virtual keyboard has correct show and hide animation.
+TEST_F(KeyboardControllerAnimationTest, ContainerAnimation) {
+ ui::Layer* layer = keyboard_container()->layer();
+ ShowKeyboard();
+
+ // Keyboard container and window should immediately become visible before
+ // animation starts.
+ EXPECT_TRUE(keyboard_container()->IsVisible());
+ EXPECT_TRUE(keyboard_window()->IsVisible());
+ float show_start_opacity = layer->opacity();
+ gfx::Transform transform;
+ transform.Translate(0, keyboard_window()->bounds().height());
+ EXPECT_EQ(transform, layer->transform());
+ EXPECT_EQ(gfx::Rect(), notified_bounds());
+
+ RunAnimationForLayer(layer);
+ EXPECT_TRUE(keyboard_container()->IsVisible());
+ EXPECT_TRUE(keyboard_window()->IsVisible());
+ float show_end_opacity = layer->opacity();
+ EXPECT_LT(show_start_opacity, show_end_opacity);
+ EXPECT_EQ(gfx::Transform(), layer->transform());
+ // KeyboardController should notify the bounds of keyboard window to its
+ // observers after show animation finished.
+ EXPECT_EQ(keyboard_window()->bounds(), notified_bounds());
+
+ // Directly hide keyboard without delay.
+ controller()->HideKeyboard(KeyboardController::HIDE_REASON_AUTOMATIC);
+ EXPECT_TRUE(keyboard_container()->IsVisible());
+ EXPECT_TRUE(keyboard_container()->layer()->visible());
+ EXPECT_TRUE(keyboard_window()->IsVisible());
+ float hide_start_opacity = layer->opacity();
+ // KeyboardController should notify the bounds of keyboard window to its
+ // observers before hide animation starts.
+ EXPECT_EQ(gfx::Rect(), notified_bounds());
+
+ RunAnimationForLayer(layer);
+ EXPECT_FALSE(keyboard_container()->IsVisible());
+ EXPECT_FALSE(keyboard_container()->layer()->visible());
+ EXPECT_FALSE(keyboard_window()->IsVisible());
+ float hide_end_opacity = layer->opacity();
+ EXPECT_GT(hide_start_opacity, hide_end_opacity);
+ EXPECT_EQ(transform, layer->transform());
+ EXPECT_EQ(gfx::Rect(), notified_bounds());
+}
+
+// Show keyboard during keyboard hide animation should abort the hide animation
+// and the keyboard should animate in.
+// Test for crbug.com/333284.
+TEST_F(KeyboardControllerAnimationTest, ContainerShowWhileHide) {
+ ui::Layer* layer = keyboard_container()->layer();
+ ShowKeyboard();
+ RunAnimationForLayer(layer);
+
+ controller()->HideKeyboard(KeyboardController::HIDE_REASON_AUTOMATIC);
+ // Before hide animation finishes, show keyboard again.
+ ShowKeyboard();
+ RunAnimationForLayer(layer);
+ EXPECT_TRUE(keyboard_container()->IsVisible());
+ EXPECT_TRUE(keyboard_window()->IsVisible());
+ EXPECT_EQ(1.0, layer->opacity());
+ EXPECT_EQ(gfx::Transform(), layer->transform());
+}
+
class KeyboardControllerUsabilityTest : public KeyboardControllerTest {
public:
KeyboardControllerUsabilityTest() {}