blob: 76a576bfe091a8296aa406e583007bcb90c028d1 [file] [log] [blame]
// Copyright (c) 2013 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 "ui/keyboard/keyboard_controller.h"
#include <set>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/macros.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/threading/thread_task_runner_handle.h"
#include "ui/aura/env.h"
#include "ui/aura/window.h"
#include "ui/aura/window_delegate.h"
#include "ui/aura/window_observer.h"
#include "ui/base/cursor/cursor.h"
#include "ui/base/hit_test.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/compositor/layer_animation_observer.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/display/types/display_constants.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/vector2d.h"
#include "ui/gfx/path.h"
#include "ui/keyboard/container_floating_behavior.h"
#include "ui/keyboard/container_full_width_behavior.h"
#include "ui/keyboard/container_type.h"
#include "ui/keyboard/keyboard_controller_observer.h"
#include "ui/keyboard/keyboard_layout_manager.h"
#include "ui/keyboard/keyboard_ui.h"
#include "ui/keyboard/keyboard_util.h"
#include "ui/keyboard/notification_manager.h"
#include "ui/wm/core/window_animations.h"
#if defined(OS_CHROMEOS)
#include "ui/ozone/public/input_controller.h"
#include "ui/ozone/public/ozone_platform.h"
#endif // if defined(OS_CHROMEOS)
namespace {
constexpr int kHideKeyboardDelayMs = 100;
// Reports an error histogram if the keyboard state is lingering in an
// intermediate state for more than 5 seconds.
constexpr int kReportLingeringStateDelayMs = 5000;
// State transition diagram (document linked from crbug.com/719905)
bool isAllowedStateTransition(keyboard::KeyboardControllerState from,
keyboard::KeyboardControllerState to) {
static const std::set<std::pair<keyboard::KeyboardControllerState,
keyboard::KeyboardControllerState>>
kAllowedStateTransition = {
// The initial ShowKeyboard scenario
// INITIAL -> LOADING_EXTENSION -> HIDDEN -> SHOWN.
{keyboard::KeyboardControllerState::UNKNOWN,
keyboard::KeyboardControllerState::INITIAL},
{keyboard::KeyboardControllerState::INITIAL,
keyboard::KeyboardControllerState::LOADING_EXTENSION},
{keyboard::KeyboardControllerState::LOADING_EXTENSION,
keyboard::KeyboardControllerState::HIDDEN},
{keyboard::KeyboardControllerState::HIDDEN,
keyboard::KeyboardControllerState::SHOWN},
// Hide scenario
// SHOWN -> WILL_HIDE -> HIDDEN.
{keyboard::KeyboardControllerState::SHOWN,
keyboard::KeyboardControllerState::WILL_HIDE},
{keyboard::KeyboardControllerState::WILL_HIDE,
keyboard::KeyboardControllerState::HIDDEN},
// Focus transition scenario
// SHOWN -> WILL_HIDE -> SHOWN.
{keyboard::KeyboardControllerState::WILL_HIDE,
keyboard::KeyboardControllerState::SHOWN},
// HideKeyboard can be called at anytime for example on shutdown.
{keyboard::KeyboardControllerState::SHOWN,
keyboard::KeyboardControllerState::HIDDEN},
};
return kAllowedStateTransition.count(std::make_pair(from, to)) == 1;
};
// The KeyboardWindowDelegate makes sure the keyboard-window does not get focus.
// This is necessary to make sure that the synthetic key-events reach the target
// window.
// The delegate deletes itself when the window is destroyed.
class KeyboardWindowDelegate : public aura::WindowDelegate {
public:
KeyboardWindowDelegate() {}
~KeyboardWindowDelegate() override {}
private:
// Overridden from aura::WindowDelegate:
gfx::Size GetMinimumSize() const override { return gfx::Size(); }
gfx::Size GetMaximumSize() const override { return gfx::Size(); }
void OnBoundsChanged(const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds) override {}
gfx::NativeCursor GetCursor(const gfx::Point& point) override {
return gfx::kNullCursor;
}
int GetNonClientComponent(const gfx::Point& point) const override {
return HTNOWHERE;
}
bool ShouldDescendIntoChildForEventHandling(
aura::Window* child,
const gfx::Point& location) override {
return true;
}
bool CanFocus() override { return false; }
void OnCaptureLost() override {}
void OnPaint(const ui::PaintContext& context) override {}
void OnDeviceScaleFactorChanged(float old_device_scale_factor,
float new_device_scale_factor) override {}
void OnWindowDestroying(aura::Window* window) override {}
void OnWindowDestroyed(aura::Window* window) override { delete this; }
void OnWindowTargetVisibilityChanged(bool visible) override {}
bool HasHitTestMask() const override { return false; }
void GetHitTestMask(gfx::Path* mask) const override {}
DISALLOW_COPY_AND_ASSIGN(KeyboardWindowDelegate);
};
void ToggleTouchEventLogging(bool enable) {
#if defined(OS_CHROMEOS)
// TODO(moshayedi): crbug.com/642863. Revisit when we have mojo interface for
// InputController for processes that aren't mus-ws.
if (aura::Env::GetInstance()->mode() == aura::Env::Mode::MUS)
return;
ui::InputController* controller =
ui::OzonePlatform::GetInstance()->GetInputController();
if (controller)
controller->SetTouchEventLoggingEnabled(enable);
#endif // defined(OS_CHROMEOS)
}
std::string StateToStr(keyboard::KeyboardControllerState state) {
switch (state) {
case keyboard::KeyboardControllerState::UNKNOWN:
return "UNKNOWN";
case keyboard::KeyboardControllerState::SHOWN:
return "SHOWN";
case keyboard::KeyboardControllerState::LOADING_EXTENSION:
return "LOADING_EXTENSION";
case keyboard::KeyboardControllerState::WILL_HIDE:
return "WILL_HIDE";
case keyboard::KeyboardControllerState::HIDDEN:
return "HIDDEN";
case keyboard::KeyboardControllerState::INITIAL:
return "INITIAL";
case keyboard::KeyboardControllerState::COUNT:
NOTREACHED();
}
NOTREACHED() << "Unknownstate: " << static_cast<int>(state);
// Needed for windows build.
return "";
}
} // namespace
namespace keyboard {
// Observer for both keyboard show and hide animations. It should be owned by
// KeyboardController.
class CallbackAnimationObserver : public ui::ImplicitAnimationObserver {
public:
CallbackAnimationObserver(base::OnceClosure callback)
: callback_(std::move(callback)) {}
private:
// ui::ImplicitAnimationObserver:
void OnImplicitAnimationsCompleted() override {
if (WasAnimationAbortedForProperty(ui::LayerAnimationElement::TRANSFORM) ||
WasAnimationAbortedForProperty(ui::LayerAnimationElement::OPACITY)) {
return;
}
DCHECK(
WasAnimationCompletedForProperty(ui::LayerAnimationElement::TRANSFORM));
DCHECK(
WasAnimationCompletedForProperty(ui::LayerAnimationElement::OPACITY));
std::move(callback_).Run();
}
base::OnceClosure callback_;
DISALLOW_COPY_AND_ASSIGN(CallbackAnimationObserver);
};
// static
KeyboardController* KeyboardController::instance_ = NULL;
KeyboardController::KeyboardController(std::unique_ptr<KeyboardUI> ui,
KeyboardLayoutDelegate* delegate)
: ui_(std::move(ui)),
layout_delegate_(delegate),
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_);
ChangeState(KeyboardControllerState::INITIAL);
}
KeyboardController::~KeyboardController() {
if (container_) {
if (container_->GetRootWindow())
container_->GetRootWindow()->RemoveObserver(this);
container_->RemoveObserver(this);
container_->RemovePreTargetHandler(&event_filter_);
}
ui_->GetInputMethod()->RemoveObserver(this);
for (KeyboardControllerObserver& observer : observer_list_)
observer.OnKeyboardClosed();
ui_->SetController(nullptr);
}
// static
void KeyboardController::ResetInstance(KeyboardController* controller) {
if (instance_ && instance_ != controller)
delete instance_;
instance_ = controller;
}
// static
KeyboardController* KeyboardController::GetInstance() {
return instance_;
}
bool KeyboardController::keyboard_visible() const {
return state_ == KeyboardControllerState::SHOWN;
}
aura::Window* KeyboardController::GetContainerWindow() {
if (!container_.get()) {
container_.reset(new aura::Window(new KeyboardWindowDelegate()));
container_->SetName("KeyboardContainer");
container_->set_owned_by_parent(false);
container_->Init(ui::LAYER_NOT_DRAWN);
container_->AddObserver(this);
container_->SetLayoutManager(new KeyboardLayoutManager(this));
container_->AddPreTargetHandler(&event_filter_);
}
return container_.get();
}
aura::Window* KeyboardController::GetContainerWindowWithoutCreationForTest() {
return container_.get();
}
void KeyboardController::NotifyContentsBoundsChanging(
const gfx::Rect& new_bounds) {
current_keyboard_bounds_ = new_bounds;
if (ui_->HasContentsWindow() && ui_->GetContentsWindow()->IsVisible()) {
notification_manager_.SendNotifications(
container_behavior_->BoundsObscureUsableRegion(),
container_behavior_->BoundsAffectWorkspaceLayout(), keyboard_locked(),
new_bounds, observer_list_);
if (keyboard::IsKeyboardOverscrollEnabled())
ui_->InitInsets(new_bounds);
else
ui_->ResetInsets();
} else {
current_keyboard_bounds_ = gfx::Rect();
}
}
void KeyboardController::MoveKeyboard(const gfx::Rect new_bounds) {
DCHECK(keyboard_visible());
SetContainerBounds(new_bounds, false);
}
void KeyboardController::SetContainerBounds(const gfx::Rect& new_bounds,
const bool contents_loaded) {
ui::LayerAnimator* animator = container_->layer()->GetAnimator();
// Stops previous animation if a window resize is requested during animation.
if (animator->is_animating())
animator->StopAnimating();
container_->SetBounds(new_bounds);
if (contents_loaded) {
const bool should_show = show_on_content_update_;
if (state_ == KeyboardControllerState::LOADING_EXTENSION)
ChangeState(KeyboardControllerState::HIDDEN);
if (should_show) {
// The window height is set to 0 initially or before switch to an IME in a
// different extension. Virtual keyboard window may wait for this bounds
// change to correctly animate in.
if (keyboard_locked()) {
// Do not move the keyboard to another display after switch to an IME in
// a different extension.
const int64_t display_id =
display::Screen::GetScreen()
->GetDisplayNearestWindow(GetContainerWindow())
.id();
ShowKeyboardInDisplay(display_id);
} else {
ShowKeyboard(false /* lock */);
}
return;
}
}
// We need to send out this notification only if keyboard is visible since
// the contents window is resized even if keyboard is hidden.
if (keyboard_visible())
NotifyContentsBoundsChanging(new_bounds);
}
void KeyboardController::AddObserver(KeyboardControllerObserver* observer) {
observer_list_.AddObserver(observer);
}
bool KeyboardController::HasObserver(
KeyboardControllerObserver* observer) const {
return observer_list_.HasObserver(observer);
}
void KeyboardController::RemoveObserver(KeyboardControllerObserver* observer) {
observer_list_.RemoveObserver(observer);
}
void KeyboardController::HideKeyboard(HideReason reason) {
TRACE_EVENT0("vk", "HideKeyboard");
switch (state_) {
case KeyboardControllerState::INITIAL:
case KeyboardControllerState::HIDDEN:
return;
case KeyboardControllerState::LOADING_EXTENSION:
show_on_content_update_ = false;
return;
case KeyboardControllerState::WILL_HIDE:
case KeyboardControllerState::SHOWN: {
ToggleTouchEventLogging(true);
keyboard::LogKeyboardControlEvent(
reason == HIDE_REASON_AUTOMATIC
? keyboard::KEYBOARD_CONTROL_HIDE_AUTO
: keyboard::KEYBOARD_CONTROL_HIDE_USER);
NotifyContentsBoundsChanging(gfx::Rect());
set_keyboard_locked(false);
animation_observer_ = std::make_unique<CallbackAnimationObserver>(
base::BindOnce(&KeyboardController::HideAnimationFinished,
base::Unretained(this)));
ui::ScopedLayerAnimationSettings layer_animation_settings(
container_->layer()->GetAnimator());
layer_animation_settings.AddObserver(animation_observer_.get());
aura::Window* window = container_.get();
{
// Scoped settings go into effect when scope ends.
::wm::ScopedHidingAnimationSettings hiding_settings(window);
container_behavior_->DoHidingAnimation(window, &hiding_settings);
}
ui_->HideKeyboardContainer(window);
ChangeState(KeyboardControllerState::HIDDEN);
for (KeyboardControllerObserver& observer : observer_list_)
observer.OnKeyboardHidden();
ui_->EnsureCaretInWorkArea();
break;
}
default:
NOTREACHED();
}
}
void KeyboardController::HideAnimationFinished() {
if (state_ != KeyboardControllerState::HIDDEN)
return;
if (enqueued_container_type_ != container_behavior_->GetType()) {
SetContainerBehaviorInternal(enqueued_container_type_);
ShowKeyboard(false /* lock */);
}
}
void KeyboardController::SetContainerBehaviorInternal(
const ContainerType type) {
switch (type) {
case ContainerType::FULL_WIDTH:
container_behavior_ = std::make_unique<ContainerFullWidthBehavior>(this);
break;
case ContainerType::FLOATING:
container_behavior_ = std::make_unique<ContainerFloatingBehavior>(this);
break;
default:
NOTREACHED();
}
}
void KeyboardController::ShowKeyboard(bool lock) {
set_keyboard_locked(lock);
ShowKeyboardInternal(display::kInvalidDisplayId);
}
void KeyboardController::ShowKeyboardInDisplay(int64_t display_id) {
set_keyboard_locked(true);
ShowKeyboardInternal(display_id);
}
bool KeyboardController::IsKeyboardWindowCreated() {
return keyboard_container_initialized() && ui_->HasContentsWindow();
}
void KeyboardController::OnWindowHierarchyChanged(
const HierarchyChangeParams& params) {
if (params.new_parent && params.target == container_.get())
OnTextInputStateChanged(ui_->GetInputMethod()->GetTextInputClient());
}
void KeyboardController::OnWindowAddedToRootWindow(aura::Window* window) {
if (!window->GetRootWindow()->HasObserver(this))
window->GetRootWindow()->AddObserver(this);
AdjustKeyboardBounds();
}
void KeyboardController::OnWindowRemovingFromRootWindow(aura::Window* window,
aura::Window* new_root) {
if (window->GetRootWindow()->HasObserver(this))
window->GetRootWindow()->RemoveObserver(this);
}
void KeyboardController::OnWindowBoundsChanged(
aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) {
if (!window->IsRootWindow())
return;
// Keep the same height when window resizes. It gets called when the screen
// rotates.
if (!keyboard_container_initialized() || !ui_->HasContentsWindow())
return;
container_behavior_->SetCanonicalBounds(GetContainerWindow(), new_bounds);
}
void KeyboardController::Reload() {
if (ui_->HasContentsWindow()) {
// A reload should never try to show virtual keyboard. If keyboard is not
// visible before reload, it should stay invisible after reload.
show_on_content_update_ = false;
ui_->ReloadKeyboardIfNeeded();
}
}
void KeyboardController::OnTextInputStateChanged(
const ui::TextInputClient* client) {
if (!container_.get()) {
DCHECK(state_ == KeyboardControllerState::HIDDEN ||
state_ == KeyboardControllerState::INITIAL);
return;
}
TRACE_EVENT0("vk", "OnTextInputStateChanged");
bool focused =
client && (client->GetTextInputType() != ui::TEXT_INPUT_TYPE_NONE &&
client->GetTextInputMode() != ui::TEXT_INPUT_MODE_NONE);
bool should_hide = !focused && container_behavior_->TextBlurHidesKeyboard();
if (should_hide) {
switch (state_) {
case KeyboardControllerState::LOADING_EXTENSION:
show_on_content_update_ = false;
return;
case KeyboardControllerState::SHOWN:
ChangeState(KeyboardControllerState::WILL_HIDE);
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&KeyboardController::HideKeyboard,
weak_factory_will_hide_.GetWeakPtr(),
HIDE_REASON_AUTOMATIC),
base::TimeDelta::FromMilliseconds(kHideKeyboardDelayMs));
return;
default:
return;
}
} else {
// Abort a pending keyboard hide.
if (WillHideKeyboard())
ChangeState(KeyboardControllerState::SHOWN);
// 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.
}
}
void KeyboardController::OnShowImeIfNeeded() {
// Calling |ShowKeyboardInternal| may move the keyboard to another display.
if (IsKeyboardEnabled() && !keyboard_locked())
ShowKeyboardInternal(display::kInvalidDisplayId);
}
void KeyboardController::LoadKeyboardUiInBackground() {
// ShowKeyboardInternal may trigger RootControllerWindow::ActiveKeyboard which
// will cause LoadKeyboardUiInBackground to potentially run even though the
// keyboard has been initialized.
if (state_ != KeyboardControllerState::INITIAL)
return;
// The container window should have been created already when
// |Shell::CreateKeyboard| was called.
DCHECK(container_.get());
PopulateKeyboardContent(display::kInvalidDisplayId, false);
}
void KeyboardController::ShowKeyboardInternal(int64_t display_id) {
DCHECK(container_.get());
keyboard::MarkKeyboardLoadStarted();
PopulateKeyboardContent(display_id, true);
}
void KeyboardController::PopulateKeyboardContent(int64_t display_id,
bool show_keyboard) {
DCHECK(show_keyboard || state_ == KeyboardControllerState::INITIAL);
TRACE_EVENT0("vk", "PopulateKeyboardContent");
if (layout_delegate_ != nullptr) {
if (display_id != display::kInvalidDisplayId)
layout_delegate_->MoveKeyboardToDisplay(display_id);
else
layout_delegate_->MoveKeyboardToTouchableDisplay();
}
if (container_->children().empty()) {
DCHECK(state_ == KeyboardControllerState::INITIAL);
aura::Window* contents = ui_->GetContentsWindow();
contents->Show();
container_->AddChild(contents);
contents->set_owned_by_parent(false);
}
switch (state_) {
case KeyboardControllerState::SHOWN:
return;
case KeyboardControllerState::LOADING_EXTENSION:
show_on_content_update_ |= show_keyboard;
return;
default:
break;
}
ui_->ReloadKeyboardIfNeeded();
ui::LayerAnimator* container_animator = container_->layer()->GetAnimator();
// If |container_| has hide animation, its visibility is set to false when the
// hide animation finished. So even if the container is visible at this
// point, it may be in the process of hiding. We still need to show keyboard
// container in this case.
if (container_->IsVisible() && !container_animator->is_animating()) {
// TODO(oka): This clause is excercised in
// VirtualKeyboardRootWindowControllerTest
// .EnsureCaretInWorkAreaWithMultipleDisplays
// when keyboard container window has been shown from outside.
// This should not happen in the real code. Fix the test and change this to
// NOTREACHED.
DCHECK(state_ == KeyboardControllerState::HIDDEN);
ChangeState(KeyboardControllerState::SHOWN);
return;
}
ToggleTouchEventLogging(!show_keyboard);
switch (state_) {
case KeyboardControllerState::INITIAL:
DCHECK(ui_->GetContentsWindow()->bounds().height() == 0);
show_on_content_update_ = show_keyboard;
ChangeState(KeyboardControllerState::LOADING_EXTENSION);
return;
case KeyboardControllerState::WILL_HIDE:
ChangeState(KeyboardControllerState::SHOWN);
return;
case KeyboardControllerState::HIDDEN: {
// If the container is not animating, makes sure the position and opacity
// are at begin states for animation.
container_behavior_->InitializeShowAnimationStartingState(
container_.get());
break;
}
default:
NOTREACHED();
}
DCHECK_EQ(state_, KeyboardControllerState::HIDDEN);
keyboard::LogKeyboardControlEvent(keyboard::KEYBOARD_CONTROL_SHOW);
container_animator->set_preemption_strategy(
ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
ui_->ShowKeyboardContainer(container_.get());
animation_observer_ = std::make_unique<CallbackAnimationObserver>(
base::BindOnce(&MarkKeyboardLoadFinished));
ui::ScopedLayerAnimationSettings settings(container_animator);
settings.AddObserver(animation_observer_.get());
container_behavior_->DoShowingAnimation(container_.get(), &settings);
ChangeState(KeyboardControllerState::SHOWN);
NotifyKeyboardBoundsChangingAndEnsureCaretInWorkArea();
}
bool KeyboardController::WillHideKeyboard() const {
bool res = weak_factory_will_hide_.HasWeakPtrs();
DCHECK(res == (state_ == KeyboardControllerState::WILL_HIDE));
return res;
}
void KeyboardController::
NotifyKeyboardBoundsChangingAndEnsureCaretInWorkArea() {
// Notify observers after animation finished to prevent reveal desktop
// background during animation.
NotifyContentsBoundsChanging(container_->bounds());
ui_->EnsureCaretInWorkArea();
}
void KeyboardController::NotifyKeyboardConfigChanged() {
for (KeyboardControllerObserver& observer : observer_list_)
observer.OnKeyboardConfigChanged();
}
void KeyboardController::AdjustKeyboardBounds() {
container_behavior_->SetCanonicalBounds(
GetContainerWindow(), container_->GetRootWindow()->bounds());
}
void KeyboardController::CheckStateTransition(KeyboardControllerState prev,
KeyboardControllerState next) {
std::stringstream error_message;
const bool valid_transition = isAllowedStateTransition(prev, next);
if (!valid_transition)
error_message << "Unexpected transition";
// Emit UMA
const int transition_record =
(valid_transition ? 1 : -1) *
(static_cast<int>(prev) * 1000 + static_cast<int>(next));
base::UmaHistogramSparse("VirtualKeyboard.ControllerStateTransition",
transition_record);
UMA_HISTOGRAM_BOOLEAN("VirtualKeyboard.ControllerStateTransitionIsValid",
transition_record > 0);
DCHECK(error_message.str().empty())
<< "State: " << StateToStr(prev) << " -> " << StateToStr(next) << " "
<< error_message.str();
}
void KeyboardController::ChangeState(KeyboardControllerState state) {
CheckStateTransition(state_, state);
if (state_ == state)
return;
state_ = state;
if (state != KeyboardControllerState::WILL_HIDE)
weak_factory_will_hide_.InvalidateWeakPtrs();
if (state != KeyboardControllerState::LOADING_EXTENSION)
show_on_content_update_ = false;
for (KeyboardControllerObserver& observer : observer_list_)
observer.OnStateChanged(state);
weak_factory_report_lingering_state_.InvalidateWeakPtrs();
switch (state_) {
case KeyboardControllerState::LOADING_EXTENSION:
case KeyboardControllerState::WILL_HIDE:
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&KeyboardController::ReportLingeringState,
weak_factory_report_lingering_state_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kReportLingeringStateDelayMs));
break;
default:
// Do nothing
break;
}
}
void KeyboardController::ReportLingeringState() {
UMA_HISTOGRAM_ENUMERATION("VirtualKeyboard.LingeringIntermediateState",
state_, KeyboardControllerState::COUNT);
}
const gfx::Rect KeyboardController::GetWorkspaceObscuringBounds() const {
if (keyboard_visible() && container_behavior_->BoundsObscureUsableRegion())
return current_keyboard_bounds_;
return gfx::Rect();
}
const gfx::Rect KeyboardController::GetKeyboardLockScreenOffsetBounds() const {
// Overscroll is generally dependent on lock state, however, its behavior
// temporarily overridden by a static field in certain lock screen contexts.
// Furthermore, floating keyboard should never affect layout.
if (keyboard_visible() && !keyboard::IsKeyboardOverscrollEnabled() &&
container_behavior_->GetType() != ContainerType::FLOATING) {
return current_keyboard_bounds_;
}
return gfx::Rect();
}
const gfx::Rect KeyboardController::AdjustSetBoundsRequest(
const gfx::Rect& display_bounds,
const gfx::Rect& requested_bounds) const {
return container_behavior_->AdjustSetBoundsRequest(display_bounds,
requested_bounds);
}
bool KeyboardController::IsOverscrollAllowed() const {
return container_behavior_->IsOverscrollAllowed();
}
void KeyboardController::HandlePointerEvent(const ui::LocatedEvent& event) {
container_behavior_->HandlePointerEvent(event);
}
void KeyboardController::SetContainerType(const ContainerType type) {
if (container_behavior_->GetType() == type)
return;
enqueued_container_type_ = type;
if (state_ == KeyboardControllerState::SHOWN) {
HideKeyboard(HIDE_REASON_AUTOMATIC);
} else {
SetContainerBehaviorInternal(type);
}
}
bool KeyboardController::SetDraggableArea(const gfx::Rect& rect) {
if (container_behavior_) {
return container_behavior_->SetDraggableArea(rect);
}
return false;
}
} // namespace keyboard