diff options
Diffstat (limited to 'chromium/ui/keyboard')
-rw-r--r-- | chromium/ui/keyboard/BUILD.gn | 2 | ||||
-rw-r--r-- | chromium/ui/keyboard/container_behavior.h | 6 | ||||
-rw-r--r-- | chromium/ui/keyboard/container_floating_behavior.cc | 70 | ||||
-rw-r--r-- | chromium/ui/keyboard/container_floating_behavior.h | 28 | ||||
-rw-r--r-- | chromium/ui/keyboard/container_floating_behavior_unittest.cc | 69 | ||||
-rw-r--r-- | chromium/ui/keyboard/container_full_width_behavior.cc | 6 | ||||
-rw-r--r-- | chromium/ui/keyboard/container_full_width_behavior.h | 6 | ||||
-rw-r--r-- | chromium/ui/keyboard/keyboard_controller.cc | 85 | ||||
-rw-r--r-- | chromium/ui/keyboard/keyboard_controller.h | 19 | ||||
-rw-r--r-- | chromium/ui/keyboard/keyboard_controller_observer.h | 10 | ||||
-rw-r--r-- | chromium/ui/keyboard/keyboard_controller_unittest.cc | 167 | ||||
-rw-r--r-- | chromium/ui/keyboard/keyboard_util.cc | 5 | ||||
-rw-r--r-- | chromium/ui/keyboard/notification_manager.cc | 10 | ||||
-rw-r--r-- | chromium/ui/keyboard/queued_container_type.cc | 24 | ||||
-rw-r--r-- | chromium/ui/keyboard/queued_container_type.h | 37 | ||||
-rw-r--r-- | chromium/ui/keyboard/resources/inputview_adapter.js | 7 |
16 files changed, 444 insertions, 107 deletions
diff --git a/chromium/ui/keyboard/BUILD.gn b/chromium/ui/keyboard/BUILD.gn index 3b2816104d8..ccf79d43925 100644 --- a/chromium/ui/keyboard/BUILD.gn +++ b/chromium/ui/keyboard/BUILD.gn @@ -35,6 +35,8 @@ jumbo_component("keyboard") { "keyboard_util.h", "notification_manager.cc", "notification_manager.h", + "queued_container_type.cc", + "queued_container_type.h", ] defines = [ "KEYBOARD_IMPLEMENTATION" ] diff --git a/chromium/ui/keyboard/container_behavior.h b/chromium/ui/keyboard/container_behavior.h index e6224c3552d..d641d88bff6 100644 --- a/chromium/ui/keyboard/container_behavior.h +++ b/chromium/ui/keyboard/container_behavior.h @@ -60,9 +60,11 @@ class KEYBOARD_EXPORT ContainerBehavior { virtual bool IsDragHandle(const gfx::Vector2d& offset, const gfx::Size& keyboard_size) const = 0; - virtual void SavePosition(const gfx::Point& position) = 0; + virtual void SavePosition(const gfx::Rect& keyboard_bounds, + const gfx::Size& screen_size) = 0; - virtual void HandlePointerEvent(const ui::LocatedEvent& event) = 0; + virtual void HandlePointerEvent(const ui::LocatedEvent& event, + const gfx::Rect& display_bounds) = 0; virtual ContainerType GetType() const = 0; diff --git a/chromium/ui/keyboard/container_floating_behavior.cc b/chromium/ui/keyboard/container_floating_behavior.cc index 9d93d393baf..855cdc202cf 100644 --- a/chromium/ui/keyboard/container_floating_behavior.cc +++ b/chromium/ui/keyboard/container_floating_behavior.cc @@ -69,7 +69,7 @@ const gfx::Rect ContainerFloatingBehavior::AdjustSetBoundsRequest( const gfx::Rect& requested_bounds) { gfx::Rect keyboard_bounds = requested_bounds; - if (UseDefaultPosition()) { + if (!default_position_) { // If the keyboard hasn't been shown yet, ignore the request and use // default. gfx::Point default_location = @@ -80,18 +80,39 @@ const gfx::Rect ContainerFloatingBehavior::AdjustSetBoundsRequest( // the screen. keyboard_bounds = ContainKeyboardToScreenBounds(keyboard_bounds, display_bounds); + SavePosition(keyboard_bounds, display_bounds.size()); } return keyboard_bounds; } +void ContainerFloatingBehavior::SavePosition(const gfx::Rect& keyboard_bounds, + const gfx::Size& screen_size) { + int left_distance = keyboard_bounds.x(); + int right_distance = screen_size.width() - (keyboard_bounds.right()); + int top_distance = keyboard_bounds.y(); + int bottom_distance = screen_size.height() - (keyboard_bounds.bottom()); + + double available_width = left_distance + right_distance; + double available_height = top_distance + bottom_distance; + + if (!default_position_) { + default_position_ = std::make_unique<KeyboardPosition>(); + } + + default_position_->left_padding_allotment_ratio = + left_distance / available_width; + default_position_->top_padding_allotment_ratio = + top_distance / available_height; +} + gfx::Rect ContainerFloatingBehavior::ContainKeyboardToScreenBounds( const gfx::Rect& keyboard_bounds, const gfx::Rect& display_bounds) const { int left = keyboard_bounds.x(); int top = keyboard_bounds.y(); - int right = left + keyboard_bounds.width(); - int bottom = top + keyboard_bounds.height(); + int right = keyboard_bounds.right(); + int bottom = keyboard_bounds.bottom(); // Prevent keyboard from appearing off screen or overlapping with the edge. if (left < 0) { @@ -118,37 +139,36 @@ bool ContainerFloatingBehavior::IsOverscrollAllowed() const { return false; } -bool ContainerFloatingBehavior::UseDefaultPosition() const { - // (-1, -1) is used as a sentinel unset value. - return default_position_.x() == -1; -} - gfx::Point ContainerFloatingBehavior::GetPositionForShowingKeyboard( const gfx::Size& keyboard_size, const gfx::Rect& display_bounds) const { // Start with the last saved position - gfx::Point position = default_position_; - if (UseDefaultPosition()) { + gfx::Point top_left_offset; + KeyboardPosition* position = default_position_.get(); + if (position == nullptr) { // If there is none, center the keyboard along the bottom of the screen. - position.set_x(display_bounds.width() - keyboard_size.width() - - kDefaultDistanceFromScreenRight); - position.set_y(display_bounds.height() - keyboard_size.height() - - kDefaultDistanceFromScreenBottom); + top_left_offset.set_x(display_bounds.width() - keyboard_size.width() - + kDefaultDistanceFromScreenRight); + top_left_offset.set_y(display_bounds.height() - keyboard_size.height() - + kDefaultDistanceFromScreenBottom); + } else { + double left = (display_bounds.width() - keyboard_size.width()) * + position->left_padding_allotment_ratio; + double top = (display_bounds.height() - keyboard_size.height()) * + position->top_padding_allotment_ratio; + top_left_offset.set_x((int)left); + top_left_offset.set_y((int)top); } // Make sure that this location is valid according to the current size of the // screen. - gfx::Rect keyboard_bounds = gfx::Rect(position, keyboard_size); + gfx::Rect keyboard_bounds = gfx::Rect(top_left_offset, keyboard_size); gfx::Rect valid_keyboard_bounds = ContainKeyboardToScreenBounds(keyboard_bounds, display_bounds); return valid_keyboard_bounds.origin(); } -void ContainerFloatingBehavior::SavePosition(const gfx::Point& position) { - default_position_ = position; -} - bool ContainerFloatingBehavior::IsDragHandle( const gfx::Vector2d& offset, const gfx::Size& keyboard_size) const { @@ -156,7 +176,8 @@ bool ContainerFloatingBehavior::IsDragHandle( } void ContainerFloatingBehavior::HandlePointerEvent( - const ui::LocatedEvent& event) { + const ui::LocatedEvent& event, + const gfx::Rect& display_bounds) { // Cannot call UI-backed operations without a KeyboardController DCHECK(controller_); auto kb_offset = gfx::Vector2d(event.x(), event.y()); @@ -205,7 +226,7 @@ void ContainerFloatingBehavior::HandlePointerEvent( const gfx::Rect new_bounds = gfx::Rect(new_keyboard_location, keyboard_bounds.size()); controller_->MoveKeyboard(new_bounds); - SavePosition(container->bounds().origin()); + SavePosition(container->bounds(), display_bounds.size()); handle_drag = true; } if (!handle_drag && drag_descriptor_) { @@ -219,9 +240,10 @@ void ContainerFloatingBehavior::SetCanonicalBounds( const gfx::Rect& display_bounds) { gfx::Point keyboard_location = GetPositionForShowingKeyboard(container->bounds().size(), display_bounds); - SavePosition(keyboard_location); - container->SetBounds( - gfx::Rect(keyboard_location, container->bounds().size())); + gfx::Rect keyboard_bounds = + gfx::Rect(keyboard_location, container->bounds().size()); + SavePosition(keyboard_bounds, display_bounds.size()); + container->SetBounds(keyboard_bounds); } bool ContainerFloatingBehavior::TextBlurHidesKeyboard() const { diff --git a/chromium/ui/keyboard/container_floating_behavior.h b/chromium/ui/keyboard/container_floating_behavior.h index 89124dfa9a4..6ea2b2abfde 100644 --- a/chromium/ui/keyboard/container_floating_behavior.h +++ b/chromium/ui/keyboard/container_floating_behavior.h @@ -23,6 +23,11 @@ namespace keyboard { constexpr int kDefaultDistanceFromScreenBottom = 20; constexpr int kDefaultDistanceFromScreenRight = 20; +struct KeyboardPosition { + double left_padding_allotment_ratio; + double top_padding_allotment_ratio; +}; + class KEYBOARD_EXPORT ContainerFloatingBehavior : public ContainerBehavior { public: ContainerFloatingBehavior(KeyboardController* controller); @@ -42,8 +47,10 @@ class KEYBOARD_EXPORT ContainerFloatingBehavior : public ContainerBehavior { bool IsOverscrollAllowed() const override; bool IsDragHandle(const gfx::Vector2d& offset, const gfx::Size& keyboard_size) const override; - void SavePosition(const gfx::Point& position) override; - void HandlePointerEvent(const ui::LocatedEvent& event) override; + void SavePosition(const gfx::Rect& keyboard_bounds, + const gfx::Size& screen_size) override; + void HandlePointerEvent(const ui::LocatedEvent& event, + const gfx::Rect& display_bounds) override; void SetCanonicalBounds(aura::Window* container, const gfx::Rect& display_bounds) override; ContainerType GetType() const override; @@ -52,6 +59,11 @@ class KEYBOARD_EXPORT ContainerFloatingBehavior : public ContainerBehavior { bool BoundsAffectWorkspaceLayout() const override; bool SetDraggableArea(const gfx::Rect& rect) override; + // Calculate the position of the keyboard for when it is being shown. + gfx::Point GetPositionForShowingKeyboard( + const gfx::Size& keyboard_size, + const gfx::Rect& display_bounds) const; + private: // Ensures that the keyboard is neither off the screen nor overlapping an // edge. @@ -62,19 +74,11 @@ class KEYBOARD_EXPORT ContainerFloatingBehavior : public ContainerBehavior { // Saves the current keyboard location for use the next time it is displayed. void UpdateLastPoint(const gfx::Point& position); - // Returns true if the keyboard has not been display/moved yet and the default - // position should be used. - bool UseDefaultPosition() const; - - // Calculate the position of the keyboard for when it is being shown. - gfx::Point GetPositionForShowingKeyboard( - const gfx::Size& keyboard_size, - const gfx::Rect& display_bounds) const; - KeyboardController* controller_; // TODO(blakeo): cache the default_position_ on a per-display basis. - gfx::Point default_position_ = gfx::Point(-1, -1); + std::unique_ptr<struct keyboard::KeyboardPosition> default_position_ = + nullptr; // Current state of a cursor drag to move the keyboard, if one exists. // Otherwise nullptr. diff --git a/chromium/ui/keyboard/container_floating_behavior_unittest.cc b/chromium/ui/keyboard/container_floating_behavior_unittest.cc index c3e81c710f4..6db3b404566 100644 --- a/chromium/ui/keyboard/container_floating_behavior_unittest.cc +++ b/chromium/ui/keyboard/container_floating_behavior_unittest.cc @@ -32,7 +32,8 @@ TEST(ContainerFloatingBehaviorTest, AdjustSetBoundsRequest) { keyboard_height); // Save an arbitrary position so that default location will not be used. - floating_behavior.SavePosition(gfx::Point(0, 0)); + floating_behavior.SavePosition( + gfx::Rect(0, 0, keyboard_width, keyboard_height), workspace.size()); gfx::Rect result = floating_behavior.AdjustSetBoundsRequest(workspace, center); @@ -49,6 +50,69 @@ TEST(ContainerFloatingBehaviorTest, AdjustSetBoundsRequest) { result); } +TEST(ContainerFloatingBehaviorTest, AdjustSetBoundsRequestVariousSides) { + ContainerFloatingBehavior floating_behavior(nullptr); + + const int keyboard_width = 100; + const int keyboard_height = 100; + gfx::Size keyboard_size = gfx::Size(keyboard_width, keyboard_height); + gfx::Rect workspace_wide(0, 0, 1000, 500); + gfx::Rect workspace_tall(0, 0, 500, 1000); + gfx::Rect top_left(0, 0, keyboard_width, keyboard_height); + gfx::Rect top_right(900, 0, keyboard_width, keyboard_height); + gfx::Rect bottom_left(0, 400, keyboard_width, keyboard_height); + gfx::Rect bottom_right(900, 400, keyboard_width, keyboard_height); + gfx::Rect bottomish_center(450, 390, keyboard_width, keyboard_height); + + // Save an arbitrary position so that default location will not be used. + floating_behavior.SavePosition( + gfx::Rect(0, 0, keyboard_width, keyboard_height), workspace_wide.size()); + + floating_behavior.AdjustSetBoundsRequest(workspace_wide, top_left); + gfx::Point result = floating_behavior.GetPositionForShowingKeyboard( + keyboard_size, workspace_wide); + ASSERT_EQ(gfx::Point(0, 0), result); + result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size, + workspace_tall); + ASSERT_EQ(gfx::Point(0, 0), result); + + floating_behavior.AdjustSetBoundsRequest(workspace_wide, top_right); + result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size, + workspace_wide); + ASSERT_EQ(gfx::Point(900, 0), result); + result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size, + workspace_tall); + ASSERT_EQ(gfx::Point(400, 0), result); + + floating_behavior.AdjustSetBoundsRequest(workspace_wide, bottom_left); + result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size, + workspace_wide); + ASSERT_EQ(gfx::Point(0, 400), result); + result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size, + workspace_tall); + ASSERT_EQ(gfx::Point(0, 900), result); + + floating_behavior.AdjustSetBoundsRequest(workspace_wide, bottom_right); + result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size, + workspace_wide); + ASSERT_EQ(gfx::Point(900, 400), result); + result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size, + workspace_tall); + ASSERT_EQ(gfx::Point(400, 900), result); + + floating_behavior.AdjustSetBoundsRequest(workspace_wide, bottomish_center); + result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size, + workspace_wide); + ASSERT_EQ(gfx::Point(450, 390), result); + result = floating_behavior.GetPositionForShowingKeyboard(keyboard_size, + workspace_tall); + + // rather than 400:0 for the vertical padding, use 390:10 + // with 900 pixels available this ratio results in 877.5, which is truncated. + // 390 / 400 * 900 = 877.5 + ASSERT_EQ(gfx::Point(200, 877), result); +} + TEST(ContainerFloatingBehaviorTest, DontSaveCoordinatesUntilKeyboardMoved) { ContainerFloatingBehavior floating_behavior(nullptr); @@ -77,7 +141,8 @@ TEST(ContainerFloatingBehaviorTest, DontSaveCoordinatesUntilKeyboardMoved) { // Simulate the user clicking and moving the keyboard to some arbitrary // location (it doesn't matter where). Now that the coordinate is known to be // user-determined. - floating_behavior.SavePosition(gfx::Point(10, 10)); + floating_behavior.SavePosition( + gfx::Rect(10, 10, keyboard_width, keyboard_height), workspace.size()); // Move the keyboard somewhere else. The coordinates should be taken as-is // without being adjusted. diff --git a/chromium/ui/keyboard/container_full_width_behavior.cc b/chromium/ui/keyboard/container_full_width_behavior.cc index 15cbf04698c..2dca9c087ec 100644 --- a/chromium/ui/keyboard/container_full_width_behavior.cc +++ b/chromium/ui/keyboard/container_full_width_behavior.cc @@ -82,7 +82,8 @@ bool ContainerFullWidthBehavior::IsOverscrollAllowed() const { return controller_ && !controller_->keyboard_locked(); } -void ContainerFullWidthBehavior::SavePosition(const gfx::Point& position) { +void ContainerFullWidthBehavior::SavePosition(const gfx::Rect& keyboard_bounds, + const gfx::Size& screen_size) { // No-op. Nothing to save. } @@ -93,7 +94,8 @@ bool ContainerFullWidthBehavior::IsDragHandle( } void ContainerFullWidthBehavior::HandlePointerEvent( - const ui::LocatedEvent& event) { + const ui::LocatedEvent& event, + const gfx::Rect& display_bounds) { // No-op. Nothing special to do for pointer events. } diff --git a/chromium/ui/keyboard/container_full_width_behavior.h b/chromium/ui/keyboard/container_full_width_behavior.h index 5a5d1a493d7..077d3b92eb6 100644 --- a/chromium/ui/keyboard/container_full_width_behavior.h +++ b/chromium/ui/keyboard/container_full_width_behavior.h @@ -39,8 +39,10 @@ class KEYBOARD_EXPORT ContainerFullWidthBehavior : public ContainerBehavior { bool IsOverscrollAllowed() const override; bool IsDragHandle(const gfx::Vector2d& offset, const gfx::Size& keyboard_size) const override; - void SavePosition(const gfx::Point& position) override; - void HandlePointerEvent(const ui::LocatedEvent& event) override; + void SavePosition(const gfx::Rect& keyboard_bounds, + const gfx::Size& screen_size) override; + void HandlePointerEvent(const ui::LocatedEvent& event, + const gfx::Rect& display_bounds) override; void SetCanonicalBounds(aura::Window* container, const gfx::Rect& display_bounds) override; ContainerType GetType() const override; diff --git a/chromium/ui/keyboard/keyboard_controller.cc b/chromium/ui/keyboard/keyboard_controller.cc index 76a576bfe09..b9ba7f519ef 100644 --- a/chromium/ui/keyboard/keyboard_controller.cc +++ b/chromium/ui/keyboard/keyboard_controller.cc @@ -12,6 +12,7 @@ #include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/threading/thread_task_runner_handle.h" +#include "base/time/time.h" #include "ui/aura/env.h" #include "ui/aura/window.h" #include "ui/aura/window_delegate.h" @@ -25,6 +26,7 @@ #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/display/types/display_constants.h" +#include "ui/events/base_event_utils.h" #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/vector2d.h" #include "ui/gfx/path.h" @@ -36,6 +38,7 @@ #include "ui/keyboard/keyboard_ui.h" #include "ui/keyboard/keyboard_util.h" #include "ui/keyboard/notification_manager.h" +#include "ui/keyboard/queued_container_type.h" #include "ui/wm/core/window_animations.h" #if defined(OS_CHROMEOS) @@ -51,6 +54,13 @@ constexpr int kHideKeyboardDelayMs = 100; // intermediate state for more than 5 seconds. constexpr int kReportLingeringStateDelayMs = 5000; +// Delay threshold after the keyboard enters the WILL_HIDE state. If text focus +// is regained during this threshold, the keyboard will show again, even if it +// is an asynchronous event. This is for the benefit of things like login flow +// where the password field may get text focus after an animation that plays +// after the user enters their username. +constexpr int kTransientBlurThresholdMs = 3500; + // State transition diagram (document linked from crbug.com/719905) bool isAllowedStateTransition(keyboard::KeyboardControllerState from, keyboard::KeyboardControllerState to) { @@ -202,12 +212,11 @@ KeyboardController::KeyboardController(std::unique_ptr<KeyboardUI> ui, show_on_content_update_(false), keyboard_locked_(false), state_(KeyboardControllerState::UNKNOWN), - enqueued_container_type_(ContainerType::FULL_WIDTH), weak_factory_report_lingering_state_(this), weak_factory_will_hide_(this) { ui_->GetInputMethod()->AddObserver(this); ui_->SetController(this); - SetContainerBehaviorInternal(enqueued_container_type_); + SetContainerBehaviorInternal(ContainerType::FULL_WIDTH); ChangeState(KeyboardControllerState::INITIAL); } @@ -385,11 +394,8 @@ void KeyboardController::HideKeyboard(HideReason reason) { } void KeyboardController::HideAnimationFinished() { - if (state_ != KeyboardControllerState::HIDDEN) - return; - - if (enqueued_container_type_ != container_behavior_->GetType()) { - SetContainerBehaviorInternal(enqueued_container_type_); + if (state_ == KeyboardControllerState::HIDDEN && queued_container_type_) { + SetContainerBehaviorInternal(queued_container_type_->container_type()); ShowKeyboard(false /* lock */); } } @@ -498,17 +504,36 @@ void KeyboardController::OnTextInputStateChanged( return; } } else { - // Abort a pending keyboard hide. - if (WillHideKeyboard()) - ChangeState(KeyboardControllerState::SHOWN); + switch (state_) { + case KeyboardControllerState::WILL_HIDE: + // Abort a pending keyboard hide. + ChangeState(KeyboardControllerState::SHOWN); + return; + case KeyboardControllerState::HIDDEN: + if (focused) + ShowKeyboardIfWithinTransientBlurThreshold(); + return; + default: + break; + } // Do not explicitly show the Virtual keyboard unless it is in the process - // of hiding. Instead, the virtual keyboard is shown in response to a user - // gesture (mouse or touch) that is received while an element has input - // focus. Showing the keyboard requires an explicit call to - // OnShowImeIfNeeded. + // of hiding or the hide duration was very short (transient blur). Instead, + // the virtual keyboard is shown in response to a user gesture (mouse or + // touch) that is received while an element has input focus. Showing the + // keyboard requires an explicit call to OnShowImeIfNeeded. } } +void KeyboardController::ShowKeyboardIfWithinTransientBlurThreshold() { + static const base::TimeDelta kTransientBlurThreshold = + base::TimeDelta::FromMilliseconds(kTransientBlurThresholdMs); + + const base::Time now = base::Time::Now(); + const base::TimeDelta time_since_last_blur = now - time_of_last_blur_; + if (time_since_last_blur < kTransientBlurThreshold) + ShowKeyboard(false); +} + void KeyboardController::OnShowImeIfNeeded() { // Calling |ShowKeyboardInternal| may move the keyboard to another display. if (IsKeyboardEnabled() && !keyboard_locked()) @@ -624,6 +649,10 @@ void KeyboardController::PopulateKeyboardContent(int64_t display_id, container_behavior_->DoShowingAnimation(container_.get(), &settings); + // the queued container behavior will notify JS to change layout when it + // gets destroyed. + queued_container_type_ = nullptr; + ChangeState(KeyboardControllerState::SHOWN); NotifyKeyboardBoundsChangingAndEnsureCaretInWorkArea(); } @@ -678,6 +707,8 @@ void KeyboardController::ChangeState(KeyboardControllerState state) { if (state_ == state) return; + KeyboardControllerState original_state = state_; + state_ = state; if (state != KeyboardControllerState::WILL_HIDE) @@ -691,11 +722,16 @@ void KeyboardController::ChangeState(KeyboardControllerState state) { switch (state_) { case KeyboardControllerState::LOADING_EXTENSION: case KeyboardControllerState::WILL_HIDE: + if (state_ == KeyboardControllerState::WILL_HIDE && + original_state == KeyboardControllerState::SHOWN) { + time_of_last_blur_ = base::Time::Now(); + } base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( FROM_HERE, base::BindOnce(&KeyboardController::ReportLingeringState, weak_factory_report_lingering_state_.GetWeakPtr()), base::TimeDelta::FromMilliseconds(kReportLingeringStateDelayMs)); + break; default: // Do nothing @@ -737,18 +773,31 @@ bool KeyboardController::IsOverscrollAllowed() const { } void KeyboardController::HandlePointerEvent(const ui::LocatedEvent& event) { - container_behavior_->HandlePointerEvent(event); + container_behavior_->HandlePointerEvent( + event, container_->GetRootWindow()->bounds()); } -void KeyboardController::SetContainerType(const ContainerType type) { - if (container_behavior_->GetType() == type) + +void KeyboardController::SetContainerType( + const ContainerType type, + base::OnceCallback<void(bool)> callback) { + if (container_behavior_->GetType() == type) { + std::move(callback).Run(false); return; + } - enqueued_container_type_ = type; if (state_ == KeyboardControllerState::SHOWN) { + // Keyboard is already shown. Hiding the keyboard at first then switching + // container type. + queued_container_type_ = + std::make_unique<QueuedContainerType>(this, type, std::move(callback)); HideKeyboard(HIDE_REASON_AUTOMATIC); } else { + // Keyboard is hidden. Switching the container type immediately and invoking + // the passed callback now. SetContainerBehaviorInternal(type); + DCHECK(GetActiveContainerType() == type); + std::move(callback).Run(true /* change_successful */); } } diff --git a/chromium/ui/keyboard/keyboard_controller.h b/chromium/ui/keyboard/keyboard_controller.h index c702b72940b..ef9356317a4 100644 --- a/chromium/ui/keyboard/keyboard_controller.h +++ b/chromium/ui/keyboard/keyboard_controller.h @@ -9,6 +9,7 @@ #include "base/macros.h" #include "base/observer_list.h" +#include "base/time/time.h" #include "ui/aura/window_observer.h" #include "ui/base/ime/input_method_observer.h" #include "ui/base/ime/text_input_type.h" @@ -22,6 +23,7 @@ #include "ui/keyboard/keyboard_layout_delegate.h" #include "ui/keyboard/keyboard_util.h" #include "ui/keyboard/notification_manager.h" +#include "ui/keyboard/queued_container_type.h" namespace aura { class Window; @@ -152,6 +154,10 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, KeyboardControllerState GetStateForTest() const { return state_; } + ContainerType GetActiveContainerType() const { + return container_behavior_->GetType(); + } + const gfx::Rect AdjustSetBoundsRequest( const gfx::Rect& display_bounds, const gfx::Rect& requested_bounds) const; @@ -170,7 +176,8 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, // Sets the active container type. If the keyboard is currently shown, this // will trigger a hide animation and a subsequent show animation. Otherwise // the ContainerBehavior change is synchronous. - void SetContainerType(const ContainerType type); + void SetContainerType(const ContainerType type, + base::OnceCallback<void(bool)> callback); // Sets floating keyboard drggable rect. bool SetDraggableArea(const gfx::Rect& rect); @@ -240,6 +247,10 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, // Reports error histogram in case lingering in an intermediate state. void ReportLingeringState(); + // Shows the keyboard if the last time the keyboard was hidden was a small + // time ago. + void ShowKeyboardIfWithinTransientBlurThreshold(); + void SetContainerBehaviorInternal(const ContainerType type); std::unique_ptr<KeyboardUI> ui_; @@ -252,6 +263,8 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, // Current active visual behavior for the keyboard container. std::unique_ptr<ContainerBehavior> container_behavior_; + std::unique_ptr<QueuedContainerType> queued_container_type_; + // If true, show the keyboard window when keyboard UI content updates. bool show_on_content_update_; @@ -268,10 +281,10 @@ class KEYBOARD_EXPORT KeyboardController : public ui::InputMethodObserver, KeyboardControllerState state_; - ContainerType enqueued_container_type_; - NotificationManager notification_manager_; + base::Time time_of_last_blur_ = base::Time::UnixEpoch(); + static KeyboardController* instance_; base::WeakPtrFactory<KeyboardController> weak_factory_report_lingering_state_; diff --git a/chromium/ui/keyboard/keyboard_controller_observer.h b/chromium/ui/keyboard/keyboard_controller_observer.h index bdaa053ac8d..822d07a3a1e 100644 --- a/chromium/ui/keyboard/keyboard_controller_observer.h +++ b/chromium/ui/keyboard/keyboard_controller_observer.h @@ -30,26 +30,26 @@ class KEYBOARD_EXPORT KeyboardControllerObserver { virtual ~KeyboardControllerObserver() {} // Called when the keyboard is shown or closed. - virtual void OnKeyboardAvailabilityChanging(const bool is_available) {} + virtual void OnKeyboardAvailabilityChanged(const bool is_available) {} // Called when the keyboard bounds are changing. - virtual void OnKeyboardVisibleBoundsChanging(const gfx::Rect& new_bounds) {} + virtual void OnKeyboardVisibleBoundsChanged(const gfx::Rect& new_bounds) {} // Called when the keyboard bounds have changed in a way that should affect // the usable region of the workspace. - virtual void OnKeyboardWorkspaceOccludedBoundsChanging( + virtual void OnKeyboardWorkspaceOccludedBoundsChanged( const gfx::Rect& new_bounds) {} // Called when the keyboard bounds have changed in a way that affects how the // workspace should change to not take up the screen space occupied by the // keyboard. - virtual void OnKeyboardWorkspaceDisplacingBoundsChanging( + virtual void OnKeyboardWorkspaceDisplacingBoundsChanged( const gfx::Rect& new_bounds){}; // Redundant with other various notification methods. Use this if the state of // multiple properties need to be conveyed simultaneously to observer // implementations without the need to track multiple stateful properties. - virtual void OnKeyboardAppearanceChanging( + virtual void OnKeyboardAppearanceChanged( const KeyboardStateDescriptor& state){}; // Called when the keyboard was closed. diff --git a/chromium/ui/keyboard/keyboard_controller_unittest.cc b/chromium/ui/keyboard/keyboard_controller_unittest.cc index fad8f1e77a7..8ecafe94069 100644 --- a/chromium/ui/keyboard/keyboard_controller_unittest.cc +++ b/chromium/ui/keyboard/keyboard_controller_unittest.cc @@ -14,7 +14,7 @@ #include "build/build_config.h" #include "testing/gtest/include/gtest/gtest.h" #include "ui/aura/client/focus_client.h" -#include "ui/aura/test/aura_test_helper.h" +#include "ui/aura/test/aura_test_base.h" #include "ui/aura/test/test_window_delegate.h" #include "ui/aura/window.h" #include "ui/base/ime/dummy_text_input_client.h" @@ -176,15 +176,39 @@ class TestKeyboardLayoutDelegate : public KeyboardLayoutDelegate { DISALLOW_COPY_AND_ASSIGN(TestKeyboardLayoutDelegate); }; +class SetModeCallbackInvocationCounter { + public: + SetModeCallbackInvocationCounter() : weak_factory_invoke_(this) {} + + void Invoke(bool status) { + if (status) + invocation_count_success_++; + else + invocation_count_failure_++; + } + + base::OnceCallback<void(bool)> GetInvocationCallback() { + return base::BindOnce(&SetModeCallbackInvocationCounter::Invoke, + weak_factory_invoke_.GetWeakPtr()); + } + + int invocation_count_for_status(bool status) { + return status ? invocation_count_success_ : invocation_count_failure_; + } + + private: + int invocation_count_success_ = 0; + int invocation_count_failure_ = 0; + base::WeakPtrFactory<SetModeCallbackInvocationCounter> weak_factory_invoke_; +}; + } // namespace -class KeyboardControllerTest : public testing::Test, +class KeyboardControllerTest : public aura::test::AuraTestBase, public KeyboardControllerObserver { public: KeyboardControllerTest() - : scoped_task_environment_( - base::test::ScopedTaskEnvironment::MainThreadType::UI), - visible_bounds_number_of_calls_(0), + : visible_bounds_number_of_calls_(0), occluding_bounds_number_of_calls_(0), is_available_number_of_calls_(0), is_available_(false), @@ -192,24 +216,14 @@ class KeyboardControllerTest : public testing::Test, ~KeyboardControllerTest() override {} void SetUp() override { - // The ContextFactory must exist before any Compositors are created. - bool enable_pixel_output = false; - ui::ContextFactory* context_factory = nullptr; - ui::ContextFactoryPrivate* context_factory_private = nullptr; - - ui::InitializeContextFactoryForTests(enable_pixel_output, &context_factory, - &context_factory_private); - ui::SetUpInputMethodFactoryForTesting(); - aura_test_helper_.reset(new aura::test::AuraTestHelper()); - aura_test_helper_->SetUp(context_factory, context_factory_private); - new wm::DefaultActivationClient(aura_test_helper_->root_window()); + aura::test::AuraTestBase::SetUp(); + new wm::DefaultActivationClient(root_window()); focus_controller_.reset(new TestFocusController(root_window())); layout_delegate_.reset(new TestKeyboardLayoutDelegate()); - controller_.reset( - new KeyboardController(std::make_unique<TestKeyboardUI>( - aura_test_helper_->host()->GetInputMethod()), - layout_delegate_.get())); + controller_.reset(new KeyboardController( + std::make_unique<TestKeyboardUI>(host()->GetInputMethod()), + layout_delegate_.get())); controller()->AddObserver(this); } @@ -218,11 +232,9 @@ class KeyboardControllerTest : public testing::Test, controller()->RemoveObserver(this); controller_.reset(); focus_controller_.reset(); - aura_test_helper_->TearDown(); - ui::TerminateContextFactoryForTests(); + aura::test::AuraTestBase::TearDown(); } - aura::Window* root_window() { return aura_test_helper_->root_window(); } KeyboardUI* ui() { return controller_->ui(); } KeyboardController* controller() { return controller_.get(); } @@ -240,16 +252,16 @@ class KeyboardControllerTest : public testing::Test, protected: // KeyboardControllerObserver overrides - void OnKeyboardVisibleBoundsChanging(const gfx::Rect& new_bounds) override { + void OnKeyboardVisibleBoundsChanged(const gfx::Rect& new_bounds) override { visible_bounds_ = new_bounds; visible_bounds_number_of_calls_++; } - void OnKeyboardWorkspaceOccludedBoundsChanging( + void OnKeyboardWorkspaceOccludedBoundsChanged( const gfx::Rect& new_bounds) override { occluding_bounds_ = new_bounds; occluding_bounds_number_of_calls_++; } - void OnKeyboardAvailabilityChanging(bool is_available) override { + void OnKeyboardAvailabilityChanged(bool is_available) override { is_available_ = is_available; is_available_number_of_calls_++; } @@ -271,6 +283,15 @@ class KeyboardControllerTest : public testing::Test, bool IsKeyboardClosed() { return keyboard_closed_; } + void SetProgrammaticFocus(ui::TextInputClient* client) { + controller_->OnTextInputStateChanged(client); + } + + void AddTimeToTransientBlurCounter(double seconds) { + controller_->time_of_last_blur_ -= + base::TimeDelta::FromMilliseconds((int)(1000 * seconds)); + } + void SetFocus(ui::TextInputClient* client) { ui::InputMethod* input_method = ui()->GetInputMethod(); input_method->SetFocusedTextInputClient(client); @@ -308,8 +329,6 @@ class KeyboardControllerTest : public testing::Test, run_loop->Run(); } - base::test::ScopedTaskEnvironment scoped_task_environment_; - std::unique_ptr<aura::test::AuraTestHelper> aura_test_helper_; std::unique_ptr<TestFocusController> focus_controller_; private: @@ -449,6 +468,82 @@ TEST_F(KeyboardControllerTest, ClickDoesNotFocusKeyboard) { keyboard_container->RemovePreTargetHandler(&observer); } +// Tests that blur-then-focus that occur in less than the transient threshold +// cause the keyboard to re-show. +TEST_F(KeyboardControllerTest, TransientBlurShortDelay) { + ScopedAccessibilityKeyboardEnabler scoped_keyboard_enabler; + ui::DummyTextInputClient input_client(ui::TEXT_INPUT_TYPE_TEXT); + ui::DummyTextInputClient no_input_client(ui::TEXT_INPUT_TYPE_NONE); + base::RunLoop run_loop; + aura::Window* keyboard_container(controller()->GetContainerWindow()); + std::unique_ptr<KeyboardContainerObserver> keyboard_container_observer( + new KeyboardContainerObserver(keyboard_container, &run_loop)); + root_window()->AddChild(keyboard_container); + + // Keyboard is hidden + EXPECT_FALSE(keyboard_container->IsVisible()); + + // Set programmatic focus to the text field. Nothing happens + SetProgrammaticFocus(&input_client); + EXPECT_FALSE(keyboard_container->IsVisible()); + + // Click it for real. Keyboard starts to appear. + SetFocus(&input_client); + EXPECT_TRUE(keyboard_container->IsVisible()); + + // Focus a non text field + SetFocus(&no_input_client); + + // It waits 100 ms and then hides. Wait for this routine to finish. + EXPECT_TRUE(WillHideKeyboard()); + RunLoop(&run_loop); + EXPECT_FALSE(keyboard_container->IsVisible()); + + // Virtually wait half a second + AddTimeToTransientBlurCounter(0.5); + // Apply programmatic focus to the text field. + SetProgrammaticFocus(&input_client); + EXPECT_TRUE(keyboard_container->IsVisible()); + EXPECT_FALSE(WillHideKeyboard()); +} + +// Tests that blur-then-focus that occur past the transient threshold do not +// cause the keyboard to re-show. +TEST_F(KeyboardControllerTest, TransientBlurLongDelay) { + ScopedAccessibilityKeyboardEnabler scoped_keyboard_enabler; + ui::DummyTextInputClient input_client(ui::TEXT_INPUT_TYPE_TEXT); + ui::DummyTextInputClient no_input_client(ui::TEXT_INPUT_TYPE_NONE); + base::RunLoop run_loop; + aura::Window* keyboard_container(controller()->GetContainerWindow()); + std::unique_ptr<KeyboardContainerObserver> keyboard_container_observer( + new KeyboardContainerObserver(keyboard_container, &run_loop)); + root_window()->AddChild(keyboard_container); + + // Keyboard is hidden + EXPECT_FALSE(keyboard_container->IsVisible()); + + // Set programmatic focus to the text field. Nothing happens + SetProgrammaticFocus(&input_client); + EXPECT_FALSE(keyboard_container->IsVisible()); + + // Click it for real. Keyboard starts to appear. + SetFocus(&input_client); + EXPECT_TRUE(keyboard_container->IsVisible()); + + // Focus a non text field + SetFocus(&no_input_client); + + // It waits 100 ms and then hides. Wait for this routine to finish. + EXPECT_TRUE(WillHideKeyboard()); + RunLoop(&run_loop); + EXPECT_FALSE(keyboard_container->IsVisible()); + + // Wait 5 seconds and then set programmatic focus to a text field + AddTimeToTransientBlurCounter(5.0); + SetProgrammaticFocus(&input_client); + EXPECT_FALSE(keyboard_container->IsVisible()); +} + TEST_F(KeyboardControllerTest, VisibilityChangeWithTextInputTypeChange) { ScopedAccessibilityKeyboardEnabler scoped_keyboard_enabler; ui::DummyTextInputClient input_client_0(ui::TEXT_INPUT_TYPE_TEXT); @@ -655,13 +750,27 @@ TEST_F(KeyboardControllerAnimationTest, ContainerAnimation) { EXPECT_EQ(gfx::Rect(), notified_occluding_bounds()); EXPECT_FALSE(notified_is_available()); - controller()->SetContainerType(ContainerType::FLOATING); + SetModeCallbackInvocationCounter invocation_counter; + controller()->SetContainerType(ContainerType::FLOATING, + invocation_counter.GetInvocationCallback()); + EXPECT_EQ(1, invocation_counter.invocation_count_for_status(true)); + EXPECT_EQ(0, invocation_counter.invocation_count_for_status(false)); ShowKeyboard(); RunAnimationForLayer(layer); + EXPECT_EQ(1, invocation_counter.invocation_count_for_status(true)); + EXPECT_EQ(0, invocation_counter.invocation_count_for_status(false)); // Visible bounds and occluding bounds are now different. EXPECT_EQ(keyboard_container()->bounds(), notified_visible_bounds()); EXPECT_EQ(gfx::Rect(), notified_occluding_bounds()); EXPECT_TRUE(notified_is_available()); + + // callback should do nothing when container mode is set to the current active + // container type. An unnecessary call gets registered synchronously as a + // failure status to the callback. + controller()->SetContainerType(ContainerType::FLOATING, + invocation_counter.GetInvocationCallback()); + EXPECT_EQ(1, invocation_counter.invocation_count_for_status(true)); + EXPECT_EQ(1, invocation_counter.invocation_count_for_status(false)); } // Show keyboard during keyboard hide animation should abort the hide animation diff --git a/chromium/ui/keyboard/keyboard_util.cc b/chromium/ui/keyboard/keyboard_util.cc index 356253966e7..35b70957c3a 100644 --- a/chromium/ui/keyboard/keyboard_util.cc +++ b/chromium/ui/keyboard/keyboard_util.cc @@ -18,6 +18,7 @@ #include "ui/base/ime/input_method_base.h" #include "ui/base/ime/text_input_client.h" #include "ui/base/ime/text_input_flags.h" +#include "ui/base/ui_base_features.h" #include "ui/base/ui_base_switches.h" #include "ui/events/event_sink.h" #include "ui/events/event_utils.h" @@ -245,12 +246,14 @@ bool SendKeyEvent(const std::string type, if (!input_method) return false; + // This can be null if no text input field is not focused. ui::TextInputClient* tic = input_method->GetTextInputClient(); SendProcessKeyEvent(ui::ET_KEY_PRESSED, host); ui::KeyEvent char_event(key_value, code, ui::EF_NONE); - tic->InsertChar(char_event); + if (tic) + tic->InsertChar(char_event); SendProcessKeyEvent(ui::ET_KEY_RELEASED, host); } } else { diff --git a/chromium/ui/keyboard/notification_manager.cc b/chromium/ui/keyboard/notification_manager.cc index 5ef18d8da2a..4838d2b3fc2 100644 --- a/chromium/ui/keyboard/notification_manager.cc +++ b/chromium/ui/keyboard/notification_manager.cc @@ -59,20 +59,20 @@ void NotificationManager::SendNotifications( for (KeyboardControllerObserver& observer : observers) { if (send_availability_notification) - observer.OnKeyboardAvailabilityChanging(is_available); + observer.OnKeyboardAvailabilityChanged(is_available); if (send_visual_bounds_notification) - observer.OnKeyboardVisibleBoundsChanging(bounds); + observer.OnKeyboardVisibleBoundsChanged(bounds); if (send_occluded_bounds_notification) - observer.OnKeyboardWorkspaceOccludedBoundsChanging(occluded_region); + observer.OnKeyboardWorkspaceOccludedBoundsChanged(occluded_region); if (send_displaced_bounds_notification) { - observer.OnKeyboardWorkspaceDisplacingBoundsChanging( + observer.OnKeyboardWorkspaceDisplacingBoundsChanged( workspace_layout_offset_region); } - observer.OnKeyboardAppearanceChanging(state); + observer.OnKeyboardAppearanceChanged(state); } } diff --git a/chromium/ui/keyboard/queued_container_type.cc b/chromium/ui/keyboard/queued_container_type.cc new file mode 100644 index 00000000000..2cf7b882e24 --- /dev/null +++ b/chromium/ui/keyboard/queued_container_type.cc @@ -0,0 +1,24 @@ +// Copyright 2018 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 "base/bind.h" +#include "ui/keyboard/keyboard_controller.h" + +namespace keyboard { + +QueuedContainerType::QueuedContainerType( + KeyboardController* controller, + ContainerType container_type, + base::OnceCallback<void(bool success)> callback) + : controller_(controller), + container_type_(container_type), + callback_(std::move(callback)){}; + +QueuedContainerType::~QueuedContainerType() { + bool change_successful = + controller_->GetActiveContainerType() == container_type_; + std::move(callback_).Run(change_successful); +}; + +} // namespace keyboard diff --git a/chromium/ui/keyboard/queued_container_type.h b/chromium/ui/keyboard/queued_container_type.h new file mode 100644 index 00000000000..ce58d930e28 --- /dev/null +++ b/chromium/ui/keyboard/queued_container_type.h @@ -0,0 +1,37 @@ +// Copyright 2018 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. + +#ifndef UI_KEYBOARD_QUEUED_CONTAINER_TYPE_H_ +#define UI_KEYBOARD_QUEUED_CONTAINER_TYPE_H_ + +#include "base/bind.h" +#include "ui/keyboard/container_type.h" + +namespace keyboard { + +class KeyboardController; + +// Tracks a queued ContainerType change request. Couples a container type with a +// callback to invoke once the necessary animation and container changes are +// complete. +// The callback will be invoked once this object goes out of scope. Success +// is defined as the KeyboardController's current container behavior matching +// the same container type as the queued container type. +class QueuedContainerType { + public: + QueuedContainerType(KeyboardController* controller, + ContainerType container_type, + base::OnceCallback<void(bool success)> callback); + ~QueuedContainerType(); + ContainerType container_type() { return container_type_; } + + private: + KeyboardController* controller_; + ContainerType container_type_; + base::OnceCallback<void(bool success)> callback_; +}; + +} // namespace keyboard + +#endif // UI_KEYBOARD_QUEUED_CONTAINER_TYPE_H_ diff --git a/chromium/ui/keyboard/resources/inputview_adapter.js b/chromium/ui/keyboard/resources/inputview_adapter.js index cbafd012b8b..eedf5648d8e 100644 --- a/chromium/ui/keyboard/resources/inputview_adapter.js +++ b/chromium/ui/keyboard/resources/inputview_adapter.js @@ -163,7 +163,8 @@ function registerInputviewApi() { NONE: 0, ALT: 8, CONTROL: 4, - SHIFT: 2 + SHIFT: 2, + CAPSLOCK: 256 }; // Mapping from keyName to keyCode (see ui::KeyEvent). @@ -295,8 +296,10 @@ function registerInputviewApi() { event.modifiers |= Modifier.ALT; if (data.ctrlKey) event.modifiers |= Modifier.CONTROL; - if (data.shiftKey || data.capsLock) + if (data.shiftKey) event.modifiers |= Modifier.SHIFT; + if (data.capsLock) + event.modifiers |= Modifier.CAPSLOCK; chrome.virtualKeyboardPrivate.sendKeyEvent(event, logIfError_); }); |