Commit 8ac6530d authored by Christopher Grant's avatar Christopher Grant Committed by Commit Bot

VR: Let clicks reposition the cursor in text fields.

BUG=799189
R=ymalik

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: I055bda2aef02a3278a090a6ebdd31a8769e8d4ce
Reviewed-on: https://chromium-review.googlesource.com/884372Reviewed-by: default avatarYash Malik <ymalik@chromium.org>
Commit-Queue: Christopher Grant <cjgrant@chromium.org>
Cr-Commit-Position: refs/heads/master@{#531971}
parent 72d7b437
...@@ -121,6 +121,13 @@ class TextTexture : public UiTexture { ...@@ -121,6 +121,13 @@ class TextTexture : public UiTexture {
SetAndDirty(&cursor_position_, position); SetAndDirty(&cursor_position_, position);
} }
int GetCursorPositionFromPoint(const gfx::PointF& point) const {
DCHECK_EQ(lines().size(), 1u);
gfx::Point pixel_position(point.x() * GetDrawnSize().width(),
point.y() * GetDrawnSize().height());
return lines().front()->FindCursorPosition(pixel_position).caret_pos();
}
void SetShadowsEnabled(bool enabled) { void SetShadowsEnabled(bool enabled) {
SetAndDirty(&shadows_enabled_, enabled); SetAndDirty(&shadows_enabled_, enabled);
} }
...@@ -137,7 +144,7 @@ class TextTexture : public UiTexture { ...@@ -137,7 +144,7 @@ class TextTexture : public UiTexture {
// the texture is modified here. // the texture is modified here.
void LayOutText(); void LayOutText();
const std::vector<std::unique_ptr<gfx::RenderText>>& lines() { const std::vector<std::unique_ptr<gfx::RenderText>>& lines() const {
return lines_; return lines_;
} }
...@@ -223,6 +230,10 @@ gfx::RectF Text::GetCursorBounds() const { ...@@ -223,6 +230,10 @@ gfx::RectF Text::GetCursorBounds() const {
bounds.height() * scale * kCursorWidthRatio, bounds.height() * scale); bounds.height() * scale * kCursorWidthRatio, bounds.height() * scale);
} }
int Text::GetCursorPositionFromPoint(const gfx::PointF& point) const {
return texture_->GetCursorPositionFromPoint(point);
}
void Text::SetShadowsEnabled(bool enabled) { void Text::SetShadowsEnabled(bool enabled) {
texture_->SetShadowsEnabled(enabled); texture_->SetShadowsEnabled(enabled);
} }
......
...@@ -100,6 +100,8 @@ class Text : public TexturedElement { ...@@ -100,6 +100,8 @@ class Text : public TexturedElement {
// texture size, relative to the upper-left corner of the element. // texture size, relative to the upper-left corner of the element.
gfx::RectF GetCursorBounds() const; gfx::RectF GetCursorBounds() const;
int GetCursorPositionFromPoint(const gfx::PointF& point) const;
// This causes the text to become uniformly shadowed. // This causes the text to become uniformly shadowed.
void SetShadowsEnabled(bool enabled); void SetShadowsEnabled(bool enabled);
......
...@@ -69,6 +69,18 @@ void TextInput::SetTextInputDelegate(TextInputDelegate* text_input_delegate) { ...@@ -69,6 +69,18 @@ void TextInput::SetTextInputDelegate(TextInputDelegate* text_input_delegate) {
delegate_ = text_input_delegate; delegate_ = text_input_delegate;
} }
void TextInput::OnButtonDown(const gfx::PointF& position) {
// Reposition the cursor based on click position.
int cursor_position = text_element_->GetCursorPositionFromPoint(position);
TextInputInfo info(text_info_);
info.selection_start = cursor_position;
info.selection_end = cursor_position;
if (text_info_ != info) {
UpdateInput(info);
ResetCursorBlinkCycle();
}
}
void TextInput::OnButtonUp(const gfx::PointF& position) { void TextInput::OnButtonUp(const gfx::PointF& position) {
RequestFocus(); RequestFocus();
} }
...@@ -125,6 +137,8 @@ void TextInput::SetHintColor(SkColor color) { ...@@ -125,6 +137,8 @@ void TextInput::SetHintColor(SkColor color) {
void TextInput::UpdateInput(const TextInputInfo& info) { void TextInput::UpdateInput(const TextInputInfo& info) {
if (text_info_ == info) if (text_info_ == info)
return; return;
DCHECK_EQ(info.selection_start, info.selection_end);
text_info_ = info; text_info_ = info;
if (delegate_ && focused_) if (delegate_ && focused_)
...@@ -161,9 +175,9 @@ void TextInput::LayOutChildren() { ...@@ -161,9 +175,9 @@ void TextInput::LayOutChildren() {
} }
bool TextInput::SetCursorBlinkState(const base::TimeTicks& time) { bool TextInput::SetCursorBlinkState(const base::TimeTicks& time) {
base::TimeDelta delta = time - base::TimeTicks(); base::TimeDelta delta = time - cursor_blink_start_ticks_;
bool visible = bool visible =
focused_ && delta.InMilliseconds() / kCursorBlinkHalfPeriodMs % 2; focused_ && (delta.InMilliseconds() / kCursorBlinkHalfPeriodMs + 1) % 2;
if (cursor_visible_ == visible) if (cursor_visible_ == visible)
return false; return false;
cursor_visible_ = visible; cursor_visible_ = visible;
...@@ -171,4 +185,8 @@ bool TextInput::SetCursorBlinkState(const base::TimeTicks& time) { ...@@ -171,4 +185,8 @@ bool TextInput::SetCursorBlinkState(const base::TimeTicks& time) {
return true; return true;
} }
void TextInput::ResetCursorBlinkCycle() {
cursor_blink_start_ticks_ = base::TimeTicks::Now();
}
} // namespace vr } // namespace vr
...@@ -33,6 +33,7 @@ class TextInput : public UiElement { ...@@ -33,6 +33,7 @@ class TextInput : public UiElement {
OnInputEditedCallback input_edit_callback); OnInputEditedCallback input_edit_callback);
~TextInput() override; ~TextInput() override;
void OnButtonDown(const gfx::PointF& position) override;
void OnButtonUp(const gfx::PointF& position) override; void OnButtonUp(const gfx::PointF& position) override;
void OnFocusChanged(bool focused) override; void OnFocusChanged(bool focused) override;
void OnInputEdited(const TextInputInfo& info) override; void OnInputEdited(const TextInputInfo& info) override;
...@@ -64,6 +65,7 @@ class TextInput : public UiElement { ...@@ -64,6 +65,7 @@ class TextInput : public UiElement {
private: private:
void LayOutChildren() final; void LayOutChildren() final;
bool SetCursorBlinkState(const base::TimeTicks& time); bool SetCursorBlinkState(const base::TimeTicks& time);
void ResetCursorBlinkCycle();
OnFocusChangedCallback focus_changed_callback_; OnFocusChangedCallback focus_changed_callback_;
OnInputEditedCallback input_edit_callback_; OnInputEditedCallback input_edit_callback_;
...@@ -72,6 +74,7 @@ class TextInput : public UiElement { ...@@ -72,6 +74,7 @@ class TextInput : public UiElement {
TextInputInfo text_info_; TextInputInfo text_info_;
bool focused_ = false; bool focused_ = false;
bool cursor_visible_ = false; bool cursor_visible_ = false;
base::TimeTicks cursor_blink_start_ticks_;
Text* hint_element_ = nullptr; Text* hint_element_ = nullptr;
Text* text_element_ = nullptr; Text* text_element_ = nullptr;
......
...@@ -61,8 +61,16 @@ void TestKeyboardDelegate::Initialize(vr::SkiaSurfaceProvider* provider, ...@@ -61,8 +61,16 @@ void TestKeyboardDelegate::Initialize(vr::SkiaSurfaceProvider* provider,
renderer_->Initialize(provider, renderer); renderer_->Initialize(provider, renderer);
} }
void TestKeyboardDelegate::UpdateInput(const vr::TextInputInfo& info) {
if (input_info_ == info)
return;
input_info_ = info;
ui_interface_->OnInputEdited(input_info_);
}
bool TestKeyboardDelegate::HandleInput(ui::Event* e) { bool TestKeyboardDelegate::HandleInput(ui::Event* e) {
DCHECK(keyboard_interface_); DCHECK(ui_interface_);
DCHECK(e->IsKeyEvent()); DCHECK(e->IsKeyEvent());
if (!editing_) if (!editing_)
return false; return false;
...@@ -72,13 +80,13 @@ bool TestKeyboardDelegate::HandleInput(ui::Event* e) { ...@@ -72,13 +80,13 @@ bool TestKeyboardDelegate::HandleInput(ui::Event* e) {
case ui::VKEY_RETURN: case ui::VKEY_RETURN:
input_info_.text.clear(); input_info_.text.clear();
input_info_.selection_start = input_info_.selection_end = 0; input_info_.selection_start = input_info_.selection_end = 0;
keyboard_interface_->OnInputCommitted(input_info_); ui_interface_->OnInputCommitted(input_info_);
break; break;
case ui::VKEY_BACK: case ui::VKEY_BACK:
input_info_.text.pop_back(); input_info_.text.pop_back();
input_info_.selection_start--; input_info_.selection_start--;
input_info_.selection_end--; input_info_.selection_end--;
keyboard_interface_->OnInputEdited(input_info_); ui_interface_->OnInputEdited(input_info_);
break; break;
default: default:
std::string character; std::string character;
...@@ -86,7 +94,7 @@ bool TestKeyboardDelegate::HandleInput(ui::Event* e) { ...@@ -86,7 +94,7 @@ bool TestKeyboardDelegate::HandleInput(ui::Event* e) {
input_info_.text = input_info_.text.append(base::UTF8ToUTF16(character)); input_info_.text = input_info_.text.append(base::UTF8ToUTF16(character));
input_info_.selection_start++; input_info_.selection_start++;
input_info_.selection_end++; input_info_.selection_end++;
keyboard_interface_->OnInputEdited(input_info_); ui_interface_->OnInputEdited(input_info_);
break; break;
} }
// We want to continue handling this keypress if the Ctrl key is down so // We want to continue handling this keypress if the Ctrl key is down so
......
...@@ -40,14 +40,14 @@ class TestKeyboardDelegate : public KeyboardDelegate { ...@@ -40,14 +40,14 @@ class TestKeyboardDelegate : public KeyboardDelegate {
void Initialize(SkiaSurfaceProvider* provider, UiElementRenderer* renderer); void Initialize(SkiaSurfaceProvider* provider, UiElementRenderer* renderer);
void SetUiInterface(KeyboardUiInterface* keyboard) { void SetUiInterface(KeyboardUiInterface* keyboard) {
keyboard_interface_ = keyboard; ui_interface_ = keyboard;
} }
void UpdateInput(const vr::TextInputInfo& info) { input_info_ = info; } void UpdateInput(const vr::TextInputInfo& info);
bool HandleInput(ui::Event* e); bool HandleInput(ui::Event* e);
private: private:
std::unique_ptr<TestKeyboardRenderer> renderer_; std::unique_ptr<TestKeyboardRenderer> renderer_;
KeyboardUiInterface* keyboard_interface_ = nullptr; KeyboardUiInterface* ui_interface_ = nullptr;
gfx::Transform world_space_transform_; gfx::Transform world_space_transform_;
bool editing_; bool editing_;
TextInputInfo input_info_; TextInputInfo input_info_;
......
...@@ -32,6 +32,10 @@ using ::testing::StrictMock; ...@@ -32,6 +32,10 @@ using ::testing::StrictMock;
namespace vr { namespace vr {
namespace {
constexpr float kFontHeightMeters = 0.050f;
}
class MockTextInputDelegate : public TextInputDelegate { class MockTextInputDelegate : public TextInputDelegate {
public: public:
MockTextInputDelegate() = default; MockTextInputDelegate() = default;
...@@ -165,9 +169,9 @@ TEST(TextInputTest, ControllerInteractionsSentToDelegate) { ...@@ -165,9 +169,9 @@ TEST(TextInputTest, ControllerInteractionsSentToDelegate) {
TEST(TextInputTest, HintText) { TEST(TextInputTest, HintText) {
UiScene scene; UiScene scene;
auto instance = auto instance = std::make_unique<TextInput>(
std::make_unique<TextInput>(10, TextInput::OnFocusChangedCallback(), kFontHeightMeters, TextInput::OnFocusChangedCallback(),
TextInput::OnInputEditedCallback()); TextInput::OnInputEditedCallback());
instance->SetName(kOmniboxTextField); instance->SetName(kOmniboxTextField);
instance->SetSize(1, 0); instance->SetSize(1, 0);
TextInput* element = instance.get(); TextInput* element = instance.get();
...@@ -185,12 +189,12 @@ TEST(TextInputTest, HintText) { ...@@ -185,12 +189,12 @@ TEST(TextInputTest, HintText) {
EXPECT_EQ(element->get_hint_element()->GetTargetOpacity(), 0); EXPECT_EQ(element->get_hint_element()->GetTargetOpacity(), 0);
} }
TEST(TextInputTest, Cursor) { TEST(TextInputTest, CursorBlinking) {
UiScene scene; UiScene scene;
auto instance = auto instance = std::make_unique<TextInput>(
std::make_unique<TextInput>(10, TextInput::OnFocusChangedCallback(), kFontHeightMeters, TextInput::OnFocusChangedCallback(),
TextInput::OnInputEditedCallback()); TextInput::OnInputEditedCallback());
instance->SetName(kOmniboxTextField); instance->SetName(kOmniboxTextField);
instance->SetSize(1, 0); instance->SetSize(1, 0);
TextInput* element = instance.get(); TextInput* element = instance.get();
...@@ -214,22 +218,56 @@ TEST(TextInputTest, Cursor) { ...@@ -214,22 +218,56 @@ TEST(TextInputTest, Cursor) {
toggled = true; toggled = true;
} }
EXPECT_TRUE(toggled); EXPECT_TRUE(toggled);
}
// TODO(cjgrant): Continue with the test cases below, when they're debugged. // TODO(cjgrant): Have this test, and others similar, check the actual position
#if 0 // of the cursor element. To make this work, the OnBeginFrame logic needs to be
TextInputInfo info; // updated to perform more of the measurement and layout steps in a test
info.text = base::UTF8ToUTF16("text"); // environment. As of now, much of this is skipped due to lack of a GL context.
TEST(TextInputTest, CursorPositionUpdatesOnKeyboardInput) {
// When the cursor position moves, the cursor element should move. auto element = std::make_unique<TextInput>(
kFontHeightMeters, TextInput::OnFocusChangedCallback(),
TextInput::OnInputEditedCallback());
TextInputInfo info(base::UTF8ToUTF16("text"));
info.selection_start = 0;
info.selection_end = 0;
element->UpdateInput(info); element->UpdateInput(info);
auto result = element->get_text_element()->LayOutTextForTest({512, 512}); element->get_text_element()->LayOutTextForTest();
auto position1 = element->get_text_element()->GetRawCursorBounds(); int x1 = element->get_text_element()->GetRawCursorBounds().x();
info.selection_start = 1;
info.selection_end = 1; info.selection_end = 1;
element->UpdateInput(info); element->UpdateInput(info);
element->get_text_element()->LayOutTextForTest({512, 512}); element->get_text_element()->LayOutTextForTest();
auto position2 = element->get_text_element()->GetRawCursorBounds(); int x2 = element->get_text_element()->GetRawCursorBounds().x();
EXPECT_NE(position1, position2);
#endif EXPECT_LT(x1, x2);
}
TEST(TextInputTest, CursorPositionUpdatesOnClicks) {
auto element = std::make_unique<TextInput>(
kFontHeightMeters, TextInput::OnFocusChangedCallback(),
TextInput::OnInputEditedCallback());
TextInputInfo info(base::UTF8ToUTF16("text"));
element->UpdateInput(info);
element->get_text_element()->LayOutTextForTest();
// Click on the left edge of the field.
element->OnButtonDown(gfx::PointF(0.0, 0.5));
element->OnButtonUp(gfx::PointF(0.0, 0.5));
element->get_text_element()->LayOutTextForTest();
auto x1 = element->get_text_element()->GetRawCursorBounds().x();
// Click on the right edge of the field.
element->OnButtonDown(gfx::PointF(1.0, 0.5));
element->OnButtonUp(gfx::PointF(1.0, 0.5));
element->get_text_element()->LayOutTextForTest();
auto x2 = element->get_text_element()->GetRawCursorBounds().x();
EXPECT_EQ(x1, 0);
EXPECT_GT(x2, 0);
} }
} // namespace vr } // namespace vr
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