blob: d07177bd5a3ae955780d9bb340ed13728b5f252f [file] [log] [blame]
// 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 "ash/keyboard/ash_keyboard_ui.h"
#include <set>
#include <string>
#include <utility>
#include "ash/keyboard/ash_keyboard_controller.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/ws/window_service_owner.h"
#include "base/bind.h"
#include "services/ws/public/mojom/window_tree.mojom.h"
#include "services/ws/remote_view_host/server_remote_view_host.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/ime/chromeos/input_method_manager.h"
#include "ui/base/ime/ime_bridge.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/keyboard/keyboard_controller.h"
#include "ui/views/background.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_delegate.h"
#include "ui/wm/core/shadow_types.h"
namespace {
const int kShadowElevationVirtualKeyboard = 2;
} // namespace
namespace ash {
////////////////////////////////////////////////////////////////////////////////
// AshKeyboardView
class AshKeyboardUI::AshKeyboardView : public views::WidgetDelegateView {
public:
explicit AshKeyboardView(aura::Window* context) {
SetLayoutManager(std::make_unique<views::FillLayout>());
InitWidget(context);
// Set the background to be transparent for custom keyboard window shape.
SetBackground(views::CreateSolidBackground(SK_ColorTRANSPARENT));
window()->SetTransparent(true);
}
~AshKeyboardView() override { widget_.reset(); }
void SetShadowElevation(bool show) {
::wm::SetShadowElevation(window(),
show ? kShadowElevationVirtualKeyboard : 0);
}
void Embed(base::UnguessableToken token, const gfx::Size& size) {
DVLOG(1) << "Embed contents. Size: " << size.ToString();
window()->SetBounds(gfx::Rect(size));
if (!server_remote_view_host_) {
server_remote_view_host_ = new ws::ServerRemoteViewHost(
Shell::Get()->window_service_owner()->window_service());
AddChildView(server_remote_view_host_);
}
server_remote_view_host_->EmbedUsingToken(
token, ws::mojom::kEmbedFlagEmbedderControlsVisibility,
base::BindOnce(
[](base::UnguessableToken token, bool result) {
DVLOG(1) << "Embed: " << token << ". Result: " << result;
},
token));
Layout();
}
// views::WidgetDelegateView:
const char* GetClassName() const override { return "AshKeyboardView"; }
void DeleteDelegate() override {}
aura::Window* window() { return GetWidget()->GetNativeView(); }
private:
void InitWidget(aura::Window* context) {
widget_ = std::make_unique<views::Widget>();
views::Widget::InitParams params(
views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
params.name = "AshKeyboardUI";
params.delegate = this;
params.context = context;
params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
widget_->Init(params);
}
std::unique_ptr<views::Widget> widget_;
ws::ServerRemoteViewHost* server_remote_view_host_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(AshKeyboardView);
};
////////////////////////////////////////////////////////////////////////////////
// AshKeyboardUI
AshKeyboardUI::AshKeyboardUI(AshKeyboardController* ash_keyboard_controller)
: ash_keyboard_controller_(ash_keyboard_controller) {}
AshKeyboardUI::~AshKeyboardUI() {
ash_keyboard_controller_->SendOnKeyboardUIDestroyed();
if (ash_keyboard_view_) {
aura::Window* keyboard_window = ash_keyboard_view_->window();
if (keyboard_window)
keyboard_window->RemoveObserver(this);
}
}
void AshKeyboardUI::KeyboardContentsLoaded(const base::UnguessableToken& token,
const gfx::Size& size) {
DVLOG(1) << "KeyboardContentsLoaded. Token: " << token
<< " Size: " << size.ToString();
if (token == contents_window_token_ && size == contents_window_size_)
return;
contents_window_token_ = token;
contents_window_size_ = size;
if (ash_keyboard_view_)
EmbedContents();
}
// keyboard::KeyboardUI:
aura::Window* AshKeyboardUI::LoadKeyboardWindow(LoadCallback callback) {
DVLOG(1) << "LoadKeyboardWindow";
DCHECK(!ash_keyboard_view_);
load_callback_ = std::move(callback);
ash_keyboard_view_ = std::make_unique<AshKeyboardView>(
keyboard::KeyboardController::Get()->parent_container());
if (!contents_window_token_.is_empty())
EmbedContents();
aura::Window* keyboard_window = ash_keyboard_view_->window();
keyboard_window->AddObserver(this);
ash_keyboard_controller_->SendOnLoadKeyboardContentsRequested();
return keyboard_window;
}
aura::Window* AshKeyboardUI::GetKeyboardWindow() const {
return ash_keyboard_view_ ? ash_keyboard_view_->window() : nullptr;
}
ui::InputMethod* AshKeyboardUI::GetInputMethod() {
// TODO(stevenjb/shuchen): |bridge| may be null in Multi-Process Mash.
// KeyboardController needs to use the TBD Mojo based IMF services instead.
ui::IMEBridge* bridge = ui::IMEBridge::Get();
if (!bridge || !bridge->GetInputContextHandler()) {
// Needed by a handful of browser tests that use MockInputMethod.
return Shell::GetRootWindowForNewWindows()->GetHost()->GetInputMethod();
}
return bridge->GetInputContextHandler()->GetInputMethod();
}
void AshKeyboardUI::ReloadKeyboardIfNeeded() {
DVLOG(1) << "ReloadKeyboardIfNeeded";
ash_keyboard_controller_->SendOnLoadKeyboardContentsRequested();
}
// aura::WindowObserver:
void AshKeyboardUI::OnWindowBoundsChanged(aura::Window* window,
const gfx::Rect& old_bounds,
const gfx::Rect& new_bounds,
ui::PropertyChangeReason reason) {
DVLOG(1) << "OnWindowBoundsChanged: " << window->GetName() << ": "
<< new_bounds.ToString();
// Normally OnKeyboardVisibleBoundsChanged is triggered from
// keyboard::KeyboardController::NotifyKeyboardBoundsChanging, however if the
// contents window token has not been provided, an intial bounds change
// needs to be sent so that the contents can size itself.
if (contents_window_token_.is_empty())
ash_keyboard_controller_->SendOnKeyboardVisibleBoundsChanged(new_bounds);
// The ContainerType changes after ash_keyboard_view_ is created, so update
// the shadow on bounds change. TODO(stevenjb): Find a better way to do this.
SetShadowElevation();
}
void AshKeyboardUI::OnWindowDestroyed(aura::Window* window) {
window->RemoveObserver(this);
}
void AshKeyboardUI::OnWindowParentChanged(aura::Window* window,
aura::Window* parent) {
SetShadowElevation();
}
// private methods:
void AshKeyboardUI::EmbedContents() {
ash_keyboard_view_->Embed(contents_window_token_, contents_window_size_);
if (!load_callback_.is_null())
std::move(load_callback_).Run();
}
void AshKeyboardUI::SetShadowElevation() {
ash_keyboard_view_->SetShadowElevation(
ash_keyboard_controller_->keyboard_controller()
->GetActiveContainerType() ==
keyboard::mojom::ContainerType::kFullWidth);
}
} // namespace ash