Commit 6e5c464e authored by Ian Vollick's avatar Ian Vollick Committed by Commit Bot

[vr] Add UiElement iterators

With this CL you can now iterate across elements both forward and
backward (for rendering and hit testing, respectively).

Bug: None
Change-Id: Iadc1f43353ac5ed8cef550103bac223129802035
Reviewed-on: https://chromium-review.googlesource.com/672605
Commit-Queue: Ian Vollick <vollick@chromium.org>
Reviewed-by: default avatarChristopher Grant <cjgrant@chromium.org>
Cr-Commit-Position: refs/heads/master@{#502926}
parent b9b7f3af
...@@ -73,6 +73,7 @@ static_library("vr_common") { ...@@ -73,6 +73,7 @@ static_library("vr_common") {
"elements/transience_manager.h", "elements/transience_manager.h",
"elements/ui_element.cc", "elements/ui_element.cc",
"elements/ui_element.h", "elements/ui_element.h",
"elements/ui_element_iterator.h",
"elements/ui_element_name.h", "elements/ui_element_name.h",
"elements/ui_element_transform_operations.cc", "elements/ui_element_transform_operations.cc",
"elements/ui_element_transform_operations.h", "elements/ui_element_transform_operations.h",
...@@ -154,6 +155,7 @@ test("vr_common_unittests") { ...@@ -154,6 +155,7 @@ test("vr_common_unittests") {
"elements/exit_prompt_unittest.cc", "elements/exit_prompt_unittest.cc",
"elements/linear_layout_unittest.cc", "elements/linear_layout_unittest.cc",
"elements/transience_manager_unittest.cc", "elements/transience_manager_unittest.cc",
"elements/ui_element_iterator_unittest.cc",
"elements/ui_element_unittest.cc", "elements/ui_element_unittest.cc",
"elements/url_bar_texture_unittest.cc", "elements/url_bar_texture_unittest.cc",
"elements/viewport_aware_root_unittest.cc", "elements/viewport_aware_root_unittest.cc",
......
...@@ -36,14 +36,6 @@ bool GetRayPlaneDistance(const gfx::Point3F& ray_origin, ...@@ -36,14 +36,6 @@ bool GetRayPlaneDistance(const gfx::Point3F& ray_origin,
return true; return true;
} }
template <typename T>
void GetAllElementsInSubtree(T* e, std::vector<T*>* elements) {
elements->push_back(e);
for (auto& child : e->children()) {
GetAllElementsInSubtree<T>(child.get(), elements);
}
}
} // namespace } // namespace
UiElement::UiElement() : id_(AllocateId()) { UiElement::UiElement() : id_(AllocateId()) {
...@@ -191,18 +183,6 @@ void UiElement::SetMode(ColorScheme::Mode mode) { ...@@ -191,18 +183,6 @@ void UiElement::SetMode(ColorScheme::Mode mode) {
OnSetMode(); OnSetMode();
} }
std::vector<UiElement*> UiElement::AllElementsInSubtree() {
std::vector<UiElement*> elements;
GetAllElementsInSubtree(this, &elements);
return elements;
}
std::vector<const UiElement*> UiElement::AllElementsInSubtree() const {
std::vector<const UiElement*> elements;
GetAllElementsInSubtree(this, &elements);
return elements;
}
void UiElement::OnSetMode() {} void UiElement::OnSetMode() {}
void UiElement::OnUpdatedInheritedProperties() {} void UiElement::OnUpdatedInheritedProperties() {}
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "chrome/browser/vr/animation_player.h" #include "chrome/browser/vr/animation_player.h"
#include "chrome/browser/vr/color_scheme.h" #include "chrome/browser/vr/color_scheme.h"
#include "chrome/browser/vr/elements/draw_phase.h" #include "chrome/browser/vr/elements/draw_phase.h"
#include "chrome/browser/vr/elements/ui_element_iterator.h"
#include "chrome/browser/vr/elements/ui_element_name.h" #include "chrome/browser/vr/elements/ui_element_name.h"
#include "chrome/browser/vr/target_property.h" #include "chrome/browser/vr/target_property.h"
#include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkColor.h"
...@@ -191,6 +192,7 @@ class UiElement : public cc::AnimationTarget { ...@@ -191,6 +192,7 @@ class UiElement : public cc::AnimationTarget {
void AddChild(std::unique_ptr<UiElement> child); void AddChild(std::unique_ptr<UiElement> child);
void RemoveChild(UiElement* child); void RemoveChild(UiElement* child);
UiElement* parent() { return parent_; } UiElement* parent() { return parent_; }
const UiElement* parent() const { return parent_; }
gfx::Point3F GetCenter() const; gfx::Point3F GetCenter() const;
gfx::Vector3dF GetNormal() const; gfx::Vector3dF GetNormal() const;
...@@ -249,8 +251,37 @@ class UiElement : public cc::AnimationTarget { ...@@ -249,8 +251,37 @@ class UiElement : public cc::AnimationTarget {
return children_; return children_;
} }
std::vector<UiElement*> AllElementsInSubtree(); typedef ForwardUiElementIterator iterator;
std::vector<const UiElement*> AllElementsInSubtree() const; typedef ConstForwardUiElementIterator const_iterator;
typedef ReverseUiElementIterator reverse_iterator;
typedef ConstReverseUiElementIterator const_reverse_iterator;
iterator begin() { return iterator(this); }
iterator end() { return iterator(nullptr); }
const_iterator begin() const { return const_iterator(this); }
const_iterator end() const { return const_iterator(nullptr); }
reverse_iterator rbegin() { return reverse_iterator(this); }
reverse_iterator rend() { return reverse_iterator(nullptr); }
const_reverse_iterator rbegin() const { return const_reverse_iterator(this); }
const_reverse_iterator rend() const {
return const_reverse_iterator(nullptr);
}
template <typename T>
struct Reversed {
explicit Reversed(T* root) : root(root) {}
reverse_iterator begin() { return root->rbegin(); }
reverse_iterator end() { return root->rend(); }
const_reverse_iterator begin() const { return root->rbegin(); }
const_reverse_iterator end() const { return root->rend(); }
T* root;
};
Reversed<UiElement> reversed() { return Reversed<UiElement>(this); }
const Reversed<const UiElement> reversed() const {
return Reversed<const UiElement>(this);
}
protected: protected:
virtual void OnSetMode(); virtual void OnSetMode();
......
// 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_ELEMENTS_UI_ELEMENT_ITERATOR_H_
#define CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_ITERATOR_H_
#include <vector>
namespace vr {
class UiElement;
// The UiElementIterator traverses a UiElement subtree. Do not use this class
// directly. You should, instead, use UiElement::end/begin/rend/rbegin (or the
// convenience functions on UiScene).
template <typename T, typename U>
class UiElementIterator {
public:
explicit UiElementIterator(T* root) {
current_ = U::Increment(&index_stack_, root, true);
}
~UiElementIterator() {}
void operator++() { current_ = U::Increment(&index_stack_, current_, false); }
void operator++(int) {
current_ = U::Increment(&index_stack_, current_, false);
}
T& operator*() { return *current_; }
bool operator!=(const UiElementIterator& rhs) {
return current_ != rhs.current_ ||
index_stack_.empty() != rhs.index_stack_.empty();
}
private:
T* current_ = nullptr;
// The iterator maintains a stack of indices which represent the current index
// in each list of children along the ancestor path.
std::vector<size_t> index_stack_;
};
template <typename T>
struct ForwardIncrementer {
static T* Increment(std::vector<size_t>* index_stack, T* e, bool init) {
if (!e || init)
return e;
if (!e->children().empty()) {
index_stack->push_back(0lu);
return e->children().front().get();
}
while (e->parent() && !index_stack->empty() &&
index_stack->back() + 1lu >= e->parent()->children().size()) {
index_stack->pop_back();
e = e->parent();
}
if (!e->parent() || index_stack->empty())
return nullptr;
index_stack->back()++;
return e->parent()->children()[index_stack->back()].get();
}
};
template <typename T>
struct ReverseIncrementer {
static T* Increment(std::vector<size_t>* index_stack, T* e, bool init) {
if (!e || (index_stack->empty() && !init))
return nullptr;
bool should_descend = false;
if (index_stack->empty()) {
should_descend = true;
} else if (e->parent() && index_stack->back() > 0lu) {
index_stack->back()--;
e = e->parent()->children()[index_stack->back()].get();
should_descend = true;
}
if (should_descend) {
while (!e->children().empty()) {
index_stack->push_back(e->children().size() - 1lu);
e = e->children().back().get();
}
return e;
}
index_stack->pop_back();
return e->parent();
}
};
typedef UiElementIterator<UiElement, ForwardIncrementer<UiElement>>
ForwardUiElementIterator;
typedef UiElementIterator<const UiElement, ForwardIncrementer<const UiElement>>
ConstForwardUiElementIterator;
typedef UiElementIterator<UiElement, ReverseIncrementer<UiElement>>
ReverseUiElementIterator;
typedef UiElementIterator<const UiElement, ReverseIncrementer<const UiElement>>
ConstReverseUiElementIterator;
} // namespace vr
#endif // CHROME_BROWSER_VR_ELEMENTS_UI_ELEMENT_ITERATOR_H_
// 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/elements/ui_element.h"
#include "base/memory/ptr_util.h"
#include "base/test/gtest_util.h"
#include "chrome/browser/vr/ui_scene.h"
namespace vr {
namespace {
// Constructs a tree of the following form
// 1 kRoot
// 2 k2dBrowsingRoot
// 3 kFloor
// 4 k2dBrowsingContentGroup
// 5 kBackplane
// 6 kContentQuad
// 7 kUrlBar
// 8 kCeiling
void MakeTree(UiScene* scene) {
auto element = base::MakeUnique<UiElement>();
element->set_name(k2dBrowsingRoot);
scene->AddUiElement(kRoot, std::move(element));
element = base::MakeUnique<UiElement>();
element->set_name(kFloor);
scene->AddUiElement(k2dBrowsingRoot, std::move(element));
element = base::MakeUnique<UiElement>();
element->set_name(k2dBrowsingContentGroup);
scene->AddUiElement(k2dBrowsingRoot, std::move(element));
element = base::MakeUnique<UiElement>();
element->set_name(kBackplane);
scene->AddUiElement(k2dBrowsingContentGroup, std::move(element));
element = base::MakeUnique<UiElement>();
element->set_name(kContentQuad);
scene->AddUiElement(k2dBrowsingContentGroup, std::move(element));
element = base::MakeUnique<UiElement>();
element->set_name(kUrlBar);
scene->AddUiElement(k2dBrowsingContentGroup, std::move(element));
element = base::MakeUnique<UiElement>();
element->set_name(kCeiling);
scene->AddUiElement(k2dBrowsingRoot, std::move(element));
}
template <typename T>
void CollectElements(T* e, std::vector<T*>* elements) {
elements->push_back(e);
for (auto& child : e->children()) {
CollectElements(child.get(), elements);
}
}
} // namespace
struct UiElementIteratorTestCase {
UiElementName root;
size_t num_elements_in_subtree;
};
class UiElementIteratorTest
: public ::testing::TestWithParam<UiElementIteratorTestCase> {};
TEST_P(UiElementIteratorTest, VerifyTraversal) {
UiScene scene;
MakeTree(&scene);
std::vector<UiElement*> elements;
CollectElements(scene.GetUiElementByName(GetParam().root), &elements);
size_t i = 0;
for (auto& e : *(scene.GetUiElementByName(GetParam().root))) {
EXPECT_GT(elements.size(), i);
EXPECT_EQ(elements[i++]->id(), e.id());
}
EXPECT_EQ(elements.size(), i);
EXPECT_EQ(GetParam().num_elements_in_subtree, i);
i = 0;
for (auto& e : *const_cast<const UiElement*>(
scene.GetUiElementByName(GetParam().root))) {
EXPECT_GT(elements.size(), i);
EXPECT_EQ(elements[i++]->id(), e.id());
}
EXPECT_EQ(elements.size(), i);
EXPECT_EQ(GetParam().num_elements_in_subtree, i);
// Since the reversed iteration happens using a different type
// (UiElement::Reversed), we'll do that separately here rather than using
// conditionals and another test param.
i = 0;
for (auto& e : scene.GetUiElementByName(GetParam().root)->reversed()) {
EXPECT_GT(elements.size(), i);
EXPECT_EQ(elements[elements.size() - i++ - 1lu]->id(), e.id());
}
EXPECT_EQ(elements.size(), i);
EXPECT_EQ(GetParam().num_elements_in_subtree, i);
i = 0;
for (auto& e :
const_cast<const UiElement*>(scene.GetUiElementByName(GetParam().root))
->reversed()) {
EXPECT_GT(elements.size(), i);
EXPECT_EQ(elements[elements.size() - i++ - 1lu]->id(), e.id());
}
EXPECT_EQ(elements.size(), i);
EXPECT_EQ(GetParam().num_elements_in_subtree, i);
}
const std::vector<UiElementIteratorTestCase> iterator_test_cases = {
{kRoot, 8},
{k2dBrowsingContentGroup, 4},
{kCeiling, 1},
};
INSTANTIATE_TEST_CASE_P(UiElementIteratorTestCases,
UiElementIteratorTest,
::testing::ValuesIn(iterator_test_cases));
} // namespace vr
...@@ -18,35 +18,13 @@ namespace vr { ...@@ -18,35 +18,13 @@ namespace vr {
namespace { namespace {
template <typename F>
void ForAllElements(UiElement* e, F f) {
f(e);
for (auto& child : e->children()) {
ForAllElements(child.get(), f);
}
}
template <typename P>
UiElement* FindElement(UiElement* e, P predicate) {
if (predicate(e)) {
return e;
}
for (const auto& child : e->children()) {
if (UiElement* match = FindElement(child.get(), predicate)) {
return match;
}
}
return nullptr;
}
template <typename P> template <typename P>
UiScene::Elements GetVisibleElements(UiElement* e, P predicate) { UiScene::Elements GetVisibleElements(UiElement* root, P predicate) {
UiScene::Elements elements; UiScene::Elements elements;
ForAllElements(e, [&elements, predicate](UiElement* element) { for (auto& element : *root) {
if (element->IsVisible() && predicate(element)) { if (element.IsVisible() && predicate(&element))
elements.push_back(element); elements.push_back(&element);
} }
});
return elements; return elements;
} }
...@@ -82,22 +60,22 @@ void UiScene::RemoveAnimation(int element_id, int animation_id) { ...@@ -82,22 +60,22 @@ void UiScene::RemoveAnimation(int element_id, int animation_id) {
void UiScene::OnBeginFrame(const base::TimeTicks& current_time, void UiScene::OnBeginFrame(const base::TimeTicks& current_time,
const gfx::Vector3dF& look_at) { const gfx::Vector3dF& look_at) {
ForAllElements(root_element_.get(), [current_time](UiElement* element) { for (auto& element : *root_element_) {
// Process all animations before calculating object transforms. // Process all animations before calculating object transforms.
element->Animate(current_time); element.Animate(current_time);
}); }
ForAllElements(root_element_.get(), [look_at](UiElement* element) { for (auto& element : *root_element_) {
element->LayOutChildren(); element.LayOutChildren();
element->AdjustRotationForHeadPose(look_at); element.AdjustRotationForHeadPose(look_at);
}); }
root_element_->UpdateInheritedProperties(); root_element_->UpdateInheritedProperties();
} }
void UiScene::PrepareToDraw() { void UiScene::PrepareToDraw() {
ForAllElements(root_element_.get(), for (auto& element : *root_element_)
[](UiElement* element) { element->PrepareToDraw(); }); element.PrepareToDraw();
} }
UiElement& UiScene::root_element() { UiElement& UiScene::root_element() {
...@@ -105,16 +83,19 @@ UiElement& UiScene::root_element() { ...@@ -105,16 +83,19 @@ UiElement& UiScene::root_element() {
} }
UiElement* UiScene::GetUiElementById(int element_id) const { UiElement* UiScene::GetUiElementById(int element_id) const {
return FindElement(root_element_.get(), [element_id](UiElement* element) { for (auto& element : *root_element_) {
return element->id() == element_id; if (element.id() == element_id)
}); return &element;
}
return nullptr;
} }
UiElement* UiScene::GetUiElementByName(UiElementName name) const { UiElement* UiScene::GetUiElementByName(UiElementName name) const {
DCHECK(name != UiElementName::kNone); for (auto& element : *root_element_) {
return FindElement(root_element_.get(), [name](UiElement* element) { if (element.name() == name)
return element->name() == name; return &element;
}); }
return nullptr;
} }
UiScene::Elements UiScene::GetVisible2dBrowsingElements() const { UiScene::Elements UiScene::GetVisible2dBrowsingElements() const {
...@@ -162,8 +143,8 @@ UiScene::~UiScene() = default; ...@@ -162,8 +143,8 @@ UiScene::~UiScene() = default;
// initialize when the binding fires, automatically. // initialize when the binding fires, automatically.
void UiScene::OnGlInitialized() { void UiScene::OnGlInitialized() {
gl_initialized_ = true; gl_initialized_ = true;
ForAllElements(root_element_.get(), for (auto& element : *root_element_)
[](UiElement* element) { element->Initialize(); }); element.Initialize();
} }
} // namespace vr } // namespace vr
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "chrome/browser/vr/color_scheme.h" #include "chrome/browser/vr/color_scheme.h"
#include "chrome/browser/vr/elements/ui_element.h"
#include "chrome/browser/vr/elements/ui_element_iterator.h"
#include "chrome/browser/vr/elements/ui_element_name.h" #include "chrome/browser/vr/elements/ui_element_name.h"
#include "third_party/skia/include/core/SkColor.h" #include "third_party/skia/include/core/SkColor.h"
...@@ -32,15 +34,6 @@ class UiElement; ...@@ -32,15 +34,6 @@ class UiElement;
class UiScene { class UiScene {
public: public:
enum Command {
ADD_ELEMENT,
UPDATE_ELEMENT,
REMOVE_ELEMENT,
ADD_ANIMATION,
REMOVE_ANIMATION,
CONFIGURE_SCENE,
};
UiScene(); UiScene();
virtual ~UiScene(); virtual ~UiScene();
...@@ -98,6 +91,23 @@ class UiScene { ...@@ -98,6 +91,23 @@ class UiScene {
void OnGlInitialized(); void OnGlInitialized();
UiElement::iterator begin() { return root_element_->begin(); }
UiElement::iterator end() { return root_element_->end(); }
UiElement::const_iterator begin() const {
return const_cast<const UiElement&>(*root_element_).begin();
}
UiElement::const_iterator end() const {
return const_cast<const UiElement&>(*root_element_).end();
}
UiElement::Reversed<UiElement> reversed() {
return UiElement::Reversed<UiElement>(root_element_.get());
}
const UiElement::Reversed<const UiElement> reversed() const {
return UiElement::Reversed<const UiElement>(root_element_.get());
}
private: private:
void Animate(const base::TimeTicks& current_time); void Animate(const base::TimeTicks& current_time);
......
...@@ -500,11 +500,11 @@ TEST_F(UiSceneManagerTest, UiUpdatesForWebVR) { ...@@ -500,11 +500,11 @@ TEST_F(UiSceneManagerTest, UiUpdatesForWebVR) {
manager_->SetBluetoothConnectedIndicator(true); manager_->SetBluetoothConnectedIndicator(true);
auto* web_vr_root = scene_->GetUiElementByName(kWebVrRoot); auto* web_vr_root = scene_->GetUiElementByName(kWebVrRoot);
for (auto* element : web_vr_root->AllElementsInSubtree()) { for (auto& element : *web_vr_root) {
SCOPED_TRACE(element->name()); SCOPED_TRACE(element.name());
EXPECT_TRUE(element->draw_phase() == kPhaseNone || EXPECT_TRUE(element.draw_phase() == kPhaseNone ||
element->draw_phase() == kPhaseOverlayBackground || element.draw_phase() == kPhaseOverlayBackground ||
element->draw_phase() == kPhaseOverlayForeground); element.draw_phase() == kPhaseOverlayForeground);
} }
// All elements should be hidden. // All elements should be hidden.
......
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