diff options
author | bshe@chromium.org <bshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-02 06:28:18 +0000 |
---|---|---|
committer | bshe@chromium.org <bshe@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-02-02 06:28:18 +0000 |
commit | 282bb08fafb3e13cd408751e79811c8c5add50ce (patch) | |
tree | 6b8b37f6217b5e9b1a7f88dc78c97467f5e1c562 /ui/keyboard | |
parent | 6568c523ae76412feb72d9f209529f94885d31f9 (diff) | |
download | chromium_src-282bb08fafb3e13cd408751e79811c8c5add50ce.zip chromium_src-282bb08fafb3e13cd408751e79811c8c5add50ce.tar.gz chromium_src-282bb08fafb3e13cd408751e79811c8c5add50ce.tar.bz2 |
Resize work space after show keyboard animation finished
This CL adds animation observer to both show and hide virtual
keyboard animation. And some tasks that should be done after
animation finished is moved to the correct place.
BUG=336257
Review URL: https://codereview.chromium.org/140823016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@248400 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui/keyboard')
-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 |
6 files changed, 264 insertions, 3 deletions
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() {} |