chromium/content/browser/renderer_host/virtual_keyboard_controller_win.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/virtual_keyboard_controller_win.h"

#include "base/trace_event/trace_event.h"
#include "content/browser/renderer_host/render_widget_host_view_aura.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/mojom/text_input_state.mojom.h"
#include "ui/base/ime/virtual_keyboard_controller.h"
#include "ui/events/event_utils.h"
#include "ui/gfx/geometry/rect.h"

namespace content {

VirtualKeyboardControllerWin::VirtualKeyboardControllerWin(
    RenderWidgetHostViewAura* host_view,
    ui::InputMethod* input_method)
    : host_view_(host_view), input_method_(input_method) {
  host_view_->SetInsets(gfx::Insets());
}

VirtualKeyboardControllerWin::~VirtualKeyboardControllerWin() {
  if (observers_registered_) {
    // De-register the input pane observers.
    if (auto* controller = input_method_->GetVirtualKeyboardController())
      controller->RemoveObserver(this);
  }
}

void VirtualKeyboardControllerWin::HideAndNotifyKeyboardInset() {
  if (auto* controller = input_method_->GetVirtualKeyboardController()) {
    if (virtual_keyboard_shown_) {
      // If the VK is already showing, then dismiss it first.
      controller->DismissVirtualKeyboard();
      // Should also scroll the content into view after the VK dismisses.
      OnKeyboardHidden();
    }
  }
}

void VirtualKeyboardControllerWin::OnKeyboardVisible(
    const gfx::Rect& keyboard_rect) {
  TRACE_EVENT0("vk", "VirtualKeyboardControllerWin::OnKeyboardVisible");
  // If the software input panel (SIP) is manually raised by the user, the flag
  // should be set so we don't call TryShow API again.
  virtual_keyboard_shown_ = true;
  if (host_view_->GetVirtualKeyboardMode() !=
      ui::mojom::VirtualKeyboardMode::kOverlaysContent) {
    host_view_->SetInsets(gfx::Insets::TLBR(
        0, 0, keyboard_rect.IsEmpty() ? 0 : keyboard_rect.height(), 0));
  } else {
    host_view_->NotifyVirtualKeyboardOverlayRect(keyboard_rect);
  }
}

void VirtualKeyboardControllerWin::OnKeyboardHidden() {
  TRACE_EVENT0("vk", "VirtualKeyboardControllerWin::OnKeyboardHidden");
  // If the software input panel (SIP) is manually closed by the user, the flag
  // should be reset so we don't call TryHide API again. Also,
  // next time user taps on an editable element after manually dismissing the
  // keyboard, this flag is used to determine whether TryShow needs to be
  // called or not. Calling TryShow/TryHide multiple times leads to SIP
  // flickering.
  virtual_keyboard_shown_ = false;
  if (host_view_->GetVirtualKeyboardMode() !=
      ui::mojom::VirtualKeyboardMode::kOverlaysContent) {
    // Restore the viewport.
    host_view_->SetInsets(gfx::Insets());
  } else {
    host_view_->NotifyVirtualKeyboardOverlayRect(gfx::Rect());
  }
}

void VirtualKeyboardControllerWin::ShowVirtualKeyboard() {
  TRACE_EVENT0("vk", "VirtualKeyboardControllerWin::ShowVirtualKeyboard");
  if (input_method_->GetVirtualKeyboardController()) {
    if (!virtual_keyboard_shown_) {
      virtual_keyboard_shown_ = true;
      input_method_->SetVirtualKeyboardVisibilityIfEnabled(true);
    }
  }
}

void VirtualKeyboardControllerWin::HideVirtualKeyboard() {
  TRACE_EVENT0("vk", "VirtualKeyboardControllerWin::HideVirtualKeyboard");
  if (auto* controller = input_method_->GetVirtualKeyboardController()) {
    if (virtual_keyboard_shown_) {
      virtual_keyboard_shown_ = false;
      controller->DismissVirtualKeyboard();
    }
  }
}

void VirtualKeyboardControllerWin::UpdateTextInputState(
    const ui::mojom::TextInputState* state) {
  // Conditions to show the VK:
  // 1. User has to interact with the editable element.
  // 2. Pointer type has to be either touch or pen.
  // 3. Accessibility has set focus on an editable element.
  // 4. If virtualkeyboardpolicy is manual, leave the SIP in its current state -
  //    script authors need to call show() or hide() explicitly to trigger SIP
  //    actions.
  // 5. If virtualkeyboardpolicy is auto, show the SIP.
  // If there are no keyboard controllers or the pointer type is neither pen or
  // touch or accessibility has not set focus into an editable element, then
  // don't change the state of the keyboard.
  auto* controller = input_method_->GetVirtualKeyboardController();
  is_manual_policy_ =
      state->vk_policy == ui::mojom::VirtualKeyboardPolicy::MANUAL;
  if (!controller ||
      !(IsPointerTypeValidForVirtualKeyboard() || is_manual_policy_) ||
      !host_view_->host()->GetView() || !host_view_->host()->delegate()) {
    return;
  }
  // Register the observers if the pointer type is pen/touch.
  if (!observers_registered_) {
    controller->AddObserver(this);
    observers_registered_ = true;
  }
  if (state->show_ime_if_needed &&
      state->vk_policy == ui::mojom::VirtualKeyboardPolicy::AUTO) {
    ShowVirtualKeyboard();
    return;
  }

  if (is_manual_policy_) {
    switch (state->last_vk_visibility_request) {
      case ui::mojom::VirtualKeyboardVisibilityRequest::SHOW:
        if (host_view_->FocusedFrameHasStickyActivation())
          ShowVirtualKeyboard();
        break;
      case ui::mojom::VirtualKeyboardVisibilityRequest::HIDE:
        HideVirtualKeyboard();
        break;
      default:
        // Don't change the state of the VK.
        break;
    }
  }
}

void VirtualKeyboardControllerWin::FocusedNodeChanged(bool is_editable) {
  if (!is_editable) {
    HideVirtualKeyboard();
    return;
  }
}

bool VirtualKeyboardControllerWin::IsPointerTypeValidForVirtualKeyboard()
    const {
  return (host_view_->GetLastPointerType() == ui::EventPointerType::kTouch ||
          host_view_->GetLastPointerType() == ui::EventPointerType::kPen);
}

}  // namespace content