Commit 24138678 authored by Yash Malik's avatar Yash Malik Committed by Commit Bot

VR: Add the notion of focusable to UiElement

Bug: 
Cq-Include-Trybots: master.tryserver.chromium.android:android_optional_gpu_tests_rel;master.tryserver.chromium.linux:linux_optional_gpu_tests_rel;master.tryserver.chromium.mac:mac_optional_gpu_tests_rel;master.tryserver.chromium.win:win_optional_gpu_tests_rel
Change-Id: I57add5c261f28762a3cec1be364c6be8572703ca
Reviewed-on: https://chromium-review.googlesource.com/809606
Commit-Queue: Yash Malik <ymalik@chromium.org>
Reviewed-by: default avatarChristopher Grant <cjgrant@chromium.org>
Cr-Commit-Position: refs/heads/master@{#522413}
parent 2aead702
...@@ -12,6 +12,7 @@ namespace vr { ...@@ -12,6 +12,7 @@ namespace vr {
Keyboard::Keyboard() { Keyboard::Keyboard() {
set_name(kKeyboard); set_name(kKeyboard);
set_focusable(false);
SetVisibleImmediately(false); SetVisibleImmediately(false);
} }
...@@ -86,6 +87,10 @@ void Keyboard::Render(UiElementRenderer* renderer, ...@@ -86,6 +87,10 @@ void Keyboard::Render(UiElementRenderer* renderer,
delegate_->Draw(camera_model); delegate_->Draw(camera_model);
} }
void Keyboard::OnSetFocusable() {
DCHECK(!focusable());
}
void Keyboard::UpdateDelegateVisibility() { void Keyboard::UpdateDelegateVisibility() {
if (!delegate_) if (!delegate_)
return; return;
......
...@@ -34,6 +34,7 @@ class Keyboard : public UiElement { ...@@ -34,6 +34,7 @@ class Keyboard : public UiElement {
const gfx::Vector3dF& head_direction) override; const gfx::Vector3dF& head_direction) override;
void Render(UiElementRenderer* renderer, void Render(UiElementRenderer* renderer,
const CameraModel& camera_model) const final; const CameraModel& camera_model) const final;
void OnSetFocusable() override;
void UpdateDelegateVisibility(); void UpdateDelegateVisibility();
......
...@@ -26,6 +26,7 @@ TextInput::TextInput(int maximum_width_pixels, ...@@ -26,6 +26,7 @@ TextInput::TextInput(int maximum_width_pixels,
text->set_type(kTypeTextInputHint); text->set_type(kTypeTextInputHint);
text->set_draw_phase(kPhaseForeground); text->set_draw_phase(kPhaseForeground);
text->set_hit_testable(false); text->set_hit_testable(false);
text->set_focusable(false);
text->set_x_anchoring(LEFT); text->set_x_anchoring(LEFT);
text->set_x_centering(LEFT); text->set_x_centering(LEFT);
text->SetSize(1, 1); text->SetSize(1, 1);
...@@ -38,6 +39,7 @@ TextInput::TextInput(int maximum_width_pixels, ...@@ -38,6 +39,7 @@ TextInput::TextInput(int maximum_width_pixels,
text->set_type(kTypeTextInputText); text->set_type(kTypeTextInputText);
text->set_draw_phase(kPhaseForeground); text->set_draw_phase(kPhaseForeground);
text->set_hit_testable(true); text->set_hit_testable(true);
text->set_focusable(false);
text->set_x_anchoring(LEFT); text->set_x_anchoring(LEFT);
text->set_x_centering(LEFT); text->set_x_centering(LEFT);
text->set_bubble_events(true); text->set_bubble_events(true);
...@@ -53,6 +55,7 @@ TextInput::TextInput(int maximum_width_pixels, ...@@ -53,6 +55,7 @@ TextInput::TextInput(int maximum_width_pixels,
cursor->set_type(kTypeTextInputCursor); cursor->set_type(kTypeTextInputCursor);
cursor->set_draw_phase(kPhaseForeground); cursor->set_draw_phase(kPhaseForeground);
cursor->set_hit_testable(false); cursor->set_hit_testable(false);
cursor->set_focusable(false);
cursor->set_x_anchoring(LEFT); cursor->set_x_anchoring(LEFT);
cursor->set_y_anchoring(BOTTOM); cursor->set_y_anchoring(BOTTOM);
cursor->SetColor(SK_ColorBLUE); cursor->SetColor(SK_ColorBLUE);
...@@ -68,10 +71,6 @@ void TextInput::SetTextInputDelegate(TextInputDelegate* text_input_delegate) { ...@@ -68,10 +71,6 @@ void TextInput::SetTextInputDelegate(TextInputDelegate* text_input_delegate) {
delegate_ = text_input_delegate; delegate_ = text_input_delegate;
} }
bool TextInput::IsEditable() {
return true;
}
void TextInput::OnButtonUp(const gfx::PointF& position) { void TextInput::OnButtonUp(const gfx::PointF& position) {
RequestFocus(); RequestFocus();
} }
......
...@@ -31,7 +31,6 @@ class TextInput : public UiElement { ...@@ -31,7 +31,6 @@ class TextInput : public UiElement {
OnInputEditedCallback input_edit_callback); OnInputEditedCallback input_edit_callback);
~TextInput() override; ~TextInput() override;
bool IsEditable() 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;
......
...@@ -87,6 +87,13 @@ void UiElement::set_draw_phase(DrawPhase draw_phase) { ...@@ -87,6 +87,13 @@ void UiElement::set_draw_phase(DrawPhase draw_phase) {
void UiElement::OnSetDrawPhase() {} void UiElement::OnSetDrawPhase() {}
void UiElement::set_focusable(bool focusable) {
focusable_ = focusable;
OnSetFocusable();
}
void UiElement::OnSetFocusable() {}
void UiElement::Render(UiElementRenderer* renderer, void UiElement::Render(UiElementRenderer* renderer,
const CameraModel& model) const { const CameraModel& model) const {
// Elements without an overridden implementation of Render should have their // Elements without an overridden implementation of Render should have their
...@@ -190,10 +197,6 @@ bool UiElement::IsHitTestable() const { ...@@ -190,10 +197,6 @@ bool UiElement::IsHitTestable() const {
return IsVisible() && hit_testable_; return IsVisible() && hit_testable_;
} }
bool UiElement::IsEditable() {
return false;
}
void UiElement::SetSize(float width, float height) { void UiElement::SetSize(float width, float height) {
animation_player_.TransitionSizeTo(last_frame_time_, BOUNDS, size_, animation_player_.TransitionSizeTo(last_frame_time_, BOUNDS, size_,
gfx::SizeF(width, height)); gfx::SizeF(width, height));
......
...@@ -140,10 +140,6 @@ class UiElement : public cc::AnimationTarget { ...@@ -140,10 +140,6 @@ class UiElement : public cc::AnimationTarget {
// Indicates whether the element should be tested for cursor input. // Indicates whether the element should be tested for cursor input.
bool IsHitTestable() const; bool IsHitTestable() const;
// Indicates whether the element is an editable element. Editable elements
// such as the input field should override this.
virtual bool IsEditable();
virtual void Render(UiElementRenderer* renderer, virtual void Render(UiElementRenderer* renderer,
const CameraModel& model) const; const CameraModel& model) const;
...@@ -201,6 +197,10 @@ class UiElement : public cc::AnimationTarget { ...@@ -201,6 +197,10 @@ class UiElement : public cc::AnimationTarget {
bool hit_testable() const { return hit_testable_; } bool hit_testable() const { return hit_testable_; }
void set_hit_testable(bool hit_testable) { hit_testable_ = hit_testable; } void set_hit_testable(bool hit_testable) { hit_testable_ = hit_testable; }
bool focusable() const { return focusable_; }
void set_focusable(bool focusable);
virtual void OnSetFocusable();
bool scrollable() const { return scrollable_; } bool scrollable() const { return scrollable_; }
void set_scrollable(bool scrollable) { scrollable_ = scrollable; } void set_scrollable(bool scrollable) { scrollable_ = scrollable; }
...@@ -430,6 +430,9 @@ class UiElement : public cc::AnimationTarget { ...@@ -430,6 +430,9 @@ class UiElement : public cc::AnimationTarget {
// If false, the reticle will not hit the element, even if visible. // If false, the reticle will not hit the element, even if visible.
bool hit_testable_ = true; bool hit_testable_ = true;
// If false, clicking on the element doesn't give it focus.
bool focusable_ = true;
// A signal to the input routing machinery that this element accepts scrolls. // A signal to the input routing machinery that this element accepts scrolls.
bool scrollable_ = false; bool scrollable_ = false;
......
...@@ -326,7 +326,7 @@ void UiInputManager::SendButtonDown(UiElement* target, ...@@ -326,7 +326,7 @@ void UiInputManager::SendButtonDown(UiElement* target,
// Clicking outside of the focused element causes it to lose focus. // Clicking outside of the focused element causes it to lose focus.
// TODO(ymalik): We will lose focus if we hit an element inside the focused // TODO(ymalik): We will lose focus if we hit an element inside the focused
// element, which is incorrect behavior. // element, which is incorrect behavior.
if (target->id() != focused_element_id_ && target->name() != kKeyboard) { if (target->id() != focused_element_id_ && target->focusable()) {
UnfocusFocusedElement(); UnfocusFocusedElement();
} }
} else { } else {
...@@ -433,7 +433,7 @@ void UiInputManager::UnfocusFocusedElement() { ...@@ -433,7 +433,7 @@ void UiInputManager::UnfocusFocusedElement() {
return; return;
UiElement* focused = scene_->GetUiElementById(focused_element_id_); UiElement* focused = scene_->GetUiElementById(focused_element_id_);
if (focused && focused->IsEditable()) { if (focused && focused->focusable()) {
focused->OnFocusChanged(false); focused->OnFocusChanged(false);
} }
focused_element_id_ = -1; focused_element_id_ = -1;
...@@ -446,7 +446,7 @@ void UiInputManager::RequestFocus(int element_id) { ...@@ -446,7 +446,7 @@ void UiInputManager::RequestFocus(int element_id) {
UnfocusFocusedElement(); UnfocusFocusedElement();
UiElement* focused = scene_->GetUiElementById(element_id); UiElement* focused = scene_->GetUiElementById(element_id);
if (!focused || !focused->IsEditable()) if (!focused || !focused->focusable())
return; return;
focused_element_id_ = element_id; focused_element_id_ = element_id;
...@@ -455,15 +455,17 @@ void UiInputManager::RequestFocus(int element_id) { ...@@ -455,15 +455,17 @@ void UiInputManager::RequestFocus(int element_id) {
void UiInputManager::OnInputEdited(const TextInputInfo& info) { void UiInputManager::OnInputEdited(const TextInputInfo& info) {
UiElement* focused = scene_->GetUiElementById(focused_element_id_); UiElement* focused = scene_->GetUiElementById(focused_element_id_);
if (!focused || !focused->IsEditable()) if (!focused)
return; return;
DCHECK(focused->focusable());
focused->OnInputEdited(info); focused->OnInputEdited(info);
} }
void UiInputManager::OnInputCommitted(const TextInputInfo& info) { void UiInputManager::OnInputCommitted(const TextInputInfo& info) {
UiElement* focused = scene_->GetUiElementById(focused_element_id_); UiElement* focused = scene_->GetUiElementById(focused_element_id_);
if (!focused || !focused->IsEditable()) if (!focused || !focused->focusable())
return; return;
DCHECK(focused->focusable());
focused->OnInputCommitted(info); focused->OnInputCommitted(info);
} }
......
...@@ -47,6 +47,9 @@ class MockRect : public Rect { ...@@ -47,6 +47,9 @@ class MockRect : public Rect {
MOCK_METHOD1(OnMove, void(const gfx::PointF& position)); MOCK_METHOD1(OnMove, void(const gfx::PointF& position));
MOCK_METHOD1(OnButtonDown, void(const gfx::PointF& position)); MOCK_METHOD1(OnButtonDown, void(const gfx::PointF& position));
MOCK_METHOD1(OnButtonUp, void(const gfx::PointF& position)); MOCK_METHOD1(OnButtonUp, void(const gfx::PointF& position));
MOCK_METHOD1(OnFocusChanged, void(bool));
MOCK_METHOD1(OnInputEdited, void(const TextInputInfo&));
MOCK_METHOD1(OnInputCommitted, void(const TextInputInfo&));
private: private:
DISALLOW_COPY_AND_ASSIGN(MockRect); DISALLOW_COPY_AND_ASSIGN(MockRect);
...@@ -76,7 +79,7 @@ class UiInputManagerTest : public testing::Test { ...@@ -76,7 +79,7 @@ class UiInputManagerTest : public testing::Test {
input_manager_ = base::MakeUnique<UiInputManager>(scene_.get()); input_manager_ = base::MakeUnique<UiInputManager>(scene_.get());
} }
StrictMock<MockRect>* CreateMockElement(float z_position) { StrictMock<MockRect>* CreateAndAddMockElement(float z_position) {
auto element = base::MakeUnique<StrictMock<MockRect>>(); auto element = base::MakeUnique<StrictMock<MockRect>>();
StrictMock<MockRect>* p_element = element.get(); StrictMock<MockRect>* p_element = element.get();
element->SetTranslate(0, 0, z_position); element->SetTranslate(0, 0, z_position);
...@@ -86,7 +89,7 @@ class UiInputManagerTest : public testing::Test { ...@@ -86,7 +89,7 @@ class UiInputManagerTest : public testing::Test {
return p_element; return p_element;
} }
StrictMock<MockTextInput>* CreateMockInputElement(float z_position) { StrictMock<MockTextInput>* CreateAndAddMockInputElement(float z_position) {
auto element = base::MakeUnique<StrictMock<MockTextInput>>(); auto element = base::MakeUnique<StrictMock<MockTextInput>>();
StrictMock<MockTextInput>* p_element = element.get(); StrictMock<MockTextInput>* p_element = element.get();
element->SetTranslate(0, 0, z_position); element->SetTranslate(0, 0, z_position);
...@@ -133,8 +136,8 @@ class UiInputManagerContentTest : public UiTest { ...@@ -133,8 +136,8 @@ class UiInputManagerContentTest : public UiTest {
}; };
TEST_F(UiInputManagerTest, FocusedElement) { TEST_F(UiInputManagerTest, FocusedElement) {
StrictMock<MockTextInput>* p_element1 = CreateMockInputElement(-5.f); StrictMock<MockTextInput>* p_element1 = CreateAndAddMockInputElement(-5.f);
StrictMock<MockTextInput>* p_element2 = CreateMockInputElement(-5.f); StrictMock<MockTextInput>* p_element2 = CreateAndAddMockInputElement(-5.f);
TextInputInfo edit(base::ASCIIToUTF16("asdfg")); TextInputInfo edit(base::ASCIIToUTF16("asdfg"));
// Focus request triggers OnFocusChanged. // Focus request triggers OnFocusChanged.
...@@ -156,6 +159,54 @@ TEST_F(UiInputManagerTest, FocusedElement) { ...@@ -156,6 +159,54 @@ TEST_F(UiInputManagerTest, FocusedElement) {
input_manager_->RequestFocus(p_element2->id()); input_manager_->RequestFocus(p_element2->id());
} }
// Verify that a focusable child clears focus off its parent. Note that the
// child isn't any different from other elements in that it should also steal
// focus from its parent.
TEST_F(UiInputManagerTest, FocusableChildStealsFocus) {
StrictMock<MockRect>* p_element = CreateAndAddMockElement(-5.f);
auto child = base::MakeUnique<StrictMock<MockRect>>();
auto* p_child = child.get();
child->set_hit_testable(true);
child->set_focusable(true);
p_element->AddChild(std::move(child));
scene_->OnBeginFrame(base::TimeTicks(), kForwardVector);
// Focus element.
testing::Sequence s;
EXPECT_CALL(*p_element, OnFocusChanged(true)).InSequence(s);
input_manager_->RequestFocus(p_element->id());
// Focus child.
EXPECT_CALL(*p_child, OnHoverEnter(_)).InSequence(s);
EXPECT_CALL(*p_child, OnButtonDown(_)).InSequence(s);
EXPECT_CALL(*p_element, OnFocusChanged(false)).InSequence(s);
HandleInput(kForwardVector, kDown);
}
// Verify that a non-focusable child does not clear focus off its parent.
TEST_F(UiInputManagerTest, NonFocusableChildDoestNotStealFocus) {
StrictMock<MockRect>* p_element = CreateAndAddMockElement(-5.f);
auto child = base::MakeUnique<StrictMock<MockRect>>();
auto* p_child = child.get();
child->set_hit_testable(true);
child->set_focusable(false);
p_element->AddChild(std::move(child));
scene_->OnBeginFrame(base::TimeTicks(), kForwardVector);
// Focus element.
testing::Sequence s;
EXPECT_CALL(*p_element, OnFocusChanged(true)).InSequence(s);
input_manager_->RequestFocus(p_element->id());
// Focus child.
EXPECT_CALL(*p_child, OnHoverEnter(_)).InSequence(s);
EXPECT_CALL(*p_child, OnButtonDown(_)).InSequence(s);
EXPECT_CALL(*p_element, OnFocusChanged(false)).Times(0).InSequence(s);
HandleInput(kForwardVector, kDown);
}
TEST_F(UiInputManagerTest, ReticleRenderTarget) { TEST_F(UiInputManagerTest, ReticleRenderTarget) {
auto element = base::MakeUnique<Rect>(); auto element = base::MakeUnique<Rect>();
UiElement* p_element = element.get(); UiElement* p_element = element.get();
...@@ -186,7 +237,7 @@ TEST_F(UiInputManagerTest, ReticleRenderTarget) { ...@@ -186,7 +237,7 @@ TEST_F(UiInputManagerTest, ReticleRenderTarget) {
// either directly at (forward) or directly away from (backward) a test element. // either directly at (forward) or directly away from (backward) a test element.
// Verify mock expectations along the way to make failures easier to track. // Verify mock expectations along the way to make failures easier to track.
TEST_F(UiInputManagerTest, HoverClick) { TEST_F(UiInputManagerTest, HoverClick) {
StrictMock<MockRect>* p_element = CreateMockElement(-5.f); StrictMock<MockRect>* p_element = CreateAndAddMockElement(-5.f);
// Move over the test element. // Move over the test element.
EXPECT_CALL(*p_element, OnHoverEnter(_)); EXPECT_CALL(*p_element, OnHoverEnter(_));
...@@ -244,8 +295,8 @@ TEST_F(UiInputManagerTest, HoverClick) { ...@@ -244,8 +295,8 @@ TEST_F(UiInputManagerTest, HoverClick) {
// releasing the button. Upon release, the previous element should see its click // releasing the button. Upon release, the previous element should see its click
// and hover states cleared, and the new element should see a hover. // and hover states cleared, and the new element should see a hover.
TEST_F(UiInputManagerTest, ReleaseButtonOnAnotherElement) { TEST_F(UiInputManagerTest, ReleaseButtonOnAnotherElement) {
StrictMock<MockRect>* p_front_element = CreateMockElement(-5.f); StrictMock<MockRect>* p_front_element = CreateAndAddMockElement(-5.f);
StrictMock<MockRect>* p_back_element = CreateMockElement(5.f); StrictMock<MockRect>* p_back_element = CreateAndAddMockElement(5.f);
// Press on an element, move away, then release. // Press on an element, move away, then release.
EXPECT_CALL(*p_front_element, OnHoverEnter(_)); EXPECT_CALL(*p_front_element, OnHoverEnter(_));
...@@ -262,7 +313,7 @@ TEST_F(UiInputManagerTest, ReleaseButtonOnAnotherElement) { ...@@ -262,7 +313,7 @@ TEST_F(UiInputManagerTest, ReleaseButtonOnAnotherElement) {
// Test that input is tolerant of disappearing elements. // Test that input is tolerant of disappearing elements.
TEST_F(UiInputManagerTest, ElementDeletion) { TEST_F(UiInputManagerTest, ElementDeletion) {
StrictMock<MockRect>* p_element = CreateMockElement(-5.f); StrictMock<MockRect>* p_element = CreateAndAddMockElement(-5.f);
// Hover on an element. // Hover on an element.
EXPECT_CALL(*p_element, OnHoverEnter(_)); EXPECT_CALL(*p_element, OnHoverEnter(_));
......
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