Commit 58e7aa54 authored by Yash Malik's avatar Yash Malik Committed by Commit Bot

VR: Add UI support for editing web input fields

This CL:
- moves the common text editing bits from TextInput into UiElement so that
  they can be reused from ContentElement.
- adds the notion of "pause until acked" to GvrKeyboardDelegate (rationale
  explained in comments).
- adds keyboard plumbing from UI to Browser which will actually do something
  useful in a follow up patch (https://chromium-review.googlesource.com/c/chromium/src/+/867513)

Bug: 641470
Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_vr;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: I53690b8068e777297bc7c2dfd7a92eac42966ff9
Reviewed-on: https://chromium-review.googlesource.com/895290Reviewed-by: default avatarChristopher Grant <cjgrant@chromium.org>
Commit-Queue: Yash Malik <ymalik@chromium.org>
Cr-Commit-Position: refs/heads/master@{#533862}
parent 00dd1b27
......@@ -62,6 +62,10 @@ void GvrKeyboardDelegate::SetUiInterface(vr::KeyboardUiInterface* ui) {
}
void GvrKeyboardDelegate::OnBeginFrame() {
// Pause keyboard updates until previous updates from the keyboard are acked.
if (pause_keyboard_update_)
return;
gvr::ClockTimePoint target_time = gvr::GvrApi::GetTimePointNow();
gvr_keyboard_set_frame_time(gvr_keyboard_, &target_time);
gvr_keyboard_advance_frame(gvr_keyboard_);
......@@ -128,9 +132,13 @@ void GvrKeyboardDelegate::OnButtonUp(const gfx::PointF& position) {
}
void GvrKeyboardDelegate::UpdateInput(const vr::TextInputInfo& info) {
cached_text_input_info_ = info;
gvr_keyboard_set_text(gvr_keyboard_, base::UTF16ToUTF8(info.text).c_str());
gvr_keyboard_set_selection_indices(gvr_keyboard_, info.selection_start,
info.selection_end);
gvr_keyboard_set_composing_indices(gvr_keyboard_, info.composition_start,
info.composition_end);
pause_keyboard_update_ = false;
}
void GvrKeyboardDelegate::OnGvrKeyboardEvent(EventType event) {
......@@ -153,9 +161,15 @@ void GvrKeyboardDelegate::OnGvrKeyboardEvent(EventType event) {
case GVR_KEYBOARD_HIDDEN:
ui_->OnKeyboardHidden();
break;
case GVR_KEYBOARD_TEXT_UPDATED:
ui_->OnInputEdited(GetTextInfo());
case GVR_KEYBOARD_TEXT_UPDATED: {
auto info = GetTextInfo();
DCHECK(!pause_keyboard_update_);
if (info != cached_text_input_info_) {
ui_->OnInputEdited(GetTextInfo());
pause_keyboard_update_ = true;
}
break;
}
case GVR_KEYBOARD_TEXT_COMMITTED:
ui_->OnInputCommitted(GetTextInfo());
break;
......@@ -175,6 +189,13 @@ vr::TextInputInfo GvrKeyboardDelegate::GetTextInfo() {
gvr_keyboard_get_selection_indices(gvr_keyboard_, &start, &end);
info.selection_start = start;
info.selection_end = end;
gvr_keyboard_get_composing_indices(gvr_keyboard_, &start, &end);
info.composition_start = start;
info.composition_end = end;
if (info.composition_start == info.composition_end) {
info.composition_start = vr::TextInputInfo::kDefaultCompositionIndex;
info.composition_end = vr::TextInputInfo::kDefaultCompositionIndex;
}
return info;
}
......
......@@ -41,7 +41,6 @@ class GvrKeyboardDelegate : public vr::KeyboardDelegate {
void OnButtonDown(const gfx::PointF& position) override;
void OnButtonUp(const gfx::PointF& position) override;
// Called to update GVR keyboard with the given text input info.
void UpdateInput(const vr::TextInputInfo& info);
......@@ -50,6 +49,16 @@ class GvrKeyboardDelegate : public vr::KeyboardDelegate {
void Init(gvr_keyboard_context* keyboard_context);
void OnGvrKeyboardEvent(EventType);
vr::TextInputInfo GetTextInfo();
// We pause updates from the keyboard until the previous update has been
// "acked" using GvrKeyboardDelegate::UpdateInput. This is to prevent weird
// behavior when editing web input fields. For example, say that the current
// text is "asdfg" and the user holds the backspace key. We get multiple
// backspace events from the keyboard, and if the second event comes before
// first event is acked (can happen because the ack comes from the Renderer),
// we'll override the keyboard state with the ack.
// TODO(ymalik): This is brittle, we should look for a better solution.
bool pause_keyboard_update_ = false;
vr::TextInputInfo cached_text_input_info_;
vr::KeyboardUiInterface* ui_;
gvr_keyboard_context* gvr_keyboard_ = nullptr;
......
......@@ -126,6 +126,13 @@ void VrGLThread::ForwardEvent(std::unique_ptr<blink::WebInputEvent> event,
base::Passed(std::move(event)), content_id));
}
void VrGLThread::OnWebInputEdited(const vr::TextInputInfo& info, bool commit) {
DCHECK(OnGlThread());
main_thread_task_runner_->PostTask(
FROM_HERE,
base::BindOnce(&VrShell::OnWebInputEdited, weak_vr_shell_, info, commit));
}
void VrGLThread::ForceExitVr() {
DCHECK(OnGlThread());
main_thread_task_runner_->PostTask(
......@@ -364,6 +371,39 @@ void VrGLThread::OnAssetsComponentReady() {
browser_ui_));
}
void VrGLThread::ShowSoftInput(bool show) {
DCHECK(OnMainThread());
task_runner()->PostTask(
FROM_HERE, base::BindRepeating(&vr::BrowserUiInterface::ShowSoftInput,
browser_ui_, show));
}
void VrGLThread::UpdateWebInputSelectionIndices(int selection_start,
int selection_end) {
DCHECK(OnMainThread());
task_runner()->PostTask(
FROM_HERE, base::BindRepeating(
&vr::BrowserUiInterface::UpdateWebInputSelectionIndices,
browser_ui_, selection_start, selection_end));
}
void VrGLThread::UpdateWebInputCompositionIndices(int composition_start,
int composition_end) {
DCHECK(OnMainThread());
task_runner()->PostTask(
FROM_HERE, base::BindRepeating(
&vr::BrowserUiInterface::UpdateWebInputCompositionIndices,
browser_ui_, composition_start, composition_end));
}
void VrGLThread::UpdateWebInputText(const base::string16& text) {
DCHECK(OnMainThread());
task_runner()->PostTask(
FROM_HERE,
base::BindRepeating(&vr::BrowserUiInterface::UpdateWebInputText,
browser_ui_, text));
}
bool VrGLThread::OnMainThread() const {
return main_thread_task_runner_->BelongsToCurrentThread();
}
......
......@@ -63,6 +63,7 @@ class VrGLThread : public base::android::JavaHandlerThread,
// vr::ContentInputForwarder
void ForwardEvent(std::unique_ptr<blink::WebInputEvent> event,
int content_id) override;
void OnWebInputEdited(const vr::TextInputInfo& info, bool commit) override;
// vr::UiBrowserInterface implementation (UI calling to VrShell).
void ExitPresent() override;
......@@ -101,6 +102,12 @@ class VrGLThread : public base::android::JavaHandlerThread,
void SetOmniboxSuggestions(
std::unique_ptr<vr::OmniboxSuggestions> result) override;
void OnAssetsComponentReady() override;
void ShowSoftInput(bool show) override;
void UpdateWebInputSelectionIndices(int selection_start,
int selection_end) override;
void UpdateWebInputCompositionIndices(int composition_start,
int composition_end) override;
void UpdateWebInputText(const base::string16& text) override;
protected:
void Init() override;
......
......@@ -12,6 +12,7 @@
#include "base/android/jni_string.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
......@@ -34,6 +35,7 @@
#include "chrome/browser/vr/assets_loader.h"
#include "chrome/browser/vr/metrics_helper.h"
#include "chrome/browser/vr/model/omnibox_suggestions.h"
#include "chrome/browser/vr/model/text_input_info.h"
#include "chrome/browser/vr/toolbar_helper.h"
#include "chrome/browser/vr/vr_tab_helper.h"
#include "chrome/common/chrome_features.h"
......@@ -621,6 +623,8 @@ void VrShell::LogUnsupportedModeUserMetric(JNIEnv* env,
LogUnsupportedModeUserMetric((vr::UiUnsupportedMode)mode);
}
void VrShell::OnWebInputEdited(const vr::TextInputInfo info, bool commit) {}
void VrShell::LogUnsupportedModeUserMetric(vr::UiUnsupportedMode mode) {
UMA_HISTOGRAM_ENUMERATION("VR.Shell.EncounteredUnsupportedMode", mode,
vr::UiUnsupportedMode::kCount);
......
......@@ -143,6 +143,7 @@ class VrShell : device::GvrGamepadDataProvider,
JNIEnv* env,
const base::android::JavaParamRef<jobject>& obj,
int mode);
void OnWebInputEdited(vr::TextInputInfo info, bool commit);
void ContentWebContentsDestroyed();
......
......@@ -246,6 +246,7 @@ static_library("vr_common") {
test("vr_common_unittests") {
sources = [
"animation_player_unittest.cc",
"content_element_unittest.cc",
"databinding/binding_unittest.cc",
"databinding/vector_binding_unittest.cc",
"elements/button_unittest.cc",
......@@ -356,8 +357,12 @@ source_set("vr_test_support") {
"test/mock_browser_ui_interface.h",
"test/mock_content_input_delegate.cc",
"test/mock_content_input_delegate.h",
"test/mock_keyboard_delegate.cc",
"test/mock_keyboard_delegate.h",
"test/mock_render_text.cc",
"test/mock_render_text.h",
"test/mock_text_input_delegate.cc",
"test/mock_text_input_delegate.h",
"test/mock_ui_browser_interface.cc",
"test/mock_ui_browser_interface.h",
"test/vr_test_suite.cc",
......
......@@ -41,6 +41,14 @@ class BrowserUiInterface {
std::unique_ptr<OmniboxSuggestions> suggestions) = 0;
virtual void OnAssetsComponentReady() = 0;
// Web contents text input related.
virtual void ShowSoftInput(bool show) = 0;
virtual void UpdateWebInputSelectionIndices(int selection_start,
int selection_end) = 0;
virtual void UpdateWebInputCompositionIndices(int composition_start,
int composition_end) = 0;
virtual void UpdateWebInputText(const base::string16& text) = 0;
// Tab handling.
virtual void AppendToTabList(bool incognito,
int id,
......
// Copyright 2017 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 "chrome/browser/vr/ui_input_manager.h"
#include <memory>
#include "build/build_config.h"
#include "chrome/browser/vr/elements/content_element.h"
#include "chrome/browser/vr/elements/keyboard.h"
#include "chrome/browser/vr/model/model.h"
#include "chrome/browser/vr/test/mock_keyboard_delegate.h"
#include "chrome/browser/vr/test/mock_text_input_delegate.h"
#include "chrome/browser/vr/test/ui_test.h"
#include "chrome/browser/vr/ui_scene.h"
#include "testing/gmock/include/gmock/gmock.h"
using ::testing::_;
using ::testing::InSequence;
using ::testing::StrictMock;
namespace vr {
class ContentElementSceneTest : public UiTest {
public:
void SetUp() override {
UiTest::SetUp();
CreateScene(kNotInCct, kNotInWebVr);
// Make test text input.
text_input_delegate_ =
std::make_unique<StrictMock<MockTextInputDelegate>>();
auto* content =
static_cast<ContentElement*>(scene_->GetUiElementByName(kContentQuad));
content->SetTextInputDelegate(text_input_delegate_.get());
OnBeginFrame();
}
protected:
std::unique_ptr<StrictMock<MockTextInputDelegate>> text_input_delegate_;
testing::Sequence in_sequence_;
};
TEST_F(ContentElementSceneTest, WebInputFocus) {
auto* content =
static_cast<ContentElement*>(scene_->GetUiElementByName(kContentQuad));
// Set mock keyboard delegate.
auto* kb = static_cast<Keyboard*>(scene_->GetUiElementByName(kKeyboard));
auto kb_delegate = std::make_unique<StrictMock<MockKeyboardDelegate>>();
EXPECT_CALL(*kb_delegate, HideKeyboard()).InSequence(in_sequence_);
kb->SetKeyboardDelegate(kb_delegate.get());
// Editing web input.
EXPECT_CALL(*text_input_delegate_, RequestFocus(_)).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, ShowKeyboard()).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, OnBeginFrame()).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, SetTransform(_)).InSequence(in_sequence_);
ui_->ShowSoftInput(true);
EXPECT_TRUE(OnBeginFrame());
// Giving content focus should tell the delegate the focued field's content.
EXPECT_CALL(*text_input_delegate_, UpdateInput(_)).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, OnBeginFrame()).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, SetTransform(_)).InSequence(in_sequence_);
content->OnFocusChanged(true);
EXPECT_TRUE(OnBeginFrame());
// Updates from the browser should update keyboard state.
TextInputInfo info(base::ASCIIToUTF16("asdfg"));
info.selection_start = 1;
info.selection_end = 1;
info.composition_start = 0;
info.composition_end = 1;
EXPECT_CALL(*text_input_delegate_, UpdateInput(info))
.InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, OnBeginFrame()).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, SetTransform(_)).InSequence(in_sequence_);
ui_->UpdateWebInputSelectionIndices(1, 1);
ui_->UpdateWebInputCompositionIndices(0, 1);
ui_->UpdateWebInputText(base::ASCIIToUTF16("asdfg"));
EXPECT_TRUE(OnBeginFrame());
// End editing.
EXPECT_CALL(*kb_delegate, HideKeyboard()).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, OnBeginFrame()).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, SetTransform(_)).InSequence(in_sequence_);
ui_->ShowSoftInput(false);
EXPECT_TRUE(OnBeginFrame());
}
} // namespace vr
......@@ -5,6 +5,7 @@
#include "chrome/browser/vr/content_input_delegate.h"
#include "base/time/time.h"
#include "chrome/browser/vr/model/text_input_info.h"
#include "chrome/browser/vr/platform_controller.h"
#include "third_party/WebKit/public/platform/WebGestureEvent.h"
#include "third_party/WebKit/public/platform/WebMouseEvent.h"
......@@ -56,6 +57,13 @@ void ContentInputDelegate::OnContentUp(
MakeMouseEvent(blink::WebInputEvent::kMouseUp, normalized_hit_point));
}
void ContentInputDelegate::OnWebInputEdited(const TextInputInfo& info,
bool commit) {
if (!content_)
return;
content_->OnWebInputEdited(info, commit);
}
void ContentInputDelegate::OnContentFlingStart(
std::unique_ptr<blink::WebGestureEvent> gesture,
const gfx::PointF& normalized_hit_point) {
......
......@@ -22,11 +22,15 @@ class PointF;
namespace vr {
struct TextInputInfo;
class ContentInputForwarder {
public:
virtual ~ContentInputForwarder() {}
virtual void ForwardEvent(std::unique_ptr<blink::WebInputEvent> event,
int content_id) = 0;
// Text input specific.
virtual void OnWebInputEdited(const TextInputInfo& info, bool commit) = 0;
};
class PlatformController;
......@@ -43,6 +47,9 @@ class ContentInputDelegate {
void OnContentDown(const gfx::PointF& normalized_hit_point);
void OnContentUp(const gfx::PointF& normalized_hit_point);
// Text Input specific.
void OnWebInputEdited(const TextInputInfo& info, bool commit);
// The following functions are virtual so that they may be overridden in the
// MockContentInputDelegate.
VIRTUAL_FOR_MOCKS void OnContentFlingStart(
......
......@@ -4,6 +4,7 @@
#include "chrome/browser/vr/elements/content_element.h"
#include "chrome/browser/vr/model/text_input_info.h"
#include "chrome/browser/vr/ui_element_renderer.h"
#include "chrome/browser/vr/ui_scene_constants.h"
#include "chrome/browser/vr/vr_gl_util.h"
......@@ -61,6 +62,20 @@ void ContentElement::Render(UiElementRenderer* renderer,
}
}
void ContentElement::OnFocusChanged(bool focused) {
focused_ = focused;
if (event_handlers_.focus_change)
event_handlers_.focus_change.Run(focused);
}
void ContentElement::OnInputEdited(const TextInputInfo& info) {
delegate_->OnWebInputEdited(info, false);
}
void ContentElement::OnInputCommitted(const TextInputInfo& info) {
delegate_->OnWebInputEdited(info, true);
}
void ContentElement::OnHoverEnter(const gfx::PointF& position) {
delegate_->OnContentEnter(position);
}
......@@ -133,6 +148,30 @@ void ContentElement::SetProjectionMatrix(const gfx::Transform& matrix) {
projection_matrix_ = matrix;
}
void ContentElement::SetTextInputDelegate(
TextInputDelegate* text_input_delegate) {
text_input_delegate_ = text_input_delegate;
}
void ContentElement::RequestFocus() {
if (!text_input_delegate_)
return;
text_input_delegate_->RequestFocus(id());
}
void ContentElement::RequestUnfocus() {
if (!text_input_delegate_)
return;
text_input_delegate_->RequestUnfocus(id());
}
void ContentElement::UpdateInput(const TextInputInfo& info) {
if (text_input_delegate_ && focused_)
text_input_delegate_->UpdateInput(info);
}
bool ContentElement::OnBeginFrame(const base::TimeTicks& time,
const gfx::Transform& head_pose) {
// TODO(mthiesse): This projection matrix is always going to be a frame
......
......@@ -8,6 +8,7 @@
#include "base/macros.h"
#include "chrome/browser/vr/content_input_delegate.h"
#include "chrome/browser/vr/elements/ui_element.h"
#include "chrome/browser/vr/text_input_delegate.h"
#include "chrome/browser/vr/ui_element_renderer.h"
namespace vr {
......@@ -40,15 +41,23 @@ class ContentElement : public UiElement {
void Render(UiElementRenderer* renderer,
const CameraModel& model) const final;
void OnFocusChanged(bool focused) override;
void OnInputEdited(const TextInputInfo& info) override;
void OnInputCommitted(const TextInputInfo& info) override;
void RequestFocus() override;
void RequestUnfocus() override;
void UpdateInput(const TextInputInfo& info) override;
void SetTextureId(unsigned int texture_id);
void SetTextureLocation(UiElementRenderer::TextureLocation location);
void SetOverlayTextureId(unsigned int texture_id);
void SetOverlayTextureLocation(UiElementRenderer::TextureLocation location);
void SetProjectionMatrix(const gfx::Transform& matrix);
void SetTextInputDelegate(TextInputDelegate* text_input_delegate);
private:
ContentInputDelegate* delegate_ = nullptr;
TextInputDelegate* text_input_delegate_ = nullptr;
ScreenBoundsChangedCallback bounds_changed_callback_;
unsigned int texture_id_ = 0;
UiElementRenderer::TextureLocation texture_location_ =
......@@ -59,6 +68,7 @@ class ContentElement : public UiElement {
gfx::SizeF last_content_screen_bounds_;
float last_content_aspect_ratio_ = 0.0f;
gfx::Transform projection_matrix_;
bool focused_ = false;
DISALLOW_COPY_AND_ASSIGN(ContentElement);
};
......
......@@ -18,10 +18,8 @@ constexpr int kCursorBlinkHalfPeriodMs = 600;
namespace vr {
TextInput::TextInput(float font_height_meters,
OnFocusChangedCallback focus_changed_callback,
OnInputEditedCallback input_edit_callback)
: focus_changed_callback_(focus_changed_callback),
input_edit_callback_(input_edit_callback) {
: input_edit_callback_(input_edit_callback) {
auto text = std::make_unique<Text>(font_height_meters);
text->SetType(kTypeTextInputHint);
text->SetDrawPhase(kPhaseForeground);
......@@ -92,8 +90,8 @@ void TextInput::OnFocusChanged(bool focused) {
if (delegate_ && focused)
delegate_->UpdateInput(text_info_);
if (focus_changed_callback_)
focus_changed_callback_.Run(focused);
if (event_handlers_.focus_change)
event_handlers_.focus_change.Run(focused);
}
void TextInput::RequestFocus() {
......@@ -165,6 +163,10 @@ void TextInput::OnSetName() {
cursor_element_->set_owner_name_for_test(name());
}
TextInputInfo TextInput::GetTextInputInfoForTest() const {
return text_info_;
}
void TextInput::LayOutChildren() {
// To avoid re-rendering a texture when the cursor blinks, the texture is a
// separate element. Once the text has been laid out, we can position the
......
......@@ -29,7 +29,6 @@ class TextInput : public UiElement {
typedef base::RepeatingCallback<void(const TextInputInfo&)>
OnInputCommittedCallback;
TextInput(float font_height_meters,
OnFocusChangedCallback focus_changed_callback,
OnInputEditedCallback input_edit_callback);
~TextInput() override;
......@@ -38,10 +37,9 @@ class TextInput : public UiElement {
void OnFocusChanged(bool focused) override;
void OnInputEdited(const TextInputInfo& info) override;
void OnInputCommitted(const TextInputInfo& info) override;
void RequestFocus();
void RequestUnfocus();
void UpdateInput(const TextInputInfo& info);
void RequestFocus() override;
void RequestUnfocus() override;
void UpdateInput(const TextInputInfo& info) override;
void SetHintText(const base::string16& text);
void SetTextColor(SkColor color);
......@@ -62,12 +60,13 @@ class TextInput : public UiElement {
Text* get_text_element() { return text_element_; }
Rect* get_cursor_element() { return cursor_element_; }
TextInputInfo GetTextInputInfoForTest() const;
private:
void LayOutChildren() final;
bool SetCursorBlinkState(const base::TimeTicks& time);
void ResetCursorBlinkCycle();
OnFocusChangedCallback focus_changed_callback_;
OnInputEditedCallback input_edit_callback_;
OnInputEditedCallback input_commit_callback_;
TextInputDelegate* delegate_ = nullptr;
......
......@@ -201,6 +201,18 @@ void UiElement::OnInputCommitted(const TextInputInfo& info) {
NOTREACHED();
}
void UiElement::RequestFocus() {
NOTREACHED();
}
void UiElement::RequestUnfocus() {
NOTREACHED();
}
void UiElement::UpdateInput(const TextInputInfo& info) {
NOTREACHED();
}
bool UiElement::PrepareToDraw() {
return false;
}
......
......@@ -62,6 +62,7 @@ struct EventHandlers {
base::Callback<void(const gfx::PointF&)> hover_move;
base::Callback<void()> button_down;
base::Callback<void()> button_up;
base::RepeatingCallback<void(bool)> focus_change;
};
struct HitTestRequest {
......@@ -216,6 +217,9 @@ class UiElement : public cc::AnimationTarget {
virtual void OnFocusChanged(bool focused);
virtual void OnInputEdited(const TextInputInfo& info);
virtual void OnInputCommitted(const TextInputInfo& info);
virtual void RequestFocus();
virtual void RequestUnfocus();
virtual void UpdateInput(const TextInputInfo& info);
gfx::SizeF size() const;
void SetSize(float width, float hight);
......@@ -446,6 +450,8 @@ class UiElement : public cc::AnimationTarget {
base::TimeTicks last_frame_time() const { return last_frame_time_; }
EventHandlers event_handlers_;
private:
virtual void OnUpdatedWorldSpaceTransform();
......@@ -564,8 +570,6 @@ class UiElement : public cc::AnimationTarget {
UpdatePhase phase_ = kClean;
EventHandlers event_handlers_;
DISALLOW_COPY_AND_ASSIGN(UiElement);
};
......
......@@ -99,6 +99,10 @@ bool Model::omnibox_editing_enabled() const {
return get_last_opaque_mode() == kModeEditingOmnibox;
}
bool Model::editing_enabled() const {
return editing_input || editing_web_input;
}
bool Model::fullscreen_enabled() const {
return get_last_opaque_mode() == kModeFullscreen;
}
......
......@@ -65,6 +65,7 @@ struct Model {
bool default_browsing_enabled() const;
bool voice_search_enabled() const;
bool omnibox_editing_enabled() const;
bool editing_enabled() const;
bool fullscreen_enabled() const;
bool web_vr_enabled() const;
bool web_vr_autopresentation_enabled() const;
......@@ -72,7 +73,10 @@ struct Model {
// Focused text state.
bool editing_input = false;
bool editing_web_input = false;
// Editable text field state.
TextInputInfo omnibox_text_field_info;
TextInputInfo web_input_text_field_info;
// Controller state.
ControllerModel controller;
......
......@@ -6,20 +6,30 @@
namespace vr {
TextInputInfo::TextInputInfo() : selection_start(0), selection_end(0) {}
TextInputInfo::TextInputInfo()
: selection_start(0),
selection_end(0),
composition_start(kDefaultCompositionIndex),
composition_end(kDefaultCompositionIndex) {}
TextInputInfo::TextInputInfo(base::string16 t)
: text(t), selection_start(t.length()), selection_end(t.length()) {}
: text(t),
selection_start(t.length()),
selection_end(t.length()),
composition_start(kDefaultCompositionIndex),
composition_end(kDefaultCompositionIndex) {}
bool TextInputInfo::operator==(const TextInputInfo& other) const {
return text == other.text && selection_start == other.selection_start &&
selection_end == other.selection_end;
selection_end == other.selection_end &&
composition_start == other.composition_start &&
composition_end == other.composition_end;
}
bool TextInputInfo::operator!=(const TextInputInfo& other) const {
return !(*this == other);
}
static_assert(sizeof(base::string16) + 8 == sizeof(TextInputInfo),
static_assert(sizeof(base::string16) + 16 == sizeof(TextInputInfo),
"If new fields are added to TextInputInfo, we must explicitly "
"bump this size and update operator==");
......
......@@ -6,6 +6,8 @@
#define CHROME_BROWSER_VR_MODEL_TEXT_INPUT_INFO_H_
#include "base/strings/string16.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
namespace vr {
......@@ -15,6 +17,8 @@ struct TextInputInfo {
TextInputInfo();
explicit TextInputInfo(base::string16 t);
static const int kDefaultCompositionIndex = -1;
bool operator==(const TextInputInfo& other) const;
bool operator!=(const TextInputInfo& other) const;
......@@ -28,6 +32,18 @@ struct TextInputInfo {
// The cursor position of the current selection end, or the caret position
// if nothing is selected.
int selection_end;
// The start position of the current composition, or -1 if there is none.
int composition_start;
// The end position of the current composition, or -1 if there is none.
int composition_end;
std::string ToString() const {
return base::StringPrintf(
"t(%s) s(%d, %d) c(%d, %d)", base::UTF16ToUTF8(text).c_str(),
selection_start, selection_end, composition_start, composition_end);
}
};
} // namespace vr
......
......@@ -40,6 +40,11 @@ class MockBrowserUiInterface : public BrowserUiInterface {
void SetOmniboxSuggestions(std::unique_ptr<OmniboxSuggestions> suggestions) {}
MOCK_METHOD0(OnAssetsComponentReady, void());
MOCK_METHOD1(ShowSoftInput, void(bool));
MOCK_METHOD2(UpdateWebInputSelectionIndices, void(int, int));
MOCK_METHOD2(UpdateWebInputCompositionIndices, void(int, int));
MOCK_METHOD1(UpdateWebInputText, void(const base::string16&));
private:
DISALLOW_COPY_AND_ASSIGN(MockBrowserUiInterface);
};
......
// 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 "chrome/browser/vr/test/mock_keyboard_delegate.h"
namespace vr {
MockKeyboardDelegate::MockKeyboardDelegate() {}
MockKeyboardDelegate::~MockKeyboardDelegate() {}
} // namespace vr
// Copyright 2017 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 CHROME_BROWSER_VR_TEST_MOCK_KEYBOARD_DELEGATE_H_
#define CHROME_BROWSER_VR_TEST_MOCK_KEYBOARD_DELEGATE_H_
#include "base/macros.h"
#include "chrome/browser/vr/keyboard_delegate.h"
#include "chrome/browser/vr/model/camera_model.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "ui/gfx/geometry/point3_f.h"
#include "ui/gfx/transform.h"
namespace vr {
class MockKeyboardDelegate : public KeyboardDelegate {
public:
MockKeyboardDelegate();
~MockKeyboardDelegate() override;
MOCK_METHOD0(ShowKeyboard, void());
MOCK_METHOD0(HideKeyboard, void());
MOCK_METHOD1(SetTransform, void(const gfx::Transform&));
MOCK_METHOD3(HitTest,
bool(const gfx::Point3F&, const gfx::Point3F&, gfx::Point3F*));
MOCK_METHOD0(OnBeginFrame, void());
MOCK_METHOD1(Draw, void(const CameraModel&));
MOCK_METHOD1(OnHoverEnter, void(const gfx::PointF&));
MOCK_METHOD0(OnHoverLeave, void());
MOCK_METHOD1(OnMove, void(const gfx::PointF&));
MOCK_METHOD1(OnButtonDown, void(const gfx::PointF&));
MOCK_METHOD1(OnButtonUp, void(const gfx::PointF&));
private:
DISALLOW_COPY_AND_ASSIGN(MockKeyboardDelegate);
};
} // namespace vr
#endif // CHROME_BROWSER_VR_TEST_MOCK_KEYBOARD_DELEGATE_H_
// 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 "chrome/browser/vr/test/mock_text_input_delegate.h"
namespace vr {
MockTextInputDelegate::MockTextInputDelegate() {}
MockTextInputDelegate::~MockTextInputDelegate() {}
} // namespace vr
// 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 CHROME_BROWSER_VR_TEST_MOCK_TEXT_INPUT_DELEGATE_H_
#define CHROME_BROWSER_VR_TEST_MOCK_TEXT_INPUT_DELEGATE_H_
#include "base/macros.h"
#include "chrome/browser/vr/model/text_input_info.h"
#include "chrome/browser/vr/text_input_delegate.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace vr {
class MockTextInputDelegate : public TextInputDelegate {
public:
MockTextInputDelegate();
~MockTextInputDelegate() override;
MOCK_METHOD1(UpdateInput, void(const TextInputInfo& info));
MOCK_METHOD1(RequestFocus, void(int));
private:
DISALLOW_COPY_AND_ASSIGN(MockTextInputDelegate);
};
} // namespace vr
#endif // CHROME_BROWSER_VR_TEST_MOCK_TEXT_INPUT_DELEGATE_H_
......@@ -12,13 +12,12 @@
#include "chrome/browser/vr/elements/keyboard.h"
#include "chrome/browser/vr/elements/text.h"
#include "chrome/browser/vr/elements/ui_element.h"
#include "chrome/browser/vr/keyboard_delegate.h"
#include "chrome/browser/vr/model/camera_model.h"
#include "chrome/browser/vr/model/model.h"
#include "chrome/browser/vr/test/animation_utils.h"
#include "chrome/browser/vr/test/constants.h"
#include "chrome/browser/vr/test/mock_keyboard_delegate.h"
#include "chrome/browser/vr/test/mock_text_input_delegate.h"
#include "chrome/browser/vr/test/ui_test.h"
#include "chrome/browser/vr/text_input_delegate.h"
#include "chrome/browser/vr/ui_scene.h"
#include "chrome/browser/vr/ui_scene_constants.h"
#include "chrome/browser/vr/ui_scene_creator.h"
......@@ -36,40 +35,6 @@ namespace {
constexpr float kFontHeightMeters = 0.050f;
}
class MockTextInputDelegate : public TextInputDelegate {
public:
MockTextInputDelegate() = default;
~MockTextInputDelegate() override = default;
MOCK_METHOD1(UpdateInput, void(const TextInputInfo& info));
MOCK_METHOD1(RequestFocus, void(int));
private:
DISALLOW_COPY_AND_ASSIGN(MockTextInputDelegate);
};
class MockKeyboardDelegate : public KeyboardDelegate {
public:
MockKeyboardDelegate() = default;
~MockKeyboardDelegate() override = default;
MOCK_METHOD0(ShowKeyboard, void());
MOCK_METHOD0(HideKeyboard, void());
MOCK_METHOD1(SetTransform, void(const gfx::Transform&));
MOCK_METHOD3(HitTest,
bool(const gfx::Point3F&, const gfx::Point3F&, gfx::Point3F*));
MOCK_METHOD0(OnBeginFrame, void());
MOCK_METHOD1(Draw, void(const CameraModel&));
MOCK_METHOD1(OnHoverEnter, void(const gfx::PointF&));
MOCK_METHOD0(OnHoverLeave, void());
MOCK_METHOD1(OnMove, void(const gfx::PointF&));
MOCK_METHOD1(OnButtonDown, void(const gfx::PointF&));
MOCK_METHOD1(OnButtonUp, void(const gfx::PointF&));
private:
DISALLOW_COPY_AND_ASSIGN(MockKeyboardDelegate);
};
class TextInputSceneTest : public UiTest {
public:
void SetUp() override {
......@@ -79,7 +44,8 @@ class TextInputSceneTest : public UiTest {
// Make test text input.
text_input_delegate_ =
std::make_unique<StrictMock<MockTextInputDelegate>>();
text_input_info_ = std::make_unique<TextInputInfo>();
text_input_info_ =
std::make_unique<TextInputInfo>(base::ASCIIToUTF16("asdfg"));
auto text_input = UiSceneCreator::CreateTextInput(
1, model_, text_input_info_.get(), text_input_delegate_.get());
text_input_ = text_input.get();
......@@ -107,12 +73,14 @@ TEST_F(TextInputSceneTest, InputFieldFocus) {
// Focusing on an input field should show the keyboard and tell the delegate
// the field's content.
EXPECT_CALL(*text_input_delegate_, UpdateInput(_)).InSequence(in_sequence_);
EXPECT_CALL(*text_input_delegate_, UpdateInput(*text_input_info_))
.InSequence(in_sequence_);
text_input_->OnFocusChanged(true);
EXPECT_CALL(*kb_delegate, ShowKeyboard()).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, OnBeginFrame()).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, SetTransform(_)).InSequence(in_sequence_);
EXPECT_TRUE(OnBeginFrame());
EXPECT_EQ(*text_input_info_, text_input_->GetTextInputInfoForTest());
// Focusing out of an input field should hide the keyboard.
text_input_->OnFocusChanged(false);
......@@ -120,6 +88,7 @@ TEST_F(TextInputSceneTest, InputFieldFocus) {
EXPECT_CALL(*kb_delegate, OnBeginFrame()).InSequence(in_sequence_);
EXPECT_CALL(*kb_delegate, SetTransform(_)).InSequence(in_sequence_);
EXPECT_TRUE(OnBeginFrame());
EXPECT_EQ(*text_input_info_, text_input_->GetTextInputInfoForTest());
}
TEST_F(TextInputSceneTest, InputFieldEdit) {
......@@ -135,7 +104,7 @@ TEST_F(TextInputSceneTest, InputFieldEdit) {
// Edits from the keyboard update the underlying text input model.
EXPECT_CALL(*text_input_delegate_, UpdateInput(_)).InSequence(in_sequence_);
TextInputInfo info(base::ASCIIToUTF16("asdfg"));
TextInputInfo info(base::ASCIIToUTF16("asdfgh"));
text_input_->OnInputEdited(info);
EXPECT_TRUE(OnBeginFrame());
EXPECT_EQ(info, *text_input_info_);
......@@ -170,8 +139,10 @@ TEST(TextInputTest, HintText) {
UiScene scene;
auto instance = std::make_unique<TextInput>(
kFontHeightMeters, TextInput::OnFocusChangedCallback(),
TextInput::OnInputEditedCallback());
kFontHeightMeters, TextInput::OnInputEditedCallback());
EventHandlers event_handlers;
event_handlers.focus_change = TextInput::OnFocusChangedCallback();
instance->set_event_handlers(event_handlers);
instance->SetName(kOmniboxTextField);
instance->SetSize(1, 0);
TextInput* element = instance.get();
......@@ -191,10 +162,11 @@ TEST(TextInputTest, HintText) {
TEST(TextInputTest, CursorBlinking) {
UiScene scene;
auto instance = std::make_unique<TextInput>(
kFontHeightMeters, TextInput::OnFocusChangedCallback(),
TextInput::OnInputEditedCallback());
kFontHeightMeters, TextInput::OnInputEditedCallback());
EventHandlers event_handlers;
event_handlers.focus_change = TextInput::OnFocusChangedCallback();
instance->set_event_handlers(event_handlers);
instance->SetName(kOmniboxTextField);
instance->SetSize(1, 0);
TextInput* element = instance.get();
......@@ -226,8 +198,10 @@ TEST(TextInputTest, CursorBlinking) {
// environment. As of now, much of this is skipped due to lack of a GL context.
TEST(TextInputTest, CursorPositionUpdatesOnKeyboardInput) {
auto element = std::make_unique<TextInput>(
kFontHeightMeters, TextInput::OnFocusChangedCallback(),
TextInput::OnInputEditedCallback());
kFontHeightMeters, TextInput::OnInputEditedCallback());
EventHandlers event_handlers;
event_handlers.focus_change = TextInput::OnFocusChangedCallback();
element->set_event_handlers(event_handlers);
TextInputInfo info(base::UTF8ToUTF16("text"));
info.selection_start = 0;
......@@ -247,8 +221,10 @@ TEST(TextInputTest, CursorPositionUpdatesOnKeyboardInput) {
TEST(TextInputTest, CursorPositionUpdatesOnClicks) {
auto element = std::make_unique<TextInput>(
kFontHeightMeters, TextInput::OnFocusChangedCallback(),
TextInput::OnInputEditedCallback());
kFontHeightMeters, TextInput::OnInputEditedCallback());
EventHandlers event_handlers;
event_handlers.focus_change = TextInput::OnFocusChangedCallback();
element->set_event_handlers(event_handlers);
TextInputInfo info(base::UTF8ToUTF16("text"));
element->UpdateInput(info);
......
......@@ -206,6 +206,26 @@ bool Ui::CanSendWebVrVSync() {
!model_->web_vr.awaiting_min_splash_screen_duration();
}
void Ui::ShowSoftInput(bool show) {
model_->editing_web_input = show;
}
void Ui::UpdateWebInputSelectionIndices(int selection_start,
int selection_end) {
model_->web_input_text_field_info.selection_start = selection_start;
model_->web_input_text_field_info.selection_end = selection_end;
}
void Ui::UpdateWebInputCompositionIndices(int composition_start,
int composition_end) {
model_->web_input_text_field_info.composition_start = composition_start;
model_->web_input_text_field_info.composition_end = composition_end;
}
void Ui::UpdateWebInputText(const base::string16& text) {
model_->web_input_text_field_info.text = text;
}
bool Ui::ShouldRenderWebVr() {
return model_->web_vr.has_produced_frames();
}
......
......@@ -103,6 +103,14 @@ class Ui : public BrowserUiInterface, public KeyboardUiInterface {
// have to worry about this, and if it were told to hide the splash screen
// like other WebVR phases (e.g. OnWebVrFrameAvailable below).
bool CanSendWebVrVSync();
void ShowSoftInput(bool show) override;
void UpdateWebInputSelectionIndices(int selection_start,
int selection_end) override;
void UpdateWebInputCompositionIndices(int composition_start,
int composition_end) override;
void UpdateWebInputText(const base::string16& text) override;
bool ShouldRenderWebVr();
void OnGlInitialized(
......
......@@ -60,9 +60,7 @@ class MockRect : public Rect {
class MockTextInput : public TextInput {
public:
MockTextInput()
: TextInput(1,
base::RepeatingCallback<void(bool)>(),
base::RepeatingCallback<void(const TextInputInfo&)>()) {}
: TextInput(1, base::RepeatingCallback<void(const TextInputInfo&)>()) {}
~MockTextInput() override = default;
MOCK_METHOD1(OnFocusChanged, void(bool));
......
......@@ -819,12 +819,36 @@ void UiSceneCreator::CreateContentQuad() {
content_input_delegate_,
base::BindRepeating(&UiBrowserInterface::OnContentScreenBoundsChanged,
base::Unretained(browser_)));
EventHandlers event_handlers;
event_handlers.focus_change = base::BindRepeating(
[](Model* model, ContentElement* e, bool focused) {
if (focused) {
e->UpdateInput(model->web_input_text_field_info);
} else {
e->UpdateInput(TextInputInfo());
}
},
model_, base::Unretained(main_content.get()));
main_content->set_event_handlers(event_handlers);
main_content->SetName(kContentQuad);
main_content->SetDrawPhase(kPhaseForeground);
main_content->SetSize(kContentWidth, kContentHeight);
main_content->set_corner_radius(kContentCornerRadius);
main_content->SetTranslate(0, 0, kContentShadowOffset);
main_content->SetTransitionedProperties({BOUNDS});
main_content->SetTextInputDelegate(text_input_delegate_);
main_content->AddBinding(std::make_unique<Binding<bool>>(
VR_BIND_LAMBDA([](Model* m) { return m->editing_web_input; },
base::Unretained(model_)),
VR_BIND_LAMBDA(
[](ContentElement* e, const bool& v) {
if (v) {
e->RequestFocus();
} else {
e->RequestUnfocus();
}
},
base::Unretained(main_content.get()))));
main_content->AddBinding(
VR_BIND(bool, Model, model_, model->fullscreen_enabled(), UiElement,
main_content.get(),
......@@ -847,7 +871,12 @@ void UiSceneCreator::CreateContentQuad() {
VR_BIND_FUNC(UiElementRenderer::TextureLocation, Model, model_,
model->content_overlay_location, ContentElement,
main_content.get(), SetOverlayTextureLocation));
main_content->AddBinding(std::make_unique<Binding<TextInputInfo>>(
VR_BIND_LAMBDA([](TextInputInfo* info) { return *info; },
base::Unretained(&model_->web_input_text_field_info)),
VR_BIND_LAMBDA([](ContentElement* e,
const TextInputInfo& value) { e->UpdateInput(value); },
base::Unretained(main_content.get()))));
shadow->AddChild(std::move(main_content));
scene_->AddUiElement(k2dBrowsingContentGroup, std::move(shadow));
......@@ -1545,14 +1574,25 @@ std::unique_ptr<TextInput> UiSceneCreator::CreateTextInput(
TextInputDelegate* text_input_delegate) {
auto text_input = std::make_unique<TextInput>(
font_height_meters,
base::BindRepeating(
[](Model* model, bool focused) { model->editing_input = focused; },
base::Unretained(model)),
base::BindRepeating(
[](TextInputInfo* model, const TextInputInfo& text_input_info) {
*model = text_input_info;
},
base::Unretained(text_input_model)));
EventHandlers event_handlers;
event_handlers.focus_change = base::BindRepeating(
[](Model* model, TextInput* text_input, TextInputInfo* text_input_info,
bool focused) {
if (focused) {
model->editing_input = true;
text_input->UpdateInput(*text_input_info);
} else {
model->editing_input = false;
}
},
base::Unretained(model), base::Unretained(text_input.get()),
base::Unretained(text_input_model));
text_input->set_event_handlers(event_handlers);
text_input->SetDrawPhase(kPhaseNone);
text_input->set_hit_testable(false);
text_input->SetTextInputDelegate(text_input_delegate);
......@@ -1570,7 +1610,7 @@ void UiSceneCreator::CreateKeyboard() {
Create<UiElement>(kKeyboardVisibilityControlForVoice, kPhaseNone);
visibility_control_root->set_hit_testable(false);
BIND_VISIBILITY_CONTROL_FOR_VOICE(visibility_control_root.get(), model_,
editing_input);
editing_enabled());
auto scaler = std::make_unique<ScaledDepthAdjuster>(kKeyboardDistance);
scaler->SetName(kKeyboardDmmRoot);
......@@ -1579,7 +1619,8 @@ void UiSceneCreator::CreateKeyboard() {
keyboard->SetKeyboardDelegate(keyboard_delegate_);
keyboard->SetDrawPhase(kPhaseForeground);
keyboard->SetTranslate(0.0, kKeyboardVerticalOffsetDMM, 0.0);
VR_BIND_VISIBILITY(keyboard, model->editing_input);
VR_BIND_VISIBILITY(keyboard,
model->editing_input || model->editing_web_input);
scaler->AddChild(std::move(keyboard));
visibility_control_root->AddChild(std::move(scaler));
scene_->AddUiElement(k2dBrowsingRepositioner,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment