diff options
-rw-r--r-- | chrome/browser/ui/ash/ash_keyboard_controller_proxy.cc | 83 | ||||
-rw-r--r-- | chrome/browser/ui/ash/ash_keyboard_controller_proxy.h | 16 | ||||
-rw-r--r-- | chrome/browser/ui/ash/ash_keyboard_controller_proxy_browsertest.cc | 185 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 1 | ||||
-rw-r--r-- | ui/keyboard/keyboard.gyp | 1 | ||||
-rw-r--r-- | ui/keyboard/keyboard_controller.cc | 117 | ||||
-rw-r--r-- | ui/keyboard/keyboard_controller.h | 13 | ||||
-rw-r--r-- | ui/keyboard/keyboard_controller_proxy.cc | 3 | ||||
-rw-r--r-- | ui/keyboard/keyboard_controller_proxy.h | 4 | ||||
-rw-r--r-- | ui/keyboard/keyboard_controller_unittest.cc | 129 |
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() {} |